Linux · 2022年10月21日 0

一种Openwrt给lan口设备DDNS公网IPv6的方式(阿里云域名)

背景

最近在玩软路由,搞了一台Dell wyse 7020做了all in one,包含ESXi、Openwrt、NextCloud和samba,但想在公网访问这些服务的时候就需要公网ip,现在运营商都已经提供公网IPv6地址了(打开公网IPv6的方式不在赘述,我使用的方式是通过光猫设置桥接使用Openwrt拨号的方式获取),但地址是变动的,所以需要DDNS(动态域名解析),于是有了这篇文章。

Openwrt有一些DDNS的插件,但安装上使用起来并不是很方便(可能是我太菜),于是在zeruns的博客找到了一个python DDNS的脚本(使用链接:https://blog.zeruns.tech/archives/507.html),使用起来非常简单,在Openwrt安装上python3环境就可以了,我的使用的是OpenWrt 22.03.0,安装python3和python3-pip可以参考下图

安装好python,再按照zeruns 文章中安装python相关的包

最后在crontab 写个定时脚本就可以定时去解析新的IPv6地址到域名了

命令参考

每十分钟去解析一次域名

crontab -e
*/10 * * * * python3 /home/ddns/aliddns.py >> /home/ddns/aliddns.log

到这Openwrt的DDNS做好了,那么lan的下的设备怎么做呢

我想到了通过路由邻居表去做lan网络下的设备DDNS

在Openwrt下输入

ip neigh show

打印出路由邻居表

其中我们需要关注的就是公网IPv6地址和网卡MAC地址,通过设备的MAC地址就能获取到对应的公网IPv6地址,再用Openwrt去DDNS解析就可以了。

比如我的ESXi设置网卡MAC地址为 00:00:1a:xx:xx:xx,通过一条shell截取到此MAC获取的IPv6地址就可以了,再替换zeruns脚本中获取IPv6地址的方式就可以实现lan口设备的DDNS了。

我简单修改了,代码能力比较好的可以自己精简,我只是提供一个思路

其中有几处要修改成你自己的配置我用红框标出来了。

代码部分

# -*- coding: utf-8
import json,requests, subprocess, os, sys, time
from threading import Timer
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

ipv4_flag = 0  
ipv6_flag = 1 
accessKeyId = "xxxxxxxxx"  
accessSecret = "xxxxxxxx"  
domain = "houzhibo.com" 
name_ipv6 = "xxxxxx"


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


def run_cmd(cmd, stdout=True, time_out=1500):
    """
    Execute shell command.
    """
    cmd = cmd
    pro = subprocess.Popen(
        cmd,
        stderr=subprocess.STDOUT,
        stdout=subprocess.PIPE,
        shell=True,
        preexec_fn=os.setsid)
    timer = Timer(time_out + 2, lambda process: process.kill(), [pro])
    try:
        timer.start()
        result = pro.stdout.read()
        wait_res = pro.wait()
        if not stdout:
            return
        if wait_res and wait_res == -9:
            return "Timeout {},The process may have entered an uninterruptible state " \
                   "and forced to kill".format(time_out)
        if sys.version[0] == "3":
            return str(result, encoding="utf-8")
        else:
            return result
    finally:
        timer.cancel()



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)


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

    ip = urlopen('https://api-ipv4.ip.sb/ip').read()  # 使用IP.SB的接口获取ipv4地址
    ipv4 = str(ip, encoding='utf-8')
    print("获取到IPv4地址:%s" % ipv4)

    if domain_list['TotalCount'] == 0:
        add(domain, name_ipv4, "A", ipv4)
        print("新建域名解析成功")
    elif domain_list['TotalCount'] == 1:
        if domain_list['DomainRecords']['Record'][0]['Value'].strip() != ipv4.strip():
            update(domain_list['DomainRecords']['Record'][0]['RecordId'], name_ipv4, "A", ipv4)
            print("修改域名解析成功")
        else:  # https://blog.zeruns.tech
            print("IPv4地址没变")
    elif domain_list['TotalCount'] > 1:
        from aliyunsdkalidns.request.v20150109.DeleteSubDomainRecordsRequest import DeleteSubDomainRecordsRequest
        request = DeleteSubDomainRecordsRequest()
        request.set_accept_format('json')
        request.set_DomainName(domain)  # https://blog.zeruns.tech
        request.set_RR(name_ipv4)
        request.set_Type("A") 
        response = client.do_action_with_exception(request)
        add(domain, name_ipv4, "A", ipv4)
        print("修改域名解析成功")

print("本程序版权属于zeruns,博客:https://blog.zeruns.tech")

if ipv6_flag == 1:
    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地址
    ip = run_cmd("ip neigh show|grep '00:00:1a:xx:xx:xx'|grep 2408|head -n 1|awk '{print $1}'") # 使用路由邻居获取ipv6地址
#    ipv6 = str(ip, encoding='utf-8')
    ipv6 = ip
    print("获取到IPv6地址:%s" % ipv6)

    if domain_list['TotalCount'] == 0:
        add(domain, name_ipv6, "AAAA", ipv6)
        print("新建域名解析成功")
    elif domain_list['TotalCount'] == 1:
        if domain_list['DomainRecords']['Record'][0]['Value'].strip() != ipv6.strip():
            update(domain_list['DomainRecords']['Record'][0]['RecordId'], name_ipv6, "AAAA", ipv6)
            print("修改域名解析成功")
        else:  # https://blog.zeruns.tech
            print("IPv6地址没变")
    elif domain_list['TotalCount'] > 1:
        from aliyunsdkalidns.request.v20150109.DeleteSubDomainRecordsRequest import DeleteSubDomainRecordsRequest
        request = DeleteSubDomainRecordsRequest()
        request.set_accept_format('json')
        request.set_DomainName(domain)
        request.set_RR(name_ipv6)  # https://blog.zeruns.tech
        request.set_Type("AAAA") 
        response = client.do_action_with_exception(request)
        add(domain, name_ipv6, "AAAA", ipv6)
        print("修改域名解析成功")

再次感谢zeruns!