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

结果如下即成功:

参考