Skip to main content

Traefik

The following are examples for configuring the Traefik proxy as a reverse proxy for Firezone.

In these examples, we assume Traefik is deployed using Docker on the same host as Firezone. For this to work, you'll need to make sure EXTERNAL_TRUSTED_PROXIES is set to include the Docker address of the Traefik service.

Without SSL termination

Since Firezone requires HTTPS for the web portal, please bear in mind a downstream proxy will need to terminate SSL connections in this scenario.

Use the following docker-compose.yml and rules.yml files as a starting point for your own Firezone config:

docker-compose.yml

version: '3'

x-deploy: &default-deploy
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
update_config:
order: start-first

networks:
firezone-network:
enable_ipv6: true
ipam:
config:
- subnet: 172.25.0.0/16
- subnet: 2001:3990:3990::/64

services:
traefik:
deploy:
<<: *default-deploy
# The official v2 Traefik docker image
image: traefik:v2.8
# Enables the web UI and tells Traefik to listen to docker
command:
- "--providers.docker"
- "--providers.file.filename=rules.yml"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.forwardedHeaders.insecure"
- "--log.level=DEBUG"
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
# The HTTP port
- "80:80"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./rules.yml:/rules.yml
# make the IP of this service deterministic
networks:
firezone-network:
ipv4_address: 172.25.0.99
ipv6_address: 2001:3990:3990::99

firezone:
image: firezone/firezone
ports:
- 51820:51820/udp
env_file:
# This should contain a list of env vars for configuring Firezone.
# See https://docs.firezone.dev/reference/env-vars for more info.
- ${FZ_INSTALL_DIR:-.}/.env
volumes:
# IMPORTANT: Persists WireGuard private key and other data. If
# /var/firezone/private_key exists when Firezone starts, it is
# used as the WireGuard private. Otherwise, one is generated.
- ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone
cap_add:
# Needed for WireGuard and firewall support.
- NET_ADMIN
- SYS_MODULE
sysctls:
# Needed for masquerading and NAT.
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
depends_on:
- postgres
networks:
firezone-network:
ipv4_address: 172.25.0.100
deploy:
<<: *default-deploy

postgres:
image: postgres:15
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${DATABASE_NAME:-firezone}
POSTGRES_USER: ${DATABASE_USER:-postgres}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?err}
networks:
- firezone-network
deploy:
<<: *default-deploy
update_config:
order: stop-first

# Postgres needs a named volume to prevent perms issues on non-linux platforms
volumes:
postgres-data:

networks:
firezone-network:
driver: bridge
ipam:
config:
- subnet: 172.25.0.0/16

rules.yml

http:
routers:
test:
entryPoints:
- "web"
service: test
rule: "Host(`44.200.42.78`)"
services:
test:
loadBalancer:
servers:
- url: "http://firezone:13000"

Now you should be able to start the Traefik proxy with docker compose up.

With SSL termination

This configuration uses Firezone's auto-generated self-signed certificates.

SSL docker-compose.yml

version: '3'

x-deploy: &default-deploy
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
update_config:
order: start-first

networks:
app:
enable_ipv6: true
ipam:
config:
- subnet: 172.28.0.0/16
- subnet: 2001:3990:3990::/64

services:
firezone:
image: firezone/firezone
ports:
- 51820:51820/udp
volumes:
# IMPORTANT: Persists WireGuard private key and other data. If
# /var/firezone/private_key exists when Firezone starts, it is
# used as the WireGuard private. Otherwise, one is generated.
- ${HOME}/.firezone/firezone:/var/firezone
environment:
EXTERNAL_URL: ${EXTERNAL_URL:?err}
ADMIN_EMAIL: ${ADMIN_EMAIL:?err}
DEFAULT_ADMIN_PASSWORD: ${DEFAULT_ADMIN_PASSWORD:?err}
GUARDIAN_SECRET_KEY: ${GUARDIAN_SECRET_KEY:?err}
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?err}
LIVE_VIEW_SIGNING_SALT: ${LIVE_VIEW_SIGNING_SALT:?err}
COOKIE_SIGNING_SALT: ${COOKIE_SIGNING_SALT:?err}
COOKIE_ENCRYPTION_SALT: ${COOKIE_ENCRYPTION_SALT:?err}
DATABASE_ENCRYPTION_KEY: ${DATABASE_ENCRYPTION_KEY:?err}

# Ensure this includes the traefik service IP.
EXTERAL_TRUSTED_PROXIES: ['']
networks:
- app
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
depends_on:
- postgres
traefik:
deploy:
<<: *default-deploy
# The official v2 Traefik docker image
image: traefik:v2.8
# Enables the web UI and tells Traefik to listen to docker
command:
- "--providers.docker"
- "--providers.file.filename=rules.yml"
- "--entrypoints.web.address=:443"
- "--entrypoints.web.forwardedHeaders.insecure"
- "--log.level=DEBUG"
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
# The HTTP port
- "443:443"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./rules.yml:/rules.yml
# make the IP of this service deterministic
networks:
app:
ipv4_address: 172.28.0.99
ipv6_address: 2001:3990:3990::99
postgres:
image: postgres:15
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
# same value as ## DB section above
POSTGRES_DB: ${DATABASE_NAME:-firezone}
POSTGRES_USER: ${DATABASE_USER:-postgres}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?err}
deploy:
<<: *default-deploy
update_config:
order: stop-first

# Postgres needs a named volume to prevent perms issues on non-linux platforms
volumes:
postgres-data:

SSL rules.yml

http:
routers:
test:
entryPoints:
- "web"
service: test
rule: "Host(`44.200.42.78`)"
tls: {}
services:
test:
loadBalancer:
servers:
- url: "http://firezone:13000"
tls:
stores:
default:
defaultCertificate:
certFile: /path/to/your/cert.crt
keyFile: /path/to/your/key.key