更新 hy2.sh
This commit is contained in:
220
hy2.sh
220
hy2.sh
@@ -4,7 +4,6 @@ trap 'echo "[错误] 第 $LINENO 行执行失败:$BASH_COMMAND"' ERR
|
|||||||
|
|
||||||
CONFIG_FILE="/etc/hysteria/config.yaml"
|
CONFIG_FILE="/etc/hysteria/config.yaml"
|
||||||
SERVICE_NAME="hysteria-server.service"
|
SERVICE_NAME="hysteria-server.service"
|
||||||
MASQUERADE_DEFAULT_URL="https://news.ycombinator.com/"
|
|
||||||
|
|
||||||
red() { printf '\033[31m%s\033[0m\n' "$*"; }
|
red() { printf '\033[31m%s\033[0m\n' "$*"; }
|
||||||
green() { printf '\033[32m%s\033[0m\n' "$*"; }
|
green() { printf '\033[32m%s\033[0m\n' "$*"; }
|
||||||
@@ -49,23 +48,6 @@ confirm_yn() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_password() {
|
|
||||||
openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_sub_prefix() {
|
|
||||||
openssl rand -base64 32 | tr -dc 'a-z0-9' | head -c 8
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
get_public_ips() {
|
|
||||||
local ipv4="" ipv6=""
|
|
||||||
ipv4="$(curl -4 -fsSL https://api.ipify.org || true)"
|
|
||||||
ipv6="$(curl -6 -fsSL https://api64.ipify.org || true)"
|
|
||||||
printf '%s|%s\n' "$ipv4" "$ipv6"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_apt_lock() {
|
wait_for_apt_lock() {
|
||||||
local timeout="${1:-300}"
|
local timeout="${1:-300}"
|
||||||
local waited=0
|
local waited=0
|
||||||
@@ -92,11 +74,24 @@ wait_for_apt_lock() {
|
|||||||
waited=$((waited + 2))
|
waited=$((waited + 2))
|
||||||
done
|
done
|
||||||
|
|
||||||
|
green "APT 锁已释放。"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_password() {
|
||||||
|
openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
get_server_ip() {
|
||||||
|
local ipv4 ipv6
|
||||||
|
ipv4="$(curl -4 -fsSL https://api.ipify.org || true)"
|
||||||
|
ipv6="$(curl -6 -fsSL https://api64.ipify.org || true)"
|
||||||
|
echo "${ipv4}|${ipv6}"
|
||||||
|
}
|
||||||
|
|
||||||
apt_update_and_install_base() {
|
apt_update_and_install_base() {
|
||||||
if ! confirm_yn "即将执行 apt update 并安装基础依赖 jq curl sed ufw openssl,是否继续?"; then
|
if ! confirm_yn "即将执行 apt update 并安装基础依赖 curl sed ufw openssl,是否继续?"; then
|
||||||
red "已取消。"
|
red "已取消。"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -109,7 +104,7 @@ apt_update_and_install_base() {
|
|||||||
|
|
||||||
blue "==> 安装基础依赖"
|
blue "==> 安装基础依赖"
|
||||||
wait_for_apt_lock 300 || { red "APT 被占用,无法继续。"; exit 1; }
|
wait_for_apt_lock 300 || { red "APT 被占用,无法继续。"; exit 1; }
|
||||||
apt install -y jq curl sed ufw openssl
|
apt install -y curl sed ufw openssl
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_existing_firewalls() {
|
disable_existing_firewalls() {
|
||||||
@@ -207,113 +202,101 @@ install_hysteria2() {
|
|||||||
green "Hysteria 2 安装完成。"
|
green "Hysteria 2 安装完成。"
|
||||||
}
|
}
|
||||||
|
|
||||||
cf_api() {
|
run_domain_selector() {
|
||||||
|
if confirm_yn "是否执行外部域名筛选脚本?"; then
|
||||||
|
blue "==> 执行域名筛选脚本"
|
||||||
|
bash <(curl -sL https://raw.githubusercontent.com/ccxkai233/Domain_Selector/main/domain_check.sh) || true
|
||||||
|
else
|
||||||
|
yellow "已跳过域名筛选脚本。"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cf_api_request() {
|
||||||
local method="$1"
|
local method="$1"
|
||||||
local endpoint="$2"
|
local url="$2"
|
||||||
local token="$3"
|
local data="${3:-}"
|
||||||
local data="${4:-}"
|
|
||||||
|
|
||||||
if [[ -n "$data" ]]; then
|
if [[ -n "$data" ]]; then
|
||||||
curl -fsSL -X "$method" "https://api.cloudflare.com/client/v4${endpoint}" \
|
curl -fsSL -X "$method" "$url" \
|
||||||
-H "Authorization: Bearer ${token}" \
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
--data "$data"
|
--data "$data"
|
||||||
else
|
else
|
||||||
curl -fsSL -X "$method" "https://api.cloudflare.com/client/v4${endpoint}" \
|
curl -fsSL -X "$method" "$url" \
|
||||||
-H "Authorization: Bearer ${token}" \
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
-H "Content-Type: application/json"
|
-H "Content-Type: application/json"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
get_cf_zone_id() {
|
get_cf_zone_id() {
|
||||||
local main_domain="$1"
|
local zone="$1"
|
||||||
local token="$2"
|
cf_api_request GET "https://api.cloudflare.com/client/v4/zones?name=${zone}" \
|
||||||
local resp zone_id
|
| sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -n 1
|
||||||
resp="$(cf_api GET "/zones?name=${main_domain}&status=active" "$token")"
|
|
||||||
zone_id="$(printf '%s' "$resp" | jq -r '.result[0].id // empty')"
|
|
||||||
|
|
||||||
if [[ -z "$zone_id" ]]; then
|
|
||||||
red "未找到 Cloudflare Zone:${main_domain}"
|
|
||||||
red "请确认主域名已接入 Cloudflare,且 API Token 对该 Zone 具有 DNS 编辑权限。"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '%s\n' "$zone_id"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_existing_dns_record_if_needed() {
|
delete_existing_cf_dns_record() {
|
||||||
local zone_id="$1"
|
local zone_id="$1"
|
||||||
local full_domain="$2"
|
local full_domain="$2"
|
||||||
local type="$3"
|
local type="$3"
|
||||||
local token="$4"
|
local record_id
|
||||||
local resp record_ids rid
|
|
||||||
|
|
||||||
resp="$(cf_api GET "/zones/${zone_id}/dns_records?type=${type}&name=${full_domain}" "$token")"
|
record_id="$(
|
||||||
record_ids="$(printf '%s' "$resp" | jq -r '.result[].id // empty')"
|
cf_api_request GET "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records?name=${full_domain}&type=${type}" \
|
||||||
|
| sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
if [[ -n "$record_ids" ]]; then
|
if [[ -n "$record_id" ]]; then
|
||||||
while IFS= read -r rid; do
|
cf_api_request DELETE "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id}" >/dev/null
|
||||||
[[ -z "$rid" ]] && continue
|
|
||||||
cf_api DELETE "/zones/${zone_id}/dns_records/${rid}" "$token" >/dev/null
|
|
||||||
done <<< "$record_ids"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
create_cf_dns_record() {
|
create_cf_dns_record_type() {
|
||||||
local zone_id="$1"
|
local zone_id="$1"
|
||||||
local full_domain="$2"
|
local full_domain="$2"
|
||||||
local type="$3"
|
local type="$3"
|
||||||
local content="$4"
|
local content="$4"
|
||||||
local token="$5"
|
|
||||||
local payload resp success
|
|
||||||
|
|
||||||
payload="$(jq -nc \
|
[[ -z "$content" ]] && return 0
|
||||||
--arg type "$type" \
|
|
||||||
--arg name "$full_domain" \
|
|
||||||
--arg content "$content" \
|
|
||||||
'{type:$type,name:$name,content:$content,ttl:120,proxied:false}')"
|
|
||||||
|
|
||||||
resp="$(cf_api POST "/zones/${zone_id}/dns_records" "$token" "$payload")"
|
local payload
|
||||||
success="$(printf '%s' "$resp" | jq -r '.success')"
|
payload=$(
|
||||||
|
cat <<EOF
|
||||||
|
{"type":"${type}","name":"${full_domain}","content":"${content}","ttl":120,"proxied":false}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
if [[ "$success" != "true" ]]; then
|
cf_api_request POST "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records" "$payload" >/dev/null
|
||||||
red "创建 Cloudflare DNS 记录失败:"
|
|
||||||
printf '%s\n' "$resp" | jq .
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_cloudflare_dns_records() {
|
create_cloudflare_dns_record() {
|
||||||
local main_domain="$1"
|
local zone="$1"
|
||||||
local sub_prefix="$2"
|
local subdomain="$2"
|
||||||
local token="$3"
|
local ipv4="$3"
|
||||||
local ip_info="$4"
|
local ipv6="$4"
|
||||||
local ipv4="${ip_info%%|*}"
|
local full_domain="${subdomain}.${zone}"
|
||||||
local ipv6="${ip_info##*|}"
|
|
||||||
local full_domain="${sub_prefix}.${main_domain}"
|
|
||||||
local zone_id
|
local zone_id
|
||||||
|
|
||||||
zone_id="$(get_cf_zone_id "$main_domain" "$token")"
|
echo "==> 在 Cloudflare 中创建 DNS 记录" >&2
|
||||||
|
echo "Zone: ${zone}" >&2
|
||||||
|
echo "完整域名: ${full_domain}" >&2
|
||||||
|
|
||||||
blue "==> 在 Cloudflare 中创建 DNS 记录"
|
zone_id="$(get_cf_zone_id "$zone")"
|
||||||
echo "Zone: ${main_domain}"
|
if [[ -z "$zone_id" ]]; then
|
||||||
echo "完整域名: ${full_domain}"
|
red "无法获取 Cloudflare Zone ID,请检查 Token 权限或 Zone 名称。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
delete_existing_cf_dns_record "$zone_id" "$full_domain" "A" || true
|
||||||
|
delete_existing_cf_dns_record "$zone_id" "$full_domain" "AAAA" || true
|
||||||
|
|
||||||
if [[ -n "$ipv4" ]]; then
|
if [[ -n "$ipv4" ]]; then
|
||||||
delete_existing_dns_record_if_needed "$zone_id" "$full_domain" "A" "$token"
|
create_cf_dns_record_type "$zone_id" "$full_domain" "A" "$ipv4"
|
||||||
create_cf_dns_record "$zone_id" "$full_domain" "A" "$ipv4" "$token"
|
echo "已创建 A 记录 -> ${ipv4}" >&2
|
||||||
green "已创建 A 记录 -> ${ipv4}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$ipv6" ]]; then
|
if [[ -n "$ipv6" ]]; then
|
||||||
delete_existing_dns_record_if_needed "$zone_id" "$full_domain" "AAAA" "$token"
|
create_cf_dns_record_type "$zone_id" "$full_domain" "AAAA" "$ipv6"
|
||||||
create_cf_dns_record "$zone_id" "$full_domain" "AAAA" "$ipv6" "$token"
|
echo "已创建 AAAA 记录 -> ${ipv6}" >&2
|
||||||
green "已创建 AAAA 记录 -> ${ipv6}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$ipv4" && -z "$ipv6" ]]; then
|
|
||||||
red "未获取到当前机器公网 IPv4/IPv6,无法创建 DNS 记录。"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "$full_domain"
|
printf '%s\n' "$full_domain"
|
||||||
@@ -328,7 +311,7 @@ backup_existing_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_config() {
|
write_config() {
|
||||||
local full_domain="$1"
|
local domain="$1"
|
||||||
local email="$2"
|
local email="$2"
|
||||||
local cf_token="$3"
|
local cf_token="$3"
|
||||||
local password="$4"
|
local password="$4"
|
||||||
@@ -341,7 +324,7 @@ listen: :443
|
|||||||
|
|
||||||
acme:
|
acme:
|
||||||
domains:
|
domains:
|
||||||
- ${full_domain}
|
- ${domain}
|
||||||
email: ${email}
|
email: ${email}
|
||||||
type: dns
|
type: dns
|
||||||
dns:
|
dns:
|
||||||
@@ -374,7 +357,7 @@ listen: :443
|
|||||||
|
|
||||||
acme:
|
acme:
|
||||||
domains:
|
domains:
|
||||||
- ${full_domain}
|
- ${domain}
|
||||||
email: ${email}
|
email: ${email}
|
||||||
type: dns
|
type: dns
|
||||||
dns:
|
dns:
|
||||||
@@ -412,17 +395,18 @@ start_service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_result() {
|
show_result() {
|
||||||
local full_domain="$1"
|
local domain="$1"
|
||||||
local password="$2"
|
local password="$2"
|
||||||
local proxy_url="$3"
|
local proxy_url="$3"
|
||||||
local ip_info="$4"
|
local ip_info="$4"
|
||||||
local ipv4="${ip_info%%|*}"
|
local ipv4="${ip_info%%|*}"
|
||||||
local ipv6="${ip_info##*|}"
|
local ipv6="${ip_info##*|}"
|
||||||
local share_link="hysteria2://${password}@${full_domain}:443/?sni=${full_domain}&insecure=0"
|
|
||||||
|
local share_link="hysteria2://${password}@${domain}:443/?sni=${domain}&insecure=0"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
green "================= HY2 节点信息 ================="
|
green "================= HY2 节点信息 ================="
|
||||||
echo "域名: ${full_domain}"
|
echo "域名: ${domain}"
|
||||||
echo "端口: 443"
|
echo "端口: 443"
|
||||||
echo "密码: ${password}"
|
echo "密码: ${password}"
|
||||||
echo "伪装站点: ${proxy_url}"
|
echo "伪装站点: ${proxy_url}"
|
||||||
@@ -433,14 +417,17 @@ show_result() {
|
|||||||
echo "${share_link}"
|
echo "${share_link}"
|
||||||
echo
|
echo
|
||||||
echo "hysteria 状态:"
|
echo "hysteria 状态:"
|
||||||
systemctl status "${SERVICE_NAME}" --no-pager -l | sed -n '1,8p'
|
systemctl --no-pager --full status "${SERVICE_NAME}" || true
|
||||||
|
echo
|
||||||
|
echo "最近日志:"
|
||||||
|
journalctl --no-pager -n 30 -u "${SERVICE_NAME}" || true
|
||||||
echo "================================================"
|
echo "================================================"
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
require_root
|
require_root
|
||||||
|
|
||||||
if ! confirm_yn "本脚本将更新软件源、关闭现有防火墙、配置 UFW、安装并配置 Hysteria 2、自动创建 Cloudflare DNS,是否继续?"; then
|
if ! confirm_yn "本脚本将更新软件源、关闭现有防火墙、配置 UFW、安装并配置 Hysteria 2,是否继续?"; then
|
||||||
red "用户取消执行。"
|
red "用户取消执行。"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -451,34 +438,43 @@ main() {
|
|||||||
require_cmd sed
|
require_cmd sed
|
||||||
require_cmd systemctl
|
require_cmd systemctl
|
||||||
require_cmd ufw
|
require_cmd ufw
|
||||||
require_cmd jq
|
|
||||||
require_cmd openssl
|
require_cmd openssl
|
||||||
|
|
||||||
local main_domain email cf_token password sub_prefix full_domain ip_info proxy_url
|
local email zone subdomain proxy_url password ip_info ipv4 ipv6 domain
|
||||||
|
|
||||||
main_domain="$(prompt_nonempty '请输入主域名(例如 example.com): ')"
|
|
||||||
email="$(prompt_nonempty '请输入 ACME 邮箱: ')"
|
email="$(prompt_nonempty '请输入 ACME 邮箱: ')"
|
||||||
cf_token="$(prompt_nonempty '请输入 Cloudflare API Token: ')"
|
zone="$(prompt_nonempty '请输入 Cloudflare Zone(例如 example.com): ')"
|
||||||
|
subdomain="$(prompt_nonempty '请输入要创建的子域名前缀(例如 hy2): ')"
|
||||||
|
CF_API_TOKEN="$(prompt_nonempty '请输入 Cloudflare API Token: ')"
|
||||||
|
export CF_API_TOKEN
|
||||||
|
|
||||||
password="$(generate_password)"
|
password="$(generate_password)"
|
||||||
sub_prefix="$(generate_sub_prefix)"
|
|
||||||
ip_info="$(get_public_ips)"
|
|
||||||
|
|
||||||
full_domain="$(create_cloudflare_dns_records "$main_domain" "$sub_prefix" "$cf_token" "$ip_info")"
|
ip_info="$(get_server_ip)"
|
||||||
proxy_url="$MASQUERADE_DEFAULT_URL"
|
ipv4="${ip_info%%|*}"
|
||||||
|
ipv6="${ip_info##*|}"
|
||||||
|
|
||||||
echo
|
if [[ -z "$ipv4" && -z "$ipv6" ]]; then
|
||||||
green "自动生成的 Hysteria 域名: ${full_domain}"
|
red "无法获取服务器公网 IP。"
|
||||||
echo "默认伪装站点: ${proxy_url}"
|
exit 1
|
||||||
echo "随机密码: ${password}"
|
fi
|
||||||
echo
|
|
||||||
|
|
||||||
disable_existing_firewalls
|
disable_existing_firewalls
|
||||||
configure_ufw
|
configure_ufw
|
||||||
install_hysteria2
|
install_hysteria2
|
||||||
write_config "${full_domain}" "${email}" "${cf_token}" "${password}" "${proxy_url}"
|
run_domain_selector
|
||||||
|
|
||||||
|
proxy_url="$(prompt_nonempty '请输入最终用于 masquerade 的完整 URL(例如 https://example.com/): ')"
|
||||||
|
|
||||||
|
if confirm_yn "是否在 Cloudflare 中自动创建 DNS 记录 ${subdomain}.${zone}?"; then
|
||||||
|
domain="$(create_cloudflare_dns_record "$zone" "$subdomain" "$ipv4" "$ipv6")"
|
||||||
|
else
|
||||||
|
domain="$(prompt_nonempty '请输入已存在并已解析到本机的完整域名: ')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
write_config "${domain}" "${email}" "${CF_API_TOKEN}" "${password}" "${proxy_url}"
|
||||||
start_service
|
start_service
|
||||||
show_result "${full_domain}" "${password}" "${proxy_url}" "${ip_info}"
|
show_result "${domain}" "${password}" "${proxy_url}" "${ip_info}"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
Reference in New Issue
Block a user