使用 Linode VPS 部署自己的动态域名

最近发现宽带速度提升较多,准备在家里部署个私用的服务器来同步和备份文件等。最近正在想办法弄个可访问的公网IP,然后配置动态域名来访问这个私有服务器。可是 DDNS 服务现在很少有免费的了,即使有,也有很多限制条件,比如 no-ip 的每月签到等。反正有 VPS,配置自己的动态域名好像还不错。用 Linode VPS 放这个网站也快有10年了。打算就这样用 Linode 的 DNS Manager 配合其 API 来创建自己的 DDNS。

GG了一下,发现很早就有人这样做了。所以可以参照这里的步骤来实现。虽然 Linode API 已经更新换代了,发现这篇几年前的文章里的操作还是可以原样使用。

配置子域名

当然是先要确定使用 Linode 的域名管理器来解析自己的域名。到 Linode 的 DNS Manager 中为自己要配置的动态子域名创建一个记录。到「A/AAAA Records」那里点击「Add a new A/AAAA record」来创建一个新的记录,填写子域名(比如 test.cnzhx.net);IP 地址刚开始可以直接使用自己 VPS 的 IP 地址;然后 TTL 选最短的那个(300s)。保存即可。

这个配置需要过几分钟才会生效,才能从 NS 那里解析出来。在此之前的 nslookup 查询会返回「NXDOMAIN」错误。

Linode API 权限设置

创建一个新用户

到 Linode 控制面板的「Account」页面创建一个新用户,例如「ddns_test」,设定密码和邮箱地址,然后下面的「Restricted User」那里点选「Yes – this user can only do what I specify」这样来限制该用户的权限。检查无误就单击下面的「Save changes」。

下面就是勾选该用户拥有的权限了。到「DNS Zone Grants」那里勾选对应域名「cnzhx.net」的「All Privs」。这样只允许该用户修改该域名的配置。保存设置即可。

当然这些参数回头都还可以再修改。

创建 API Key

特别提醒:任何拥有该 Key 的人或者网络机器人都可以随意执行该用户有权限执行的一切操作!因此需要特别注意该字符串的安全。所以进行这些操作的浏览器要安全(如果不知道哪些浏览器可能不安全,那还是不要往下进行这样的任务了。当然执行该任务的计算机也需要是安全的。安装了那些披着羊皮的所谓安全软件、杀毒软件、优化软件、输入法软件和音视频软件的童鞋,建议就此打住,不要继续了。

现在使用新创建的用户登录 Linode Manager,到「my profile」 中创建该用户的 API Key。这里需要再次输入用户密码进行验证。然后切换到 API Keys 来创建一个新的。输入标签 Label,比如 ddns,以方便辨认。确保过期时间 Expires 设置为 Never 这样才能永不过期。单击「Create API Key」之后新的密钥就创建好了。

此时页面上会有提示,让保存好这个新创建的密钥,因为不会再次显示它了。如果没保存,那就需要重新创建一个。

Please record your new key: gxasxtwBV5MgtqQjdl8NW3QQpvN29ePJJ201xYuQptNvTdY3bfmWPeMA1pP7SaNts - it will not be shown again.

获取域名及其 Resource ID

下面就用刚才创建的密钥来获取需要远程更新的域名及其对应的资源键值「Resource ID」。用这些信息可以创建用于更新资源的链接地址 URL。访问该地址可以在 IP 发生变化的时候更新域名记录。

下面的链接 URL 中的「my_api_key」需要对应替换为刚才保存的那一串字符(key)。然后将修改后的链接粘贴到浏览器地址栏访问就能看到返回的数据。返回的数据默认是显示 JSON 格式的,看着很明了。特别注意,下面的链接都应该使用 https (加密连接) 发起。当然也不是必须用浏览器(免得被恶意软件知道了我们的「my_api_key」),还可以用 wget 或者 curl 来通过命令行访问所列网址,在本小节的结尾有示例。

查看域名列表,

https://api.linode.com/?api_key=my_api_key&api_action=domain.list

新版 APi v4 推荐使用 curl 类似的方式,但是是使用 Token Auth 而不是上面的 API Key。但是这种方式虽然安全可是用起来太麻烦,形式如下,

curl -H "Authorization: Bearer $TOKEN" \
https://api.linode.com/v4/domains

为了简单,就暂时不用这个方法了,下面还是用前面给出的 API Key 来操作。再次提醒,这个 API Key 千万不要泄漏。

废话少说,继续。在浏览器中访问前面的 URL 后返回的数据中我们需要其中的「DOMAINID」的值(一串数字)。将该值继续替换到下面的链接中的「my_domain_id」可以再得到需要的「Resource ID」(另一串数字)。当然「my_api_key」还是需要对应替换为刚才保存的那一串字符(key)。

https://api.linode.com/?api_key=my_api_key&api_action=domain.resource.list&domainid=my_domain_id

从返回结果中定位到刚才创建的那个子域名的「RESOURCEID」。其下应该有「TYPE “A”」和「NAME “test”」字样。这里的 test 就是我的子域名 test.cnzhx.net 的名字(主域名省略了)。

现在有了 API key、Domain ID 和 Resource ID (链接 URL 中分别以「my_api_key」、「my_domain_id」和「my_resource_id」替换),我们就可以创建更新该记录 IP 地址的链接 URL 了,格式(新版的话参见这里)为,

https://api.linode.com/?api_key=my_api_key&api_action=domain.resource.update&domainid=my_domain_id&resourceid=my_resource_id&target=[remote_addr]

其中「[remote_addr]」就是我们需要更新的新的 IP 地址。但是这里并不需要我们手动填入新的 IP。当然也可以自己填入 IP 地址。该字符串是一个动态占位符,Linode API 服务器会自动将它替换成发起该访问的设备对应的公网 IP 地址。只需要在动态域名需要指向的机器上访问该链接 URL,Linode DNS Manager 就会自动将这台机器的公网 IP 更新到域名记录中。因此,一定要用正确的机器来访问该 URL,而且切记不能走代理

访问的方式有很多,从浏览器地址栏来访问是最费事的。Linux 系统中可以通过命令行这么访问,

wget --inet4-only -qO --no-check-certificate https://api.linode.com/?api_key=my_api_key&api_action=domain.resource.update&domainid=my_domain_id&resourceid=my_resource_id&target=[remote_addr]

但是 wget 没法检查网站证书,显然不够安全。那么还是用 curl 吧。如下,

curl -s https://api.linode.com/?api_key=my_api_key\&api_action=domain.resource.update\&domainid=my_domain_id\&resourceid=my_resource_id\&target=[remote_addr]

Windows 的话,curl 也有 Windows 版本的(curl 下载)。参考 CSDN 上的解说。(2020.07.11 更新之前的错误)需要特别注意的是,使用 wget 或者 curl 的时候,网址中的 & 符号需要转义(在其前面加上 \ 符号)。

创建计划任务自动更新 IP 记录

前面的例子中用的是旧版的 Linode API。新版(v4)Linode API 与往前的有较大区别。不过旧版的现在仍然是可以用的,而且方便通过浏览器地址栏来直接访问。这里不需要通过浏览器来完成,而是通过 Linux 命令行来执行,就可以使用新版的格式来写 bash 脚本了。最重要的是,使用命令行来完成比较容易做成定时执行的计划任务。选这种方式是因为可以直接放到路由器的 AdvancedTomato 固件里的计划任务中定期执行(已经不记得了,好像这款路由器官方固件也有计划任务这样的功能的)。

如果有多个记录需要更新,可以做个 bash 脚本。当然一个记录也可以做的。参考这里。我这里就是要做个脚本,顺便加点儿判断,

  • 先查询当前 IP,与上次更新的 IP 不一致的时候才通过 API 更新域名记录,否则不更新。

流程是,

  • 定义需要的一些变量,比如「my_api_key」等等;
  • 查询当前的 IP 地址;
  • 比较当前 IP 与上次的 IP;
  • 根据比较结果选择更新域名记录。

脚本如下(仿自 mhussain),

#!/bin/sh
# modified from https://gist.github.com/mhussain/2634050, which is
# modified by jfro from http://www.cnysupport.com/index.php/linode-dynamic-dns-ddns-update-script
# Uses curl to be compatible with machines that don't have wget by default

# Personal configuration
LINODE_API_KEY=my_api_key
DOMAIN_ID=DOMAINID
RESOURCE_ID=RESOURCEID
WAN_IP_FILE="/opt/wan_ip.txt" # 我的路由器上的存储位置
UPDATING_LOG_FILE="/dev/null" # 暂时把更新记录直接丢弃

# main script
# 因为查询 IP 的服务器经常连不上,所以这里加了两个备用的
WAN_IP=`curl -4 -s -m 5 icanhazip.com`
if [ -z $WAN_IP ] ; then
    WAN_IP=`curl -4 -s -m 5 api.ip.la`
fi
if [ -z $WAN_IP ] ; then
    WAN_IP=`curl -4 -s -m 5 ipecho.net/plain`
fi
if [ -z $WAN_IP ] ; then
    WAN_IP=`curl -4 -s -m 5 ifconfig.me/ip`
fi

if [ ! -z $WAN_IP ] ; then
    if [ -f $WAN_IP_FILE ]; then
        OLD_WAN_IP=`cat $WAN_IP_FILE`
    else
        echo "No file, need IP"
        OLD_WAN_IP="127.0.0.1"
        touch $WAN_IP_FILE
        echo $OLD_WAN_IP > $WAN_IP_FILE
    fi

    if [ "$WAN_IP" = "$OLD_WAN_IP" ]; then
        echo "IP Unchanged"
    else
        echo $WAN_IP > $WAN_IP_FILE
        echo "Updating DNS to $WAN_IP"
        curl -s https://api.linode.com/?api_key="$LINODE_API_KEY"\&api_action=domain.resource.update\&DomainID="$DOMAIN_ID"\&ResourceID="$RESOURCE_ID"\&Target="$WAN_IP" > $UPDATING_LOG_FILE
    fi
fi

至于在 AdvancedTomato 里增加定时执行,可以参考这里的例子。一般的 Linux 和 Windows 系统也就不需要举例子了,网上到处都是计划任务的例子。

后来发现联通已经开始支持 IPv6 了。上面的代码有个问题就是会根据访问来源返回 IPv4 或者 IPv6 的 IP 地址。这样一来,就会将 IPv6 的地址更新到 Linode Manager。这并无必要且会增加麻烦。所以在上面的代码中给 curl 增加了 -4 参数以强制使用 IPv4 协议。

增加一个更新 IP 记录的方法 2020.07.11

最近发现国内访问 Linode 的控制面板和域名服务器都存在一些断断续续的问题。这种情况对其它的服务器供应商应该也可能存在。为了免除烦恼,又琢磨了一个新的思路来动态的更新域名记录中的路由器 IP 地址。

思路其实也很简单,分为两个部分,

  1. 家中路由器或者网关上对当前分配的 IP 地址做个记录,比如上面提到的 "/opt/wan_ip.txt" 文件。在每次路由器重启,或者是检测到公网 IP 地址发生变化时(通过比对文件中记录的地址),通过命令行(curl)访问服务器上的一个 PHP 脚本文件来告知服务器当前有效的 IP 地址。该 PHP 脚本将此 IP 地址保存到一个文件中,例如 "/srv/www/html/cnzhxnet/homeip.txt"。只要服务器的 IP 地址没有被屏蔽,这个更新还是很可靠的。
  2. 服务器上同样运行一个定时任务,定期检查"/srv/www/html/cnzhxnet/homeip.txt"文件,并在服务器上访问 VPS 供应商的域名解析服务器,作比对,不一致则在服务器上通过 curl 来访问前面构建的更新域名记录的网址。

考虑到网络环境越来越差,这种两步法反而比家里路由器上直接更新域名解析要来得可靠。

脚本非常简单,暂时就不放上来了。如果有朋友需要,请在评论区留言。

更新使用 Linode API v4 2023.07.10

只是更改了 curl 的方法。见新博文

后记

目前家里的宽带虽然弄到公网 IP 了,但是还没办法完全控制,不能修改光猫的端口转发之类的。正在等我的 TP-Link 的 TL-GP110,希望到手之后能搞定联通的认证。然后可以用路由器来拨号了。到时候剩下的就简单了。

暂时测试发现的问题是 Linode 的域名管理器没法自由调整域名记录的 TTL(多种),所以只能选择那个 5 分钟。不知道是不是因为这个,这边更新 IP 好一会儿(还没总结出具体时间)之后才能从 nslookup 中查询到更新后的 IP。©

本文发表于水景一页。永久链接:<https://cnzhx.net/blog/ddns-with-linode-vps/>。转载请保留此信息及相应链接。

7 条关于 “使用 Linode VPS 部署自己的动态域名” 的评论

  1. 引用通告: 河南联通光纤宽带换自己的光猫且联通已支持 IPv6 | 水景一页

      • 是的,就是“增加一个更新 IP 记录的方法 2020.07.11”里的,我是PHP小白

        • 那我贴到这里吧,非常简陋且没有做安全检查:

          <?php
          date_default_timezone_set("Asia/Shanghai");
          file_put_contents("homeip.txt", "Updated at " . date("Y-m-d h:i:sa") . "\r\n" . file_get_contents("php://input"));
          ?>
          

          homeip.txt 会保存到网站根目录。
          假设 PHP 文件存放在网站根目录,名称是 homeip.php,在家中路由器上使用 curl,

          curl -d "ip=$WAN_IP" -X POST yourdomain.com/homeip.php

          其中 $WAN_IP 是公网 IP。服务器上的这个 PHP 脚本就能知道 IP 地址并保存到 homeip.txt 文件中。

  2. 引用通告: 河南联通光纤宽带换自己的光猫且联通已支持 IPv6 | 水景一页

雁过留声,人过留名

您的电子邮箱地址不会被公开。 必填项已用 * 标注

特别提示:与当前文章主题无关的讨论相关但需要较多讨论求助信息请发布到水景一页讨论区的相应版块,谢谢您的理解与合作!请参考本站互助指南
您可以在评论中使用如下的 HTML 标记来辅助表达: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>