Skip to content

TLS & public exposure

The firebox daemon listens HTTP on :8765 and trusts the bearer token for every call. That's fine on a LAN. For anything publicly reachable, terminate TLS in front of it.

Topology

flowchart LR
    Net[Internet] --> RP[Reverse proxy<br/>TLS termination]
    RP --plain HTTP--> D[firebox-daemon :8765]

    classDef proxy fill:#42a5f5,stroke:#1565c0,color:#fff
    class RP proxy

The reverse proxy can be Caddy, Cloudflared, Tailscale Funnel, nginx, or whatever else you already run — the daemon doesn't care, as long as the bearer header makes it through.

Caddy (cleanest from-scratch path)

scripts/Caddyfile ships with the repo:

firebox.example.com {
    encode zstd gzip
    reverse_proxy http://127.0.0.1:8765 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        flush_interval -1     # NDJSON streaming for /sandboxes/{id}/stream
    }
    @admin path /admin/*
    rate_limit @admin 10r/m
}

Install:

sudo apt install caddy
sudo cp /opt/firebox/repo/scripts/Caddyfile /etc/caddy/Caddyfile
sudo $EDITOR /etc/caddy/Caddyfile           # swap firebox.example.com
sudo systemctl restart caddy

Caddy auto-issues + renews a Let's Encrypt cert, no other config needed. Point your DNS A/AAAA at the host, wait for cert issuance, done.

Then on the firewall: only port 443 is public; the daemon's own port stays loopback or LAN-only.

sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# 8765 stays internal — Caddy reaches it via 127.0.0.1.

Cloudflared tunnel

If you don't want to expose ports at all, Cloudflare Tunnel works beautifully. Add to /etc/cloudflared/config.yml:

tunnel: <your-tunnel-uuid>
credentials-file: /etc/cloudflared/<uuid>.json
ingress:
  - hostname: firebox.example.com
    service: http://localhost:8765
  - service: http_status:404

Restart cloudflared. Public TLS is provided by Cloudflare; firebox sees only loopback traffic.

Tailscale Funnel

For a private-by-default setup that's still reachable from the public internet:

sudo tailscale funnel 8765

Tailscale terminates TLS via their *.ts.net domain or your custom hostname.

What changes for clients

Once TLS is in front:

# Was:
export FIREBOX_URL=http://192.168.1.50:8765

# Becomes:
export FIREBOX_URL=https://firebox.example.com

Nothing else changes — the bearer header rides through every proxy unmodified.

Hardening checklist

  • [ ] Bind to loopback if a reverse proxy lives on the same host: change the firebox-daemon.service unit to --host 127.0.0.1 after Caddy / cloudflared is in front.
  • [ ] Rate-limit /admin/* at the proxy. The CLI is rare; brute force is suspicious.
  • [ ] Token rotation policy: admin tokens — long-lived; user tokens — issue with a note and a known expiry, revoke when the relationship ends.
  • [ ] Backups: /etc/firebox/, /srv/firebox/templates/, /var/firebox-profiles/ are the only state worth restoring to a fresh host.
  • [ ] Logs: journalctl -u firebox-daemon is single source of truth. Pipe to your log shipper / SIEM.