Skip to content

Commit

Permalink
feat(proxy): emit JWT auth method and JWT issuer in parquet logs
Browse files Browse the repository at this point in the history
  • Loading branch information
conradludgate committed Dec 2, 2024
1 parent 1b60571 commit b29c6ec
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 30 deletions.
10 changes: 7 additions & 3 deletions proxy/src/auth/backend/jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,13 @@ impl JwkCacheEntryLock {
let header = base64::decode_config(header, base64::URL_SAFE_NO_PAD)?;
let header = serde_json::from_slice::<JwtHeader<'_>>(&header)?;

let payloadb = base64::decode_config(payload, base64::URL_SAFE_NO_PAD)?;
let payload = serde_json::from_slice::<JwtPayload<'_>>(&payloadb)?;

if let Some(iss) = &payload.issuer {
ctx.set_jwt_issuer(iss.as_ref().to_owned());
}

let sig = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;

let kid = header.key_id.ok_or(JwtError::MissingKeyId)?;
Expand Down Expand Up @@ -388,9 +395,6 @@ impl JwkCacheEntryLock {
key => return Err(JwtError::UnsupportedKeyType(key.into())),
};

let payloadb = base64::decode_config(payload, base64::URL_SAFE_NO_PAD)?;
let payload = serde_json::from_slice::<JwtPayload<'_>>(&payloadb)?;

tracing::debug!(?payload, "JWT signature valid with claims");

if let Some(aud) = expected_audience {
Expand Down
9 changes: 9 additions & 0 deletions proxy/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct RequestContextInner {
application: Option<SmolStr>,
error_kind: Option<ErrorKind>,
pub(crate) auth_method: Option<AuthMethod>,
jwt_issuer: Option<String>,
success: bool,
pub(crate) cold_start_info: ColdStartInfo,
pg_options: Option<StartupMessageParams>,
Expand All @@ -79,6 +80,7 @@ pub(crate) enum AuthMethod {
ScramSha256,
ScramSha256Plus,
Cleartext,
Jwt,
}

impl Clone for RequestContext {
Expand All @@ -100,6 +102,7 @@ impl Clone for RequestContext {
application: inner.application.clone(),
error_kind: inner.error_kind,
auth_method: inner.auth_method.clone(),
jwt_issuer: inner.jwt_issuer.clone(),
success: inner.success,
rejected: inner.rejected,
cold_start_info: inner.cold_start_info,
Expand Down Expand Up @@ -148,6 +151,7 @@ impl RequestContext {
application: None,
error_kind: None,
auth_method: None,
jwt_issuer: None,
success: false,
rejected: None,
cold_start_info: ColdStartInfo::Unknown,
Expand Down Expand Up @@ -246,6 +250,11 @@ impl RequestContext {
this.auth_method = Some(auth_method);
}

pub(crate) fn set_jwt_issuer(&self, jwt_issuer: String) {
let mut this = self.0.try_lock().expect("should not deadlock");
this.jwt_issuer = Some(jwt_issuer);
}

pub fn has_private_peer_addr(&self) -> bool {
self.0
.try_lock()
Expand Down
53 changes: 29 additions & 24 deletions proxy/src/context/parquet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub(crate) struct RequestData {
branch: Option<String>,
pg_options: Option<String>,
auth_method: Option<&'static str>,
jwt_issuer: Option<String>,

error: Option<&'static str>,
/// Success is counted if we form a HTTP response with sql rows inside
/// Or if we make it to proxy_pass
Expand Down Expand Up @@ -138,7 +140,9 @@ impl From<&RequestContextInner> for RequestData {
super::AuthMethod::ScramSha256 => "scram_sha_256",
super::AuthMethod::ScramSha256Plus => "scram_sha_256_plus",
super::AuthMethod::Cleartext => "cleartext",
super::AuthMethod::Jwt => "jwt",
}),
jwt_issuer: value.jwt_issuer.clone(),
protocol: value.protocol.as_str(),
region: value.region,
error: value.error_kind.as_ref().map(|e| e.to_metric_label()),
Expand Down Expand Up @@ -519,6 +523,7 @@ mod tests {
branch: Some(hex::encode(rng.gen::<[u8; 16]>())),
pg_options: None,
auth_method: None,
jwt_issuer: None,
protocol: ["tcp", "ws", "http"][rng.gen_range(0..3)],
region: "us-east-1",
error: None,
Expand Down Expand Up @@ -599,15 +604,15 @@ mod tests {
assert_eq!(
file_stats,
[
(1312632, 3, 6000),
(1312621, 3, 6000),
(1312680, 3, 6000),
(1312637, 3, 6000),
(1312773, 3, 6000),
(1312610, 3, 6000),
(1312404, 3, 6000),
(1312639, 3, 6000),
(437848, 1, 2000)
(1313105, 3, 6000),
(1313094, 3, 6000),
(1313153, 3, 6000),
(1313110, 3, 6000),
(1313246, 3, 6000),
(1313083, 3, 6000),
(1312877, 3, 6000),
(1313112, 3, 6000),
(438020, 1, 2000)
]
);

Expand Down Expand Up @@ -639,11 +644,11 @@ mod tests {
assert_eq!(
file_stats,
[
(1203465, 5, 10000),
(1203189, 5, 10000),
(1203490, 5, 10000),
(1203475, 5, 10000),
(1203729, 5, 10000)
(1204324, 5, 10000),
(1204048, 5, 10000),
(1204349, 5, 10000),
(1204334, 5, 10000),
(1204588, 5, 10000)
]
);

Expand All @@ -668,15 +673,15 @@ mod tests {
assert_eq!(
file_stats,
[
(1312632, 3, 6000),
(1312621, 3, 6000),
(1312680, 3, 6000),
(1312637, 3, 6000),
(1312773, 3, 6000),
(1312610, 3, 6000),
(1312404, 3, 6000),
(1312639, 3, 6000),
(437848, 1, 2000)
(1313105, 3, 6000),
(1313094, 3, 6000),
(1313153, 3, 6000),
(1313110, 3, 6000),
(1313246, 3, 6000),
(1313083, 3, 6000),
(1312877, 3, 6000),
(1313112, 3, 6000),
(438020, 1, 2000)
]
);

Expand Down Expand Up @@ -713,7 +718,7 @@ mod tests {
// files are smaller than the size threshold, but they took too long to fill so were flushed early
assert_eq!(
file_stats,
[(657696, 2, 3001), (657410, 2, 3000), (657206, 2, 2999)]
[(658014, 2, 3001), (657728, 2, 3000), (657524, 2, 2999)]
);

tmpdir.close().unwrap();
Expand Down
4 changes: 4 additions & 0 deletions proxy/src/serverless/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ impl PoolingBackend {
user_info: &ComputeUserInfo,
password: &[u8],
) -> Result<ComputeCredentials, AuthError> {
ctx.set_auth_method(crate::context::AuthMethod::Cleartext);

let user_info = user_info.clone();
let backend = self.auth_backend.as_ref().map(|()| user_info.clone());
let (allowed_ips, maybe_secret) = backend.get_allowed_ips_and_secret(ctx).await?;
Expand Down Expand Up @@ -115,6 +117,8 @@ impl PoolingBackend {
user_info: &ComputeUserInfo,
jwt: String,
) -> Result<ComputeCredentials, AuthError> {
ctx.set_auth_method(crate::context::AuthMethod::Jwt);

match &self.auth_backend {
crate::auth::Backend::ControlPlane(console, ()) => {
self.config
Expand Down
3 changes: 0 additions & 3 deletions proxy/src/serverless/sql_over_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,6 @@ fn get_conn_info(
headers: &HeaderMap,
tls: Option<&TlsConfig>,
) -> Result<ConnInfoWithAuth, ConnInfoError> {
// HTTP only uses cleartext (for now and likely always)
ctx.set_auth_method(crate::context::AuthMethod::Cleartext);

let connection_string = headers
.get(&CONN_STRING)
.ok_or(ConnInfoError::InvalidHeader(&CONN_STRING))?
Expand Down

0 comments on commit b29c6ec

Please sign in to comment.