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

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).

ImageContentsUserHEALTHCHECK
:lateststatic distroless, no shell65532 (nonroot)yes
:alpineAlpine, with a shell for diagnostics65534 (nobody)yes
:scratchthe binary alone, no certificate storenone declared: set --user 65532no

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, /data belongs to its user, and a named volume inherits that; a host directory mounted in its place must belong to it: sudo chown 65532 data (65534 for :alpine).
  • :scratch has no certificate roots: an outbound TLS connection (S3, JWKS, OTLP, Knox) needs a mounted bundle and SSL_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.

FeatureBackend or door
backend-locallocal; required (the server’s state, locks and bans live on the local disk)
backend-sftpsftp (proxy)
backend-s3s3
backend-webhdfswebhdfs
door-sftpthe SFTP door, [sftp]
door-restthe REST file API and the file explorer, [api]
k8sbans 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.