Separate SSH key probe from authentication
This commit is contained in:
parent
b8bd0ec545
commit
a3ecf0c88b
@ -14,6 +14,12 @@ pub struct SshAuthService {
|
||||
db: AppDatabase,
|
||||
}
|
||||
|
||||
pub struct SshKeyUser {
|
||||
pub user: user::Model,
|
||||
pub key_id: i64,
|
||||
pub key_title: String,
|
||||
}
|
||||
|
||||
impl SshAuthService {
|
||||
pub fn new(db: AppDatabase) -> Self {
|
||||
Self { db }
|
||||
@ -97,7 +103,7 @@ impl SshAuthService {
|
||||
pub async fn find_user_by_public_key(
|
||||
&self,
|
||||
public_key_str: &str,
|
||||
) -> Result<Option<user::Model>, DbErr> {
|
||||
) -> Result<Option<SshKeyUser>, DbErr> {
|
||||
let fingerprint = match self.generate_fingerprint_from_public_key(public_key_str) {
|
||||
Ok(fp) => fp,
|
||||
Err(e) => {
|
||||
@ -144,16 +150,20 @@ impl SshAuthService {
|
||||
.one(self.db.reader())
|
||||
.await?;
|
||||
|
||||
if let Some(ref user) = user_model {
|
||||
if let Some(user) = user_model {
|
||||
tracing::info!(
|
||||
"user authenticated via SSH key user={} key={}",
|
||||
"SSH key matched user={} key={}",
|
||||
user.username,
|
||||
ssh_key.title
|
||||
);
|
||||
self.update_key_last_used_async(ssh_key.id);
|
||||
return Ok(Some(SshKeyUser {
|
||||
user,
|
||||
key_id: ssh_key.id,
|
||||
key_title: ssh_key.title,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(user_model)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn is_key_expired(&self, ssh_key: &user_ssh_key::Model) -> bool {
|
||||
@ -165,7 +175,7 @@ impl SshAuthService {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_key_last_used_async(&self, key_id: i64) {
|
||||
pub fn update_key_last_used_async(&self, key_id: i64) {
|
||||
let db_clone = self.db.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = Self::update_key_last_used_sync(db_clone, key_id).await {
|
||||
|
||||
@ -163,7 +163,52 @@ impl russh::server::Handler for SSHandle {
|
||||
user: &str,
|
||||
public_key: &PublicKey,
|
||||
) -> Result<Auth, Self::Error> {
|
||||
self.auth_publickey(user, public_key).await
|
||||
let client_info = self
|
||||
.client_addr
|
||||
.map(|addr| format!("{}", addr))
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
if user != "git" {
|
||||
tracing::warn!(
|
||||
"auth_publickey_offer_rejected_invalid_username user={} client={}",
|
||||
user,
|
||||
client_info
|
||||
);
|
||||
return Err(russh::Error::NotAuthenticated);
|
||||
}
|
||||
|
||||
let public_key_str = public_key.to_string();
|
||||
if public_key_str.len() < 32 {
|
||||
tracing::warn!(
|
||||
"auth_publickey_offer_rejected_invalid_key_length key_length={}",
|
||||
public_key_str.len()
|
||||
);
|
||||
return Err(russh::Error::NotAuthenticated);
|
||||
}
|
||||
|
||||
tracing::info!("auth_publickey_offer client={}", client_info);
|
||||
match self.auth.find_user_by_public_key(&public_key_str).await {
|
||||
Ok(Some(key_user)) => {
|
||||
tracing::info!(
|
||||
"auth_publickey_offer_accepted user={} key={} client={}",
|
||||
key_user.user.username,
|
||||
key_user.key_title,
|
||||
client_info
|
||||
);
|
||||
Ok(Auth::Accept)
|
||||
}
|
||||
Ok(None) => {
|
||||
tracing::warn!(
|
||||
"auth_publickey_offer_rejected_key_not_found client={}",
|
||||
client_info
|
||||
);
|
||||
Err(russh::Error::NotAuthenticated)
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("auth_publickey_offer_error error={}", e);
|
||||
Err(russh::Error::NotAuthenticated)
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn auth_publickey(
|
||||
&mut self,
|
||||
@ -193,8 +238,8 @@ impl russh::server::Handler for SSHandle {
|
||||
}
|
||||
|
||||
tracing::info!("auth_publickey_attempt client={}", client_info);
|
||||
let user_model = match self.auth.find_user_by_public_key(&public_key_str).await {
|
||||
Ok(Some(model)) => model,
|
||||
let key_user = match self.auth.find_user_by_public_key(&public_key_str).await {
|
||||
Ok(Some(key_user)) => key_user,
|
||||
Ok(None) => {
|
||||
tracing::warn!("auth_rejected_key_not_found client={}", client_info);
|
||||
return Err(russh::Error::NotAuthenticated);
|
||||
@ -207,10 +252,11 @@ impl russh::server::Handler for SSHandle {
|
||||
|
||||
tracing::info!(
|
||||
"auth_publickey_success user={} client={}",
|
||||
user_model.username,
|
||||
key_user.user.username,
|
||||
client_info
|
||||
);
|
||||
self.operator = Some(user_model);
|
||||
self.auth.update_key_last_used_async(key_user.key_id);
|
||||
self.operator = Some(key_user.user);
|
||||
Ok(Auth::Accept)
|
||||
}
|
||||
async fn auth_openssh_certificate(
|
||||
@ -241,8 +287,8 @@ impl russh::server::Handler for SSHandle {
|
||||
}
|
||||
|
||||
tracing::info!("auth_publickey_attempt client={}", client_info);
|
||||
let user_model = match self.auth.find_user_by_public_key(&public_key_str).await {
|
||||
Ok(Some(model)) => model,
|
||||
let key_user = match self.auth.find_user_by_public_key(&public_key_str).await {
|
||||
Ok(Some(key_user)) => key_user,
|
||||
Ok(None) => {
|
||||
tracing::warn!("auth_rejected_key_not_found client={}", client_info);
|
||||
return Err(russh::Error::NotAuthenticated);
|
||||
@ -255,10 +301,11 @@ impl russh::server::Handler for SSHandle {
|
||||
|
||||
tracing::info!(
|
||||
"auth_publickey_success user={} client={}",
|
||||
user_model.username,
|
||||
key_user.user.username,
|
||||
client_info
|
||||
);
|
||||
self.operator = Some(user_model);
|
||||
self.auth.update_key_last_used_async(key_user.key_id);
|
||||
self.operator = Some(key_user.user);
|
||||
Ok(Auth::Accept)
|
||||
}
|
||||
async fn authentication_banner(&mut self) -> Result<Option<String>, Self::Error> {
|
||||
|
||||
@ -82,7 +82,9 @@ impl russh::server::Server for SSHServer {
|
||||
);
|
||||
|
||||
if io_err.kind() == io::ErrorKind::UnexpectedEof {
|
||||
tracing::warn!("Client disconnected during handshake or before authentication");
|
||||
tracing::warn!(
|
||||
"SSH peer closed the connection before a clean disconnect was received"
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user