NFS服务v4不再需要111端口(rpcbind)--一般直接关闭就行。
但pve等备份还是需要--否则不能找到nfs备份位置。
而因为上次收到🇩🇪的网络安全警报信,索性也处理下其他的nfs服务器。
以下两个方案都可以使防火墙阻止公网111端口的访问,而只开放给WG内网。
一、使用iptables
# Allow WireGuard network
iptables -I INPUT -p tcp -s 10.8.0.0/24 --dport 111 -j ACCEPT
iptables -I INPUT -p udp -s 10.8.0.0/24 --dport 111 -j ACCEPT
# Deny all other access to 111
iptables -A INPUT -p tcp --dport 111 -j DROP
iptables -A INPUT -p udp --dport 111 -j DROP从外部测试
# Run from a different machine or a public VPS
nmap -p 111 <Your_Public_IP>显示结果filtered而不是open
Host is up (0.23s latency).
PORT STATE SERVICE
111/tcp filtered rpcbind
Nmap done: 1 IP address (1 host up) scanned in 3.01 seconds【删除旧文档/修正错误】
重启服务:
systemctl daemon-reload systemctl restart rpcbind
验证方法:
运行 ss -tulpn | grep 111。如果你看到 10.8.0.1:111 或者 127.0.0.1:111,说明它已经不再监听公网 IP,此时即使防火墙没配置,外部也无法扫描到该端口。
补充下如果收到docker或rule order干扰的处理方式:
1. 检查规则优先级
请运行以下命令查看当前的规则顺序,重点观察 INPUT 链:
iptables -L INPUT -n --line-numbers诊断依据:
- 如果你看到某处有一条
ACCEPT规则且其num小于你设置的DROP规则,或者该链的默认策略(Policy)是ACCEPT且没有与之匹配的拒绝规则,那么你的DROP规则将无效。 - 解决方法:你需要将
DROP规则插入到链的最前面,或者使用-I(Insert) 而不是-A(Append) 来确保它是第一条生效的规则:
# 假设你的 DROP 规则目前在末尾,先删掉它们
iptables -D INPUT -p tcp --dport 111 -j DROP
iptables -D INPUT -p udp --dport 111 -j DROP
# 将它们直接插入到 INPUT 链的第一条位置(强制优先匹配)
iptables -I INPUT 1 -p tcp --dport 111 -j DROP
iptables -I INPUT 2 -p udp --dport 111 -j DROP注意:执行此操作前,请确保你已经放行了 WireGuard 网段的规则,否则你会把自己锁在外面。此时你的规则顺序应该是:WG放行 -> 端口全局DROP。
2. 考虑 Docker 干扰
由于你正在使用 Docker,Docker 会在 FORWARD 链以及特定的 DOCKER-USER 链中自动注入规则。如果你的 RPC 服务是通过 Docker 容器提供的,或者宿主机的网络被 Docker 接管,普通的 INPUT 规则可能无法拦截这些数据包。
对于 Docker 环境,建议将拦截规则放入 DOCKER-USER 链中,它是 Docker 预留给管理员自定义过滤的:
# 在 DOCKER-USER 链中放行 WireGuard
iptables -I DOCKER-USER -p tcp -s 10.8.0.0/24 --dport 111 -j ACCEPT
iptables -I DOCKER-USER -p udp -s 10.8.0.0/24 --dport 111 -j ACCEPT
# 在 DOCKER-USER 链中拦截其他所有访问
iptables -A DOCKER-USER -p tcp --dport 111 -j DROP
iptables -A DOCKER-USER -p udp --dport 111 -j DROP优先选择用iptable的方案 原因如下:
这是一个非常经典的“坑”。在现代 Linux 发行版(如 Ubuntu, Debian, CentOS 7+)中,rpcbind 的监听行为通常不由其服务启动参数完全控制,而是由 systemd socket 激活机制 决定的。
以下是导致你尝试失败的核心原因分析及解决方案:
1. 根本原因:Systemd Socket 激活机制
现代系统为了加快启动速度,使用了 rpcbind.socket。
- 现象: Systemd 会预先创建并监听端口(默认是
0.0.0.0:111和[::]:111),然后才启动rpcbind.service。 - 冲突: 当
rpcbind启动时,它会接管 Systemd 已经打开的文件描述符。此时你在ExecStart中添加的-h参数往往会被忽略,或者因为端口已被 Systemd 占用而无法生效。
2. 配置文件的语法错误
在你提供的片段中:
[Service]ExecStart=ExecStart=/sbin/rpcbind -f -w -h 10.8.0.1
- 语法重复:
ExecStart=ExecStart=是错误的语法。 - 覆盖规则: 在 Systemd 的 override 文件中,如果你要修改
ExecStart,必须先用一个空的ExecStart=清除原有的命令,否则会报错(因为rpcbind.service通常不允许定义多个启动命令)。
正确的解决方案
要真正限制 rpcbind 的监听地址,你需要同时修改 Service 和 Socket。
第一步:修改 Socket 监听配置
这是最关键的一步,决定了端口在哪个 IP 上开启。
- 执行
systemctl edit rpcbind.socket。 - 输入以下内容(这会覆盖默认的全局监听):
Ini, TOML
[Socket]
# 先清空默认的监听列表
ListenStream=
ListenDatagram=
# 补充!# 补回必要的本地 Unix Socket (非常重要!)
ListenStream=/run/rpcbind.sock
# 指定只监听内网 IP
ListenStream=10.8.0.1:111
ListenDatagram=10.8.0.1:111
###补充重要###告诉 systemd:即使这个 IP 现在不存在,也请先建立监听
FreeBind=yes第二步:修改 Service 启动参数
确保 rpcbind 进程本身也只在指定 IP 运行。
- 执行
systemctl edit rpcbind.service。 - 输入以下内容:
Ini, TOML
[Service]
# 先清空原有的 ExecStart
ExecStart=
# 重新定义并添加 -h 参数
ExecStart=/sbin/rpcbind -f -w -h 10.8.0.1第三步:应用更改
systemctl daemon-reload && \
systemctl stop rpcbind.service && \
systemctl restart rpcbind.socket && \
systemctl start rpcbind.service验证方法
使用以下命令检查 111 端口到底绑定在哪个 IP 上:
Bash
ss -ntulp | grep :111
# 或者
netstat -tunlp | grep 111如果你看到的是 10.8.0.1:111 而不是 0.0.0.0:111 或 :::111,说明屏蔽成功。
💡 更好的建议:防火墙(兜底方案)
即使修改了监听 IP,暴露 111 端口(RPC 协议)在公网上依然有安全风险。强烈建议使用防火墙进行物理隔离,这比修改服务配置更稳健:
- UFW (Ubuntu/Debian):
Bash
ufw deny 111
ufw allow from 10.8.0.0/24 to any port 111总结: 你之前的尝试失败是因为 Systemd 已经在 0.0.0.0 上把坑占住了,光改 rpcbind 的启动参数是没用的。修改 rpcbind.socket 才是解决问题的钥匙。
---
### 没想到这个111端口如果不用ufw会相当头疼🤕
### 推荐下面的这个‘修改 `/etc/netconfig` (最彻底、推荐)’!
root@brr6:~# ss -tunlp | grep :111
udp UNCONN 0 0 100.64.0.5:111 0.0.0.0:* users:(("rpcbind",pid=754360,fd=5),("systemd",pid=1,fd=80))
udp UNCONN 0 0 [::1]:111 [::]:* users:(("rpcbind",pid=754360,fd=8))
tcp LISTEN 0 65535 100.64.0.5:111 0.0.0.0:* users:(("rpcbind",pid=754360,fd=4),("systemd",pid=1,fd=79))
tcp LISTEN 0 4096 [::]:111 [::]:* users:(("rpcbind",pid=754360,fd=11))
root@brr6:~# cat /etc/default/rpcbind
/etc/init.d/rpcbind
OPTIONS=""
Cause rpcbind to do a "warm start" utilizing a state file (default)
OPTIONS="-w"
Uncomment the following line to restrict rpcbind to localhost only for UDP requests
OPTIONS="${OPTIONS} -h 127.0.0.1 -h ::1"
Uncomment the following line to enable libwrap TCP-Wrapper connection logging
OPTIONS="${OPTIONS} -l "
root@brr6:~# /etc/sysconfig/rpcbind
-bash: /etc/sysconfig/rpcbind: No such file or directory
root@brr6:~# systemctl show rpcbind.socket -p Listen
Listen=/run/rpcbind.sock (Stream)
Listen=100.64.0.5:111 (Stream)
Listen=100.64.0.5:111 (Datagram)
可以看出v6的111仍然open!!!
从 `ss` 命令的输出中,我们可以一眼看出问题的根源:
### 核心诊断:`rpcbind` 在“自作聪明”
请看你的 `ss -tunlp` 输出中 `users` 这一列:
* **100.64.0.5:111**:显示 `users:(("rpcbind",...,fd=5),("systemd",...,fd=80))`。这说明这个端口是 **systemd 传递**给 rpcbind 的,符合你的预期。
* **[::]:111**:显示 `users:(("rpcbind",...,fd=11))`。**注意:这里没有 systemd!**
**结论:** 这个 IPv6 端口不是由 `systemd.socket` 开启的,而是 `rpcbind` 进程启动后,自行读取系统网络配置并主动监听的。即便你在 systemd 里关掉了 IPv6,`rpcbind` 也会绕过 systemd 自己去开。
---
### 解决方案
要彻底禁用 IPv6 监听,你需要通过以下两种方式之一来限制 `rpcbind` 的行为:
#### 方法一:修改 `/etc/netconfig` (最彻底、推荐)
`rpcbind` 使用 `libtirpc` 库,它会读取 `/etc/netconfig` 来决定启用哪些网络协议。只要在这里注释掉 IPv6 相关的行,`rpcbind` 就再也找不到 IPv6 的门路了。
1. 编辑文件:`nano /etc/netconfig`
2. 找到 `udp6` 和 `tcp6` 开头的行,在行首加 `#` 注释掉:udp6 tpi_clts v inet6 udp - -
tcp6 tpi_cots_ord v inet6 tcp - -
3. 重启服务(见下文)。
#### 方法二:强制指定启动参数 `-h`
修改你的 `/etc/default/rpcbind` 文件,显式告诉它只监听你需要的 IP。
1. 编辑文件:`nano /etc/default/rpcbind`
2. 修改 `OPTIONS` 行(注意:如果有多个 IP,需要多次使用 `-h`):
# 这里的 -h 会强制 rpcbind 只在指定地址上工作
OPTIONS="-w -h 100.64.0.5 -h 127.0.0.1"
*注意:加上 127.0.0.1 通常是为了保证本地某些服务调用 RPC 正常。*
---
### 最后一步:彻底重启以应用更改
由于 `rpcbind` 的套接字激活机制比较粘性,请务必按此顺序执行,确保旧的 IPv6 套接字被完全释放:
1. 彻底停掉服务和 Socket
systemctl stop rpcbind.service rpcbind.socket
2. 重新加载配置 (如果你改了 systemd 配置)
systemctl daemon-reload
3. 重新启动
systemctl start rpcbind.socket
systemctl start rpcbind.service
4. 验证
ss -tunlp | grep :111
**预期结果:**
你将不再看到任何带有 `[::]` 或 `:::111` 的输出。所有的 `111` 端口都应该只绑定在 `100.64.0.5`(以及如果你设置了的 `127.0.0.1`)上。
这样操作后,IPv6 的 111 端口就彻底关闭了。
---
这种“失控感”确实让人恼火。你明明在 `systemd` 这个“大管家”里下达了死命令,结果 `rpcbind` 这个“小员工”背着你偷偷开了后门。
这种现象在 Linux 系统管理中并不罕见,尤其是在处理像 RPC(远程过程调用)这种**历史悠久且逻辑复杂**的服务时。
### 为什么会出现这种情况?
简单来说,这是一个“双重管理”导致的冲突。
1. **systemd 的逻辑(套接字激活/Socket Activation):**`systemd` 的想法是:“我替你监听端口,有连接进来了我再拉起你(或者把端口传给你)。你只管干活,别管监听。”
2. **rpcbind 的逻辑(传输独立性/TI-RPC):**`rpcbind` 诞生于 systemd 出现之前的几十年。它内置了一套名为 `libtirpc` 的库,其核心逻辑是“传输独立”。它启动后会扫描系统的网络配置(主要是 `/etc/netconfig`),它的目标是:“我要确保所有的传输协议(TCP/UDP/IPv4/IPv6)都能支持 RPC 映射。”
**于是矛盾就发生了:**
当你通过 `systemd` 启动 `rpcbind` 时,`systemd` 把你指定的 IPv4 端口作为“遗产”传给了它。但 `rpcbind` 启动后,“职业病”发作,它看了一眼系统的 `/etc/netconfig`,发现:“哎?IPv6 也是开启状态,我也得为 IPv6 服务啊!”于是它绕过 `systemd`,直接向内核申请了 IPv6 的 111 端口。
### 我不能控制这个系统吗?!
**当然能。** 只是你需要从“全局管家”的视角,切换到“具体逻辑”的视角。
在 Linux 中,控制一个服务的行为通常有三个层面。你之前只在第一层,而 `rpcbind` 在第二、三层玩“自作主张”。
#### 1. 资源分配层 (systemd.socket) —— 你已经试过了
这是限制“谁能把端口交给你”。但它管不住程序启动后自己去申请新端口。
#### 2. 行为参数层 (OPTIONS)
很多服务可以通过启动参数(FLAGS)强制闭嘴。
* **方法:** 在 `/etc/default/rpcbind` 中加入 `-h` 参数。
* **逻辑:** 这相当于在 `rpcbind` 启动时指着它鼻子说:“**只准**在这些 IP 上监听,别去管其他的!”
#### 3. 运行环境层 (/etc/netconfig) —— **终极必杀**
这是 `rpcbind` 赖以生存的“世界观”。
* **方法:** 注释掉 `/etc/netconfig` 里的 `udp6` 和 `tcp6`。
* **逻辑:** 这相当于直接修改了程序的“认知”。既然它根据这个文件来决定开启哪些协议,你把它删了,它就会认为这个系统根本不支持 IPv6 的 RPC 传输,自然也就不会去监听 111 端口了。
### 总结
你绝对拥有系统的控制权,只是 `rpcbind` 这种老牌服务比较“固执”,它有一套独立于 `systemd` 的生存逻辑。
**建议操作建议:**
如果你想一劳永逸,**直接修改 `/etc/netconfig`**。这是对付 `rpcbind` 最优雅的方式,因为它从底层逻辑上切断了它对 IPv6 的渴望,而不需要你在 `systemd` 里写一大堆复杂的覆盖配置。
修改完后,别忘了重启:
Bash
systemctl stop rpcbind.service rpcbind.socket
systemctl start rpcbind.socket
systemctl start rpcbind.service
这种“与老旧服务斗智斗勇”的过程,正是深入理解 Linux 底层机制的必经之路。现在的感觉是不是好受点儿了?
---
修改 `/etc/netconfig` 就像是修改了程序的“基因图谱”。因为 `rpcbind` 和其他使用 `libtirpc` 库的程序是通过这个文件来了解系统支持哪些网络协议的。
注释掉 `udp6` 和 `tcp6` 的影响非常**精准且局部**,它并不会像禁用整个内核 IPv6 那样产生巨大的副作用。
---
### 1. 受影响的范围:仅限 RPC 相关服务
这是最关键的一点:**非 RPC 协议的服务完全不受影响。**
你的 SSH (22)、HTTP/Caddy (80/443)、WireGuard、Headscale 等服务**依然可以正常使用 IPv6**。它们不使用 `libtirpc` 库,所以根本不看 `/etc/netconfig`。
**真正会受影响的是:**
* **NFS (客户端与服务端):** 你将无法通过 IPv6 地址挂载远程 NFS 目录,也无法通过 IPv6 为别人提供 NFS 服务。
* **RPC 辅助服务:** 如 `rpc.statd` (文件锁定服务) 和 `rpc.mountd`,它们也会失去 IPv6 能力。
* **配额管理 (Quota):** 某些老旧的远程磁盘配额管理工具如果基于 RPC,也会受限。
---
### 2. 对系统的具体表现
#### 优势(你想要的控制感):
* **彻底闭嘴:** `rpcbind` 启动时发现配置文件里没有 IPv6 协议,它会直接跳过 IPv6 套接字的创建。即便 `ss -tunlp` 也再找不到 `[::]:111`。
* **安全性提升:** 杜绝了通过公网 IPv6 地址进行的 RPC 扫描或放大攻击。
#### 可能的负面表现:
* **启动日志警告:** 在 `journalctl -u rpcbind` 中,你可能会看到类似 `cannot get transport for udp6` 的警告消息。但这仅仅是提示,不会导致服务崩溃。
* **NFS 挂载超时(仅限 IPv6):** 如果你以后无意中尝试 `mount -t nfs [IPv6地址]:/data /mnt`,系统会直接报错“协议不支持”,而不是尝试连接。
---
### 3. 与“禁用内核 IPv6”的区别
| 特性 | 修改 `/etc/netconfig` | 禁用内核 IPv6 (sysctl) |
| --- | --- | --- |
| **影响范围** | **仅限 RPC 程序** (NFS, rpcbind) | **全局所有程序** (SSH, Web, 联网) |
| **网络层级** | 应用层配置映射 | 内核协议栈 |
| **灵活性** | 高(其他程序仍可玩转 IPv6) | 低(整个 IPv6 栈被关闭) |
| **推荐程度** | **针对 RPC 问题的最优解** | 除非完全不用 IPv6,否则不推荐 |
---
### 总结
**这对你的 PVE 环境几乎没有负面影响**(除非你正打算通过 IPv6 挂载 NFS 存储)。