背景
在之前一篇文章中介绍了利用路由器的端口映射功能实现内网穿透的方法。
在实际的使用过程中,发现这一实现途径存在一些不足之处,即当路由器的公网ip地址改变之后,需要重新在ubuntu主机或者路由器上查看其公网ip地址,然后再在windows上修改ip地址。在远程访问的情况下,实际操作ubuntu主机或者路由器往往比较费时费力,因此本文描述了一种能够自动获取公网ip地址,当发现公网ip地址变化的时候,自动发送邮件至指定邮箱通知管理员,而不需要管理员再实际操作物理ubuntu主机的实现方案。
实现方法简介
本文主要利用python语言构建一个在ubuntu主机上运行的程序,其能够利用访问获取外网ip地址的网页方式自动获取外网ip地址,并保存在ubuntu主机上,当发现公网ip地址发生变化的时候,利用SMTP协议发送邮件,当公网ip没有发生变化的时候,不发送邮件。
具体实现介绍
启动SMTP服务
这里利用邮箱的SMTP功能实现邮件的发送,以163邮箱为例,在网页版邮箱设置页面启用SMTP,会生成一个授权密码,该授权码只显示一次,需复制到之后的python程序之中。
python程序代码
import json
from urllib.request import urlopen
import os
import time
import smtplib
from email.header import Header
from email.mime.text import MIMEText
# 两个获取ip地址的网站
ip_url_1 = 'https://api.ipify.org/?format=json'
ip_url_2 = 'http://jsonip.com'
# 配置文件名
config_file_name = '.global_ip.json'
# 第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "xxx@mail.com" # 用户名
mail_pass = "xxx" # 授权密码,非登录密码
sender = 'xxx@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = ['xxx@mail.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
title = 'update_addr' # 邮件主题
content = '' # 邮件内容
# 检查配置文件及其权限
def check_configfile_exist():
file_exist = os.access(config_file_name, os.F_OK)
file_read = os.access(config_file_name, os.R_OK)
file_write = os.access(config_file_name, os.W_OK)
return{'file_exist':file_exist,'file_read':file_read,'file_write':file_write}
def generate_configfile(ip_addr):
config_construct = {
"ip_addr": ip_addr
}
with open(config_file_name, "w", encoding='utf8') as fp:
fp.write(json.dumps(config_construct,indent=4, ensure_ascii=False))
fp.close()
def sendEmail():
message = MIMEText(content, 'plain', 'utf-8') # 内容, 格式, 编码
message['From'] = "{}".format(sender)
message['To'] = ",".join(receivers)
message['Subject'] = title
try:
smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 启用SSL发信, 端口一般是465
smtpObj.login(mail_user, mail_pass) # 登录验证
smtpObj.sendmail(sender, receivers, message.as_string()) # 发送
print("mail has been send successfully.")
except smtplib.SMTPException as e:
print(e)
def send_email2(SMTP_host, from_account, from_passwd, to_account, subject, content):
email_client = smtplib.SMTP(SMTP_host)
email_client.login(from_account, from_passwd)
# create msg
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8') # subject
msg['From'] = from_account
msg['To'] = to_account
email_client.sendmail(from_account, to_account, msg.as_string())
email_client.quit()
localtime = time.localtime(time.time()) # 打印本地时间
print("\n" + time.asctime(localtime))
# 通过两个网站获取ip地址
my_ip_1 = str(json.load(urlopen(ip_url_1))['ip'])
my_ip_2 = str(json.load(urlopen(ip_url_2))['ip'])
if (my_ip_1 == my_ip_2):
ip_addr = my_ip_1
else:
ip_addr = "ip_1 :" + my_ip_1 + "\n" + "ip_2 :" + my_ip_2
if(check_configfile_exist()['file_exist'] & check_configfile_exist()['file_write']):
config_file = open(config_file_name,'r')
read_context = json.load(config_file)
old_ip = read_context['ip_addr']
config_file.close()
if (old_ip == ip_addr):
print("ip address is up-to-date")
else:
content = "old ip address is : " + old_ip + '\n' + "new ip address is : " + ip_addr
sendEmail()
generate_configfile(ip_addr)
else:
generate_configfile(ip_addr)
content = "new ip address is : " + ip_addr
sendEmail()
这里的程序都是一些细节的实现,需要注意的是该程序提供了两个发送邮件的函数,其区别在于是否使用SSL发信,在163邮箱的设置页有说明,即上图的底部说明全部支持SSL连接,因此此处使用支持SSL的发信函数。
定时启动python程序
以上的程序实现了获取公网ip地址并发送邮件的功能,但是还缺少一步,就是让其在ubuntu主机上周期性的运行,例如每隔15分钟运行一次,检查一次ip地址,因此下一步,将利用crontab来实现这一功能。
Linux crontab是用来定期执行程序的命令。
利用crontab -e 即可进入编辑执行命令。
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
以上是其说明文字,从中可以看到命令的格式说明,m h dom mon dow command这几个缩写是以下英文的缩写:
minute, hour, day of month, month, day of week, command
利用这些信息即可编写定时启动python程序的代码,这里我设置的是每隔15分钟进行一次执行,即:
0,15,30,45 * * * *
/path/to/python3.x
/path/to/python/file/get_ip_and_mail.py >>
/path/to/log/file/check_and_email_ip.log
这里需要注意的是python的路径,python文件的路径以及log文件的保存路径,当然这里可以不输出log文件,保存log文件只是为了后续方便调试程序,运行出错时方便寻找问题。另一方面该命令的执行目录是在$HOME变量所在的目录,倘若需要修改执行目录,可以修改$HOME变量,或者修改命令为cd /path/to/target; command,转到目标路径下再执行命令。
配置好后重启一下crontab服务。
service cron restart
当第一次运行的时候程序会自动生成配置文件并发送邮件至指定邮箱,如下图:
当ip地址发生变化时,会收到如下的邮件信息:
综上完成了该方法的介绍。