0x01 前言

阿里云

以下是很久以前的文章:

0x02 准备

阿里云的API已经支持python3,所以我将使用python3编写整个脚本。windows的朋友则需要自行配置python3的环境,以便使用相关命令。

然后使用以下命令安装相关模块:

#安装阿里云SDK核心-v3版本
[[email protected] ~]# pip3 install aliyun-python-sdk-core-v3

#安装阿里云alidns服务SDK
[[email protected] ~]# pip3 install aliyun-python-sdk-alidns

#安装requests模块
[[email protected] ~]# pip3 install requests

准备好相关模块后,还需要准备阿里云DNS解析的一些信息:

  1. access key id
  2. access key secret
  3. 一级域名
  4. 子域名
  5. ttl

其中第3、4项中的内容需要注意,例如我想用“home.ngx.hk”作为DDNS的域名,那么第3、4项应该是这样的:

  • 一级域名:ngx.hk
  • 子域名:home

而TTL则需要根据你所购买的DNS VIP服务细则内所允许的最小TTL来配置,建议设置为10,这样更符合DDNS服务的及时性与有效性。

最后是下载脚本,通过以下命令从我的私有gitlab中下载脚本:

[[email protected] ~]# git clone https://gitlab.ngx.hk/aliyun/DDNS.git

或者直接下载压缩包:

[[email protected] ~]# wget https://gitlab.ngx.hk/aliyun/DDNS/repository/master/archive.zip

0x03 脚本

再继续往下配置之前,我们先来看看脚本的内容。

为了提高安全性,我将需要配置的信息都迁移至外部的配置文件中,所以脚本开始会先检查配置文件是否存在于json格式是否有误:

# 尝试打开配置文件
try:
    config_r = open(sys.path[0] + '/config.json', 'r')
except Exception as e:
    print('An error occurred, open config file fail! Error MSG: ')
    print(e)
    print('Script exit!')
    sys.exit(0)
else:
    config_content = config_r.read()

# 尝试格式化配置文件
try:
    config_json = json.loads(config_content)
except Exception as e:
    print('Load json fail, please recheck config file! Error MSG: ')
    print(e)
    print('Script exit!')
    sys.exit(0)
else:
    pass

配置文件通过检查后,将各种信息分别赋值至变量:

# 定义变量
rc_access_key_id = config_json['access_key_id']
rc_access_Key_secret = config_json['access_Key_secret']
rc_domain = config_json['domain']
rc_sub_domain = config_json['sub_domain']
rc_ttl = config_json['ttl']

以上是配置信息的配置、获取与定义阶段。

紧接着需要定义各类函数,首先是获取本地网络公网的IP:

# 通过淘宝API获取本地公网IP
def my_ip():
    get_ip_method = requests.get('http://ip.taobao.com/service/getIpInfo.php?ip=myip').content.decode()
    get_ip_value = json.loads(get_ip_method)
    get_ip_value = get_ip_value['data']['ip']
    return get_ip_value

这里使用requests模块访问阿里云的IP库服务,获取本地IP的相关信息并解析。

因为这个脚本已经去除手动获取子域名ID的部分,所以我需要编写一个模块以便自动检索:

# 获取域名信息
# 输出格式:[RecordId, Value]
def get_record_info():
    clt = client.AcsClient(rc_access_key_id, rc_access_Key_secret, 'cn-hangzhou')
    request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
    request.set_DomainName(rc_domain)
    request.set_accept_format('json')
    try:
        result = clt.do_action_with_exception(request)
    except Exception as f:
        return 'An error occurred! Error MSG: ' + str(f)
    else:
        result = result.decode()
        result_dict = json.JSONDecoder().decode(result)
        result_list = result_dict['DomainRecords']['Record']
        result = []
        for i in result_list:
            if rc_sub_domain == i['RR']:
                result.append(i['RecordId'])
                result.append(i['Value'])
                break
        return result

使用账户信息与域名访问阿里云的API,调取该域名下的所有子域的解析记录,然后循环比对是否存在目标子域,如果是,则返回相关列表,否则返回空列表。

如果子域已经存在,当IP发生变动时,则更新该子域即可,相关模块如下:

# 更新子域名信息
def update_dns(dns_value, dns_record_id):
    clt = client.AcsClient(rc_access_key_id, rc_access_Key_secret, 'cn-hangzhou')
    request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
    request.set_RR(rc_sub_domain)
    request.set_Type('A')
    request.set_Value(dns_value)
    request.set_RecordId(dns_record_id)
    request.set_TTL(rc_ttl)
    request.set_accept_format('json')
    result = clt.do_action_with_exception(request)
    return result

需要注意的是,我将解析类型规定为A记录,因为需要写入IP地址,如果需要改为其他解析类型,请手动修改。如果可以,修改为TXT记录可以有效提高安全性。

如果子域不存在,则需要新增解析记录,相关模块如下:

# 新增子域名解析
def add_dns(dns_value):
    clt = client.AcsClient(rc_access_key_id, rc_access_Key_secret, 'cn-hangzhou')
    request = AddDomainRecordRequest.AddDomainRecordRequest()
    request.set_DomainName(rc_domain)
    request.set_RR(rc_sub_domain)
    request.set_Type('A')
    request.set_Value(dns_value)
    request.set_TTL(rc_ttl)
    result = clt.do_action_with_exception(request)
    return result

同样的,默认记录类型为A记录,如有需要请手动修改。

最后是日志写入模块:

# 写入日志
def write_to_file(dns_value, dns_output):
    time_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    write = open(sys.path[0] + './aliyun_ddns.log', 'a')
    write.write(time_now + ' ' + str(dns_value) + ' ' + dns_output + '\n')
    write.close()

一切准备就绪后,就可以开始执行了:

# 运行
if __name__ == '__main__':
    result_list = get_record_info()
    current_ip = my_ip()
    if len(result_list) == 0:
        aliyun_output = add_dns(current_ip).decode()
        write_to_file(current_ip, aliyun_output)
        print(aliyun_output)
    else:
        result_record_id = result_list[0]
        old_ip = result_list[1]
        if old_ip == current_ip:
            print('The specified value of parameter Value is the same as old')
        else:
            aliyun_output = update_dns(current_ip, result_record_id).decode()
            write_to_file(current_ip, aliyun_output)
            print(aliyun_output)

0x04 执行

在执行之前需要将config.json.template这个文件重命名为config.json,然后填写该文件内的信息并再三确认!但请勿修改该文件的格式与符号。

{
  "access_key_id": "",
  "access_Key_secret": "",
  "domain": "",
  "sub_domain": "",
  "ttl": ""
}

最后通过以下命令调用脚本:

[[email protected] ~]# python3 /usr/local/services_data/shell/DDNS/aliyun_ddns.py 

如果希望可以定时执行,请执行以下命令:

#打开并修改crontab
[[email protected] ~]# vim /etc/crontab

#在文件底部粘贴此行
*/2 * * * * root python3 /usr/local/services_data/shell/DDNS/aliyun_ddns.py

粘贴完成后保存并退出即可,然后使用以下命令重启crontab:

[[email protected] ~]# systemctl restart crond.service

上面的计划任务语句的意义为:每2分钟执行一次该DDNS脚本。

至此,所有配置均已结束。每次DNS修改时,都会有新的日志条目增加至日志中,并且阿里云也会发送一封邮件告知账户所有者,DNS解析变更的详细内容。

0x05 结语

请大家持续关注我的私有gitlab,若有更新,恕不另行通知。