feat(nginx,sni): enable SNI-based 443 multiplexing (turns:443) and wiring

- docker/scripts/generate-config.sh
	- Add --enable-sni443/--no-sni443 flags; default enable in full+domain.
	- Generate Nginx stream{} with ssl_preread SNI routing: turn.<domain> -> coturn:5349; others -> web:8443.
	- When SNI is enabled, serve HTTPS on 8443 (http layer); otherwise keep 443.
- deploy.sh:
	- Add --with-sni443 and propagate to config generation and LE provisioning.
	- No compose changes required; 8443 remains internal.
- Notes:
	- Backward compatible. SNI is auto-enabled for full+domain, can be toggled with flags.
	- Leverages existing LE automation and TURN cert reuse.
This commit is contained in:
david_bai
2025-10-07 21:43:27 +08:00
parent 85baa97804
commit 7809373f88
2 changed files with 81 additions and 6 deletions
+11 -2
View File
@@ -45,6 +45,7 @@ PrivyDrop Docker 一键部署脚本
full: 完整HTTPS部署 + TURN服务器
--with-nginx 启用Nginx反向代理
--with-turn 启用TURN服务器
--with-sni443 启用 443 SNI 分流 (full 模式默认启用)
--le-email EMAIL 使用 Let's Encrypt 时的证书邮箱(full 模式推荐传入)
--clean 清理现有容器和数据
--help 显示帮助信息
@@ -69,6 +70,7 @@ parse_arguments() {
WITH_TURN=false
CLEAN_MODE=false
LE_EMAIL=""
WITH_SNI443=false
while [[ $# -gt 0 ]]; do
case $1 in
@@ -88,6 +90,10 @@ parse_arguments() {
WITH_TURN=true
shift
;;
--with-sni443)
WITH_SNI443=true
shift
;;
--le-email)
LE_EMAIL="$2"
shift 2
@@ -268,8 +274,10 @@ provision_letsencrypt_cert() {
sudo cp "$lineage_dir/privkey.pem" docker/ssl/server-key.pem
sudo chmod 600 docker/ssl/server-key.pem || true
# 启用 443 配置(证书已就绪):不清理,仅追加
bash "$DOCKER_SCRIPTS_DIR/generate-config.sh" --mode full --domain "$DOMAIN_NAME" --no-clean --ssl-mode letsencrypt || true
# 启用 443 配置(证书已就绪):不清理,仅追加;传递 SNI 开关(默认 full 启用)
local gen_args=(--mode full --domain "$DOMAIN_NAME" --no-clean --ssl-mode letsencrypt)
[[ "$WITH_SNI443" == "true" ]] && gen_args+=(--enable-sni443)
bash "$DOCKER_SCRIPTS_DIR/generate-config.sh" "${gen_args[@]}" || true
# 热重载 nginx 以启用 443
docker compose exec -T nginx nginx -s reload || docker compose restart nginx
@@ -327,6 +335,7 @@ setup_environment() {
local detect_args=""
[[ -n "$DOMAIN_NAME" ]] && detect_args="--domain $DOMAIN_NAME"
[[ -n "$DEPLOYMENT_MODE" ]] && detect_args="$detect_args --mode $DEPLOYMENT_MODE"
[[ "$WITH_SNI443" == "true" ]] && detect_args="$detect_args --enable-sni443"
if ! bash "$DOCKER_SCRIPTS_DIR/detect-environment.sh" $detect_args; then
log_error "环境检测失败"
+70 -4
View File
@@ -35,6 +35,7 @@ TURN_MIN_PORT_DEFAULT=49152
TURN_MAX_PORT_DEFAULT=49252
TURN_MIN_PORT="$TURN_MIN_PORT_DEFAULT"
TURN_MAX_PORT="$TURN_MAX_PORT_DEFAULT"
ENABLE_SNI443="${ENABLE_SNI443:-}"
parse_turn_port_range() {
local range="$1"
@@ -92,6 +93,8 @@ PrivyDrop 配置生成脚本(Docker 版)
--turn-port-range R 指定TURN端口段(UDP),格式 MIN-MAX;默认 49152-49252
--domain DOMAIN 指定域名(用于 Nginx/证书/TURN realm,如 turn.DOMAIN
--local-ip IP 指定本机局域网IP(不传则自动探测)
--enable-sni443 启用 443 SNI 分流(turn.DOMAIN → coturn:5349,其余 → web:8443
--no-sni443 关闭 443 SNI 分流(HTTPS 直接监听 443
--help 显示本帮助
--no-clean 跳过清理历史生成物(推荐用于二次生成避免清理 SSL)
--reset-ssl 强制清理 docker/ssl/*(默认不清理)
@@ -543,12 +546,16 @@ EOF
# 生成HTTPS Nginx配置
generate_https_nginx_config() {
log_info "生成HTTPS Nginx配置..."
local https_port="443"
if [[ "$ENABLE_SNI443" == "true" ]]; then
https_port="8443"
fi
cat >> docker/nginx/conf.d/default.conf << EOF
# HTTPS服务器配置
server {
listen 443 ssl http2;
listen ${https_port} ssl http2;
server_name ${DOMAIN_NAME:-${LOCAL_IP}};
# SSL配置
@@ -629,14 +636,56 @@ EOF
log_success "HTTPS配置已添加"
}
# 生成 Nginx stream SNI 分流(443
generate_stream_sni443() {
if [[ "$ENABLE_SNI443" != "true" ]]; then
return 0
fi
if [[ -z "$DOMAIN_NAME" ]]; then
log_warning "SNI 443 需要域名,未指定域名,跳过 stream 配置"
return 0
fi
# 避免重复追加
if grep -q "## SNI 443 stream" docker/nginx/nginx.conf 2>/dev/null; then
log_info "已存在 SNI 443 stream 配置,跳过追加"
return 0
fi
log_info "追加 SNI 443 stream 配置到 nginx.conf"
cat >> docker/nginx/nginx.conf << EOF
## SNI 443 stream
stream {
map \$ssl_preread_server_name \$sni_upstream {
~^turn\.(${DOMAIN_NAME})$ coturn;
default web;
}
upstream coturn { server coturn:5349; }
upstream web { server 127.0.0.1:8443; }
server {
listen 443 reuseport;
proxy_pass \$sni_upstream;
ssl_preread on;
}
}
EOF
}
# 当证书存在时再启用 443 配置(适用于 letsencrypt/provided
enable_https_if_cert_present() {
if [[ -f "docker/ssl/server-cert.pem" && -f "docker/ssl/server-key.pem" ]]; then
# 若 default.conf 中尚未存在 443 server,则追加
if ! grep -q "listen 443 ssl" docker/nginx/conf.d/default.conf 2>/dev/null; then
# SNI 开启时,先追加 stream 分流,再生成 8443/443 的 HTTPS
if [[ "$ENABLE_SNI443" == "true" && -n "$DOMAIN_NAME" ]]; then
generate_stream_sni443
fi
# 若 default.conf 中尚未存在 HTTPS server,则追加(端口根据 SNI 开关决定)
local expected="listen 443 ssl"
[[ "$ENABLE_SNI443" == "true" ]] && expected="listen 8443 ssl"
if ! grep -q "$expected" docker/nginx/conf.d/default.conf 2>/dev/null; then
generate_https_nginx_config
else
log_info "检测到已存在 443 配置,跳过追加"
log_info "检测到已存在 HTTPS(${ENABLE_SNI443:+SNI=on}) 配置,跳过追加"
fi
else
log_warning "未检测到证书 (docker/ssl/server-*.pem),暂不启用 443 配置"
@@ -814,6 +863,14 @@ main() {
parse_turn_port_range "$2"
shift 2
;;
--enable-sni443)
ENABLE_SNI443=true
shift
;;
--no-sni443)
ENABLE_SNI443=false
shift
;;
--no-clean)
NO_CLEAN=true
shift
@@ -870,6 +927,15 @@ main() {
fi
fi
# SNI 443 默认启用:full 模式且有域名,除非显式 --no-sni443
if [[ -z "$ENABLE_SNI443" ]]; then
if [[ "$DEPLOYMENT_MODE" == "full" && -n "$DOMAIN_NAME" ]]; then
ENABLE_SNI443=true
else
ENABLE_SNI443=false
fi
fi
generate_ssl_certificates
echo ""