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} ${args}"; 58 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 59 Type = "notify"; 60 }; 61 62 restartTriggers = [ configFile ]; 63 }; 64 65in 66 67{ 68 69 ###### interface 70 71 options = { 72 73 # !!! clean up the descriptions. 74 75 services.samba = { 76 77 enable = mkOption { 78 type = types.bool; 79 default = false; 80 description = '' 81 Whether to enable Samba, which provides file and print 82 services to Windows clients through the SMB/CIFS protocol. 83 84 <note> 85 <para>If you use the firewall consider adding the following:</para> 86 <programlisting> 87 networking.firewall.allowedTCPPorts = [ 139 445 ]; 88 networking.firewall.allowedUDPPorts = [ 137 138 ]; 89 </programlisting> 90 </note> 91 ''; 92 }; 93 94 enableNmbd = mkOption { 95 type = types.bool; 96 default = true; 97 description = '' 98 Whether to enable Samba's nmbd, which replies to NetBIOS over IP name 99 service requests. It also participates in the browsing protocols 100 which make up the Windows "Network Neighborhood" view. 101 ''; 102 }; 103 104 enableWinbindd = mkOption { 105 type = types.bool; 106 default = true; 107 description = '' 108 Whether to enable Samba's winbindd, which provides a number of services 109 to the Name Service Switch capability found in most modern C libraries, 110 to arbitrary applications via PAM and ntlm_auth and to Samba itself. 111 ''; 112 }; 113 114 package = mkOption { 115 type = types.package; 116 default = pkgs.samba; 117 defaultText = "pkgs.samba"; 118 example = literalExample "pkgs.samba3"; 119 description = '' 120 Defines which package should be used for the samba server. 121 ''; 122 }; 123 124 syncPasswordsByPam = mkOption { 125 type = types.bool; 126 default = false; 127 description = '' 128 Enabling this will add a line directly after pam_unix.so. 129 Whenever a password is changed the samba password will be updated as well. 130 However, you still have to add the samba password once, using smbpasswd -a user. 131 If you don't want to maintain an extra password database, you still can send plain text 132 passwords which is not secure. 133 ''; 134 }; 135 136 invalidUsers = mkOption { 137 type = types.listOf types.str; 138 default = [ "root" ]; 139 description = '' 140 List of users who are denied to login via Samba. 141 ''; 142 }; 143 144 extraConfig = mkOption { 145 type = types.lines; 146 default = ""; 147 description = '' 148 Additional global section and extra section lines go in here. 149 ''; 150 example = '' 151 guest account = nobody 152 map to guest = bad user 153 ''; 154 }; 155 156 configText = mkOption { 157 type = types.nullOr types.lines; 158 default = null; 159 description = '' 160 Verbatim contents of smb.conf. If null (default), use the 161 autogenerated file from NixOS instead. 162 ''; 163 }; 164 165 securityType = mkOption { 166 type = types.str; 167 default = "user"; 168 example = "share"; 169 description = "Samba security type"; 170 }; 171 172 nsswins = mkOption { 173 default = false; 174 type = types.bool; 175 description = '' 176 Whether to enable the WINS NSS (Name Service Switch) plug-in. 177 Enabling it allows applications to resolve WINS/NetBIOS names (a.k.a. 178 Windows machine names) by transparently querying the winbindd daemon. 179 ''; 180 }; 181 182 shares = mkOption { 183 default = {}; 184 description = '' 185 A set describing shared resources. 186 See <command>man smb.conf</command> for options. 187 ''; 188 type = types.attrsOf (types.attrsOf types.unspecified); 189 example = 190 { public = 191 { path = "/srv/public"; 192 "read only" = true; 193 browseable = "yes"; 194 "guest ok" = "yes"; 195 comment = "Public samba share."; 196 }; 197 }; 198 }; 199 200 }; 201 202 }; 203 204 205 ###### implementation 206 207 config = mkMerge 208 [ { assertions = 209 [ { assertion = cfg.nsswins -> cfg.enableWinbindd; 210 message = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled"; 211 } 212 ]; 213 # Always provide a smb.conf to shut up programs like smbclient and smbspool. 214 environment.etc = singleton 215 { source = 216 if cfg.enable then configFile 217 else pkgs.writeText "smb-dummy.conf" "# Samba is disabled."; 218 target = "samba/smb.conf"; 219 }; 220 } 221 222 (mkIf cfg.enable { 223 224 system.nssModules = optional cfg.nsswins samba; 225 226 systemd = { 227 targets.samba = { 228 description = "Samba Server"; 229 requires = [ "samba-setup.service" ]; 230 after = [ "samba-setup.service" "network.target" ]; 231 wantedBy = [ "multi-user.target" ]; 232 }; 233 234 services = { 235 "samba-smbd" = daemonService "smbd" "-F"; 236 "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F"); 237 "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F"); 238 "samba-setup" = { 239 description = "Samba Setup Task"; 240 script = setupScript; 241 unitConfig.RequiresMountsFor = "/var/lib/samba"; 242 }; 243 }; 244 }; 245 246 security.pam.services.samba = {}; 247 248 }) 249 ]; 250 251}