OVERSEER.LOG
READING MODE
← 返回日志

// NETWORK / DNS

动态 IPv6 下的 SSH DDNS 实践

运营商给的是会变的前缀,而 SSH 需要一个稳定的名字。 用一个一分钟一次的 cron、一条最小的 DNS 权限和一个只改 AAAA 的脚本, 就能把入口钉在域名上。

00 / 问题在哪

家用宽带的 IPv4 通常不是固定的,IPv6 前缀也会在一段时间后变化。 如果直接把地址写进 SSH 客户端配置,前缀一变就得去路由器或 VPS 控制台查新地址。 更麻烦的是:你往往是在连不上的时候才发现它变了。

目标很简单:让 ssh.example.com 始终指向当前机器的全局 IPv6, 客户端只记域名,不记地址。

01 / 记录类型与代理状态

SSH 不是 HTTP,不能走 Cloudflare 的七层代理。因此这条记录必须是 AAAA + DNS only(灰云),让客户端直接连到源站地址。

如果开了橙云,Cloudflare 只会代理特定端口,SSH 连接会直接失败; 而且 DNS 返回的也不是你的服务器地址。

02 / 准备一个最小权限的 Token

在 Cloudflare 后台创建一个 API Token,权限只给 Zone → DNS → Edit,范围只限这个域名。不要用全局 API Key。

Token 和 Zone ID 放在一个只有 root 可读的环境文件里:

CF_API_TOKEN=your-scoped-token
CF_ZONE_ID=your-zone-id
SSH_HOST=ssh.example.com
IFACE=eth0
chmod 600 /etc/cloudflare/ssh-dns.env
chown root:root /etc/cloudflare/ssh-dns.env

03 / 更新脚本

脚本只做三件事:读当前 IPv6、读 DNS 里的旧值、不同才更新。 这样既能避免无意义的 API 调用,也不会因为日志刷屏而掩盖真正的问题。

#!/bin/sh
set -eu

. /etc/cloudflare/ssh-dns.env

IP=$(ip -6 addr show dev "$IFACE" scope global \
  | awk '/inet6/ {print $2}' | cut -d/ -f1 | head -n1)

[ -n "$IP" ] || exit 1

API="https://api.cloudflare.com/client/v4"
AUTH="Authorization: Bearer $CF_API_TOKEN"

RECORD=$(curl -sS -H "$AUTH" \
  "$API/zones/$CF_ZONE_ID/dns_records?type=AAAA&name=$SSH_HOST" \
  | jq -r '.result[0] // empty')

CURRENT=$(printf '%s' "$RECORD" | jq -r '.content // empty')
ID=$(printf '%s' "$RECORD" | jq -r '.id // empty')

[ "$CURRENT" = "$IP" ] && exit 0
[ -n "$ID" ] || exit 1

curl -sS -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
  "$API/zones/$CF_ZONE_ID/dns_records/$ID" \
  --data "{\"type\":\"AAAA\",\"name\":\"$SSH_HOST\",\"content\":\"$IP\",\"ttl\":60,\"proxied\":false}" \
  >/dev/null

echo "$(date -Iseconds) updated $SSH_HOST -> $IP"

依赖 jq 来解析 JSON。Alpine 上执行 apk add jq curl 即可。 用 sed 硬抠 JSON 也能跑,但字段顺序一变就会坏,不值得省这个包。

04 / 一分钟一次的 cron

* * * * * /usr/local/sbin/cf-aaaa-update.sh >> /var/log/cf-aaaa-update.log 2>&1

脚本在地址没变时直接退出,所以每分钟执行一次不会产生太多请求。 如果担心日志无限增长,可以用 logrotate 或定期截断。

05 / 验证

先在服务器上手动跑一次,再检查 DNS:

/usr/local/sbin/cf-aaaa-update.sh
tail -20 /var/log/cf-aaaa-update.log
nslookup ssh.example.com 1.1.1.1
Resolve-DnsName ssh.example.com -Type AAAA -Server 1.1.1.1
ssh -v [email protected]

前缀变化后,通常 1 到 2 分钟就能生效。如果域名解析正确但 SSH 仍超时, 先检查客户端有没有 IPv6,再检查服务商防火墙是否放行 22。

06 / 安全提醒

这套东西不复杂,但很依赖细节:记录类型、代理状态、IPv6 地址来源、Token 权限, 任何一个错了都可能表现为「域名能查,但连不上」。