前言
此文章用于记录我自己维护防火墙的方法,记录在Gentoo/OpenRC环境下维护防火墙时逐渐形成的思路。
一些说明
gentoo发行版有自己官方的nftables服务,通常执行rc-update add nftables default即可使用,但随着我的需求增大,不少端口我都需要手动控制,比如sunshine串流,热点配置等,此时官方的save/restore模型就不够用了。
portage中有etc-update机制,正确设置可以保证修改不会被重新emerge nftables冲掉。下面我先说官方的配置具体是怎样的,再说怎么接管nftables服务。
官方配置
gentoo 的 nftables 服务是快照模型:先把规则装进内核,然后
sudo rc-service nftables save它会把 nft list ruleset 的输出写进 /var/lib/nftables/rules-save,开机时再从这个文件读回来。ebuild 装完提示的也是这个用法。
加载那一步实际执行的是
printf 'flush ruleset\ninclude "%s"\n' "${NFTABLES_SAVE}" | nft -f -flush ruleset 是写死在 /usr/libexec/nftables/nftables.sh 里的,改 /etc/conf.d/nftables 也去不掉。在快照模型下这么做其实是合理的,因为快照本身是全量的,机器上所有表都在里面,清空再整体装回来也不会丢东西。
但我的用法不是这样。tailscale、libvirt 等软件会往内核里加入自己的防火墙配置,这种情况下官方的一条 flush ruleset 就会把它们的表一起清掉,结果可想而知,tailnet 不通、虚拟机没网,用rc-service看状态又全是 started,很容易往错的方向查。为了避免这些问题,所以我接下来打算接管官方服务。
接管官方服务
/etc/init.d/nftables 属于 net-firewall/nftables 这个包,但它在 /etc 里,而 /etc 是配置目录,不是包来管理的。portage 对待配置目录的规则写在 emerge(1) 里:
The purpose of this feature is to prevent new package installs from clobbering existing configuration files.
具体做法是,安装时如果发现同名文件已经存在,就把要装的那个改名成 ._cfg0000_foo 放在旁边,等用户自己决定怎么合并。所以升级一个包不会动改过的文件,只会在旁边多放一个待合并的新版本。这是设计出来的行为,不是副作用。
而且这里还有一层。make.conf(5) 里有个默认开启的 FEATURES:
config-protect-if-modified This causes the CONFIG_PROTECT behavior to be skipped for files that have not been modified since they were installed.
意思是,如果某个文件从装好到现在都没有被修改过(mtime),portage 就会认为没必要保护它,升级时直接换成新的;该文件一旦修改过,它从此就会进入保护状态,生成 ._cfg0000_ 并询问用户选择如何处理。
所以直接把自己的逻辑写进 /etc/init.d/nftables 是比较合理的做法,服务名保持不变,用起来也几乎感受不到区别。
但要千万注意执行etc-update命令后提示到此文件需要更新时要看清楚,千万不要选-3。选-3会用官方脚本把你这份覆盖掉,而官方脚本加载时执行的是 flush ruleset 加上 include 那个 0 字节的 rules-save,结果将会是清空全部表、且不加载任何规则,机器变成完全没有防火墙。而且 -3 是全局覆盖,作用于整个扫描范围,一次会把所有包所有待合并文件全部覆盖。为了保险还是建议手动创建备份文件cp -a /etc/init.d/nftables /etc/init.d/nftables.bak,用于防止手贱选了-3选项配置被清除后没有及时做备份。
服务脚本
以下配置仅供参考:
#!/sbin/openrc-run# ============================================================================## 作用: 加载 /etc/nftables.conf, 只管理我自己的表。## 升级注意:# /etc 在 CONFIG_PROTECT 里, 升级本包时会在旁边生成# /etc/init.d/._cfg0000_nftables。合并时选 -7 即可保留本脚本:# sudo etc-update /etc/init.d# ============================================================================
description="nftables firewall (single source: /etc/nftables.conf)"
NFT_FILE="/etc/nftables.conf"
# 写成 "族:表名" 的形式, 空格分隔; 要与 nftables.conf 里的定义保持一致。MY_TABLES="inet:<过滤表> ip:<NAT表>"
extra_commands="check"extra_started_commands="check reload"
depend() { need localmount before net}
check() { ebegin "语法检查 ${NFT_FILE}" nft -c -f "${NFT_FILE}" eend $? "语法/语义有问题, 上面是报错位置"}
start() { ebegin "加载 nftables 规则 (${NFT_FILE})" nft -f "${NFT_FILE}" eend $? "加载失败 —— 当前处于无防火墙状态"}
stop() { ebegin "移除本服务的表 (${MY_TABLES})" for t in ${MY_TABLES}; do fam="${t%%:*}" name="${t##*:}" # 先确认存在再删, 保证 stop 可以重复执行。 if nft list table "${fam}" "${name}" >/dev/null 2>&1; then nft delete table "${fam}" "${name}" || true fi done eend 0}
reload() { ebegin "重新加载 nftables 规则 (${NFT_FILE})" nft -f "${NFT_FILE}" eend $? "重载失败, 旧规则仍在生效"}记得将MY_TABLES 两处占位符换成在 /etc/nftables.conf 里定义的实际表名,否则 stop 时查不到表会直接跳过。
写好后再给/etc/init.d/nftables可执行权限,加进 default 运行级,再启动一次:
sudo chmod +x /etc/init.d/nftablessudo rc-update add nftables defaultsudo rc-service nftables start配合其他软件的防火墙配置来管理自己的防火墙规则
tailscale、libvirt 等等其他软件都有自己的一套防火墙配置。这些配置通常不需要也不建议用户来手动更改,它们启动停止时自己会处理。
所以用户在管理nftables时只需遵守以下原则:
1、不随意执行sudo nft flush ruleset
2、排除防火墙问题时检查是否与软件自带的防火墙配置有冲突
sudo nft list tablessudo nft list chain ip filternft list tables 列出的表通常比自己的多,看一眼链名就能认出是谁建的,比如 ts-input、ts-forward 是 tailscale 的。自己的表名带上能一眼区分的名字,查问题的时候省事。
还有一类误判是容器。网上很多配置会写 iifname "podman*" accept、oifname "veth*" accept 之类的转发规则,但那是 rootful 加 bridge 网络才有意义。用 rootless 配 pasta 的话,容器流量在内核看来就是本机进程自己发出去的,走 output 链,根本不经过 forward。那些规则不会生效,纯粹是死代码。先看一眼有没有网桥接口就知道自己属于哪种:
ip -br link日常维护
改完先检查再加载:
sudo nft -c -f /etc/nftables.confsudo rc-service nftables reloadct state established,related accept 每个链都应该要有。它放行本机主动发起的连接的回程包,不用给每个服务写双向规则。forward 链上漏了这条,表现是有 IP、能解析域名,但所有网页都打不开 —— 因为回程包没有任何规则能匹配上。
重建表用 destroy 而不是 flush:
destroy table inet <过滤表>destroy table ip <NAT表>destroy 只删指定的表,表不存在也不报错,适合每次全量加载;flush ruleset 会把别人的表一起清掉。
平时不用手动 start,服务已经在 default 运行级里了,开机自己会加载。start 只在手动 stop 过之后才需要。
若想加一个新服务的端口,顺序是先用
ss -tulnp | grep <关键字>看清楚它实际监听的是什么协议、哪个端口,再把对应端口加进 /etc/nftables.conf。
Share Article
Generate a share poster or copy the link to share this article.
Continue reading
Last updated on , 1 days ago
Some content may be outdated