自交大毕业之后便无比怀念交大的静态 IP,拿来作为服务器自然是再合适不过。 来浦东这边租房后用的是房东免费给的联通宽带,虽然只有 10 Mb 带宽,但网页浏览和一路 1080p YouTube 视频还是比较流畅的。ISP 默认分配的是运营商内网地址,想把家里的服务器作为公网访问的服务器就非常麻烦了。

人工投诉索要公网 IP

某个周末的下午试着给联通打了个投诉电话说转人工宽带服务,说明要公网地址,客服问了下账户信息就答复说24小时内处理好并回电,约摸两个小时后有个工程师就来电说已经处理好,请重启路由器。重启路由器后发现联通诚不欺我,全程下来十分顺畅,原以为要说一堆理由并且还有可能不给开通公网地址,没想到联通这么爽快,赞!

光猫用户

需要注意的是使用了光猫的客户,即使申请获得公网地址,这个地址也只存在于光猫的 WAN 口,内网服务器还是无法发挥作用,所以还需要先破解光猫获得超级用户权限以设置 DMZ 和端口映射等。以中兴的 F607 联通版为例,浏览器中输入 http://192.168.1.1/cu.html, 用户名密码可能为如下组合:

  • CUAdmin/CUAdmin
  • admin/admin
  • unicomadmin/adminunicom

网络拓扑

一般情况下服务器网络入口使用防火墙还是很有必要的,获取光猫超级用户权限后虽然可以设置端口映射,但是受制于光猫的有线网口数和硬件布局,使用起来多少有些不便。考虑到连接光猫的至少有一台无线路由器,所以可以在光猫中固定这台无线路由器 WAN 口的 IP,并对其 DMZ, 这样你的路由器就相当于外接广域网了,后面对路由器下的服务器做端口映射就比较方便了。端口映射在有些路由器固件中也称为虚拟服务器,不同路由器的称呼不太一样。

DDNS

有了公网 IP 还只是对外服务的第一步,ISP 给你分配公网 IP,并不代表你每次重新拨号获得的 IP 不变,所以你还需要 DDNS 来动态获知这一 IP,典型的 DDNS 服务提供商有花生壳,但是这货并不太稳定,某些固件在路由器界面开启 DDNS 后还会引发路由器工作异常。

DNSPod DDNS

有自己域名的基本都知道这家 DNSPod,提供的 API 十分丰富,服务也是比较稳定的。下面是我参考了 leeyiw/dnspod_ddnsPython dynamic DNSPod DNS Script 改进的 DNSPod DDNS Python Script

{% gist id="billryan/239778f0821937939c8c140bdddd6840”,file="dnspod_ddns.py” %}{% endgist %}

以上脚本依赖第三方库 requests, 使用 pip install requests 安装。 DNSPod 的 ID 和 Token 通过 API 鉴权方式升级为 Token 获得,接着将 domainsub_domain 替换为你想要的域名,接下来运行即可。如果想同时使用花生壳的 DDNS 服务,可以在自己的域名处添加一条 CNAME 记录指向花生壳那个奇奇怪怪的域名。

基本工作流程就是若 sub_domain 之前不存在则会调用 create_record 为你创建新的子域名,若已存在则会通过 get_record_list 获取 record_id 和已设定的 IP. 随后通过与 DNSPod 创建 socket 获取公网 IP, 若此刻获取的公网 IP 与远端已记录 IP 不一致则通过 DDNS API 更新之。默认每 30s 检测一次,公网 IP 有变化才会更新。需要注意的是检测远端已记录 IP 值只会在启动时执行,故运行期间人为在 Web 端更改记录值本地并不会更新。一般情况下这种情况不会发生啦,除非你在多台服务器中使用这个脚本时忘改 sub_domain 了… 若改成每次检测公网 IP 时同时检测远端记录值,虽然更准确,但考虑到家用服务器中只会由一台服务器去更新公网 IP,而且频繁调用 DNSPod API 也有额外开销,思来想去还是只在本地检测公网 IP 和上一次 IP 值。

以 Archlinux 为例,按照如下步骤操作即可设置开机启动这个 DDNS 服务。

sudo wget -O /usr/local/bin/dnspod_ddns.py https://gist.github.com/billryan/239778f0821937939c8c140bdddd6840/raw/dnspod_ddns.py
# edit domain info
sudo chmod +x /usr/local/bin/dnspod_ddns.py
sudo wget -O /etc/systemd/system/dnspod-ddns.py.service https://gist.github.com/billryan/239778f0821937939c8c140bdddd6840/raw/dnspod-ddns.service
sudo systemctl start dnspod-ddns
sudo systemctl enable dnspod-ddns