vsftpd
Contents
About
man 5 vsftpd.conf
man 8 vsftpd
man 5 ftpusers
- Default log-files
/var/log/vsftpd.log
/var/log/xferlog
- Hints
Guests are if enabled mapped to username ftp (Please see option guest_username)
lftp
Installation
Install vsftpd
1 apt install vstftd ca-certificates
Configure
Create users
Create an unpriviledged system user ftpsecure.
This user will have group nogroup, no home directory /nonexistent and no login-shell /usr/sbin/nologin.
Configure the firewall
Define a firewall window in your perimeter firewall (e.g. 50000-50999). And if you are running a local firewall ensure connections in this window are also accepted.
User access control
/etc/ftpusers
/etc/vsftpd.user_list
If userlist_deny=YES, /etc/vsftpd.user_list could be a link to /etc/ftpusers
1 ln -s /etc/ftpusers /etc/vsftpd.user_list
=== vsftpd banner ===
/etc/vsftpd.banner
vsftpd main configuration file
Quite a long file (toggle the display with the comments)
/etc/vsftpd.conf
Restart the vsftpd.service
1 systemctl restart vsftpd.service
Skeleton dir
It may be nice to prepare the skeleton directory /etc/skel to contain some writable directories like "ftp" or "www"
1 install -o root -g root -m 0750 -d /etc/skel/www
Test connection
Create two users on the FTP server with the same password
1 for UNAME in "valid" "invalid"; do
2 adduser "$UNAME"
3 ### DETERMINE USERHOME
4 UHOME="$(getent passwd "$UNAME" |cut -d: -f6)"
5 ### SET USERHOME READONLY
6 if [ "$UHOME" ]; then
7 chown root:"$UNAME" "$UHOME"
8 chmod 0750 "$UHOME"
9 fi
10 done
11
12 ls -lRa /home/*valid
13
14 ### ADD USER TO POSITIVE LIST
15 echo "valid" >> /etc/vsftpd.user_list
Remember to the test users afterwards
Create a test script locally ftp_testscript
Test the server automatically, again and again
And the actual connection result
Looks fine.
Troubleshooting
Fatal error: gnutls_record_recv: An unexpected TLS packet was received.
If you receive the following error on connection.
That error is a bit miss-leading, I guess. It probably denotes, that vsftpd has a problem with the chroot directory.
- either it's writable by the login user
- or it's not readable at all
You can allow add the undocumented option to /etc/vsftpd.conf
1 allow_writeable_chroot=YES
Writeable Chroot
- The dot-files of the user need to be protected.
- Otherwise an attacker may place code which is executed, when a user logs in the next time via shell.
Please do not run su or sudo with an interactive/login shell.
- There is an attack which "Roaring Beast" attack.
Exploits lazy loading of dynamic libraries in /etc or /lib
- Malicious libraries could have been placed there by an attacker.
- The attacker than issues an ftp command to the server, that triggers loading the libraries.
- The ftp-daemon is running under root privileges, than allows full privilege escalation.
426 Failure reading network stream.
If you receive the following error when uploading multiple files
1 ---- Connecting to static1.rockstable.it (178.63.149.235) port 21
2 <--- 220-###
3 <--- 220-# Rockstable VSFTPD
4 <--- 220-#
5 <--- 220-# If you are not authorized to utilize this FTP service,
6 <--- 220-# disconnect immediately. Any malicious usage will be persecuted.
7 <--- 220-###
8 <--- 220
9 ---> FEAT
10 <--- 211-Features:
11 <--- EPRT
12 <--- EPSV
13 <--- MDTM
14 <--- PASV
15 <--- PBSZ
16 <--- PROT
17 <--- REST STREAM
18 <--- SIZE
19 <--- TVFS
20 <--- 211 End
21 ---> AUTH TLS
22 <--- 234 Proceed with negotiation.
23 ---> USER username
24 Certificate depth: 2; subject: /C=US/O=Internet Security Research Group/CN=ISRG Root X1; issuer: /C=US/O=Internet Security Research Group/CN=ISRG Root X1
25 Certificate depth: 1; subject: /C=US/O=Let's Encrypt/CN=R10; issuer: /C=US/O=Internet Security Research Group/CN=ISRG Root X1
26 Certificate depth: 0; subject: /CN=static1.rockstable.it; issuer: /C=US/O=Let's Encrypt/CN=R10
27 Certificate verification: subjectAltName: ‘static1.rockstable.it’ matched
28 <--- 331 Please specify the password.
29 ---> PASS XXXX
30 <--- 230 Login successful.
31 ---> PBSZ 0
32 <--- 200 PBSZ set to 0.
33 ---> PROT P
34 <--- 200 PROT now Private.
35 ---- CWD path to be sent is `/html'
36 ---> CWD /html
37 <--- 250 Directory successfully changed.
38 ---> TYPE I
39 <--- 200 Switching to Binary mode.
40 ---> PASV
41 <--- 227 Entering Passive Mode (178,63,149,235,196,87).
42 ---- Connecting data socket to (178.63.149.235) port 50263
43 ---- Data connection established
44 ---> STOR file.name
45 <--- 150 Ok to send data.
46 Certificate verification: subjectAltName: ‘static1.rockstable.it’ matched
47 ---- Closing data socket
48 <--- 426 Failure reading network stream.
49 ---> QUIT
50 <--- 221 Goodbye.
51 ---- Closing control socket
52 copy: put rolled back to 0, seeking get accordingly
Your client probably does not well terminate the TLS connection. Unfortunately modern ciphers require this.
This was detected with lftp 4.9.2-2+b1. This issue is already known, but the new version has not hit the repositories, yet. Update your client to a more recent version.
https://github.com/lavv17/lftp/issues/753
You may try to set, but it won't help.
/etc/vsftpd.conf
1 ### FOR UPLOADS
2 #If enabled, SSL data uploads are required to terminate via SSL, not an EOF on the socket.
3 #This option is required to be sure
4 #that an attacker did not terminate an upload prematurely with a faked TCP FIN.
5 #Unfortunately, it is not enabled by default because so few clients get it right.
6 #(New in v2.0.7).
7 #Default: NO
8 strict_ssl_read_eof=NO
9
10 #If enabled, SSL data downloads are required to terminate via SSL, not an EOF on the socket.
11 #This is off by default as I was unable to find a single FTP client that does this.
12 #It is minor.
13 #All it affects is our ability to tell
14 #whether the client confirmed full receipt of the file.
15 #Even without this option, the client is able to check the integrity of the download.
16 #(New in v2.0.7).
17 #Default: NO
18 strict_ssl_write_shutdown=NO
Lftp in the distribution repos does not yet contain a fix. So the only workaround currently is to subsititute lftp, that has several drawbacks.
Options
- sitecopy
- wput
- ftp-ssl
- curl
- …
Here's a solution with curl
1 LFTP_PROTOCOL="ftp"
2 LFTP_HOST="remote-host.example.com"
3 LFTP_PATH_LOCAL="./public"
4 LFTP_PATH_REMOTE="./remote/path"
5 LFTP_USER="username"
6 LFTP_PASSWORD="__secure_long_password"
7 ### SIMPLE AND BAD SUBSTITUTION FOR LFTP
8 ### NO DRY-RUN, NO DELETION
9 find "${LFTP_PATH_LOCAL:=./public}" -type f \
10 | sed "s#${LFTP_PATH_LOCAL}/##" \
11 | xargs -P 2 -tI{} -- \
12 curl --ssl-reqd \
13 -u "$LFTP_USER:$LFTP_PASSWORD" \
14 --ftp-create-dirs \
15 --upload-file "${LFTP_PATH_LOCAL}/{}" \
16 "${LFTP_PROTOCOL}://${LFTP_HOST}${LFTP_PATH_REMOTE}/{}";