前几天借着 Github 的学生优惠在 name.com 上嫖了一个域名,这个域名是带 SSL 的,而恰巧家里的主机因为 443 端口被封的缘故无法通过 Let’s Encrypt 获得证书,所以把这个域名给家里的主机升级 HTTPS 再合适不过了。
唯一的问题是 name.com 只提供静态 DNS 服务,而服务器放在家里自然是动态 IP 的。虽然 IP 不是经常换但是如果换了 IP 没有及时更新记录就会出问题,何况手动更新记录也有点烦。有两个方案:
- 我目前使用的是花生壳的 DDNS,这个的记录是动态更新的。我可以在新域名下面新建一条 CNAME 记录指向动态域名。这样的好处是省事,坏处是可能会增加 DNS 解析的时间,我目前还不清楚 HTTPS 要不要求 CNAME 指向的域名也有证书,如果要求的话这个方法就更不行了。
- name.com 作为一家比较大的域名商有自己的 API 以及完备的文档,可以自己写一个定时更新脚本来实现类似 DDNS 的功能。
经过考虑之后我选择后者。自己写的脚本如下:
#!/bin/sh
domain='<DOMAIN>'
credential='<ACCESS TOKEN>'
ttl=300
interval=10
echo 'Querying type A record ID...'
rec_id=$(curl -su $credential "https://api.name.com/v4/domains/${domain}/records" | jq '.records|map(select(.type=="A"))|.[0].id')
echo 'Found type A record ID:' $rec_id
while true; do
res_ip=$(host $domain | grep -ohP '\b(?:\d{1,3}\.){3}\d{1,3}\b')
real_ip=$(curl -s myip.ipip.net | grep -ohP '\b(?:\d{1,3}\.){3}\d{1,3}\b')
echo 'Resolution IP:' $res_ip 'Real IP:' $real_ip
if [[ $real_ip != $res_ip ]]; then
echo "Resolution mismatch! Updating record..."
req_data="{\"type\":\"A\",\"fqdn\":\"${domain}.\",\"answer\":\"${real_ip}\",\"ttl\":${ttl}}"
echo "Request data:" $(echo $req_data | jq '.')
req_res=$(curl -m 30 -su $credential "https://api.name.com/v4/domains/${domain}/records/${rec_id}" \
-X PUT -H 'Content-Type: application/json' --data $req_data | jq '.')
echo "Request result:" $req_res
sleep $ttl
fi
sleep $interval
done
其实很简单,主要分为一下几个部分:
- 记录 id 的获取。name.com 对每一个记录都分配了一个 id 以便于 API 操作,这个 id 在网站管理面板上是不可见的,因此需要在运行时查询,命令为
curl -su $credential "https://api.name.com/v4/domains/${domain}/records"
。查询之后需要解析 JSON,这里我使用的是jq
这个第三方 JSON parser。 - 当前 DNS 解析的 IP。这个使用
host
结合grep
提取 IP 字符串即可。 - 获取本机真实 IP。这个接口就多了,我这里用的是
myip.ipip.net
的接口。据我所知还有接口是直接返回 IP 字符串的,还可以后处理的功夫。 - 如果解析 IP 和真实 IP 不符,那就调用 API 更新记录。注意在更新完之后最好等待 TTL 的时间以避免更新生效前多次更新。
写完这个脚本再写一个配套的 systemd service
file,然后 systemctl
挂在后台运行就好了。运行到现在效果非常好。如果有 name.com 的域名同时也有类似需求的或许可以参考一下。