本文介绍两种方便获取主机动态IP的方式(DDNS,IP报告网页),并给出相应的代码实现.

shell脚本获取本机IP,执行上传操作和更新DNS操作.定期执行通过crontab或者systemd等服务.

应用场景

远程访问具有动态IP的公网或内网主机时,如果通过ip进行访问,由于公网IP总是在变化,我们不得不每次去查看新的ip地址,往往这个重复的过程比较麻烦.

远程主机联网的方式有所不同,主要有以下几种情形:

  1. 远程主机是通过PPPoE拨号上网,通常获取到动态的私有网络(内网)地址
  2. 远程主机直接获取到的是动态公网ipv4地址.
  3. 在教育网中通常还能获得动态的公网ipv6地址.

又可以简易地分成两类: 配有公网ip的主机与仅配置内网ip的主机.

内网主机访问方式

  1. 反向隧道

    对于躲在NAT之后的内网主机,比较方便的方式是在内网主机建立到公网主机的反向隧道,命令行建立反向隧道工具有:ssh,ngrok,tmate等,参考我之前的反向隧道的文章. 这些工具往往都能在ip发生改变后自动重建连接.

    缺点是我们需要一台拥有公网ip的主机,并且时刻保持隧道长连接,另外由于远程访问内网主机需要经过这个公网主机中转,速度变慢.

  2. 内网穿透

    通过公网服务器得到内网主机在NAT设备的转码地址,然后可以建立p2p的连接.QQ,TeamViewer即是类似原理.前提是内网容易穿透.

  3. 路由器端口映射

    外网IP和端口映射到内网:在路由器的「转发规则」页面添加外网的端口到内网某主机端口的映射.

本着只要有不断重复的麻烦事就用脚本实现的原则,我们通过一些脚本来方便我们的工作.

DDNS (动态域名IP解析)

IP报告/收集脚本

IP报告脚本

通过linux的ip命令获取到本机的公网ip以及通过网站获取本机的外网ip,然后上传到自建的php服务器上.

脚本使用了本地文件记录前一次变更的ip地址,当ip发生变化才执行网络操作.文件保存在内存文件系统或临时文件中.

reportIP.sh

#!/bin/bash
# __author__ = fyk
# get global ipv6 & ipv4 address,
# note that both ipv6 & ipv4 addr may have more than 1. # grep -v to exclude temporary ipv6 privacy addr.
ip6s=$(ip -6 addr |grep 'global'|grep -v 'tmpaddr'|awk '{print $2}'|sed 's/\/.*//' | uniq)
ip4s_local=$(ip -4 a | grep global |awk '{print $2}' | uniq) # local v4 ips ,public or prive ips that behind NAT
ips_pub=$(curl -s ifconfig.me) # there are lots of websites supplying IP echo services
ip_data=$ips_pub' '${ip6s}' '${ip4s_local}
#echo $ip_data
IP_FILE='/dev/shm/lastip97451' # or in /tmp .etc
ip_data_old=$(cat $IP_FILE 2> /dev/null) # for non-exist file,content is null
if [ "$ip_data" != "$ip_data_old" ];then
echo 'IP changed,push to remote.'
#echo $ip_data > $IP_FILE # update the file
params='k=fyk'
cnt=0
for ip in $ip_data;do
echo $ip
params=$params"&ip$cnt=$ip" # shell will handle & specially,so we first trans & to %26
((cnt=cnt+1))
done
params="n=$cnt&$params"
sever_addr="http://x.makefile.tk/"
#echo ${sever_addr}
curl -G -d "$params" "$sever_addr" else echo 'IP unchanged.'
fi
# TODO:增加断网重连

php收集脚本

提供的服务地址形式是:http://x.makefile.tk/?k=password&n=2&p0=x.x.x.x&p1=x.x.x.x,其中n是ip地址个数,p0,p1,...分别是单独的ip,参数k为了简单地防止一些人捣乱.直接访问http://x.makefile.tk/将能看到上一次保存的ip地址.

下面是index.php代码,通过文件来记录ip地址,不能够并发写入.同shell报告脚本一样可以使用内存文件来加快读写速度.

<html>
<body>
<?php
if ( !function_exists('sys_get_temp_dir')) {
function sys_get_temp_dir() {
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
$tempfile=tempnam(uniqid(rand(),TRUE),'');
if (file_exists($tempfile)) {
unlink($tempfile);
return realpath(dirname($tempfile));
}
}
}
?> <?php
//echo "QUERY_STRING: " . $_SERVER['QUERY_STRING'];
//echo "<br>";
//$temp_file = tempnam(sys_get_temp_dir(), 'Tux');
//$temp_file = sys_get_temp_dir() . 'ip97845';
//$temp_file = '/dev/shm/ip97845';//seems to be frequently erased by cloud host.
$temp_file = 'ip97845';
//if has param of n,then save ip to file
if(isset($_GET['n'])){
$n = $_GET['n'];
if(isset($_GET['k'])){
$key = $_GET['k'];//for simple security
if($key == 'password'){
$myfile = fopen($temp_file, "w") or die("Unable to open file!");
for ($x=0; $x<$n; $x++) {
$ip_idx = 'ip' . $x;
$line = $ip_idx . "=$_GET[$ip_idx]<br>";
echo $line;
fwrite($myfile, $line);
}
fclose($myfile);
echo 'save ip ok!<br>' ;
}else echo 'key error';
}else echo 'no key error'; }else{ // read from file
$myfile = fopen($temp_file, "r") or die("Unable to open file!");
echo fread($myfile,filesize($temp_file));
fclose($myfile);
} echo "<br><br>";
echo "Your INFO:<br>";
echo "IP: " . $_SERVER['REMOTE_ADDR'];
echo "<br>";
echo "UA: " . $_SERVER['HTTP_USER_AGENT'];
echo "<br>"; ?> </body>
</html>

对于这种web应用,使用网络上各种php建站即可.

公用DNS服务

准备:购买公网域名,域名设置DNS解析服务为Dnspod或CloudFlare.本文的代码使用CloudFlare的API动态修改DNS记录.

思路是修改IP报告脚本,将更新的IP更新的公共的DNS服务上.

ipv6-dns.sh 代码:

#!/bin/bash
# __author__ = fyk
# get global ipv6 & ipv4 address,
# note that both ipv6 & ipv4 addr may have more than 1. # get variables in dns.conf which includes cloudflare info
# source dns.conf
if [ -z "$1" ] ;then
echo 'please specify conf file'
exit 0
else
source $1
fi # grep -v to exclude temporary ipv6 privacy addr.
ip6s=$(ip -6 addr |grep 'global'|grep -v 'tmpaddr'|awk '{print $2}'|sed 's/\/.*//')
#for my own needs,i only use ipv6
#ip4s=$(ip -4 a | grep global |awk '{print $2}')
for ip in $ip6s;do
ip_data=$ip
break # only use first one
done
#ip_data=${ip6s}' '${ip4s}
#echo $ip_data API_URL="https://api.cloudflare.com/client/v4"
CURL="curl -s \
-H Content-Type:application/json \
-H X-Auth-Key:$AUTH_KEY \
-H X-Auth-Email:$AUTH_EMAIL " update_dns(){
UPDATE_DATA=$(cat << EOF
{ "type": "AAAA",
"name": "$DOMAIN_NAME",
"content": "$2",
"proxied": false }
EOF
)
#"ttl": 1, # let it be Automatic
echo "update dns: $DOMAIN_NAME -> $2"
$CURL -X PUT "$API_URL/zones/$ZONE_ID/dns_records/$1" -d "$UPDATE_DATA" > /tmp/cloudflare-ddns.json
}
# get current IP
get_dns_ip(){
RECS=$($CURL "$API_URL/zones/$ZONE_ID/dns_records?name=$DOMAIN_NAME")
IP=$(echo "$RECS" | sed -e 's/[{}]/\n/g' | sed -e 's/,/\n/g' | grep '"content":"' | cut -d'"' -f4)
echo $IP
} IP_FILE='/dev/shm/lastip9745' # or in /tmp .etc
ip_data_old=$(cat $IP_FILE 2> /dev/null) # for non-exist file,content is null
if [ "$ip_data" == "$ip_data_old" ];then
echo 'IP unchanged.'
exit 0
fi
echo 'IP changed,push to remote.'
if [ -z "$REC_ID" ] ; then
RECS=$($CURL "$API_URL/zones/$ZONE_ID/dns_records?name=$DOMAIN_NAME")
echo $RECS
REC_ID=$(echo "$RECS" | sed -e 's/[{}]/\n/g' | sed -e 's/,/\n/g' | grep '"id":"' | cut -d'"' -f4)
echo "REC_ID=$REC_ID"
fi
update_dns "$REC_ID" "$ip_data"
cur_ip=$(get_dns_ip)
if [ "$cur_ip"=="$ip_data" ];then
echo $ip_data > $IP_FILE # update the file
else
echo 'update dns failed.'
fi

脚本中通过source dns.conf读取了配置信息:

# this is Cloudflare api info for DDNS
# !!do not leave space around =
AUTH_EMAIL=<cloudflare-auth-email>
#This is your *Global API Key* under Cloudflare account settings
AUTH_KEY=<cloudflare-auth-key>
#Zone ID:can be find out there: <https://www.cloudflare.com/a/overview/>
ZONE_ID=<DNS Zone>
#your sub domain name
DOMAIN_NAME="ip.example.com"

具体API使用方法查阅https://api.cloudflare.com

自建DNS服务

准备:外网服务器B,搭建bind9服务用来提供DNS服务

借助于IP报告/收集脚本,在服务器B上不断更新域名解析.

客户端机器C,手动设置DNS服务地址为B的IP.这种方式的优点是域名想怎么写就怎么写.

示意图:

通过DNS服务可以实现与著名的花生壳相类似的服务,而且成本低,“自主、可控”:) 。

关于域名解析ttl

TTL是英语Time-To-Live的简称,意思为一条域名解析记录在DNS服务器中的存留时间。当各地的DNS服务器接受到解析请求时,就会向域名指定的NS服务器发出解析请求从而获得解析记录;在获得这个记录之后,记录会在DNS服务器中保存一段时间,这段时间内如果再接到这个域名的解析请求,DNS服务器将不再向NS服务器发出请求,而是直接返回刚才获得的记录;而这个记录在DNS服务器上保留的时间,就是TTL值。

如果域名的IP经常变更,那么减小TTL的值,如果很少改变,调大成几个小时都行.

将TTL设为1,表示'Automatic',如Cloudflare的DNS会在约5分钟内push出去.

IP地址变更事件通知

得到网络变化的方式有多种:

  1. C语言使用linux下的rtnetlink的NETLINK_ROUTE socket,监听之后会收到消息.要求写的程序一直在运行.参考.
  2. 如果网络管理器使用的Gnome的NetworkManager,那么会通过D-Bus广播事件(浏览器等常通过这种方式切换在线/离线模式).
  3. Debian系统中网络接口up或down时会执行/etc/network下的相关脚本.
  4. ifplugd 当网线被拔掉或接入时会执行相应脚本.

如果网络是使用NetworkManager(Ubuntu等系统默认的网络管理器)进行DHCP获取动态IP,比较方便的方式是将我们的脚本添加到事件响应脚本中. 参考man手册,在/etc/NetworkManager/dispatcher.d中添加脚本.

#!/bin/bash
# put this script in /etc/NetworkManager/dispatcher.d IF=$1
STATUS=$2 case "$STATUS" in
down)
#logger -s "NM Script down $IF triggered"
;;
dhcp6-change|up) #
#if [ $IP6_NUM_ADDRESSES > 0 ];then
# echo $IP6_ADDRESS_0 //0,1,2,...
#fi
# msg logged to /va/log/syslog
logger "IP6_ADDRESS_0 = $IP6_ADDRESS_0"
/path/to/ipv6-dns.sh /path/to/dns.conf 2>&1 > /dev/null
*)
;;
esac

cron定时执行

执行crontab -e将会编辑用户的crontab文件,其创建/tmp下的临时文件进行编辑,保存后将会提交到系统目录下(/var/spool/cron),这种设计方式类似visudo,目的是先检查用户的输入,防止错误的输入带来的破坏.系统重启后/tmp下的文件会删除,而crontab不会丢失.

也可以使用自定义的crontab文件导入到系统任务中:`crontab /path/to/cronfile

文件内容如下,注意使用绝对路径:

0 */1 * * * /home/s05/fyk/ip/ipv6-dns.sh /home/s05/fyk/ip/dns.conf

cron job的执行命令情况可以在/var/log/syslog中看到.

使用logger 'msg'可以将msg记录到syslog文件中.

crontab无需重启会立即生效.

邮件通知

发送邮件.适合于ip更新不太频繁的情形,通过代码发送邮件的代码很简便,Python,Java等语言均有方便的实现.

More

本文代码地址: https://github.com/makefile/CharmScript/tree/master/DDNS

其它资源:

动态IP解析的更多相关文章

  1. 更新TP-LINK路由器的外网IP到花生壳动态IP解析

    ------------------------------------------------------------------------------- 以下内容可能还是存在问题,等之后有时间再 ...

  2. 域名动态解析到动态IP

    一般宽带用户的IP都是动态IP,重连之后IP可能会发生变化. 如果想在其他地方连接家里的设备,或者在家中搭建服务器,就会受到影响. 现在提供一种动态解析域名的方式,只要检测到IP的变化,那么就调用阿里 ...

  3. Linux 静态IP动态IP设置

    1.设置动态IP ifconfig eth0 192.168.1.12 设置后立即生效,重启机器后就无效了 2.设置静态IP 编辑文件 /etc/sysconfig/network-scripts/i ...

  4. 动态IP或无公网IP时外网訪问内网ORACLE数据库

    ORACLE数据库是应用最多的一个数据库.一般项目应用.将ORACLE部署在内网,内网调用,及运维都仅仅能是内网完毕. 假设ORACLE主机或所在局域网没有固定公网IP,又想在外网对ORACLE进行訪 ...

  5. 动态IP下群晖搭建DDNS服务

    转载地址:https://www.zimrilink.com/share/dsm_aliddns_server.html 通过阿里云API(php)搭建出DDNS动态域名解析服务器;不同的是本文的方法 ...

  6. nodejs爬虫如何设置动态ip以及userAgent

    nodejs爬虫如何设置动态ip以及userAgent 转https://blog.csdn.net/u014374031/article/details/78833765 前言 在写nodejs爬虫 ...

  7. 2014-10-2 bug更新5 ecshop和ectouch解决动态ip登录超时和购物车清空问题

    有客户说登陆网站后台操作的时候,会时不时的掉一下,要重新登陆才能继续操作,自动登出的频率快和时间短,针对这个问题是因为: 购物车问题原因的产生是因为动态IP的SESSEION机制导致很多在公司或者其他 ...

  8. 实体ip 虚拟ip 固定ip 动态ip

    实体 IP:在网络的世界里,为了要辨识每一部计算机的位置,因此有了计算机 IP 位址的定义.一个 IP 就好似一个门牌!例如,你要去微软的网站的话,就要去『 207.46.197.101 』这个 IP ...

  9. IP工具类-自己动手做个ip解析器

    IP工具类-自己动手做个ip解析器 一.资料准备 导入依赖包:

随机推荐

  1. 解决MVC模式文件下载附件中文名称乱码

    解决如下: 进行url编码:Server.UrlPathEncode(file.AttachmentName) return File(file.TempWorkPath, CommonTools.G ...

  2. Kafka Streams 剖析

    1.概述 Kafka Streams 是一个用来处理流式数据的库,属于Java类库,它并不是一个流处理框架,和Storm,Spark Streaming这类流处理框架是明显不一样的.那这样一个库是做什 ...

  3. 团队作业4----第一次项目冲刺(Alpha版本)4.29

    a.站立式会议照片 会议内容: 会议讨论有一些内容要放在beta版本,所以燃尽图可能有些未完成.初级阶段我们只打算先写java代码的统计和显示.这是最基本的任务.在繁忙的任务中,不需要全部按照燃尽图. ...

  4. 团队作业8——第二次项目冲刺(Beta阶段)5.27

    1.当天站立式会议照片 会议内容: 本次会议为第七次会议 本次会议在陆大楼2楼召开,本次会议内容: ①:检查总结上次任务完成情况 ②:安排今天的分工 ③:对昨天的问题进行讨论 2. 每个人的工作 (有 ...

  5. 结对作业1----基于flask框架的四则运算生成器

    011.012结对作业 coding地址:https://coding.net/u/nikochan/p/2nd_SE/git 一.作业描述 由于上次作业我没有按时完成,而且庞伊凡同学编程能力超棒,所 ...

  6. Swing-JDialog示例代码-用户登陆UI

    JDialog是一种对话框组件,它常常与JOptionPane配合使用.JOptionPane提供对话框内部的消息.按钮等内容,JDialog提供对话框窗体,提供模态/非模态等属性.JDialog与J ...

  7. 201521044091 《Java学习笔记》 第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结.注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖面 ...

  8. SpringSecurity 登录 - 以及Md5加密

    我们现在开放一个链接给其他系统,来访问我们的系统 http://localhost:8080/hulk-teller-web/haihui!init.jspa?loginId=teller01& ...

  9. birt-j脚本调试 & 动态sql的实现

    一个比较好的birt问题解决网址: http://www.myexception.cn/h/1335919.html 1,Birt的JavaScript脚本简单调试 Birt中的js脚本不能用aler ...

  10. 利用ADO让普通人用excel读取oracle数据库表的通用办法

    Ref:http://blog.csdn.net/iamlaosong/article/details/8465177 Excel通过ADO方式连接到Oracle并操作Oracle给那些编程能力不强的 ...