πŸ“‚ 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:

  1. IP Block Check β€” The connection is rejected immediately if the client IP is logged inside the active ecosystem block lists.
  2. Virtual User Lookup β€” Check the SQLite database's FtpUsers bucket for the matching username. If found, validate the stored password.
  3. 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:

go
// 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:

sql
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:

json
{
  "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

systemd
[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:

bash
# Allow FTP control + passive ports
ufw allow 2121/tcp
ufw allow 30000:30100/tcp

# Reload firewall
ufw reload
Copied snippet to clipboard!