家庭服务器或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("多个相同子域名,不做修改")
我需要修改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 &