自建NAS,动态IPv6,DDNS解决方案

家庭服务器或NAS建立完成后,需要外网访问,但是IPv4比较难申请,不过IPv6大部分地区已经普及,故选择IPv6来做DDNS。

本文,使用的是阿里域名解析,以及Ubuntu系统,方法如下。

1. 首先需要申请对应的key和密钥,建议创建个子用户,仅有修改DNS的权限,如下:

2. 创建完密钥后,安装依赖的库:

# 使用 pip 命令,安装阿里云DNS相关的库:

$ pip install aliyun-python-sdk-alidns aliyun-python-sdk-core aliyun-python-sdk-core-v3 aliyun-python-sdk-domain

3. 创建 aliddns.py 文件,我的脚本里会同步修改本地的一些配置文件,请根据实际情况,选择是否屏蔽;以下是我的脚本:

from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkalidns.request.v20150109.DescribeSubDomainRecordsRequest import DescribeSubDomainRecordsRequest
from aliyunsdkalidns.request.v20150109.DescribeDomainRecordsRequest import DescribeDomainRecordsRequest
import requests
from urllib.request import urlopen
import json
import sys
from netaddr.ip import IPNetwork, IPAddress
import subprocess

# ---------------------------- key 密钥 域名 ------------------------------------------
accessKeyId = "TODO 改成自己的accessKeyId"
accessSecret = "TODO 改成自己的accessSecret"
domain = "qiangubafang.demo"  #TODO 你的主域名
name_ipv6 = "subdomain"  #TODO 要进行ipv6 ddns解析的子域名


client = AcsClient(accessKeyId, accessSecret, 'cn-hangzhou')

def update(RecordId, RR, Type, Value):  # 修改域名解析记录
    from aliyunsdkalidns.request.v20150109.UpdateDomainRecordRequest import UpdateDomainRecordRequest
    request = UpdateDomainRecordRequest()
    request.set_accept_format('json')
    request.set_RecordId(RecordId)
    request.set_RR(RR)
    request.set_Type(Type)
    request.set_Value(Value)
    response = client.do_action_with_exception(request)


def add(DomainName, RR, Type, Value):  # 添加新的域名解析记录
    from aliyunsdkalidns.request.v20150109.AddDomainRecordRequest import AddDomainRecordRequest
    request = AddDomainRecordRequest()
    request.set_accept_format('json')
    request.set_DomainName(DomainName)
    request.set_RR(RR)  # https://blog.zeruns.tech
    request.set_Type(Type)
    request.set_Value(Value)
    response = client.do_action_with_exception(request)



print("自动修改阿里域名地址")

request = DescribeSubDomainRecordsRequest()
request.set_accept_format('json')
request.set_DomainName(domain)
request.set_SubDomain(name_ipv6 + '.' + domain)
request.set_Type("AAAA")
response = client.do_action_with_exception(request)  # 获取域名解析记录列表
domain_list = json.loads(response)  # 将返回的JSON数据转化为Python能识别的

ip = urlopen('https://api-ipv6.ip.sb/ip').read()  # 使用IP.SB的接口获取ipv6地址
ipv6 = str(ip, encoding='utf-8')
print("获取到IPv6地址:%s" % ipv6)
if len(sys.argv) == 2 :
    ipv6 = sys.argv[1]
    print("手动设置IP: %s" % sys.argv[1])

if domain_list['TotalCount'] == 0:
    add(domain, name_ipv6, "AAAA", ipv6)
    print("新建域名解析成功")
elif domain_list['TotalCount'] == 1:
    dnsIPv6Addr   = domain_list['DomainRecords']['Record'][0]['Value'].strip()
    dnsIPv6Prefix = str(IPNetwork(dnsIPv6Addr + "/64").network)
    curIPv6Prefix = str(IPNetwork(ipv6.strip() + "/64").network)
    curIPv6Addr   = curIPv6Prefix + "100" #TODO  改为末尾为100的固定IP;如不需要固定可注释掉该行

    print("IPv6 DNS地址为  : %s " %  dnsIPv6Addr)
    print("IPv6 当前地址为 : %s " %  curIPv6Addr)
    if dnsIPv6Addr != curIPv6Addr:
        update(domain_list['DomainRecords']['Record'][0]['RecordId'], name_ipv6, "AAAA", curIPv6Addr)
        print("IPv6 修改域名解析成功,新IP为 %s" % curIPv6Addr)

        print("-----------------注意:更新域名成功后,这里会修改本地配置,如不需要可注释掉-----------------------------")
        print("修改IPv6配置文件(bind9, netplan, iptables, isc-dhcp-server):");
        cmdChangeIPv6Prefix = subprocess.getoutput("/home/my_cron.sh/chip6_prefix.sh " + dnsIPv6Prefix + "  " + curIPv6Prefix + " yes")
        print('  输出: ', cmdChangeIPv6Prefix);
        print("--------------------------------------------------------------------------------------")

    else:
        print("IPv6 地址相同   : 不做修改")


elif domain_list['TotalCount'] > 1:
    print("多个相同子域名,不做修改")
     

4. 根据需要选择是否修改本地配置文件,该步骤可选。  

我需要修改bind9,netplan,iptables防火墙的配置文件,调用时需要root权限,我的chip6_prefix.sh脚本如下:

$ cat chip6_prefix.sh
#!/bin/bash
old_ip=$1
new_ip=$2

if [ ! -n "$1" ] ;then
        echo "参数1为空, 用法->  [命令  旧IP前缀  新IP前缀 仅模拟/执行修改]";
        exit;
fi

if [ ! -n "$2" ]; then
        echo "参数2为空, 用法->  [命令  旧IP前缀  新IP前缀 仅模拟/执行修改]";
        exit;
fi

if [[ $3 != "yes" ]];
then
        echo "test change file.";
        echo "s/$old_ip/$new_ip/gi";

        sed  "s/$old_ip/$new_ip/gi" /home/ip-firewall/run.sh;
        sed  "s/$old_ip/$new_ip/gi" /etc/bind/db.yun.rangotec.com;
        sed  "s/$old_ip/$new_ip/gi" /etc/dhcp/dhcpd6.conf;
        sed  "s/$old_ip/$new_ip/gi" /etc/netplan/00-installer-config.yaml;

        old_ip_arpa=`arpaname ${old_ip}100`;
        new_ip_arpa=`arpaname ${new_ip}100`;

        echo "s/$old_ip_arpa/$new_ip_arpa/gi";

        sed  "s/$old_ip_arpa/$new_ip_arpa/gi" /etc/bind/named.conf.local;

        echo "simulation finished";
else
        echo "do change file!!!!";

        echo "s/$old_ip/$new_ip/gi";

        sed -i "s/$old_ip/$new_ip/gi" /home/ip-firewall/run.sh;
        sed -i "s/$old_ip/$new_ip/gi" /etc/bind/db.yun.rangotec.com;
        sed -i "s/$old_ip/$new_ip/gi" /etc/dhcp/dhcpd6.conf;
        sed -i "s/$old_ip/$new_ip/gi" /etc/netplan/00-installer-config.yaml;

        old_ip_arpa=`arpaname ${old_ip}100`;
        new_ip_arpa=`arpaname ${new_ip}100`;

        echo "s/$old_ip_arpa/$new_ip_arpa/gi";

        sed -i "s/$old_ip_arpa/$new_ip_arpa/gi" /etc/bind/named.conf.local;

        echo "process finished";

        echo "----->restart service"
        systemctl reload bind9
        systemctl restart isc-dhcp-server
        systemctl restart ip_firewall
        netplan apply
        echo "<<<<<<restart service finished"
fi

单独调用方法为:

chip6_prefix.sh  旧IP前缀  新IP前缀  yes   # 传入yes为真实修改,否则仅模拟

5. 如有必要,把aliddns.py 添加到 /tec/cron.d/crontab脚本里,定时检查,一旦不一致,自动更新。如每5分钟检查一次:

# 每几分钟检查下IPv6是否发生变化
*/5 * * * * root python3 /home/my_cron.sh/aliddns.py >> /var/log/my_cron.log 2>&1 &

 

 

评论列表: