有一臺 linux 伺服器,運營商提供了公網ipv6地址,在外面可以通過 ssh 訪問。
公網ipv6地址偶爾也會變,給訪問造成了巨大的麻煩。為了實現在外部可靠ssh訪問伺服器的目標,通常有兩種思路:
- 動態埠對映(
DDNS):將伺服器埠對映到有公網ip地址的遠端機器埠上。通過遠端機器的公網ip地址訪問。帶有公網ip地址的雲伺服器的體驗套餐比較便宜,可以實現。 - 伺服器獲取
ipv6地址,傳送到指定郵箱,實現起來方便快捷,是本文要介紹的內容。
linux 伺服器自動傳送ipv6地址到指定郵箱需要經過三個過程:
- 用 python 獲取本機
ip地址 - 用
smtp傳送郵件(而不是登陸郵箱點傳送按鈕) - 用
crond讓整個過程自動化
1. 用 python 獲取本機ip地址
使用ipconfig或ip addr可以獲取本機ip地址:
通過ip addr命令檢視ip,其中紅框inet6就是我們需要的ipv6地址。
內網ip地址是192.168.1.6。實際上也可以用這種方式獲取本機ip地址。python 有 subprocess和os這樣的包,可以執行shell 指令碼,獲取輸出。為了得到真正想要的ip地址,還需要比較複雜的字串處理。選擇優雅socket包:
import socket
def get_ip():
hostname = socket.gethostname()
addr_infos = socket.getaddrinfo(hostname, None)
ips = set([addr_info[-1][0] for addr_info in addr_infos])
global_ips = [ip for ip in ips if ip.startswith("24")]
return global_ips
socket會獲取所有的ip地址,但需要的是公網ipv6。公網ipv6一般是以24開頭。這裡加了一個篩選。獲取的結果是一個list,每個元素是ipv6地址的字串。
2. 用 smtp 傳送郵件
使用 python 的smtp包傳送郵件,需要一個支援smtp的郵箱(常見的郵箱都支援smtp),以sina.com郵箱為例:
import smtplib
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from time import asctime
def send_an_email(email_content): # email_content是一个字符串
mail_host = "smtp.sina.com" # 这个去邮箱找,这里用的sina邮箱
mail_user = 发送的邮箱地址
mail_auth_code = 授权码,不是邮箱的登陆密码
mail_sender = mail_user # 用mail_user 作为发送人
mail_receivers = [收件人list]
message = MIMEMultipart()
message['From'] = Header(mail_sender) # 寄件人
message['Subject'] = Header("主题名字")
message.attach(MIMEText(asctime(), 'plain', 'utf-8'))
message.attach(MIMEText(email_content, 'plain', 'utf-8'))
print("message is {}".format(message.as_string())) # debug用
smtpObj = smtplib.SMTP(mail_host)
# smtpObj.set_debuglevel(1) # 同样是debug用的
smtpObj.login(mail_user, mail_auth_code) # 登陆
smtpObj.sendmail(mail_sender, mail_receivers, message.as_string()) # 真正发送邮件就是这里
登陸自己的郵箱,找到設定,找到用戶端 pop/imap/smtp。從這裡找到smtp伺服器的地址,以及生成授權碼。
這裡會看到授權碼。(由於我之前是申請過了的,所以這裡是重置。)如果沒有的話,則是要你申請,在郵箱設定裡面獲得授權碼。
將郵箱設定好之後,可以呼叫這個send_an_email函式。自己修改email_content和Header,檢視傳送效果。
通過smtp傳送了一封郵件。
3. 自動化這個過程
雖然我們實現了自動獲取ip地址,也實現了通過 python 的smtp包進行郵件傳送。但是離智慧還是差一點:所有過程都得人工去執行這個指令碼。我們希望的是:ipv6地址一旦變化,就給我發郵件,但是又不要時時刻刻給我發郵件。
實現思路是監控伺服器的ipv6地址,將當前的ipv6地址儲存下來,每隔一個固定時間就檢查一下伺服器ipv6地址,如果沒有變化,則不傳送郵件,如果有變化,則傳送郵件。按照這個思路對獲取ipv6地址的程式碼進行改造:
def get_temp_ip(current_ip):
temp_ip_json_path = "/var/tmp/ip.json"
if not os.path.exists(temp_ip_json_path):
print("No {}, dump it.".format(temp_ip_json_path))
with open(temp_ip_json_path, 'w') as jo:
json.dump(current_ip, jo)
return True, current_ip
else:
with open(temp_ip_json_path, 'r') as jo:
origin_ip = json.load(jo)
if origin_ip == current_ip:
print("Current ip {} do not change, no need to send".format(current_ip))
return False, current_ip
else:
print("The ip updated from {} to {}, update it.".format(origin_ip, current_ip))
os.remove(temp_ip_json_path)
with open(temp_ip_json_path, 'w') as jo:
json.dump(current_ip, jo)
return True, current_ip
def get_ip():
hostname = socket.gethostname()
addr_infos = socket.getaddrinfo(hostname, None)
ips = set([addr_info[-1][0] for addr_info in addr_infos])
global_ips = [ip for ip in ips if ip.startswith("24")]
whether_to_send, send_ip = get_temp_ip(global_ips)
send_ip = json.dumps(send_ip)
return whether_to_send, send_ip
先判斷是否有/var/tmp/ip.json檔案。如沒有就將當前ipv6地址儲存,且傳送郵件。如存在就讀取其中的ipv6地址,若當前ipv6和儲存的ipv6地址一樣,就繼續等待下一次檢查,如不一樣就更新ip.json檔案並且傳送郵件。通常還要給指令碼新增上main:
if __name__ == "__main__":
whether_to_send, global_ips = get_ip()
if whether_to_send:
send_an_email(global_ips)
else:
print("wait and no send")
給指令碼新增上可執行權限:
sudo chmod +x send_ip_to_mailbox.py
使用 linux 的排程工具crond將任務排期自動執行,這裡設定的是每隔 1 分鐘執行一次ipv6地址檢查。
# 在/etc/cron.d/下面建立一个空白文件
sudo vim /etc/cron.d/ipsync
# 内容如下
*/1 * * * * chinglin /abs_path_to_send_ip_to_mailbox.py
啟動crond:
sudo systemctl restart crond # 启动
sudo systemctl enable crond # 设定开机启动
至此,可以用郵箱收取伺服器的ipv6地址了。
總結
本文主要介紹自動將linux伺服器ip地址傳送到指定郵箱的方式。基本步驟是用socket獲取ipv6地址,用smtp傳送郵件,用crond自動化執行。