/etc/proftpd/proftpd.conf
ServerName "IServ"
ServerType standalone
DeferWelcome off
MultilineRFC2228 on
DefaultServer on
#ListOptions "-l"
UseSendFile off
RequireValidShell off
# Symlink behaviour
# We need to disable ShowSymlinks because the Windows FTP client (which uses
# the old mod_ls LIST command) can't handle them; it just won't show any
# symlinked folders like the Groups symlinks.
# Unfortunately, "ShowSymlinks off" also breaks the modern mod_facts MLSD
# command (for example used by FileZilla) because of Bug#3859 in proftpd (fixed
# upstream and in Debian jessie): http://bugs.proftpd.org/show_bug.cgi?id=3859
# This bug causes proftpd always to report type=file even for symlinked
# directories, which now breaks FileZilla in turn. Disabling mod_facts
# unfortunately seems to be the only possible workaround for the time being.
#ShowSymlinks off
#FactsAdvertise off
#20220127: by fh show hidden files and break windows ftp client.
ListOptions "-la"
ShowSymlinks on
FactsAdvertise on
TimeoutNoTransfer 600
TimeoutStalled 600
TimeoutIdle 1200
DenyFilter \*.*/
UseIPv6 on
Port 21
# PassivePorts have to be kept in sync with our firewall
PassivePorts 63000 63099
MaxInstances 30
User proftpd
Group nogroup
Umask 002 002
TimesGMT off
AllowOverwrite on
AllowRetrieveRestart on
AllowStoreRestart on
LogFormat writelog "%a %l %u %t \"%r\" (%f) %s %b"
SystemLog /var/log/proftpd/proftpd.log
ExtendedLog /var/log/proftpd/write.log write writelog
TransferLog /var/log/proftpd/xferlog
# TLS
TLSEngine on
TLSRequired on
TLSOptions NoSessionReuseRequired
TLSLog /var/log/proftpd/tls.log
TLSRSACertificateFile /etc/ssl/certs/iserv.crt
TLSRSACertificateKeyFile /etc/ssl/private/iserv.key
TLSCertificateChainFile /etc/ssl/certs/iserv.chain
TLSCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
TLSProtocol TLSv1.2
QuotaEngine on
DelayEngine on
ControlsEngine on
ControlsMaxClients 2
ControlsLog /var/log/proftpd/controls.log
ControlsInterval 5
ControlsSocket /run/proftpd.sock
AdminControlsEngine on
ControlsACLs all allow user root
AdminControlsACLs all allow user root
DelayControlsACLs all allow user root
TLSControlsACLs all allow user root
SQLEngine log
SQLAuthenticate off
SQLBackend postgres
SQLConnectInfo iserv@/run/postgresql/ proftpd
# The list of commands is too long to list in entirety; commands include
# CWD, DELE, HELP, LIST, MKD, MODE, NLST, PASS, PASV, PORT
# and many more. For the complete list check the FTP RFCs
# session log
SQLLog PASS session_log_login
SQLNamedQuery session_log_login FREEFORM "INSERT INTO session_log (act, service, protocol, encrypted, server_addr, server_port, client_addr, pid) VALUES ('%u', 'proftpd', upper('%{protocol}'), '%{protocol}' = 'ftps', regexp_replace('%L', '^::ffff:', '')::INET, '%p', '%a', '%P')"
SQLLog EXIT session_log_logout
SQLNamedQuery session_log_logout FREEFORM "UPDATE session_log SET logout = now() WHERE id IN (SELECT id FROM session_log WHERE act = '%u' AND service = 'proftpd' AND protocol = upper('%{protocol}') AND server_addr = regexp_replace('%L', '^::ffff:', '')::INET AND server_port = '%p' AND client_addr = '%a' AND pid = '%P' AND logout IS NULL ORDER BY login DESC LIMIT 1)"
# Login Log
SQLLog PASS pass
SQLLog ERR_PASS E_pass
SQLLOG EXIT quit
## Log accountname
#SQLNamedQuery pass FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT '%u','%u','%a','FTP-Login erfolgreich'"
#SQLNamedQuery E_pass FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT '%U','%U','%a','FTP-Login fehlgeschlagen' WHERE EXISTS (SELECT * from users WHERE act='%U')"
# Have to use %U because otherwise only proftpd would be logged
#SQLNamedQuery quit FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT '%u','%u','%a','FTP-Logout' WHERE EXISTS (SELECT * from users WHERE act='%u')"
## Log firstname lastname
SQLNamedQuery pass FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT (SELECT firstname || ' ' || lastname from users where act='%u'), '%u', '%a', 'FTP-Login erfolgreich' WHERE EXISTS (SELECT * from users WHERE act='%u')"
SQLNamedQuery E_pass FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT (SELECT firstname || ' ' || lastname from users where act='%U'), '%U', '%a', 'FTP-Login fehlgeschlagen' WHERE EXISTS (SELECT * from users WHERE act='%U')"
# Have to use %U because otherwise only 'proftpd' would be logged as Username
SQLNamedQuery quit FREEFORM "INSERT INTO log (name, \"user\", ip, text) SELECT (SELECT firstname || ' ' || lastname from users where act='%u'), '%u', '%a', 'FTP-Logout' WHERE EXISTS (SELECT * from users WHERE act='%u')"
# File Log
#-- Possible Actions
#-- idesk webdav samba proftp file_log
#-- upload upload open|w STOR/STOU upload
#-- rename mv? rename RNFR/RNTO mv
#-- rm rm unlink DELE rm
#-- mkdir mkdir mkdir [X]MKD mkdir
#-- rm rmdir rmdir [X]RMD rmdir
#-- mv mv rename RNFR/RNTO mv
#-- - - - APPE append
SQLLog STOR,STOU upload
SQLLog RNFR RNFR
SQLLog RNTO RNTO
SQLLog ERR_RNTO DEL_RNFR
SQLLog DELE rm
SQLLog MKD,XMKD mkdir
SQLLog RMD,XRMD rmdir
SQLLog APPE append
SQLNamedQuery upload FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename, Filetype, Action, Service) SELECT '%a', users.uuid, '%f', 'file', 'upload', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery RNFR FREEFORM "INSERT INTO file_log_RNFR (IP, ActorAct, actor_uuid, Filename, Service) SELECT '%a', '%U', users.uuid, '%f', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery RNTO FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename_New, Action, Service) SELECT '%a', users.uuid, '%f', 'RNTO', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery rm FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename, Filetype, Action, Service) SELECT '%a', users.uuid, '%f', 'file', 'rm', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery mkdir FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename, Filetype, Action, Service) SELECT '%a', users.uuid, '%f', 'dir', 'mkdir', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery rmdir FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename, Filetype, Action, Service) SELECT '%a', users.uuid, '%f', 'dir', 'rmdir', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery append FREEFORM "INSERT INTO file_log (IP, actor_uuid, Filename, Filetype, Action, Service) SELECT '%a', users.uuid, '%f', 'file', 'append', 'ftp' FROM users WHERE users.act = '%U'"
SQLNamedQuery DEL_RNFR FREEFORM "DELETE FROM file_log_RNFR WHERE ctid IN ( SELECT ctid FROM file_log_RNFR WHERE IP='%a' AND ActorAct='%U' AND Service='ftp' ORDER BY date DESC LIMIT 1)"
<Directory />
AllowAll
</Directory>
<Directory /*>
DenyAll
</Directory>
<Directory /group>
AllowAll
</Directory>
<Directory /home>
AllowAll
</Directory>
<Directory /home/*>
DenyAll
</Directory>
<Directory ~>
AllowAll
</Directory>
<Directory /srv/shares>
AllowAll
</Directory>
<Directory /var/lib/iserv/vplan/files>
AllowAll
</Directory>
<Directory /var/lib/iserv/infodisplay/files>
AllowAll
</Directory>
<Directory /var/lib/iserv/infodisplay/pdf>
AllowAll
</Directory>
<Directory /var/lib/iserv/infodisplay/pictures>
AllowAll
</Directory>