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
|
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() {
|
get_pbk() {
|
||||||
is_tmp_pbk=($($is_core_bin generate reality-keypair | sed 's/.*://'))
|
is_tmp_pbk=($($is_core_bin generate reality-keypair | sed 's/.*://'))
|
||||||
is_public_key=${is_tmp_pbk[1]}
|
is_public_key=${is_tmp_pbk[1]}
|
||||||
@@ -369,7 +479,12 @@ create() {
|
|||||||
# get json
|
# get json
|
||||||
[[ $is_change || ! $json_str ]] && get protocol $2
|
[[ $is_change || ! $json_str ]] && get protocol $2
|
||||||
[[ $net == "reality" ]] && is_add_public_key=",outbounds:[{type:\"direct\"},{tag:\"public_key_$is_public_key\",type:\"direct\"}]"
|
[[ $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
|
[[ $is_test_json ]] && return # tmp test
|
||||||
# only show json, dont save to file.
|
# only show json, dont save to file.
|
||||||
[[ $is_gen ]] && {
|
[[ $is_gen ]] && {
|
||||||
@@ -394,6 +509,8 @@ create() {
|
|||||||
msg "正在重启 Caddy 以释放 443 端口..."
|
msg "正在重启 Caddy 以释放 443 端口..."
|
||||||
manage restart caddy &>/dev/null
|
manage restart caddy &>/dev/null
|
||||||
sleep 1
|
sleep 1
|
||||||
|
install_haproxy
|
||||||
|
update_haproxy_config
|
||||||
fi
|
fi
|
||||||
# caddy auto tls
|
# caddy auto tls
|
||||||
[[ $is_caddy && $host && ! $is_no_auto_tls ]] && {
|
[[ $is_caddy && $host && ! $is_no_auto_tls ]] && {
|
||||||
@@ -701,6 +818,7 @@ del() {
|
|||||||
pause
|
pause
|
||||||
fi
|
fi
|
||||||
rm -rf $is_conf_dir/"$is_config_file"
|
rm -rf $is_conf_dir/"$is_config_file"
|
||||||
|
update_haproxy_config
|
||||||
[[ ! $is_new_json ]] && manage restart &
|
[[ ! $is_new_json ]] && manage restart &
|
||||||
[[ ! $is_no_del_msg ]] && _green "\n已删除: $is_config_file\n"
|
[[ ! $is_no_del_msg ]] && _green "\n已删除: $is_config_file\n"
|
||||||
|
|
||||||
@@ -1174,11 +1292,19 @@ get() {
|
|||||||
[[ ${!v} == 'null' ]] && unset $v
|
[[ ${!v} == 'null' ]] && unset $v
|
||||||
done
|
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
|
if [[ $is_private_key ]]; then
|
||||||
is_reality=1
|
is_reality=1
|
||||||
net_type+=reality
|
net_type+=reality
|
||||||
is_public_key=${is_public_key/public_key_/}
|
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
|
fi
|
||||||
is_socks_user=$username
|
is_socks_user=$username
|
||||||
is_socks_pass=$password
|
is_socks_pass=$password
|
||||||
|
|||||||
Reference in New Issue
Block a user