mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
Respect explicit tunnel security off
This commit is contained in:
@@ -7,7 +7,7 @@ AETHER_TUNNEL_MANAGEMENT_TOKEN=ae_xxxxx
|
|||||||
# Node identification
|
# Node identification
|
||||||
AETHER_TUNNEL_NODE_NAME=jp-proxy-01
|
AETHER_TUNNEL_NODE_NAME=jp-proxy-01
|
||||||
|
|
||||||
# Secure non-TLS tunnel. http:// plus a key auto-enables non_tls_required.
|
# Secure non-TLS tunnel. Set non_tls_required with a key to enable secure tunnel.
|
||||||
AETHER_TUNNEL_SECURITY=off
|
AETHER_TUNNEL_SECURITY=off
|
||||||
# AETHER_TUNNEL_ENCRYPTION_KEY=base64-32-bytes
|
# AETHER_TUNNEL_ENCRYPTION_KEY=base64-32-bytes
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ sudo aether-tunnel uninstall
|
|||||||
| `--aether-url` | `AETHER_TUNNEL_AETHER_URL` | **必填** | Aether 服务器地址 |
|
| `--aether-url` | `AETHER_TUNNEL_AETHER_URL` | **必填** | Aether 服务器地址 |
|
||||||
| `--management-token` | `AETHER_TUNNEL_MANAGEMENT_TOKEN` | **必填** | 管理员 Token(`ae_xxx` 格式) |
|
| `--management-token` | `AETHER_TUNNEL_MANAGEMENT_TOKEN` | **必填** | 管理员 Token(`ae_xxx` 格式) |
|
||||||
| `--node-name` | `AETHER_TUNNEL_NODE_NAME` | **必填** | 节点名称标识 |
|
| `--node-name` | `AETHER_TUNNEL_NODE_NAME` | **必填** | 节点名称标识 |
|
||||||
| `--tunnel-security` | `AETHER_TUNNEL_SECURITY` | `off` | Aether ↔ tunnel 通道安全模式;支持 `off` / `non_tls_required`;`http://` 且提供 key 时会自动按 `non_tls_required` 生效 |
|
| `--tunnel-security` | `AETHER_TUNNEL_SECURITY` | `off` | Aether ↔ tunnel 通道安全模式;支持 `off` / `non_tls_required`;在 `[[servers]]` 中省略该字段且 `http://` 提供 key 时会自动按 `non_tls_required` 生效 |
|
||||||
| `--tunnel-encryption-key` | `AETHER_TUNNEL_ENCRYPTION_KEY` | 空 | secure tunnel 使用的长期 PSK(base64 32-byte),每个 `[[servers]]` 节点独立配置 |
|
| `--tunnel-encryption-key` | `AETHER_TUNNEL_ENCRYPTION_KEY` | 空 | secure tunnel 使用的长期 PSK(base64 32-byte),每个 `[[servers]]` 节点独立配置 |
|
||||||
| `--public-ip` | `AETHER_TUNNEL_PUBLIC_IP` | 自动检测 | 公网 IP |
|
| `--public-ip` | `AETHER_TUNNEL_PUBLIC_IP` | 自动检测 | 公网 IP |
|
||||||
| `--node-region` | `AETHER_TUNNEL_NODE_REGION` | 自动检测 | 地区标识 |
|
| `--node-region` | `AETHER_TUNNEL_NODE_REGION` | 自动检测 | 地区标识 |
|
||||||
@@ -231,7 +231,7 @@ tunnel_encryption_key = "base64-32-bytes"
|
|||||||
|
|
||||||
`tunnel_security = "non_tls_required"` 是非 TLS secure tunnel 的 MVP 配置面:它要求同时提供当前 `[[servers]]` 条目的 `tunnel_encryption_key`,后续握手使用 `node_name` / `X-Node-Id` 查找对应 PSK,不引入 `tunnel_encryption_key_id`。`wss://` 仍是推荐方案;`ws:// + secure tunnel` 只保护 Aether ↔ tunnel 之间的 token 和 payload,不等价于 HTTPS 伪装,也不覆盖 tunnel ↔ origin/provider 这段链路。
|
`tunnel_security = "non_tls_required"` 是非 TLS secure tunnel 的 MVP 配置面:它要求同时提供当前 `[[servers]]` 条目的 `tunnel_encryption_key`,后续握手使用 `node_name` / `X-Node-Id` 查找对应 PSK,不引入 `tunnel_encryption_key_id`。`wss://` 仍是推荐方案;`ws:// + secure tunnel` 只保护 Aether ↔ tunnel 之间的 token 和 payload,不等价于 HTTPS 伪装,也不覆盖 tunnel ↔ origin/provider 这段链路。
|
||||||
|
|
||||||
如果 `aether_url` 使用 `http://` 且当前 `[[servers]]` 条目提供了 `tunnel_encryption_key`,即使省略或保留 `tunnel_security = "off"`,运行时也会自动按 `non_tls_required` 生效。secure tunnel 会在 WebSocket tunnel 上加密所有二进制 tunnel frame;未配置 key 的旧节点仍按原明文协议工作。
|
如果 `aether_url` 使用 `http://` 且当前 `[[servers]]` 条目提供了 `tunnel_encryption_key`,省略 `tunnel_security` 时运行时会自动按 `non_tls_required` 生效;显式配置 `tunnel_security = "off"` 会关闭该自动推断。secure tunnel 会在 WebSocket tunnel 上加密所有二进制 tunnel frame;未配置 key 或显式关闭的旧节点仍按原明文协议工作。
|
||||||
|
|
||||||
## 发布新版本
|
## 发布新版本
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ function Add-ServerConfig([string]$AetherUrl, [string]$ManagementToken, [string]
|
|||||||
$QuotedUrl = ConvertTo-TomlQuotedString $AetherUrl
|
$QuotedUrl = ConvertTo-TomlQuotedString $AetherUrl
|
||||||
$QuotedToken = ConvertTo-TomlQuotedString $ManagementToken
|
$QuotedToken = ConvertTo-TomlQuotedString $ManagementToken
|
||||||
$QuotedName = ConvertTo-TomlQuotedString $NodeName
|
$QuotedName = ConvertTo-TomlQuotedString $NodeName
|
||||||
$QuotedTunnelSecurity = ConvertTo-TomlQuotedString $TunnelSecurity
|
|
||||||
$QuotedTunnelEncryptionKey = ConvertTo-TomlQuotedString $TunnelEncryptionKey
|
$QuotedTunnelEncryptionKey = ConvertTo-TomlQuotedString $TunnelEncryptionKey
|
||||||
|
|
||||||
if (Test-ServerExists $script:ConfigPath $QuotedUrl $QuotedName) {
|
if (Test-ServerExists $script:ConfigPath $QuotedUrl $QuotedName) {
|
||||||
@@ -134,9 +133,12 @@ function Add-ServerConfig([string]$AetherUrl, [string]$ManagementToken, [string]
|
|||||||
'[[servers]]',
|
'[[servers]]',
|
||||||
"aether_url = $QuotedUrl",
|
"aether_url = $QuotedUrl",
|
||||||
"management_token = $QuotedToken",
|
"management_token = $QuotedToken",
|
||||||
"node_name = $QuotedName",
|
"node_name = $QuotedName"
|
||||||
"tunnel_security = $QuotedTunnelSecurity"
|
|
||||||
) -join "`n"
|
) -join "`n"
|
||||||
|
if ($TunnelSecurity) {
|
||||||
|
$QuotedTunnelSecurity = ConvertTo-TomlQuotedString $TunnelSecurity
|
||||||
|
$Block += "`ntunnel_security = $QuotedTunnelSecurity"
|
||||||
|
}
|
||||||
if ($TunnelEncryptionKey) {
|
if ($TunnelEncryptionKey) {
|
||||||
$Block += "`ntunnel_encryption_key = $QuotedTunnelEncryptionKey"
|
$Block += "`ntunnel_encryption_key = $QuotedTunnelEncryptionKey"
|
||||||
}
|
}
|
||||||
@@ -149,9 +151,9 @@ function Main {
|
|||||||
$AetherUrl = Prompt-IfEmpty 'AETHER_TUNNEL_AETHER_URL' $env:AETHER_TUNNEL_AETHER_URL 'Aether URL'
|
$AetherUrl = Prompt-IfEmpty 'AETHER_TUNNEL_AETHER_URL' $env:AETHER_TUNNEL_AETHER_URL 'Aether URL'
|
||||||
$ManagementToken = Prompt-IfEmpty 'AETHER_TUNNEL_MANAGEMENT_TOKEN' $env:AETHER_TUNNEL_MANAGEMENT_TOKEN 'Management token (ae_xxx)'
|
$ManagementToken = Prompt-IfEmpty 'AETHER_TUNNEL_MANAGEMENT_TOKEN' $env:AETHER_TUNNEL_MANAGEMENT_TOKEN 'Management token (ae_xxx)'
|
||||||
$NodeName = Prompt-IfEmpty 'AETHER_TUNNEL_NODE_NAME' $env:AETHER_TUNNEL_NODE_NAME 'Node name'
|
$NodeName = Prompt-IfEmpty 'AETHER_TUNNEL_NODE_NAME' $env:AETHER_TUNNEL_NODE_NAME 'Node name'
|
||||||
$TunnelSecurity = if ($env:AETHER_TUNNEL_SECURITY) { $env:AETHER_TUNNEL_SECURITY } else { 'off' }
|
$TunnelSecurity = if ($env:AETHER_TUNNEL_SECURITY) { $env:AETHER_TUNNEL_SECURITY } else { '' }
|
||||||
$TunnelEncryptionKey = if ($env:AETHER_TUNNEL_ENCRYPTION_KEY) { $env:AETHER_TUNNEL_ENCRYPTION_KEY } else { '' }
|
$TunnelEncryptionKey = if ($env:AETHER_TUNNEL_ENCRYPTION_KEY) { $env:AETHER_TUNNEL_ENCRYPTION_KEY } else { '' }
|
||||||
if ($TunnelSecurity -notin @('off', 'non_tls_required')) {
|
if ($TunnelSecurity -and ($TunnelSecurity -notin @('off', 'non_tls_required'))) {
|
||||||
Fail 'AETHER_TUNNEL_SECURITY must be off or non_tls_required'
|
Fail 'AETHER_TUNNEL_SECURITY must be off or non_tls_required'
|
||||||
}
|
}
|
||||||
if (($TunnelSecurity -eq 'non_tls_required') -and -not $TunnelEncryptionKey) {
|
if (($TunnelSecurity -eq 'non_tls_required') -and -not $TunnelEncryptionKey) {
|
||||||
|
|||||||
@@ -193,8 +193,10 @@ append_server_config() {
|
|||||||
quoted_url=$(toml_quote "$aether_url")
|
quoted_url=$(toml_quote "$aether_url")
|
||||||
quoted_token=$(toml_quote "$management_token")
|
quoted_token=$(toml_quote "$management_token")
|
||||||
quoted_name=$(toml_quote "$node_name")
|
quoted_name=$(toml_quote "$node_name")
|
||||||
quoted_security=$(toml_quote "$tunnel_security")
|
|
||||||
quoted_encryption_key=$(toml_quote "$tunnel_encryption_key")
|
quoted_encryption_key=$(toml_quote "$tunnel_encryption_key")
|
||||||
|
if [ -n "$tunnel_security" ]; then
|
||||||
|
quoted_security=$(toml_quote "$tunnel_security")
|
||||||
|
fi
|
||||||
|
|
||||||
if has_legacy_single_server_keys; then
|
if has_legacy_single_server_keys; then
|
||||||
fail "现有配置仍使用旧的顶层 aether_url/management_token,请先运行 aether-tunnel setup 迁移为 [[servers]] 后重试:$CONFIG_PATH"
|
fail "现有配置仍使用旧的顶层 aether_url/management_token,请先运行 aether-tunnel setup 迁移为 [[servers]] 后重试:$CONFIG_PATH"
|
||||||
@@ -218,7 +220,9 @@ append_server_config() {
|
|||||||
printf 'aether_url = %s\n' "$quoted_url"
|
printf 'aether_url = %s\n' "$quoted_url"
|
||||||
printf 'management_token = %s\n' "$quoted_token"
|
printf 'management_token = %s\n' "$quoted_token"
|
||||||
printf 'node_name = %s\n' "$quoted_name"
|
printf 'node_name = %s\n' "$quoted_name"
|
||||||
printf 'tunnel_security = %s\n' "$quoted_security"
|
if [ -n "$tunnel_security" ]; then
|
||||||
|
printf 'tunnel_security = %s\n' "$quoted_security"
|
||||||
|
fi
|
||||||
if [ -n "$tunnel_encryption_key" ]; then
|
if [ -n "$tunnel_encryption_key" ]; then
|
||||||
printf 'tunnel_encryption_key = %s\n' "$quoted_encryption_key"
|
printf 'tunnel_encryption_key = %s\n' "$quoted_encryption_key"
|
||||||
fi
|
fi
|
||||||
@@ -235,10 +239,10 @@ main() {
|
|||||||
aether_url=$(prompt_if_empty AETHER_TUNNEL_AETHER_URL "${AETHER_TUNNEL_AETHER_URL:-}" "Aether URL: ")
|
aether_url=$(prompt_if_empty AETHER_TUNNEL_AETHER_URL "${AETHER_TUNNEL_AETHER_URL:-}" "Aether URL: ")
|
||||||
management_token=$(prompt_if_empty AETHER_TUNNEL_MANAGEMENT_TOKEN "${AETHER_TUNNEL_MANAGEMENT_TOKEN:-}" "Management token (ae_xxx): ")
|
management_token=$(prompt_if_empty AETHER_TUNNEL_MANAGEMENT_TOKEN "${AETHER_TUNNEL_MANAGEMENT_TOKEN:-}" "Management token (ae_xxx): ")
|
||||||
node_name=$(prompt_if_empty AETHER_TUNNEL_NODE_NAME "${AETHER_TUNNEL_NODE_NAME:-}" "Node name: ")
|
node_name=$(prompt_if_empty AETHER_TUNNEL_NODE_NAME "${AETHER_TUNNEL_NODE_NAME:-}" "Node name: ")
|
||||||
tunnel_security="${AETHER_TUNNEL_SECURITY:-off}"
|
tunnel_security="${AETHER_TUNNEL_SECURITY:-}"
|
||||||
tunnel_encryption_key="${AETHER_TUNNEL_ENCRYPTION_KEY:-}"
|
tunnel_encryption_key="${AETHER_TUNNEL_ENCRYPTION_KEY:-}"
|
||||||
case "$tunnel_security" in
|
case "$tunnel_security" in
|
||||||
off|non_tls_required) ;;
|
""|off|non_tls_required) ;;
|
||||||
*) fail "AETHER_TUNNEL_SECURITY 必须是 off 或 non_tls_required" ;;
|
*) fail "AETHER_TUNNEL_SECURITY 必须是 off 或 non_tls_required" ;;
|
||||||
esac
|
esac
|
||||||
if [ "$tunnel_security" = "non_tls_required" ] && [ -z "$tunnel_encryption_key" ]; then
|
if [ "$tunnel_security" = "non_tls_required" ] && [ -z "$tunnel_encryption_key" ]; then
|
||||||
|
|||||||
@@ -289,8 +289,10 @@ pub fn effective_tunnel_security(
|
|||||||
configured: Option<TunnelSecurity>,
|
configured: Option<TunnelSecurity>,
|
||||||
tunnel_encryption_key: Option<&str>,
|
tunnel_encryption_key: Option<&str>,
|
||||||
) -> TunnelSecurity {
|
) -> TunnelSecurity {
|
||||||
if configured == Some(TunnelSecurity::NonTlsRequired) {
|
match configured {
|
||||||
return TunnelSecurity::NonTlsRequired;
|
Some(TunnelSecurity::NonTlsRequired) => return TunnelSecurity::NonTlsRequired,
|
||||||
|
Some(TunnelSecurity::Off) => return TunnelSecurity::Off,
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
if aether_url.trim_start().starts_with("http://")
|
if aether_url.trim_start().starts_with("http://")
|
||||||
&& tunnel_encryption_key
|
&& tunnel_encryption_key
|
||||||
@@ -738,12 +740,7 @@ impl Config {
|
|||||||
if self.node_name.trim().is_empty() {
|
if self.node_name.trim().is_empty() {
|
||||||
anyhow::bail!("node_name must not be empty");
|
anyhow::bail!("node_name must not be empty");
|
||||||
}
|
}
|
||||||
let effective_security = effective_tunnel_security(
|
if self.tunnel_security == TunnelSecurity::NonTlsRequired {
|
||||||
&self.aether_url,
|
|
||||||
Some(self.tunnel_security),
|
|
||||||
self.tunnel_encryption_key.as_deref(),
|
|
||||||
);
|
|
||||||
if effective_security == TunnelSecurity::NonTlsRequired {
|
|
||||||
let Some(key) = normalized_proxy_url(&self.tunnel_encryption_key) else {
|
let Some(key) = normalized_proxy_url(&self.tunnel_encryption_key) else {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"tunnel_encryption_key must be set when tunnel_security=non_tls_required"
|
"tunnel_encryption_key must be set when tunnel_security=non_tls_required"
|
||||||
@@ -1796,14 +1793,22 @@ node_name = "tunnel-test"
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
effective_tunnel_security(
|
effective_tunnel_security(
|
||||||
&config.aether_url,
|
&config.aether_url,
|
||||||
Some(config.tunnel_security),
|
None,
|
||||||
config.tunnel_encryption_key.as_deref(),
|
config.tunnel_encryption_key.as_deref(),
|
||||||
),
|
),
|
||||||
TunnelSecurity::NonTlsRequired
|
TunnelSecurity::NonTlsRequired
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
effective_tunnel_security(
|
||||||
|
&config.aether_url,
|
||||||
|
Some(TunnelSecurity::Off),
|
||||||
|
config.tunnel_encryption_key.as_deref(),
|
||||||
|
),
|
||||||
|
TunnelSecurity::Off
|
||||||
|
);
|
||||||
config
|
config
|
||||||
.validate()
|
.validate()
|
||||||
.expect("http URL with PSK should infer secure tunnel mode");
|
.expect("http URL with PSK should validate when tunnel_security is off");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1816,6 +1821,8 @@ node_name = "tunnel-test"
|
|||||||
"ae_test",
|
"ae_test",
|
||||||
"--node-name",
|
"--node-name",
|
||||||
"tunnel-test",
|
"tunnel-test",
|
||||||
|
"--tunnel-security",
|
||||||
|
"non_tls_required",
|
||||||
"--tunnel-encryption-key",
|
"--tunnel-encryption-key",
|
||||||
"not-a-valid-32-byte-key",
|
"not-a-valid-32-byte-key",
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -96,10 +96,10 @@ impl ServerTab {
|
|||||||
Field {
|
Field {
|
||||||
label: "Tunnel Security",
|
label: "Tunnel Security",
|
||||||
key: "tunnel_security",
|
key: "tunnel_security",
|
||||||
value: "off".into(),
|
value: String::new(),
|
||||||
kind: FieldKind::Text,
|
kind: FieldKind::Text,
|
||||||
required: false,
|
required: false,
|
||||||
help: "off or non_tls_required; http:// plus a key auto-enables secure tunnel",
|
help: "off or non_tls_required; omit to auto-enable for http:// plus a key",
|
||||||
},
|
},
|
||||||
Field {
|
Field {
|
||||||
label: "Tunnel Encryption Key",
|
label: "Tunnel Encryption Key",
|
||||||
@@ -1180,6 +1180,15 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_config_omits_blank_tunnel_security_for_auto_inference() {
|
||||||
|
let app = sample_app();
|
||||||
|
|
||||||
|
let cfg = app.to_config().expect("config should serialize");
|
||||||
|
assert_eq!(cfg.servers.len(), 1);
|
||||||
|
assert_eq!(cfg.servers[0].tunnel_security, None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_config_rejects_non_tls_security_without_key() {
|
fn to_config_rejects_non_tls_security_without_key() {
|
||||||
let mut app = sample_app();
|
let mut app = sample_app();
|
||||||
|
|||||||
Reference in New Issue
Block a user