上一篇文章介绍了我NAS的硬件组成。之后要做的原本是部署各种软件,但想来想去,应该还是先介绍一下网络安全的问题。
Unraid作为一个操作系统,本身是没有类似firewalld或者ufw这种防火墙的。这也就意味着默认情况下,Unraid所暴露出的网页端口、SSH和VNC是默认可以被任何人访问的。在家庭网络环境中,大部分情况下我们的Unraid服务器都位于一个IPv4的NAT后面,并且大部分情况下Unraid服务器并不位于DMZ,因此就IPv4来说,除非经过特殊设置(例如设置路由器DMZ并且向运营商索要公网IP),我们家中的Unraid服务器是不会被外人访问到的。我的情况和大部分人类似:新装宽带已经不给公网IPv4了,而且从路由器获取到的WAN口IP来看,我处在一个运营商NAT之后,也就是说即便我设置了DMZ,一般人也无法轻易访问到我的Unraid服务器(即便如此,我仍然不建议大家在不充分了解DMZ和公网IP的情况下盲目跟着网上的教程操作)。
但IPv6就是另一个故事了。IPv6的提出就是为了解决公网IPv4地址不够用的局面,得益于IPv6,我家中的每一个设备都能获得一个独一无二的公网IPv6地址,很可能你家也一样。但这个v6地址不一定通,有可能是运营商的问题,但更大概率是你路由器的问题。以我使用的TP-Link为例,我老家的TP-Link路由器型号是TL-XDR3010,这个路由器支持IPv6,但是内置一个关不掉的IPv6防火墙,会拒绝所有主动入站的连接。而我现在的住所用的是TL-XDR5480,这个路由器就比较高级了——你可以选择关掉内置的v6防火墙,让自己局域网的设备全部暴露在v6公网上。关掉v6防火墙的本意是提高v6网络的联通性,这样就如之前的文章所说,我的朋友可以直接通过v6地址连上运行在我电脑上的Minecraft服务器,但这样一来,不光我的朋友能连上,其他人可以连上。Unraid默认是只使用v4栈,你可以手动改成双栈,但获取到v6公网地址且关闭了路由器的防火墙之后,这意味着每一个知道你服务器v6地址的人,都可以访问Unraid上面的服务。而Unraid本身又没有过多防备,因此这就是一个巨大的安全隐患。
同样地道理适用于其他设备。在关闭路由器的v6防火墙之前,我启用了所有电脑的防火墙,对于不需要v6联通性的设备,我索性直接关闭了v6栈。如果你不知道关闭v6防火墙意味着什么,那就不要关闭。
所以接下来我们就说说怎么让Unraid更安全地使用IPv6地址。虽然Unraid没有防火墙,但它有iptables啊,基于此我们可以配置一些规则让Linux内核丢掉一些连接,从而起到保护服务的作用。由于我开放IPv6的主要目的是提高P2P应用的联通性,而我又懒得给每个应用单独开端口(主要是记不住),所以防火墙主要以按照端口号拒绝为主。例如默认情况下接受连接,但拒绝来自公网向80、443这样的端口的连接。
关于iptables的配置,我们需要借助一些插件,因为Unraid的系统存储于u盘上,每次系统重启时关于iptables的配置都会丢失。这里我们需要借助一个叫做 User Scripts
的插件,将配置防火墙的脚本保存下来。在插件中添加一个新脚本,脚本内容如下所示:
#!/bin/bash
# Clear rules
ip6tables -F INPUT
# Allow IPv6 input by default
ip6tables -P INPUT ACCEPT
# Allow traffic from LAN and private address (include tailscale) in all cases
ip6tables -A INPUT -s fd00::/8 -j ACCEPT
ip6tables -A INPUT -s fe80::/10 -j ACCEPT
# Allow ICMP in all cases
ip6tables -A INPUT -p icmpv6 -j ACCEPT
# Allow existing connections in all cases
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Drop traffic on other interfaces
# ssh
ip6tables -A INPUT -p tcp --dport 22 -j DROP
# web
ip6tables -A INPUT -p tcp --dport 80 -j DROP
ip6tables -A INPUT -p tcp --dport 443 -j DROP
# smb
ip6tables -A INPUT -p tcp --dport 139 -j DROP
ip6tables -A INPUT -p tcp --dport 445 -j DROP
# wsdd2
ip6tables -A INPUT -p tcp --dport 3702 -j DROP
ip6tables -A INPUT -p tcp --dport 5355 -j DROP
# Print chain with no port name (-n)
ip6tables -L INPUT -n
因为我们是以IPv6为主,所以直接使用ip6tables
来配置IPv6相关的链。由于我们主要关注在入站,因此配置的链是INPUT
。由于没有其他脚本修改iptables,因此脚本一上来先使用-F
来flush,也就是清空INPUT链。随后使用ip6tables -P INPUT ACCEPT
命令将默认策略设置成ACCEPT,也就是说没有匹配到明确的规则,那么就接受这个入站连接。这种配置具有一定的风险,比如你启动了一个新的服务,但是忘记告诉它不要绑定v6地址,那这个服务就会直接被暴露到公网上。如果想要安全的话可以直接配置默认为DROP,然后在下面手动配置哪些端口可以放行。
再后面就是配置规则了。虽然默认是放行,但为了避免内网来的流量匹配上规则被拒绝,先在一开始根据源ip地址放行来自fe80::/10
(类似路由器的192.168.0.0)和fd00::/8
(类似169.254.0.0,其中的fe70段会被tailscale使用)。这两条规则能够保证来自这些ip的请求在无论何时都会被放行。下面还放行了ICMPv6协议,这个ICMP就是ping命令使用的协议,如果没有ping的需求的话可以关掉。再后面就是放行已经建立或相关的连接。
之后就可以配置我们的DROP规则了。使用lsof -nP -i 6 | grep LISTEN
命令可以列出来所有正在监听的IPv6端口。我这边按照这个命令的结果禁用了SSH的22端口,网页面板的80和443端口,SMB的139和445端口,还有WSD的3702和5355端口。这里我没有禁用Docker服务和虚拟机vnc的端口,这是因为docker容器可以通过配置让它只监听v4,而虚拟机VNC的端口是只能监听v4,所以不用管。
关于脚本的运行时机,我选的是At Startup of Array
,即在阵列启动时执行。其实最理想的时机应该是开机后执行,因为像是SSH、网页面板这些服务本身并不依赖阵列启动。不过在大部分情况下Unraid启动后阵列也会跟着启动,所以问题不大。如果你真的对安全很看重的话,也许Unraid并不是最适合你的系统?
关于Docker,在创建容器的时候可以选择映射的端口号。比如你填8080
,那端口就会被映射到IPv4和IPv6的8080上。但如果你写0.0.0.0:8080
,docker就会把端口映射到IPv4地址的8080上,从而禁用v6的监听。
至此我就可以安心的把Unraid暴露在IPv6的公网上了。
-全文完-
【歪门邪道】从零开始组NAS - 防火墙篇 由 天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。