Gumdrop provides a complete FTP server implementation following RFC 959 and its security extensions (RFC 4217 for FTP-TLS). Like all Gumdrop protocol implementations, the FTP server uses the event-driven, non-blocking architecture that enables high concurrency with minimal resource consumption.
The FTP server implements the File Transfer Protocol as defined in RFC 959, with modern extensions for security and IPv6.
All standard FTP commands are implemented:
YYYYMMDDhhmmss
UTC (RFC 3659 section 3)Both active and passive modes are supported:
The server implements RFC 4217 (Securing FTP with TLS):
Upgrades the control connection to TLS. After successful AUTH TLS, credentials and commands are encrypted:
C: AUTH TLS S: 234 AUTH TLS successful [TLS negotiation] C: USER alice ...
When configured with secure="true", the server expects TLS from
connection establishment. The listening port automatically defaults to 990
(per RFC 4217) unless an explicit port is set.
Use requireTLSForData="true" to mandate encrypted data transfers.
In passive mode, the server verifies that the IP address of each incoming data connection matches the control connection’s client IP. Connections from mismatched addresses are rejected to prevent data connection hijacking.
Authentication uses Gumdrop's centralised Realm interface. The
FTP server supports traditional USER/PASS authentication:
C: USER alice S: 331 User name okay, need password C: PASS secret123 S: 230 User logged in
A Realm represents a collection of authenticatable principals. The
built-in BasicRealm reads users from an XML file:
<realm> <group id="readers" name="ftp-read"/> <group id="writers" name="ftp-read ftp-write"/> <group id="admins" name="ftp-admin"/> <user name="alice" password="secret" groups="#writers"/> <user name="bob" password="password123" groups="#readers"/> <user name="admin" password="admin123" groups="#admins"/> </realm>
Custom realm implementations can integrate with LDAP, databases, or external identity providers.
File operations are delegated to an FTPConnectionHandler, which
provides access to the underlying storage:
The handler interface defines methods for all file operations:
getFileSystem() - returns the FTPFileSystem for the sessionisAuthorized(operation, path, metadata) - checks access rightshandleSiteCommand(command, metadata) - processes SITE commands
The RoleBasedFTPHandler enforces fine-grained access control
based on user roles defined in the realm.
| Commands | Required Role |
|---|---|
| RETR, LIST, NLST, STAT | ftp-read |
| STOR, STOU, APPE, MKD | ftp-write |
| DELE, RMD | ftp-delete |
| RNFR, RNTO | ftp-write (source) + ftp-delete (if replacing) |
| SITE commands | ftp-admin |
<realm id="ftpRealm" class="org.bluezoo.gumdrop.auth.BasicRealm">
<property name="href">ftp-users.xml</property>
</realm>
<service id="ftp" class="org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService">
<property name="realm" ref="#ftpRealm"/>
<property name="root-directory">/srv/ftp</property>
<listener class="org.bluezoo.gumdrop.ftp.FTPListener">
<property name="port">21</property>
<property name="keystore-file">keystore.p12</property>
<property name="keystore-pass">secret</property>
</listener>
</service>
When filesystem-enforcement is true on
RoleBasedFTPService, each handler's file system is wrapped in
RoleAwareFTPFileSystem. This enforces read/write/delete roles at
the filesystem operation level as a defence-in-depth layer, complementing the
command-level checks in RoleBasedFTPHandler.
org.bluezoo.gumdrop.ftp.FTPListener (transport configuration):
port - listening port (default: 21, or 990 for FTPS)secure - enable implicit TLS (FTPS)keystore-file - keystore for TLSkeystore-pass - keystore passwordrequire-tls-for-data - mandate TLS for data connections
org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService (service logic):
realm - reference to a Realm for authentication and RBACroot-directory - filesystem root for FTP accesswelcome-message - greeting banner textquota-manager - reference to a QuotaManagerfilesystem-enforcement - wrap filesystem in RoleAwareFTPFileSystem (default: false)passive-port-min - minimum port for passive data connectionspassive-port-max - maximum port for passive data connectionspassive-address - external IP for passive mode (for NAT)login-timeout - maximum time for authenticationidle-timeout - control connection idle timeoutdata-timeout - data transfer timeout
<!-- FTP with explicit TLS (port 21 + AUTH TLS) -->
<service id="ftp" class="org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService">
<property name="realm" ref="#ftpRealm"/>
<property name="root-directory">/srv/ftp</property>
<listener class="org.bluezoo.gumdrop.ftp.FTPListener">
<property name="port">21</property>
<property name="keystore-file">keystore.p12</property>
<property name="keystore-pass">secret</property>
<property name="passive-port-min">50000</property>
<property name="passive-port-max">50100</property>
<property name="require-tls-for-data">true</property>
</listener>
</service>
<!-- FTPS with implicit TLS (port defaults to 990 when secure) -->
<service id="ftps" class="org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService">
<property name="realm" ref="#ftpRealm"/>
<property name="root-directory">/srv/ftp</property>
<listener class="org.bluezoo.gumdrop.ftp.FTPListener">
<property name="secure">true</property>
<property name="keystore-file">keystore.p12</property>
<property name="keystore-pass">secret</property>
</listener>
</service>
The FTP server supports storage quotas to limit disk usage per user. Quotas are enforced on upload operations and can be queried via SITE commands.
Users can query their current quota status:
ftp> quote SITE QUOTA 211-Quota Status for user 'alice' 211-Source: Role-based quota (premium) 211-Storage: 2.5 GB used of 10 GB (25%) 211 End
Administrators with the ftp-admin role can check other users:
ftp> quote SITE QUOTA bob 211-Quota Status for user 'bob' 211-Source: System default quota 211-Storage: 45 MB used of 100 MB (45%) 211 End
Administrators can set per-user quotas:
ftp> quote SITE SETQUOTA alice 20GB 200 Quota set for user 'alice': 20 GB
Sizes accept standard suffixes: KB, MB, GB, TB. Use unlimited
for no limit.
When a user attempts to upload a file that would exceed their quota, the server returns a 552 error:
ftp> put largefile.zip 552 Quota exceeded: 98 MB used of 100 MB
Quotas are managed through a QuotaManager configured on the
handler factory:
<component id="ftpQuota"
class="org.bluezoo.gumdrop.quota.RoleBasedQuotaManager">
<property name="realm" ref="#ftpRealm"/>
<property name="storage-dir">/var/ftp/quota</property>
<property name="default-quota">100MB</property>
<property name="role-quota.premium">10GB</property>
<property name="role-quota.enterprise">unlimited</property>
</component>
<service id="ftp" class="org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService">
<property name="realm" ref="#ftpRealm"/>
<property name="root-directory">/srv/ftp</property>
<property name="quota-manager" ref="#ftpQuota"/>
<listener class="org.bluezoo.gumdrop.ftp.FTPListener" port="21"/>
</service>
The RoleBasedQuotaManager determines a user's quota by checking:
roleQuota.{rolename} propertiesdefaultQuota propertyQuota operations integrate with Gumdrop's telemetry:
QUOTA_CHECK - SITE QUOTA queriesQUOTA_SET - SITE SETQUOTA changesQUOTA_EXCEEDED - upload denied with limit details← Back to Main Page | HTTP Server & Client | SMTP Server & Client | IMAP Server | POP3 Server | Telemetry
Gumdrop FTP Server