几种搭建内网穿透环境的方案及实现(二):bind9搭建私有dns服务器并实现ddns
通过前面章节的介绍,我们了解了内网穿透的各种方案,本文将着重于利用bind9搭建的方式来实现内网穿透。整个搭建方案分为三部分:1、配置dnspod解析记录;2、利用bind9搭建私有dns服务器;3、ddns实现脚本;
配置dns供应商的解析记录(以dnspod为例)
1、添加A记录,将域名dns.domain.com解析至服务器的ip地址,即将该域名指向dns服务器:
2、添加NS记录,将webdav.domain.com的解析服务器设定为1中设置的dns服务器
利用上面的两步即可将域名webdav.domain.com交由自己的dns服务器进行解析。
对解析记录不了解的可以参考: 域名解析的记录类型:A记录、CNAME…
利用bind9搭建私有dns服务器(以ubuntu系统为例)
输入以下命令安装bind9:
sudo apt install bind9
bind9的默认路径如下:
配置文件
/etc/resolv.conf # 配置解析服务器的地址
/etc/bind/named.conf # 主配置文件,自动include下面两个配置文件中的信息(.local/.option)
/etc/bind/named.conf.local # 列出了所有区域数据文件(.zone)的信息
/etc/bind/named.conf.option # 定义选项
区域数据文件
/var/cache/bind/ # 工作路径,用于存放.zone的区域配置文件
/var/cache/bind/kasumiksm.com.zone # 定义区域域名解析的具体参数
# 文件名命名格式无限定
/var/cache/bind/ip.zone # 定义反向解析时的具体参数
# 文件名命名格式无限定
/etc/resolv.conf
nameserver 127.0.0.1 # 设置本地采用的域名解析服务器地址。
# 欲利用nslookup查询bind9设置情况时需要将其设置为127.0.0.1
# 但是当设置为127.0.0.1地址后,本所有dns流量均被其劫持可能会导致无法上网,
# 正确的方式是添加在设置为127.0.0.1后,在添加如下的一条规则
nameserver 8.8.8.8
# 但是在该文件中配置的信息,在重启systemd-resolved服务后均将失效
永久修改的方法如下:
# 在/etc/network/interfaces文件中添加配置即可
dns-nameservers 8.8.8.8
dns-nameservers 127.0.0.1
关于防止/etc/resolv.conf被失效的方法详情见: ubuntu解决resolv.conf被重写问题
/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";
//key "ddns" {
// algorithm hmac-md5;
// secret "GAgsKfYTueMc4lawB8qKzg==";
//};
zone "kasumiksm.com" IN {
type master;
file "/var/cache/bind/kasumiksm.com.zone";
};
// 添加一个正向解析的区域,当需要查询的域名的根域名为kasumiksm.com时
// 均会查询该区域。即不可将携带其他根域名的域名放在该区域。
// 如需让该区域负责所有.com结尾的域名时,将"kasumiksm.com"改为
// "com"即可(未测试)
zone "117.1.in-addr.arpa" IN {
type master;
file "/var/cache/bind/ip.zone";
};
// 添加一个反向解析的区域,并非必须的区域,当需要查询的ip的头几位为1.117时
// 将进入该区域查询,勿将其他开头的ip地址放入该区域(注意这里的ip需反向写)
// 如需将该区域设置为反向解析所有的ip,则将"117.1.in-addr.arpa"改为
// "in-addr.arpa"即可(已测试)
/etc/bind/named.conf.option
options {
directory "/var/cache/bind";
//设置工作路径
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.
// forwarders {
// 0.0.0.0;
// };
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation auto;
listen-on-v6 { any; };
//开启listen-on-v6就会默认也监听ipv4。
//listen-on { 5.6.7.8; };会默认监听5.6.7.8:53,即在5.6.7.8监听53端口
//listen-on port 1234 { 127.0.0.1; };会监听127.0.0.1:1234
allow-query {any;};
//定义可以进行dns查询的主机,如:allow-query {127.0.0.1;};只能通过本机查询
//allow-query也能在zone语句中设定,这样全局options中的allow-query选项
//在这里就不起作用了。
auth-nxdomain no;
//默认即可
};
更进一步的设置参考: DNS域名解析服务器 BIND配置文件详解(二)
/var/cache/bind/kasumiksm.com.zone
$TTL 10 //ttl 10秒 //dns服务器的域名 //管理员的邮箱,为了防止歧义将@改为.
@ IN SOA dns.kasumiksm.com. 1412251241.qq.com.(
10 ;serial //序列号
1M ;refresh
1M ;retry
1M ;expire
1M) ;negative cache ttl
//域名 interntet 记录类型 正向解析的结果
@ IN NS dns.kasumiksm.com.
dns IN A 1.117.247.180
webdav IN A 2.117.2.2
abc IN A 2.117.2.2
当域名写成dns
和dns.
分别代表dns.kasumiksm.com.以及dns.
即如果没有点则会自动在后面补充区域的域名
/var/cache/bind/ip.zone
$TTL 10
@ IN SOA dns.kasumiksm.com. 1412251241.qq.com.(
10 ;serial
1M ;refresh
1M ;retry
1M ;expire
1M) ;negative cache ttl
@ IN NS dns.kasumiksm.com.
180.247.117.1 IN PTR dns.kasumiksm.com.
2.2.117.2 IN PTR webdav.kasumiksm.com.
2.2.117.2 IN PTR abc.kasumiksm.com.
与域名的设置类似,但是要注意将ip反向书写
检查写好的配置文件格式是否合规
利用
named-checkconf
named-checkzone
分别对配置文件及区域文件进行格式的检查。
参考链接:
基于Linux(CentOS/Ubuntu)使用Bind9自建私有权威DNS
bugshooting
因为之前将.zone文件直接放在/etc/bind/目录下,导致在利用nsupdate修改时出错(就算如何修改文件的权限,也会提示无权限创建.jnl文件的错误)遇到这个问题是因为ubuntu的Apparmor机制。可以在/var/log/kern.log
看到错误日志。
想要去除该bug的方法有二:
方法一、运行指令sudo aa-complain /path/to/bin
。其中/path/to/bin
代表可执行文件的路径。该信息会在/var/log/kern.log
中体现。
该指令可开启complain模式,使得Apparmor不再直接拦截应用的行为,但是仍会在/var/log/kern.log
产生complain记录。
方法二、在/etc/apparmor.d
修改相关程序的配置文件如对于bind9,其相关的配置文件为:/etc/apparmor.d/usr.sbin.named
。通过指令
dpkg -S /etc/apparmor.d/usr.sbin.named
可以查询该配置文件对应的程序名称。例如:bind9
相关参考链接: DebuggingApparmor
参考链接:
基于Linux(CentOS/Ubuntu)使用Bind9自建私有权威DNS
ISC BIND9 - 最详细、最认真的从零开始的 BIND 9 - DNS服务搭建及其原理讲解
ddns实现脚本
ddns脚本预备知识
rndc工具
运行命令sudo rndc reload
即可重新加载bind9的所有配置文件及区域数据文件(.zone),进行配置及ip地址的更新。sudo rndc reload kasumiksm.com.zone
可以加载单个区域文件进行更新。当运行指令后出现rndc: 'reload' failed: dynamic zone
的提示。则需要修改named.conf.local文件。参见:利用rndc对单个zone进行reload的时候提示rndc: ‘reload’ failed: dynamic zone
参考链接: bind详解,主从DNS
利用rndc对单个zone进行reload的时候提示rndc: ‘reload’ failed: dynamic zone
ddns功能实现方式
利用python脚本结合bash,实现登录ssh主机修改.zone文件
本文采用的即是该方法,需要考虑以下问题: 1、利用python3中的paramiko模块可以通过ssh登录服务器(支持密码登录以及密钥登陆)。使用方法: python通过ssh连接服务器,执行命令
python ssh 连接远程服务器,修改文本内容,调用脚本
2、对服务器的ubuntu系统进行设置,生成密钥,并使其支持密钥登陆。参考链接: UBUNTU设置SSH通过密钥登陆
ubuntu ssh密钥_生成SSH密钥以在Ubuntu中进行无密码登录
3、为避免执行sudo后,会出现的密码验证。需要对服务器进行如下设置: ubuntu sudo不需要输入密码的方法
4、为了实现ddns,必须先获得内网主机的公网ip地址。实现代码如下: python 获取公网 外网 ip 几种方式
5、通过bash语言中的sed指令对服务器上的.zone文件进行修改。 需要理解的指令有:
sed -i '$ahello' 1.txt # 在最后一行添加hello
sed -i '/123/d' 1.txt # 删除以123开头的行
更多参考链接:
Linux shell 用sed删除第一行、最后一行或增加删除某行
利用nsupdate工具
该方法本身存在一些问题,并不推荐。这里只做参考: bind9 实现动态解析域名 使用BIND配置动态DDNS
在github上有利用该方法实现ddns的例子,供参考: andreasscherbaum/bind-dynamic-dns-update
实现脚本
import paramiko
from urllib.request import urlopen
from json import load
class LinuxOrder:
def __init__(self, demain, port, username, private_key_path, timeout):
"""
:param demain: 服务器域名或地址均可
:param port: ssh 连接的端口
:param username: 服务器用户名
:param private_key_path: 本地密钥路径
:param timeout: 连接超时时间
"""
try:
self.demain = demain
self.port = port
self.username = username
self.private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
self.timeout = timeout
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(self.demain, self.port, self.username, pkey=self.private_key, timeout=self.timeout)
print('{0}:连接成功'.format(self.demain))
except Exception as e:
print('{0}:连接失败'.format(self.demain))
raise e
def run(self, order):
"""
执行代码
:param order: 命令
:return:
"""
stdin, stdout, stderr = self.ssh.exec_command(order)
print(stderr.read())
print(stdout.read())
def close_ssh(self):
"""关闭ssh连接"""
self.ssh.close()
print('关闭ssh连接')
if __name__ == '__main__':
demain = 'dns.kasumiksm.com' # dns服务器域名或ip地址均可
port = '22'
usr = 'ubuntu'
timeout = 3
private_key_path = 'cvm_shanghai3.pem' # 本地密匙
ddns_ip = load(urlopen('https://api.ipify.org/?format=json'))['ip'] # 获取ddns的公网ip
# 第一行为删除以webdav开始的行,第二行为添加webdav的解析记录,第三行为更改区域文件的权限,第四行为重载区域文件及配置文件,更新ip
order = "sudo sed -i '/^webdav/d' /var/cache/bind/kasumiksm.com.zone\n" \
"sudo sed -i '$awebdav\tIN\tA\t" + ddns_ip + "' /var/cache/bind/kasumiksm.com.zone\n" \
"sudo chown bind:bind /var/cache/bind/kasumiksm.com.zone\n" \
"sudo rndc reload\n"
L = LinuxOrder(demain, port, usr, private_key_path, timeout)
L.run(order)
L.close_ssh()
dns测试工具
正向解析:
nslookup webdav.kasumiksm.com
得出如下结果即为设置成功
Server: 127.0.0.1
Address: 127.0.0.1#53
Name: webdav.kasumiksm.com
Address: 2.2.2.2
反向解析:
nslookup 1.117.247.180
得出如下结果即为设置成功
180.247.117.1.in-addr.arpa name = dns.kasumiksm.com.