0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Hysteria 2 部署指南:IPv6-Only VPS + 客户端双栈自动回退与分流

0
Posted at

Hysteria 2 部署指南:IPv6-Only VPS + 客户端双栈自动回退与分流

适用场景:VPS 仅有公网 IPv6 地址(无 IPv4),客户端处于 IPv4/IPv6 双栈网络,需要通过 Hysteria 2 实现透明代理及智能分流。


目录

  1. 背景与架构概述
  2. 服务端配置(IPv6-Only VPS)
  3. DNS64 / NAT64 支持(可选)
  4. 客户端配置(双栈环境)
  5. 自动回退逻辑设计
  6. 分流规则配置
  7. Linux 路由表进阶配置
  8. 故障排查

1. 背景与架构概述

问题模型

客户端(双栈:IPv4 + IPv6)
        │
        ├─── IPv6 ──→ VPS(仅 IPv6):443/UDP  ← Hysteria 2 服务端
        │
        └─── IPv4 ──→ ??? (VPS 无法直接接收 IPv4)

核心挑战

问题 说明
VPS 无 IPv4 客户端若走 IPv4 则无法连接服务端
客户端优先级 Happy Eyeballs / 系统可能优先选择 IPv4
回退机制缺失 Hysteria 2 本身不内置多地址自动切换
DNS 解析 VPS 上运行的代理需解析 IPv4 目标(如访问 IPv4-only 站点)

推荐架构

                        ┌─────────────────────────────────┐
                        │   客户端(Linux / macOS / Win)  │
                        │                                  │
                        │  ┌──────────┐  ┌─────────────┐  │
                        │  │ 分流引擎 │  │ 回退检测器  │  │
                        │  └────┬─────┘  └──────┬──────┘  │
                        │       │                │         │
                        │  ┌────▼────────────────▼──────┐ │
                        │  │     Hysteria 2 Client       │ │
                        │  └────────────────┬────────────┘ │
                        └───────────────────┼──────────────┘
                                            │ QUIC/UDP over IPv6
                                 ┌──────────▼──────────┐
                                 │  VPS [2001:db8::1]  │
                                 │  Hysteria 2 Server  │
                                 │  + NAT64 出口       │
                                 └─────────────────────┘

2. 服务端配置(IPv6-Only VPS)

2.1 安装 Hysteria 2

# 官方安装脚本
bash <(curl -fsSL https://get.hy2.sh/)

# 验证安装
hysteria version

2.2 申请 TLS 证书

由于 VPS 仅有 IPv6,需确保域名有 AAAA 记录指向 VPS。

# 方式一:ACME 自动申请(推荐,需域名 AAAA 记录已生效)
hysteria server --acme-domain your.domain.com

# 方式二:使用 certbot(适配 IPv6-only 环境)
certbot certonly \
  --standalone \
  --preferred-challenges http \
  -d your.domain.com \
  --server https://acme-v02.api.letsencrypt.org/directory
# 注意:Let's Encrypt CA 需能通过 IPv6 回访你的域名

2.3 服务端配置文件

路径:/etc/hysteria/config.yaml

# /etc/hysteria/config.yaml

listen: "[::]:443"           # 监听所有 IPv6 地址的 443/UDP

tls:
  cert: /etc/letsencrypt/live/your.domain.com/fullchain.pem
  key:  /etc/letsencrypt/live/your.domain.com/privkey.pem

auth:
  type: password
  password: "your-strong-password"

# 带宽限速(按实际线路调整)
bandwidth:
  up:   100 mbps
  down: 100 mbps

# QUIC 参数优化(IPv6 网络通常 MTU 更大)
quic:
  initStreamReceiveWindow: 8388608    # 8 MiB
  maxStreamReceiveWindow:  8388608
  initConnReceiveWindow:   20971520   # 20 MiB
  maxConnReceiveWindow:    20971520
  maxIdleTimeout:          60s
  keepAlivePeriod:         10s
  disablePathMTUDiscovery: false

# 日志(便于排查 IPv6 连接问题)
log:
  level: info
  timestamp: true

2.4 IPv6 内核参数

# /etc/sysctl.d/99-ipv6-forward.conf
cat >> /etc/sysctl.d/99-ipv6-forward.conf << 'EOF'
# 开启 IPv6 转发(NAT64 所需)
net.ipv6.conf.all.forwarding = 1

# 禁用 IPv6 Privacy Extensions(服务端不需要)
net.ipv6.conf.all.use_tempaddr = 0

# 增大 UDP 缓冲区(QUIC 性能关键)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
EOF

sysctl -p /etc/sysctl.d/99-ipv6-forward.conf

2.5 防火墙规则(nftables)

#!/usr/sbin/nft -f
# /etc/nftables-hysteria.conf

table inet hysteria {
  chain input {
    type filter hook input priority 0; policy accept;

    # 放行 Hysteria 2 QUIC 端口
    ip6 daddr { ::/0 } udp dport 443 accept comment "Hysteria2 QUIC"

    # 放行 ICMP6(邻居发现等)
    icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert,
                  nd-router-solicit, nd-router-advert } accept
  }

  chain forward {
    type filter hook forward priority 0; policy drop;

    # 允许已建立连接转发
    ct state { established, related } accept

    # NAT64 转发(见第3节)
    ip6 daddr 64:ff9b::/96 accept comment "NAT64 traffic"
  }
}
nft -f /etc/nftables-hysteria.conf
systemctl enable --now nftables

3. DNS64 / NAT64 支持(可选)

当客户端通过 Hysteria 2 代理访问 IPv4-only 站点时,服务端需要 NAT64 能力将 IPv6 流量转换为 IPv4 出口。

3.1 安装 Tayga(用户态 NAT64)

apt install tayga   # Debian/Ubuntu
# 或
dnf install tayga   # Fedora/RHEL

3.2 Tayga 配置

# /etc/tayga.conf
tun-device   nat64
ipv4-addr    192.168.255.1
prefix       64:ff9b::/96
dynamic-pool 192.168.255.0/24
data-dir     /var/lib/tayga
# 创建 TUN 设备并启动
tayga --mktun
ip link set nat64 up
ip route add 192.168.255.0/24 dev nat64
ip -6 route add 64:ff9b::/96 dev nat64

# 开启 IPv4 转发并配置 MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

tayga

3.3 DNS64(unbound 配置)

# /etc/unbound/unbound.conf.d/dns64.conf
server:
    # 只监听 IPv6
    interface: ::1
    interface: ::

    # DNS64 合成前缀(与 NAT64 一致)
    dns64-prefix: 64:ff9b::/96

    # 上游 DNS(IPv6 可达的公共解析器)
    do-ip4: no
    do-ip6: yes

forward-zone:
    name: "."
    forward-addr: 2001:4860:4860::8888    # Google IPv6 DNS
    forward-addr: 2606:4700:4700::1111    # Cloudflare IPv6 DNS
systemctl enable --now unbound

4. 客户端配置(双栈环境)

4.1 基础连接配置

# /etc/hysteria/client.yaml

server: your.domain.com:443    # 域名需有 AAAA 记录

auth: "your-strong-password"

# 强制走 IPv6 连接服务端(核心配置)
# Hysteria 2 使用 Happy Eyeballs,可通过 server 字段直接写 IPv6 字面量绕过
# server: "[2001:db8::1]:443"

bandwidth:
  up:   50 mbps
  down: 100 mbps

# SOCKS5 入站代理
socks5:
  listen: 127.0.0.1:1080

# HTTP 代理入站
http:
  listen: 127.0.0.1:8080

# TUN 模式(透明代理,Linux 推荐)
tun:
  name:    hysteria-tun
  mtu:     1500
  address:
    ipv4: 100.100.100.1/30
    ipv6: 2001:db8:1::1/126

4.2 强制 IPv6 出站的技巧

Hysteria 2 客户端默认使用 Happy Eyeballs 同时尝试 IPv4/IPv6,对于 IPv6-only 服务端,需确保 DNS 解析到 AAAA 记录并优先使用:

# 检查域名 AAAA 记录是否正常
dig AAAA your.domain.com

# 若客户端所在网络 IPv6 路由不通,可临时指定本地绑定地址
# 在 client.yaml 中添加:
# 追加至 client.yaml
transport:
  udp:
    hopInterval: 30s    # 端口跳跃间隔(抗干扰)

# 绑定特定本地 IPv6 地址出站
# localAddr: "[fe80::1%eth0]:0"    # 取消注释并按需修改

5. 自动回退逻辑设计

Hysteria 2 本身无内置多服务端自动切换,需在外部实现。以下提供两种方案。

方案 A:使用 systemd + 健康检查脚本

#!/usr/bin/env bash
# /usr/local/bin/hysteria-watchdog.sh
# 监控 Hysteria 2 客户端连通性,自动切换备用节点

PRIMARY_SERVER="your.domain.com:443"
FALLBACK_SERVER="[2001:db8::2]:443"   # 备用节点(也是 IPv6-only)
CONFIG_DIR="/etc/hysteria"
CHECK_INTERVAL=15
FAIL_THRESHOLD=3

fail_count=0
current_server="$PRIMARY_SERVER"

check_connectivity() {
    # 通过 SOCKS5 发送测试请求,超时 5 秒
    curl --socks5-hostname 127.0.0.1:1080 \
         --max-time 5 \
         --silent \
         --output /dev/null \
         https://captive.apple.com/hotspot-detect.html
    return $?
}

switch_to_fallback() {
    echo "[$(date -Iseconds)] 切换至备用节点: $FALLBACK_SERVER"
    sed -i "s|server:.*|server: $FALLBACK_SERVER|" "$CONFIG_DIR/client.yaml"
    systemctl restart hysteria-client
    current_server="$FALLBACK_SERVER"
    fail_count=0
}

switch_to_primary() {
    echo "[$(date -Iseconds)] 恢复主节点: $PRIMARY_SERVER"
    sed -i "s|server:.*|server: $PRIMARY_SERVER|" "$CONFIG_DIR/client.yaml"
    systemctl restart hysteria-client
    current_server="$PRIMARY_SERVER"
    fail_count=0
}

while true; do
    if ! check_connectivity; then
        (( fail_count++ ))
        echo "[$(date -Iseconds)] 连通性检查失败 ($fail_count/$FAIL_THRESHOLD)"

        if [[ $fail_count -ge $FAIL_THRESHOLD ]]; then
            if [[ "$current_server" == "$PRIMARY_SERVER" ]]; then
                switch_to_fallback
            else
                echo "[$(date -Iseconds)] 备用节点也不可用,等待恢复..."
            fi
        fi
    else
        # 若当前在备用节点且主节点可能已恢复,尝试切回
        if [[ "$current_server" != "$PRIMARY_SERVER" ]]; then
            # 直接探测主节点 IPv6 可达性
            if ping6 -c 2 -W 3 "${PRIMARY_SERVER%%:*}" &>/dev/null; then
                switch_to_primary
            fi
        fi
        fail_count=0
    fi

    sleep "$CHECK_INTERVAL"
done
# /etc/systemd/system/hysteria-watchdog.service
[Unit]
Description=Hysteria 2 Watchdog (Auto Failover)
After=hysteria-client.service
Requires=hysteria-client.service

[Service]
Type=simple
ExecStart=/usr/local/bin/hysteria-watchdog.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

方案 B:使用 sing-box 多出站负载均衡(推荐)

sing-box 内置健壮的出站管理与健康检测,可封装 Hysteria 2 协议:

{
  "outbounds": [
    {
      "type": "hysteria2",
      "tag": "hy2-primary",
      "server": "your.domain.com",
      "server_port": 443,
      "password": "your-strong-password",
      "up_mbps": 50,
      "down_mbps": 100
    },
    {
      "type": "hysteria2",
      "tag": "hy2-fallback",
      "server": "your-fallback.domain.com",
      "server_port": 443,
      "password": "your-strong-password",
      "up_mbps": 50,
      "down_mbps": 100
    },
    {
      "type": "urltest",
      "tag": "auto-select",
      "outbounds": ["hy2-primary", "hy2-fallback"],
      "url": "https://www.gstatic.com/generate_204",
      "interval": "30s",
      "tolerance": 50
    },
    {
      "type": "direct",
      "tag": "direct"
    },
    {
      "type": "block",
      "tag": "block"
    }
  ]
}

6. 分流规则配置

6.1 基于 GeoIP / GeoSite 的分流(sing-box)

{
  "route": {
    "rules": [
      {
        "protocol": "dns",
        "outbound": "dns-out"
      },
      {
        "geoip": ["private"],
        "outbound": "direct"
      },
      {
        "geosite": ["cn"],
        "geoip": ["cn"],
        "outbound": "direct"
      },
      {
        "geosite": ["ads"],
        "outbound": "block"
      },
      {
        "outbound": "auto-select"
      }
    ],
    "final": "auto-select",
    "auto_detect_interface": true
  }
}

6.2 IPv4/IPv6 流量的差异化分流

关键点:客户端访问 IPv4 目标时,需通过代理(服务端 NAT64 转换);访问 IPv6 目标时可直连或代理均可。

{
  "route": {
    "rules": [
      {
        "ip_version": 6,
        "geoip": ["cn"],
        "outbound": "direct",
        "comment": "国内 IPv6 直连"
      },
      {
        "ip_version": 4,
        "geoip": ["cn"],
        "outbound": "direct",
        "comment": "国内 IPv4 直连(客户端自身有 IPv4)"
      },
      {
        "ip_version": 4,
        "outbound": "auto-select",
        "comment": "境外 IPv4 走代理(由服务端 NAT64 处理)"
      },
      {
        "ip_version": 6,
        "outbound": "auto-select",
        "comment": "境外 IPv6 走代理"
      }
    ]
  }
}

6.3 DNS 分流配置

{
  "dns": {
    "servers": [
      {
        "tag": "dns-remote",
        "address": "https://1.1.1.1/dns-query",
        "detour": "auto-select"
      },
      {
        "tag": "dns-direct",
        "address": "223.5.5.5",
        "detour": "direct"
      },
      {
        "tag": "dns-local",
        "address": "local"
      }
    ],
    "rules": [
      {
        "geosite": ["cn"],
        "server": "dns-direct"
      },
      {
        "outbound": ["auto-select"],
        "server": "dns-remote",
        "disable_cache": false
      }
    ],
    "final": "dns-remote",
    "strategy": "prefer_ipv6",
    "disable_expire": false
  }
}

strategy: prefer_ipv6:DNS 优先返回 AAAA 记录,减少对代理服务端 NAT64 的依赖,提升性能。


7. Linux 路由表进阶配置

7.1 TUN 模式下的策略路由

#!/usr/bin/env bash
# /usr/local/bin/setup-hysteria-routes.sh
# 配置 Hysteria 2 TUN 模式策略路由

TUN_IFACE="hysteria-tun"
MARK=0x1234
TABLE=100

# ── 1. 创建路由表 ──────────────────────────────────────────
grep -q "^$TABLE " /etc/iproute2/rt_tables || \
    echo "$TABLE  hysteria" >> /etc/iproute2/rt_tables

# ── 2. 为 Hysteria 进程本身打标记(避免代理流量递归)──────
# 通过 cgroup 或 UID 标记 hysteria 进程
iptables -t mangle -A OUTPUT -m owner --uid-owner hysteria \
    -j MARK --set-mark $MARK

ip6tables -t mangle -A OUTPUT -m owner --uid-owner hysteria \
    -j MARK --set-mark $MARK

# ── 3. 标记流量走原始路由(绕过 TUN)────────────────────
ip rule add fwmark $MARK table $TABLE priority 100
ip -6 rule add fwmark $MARK table $TABLE priority 100

ip route add default via $(ip route show default | awk '/via/{print $3}') \
    table $TABLE
ip -6 route add default via $(ip -6 route show default | awk '/via/{print $3}') \
    dev $(ip -6 route show default | awk '/dev/{print $5}') \
    table $TABLE

# ── 4. 其余流量走 TUN ────────────────────────────────────
ip rule add not fwmark $MARK table main priority 200

echo "策略路由配置完成"

7.2 IPv6 默认路由保留(防止服务端连接断开)

# 连接 Hysteria 2 服务端的 IPv6 流量必须走物理网卡,不能进 TUN
# 获取 VPS 的 IPv6 地址
VPS_IPV6=$(dig AAAA your.domain.com +short | head -1)
PHYS_IFACE=$(ip -6 route get "$VPS_IPV6" | awk '/dev/{print $5}' | head -1)
GW6=$(ip -6 route show default | awk '/via/{print $3}' | head -1)

ip -6 route add "$VPS_IPV6/128" via "$GW6" dev "$PHYS_IFACE"
echo "已添加服务端 IPv6 主机路由,绕过 TUN"

7.3 完整 nftables 策略(客户端侧)

#!/usr/sbin/nft -f
# 客户端 nftables:透明代理 + 绕过本机流量

define HYSTERIA_UID = 1001           # Hysteria 进程运行的 UID
define BYPASS_MARK = 0x00001234
define TUN_IFACE   = "hysteria-tun"

table inet tproxy_rules {

  # 放行 Hysteria 自身流量(防环)
  chain mangle_output {
    type route hook output priority mangle; policy accept;

    meta skuid $HYSTERIA_UID mark set $BYPASS_MARK return
    ip  daddr { 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } return
    ip6 daddr { ::1/128, fe80::/10, fd00::/8 } return

    # 国内 IP 段直连(需提前加载 geoip 集合,此处示意)
    # ip  daddr @cn_ipv4 return
    # ip6 daddr @cn_ipv6 return

    mark set 1 comment "标记需代理流量"
  }

  chain mangle_prerouting {
    type filter hook prerouting priority mangle; policy accept;
    mark 1 tproxy to 127.0.0.1:7895 comment "TProxy 重定向至 sing-box"
  }
}

8. 故障排查

8.1 常见问题速查

症状 可能原因 排查命令
客户端无法连接服务端 客户端无 IPv6 路由 / AAAA 解析失败 ping6 your.domain.com
连接抖动 / 频繁重连 UDP 被运营商 QoS hysteria client --log-level debug
IPv4 站点无法访问 服务端 NAT64 未配置 curl --socks5 127.0.0.1:1080 http://1.1.1.1
DNS 泄漏 本地 DNS 未走代理 curl https://ipleak.net/json/
TUN 流量递归 策略路由标记缺失 ip rule show; ip -6 rule show

8.2 IPv6 连通性检查脚本

#!/usr/bin/env bash
# 一键检查 IPv6 连通性与 Hysteria 2 服务可达性

SERVER_DOMAIN="your.domain.com"
SERVER_PORT=443

echo "=== 本机 IPv6 地址 ==="
ip -6 addr show scope global | grep inet6

echo ""
echo "=== 默认 IPv6 路由 ==="
ip -6 route show default

echo ""
echo "=== DNS 解析 AAAA ==="
dig AAAA "$SERVER_DOMAIN" +short

echo ""
echo "=== IPv6 Ping 服务端 ==="
ping6 -c 4 "$SERVER_DOMAIN"

echo ""
echo "=== UDP 端口可达性(需 netcat-openbsd)==="
SERVER_IP=$(dig AAAA "$SERVER_DOMAIN" +short | head -1)
echo "Q" | timeout 3 nc -6 -u "$SERVER_IP" "$SERVER_PORT" \
    && echo "UDP 端口可达" \
    || echo "UDP 端口超时(正常,QUIC 需握手)"

echo ""
echo "=== Hysteria 2 服务状态 ==="
systemctl is-active hysteria-client && \
    echo "客户端运行中" || echo "客户端未运行"

echo ""
echo "=== 代理连通性测试 ==="
curl --socks5-hostname 127.0.0.1:1080 \
     --max-time 10 \
     --silent \
     https://api.ipify.org?format=json && echo ""

8.3 性能调优参数参考

# 针对 QUIC 的系统级调优(服务端 + 客户端均适用)

# UDP 接收/发送缓冲区
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216

# 开启 GSO/GRO(需内核 5.x+,显著降低 QUIC CPU 开销)
ethtool -K eth0 gso on gro on

# 增大连接跟踪表(高并发场景)
sysctl -w net.netfilter.nf_conntrack_max=1048576

# IPv6 邻居表(大规模部署)
sysctl -w net.ipv6.neigh.default.gc_thresh3=8192

附录:完整文件结构

/etc/hysteria/
├── config.yaml          # 服务端配置(VPS 上)
├── client.yaml          # 客户端基础配置
└── rules/
    ├── geoip.db         # GeoIP 数据库
    └── geosite.db       # GeoSite 数据库

/usr/local/bin/
├── hysteria-watchdog.sh # 自动回退守护脚本
└── setup-hysteria-routes.sh  # 路由初始化脚本

/etc/systemd/system/
├── hysteria-client.service
└── hysteria-watchdog.service

最后更新:2026-04 | 适用版本:Hysteria 2.x、sing-box 1.9+、Linux kernel 5.15+

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?