记录一次家庭内网使用DDNS让外网访问, 同时动态更新域名

相信很多同学在自己家里学习啥的搞些小网站小程序, 比如部署一些 个人多媒体个人网盘个人博客等的网站, 但是在公司时想访问记录或查询一些资料, 但是又因为访问不了家庭网络而烦躁...

在此分享能使用任意外网访问家庭内网, 同时动态更新域名的方法.
当然在此之前我使用过花生壳、公云等一些软件, 他也可以让你从外网访问家庭内网, 但是别的不说, 他限速而且收费呀... 自己搭建可以全速使用家里的带宽
步骤如下:

一. 外网访问

1. 申请公网IP

想访问家庭网络必定需要找家里开网络的运营商, 让他们给开公网IP, 我家里使用的是电信宽带, 电话直接打 10000 号人工服务让他们帮忙开通, 理由嘛很简单( 找个借口说家里安装监控就给你开了 ) , 电信现在默认都是给的私网IP.

2. 光猫改为桥接模式

申请完公网IP先别急着挂, 还需要让他们把宽带网络改成桥接模式, 后面我们路由器使用拨号上网

3. 查询宽带账号和密码

由于路由器现在是使用拨号上网, 所以还需要找他运营商拿到宽带的账户和密码, 这些都是必要条件

4. 设备网线连接

我们使用网线连接 光猫的网口路由器WAN口, 电脑主机的网线则联通路由器的LAN口, ( WAN口是连接外部网络, LAN口是连接内部网络, 家里的电脑网线都可以用LAN接口连接, 并且此时我们电脑是没有网络的 )

光猫网口 --> 路由器WAN口

路由器LAN口 --> 电脑网口

5. 设置路由器

此时已经具备的条件:
公网ip, 宽带改为桥接模式, 宽带账号和密码, 设备网线正确连接

  • 开始设置路由器:
    我的路由器设备使用的小米路由器, 暂以小米路由器为例, 路由器网关是 192.168.31.1 , 自己的路由器网关自己搜一下, 然后输入路由器用户名密码
  • 上网设置
    上网设置PPPoE拨号
    如图, 上网方式选择PPPOE手动拨号, 然后输入宽带的账号和密码即可
    拨号成功应该就可以上网了~~~

6. 检查IP地址

百度查询自己本机的IP是否与路由器拨号成功获得的IP地址相同.
如果不同, 那一般都是私网ip 没有申请公网ip的.
检查ip是否一致

7. 路由转发

一般路由器都拥有路由转发功能, 可以自己配置转发规则.
配置路由转发规则

  • 端口转发: 映射端口, 访问外网 ip:端口, 会直接映射到内网的ip:端口
    如: 访问外网地址 22.135.173.55:8848, 会被转发到内网 192.168.31.26:8000
    端口映射规则

8. 测试

  • 我本地电脑随便开启一个服务

    部署成功, 内网ip:port 192.168.31.26:8401
    启动成功啦, 内网IP访问
  • 接着使用外网ip访问, 注意自己映射的端口哦
    外网IP访问
    访问成功~~

二. 设置动态更新域名

由于电信给的公网IP是动态IP, 每次关闭重启光猫都会更换公网IP地址, 所以这也是个很头疼的事情...

小编提供个人解决方案:

1. 准备域名 ( 本方案只支持腾讯云域名, 对接腾讯云API )

我使用的方法, 使用Python写了一个脚本, 动态去更新域名, 需要准备一个腾讯云的域名。
提供购买链接 https://buy.cloud.tencent.com/domain?from=console

2. 开通腾讯云 API 密钥

API 密钥代表你的账号身份和所拥有的权限,使用腾讯云 API 可以操作您名下的所有腾讯云资源。给上链接 https://console.cloud.tencent.com/cam/capi

  • 开通完后新建密钥( 单机即可, 自动创建 ):
    新建腾讯云 API 密钥

3. 安装Python3

由于使用的 Python 写的脚本, 需要环境拥有Python, 版本 3 及以上.
安装方法参考 https://www.cnblogs.com/weven/p/7252917.html
安装完成后查看Python版本:
查看Python版本

4. 献上脚本

upload successful

  • 复制以下代码 保存为 xxx.py 格式就行
VERSION = 1
from hashlib import sha1
import json
import time
import base64
import hmac

class DDnsHelper():
    def __init__(self, mid=0, params={}):
        pass

    def excute(self):
        import requests
        SecretId = '*需要填写*'
        SecretKey = '*需要填写*'
        runningPause = 20
        domainName = 'xxxx.com' # *需要填写你自己的域名*
        ddnsDomains = [
            {
                  # *需要填写你自己的域名*
                'name': '@.xxxx.com', 
                'value': '', # 
                'always': False,
                # DNS生存时间 
                'ttl': 600, 
                # 主机记录, 即域名前缀
                'subDomain': '@', 
                'recordId': '',
                # 记录类型
                'recordType': 'A', 
                # 线路类型, 指定细分解析线路
                'recordLine': '默认', 
                'description': '本地提供api服务的地址',
                # 查看本地IP地址的接口, 这是自己写的接口, 仅仅返回一个纯粹的本地外网IP地址
                'localDomain': 'https://www.hosix.cn/ip',
                'localValue': ''
            }

        ]

        class tenXunDDNS_Helper():
            def __init__(self):
                self.running = True
                self.action = ""
                self.secretId = SecretId
                self.nonce = 38651
                self.region = 'ap-guangzhou'
                self.secretKey = SecretKey
                self.version = '2017-03-12'
                self.domain = domainName
                self.url = 'cns.api.qcloud.com/v2/index.php'
                self.httpType = 'https://'
                self.endpoint = self.httpType + self.url
                self.ddnsDomain = ddnsDomains
                self.runningPause = runningPause
                self.remoteRecords = []

            def getServerIp(self):
                timeStamp = int(time.time())
                params = {}
                params['Action'] = 'RecordList'
                params['domain'] = self.domain
                params['Nonce'] = self.nonce
                params['SecretId'] = self.secretId
                params['Timestamp'] = timeStamp
                s = self.get_string_to_sign("GET", self.url, params)
                Signature = self.sign_str(self.secretKey, s, sha1)
                params['Signature'] = Signature
                response = requests.get(self.endpoint, params=params)
                result = json.loads(response.text)
                if int(result['code']) != 0:
                    raise Exception('获取域名失败' + ':' + str(result['code']) + ':' + result['message'])
                data = result['data']

                self.remoteRecords = data['records']
                # print(self.remoteRecords)

            def postServerIp(self, domain):
                timeStamp = int(time.time())
                params = {}
                params['Action'] = 'RecordCreate'
                params['domain'] = self.domain
                params['Nonce'] = self.nonce
                params['SecretId'] = self.secretId
                params['Timestamp'] = timeStamp
                params['subDomain'] = domain['subDomain']
                params['recordType'] = domain['recordType']
                params['recordLine'] = domain['recordLine']
                params['value'] = domain['value']
                params['ttl'] = domain['ttl']
                s = self.get_string_to_sign("GET", self.url, params)
                Signature = self.sign_str(self.secretKey, s, sha1)
                params['Signature'] = Signature
                response = requests.get(self.endpoint, params=params)
                result = json.loads(response.text)
                if int(result['code']) != 0:
                    raise Exception('添加域名失败' + ':' + str(result['code']) + ':' + result['message'])
                data = result['data']
                print('添加域名成功ip:%s,本地ip:%s,域名:%s' % (domain['value'], domain['localValue'], domain['name']))

            def updateServerIp(self, domain):
                timeStamp = int(time.time())
                params = {}
                params['Action'] = 'RecordModify'
                params['domain'] = self.domain
                params['Nonce'] = self.nonce
                params['SecretId'] = self.secretId
                params['Timestamp'] = timeStamp
                params['subDomain'] = domain['subDomain']
                params['recordId'] = domain['recordId']
                params['recordType'] = domain['recordType']
                params['recordLine'] = domain['recordLine']
                params['value'] = domain['localValue']
                params['ttl'] = domain['ttl']
                s = self.get_string_to_sign("GET", self.url, params)
                Signature = self.sign_str(self.secretKey, s, sha1)
                params['Signature'] = Signature
                response = requests.get(self.endpoint, params=params)
                result = json.loads(response.text)
                if int(result['code']) != 0:
                    raise Exception('更新域名失败' + ':' + str(result['code']) + ':' + result['message'])
                data = result['data']
                print('更新域名成功源ip:%s,本地ip:%s,域名:%s' % (domain['value'], domain['localValue'], domain['name']))

            def deleteServerIp(self):
                pass

            def getLocalDomain(self, domain):
                res = requests.get(domain['localDomain'])
                ip = res.content.decode("utf-8").strip()
                return ip

            def clearDnsRecord(self):
                for domain in self.ddnsDomain:
                    if not domain['always']:
                        domain['value'] = ''
                        domain['localValue'] = ''

            def run(self):
                self.getServerIp()
                for domain in self.ddnsDomain:
                    if (not domain['always']):
                        # 获取本地ip
                        try:
                            domain['localValue'] = self.getLocalDomain(domain)
                        except Exception as e:
                            print("获取本地ip失败 不更新:%s,本地ip:%s,域名:%s" % (
                            domain['value'], domain['localValue'], domain['name']))
                    else:
                        continue
                    flag = False
                    for remoteDomain in self.remoteRecords:
                        if (domain['subDomain'] == remoteDomain['name']):
                            domain['recordId'] = remoteDomain['id']
                            domain['value'] = remoteDomain['value']
                            flag = True
                            break

                    if flag:
                        if (domain['value'] == domain['localValue']):
                            pass
                            # print("无需更新ip:%s,本地ip:%s,域名:%s" % (domain['value'],domain['localValue'],domain['name']))
                        else:
                            self.updateServerIp(domain)
                        continue
                    else:
                        domain['value'] = domain['localValue']
                        self.postServerIp(domain)
                self.clearDnsRecord()

            def start(self):
                start_time = int(time.time())
                while self.running:
                    next_time = int(time.time()) + self.runningPause
                    if (next_time - start_time) > self.runningPause:
                        try:
                            self.run()
                        except Exception as e:
                            print(e.__str__())
                        start_time = start_time + self.runningPause
                    else:
                        time.sleep(self.runningPause / 10)

            def get_string_to_sign(self, method, endpoint, params):
                s = method + endpoint + "?"
                query_str = "&".join("%s=%s" % (k, params[k]) for k in sorted(params))
                # print(s + query_str)
                return s + query_str

            def sign_str(self, key, s, method):
                hmac_str = hmac.new(key.encode("utf8"), s.encode("utf8"), method).digest()
                return base64.b64encode(hmac_str)

        t1 = tenXunDDNS_Helper()
        t1.start()
d = DDnsHelper()
d.excute()
  • 最后双击运行~ OK

5. 运行原理和注意事项

  • -原理: 该脚本是通过定时查询公网IP接口: https://www.hosix.cn/ip , 然后调用腾讯云API去动态更新域名. 非常简单.....
  • 注意事项:
    1.该脚本需要持续运行, 如果是Linux系统, 直接挂后台运行即可, 该脚本资源消耗不高
    2.如果提示没安装 requests 模块, 打开cmd窗口
    输入: pip install requests
    pip install requests
    3.如运行闪退, 请检查域名等是否正确填写

如有问题请评论或留言: 544010165@qq.com

最后修改:2024 年 04 月 29 日
如果觉得我的文章对你有用,请点个赞吧~