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

【删除旧文档/修正错误】

  1. 重启服务:

    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 上开启。

  1. 执行 systemctl edit rpcbind.socket。
  2. 输入以下内容(这会覆盖默认的全局监听):

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 运行。

  1. 执行 systemctl edit rpcbind.service。
  2. 输入以下内容:

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 存储)。