Context
ADR-0018 made the Umami dashboard reachable only through an SSH tunnel. The owner wants it at https://stats.aliakbari.dev, from any browser, without weakening what ADR-0018 protects: visitor data stays private and the dashboard is not exposed to the internet. Umami 3.4.0 has open upstream vulnerabilities (docs/testing.md), so its login page must not be the only barrier.
Decision
- Cloudflare Access (Zero Trust, free plan) protects the hostname. Only the owner's e-mail is
admitted (one-time code); a second Access application bypasses Access for
/.well-known/acme-challenge/only, so certificate renewals work unattended. - The origin verifies Access's token. nginx sends every request through
auth_requesttoaccess-gate, a small Go service (services/api/cmd/access-gate, shipped in the signed API image). It accepts only RS256 tokens signed by a key of the team's published key set, with the application's audience, the team's issuer, a valid time window, typeapp, and an allowed e-mail. A request that reaches the server without passing through Access (sent straight to its address, or with a forged header or cookie) gets 403. Without configuration, or before keys are loaded, it refuses everything. - Umami's own login remains as the third layer; the SSH tunnel (port 8091) remains as a fallback when Cloudflare is unavailable.
- Own certificate lineage.
stats.aliakbari.devhas its own Let's Encrypt certificate, loaded per handshake, so a renewal problem or a missing certificate affects only the dashboard; the site's certificate and nginx's startup do not depend on it. The renewal hook fixes key permissions for whichever lineage renewed. - The hostname is classified as internal by the ledger (
int), so dashboard use never appears in the site's published operational figures. The WAF is not applied: everything that reaches Umami has passed Access and the token check.
Consequences
- The dashboard is reachable from any browser after an e-mail code, and not by anyone else.
access-gateneeds outbound HTTPS to<team>.cloudflareaccess.com(it is on theedgenetwork); every other application container still has no internet route.- Setup needs the owner once: a DNS record, two Access applications, a certificate (sudo), and the
Access settings in
/srv/aliakbari/secrets/access.env(runbook, "Analytics dashboard").