家里閑置著一臺老款的Mac mini Server,跑OS X越來越慢构挤,索性裝上了Cent OS 7髓介,變成了一臺家庭服務(wù)器,裝上了Plex媒體服務(wù)器和Transmission下載服務(wù)筋现,同時唐础,也裝上了Nginx、Mysql矾飞、MongoDB一膨、Redis等,可以調(diào)試代碼洒沦,甚至擔(dān)當(dāng)一些小型項目的服務(wù)器豹绪。
不過,只在家庭內(nèi)網(wǎng)使用微谓,功能太有限森篷,于是接下來面臨的一個問題就是內(nèi)網(wǎng)穿透。使用過花生殼和花生棒豺型,服務(wù)相當(dāng)不穩(wěn)定仲智,而且種種受限,每要多加一個端口就要多花錢姻氨,安全性也有問題钓辆。
其實想想,內(nèi)網(wǎng)穿透的最大難題無非就是家里是動態(tài)公網(wǎng)IP,每變換一次公網(wǎng)IP前联,需要重新解析一次域名功戚。而阿里云等大型的云服務(wù)商,目前都已經(jīng)實現(xiàn)了域名解析管理的API接口似嗤,而且基本都是立即就可生效 啸臀。
所以我的思路就是,系統(tǒng)運行一個定期執(zhí)行的程序烁落,每隔一段時間掃描一下最新的公網(wǎng)IP乘粒,如果發(fā)現(xiàn)最新的公網(wǎng)IP與域名解析到的IP地址不一致,就通過阿里云API自動更新解析設(shè)置即可伤塌。這樣的花費不過每年一個域名的費用灯萍,最貴也就幾十塊錢。
具體實現(xiàn)步驟如下:
1. 阿里云設(shè)置
首先每聪,要確定一個準(zhǔn)備用于外網(wǎng)訪問的域名旦棉,并將此域名轉(zhuǎn)入到阿里云的云解析服務(wù)來解析。如圖所示药薯,添加需要管理的域名绑洛。
轉(zhuǎn)入后,在解析設(shè)置中果善,設(shè)置一下A記錄解析诊笤,解析的IP地址可以填當(dāng)前的公網(wǎng)IP。如果不知道自己的公網(wǎng)IP巾陕,在CentOS系統(tǒng)下讨跟,可以輸入使用以下命令獲取當(dāng)前的公網(wǎng)IP。
curl ifconfig.me
獲取公網(wǎng)IP后鄙煤,在阿里云云解析中設(shè)置完A記錄解析晾匠。
在阿里云賬戶管理后臺,點擊右上角的賬戶頭像梯刚,然后點擊accesskeys凉馆,或者直接登陸https://ak-console.aliyun.com,獲取阿里云的AccessKeyID和AccessKeySecret亡资。
2. 路由器設(shè)置
阿里云的設(shè)置完成后澜共,需要對路由器設(shè)置端口映射,使外網(wǎng)對公網(wǎng)IP的端口訪問能轉(zhuǎn)發(fā)到內(nèi)網(wǎng)服務(wù)器的相應(yīng)端口锥腻。絕大部分的路由器都支持端口映射嗦董。
常見的服務(wù)端口包括,用于WEB訪問的80端口瘦黑、SSH遠(yuǎn)程管理的22端口京革、Mysql數(shù)據(jù)庫的3306端口奇唤、Transmission下載服務(wù)管理的9091端口和Plex媒體服務(wù)的32400端口等等。不用花生殼的好處就是沒有端口數(shù)量限制匹摇,想設(shè)置多少就可以設(shè)置多少咬扇。
當(dāng)然,在設(shè)置端口映射之前廊勃,應(yīng)確保服務(wù)器的內(nèi)網(wǎng)IP已經(jīng)設(shè)置為靜態(tài)IP懈贺,而不是DHCP動態(tài)獲取。
3. 服務(wù)器設(shè)置
服務(wù)器端安裝好想要使用的各種服務(wù)后坡垫,別忘了在防火墻中開啟相應(yīng)的端口隅居,在CentOS 7中,防火墻永久開啟端口的命令是:
firewall-cmd --add-port=80/tcp --permanent
開啟之后別忘了重新載入防火墻的設(shè)置以使其生效葛虐,命令如下:
firewall-cmd --reload
4. 自動更新域名解析程序
準(zhǔn)備工作都做好了,接下來就是通過程序檢測公網(wǎng)IP棉钧,并在公網(wǎng)IP發(fā)生變化時屿脐,及時更新阿里云的域名解析。
這個程序是用Python寫的宪卿,先使用Python的包管理工具pip下載安裝阿里云的Python SDK的诵。如果沒有安裝pip,則先安裝pip:
yum install pip
安裝好pip后佑钾,安裝阿里云的Python核心SDK以及云解析SDK:
pip install aliyun-python-sdk-core
pip install aliyun-python-sdk-alidns
導(dǎo)入項目所需要的包西疤,如果缺少則使用pip安裝:
import os
import json
from urllib2 import urlopen
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest
完整代碼如下:
#!/usr/bin/env python
# coding= utf-8
import os
import json
from urllib2 import urlopen
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest
class DnsHandler:
# 從阿里云開發(fā)者后臺獲取Access_Key_Id和Access_Key_Secret
access_key_id = ""
access_key_secret = ""
# 填入自己的域名
domain_name = ""
# 填入二級域名的RR值
rr_keyword = ""
# 解析記錄類型,一般為A記錄
record_type = "A"
# 用于儲存解析記錄的文件名
file_name = ".ip_addr"
client = None
record = None
current_ip = ''
# 初始化休溶,獲取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()
# 如果公網(wǎng)IP發(fā)生變化代赁,則自動修改阿里云解析記錄
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):
if os.path.isfile(self.file_name) :
file_handler = open(self.file_name, 'r')
r = file_handler.read()
file_handler.close()
else :
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)
file_handler = open(self.file_name, 'w')
file_handler.write(r)
file_handler.close()
return json.loads(r)
# 獲取阿里云域名解析記錄ID
def get_record_id(self) :
return self.record["DomainRecords"]["Record"][0]["RecordId"]
# 獲取當(dāng)前域名解析記錄
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)
# 獲取當(dāng)前公網(wǎng)IP
def get_current_ip(self):
return json.load(urlopen('http://jsonip.com'))['ip']
# 實例化類并啟動更新程序
dns = DnsHandler()
dns.reset()
將以上代碼保存為dns.py文件兽掰,并賦予執(zhí)行權(quán)限:
chmod +x dns.py
5. 設(shè)置定時運行
CentOS內(nèi)置有強大的計劃任務(wù)工具Crontab芭碍,如果系統(tǒng)里沒有則先使用yum安裝:
yum install crontabs
首先,設(shè)置執(zhí)行用戶的環(huán)境變量孽尽,比如窖壕,我們使用root用戶來執(zhí)行這一程序,則先在用戶目錄下建立.profile文件杉女,或者在已有的.profile文件下加入如下一行瞻讽,以使得可以使用VI來編輯cron文件:
EDITOR=vi; export EDITOR
建立mycron文件,加入如下內(nèi)容:
*/10 * * * * /root/ddns/dns.py
這意味著每10分鐘執(zhí)行一次任務(wù)熏挎,即掃描公網(wǎng)IP速勇,若與阿里云解析不一致,則修改阿里云解析婆瓜。
然后快集,提交crontab任務(wù):
crontab mycron
好了贡羔,大功告成,接下來个初,程序會每隔10分鐘自動掃描公網(wǎng)IP乖寒,然后自動更新阿里云的解析,速度院溺、穩(wěn)定性和安全性都遠(yuǎn)勝于第三方的DDNS服務(wù)楣嘁。