feat(core): implement Identity, IdentityProvider trait, and ConfigIdentityProvider

Add Identity struct with id/scopes/resources fields and IdentityProvider
trait with resolve_from_fingerprint/resolve_from_token methods. Implement
ConfigIdentityProvider reading from ArcSwap<DynamicConfig.auth> for
fingerprint-based key lookups. Delegate ServerHandler::auth_publickey()
through IdentityProvider instead of direct AuthPolicy access. Store
authenticated Identity in the handler for use by ForwardingPolicy.
This commit is contained in:
2026-06-07 14:21:14 +00:00
parent ee1b3f3819
commit c64dbd19d5
5 changed files with 245 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ use russh::server::{Auth, Handler, Msg, Session};
use russh::Channel;
use russh::ChannelId;
use crate::auth::identity::{ConfigIdentityProvider, Identity, IdentityProvider};
use crate::config::DynamicConfig;
use crate::server::control_channel::{ControlChannelHandler, ControlChannelRouter, ALKNET_PREFIX};
use crate::server::rate_limit::{AuthAttemptLimiter, ConnectionRateLimiter};
@@ -43,7 +44,7 @@ impl std::fmt::Display for TransportKind {
}
pub struct ServerHandler {
dynamic: Arc<ArcSwap<DynamicConfig>>,
identity_provider: Box<dyn IdentityProvider>,
#[allow(dead_code)]
outbound_proxy: Option<ProxyConfig>,
remote_addr: Option<SocketAddr>,
@@ -54,6 +55,7 @@ pub struct ServerHandler {
connection_allowed: bool,
auth_limiter: AuthAttemptLimiter,
connected_at: Instant,
authenticated_identity: Option<Identity>,
}
impl ServerHandler {
@@ -65,6 +67,9 @@ impl ServerHandler {
connection_limiter: Arc<ConnectionRateLimiter>,
max_auth_attempts: usize,
) -> Self {
let identity_provider: Box<dyn IdentityProvider> =
Box::new(ConfigIdentityProvider::new(Arc::clone(&dynamic)));
let allowed = if let Some(addr) = remote_addr {
let ip = addr.ip();
if connection_limiter.check(ip) {
@@ -88,7 +93,7 @@ impl ServerHandler {
};
Self {
dynamic,
identity_provider,
outbound_proxy,
remote_addr,
control_channel_router: ControlChannelRouter::without_handler(),
@@ -97,9 +102,19 @@ impl ServerHandler {
connection_allowed: allowed,
auth_limiter: AuthAttemptLimiter::new(max_auth_attempts),
connected_at: Instant::now(),
authenticated_identity: None,
}
}
pub fn with_identity_provider(mut self, provider: Box<dyn IdentityProvider>) -> Self {
self.identity_provider = provider;
self
}
pub fn authenticated_identity(&self) -> Option<&Identity> {
self.authenticated_identity.as_ref()
}
pub fn is_connection_allowed(&self) -> bool {
self.connection_allowed
}
@@ -167,12 +182,13 @@ impl Handler for ServerHandler {
.remote_addr
.map_or("unknown".to_string(), |a| a.to_string());
let russh_pub = russh::keys::PublicKey::new(public_key.key_data().clone(), user);
let auth_config = self.dynamic.load();
let result = auth_config.auth.authenticate_publickey(&russh_pub);
let identity = self
.identity_provider
.resolve_from_fingerprint(&fingerprint);
match result {
Ok(()) => {
match identity {
Some(id) => {
self.authenticated_identity = Some(id);
tracing::info!(
remote_addr = %remote_addr_display,
user = user,
@@ -182,7 +198,7 @@ impl Handler for ServerHandler {
);
Ok(Auth::Accept)
}
Err(_) => {
None => {
self.auth_limiter.on_failure();
tracing::info!(
remote_addr = %remote_addr_display,