1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 inInitrd = any (fs: fs == "nfs") config.boot.initrd.supportedFilesystems;
8
9 nfsStateDir = "/var/lib/nfs";
10
11 rpcMountpoint = "${nfsStateDir}/rpc_pipefs";
12
13 idmapdConfFile = pkgs.writeText "idmapd.conf" ''
14 [General]
15 Pipefs-Directory = ${rpcMountpoint}
16 ${optionalString (config.networking.domain != null)
17 "Domain = ${config.networking.domain}"}
18
19 [Mapping]
20 Nobody-User = nobody
21 Nobody-Group = nogroup
22
23 [Translation]
24 Method = nsswitch
25 '';
26
27 cfg = config.services.nfs;
28
29in
30
31{
32 ###### interface
33
34 options = {
35
36 services.nfs = {
37 statdPort = mkOption {
38 default = null;
39 example = 4000;
40 description = ''
41 Use fixed port for rpc.statd, useful if NFS server is behind firewall.
42 '';
43 };
44 lockdPort = mkOption {
45 default = null;
46 example = 4001;
47 description = ''
48 Use fixed port for NFS lock manager kernel module (lockd/nlockmgr),
49 useful if NFS server is behind firewall.
50 '';
51 };
52 };
53 };
54
55 ###### implementation
56
57 config = mkIf (any (fs: fs == "nfs" || fs == "nfs4") config.boot.supportedFilesystems) {
58
59 services.rpcbind.enable = true;
60
61 system.fsPackages = [ pkgs.nfs-utils ];
62
63 boot.extraModprobeConfig = mkIf (cfg.lockdPort != null) ''
64 options lockd nlm_udpport=${toString cfg.lockdPort} nlm_tcpport=${toString cfg.lockdPort}
65 '';
66
67 boot.kernelModules = [ "sunrpc" ];
68
69 boot.initrd.kernelModules = mkIf inInitrd [ "nfs" ];
70
71 systemd.services.statd =
72 { description = "NFSv3 Network Status Monitor";
73
74 path = [ pkgs.nfs-utils pkgs.sysvtools pkgs.utillinux ];
75
76 wantedBy = [ "remote-fs-pre.target" ];
77 before = [ "remote-fs-pre.target" ];
78 requires = [ "basic.target" "rpcbind.service" ];
79 after = [ "basic.target" "rpcbind.service" ];
80
81 unitConfig.DefaultDependencies = false; # don't stop during shutdown
82
83 preStart =
84 ''
85 mkdir -p ${nfsStateDir}/sm
86 mkdir -p ${nfsStateDir}/sm.bak
87 sm-notify -d
88 '';
89
90 serviceConfig.Type = "forking";
91 serviceConfig.ExecStart = ''
92 @${pkgs.nfs-utils}/sbin/rpc.statd rpc.statd --no-notify \
93 ${if cfg.statdPort != null then "-p ${toString cfg.statdPort}" else ""}
94 '';
95 serviceConfig.Restart = "always";
96 };
97
98 systemd.services.idmapd =
99 { description = "NFSv4 ID Mapping Daemon";
100
101 path = [ pkgs.sysvtools pkgs.utillinux ];
102
103 wantedBy = [ "remote-fs-pre.target" ];
104 before = [ "remote-fs-pre.target" ];
105 requires = [ "rpcbind.service" ];
106 after = [ "rpcbind.service" ];
107
108 preStart =
109 ''
110 mkdir -p ${rpcMountpoint}
111 mount -t rpc_pipefs rpc_pipefs ${rpcMountpoint}
112 '';
113
114 postStop =
115 ''
116 umount ${rpcMountpoint}
117 '';
118
119 serviceConfig.Type = "forking";
120 serviceConfig.ExecStart = "@${pkgs.nfs-utils}/sbin/rpc.idmapd rpc.idmapd -c ${idmapdConfFile}";
121 serviceConfig.Restart = "always";
122 };
123
124 };
125}