根据校内网/公网分流的智能分区解析DNS

问题背景:部署一个服务,想要让北大校内访问的时候解析到北大校内服务器,校外访问解析到阿里云内网穿透服务器,分别达到各自的最优网络路径。

我们不需要跟计算中心PY也可以实现这个目的,因为北大校内网络通过DHCP下发的DNS(162.105.129.88, .122)是计算中心在校内运行的递归DNS,有自己的出口地址,假如我们自己为自己的域名架设权威DNS,我们就可以根据来源DNS请求是否为162.105.x.x来确定是不是北大校内的DNS解析请求,并返回相应的IP地址。

关于递归DNS和权威DNS
DNS是一个多级缓存的去中心化树形系统,每个域名都有自己的“权威DNS”(如ns1.xx.com, xx是cloudflare或者aliyun等),负责定义它的解析记录,而用户会向自己网络内的“递归DNS”(如8.8.8.8等)查询解析记录,递归DNS会查询这个域名的权威DNS是谁,向权威DNS发起请求,并把请求结果交回给客户端,同时在一定时间内缓存这个结果以备后查。递归DNS还可能采用自己的上游递归DNS。

假如在网上搜索“搭建DNS”,搜出来的其实都不是我们想要的,因为“搭建DNS”可以解读成搭建递归DNS(为了绕开运营商DNS污染、去广告、或者IP优选加速等目的)或者搭建权威DNS,而后者这个需求相当小众,很难找出正确的关键词(authoritative dns)。

这样的“见人下菜碟版”权威DNS在中文语境里又叫做智能解析DNS,在英文里叫做split-horizon DNS或者DNS view。阿里云、腾讯云(DNSPod)等都提供这种服务,但是都需要购买非常昂贵的专业版付费套餐,免费版和基础付费版只提供基于运营商的简单分区,不能用自定义来源IP匹配规则。其实,自行搭建一个这样的权威DNS相当简单。

接下来我们以域名git.cslabs.cn为例介绍一下详细的步骤。在实际实现中,我们cslabs.cn域名本身的权威DNS仍然用的阿里云,但是把.servers.cslabs.cn这个二级域名下的解析转交我们自己的权威DNS。具体而言,阿里云那里使用了这些解析记录:

ns IN  A  8.131.230.227
servers IN  NS  ns.cslabs.cn
git IN  CNAME  macmini.servers.cslabs.cn

这样一来,git.cslabs.cn是递归由macmini.servers.cslabs.cn定义的,进而落入了我们自己的权威DNS服务接管。
接下来,在我们自己的服务器上(用安全组或者防火墙)开启UDP 53端口,安装bind9:

apt install bind9 bind9utils
systemctl enable --now bind9

再接下来开始配置。

/etc/bind/named.conf

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the 
// structure of BIND configuration files in Debian, *BEFORE* you customize 
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";

// include "/etc/bind/named.conf.default-zones";

/etc/bind/named.conf.local

//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";

acl pku {
    162.105.0.0/16;
    202.112.7.0/24;
    202.112.8.0/24;
    222.29.0.0/17;
    222.29.128.0/19;
    115.27.0.0/16;
};

view pku {
    match-clients { pku; };
    allow-query { any; };
    include "/etc/bind/named.conf.default-zones";

    zone "cslabs.cn" {
        type master;
        file "/etc/bind/zones/cslabs.cn/db-pku.cslabs.cn";
    };
};

view external {
    match-clients { any; };
    allow-query { any; };
    include "/etc/bind/named.conf.default-zones";

    zone "cslabs.cn" {
        type master;
        file "/etc/bind/zones/cslabs.cn/db.cslabs.cn";
    };
};

/etc/bind/zones/cslabs.cn/db.cslabs.cn

$TTL 600
@   IN  SOA ns.cslabs.cn. webmaster.cslabs.cn. (
       2025110101 ; Serial
       3600       ; Refresh
       1800       ; Retry
       1209600    ; Expire
       86400 )    ; Minimum TTL


; This is a comment line


; Nameservers
   IN  NS  ns.cslabs.cn.


; "A" records
macmini.servers   IN  A   8.131.230.227

; additional records
ns IN  A  8.131.230.227

/etc/bind/zones/cslabs.cn/db-pku.cslabs.cn

$TTL 600
@   IN  SOA ns.cslabs.cn. webmaster.cslabs.cn. (
       2025110101 ; Serial
       3600       ; Refresh
       1800       ; Retry
       1209600    ; Expire
       86400 )    ; Minimum TTL


; This is a comment line


; Nameservers
   IN  NS  ns.cslabs.cn.


; "A" records
macmini.servers   IN  A   10.129.145.123

; additional records
ns IN  A  8.131.230.227

检查配置是否正确:

named-checkconf
named-checkzone cslabs.cn /etc/bind/zones/cslabs.cn/db.cslabs.cn
named-checkzone cslabs.cn /etc/bind/zones/cslabs.cn/db-pku.cslabs.cn

然后重启服务生效:

systemctl restart bind9

测试效果:从校内

zizhengguo@amax:~$ dig A git.cslabs.cn

; <<>> DiG 9.18.39-0ubuntu0.22.04.2-Ubuntu <<>> A git.cslabs.cn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50255
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;git.cslabs.cn.			IN	A

;; ANSWER SECTION:
git.cslabs.cn.		600	IN	CNAME	macmini.servers.cslabs.cn.
macmini.servers.cslabs.cn. 600 IN	A	10.129.145.123

;; Query time: 12 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Wed Jan 28 19:01:38 CST 2026
;; MSG SIZE  rcvd: 88

从校外

root@zzcloud2026:~# dig A git.cslabs.cn

; <<>> DiG 9.18.24-0ubuntu5-Ubuntu <<>> A git.cslabs.cn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32671
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;git.cslabs.cn.			IN	A

;; ANSWER SECTION:
git.cslabs.cn.		600	IN	CNAME	macmini.servers.cslabs.cn.
macmini.servers.cslabs.cn. 600	IN	A	8.131.230.227

;; Query time: 533 msec
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; WHEN: Wed Jan 28 10:37:47 UTC 2026
;; MSG SIZE  rcvd: 88