mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-27 18:10:37 +00:00
## Problem In #5539, I moved the connect_to_compute latency to start counting before authentication - this is because authentication will perform some calls to the control plane in order to get credentials and to eagerly wake a compute server. It felt important to include these times in the latency metric as these are times we should definitely care about reducing. What is not interesting to record in this metric is the roundtrip time during authentication when we wait for the client to respond. ## Summary of changes Implement a mechanism to pause the latency timer, resuming on drop of the pause struct. We pause the timer right before we send the authentication message to the client, and we resume the timer right after we complete the authentication flow.
84 lines
3.1 KiB
Rust
84 lines
3.1 KiB
Rust
use super::{AuthSuccess, ComputeCredentials};
|
|
use crate::{
|
|
auth::{self, AuthFlow, ClientCredentials},
|
|
compute,
|
|
config::AuthenticationConfig,
|
|
console::{self, AuthInfo, ConsoleReqExtra},
|
|
proxy::LatencyTimer,
|
|
sasl, scram,
|
|
stream::PqStream,
|
|
};
|
|
use tokio::io::{AsyncRead, AsyncWrite};
|
|
use tracing::{info, warn};
|
|
|
|
pub(super) async fn authenticate(
|
|
api: &impl console::Api,
|
|
extra: &ConsoleReqExtra<'_>,
|
|
creds: &ClientCredentials<'_>,
|
|
client: &mut PqStream<impl AsyncRead + AsyncWrite + Unpin>,
|
|
config: &'static AuthenticationConfig,
|
|
latency_timer: &mut LatencyTimer,
|
|
) -> auth::Result<AuthSuccess<ComputeCredentials>> {
|
|
info!("fetching user's authentication info");
|
|
let info = api.get_auth_info(extra, creds).await?.unwrap_or_else(|| {
|
|
// If we don't have an authentication secret, we mock one to
|
|
// prevent malicious probing (possible due to missing protocol steps).
|
|
// This mocked secret will never lead to successful authentication.
|
|
info!("authentication info not found, mocking it");
|
|
AuthInfo::Scram(scram::ServerSecret::mock(creds.user, rand::random()))
|
|
});
|
|
|
|
let flow = AuthFlow::new(client);
|
|
let scram_keys = match info {
|
|
AuthInfo::Md5(_) => {
|
|
info!("auth endpoint chooses MD5");
|
|
return Err(auth::AuthError::bad_auth_method("MD5"));
|
|
}
|
|
AuthInfo::Scram(secret) => {
|
|
info!("auth endpoint chooses SCRAM");
|
|
let scram = auth::Scram(&secret);
|
|
|
|
let auth_outcome = tokio::time::timeout(
|
|
config.scram_protocol_timeout,
|
|
async {
|
|
// pause the timer while we communicate with the client
|
|
let _paused = latency_timer.pause();
|
|
|
|
flow.begin(scram).await.map_err(|error| {
|
|
warn!(?error, "error sending scram acknowledgement");
|
|
error
|
|
})?.authenticate().await.map_err(|error| {
|
|
warn!(?error, "error processing scram messages");
|
|
error
|
|
})
|
|
}
|
|
)
|
|
.await
|
|
.map_err(|error| {
|
|
warn!("error processing scram messages error = authentication timed out, execution time exeeded {} seconds", config.scram_protocol_timeout.as_secs());
|
|
auth::io::Error::new(auth::io::ErrorKind::TimedOut, error)
|
|
})??;
|
|
|
|
let client_key = match auth_outcome {
|
|
sasl::Outcome::Success(key) => key,
|
|
sasl::Outcome::Failure(reason) => {
|
|
info!("auth backend failed with an error: {reason}");
|
|
return Err(auth::AuthError::auth_failed(creds.user));
|
|
}
|
|
};
|
|
|
|
compute::ScramKeys {
|
|
client_key: client_key.as_bytes(),
|
|
server_key: secret.server_key.as_bytes(),
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(AuthSuccess {
|
|
reported_auth_ok: false,
|
|
value: ComputeCredentials::AuthKeys(tokio_postgres::config::AuthKeys::ScramSha256(
|
|
scram_keys,
|
|
)),
|
|
})
|
|
}
|