NFS + Kerberos

传统地,NFS服务器信任由客户端所发送的UID(NFSv2, NFSv3)或用户名(NFSv4),并且不提供(密码学意义上的)加密。这给予潜在的攻击者可乘之机。Kerberos可被用于弥补上述两个问题。

上文记述了Kerberos KDC的架设。在此文中,将用Kerberos完成对NFS(v4)的鉴权和加密。

假设

  1. Kerberos KDC的域名是kdc.oxlab.org
  2. NFS服务器的域名是n.oxlab.org
  3. NFS客户端的域名是r.oxlab.org
  4. 其他来自上文的(与KDC相关的)假设

NFSv4

设置过程大抵按照ArchWiki上的NFS一文1的第2, 3章节完成。仅使用NFSv4,并不使用NFSv3。在此过程中有两点需要注意:

  1. /etc/idmapd.conf中设置域名。须确保客户端和服务器配置相同域名。本例中,设置为oxlab.org
  2. 执行systemctl mask rpcbind.socket rpcbind.service nfs-server.service以确保停用NFSv3。

在此之后,服务器端新产生的监听端口应仅有TCP/2049(NFS),而没有111(portmapper),如有后者则表明未停用NFSv3。

可能的问题: 无法执行chown

这是由于NFS的idmapd没有被启动造成的。nfs-utils上游提供的nfs-idmapd.service(一般位于/usr/lib/systemd/system/nfs-idmapd.service)内容如下:

[Unit]
Description=NFSv4 ID-name mapping service
DefaultDependencies=no
Requires=rpc_pipefs.target
After=rpc_pipefs.target local-fs.target network-online.target
Wants=network-online.target

BindsTo=nfs-server.service

[Service]
Type=forking
ExecStart=/usr/sbin/rpc.idmapd

其中,BindsTo=nfs-server.service一行造成了这个问题。nfs-server.service系NFSv3服务器,而idmapd被用于NFSv4,且上文已将nfs-server.service遮蔽(systemctl mask),故nfs-idmapd.service未被启动。

可将/usr/lib/systemd/system/nfs-idmapd.service复制到/etc/systemd/system/nfs-idmapd.service,并删去其中BindsTo=nfs-server.service一行,或将其修改为BindsTo=nfsv4-server.service,再执行systemctl daemon-reload便可解决。

至此,一个工作正常的NFSv4服务器/客户端被架设。

NFSv4 + Kerberos: 挂载

为客户端和服务器创建Kerberos principals,并加入global keytab (/etc/krb5.keytab)

# kadmin5
addprinc -randkey nfs/n.oxlab.org
addprinc -randkey host/n.oxlab.org # 非必要
addprinc -randkey host/r.oxlab.org

在客户端执行systemctl enable --now nfs-client.target gssproxy.service,在服务器执行systemctl enable --now gssproxy.service。之后在客户端挂载服务器上导出(export)的目录,添加-o vers=4,sec=krb5p为挂载选项以使用Kerberos加密。

此时,挂载可以正常完成,但任何用户,包括root,无法访问挂载的目录。

可能导致无法挂载的问题

  1. 主机名与Kerberos principals不匹配,或未正确设置DNS解析: 向/etc/hosts添加相关字段以解决。
  2. KDC/服务器/客户端的系统时间不同步或导致Kerberos不能正常工作,启用NTP以防止这种情形。

使客户端的主机密钥映射为root

对服务器的/etc/krb5.conf做如下修改(添加了以auth_to_local开始的两行),便可使客户端的主机密钥(host/r.oxlab.org)被映射为服务器的root用户23。这样,在客户端使用root便可以访问挂载的目录了——这相当于在服务器上使用root访问。

[libdefaults]
	default_realm = OXLAB.ORG

[realms]
# use "kdc = ..." if realm admins haven't put SRV records into DNS
	OXLAB.ORG = {
		admin_server = kdc.oxlab.org
		kdc = kdc.oxlab.org
		default_principal_flags = +preauth

		auth_to_local = RULE:[2:$1/$2@$0](host/r.oxlab.org@OXLAB.ORG)s/.*/root/
		auth_to_local = DEFAULT
	}

[domain_realm]
	oxlab.org = OXLAB.ORG
	.oxlab.org = OXLAB.ORG
	.n.oxlab.org = OXLAB.ORG
	.r.oxlab.org = OXLAB.ORG

[logging]
#	kdc = SYSLOG:NOTICE
#	admin_server = SYSLOG:NOTICE
#	default = SYSLOG:NOTICE

出于安全考量,不建议这样做。

至此,使用Kerberos鉴权和加密的NFSv4配置完了。然而,(除root以外)没有用户能够访问这个挂载点,因此我们还需为每个用户导入Kerberos principal。

NFSv4 + Kerberos: 用户鉴权

为用户(本例中为leojenny,他们的UID分别是10001001)创建Kerberos principal并设置密码。

# kadmin
addprinc leo
addprinc jenny

使用ktutil将两用户的密码分别添加到与其UID对应的GSS-Proxy keytab中4。需要使用root用户以确保相关文件可写。

# ktutil
addent -password -p leo -k 1 -f
wkt /var/lib/gssproxy/clients/1000.keytab
q

# ktutil
addent -password -p jenny -k 1 -f
wkt /var/lib/gssproxy/clients/1001.keytab
q

rpc.gssd设置环境变量GSS_USE_PROXY=yes以令其使用GSS-Proxy5。在采用systemd的发行版,可创建/etc/systemd/system/rpc-gssd.service.d/gssproxy.conf并填入一下内容,后执行systemctl daemon-reload; systemctl restart rpc-gssd.service

[Service]
Environment="GSS_USE_PROXY=yes"

至此,用户leojenny也可以正常地访问他们位于NFS服务器上的内容了——以他们自己的身份,而非root,就像在服务器上一样。

可能的问题: systemctl daemon-reload卡住

在客户端通过/etc/fstab挂载使用了Kerberos的NFS后,运行systemctl daemon-reload可能十分缓慢,须等待约90秒。(systemd默认的DefaultDeviceTimeoutSec)

在执行systemctl daemon-reload时,systemd会对挂载点(包括NFS)运行statfs()。如将systemd的log level调高至debug(systemctl log-level debug),可见NFS相关的target出错并最终超时,可能系依赖关系问题导致。

使用systemd.mount而非/etc/fstab,并正确配置依赖似乎可以缓解这个问题。以下文件(位于客户端的/etc/systemd/system/mnt.mount)将服务器的/挂载到客户端的/mnt

[Unit]
Description=NFS mount: /mnt
Wants=nss-lookup.target nfs-client.target
After=nss-lookup.target nfs-client.target

[Mount]
What=n.oxlab.org:/
Where=/mnt
Options=defaults,sec=krb5p,nosuid,noatime,nofail
Type=nfs4
TimeoutSec=20

[Install]
WantedBy=multi-user.target

可能的问题: 性能相关

NFSv4默认的读/写块大小皆为1MiB(1048576)。在一些情况下,减小这个值可能提告性能。

例如,在笔者的系统上,设置rsize=8192,wsize=8192能显著提告性能。

  1. https://wiki.archlinux.org/title/NFS ↩︎
  2. https://web.mit.edu/Kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html ↩︎
  3. https://access.redhat.com/articles/4040141 ↩︎
  4. https://web.mit.edu/kerberos/krb5-1.12/doc/admin/admin_commands/kadmin_local.html#ktadd ↩︎
  5. https://github.com/gssapi/gssproxy/blob/main/docs/NFS.md#nfs-client ↩︎

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注