Authenticating vsFTPd virtual users with pam_pwdfile.so


For years, the standard way to set up password authentication for vsFTPd FTP server was to use PAM with the pam_userdb.so module.  It looks great on paper, but if you have tried this, you know that generating a Berkeley DB password file is a PITA, debugging is blind and brutal, and password file generation does not play well with automated deployments.  On top of that, it turns out that pam_userdb.so is (apparently) being phased out of the PAM package.

I stumbled across the pam_pwdfile.so module and it worked for us without all the confusing dead-ends we got with userdb. This module seems to be supported long-term, and uses an htpasswd-like password file. Here’s how to set it up, in four steps:

Installing pam_pwdfile.so

We’re using Ubuntu 14.04 at the moment, and you must install this module as a package:

sudo apt-get install libpam-pwdfile

or in a chef recipe, simply:

package 'libpam-pwdfile'

Creating a PAM service

Create this file at /etc/pam.d/vsftpd

auth required pam_pwdfile.so debug pwdfile=/etc/vsftpd/pwdfile
account required pam_permit.so

This creates a “PAM service” named vsftpd.   The debug option dumps some extra info to /var/log/auth.log and is very helpful in getting things set up the first time.  The pwdfile= option denotes the filename of the user/pw database we’ll create next.

Configuring vsFTPd

To use this new service, just add the following option to /etc/vsftpd.conf.

pam_service_name=vsftpd

Creating the user/password file

This is the payoff.  There are a couple ways to generate the password file.  From the command line you can user the Apache htpasswd utility, and there seems to be a number of other tools to generate these files as well.

But we’re deploying with Chef and it would be great to be able to automate our deployment, and with this file format we can do it. The key here is to know you can create a properly-hashed password using openssl passwd -1 mypa$$w0rd . Here’s an example of how to create the whole pwdfile in a Chef recipe:

# we pull this from a data bag item:  
#   user_passwords = {'user1' =>'pw1', 'user2' => 'pw2'} # etc

pwdfile_lines = user_passwords.map do |user, pw|
  # note clunky backtick call to openssl... 
  # the Ruby openssl lib does not seem to implement the required hash
  hash = `openssl passwd -1 '#{pw}'`.chop
  "#{user}:#{hash}\n"
end
file '/etc/vsftpd/pwdfile' do
  content pwdfile_lines.join
  user 'root'
  group 'root'
  mode '0400'
  sensitive true
  action :create
end

That’s it.  VIrtual users should now be able to log in using passwords hashed in the passwd file.  (I’m assuming the rest of the vsFTPd configuration supports using virtual users.  This can be a can of worms to get it set up the way you want, but is beyond the scope of this post.)

Troubleshooting

First off, don’t forget to restart the vsftpd service after all the changes… and make sure it starts!  A common issue is that certain config error seem to send vsftpd into a restart loop and the system kills it.  So your start messages looks good but then it dies.

In my experience, the most likely problem here will be with the vsftpd setup and not the authentication.  To effectively “stub out” the authentication, temporarily replace the /etc/pam.d/vsftpd file with this:

auth required pam_permit.so
account required pam_permit.so

This allows any user/pw to log in.  If you cannot log in now, your problem is with vsFTPd, your firewall, etc.  (Don’t forget that this leaves the FTP server wide open!)

For PAM problems, the debug option in the pam service file is helpful, as is just watching the FTP connection/login conversation.

Good luck.  I hope this saves you some of the 8+ hours we spent screwing with promising “solutions” that did not work!

, , ,