有一台 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
自动化运行。