Featured image of post 在 RouterOS 上使用 SmartDNS

在 RouterOS 上使用 SmartDNS

一个功能丰富的 DNS 缓存服务

SmartDNS 是一个功能丰富的本地 DNS 服务器,具体支持的特性可以看一下他们的官方网站,支持 Linux 和 Windows 平台。

虽然 RouterOS 是不能直接运行 Linux 软件的,但是从 RouterOS 7.4beta4 开始,MikroTik 针对部分比较现代的架构(arm, arm64, x86)增加了容器(Container)的支持,通过这个机制我们可以比较高效的直接在 RouterOS 设备上运行一些 Linux 应用,例如我们这边将要演示的 SmartDNS。

开启容器支持

出于安全因素考虑,RouterOS 并没有默认开启容器的支持,我们需要用以下命令开启:

1
/system/device-mode/update container=yes

完成后输出会提醒用户去重启设备,以 CCR2004 为例,就去按一下设备上的 reset 键即可。对于 CHR,进行一次冷重启也能达到相同的效果。重启完成后,可以使用命令 /container/print 来查看是否有已经开启容器功能。

准备网络接口

RouterOS 官方的文档中使用了 veth 为容器提供网络接入。如果之前用过 Linux 的 netns(network namespace)相关的功能对 veth 这个概念肯定有所了解,如果没有用过也不用担心。Linux 官方的手册中如此描述:

The veth devices are virtual Ethernet devices. They can act as tunnels between network namespaces to create a bridge to a physical network device in another namespace, but can also be used as standalone network devices.

所以我们可以简单粗暴的把 veth 理解为一组直接用网线相连的网卡,一头“插在”设备上,另一头“插在”容器,或是另一个 netns 中,也就是一个设备上的虚拟 L2 点对点连接。

RouterOS 和 Linux 的 netns 并无区别(毕竟 RouterOS 的的确确使用了 Linux 内核),只不过创建和使用的流程有一些小区别。使用如下命令创建一个 veth 接口:

1
/interface/veth/add name=ether-pihole address=192.168.1.2/24 gateway=192.168.1.1

其中,address 是容器中接口的 IP 地址,gateway 则是容器的默认路由,也就是我们 RouterOS 本身的 IP 地址。

因为我希望我局域网内的设备可以直接访问 smartdns,所以我们可以直接将 ether-pihole 加入 LAN 的 bridge 中,就不需要任何端口转发之类的额外操作了:

1
/interface/bridge/port add bridge=bridge-lan interface=ether-pihole

这样我们的容器就和我们的局域网处在同一个二层网络中了。

创建容器和配置服务

为了避免容器内应用日志和拉取镜像时产生的写入对路由器的 nand 造成太多的负担,可以创建一个内存盘用来存放这些文件:

1
2
/disk/add type=tmpfs tmpfs-max-size=500M
/container/mounts/add name="smartdns-tmpfs" src="/tmp1/smartdns_log" dst="/var/log"

同时,为了方便后续对网络进行一些自定义的调整,我不打算直接用网上现成的 dockerfile 来创建我们的 SmartDNS 容器,而是直接使用 alpine:latest 为基础镜像,直接进入这个容器内下载 SmartDNS:

1
2
/container/config/set registry-url=https://registry-1.docker.io tmpdir=tmp1/pull
/container/add remote-image="library/alpine:latest" interface=ether-smartdns cmd="tail -f /dev/null" root-dir=container/smartdns mounts=smartdns-tmpfs dns=223.5.5.5 hostname="smartdns" logging=yes start-on-boot=yes

这里写的 cmd 随便准备的一个可以执行很久,不会主动退出的命令,这样我们就有充足的时间来完成容器的配置。执行创建命令后可以用 /container/print 观察状态,完成之后可以使用 /container/start 0 来启动容器。

1
2
3
4
5
6
[admin@shanghai-router] > /container/shell 0
/ # ls
bin     dev     etc     home    lib     media   mnt     opt
proc    root    run     sbin    srv     sys     tmp     usr
var
/ #

到这里我们就能和像在普通的 Linux 环境一样下载和安装 SmartDNS 了:

1
2
3
4
5
6
7
8
# 下载二进制
cd /usr/local/bin
wget https://github.com/pymumu/smartdns/releases/download/Release43/smartdns-aarch64 -O smartdns
chmod +x smartdns

# 创建配置目录并更新配置
mkdir -p /etc/smartdns
vi /etc/smartdns/smartdns.conf

具体的配置细节这里就不细说了,大家可以查看官方的文档。配置完成后可以在容器中直接启动 smartdns 来验证配置是否正确,一切正常之后,我们可以准备开始创建我们的启动脚本了。因为我的容器直接暴露在家庭局域网的 L2 内,所以我希望直接对容器增加 IPv6 地址,好在 SLAAC 和 DHCPv4 中直接分发容器的 IPv4/v6 配置。我的方案很粗暴,直接准备一个 /start.sh 文件用来完成相关的配置:

1
2
3
ip addr add 192.168.1.2/24 dev eth0 || true
ip addr add 2001:db8::1000/64 dev eth0 || true
/usr/local/bin/smartdns -f -p /tmp/smartdns.pid

然后更新一下容器配置,并重启容器即可:

1
2
3
/container/set cmd="sh /start.sh" numbers=0
/container/stop 0
/container/start 0

最后,我们可以找一个别的设备来验证一下 DNS 服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
ntzyz@localhost ~ % dig @192.168.1.2 twitter.com

; <<>> DiG 9.10.6 <<>> @192.168.1.2 twitter.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21559
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;twitter.com.			IN	A

;; ANSWER SECTION:
twitter.com.		597	IN	A	104.244.42.193
twitter.com.		597	IN	A	104.244.42.129
twitter.com.		597	IN	A	104.244.42.65
twitter.com.		597	IN	A	104.244.42.1

;; Query time: 33 msec
;; SERVER: 192.168.1.2#53(192.168.1.2)
;; WHEN: Sat Aug 26 14:45:00 CST 2023
;; MSG SIZE  rcvd: 93

实际上,通过类似的方法我们几乎可以运行任何 Linux 应用来拓展 RouterOS 的功能,例如 BIND 或者是 pi-hole 等等。

comments powered by Disqus
Except where otherwise noted, content on this blog is licensed under CC-BY 2.0.
Built with Hugo
主题 StackJimmy 设计