#!/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 = 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 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 访问。公网连通情况请在平台查看同步状态。'