-
Notifications
You must be signed in to change notification settings - Fork 468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adapter: Prevent login to mz_system externally #14093
Conversation
654ecc2
to
3dfd866
Compare
that only an administrator should be doing. This commit prevents logging into the mz_system user from an external port. You can only login to the mz_system user via internal ports. Works towards resolving: MaterializeInc/cloud#3292
3dfd866
to
1c8a30c
Compare
src/adapter/src/client.rs
Outdated
@@ -152,6 +164,8 @@ impl ConnClient { | |||
session: Session, | |||
create_user_if_not_exists: bool, | |||
) -> Result<(SessionClient, StartupResponse), AdapterError> { | |||
self.validate_user(session.user())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this go wherever the frontegg login stuff also happens? I guess not because it needs the client type. But I'm not thrilled that "can i login?" is now in multiple places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not very familiar with the Frontegg code, but I agree with your point. I'll do some digging and see if I can integrate it with the Frontegg stuff.
src/adapter/src/catalog.rs
Outdated
@@ -95,7 +95,8 @@ pub mod builtin; | |||
pub mod storage; | |||
|
|||
pub const SYSTEM_CONN_ID: ConnectionId = 0; | |||
const SYSTEM_USER: &str = "mz_system"; | |||
pub const SYSTEM_USER: &str = "mz_system"; | |||
pub const HTTP_DEFAULT_USER: &str = "mz_http_default_user"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very slight preference for this to be mz_http_internal_user
because default suggests that it's a fallback and is regularly used on the external http connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually only used on the external http connection as a fallback if there's no user authenticated. So maybe it should be mz_http_default_external_user
?
@@ -330,7 +331,7 @@ pub async fn serve(config: Config) -> Result<Server, anyhow::Error> { | |||
let http_server = http::Server::new(http::Config { | |||
tls: http_tls, | |||
frontegg: config.frontegg, | |||
adapter_client, | |||
adapter_client: external_adapter_client, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the internal http server get its internal adapter client?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit perplexing to me. The internal http port uses the http::InternalServer
struct (initialized above all of this on line 204) which doesn't use an adapter client and has the following endpoints:
/metrics
/api/livez
/api/opentelemetry/config
The external http port uses the http::Server
struct which uses an adapter client and has the following endpoints:
/
/api/internal/catalog
(This one is really weird since it contains "internal" in it's path but is only accessible from the external port)./api/sql
/memory
/hierarchical-memory
/prof/
/static/*path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good point! We should move that internal catalog API to the internal HTTP server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks very much for whipping this one up, @jkosh44! A few quick thoughts now having looked at your PR:
- Can we make
mz_http_default_user
not a built-in role? It's something that only is relevant when you're running locally without authentication, so it'd be nice to hide it. What if we called the useranonymous
, and created it automatically ifffrontegg_auth.is_none() && tls_mode.is_none()
? - I'd love to leave authentication the sole responsibility of the
environmentd
crate, rather than putting some of the logic into theadapter
crate! At present it's kinda nice that theadapter
layer just blindly trusts the user that you tell it. Can we instead add a check to the external HTTP and SQL servers that rejects any user name that begins withmz_
? That'll give us the flexibility to add future restricted-access system users, should they become necessary.
src/environmentd/src/http.rs
Outdated
// Create http default user for local usage. | ||
let create_default_user = frontegg.is_none() && tls_mode.is_none() && user == HTTP_DEFAULT_USER; | ||
// Add the authenticated user as an extension so downstream handlers can | ||
// inspect it if necessary. | ||
req.extensions_mut().insert(AuthedUser { | ||
user, | ||
create_if_not_exists: frontegg.is_some(), | ||
create_if_not_exists: frontegg.is_some() || create_default_user, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benesch Is this what you had in mind for creating the http user on demand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it expected ever that frontegg.is_none()
and the tls mode is TlsMode::Require
? If so this won't work because TlsMode::Require
and frontegg.is_none()
defaults the user to HTTP_DEFAULT_USER
, but then we won't create the default user because tls_mode.is_some()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm asking because there are tests in auth.rs
that explicitly test the HTTP API with TlsMode::Require
and no frontegg and expect it to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After discussing with @benesch in person, this condition should be something like frontegg.is_some() || tls_mode != TlsMode::AssumeUser
.
@mjibson @benesch I moved the authentication logic out of the |
…system-login � Conflicts: � src/environmentd/src/lib.rs
@@ -93,7 +93,8 @@ pub mod builtin; | |||
pub mod storage; | |||
|
|||
pub const SYSTEM_CONN_ID: ConnectionId = 0; | |||
const SYSTEM_USER: &str = "mz_system"; | |||
pub const SYSTEM_USER: &str = "mz_system"; | |||
pub const HTTP_DEFAULT_USER: &str = "anonymous_http_user"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should start with mz_
because that's guaranteed to be a protected username suffix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is possible because the way this works is that anonymous_http_user
doesn't exist unless someone tries to use the HTTP API with a username. Then we will create anonymous_http_user
on the spot as a user Role. However we don't allow any user roles to be created with the prefix mz_
because it's a reserved prefix.
@@ -988,7 +989,7 @@ fn test_auth() -> Result<(), Box<dyn Error>> { | |||
assert: Assert::Success, | |||
}, | |||
TestCase::Http { | |||
user: "mz_system", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a test for logging in as mz_system but failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
Oh, I missed this and the discussion about it during my review. I didn't realize it's local only. Ok, LGTM then. |
mz_system is the system user that is responsible for executing commands
that only an administrator should be doing. This commit prevents
logging into the mz_system user from an external port. You can only
login to the mz_system user via internal ports.
Works towards resolving: https://github.com/MaterializeInc/cloud/issues/3292
Motivation
This PR adds a known-desirable feature.
Checklist
This PR has adequate test coverage / QA involvement has been duly considered.
This PR evolves an existing
$T ⇔ Proto$T
mapping (possibly in a backwards-incompatible way) and therefore is tagged with aT-protobuf
label.This PR includes the following user-facing behavior changes: