1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 smbToString = x: if builtins.typeOf x == "bool" 8 then boolToString x 9 else toString x; 10 11 cfg = config.services.samba; 12 13 samba = cfg.package; 14 15 shareConfig = name: 16 let share = getAttr name cfg.shares; in 17 "[${name}]\n " + (smbToString ( 18 map 19 (key: "${key} = ${smbToString (getAttr key share)}\n") 20 (attrNames share) 21 )); 22 23 configFile = pkgs.writeText "smb.conf" 24 (if cfg.configText != null then cfg.configText else 25 '' 26 [global] 27 security = ${cfg.securityType} 28 passwd program = /run/wrappers/bin/passwd %u 29 invalid users = ${smbToString cfg.invalidUsers} 30 31 ${cfg.extraConfig} 32 33 ${smbToString (map shareConfig (attrNames cfg.shares))} 34 ''); 35 36 # This may include nss_ldap, needed for samba if it has to use ldap. 37 nssModulesPath = config.system.nssModules.path; 38 39 daemonService = appName: args: 40 { description = "Samba Service Daemon ${appName}"; 41 42 after = [ (mkIf (cfg.enableNmbd && "${appName}" == "smbd") "samba-nmbd.service") ]; 43 requiredBy = [ "samba.target" ]; 44 partOf = [ "samba.target" ]; 45 46 environment = { 47 LD_LIBRARY_PATH = nssModulesPath; 48 LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; 49 }; 50 51 serviceConfig = { 52 ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}"; 53 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 54 LimitNOFILE = 16384; 55 PIDFile = "/run/${appName}.pid"; 56 Type = "notify"; 57 NotifyAccess = "all"; #may not do anything... 58 }; 59 unitConfig.RequiresMountsFor = "/var/lib/samba"; 60 61 restartTriggers = [ configFile ]; 62 }; 63 64in 65 66{ 67 imports = [ 68 (mkRemovedOptionModule [ "services" "samba" "defaultShare" ] "") 69 (mkRemovedOptionModule [ "services" "samba" "syncPasswordsByPam" ] "This option has been removed by upstream, see https://bugzilla.samba.org/show_bug.cgi?id=10669#c10") 70 ]; 71 72 ###### interface 73 74 options = { 75 76 # !!! clean up the descriptions. 77 78 services.samba = { 79 80 enable = mkOption { 81 type = types.bool; 82 default = false; 83 description = lib.mdDoc '' 84 Whether to enable Samba, which provides file and print 85 services to Windows clients through the SMB/CIFS protocol. 86 87 ::: {.note} 88 If you use the firewall consider adding the following: 89 90 services.samba.openFirewall = true; 91 ::: 92 ''; 93 }; 94 95 openFirewall = mkOption { 96 type = types.bool; 97 default = false; 98 description = lib.mdDoc '' 99 Whether to automatically open the necessary ports in the firewall. 100 ''; 101 }; 102 103 enableNmbd = mkOption { 104 type = types.bool; 105 default = true; 106 description = lib.mdDoc '' 107 Whether to enable Samba's nmbd, which replies to NetBIOS over IP name 108 service requests. It also participates in the browsing protocols 109 which make up the Windows "Network Neighborhood" view. 110 ''; 111 }; 112 113 enableWinbindd = mkOption { 114 type = types.bool; 115 default = true; 116 description = lib.mdDoc '' 117 Whether to enable Samba's winbindd, which provides a number of services 118 to the Name Service Switch capability found in most modern C libraries, 119 to arbitrary applications via PAM and ntlm_auth and to Samba itself. 120 ''; 121 }; 122 123 package = mkOption { 124 type = types.package; 125 default = pkgs.samba; 126 defaultText = literalExpression "pkgs.samba"; 127 example = literalExpression "pkgs.samba4Full"; 128 description = lib.mdDoc '' 129 Defines which package should be used for the samba server. 130 ''; 131 }; 132 133 invalidUsers = mkOption { 134 type = types.listOf types.str; 135 default = [ "root" ]; 136 description = lib.mdDoc '' 137 List of users who are denied to login via Samba. 138 ''; 139 }; 140 141 extraConfig = mkOption { 142 type = types.lines; 143 default = ""; 144 description = lib.mdDoc '' 145 Additional global section and extra section lines go in here. 146 ''; 147 example = '' 148 guest account = nobody 149 map to guest = bad user 150 ''; 151 }; 152 153 configText = mkOption { 154 type = types.nullOr types.lines; 155 default = null; 156 description = lib.mdDoc '' 157 Verbatim contents of smb.conf. If null (default), use the 158 autogenerated file from NixOS instead. 159 ''; 160 }; 161 162 securityType = mkOption { 163 type = types.str; 164 default = "user"; 165 description = lib.mdDoc "Samba security type"; 166 }; 167 168 nsswins = mkOption { 169 default = false; 170 type = types.bool; 171 description = lib.mdDoc '' 172 Whether to enable the WINS NSS (Name Service Switch) plug-in. 173 Enabling it allows applications to resolve WINS/NetBIOS names (a.k.a. 174 Windows machine names) by transparently querying the winbindd daemon. 175 ''; 176 }; 177 178 shares = mkOption { 179 default = {}; 180 description = lib.mdDoc '' 181 A set describing shared resources. 182 See {command}`man smb.conf` for options. 183 ''; 184 type = types.attrsOf (types.attrsOf types.unspecified); 185 example = literalExpression '' 186 { public = 187 { path = "/srv/public"; 188 "read only" = true; 189 browseable = "yes"; 190 "guest ok" = "yes"; 191 comment = "Public samba share."; 192 }; 193 } 194 ''; 195 }; 196 197 }; 198 199 }; 200 201 202 ###### implementation 203 204 config = mkMerge 205 [ { assertions = 206 [ { assertion = cfg.nsswins -> cfg.enableWinbindd; 207 message = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled"; 208 } 209 ]; 210 # Always provide a smb.conf to shut up programs like smbclient and smbspool. 211 environment.etc."samba/smb.conf".source = mkOptionDefault ( 212 if cfg.enable then configFile 213 else pkgs.writeText "smb-dummy.conf" "# Samba is disabled." 214 ); 215 } 216 217 (mkIf cfg.enable { 218 219 system.nssModules = optional cfg.nsswins samba; 220 system.nssDatabases.hosts = optional cfg.nsswins "wins"; 221 222 systemd = { 223 targets.samba = { 224 description = "Samba Server"; 225 after = [ "network.target" ]; 226 wants = [ "network-online.target" ]; 227 wantedBy = [ "multi-user.target" ]; 228 }; 229 # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd 230 # for correct use with systemd 231 services = { 232 samba-smbd = daemonService "smbd" ""; 233 samba-nmbd = mkIf cfg.enableNmbd (daemonService "nmbd" ""); 234 samba-winbindd = mkIf cfg.enableWinbindd (daemonService "winbindd" ""); 235 }; 236 tmpfiles.rules = [ 237 "d /var/lock/samba - - - - -" 238 "d /var/log/samba - - - - -" 239 "d /var/cache/samba - - - - -" 240 "d /var/lib/samba/private - - - - -" 241 ]; 242 }; 243 244 security.pam.services.samba = {}; 245 environment.systemPackages = [ cfg.package ]; 246 247 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ 139 445 ]; 248 networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 137 138 ]; 249 }) 250 ]; 251 252}