π
onifast-ftp
Virtual FTP server daemon with sandboxed basePath fs, dual-mode authentication, and IP fail2ban blocks.
FTP Server
chroot Sandboxed
Virtual + PAM
onifast-ftp is a production-ready FTP server built on the standard github.com/fclairamb/ftpserverlib core engine with a secure, chroot-sandboxed filesystem wrapper for each user.
Core Strengths
chroot Sandboxing
Fenced via afero.NewBasePathFs, preventing users from traversing above their assigned directory root.
Dual Auth Scheme
Authenticate virtual panel accounts via SQLite, and Linux system users via direct PAM fallback.
IP Blocking
Active integration with Onifast fail2ban databases to terminate malicious requests instantly.
Port Allocation Reference
Ensure these port definitions match your server firewall configurations:
| Port |
Visibility |
Description |
| 2121 |
Public |
FTP control port (configurable via ftp_port in serverconfig.json) |
| 30000β30100 |
Public |
Passive mode data transfer ports (required for active file listings) |
UFW Setup Tip
Make sure UFW rules allow both port 2121 (control) and 30000β30100 (passive range). Without the passive range open, client listings in passive mode will hang indefinitely.
Sequential Login Evaluation
On every FTP login attempt, the server executes these authentication steps in sequence:
- IP Block Check β The connection is rejected immediately if the client IP is logged inside the active ecosystem block lists.
- Virtual User Lookup β Check the SQLite database's
FtpUsers bucket for the matching username. If found, validate the stored password.
- PAM Fallback β If no virtual user record is found, authenticate against real Linux system accounts using the system PAM core.
ποΈ Virtual Users
Managed directly by the panel's FTP module. Credentials are saved inside ftp_users.db. The root directory is configured on a per-user basis β ideal for isolated hosting accounts.
π§ System Users (PAM)
Leverages Linux PAM authentication. Home directory is auto-detected from /etc/passwd via Go's user.Lookup() framework. Root user automatically lands in /root.
Virtual Users Schema
| Field |
Description |
username |
FTP connection login name |
password |
FTP plaintext account password |
root_dir |
Chroot path β users cannot browse above this folder |
owner |
Control panel administrator who manages this account |
System Users (PAM) Fallback
If no virtual record matches, the server falls back to direct PAM. The home directory is read from the OS user registry:
// For root:
rootDir = "/root"
// For other users:
uinfo, _ := user.Lookup(username)
rootDir = uinfo.HomeDir // e.g. /home/myuser
Fail2ban IP Blocker
The FTP server integrates with the shared ipblocker.db database used across the entire Onifast ecosystem. On every connection check:
- Whitelist lookup first β Checks for exact IP and CIDR matches inside the
fail2ban_whitelist bucket.
- Blocklist lookup β Checks for exact IP and CIDR matches inside the
fail2ban_blocked bucket.
- Expiry Checks β Expired entries are automatically skipped, allowing temporary bans to resolve naturally.
Activity Logging
All FTP operations are reported directly to the panel's audit log via POST http://localhost:4050/api/internal/log/add:
| Event Type |
Logged Message Format |
| Login attempt |
Login attempt by '<user>' |
| Login success |
User '<user>' connected (Virtual/System) |
| Login failure |
Login FAILED for '<user>' |
| Directory listing |
User '<user>' listed directory: /path |
| File download |
User '<user>' downloading: /path/file.txt |
| File upload |
User '<user>' uploading: /path/file.txt |
| IP blocked |
Connection blocked by IP Blocker |
Telegram Notifications
Login events trigger instant Telegram alerts via the panelβs notifications engine:
- β
Login Success β reports username, client IP, timestamp, and context (Virtual/System).
- π« Login Failure β sends same parameters, marked as failure.
SQLite Schema
Virtual accounts are configured inside the central directory at /home/root/onifast/config/ftp_users.db:
CREATE TABLE domain (
bucket TEXT, -- 'FtpUsers'
domain TEXT, -- username (used as key)
value TEXT, -- JSON FtpUser object
PRIMARY KEY (bucket, domain)
);
JSON Payload Structure
The value column stores serialized FtpUser JSON payloads:
{
"username": "alice",
"password": "s3cr3t_password",
"root_dir": "/home/root/onifast/users/alice/public_html",
"owner": "root"
}
Config Properties
General properties inside serverconfig.json:
| Key Name |
Description |
Default Value |
ftp_port |
Control port the daemon binds to |
2121 |
timezone |
Timezone for log reporting |
System default |
Systemd Service Daemon
[Unit]
Description=Onifast FTP Server
After=network.target
[Service]
ExecStart=/home/root/go/cmd/onifast-ftp/onifast-ftp
Restart=always
[Install]
WantedBy=multi-user.target
Firewall Adjustments
Run these commands to verify that UFW allows traffic through the FTP control and passive ranges:
# Allow FTP control + passive ports
ufw allow 2121/tcp
ufw allow 30000:30100/tcp
# Reload firewall
ufw reload