diff --git a/hy2.sh b/hy2.sh index 8e8718e..682177a 100644 --- a/hy2.sh +++ b/hy2.sh @@ -4,6 +4,9 @@ trap 'echo "[错误] 第 $LINENO 行执行失败:$BASH_COMMAND" >&2' ERR CONFIG_FILE="/etc/hysteria/config.yaml" SERVICE_NAME="hysteria-server.service" +DEVICE_USERS=(macminim4 iphone12p ipadmini5 pve_debian other) +USERPASS_ENTRIES=() +TRAFFIC_STATS_SECRET="" red() { printf '\033[31m%s\033[0m\n' "$*"; } green() { printf '\033[32m%s\033[0m\n' "$*"; } @@ -89,8 +92,16 @@ wait_for_apt_lock() { } generate_password() { - openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24 - echo + local length="${1:-36}" + local password="" + + while ((${#password} < length)); do + password+="$( + openssl rand -base64 48 | tr -dc 'A-Za-z0-9' + )" + done + + printf '%s\n' "${password:0:length}" } mask_secret() { @@ -103,6 +114,13 @@ mask_secret() { fi } +yaml_quote() { + local value="$1" + value="${value//\\/\\\\}" + value="${value//\"/\\\"}" + printf '"%s"' "$value" +} + validate_domain() { local domain="$1" [[ -n "$domain" ]] || return 1 @@ -369,12 +387,75 @@ backup_existing_config() { fi } -write_config() { +render_userpass_block() { + local indent="${1:-4}" + local prefix="" entry username password + + prefix="$(printf '%*s' "${indent}" '')" + for entry in "${USERPASS_ENTRIES[@]}"; do + username="${entry%%:*}" + password="${entry#*:}" + printf '%s%s: %s\n' "${prefix}" "${username}" "$(yaml_quote "$password")" + done +} + +render_config() { local domain="$1" local email="$2" local cf_token="$3" - local password="$4" - local proxy_url="$5" + local proxy_url="$4" + + cat < 即将写入如下配置到 ${CONFIG_FILE}" - cat < "${CONFIG_FILE}" < "${CONFIG_FILE}" chown root:root "${CONFIG_FILE}" - chmod 644 "${CONFIG_FILE}" + chmod 600 "${CONFIG_FILE}" green "配置已写入 ${CONFIG_FILE}" } @@ -463,25 +513,34 @@ start_service() { show_result() { local domain="$1" - local password="$2" - local proxy_url="$3" - local ip_info="$4" + local proxy_url="$2" + local ip_info="$3" local ipv4="${ip_info%%|*}" local ipv6="${ip_info##*|}" - - local share_link="hysteria2://${password}@${domain}:443/?sni=${domain}&insecure=0" + local entry username password echo green "================= HY2 节点信息 =================" echo "域名: ${domain}" echo "端口: 443" - echo "密码: ${password}" + echo "认证方式: userpass" echo "伪装站点: ${proxy_url}" + echo "流量统计监听: 127.0.0.1:9999" + echo "流量统计密钥: ${TRAFFIC_STATS_SECRET}" [[ -n "${ipv4}" ]] && echo "IPv4: ${ipv4}" [[ -n "${ipv6}" ]] && echo "IPv6: ${ipv6}" echo - echo "代理链接:" - echo "${share_link}" + echo "客户端连接参数:" + echo " server: ${domain}:443" + echo " sni: ${domain}" + echo " auth: userpass" + echo + echo "设备账号:" + for entry in "${USERPASS_ENTRIES[@]}"; do + username="${entry%%:*}" + password="${entry#*:}" + echo " ${username}: ${password}" + done echo echo "hysteria 状态:" systemctl --no-pager --full status "${SERVICE_NAME}" || true @@ -509,7 +568,7 @@ main() { require_cmd python3 require_python3_json || { red "python3 缺少 json 模块,无法解析 Cloudflare API 返回值。"; exit 1; } - local email zone subdomain proxy_url password ip_info ipv4 ipv6 domain + local email zone subdomain default_subdomain proxy_url ip_info ipv4 ipv6 domain username email="$(prompt_nonempty '请输入 ACME 邮箱: ')" zone="$(prompt_nonempty '请输入 Cloudflare Zone(例如 example.com): ')" @@ -522,7 +581,11 @@ main() { CF_API_TOKEN="$(prompt_nonempty '请输入 Cloudflare API Token: ')" export CF_API_TOKEN - password="$(generate_password)" + USERPASS_ENTRIES=() + for username in "${DEVICE_USERS[@]}"; do + USERPASS_ENTRIES+=("${username}:$(generate_password 36)") + done + TRAFFIC_STATS_SECRET="$(generate_password 36)" ip_info="$(get_server_ip)" ipv4="${ip_info%%|*}" @@ -549,9 +612,9 @@ main() { domain="$(prompt_nonempty '请输入已存在并已解析到本机的完整域名: ')" fi - write_config "${domain}" "${email}" "${CF_API_TOKEN}" "${password}" "${proxy_url}" + write_config "${domain}" "${email}" "${CF_API_TOKEN}" "${proxy_url}" start_service - show_result "${domain}" "${password}" "${proxy_url}" "${ip_info}" + show_result "${domain}" "${proxy_url}" "${ip_info}" } main "$@"