Setup
Traefik
If you’re new to Traefik and you haven’t read my Traefik 2.5 quick-start guide, please do so before proceeding.
We’ll create an SFTP entrypoint for port 2222/TCP
in the Static Configuration, and define the rest of SFTPGo’s entry in the Dynamic Configuration.
Static Configuration
Adding the entrypoint in, the relevant section would appear in traefik.toml
as:
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http]
[entryPoints.web.http.redirections]
[entryPoints.web.http.redirections.entryPoint]
to = "websecure"
scheme = "https"
permanent = true
[entryPoints.websecure]
address = ":443"
[entryPoints.sftp]
address = ":2222/tcp"
Dynamic Configuration
SFTPGo provides multiple services that span several ports:
- SFTP
- HTTP
- Telemetry
- WebDAV
We’re going to glue all of this together in Traefik, with two entrypoints:
- HTTPS: port
443/TCP
- SFTP: port
2222/TCP
Traefik will route directly to SFTP via TCP with the Proxy Protocol. Prioritized rulesets with path definitions will filter all other requests that Traefik will forward to the appropriate service. Attributes for the HTTPS requests to keep in mind:
- Web interface for administrators and clients.
/web/admin
— Restricted to bastion hosts (i.e., VPN servers).- /web/client — Accessible to the public.
- API for scripts to programmatically administer.
- /api — Restricted to bastion hosts and/or jump hosts (i.e., VPN servers and secure machines).
- Assets — Multimedia resources (e.g., images, JavaScript, and CSS).
- /static — Accessible to the public.
- Index
- / — By default, SFTPGo will redirect these requests to the web interface for clients, but I’ll demonstrate how to redirect these requests to the web interface for administrators.
- Telemetry — Restricted to jump hosts (i.e., secured servers for administrative duties, such as processing raw data with Grafana).
- /healthz — Health checks.
- /metrics — Prometheus metrics.
- /debug/pprof — Profiling with pprof.
- WebDAV — Lower priority pattern matching if none of the other rules match. An example is if Traefik answers example.com and you used WebDAV to download profile.png. The request with the GET method would be for
https://example.com/profile.png
and would show up to Traefik as/profile.png
. Since SFTPGo has such clearly defined URLs that we’ve defined in Traefik and have prioritized above this one, we can assume that anything else that doesn’t match rules for known paths should be a direct query from the WebDAV service.
The bastion host file will be located at /srv/traefik/traefik.d/core-bastions.toml
to allow unbridled access from three servers to HTTP and TCP. The contents will be as follows:
[http.middlewares]
[http.middlewares.bastion-hosts.ipWhiteList]
sourceRange = [
"1.1.1.1", # Server 1
"2.2.2.2", # Server 2
"3.3.3.3", # Server 3
]
[tcp.middlewares]
[tcp.middlewares.bastion-hosts.ipWhiteList]
sourceRange = [
"1.1.1.1", # Server 1
"2.2.2.2", # Server 2
"3.3.3.3", # Server 3
]
Now we only need a single HTTPS entrypoint instead of three. Thus, the contents of /srv/traefik/traefik.d/sftpgo.toml
are:
# Web Admin — Privileged user
[http.routers.Router-SFTPGo-Web-Admin]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/web/admin`)"
priority = 100
service = "SFTPGo-Web"
tls = true
middlewares = [
"bastion-hosts",
]
# API — Privileged user
[http.routers.Router-SFTPGo-API]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/api`)"
priority = 100
service = "SFTPGo-Web"
tls = true
middlewares = [
"bastion-hosts",
]
# Assets
[http.routers.Router-SFTPGo-Assets]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/static`)"
priority = 100
service = "SFTPGo-Web"
tls = true
# Web Client
[http.routers.Router-SFTPGo-Web-Client]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/web/client`)"
priority = 100
service = "SFTPGo-Web"
tls = true
# Index
[http.routers.Router-SFTPGo-Index]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && Method(`GET`) && Path(`/`)"
priority = 100
service = "SFTPGo-Web"
tls = true
middlewares = [
"SFTPGo-Redirect-Index",
]
# Telemetry : Health checks — https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md#telemetry-server
[http.routers.Router-SFTPGo-Telemetry-Health]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/healthz`)"
priority = 100
service = "SFTPGo-Telemetry"
tls = true
middlewares = [
"bastion-hosts",
]
# Telemetry : Prometheus metrics — https://github.com/drakkan/sftpgo/blob/main/docs/metrics.md
[http.routers.Router-SFTPGo-Telemetry-Metrics]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/metrics`)"
priority = 100
service = "SFTPGo-Telemetry"
tls = true
middlewares = [
"bastion-hosts",
]
# Telemetry : Profiling — https://github.com/drakkan/sftpgo/blob/main/docs/profiling.md
[http.routers.Router-SFTPGo-Telemetry-Profiling]
entrypoints = ["websecure"]
rule = "Host(`example.com`) && PathPrefix(`/debug/pprof`)"
priority = 100
service = "SFTPGo-Telemetry"
tls = true
middlewares = [
"bastion-hosts",
]
# WebDAV
[http.routers.Router-SFTPGo-WebDAV]
entrypoints = ["websecure"]
rule = "Host(`example.com`)"
priority = 90
service = "SFTPGo-WebDAV"
tls = true
# SFTP
[tcp.routers.SFTP]
entryPoints = ["sftp"]
rule = "HostSNI(`*`)" # Catch every request (only available rule for non-tls routers.)
service = "SFTPGo-SFTP"
# Middlewares
[http.middlewares]
# Index page
[http.middlewares.SFTPGo-Redirect-Index.redirectRegex]
regex = ".*"
replacement = "/web/admin"
# Services
# TCP
[tcp.services]
[tcp.services.SFTPGo-SFTP.loadBalancer]
# Enable Proxy Protocol
[tcp.services.SFTPGo-SFTP.loadBalancer.proxyProtocol]
version = 2
# TCP: SFTP
[[tcp.services.SFTPGo-SFTP.loadBalancer.servers]]
address = "sftpgo:1111"
# HTTP
[http.services]
# HTTP: Web
[http.services.SFTPGo-Web.loadBalancer]
[[http.services.SFTPGo-Web.loadBalancer.servers]]
url = "http://sftpgo:2222"
# HTTP: Telemetry
[http.services.SFTPGo-Telemetry.loadBalancer]
[[http.services.SFTPGo-Telemetry.loadBalancer.servers]]
url = "http://sftpgo:3333"
# HTTP: WebDAV
[http.services.SFTPGo-WebDAV.loadBalancer]
[[http.services.SFTPGo-WebDAV.loadBalancer.servers]]
url = "http://sftpgo:4444"
Docker Compose
I prefer to configure SFTPGo almost entirely with its environment variables, and have based several of the values below from the default sftpgo.json file found in the repository. In my prior Traefik article, I covered how I go about using Link-local address segments to prevent my containers from conflicting with other networks (and the host itself), and I am doing the same here.
Thus, the contents of /etc/docker/compose/sftpgo/docker-compose.yaml
are:
---
version: "3.0"
networks:
default:
driver: bridge
ipam:
config:
- subnet: 169.254.0.0/29
services:
#-------------------------------------------------------------------------------
traefik:
image: traefik:v2.6
container_name: traefik
depends_on:
- "sftpgo"
# Public-facing ports
ports:
- "80:80/tcp" # HTTP
- "443:443/tcp" # HTTPS
- "2222:2222/tcp" # SFTP
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # Listen to the Docker events
- /srv/traefik/traefik.toml:/traefik.toml:ro # Static config.
- /srv/traefik/traefik.d:/etc/traefik:ro # Dynamic config.
- /srv/traefik/logs:/logs # Diagnosis / Traffic
- /srv/traefik/users:/users:ro # Basic auth. users
- /srv/traefik/plugins:/plugins-local:ro # Local plugins without Traefik Pilot
- /srv/letsencrypt:/letsencrypt:ro # Offsite certs.
- /etc/ssl/certs/:/cacerts:ro # Certificate Authorities
restart: "always"
#-------------------------------------------------------------------------------
sftpgo:
image: drakkan/sftpgo:v2-alpine
container_name: sftpgo
# Peer-facing ports
expose:
- "1111/tcp"
- "2222/tcp"
- "3333/tcp"
- "4444/tcp"
environment:
# Proxy Protocol
- "SFTPGO_COMMON__PROXY_PROTOCOL=2"
- "SFTPGO_COMMON__PROXY_ALLOWED=169.254.0.0/29"
# SFTP
- "SFTPGO_SFTPD__BINDINGS__0__PORT=1111"
# HTTP
- "SFTPGO_HTTPD__BINDINGS__0__PORT=2222"
- "SFTPGO_HTTPD__BINDINGS__0__PROXY_ALLOWED=169.254.0.0/29"
# Telemetry
- "SFTPGO_TELEMETRY__BIND_PORT=3333"
- "SFTPGO_TELEMETRY__BIND_ADDRESS=0.0.0.0"
# Uncomment if you want to profile SFTPGo, see https://github.com/drakkan/sftpgo/blob/main/docs/profiling.md
# - "SFTPGO_TELEMETRY__ENABLE_PROFILER=true"
# WebDAV
- "SFTPGO_WEBDAVD__BINDINGS__0__PORT=4444"
- "SFTPGO_WEBDAVD__BINDINGS__0__PROXY_ALLOWED=169.254.0.0/29"
# Defender (similar concept as Fail2ban) — see https://github.com/drakkan/sftpgo/blob/main/docs/defender.md
- "SFTPGO_COMMON__DEFENDER__ENABLED=true"
- "SFTPGO_COMMON__DEFENDER__BAN_TIME=30"
- "SFTPGO_COMMON__DEFENDER__BAN_TIME_INCREMENT=50"
- "SFTPGO_COMMON__DEFENDER__THRESHOLD=15"
- "SFTPGO_COMMON__DEFENDER__OBSERVATION_TIME=30"
volumes:
- /srv/sftpgo/home:/srv/sftpgo/data
- /srv/sftpgo/backups:/srv/sftpgo/backups
- /srv/sftpgo/config:/var/lib/sftpgo
restart: unless-stopped
Table of Contents
3 replies on “File Traefik”
I always get this error:
middleware “bastion-hosts@file” does not exist
How can I fix this?
Hi Zeekya,
I just revised the instructions to include the contents of the file. Thanks for finding this mistake and letting me know.
—Louis
May I ask if you could explain a bit what Index is used for and why you would want to redirect these requests to the web interface for administrators instead of the web interface for clients?