家宽运营商封锁 80/443 端口公网入站, 导致自建服务无法通过标准端口对外访问. 本文利用 Happy Eyeballs (快乐眼球) 算法的双栈回退机制, 通过 DDNS-Go 将 AAAA 解析指向家宽 IPv6 地址, A 解析指向 CDN 节点, CDN 通过非标端口 (如 8443) 回源 IPv6. 公网用户走 IPv4 -> CDN -> 非标端口回源; 局域网用户走 IPv6 直连, 从而绕过端口封锁实现双栈分流建站. (AI摘要总结)

前言

家宽运营商通常封锁 80 和 443 端口的公网入站流量, 导致自建服务无法通过标准端口对外提供访问. 例如 DDNS 域名和 [ipv6]:80.

原理

在 IPv4 和 IPv6 过渡期间, 为了追求更佳的用户体验, 诞生了一种名为 Happy Eyeballs(快乐眼球) 的算法.

Happy Eyeballs(快乐眼球),亦被称作 Fast Fallback(快速回退),是一个由 IETF 发布的算法. 支持该算法的程序会尝试同时使用 IPv4 和 IPv6 协议进行连接, 若两者皆可连通则首选 IPv6, 使得同时支持 IPv4 和 IPv6 的双栈应用程序对用户的响应更加灵敏, 从而最大限度地减少 IPv6 连接或设置不完善的用户遇到的常见问题.

实际应用中 浏览器会优先发起 IPv6连接 , 一段时间后再发起 IPv4连接.

利用这个机制: 我们可以通过DDNS-Go , 将 AAAA解析 指向家宽IPv6地址; 将 A解析 指向 CDN . CDN回源家宽IPv6非标准端口( 如:81 ).

补充一个冷知识: 除了 Cloudflare(不含Saas) 这类需要 NS 接入的CDN, 绝大多数CDN可以直接通过 A/AAAA 解析指向CDN节点(Cloudflare Saas 优选的原理也是这个).

流量路径:

  • 公网环境:

    • 根据 Happy Eyeballs 同时向 IPv4/IPv6 建立连接, IPv6 端口不可达; IPv4 连接成功.
    • 用户 —IPv4—> CDN —非标端口—> 目标网站
  • 局域网环境:

    • 根据 Happy Eyeballs 同时向 IPv4/IPv6 建立连接, IPv6 连接成功.
    • 用户 —IPv6—> 目标网站

教程

教程使用: Debian 13 系统, Web服务器使用 Nginx 1.30.2 , DNS使用 腾讯云DNSPOD , CDN使用 阿里ESA , 演示中的网站访问域名为 vm.yppp.net .

准备

你需要:

  • 一个域名(有备案肯定更好了)
  • 一个支持IPv6回源的CDN(如果你的域名已经完成了ICP备案 可以使用免费的ESA/EO)(Cloudflare自己折腾吧 用Cloudflare Saas.)
  • 家宽有IPv6, 非标端口未封入站.

安装DDNS-Go

项目地址: jeessy2/ddns-go

下载可执行文件 一般情况下是 x86_64 .

可以使用以下命令查询CPU架构:

1
uname -m
1
2
wget https://github.com/jeessy2/ddns-go/releases/download/v6.17.1/ddns-go_6.17.1_linux_x86_64.tar.gz
tar -xvzf ddns-go_6.17.1_linux_x86_64.tar.gz

安装 DDNS-Go.

1
2
chmod +x ddns-go
./ddns-go -s install

配置DDNS-Go的部分参考 借助 DDNS-Go 使用家宽 IPv6 套 CDN 建站 .

教程的演示网站访问域名为 vm.yppp.net DDNS-Go的配置应该如下:

home-docker.yppp.net 用于CDN回源.

安装Nginx

国内镜像源的Nginx都很老, 我们需要手动安装最新版本的Nginx, 新版本修复了很多高位漏洞.

如果你可以流畅地访问 Nginx 官方源 可以直接使用官方文档中的教程: https://nginx.org/en/linux_packages.html#Debian.

多数情况下是无法访问; 或者访问速度极慢(比如我这才20Kb/s). 我们需要手动安装Nginx.

浏览器访问 https://nginx.org/packages/debian/pool/nginx/n/nginx/.

下载 nginx_1.30.2-1~bullseye_amd64.deb(amd64=x86_64 下载对应CPU架构的deb文件), 手动上传到服务器, 使用以下命令安装:

1
apt install ./nginx_1.30.2-1~bullseye_amd64.deb

配置Nginx

Nginx的配置文件在 /etc/nginx . 建议在 conf.d 文件夹中增加配置, 而不是修改 nginx.conf .

推荐一个SSH客户端 HexHub 可以很方便的远程编辑配置文件. 免费版功能足够使用.

创建一个新文件 我喜欢用 域名.conf 的格式来命名 粘贴以下内容 将 vm.yppp.net 修改为你实际的域名.

这份配置仅监听IPv6 并只提供HTTPS访问 CDN也将通过HTTPS回源. 80/443用于局域网访问; 8443用于CDN回源. 你需要配置 SSL证书, 可以使用 Certimate 实现自动化部署.

注: 你需要为 vm.yppp.net 也添加DDNS-Go 如果你有n个站点 在DDNS-Go中 应该有n+1个域名 一个域名用来CDN回源(这个域名应该只有IPv6解析) 一个域名用于访问.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
server {
listen [::]:80;
server_name vm.yppp.net;
return 301 https://$host$request_uri;
}

server {
listen [::]:443 ssl;
http2 on;
server_name vm.yppp.net;

ssl_certificate /etc/ssl/yppp.net/cert.crt;
ssl_certificate_key /etc/ssl/yppp.net/cert.key;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=15552000" always;

location / {
proxy_pass http://127.0.0.1:8001;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;

proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
# 如果需要WS 就取消注释

proxy_connect_timeout 30;
proxy_send_timeout 30;
proxy_read_timeout 30;
}
}

server {
listen [::]:8443 ssl;
http2 on;
server_name vm.yppp.net;

ssl_certificate /etc/ssl/yppp.net/cert.crt;
ssl_certificate_key /etc/ssl/yppp.net/cert.key;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=15552000" always;

set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;

location / {
proxy_pass http://127.0.0.1:8001;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;

proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";

proxy_connect_timeout 30;
proxy_send_timeout 30;
proxy_read_timeout 30;
}
}

配置CDN

以 阿里ESA 为例, 添加站点 vm.yppp.net , 记录类型选择 CNAME , 记录值/源站选择 域名 , 在这里输入 ddns-go 的域名

SSL/TLS -> 源站证书 中, 将 回源协议和端口 修改为HTTPS 端口修改为上方Nginx配置文件中的第三个Server块中的监听端口号

如果 CDN 中有其他网站 可以在 规则 -> 回源规则 中只修改需要特定端口回源的域名

DNS解析

DDNS-Go 阶段 已经添加了 vm.yppp.net的 AAAA解析, 因为CNAME解析会覆盖AAAA解析 只能添加A解析来指向CDN

使用 https://www.itdog.cn/ TCPing 测试 , 测试CDN服务商分配的CNAME域名 将 IP 结果作为 A 解析的记录值添加(随便添加几个就行了).

总结:

  1. ddns-go中添加两个域名 一个作为回源 一个作为访问域名 仅添加AAAA解析
  2. 配置完成Nginx和CDN后 手动添加A解析 指向CDN节点
  3. 手动在Nginx和CDN上配置证书(推荐 Certimate )\