Allow rails users to login via vsftpd
So you've installed Ruby on Rails with Postgres, and now you want to allow your users to login to your server via FTP.
First we need to install our FTP server. (In the config window, I left the defaults)
freebsd-vm# cd /usr/ports/ftp/vsftpd freebsd-vm# make install cd /usr/ports/ftp/vsftpd && make config; [...] ports included in the Ports Collection. Please type 'make deinstall' to deinstall the port if this is a concern. For more information, and contact details about the security status of this software, see the following webpage: http://vsftpd.beasts.org/ freebsd-vm#Edit the vsftpd conf file.
#vi /usr/local/etc/vsftpd.conf # Remove all lines and replace with the following background=YES listen=YES anonymous_enable=NO local_enable=YES virtual_use_local_privs=YES write_enable=YES connect_from_port_20=YES secure_chroot_dir=/usr/local/share/vsftpd/empty pam_service_name=vsftpd guest_enable=YES user_sub_token=$USER local_root=/usr/local/www/apache22/data/$USER chroot_local_user=YES hide_ids=YES ftpd_banner=Welcome to FTP server file_open_mode=0770 local_umask=0000 anon_mkdir_write_enable=NO guest_username=vsftpd user_config_dir=/etc/vsftpd/usersCreate the vsftpd startup script.
# vi /usr/local/etc/rc.d/vsftpd #!/bin/sh # # $FreeBSD: ports/ftp/vsftpd/files/vsftpd.sh.in,v 1.7 2006/02/20 20:47:01 dougb Exp $ # # PROVIDE: vsftpd # REQUIRE: DAEMON # Add the following line to /etc/rc.conf to enable `vsftpd': # # vsftpd_enable="YES" # vsftpd_flags="/some/path/conf.file" # Not required # . "/etc/rc.subr" name="vsftpd" rcvar=`set_rcvar` load_rc_config "$name" : ${vsftpd_enable:="NO"} : ${vsftpd_flags:=""} command="/usr/local/libexec/$name" required_files="/usr/local/etc/$name.conf" start_precmd="vsftpd_check" vsftpd_check() { if grep -q "^ftp[ ]" /etc/inetd.conf ${required_files} then err 1 "ftp is already activated in /etc/inetd.conf" fi if ! egrep -q -i -E "^listen.*=.*YES$" ${required_files} then err 1 "vsftpd script need "listen=YES" on config file" fi if ! egrep -q -i -E "^background.*=.*YES$" ${required_files} then err 1 "vsftpd script need "background=YES" on config file" fi } run_rc_command "$1"We also need to install the Postgres PAM module.
freebsd-vm# cd /usr/ports/security/pam-pgsql/ freebsd-vm# make install => libpam-pgsql-0.6.3.tar.bz2 doesn't seem to exist in /usr/ports/distfiles/. => Attempting to fetch from http://nchc.dl.sourceforge.net/sourceforge/pam-pgsql/. [...] to use this module. Note, that unlike most other ports, this port installs a file into /usr/lib directly (/usr/lib/pam_pgsql.so), because PAM requires that. ===> Registering installation for pam-pgsql-0.6.3_1 freebsd-vm#We need to tell vsftpd to use pam-pgsql for authentication.
freebsd-vm# vi /etc/pam.d/vsftpd auth required pam_pgsql.so config_file=/etc/pam_pgsql_vsftpd.conf account required pam_pgsql.so config_file=/etc/pam_pgsql_vsftpd.conf freebsd-vm# vi /etc/pam_pgsql_vsftpd.conf debug pw_type = md5 connect = hostaddr=127.0.0.1 port=5432 dbname=sample_development \ user=sample_user password='password' connect_timeout=15 auth_query = select password from users where username = %u acct_query = select ftp_disabled as acc_expired, \ 0 as acc_new_pwreq, (password ISNULL or password = '') as user_password \ from users where username = %u freebsd-vm#(Long lines above with \ in them should be one long continuous line in your config file)
The configuration above assumes three things:
- Your rails database name is sample_development and can be connected with the username sample_user and the password password (change red items as needed)
- Your rails users are stored in the users table. The password field is password and the username field is username (change yellow items as needed)
- Your rails users are stored in the users table. The ftp_disabled field holds whether they should be allowed access (false) or denied (true). (Change white items as needed)
freebsd-vm# adduser Username: vsftpd Full name: FTP User Uid (Leave empty for default): Login group [vsftpd]: Login group is vsftpd. Invite vsftpd into other groups? []: Login class [default]: Shell (sh csh tcsh nologin) [sh]: nologin Home directory [/home/vsftpd]: Use password-based authentication? [yes]: no Lock out the account after creation? [no]: Username : vsftpd Password :Configure vsftpd to start on boot and manually start vsftpd once.Full Name : FTP User Uid : 1002 Class : Groups : vsftpd Home : /home/vsftpd Shell : /usr/sbin/nologin Locked : no OK? (yes/no): yes adduser: INFO: Successfully added (vsftpd) to the user database. Add another user? (yes/no): no Goodbye! freebsd-vm#
freebsd-vm# echo vsftpd_enable="YES" >> /etc/rc.conf freebsd-vm# chmod 755 /usr/local/etc/rc.d/vsftpd freebsd-vm# /usr/local/etc/rc.d/vsftpd start Starting vsftpd. freebsd-vm#Now we need to create a couple of users to test this with. Below we'll create a user bob that can login and a user bob2 that can not login.
freebsd-vm$ psql -U sample_rails_app sample_rails_app_development Welcome to psql 8.3.3, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quit sample_rails_app_development=> select * from users; id | username | password | homedir | ftp_disabled | created_at | updated_at ----+----------+----------+---------+--------------+------------+------------ (0 rows) sample_rails_app_development=> insert into users (username,password,homedir,ftp_disabled) values ('bob',md5('bob'),'/home/bob',false); INSERT 0 1 sample_rails_app_development=> insert into users (username,password,homedir,ftp_disabled) values ('bob2',md5('bob2'),'/home/bob2',true); INSERT 0 1 sample_rails_app_development=> select * from users; id | username | password | homedir | ftp_disabled | created_at | updated_at ----+----------+----------------------------------+------------+--------------+------------+------------ 2 | bob | 9f9d51bc70ef21ca5c14f307980a29d8 | /home/bob | f | | 3 | bob2 | 436187b1cb437f7ddb11952542e351a4 | /home/bob2 | t | | (2 rows) sample_rails_app_development=> \q freebsd-vm$Next, we need to create bob's virtual home directory (as root).
freebsd-vm# mkdir -p /usr/local/www/apache22/data/bob freebsd-vm# chown vsftpd:vsftpd /usr/local/www/apache22/data/bobNow we can login as bob (and not bob2).
freebsd-vm$ ftp localhost Trying 127.0.0.1... Connected to localhost. 220 Welcome to FTP server Name (localhost:jasonn): bob 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> pwd Remote directory: / ftp> quit 221 Goodbye. freebsd-vm$ ftp localhost Trying 127.0.0.1... Connected to localhost. 220 Welcome to FTP server Name (localhost:jasonn): bob2 331 Please specify the password. Password: 530 Login incorrect. ftp: Login failed. ftp> quit 221 Goodbye. freebsd-vm$Now we need a script that will setup the FTP user's home directory. (Run this in your Rails project's home directory)
freebsd-vm# echo "User.write_ftp_configs" >> write_ftp_configs freebsd-vm#Change the User model in your Rails application
freebsd-vm$ vi app/models/user.rb class User < ActiveRecord::Base require 'ftools' def self.write_ftp_configs User.find(:all, :conditions => ["ftp_disabled is false"]).each do |user| unless File.directory?(user.homedir) # If their home directory doesn't exist, create it # and chown the directory to vsftpd (1002) File.makedirs user.homedir File.chown(1002, 1002, user.homedir) end if File.directory?("/etc/vsftpd/users") unless Dir.entries("/etc/vsftpd/users").include?(user.homedir) File.open("/etc/vsftpd/users/#{user.username}", 'w') {|f| f.write("local_root=#{user.homedir}\n"); } end end end end endNow you just need to setup a cronjob to run as root. This script should cd to your rails root directory and run "script/runner write_ftp_configs".
WARNING: There may be security implications with this script. If a user has their homedir set to something "sensitive" this script will create that directory for them. Make sure you trust anyone that can add users via your Rails application. You could fix this by adding a validation on the homedir field so that it's only a subdirectory of a given directory (i.e. $RAILS_ROOT/upload) or something.
Labels: FreeBSD, pam, postgres, ruby on rails, vsftpd