feat(core): implement ForwardingPolicy with rule-based allow/deny

Add ForwardingPolicy, ForwardingAction, ForwardingRule, and TargetPattern
types in config/forwarding.rs. Implement policy evaluation with first-match
wins semantics, principal and transport matching, CIDR and glob patterns.

Modify ServerHandler to check ForwardingPolicy before proxying in
channel_open_direct_tcpip. Reserved alknet-* destinations bypass policy.
Preserve existing behavior with default allow_all() policy.
This commit is contained in:
2026-06-07 14:47:44 +00:00
parent 92a307fd03
commit 9478e2911d
6 changed files with 503 additions and 41 deletions

View File

@@ -26,7 +26,7 @@ pub struct ProxyConfig {
pub mode: ProxyMode,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TransportKind {
Tcp,
Tls,
@@ -48,6 +48,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>,
@@ -97,6 +98,7 @@ impl ServerHandler {
};
Self {
dynamic,
identity_provider,
outbound_proxy,
remote_addr,
@@ -236,6 +238,34 @@ impl Handler for ServerHandler {
return Ok(true);
}
let identity = self
.authenticated_identity
.clone()
.unwrap_or_else(|| Identity {
id: String::new(),
scopes: vec![],
resources: std::collections::HashMap::new(),
});
let policy = self.dynamic.load();
let allowed = policy.forwarding.check(
host_to_connect,
port_to_connect as u16,
&identity,
self.transport,
);
if !allowed {
tracing::info!(
remote_addr = ?self.remote_addr,
target = %format!("{host_to_connect}:{port_to_connect}"),
identity = %identity.id,
transport = %self.transport,
"forwarding denied by policy"
);
return Ok(false);
}
let target_host = host_to_connect.to_string();
let target_port = port_to_connect;
let proxy_config = self.outbound_proxy.clone().unwrap_or(ProxyConfig {

View File

@@ -509,7 +509,7 @@ impl Server {
.first()
.expect("at least one listener required");
let transport_kind = listener.transport_kind.clone();
let transport_kind = listener.transport_kind;
let stealth = listener.stealth;
let listen_addr = listener.listen_addr.clone();
@@ -573,7 +573,7 @@ impl Server {
};
let remote_addr = info.remote_addr;
let handler_transport_kind = transport_kind.clone();
let handler_transport_kind = transport_kind;
let handler = ServerHandler::new(
Arc::clone(&server.dynamic),