Vsftpd FTP Server With Virtual Users ( Berkeley DB + PAM )

Posted on in Categories CentOS, FTP Server, Howto, Linux, Networking, package management, RedHat/Fedora Linux, Security last updated January 21, 2009

Vsftpd supports virtual users with PAM (pluggable authentication modules). A virtual user is a user login which does not exist as a real login on the system in /etc/passwd and /etc/shadow file. Virtual users can therefore be more secure than real users, because a compromised account can only use the FTP server but cannot login to system to use other services such as ssh or smtp.

Required software

  • Berkeley DB (version 4) databases
  • pam_userdb.so

Install Berkeley DB And Utilities Under RHEL / CentOS

Type the following command:
# yum install db4-utils db4

Create The Virtual Users Database

To create a “db4” format file, first create a plain text files with the usernames and password on alternating lines. For e.g. create user called “vivek” with password called “vivekpass” and sayali with password “sayalipass”:
# cd /etc/vsftpd
# cat > vusers.txt

Sample output:

vivek
vivekpass
sayali
sayalipass

Next, create the actual database file like this:
# db_load -T -t hash -f vusers.txt vsftpd-virtual-user.db
# chmod 600 vsftpd-virtual-user.db
# rm vusers.txt

Configure VSFTPD for virtual user

Edit the vsftpd configuration file. Add or correct the following configuration options:

anonymous_enable=NO
local_enable=YES
# Virtual users will use the same privileges as local users.
# It will grant write access to virtual users. Virtual users will use the
# same privileges as anonymous users, which tends to be more restrictive
# (especially in terms of write access).
virtual_use_local_privs=YES
write_enable=YES
 
# Set the name of the PAM service vsftpd will use
# RHEL / centos user should use /etc/pam.d/vsftpd
pam_service_name=vsftpd.virtual
 
# Activates virtual users
guest_enable=YES
 
# Automatically generate a home directory for each virtual user, based on a template.
# For example, if the home directory of the real user specified via guest_username is
# /home/virtual/$USER, and user_sub_token is set to $USER, then when virtual user vivek
# logs in, he will end up (usually chroot()'ed) in the directory /home/virtual/vivek.
# This option also takes affect if local_root contains user_sub_token.
user_sub_token=$USER
 
# Usually this is mapped to Apache virtual hosting docroot, so that
# Users can upload files
local_root=/home/vftp/$USER
 
# Chroot user and lock down to their home dirs
chroot_local_user=YES
 
# Hide ids from user
hide_ids=YES

Save and close the file.

Create a PAM File Which Uses Your New Database

The following PAM is used to authenticate users using your new database. Create /etc/pam.d/vsftpd.virtual:
# cat > /etc/pam.d/vsftpd.virtual
Append the following:

#%PAM-1.0
auth       required     pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user
account    required     pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user
session    required     pam_loginuid.so

Create The Location Of The Files

You need to set up the location of the files / dirs for the virtual users. Type the following command:
# mkdir /home/vftp
# mkdir -p /home/vftp/{vivek,sayali}
# chown -R ftp:ftp /home/vftp

Restart The FTP Server

Type the following command:
# service vsftpd restart

Test Your Setup

Open another shell session and type:
$ ftp ftp.nixcraft.net
Sample output:

Connected to ftp.nixcraft.net.in.
Name (localhost:root): vivek
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> 

Sample log from /var/log/secure:
# tail -f /var/log/secure
Output

May 21 16:54:28 xentest vsftpd: pam_userdb(vsftpd.virtual:auth): user 'vivek' granted access

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin and a trainer for the Linux operating system/Unix shell scripting. He has worked with global clients and in various industries, including IT, education, defense and space research, and the nonprofit sector. Follow him on Twitter, Facebook, Google+.

50 comment

  1. Thanks for the helpful article. When running vsftpd on CentOS 5, one small correction I had to make on the configuration of vsftpd.virtual was to add crypt=hash to end of the auth and account lines.

    #%PAM-1.0
    auth required pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user crypt=hash
    account required pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user crypt=hash
    session required pam_loginuid.so

  2. @Krishantha,

    I will add another entry to this series to manage users after vsftpd is configured, such as deleting users, changing passwords and so on.

  3. hi Gents,

    does anybody know this error msg? Fatal error: gnutls_record_recv: A record packet with illegal version was received.

    @t61:~$ lftp localhost
    lftp localhost:~> user daniel
    Password:
    daniel@localhost:~> ls
    ls: Fatal error: gnutls_record_recv: A record packet with illegal version was received.
    lftp daniel@localhost:~> exit
    
    I'm using Debian, vsftpd 2.1.1
    @t61:~$ sudo vsftpd -v
    vsftpd: version 2.1.1
    @t61:~$ uname -a
    Linux t61 2.6.26-2-686 #1 SMP Sun Jun 21 04:57:38 UTC 2009 i686 GNU/Linux
    @t61:~$
    
    Here is the vsftpd log:
    Sun Aug  9 10:09:54 2009 [pid 20797] CONNECT: Client "127.0.0.1"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-                       +-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-                       |D|e|b|i|a|n| |G|N|U|/|L|i|n|u|x|"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-                       +-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-You are accessing my private Linux machine that is provided for authorized use only."
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220-"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "220 "
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP command: Client "127.0.0.1", "FEAT"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "211-Features:"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " AUTH SSL??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " AUTH TLS??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " EPRT??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " EPSV??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " MDTM??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " PASV??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " PBSZ??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " PROT??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " REST STREAM??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " SIZE??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " TVFS??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", " UTF8??"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "211 End"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP command: Client "127.0.0.1", "AUTH TLS"
    Sun Aug  9 10:09:54 2009 [pid 20797] FTP response: Client "127.0.0.1", "234 Proceed with negotiation."
    Sun Aug  9 10:09:55 2009 [pid 20797] FTP command: Client "127.0.0.1", "OPTS UTF8 ON"
    Sun Aug  9 10:09:55 2009 [pid 20797] FTP response: Client "127.0.0.1", "200 Always in UTF8 mode."
    Sun Aug  9 10:09:55 2009 [pid 20797] FTP command: Client "127.0.0.1", "USER daniel"
    Sun Aug  9 10:09:55 2009 [pid 20797] [daniel] FTP response: Client "127.0.0.1", "331 Please specify the password."
    Sun Aug  9 10:09:55 2009 [pid 20797] [daniel] FTP command: Client "127.0.0.1", "PASS "
    Sun Aug  9 10:09:55 2009 [pid 20796] [daniel] OK LOGIN: Client "127.0.0.1"
  4. Great article and thanks for putting it out there!

    One little addition that you may need is if you are running SELinux it may prevent the users from locating their home dir. To correct this you can turn SELinux off if you wish or tweak it with /usr/sbin/setsebool -P ftp_home_dir 1

    I’m really looking forward to the rest of the series especially the management of users and data.

    One question though – how could you set up multiple users to use the same home dir? for example say I’m working on a project (Project456) and I want one place for all the data but have to issue external parties with their own user accounts.

  5. Great article, however I’m having a very frustrating problem (been working on it all day) where I can login just fine, but then cannot list the directory, nor can I upload any files. I can’t for the life of me figure out whats wrong. I’ve done everything by the book and read several different articles on setting this up. Here is my vsftpd.conf (minus comments).


    local_enable=YES
    write_enable=YES
    local_umask=022
    dirmessage_enable=YES
    xferlog_enable=YES
    connect_from_port_20=YES
    xferlog_file=/var/log/xferlog
    xferlog_std_format=NO
    listen=YES
    pam_service_name=vsftpd
    userlist_enable=YES
    tcp_wrappers=YES
    log_ftp_protocol=YES
    anonymous_enable=NO
    local_enable=YES
    virtual_use_local_privs=YES
    write_enable=YES
    guest_enable=YES
    user_sub_token=$USER
    local_root=/home/clients/$USER
    chroot_local_user=YES
    hide_ids=YES

    Also, is there any way to get any useful debug output from vsftpd so I can try to trouble-shoot what is actually going wrong? The vsftpd.log doesn’t tell me anything that I don’t already get from the ftp-client end.

    Thanks for any help.

  6. To add to that.

    I think I am suddenly able to list the directory now… however I still cannot upload anything.

    Is there any special thing I need to do to allow users to upload?

  7. Hi Vivek,

    Thanks for the guide! I wish I have read this before during my previous deployment without the use of MySQL, anyways is it possible to add new users by re-creating the virtual users text file containing new users and passwords respectively and then run db_load all over again?

    TIA.

  8. Hi Vivek, awesome guide. Managed to go through all the way except that when I try and FTP in, it authenticates successfully but gets stuck at :

    Response: 227 Entering Passive Mode (IP address here)
    Command: LIST
    Error: Connection timed out
    Error: Failed to retrieve directory listing

      1. Im installing the vsftpd with PAM as you show in this excellent article. but im stucked with
        227 Entering Passive Mode (50,57,88,197,91,107)
        LIST

        Error: Cannot retrieve directory list.

        Im already

        /sbin/iptables -I RH-Firewall-1-INPUT 1 -p tcp –dport ftp -j ACCEPT
        /sbin/iptables -I RH-Firewall-1-INPUT 1 -p tcp –dport ftp-data -j ACCEPT
        /sbin/service iptables save
        /sbin/service iptables restart

        Howhever i think could be the firewall???

        1. After some working around this issue ive abled to loggin successfully with the following commands

          /sbin/modprobe ip_conntrack
          /sbin/modprobe ip_conntrack_ftp

          Was the firewall(iptables) that need to follow the ftp data!!

  9. Is it possible to enable UTF8 in the log file? Currently, the high characters are replaced as question marks though they appear in the file lists correctly.

  10. Great guide! I can not thank you enough for it!!

    I had done something wrong originally, but cleaning everything and starting over i have a working TLS / SSL based FTP server running under CentOS 5.5 with Virtual users.

    Question, is using:

    Explicit SSL with (Auth SSL)
    different then
    Explicit SSL with (Auth TSL)

    since the server is allowing either one to log in successfully, is this normal?

    Also, i was now curious as to how i can set permissions for specific people, this vsftpd is very new to me, as is most things CentOS over the last 3 weeks!

    i plan to have users who have access to the apache doc root for website modification, but i do want to limit some things like what they can and can not delete or access.

  11. > May 21 16:54:28 xentest vsftpd: pam_userdb(vsftpd.virtual:auth): user ‘vivek’ granted access

    I noticed that that line doesn’t list the IP of the user. I recently had someone trying to access one of my servers repeatedly and I’d like to know the IP address of the person that did that, is there any way?

  12. You also have to add

    guest_username=virtual

    in vsftpd.conf file otherwise you will get the error

    Starting vsftpd for vsftpd: 500 OOPS: unrecognised variable in config file: guest_enable

  13. Awesome post! It was all going so well until I came up to a world of pain, I’ve managed to get the system working with virtual users, but as soon as I start trying to write to the ftp server I get “553 Could not create file”.

    I’ve looked everywhere, and I know it has to do with permissions, but I can’t get my head around it without compromising the security of the server. Do you have any tips? Is that something you came accross?

    Cheers,

    Guy

  14. Hello, in case you are running ubuntu the path for pam db are different:

    auth required /lib/i386-linux-gnu/security/pam_userdb.so db=/etc/vsftpd_login
    account required /lib/i386-linux-gnu/security/pam_userdb.so db=/etc/vsftpd_login

    now it works! cheers

  15. hi ,me also met with the same error while uploading the file “553 Could not create file.” but thing s that when i change my se linux context selinux to “permissive mode ,” uploading s data is possible,since i can’t compromise on security of server, i changed selinux context to enfoceing mode. in that context i can’t upload the file… what might be the reason im using Redhat 6

  16. Thanks for the great document. Though, i have one question.

    how to enable local user along with the virtual user?

  17. This did not work for me. Any ideas?

    C:Documents and Settingssam>ftp 50.17.xxx.xxx
    Connected to 50.17.xxx.xxx.
    220 (vsFTPd 2.2.2)
    User (50.17.xxx.xxx:(none)): sam
    331 Please specify the password.
    Password:
    500 OOPS: cannot change directory:/home/vftp/samConnection closed by remote host.
    C:Documents and Settingssam>

    1. Were you ever able to get this figured out? I’m having the same issue.
      Command: USER john
      Response: 331 Please Specify the password.
      Command: PASS ************************
      Response: 500 OOPS: cannot change directory:/home/vftp/john
      Error: Critical Error
      Error: Could not connect to server

        1. Check your SELinux settings:
          getsebool -a | grep ftp

          If you see the following:
          ftp_home_dir –> off

          Use this to enable it:
          setsebool -P ftp_home_dir on

          An alternative option is to disable SELinux altogether, however I don’t recommend that for a public facing server.

  18. Jan 24 18:05:38 mann vsftpd[1804]: pam_unix(vsftpd:auth): check pass; user unknown
    Jan 24 18:05:38 mann vsftpd[1804]: pam_unix(vsftpd:auth): authentication failure; logname= uid=0 euid=0 tty=ftp ruser=anonymous rhost=192.168.0.198
    Jan 24 18:05:38 mann vsftpd[1804]: pam_succeed_if(vsftpd:auth): error retrieving information about user anonymous
    Jan 24 18:05:59 mann vsftpd[1806]: pam_unix(vsftpd:auth): check pass; user unknown
    Jan 24 18:05:59 mann vsftpd[1806]: pam_unix(vsftpd:auth): authentication failure; logname= uid=0 euid=0 tty=ftp ruser=vivek rhost=192.168.0.198
    Jan 24 18:05:59 mann vsftpd[1806]: pam_succeed_if(vsftpd:auth): error retrieving information about user vivek

  19. Dear All, Currently I have VSFTPD running with Virtual users and PAM bases authentication.

    Now, I want to convert the authentication method, from Virtual PAM based to LDAP bases authentication. I do have a LDAP server, and I access it using the url: ldap://ldap..com.

    Could any one can tell me how to convert this authentication.

    Dilip

  20. unable to create virtual users in cenot6 .. im new to this …when i give this command # cd /etc/vsftpd
    # cat > vusers.txt

    empty no users ….

    how to create virtual users

  21. With this type of configuration, has anyone tested or expect the ability to delete files from the FTP directory?

    The clients attempt RMD “file or folder name” and the server responds with “550 Permission Denied”.

    Any help would be appreciated!

  22. hey tim
    maybe a bit late but you need to set the permissions (chmod) for the files/folders so that the ftp user can work with them
    rs

  23. Not sure if anyone’s made better scritps for dealing with users.. I looked at the perl script someone pointed to but it didn’t write out in the correct format, or it didn’t work for me at least. Since I’m no good at perl, I made these…

    addftpuser.sh:

    #!/bin/bash
    
    if [ "$1" = "-?" ] || [ "$#" -ne 1 ]
    then
            echo "Usage: $0 "
            exit 1
    else
            if [ `grep -c $1 /etc/vsftpd/vsftpd-virtual-user.txt` -gt 0 ]
            then
                    echo "Error: User "$1" may already exist. Check the virtual user file."
                    exit 1
            else
                    echo -n "Enter password for $1: "
                    oldmodes=`stty -g`
                    stty -echo
                    read pass
                    stty $oldmodes
                    echo
                    echo $1 >> /etc/vsftpd/vsftpd-virtual-user.txt
                    echo $pass >> /etc/vsftpd/vsftpd-virtual-user.txt
                    rm /etc/vsftpd/vsftpd-virtual-user.db
                    db_load -T -t hash -f /etc/vsftpd/vsftpd-virtual-user.txt /etc/vsftpd/vsftpd-virtual-user.db
                    mkdir /home/ftp/$1
                    chown ftp.ftp /home/ftp/$1
                    echo "Done."
                    exit 0
            fi
    fi

    and

    delftpuser.sh:

    #!/bin/bash
    
    if [ "$1" = "-?" ] || [ "$#" -ne 1 ]
    then
            echo "Usage: $0 "
            exit 1
    else
            if [ `grep -c $1 /etc/vsftpd/vsftpd-virtual-user.txt` -ne 1 ]
            then
                    echo "Error: User "$1" might not exist. Check the virtual user file."
                    exit 1
            else
                    echo -n "Are you sure you want to remove user "$1" ? "
                    read reply
                    reply=$(tr '[:upper:]' '[:lower:]' <<< $reply)
                    if [ "$reply" = "y" ]
                    then
                            echo -n "Do you want to remove the ftp directory and all its contents? "
                            read deldir
                            deldir=$(tr '[:upper:]' '[:lower:]' <<< $deldir)
                            echo "Processing.."
                            pass=`grep -A1 $1 /etc/vsftpd/vsftpd-virtual-user.txt|tail -1`
                            sed -i '/'$1'/d' /etc/vsftpd/vsftpd-virtual-user.txt
                            sed -i '/'$pass'/d' /etc/vsftpd/vsftpd-virtual-user.txt
                            rm /etc/vsftpd/vsftpd-virtual-user.db
                            db_load -T -t hash -f /etc/vsftpd/vsftpd-virtual-user.txt /etc/vsftpd/vsftpd-virtual-user.db
                            echo "The user has been removed from the virtual user file."
                            if [ "$deldir" = "y" ] && [ -d /home/ftp/$1 ]
                            then
                                    rm -rf /home/ftp/$1
                                    echo "The user's ftp directory and all its contents have been deleted!"
                            else
                                    echo "The user's ftp directory was NOT deleted!"
                            fi
                            echo "Done."
                            exit 0
                    else
                            echo "Aborted."
                            exit 0
                    fi
            fi
    fi
  24. Oops. That should say “scripts”, and then in the chown line there shouldn’t be an http:// in there, but hopefully people will figure that out.

  25. Okay but about the right of the virtual USer …
    how to create or make a directory or a file by a virtual user …. ??????

Comments are closed.