feat: automate HAProxy L4 SNI routing for Caddy and VLESS-REALITY co-existence on port 443
Main / build (push) Waiting to run
Main / build (push) Waiting to run
This commit is contained in:
+128
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user