Python3实现阿里云DDNS域名动态解析

前言

  • 家里部署了一台NAS服务器,在公司平时都是通过IP访问的,现在想更改为用域名去访问,但是家里的宽带都是动态的公网IP,每次IP变了都需要手动解析一次域名,这样就比较麻烦,那怎么办了?这个时候我们就可以用到阿里云DDSN来实现自动进行域名解析, 通过阿里云的SDK来添加修改域名解析,检查本机公网IP与解析的IP是否一致,若不同则自动修改解析,达到动态解析的目的。

一、准备工作

  • 公网IP(ipv4或ipv6)(如何检查家里的IP是否是公网IP,我们可以通过查看路由器wan口IP和通过百度获取IP,查看两个IP是否一致,如一致者是公网IP,反之者不是)
  • 阿里云域名
  • 获取阿里云的accessKeyId和accessSecret(可以在阿里云控制台个人中心直接获取,建议使用RAM角色来进行权限控制,这样的话安全风险较小)
  • CentOS 7 服务器,版本:CentOS Linux release 7.7.1908

二、安装所需的Python包

  • Centos 7安装python3详细教程,链接:文章链接
# Windows系统:
pip3 install aliyun-python-sdk-core-v3 
pip3 install aliyun-python-sdk-domain 
pip3 install aliyun-python-sdk-alidns 
pip3 install requests
pip3 install apscheduler

# CentOS 7 系统:
pip3 install aliyun-python-sdk-core-v3==2.13.10
pip3 install aliyun-python-sdk-domain
pip3 install aliyun-python-sdk-alidns 
pip3 install requests
pip3 install apscheduler
# 在系统下先执行 openssl version 查看ssl版本,如是低于1.1.1版本者需要安装指定ssl版本
# 安装指定版本的 urllib3 库,请确保指定的版本与您当前的环境和其他依赖项兼容
pip3 install urllib3==1.25.10

三、阿里云ddns动态域名解析代码

  • alyddns.py
import json
import sys
import os
import requests
import logging
from apscheduler.schedulers.blocking import BlockingScheduler
from aliyunsdkcore.client import AcsClient
from aliyunsdkalidns.request.v20150109.DescribeSubDomainRecordsRequest import DescribeSubDomainRecordsRequest
from aliyunsdkalidns.request.v20150109.AddDomainRecordRequest import AddDomainRecordRequest
from aliyunsdkalidns.request.v20150109.UpdateDomainRecordRequest import UpdateDomainRecordRequest
from aliyunsdkalidns.request.v20150109.DeleteSubDomainRecordsRequest import DeleteSubDomainRecordsRequest


class Dnscontroller:

    def __init__(self, access_key_id, access_key_secret, region):
        """
        初始化 AcsClient

        Args:
            access_key_id (str): 阿里云访问密钥 ID
            access_key_secret (str): 阿里云访问密钥 密钥
            region (str): 设置区域, 默认cn-shenzhen
        """
        self.client = AcsClient(access_key_id, access_key_secret, region)
        # 获取当前脚本文件所在的目录路径
        self.current_dir = os.path.dirname(os.path.abspath(sys.argv[0]))

    def log(self):
        """日志模块"""
        log_filename = os.path.join(self.current_dir, "alyddns.log")
        # 配置日志输出格式
        log_format = '%(asctime)s - %(levelname)s - %(lineno)d : %(message)s'
        logging.basicConfig(level=logging.INFO,
                            format=log_format,
                            filename=log_filename,
                            encoding='utf-8')

        logger = logging.getLogger(__name__)
        return logger

    def add(self, DomainName, RR, Type, Value):
        """
        添加新的域名解析记录

        Args:
            set_DomainName: 传入域名
            set_RR: 传入主机记录
            set_Type: 传入记录类型
            set_Value: 传入记录值
        """
        request = AddDomainRecordRequest()
        request.set_accept_format('json')
        request.set_DomainName(DomainName)
        request.set_RR(RR)
        request.set_Type(Type)
        request.set_Value(Value)
        response = self.client.do_action_with_exception(request)
        return json.loads(response)

    def update(self, RecordId, RR, Type, Value):
        """
        修改指定子域名解析记录

        Args:
            set_RecordId: 传入指定子域名解析的RecordId
            set_RR: 传入主机记录
            set_Type: 传入记录类型
            set_Value: 传入记录值
        """
        request = UpdateDomainRecordRequest()
        request.set_accept_format('json')
        request.set_RecordId(RecordId)
        request.set_RR(RR)
        request.set_Type(Type)
        request.set_Value(Value)
        response = self.client.do_action_with_exception(request)
        return json.loads(response)

    def delete(self, DomainName, RR):
        """
        删除指定子域名所有解析记录

        Args:
            set_DomainName: 传入域名
            set_RR: 传入主机记录
        """
        request = DeleteSubDomainRecordsRequest()
        request.set_accept_format('json')
        request.set_DomainName(DomainName)
        request.set_RR(RR)
        response = self.client.do_action_with_exception(request)
        return json.loads(response)

    def get_domain_res_record(self, host_record, domain_name):
        """
        获取指定子域名解析记录

        Args:
            set_SubDomain: 传入主机记录.域名, 例如blog.csdn.net
            API请求就会针对子域名blog.csdn.net进行操作, 获取该子域名的解析记录
        """
        request = DescribeSubDomainRecordsRequest()
        request.set_accept_format('json')
        request.set_SubDomain(host_record + "." + domain_name)
        response = self.client.do_action_with_exception(request)
        return json.loads(response)

    def domain_name_analysis(self, mode, domain_name, host_record, record_type):
        try:
            # 获取当前子域名的所有解析列表
            domain_list = self.get_domain_res_record(host_record, domain_name)
            if mode == "ipv4":
                if record_type == "A":
                    # 获取IPv4,该地址可能会失效,如失效者更换新地址
                    ipv4 = requests.get('https://ifconfig.me/ip').text
                    print(f"获取到IPv4地址: {ipv4}")

                    if domain_list['TotalCount'] == 0:
                        self.add(domain_name, host_record, record_type, ipv4)
                        print("新建域名解析成功")

                    elif domain_list['TotalCount'] == 1:
                        if domain_list['DomainRecords']['Record'][0]['Value'] != ipv4:
                            self.update(domain_list["DomainRecords"]["Record"][0]["RecordId"], host_record, record_type, ipv4)
                            print("修改域名解析成功—update")
                        else:
                            print("IPv4地址没变")

                    else:
                        self.delete(domain_name, host_record)
                        self.add(domain_name, host_record, record_type, ipv4)
                        print("修改域名解析成功—delete")
                else:
                    self.log().error("record_type记录类型(ipv4)填写不正确, 必须是: A")
                    sys.exit()

            elif mode == "ipv6":
                if record_type == "AAAA":
                    # 获取IPv6,该地址可能会失效,如失效者更换新地址
                    ipv6 = requests.get("https://6.ipw.cn/api/ip/myip?json").text
                    get_ipv6 = json.loads(ipv6)["IP"]
                    print(f"获取到IPv6地址: {get_ipv6}")

                    if domain_list['TotalCount'] == 0:
                        self.add(domain_name, host_record, record_type, get_ipv6)
                        print("新建IPV6域名解析成功")

                    elif domain_list['TotalCount'] == 1:
                        if domain_list['DomainRecords']['Record'][0]['Value'] != get_ipv6:
                            self.update(domain_list["DomainRecords"]["Record"][0]["RecordId"], host_record, record_type, get_ipv6)
                            print("修改IPV6域名解析成功—update")
                        else:
                            print("IPV6地址没变")

                    else:
                        self.delete(domain_name, host_record)
                        self.add(domain_name, host_record, record_type, get_ipv6)
                        print("修改IPV6域名解析成功—delete")
                else:
                    self.log().error("record_type记录类型(ipv6)填写不正确, 必须是: AAAA")
                    sys.exit()

            else:
                self.log().error("mode类型填写不正确, 必须是: ipv4 & ipv6")
                sys.exit()

        except Exception as i:
            self.log().error(i)
            sys.exit()


if __name__ == "__main__":
    def execute():
        run = Dnscontroller("阿里云账号密钥 ID", "阿里云账号密钥 密钥", "cn-shenzhen")
        run.domain_name_analysis("类型(ipv4 & ipv6)", "域名", "主机记录", "记录类型(A & AAAA)")

    # 先执行一次任务
    execute()
    # BlockingScheduler调度器,适用于小型应用程序或简单任务调度的场景
    scheduler = BlockingScheduler(timezone='Asia/Shanghai')
    # 按间隔一定时间执行任务:seconds=秒;minutes=分钟;hours=小时
    scheduler.add_job(execute, 'interval', minutes=10)
    scheduler.start()

四、CentOS 7 设置开机自动运行脚本

1.把alyddns.py文件上传到服务器“/home”目录下,并赋予权限

cd /home
chmod +x alyddns.py

2.脚本添加到systemd服务管理器中

# ①创建一个新的服务单元文件
sudo vi /etc/systemd/system/alyddns.service

# ②在该文件中,插入以下内容:
[Unit]
Description=aly DDNS
After=network.target network-online.target systemd-networkd-wait-online.service

[Service]
ExecStart=/usr/bin/python3 /home/alyddns.py
WorkingDirectory=/home/

[Install]
WantedBy=multi-user.target
# Description:描述该服务的文本,可以随意命名,用于标识该服务的目的
# After:定义所依赖的其他系统单元(units)实例中表示:
	# network.target表示基本的网络服务已启动完成
	# network-online.target表示网络连接已准备就绪
	# systemd-networkd-wait-online.service确保网络连接建立后继续启动
# ExecStart: 指定要执行的命令或脚本(/usr/bin/python3程序路径,根据实际情况替换。/home/alyddns.py脚本路径)
# WorkingDirectory: 指定服务的工作目录
# WantedBy: 指定服务所属的运行级别。在CentOS 7中,multi-user.target表示该服务将在多用户模式下启动

# ③保存并关闭文件,重新加载systemd配置
sudo systemctl daemon-reload

# ④启用服务以在开机时自动运行
sudo systemctl enable alyddns.service

# ⑤启动服务,使其立即生效
sudo systemctl start alyddns.service
# 停止服务
sudo systemctl stop alyddns.service
# 查看服务状态
sudo systemctl status alyddns.service

你可能感兴趣的:(#,阿里云DDNS,阿里云,python,centos)