给纯 IPv4 服务器开 IPv6(含 nginx / Docker / 防火墙的几个坑)

起因

VPS 厂商给的 IP 默认是 IPv4。但越来越多用户网络只走 IPv6(移动 5G、
家宽 IPv6-only),网站不支持 IPv6 会被这些用户判为"无法访问"。
另外 SEO(Google)也把 IPv6 当作 ranking signal。

许多 VPS 厂商免费送 IPv6 /64 段,不开白不开。

解决方案

1. 拿到 IPv6 配置

VPS 厂商控制台 → Network → 看 IPv6 段(如 2001:db8::/64)+ gateway。
有些厂商默认开了,ip -6 addr 能看到;有些要在控制台手动启用。

2. 配置接口

/etc/netplan/01-netcfg.yaml(Ubuntu):

network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 192.0.2.10/24
        - 2001:db8::a/64
      gateway6: 2001:db8::1
      nameservers:
        addresses:
          - 1.1.1.1
          - 2606:4700:4700::1111   # 同时配 v4 + v6 DNS
sudo netplan apply
ip -6 addr show eth0
# 应该看到 2001:db8::a/64

老 Debian 用 /etc/network/interfaces

iface eth0 inet6 static
    address 2001:db8::a/64
    gateway 2001:db8::1

3. 校验

ping6 ipv6.google.com
# PING ipv6.google.com(... 2607:f8b0:...) ...

curl -6 https://ifconfig.co
# 你的 IPv6 地址

4. DNS 加 AAAA 记录

DNS 控制面板加:

example.com.    AAAA    2001:db8::a
www.example.com.  AAAA    2001:db8::a

A 记录保留给 v4;AAAA 加给 v6。客户端按需选。

dig AAAA example.com
# example.com.   300  IN  AAAA  2001:db8::a

Cloudflare 用户:A + AAAA 都加上让 Cloudflare 处理 dual stack。

5. nginx listen on IPv6

server {
    listen 80;
    listen [::]:80;          # IPv6
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com;
    ...
}

[::] 是 IPv6 通配地址。重启:

sudo systemctl reload nginx
ss -tlnp | grep nginx
# 同时看到 *:80 和 :::80

6. 防火墙 IPv6 规则

ufw 默认同时管 v4 + v6(看 /etc/default/ufwIPV6=yes):

sudo ufw allow 22
sudo ufw allow 80
sudo ufw allow 443
# 自动同时管 v4 + v6

iptables / nftables 要分别管 v4 / v6:

# nftables(推荐:table inet 一管两种)
table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
        iif lo accept
        ip protocol icmp accept
        meta nfproto ipv6 icmpv6 accept   # IPv6 ICMP(必须开,PMTU 用)
        tcp dport { 22, 80, 443 } accept
    }
}

v6 ICMP 必须放行——v6 路径 MTU 发现完全依赖 ICMP。封了 → 大包不通 →
间歇性页面打不开。

7. systemd / 应用监听 v6

很多 service 默认只听 v4:

listen 0.0.0.0:8080;   # 只 v4
# vs
listen [::]:8080;       # 既 v6 也 v4(Linux 默认开 IPV6_V6ONLY=0)
listen [::]:8080 ipv6only=on;  # 仅 v6

Python:

# 单一 socket 同时 v4 + v6
import socket
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.bind(('::', 8080))
# accept v4 client 时收到 ::ffff:1.2.3.4 形式

Node:

server.listen({ host: '::', port: 8080 })

Go:

http.ListenAndServe(":8080", nil)   // Go 默认双栈

8. Docker 容器开 v6

默认 Docker 容器只有 v4 网络。开 v6:

/etc/docker/daemon.json:

{
  "ipv6": true,
  "fixed-cidr-v6": "fd00::/80",
  "experimental": true,
  "ip6tables": true
}
sudo systemctl restart docker
docker run --rm alpine ip -6 addr
# 看到 inet6

或者用 host network 模式:

docker run --network host nginx
# 容器直接用 host 的网络栈 + v6

Docker Compose:

networks:
  default:
    enable_ipv6: true
    ipam:
      config:
        - subnet: 'fd00:dead:beef::/64'

9. systemd-resolved 配 v6

sudo nano /etc/systemd/resolved.conf
# DNS=1.1.1.1 1.0.0.1 2606:4700:4700::1111
# FallbackDNS=8.8.8.8 8.8.4.4 2001:4860:4860::8888
sudo systemctl restart systemd-resolved

或者直接 /etc/resolv.conf 加 v6 nameserver。

10. 监控两栈连通

写一个 cron:

#!/usr/bin/env bash
# /usr/local/sbin/dual-stack-check.sh
set -e
curl -sf -4 https://example.com/health || echo "v4 down" | mail -s alert ops@
curl -sf -6 https://example.com/health || echo "v6 down" | mail -s alert ops@

或者用 Uptime Kuma 配两个 monitor(v4 + v6)。

实测一些坑

A. ping6 ICMP 被防火墙挡

ping6 ipv6.google.com
# ping: connect: Network is unreachable

99% 是 ICMPv6 被防火墙误封。nft list rulesetmeta nfproto ipv6 icmpv6 accept 是否在。

B. v4 通 v6 不通

curl example.com         # OK
curl -6 example.com      # timeout

检查:
1. AAAA 记录有没有:dig AAAA example.com
2. nginx 是否 listen :::80:ss -tlnp | grep nginx
3. 防火墙 v6 允许 80/443:ip6tables -L / nft list

C. 部分客户端"happy eyeballs" 走 v6 失败

浏览器有 happy eyeballs 算法:v4/v6 都试,谁先连上用谁。
如果 v6 路径慢(你 ISP 链路差),happy eyeballs 会让浏览器走 v4,
看起来一切正常。但 v6-only 用户仍打不开 → 监控里抓不到。
专门做 v6-only 测试机定期 check。

D. CDN / WAF v6 支持

Cloudflare / Fastly 自动双栈。
某些小 CDN 不支持 v6 → 源站直连 v6 用户慢。问清楚。

E. SSH brute-force 在 v6 上

v6 地址空间大,扫不动。所以 v6 上 SSH 攻击罕见。
不过 fail2ban 默认只管 v4,要专门启 v6 支持:

[sshd]
banaction = ufw   # 或 nftables-multiport ipv6 friendly

F. AAAA 记录加错让网站慢

如果 AAAA 指向不可达 v6(你机器没真开 v6),浏览器会先试 v6 失败
+ timeout + 再试 v4。首屏慢 1-3 秒
要么真开 v6,要么删 AAAA。

效果

我们站点开 IPv6 后:

  • 移动 5G 用户访问稳定(之前部分运营商 v4 NAT 偶尔丢包)
  • Google PageSpeed Insights "Modern web" 那条满分
  • 全球访问质量轻微改善(v6 路径在某些链路上更直)
  • 部分 API 请求量上升(v6-only 客户端被 unblock)

测试工具

踩过的坑(再总结)

  1. AAAA 记录加了但服务器没真 listen v6 → 用户访问慢
  2. ICMPv6 防火墙封死 → MTU 探测失败 → 大包不通
  3. systemd service 只 bind 0.0.0.0 → v6 客户端连不到
  4. Docker 容器忘开 v6 → 容器内服务对 v6 client 不响应
  5. fail2ban 只防 v4 → v6 上 attacker 不受限(虽然少见但仍要防)
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。