0x01 前言
我本来是使用DNSPod的DDNS服务,但自从我撤走极路由后就再也用不了了。同时DNSPod免费用户最低的TTL仅能设为600(10分钟),不足以满足DDNS的需求。我试过很多次重启服务器或者重新拨号后都无法在短时间内更新DNS的A记录。
其实DNSPod也有简单易用的API可供我编写python脚本,相对于阿里云年付的40.8元,DNSPod的年付360确实有点贵,同时DNSPod个人专业版的TTL最低也就300(5分钟),但阿里云最低却能设为1(1秒)。
如果你需要阿里云的付费服务,以下是一个9折优惠码:
1 |
阿里云9折优惠码:nfasn1 |


0x02 准备
因为受限于阿里云python SDK,这里使用python2.7进行编写。运行时请使用python2.x版本运行,而使用python3.x将会出现错误。
首先要在系统上安装阿里云的python SDK:
1 2 3 4 5 |
#新安装的系统没有python pip,所以先安装它和其他依赖包 [root@test~]# yum install python-pip gcc gcc-devel autoconf automake python-devel python-pip python-crypto python-cryptography -y #安装alidns python DSK [root@test~]# pip install aliyun-python-sdk-alidns |
还需要准备以下账号内容:
- Access Key ID
- Access Key Secret
- 账号ID
准备好上面的内容后,将你的域名添加到阿里云解析DNS中:
完成后再在这个域名下添加一个主机A记录:
至此,准备工作已经完成。下面来获取域名的关键信息。
0x03 关键信息
通过脚本更新DNS记录需要几个关键的信息,如下:
- 一级域名(你的域名)
- 主机记录(你的二级域名)
- 记录类型(你的本地IP地址)
- 记录ID(这个解析记录的ID)
- 记录TTL(记录有效生存时间)
0x03.1 记录ID
其中1,2,3,5是可以确定的,而4则需要通过阿里云API获取:
1 2 3 4 5 6 7 8 9 10 11 12 |
def check_records(dns_domain): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=DescribeDomainRecordsRequest.DescribeDomainRecordsRequest() request.set_DomainName(dns_domain) request.set_accept_format(rc_format) result=clt.do_action_with_exception(request) result=result.decode() result_dict=json.JSONDecoder().decode(result) result_list=result_dict[‘DomainRecords’][‘Record’] forjinresult_list: print(‘Subdomain:’+j[‘RR’].encode()+‘ ‘+‘| RecordId:’+j[‘RecordId’].encode()) return |
返还的内容如下:
1 2 3 4 5 6 7 |
[root@web aliyun_ddns]# python aliyun_ddns.py Subdomain:subdomain_1|RecordId:3331111111111111 Subdomain:subdomain_2|RecordId:3331111111111111 Subdomain:subdomain_3|RecordId:3331111111111111 Subdomain:subdomain_4|RecordId:3331111111111111 Subdomain:subdomain_5|RecordId:3331111111111111 Subdomain:subdomain_6|RecordId:3331111111111111 |
0x03.2 本机IP
而获取本机IP,我选用ip.cn这个网站。当使用curl访问这个网站时,它会返还IP归属地和IP地址。使用脚本获取:
1 2 3 4 5 6 |
def my_ip_method_1(): get_ip_method=os.popen(‘curl -s ip.cn’) get_ip_responses=get_ip_method.readlines()[0] get_ip_pattern=re.compile(r‘d+.d+.d+.d+’) get_ip_value=get_ip_pattern.findall(get_ip_responses)[0] returnget_ip_value |
可能因为ip.cn这个站点受到攻击,他们的服务目前不太稳定,请使用my_ip_method_2或my_ip_method_3这两种本地IP的获取方式。
0x04 对比 | 更新
怎样才能知道IP地址是否有改变?
在获取本地IP后,再通过阿里云DNS API获取上一次的记录,两者相对比,如果不一致则更新DNS记录。
0x04.1 上一次的记录
1 2 3 4 5 6 7 8 9 |
def old_ip(): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=DescribeDomainRecordInfoRequest.DescribeDomainRecordInfoRequest() request.set_RecordId(rc_record_id) request.set_accept_format(rc_format) result=clt.do_action(request) result=json.JSONDecoder().decode(result) result=result[‘Value’] returnresult |
0x04.2 更新记录
1 2 3 4 5 6 7 8 9 10 11 |
def update_dns(dns_rr,dns_type,dns_value,dns_record_id,dns_ttl,dns_format): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=UpdateDomainRecordRequest.UpdateDomainRecordRequest() request.set_RR(dns_rr) request.set_Type(dns_type) request.set_Value(dns_value) request.set_RecordId(dns_record_id) request.set_TTL(dns_ttl) request.set_accept_format(dns_format) result=clt.do_action(request) returnresult |
0x04.3 对比
1 2 3 4 |
ifrc_value_old==rc_value: print‘The specified value of parameter Value is the same as old’ else: update_dns(rc_rr,rc_type,rc_value,rc_record_id,rc_ttl,rc_format) |
0x05 记录
我不但想要更新DDNS记录,我还想记录下每一次重新拨号后获取的IP,说不定日后能做个分析什么的。那么将记录写入文件:
1 2 3 4 5 6 7 8 9 |
def write_to_file(): time_now=datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’) current_script_path=sys.path[7] print current_script_path log_file=current_script_path+‘/’+‘aliyun_ddns_log.txt’ write=open(log_file,‘a’) write.write(time_now+‘ ‘+str(rc_value)+‘n’) write.close() return |
0x06 完整脚本
最新完整脚本请移步至GitHub:阿里云DDNS python脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# -*- coding: UTF-8 -*- import json import os import re import sys from datetime import datetime import requests from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest,DescribeDomainRecordsRequest, DescribeDomainRecordInfoRequest from aliyunsdkcore import client access_key_id=“” access_Key_secret=“” # 请填写你的账号ID account_id=“” # 如果填写yes,则运行程序后仅显示域名信息,并不会更新记录,用于获取解析记录ID。 # 如果填写no,则运行程序后不显示域名信息,仅更新记录。 i_dont_know_record_id=‘yes’ # 请填写你的一级域名 rc_domain=” # 请填写你的解析记录 rc_rr=” # 请填写你的记录类型,DDNS请填写A,表示A记录 rc_type=‘A’ # 请填写解析记录ID rc_record_id=” # 请填写解析有效生存时间TTL,单位:秒 rc_ttl=’30’ # 请填写返还内容格式,json,xml rc_format=‘json’ def my_ip_method_1(): get_ip_method=os.popen(‘curl -s ip.cn’) get_ip_responses=get_ip_method.readlines()[0] get_ip_pattern=re.compile(r‘d+.d+.d+.d+’) get_ip_value=get_ip_pattern.findall(get_ip_responses)[0] returnget_ip_value def my_ip_method_2(): get_ip_method=os.popen(‘curl -s http://ip-api.com/json’) get_ip_responses=get_ip_method.readlines()[0] get_ip_responses=eval(str(get_ip_responses)) get_ip_value=get_ip_responses[‘query’] returnget_ip_value def my_ip_method_3(): get_ip_method=requests.get(‘http://ifconfig.co/json’).content get_ip_value=eval(get_ip_method) get_ip_value=get_ip_value[‘ip’] returnget_ip_value def check_records(dns_domain): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=DescribeDomainRecordsRequest.DescribeDomainRecordsRequest() request.set_DomainName(dns_domain) request.set_accept_format(rc_format) result=clt.do_action_with_exception(request) result=result.decode() result_dict=json.JSONDecoder().decode(result) result_list=result_dict[‘DomainRecords’][‘Record’] forjinresult_list: print(‘Subdomain:’+j[‘RR’].encode()+‘ ‘+‘| RecordId:’+j[‘RecordId’].encode()) return def old_ip(): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=DescribeDomainRecordInfoRequest.DescribeDomainRecordInfoRequest() request.set_RecordId(rc_record_id) request.set_accept_format(rc_format) result=clt.do_action_with_exception(request).decode() result=json.JSONDecoder().decode(result) result=result[‘Value’] returnresult def update_dns(dns_rr,dns_type,dns_value,dns_record_id,dns_ttl,dns_format): clt=client.AcsClient(access_key_id,access_Key_secret,‘cn-hangzhou’) request=UpdateDomainRecordRequest.UpdateDomainRecordRequest() request.set_RR(dns_rr) request.set_Type(dns_type) request.set_Value(dns_value) request.set_RecordId(dns_record_id) request.set_TTL(dns_ttl) request.set_accept_format(dns_format) result=clt.do_action_with_exception(request) returnresult def write_to_file(): time_now=datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’) current_script_path=sys.path[1] log_file=current_script_path+‘/’+‘aliyun_ddns_log.txt’ write=open(log_file,‘a’) write.write(time_now+‘ ‘+str(rc_value)+‘n’) write.close() return ifi_dont_know_record_id==‘yes’: check_records(rc_domain) elif i_dont_know_record_id==‘no’: rc_value=my_ip_method_3() rc_value_old=old_ip() ifrc_value_old==rc_value: print(‘The specified value of parameter Value is the same as old’) else: print(update_dns(rc_rr,rc_type,rc_value,rc_record_id,rc_ttl,rc_format)) write_to_file() |
可能因为ip.cn这个站点收到攻击,他们的服务目前不太稳定,请使用my_ip_method_2或my_ip_method_3这两种本地IP的获取方式。
修改第117行即可修改本地IP的获取方式。
0x07 运行
将程序通过crontab每分钟运行一次,请将脚本路径修改为你的实际路径:
1 |
*/1****root/usr/bin/python2.7/usr/local/shell/aliyun_ddns.py>/dev/null1>/dev/null |
0x08 结语
其实这个脚本也可以更新其他类型的DNS记录,例如:CNAME,TXT等,只要知道解析记录ID即可。
经过近3天的运行,一切正常。
Leave a reply