Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrating from ProFTPD

Moving from a ProFTPD with virtual users (AuthUserFile, ftpd.passwd) to CraftFileGate.

The model

ProFTPD puts identity, home and rights in one ftpd.passwd entry and in <Directory> blocks. CraftFileGate separates them: users.toml says who you are (username, password_hash, authorized_keys, authorities), roles.toml what you see ([[backends]], [[roles]] and their mounts). The home belongs to a role’s mount, not to the user: Users, roles and mounts.

Translating a ftpd.passwd entry

alice:$6$rounds=5000$xyz...:1001:1001:Alice:/srv/ftp/alice:/bin/false
# users.toml
[[users]]
username = "alice"
password_hash = "$6$rounds=5000$xyz..."     # kept as is, see below
authorized_keys = ["ssh-ed25519 AAAA... alice@poste"]
authorities = ["clients"]
# roles.toml: one role for all clients
[[backends]]
name = "ftp"
type = "local"
root = "/srv/ftp"

[[roles]]
name = "clients"

[[roles.mounts]]
backend = "ftp"
home_dir = "/{username}"     # DefaultRoot ~: alice sees /srv/ftp/alice as /
create_home = true           # CreateHome on
acl = [{ path = "/", rights = ["read", "write", "list", "delete", "rename"], recursive = true }]
ProFTPD fieldBecomes
passwordpassword_hash
uid, gidnothing: everything is written by the service account, without setuid
homethe mount’s home_dir, {username} for %u
shell, gecosnothing: only the sftp subsystem is served

{username} accepts only A-Z a-z 0-9 . _ -, 64 characters at most, with no leading dot; any other name cannot connect to that mount.

Passwords

[auth.methods]
local = { enabled = true, allow_sha512_crypt = true }   # or allow_bcrypt

$6$ and bcrypt are taken as is (Hashes); DES and MD5 ($1$) need a new password or a public key. Keeping the hashes is a migration step: sha512-crypt is not memory-hard. Regenerate them as argon2id at the first opportunity:

echo -n "mot-de-passe" | craft-file-gate hash-password --user alice   # a [[users]] block

An account that logs in only by key keeps a password_hash (the field is required): put the hash of a random secret in it, or turn off passwords for everyone (local.password = false). An identity provider can also replace users.toml: see Authentication.

Directive mapping

ProFTPDCraftFileGate
AuthUserFileauth.methods.local.users_file
AuthGroupFileauthorities and [[roles]]
DefaultRoot ~home_dir = "/{username}"
CreateHome oncreate_home = true (local backend)
<Directory> and <Limit>[[roles.mounts.acl]], see below
MaxLoginAttempts[sftp.ban] max_failures: bans the address
MaxClientsPerUserserver.max_sessions_per_user
MaxClientsnothing; [sftp.rate_limit] bounds the connection rate
HiddenStores on[server.hidden_stores] enabled = true; disabled by default, as in ProFTPD
mod_quotatabthe mount’s max_file_mb: a cap per file, not a cumulative volume
TransferLog, ExtendedLogthe audit trail, see Audit
TLSEngine (FTPS), PassivePorts, MasqueradeAddressnothing: SFTP on a single TCP port
SFTPHostKeysftp.host_keys
SFTPAuthorizedUserKeysthe user’s authorized_keys
mod_sql, mod_ldap[auth.jwt] or auth.authz_base_url
Umaskthe umask inherited at launch, for client files (Security)
<Anonymous>a dedicated account, with a read-only role

With hidden_stores, the in-progress file is named as in ProFTPD, with a token: .in.rapport.csv.<token>. (Atomic writes).

Translating <Limit> blocks

FTP commandsRight
RETR, READread
STOR, APPE, MKD, WRITEwrite
LIST, NLST, CWD, DIRSlist
DELE, RMDdelete
RNFR, RNTOrename

There is no inheritance: the most specific entry decides alone, and a read-only subtree (<Limit WRITE> DenyAll) restates everything still allowed there, { path = "/archive", rights = ["read", "list"], recursive = true }. A path that no entry covers is refused. See ACL.

The cutover

TopicCraftFileGate
upload resume (AllowStoreRestart)served, except with hidden_stores and on S3
file ownershipthe service account; any other ownership goes through the directory’s permissions (setgid, POSIX ACL)
FTP and FTPSnot served: each client moves to SFTP or to the REST API
  1. Inventory the clients; extract names, hashes and homes from ftpd.passwd.
  2. Write roles.toml and users.toml; launch on another port.
  3. Validate with a test account (each right, one refusal), distribute the secrets.
  4. Shut down ProFTPD, switch the port, follow the audit trail.