Merge #361424: refactor lib.packagesFromDirectoryRecursive (v2)
[NixPkgs.git] / nixos / modules / services / networking / vsftpd.nix
blobe6ef14b2e3e405ff5e5f75447038c7b5d1259379
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 with lib;
10 let
12   /*
13     minimal secure setup:
15     enable = true;
16     forceLocalLoginsSSL = true;
17     forceLocalDataSSL = true;
18     userlistDeny = false;
19     localUsers = true;
20     userlist = ["non-root-user" "other-non-root-user"];
21     rsaCertFile = "/var/vsftpd/vsftpd.pem";
22   */
24   cfg = config.services.vsftpd;
26   inherit (pkgs) vsftpd;
28   yesNoOption = nixosName: vsftpdName: default: description: {
29     cfgText = "${vsftpdName}=${if getAttr nixosName cfg then "YES" else "NO"}";
31     nixosOption = {
32       type = types.bool;
33       name = nixosName;
34       value = mkOption {
35         description = description;
36         inherit default;
37         type = types.bool;
38       };
39     };
40   };
42   optionDescription = [
43     (yesNoOption "allowWriteableChroot" "allow_writeable_chroot" false ''
44       Allow the use of writeable root inside chroot().
45     '')
46     (yesNoOption "virtualUseLocalPrivs" "virtual_use_local_privs" false ''
47       If enabled, virtual users will use the same privileges as local
48       users. By default, virtual users will use the same privileges as
49       anonymous users, which tends to be more restrictive (especially
50       in terms of write access).
51     '')
52     (yesNoOption "anonymousUser" "anonymous_enable" false ''
53       Whether to enable the anonymous FTP user.
54     '')
55     (yesNoOption "anonymousUserNoPassword" "no_anon_password" false ''
56       Whether to disable the password for the anonymous FTP user.
57     '')
58     (yesNoOption "localUsers" "local_enable" false ''
59       Whether to enable FTP for local users.
60     '')
61     (yesNoOption "writeEnable" "write_enable" false ''
62       Whether any write activity is permitted to users.
63     '')
64     (yesNoOption "anonymousUploadEnable" "anon_upload_enable" false ''
65       Whether any uploads are permitted to anonymous users.
66     '')
67     (yesNoOption "anonymousMkdirEnable" "anon_mkdir_write_enable" false ''
68       Whether any uploads are permitted to anonymous users.
69     '')
70     (yesNoOption "chrootlocalUser" "chroot_local_user" false ''
71       Whether local users are confined to their home directory.
72     '')
73     (yesNoOption "userlistEnable" "userlist_enable" false ''
74       Whether users are included.
75     '')
76     (yesNoOption "userlistDeny" "userlist_deny" false ''
77       Specifies whether {option}`userlistFile` is a list of user
78       names to allow or deny access.
79       The default `false` means whitelist/allow.
80     '')
81     (yesNoOption "forceLocalLoginsSSL" "force_local_logins_ssl" false ''
82       Only applies if {option}`sslEnable` is true. Non anonymous (local) users
83       must use a secure SSL connection to send a password.
84     '')
85     (yesNoOption "forceLocalDataSSL" "force_local_data_ssl" false ''
86       Only applies if {option}`sslEnable` is true. Non anonymous (local) users
87       must use a secure SSL connection for sending/receiving data on data connection.
88     '')
89     (yesNoOption "portPromiscuous" "port_promiscuous" false ''
90       Set to YES if you want to disable the PORT security check that ensures that
91       outgoing data connections can only connect to the client. Only enable if you
92       know what you are doing!
93     '')
94     (yesNoOption "ssl_tlsv1" "ssl_tlsv1" true ''
95       Only applies if {option}`ssl_enable` is activated. If
96       enabled, this option will permit TLS v1 protocol connections.
97       TLS v1 connections are preferred.
98     '')
99     (yesNoOption "ssl_sslv2" "ssl_sslv2" false ''
100       Only applies if {option}`ssl_enable` is activated. If
101       enabled, this option will permit SSL v2 protocol connections.
102       TLS v1 connections are preferred.
103     '')
104     (yesNoOption "ssl_sslv3" "ssl_sslv3" false ''
105       Only applies if {option}`ssl_enable` is activated. If
106       enabled, this option will permit SSL v3 protocol connections.
107       TLS v1 connections are preferred.
108     '')
109   ];
111   configFile = pkgs.writeText "vsftpd.conf" ''
112     ${concatMapStrings (x: "${x.cfgText}\n") optionDescription}
113     ${optionalString (cfg.rsaCertFile != null) ''
114       ssl_enable=YES
115       rsa_cert_file=${cfg.rsaCertFile}
116     ''}
117     ${optionalString (cfg.rsaKeyFile != null) ''
118       rsa_private_key_file=${cfg.rsaKeyFile}
119     ''}
120     ${optionalString (cfg.userlistFile != null) ''
121       userlist_file=${cfg.userlistFile}
122     ''}
123     background=YES
124     listen=NO
125     listen_ipv6=YES
126     nopriv_user=vsftpd
127     secure_chroot_dir=/var/empty
128     ${optionalString (cfg.localRoot != null) ''
129       local_root=${cfg.localRoot}
130     ''}
131     syslog_enable=YES
132     ${optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") ''
133       seccomp_sandbox=NO
134     ''}
135     anon_umask=${cfg.anonymousUmask}
136     ${optionalString cfg.anonymousUser ''
137       anon_root=${cfg.anonymousUserHome}
138     ''}
139     ${optionalString cfg.enableVirtualUsers ''
140       guest_enable=YES
141       guest_username=vsftpd
142     ''}
143     pam_service_name=vsftpd
144     ${cfg.extraConfig}
145   '';
151   ###### interface
153   options = {
155     services.vsftpd = {
157       enable = mkEnableOption "vsftpd";
159       userlist = mkOption {
160         default = [ ];
161         type = types.listOf types.str;
162         description = "See {option}`userlistFile`.";
163       };
165       userlistFile = mkOption {
166         type = types.path;
167         default = pkgs.writeText "userlist" (concatMapStrings (x: "${x}\n") cfg.userlist);
168         defaultText = literalExpression ''pkgs.writeText "userlist" (concatMapStrings (x: "''${x}\n") cfg.userlist)'';
169         description = ''
170           Newline separated list of names to be allowed/denied if {option}`userlistEnable`
171           is `true`. Meaning see {option}`userlistDeny`.
173           The default is a file containing the users from {option}`userlist`.
175           If explicitly set to null userlist_file will not be set in vsftpd's config file.
176         '';
177       };
179       enableVirtualUsers = mkOption {
180         type = types.bool;
181         default = false;
182         description = ''
183           Whether to enable the `pam_userdb`-based
184           virtual user system
185         '';
186       };
188       userDbPath = mkOption {
189         type = types.nullOr types.str;
190         example = "/etc/vsftpd/userDb";
191         default = null;
192         description = ''
193           Only applies if {option}`enableVirtualUsers` is true.
194           Path pointing to the `pam_userdb` user
195           database used by vsftpd to authenticate the virtual users.
197           This user list should be stored in the Berkeley DB database
198           format.
200           To generate a new user database, create a text file, add
201           your users using the following format:
202           ```
203           user1
204           password1
205           user2
206           password2
207           ```
209           You can then install `pkgs.db` to generate
210           the Berkeley DB using
211           ```
212           db_load -T -t hash -f logins.txt userDb.db
213           ```
215           Caution: `pam_userdb` will automatically
216           append a `.db` suffix to the filename you
217           provide though this option. This option shouldn't include
218           this filetype suffix.
219         '';
220       };
222       localRoot = mkOption {
223         type = types.nullOr types.str;
224         default = null;
225         example = "/var/www/$USER";
226         description = ''
227           This option represents a directory which vsftpd will try to
228           change into after a local (i.e. non- anonymous) login.
230           Failure is silently ignored.
231         '';
232       };
234       anonymousUserHome = mkOption {
235         type = types.path;
236         default = "/home/ftp/";
237         description = ''
238           Directory to consider the HOME of the anonymous user.
239         '';
240       };
242       rsaCertFile = mkOption {
243         type = types.nullOr types.path;
244         default = null;
245         description = "RSA certificate file.";
246       };
248       rsaKeyFile = mkOption {
249         type = types.nullOr types.path;
250         default = null;
251         description = "RSA private key file.";
252       };
254       anonymousUmask = mkOption {
255         type = types.str;
256         default = "077";
257         example = "002";
258         description = "Anonymous write umask.";
259       };
261       extraConfig = mkOption {
262         type = types.lines;
263         default = "";
264         example = "ftpd_banner=Hello";
265         description = "Extra configuration to add at the bottom of the generated configuration file.";
266       };
268     } // (listToAttrs (catAttrs "nixosOption" optionDescription));
270   };
272   ###### implementation
274   config = mkIf cfg.enable {
276     assertions = [
277       {
278         assertion =
279           (cfg.forceLocalLoginsSSL -> cfg.rsaCertFile != null)
280           && (cfg.forceLocalDataSSL -> cfg.rsaCertFile != null);
281         message = "vsftpd: If forceLocalLoginsSSL or forceLocalDataSSL is true then a rsaCertFile must be provided!";
282       }
283       {
284         assertion =
285           (cfg.enableVirtualUsers -> cfg.userDbPath != null) && (cfg.enableVirtualUsers -> cfg.localUsers);
286         message = "vsftpd: If enableVirtualUsers is true, you need to setup both the userDbPath and localUsers options.";
287       }
288     ];
290     users.users =
291       {
292         "vsftpd" = {
293           group = "vsftpd";
294           isSystemUser = true;
295           description = "VSFTPD user";
296           home =
297             if cfg.localRoot != null then
298               cfg.localRoot # <= Necessary for virtual users.
299             else
300               "/homeless-shelter";
301         };
302       }
303       // optionalAttrs cfg.anonymousUser {
304         "ftp" = {
305           name = "ftp";
306           uid = config.ids.uids.ftp;
307           group = "ftp";
308           description = "Anonymous FTP user";
309           home = cfg.anonymousUserHome;
310         };
311       };
313     users.groups.vsftpd = { };
314     users.groups.ftp.gid = config.ids.gids.ftp;
316     # If you really have to access root via FTP use mkOverride or userlistDeny
317     # = false and whitelist root
318     services.vsftpd.userlist = optional cfg.userlistDeny "root";
320     systemd = {
321       tmpfiles.rules =
322         optional cfg.anonymousUser
323           #Type Path                       Mode User   Gr    Age Arg
324           "d    '${builtins.toString cfg.anonymousUserHome}' 0555 'ftp'  'ftp' -   -";
325       services.vsftpd = {
326         description = "Vsftpd Server";
328         wantedBy = [ "multi-user.target" ];
330         serviceConfig.ExecStart = "@${vsftpd}/sbin/vsftpd vsftpd ${configFile}";
331         serviceConfig.Restart = "always";
332         serviceConfig.Type = "forking";
333       };
334     };
336     security.pam.services.vsftpd.text = mkIf (cfg.enableVirtualUsers && cfg.userDbPath != null) ''
337       auth required pam_userdb.so db=${cfg.userDbPath}
338       account required pam_userdb.so db=${cfg.userDbPath}
339     '';
340   };