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 setupScript = 16 '' 17 mkdir -p /var/lock/samba /var/log/samba /var/cache/samba /var/lib/samba/private 18 ''; 19 20 shareConfig = name: 21 let share = getAttr name cfg.shares; in 22 "[${name}]\n " + (smbToString ( 23 map 24 (key: "${key} = ${smbToString (getAttr key share)}\n") 25 (attrNames share) 26 )); 27 28 configFile = pkgs.writeText "smb.conf" 29 (if cfg.configText != null then cfg.configText else 30 '' 31 [global] 32 security = ${cfg.securityType} 33 passwd program = /run/wrappers/bin/passwd %u 34 pam password change = ${smbToString cfg.syncPasswordsByPam} 35 invalid users = ${smbToString cfg.invalidUsers} 36 37 ${cfg.extraConfig} 38 39 ${smbToString (map shareConfig (attrNames cfg.shares))} 40 ''); 41 42 # This may include nss_ldap, needed for samba if it has to use ldap. 43 nssModulesPath = config.system.nssModules.path; 44 45 daemonService = appName: args: 46 { description = "Samba Service Daemon ${appName}"; 47 48 requiredBy = [ "samba.target" ]; 49 partOf = [ "samba.target" ]; 50 51 environment = { 52 LD_LIBRARY_PATH = nssModulesPath; 53 LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; 54 }; 55 56 serviceConfig = { 57 ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}"; 58 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 59 LimitNOFILE = 16384; 60 PIDFile = "/run/${appName}.pid"; 61 Type = "notify"; 62 NotifyAccess = "all"; #may not do anything... 63 }; 64 65 restartTriggers = [ configFile ]; 66 }; 67 68in 69 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 = '' 84 Whether to enable Samba, which provides file and print 85 services to Windows clients through the SMB/CIFS protocol. 86 87 <note> 88 <para>If you use the firewall consider adding the following:</para> 89 <programlisting> 90 networking.firewall.allowedTCPPorts = [ 139 445 ]; 91 networking.firewall.allowedUDPPorts = [ 137 138 ]; 92 </programlisting> 93 </note> 94 ''; 95 }; 96 97 enableNmbd = mkOption { 98 type = 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 enableWinbindd = mkOption { 108 type = types.bool; 109 default = true; 110 description = '' 111 Whether to enable Samba's winbindd, which provides a number of services 112 to the Name Service Switch capability found in most modern C libraries, 113 to arbitrary applications via PAM and ntlm_auth and to Samba itself. 114 ''; 115 }; 116 117 package = mkOption { 118 type = types.package; 119 default = pkgs.samba; 120 defaultText = "pkgs.samba"; 121 example = literalExample "pkgs.samba3"; 122 description = '' 123 Defines which package should be used for the samba server. 124 ''; 125 }; 126 127 syncPasswordsByPam = mkOption { 128 type = types.bool; 129 default = false; 130 description = '' 131 Enabling this will add a line directly after pam_unix.so. 132 Whenever a password is changed the samba password will be updated as well. 133 However, you still have to add the samba password once, using smbpasswd -a user. 134 If you don't want to maintain an extra password database, you still can send plain text 135 passwords which is not secure. 136 ''; 137 }; 138 139 invalidUsers = mkOption { 140 type = types.listOf types.str; 141 default = [ "root" ]; 142 description = '' 143 List of users who are denied to login via Samba. 144 ''; 145 }; 146 147 extraConfig = mkOption { 148 type = types.lines; 149 default = ""; 150 description = '' 151 Additional global section and extra section lines go in here. 152 ''; 153 example = '' 154 guest account = nobody 155 map to guest = bad user 156 ''; 157 }; 158 159 configText = mkOption { 160 type = types.nullOr types.lines; 161 default = null; 162 description = '' 163 Verbatim contents of smb.conf. If null (default), use the 164 autogenerated file from NixOS instead. 165 ''; 166 }; 167 168 securityType = mkOption { 169 type = types.str; 170 default = "user"; 171 example = "share"; 172 description = "Samba security type"; 173 }; 174 175 nsswins = mkOption { 176 default = false; 177 type = types.bool; 178 description = '' 179 Whether to enable the WINS NSS (Name Service Switch) plug-in. 180 Enabling it allows applications to resolve WINS/NetBIOS names (a.k.a. 181 Windows machine names) by transparently querying the winbindd daemon. 182 ''; 183 }; 184 185 shares = mkOption { 186 default = {}; 187 description = '' 188 A set describing shared resources. 189 See <command>man smb.conf</command> for options. 190 ''; 191 type = types.attrsOf (types.attrsOf types.unspecified); 192 example = 193 { public = 194 { path = "/srv/public"; 195 "read only" = true; 196 browseable = "yes"; 197 "guest ok" = "yes"; 198 comment = "Public samba share."; 199 }; 200 }; 201 }; 202 203 }; 204 205 }; 206 207 208 ###### implementation 209 210 config = mkMerge 211 [ { assertions = 212 [ { assertion = cfg.nsswins -> cfg.enableWinbindd; 213 message = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled"; 214 } 215 ]; 216 # Always provide a smb.conf to shut up programs like smbclient and smbspool. 217 environment.etc."samba/smb.conf".source = mkOptionDefault ( 218 if cfg.enable then configFile 219 else pkgs.writeText "smb-dummy.conf" "# Samba is disabled." 220 ); 221 } 222 223 (mkIf cfg.enable { 224 225 system.nssModules = optional cfg.nsswins samba; 226 227 systemd = { 228 targets.samba = { 229 description = "Samba Server"; 230 requires = [ "samba-setup.service" ]; 231 after = [ "samba-setup.service" "network.target" ]; 232 wantedBy = [ "multi-user.target" ]; 233 }; 234 # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd 235 # for correct use with systemd 236 services = { 237 "samba-smbd" = daemonService "smbd" ""; 238 "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" ""); 239 "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" ""); 240 "samba-setup" = { 241 description = "Samba Setup Task"; 242 script = setupScript; 243 unitConfig.RequiresMountsFor = "/var/lib/samba"; 244 }; 245 }; 246 }; 247 248 security.pam.services.samba = {}; 249 250 }) 251 ]; 252 253}