ZZ.NIC 一直计划为 ZZ.AC 公益域名持有者提供免费 Linux 主机空间,用作研究和练习 Linux 相关技术。之前曾经基于 RackNerd 的 VPS 构建过一套纯 IPv6 mtxt环境的服务器1。但该服务未能实现自助开通,只能手工操作,而且该 VPS 只有单核,性能太弱,到期后就没有再续费了。最近基于 Oracle 的 ARM 免费机型,重新打造了一套环境,而且实现了自助开通,即开即用。本文主要分享该服务的特色、申请方法和基本的玩法,供各位爱好者参考。

现在市面上也有一些免费资源,比如 serv00。但免费最大的问题是权责不对等,只享受权利却不用承担责任。这必然会导致服务被滥用。滥用得多了就可能被封禁,那么正规的用户就更不敢使用。久而久之就只剩下灰黑产了。

防止滥用

ZZ.NIC 向爱好者提供的所有资源都只能用于学习和研究目的。主机服务也不例外。那怎样才能减少滥用呢?我在上一版服务中尝试用纯 IPv6 网络环境来提升使用门槛。但并没有真正解决问题。因为滥用的形式有很多,包括但不仅限于以下几种:

  • 通过 web 服务发布违法内容
  • 发送垃圾电子邮件
  • 部署资源下载站,恶意消耗网络流量
  • 扫描他人主机的端口
  • 恶意抓取他人网站内容
  • 用作翻墙工具

以上种种行为都可能导致主机 IP 被封,从而影响所有用户。新的方案应该尽力避免这些问题。显然,纯 IPv6 网络也面临同样的问题。后来我想到了解决办法,使用 HTTP 代理!

每个用户都使用独立的网络空间。该空间中设置 veth 的一端,并且只配置 IPv6 的本地链路地址,假定为 fe80::2/64;它的对端设备分配到宿主机默认网络空间,指定本地链路地址为 fe80::1/64。用户网络和宿主机只能通过本地链路地址通信。这样用户直接访问外部网络,更不要说是扫描他人主机了。

如果要下载资源,比如访问 NPM 或者 GitHub,需要通过宿主机的 HTTP 代理来发起请求。该代理不但可以限制目标域名,还能记录每个用户访问过的域名。如果有用户恶意抓取他人内容,则可以秋后算账。

如果用户想对外发布内容该当如何呢?使用反向代理。宿主机上会运行 Caddy 服务,自动化处理 TLS 证书相关逻辑,并把不同域名的 HTTP 请求转发给对应用户的 veth 链路。

此外,还需要调整 SSH 的配置。对于所有共享用户都禁止端口转发,避免被用作梯子。另外还需要在服务器端禁用密码登录,尽量降低账号被爆破的风险。

以上便的新公益 Linux 主机的主要特色限制。那应该怎样申请呢?

申请过程

首先,你得申请到一枚 ZZ.AC 后缀域名。申请到心仪的域名之后,你需要设置几条特殊的 DNS 记录。

假如你的域名是 example.zz.ac,你想通过 lab.example.zz.ac 来该问公益主机。这里的 lab 子域名可以使用任意你喜欢的名字。

Warning

你的 ZZ.AC 主域名部分不能超过12个字符,Linux 对网卡设备的名称有限制。

你得给 lab.example.zz.ac 同时添加 IPv4 和 IPv6 记录,指向 h1.lab.zz.ac 这台主机。

最简单的办法是使用 CNAME 记录:

lab 5m CNAME h1.lab.zz.ac.

当然也可以自行指定 A/AAAA 记录:

lab 5m A    146.235.195.242
lab 5m AAAA 2603:c024:c00c:9800:911a:29ef:738b:c2a6

然后你需要通过 DNS 来发布自己的 SSH 公钥,而且必须是 ED25519 格式的。当前业界还没有通行规范来发布公钥,我就自定义了_sshpk标签,并通过 TXT 记录来发布:

_sshpk.lab 5m TXT "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILpSjbp5HFcYg82NMJqiaOKwBvSwpRkavZjHwPz6rCmJ"

DNS 记录保存之后需要等五到十分钟生效。ZZ.AC 域的权威解析服务有使用 HE.NET 的 DNS 服务。它的 anycast 同步数据延迟较大,我在犹豫要不要去掉🤦‍♂️

DNS 同步之后就可以自助开通账号了。

请打开域名管理界面 https://nic.zz.ac/#/zone 登录后在最底下展开「高级特性」,在表单中输入 lab 子域名。然后提交。如果一切正常,你会看到如下输出:

== Querying SSH public key from TXT _sshpk.lab.example.zz.ac
== Checking type of ssh public key
== Checking A record for lab.example.zz.ac
== Checking AAAA record for lab.example.zz.ac
== Checking if account example exists
== Creating account example
== Enable login linter for example
✨ You can login via ssh example@lab.example.zz.ac now 🥂

到这里你就可以通过ssh example@lab.example.zz.ac登录了。

这里注意,用户名是你的 ZZ.AC 主域名。比如 lab.example.zz.ac 的登录名只能是 example,系统自动指定,无法自定义。

基本玩法

登录之后查看网络信息:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 4e:37:c2:a9:f6:66 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::2/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::4c37:c2ff:fea9:f666/64 scope link
       valid_lft forever preferred_lft forever

这里的 eth0@if23 就是前面说的 veth 链路的用户端设备。注意它已经设置了 fe80::2/64 本地链接地址。登录之后无法直接访问外部网络环境。但你用 curl 访问 github.com 就会发现是通的。这是因为系统会自动设置 HTTP 代理环境变量:

$ export|grep http
declare -x http_proxy="http://[fe80::1%eth0]:8888"
declare -x https_proxy="http://[fe80::1%eth0]:8888"

非 HTTP 协议无法通信!

如果你做个网站对外提供 web 内容,就需要自己运行 http 服务。我们以 python3 内置的 http.server 包为例演示用法。

先准备 web 内容:

mkdir web
echo "Hello, World!" > web/index.html

然后运行 http.server

cd web
python3 -m http.server -b :: 8080

注意,这里使用-b ::来监听所有 IPv6 地址,包括我们自己的 fe80::2/64。端口只能固定为 8080。

到此,web 服务就准备好了。打开浏览器访问https://lab.example.zz.ac就会看到熟悉的”Hello, World!“。宿主机上的 Caddy 会自动申请 SSL 证书,并将请求转发到对应的用户网络空间。

为了让 web 服务在 ssh 退出后继续运行,你需要使用创建用户态的 systemd 服务。

服务路径为 ~/.config/systemd/user/web.service,内容如下:

[Unit]
Description=Web Service

[Service]
WorkingDirectory=%h/www
ExecStart=/usr/bin/python3 -m http.server -b :: 8080

[Install]
WantedBy=default.target

保存之后通过 systemctl 启动服务:

systemctl --user enable --now web.service

这样你的服务就能在后台自动运行了。而且宿主机重启之后你的服务也会自动启动。

高级玩法

以上展示了公益主机的基本玩法。如果觉得不过瘾,还可以自己折腾高级玩法。

虽然申请到的用户没有 root 权限,不能直接在系统层面安装软件。用户还是可以通过 Linux 版的 Homebrew或者 Nix 包管理器在自己的家目录安装需要的工具,然后通过 systemd 来管理。

✏️Note

实践发现 Linux 版的 Homebrew 不太行。虽然它支持以非 root 账号运行,但这需要修改软件仓库的默认路径。此路径一旦被修改,很多软件都得从源码安装,整个过程会比较漫长。而 Nix 原生支持多用户使用,不同用户可以共用相同的软件包和缓存,建议使用。

我在宿机上部署了 podman,这是 docker 的平替工具,不需要 root 权限就能运行,命令参数几乎跟 docker 一样,大家可以玩玩。

💡Tip

虽然 pacman 原生支持 rootless 模式,但每个用户运行 podman 的镜像缓存是独立的。不同用户下载同一个镜像会在系统上保存多份,浪费磁盘空间。因此建议大家使用 Nix。

Nix 可以参考这篇文章 https://bosh.zz.ac/posts/3857780209.html

以上就是本文的主要内容。心动不如行动,赶紧注册 ZZ.AC 域名2玩起来吧。


  1. ../unix/multi-tenant-linux.html↩︎

  2. ../dns/zz.html↩︎