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