Files

167 lines
7.2 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 访问。公网连通情况请在平台查看同步状态。'