DNS背景1
DNS ( Domain Name Service) 作为一个非常让人耳熟能详的概念,可以把复杂难记的IP地址转化成为一个容易被记住域名供人们去调用,这样人们只需要记住每一个网站的域名而不需要记住非常复杂的IP地址。此外,由于网络组网通讯的局限性,不能将所有的网站的IP地址一一规划用,因此 DNS 也提供了网络地址转换的相关功能。
DNS Zone,DNS域,被用来划分DNS主域。传统的DNS域类似一个树状的结构,被分成不同的区域,这些区域可区分一个DNS服务器中命名空间中不同的区域。
DNS区域是构成DNS命名空间的一部分,由特定组织或管理员加以管理,其可以对权威性域名服务器等DNS组件更加精细的控制。 域名空间是一个分层数,其中DNS的根域位于顶部,DNS区域始于该树中的一个域,并且可以扩展到下边的子域。
一,序论
本文介绍一种利用Bind 9与PowerDNS在内网环境搭建一个可以进行递归查询的DNS架构,结构按现实互联网DNS部署。
在此内部网络中我们创建了两个私有的顶级域,“.ses”与“.g”。
我们部署了根服务器(A)、顶级域.ses服务器(B1)、.g根服务器(B2)、二级域.uk.ses根服务器(C),详细信息见下表。
服务器名称 | 操作系统 | IP | 部署软件 | 负责解析域 |
---|---|---|---|---|
根服务器(A) | Ubuntu 22 | 10.10.254.5 10.10.254.6 | Bind 9 | . |
.ses域根NS(B1) | Ubuntu 20 | 10.10.254.9 10.10.254.10 | Power DNS | ses. |
.g 域根NS(B2) | Ubuntu 20 | 同上 | Power DNS | g. |
.uk.ses域根NS(C) | Ubuntu 20 | 10.10.254.20 | Bind 9 | .uk.ses. |
本文主要介绍的是一套纯内网的DNS架构,架构体系为下图1-1所示,如果需要出网配置,建议找一台既有内网又有外网权限的服务器作为根服务器。
1.1 DNS软件选择
BIND(Berkeley Internet Name Domain)作为一款目前市面是最主流的开源DNS软件,占据了市面上DNS服务器软件的九成,目前由互联网系统协会 (Internet Systems Consortium) 负责开发和维护。目前,最新的稳定版BIND已经更新到了 BIND 9 版本。BIND 9作为一个轻量级的DNS软件,可谓是麻雀虽小五脏俱全,引用BIND官网的介绍:
BIND 9 has evolved to be a very flexible, full-featured DNS system. Whatever your application is, BIND 9 probably has the required features. As the first, oldest, and most commonly deployed solution, there are more network engineers who are already familiar with BIND 9 than with any other system.
根据结构,根服务器(A)只负责记录.ses与.g的根域名服务器地址,不需要处理其他任何请求,所以BIND 9就是一个极佳的选择。
Power DNS(PDNS)成立于20世纪90年代末,是开源DNS软件、服务和支持的主要供应商,它们提供的权威认证DNS服务器和递归认证DNS服务器都是100%开源的软件,同时也和红帽等开源方案提供商一样提供了付费的技术支持版本。同时官方表示为了避免和软件使用者出现竞争,他们只提供服务支持而不提供DNS托管服务。
.ses与.g的根服务器都需要记录各自域内的二级或三级域名的解析记录,所以一个方便便捷的,可以直接在Web页面进行修改的DNS服务器就很有成效,所以在这里我们选择了PowerDNS作为顶级域DNS服务器B1与B2的服务提供软件。
1.2 部署方式
为了方便部署,并且减少运维成本,我们选择通过Docker部署配置PowerDNS,物理机(Ubuntu)直接安装BIND 9的方式部署整套DNS系统。
二,详细部署
2.1 根服务器 (A)
根服务器(A)只负责存储ses.域、g.域的根服务器(B1、B2)信息,不向下进行递归查询,故配置起来比较简单。
2.1.1 安装BIND 9
根服务器A我们选用的是BIND 9作为DNS服务器,在Ubuntu系统上,直接执行命令:
apt install bind9
就可以完成BIND 9的安装。
2.1.2 关闭systemd-resolved占用的53端口(Debian发行版操作)
注意:在部分Debian的发行版中,systemd-resolved服务会占用系统53端口,所以需要多操作以下几个步骤去取消此服务对53端口的占用。
首先执行命令查看53端口是否被此服务占用:
netstat -tunlp | grep 53
如果查看到有53端口被systemd-resolved占用,则继续下面的步骤,如果没有软件占用53端口则直接跳过本节。
编辑文件:
vi /etc/systemd/resolved.conf
在文件中有一行为 #DNSStubListener=yes ,将这行的注释取消并改为no,修改完效果为 DNSStubListener=no。同时在“DNS=”这个参数中添加几个DNS服务器,需要修改的部分如下:
[Resolve]
DNS=1.1.1.1 223.5.5.5 8.8.8.8
DNSStubListener=no
修改完毕后保存文件,并重启此服务:
systemctl restart systemd-resolved.service
执行命令查看53端口是否没有软件在监听:
netstat -tunlp | grep 53
如果没有看到有软件监听53端口,占用问题就已经解决了。
2.1.3 配置BIND 9
BIND 9的配置文件路径为 /etc/bind
,在此路径下,我们需要关注的文件有以下几个:
- named.conf
- named.conf.local
- named.conf.options
named.conf:此文件是BIND 9的默认配置文件,为了方便区分各个管理文件的作用,在此文件里面都是include其他配置文件
named.conf.local: 此文件是用来添加用户私有配置的配置文件
named.conf.options: 此文件是用来修改一些关于BIND 9软件配置的文件
首先我们需要先创建一个根域“.”,在/etc/bind目录下创建文件db.root并编辑其内容。
db.root 配置如下:
$TTL 86400
. IN SOA ns1.root ns.root (
2024052806 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Negative Cache TTL
. IN NS ns1.root.
. IN NS ns2.root.
ns1.root IN A 10.10.254.5
ns2.root IN A 10.10.254.6
; ses. domain section
ses. IN NS ns1.ses.
ses. IN NS ns2.ses.
ns1.ses. IN A 10.10.254.9
ns2.ses. IN A 10.10.254.10
; g. domain section
g. IN NS ns1.g.
g. IN NS ns2.g.
ns1.g. IN A 10.20.254.9
ns2.g. IN A 10.20.254.10
在 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";
zone "." {
type master;
file "/etc/bind/db.root";
};
由于我们这台服务器是作为根服务器存在,所以我们不需要现实中的根作为我们的上级服务器,所以我们需要在 named.conf 中注释掉 /etc/bind/named.conf.default-zones 引用部分:
// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian 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";
同时为了方便,我们并没有配置DNSSEC,所以我们需要关闭DNSSEC检测功能,同时我们需要允许下游服务器的IP访问此DNS服务,修改 named.conf.options 文件:
options {
directory "/var/cache/bind";
allow-query-cache { 10.0.0.0/8; };
qname-minimization off;
dnssec-validation no;
listen-on-v6 { any; };
};
以上操作完毕后重启BIND 9:
service named restart
至此,根服务器(A)就已经配置完毕。
2.1.4 验证配置
此时我们使用dig工具对我们的配置进行检验。
dig(域信息搜索器)命令是一个用于询问 DNS 域名服务器的灵活的工具。它执行 DNS 搜索,显示从受请求的域名服务器返回的答复。多数 DNS 管理员利用 dig 作为 DNS 问题的故障诊断,因为它灵活性好、易用、输出清晰。
在一台已经加入此子网的设备中执行dig命令,查询根域的DNS信息:
dig ns @10.10.254.5 .
通过以上查询我们能够查询到根域的NS信息如下:
在这里我们可以看到已经成功查询到根域的NS服务器信息,我们继而利用dig工具查询ses.域的NS配置是否正确,查询命令如下:
dig ns @10.10.254.5 ses.
通过以上查询我们能够查询到ses.域的NS信息如下:
至此我们已经配置完毕根域名服务器,并且成功创建ses.和g.顶级域,接下来我们就要开始配置ses.和g.顶级域的NS服务器了。
2.2 顶级域NS(B1、B2)
顶级域的NS服务器选用的是PowerDNS,由于PowerDNS所需要的组件比较多,我们在这里就采用Docker的方式进行部署。当然,您也可以通过包管理软件部署,详细部署流程可根据您的需求变化,配置上的内容都是一样的。
!!!如果是在Debian的发行版下运行,请重复2.1.2步骤解除53端口占用问题!!!
2.2.1 PowerDNS安装2
我们为了方便管理,我们采用Docker-compose进行安装,所需要的配置文件结构如下:
./docker-compose.yml
./pdns/config/pdns.conf
./pdnsdb/init-scripts/init.sql
在docker-compose文件中,配置了pdnsadmin的web端口为9191,各位可根据自身需求修改。
docker-compose.yml 配置为:
version: '3'
services:
pdnsdb:
image: mysql:5.7.35
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: pdns
MYSQL_USER: pdns
MYSQL_PASSWORD: pdns123
volumes:
- ./pdnsdb/data:/var/lib/mysql
- ./pdnsdb/init-scripts:/docker-entrypoint-initdb.d
networks:
- pdns_net
pdns:
image: powerdns/pdns-auth-47:4.7.4
restart: always
user: root
privileged: true
environment:
SECRET_KEY: qazwsxedcrfvtgb
ports:
- "53:53/tcp"
- "53:53/udp"
volumes:
- ./pdns/config:/etc/powerdns
depends_on:
- pdnsdb
networks:
- pdns_net
pdnsadmindb:
image: mysql:5.7.35
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: pdnsadmin
MYSQL_USER: pdnsadmin
MYSQL_PASSWORD: pdnsadmin123
volumes:
- ./pdnsadmindb/data:/var/lib/mysql
networks:
- pdns_net
pdnsadmin:
image: powerdnsadmin/pda-legacy:latest
restart: always
ports:
- "9191:80"
environment:
- SQLALCHEMY_DATABASE_URI=mysql://pdnsadmin:pdnsadmin123@pdnsadmindb/pdnsadmin
- GUNICORN_TIMEOUT=60
- GUNICORN_WORKERS=2
- GUNICORN_LOGLEVEL=DEBUG
depends_on:
- pdns
- pdnsadmindb
networks:
- pdns_net
networks:
pdns_net:
driver: bridge
./pdns/config/pdns.conf 的配置为:
api=yes
api-key=qwerasdf
launch=gmysql
gmysql-host=pdnsdb
gmysql-port=3306
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=pdns123
local-address=0.0.0.0
local-port=53
webserver=yes
webserver-address=0.0.0.0
webserver-allow-from=0.0.0.0/0
webserver-port=8081
enable-lua-records=yes
slave=yes
allow-notify-from=10.10.254.5/32
allow-axfr-ips=10.10.254.5/32
./pdnsdb/init-scripts/init.sql 配置为:
use pdns;
CREATE TABLE domains (
id INT AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
master VARCHAR(128) DEFAULT NULL,
last_check INT DEFAULT NULL,
type VARCHAR(8) NOT NULL,
notified_serial INT UNSIGNED DEFAULT NULL,
account VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
options VARCHAR(64000) DEFAULT NULL,
catalog VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE UNIQUE INDEX name_index ON domains(name);
CREATE INDEX catalog_idx ON domains(catalog);
CREATE TABLE records (
id BIGINT AUTO_INCREMENT,
domain_id INT DEFAULT NULL,
name VARCHAR(255) DEFAULT NULL,
type VARCHAR(10) DEFAULT NULL,
content VARCHAR(64000) DEFAULT NULL,
ttl INT DEFAULT NULL,
prio INT DEFAULT NULL,
disabled TINYINT(1) DEFAULT 0,
ordername VARCHAR(255) BINARY DEFAULT NULL,
auth TINYINT(1) DEFAULT 1,
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE INDEX nametype_index ON records(name,type);
CREATE INDEX domain_id ON records(domain_id);
CREATE INDEX ordername ON records (ordername);
CREATE TABLE supermasters (
ip VARCHAR(64) NOT NULL,
nameserver VARCHAR(255) NOT NULL,
account VARCHAR(40) CHARACTER SET 'utf8' NOT NULL,
PRIMARY KEY (ip, nameserver)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE TABLE comments (
id INT AUTO_INCREMENT,
domain_id INT NOT NULL,
name VARCHAR(255) NOT NULL,
type VARCHAR(10) NOT NULL,
modified_at INT NOT NULL,
account VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
comment TEXT CHARACTER SET 'utf8' NOT NULL,
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE INDEX comments_name_type_idx ON comments (name, type);
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);
CREATE TABLE domainmetadata (
id INT AUTO_INCREMENT,
domain_id INT NOT NULL,
kind VARCHAR(32),
content TEXT,
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE INDEX domainmetadata_idx ON domainmetadata (domain_id, kind);
CREATE TABLE cryptokeys (
id INT AUTO_INCREMENT,
domain_id INT NOT NULL,
flags INT NOT NULL,
active BOOL,
published BOOL DEFAULT 1,
content TEXT,
PRIMARY KEY(id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE INDEX domainidindex ON cryptokeys(domain_id);
CREATE TABLE tsigkeys (
id INT AUTO_INCREMENT,
name VARCHAR(255),
algorithm VARCHAR(50),
secret VARCHAR(255),
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);
创建完毕后,启动Docker:
docker-compose up -d
启动成功后,访问 http://10.10.254.10:9191 ,Docker启动后,需要一段时间去启动各个容器,刚启动后访问是访问不到的,需要等大概2-3分钟再访问就可以了,如果还是访问不到可以看一下pdnsadmin容器的log,看一下是什么问题。如果能够成功访问,则证明已经启动成功。
2.2.2 PowerDNS Web配置
首先访问到 http://10.10.254.10:9191,进入登录页面,点击Register注册用户,PowerDNS第一个注册的用户为最高管理用户,用户信息可自定义。
登录完成后进行API端口的配置,PowerDNS Web是通过操作Power DNS的API进行的。
配置如下:
PowerDNS API URL: http://pdns:8081
PowerDNS API Key: qwerasdf
PowerDNS Version: 4.7.4
配置完毕后,点击Save Settings保存配置,这样我们就完成了Web管理终端的配置,在这里我们就可以创建新的Zone,图形化界面修改解析配置。
2.2.3 根域信息获取
因为此服务器只是我们的一个顶级域的NS服务器,里面并没有存储根域的信息,所以我们需要为他配置一个从根域拉取配置的信息。但是由于PowerDNS Web的逻辑限制,我们没有办法配置添加根域(.),只能够通过命令行操作Power DNS的方式添加。
首先我们需要知道pdns-auth这个容器的ID,获取到ID后我们进入此容器的bash:
docker exec -it <Container ID> /bin/bash
进入容器后,创建一个二级域,此域从10.10.254.5拉取DNS配置:
pdnsutil create-secondary-zone . 10.10.254.5
创建完毕后,我们可以通过重启此容器的方式让Power DNS从根服务器拉取配置。
docker restart <Container ID>
重启完毕后可以通过查看两端的log判断是否拉取成功,拉取成功后即可。
2.2.4 ses.顶级域NS配置
我们需要在Web管理端创建一个ses顶级域,配置见下图。
创建完毕后,在Dashboard中进入此顶级域添加解析,内容见下表。
Name | Type | Status | TTL | Data |
---|---|---|---|---|
@ | SOA | Active | 86400 | ns1.ses. nic.ses. 2024052805 3600 1800 604800 86400 |
@ | NS | Active | 86400 | ns1.ses. |
@ | NS | Active | 86400 | ns2.ses. |
ns1 | A | Active | 86400 | 10.10.254.9 |
ns2 | A | Active | 86400 | 10.10.254.10 |
至此,我们成功配置了ses顶级域的NS服务器,g顶级域的配置与此过程是一样的,就不再赘述了。
2.2.5 验证配置
同样,我们使用dig工具进行配置验证,通过向新搭建的顶级域NS请求根域的信息,判断顶级域NS是否成功与根域通信。
dig ns @10.10.254.10 .
上述命令会返回根域信息如下图,证明成功拉取到根域的信息。
我们继续测试顶级域域ses.是否正确配置,执行命令:
dig ns @10.10.254.10 ses.
可以看出ses根已经成功获取到了。
测试从根节点开始查询是否能够轮询到顶级域ns(可选)
此项测试需要将测试机器的系统DNS修改为10.10.254.10,具体原因请看这篇文章:https://www.cnblogs.com/donggongdechen/p/16053570.html
执行测试命令:
dig ses. +trace
返回信息我们可以看到成功的从根节点开始查询,然后查询到ses域的根,证明配置正常。
总结
在此次搭建DNS服务器的过程中,改变了我对DNS的已有的认知,对DNS有了很深刻的理解,纠正了以往的错误认识。
每个DNS服务器上都存储着一份root.hints,这份文件里面包含了根域服务器的所有信息,由IANA负责维护(跳转链接),现实世界的root.hints的文件长下面的样子:
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . <file>"
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: April 18, 2024
; related version of root zone: 2024041801
;
; FORMERLY NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2
B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b
;
; FORMERLY C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
. 3600000 NS D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
. 3600000 NS E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
. 3600000 NS F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
. 3600000 NS G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
. 3600000 NS H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
. 3600000 NS I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
. 3600000 NS K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
;
; OPERATED BY ICANN
;
. 3600000 NS L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
;
; OPERATED BY WIDE
;
. 3600000 NS M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
; End of file
在这个hints的作用下,如果我们要求从根域查询(就是+trace的那个命令),那么我们的请求会首先向我们设置的DNS服务器发送根域的请求,DNS会将他所存储的root.hints文件返回给我们,我们会按照流程进行向下查询。
举个简单的例子,如果我们访问www.baidu.com,我们设置的设备DNS为223.5.5.5(阿里),我们现在想从根域开始查询www.baidu.com的A地址记录,利用dig命令查询就是:
dig www.baidu.com +trace
整体的流程是:
- 设备向223.5.5.5请求root.hints,也是是存储着根域的信息。223.5.5.5会给我们返回一个在上面的那个root.hints文件所示的响应。
- 设备接收到root.hints后,向其中一个根域名服务器发送请求,问根域名服务器com.这个域我应该去找谁获取地址。根域名服务器接收到我们的请求后,会向我们返回所有com.域的NS服务器。
- 设备接收到com.域的NS服务器后,会向其中一个com.域的NS服务器发送请求,问baidu.com的IP地址是什么?如果com.域的服务器上有baidu.com的A记录,就会直接返回IP地址。但是现实情况是百度有自己的NS服务器,所以我们查询到的结果会是baidu.com这个二级域的NS服务器地址。
- 设备接收到baidu.com.域的NS服务器后,会向此服务器请求www.baidu.com这个三级域的IP地址,到这步,我们就成功的获取到了www.baidu.com的IP地址。
这张图很好的解释了像223.5.5.5这种公共DNS是如何工作的。
学无止境啊
【引用】
- ISC BIND9 – 最详细、最认真的从零开始的 BIND 9 – DNS服务搭建及其原理讲解. https://www.cnblogs.com/doherasyang/p/14464999.html#13-bind ↩︎
- 使用Docker-Compose轻松搭建PowerDNS与PowerDNS-Admin的全栈解决方案. https://blog.csdn.net/sdhzdtwhm/article/details/135910057 ↩︎
很详细的流程,感谢!
不客气
不错不错