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:
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user