MENU

基于阿里云API实现DDNS内网穿透功能

September 15, 2021 • 博文

基于阿里云 API 实现 DDNS 内网穿透功能展开目录

硬件拓扑场景展开目录

实现前提展开目录

  • 具备阿里云注册的域名
  • 内网运营商给你的对外 IP 是公网(动态的)

参考:如何验证你的路由器 (光猫) 使用的是公网 IP

网络结构调整展开目录

获取超级管理账户展开目录

由于光猫本身的路由功能有限,建议使用路由器的拨号上网功能,光猫使用桥接模式。

在修改光猫的 WAN 模式前,需要获取光猫的超级管理员账号,可以咨询客服获取,或者参考如下方法自行获取(适用于天翼 2.0 的网关猫 型号是烽火 HG2201T):

假设管理地址是 192.168.1.1 (光猫铭牌上有)

超密一般就是 telecomadmin + 八位数字,所以后面 8 组数字就代表 8 为数字 从 48 到 57 就是 0 到 9。因此只需要解析后面八组数字就可以拿到管理密码

修改光猫的宽带设置展开目录

超级管理账户登录光猫后台,选择网络宽带配置,修改前确保你已截图备份路由模式下的配置,并知晓宽带账号级密码

备份工作后,修改连接模式为桥接,保存即可

为防止与之前的配置冲突,建议修改 DHCP 配置,选择用户侧管理 - IPv4 设置,将 IP 地址修改为与之前不一样的,注意同步修改 DHCP 网段

使用路由器进行拨号上网展开目录

确保路由器已经连接光猫,连接路由器的无线,登录后台,此处使用的路由器为华为 AX3

如下图配置即可,宽带账号修改为自己的

配置后保存,正常即可连上网络

配置 DMZ 主机暴露自己的内网服务器展开目录

也可以使用 NAT 服务配置端口映射实现单端口的外网访问

至此你可以使用你的动态公网 IP 访问到你的内网服务器了展开目录

内网服务器配置 (树莓派 4B 为例) 展开目录

配置服务器静态 IP 展开目录

vi /etc/dhcpcd.conf
# 使用 vi 编辑文件,增加下列配置项

# 指定接口 eth0 (无线连接则是wlan0)
interface eth0
# 指定静态IP,/24表示子网掩码为 255.255.255.0
static ip_address=192.168.3.11/24
# 路由器/网关IP地址
static routers=192.168.3.1
# 手动自定义DNS服务器
static domain_name_servers=114.114.114.114 8.8.8.8 223.5.5.5 223.6.6.6 192.168.3.1

# 修改完成后,按esc键后输入 :wq 保存。重启树莓派就生效了
sudo reboot

配置阿里云域名解析记录展开目录

Python 调用阿里云 SDK 更新解析记录展开目录

首先,要确定一个准备用于外网访问的域名,并将此域名转入到阿里云的云解析服务来解析。如图所示,添加需要管理的域名。

转入后,在解析设置中,设置一下 A 记录解析,解析的 IP 地址可以填当前的公网 IP。如果不知道自己的公网 IP,在 CentOS 系统下,可以输入使用以下命令获取当前的公网 IP。

脚本原理展开目录

  • 获取主机的外网 IP
  • 调用接口获取域名解析配置并比对当前外网 IP 与域名解析配置中的 IP 是否一致
  • IP 不一致则调用域名解析配置更新接口更新为当前的公网 IP,否则不操作
  • 使用 Linux 定时任务每天执行一次(可根据实际情况调整频率)

安装依赖展开目录

sudo apt-get install python3-pip
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev
pip3 install aliyun-python-sdk-core -i http://mirrors.aliyun.com/pypi/simple
pip3 install aliyun-python-sdk-alidns -i http://mirrors.aliyun.com/pypi/simple 

脚本展开目录

/root/ddns/aliyunApiForDDNS.py 内容如下:
#!/usr/bin/env python3
# coding=utf-8

import json
from urllib.request import urlopen

from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest
from aliyunsdkcore.client import AcsClient


class DnsHandler:
    # 从阿里云开发者后台获取Access_Key_Id和Access_Key_Secret
    access_key_id = '你自己的key'
    access_key_secret = '你自己的secret'
    region_id = 'cn-hangzhou'

    # 域名
    domain_name = "mikuai.tech"
    # 填入二级域名的RR值
    rr_keyword = "www"
    # 解析记录类型,一般为A记录
    record_type = "A"

    # 用于储存解析记录的文件名
    file_name = ".ip_addr"

    client = None
    record = None
    current_ip = '117.83.29.186'

    # 初始化,获取client实例
    def __init__(self):
        self.client = AcsClient(
            self.access_key_id,
            self.access_key_secret,
            self.region_id
        )
        self.record = self.get_record()
        self.current_ip = self.get_current_ip()

    # 如果公网IP发生变化,则自动修改阿里云解析记录
    def reset(self):
        if self.current_ip != self.get_record_value():
            print(self.update_record(self.current_ip))
            self.get_record()

    # 获取阿里云域名解析完整记录
    def get_record(self):
        """
        未做记录为空判断,要求阿里云域名对应记录存在
        :return: 域名记录字典
        """
        request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
        request.set_PageSize(10)
        request.set_action_name("DescribeDomainRecords")
        request.set_DomainName(self.domain_name)
        request.set_RRKeyWord(self.rr_keyword)
        request.set_TypeKeyWord(self.record_type)
        r = self.client.do_action_with_exception(request)
        return json.loads(r)

    # 获取阿里云域名解析记录ID
    def get_record_id(self):
        return self.record["DomainRecords"]["Record"][0]["RecordId"]

    # 获取当前域名解析记录
    def get_record_value(self):
        return self.record["DomainRecords"]["Record"][0]["Value"]

    # 修改阿里云解析记录
    def update_record(self, value):
        request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
        request.set_action_name("UpdateDomainRecord")
        request.set_RecordId(self.get_record_id())
        request.set_Type(self.record_type)
        request.set_RR(self.rr_keyword)
        request.set_Value(value)
        return self.client.do_action_with_exception(request)

    # 获取当前公网IP
    def get_current_ip(self):
        return json.load(urlopen('https://ipv4.jsonip.com/'))['ip']


# 实例化类并启动更新程序
dns = DnsHandler()
dns.reset()

设置定时任务展开目录

sudo -s 
crontab -e
# 添加如下配置
00 03 * * * /usr/bin/python3 /root/ddns/aliyunApiForDDNS.py
# 重启服务
systemctl restart cron

测试展开目录

# 树莓派安装nginx
sudo apt-get install nginx
# 启动nginx
systemctl start nginx
# 修改默认访问端口为8888
sudo nano /etc/nginx/sites-available/default
# 重启nginx
systemctl restart nginx

访问你的域名 + 端口 8888

结果如下即成功:

参考展开目录