1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.security.pam.mount;
9
10 oflRequired = cfg.logoutHup || cfg.logoutTerm || cfg.logoutKill;
11
12 fake_ofl = pkgs.writeShellScriptBin "fake_ofl" ''
13 SIGNAL=$1
14 MNTPT=$2
15 ${pkgs.lsof}/bin/lsof | ${pkgs.gnugrep}/bin/grep $MNTPT | ${pkgs.gawk}/bin/awk '{print $2}' | ${pkgs.findutils}/bin/xargs ${pkgs.util-linux}/bin/kill -$SIGNAL
16 '';
17
18 anyPamMount = lib.any (svc: svc.enable && svc.pamMount) (
19 lib.attrValues config.security.pam.services
20 );
21in
22
23{
24 options = {
25
26 security.pam.mount = {
27 enable = lib.mkOption {
28 type = lib.types.bool;
29 default = false;
30 description = ''
31 Enable PAM mount system to mount filesystems on user login.
32 '';
33 };
34
35 extraVolumes = lib.mkOption {
36 type = lib.types.listOf lib.types.str;
37 default = [ ];
38 description = ''
39 List of volume definitions for pam_mount.
40 For more information, visit <https://pam-mount.sourceforge.net/pam_mount.conf.5.html>.
41 '';
42 };
43
44 additionalSearchPaths = lib.mkOption {
45 type = lib.types.listOf lib.types.package;
46 default = [ ];
47 example = lib.literalExpression "[ pkgs.bindfs ]";
48 description = ''
49 Additional programs to include in the search path of pam_mount.
50 Useful for example if you want to use some FUSE filesystems like bindfs.
51 '';
52 };
53
54 cryptMountOptions = lib.mkOption {
55 type = lib.types.listOf lib.types.str;
56 default = [ ];
57 example = lib.literalExpression ''
58 [ "allow_discard" ]
59 '';
60 description = ''
61 Global mount options that apply to every crypt volume.
62 You can define volume-specific options in the volume definitions.
63 '';
64 };
65
66 fuseMountOptions = lib.mkOption {
67 type = lib.types.listOf lib.types.str;
68 default = [ ];
69 example = lib.literalExpression ''
70 [ "nodev" "nosuid" "force-user=%(USER)" "gid=%(USERGID)" "perms=0700" "chmod-deny" "chown-deny" "chgrp-deny" ]
71 '';
72 description = ''
73 Global mount options that apply to every FUSE volume.
74 You can define volume-specific options in the volume definitions.
75 '';
76 };
77
78 debugLevel = lib.mkOption {
79 type = lib.types.int;
80 default = 0;
81 example = 1;
82 description = ''
83 Sets the Debug-Level. 0 disables debugging, 1 enables pam_mount tracing,
84 and 2 additionally enables tracing in mount.crypt. The default is 0.
85 For more information, visit <https://pam-mount.sourceforge.net/pam_mount.conf.5.html>.
86 '';
87 };
88
89 logoutWait = lib.mkOption {
90 type = lib.types.int;
91 default = 0;
92 description = ''
93 Amount of microseconds to wait until killing remaining processes after
94 final logout.
95 For more information, visit <https://pam-mount.sourceforge.net/pam_mount.conf.5.html>.
96 '';
97 };
98
99 logoutHup = lib.mkOption {
100 type = lib.types.bool;
101 default = false;
102 description = ''
103 Kill remaining processes after logout by sending a SIGHUP.
104 '';
105 };
106
107 logoutTerm = lib.mkOption {
108 type = lib.types.bool;
109 default = false;
110 description = ''
111 Kill remaining processes after logout by sending a SIGTERM.
112 '';
113 };
114
115 logoutKill = lib.mkOption {
116 type = lib.types.bool;
117 default = false;
118 description = ''
119 Kill remaining processes after logout by sending a SIGKILL.
120 '';
121 };
122
123 createMountPoints = lib.mkOption {
124 type = lib.types.bool;
125 default = true;
126 description = ''
127 Create mountpoints for volumes if they do not exist.
128 '';
129 };
130
131 removeCreatedMountPoints = lib.mkOption {
132 type = lib.types.bool;
133 default = true;
134 description = ''
135 Remove mountpoints created by pam_mount after logout. This
136 only affects mountpoints that have been created by pam_mount
137 in the same session.
138 '';
139 };
140 };
141
142 };
143
144 config = lib.mkIf (cfg.enable || anyPamMount) {
145
146 environment.systemPackages = [ pkgs.pam_mount ];
147 environment.etc."security/pam_mount.conf.xml" = {
148 source =
149 let
150 extraUserVolumes = lib.filterAttrs (
151 n: u: u.cryptHomeLuks != null || u.pamMount != { }
152 ) config.users.users;
153 mkAttr = k: v: ''${k}="${v}"'';
154 userVolumeEntry =
155 user:
156 let
157 attrs = {
158 user = user.name;
159 path = user.cryptHomeLuks;
160 mountpoint = user.home;
161 } // user.pamMount;
162 in
163 "<volume ${lib.concatStringsSep " " (lib.mapAttrsToList mkAttr attrs)} />\n";
164 in
165 pkgs.writeText "pam_mount.conf.xml" ''
166 <?xml version="1.0" encoding="utf-8" ?>
167 <!DOCTYPE pam_mount SYSTEM "pam_mount.conf.xml.dtd">
168 <!-- auto generated from Nixos: modules/config/users-groups.nix -->
169 <pam_mount>
170 <debug enable="${toString cfg.debugLevel}" />
171 <!-- if activated, requires ofl from hxtools to be present -->
172 <logout wait="${toString cfg.logoutWait}" hup="${if cfg.logoutHup then "yes" else "no"}" term="${
173 if cfg.logoutTerm then "yes" else "no"
174 }" kill="${if cfg.logoutKill then "yes" else "no"}" />
175 <!-- set PATH variable for pam_mount module -->
176 <path>${lib.makeBinPath ([ pkgs.util-linux ] ++ cfg.additionalSearchPaths)}</path>
177 <!-- create mount point if not present -->
178 <mkmountpoint enable="${if cfg.createMountPoints then "1" else "0"}" remove="${
179 if cfg.removeCreatedMountPoints then "true" else "false"
180 }" />
181 <!-- specify the binaries to be called -->
182 <!-- the comma in front of the options is necessary for empty options -->
183 <fusemount>${pkgs.fuse}/bin/mount.fuse %(VOLUME) %(MNTPT) -o ,${
184 lib.concatStringsSep "," (cfg.fuseMountOptions ++ [ "%(OPTIONS)" ])
185 }'</fusemount>
186 <fuseumount>${pkgs.fuse}/bin/fusermount -u %(MNTPT)</fuseumount>
187 <!-- the comma in front of the options is necessary for empty options -->
188 <cryptmount>${pkgs.pam_mount}/bin/mount.crypt -o ,${
189 lib.concatStringsSep "," (cfg.cryptMountOptions ++ [ "%(OPTIONS)" ])
190 } %(VOLUME) %(MNTPT)</cryptmount>
191 <cryptumount>${pkgs.pam_mount}/bin/umount.crypt %(MNTPT)</cryptumount>
192 <pmvarrun>${pkgs.pam_mount}/bin/pmvarrun -u %(USER) -o %(OPERATION)</pmvarrun>
193 ${lib.optionalString oflRequired "<ofl>${fake_ofl}/bin/fake_ofl %(SIGNAL) %(MNTPT)</ofl>"}
194 ${lib.concatStrings (map userVolumeEntry (lib.attrValues extraUserVolumes))}
195 ${lib.concatStringsSep "\n" cfg.extraVolumes}
196 </pam_mount>
197 '';
198 };
199
200 };
201}