有一臺 linux 伺服器,運營商提供了公網ipv6地址,在外面可以通過 ssh 訪問。 公網ipv6地址偶爾也會變,給訪問造成了巨大的麻煩。為了實現在外部可靠ssh訪問伺服器的目標,通常有兩種思路:

  • 動態埠對映(DDNS):將伺服器埠對映到有公網ip地址的遠端機器埠上。通過遠端機器的公網ip地址訪問。帶有公網ip地址的雲伺服器的體驗套餐比較便宜,可以實現。
  • 伺服器獲取ipv6地址,傳送到指定郵箱,實現起來方便快捷,是本文要介紹的內容。

linux 伺服器自動傳送ipv6地址到指定郵箱需要經過三個過程:

  1. 用 python 獲取本機ip地址
  2. smtp傳送郵件(而不是登陸郵箱點傳送按鈕)
  3. crond讓整個過程自動化

1. 用 python 獲取本機ip地址

使用ipconfigip addr可以獲取本機ip地址:

通過ip addr命令檢視ip,其中紅框inet6就是我們需要的ipv6地址。 內網ip地址是192.168.1.6。實際上也可以用這種方式獲取本機ip地址。python 有 subprocessos這樣的包,可以執行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_contentHeader,檢視傳送效果。

通過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自動化執行。