Linux 的路由功能之强大,确实是 Windows 难以媲美的。而且在性能上只要经过仔细调优,可以达到100Gbps的线速吞吐,与基于ASIC电路的硬件路由器相比毫不逊色。
笔者:国际认证信息系统审计师、软考系统分析师
虽然 Windows 也不是不能打,但从各种可控角度,事实上我还是推荐用 Linux 做软路由。
加上现在要逐步实现国产化替代,Linux 软路由也就继续有了应用的意义。所以这次就不用 Rocky Linux 了,改用国产的 Linux 发行版:龙蜥 Anolis 8。
这次的文章也特意大量引用别人公众号文章,作为一种新风格的尝试。
言归正传。Linux 系统做软路由服务器其实并不复杂。
网络接口涉及到物理接口和虚拟接口。现在大多数发行版都默认安装了 NetworkManager,可以通过 nmcli 进行命令行配置,也可以用 nmtui 使用文本界面进行配置。
只是从笔者角度觉得,还是写 ifcfg 配置文件比用 nmcli 或者 nmtui 命令来得简单直接,更不用说图形界面了,真 · Linux 管理员根本不需要 GUI 的好吧。
NetworkManager 作为守护程序,它的最大作用是应对网络连接变化时的自动切换和配置。这对于移动终端还是有必要的,但如果是服务器且静态配置网络设置,完全可以关闭该服务以节省资源。
单臂软路由服务器一般需要两个物理的网络接口,分别用于内外网。这里依然要说一下之前说过的观点:某些网上教程简化为一个物理接口,这我是强烈反对,因为没有从物理上把内外网分区,没有安全性可言。
1、首先是配置外网接口,创建连接并添加地址。
为了有效区分内外网,我把外网连接命名为 ens_wan,内网连接则是 ens_lan。设备名称就不要改,安装时自动命名是什么就是什么。这个步骤最适宜是在安装操作系统时就设置好。
如果是安装完成后设置,那就需要把连接改名,通过 nmcli 可以如下操作:
# nmcli connection modify ens33 connection.id ens_wan
这就把在安装过程中自动命名的 ens33 改为 ens_wan 了,这个操作可以立即生效。
2、其次是配置内网接口。
单臂软路由特点就是一个内网网口要对接多个子网。使用 nmcli 对内网连接添加作为子网网关的IP地址,然后用 connection up 操作刷新即可。如下是添加192.168.101.0/24子网的网关192.168.101.1:
# nmcli connection modify ens_lan +ipv4.addresses "192.168.101.1/24"
# nmcli connection up ens_lan
连接已成功激活 (D-Bus 活动路径: /org/freedesktop/NetworkManager/ActiveConnection/6)
也可以用 nmtui 去实现各种配置过程,新手来说,某程度上会比打命令稍方便一些,比如这样的:
3、再然后就是路由转发。
老生常谈的启用路由转发操作:
# echo 1 > /proc/sys/net/ipv4/ip_forward
不过现在 Anolis 8 已经不需要了,默认就是1而且改不回去0。
设置路由就是 route 命令,这已经是网管基本功。
既然是路由器的用途就会有设置静态路由的时候。正常情况下,随着网络连接上添加了子网网关地址,相应的路由规则也会自动添加。但如果是再下游的子网,就要考虑通过手工增加静态路由实现网络互通。
NetworkManager 的 nmcli 也支持设置路由,因此现在一般可以先尝试用传统的 route 命令进行设置和测试,比如这样设置局域网子网的静态路由:
# route add -net 192.168.48.0/24 gw 192.168.49.90 dev ens_lan
写这些有点灌水的感觉了,建议参考这里:
测试通过后,再用 nmcli 将其固化:
# nmcli connection modify ens_lan +ipv4.routes "192.168.48.0/24 192.168.49.90"
注意:
1)nmcli 添加静态路由后需要将网络连接重新激活才能生效(不需要down,只需要up就能生效,删除路由后亦然)。
# nmcli connection up ens_lan
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/3)
2)命令行参数方式的 nmcli 操作默认保存操作结果。但如果是交互模式则需要执行 save persistent 命令才能保存。
4、NetworkManager 的安全相关要点
如果是企业的分支机构,通常要设置 VPN 接入到总部。但 NetworkManager 默认不连接需要输入密码的连接。因此,需要先把 VPN 连接设置为所有用户可用,并设置为所有用户保存密码(必须是明文而不能是keyring方式保存),然后记录下 VPN 连接的 UUID,添加到路由器互联网连接的附加连接上:
# UUID=$(nmcli --get-values connection.uuid connection show VPN连接名)
# nmcli connection modify ens_wan connection.secondaries "$UUID"
但是,明文保存的密码不具备安全性。从个人经验认为,由于现在讨论的是软路由服务器,一般不可能有网络安全三员之外的登录用户,而VPN是两地站点之间的数据通道作用,基于证书认证登录即可保证登录安全性,万一证书私钥泄露,基于证书更替撤销等操作机制也可以保证通道的安全性,所以没必要再对证书私钥下密码导致还需要明文保存密码。
NetworkManager 更详细的使用方法可以参考其文档:
早期发行版是通过 iptables 命令进行操作,现在都是通过 firewalld 服务以及 firewall-cmd 命令进行操作。
firewalld 的配置操作比直接操作 iptables 要方便,不需要手工写脚本或者编辑配置文件。尤其是常见操作,比如做路由器所必须设置的 NAT 转换,如下简单一条命令就实现了在外部网区域启用 NAT 转换:
# firewall-cmd --zone=external --add-masquerade
如果要设置开放端口,操作命令也很清晰,注意如下命令操作的是内部网区域,开放 SSH 端口用于远程登录管理:
# firewall-cmd --zone=internal --add-port=22/tcp
firewall-cmd 可实现的操作非常多,只说几个要点:
1、最容易被忽视的,但又非常有用的是 firewalld 预定义了一些区域,可以利用这些区域去绑定不同的网卡,从而区分细化过滤规则,不像以前 iptables 还要自己定义,且清晰度不如 firewalld。
比如上面的操作我们对外部区域 external 设置了NAT,那么接下来把公网接口转入该区域,这个设置就有意义了:
# firewall-cmd --zone=external --change-interface=公网接口名称
# firewall-cmd --set-default-zone=external
关于 firewalld 的预定义区域,可以参考:
2、对于 firewalld,我一向觉得 firewall-cmd 这个命令实在有点长,为了少打字,我会在 /root/bin 下面建立一个符号链接叫 fwcmd,指向 /bin/firewall-cmd:
# mkdir /root/bin
# ln -s /bin/firewall-cmd /root/bin/fwcmd
3、初学 firewalld 的管理员最容易忽略的就是固化配置:
# firewall-cmd --runtime-to-permanent
要把运行配置保存下来成为固化配置。
4、firewalld 有个特殊的参数叫“允许区域漂移”,为了保持兼容性默认为“yes”,可以根据实际情况而修改为“no”,参考我以前的文章:
firewalld警告信息处理两例之清理virbr0及关闭区域漂移
5、如果 firewalld 配置了大量的开放端口或服务,一个个删除的确是很折磨,firewall-cmd 也没有提供一次全删的命令参数。但我们可以通过命令行脚本实现批量删除。
如下是删除全部开放了的端口的命令,注意其中的 --permanent 参数,删除的是固化配置而不是当前运行配置:
# firewall-cmd --permanent --list-all | grep ports | head -n 1 | \
cut -d: -f2 | tr ' ' '\n' | xargs -I {} firewall-cmd --permanent --remove-port={}
如下是删除全部开放了的服务的命令,和上一操作大同小异,我特意没写 --permanent 参数来对比,所以操作的是当前运行配置:
# firewall-cmd --list-all | grep services | head -n 1 | \
cut -d: -f2 | tr ' ' '\n' | xargs -I {} firewall-cmd --remove-service={}
提醒:上述操作是全部删除,没有保留。如果是远程操作比如 SSH 或者 VNC 方式,建议是对固化配置进行清除,然后对固化配置添加当前远程操作的服务(或端口),最后再 RELOAD 生效。否则如果是对运行配置操作,会立即无法访问服务器。
1、首先是DNS缓冲服务器的设置。
要在内部区域开放DNS端口:
# firewall-cmd --zone=internal --add-port=53/udp
还需要安装 BIND 并将之配置为仅起缓冲用途的 DNS 缓冲服务器。这里简单归纳一下配置步骤,首先是安装BIND:
# dnf install named -y
然后修改配置文件,包括以下4个关键项目:
1)侦听端口和地址。懒人可以直接设置地址为any,反正防火墙会拦截外网接口不暴露在侦听53端口。
listen-on port 53 { any; };
但从良好实践出发,应改为内网各子网网关地址:
listen-on port 53 { 192.168.100.1; 192.168.101.1; };
2)允许查询的地址范围,同样可以填any或设置具体的地址范围。
allow-query { any; };
3)查询转发地址,填写1个或多个上游DNS服务器地址。
forwarders { 上游DNS服务器地址1; 上游DNS服务器地址2; };
要根据情况选择合适的上游 DNS 服务器地址。一种错误情况是使用A运营商的线路但设置了B运营商的 DNS 服务器地址,会有可能导致被访问的网站相关的 CDN 加速失效。所以这里就没写具体IP了。
4)关闭dnssec校验。
dnssec-validation no;
最后在内网区域开放防火墙端口、启用和启动服务:
# firewall-cmd --zone=internal --permanent --add-port=53/udp
# firewall-cmd --reload
# systemctl enable named
# systemctl start named
注意上面修改过程中各处的分号不可少。
经过配置后,就可以在内网终端上测试 DNS 缓冲服务器的效果了。还可以参考类似的公众号文章:
飓风网络安全:Linux之DNS服务器搭建及常见DNS攻击和防御
关于 DNS 服务器其实远不止这么点事情,可以看看我之前关于 DNS 安全和 DNS 解析技巧的文章:
2、简单的DHCP服务器配置
先简单地安装软件包,然后使用默认配置文件:
# dnf install dhcp-server -y
# \cp /usr/share/doc/dhcp-server/dhcpd.conf.example /etc/dhcp/dhcpd.conf
cp前面的反斜杠是压制询问是否覆盖的提示。
然后修改这个配置文件......改的还挺多,直接贴修改后的有效配置文件作为示例吧,不适用的配置已经删除,补充了中文注释:
# dhcpd.conf
# 首先是全局配置,也就是DHCP区域定义中不另外定义时就使用全局配置。
# 本地主域名及后缀,随便命名,比如lan.corp
option domain-name "lan.corp";
# DNS服务器名称或IP地址,多个时用逗号分隔。
# 实际应该在子网定义中按子网进行配置。
# 这里设置的是路由器本身(假设为192.168.49.90)和一个外部DNS(8.8.4.4)。
option domain-name-servers 192.168.49.90, 8.8.4.4;
# 默认释放时间,秒数
default-lease-time 600;
# 最大释放时间,秒数
max-lease-time 7200;
# 是否开启动态DNS更新,一般按默认值none,即不允许
#ddns-update-style none;
# 如果DHCP服务器是本地网络的正式服务器,需要取消下一行的注释
#authoritative;
# 启用下行为设置DHCP的日志信息到其他日志文件,
# 需要同步修改SYSLOG.CONF去实现配置。
#log-facility local7;
# 第一张子网,很基本的子网定义,注意路由器IP地址设置可以设置多个,用逗号分隔
# 子网192.168.100.0/24
# 动态分配范围从 192.168.100~150
# 网关为路由服务器的接口地址 192.168.100.1
# 域名服务器重定义为 192.168.100.1, 8.8.4.4 两项
# 域名定义为 sublan100.corp
# 注意:如果域名服务器有定义内部域,就不能这样一个外部一个内部的设置!
# 只能要么只有1个内部,要么主备2个内部。
# 1内1外的设置仅适用于内部DNS服务器是纯缓冲服务器
subnet 192.168.100.0 netmask 255.255.255.0 {
range 192.168.100.100 192.168.100.150;
option routers 192.168.100.1;
option domain-name-servers 192.168.100.1, 8.8.4.4;
option domain-name "sublan100.corp";
}
# 第二张子网,比较完整的配置,但没有实际区别
# 子网192.168.101.0/24
# 动态分配范围从 192.168.101.200~250
# 网关为路由服务器接口地址 192.168.101.1
# 广播地址设置为 192.168.101.255,
# 域名服务器重定义为 192.168.101.1 8.8.4.4 两项
# 域名定义为 sublan101.corp
# 默认释放时间重定义为 1200 秒
# 最大释放时间重定义为 28800 秒
subnet 192.168.101.0 netmask 255.255.255.0 {
range 192.168.101.200 192.168.101.250;
option domain-name-servers 192.168.101.1, 8.8.4.4;
option domain-name "sublan101.corp";
option routers 192.168.101.1;
option broadcast-address 192.168.101.255;
default-lease-time 1200;
max-lease-time 28800;
}
# 建议对于不分配IP地址的范围同样在DHCP中定义,以便DHCP服务器掌握网络拓扑
subnet 192.168.49.0 netmask 255.255.255.0 {
}
# 需要特别配置的主机声明,固定地址如果不设置则依然是动态分配。
# 如果设置,则要注意固定地址不能在可分配的地址池范围内。
# 固定地址也可用主机域名,但这就需要在DNS服务器中配置本地域解析了。
host mylaptop {
hardware ethernet a1:02:c0:51:6d:75;
fixed-address 192.168.49.99;
}
# 终端分类、共享地址池等相对少用的配置这里就省略掉。
完成修改后,照样是开放防火墙端口、启用和启动服务:
# firewall-cmd --zone=internal --permanent --add-service=dhcp
# firewall-cmd --reload
# systemctl enable dhcpd
# systemctl start dhcpd
也可以参考:
1、吞吐量优化
作为软路由服务器,优化在于网络性能,这当然就是时延和吞吐量这两个关键指标先行,NAT 负载能力跟上了。
对于时延和吞吐量,之前写的关于 Windows 作为软路由服务器的硬件优化措施是同样适用的:
我之前有一篇是专门介绍Linux网络吞吐优化设置的,主要聚焦于吞吐量和 NAT 承载能力,也可以参考:
需要说明的是随着 Linux 内核不断发展,其网络子系统所提供的可调节参数也在不断变化。对于本次作为实践环境的 Anolis 8 所使用的 5.10 内核,这些参数都还在,只是个别默认值不同于以前,比如:
/proc/sys/net/ipv4/tcp_rmem
默认值是:
4096 131072 6291456
这三个值中间的值比我原来设计的 87380 还要高。读者可以自己再研究是保留默认值还是另外设置一个新的值。
2、网络安全
作为路由器,网络安全包括自身安全和局域网安全两个范畴。自身安全通过防火墙封禁端口、设置各种密码管理规则、启用 SELinux 等等手段实现。
需要前置提醒的是,Anolis 8 安装后默认不启用 SELinux,但应该启用。方法可以参考我之前关于 SELinux 的文章:
在局域网安全范畴,安全管理员和网络管理员应收集内网终端的 MAC 地址,然后在 DHCP 服务的配置中设置 MAC 地址和 IP 地址的绑定分配关系,同时通过防火墙设置 MAC 地址过滤,实现严格控制终端通过路由器访问出外网或跨网段访问其它子网。
要做好安全措施,还需要通过网络交换机,设置端口绑定、VLAN 等措施,进一步限制恶意外来终端通过广播方式尝试访问网络范围的其他网络资产。
最后是关于流量监控,ntopng 这个软件。先提一下 ntop 这个文本方式的命令行工具,用于查看网络流量情况。但该工具已经过时,被 Anolis 8 的上游发行版删除,所以也不包含在 Anolis 8 中了。
继承者是 ntopng,即ntop next gen,顾名思义就是ntop的下一代。与传统的 ntop 相比,ntopng 是个复杂的网络流量监控软件:由多个组件组合而成,提供 WEB 界面,支持流量分析、抓包以及深度包检测等网络流量监控功能,非常适合在软路由服务器上安装使用,但对服务器资源要求比如 CPU 和内存都较高。
ntopng 具体的功能特性、安装使用教程到处都有,直接引用如下:
Python运维实践:基于web的网络流量分析工具ntopng及使用
但是这些教程都没有指出,ntopng 如果不加以适当配置,是有坑的。
第一个坑是内存占用,来自于 ntopng 数据库所依赖的 redis 内存型键值数据库。
该数据库需要占用大量的内存,如果不设置限值,随着数据规模增长,所有的内存都有可能被其占用耗尽。因此,需要修改 /etc/redis.conf 配置文件,按照服务器的实际内存情况和使用需要,设置其中的 maxmemory 和 maxmemory-policy 两项设置。比如:
maxmemory 300mb
maxmemory-policy volalite-lru
即最大内存限制设置为300mb,内存的替换算法设置为最近易失性。
最大内存限值的设置还需要考虑 redis 异步持久化写盘机制会对内存数据库产生临时的内存快照。这个快照需要的内存是1比1的,所以 maxmemory 的实质是会占用该值两倍大小的内存,只是其中的一半是在持久化过程中动态占用。
第二个是大坑:硬盘寿命问题。源头也同样是 redis 键值数据库。
如果路由服务器使用的是 SSD 硬盘,那么在默认配置下会迅速耗尽硬盘寿命。
这是因为 redis 的持久化机制,把内存中的数据生成快照保存到磁盘里面的过程,是异步、后台且持续的。默认就被配置为持续不间断地进行。
在安装了 ntopng 后,检查/etc/redis.conf配置文件,有如下的默认设置:
save 900 1
save 300 10
save 60 10000
这三行配置的意思是:
服务器在 900 秒之内,对数据库进行了至少 1 次修改。
服务器在 300 秒之内,对数据库进行了至少 10 次修改。
服务器在 60 秒之内,对数据库进行了至少 10000 次修改。
只要满足任意一个,就会进行写盘。这样的频率,稍微有点流量的网关随便都能触发第二条,甚至最频繁的第三条(取决于网络内终端的数量)。而每次写盘都是全数据库写盘,简单的乘法计算就可以知道一天下来会向硬盘写入多少数据。
这个数据量,对于小容量的 SSD 硬盘是致命的。由于服务器企业级 SSD 硬盘都比较贵,而且作为路由服务器的存储要求并不高,用户采购时自然会选择较小的固态硬盘。
结果还不够2年,服务器的管理固件就警告 SSD 硬盘寿命只剩下1%。
老郁闷了。
解决办法包括:
1、调整持久化触发频率
最简单的调整办法就是修改触发条件从而降低触发频率。如果不关心 redis 存下来的数据,也对服务器的稳定性有信心,那么可以调整触发条件,简单地只保留1条规则且把时间间隔修改为比较长的间隔,比如1个小时。写入的数据量就会随之而成比例地下降。
该调整的缺点在于写盘间隔越长,可恢复的有价值数据就越少,可持续性容灾设计中的 RPO 是也。
2、修改持久化机制
从默认的全量写盘方式改为增量写盘方式,即 AOF 方式。
开启 AOF 功能需要 /etc/redis.conf 文件中将 appendonly 配置项修改为 yes,并指定 AOF 文件名称和备份存放路径:
appendonly yes
appendfilename appendonly.aof
dir /usr/local/var/db/redis/
但是,此方法对于繁忙的路由服务器是没有太多意义的,因为很可能单位时间内的增量甚至还大于内存数据库的内存占用量。
而且,AOF 文件是要占硬盘空间的,redis 还需要通过重写操作将其覆盖以释放空间,复杂性大大增加。
所以这个方法只适用于小型网络,二三十台终端的情况。
3、挂载机械硬盘
问题很多时候还是需要从最底层去解决。挂载机械硬盘专门用于 redis 持久化是最可行的措施,成本也不高,如果要数据安全就使用 RAID1 冗余即可。
具体方法就是为路由服务器采购配置企业级机械硬盘(建议使用 NLSAS 近线 SAS 硬盘),挂载到 redis 的存放路径承接数据写入。
当前主流 NLSAS 硬盘顺序写入速度可以达到170MB/s(注意:大于千兆网最大速率),假设 redis 数据库为300MB,理想情况下2秒就可以写完。所以,设置稍大的缓冲区和写盘触发周期就可以获得足够的写盘时隙了。
如果阵列卡有内存缓冲(切勿是 SSD 缓冲,死得更快),写盘过程还可以获得进一步的效率提升。
再如果启用了滚动抓包,专门硬盘的优势会更明显。持续写就写吧,反正写不死,只是比较忙。
不可替代的网管需要有全栈能力:从物理层到应用层。谁会想到配置 Linux 软路由服务器,最后居然是要折腾 NoSQL 数据库写盘调优避坑。本文引用较多,写得比较散乱,如有遗漏可留言指出。
本站微信订阅号:
本页网址二维码: