
Linux NFS ID Mapper 深度解析UID/GID 与用户名/组名的双向映射与 request-key 配置实战【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linuxNFSv4 协议在网络上以“用户名域”这样的字符串形式传递文件属主而本地内核只用数字 UID/GID 表示身份。NFS ID Mapper 就是负责在两者之间做转换的机制本文基于内核文档 nfs-idmapper.rst 展开完整覆盖 request-key 方式的配置方法、idmap 管道协议的报文字段并结合 fs/nfs/nfs4idmap.c 源码讲清“优先走 /sbin/request-key、失败后回退到 rpc.idmap 守护进程”的双通道实现帮助你在配置 NFSv4 客户端属主映射时做到知其然亦知其所以然。一、ID Mapper 解决什么问题NFS 需要把用户和组的 ID 翻译成名字也要把名字翻译回 ID文档原文见 Documentation/admin-guide/nfs/nfs-idmapper.rst。这部分翻译涉及向用户态发起 upcall 请求。内核支持两条获取信息的通道request-key 通道首选调用/sbin/request-key成功后结果缓存在通用的 request-key 缓存中rpc.idmap 管道通道回退当/etc/request-key.conf没有为id_resolver密钥类型配置规则时请求-key 会失败idmapper 转而询问传统的rpc.idmap守护进程结果存放在 NFS 自有的 idmap 缓存中。从源码结构看这个“先 A 后 B”的优先级在 nfs_idmap_request_key() 中体现得非常直接if (!idmap-user_ns || idmap-user_ns init_user_ns) rkey request_key(key_type_id_resolver, desc, ); if (IS_ERR(rkey)) { mutex_lock(idmap-idmap_mutex); rkey request_key_with_auxdata(key_type_id_resolver_legacy, desc, NULL, , 0, idmap); mutex_unlock(idmap-idmap_mutex); }第一次尝试使用名为id_resolver的密钥类型失败后IS_ERR才回退到id_legacy类型——后者通过request_key回调最终落到 rpc.idmap 管道见下文“回退通道”一节。二、Configuring配置 /etc/request-key.conf这是文档中唯一需要管理员动手的部分。修改/etc/request-key.conf使/sbin/request-key知道如何转发 upcall加入如下一行表头即文档原文格式#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... # create id_resolver * * /usr/sbin/nfs.idmap %k %d 600这一行把所有id_resolver请求都指向/usr/sbin/nfs.idmap程序。要点%k展开为序列化密钥serialized key%d展开为密钥描述key description最后一个参数 600表示密钥在 600 秒后过期缓存有效期该参数对/usr/sbin/nfs.idmap是可选的不指定时 nfs.idmap 默认就是 600 秒——这与内核侧的默认值呼应nfs_idmap_cache_timeout在 fs/nfs/super.c 中初始化为600并作为模块参数暴露module_param(nfs_idmap_cache_timeout, int, 0644)fs/nfs/super.c。四类密钥描述uid / gid / user / groupID mapper 使用的密钥描述格式为前缀:名称四种前缀各管一类查询uid: Find the UID for the given user 名字 → UID gid: Find the GID for the given group 名字 → GID user: Find the user name for the given UID UID → 名字 group: Find the group name for the given GID GID → 名字这四个前缀在内核里的“真身”是一张匹配表见 nfs_idmap_tokensstatic const match_table_t nfs_idmap_tokens { { Opt_find_uid, uid:%s }, { Opt_find_gid, gid:%s }, { Opt_find_user, user:%s }, { Opt_find_group, group:%s }, { Opt_find_err, NULL } };描述串由 nfs_idmap_get_desc() 拼装前缀:名字/数字例如uid:bobmy.domain。按查询类型分别指定处理程序你不必让一个通用程序处理全部四类查询。request-key 的规则匹配是“从上到下取第一条命中的规则”因此可以为 uid 查询单独指定程序并把这条新规则放在通用规则上方#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... # create id_resolver uid:* * /some/other/program %k %d 600 create id_resolver * * /usr/sbin/nfs.idmap %k %d 600这样/some/other/program处理所有 uid 查找/usr/sbin/nfs.idmap处理 gid、user、group 查找。request-key 功能的更多细节可参阅 Documentation/security/keys/request-key.rst。三、nfs.idmap 与 request-key 的配合文档对 nfs.idmap 的说明是它被设计为由 request-key 调用不应手工运行。程序接收两个参数——序列化密钥和密钥描述序列化密钥先被转换成key_serial_t然后连同描述一起传给keyctl_instantiate两者均属于 keyutils.h 的接口。实际查询由 nfsidmap.h 中的函数完成nfs.idmap 观察描述串的第一段来决定调用哪个函数例如 uid 查询的描述形如uid:userdomain。nfs.idmap 在密钥实例化成功时返回 0否则返回非 0。内核侧与之对应的注册流程在 nfs_idmap_init() 中由 NFSv4 模块初始化 init_nfs_v4() 调用注册密钥类型 key_type_id_resolver其.name id_resolver即 request-key.conf 里 TYPE 列要写的名字分配全局密钥环.id_resolver并挂到专用 cred 上作为缓存id_resolver_cache这就是“通用 request-key 缓存”的落地位置同时注册id_legacy密钥类型为回退通道做准备。值得注意的一个优化并非所有映射都要发起 upcall。若远端传来的属主字符串本身就是纯数字不含且可解析为无符号整数nfs_map_string_to_numeric() 会直接把它转成数值 ID跳过整个用户态往返。四、回退通道rpc.idmap 管道协议当 request-key 通道不可用时id_legacy密钥类型的.request_key回调是 nfs_idmap_legacy_upcall()。它通过 RPC 管道文件系统rpc_pipe把请求送到/var/lib/nfs/rpc_pipefs下的idmap管道由用户态 rpc.idmap 守护进程读取并写回结果downcall。报文字段struct idmap_msgupcall 与 downcall 传递的报文结构是内核与用户空间共用的 uAPI 定义位于 include/uapi/linux/nfs_idmap.hstruct idmap_msg { __u8 im_type; /* IDMAP_TYPE_USER0 / IDMAP_TYPE_GROUP1 */ __u8 im_conv; /* IDMAP_CONV_IDTONAME0 / IDMAP_CONV_NAMETOID1 */ char im_name[IDMAP_NAMESZ]; /* 128 字节名字字段 */ __u32 im_id; /* 数字 ID */ __u8 im_status; /* 状态位 */ };配套宏定义了状态位IDMAP_STATUS_INVALIDMSG 0x01、IDMAP_STATUS_AGAIN 0x02、IDMAP_STATUS_LOOKUPFAIL 0x04、IDMAP_STATUS_SUCCESS 0x08。消息的组装与校验组装nfs_idmap_prepare_message() 用上面的nfs_idmap_tokens解析描述串填出im_typeUSER/GROUP、im_convNAMETOID/IDTONAME以及名字或数字 ID接收idmap_pipe_downcall() 校验报文长度必须恰为sizeof(struct idmap_msg)、im_status必须带IDMAP_STATUS_SUCCESS位、名字非空且未截断随后 nfs_idmap_read_and_verify_message() 比对应答的im_type、im_conv以及所查的名字/ID 与请求是否一致一致才把结果实例化成密钥并链接进.id_resolver缓存环缓存超时downcall 成功后执行key_set_timeout(rka-target_key, nfs_idmap_cache_timeout)fs/nfs/nfs4idmap.c即文档所说“结果存放在自定义 NFS idmap 缓存中”并带过期时间。该超时值有两个用户态调整入口模块参数nfs_idmap_cache_timeoutfs/nfs/super.c以及 sysctlfs/nfs/idmap_cache_timeout在 nfs4_register_sysctl() 中注册于fs/nfs目录条目定义见 fs/nfs/nfs4sysctl.c权限 0644 可运行时读写。五、四个映射函数数据流的终点无论走哪条通道最终都汇聚到四个对外接口声明在 fs/nfs/nfs4idmap.h实现均在 fs/nfs/nfs4idmap.c函数方向描述前缀说明nfs_map_name_to_uid()名字 → UIDuid纯数字字符串直接转换否则查 key 缓存/upcallnfs_map_group_to_gid()名字 → GIDgid同上nfs_map_uid_to_name()UID → 名字user映射失败时回退为数字字符串nfs_map_gid_to_group()GID → 名字group同上几个实现细节值得注意user namespace 感知ID 与本地kuid_t/kgid_t的转换都经过idmap_userns()fs/nfs/nfs4idmap.c转换后若在本用户命名空间内无效uid_valid为假则返回-ERANGE回退不报错UID→名字方向在 key 查询失败时会退化为直接输出数字 IDnfs_map_numeric_to_string保证ls -l等路径不会因为一次映射失败而整体失败调用时机这些函数主要在属性路径上被调用例如 GETATTR 应答解码处 nfs4xdr.c 用nfs_map_uid_to_name()把属主 UID 渲染为名字nfs4xdr.c 与 nfs4xdr.c 分别用nfs_map_name_to_uid()/nfs_map_group_to_gid()把名字翻译回 UID/GID。六、实操清单配置 NFS 客户端 ID 映射结合本文各节一套完整的客户端配置核查步骤如下确认 NFSv4 客户端已加载——id_resolver密钥类型由 NFSv4 模块初始化时注册init_nfs_v4() 调用nfs_idmap_init()启动日志可见NFS: Registering the id_resolver key typefs/nfs/nfs4idmap.c编辑/etc/request-key.conf加入前文给出的create id_resolver * * /usr/sbin/nfs.idmap %k %d 600规则按需为uid:*等子集增加更具体的规则并置于通用规则之前若坚持使用传统 rpc.idmap 守护进程则不必配置 request-key内核会自动回退到idmap管道通道此时可用fs/nfs/idmap_cache_timeoutsysctl或模块参数nfs_idmap_cache_timeout调整缓存有效期默认 600 秒验证对远端文件执行ls -l观察属主是否被解析为名字而非数字也可用keyctl查看.id_resolver环中的缓存条目。小结NFS ID Mapper 的价值在于让 NFSv4 的“属主是字符串”模型与本地“属主是数字 ID”模型无缝衔接且缓存、命名空间转换与失败回退都在内核侧闭环处理。理解id_resolver密钥类型与idmap管道这两条通道及其各自的报文/参数约定是排查“属主显示为数字”“属主变成 nobody”等典型 NFSv4 映射问题的关键。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考