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}