騰訊雲 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

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