一般家庭網絡是沒有固定 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())