≡ Menu

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

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
Tweet itFacebook itGoogle+ itPDF itFound an error/typo on this page?

Comments on this entry are closed.

  • Chris July 6, 2009, 9:32 pm

    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

  • Krishantha July 10, 2009, 9:50 am

    it is working.. fine. but I have problem. How to delete user or how to edit user from PAM DB

  • nixCraft July 10, 2009, 1:22 pm

    @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.

  • Krishantha July 10, 2009, 1:41 pm

    Good.. and thanks… please send me the link to that article..

  • nixCraft July 10, 2009, 2:05 pm

    It will be added to this page automatically, bookmark this page.

  • Krishantha August 7, 2009, 10:51 am

    waititng for above inquary ;)

  • robin August 9, 2009, 8:17 am

    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"
  • Tim SUtton August 13, 2009, 1:22 pm

    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.

  • Nick September 24, 2009, 10:20 pm

    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.

    • jken September 28, 2012, 6:47 am

      I think you should turn off the iptables and then take a try again!

  • Nick September 24, 2009, 10:31 pm

    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?

  • Jojo November 12, 2009, 3:06 am

    is it possible to use multiple pam? i mean is it ok to use the virtualdb pam and the local users pam?

  • madapaka November 14, 2009, 3:52 am

    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.

  • Gunflak January 21, 2010, 7:00 am

    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

    • nixCraft January 21, 2010, 8:19 am

      Check your local or remote firewall settings.

    • Mathiau July 23, 2010, 9:44 pm

      GunFlak,
      \

      First, try to connect to the server with Passive mode turned OFF, if it works, then you need to set vsftpd to use a specific passive mode port range

      https://www.redhat.com/docs/manuals/enterprise/RHEL-3-Manual/ref-guide/s1-ftp-vsftpd-conf.html

      see the options if you search for Passive on the site starting at pasv_address

      then you need to open the same ports on your firewall to access the server externally.

      • Julio July 4, 2011, 11:47 pm

        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???

        • Julio August 22, 2011, 4:08 am

          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!!

  • Random February 27, 2010, 12:08 pm

    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.

  • vampir3x June 16, 2010, 8:59 am

    Hello, can you advice how to delete users and manage the db4 database?

    Thanks,

  • Mathiau July 23, 2010, 9:42 pm

    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.

  • leo_rockway October 15, 2010, 4:26 pm

    > 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?

    • leo_rockway October 15, 2010, 4:36 pm

      I found the information I was looking for in /var/log/audit/audit.log.

  • Ankit February 3, 2011, 7:18 am

    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

  • AndySouth March 20, 2011, 7:08 pm

    Very thanks!
    You saved me some time.

  • snap March 27, 2011, 1:31 pm

    There is a simple perl script for managing pam_userdb / virtual vsftpd accounts available at:

    http://dist.epipe.com/perl/pam_userdb_admin.pl

    I thought it might be helpful starting point for people who do not like db_load.

  • Ali Ismayilov April 27, 2011, 12:30 pm

    Thank you for great post.

    However, I have the problem when I install OS via pxe. Can I allow any virtual user to install OS via pxe?

    Thank you for the answer

  • Guy Armitage April 29, 2011, 8:48 am

    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

  • Ali Ismayilov April 29, 2011, 11:17 am

    I did it, myself :) Thank you again for the awesome post, it helps me, so much:)

    Sincerely,

    Ismayilov Ali

  • Eli May 4, 2011, 4:02 pm

    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

    • George May 12, 2012, 8:21 pm

      Thanks so much – this was the bit I was stuck on!

    • Eman July 9, 2014, 5:12 pm

      Thanks a lot dude !

  • anand August 23, 2011, 6:25 am

    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

  • Figo Kim November 4, 2011, 2:52 am

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

    how to enable local user along with the virtual user?

  • Sam Pizzuto December 6, 2011, 6:17 pm

    This did not work for me. Any ideas?

    C:\Documents and Settings\sam>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 Settings\sam>

    • Andrew Bowns August 1, 2012, 10:30 pm

      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

      • Sam Pizzuto August 5, 2012, 11:47 am

        At this time, no I have not. I just let it go.

        • Mike Knox March 17, 2013, 4:38 am

          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.

  • Shailendra Kumar January 24, 2012, 12:37 pm

    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

    • Shailendra Kumar January 25, 2012, 11:35 am

      Edit the vsftpd configuration file.

      virtual_use_local_privs=YES
      pam_service_name=vsftpd.virtual

  • Marius March 16, 2012, 3:45 pm

    very good job ! but… How can i edit PAM DB..add user, detele user, change user password ?

  • dilip March 19, 2012, 2:54 pm

    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

  • sudharsan April 20, 2012, 2:34 pm

    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

  • Tim June 14, 2012, 4:28 pm

    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!

  • raystrach July 24, 2012, 7:52 am

    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

  • hoangthao November 6, 2012, 3:35 pm

    I can’t login with user and pass in vusers.txt.
    Can you help me

  • SAtishCM December 18, 2012, 4:57 am

    Thanks for the document. It really helped me lot. Very nicely written.

  • Frowny Ball April 5, 2013, 11:48 pm

    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
  • Frowny Ball April 5, 2013, 11:51 pm

    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.

  • Steve C September 25, 2014, 9:54 pm

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