πŸ” Securing Ops Dashboards With Authelia

October 23, 2025

Building a unified identity layer across my infrastructure, handling both services with mature OIDC support and those that needed proxy-level enforcement.

The Problem

The more services I end up self-hosting and running on my infra, the more passwords I end up having to remember or add to a secrets manager such as the Vaultwarden instance I've been running on vault.beane.me.

At first it seems to be a workable solution - but these passwords pile up quick and I realized I needed a better solution to simplify these logins. It was creating a great deal of operational friction, and while it was certainly secure, it wasn't neccessarily the best way to go about things.

The Solution

Integrating Authelia as the central IdP seemed like a great way to prevent this pile-up so we only have to deal with one secure password where it's supported, in addition to 2FA to enhance security. Anywhere it's not supported; we could use reverse proxies as an enforcement layer - not to simplify these logins as we did elsewhere (on Grafana and Portainer), but to ensure security.

Architecture Overview

                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚       Authelia        β”‚
                   β”‚  (Central IdP + 2FA)  β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚                                   β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚   Direct OIDC     β”‚               β”‚  Proxy Enforcement β”‚
   β”‚   Federation      β”‚               β”‚   (auth_request)   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚                                   β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Grafana          β”‚               β”‚  Trilium Notes      β”‚
   β”‚  Portainer (x4)   β”‚               β”‚  (no native OIDC)   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚  Protected by NGINX β”‚
                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Implementation

Rolling out Authelia wasn’t a one‑off config change - it became a repeatable pattern. Each service started with the same first step: registering a client in Authelia with the right redirect URI and scopes. From there, Grafana and Portainer (Triton, Zephyrus, Apollo, and Vault) could be federated directly through OIDC, while Trilium required a different approach: enforcing identity at the proxy layer with auth_request.

The process wasn’t without quirks. Grafana refused to accept non‑standard scopes until I pared things back to openid profile email groups. Portainer’s local admin clashed with IdP subjects until I standardized usernames. And Trilium’s first attempt at proxy enforcement ended in 502 loops before I corrected the subrequest pattern. But once those hurdles were cleared, the flow became consistent: configure the client, wire up the service, test the login, and document the edge cases.

What started as a series of fixes turned into a template I can now apply to any new service. The value wasn’t just in getting Authelia working once - it was in building a pattern I can reuse, and continue to extend.

Challenges

Not everything worked on the first try. OIDC is notoriously unforgiving, and I hit the usual wall with redirect URIs - a single trailing slash was enough to derail the login flow until I normalized them. Grafana added its own wrinkle, rejecting non‑standard scopes until I pared things back to the essentials: openid profile email groups.

Portainer introduced its own challenge when the local admin account clashed with the IdP subject, forcing me to rethink how identities were mapped and standardized. Trilium was no easier: my first attempt at proxy enforcement ended in a loop of 502 errors, which I resolved by switching to the proper auth_request subrequest pattern.

Each of these blockers slowed me down, but each also pushed me deeper into how OIDC and reverse proxies really behave - turning friction into lessons worth documenting.

Results

  • Unified login with 2FA across dashboards
  • Reduced credential sprawl for faster service access
  • Consistent pattern that future‑proofs and speeds onboarding of new services

The outcome was a login experience that felt seamless, consistent, and secure across the dashboards I rely on.

The Takeaway

  • Centralized identity reduces friction and risk
  • Proxy‑enforced auth provides a fallback when OIDC isn’t viable
  • Group‑based rules scale access cleanly across services
  • Documenting quirks turns blockers into teaching material
One gate, many doors. Open it once, and the rest follow.