腾讯云 DDNS 方案

DDNS 是什么

DDNS(Dynamic Domain Name Server,动态域名服务)是将用户的动态IP地址映射到一个固定的域名解析服务上
——百度百科

前段时间,我有个需求:家里 NAS 的 IP 不固定,而我需要一个域名,怎么样才能通过域名动态访问这个变化的 IP 呢?查了资料才知道,变化的 IP 和静态的域名作绑定,这就是 DDNS。看起来 DDNS 也不怎么高深。
目前提供 DDNS 服务的厂家非常多,比较注明的像 noip,花生壳 等等,有不少 OpenWRT 的路由器都有集成他们的服务,花生壳甚至有自己的硬件来解决 DDNS 和内网穿透的问题。但是如果要用他们的 DDNS 服务,就意味着需要使用他们的 DNS 服务。
我之前从腾讯云买了一个域名,所以打算直接腾讯云全家桶,直接用腾讯的 DNS 服务。腾讯云虽然没有现成的 DDNS 服务,但是提供了 DNS 解析记录查询、修改的接口,于是我觉得自己写一个小工具来解决 DDNS 的问题。

前期准备

这个文档是针对使用了腾讯云 DNS 服务的用户准备的,当然如果你使用了阿里云,相关的操作就要遵循阿里云的文档了,但是道理其实是相通的。

  • 在腾讯云平台进行注册,网址
  • 生成 API 密钥,会有一个 SecretId 和一个 SecretKey,网址
  • 我用的是 Python,当然你也可以选择别的编程语言

第一步:使用腾讯云 DNS 服务

可以先在平台上手动添加一个域名,并创建一个解析记录,当然也可以通过接口来创建,但是我觉得手动创建一条比接口创建还要快。
链接在这里

第二步:查看腾讯云的相关接口文档

如果想快点看结论,这一步可以跳过,直接进入下一步。
相关说明主要来自于腾讯云的官方文档,大家可以跟着腾讯云的官方教程具体了解

  • 首先是腾讯云的“公共参数”,是每个应用都需要添加的参数。原文链接
  • 其中最麻烦的是 Signature 这个参数的生成,可以参考原文链接,官网中说的还是很清楚的。
  • 我们再来看看两个我们需要用到的接口说明,一个是查询解析记录接口,一个是修改解析记录接口。

第三步:获取解析记录的 ID

我们在第一步中手动添加了域名,并且添加了一条解析记录,这条解析记录会有自己的 ID,我们需要获取这个 ID 才能通过接口来更新它。

import hmac # 用于签名加密,参考第二步中,腾讯 Signature 生成的说明
import base64 # 用于生成 base64 字符串,参考第二步中,腾讯 Signature 生成的说明
from hashlib import sha1 # 签名加密算法,参考第二步中,腾讯 Signature 生成的说明
from urllib.parse import urlencode, quote # url 编码相关操作,参考第二步中,腾讯 Signature 生成的说明
from urllib import request # 网络请求
from datetime import datetime # 用于公共参数中时间戳的生成
import json


# 生成时间戳
ts = str(int(datetime.timestamp(datetime.now())))

# 填写你自己的 Id、Key 和域名
secretId = 'AKIDixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
secretKey = 'ZLs9rxxxxxxxxxxxxxxxxxxxxxxxxxxx'
domain = 'abc.com'
subdomian = 'www'

# 请求地址和参数
baseUrl = 'cns.api.qcloud.com/v2/index.php?'
para = {
    'Action': 'RecordList',
    'Nonce': '18357',
    'SecretId': secretId,
    'Timestamp': ts,
    'domain': domain, 
    'subDomain': subdomain
}

# 签名使用的原始字符串
sigSrcStr = 'GET' + baseUrl + urlencode(para)
# 经过签名加密和 
para['Signature'] = base64.b64encode(hmac.new(secretKey.encode('utf-8'), sigSrcStr.encode('utf-8'), digestmod=sha1).digest())

url = 'https://' + baseUrl + urlencode(para, safe='')

response = request.urlopen(url).read().decode('utf-8')
result = json.loads(response)
print(result['data']['records'][0]['id'])

结果形如 555xxxxxx,这一步只要做一次,查询到这个 ID 即可

第四步:获取本地 IP 地址

通过 Python 获取外网 IP 地址,其实有两个思路:

  • 比较简单的方案使用一些 IP 查询的服务,但这个方法比较慢
  • 比较快的方案是直接查询本机的网卡,这里我默认需要有公网 IP,否则是需要先搞定内网穿透的

第一种方案两行代码就能搞定

from urllib import request
IP = request.urlopen("https://api.ipify.org").read().decode('utf8')

第二种方法可以使用 Popen,相当于在命令行使用 ip aifconfig 的方法来查询;或者使用 Python 提供的 socket 服务。这篇文章 有非常详细的说明,代码是从 StackOverflow 上撸的

import socket
import fcntl
import struct

def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,
        struct.pack('256s', ifname[:15]))[20:24])

IP = get_ip_address('eth0') # 这里的 eth0 需要改成你自己的外网网卡名,例如 ppp0、en0

第五步:通过接口更新解析记录

通过

  • 本机的 IP
  • 腾讯云解析记录 ID

我们就可以通过腾讯云提供的接口来更新解析记录了。

import hmac
import base64
from hashlib import sha1
from urllib.parse import urlencode, quote
from urllib import request
from datetime import datetime
import socket
import json

def update_dns(IP):

    ts = str(int(datetime.timestamp(datetime.now())))
    
    # 填写你自己的 Id、Key 和域名
	secretId = 'AKIDixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
	secretKey = 'ZLs9rxxxxxxxxxxxxxxxxxxxxxxxxxxx'
	domain = 'abc.com'
	subdomian = 'www'

    baseUrl = 'cns.api.qcloud.com/v2/index.php?'
    
    # 请求的参数
    para = {
        'Action': 'RecordModify',
        'Nonce': '18358',
        'SecretId': secretId,
        'Timestamp': ts,
        'domain': domain,
        'recordId': '555xxxxxx',
        'recordLine': '默认',
        'recordType': 'A', # "A","CNAME","MX","TXT","NS","AAAA","SRV"
        'subDomain': subDomain,
        'value': IP
    }

    sigSrcList = []
	
	# urlencode 会对中文进行编码,导致验证失败,因此这里手动组装签名原始字符串
    for k in para.keys():
        sigSrcList.append(k + '=' + para[k])

    sigSrcStr = 'GET' + baseUrl + '&'.join(sigSrcList)
    para['Signature'] = base64.b64encode(hmac.new(secretKey.encode('utf-8'), sigSrcStr.encode('utf-8'), digestmod=sha1).digest())
    url = 'https://' + baseUrl + urlencode(para, safe='')
    response = request.urlopen(url)

    print(response.read())


update_dns(IP)

第六步:定期执行更新任务

以上是腾讯 DDNS 服务的基本思路,你还可以增加一些额外的判断,例如 IP 不变时,不对记录进行更新。
定期执行更新任务,不同系统提供的服务可能不同。在 Linux 和 Mac 上可以使用 crond 服务。
你需要通过 crontab 对定期任务进行编辑,可以参考这篇教程

总结

以上针对腾讯云的 DDNS 服务搭建提供了一个思路和方法,希望对大家有所帮助

参考文档

1 详解 Python 获取网卡 IP 地址 https://www.cnblogs.com/my_life/articles/9187714.html
2 Linux crontab 命令 https://www.runoob.com/linux/linux-comm-crontab.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章