Instalar servidor FTP con ProFTPD

  servidor-ftp

Probado en Debian 9.6.

Permite multi-usuario, con cuentas virtuales, definidas en una DB, acceso encriptado con TLS, control de quota, con seguridad a nivel de firewall para evitar ataques de fuerza bruta.

Comenzamos por instalar el servidor y módulo.

$ apt -y install proftpd proftpd-mod-mysql
$ id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ mkdir /var/www/ftp
$ chown -R www-data:www-data /var/www/ftp
$ chmod -R 0755 /var/www/ftp

Crear la db, usuario y tablas.

CREATE USER 'proftpd'@'localhost' IDENTIFIED VIA mysql_native_password USING '<password>';

GRANT USAGE ON *.* TO 'proftpd'@'localhost';

CREATE DATABASE IF NOT EXISTS `proftpd`;

GRANT ALL PRIVILEGES ON `proftpd`.* TO 'proftpd'@'localhost';

ALTER DATABASE `proftpd` DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci
FLUSH PRIVILEGES;

USE `proftpd`;

CREATE TABLE ftpgroup(
  groupname varchar(45) NOT NULL default '',
  gid smallint(6) NOT NULL default '33',
  members varchar(45) NOT NULL default '',
  KEY groupname (groupname)
);

CREATE TABLE ftpquotalimits(
  name varchar(45) default NULL, 
  quota_type enum('user','group','class','all') NOT NULL default 'user',
  per_session enum('false','true') NOT NULL default 'false',
  limit_type enum('soft','hard') NOT NULL default 'soft',
  bytes_in_avail bigint(20) unsigned NOT NULL default '0',
  bytes_out_avail bigint(20) unsigned NOT NULL default '0',
  bytes_xfer_avail bigint(20) unsigned NOT NULL default '0',
  files_in_avail int(10) unsigned NOT NULL default '0',
  files_out_avail int(10) unsigned NOT NULL default '0',
  files_xfer_avail int(10) unsigned NOT NULL default '0'
);

CREATE TABLE ftpquotatallies(
  name varchar(45) NOT NULL default '',
  quota_type enum('user','group','class','all') NOT NULL default 'user',
  bytes_in_used bigint(20) unsigned NOT NULL default '0',
  bytes_out_used bigint(20) unsigned NOT NULL default '0',
  bytes_xfer_used bigint(20) unsigned NOT NULL default '0',
  files_in_used int(10) unsigned NOT NULL default '0',
  files_out_used int(10) unsigned NOT NULL default '0',
  files_xfer_used int(10) unsigned NOT NULL default '0'
);

CREATE TABLE ftpuser(
  id int(10) unsigned NOT NULL auto_increment,
  userid varchar(45) NOT NULL default '',
  passwd char(32) NOT NULL default '',
  uid smallint(6) NOT NULL default '33',
  gid smallint(6) NOT NULL default '33',
  homedir varchar(255) NOT NULL default '',
  shell varchar(32) NOT NULL default '/usr/sbin/nologin',
  count int(11) NOT NULL default '0',
  accessed datetime NOT NULL default '0000-00-00 00:00:00',
  modified datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY (id)
);

Editar archivo de módulos y des-comentar las 4 líneas indicadas.

$ nano /etc/proftpd/modules.conf
[...]
# Install proftpd-mod-mysql or proftpd-mod-pgsql to use this
LoadModule mod_sql.c
[...]
# Install proftpd-mod-mysql to use this
LoadModule mod_sql_mysql.c
[...]
# Install proftpd-mod-pgsql or proftpd-mod-mysql to use this
LoadModule mod_quotatab_sql.c
[...]
LoadModule mod_sql_passwd.c

Editar el archivo de configuración principal, y hacer los siguientes cambios

$ nano /etc/proftpd/proftpd.conf
[...]
#<IfModule mod_quotatab.c>
#QuotaEngine off
#</IfModule>
[...]
PassivePorts                  40100 40150
[...]
#
# Alternative authentication frameworks
#
#Include /etc/proftpd/ldap.conf
Include /etc/proftpd/sql.conf
[...]
Include /etc/proftpd/tls.conf

La cuestión con la configuración con TLS es tener a mano los certificados de Let’s Encrypt en algún host que resuelva al servidor FTP, y de esa forma reutilizarlos para el acceso. Esto se hace de la siguiente manera, editando el archivo con $ nano /etc/proftpd/tls.conf

TLSEngine                  on
TLSLog                     /var/log/proftpd/tls.log
TLSProtocol                SSLv23
[...]
TLSRSACertificateFile      /etc/letsencrypt/live/[your domain]/cert.pem
TLSRSACertificateKeyFile   /etc/letsencrypt/live/[your domain]/privkey.pem
TLSCACertificateFile       /etc/letsencrypt/live/[your domain]/chain.pem
[...]
TLSRequired                on

Ahora editamos el archivo de configuración del modulo para SQL.

$ nano /etc/proftpd/sql.conf
#
# Proftpd sample configuration for SQL-based authentication.
#
# (This is not to be used if you prefer a PAM-based SQL authentication)
#

<IfModule mod_sql_passwd.c>
 SQLPasswordEngine on
</IfModule>
<IfModule mod_sql.c>
#
# Choose a SQL backend among MySQL or PostgreSQL.
# Both modules are loaded in default configuration, so you have to specify the backend
# or comment out the unused module in /etc/proftpd/modules.conf.
# Use 'mysql' or 'postgres' as possible values.
#
#SQLBackend        mysql
#
#SQLEngine on
#SQLAuthenticate on
#
# Use both a crypted or plaintext password
#SQLAuthTypes Crypt Plaintext
#
# Use a backend-crypted or a crypted password
#SQLAuthTypes Backend Crypt
#
# Connection
#SQLConnectInfo proftpd@sql.example.com proftpd_user proftpd_password
#
# Describes both users/groups tables
#
#SQLUserInfo users userid passwd uid gid homedir shell
#SQLGroupInfo groups groupname gid members
#
DefaultRoot ~

SQLBackend              mysql
# The passwords in MySQL are encrypted using CRYPT
SQLAuthTypes            MD5
SQLAuthenticate         users groups


# used to connect to the database
# databasename@host database_user user_password
SQLConnectInfo  proftpd@localhost proftpd password


# Here we tell ProFTPd the names of the database columns in the "usertable"
# we want it to interact with. Match the names with those in the db
SQLUserInfo     ftpuser userid passwd uid gid homedir shell

# Here we tell ProFTPd the names of the database columns in the "grouptable"
# we want it to interact with. Again the names match with those in the db
SQLGroupInfo    ftpgroup groupname gid members

# set min UID and GID - otherwise these are 999 each
SQLMinID        10

# create a user's home directory on demand if it doesn't exist
CreateHome on

# Update count every time user logs in
SQLLog PASS updatecount
SQLNamedQuery updatecount UPDATE "count=count+1, accessed=now() WHERE userid='%u'" ftpuser

# Update modified everytime user uploads or deletes a file
SQLLog  STOR,DELE modified
SQLNamedQuery modified UPDATE "modified=now() WHERE userid='%u'" ftpuser

# User quotas
# ===========
QuotaEngine on
QuotaDirectoryTally on
QuotaDisplayUnits Mb
QuotaShowQuotas on

SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session, limit_type, bytes_in_avail, bytes_out_avail, bytes_xfer_avail, files_in_avail, files_out_avail, files_xfer_avail FROM ftpquotalimits WHERE name = '%{0}' AND quota_type = '%{1}'"

SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used, files_in_used, files_out_used, files_xfer_used FROM ftpquotatallies WHERE name = '%{0}' AND quota_type = '%{1}'"

SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name = '%{6}' AND quota_type = '%{7}'" ftpquotatallies

SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" ftpquotatallies

QuotaLimitTable sql:/get-quota-limit
QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

RootLogin off
RequireValidShell off

</IfModule>

Reiniciar servicio, abrir puertos con ufw, y configurar jaula para fail2ban.

$ service proftpd restart
$ nano /etc/ufw/applications.d/ufw-ftpserver
[FTPServer]
title=FTP Server
description=FTP Server
ports=20,21,40100:40150/tcp
$ ufw app list
[...]
FTPServer
[...]
$ ufw allow FTPServer
$ nano /etc/fail2ban/jail.d/ftp-server.local
[proftpd-with-ufw]
enabled = true
filter = proftpd
action = ufw[application="FTPServer", blocktype=reject]
logpath = %(proftpd_log)s
$service fail2ban force-reload

Primer registro y usuario, al que le daremos 10GB de quota ( 10GB = 10 * 1024 * 1024 * 1024 = 10737418240 bytes.)

USE `proftpd`;

INSERT INTO `ftpgroup` (`groupname`, `gid`, `members`) VALUES ('www-data', '33', 'www-data');


INSERT INTO `ftpquotalimits` (`name`, `quota_type`, `per_session`, `limit_type`, `bytes_in_avail`, `bytes_out_avail`, `bytes_xfer_avail`, `files_in_avail`, `files_out_avail`, `files_xfer_avail`) VALUES ('example_user', 'user', 'true', 'hard', '10737418240', '0', '0', '0', '0', '0');

INSERT INTO `ftpuser` (`id`, `userid`, `passwd`, `uid`, `gid`, `homedir`, `shell`, `count`, `accessed`, `modified`) VALUES ('1', 'usuario', MD5('clave'), '33', '33', '/var/www/ftp/usuario', '/usr/sbin/nologin', '0', '0000-00-00 00:00:00.000000', '0000-00-00 00:00:00.000000');

Probamos, y tendría que estar todo ok. Para realizar un backup es tan sencillo como hacer un dump de la DB. Además, teniendo acceso por phpmyadmin la administración se vuelve muy simple.

Esta instalación está basada en el artículo publicado en…
https://www.howtoforge.com/virtual-hosting-with-proftpd-and-mysql-debian-lenny-p2

 

 

 

Deja un comentario