-
Notifications
You must be signed in to change notification settings - Fork 471
Unify HTTP servers #32409
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
base: main
Are you sure you want to change the base?
Unify HTTP servers #32409
Conversation
a3dd5eb
to
2b01e80
Compare
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 have pushed a commit that fixes the remaining test failures: d9a4779
Also triggered a nightly run: https://buildkite.com/materialize/nightly/builds/11971
7d5dd69
to
bd359e7
Compare
let (authenticator_frontegg_tx, authenticator_frontegg_rx) = oneshot::channel(); | ||
let authenticator_frontegg_rx = authenticator_frontegg_rx.shared(); | ||
let (authenticator_none_tx, authenticator_none_rx) = oneshot::channel(); | ||
let authenticator_none_rx = authenticator_none_rx.shared(); |
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 partially in preparation for Authenticator::Password(AdapterClient)
support coming in the next PR. While we can immediately create the Authenticator::Frontegg
and Authenticator::None
now, we won't be able to immediately create Authenticator::Password
, since we want to launch this HTTP server before the AdapterClient
is instantiated.
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.
Interesting, why won't we be able to construct a Password Authenticator here?
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.
We can't construct an Authenticator::Password
because it contains an AdapterClient
, which isn't ready yet. We want the listener to be available for metrics endpoints during the startup, so we can't wait to start the HTTP server until after the AdapterClient
is ready.
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.
Gotcha. I remember having to do something similar with pgwire. Perhaps in the future we should split out the metrics server if it's lifecycle is separate.
2712413
to
86384cb
Compare
86384cb
to
267b2af
Compare
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.
Looking good! Left a few notes
#[derive(Debug, Clone)] | ||
pub enum Authenticator { | ||
Frontegg(FronteggAuthenticator), | ||
None, |
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.
What is the meaning of None here?
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.
It means no authentication, like our internal port currently behaves (with the exception of removal of the default value for x-materialize-user
header).
if let AuthenticatorKind::None = authenticator_kind { | ||
ws_router = ws_router.layer(middleware::from_fn(x_materialize_user_header_auth)); | ||
} |
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.
If AuthenticatorKind::None we want to let anyone auth as mz_system? (We might very well, I just want to be careful here)
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.
Yes, this is maintaining the current behavior.
Even without this, if they set the basic auth user to mz_system
that would also allow them in with AuthenticatorKind::None
.
match authenticator_kind { | ||
AuthenticatorKind::None => { | ||
base_router = base_router.layer(middleware::from_fn(x_materialize_user_header_auth)) | ||
} | ||
_ => {} | ||
} |
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.
We manually place this above on specific routepath. If we generally apply it here, do we need the specific cases?
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.
We want it on these two sets of routes. We don't want it on all routes, though. For example, the webhook router does not have it.
@@ -11,6 +11,7 @@ $ skip-if | |||
SELECT mz_version_num() < 8300; | |||
|
|||
> SELECT name FROM mz_roles; | |||
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.
where does this user come from?
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 fixing an inconsistency in our prior behavior. The anonymous_http_user
comes from the DEFAULT_HTTP_USER
constant, which is used with Authenticator::None
when no x-materialize-user
or basic auth header is provided. Previously, the x-materialize-user
handling was applied first and defaulted to mz_system
user. I have removed this x-materialize-user
default, so it instead gets the basic auth default of DEFAULT_HTTP_USER
.
I hope to follow up after this PR is deployed with changing teleport to use basic auth instead of the x-materialize-user
header, so that we can later entirely remove the x-materialize-user
code path.
let (authenticator_frontegg_tx, authenticator_frontegg_rx) = oneshot::channel(); | ||
let authenticator_frontegg_rx = authenticator_frontegg_rx.shared(); | ||
let (authenticator_none_tx, authenticator_none_rx) = oneshot::channel(); | ||
let authenticator_none_rx = authenticator_none_rx.shared(); |
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.
Interesting, why won't we be able to construct a Password Authenticator here?
f2834d9
to
e222f6f
Compare
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.
Looks good! Thanks for the changes
async fn x_materialize_user_header_auth(mut req: Request, next: Next) -> impl IntoResponse { | ||
// TODO integrate this into the single auth flow | ||
// or migrate teleport to basic auth. | ||
if let Some(username) = req.headers().get("x-materialize-user").map(|h| h.to_str()) { |
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.
Not needed in this PR but in the follow up if we are hanging onto x-materialize-user lets throw it in a constant
let (authenticator_frontegg_tx, authenticator_frontegg_rx) = oneshot::channel(); | ||
let authenticator_frontegg_rx = authenticator_frontegg_rx.shared(); | ||
let (authenticator_none_tx, authenticator_none_rx) = oneshot::channel(); | ||
let authenticator_none_rx = authenticator_none_rx.shared(); |
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.
Gotcha. I remember having to do something similar with pgwire. Perhaps in the future we should split out the metrics server if it's lifecycle is separate.
Unify HTTP servers, and mostly unify their authentication.
We still retain the
x-materialize-user
header for now, but there is a change in behavior that we no longer specify a default value for it if it is not passed.Motivation
This is an attempt to split up Mz system password support #32245, as that PR is too big and complicated for review, and it conflated a few things which made isolating test failures difficult.
The
x-materialize-user
header is only used by Teleport, and it always uses themz_support
user, not themz_system
user we default to. This header doesn't even need to exist, as we could just have Teleport use basic auth without a password. Removing the default value for this header should allow us to migrate Teleport to using a basic auth header instead.Tips for reviewer
Checklist
$T ⇔ Proto$T
mapping (possibly in a backwards-incompatible way), then it is tagged with aT-proto
label.