Tokio 在 NetBSD 取对端凭证报 ENOPROTOOPT,检查选项层级
Tokio 1.53.1在NetBSD使用错误的socket选项层级查询凭证。采用1.54系列修正,并用listener连接核对UID/GID,区分socketpair条件。
NetBSD 11 上的 Unix 域连接已经建立,Tokio 的 UnixStream::peer_cred() 却返回 ENOPROTOOPT,错误文字为 Protocol option not available。如果实际依赖是 Tokio 1.53.1,要检查获取对端凭证时采用的 socket 选项层级。
这条故障发生在查询凭证时。它不表示 Unix socket 不能通信,也不能靠把服务改成 root 解决;即使服务有较高权限,错误的选项层级仍然不会成为有效请求。
LOCAL_PEEREID 要在 SOL_LOCAL 查询
Tokio 1.53.1 的 NetBSD 分支将 LOCAL_PEEREID 交给 getsockopt(),却把 level 设成 SOL_SOCKET。NetBSD 为这个选项规定的层级是 SOL_LOCAL。因此,失败发生在内核选择 socket 选项的阶段,尚未取得应用想检查的 UID、GID 和 PID。
先从完整错误路径确认确实是 peer_cred() 返回的错误,再核对构建产物的 Tokio 版本和目标平台。Linux 的 SO_PEERCRED、其他 BSD 的对应实现不同,不能用 Linux 上同一段代码成功来排除 NetBSD 分支问题。
使用包含层级修正的正式版本
Tokio 1.54.0 已列入 NetBSD 凭证修复,1.54.1 的正式实现也使用 SOL_LOCAL,同时保留 LOCAL_PEEREID 提供的 PID。项目允许升级时,选择包含修正的兼容版本,核对 Cargo.lock 与真正出错的依赖实例。1.54 系列要求 Rust 1.85,更新依赖前也要检查项目工具链约束。
获取与授权仍应分开。下面是放在已有异步服务中的凭证读取函数,只要求 Tokio 的 net 功能;传入值应是已连接或从 listener 接受的连接:
use std::io;
use tokio::net::UnixStream;
fn peer_ids(stream: &UnixStream) -> io::Result<(u32, u32)> {
let credentials = stream.peer_cred()?;
Ok((credentials.uid(), credentials.gid()))
}
函数把查询失败原样交给调用方,没有默认放行。真正的服务仍应按原有 UID/GID 策略决定是否接收请求,而不是把“能读到凭证”当作所有用户均可信。
暂时不能升级时,NetBSD 的 getpeereid(3) 可以取得有效 UID 和 GID,但不提供这里所需的 PID。只有授权策略本来就只依赖 UID/GID,且项目能够正确管理平台限定和文件描述符生命周期,才适合采用这条过渡路径。需要 PID 的策略不能悄悄删掉该条件,也不要把错误转成 UID 0 或当前进程身份。
用 listener 连接验收,别只测 pair
NetBSD 上 UnixStream::pair() 不等价于经 listener 建立的连接。此平台的 pair 路径可能没有可供同样方式查询的对端凭证,出现 EINVAL 时不能直接判定层级修复无效。
在隔离 socket 路径上,分别核对连接端和接受端的凭证,确认返回值与建立连接时的身份相符,再验证允许和拒绝两条授权路径。测试目录与 socket 权限应延续服务原来的限制;不需要扩大权限来完成查询。升级后保留原依赖锁定便于回退,但回退到受影响实现时也必须恢复其明确的拒绝或过渡策略。


