feat: 支持证书托管和节点自动安装

This commit is contained in:
2026-09-29 13:06:56 +08:00
parent ab7d1c937a
commit 586c2399d1
20 changed files with 1600 additions and 20 deletions
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
[ -n "${BASH_VERSION:-}" ] || exec bash "$0" "$@"
set -Eeuo pipefail
umask 077
PANEL=__PANEL__
TOKEN=__TOKEN__
INSTALL_ID=__INSTALL_ID__
SLUG=__SLUG__
DOMAIN=""
HY2_PORT=""
STATS_PORT=""
HY2_LISTEN=""
STATS_LISTEN=""
while [ "$#" -gt 0 ]; do
[ "$#" -ge 2 ] || { echo "参数缺少值:$1"; exit 1; }
case "$1" in
--domain) DOMAIN=$2;;
--hy2-port) HY2_PORT=$2;;
--stats-port) STATS_PORT=$2;;
--hy2-listen-port) HY2_LISTEN=$2;;
--stats-listen-port) STATS_LISTEN=$2;;
*) echo "未知参数:$1"; exit 1;;
esac
shift 2
done
[ "$(id -u)" -eq 0 ] || { echo '请使用 root 执行。'; exit 1; }
command -v systemctl >/dev/null || { echo '需要使用 systemd 的 Linux 服务器。'; exit 1; }
[ -d /run/systemd/system ] || { echo 'systemd 未运行。'; exit 1; }
case "$(uname -m)" in
x86_64) ARCH=amd64;; aarch64|arm64) ARCH=arm64;;
armv7l) ARCH=arm;; *) echo '暂不支持此 CPU 架构。'; exit 1;;
esac
if [ -e /etc/hysteria/config.yaml ] || [ -e /etc/systemd/system/hy2-panel-node.service ]; then
[ "$(cat /etc/hysteria/.panel-install-id 2>/dev/null || true)" = "$INSTALL_ID" ] || {
echo '检测到已有 HY2 配置;为避免覆盖,安装已停止。请在新服务器运行。'; exit 1;
}
fi
for unit in hysteria-server.service hysteria-server@config.service; do
if systemctl is-active --quiet "$unit"; then echo '已有 Hysteria 服务正在运行,安装已停止。'; exit 1; fi
done
read_port() {
local value
while true; do
read -r -p "$1" value </dev/tty
if [[ "$value" =~ ^[0-9]{1,5}$ ]] && (( 10#$value >= 1 && 10#$value <= 65535 )); then
printf '%s' "$((10#$value))"; return
fi
echo '端口必须为 1–65535。' >&2
done
}
echo "节点:$SLUG"
if [ -z "$DOMAIN" ]; then read -r -p '节点域名(输入 jp1 或 jp1.p.xn--876a.net):' DOMAIN </dev/tty; fi
DOMAIN=$(printf '%s' "$DOMAIN" | tr '[:upper:]' '[:lower:]')
if [[ "$DOMAIN" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then DOMAIN="$DOMAIN.p.xn--876a.net"; fi
[[ "$DOMAIN" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.p\.xn--876a\.net$ ]] || { echo '请填写 .p.xn--876a.net 下的单级子域名,例如 jp1.p.xn--876a.net。'; exit 1; }
[ -n "$HY2_PORT" ] || HY2_PORT=$(read_port 'HY2 公网 UDP 端口:')
[ -n "$STATS_PORT" ] || STATS_PORT=$(read_port '流量统计公网 TCP 端口:')
if [ -z "$HY2_LISTEN" ] && [ -z "$STATS_LISTEN" ] && [ -t 0 ]; then
read -r -p 'NAT 内外端口是否不同?[y/N] ' MAPPING </dev/tty
if [[ "$MAPPING" =~ ^[yY]$ ]]; then
HY2_LISTEN=$(read_port 'HY2 本机 UDP 监听端口:')
STATS_LISTEN=$(read_port '流量统计本机 TCP 监听端口:')
fi
fi
HY2_LISTEN=${HY2_LISTEN:-$HY2_PORT}
STATS_LISTEN=${STATS_LISTEN:-$STATS_PORT}
for port in "$HY2_PORT" "$STATS_PORT" "$HY2_LISTEN" "$STATS_LISTEN"; do
if ! [[ "$port" =~ ^[1-9][0-9]{0,4}$ ]] || (( port > 65535 )); then echo '端口必须为 1–65535(无前导零)。'; exit 1; fi
done
if ! command -v curl >/dev/null || ! command -v jq >/dev/null || ! command -v openssl >/dev/null; then
if command -v apt-get >/dev/null; then
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y curl jq openssl ca-certificates
elif command -v dnf >/dev/null; then dnf install -y curl jq openssl ca-certificates
elif command -v yum >/dev/null; then yum install -y curl jq openssl ca-certificates
else echo '请先安装 curl、jq、openssl 和 ca-certificates。'; exit 1; fi
fi
WORK=$(mktemp -d)
STARTED=0
cleanup() {
local status=$?
rm -rf "$WORK"
if [ "$status" -ne 0 ]; then
if [ "$STARTED" -eq 1 ]; then systemctl stop hy2-panel-node.service || true; fi
echo '安装未完成。修复网络/端口问题后,可在凭据有效期内使用相同脚本和端口重试。' >&2
fi
}
trap cleanup EXIT
printf 'Authorization: Bearer %s\n' "$TOKEN" > "$WORK/headers"
# 使用 Hysteria 官方自动化下载入口,仅允许 HTTPS。
curl --proto '=https' --proto-redir '=https' -fSL --retry 3 --connect-timeout 20 --max-time 300 \
"https://download.hysteria.network/app/latest/hysteria-linux-$ARCH" -o "$WORK/hysteria"
chmod 700 "$WORK/hysteria"
"$WORK/hysteria" version
jq -n --arg domain "$DOMAIN" --argjson h "$HY2_PORT" --argjson s "$STATS_PORT" --argjson hl "$HY2_LISTEN" --argjson sl "$STATS_LISTEN" \
'{action:"prepare",ports:{domain:$domain,hy2Port:$h,statsPort:$s,hy2ListenPort:$hl,statsListenPort:$sl}}' > "$WORK/request"
api() {
local status
status=$(curl -sS --connect-timeout 20 --max-time 60 -o "$WORK/response" -w '%{http_code}' \
-H @"$WORK/headers" -H 'Content-Type: application/json' --data-binary @"$WORK/request" "$PANEL/api/install")
if [ "$status" != 200 ]; then
jq -r '.error // "平台请求失败"' "$WORK/response" >&2 || true
return 1
fi
}
api
jq -er '.config' "$WORK/response" > "$WORK/config.yaml"
jq -er '.certificate' "$WORK/response" > "$WORK/server.crt"
jq -er '.privateKey' "$WORK/response" > "$WORK/server.key"
jq -er '"Authorization: " + .statsSecret' "$WORK/response" > "$WORK/stats-header"
openssl x509 -in "$WORK/server.crt" -noout -checkend 0 >/dev/null
openssl x509 -in "$WORK/server.crt" -pubkey -noout > "$WORK/cert.pub"
openssl pkey -in "$WORK/server.key" -pubout > "$WORK/key.pub"
cmp -s "$WORK/cert.pub" "$WORK/key.pub" || { echo '证书与私钥不匹配。'; exit 1; }
install -d -m 700 /etc/hysteria /usr/local/lib/hy2-panel
# 仅允许重试本安装记录生成的配置。
printf '%s' "$INSTALL_ID" > /etc/hysteria/.panel-install-id
systemctl stop hy2-panel-node.service 2>/dev/null || true
install -m 755 "$WORK/hysteria" /usr/local/lib/hy2-panel/hysteria
install -m 600 "$WORK/config.yaml" /etc/hysteria/config.yaml
install -m 600 "$WORK/server.crt" /etc/hysteria/server.crt
install -m 600 "$WORK/server.key" /etc/hysteria/server.key
cat > /etc/systemd/system/hy2-panel-node.service <<'UNIT'
[Unit]
Description=Hysteria 2 (HY2 Panel)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/lib/hy2-panel/hysteria server --config /etc/hysteria/config.yaml
Restart=on-failure
RestartSec=5
LimitNOFILE=1048576
NoNewPrivileges=true
ProtectHome=true
ProtectSystem=strict
PrivateTmp=true
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl start hy2-panel-node.service
STARTED=1
READY=0
for attempt in {1..15}; do
if systemctl is-active --quiet hy2-panel-node.service && \
curl -fsS --max-time 2 -H @"$WORK/stats-header" "http://127.0.0.1:$STATS_LISTEN/traffic" -o /dev/null; then
READY=1; break
fi
sleep 1
done
[ "$READY" -eq 1 ] || { echo '服务健康检查失败,请检查端口占用和 systemctl status hy2-panel-node。'; exit 1; }
# 完成通知可能已被平台处理但响应丢失;保留已健康启动的服务,避免误停。
systemctl enable hy2-panel-node.service
STARTED=0
printf '{"action":"complete"}' > "$WORK/request"
# 重试完成通知,平台支持幂等确认。
for attempt in {1..3}; do
if api; then break; fi
[ "$attempt" -lt 3 ] || exit 1
sleep 2
done
STARTED=0
echo "安装完成,节点 $SLUG 已注册并启用。"
echo "请在 NAT / 云防火墙中映射 UDP $HY2_PORT → $HY2_LISTEN、TCP $STATS_PORT → $STATS_LISTEN。"
echo '流量统计 TCP 端口建议仅允许面板服务器 IP 访问。公网连通情况请在平台查看同步状态。'