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