1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.samba;
9
10 settingsFormat = pkgs.formats.ini {
11 listToValue = lib.concatMapStringsSep " " (lib.generators.mkValueStringDefault { });
12 };
13 # Ensure the global section is always first
14 globalConfigFile = settingsFormat.generate "smb-global.conf" { global = cfg.settings.global; };
15 sharesConfigFile = settingsFormat.generate "smb-shares.conf" (
16 lib.removeAttrs cfg.settings [ "global" ]
17 );
18
19 configFile = pkgs.concatText "smb.conf" [
20 globalConfigFile
21 sharesConfigFile
22 ];
23
24in
25
26{
27 meta = {
28 doc = ./samba.md;
29 maintainers = [ lib.maintainers.anthonyroussel ];
30 };
31
32 imports = [
33 (lib.mkRemovedOptionModule [ "services" "samba" "defaultShare" ] "")
34 (lib.mkRemovedOptionModule [ "services" "samba" "syncPasswordsByPam" ]
35 "This option has been removed by upstream, see https://bugzilla.samba.org/show_bug.cgi?id=10669#c10"
36 )
37
38 (lib.mkRemovedOptionModule [ "services" "samba" "configText" ] ''
39 Use services.samba.settings instead.
40
41 This is part of the general move to use structured settings instead of raw
42 text for config as introduced by RFC0042:
43 https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md
44 '')
45 (lib.mkRemovedOptionModule [
46 "services"
47 "samba"
48 "extraConfig"
49 ] "Use services.samba.settings instead.")
50 (lib.mkRenamedOptionModule
51 [ "services" "samba" "invalidUsers" ]
52 [ "services" "samba" "settings" "global" "invalid users" ]
53 )
54 (lib.mkRenamedOptionModule
55 [ "services" "samba" "securityType" ]
56 [ "services" "samba" "settings" "global" "security" ]
57 )
58 (lib.mkRenamedOptionModule [ "services" "samba" "shares" ] [ "services" "samba" "settings" ])
59
60 (lib.mkRenamedOptionModule
61 [ "services" "samba" "enableWinbindd" ]
62 [ "services" "samba" "winbindd" "enable" ]
63 )
64 (lib.mkRenamedOptionModule
65 [ "services" "samba" "enableNmbd" ]
66 [ "services" "samba" "nmbd" "enable" ]
67 )
68 ];
69
70 ###### interface
71
72 options = {
73 services.samba = {
74 enable = lib.mkEnableOption "Samba, the SMB/CIFS protocol";
75
76 package = lib.mkPackageOption pkgs "samba" {
77 example = "samba4Full";
78 };
79
80 openFirewall = lib.mkEnableOption "opening the default ports in the firewall for Samba";
81
82 smbd = {
83 enable = lib.mkOption {
84 type = lib.types.bool;
85 default = true;
86 description = "Whether to enable Samba's smbd daemon.";
87 };
88
89 extraArgs = lib.mkOption {
90 type = lib.types.listOf lib.types.str;
91 default = [ ];
92 description = "Extra arguments to pass to the smbd service.";
93 };
94 };
95
96 nmbd = {
97 enable = lib.mkOption {
98 type = lib.types.bool;
99 default = true;
100 description = ''
101 Whether to enable Samba's nmbd, which replies to NetBIOS over IP name
102 service requests. It also participates in the browsing protocols
103 which make up the Windows "Network Neighborhood" view.
104 '';
105 };
106
107 extraArgs = lib.mkOption {
108 type = lib.types.listOf lib.types.str;
109 default = [ ];
110 description = "Extra arguments to pass to the nmbd service.";
111 };
112 };
113
114 winbindd = {
115 enable = lib.mkOption {
116 type = lib.types.bool;
117 default = true;
118 description = ''
119 Whether to enable Samba's winbindd, which provides a number of services
120 to the Name Service Switch capability found in most modern C libraries,
121 to arbitrary applications via PAM and ntlm_auth and to Samba itself.
122 '';
123 };
124
125 extraArgs = lib.mkOption {
126 type = lib.types.listOf lib.types.str;
127 default = [ ];
128 description = "Extra arguments to pass to the winbindd service.";
129 };
130 };
131
132 usershares = {
133 enable = lib.mkEnableOption "user-configurable Samba shares";
134 group = lib.mkOption {
135 type = lib.types.str;
136 default = "samba";
137 description = ''
138 Name of the group members of which will be allowed to create usershares.
139
140 The group will be created automatically.
141 '';
142 };
143 };
144
145 nsswins = lib.mkEnableOption ''
146 WINS NSS (Name Service Switch) plug-in.
147
148 Enabling it allows applications to resolve WINS/NetBIOS names (a.k.a.
149 Windows machine names) by transparently querying the winbindd daemon
150 '';
151
152 settings = lib.mkOption {
153 type = lib.types.submodule {
154 freeformType = settingsFormat.type;
155 options = {
156 global.security = lib.mkOption {
157 type = lib.types.enum [
158 "auto"
159 "user"
160 "domain"
161 "ads"
162 ];
163 default = "user";
164 description = "Samba security type.";
165 };
166 global."invalid users" = lib.mkOption {
167 type = lib.types.listOf lib.types.str;
168 default = [ "root" ];
169 description = "List of users who are denied to login via Samba.";
170 };
171 global."passwd program" = lib.mkOption {
172 type = lib.types.str;
173 default = "/run/wrappers/bin/passwd %u";
174 description = "Path to a program that can be used to set UNIX user passwords.";
175 };
176 };
177 };
178 default = {
179 "global" = {
180 "security" = "user";
181 "passwd program" = "/run/wrappers/bin/passwd %u";
182 "invalid users" = [ "root" ];
183 };
184 };
185 example = {
186 "global" = {
187 "security" = "user";
188 "passwd program" = "/run/wrappers/bin/passwd %u";
189 "invalid users" = [ "root" ];
190 };
191 "public" = {
192 "path" = "/srv/public";
193 "read only" = "yes";
194 "browseable" = "yes";
195 "guest ok" = "yes";
196 "comment" = "Public samba share.";
197 };
198 };
199 description = ''
200 Configuration file for the Samba suite in ini format.
201 This file is located in /etc/samba/smb.conf
202
203 Refer to <https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html>
204 for all available options.
205 '';
206 };
207 };
208 };
209
210 ###### implementation
211
212 config = lib.mkMerge [
213 {
214 assertions = [
215 {
216 assertion = cfg.nsswins -> cfg.winbindd.enable;
217 message = "If services.samba.nsswins is enabled, then services.samba.winbindd.enable must also be enabled";
218 }
219 ];
220 }
221
222 (lib.mkIf cfg.enable {
223 environment.etc."samba/smb.conf".source = configFile;
224
225 system.nssModules = lib.optional cfg.nsswins cfg.package;
226 system.nssDatabases.hosts = lib.optional cfg.nsswins "wins";
227
228 systemd = {
229 slices.system-samba = {
230 description = "Samba (SMB Networking Protocol) Slice";
231 };
232 targets.samba = {
233 description = "Samba Server";
234 after = [ "network.target" ];
235 wants = [ "network-online.target" ];
236 wantedBy = [ "multi-user.target" ];
237 };
238 tmpfiles.rules = [
239 "d /var/lock/samba - - - - -"
240 "d /var/log/samba - - - - -"
241 "d /var/cache/samba - - - - -"
242 "d /var/lib/samba/private - - - - -"
243 ];
244 };
245
246 security.pam.services.samba = { };
247 environment.systemPackages = [ cfg.package ];
248 # Like other mount* related commands that need the setuid bit, this is
249 # required too.
250 security.wrappers."mount.cifs" = {
251 program = "mount.cifs";
252 source = "${lib.getBin pkgs.cifs-utils}/bin/mount.cifs";
253 owner = "root";
254 group = "root";
255 setuid = true;
256 };
257
258 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
259 139
260 445
261 ];
262 networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [
263 137
264 138
265 ];
266 })
267
268 (lib.mkIf (cfg.enable && cfg.nmbd.enable) {
269 systemd.services.samba-nmbd = {
270 description = "Samba NMB Daemon";
271 documentation = [
272 "man:nmbd(8)"
273 "man:samba(7)"
274 "man:smb.conf(5)"
275 ];
276
277 after = [
278 "network.target"
279 "network-online.target"
280 ];
281
282 partOf = [ "samba.target" ];
283 wantedBy = [ "samba.target" ];
284 wants = [ "network-online.target" ];
285
286 environment.LD_LIBRARY_PATH = config.system.nssModules.path;
287
288 serviceConfig = {
289 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
290 ExecStart = "${cfg.package}/sbin/nmbd --foreground --no-process-group ${lib.escapeShellArgs cfg.nmbd.extraArgs}";
291 LimitCORE = "infinity";
292 PIDFile = "/run/samba/nmbd.pid";
293 Slice = "system-samba.slice";
294 Type = "notify";
295 };
296
297 unitConfig.RequiresMountsFor = "/var/lib/samba";
298
299 restartTriggers = [ configFile ];
300 };
301 })
302
303 (lib.mkIf (cfg.enable && cfg.smbd.enable) {
304 systemd.services.samba-smbd = {
305 description = "Samba SMB Daemon";
306 documentation = [
307 "man:smbd(8)"
308 "man:samba(7)"
309 "man:smb.conf(5)"
310 ];
311
312 after =
313 [
314 "network.target"
315 "network-online.target"
316 ]
317 ++ lib.optionals (cfg.nmbd.enable) [
318 "samba-nmbd.service"
319 ]
320 ++ lib.optionals (cfg.winbindd.enable) [
321 "samba-winbindd.service"
322 ];
323
324 partOf = [ "samba.target" ];
325 wantedBy = [ "samba.target" ];
326 wants = [ "network-online.target" ];
327
328 environment.LD_LIBRARY_PATH = config.system.nssModules.path;
329
330 serviceConfig = {
331 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
332 ExecStart = "${cfg.package}/sbin/smbd --foreground --no-process-group ${lib.escapeShellArgs cfg.smbd.extraArgs}";
333 LimitCORE = "infinity";
334 LimitNOFILE = 16384;
335 PIDFile = "/run/samba/smbd.pid";
336 Slice = "system-samba.slice";
337 Type = "notify";
338 };
339
340 unitConfig.RequiresMountsFor = "/var/lib/samba";
341
342 restartTriggers = [ configFile ];
343 };
344 })
345
346 (lib.mkIf (cfg.enable && cfg.winbindd.enable) {
347 systemd.services.samba-winbindd = {
348 description = "Samba Winbind Daemon";
349 documentation = [
350 "man:winbindd(8)"
351 "man:samba(7)"
352 "man:smb.conf(5)"
353 ];
354
355 after =
356 [
357 "network.target"
358 ]
359 ++ lib.optionals (cfg.nmbd.enable) [
360 "samba-nmbd.service"
361 ];
362
363 partOf = [ "samba.target" ];
364 wantedBy = [ "samba.target" ];
365
366 environment.LD_LIBRARY_PATH = config.system.nssModules.path;
367
368 serviceConfig = {
369 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
370 ExecStart = "${cfg.package}/sbin/winbindd --foreground --no-process-group ${lib.escapeShellArgs cfg.winbindd.extraArgs}";
371 LimitCORE = "infinity";
372 PIDFile = "/run/samba/winbindd.pid";
373 Slice = "system-samba.slice";
374 Type = "notify";
375 };
376
377 unitConfig.RequiresMountsFor = "/var/lib/samba";
378
379 restartTriggers = [ configFile ];
380 };
381 })
382
383 (lib.mkIf (cfg.enable && cfg.usershares.enable) {
384 users.groups.${cfg.usershares.group} = { };
385
386 systemd.tmpfiles.settings."50-samba-usershares"."/var/lib/samba/usershares".d = {
387 user = "root";
388 group = cfg.usershares.group;
389 mode = "1775"; # sticky so users can't delete others' shares
390 };
391
392 # set some reasonable defaults
393 services.samba.settings.global = lib.mkDefault {
394 "usershare path" = "/var/lib/samba/usershares";
395 "usershare max shares" = 100; # high enough to be considered ~unlimited
396 "usershare allow guests" = true;
397 };
398 })
399 ];
400}