feat: automate HAProxy L4 SNI routing for Caddy and VLESS-REALITY co-existence on port 443
Main / build (push) Waiting to run

This commit is contained in:
2026-07-08 01:53:59 +08:00
parent c573968dfc
commit 3f383af8d4
+128 -2
View File
@@ -171,6 +171,116 @@ update_caddyfile_fallback() {
fi
}
install_haproxy() {
[[ $(type -P haproxy) ]] && return
msg "正在安装 HAProxy 用于四层端口分流..."
if [[ $(type -P apt-get) ]]; then
apt-get update && apt-get install -y haproxy
elif [[ $(type -P dnf) ]]; then
dnf install -y haproxy
elif [[ $(type -P yum) ]]; then
yum install -y haproxy
else
err "未知的软件包管理器,请手动安装 haproxy!"
fi
}
restore_caddy_default() {
[[ ! -f $is_caddyfile ]] && return
if [[ -f ${is_caddyfile}.bak ]]; then
mv -f ${is_caddyfile}.bak $is_caddyfile
else
sed -i 's/servers :4443/servers :443/g' $is_caddyfile
sed -i 's/:4443[[:space:]]*{/:443 {/g' $is_caddyfile
sed -i 's/https_port 4443/https_port 443/g' $is_caddyfile
fi
systemctl restart caddy >/dev/null 2>&1
}
update_haproxy_config() {
[[ ! -d $is_conf_dir ]] && return
local haproxy_conf="/etc/haproxy/haproxy.cfg"
[[ ! -d /etc/haproxy ]] && mkdir -p /etc/haproxy
[[ -f $haproxy_conf ]] && cp -f $haproxy_conf ${haproxy_conf}.bak
cat >$haproxy_conf <<'EOF'
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
timeout connect 5s
timeout client 50s
timeout server 50s
frontend https-in
bind *:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
EOF
local acl_rules=""
local use_backend_rules=""
local backends=""
local has_reality=0
for config in $is_conf_dir/*.json; do
[[ ! -f $config ]] && continue
local json_clean=$(cat "$config" | sed s#//.*##)
local is_real=$(jq -r '.inbounds[0].tls.reality.enabled // empty' <<<"$json_clean")
[[ $is_real != "true" ]] && continue
local local_port=$(jq -r '.inbounds[0].listen_port // empty' <<<"$json_clean")
local sni=$(jq -r '.inbounds[0].tls.server_name // empty' <<<"$json_clean")
[[ ! $local_port || ! $sni ]] && continue
[[ $local_port == "443" ]] && continue
has_reality=1
local backend_name="singbox_backend_${local_port}"
acl_rules="${acl_rules}\n acl is_reality_${local_port} req.ssl_sni -i ${sni}"
use_backend_rules="${use_backend_rules}\n use_backend ${backend_name} if is_reality_${local_port}"
backends="${backends}\n\nbackend ${backend_name}\n mode tcp\n server srv1 127.0.0.1:${local_port}"
done
if [[ $has_reality -eq 1 ]]; then
echo -e "$acl_rules" >>$haproxy_conf
echo -e "$use_backend_rules" >>$haproxy_conf
echo -e " default_backend caddy_backend" >>$haproxy_conf
cat >>$haproxy_conf <<'EOF'
backend caddy_backend
mode tcp
server caddy 127.0.0.1:4443
EOF
echo -e "$backends" >>$haproxy_conf
systemctl daemon-reload
systemctl enable haproxy >/dev/null 2>&1
systemctl restart haproxy >/dev/null 2>&1
else
if systemctl is-active --quiet haproxy; then
systemctl stop haproxy >/dev/null 2>&1
systemctl disable haproxy >/dev/null 2>&1
fi
restore_caddy_default
fi
}
get_pbk() {
is_tmp_pbk=($($is_core_bin generate reality-keypair | sed 's/.*://'))
is_public_key=${is_tmp_pbk[1]}
@@ -369,7 +479,12 @@ create() {
# get json
[[ $is_change || ! $json_str ]] && get protocol $2
[[ $net == "reality" ]] && is_add_public_key=",outbounds:[{type:\"direct\"},{tag:\"public_key_$is_public_key\",type:\"direct\"}]"
is_new_json=$(jq "{inbounds:[{tag:\"$is_config_name\",type:\"$is_protocol\",$is_listen,listen_port:$port,$json_str}]$is_add_public_key}" <<<{})
local write_port=$port
if [[ $is_reality_fallback ]]; then
get_port
write_port=$tmp_port
fi
is_new_json=$(jq "{inbounds:[{tag:\"$is_config_name\",type:\"$is_protocol\",$is_listen,listen_port:$write_port,$json_str}]$is_add_public_key}" <<<{})
[[ $is_test_json ]] && return # tmp test
# only show json, dont save to file.
[[ $is_gen ]] && {
@@ -394,6 +509,8 @@ create() {
msg "正在重启 Caddy 以释放 443 端口..."
manage restart caddy &>/dev/null
sleep 1
install_haproxy
update_haproxy_config
fi
# caddy auto tls
[[ $is_caddy && $host && ! $is_no_auto_tls ]] && {
@@ -701,6 +818,7 @@ del() {
pause
fi
rm -rf $is_conf_dir/"$is_config_file"
update_haproxy_config
[[ ! $is_new_json ]] && manage restart &
[[ ! $is_no_del_msg ]] && _green "\n已删除: $is_config_file\n"
@@ -1174,11 +1292,19 @@ get() {
[[ ${!v} == 'null' ]] && unset $v
done
# Override port if it's mapped behind HAProxy
local file_port=$(echo "$is_config_file" | grep -oE '[0-9]+\.json' | grep -oE '[0-9]+')
if [[ $file_port && $port && $port != $file_port ]]; then
port=$file_port
fi
if [[ $is_private_key ]]; then
is_reality=1
net_type+=reality
is_public_key=${is_public_key/public_key_/}
[[ $is_reality_fallback_port == '4443' ]] && is_reality_fallback=1
if [[ $is_reality_fallback_port == '4443' || $port == '443' ]]; then
is_reality_fallback=1
fi
fi
is_socks_user=$username
is_socks_pass=$password