IPv6-only VPS通过NAT64访问IPv4域名

背景

最近从德国euserv买了一台IPv6-only的VPS,定期免费续期就能一直使用。

但IPv4的域名就没法访问了。

所以看了下DNS64和NAT64,实现了访问IPv4的需求。

理论知识

IPv6提出后,矛盾在于:

  • 一方面我们希望整个互联网底层是基于IPv6构建的,增量也尽量接入IPv6。
  • 另一方面,原有的很多IPv4地址无法通过IPv6访问,还是要保留、新增IPv4的基础设施。

NAT64

由于IPv6地址超级多,自然而然的,我们为什么不能将IPv6地址的一部分“抠出来”,映射到IPv4网络呢?

比如192.0.2.1地址,映射到地址64:ff9b::192.0.2.1

然后所有到达64:ff9b::192.0.2.1的请求,我们都原样转发给192.0.2.1,这样就能实现IPv6访问IPv4了。

DNS64

上面我们解决了IP地址映射的问题,那么访问某一个IPv6网站的时候,DNS返回的仍然是IPv4地址,很多域名还是无法通过IPv6访问。

问题只解决了一半?

很简单,我们只需要在DNS上替换掉这个IPv4地址就可以了。这就叫DNS64服务。

一个例子

我们以ip4only.me为例,看看如何通过DNS64和NAT64服务进行访问:

  1. 发送AAAA解析请求到DNS64服务器,DNS64服务器先是获取ip4only.me的IPv4地址,然后通过转换规则转换为NAT64的IPv6地址。
  2. 我们的请求通过IPv6网络到达这个NAT64 IPv6地址的时候,NAT64服务会把这个请求通过IPv4网络发送到真正的网站服务器。

当然,缺点也是有的,那就是网络速度会受限于NAT64服务器的速度

但好处是,构建了一个原生的IPv6网络,同时兼容了IPv4网络,我们在新增的网络设备中,就无需部署IPv4设施了。

好了,理论知识OK了,我们开始实战。

手动测试下DNS64&NAT64

nat64.net 提供了免费的NAT64服务,我们先尝试解析一个IPv4域名:

1
2
3
4
5
6
$ dig @2a00:1098:2b::1 ip4only.me aaaa
....
;; ANSWER SECTION:
ip4only.me. 10800 IN AAAA 2a00:1098:2b::1:1799:84e
ip4only.me. 10800 IN AAAA 2a00:1098:2c::5:1799:84e
ip4only.me. 10800 IN AAAA 2a01:4f8:c2c:123f:64:5:1799:84e

其实这个域名是没有AAAA记录的,也就是说没有对应的IPv6地址。

上面的IPv6地址,仅仅是nat64.net在自己的地址范围内做了一个IPv4到IPv6地址的映射的结果。

那我们任选一个IP访问看看:

1
2
3
4
5
6
7
8
$ curl -v 'https://ip4only.me/api/' \
--resolve 'ip4only.me:443:[2a00:1098:2b::1:1799:84e]'
* Added ip4only.me:443:[2a00:1098:2b::1:1799:84e] to DNS cache
* Hostname ip4only.me was found in DNS cache
* Trying [2a00:1098:2b::1:1799:84e]:443...
* Connected to ip4only.me (2a00:1098:2b::1:1799:84e) port 443 (#0)
...
IPv4,46.235.231.114,v1.1,,,See http://ip6.me/docs/ for api documentation

可以看到,我们通过这个IPv6地址访问的时候,网站仍然认为我们是通过IPv4访问的。

那么,对于支持IPv4和IPv6的双栈网站域名,是什么表现呢,我们来试一试:

1
2
3
4
$ dig @2a00:1098:2b::1 ip6.me aaaa
ip6.me. 10800 IN AAAA 2001:4810:0:3::71
$ dig @dns.google ip6.me aaaa
ip6.me. 10800 IN AAAA 2001:4810:0:3::71

对于双栈网站,nat64.net解析的结果和google dns一样,都是本来的IPv6地址,没有经过代理。

OK,nat64.net工作的很完美,那么如何用上呢?总不能每次都要改命令吧。

如何让IPv6-only的VPS用上呢?

很简单,对于通过域名访问的服务,只需要将DNS服务器替换为nat64.net的DNS服务就可以了。

比如以Debian GNU/Linux 12 (bookworm)为例,resolv.conf配置由systemd-resolved管理,我们只需要修改systemd-resolved的配置就可以了:

/etc/systemd/resolved.conf中指定:

1
2
[Resolve]
DNS=2a00:1098:2b::1 2a00:1098:2c::1

然后通过systemctl restart systemd-resolved应用变更。

最后测试下:

1
2
3
4
5
6
7
8
9
10
$ curl -6 -v 'https://ip4only.me/api/'
* Trying [2a00:1098:2b::1:1799:84e]:443...
* Connected to ip4only.me (2a00:1098:2b::1:1799:84e) port 443 (#0)
...
IPv4,46.235.231.114,v1.1,,,See http://ip6.me/docs/ for api documentation
$ curl -6 -v 'https://ip6.me/api/'
* Trying [2001:4810:0:3::71]:443...
* Connected to ip6.me (2001:4810:0:3::71) port 443 (#0)
...
IPv6,2b00:180:6:1::2e4,v1.1,,,See http://ip6.me/docs/ for api documentation

另外,还有一个bonus:

这时候,你可以在VPS上访问GitHub了(不管是HTTPS方式,还是SSH方式)。

备注

已经有电信运营商开始用类似的方式,向IPv6迁移了

  1. Telstra分配给客户端一个正常的IPv6地址,加上一个mock的IPv4地址192.0.0.8
  2. 访问IPv4网站时,DNS返回一个NAT64地址,通过这个地址,可以访问IPv4网站

来源:https://www.zhihu.com/question/577859676/answer/3245843113

如何使用新版本技术,替换老版本技术

不管是IPv6(尝试)替换IPv4,还是Golang中,math/rand/v2取代math/rand,都会遇到新版本的优点和旧版本的兼容阻力两个冲突。

这时候,Golang官方整理了新旧版本演进的原则(有修改):

  1. v1 和 v2 可以在同一个系统中共存,这对于逐步转换到新版本至关重要。
  2. 所有更改都必须以尊重现有用法和用户为基础:我们绝不能引入不必要的变化,无论是对现有软件包的不必要更改,还是必须学习的全新软件包。
  3. v2版本v1用户抛在后面,完全不管了。在理想情况下,v2版本应该能做v1版本能做的一切事情。当v2发布时,v1软件包应被重写为v2的薄封装。这将确保现有的v1版本继续受益于v2中的错误修复和性能优化,也保证了v1的兼容性。

Golang的math/rand/v2,和本文中的NAT64,都是这个原则的例子。

这种新旧版本演进的方式,能够在最大程度上避免技术债,能够比较优雅的迁移到新版本。

当然,这种方式也会有薄封装本身稳定性的问题。

anyway,这是另外一个话题了,改天聊。

参考资料

  1. nat64.net提供了免费的NAT64服务👍
  2. DNS64由RFC 6147标准化
  3. NAT64由RFC 6146标准化
  4. nat64.xyz提供了一个免费的NAT64服务列表
  5. Golang团队提的,新旧版本演进的原则

IPv6-only VPS通过NAT64访问IPv4域名

https://robberphex.com/access-IPv6-vps-via-dns64-and-nat64/

作者

Robert Lu

发布于

2024-12-14

许可协议

评论