扫一扫
关注微信公众号

用BIND架设DDNS Server提供DDNS服务
2006-08-07   chinaunix

1.前言

目前动态 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 所做的讲义:

http://dns-learning.twnic.net.tw/DNS94/

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

http://www.study-area.org/tips/tipsfr1.htm

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

http://www.isc.org/sw/bind/arm93/Bv9ARM.pdf

本文不讨论 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 对有些朋友来说可能会变得稍复杂些了

CODE:[Copy to clipboard]# named.conf

# 其它略

zone "dyndns.twnic.tw" {

type master;

file "dyndns.twnic.tw";

allow-update {127.0.0.1;}; # 开放 127.0.0.1 进行动态更新

allow-transfer { slave_ip;127.0.0.1;}; # slave 主机,可能一部或多部,若无请写 none

};

以上是开放让 127.0.0.1 进行动态更新,动态更新有其指令,详细您可看看 nsupdate man page

(man nsupdate),以下仅以最常用的进行说明:

CODE:[Copy to clipboard]#nsupdate

[root@eai1 dyndns]# nsupdate

> server 127.0.0.1

> zone dyndns.twnic.tw

> update delete user1.dyndns.twnic.tw A 211.72.210.249

> update add user1.dyndns.twnic.tw A 211.72.210.251

ttl 'A': not a valid number # 这个例子是错误示范,加一笔记录要有 TTL 值

> update add user1.dyndns.twnic.tw 60 A 211.72.210.251

> show

Outgoing update query:

;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 0

;; flags: ; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0

;; UPDATE SECTION:

user1.dyndns.twnic.tw. 0 NONE A 211.72.210.249

user1.dyndns.twnic.tw. 60 IN A 211.72.210.251

> send

> quit

CODE:[Copy to clipboard]server 指向某一台 NameServer 进行 update 操作

zone 修改某个 zone file

update delete 进行 update 的 delete 动作,这个指令格式是

update delete FQDN TYPE RDATA,如果有多笔相同的 A

记录或不同的 MX 记录要删除某一笔需将 RDATA 补上,

若没有 RDATA 则表示这个 FQDN 的这个 TYPE 都要删除

(TYPE 即是 A,MX,PTR,SOA,NS..等,RDATA 就是 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 状况等)

CODE:[Copy to clipboard]# 显示 dyndns.twnic.tw zone file 内容

[root@eai1 named]# cat /var/named/dyndns.twnic.tw

$TTL 86400 ; 1 day

@ IN SOA twnic.net.tw. snw.twnic.net.tw. (

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)

)

NS ns2.dyndns.twnic.tw.

NS eai1.twnic.tw.

ns2 A 203.73.24.204

; 如果别人在查询时,我们有这个记录则响应这个记录的 IP,若没有这个记录代表

; 这个网站没有上线,所以此时我们可以建立一笔 wildcard 记录,指向自己的说

; 明网站,以供 user 识别这个网站没有上线,而这笔 wildcard 的记录 TTL 时间

; 不能太长,以免别的 DNS Cache 了这资料

* 0 A 211.72.210.251

# 目录里的东西

[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 dyndns.twnic.tw

-rw-r--r-- 1 named named 104177 7月 27 10:01 dyndns.twnic.tw.jnl

-rw-r--r-- 1 named named 195 7月 4 2001 localhost.zone

-rw-r--r-- 1 named named 2851 10月 17 2003 named.ca

我们可以看到这个 .jnl 的产生,这个 .jnl 档是不能随便删除,因为它等于是 dyndns.twnic.tw 的补

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

CODE:[Copy to clipboard][root@eai1 dyndns]# dig @127.0.0.1 dyndns.twnic.tw axfr

; <<>> DiG 9.3.0 <<>> @127.0.0.1 dyndns.twnic.tw axfr

;; global options: printcmd

dyndns.twnic.tw. 86400 IN SOA twnic.net.tw. snw.twnic.net.tw. 2006073528 7200 1800 2419000 300

dyndns.twnic.tw. 86400 IN NS ns2.dyndns.twnic.tw.

dyndns.twnic.tw. 86400 IN NS eai1.twnic.tw.

*.dyndns.twnic.tw. 0 IN A 211.72.210.251

ns2.dyndns.twnic.tw. 86400 IN A 203.73.24.204

user1.dyndns.twnic.tw. 60 IN A 211.72.210.248

user1.dyndns.twnic.tw. 60 IN MX 10 user1.dyndns.twnic.tw.

# 以下略

...

2.设定 NameServer 仅进行差异化的同步

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

CODE:[Copy to clipboard]# master DNS's named.conf

key "rndc-key" {

algorithm hmac-md5;

secret "HpXtFRFdLaRPFjpZokIwusyezyyRNjxhcafCfmktWNyGkDFzHAXlpTZQtVLc";

};

controls {

inet 127.0.0.1 port 953

allow { 127.0.0.1; } keys { "rndc-key"; };

};

options {

directory "/var/named";

pid-file "/var/run/named/named.pid";

allow-transfer { none; };

provide-ixfr yes; # 提供 slave 主机以 IXFR 同步,default yes

request-ixfr yes; # slave 以 IXFR 向 master 进行同步,default yes

recursion no; # 不允许递归查询

};

zone "0.0.127.in-addr.arpa" {

type master;

file "named.local";

};

zone "." {

type hint;

file "named.ca";

};

zone "dyndns.twnic.tw" {

type master;

file "dyndns.twnic.tw";

max-journal-size 500k; # 设定日志文件大小

allow-transfer { 203.73.24.204;127.0.0.1;}; # 可做 ixfr/axfr 的来源 IP,必需写上 slave,

# 127.0.0.1 是方使我们自己查看 zone file 现况

also-notify {203.73.24.205;211.72.210.251}; # 额外的同步主机,这可能是 hot site 备份主机

allow-update { 127.0.0.1;}; # 允许动态更新的来源

};

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

CODE:[Copy to clipboard]# slave DNS's named.conf

# 其它设定皆同上,只有 zone ...稍有不同,同于一般的 slave zone,不需要再开 allow-update (default none)

zone "dyndns.twnic.tw" {

type slave;

masters {211.72.210.249;};

file "dyndns.twnic.tw";

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 , 这种做法也是可以的,不过中间若漏了一步或那一台少做了一件事,那两边的数据就会不一致,导致可能同一个名称会有不同的解析结果 (例如 gnway.net 做法)

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

说是范例主要是让大家参考原理,并没有必要一定都用我的方式,只要前面讲的东西您可以了解,程控的部份仅是末节,以下仅列出我所用的方式供大家参考

3.1 MYSQL table

主要由三个表构成,分别为 RR (Resource Record), RR_LOG (旧资料,建议您依状况适当保存),USER (user 认证)

CODE:[Copy to clipboard]CREATE TABLE RR (

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,

PRIMARY KEY (SN),

KEY USERNAME (USERNAME),

KEY FQDN (FQDN),

KEY TTL (TTL),

KEY TYPE (TYPE),

KEY CREATE_TIME (CREATE_TIME)

) TYPE=MyISAM;

--

-- Table structure for table 'RR_LOG'

--

CREATE 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,

PRIMARY KEY (SN),

KEY USERNAME (USERNAME),

KEY FQDN (FQDN),

KEY CREATE_TIME (CREATE_TIME)

) TYPE=MyISAM;

--

-- Table structure for table 'USER'

--

CREATE 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 '',

PRIMARY KEY (SN),

UNIQUE KEY USERNAME (USERNAME)

) TYPE=MyISAM;

3.2 dyndns.cfg 设定档

这个设定文件主要为了给 CGI 程序及产生 nsupdate 的程序 (dyndns-cron.sh) 所使用,透过 eval 方式来执行,

以取得共同的变量

CODE:[Copy to clipboard]# mysql host/db/user/password

DBHOST=localhost

DBNAME=dyndns

DBUSER=UserName

DBPASS=Your_Passwd

MYSQL="mysql $DBNAME -h $DBHOST -u $DBUSER -p$DBPASS"

# dyndns domain

DOMAIN=dyndns.twnic.tw

# Master IP

DYNDNS_MASTER=127.0.0.1

# nsupdate command file

CMD_FILE=/tmp/nsupdate.cmd

# update freqency

UPD_FREQ=15

# RR valid time (seconds),default 20 mins

RR_ALIVE=1200

3.3 dyndns.cgi CGI 程序

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

CODE:[Copy to clipboard]#!/bin/sh

echo -ne "Content-Type: text/html "

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"

else

# 取得 IP, 需判断有 Proxy 存在,但是不考虑 Proxy 后是 NAT 情形

IP=${HTTP_X_FORWARDED_FOR:-$REMOTE_ADDR}

FQDN="$LOGIN.$DOMAIN"

# 删除上一次的登入

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

fi

else

# 以下只是网页的部份,我没有做 DDNS 申请,这个部份我想只要懂网页的朋友应该都会才是

cat <

热词搜索:

上一篇:LAMP:Linux+Apache+Mysql+PHP典型配置
下一篇:教您五种经典方式来巧妙设置路由器(1)

分享到: 收藏