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