Gate non-loopback remote binds
This commit is contained in:
@@ -506,6 +506,7 @@ native_auth_rate_limit_per_minute = 30
|
|||||||
attach_ticket_ttl_secs = 86400
|
attach_ticket_ttl_secs = 86400
|
||||||
allow_tcp_forwarding = true
|
allow_tcp_forwarding = true
|
||||||
allow_remote_forwarding = false
|
allow_remote_forwarding = false
|
||||||
|
allow_remote_non_loopback_bind = false
|
||||||
allow_agent_forwarding = false
|
allow_agent_forwarding = false
|
||||||
accept_env = ["LANG", "LC_*", "TERM", "COLORTERM"]
|
accept_env = ["LANG", "LC_*", "TERM", "COLORTERM"]
|
||||||
```
|
```
|
||||||
|
|||||||
+24
-4
@@ -1443,10 +1443,7 @@ async fn start_remote_forwards(
|
|||||||
.bind_host
|
.bind_host
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "127.0.0.1".to_string());
|
.unwrap_or_else(|| "127.0.0.1".to_string());
|
||||||
anyhow::ensure!(
|
remote_bind_allowed(&config, &bind_host)?;
|
||||||
is_loopback_bind(&bind_host),
|
|
||||||
"remote forwarding may only bind loopback in this server build"
|
|
||||||
);
|
|
||||||
let listen_port = forward.listen_port;
|
let listen_port = forward.listen_port;
|
||||||
let target_host = forward
|
let target_host = forward
|
||||||
.target_host
|
.target_host
|
||||||
@@ -1547,6 +1544,16 @@ fn is_loopback_bind(host: &str) -> bool {
|
|||||||
matches!(host, "127.0.0.1" | "localhost" | "::1")
|
matches!(host, "127.0.0.1" | "localhost" | "::1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remote_bind_allowed(config: &ServerConfig, host: &str) -> Result<()> {
|
||||||
|
if !is_loopback_bind(host) {
|
||||||
|
anyhow::ensure!(
|
||||||
|
config.allow_remote_non_loopback_bind,
|
||||||
|
"remote forwarding non-loopback bind disabled"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn stream_open_allowed(
|
fn stream_open_allowed(
|
||||||
config: &ServerConfig,
|
config: &ServerConfig,
|
||||||
client: &ClientState,
|
client: &ClientState,
|
||||||
@@ -2151,4 +2158,17 @@ mod tests {
|
|||||||
assert_eq!(data.stream_id, 42);
|
assert_eq!(data.stream_id, 42);
|
||||||
assert_eq!(data.bytes, b"hello");
|
assert_eq!(data.bytes, b"hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remote_non_loopback_bind_requires_explicit_config() {
|
||||||
|
let config = ServerConfig::default();
|
||||||
|
remote_bind_allowed(&config, "127.0.0.1").unwrap();
|
||||||
|
assert!(remote_bind_allowed(&config, "0.0.0.0").is_err());
|
||||||
|
|
||||||
|
let config = ServerConfig {
|
||||||
|
allow_remote_non_loopback_bind: true,
|
||||||
|
..ServerConfig::default()
|
||||||
|
};
|
||||||
|
remote_bind_allowed(&config, "0.0.0.0").unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ pub struct ServerConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub allow_remote_forwarding: bool,
|
pub allow_remote_forwarding: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub allow_remote_non_loopback_bind: bool,
|
||||||
|
#[serde(default)]
|
||||||
pub allow_agent_forwarding: bool,
|
pub allow_agent_forwarding: bool,
|
||||||
#[serde(default = "default_accept_env")]
|
#[serde(default = "default_accept_env")]
|
||||||
pub accept_env: Vec<String>,
|
pub accept_env: Vec<String>,
|
||||||
@@ -61,6 +63,7 @@ impl Default for ServerConfig {
|
|||||||
native_auth_rate_limit_per_minute: default_native_auth_rate_limit_per_minute(),
|
native_auth_rate_limit_per_minute: default_native_auth_rate_limit_per_minute(),
|
||||||
allow_tcp_forwarding: true,
|
allow_tcp_forwarding: true,
|
||||||
allow_remote_forwarding: false,
|
allow_remote_forwarding: false,
|
||||||
|
allow_remote_non_loopback_bind: false,
|
||||||
allow_agent_forwarding: false,
|
allow_agent_forwarding: false,
|
||||||
accept_env: default_accept_env(),
|
accept_env: default_accept_env(),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user