一般家庭网络是没有固定 ip 的,这就导致了外部无法方便地访问家里电脑上的文件或网络服务。花生壳等内网穿透工具免费的又不好使,收费的又划不来,怎么办?今天我来给大家分享一个方法,轻松搞定。
准备
- 阿里云账号
- 阿里云域名
- 一台路由器
思路
阿里云解析DNS提供接口可以让我们通过程序更新域名设置,这是核心要点。查看文档。
另外我们还需要知道家里的IP地址。
然后只要我们根据运营商跟换ip的频率(一般一天换一个)定时执行任务,进行更新域名指向,这样就能实现只要访问固定域名就能指向指定IP了。
然后再设置一下路由器,映射到内网ip地址(即提供网路服务的主机地址),可以使用树莓派、老旧电脑或者NAS作为主机,搭建自己的博客或者提供简单的网络服务。
大功告成!
widows 可以利用计划任务,Linux 可以利用 crontab,来完成定时任务。
话不多说,都在代码里:
import logging
import requests
import hmac
import random
import string
from datetime import datetime, timezone
import base64
logger = logging.getLogger('test')
logger.setLevel(logging.DEBUG)
# 控制台输出
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# 输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
class DynamicRouter:
""" 利用阿里云解析接口,将域名动态解析到指定 ip
适用于家庭宽带的IP是公网IP的小伙伴,配合路由器的转发功能对外提供服务。类似花生壳的功能。
注意:80 端口一般被运营商封掉,在可以尝试其他端口。
"""
def __init__(self, access_key_id, access_key_secret):
self.APP_ID = access_key_id
self.APP_SECRET = access_key_secret
self.base_host = "https://alidns.aliyuncs.com/"
self._common_req_params = {
"Format": "JSON",
"Version": "2015-01-09",
"AccessKeyId": self.APP_ID,
"SignatureMethod": "HMAC-SHA1",
"Timestamp": datetime.now(tz=timezone.utc).isoformat().split('.')[0] + 'Z',
"SignatureVersion": "1.0",
"SignatureNonce": ''.join([random.choice(string.ascii_letters + string.digits) for i in range(10)]),
}
def get_signature(self, **kwargs) -> bytes:
""" 根据规则生成签名
:return signature
"""
signature_string_base = 'POST' + '&' + '%2F' + '&'
signature_string_to_params = ''
for key in sorted(kwargs):
if key == "Timestamp":
kwargs[key] = kwargs[key].replace(':', '%253A')
signature_string_to_params += f'{key}%3D{kwargs[key]}%26'
signature_string_to_sign = signature_string_base + signature_string_to_params.rstrip('%26')
signature = hmac.new(key=(self.APP_SECRET + '&').encode(), msg=signature_string_to_sign.encode(),
digestmod="SHA1").digest()
return base64.b64encode(signature)
def get_record_id(self, domain_name: str, rr: str) -> str:
""" 获取该域名下二级域名的解析记录 id
:param domain_name:域名名称
:param rr:主机记录(二级域名,若没有,默认为'@')
:return record_id or None
"""
data = {}
data.update(self._common_req_params)
data.update({'Action': 'DescribeDomainRecords', "DomainName": domain_name})
signature = self.get_signature(**data)
data['Signature'] = signature
res = requests.post(self.base_host, data=data).json()
records = res['DomainRecords']['Record']
for i in records:
if i['RR'] == rr:
return i['RecordId']
else:
logger.error('未获取到解析记录,检查参数是否正确。')
raise Exception("can't get value")
def update_domain_record(self, record_id: str, rr, value: str, ttl: int = 600, rtype: str = 'A') -> None:
""" 更新解析记录为 record_id 的设置,包括但不限于ip地址、主机记录、解析类型等
:param record_id : 解析记录
:param rr : 主机记录(二级域名)
:param value : 解析记录(若是 记录类型 A,则为 ip地址)
:param ttl:解析生效时间
:param rtype:记录类型(默认为'A',即跳转到 ip4 地址)
:return None
"""
data = {
'Action': 'UpdateDomainRecord',
'RecordId': record_id,
"RR": rr,
"Type": rtype,
"Value": value,
"TTL": ttl
}
data.update(self._common_req_params)
signature = self.get_signature(**data)
data['Signature'] = signature
res = requests.post(self.base_host, data=data)
logger.info("更新域名记录结果:" + res.text)
return None
@staticmethod
def get_ip():
"""
获取本机外网ip
接口是利用阿里云函数计算服务(有免费额度)提供,比爬取网络获取方便。
"""
req_url = 'https://1142905836248003.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/musicApi/get_ip/'
return requests.get(req_url).text
if __name__ == "__main__":
eg = DynamicRouter(access_key_id='your-appid', access_key_secret='your-secret')
record_id = eg.get_record_id('your-DomainName', 'your-RR')
eg.update_domain_record(record_id=record_id, rr='blog', value=eg.get_ip())