diff --git a/nixos/.topmsg b/nixos/.topmsg new file mode 100644 index 000000000000..9632e5926312 --- /dev/null +++ b/nixos/.topmsg @@ -0,0 +1 @@ +improvements to vsftpd module diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix index 1b2432401def..265afb3618ca 100644 --- a/nixos/modules/services/networking/vsftpd.nix +++ b/nixos/modules/services/networking/vsftpd.nix @@ -4,12 +4,92 @@ with pkgs.lib; let + /* minimal secure setup: + + enable = true; + forceLocalLoginsSSL = true; + forceLocalDataSSL = true; + userlistDeny = false; + localUsers = true; + userlist = ["non-root-user" "other-non-root-user"]; + rsaCertFile = "/var/vsftpd/vsftpd.pem"; + + */ + cfg = config.services.vsftpd; inherit (pkgs) vsftpd; - yesNoOption = p : name : - "${name}=${if p then "YES" else "NO"}"; + yesNoOption = nixosName: vsftpdName: default: description: { + cfgText = "${vsftpdName}=${if getAttr nixosName cfg then "YES" else "NO"}"; + + nixosOption = { + name = nixosName; + value = mkOption { + inherit description default; + type = types.bool; + }; + }; + }; + + optionDescription = [ + + (yesNoOption "anonymousUser" "anonymous_enable" false '' + Whether to enable the anonymous FTP user. + '') + (yesNoOption "localUsers" "local_enable" false '' + Whether to enable FTP for local users. + '') + (yesNoOption "writeEnable" "write_enable" false '' + Whether any write activity is permitted to users. + '') + (yesNoOption "anonymousUploadEnable" "anon_upload_enable" false '' + Whether any uploads are permitted to anonymous users. + '') + (yesNoOption "anonymousMkdirEnable" "anon_mkdir_write_enable" false '' + Whether any uploads are permitted to anonymous users. + '') + (yesNoOption "chrootlocalUser" "chroot_local_user" false '' + Whether local users are confined to their home directory. + '') + (yesNoOption "userlistEnable" "userlist_enable" false '' + Whether users are included. + '') + (yesNoOption "userlistDeny" "userlist_deny" false '' + Specifies whether is a list of user + names to allow or deny access. + The default false means whitelist/allow. + '') + (yesNoOption "forceLocalLoginsSSL" "force_local_logins_ssl" true '' + Only applies if is true. Non anonymous (local) users + must use a secure SSL connection to send a password. + '') + (yesNoOption "forceLocalDataSSL" "force_local_data_ssl" true '' + Only applies if is true. Non anonymous (local) users + must use a secure SSL connection for sending/receiving data on data connection. + '') + (yesNoOption "ssl_tlsv1" "ssl_tlsv1" true '' '') + (yesNoOption "ssl_sslv2" "ssl_sslv2" false '' '') + (yesNoOption "ssl_sslv3" "ssl_sslv3" false '' '') + + { + cfgText = if cfg.rsaCertFile == null then "" + else '' + sslEnable=YES + rsa_cert_file=${cfg.rsaCertFile} + ''; + + nixosOption = { + name = "rsaCertFile"; + value = mkOption { + default = null; + description = '' + rsa certificate file. + ''; + }; + }; + } + ]; in @@ -26,52 +106,27 @@ in description = "Whether to enable the vsftpd FTP server."; }; - anonymousUser = mkOption { - default = false; - description = "Whether to enable the anonymous FTP user."; + userlist = mkOption { + default = []; + + description = '' + See . + ''; }; - anonymousUserHome = mkOption { - default = "/home/ftp"; - description = "Path to anonymous user data."; + userlistFile = mkOption { + default = pkgs.writeText "userlist" (concatMapStrings (x: "${x}\n") cfg.userlist); + description = '' + Newline separated list of names to be allowed/denied if + is true. Meaning see . + + The default is a file containing the users from . + + If explicitely set to null userlist_file will not be set in vsftpd's config file. + ''; }; - localUsers = mkOption { - default = false; - description = "Whether to enable FTP for local users."; - }; - - writeEnable = mkOption { - default = false; - description = "Whether any write activity is permitted to users."; - }; - - anonymousUploadEnable = mkOption { - default = false; - description = "Whether any uploads are permitted to anonymous users."; - }; - - anonymousMkdirEnable = mkOption { - default = false; - description = "Whether mkdir is permitted to anonymous users."; - }; - - chrootlocalUser = mkOption { - default = false; - description = "Whether local users are confined to their home directory."; - }; - - userlistEnable = mkOption { - default = false; - description = "Whether users are included."; - }; - - userlistDeny = mkOption { - default = false; - description = "Whether users are excluded."; - }; - - }; + } // (listToAttrs (catAttrs "nixosOption" optionDescription)) ; }; @@ -80,6 +135,15 @@ in config = mkIf cfg.enable { + assertions = [ + { + assertion = + (cfg.forceLocalLoginsSSL -> cfg.rsaCertFile != null) + && (cfg.forceLocalDataSSL -> cfg.rsaCertFile != null); + message = "vsftpd: If forceLocalLoginsSSL or forceLocalDataSSL is true then a rsaCertFile must be provided!"; + } + ]; + users.extraUsers = [ { name = "vsftpd"; uid = config.ids.uids.vsftpd; @@ -99,6 +163,21 @@ in gid = config.ids.gids.ftp; }; + # If you really have to access root via FTP use mkOverride or userlistDeny + # = false and whitelist root + services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else []; + + environment.etc."vsftpd.conf".text = + concatMapStrings (x: "${x.cfgText}\n") optionDescription + + '' + ${if cfg.userlistFile == null then "" + else "userlist_file=${cfg.userlistFile}"} + background=NO + listen=YES + nopriv_user=vsftpd + secure_chroot_dir=/var/empty + ''; + jobs.vsftpd = { description = "vsftpd server"; @@ -107,22 +186,6 @@ in preStart = '' - # !!! Why isn't this generated in the normal way? - cat > /etc/vsftpd.conf <