BIND 架设 DDNS Server 提供 DDNS 服务

  categories:资料  author:

这个是 非常详细的 ddns配置的 文章, 采用bind

Set up your own Dynamic DNS

The problem with external dynamic DNS services like,, etc. is that you constantly have to look after them. Either they are free, but they expire after 1 month and you have to go to their web site to re-activate your account. Or you pay for them, but then you need to take care of the payments, update the credit card info, etc. This is all much too cumbersome for something that should be entirely automated.

If you manage your own DNS anyway, it may be simpler in the long run to set-up your own dynamic DNS system.

Bind has everything needed. There is a lot of info on the Internet on how to do it, but what I found tended to be more complicated than becessary or insecure or both. So here is how I did it on a Debian 6 (“squeeze”) server.

The steps described below are:

Initialize variables
Create key
Configure bind
Create zone file
Edit /etc/bind/named.conf.local
Reload server config
Web update.cgi

Initialize variables

To make it easier to copy/paste commands, we initialize a few variables


(In Debian, you can use grep directory /etc/bind/named.conf.options to find the correct binddir value)

For dynamic hosts, we will use a subdomain of our main zone:


Create key

Most example use the dnssec-keygen command. That would create 2 files (with ugly names): one .private and one .key (public) file. This is useless since the secret key is the same in both files, and the nsupdate method doesn’t use a public/private key mechanism anyway.

There is a less-known and more appropriate command in recent distributions : ddns-confgen. By default, it will just print sample entries with instructions to STDOUT. You can try it out with:

ddns-confgen -r /dev/urandom -s $host.$zone.

The options we use here are to use an “hmac-md5″ algorithm instead of the default “hmac-sha256″. It simplifies things with nsupdate later. And we also specify the key name to be the same as the host’s name. That way, we can use a wildcard in the “update-policy” in named.conf.local and don’t need to update it every time we add a host.

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys

chown root:bind   $etcdir/$zone.keys
chmod u=rw,g=r,o= $etcdir/$zone.keys

Depending on how you intend to use nsupdate, you may want to also have a separate key file for every host key. nsupdate cannot use the $zone.keys file if it contains multiple keys. So you might prefer to directly create these individual keyfiles by adding something like > $etcdir/key.$host.$zone :

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys > $etcdir/key.$host.$zone

chown root:bind   $etcdir/$zone.keys $etcdir/key.*
chmod u=rw,g=r,o= $etcdir/$zone.keys $etcdir/key.*

Configure bind
Create zone file

Edit $binddir/$zone :

$TTL  3600 ; 1 hour IN SOA (
1 ; serial (start at 1 for a dynamic zone instead of the usual date-based serial)
3600 ; refresh by secondaries (but they get NOTIFY-ed anyway)
600 ; retry (every 10 minutes if refresh fails)
604800 ; expire (slaves remove the record after 1 week if they could not refresh it)
300 ; minimum ttl for negative answers (5 minutes)


Edit /etc/bind/named.conf.local

Edit /etc/bind/named.conf.local to add :

// DDNS keys
include “/etc/bind/”;

// Dynamic zone
zone “” {
type master;
file “/var/cache/bind/”;
update-policy {
// allow host to update themselves with a key having their own name
grant * self;

Reload server config

rndc reload && sleep 3 && grep named /var/log/daemon.log | tail -20

(adjust the sleep and tail values depending on the number of zones your DNS server handles, so that it has time to report any problems)

If you created individual key files, or your $zone.keys file contains only a single key, you can test like this:

host=myhost; ip=;;; keyfile=$etcdir/key.$host.$zone

echo -e “server $server\n zone $zone.\n update delete $host.$zone.\n update add $host.$zone. 600 A $ip\n send” | nsupdate -k “$keyfile”

Or, more readable and with an extra TXT record:

cat <<EOF | nsupdate -k $keyfile
server $server
zone $zone.
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT “Updated on $(date)”

(If you get a could not read key from $keyfile: file not found error, and the file actually exists and is owned by the bind process user, you may be using an older version of nsupdate (like the version in Debian Etch). In that case, replace nsupdate -k $keyfile with nsupdate -y “$key_name:$secret” using the key name and secret found in your key file.)

Check the result:

host -t ANY $host.$zone

It should output something like descriptive text “Update on Tue Jan  1 17:16:03 CET 2013″ has address

If you try to use a file with multiple keys in the -k option to nsupdate, you will get an error like this:

… ‘key’ redefined near ‘key’
could not read key from FILENAME.keys.{private,key}: already exists


In a /etc/network/if-up.d/ddnsupdate script.

If you have setup an update CGI page on your server, you could use something like this, letting the web server use the IP address it received anyway with your request.

secret=”xBa2pz6ZCGQJ5obmvmp26w==” # copy the right key from $etcdir/$zone.keys

wget -O /dev/null –no-check-certificate “https://$server/ddns/update.cgi?host=$host;secret=$secret”

Otherwise, you can use nsupdate, but you need to determine your external IP first :

secret=”xBa2pz6ZCGQJ5obmvmp26w==” # copy the right key from $etcdir/$zone.keys

ip=$(wget -q -O –

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT “Updated on $(date)”

I used a very simple myip.cgi script on the web server, to avoid having to parse the output of the various existing services which show your IP in the browser:

echo “Content-type: text/plain”
echo “”

This alternative script example uses SNMP to get the WAN IP from the cable router. It only does the update if the address has changed, and logs to syslog.


server=$(dig +short -t SOA $zone | awk ‘{print $1}’)

ip=$( snmpwalk -v1 -m RFC1213-MIB -c public $router ipAdEntAddr | awk ‘!'”/$router/ {print \$4}” )

if [ -z “$ip” ]; then
echo “Error getting wan ip from $router” 1>&2
exit 1

oldip=$(dig +short $host.$zone)

if [ “$ip” == “$oldip” ]; then
logger -t `basename $0` “No IP change for $host.$zone ($ip)”

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT “Updated on $(date)”

logger -t `basename $0` “IP for $host.$zone changed from $oldip to $ip”

Web server update.cgi

An example update.cgi :


## Use nsupdate to update a DDNS zone.

## (This could be done with the Net::DNS module. It
##  would be more portable (Windows, etc.), but also
##  more complicated. So I chose the nsupdate utility
##  that comes with Bind instead.)

# “mi\”, 2013

use strict;

my $VERSION = 0.2;
my $debug = 1;

my $title = “DDNS update”;

my $zone     = “”;
my $server   = “localhost”;
my $nsupdate = “/usr/bin/nsupdate”;

use CGI qw(:standard);

my $q = new CGI;

my $CR = “\r\n”;

print $q->header(),
$q->start_html(-title => $title),

if (param(“debug”)) {
$debug = 1;

my $host   = param(“host”);
my $secret = param(“secret”);
my $ip     = param(“ip”) || $ENV{“REMOTE_ADDR”};
my $time   = localtime(time);

foreach ($host, $secret, $ip) {
s/[^A-Za-z0-9\.\/\+=]//g; # sanitize, just in case…
unless (length($_)) {
die “Missing or bad parameters. host=’$host’, secret=’$secret’, ip=’$ip’\n”;

my $commands = qq{
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT “Updated by $0 v. $VERSION, $time”

print $q->p(“sending update commands to $nsupdate:”), $CR,
$q->pre($commands), $CR;

open( NSUPDATE, “| $nsupdate” ) or die “Cannot open pipe to $nsupdate : $!\n”;
print NSUPDATE $commands        or die “Error writing to $nsupdate : $!\n”;
close NSUPDATE                  or die “Error closing $nsupdate : $!\n”;

print $q->p(“Done:”), $CR;

my @result = `host -t ANY $host.$zone`;

foreach (@result) {
print $q->pre($_), $CR;

if ($debug) {
# also log received parameters
my @lines;
for my $key (param) {
my @values = param($key);
push @lines, “$key=” . join(“, “, @values);
warn join(“; “, @lines), “\n”;

print $q->end_html, $CR;


目前动态 DNS 两大主流,一个是 BIND (ISC),另一个就是套接 DB 的 DNS 如 PowerDNS (或 mydns)
等,两种方式各有好坏,主要是因为 BIND 会有一些複杂性,但效果非常好,而 PowerDNS 则是很简单,
但相对的它不能承受大量查询,主要原因在于资料库上先天的限制. 本文主要为介绍 BIND 之动态
DNS 做法,而这个做法之最重要重点则在于nsupdate 这个指令及 IXFR (incremental zone transfer
request),是不同于传统的 AXFR (full zone transfer),IXFR 在做 Zone Transfer (DNS 的同步机制)
时,会以差异化的部份进行同步, 而 AXFR 则是以整个 Zone 进行同步.DDNS 主要由 RFC 2136 构成,
建议若您要对 DDNS 有一定深入的了解,可以阅读这篇 RFC 以了解更多重要的资讯
(1034 1035 1995 是 2136 的基础)

本文适用于对 DNS 巳有一定了解的朋友,若是不甚清楚建议您可先参考 TWNIC 所做的讲义:

如果你想对 nsupdate + key 的方法有更深入的了解可以参考

或参考 isc bind 的文件有最详细的解说

本文不讨论 view 的情形,若是 view 情形您必需从 view 的 match-client 去更新或是使用不同
的 key,所以建议您多参考 isb bind 的文件,虽然辛苦些,但资料绝对是最官方最正确的

2. 必要的资讯及知识
本文的范例以 Shell Script 做成,重点在于原理,採用什麽工具或做法完全视您个人的能力.以下

2.1 BIND 动态更新
基本上在 BIND8,BIND9 都是支援 nsupdate 的,但这里面要注意的是 BIND8 在8.3.X 后才支援
IXFR,而 BIND9 则都支援,所以若您的 Server 在 8.3.0 前的版本,那就不建议了,更何况这个以

2.1.1 nsupdate:
bind 要开 allow-update 选项,让你的程式可以来执行更新指令,allow-update 选项可以是 IP 或
key,而本文仅就 IP进行介绍,若用 Key 对有些朋友来说可能会变得稍複杂些了
代码: [选择]

# named.conf
# 其他略
zone “” {
type master;
file “”;
allow-update {;};    # 开放 进行动态更新
allow-transfer { slave_ip;;};  # slave 主机,可能一部或多部,若无请写 none

以上是开放让 进行动态更新,动态更新有其指令,详细您可看看 nsupdate man page
(man nsupdate),以下仅以最常用的进行说明:
代码: [选择]

[root@eai1 dyndns]# nsupdate
> server
> zone
> update delete A
> update add A
ttl ‘A': not a valid number  # 这个例子是错误示范,加一笔记录要有 TTL 值
> update add 60 A
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags: ; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; UPDATE SECTION:  0       NONE    A  60      IN      A

> send
> quit

代码: [选择]

server  指向某一台 NameServer 进行 update 操作
zone    修改某个 zone file
update delete  进行 update 的 delete 动作,这个指令格式是
update delete FQDN TYPE RDATA,如果有多笔相同的 A
记录或不同的 MX 记录要删除某一笔需将 RDATA 补上,
若没有 RDATA 则表示这个 FQDN 的这个 TYPE 都要删除
接的东西,如 A 的 RDATA 是 IP 而 MX 的 RDATA 是 优
先权 FQDN)
update add     进行记录的增加,操作如同 delete, 但是一定要有
TTL 值,且必需明确写出 RDATA
show    这只是操作后要显示进行了那些 update 指令
send    这个代表要把整个 update 指令送给 server,操作 update
时资料不是马上送出的,所以 update 可以很多行,最后
nsupdate 看到 send 时,才会将整个所有 update 送出,
而 update 使用 port 53/udp 若送出的资料量 (DNS packet)
大于 512 bytes,则会 truncate 而改使用 53/tcp,这是您
需要注意的地方,而只要您有 send, 则 SOA 记录的 serial
会自动加1,以期让 slave 来进行同步,所以过多的 send 可
能造成过多的 traffic

2.1.2 zone file 及日志档
如果您进行了 nsupdate 的操作,则原来 directory 所指的目录将会产生一些日志档,这个日志档即为
zone_name.jnl (directory 习惯上都设在 /var/named , 您自己必要注意 named 程式要有写入的权限,
chroot 状况等)
代码: [选择]

# 显示 zone file 内容
[root@eai1 named]# cat /var/named/
$TTL 86400      ; 1 day
@         IN SOA (
2006073267 ; serial
7200       ; refresh (2 hours)
1800       ; retry (30 minutes)
2419000    ; expire (3 weeks 6 days 23 hours 56 minutes 40 seconds)
300        ; minimum (5 minutes)
ns2                     A
; 如果别人在查询时,我们有这个记录则回应这个记录的 IP,若没有这个记录代表
; 这个网站没有上线,所以此时我们可以建立一笔 wildcard 记录,指向自己的说
; 明网站,以供 user 识别这个网站没有上线,而这笔 wildcard 的记录 TTL 时间
; 不能太长,以免别的 DNS Cache 了这资料
*                 0    A

# 目录里的东西
[root@eai1 named]# ls -la /var/named/
总计 128
drwxr-xr-x    2 named    named        4096  7月 27 09:25 .
drwxr-xr-x   20 root     root         4096  3月 13 16:50 ..
-rw-r–r–    1 named    named         451  7月 27 09:25
-rw-r–r–    1 named    named      104177  7月 27 10:01
-rw-r–r–    1 named    named         195  7月  4  2001
-rw-r–r–    1 named    named        2851 10月 17  2003

我们可以看到这个 .jnl 的产生,这个 .jnl 档是不能随便删除,因为它等于是 的补
充资料,而这些补充资料在 DNS reload/restart 时, named 还会在把它读进来,所以动态更新后的资料,
并不会随著 dns 重启后而消失,如果你想让现在整个 zone 档出现所有的记录,那可以 rndc stop 来停止
dns, 此时 named 会把 .jnl资料写入原来的 zone file,不过这种方式一般来说较不建议,因为我们的 zone
file 只要存一个样版 (template),其他的东西都是临时性的. 而若你想知道现在整个 zone 的内容,在可
以做 zone transfer 的主机上(如上例为 slave_ip及, 以 dig 指令来执行 axfr:
代码: [选择]

[root@eai1 dyndns]# dig @ axfr

; <<>> DiG 9.3.0 <<>> @ axfr
;; global options:  printcmd        86400   IN      SOA 2006073528 7200 1800 2419000 300        86400   IN      NS        86400   IN      NS
*      0      IN      A    86400   IN      A  60      IN      A  60      IN      MX      10
# 以下略

2.设定 NameServer 仅进行差异化的同步
Master/Slave 要进行 zone file 的同步,而 ddns server 若只有一部是可以不用考虑这些问题的,但是若有两
部以上的 DNS Server, 就需要考虑到同步的进行方式,若 zone file 的总资料量小,採用什麽同步方式是无所
谓的,但若资料量多,或是经常处在变动状况,那差异化的同步就会显得很重要,因为它可以让所有的 DNS 在短时
间内全部同步完成,BIND 支援 IXFR 后,其预设即是採用 IXFR, 若没有 IXFR (update) 时,则採用 AXFR,所以不
需要对 IXFR 进行额外设定,但为使大家了解其参数,本处还是举例来进行说明,好让大家能够更了解

代码: [选择]

# master DNS’s named.conf
key “rndc-key” {
algorithm hmac-md5;
secret “HpXtFRFdLaRPFjpZokIwusyezyyRNjxhcafCfmktWNyGkDFzHAXlpTZQtVLc”;

controls {
inet port 953
allow {; } keys { “rndc-key”; };

options {
directory “/var/named”;
pid-file “/var/run/named/”;
allow-transfer { none; };
provide-ixfr yes;   # 提供 slave 主机以 IXFR 同步,default yes
request-ixfr yes;   # slave 以 IXFR 向 master 进行同步,default yes
recursion no;       # 不允许递迴查询
zone “” {
type master;
file “named.local”;
zone “.” {
type hint;
file “”;
zone “” {
type master;
file “”;
max-journal-size 500k;                      # 设定日志档大小
allow-transfer {;;}; # 可做 ixfr/axfr 的来源 IP,必需写上 slave,
# 是方使我们自己查看 zone file 现况
also-notify {;}; # 额外的同步主机,这可能是 hot site 备份主机
allow-update {;};                 # 允许动态更新的来源

上述的东西相信只要对 BIND DNS 有一定了解的朋友应该都是没有问题的,较不常出现的项目我都加上了的注解以利
大家了解,而 slave 的设法都同于 master, 只有在 zone 的部份稍有不同:
代码: [选择]

# slave DNS’s named.conf
# 其他设定皆同上,只有 zone …稍有不同,同于一般的 slave zone,不需要再开 allow-update (default none)
zone “” {
type slave;
masters {;};
file “”;
allow-transfer { none;};

所以从上述我们可以知道 DDNS for Master/Slave 您只要对 master 进行 update,每次的 update 送出 (send)
会使该zone 的序号加一,只要 zone 有更新 , dns 会送出 notify 讯息给所有的 NS 主机(NS 记录上所列的名称
伺服器),及可能的 also-notify 对象,使 slave 主机知道要进行同步,同步时优先採用 IXFR,若 master 不支援
IXFR 则改使用 AXFR,以达到即使更新,及时同步的效果

此外,有些做 DDNS 的公司可能对 IXFR 不了解,而是把所有的 zone type 都设成了 MASTER,然后对这些 NameServer
进行 nsupdate , 这种做法也是可以的,不过中间若漏了一步或那一台少做了一件事,那两边的资料就会不一致,导致
可能同一个名称会有不同的解析结果 (例如 做法)

3. DDNS 的前端及后台控制范例

3.1 MYSQL table
主要由三个表构成,分别为 RR (Resource Record), RR_LOG (旧资料,建议您依状况适当保存),USER (user 认证)
代码: [选择]

SN int(20) NOT NULL auto_increment,
USERNAME varchar(64) NOT NULL default ”,
FQDN varchar(64) NOT NULL default ”,
TTL int(5) NOT NULL default ’60’,
TYPE varchar(10) NOT NULL default ”,
RDATA varchar(64) NOT NULL default ”,
CREATE_TIME timestamp(14) NOT NULL,

— Table structure for table ‘RR_LOG’

SN int(20) NOT NULL default ‘0’,
USERNAME varchar(64) NOT NULL default ”,
FQDN varchar(64) NOT NULL default ”,
TTL int(5) NOT NULL default ’60’,
TYPE varchar(10) NOT NULL default ”,
RDATA varchar(64) NOT NULL default ”,
CREATE_TIME varchar(14) default NULL,

— Table structure for table ‘USER’

SN int(20) NOT NULL auto_increment,
USERNAME varchar(64) NOT NULL default ”,
PASSWD varchar(64) NOT NULL default ”,
EMAIL varchar(64) NOT NULL default ”,
MEMO varchar(255) NOT NULL default ”,

3.2 dyndns.cfg 设定档
这个设定档主要为了给 CGI 程式及产生 nsupdate 的程式 ( 所使用,透过 eval 方式来执行,
代码: [选择]

# mysql host/db/user/password

# dyndns domain

# Master IP

# nsupdate command file

# update freqency

# RR valid time (sec

3.3 dyndns.cgi CGI 程式
这个 CGI 主要用于接收 USER 端来的资讯,验证通过后即为把 USERNAME.DOMAIN 资料,A/MX 及对应 IP 存入
Table RR 中,此外这个 CGI 以 shell script 做成,可以于多数人的环境执行 (chmod 755 及目录的 CGI 执
行权限 ExecCGI 莫忘)
代码: [选择]

echo -ne “Content-Type: text/html\n\n”

if [ -n “$QUERY_STRING” ];then
# 取得 QUERY_STRING,以下这个作法是危险的,因为没有检查资料的正确性就 eval
# 我的用意只在于说明作法
eval `echo “$QUERY_STRING” | sed “s/&/;/g”`
# 读取设定档,这个路径您需要自行调整
eval `cat /home/abelyang/dyndns/dyndns.cfg `
sql0=”select 1 from USER where USERNAME=’$LOGIN’ and PASSWD=’$PASSWD'”
res=`echo $sql0 | $MYSQL `
# 如果 USER 密码正确, ${#res} 应为2,不对则为 0
if [ ${#res} -lt 1 ];then
echo “Login Failure”
# 取得 IP, 需判断有 Proxy 存在,但是不考虑 Proxy 后是 NAT 情形
# 删除上一次的登入
sql1=”delete from RR where USERNAME=’$LOGIN'”
# 预设的动态更新项目为 A/MX
sql2=”insert into RR(USERNAME,FQDN,TYPE,RDATA) values(‘$LOGIN’,’$FQDN’,’A’,’$IP’)”
sql3=”insert into RR(USERNAME,FQDN,TYPE,RDATA) values(‘$LOGIN’,’$FQDN’,’MX’,’10 $FQDN’)”
echo $sql1 | $MYSQL
echo $sql2 | $MYSQL
echo $sql3 | $MYSQL
echo $LOGIN login success @$IP
# 以下只是网页的部份,我没有做 DDNS 申请,这个部份我想只要懂网页的朋友应该都会才是
cat <<EOF
<meta http-equiv=”Content-Type” content=”text/html; charset=big5″ />
<link rel=”stylesheet” href=”./style1.css”>
<h3>Abel Dyndns Demo </h3>
Login:<input type=text name=LOGIN ><BR>
Passwd:<input type=password name=PASSWD><BR>
<input type=submit values=”Start DynDNS”>



3.4 定时产生 nsupdate
这隻程式主要进行读取 Table RR , 并产生 nsupdate 所需要的指令格式后执行 nsupdate, 程式预设
每15秒执行一次(参数如上 dyndns.cfg 中的 UPD_FREQ 更新频率),您可以拿掉 while [ 1 ] 的迴圈,
改用 crontab 方式来跑,不过这样就较不容易控制一分钟以内的更新频率了,最后,这个程式会把 SOA
的序号改成更新时间,这个时间是 UTC 的秒数(意即 1970/1/1 至今秒数, date +%s 可得),由 SOA 的
序号就可以知道最后的 update 时间,这个原理和 .com 的verisign 或是 dyndns 中的 是相同的.

另外,根据 RR_ALIVE 参数,若 USER 的登录时间 小于 现在时间-RR_ALIVE (秒数),则该 Record 视同
离线,所以我们需要进行 delete 动作

代码: [选择]


while [ 1 ]
# 您必需调整路径,放在 while loop 里是要让修改了设定档即可生效,若不要可放在 while 之外
eval `cat /home/abelyang/dyndns/dyndns.cfg`
cat <<EOF > $CMD_FILE
zone $DOMAIN

# 取得最后一次的更新时间, 多减一秒是为了预防程式的 delay
last=`date -d “-$UPD_FREQ seconds -3 seconds” “+%Y%m%d%H%M%S”`
now=`date “+%Y%m%d%H%M%S”`

# 取得这段时间内有上来更新的 USER,这个部份不检查 IP 不变动情形
# 主要因为 nsupdate 巳执行很快,而且 named 它自己会检查重覆更新的东西
echo “select FQDN,TTL,TYPE,RDATA from RR where CREATE_TIME between $last and $now” | $MYSQL| grep -v ‘RDATA’ | while read FQDN TTL TYPE RDATA RDATA2

# 组出更新指令,主要为一个删除,一个增加
echo “update delete $FQDN $TYPE $RDATA $RDATA2″ >>$CMD_FILE
echo “update add $FQDN $TTL $TYPE $RDATA $RDATA2″ >>$CMD_FILE

# 超过1小时未有 login 资料则清除 DNS 记录
last=`date -d “-$RR_ALIVE seconds” “+%Y%m%d%H%M%S”`
# 备份旧的资料,并清除过时资料 (看你自己要不要备份了)
#       echo “insert into RR_LOG select * from RR where CREATE_TIME<$last”|$MYSQL
#       echo “delete from RR where CREATE_TIME < $last” |$MYSQL

# 取得过期 (RR_VAILD) 而未登录的列表进行删除动作,因为我们用了 wildcard (*),所以若别人连时
# 将会被指到 wildcard 所指的 IP 上,而您可这个 Web Server 上做一些文章,例如 Offline 说明等
echo “select USERNAME,FQDN,TYPE,RDATA from RR  where CREATE_TIME – $last < 0 order by USERNAME” | $MYSQL | grep -v ‘USERNAME’ | while read USERNAME FQDN TYPE RDATA RDATA2
echo “; delete $FQDN $TYPE $RDATA $RDATA2″ >>$CMD_FILE
echo “update delete $FQDN $TYPE $RDATA $RDATA2″ >>$CMD_FILE

# 自定更新 SOA, 主要是为了让序号栏位为现在时间 (UTC)
echo “update delete $DOMAIN SOA” >>$CMD_FILE
echo “update add $DOMAIN 600 SOA $(date +%s) 900 60 604800 60″ >>$CMD_FILE
echo “send” >>$CMD_FILE
# 执行 nsupdate 指令
nsupdate $CMD_FILE
#       echo “process ok”
sleep $UPD_FREQ

3.5 登入及更新方法
代码: [选择]

wget “” -O /tmp/dyndns-login-status 2>/dev/null

在最多等待15秒(我的预设值)的情况下,就可以更新到 DNS 中了

3.6 其他资料
其他资料如 named.conf , zone file 您都可以在前面的说明里找到,我于下面 link
放了一份所有的资料供大家参考,较不用费事 copy & paste ,但不保证下面 link 永远有效 (其中的
.tgz 即有所有档案的 tarball)

4. DDNS 再探讨
如前言所言, DDNS 可以使用资料库来用 (意即我的范例中可以少掉 那隻),不过资料库
因其先天的状况,更据我的测试(PowerDNS),在 5 万资 Record 的情况下,只能到达每秒 1000 次的查询,
而且此时尚不考量同时有 update/delete/insert 等情形,主要因为受限于先天 DB 的 select 速度所致
,当然您可以透过微调或细部处理让这个数字变成1500 或 2000, 但都永不如 BIND 随便都可以透过每秒
6000 次查询,当然用 DB 直接来做一定是可以且更简单的,不过安全性及抗压性 PowerDNS 是随时都会有
当掉的风险,至于用 BIND 倒是没有看过,主要是因为这种 Server 肯定是不递迴(recursion no). 所以
著名的 DDNS 厂商都是用 BIND 而不用 DB 方式,因其抗压性不足而致风险过高.

此外,若我们看 的做法,可以知道他们也是用 BIND 来做,你可以查询其 SOA 的序
号即可以知道他每60秒更新一次,若是使用 DB 来做是没有必要顾虑序号问题的 (DB 有 DB 同步方法,用
SOA 序号无关),此外您更可以查看 .com 的 Verisign,他们的做法也是像 BIND 一样,而其以每15秒更新
频率在进行,所以若您使用 .com 的域名,变更DNS 大概只要15秒就可以同步到所有的 .com NameServer,
而不是过去的2天 (因为过去是 AXFR,现在是 IXFR),虽然Verigisn 仍不降低 NS 记录的 TTL 值 (二天),
但至少不会发生像过去最多会4天 .com 的 DNS 资料 Cache 才会过期的情况 (二天的更新频率+二天的

代码: [选择]

# 检查 .com 的 NameServer ( SOA 资讯来验证
[root@eai1 example]# for i in `seq 1 1000`;do dig +short com soa;sleep 1;done 1153990708 1800 900 604800 900 1153990708 1800 900 604800 900 1153990708 1800 900 604800 900 1153990723 1800 900 604800 900 # 这里变更了, serial 即时间 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990723 1800 900 604800 900 1153990738 1800 900 604800 900 # 变更序号 1153990738 1800 900 604800 900 1153990738 1800 900 604800 900

5. 结语
我所写的 script 都是以简单的角度来出发,以供大家参考,至于用户及域名管理这个有待想要用的人自行
开发,用 shell script 有利于多数人阅读及了解原理,细节的东西唯有您自己做了后才更能体会.
对多数的公司来说使用 DDNS 是没有意义的,除了 Registy (.com/cn/tw/jp/hk…),或是以 DDNS 做为营
运的公司,而 DDNS其实是不难的,但是网路上较缺乏这方面的介绍文章,所以在此为大家介绍一下这些东西
的细节, 以利想要研究的朋友能初窥门径. 有任何意见都非常欢迎大家多多交流



nsupdate [ -d ] [ [ -y keyname:secret ] [ -k keyfile ] ] [ -v ] [ filename ]
-d 调试模式。
-k 从keyfile文件中读取密钥信息。
-y keyname是密钥的名称,secret是以base64编码的密钥。
-v 使用TCP协议进行nsupdate.默认是使用UDP协议。
server servername [ port ]
> server 53
local address [ port ]
zone zonename
class classname
key name secret
prereq nxdomain domain-name
prereq yxdomain domain-name
prereq nxrrset domain-name [ class ] type
prereq yxrrset domain-name [ class ] type
update delete domain-name [ ttl ] [ class ] [ type [ data… ] ]
update add domain-name ttl [ class ] type data…


修改named.conf中 zone添加
allow-update { any; };


[plain] view plain copy

> server
> update add 6000 IN A
> send
> quit


[plain] view plain copy

dig @

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.10.rc1.el6 <<>> @
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39140
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;             IN      A

;; ANSWER SECTION:      6000    IN      A

;; AUTHORITY SECTION:               86400   IN      NS

;; ADDITIONAL SECTION:           86400   IN      A

;; Query time: 1 msec
;; WHEN: Sat Apr 18 22:21:43 2015
;; MSG SIZE  rcvd: 85


3.通过TSIG key实现nsupdate功能
1、使用dnssec-keygen -a HMAC-MD5 -b 128 -n USER testkey命令来生成密钥。
-a HMAC-MD5:采用HMAC-MD5加密算法。
-b 128:生成的密钥长度为128位。
-n USER testkey:密钥的用户名为testkey。
cat ***.+157+xxx.key
cat ***.+157+xxx.private
6、将test.com区域中的allow-update { none; }中的“none”改成“key testkey”;
将“none”改成“key testkey”的意思是指明采用“key testkey”作为密钥的用户可以动态更新“”区域。
[plain] view plain copy

type master;
file “”;

命令是:nsupdate -y test2key:epYaIl5VMJGRSG4WMeFW5g== (或 -k /etc/test_key)
>update add 86400 A
返回错误结果:update failed: REFUSED
错误分析:执行nsupdate的主机地址包含在acl1中, 进入了view “view-test”执行更新,显然key不匹配,被拒绝。
解决办法:在view “view-test”的match-clients{}中将执行nsupdate主机的地址(bind主机地址)排除掉,
命令:match-clients{ key testkey; !; !x.x.x.x; acl1;} //x.x.x.x填写bind主机真实ip地址


December 17, 2014
I blogged about this topic before. This post shows a slightly different way of using nsupdate remotely against a DNS server running BIND 9 in order to programatically update DNS records. The scenario I am describing here involves an Ubuntu 12.04 DNS server running BIND 9 and an Ubuntu 12.04 client running nsupdate against the DNS server.

1) Run ddns-confgen and specify /dev/urandom as the source of randomness and the name of the zone file you want to dynamically update via nsupdate:

$ ddns-confgen -r /dev/urandom -z

# To activate this key, place the following in named.conf, and
# in a separate keyfile on the system or systems from which nsupdate
# will be run:
key “” {
algorithm hmac-sha256;
secret “1D1niZqRvT8pNDgyrJcuCiykOQCHUL33k8ZYzmQYe/0=”;

# Then, in the “zone” definition statement for “”,
# place an “update-policy” statement like this one, adjusted as
# needed for your preferred permissions:
update-policy {
grant zonesub ANY;

# After the keyfile has been placed, the following command will
# execute nsupdate using this key:
nsupdate -k <keyfile>

2) Follow the instructions in the output of ddns-keygen (above). I actually named the key just ddns-key, since I was going to use it for all the zones on my DNS server. So I added this stanza to /etc/bind/named.conf on the DNS server:

key “ddns-key” {
algorithm hmac-sha256;
secret “1D1niZqRvT8pNDgyrJcuCiykOQCHUL33k8ZYzmQYe/0=”;

3) Allow updates when the key ddns-key is used. In my case, I added the allow-update line below to all zones that I wanted to dynamically update, not only to

zone “” {
type master;
file “/etc/bind/zones/”;
allow-update { key “ddns-key”; };

At this point I also restarted the bind9 service on my DNS server.

4) On the client box, create a text file containing nsupdate commands to be sent to the DNS server. In the example below, I want to dynamically add both an A record and a reverse DNS PTR record:

$ cat update_dns1.txt
debug yes
update add 3600 A
update add 3600 PTR

Still on the client box, create a file containing the stanza with the DDNS key generated in step 1:

$ cat ddns-key.txt
key “ddns-key” {
algorithm hmac-sha256;
secret “Wxp1uJv3SHT+R9rx96o6342KKNnjW8hjJTyxK2HYufg=”;

5) Run nsupdate and feed it both the update_dns1.txt file containing the commands, and the ddns-key.txt file:

$ nsupdate -k ddns-key.txt -v update_dns1.txt

You should see some fairly verbose output, since the command file specifies ‘debug yes’. At the same time, tail /var/log/syslog on the DNS server and make sure there are no errors.

In my case, there were some hurdles I had to overcome on the DNS server. The first one was that apparmor was installed and it wasn’t allowing the creation of the journal files used to keep track of DDNS records. I saw lines like these in /var/log/syslog:

Dec 16 11:22:59 dns1 kernel: [49671335.189689] type=1400 audit(1418757779.712:12): apparmor=”DENIED” operation=”mknod” parent=1 profile=”/usr/sbin/named” name=”/etc/bind/zones/” pid=31154 comm=”named” requested_mask=”c” denied_mask=”c” fsuid=107 ouid=107
Dec 16 11:22:59 dns1 kernel: [49671335.306304] type=1400 audit(1418757779.828:13): apparmor=”DENIED” operation=”mknod” parent=1 profile=”/usr/sbin/named” name=”/etc/bind/zones/” pid=31153 comm=”named” requested_mask=”c” denied_mask=”c” fsuid=107 ouid=107

To get past this issue, I disabled apparmor for named:

# ln -s /etc/apparmor.d/usr.sbin.named /etc/apparmor.d/disable/
# service apparmor restart

The next issue was an OS permission denied (nothing to do with apparmor) when trying to create the journal files in /etc/bind/zones:

Dec 16 11:30:54 dns1 named[32640]: /etc/bind/zones/ create: permission denied
Dec 16 11:30:54 dns named[32640]: /etc/bind/zones/ create: permission denied

I got past this issue by running

# chown -R bind:bind /etc/bind/zones

At this point everything worked as expected.

快乐成长 每天进步一点点