Setting up a file transfer host
Had to setup a new file transfer host recently, with the following requirements:
- individual login accounts required (for customers, no anonymous access)
- support for (secure) downloads, ideally via a browser (no special software required)
- support for (secure) uploads, ideally via sftp (most of our customers are familiar with ftp)
Our target was RHEL/CentOS 7, but this should transfer to other linuxes pretty readily.
Here's the schema we ended up settling on, which seems to give us a good mix of security and flexibility.
- use apache with HTTPS and PAM with local accounts, one per customer, and
nologin
shell accounts - users have their own groups (group=
$USER
), and also belong to thesftp
group - we use the
users
group for internal company accounts, but NOT for customers - customer data directories live in /data
- we use a 3-layer hierarchy for security:
/data/chroot_$USER/$USER
are created with anologin
shell - the
/data/chroot_$USER
directory must be owned byroot:$USER
, with permissions750
, and is used for an sftp chroot directory (not writeable by the user) - the next-level
/data/chroot_$USER/$USER
directory should be owned by$USER:users
, with permissions2770
(whereusers
is our internal company user group, so both the customer and our internal users can write here) - we also add an ACL to
/data/chroot_$USER
to allow the company-internalusers
group read/search access (but not write)
We just use openssh internal-sftp
to provide sftp access, with the following config:
Subsystem sftp internal-sftp Match Group sftp ChrootDirectory /data/chroot_%u X11Forwarding no AllowTcpForwarding no ForceCommand internal-sftp -d /%u
So we chroot sftp connections to /data/chroot_$USER
and then (via the ForceCommand
)
chdir to /data/chroot_$USER/$USER
, so they start off in the writeable part of their
tree. (If they bother to pwd
, they see that they're in /$USER
, and they can chdir
up a level, but there's nothing else there except their $USER
directory, and they
can't write to the chroot.)
Here's a slightly simplified version of the newuser
script we use:
die() { echo $* exit 1 } test -n "$1" || die "usage: $(basename $0) <username>" USERNAME=$1 # Create the user and home directories mkdir -p /data/chroot_$USERNAME/$USERNAME useradd --user-group -G sftp -d /data/chroot_$USERNAME/$USERNAME -s /sbin/nologin $USERNAME # Set home directory permissions chown root:$USERNAME /data/chroot_$USERNAME chmod 750 /data/chroot_$USERNAME setfacl -m group:users:rx /data/chroot_$USERNAME chown $USERNAME:users /data/chroot_$USERNAME/$USERNAME chmod 2770 /data/chroot_$USERNAME/$USERNAME # Set user password manually passwd $USERNAME
And we add an apache config file like the following to /etc/httpd/user.d
:
Alias /CUSTOMER /data/chroot_CUSTOMER/CUSTOMER <Directory /data/chroot_CUSTOMER/CUSTOMER> Options +Indexes Include "conf/auth.conf" Require user CUSTOMER </Directory>
(with CUSTOMER
changed to the local username), and where conf/auth.conf
has
the authentication configuration against our local PAM users and allows internal
company users access.
So far so good, but how do we restrict customers to their own /CUSTOMER
tree?
That's pretty easy too - we just disallow customers from accessing our apache document
root, and redirect them to a magic '/user' endpoint using an ErrorDocument 403
directive:
<Directory /var/www/html> Options +Indexes +FollowSymLinks Include "conf/auth.conf" # Any user not in auth.conf, redirect to /user ErrorDocument 403 "/user" </Directory>
with /user
defined as follows:
# Magic /user endpoint, redirecting to /$USERNAME <Location /user> Include "conf/auth.conf" Require valid-user RewriteEngine On RewriteCond %{LA-U:REMOTE_USER} ^[a-z].* RewriteRule ^\/(.*)$ /%{LA-U:REMOTE_USER}/ [R] </Location>
The combination of these two says that any valid user NOT in auth.conf should
be redirected to their own /CUSTOMER
endpoint, so each customer user lands
there, and can't get anywhere else.
Works well, no additional software is required over vanilla apache and openssh, and it still feels relatively simple, while meeting our security requirements.
blog comments powered by Disqus