at v206 6.3 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 /* minimal secure setup: 8 9 enable = true; 10 forceLocalLoginsSSL = true; 11 forceLocalDataSSL = true; 12 userlistDeny = false; 13 localUsers = true; 14 userlist = ["non-root-user" "other-non-root-user"]; 15 rsaCertFile = "/var/vsftpd/vsftpd.pem"; 16 17 */ 18 19 cfg = config.services.vsftpd; 20 21 inherit (pkgs) vsftpd; 22 23 yesNoOption = nixosName: vsftpdName: default: description: { 24 cfgText = "${vsftpdName}=${if getAttr nixosName cfg then "YES" else "NO"}"; 25 26 nixosOption = { 27 type = types.bool; 28 name = nixosName; 29 value = mkOption { 30 inherit description default; 31 type = types.bool; 32 }; 33 }; 34 }; 35 36 optionDescription = [ 37 (yesNoOption "anonymousUser" "anonymous_enable" false '' 38 Whether to enable the anonymous FTP user. 39 '') 40 (yesNoOption "localUsers" "local_enable" false '' 41 Whether to enable FTP for local users. 42 '') 43 (yesNoOption "writeEnable" "write_enable" false '' 44 Whether any write activity is permitted to users. 45 '') 46 (yesNoOption "anonymousUploadEnable" "anon_upload_enable" false '' 47 Whether any uploads are permitted to anonymous users. 48 '') 49 (yesNoOption "anonymousMkdirEnable" "anon_mkdir_write_enable" false '' 50 Whether any uploads are permitted to anonymous users. 51 '') 52 (yesNoOption "chrootlocalUser" "chroot_local_user" false '' 53 Whether local users are confined to their home directory. 54 '') 55 (yesNoOption "userlistEnable" "userlist_enable" false '' 56 Whether users are included. 57 '') 58 (yesNoOption "userlistDeny" "userlist_deny" false '' 59 Specifies whether <option>userlistFile</option> is a list of user 60 names to allow or deny access. 61 The default <literal>false</literal> means whitelist/allow. 62 '') 63 (yesNoOption "forceLocalLoginsSSL" "force_local_logins_ssl" false '' 64 Only applies if <option>sslEnable</option> is true. Non anonymous (local) users 65 must use a secure SSL connection to send a password. 66 '') 67 (yesNoOption "forceLocalDataSSL" "force_local_data_ssl" false '' 68 Only applies if <option>sslEnable</option> is true. Non anonymous (local) users 69 must use a secure SSL connection for sending/receiving data on data connection. 70 '') 71 (yesNoOption "portPromiscuous" "port_promiscuous" false '' 72 Set to YES if you want to disable the PORT security check that ensures that 73 outgoing data connections can only connect to the client. Only enable if you 74 know what you are doing! 75 '') 76 (yesNoOption "ssl_tlsv1" "ssl_tlsv1" true '' '') 77 (yesNoOption "ssl_sslv2" "ssl_sslv2" false '' '') 78 (yesNoOption "ssl_sslv3" "ssl_sslv3" false '' '') 79 ]; 80 81 configFile = pkgs.writeText "vsftpd.conf" 82 '' 83 ${concatMapStrings (x: "${x.cfgText}\n") optionDescription} 84 ${optionalString (cfg.rsaCertFile != null) '' 85 ssl_enable=YES 86 rsa_cert_file=${cfg.rsaCertFile} 87 ''} 88 ${optionalString (cfg.userlistFile != null) '' 89 userlist_file=${cfg.userlistFile} 90 ''} 91 background=YES 92 listen=YES 93 nopriv_user=vsftpd 94 secure_chroot_dir=/var/empty 95 syslog_enable=YES 96 ${optionalString (pkgs.stdenv.system == "x86_64-linux") '' 97 seccomp_sandbox=NO 98 ''} 99 anon_umask=${cfg.anonymousUmask} 100 ''; 101 102in 103 104{ 105 106 ###### interface 107 108 options = { 109 110 services.vsftpd = { 111 112 enable = mkOption { 113 default = false; 114 description = "Whether to enable the vsftpd FTP server."; 115 }; 116 117 userlist = mkOption { 118 default = []; 119 description = "See <option>userlistFile</option>."; 120 }; 121 122 userlistFile = mkOption { 123 default = pkgs.writeText "userlist" (concatMapStrings (x: "${x}\n") cfg.userlist); 124 description = '' 125 Newline separated list of names to be allowed/denied if <option>userlistEnable</option> 126 is <literal>true</literal>. Meaning see <option>userlistDeny</option>. 127 128 The default is a file containing the users from <option>userlist</option>. 129 130 If explicitely set to null userlist_file will not be set in vsftpd's config file. 131 ''; 132 }; 133 134 anonymousUserHome = mkOption { 135 type = types.path; 136 default = "/home/ftp/"; 137 description = '' 138 Directory to consider the HOME of the anonymous user. 139 ''; 140 }; 141 142 rsaCertFile = mkOption { 143 type = types.nullOr types.path; 144 default = null; 145 description = "RSA certificate file."; 146 }; 147 148 anonymousUmask = mkOption { 149 type = types.string; 150 default = "077"; 151 example = "002"; 152 description = "Anonymous write umask."; 153 }; 154 155 } // (listToAttrs (catAttrs "nixosOption" optionDescription)); 156 157 }; 158 159 160 ###### implementation 161 162 config = mkIf cfg.enable { 163 164 assertions = singleton 165 { assertion = 166 (cfg.forceLocalLoginsSSL -> cfg.rsaCertFile != null) 167 && (cfg.forceLocalDataSSL -> cfg.rsaCertFile != null); 168 message = "vsftpd: If forceLocalLoginsSSL or forceLocalDataSSL is true then a rsaCertFile must be provided!"; 169 }; 170 171 users.extraUsers = 172 [ { name = "vsftpd"; 173 uid = config.ids.uids.vsftpd; 174 description = "VSFTPD user"; 175 home = "/homeless-shelter"; 176 } 177 ] ++ optional cfg.anonymousUser 178 { name = "ftp"; 179 uid = config.ids.uids.ftp; 180 group = "ftp"; 181 description = "Anonymous FTP user"; 182 home = cfg.anonymousUserHome; 183 }; 184 185 users.extraGroups.ftp.gid = config.ids.gids.ftp; 186 187 # If you really have to access root via FTP use mkOverride or userlistDeny 188 # = false and whitelist root 189 services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else []; 190 191 systemd.services.vsftpd = 192 { description = "Vsftpd Server"; 193 194 wantedBy = [ "multi-user.target" ]; 195 196 preStart = 197 optionalString cfg.anonymousUser 198 '' 199 mkdir -p -m 555 ${cfg.anonymousUserHome} 200 chown -R ftp:ftp ${cfg.anonymousUserHome} 201 ''; 202 203 serviceConfig.ExecStart = "@${vsftpd}/sbin/vsftpd vsftpd ${configFile}"; 204 serviceConfig.Restart = "always"; 205 serviceConfig.Type = "forking"; 206 }; 207 208 }; 209 210}