DEV Community

Määäx
Määäx

Posted on

I put SSO in front of eleven self-hosted services — then locked myself out of all of them

Eleven self-hosted services. Eleven logins. And — being honest here — at least four of them had a password I'd have described as "good enough, I'm the only one who gets in anyway."

That sentence is the beginning of the end.

There was one evening that broke it for me. I wanted to move a single photo from Nextcloud into Paperless. One handgrip. It cost me three different passwords, plus the container dashboard on top, because something in the stack was acting up and I needed to see why. A homelab doesn't grow according to plan — it sprawls. And at some point user management stops being a detail and turns out to be the foundation you forgot to pour.

So I made a decision a lot of people find excessive: before finishing the rest of the homelab, I put a central identity layer in front of it. Single sign-on with Authentik. One login for everything.

Overkill for a home network? I didn't think so. But it did lock me out of my own house, and it did cost me two evenings of chasing a bug that turned out to be one missing thing. Both are worth writing down, because both are the kind of mistake the tutorials skip.

What I actually built

Authentik is a self-hosted identity provider. The mental picture that works for me: it's the office door with the one badge reader, and every room is behind it.

It speaks the usual languages — OpenID Connect and SAML for apps that support them, and forward auth through the router that sits in front of my services for everything that doesn't. In a homelab, "everything that doesn't" is most of it.

Concretely it runs as a Docker stack: Authentik server, worker, PostgreSQL, Redis. In front of that sits the edge router (Traefik-style routing — Nginx or Caddy do the same job) that takes every request for a protected service and asks Authentik first: is this person allowed in?

Not signed in → you land on the login page. Signed in → you get waved through, and the app is told over a header who just walked in.

The end state: one login for eleven services, 2FA in exactly one place, and when I want to give someone — partner, family — access to exactly one service, I do it through a group instead of handing out another shared password.

Mistake 1: I locked myself out. Completely.

When I set up the forward-auth rule on the router, I was thorough. Too thorough. I put everything behind Authentik — including Authentik's own login page.

Read that again slowly, because that's exactly how long it took me to see it: in order to sign in, I had to be signed in.

A perfect infinite loop. I sat in front of a redirect pointing at itself and could not reach a single one of the eleven services. Not the dashboard, not the file storage, not the thing I'd been trying to fix in the first place.

What saved me was that I could still SSH into the host and pull the routing rule for the Authentik host back out.

The lesson has two halves, and the second one is the important one:

  1. The identity provider and its own login route must never sit behind the auth it provides. Exclude it explicitly.
  2. You always need a way past the front door. An SSH path to the host that does not depend on the exact system you're currently rebuilding. Check that it's open before you touch the auth layer, not after.

That second point generalizes far past SSO. Any time you're putting a new gate in front of everything, the first question is: what's my way in if the gate is broken?

Mistake 2: SSO "worked", and every app thought I was a guest

Once the login finally stood up, some services worked immediately. One let me through — and then showed the user as an unnamed guest. Everywhere. Every page, no name, no groups, no permissions that matched who I actually was.

I spent two evenings on this. I suspected the app's config. I suspected Authentik's mappings. I re-read the docs twice.

The cause was banal and, I've since learned, extremely common: the router validated Authentik's answer, but never passed the headers with username and groups on to the app.

So the application received a clean "authenticated: yes" — and no name. With no name, it did the only sensible thing it could and fell back to its guest user.

The lesson: forward auth is two stages, and they fail independently.

  • Stage one — may this request in? That's the auth check. When this breaks, you notice instantly, because nothing loads.
  • Stage two — who is it? Those are the headers passed from the auth endpoint to the actual application. When this breaks, everything looks like it works.

If SSO is "sort of half working" — you get in, but the app doesn't know you — it's almost always stage two. Go look at your header pass-through before you look at anything else.

How I'd set it up today

Same result, without the lockout and without the two evenings:

  1. Start Authentik in isolation first. Server, worker, database, cache as their own stack. Test the login while nothing sits in front of it.
  2. Secure a second way in. SSH to the host, independent of SSO. That's your fire exit. Verify it's open before every auth change.
  3. Connect exactly one unimportant service. Not all eleven. One test service where you get the forward-auth pattern right in a place where breaking it costs you nothing.
  4. Verify both stages separately. Does a signed-out visitor land on the login page (stage: may they in?), and does the service then show the correct username (stage: who? ↔ the headers).
  5. Exclude the identity provider itself. Explicitly carve the Authentik login route out of the auth rule. Otherwise: loop.
  6. Only then roll out — service by service, signing out and back in after each one.
  7. 2FA and groups last. Second factor at the one door, access through groups instead of shared passwords.

What actually changed

The real gain wasn't convenience, which surprised me.

It was that for the first time I had one place where I can see and control who gets into my homelab — and that I can revoke someone's access with one click, without having to remember eleven separate places where I once gave it to them.

Building SSO before the rest feels like pouring a foundation when you'd rather already be putting up walls. That's exactly why it holds later.


I write about self-hosting in more depth — one homelab component per issue, always including what broke — in my German-language newsletter Souveränes Homelab: souveraenes-homelab.beehiiv.com. The English write-ups land here.

Määäx

Top comments (0)