Docker
ssh-keygen -t ed25519 -f ./host_ed25519 -N "" # the host key
sudo chown 65532 host_ed25519 # readable by the image's user
docker run -d --name craft-file-gate \
-v ./config.toml:/config/config.toml:ro \
-v ./host_ed25519:/config/host_ed25519:ro \
-v ./roles.toml:/config/roles.toml:ro -v ./users.toml:/config/users.toml:ro \
-v data:/data \
-p 2222:2222 -p 8080:8080 -p 8081:8081 \
craftogether/craft-file-gate:latest
config.toml follows the quick start
with what a container changes: listeners on 0.0.0.0, a key path
relative to the directory of config.toml, the console by account and password
(the doors are published), storage and accounts in their own files.
docker run --rm craftogether/craft-file-gate:latest config schema > config.schema.json
writes next to it the schema that #:schema names (Checking a configuration);
the image carries it at /usr/share/craft-file-gate/config.schema.json.
#:schema ./config.schema.json
[sftp]
listen = "0.0.0.0:2222"
host_keys = ["host_ed25519"] # /config/host_ed25519
[auth.methods]
local = { enabled = true } # the accounts in users.toml
jwt = { enabled = false } # no identity provider
[admin]
listen = "0.0.0.0:8080" # file API, file explorer
control_listen = "0.0.0.0:8081" # admin console, /admin, probes, /metrics
[[admin.roles]] # a [[users]] account with authorities = ["admins"]
name = "admins"
permissions = ["overview", "sessions", "kick", "bans", "unban", "config", "logs", "audit", "revoke", "grant"]
[admin.ban]
max_failures = 5
[api]
enabled = true
[api.ban]
max_failures = 5
[api.ui]
enabled = true
The first mount and its ACL: roles.toml ([[backends]], [[roles]]) and
users.toml ([[users]]), next to config.toml, are picked up without being declared.
#:schema ./config.schema.json
[[backends]] # roles.toml
name = "donnees"
type = "local"
root = "/data" # the data:/data volume
[[roles]]
name = "utilisateurs"
[[roles.mounts]]
backend = "donnees"
home_dir = "/{username}" # alice sees /data/alice as her root /
create_home = true
[[roles.mounts.acl]]
path = "/"
rights = ["read", "write", "list", "delete", "rename"]
recursive = true
#:schema ./config.schema.json
[[users]] # users.toml
username = "alice"
password_hash = "$argon2id$..." # output of hash-password (below)
authorities = ["utilisateurs", "admins"] # her files, and the admin console
The console: http://host:8081/, the file explorer: http://host:8080/files
(Console sessions, File explorer).
| Image | Contents | User | HEALTHCHECK |
|---|---|---|---|
:latest | static distroless, no shell | 65532 (nonroot) | yes |
:alpine | Alpine, with a shell for diagnostics | 65534 (nobody) | yes |
:scratch | the binary alone, no certificate store | none declared: set --user 65532 | no |
The image exposes 2222 (SFTP) and 8080; control_listen publishes a third one.
The HEALTHCHECK runs craft-file-gate healthcheck
on /config/config.toml. ENTRYPOINT is the binary alone, the command
--config /config/config.toml; the binary handles SIGTERM as PID 1.
- Mount read-only (
:ro) the files the server trusts: configuration, users, roles, host key, TLS pair (Security). - In the image,
/databelongs to its user, and a named volume inherits that; a host directory mounted in its place must belong to it:sudo chown 65532 data(65534for:alpine). :scratchhas no certificate roots: an outbound TLS connection (S3, JWKS, OTLP, Knox) needs a mounted bundle andSSL_CERT_FILE.- Subcommands pass through as is:
echo -n 'mot-de-passe' | docker run -i --rm craftogether/craft-file-gate hash-password. - Keep Docker’s stop timeout above the server’s:
docker stop -t 90. See Shutdown.
Docker Compose
services:
craft-file-gate:
image: craftogether/craft-file-gate:latest
restart: unless-stopped
stop_grace_period: 90s
ports:
- "2222:2222" # SFTP
- "8080:8080" # file API, file explorer
- "8081:8081" # admin console, /admin, probes, metrics
volumes:
- ./config.toml:/config/config.toml:ro
- ./roles.toml:/config/roles.toml:ro
- ./users.toml:/config/users.toml:ro
- ./host_ed25519:/config/host_ed25519:ro
- data:/data
volumes:
data:
The binary’s features
Each backend type and each door is a Cargo feature, all enabled
by default. The published binaries and images carry them all, plus k8s.
A binary built with fewer features refuses at startup the table
of a door or the type of a backend it does not carry, naming the feature.
| Feature | Backend or door |
|---|---|
backend-local | local; required (the server’s state, locks and bans live on the local disk) |
backend-sftp | sftp (proxy) |
backend-s3 | s3 |
backend-webhdfs | webhdfs |
door-sftp | the SFTP door, [sftp] |
door-rest | the REST file API and the file explorer, [api] |
k8s | bans and revocations shared through a ConfigMap, session key in a Secret; off by default in a custom build |
The console, the probes and /metrics have no feature: every binary
carries them.
Subcommands (hash-password, verify-password, healthcheck):
The configuration file.