Skip to content

Autentizace pred sluzbami

Martin Skalicky edited this page Aug 4, 2026 · 2 revisions

Autentizace před službami

Spousta self-hosted aplikací má přihlašování slabé nebo žádné. Řešením je postavit před ně vrstvu, která ověří uživatele dřív, než ho k aplikaci vůbec pustí.

Předpokládá to fungující reverse proxy.

Jestli si chceš jen vybrat a nečíst celou stránku: pro rychlé zakrytí jednoho dashboardu stačí základní autentizace, pro všechno ostatní vezmi Pocket ID s tinyauthem.

Kdy to potřebuješ

Aplikace nemá přihlašování vůbec. Uptime Kuma na začátku, různé dashboardy, Prometheus, přehledy.

Má, ale slabé. Bez dvoufaktoru, bez omezení pokusů, s výchozím heslem.

Chceš jedno přihlášení pro všechno. Deset služeb, deset hesel je otrava a vede k opakovaným heslům.

Když je služba jen za VPN, většinou to nepotřebuješ. Tohle je pro věci vystavené do internetu.

Nejjednodušší: základní HTTP autentizace

Jeden řádek konfigurace, žádná další služba.

V Caddy:

tajne.example.cz {
    basic_auth {
        martin $2a$14$hash
    }
    reverse_proxy 127.0.0.1:9000
}

V nginxu:

location / {
    auth_basic "Přihlášení";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:9000;
}

Omezení jsou reálná: ošklivé okno prohlížeče, žádný dvoufaktor, žádné odhlášení, rozbíjí to mobilní aplikace a API. Pro rychlé zakrytí dashboardu to stačí, pro cokoliv, co používáš denně, ne.

Doporučeno: Pocket ID + tinyauth

Tohle je dnes pro homelab nejrozumnější kombinace a je to varianta, kterou bych volil znovu.

Dělí si práci na dvě části, které dělá každá jeden nástroj pořádně:

Pocket ID je poskytovatel identity. Spravuje uživatele a skupiny a mluví OIDC. Zvláštní je tím, že přihlašování řeší výhradně passkeys — žádná hesla, ani jako záložní možnost. To zní jako omezení, ale je to ta nejlepší vlastnost celé sestavy: co neexistuje, nejde uhodnout, vyzradit ani vyphishovat. Nové zařízení se přidává jednorázovým kódem z administrace.

tinyauth je brána. Sedí u reverse proxy, ptá se jí na každý požadavek a neověřené posílá na přihlášení. Umí obecné OIDC, takže ho na Pocket ID napojíš.

Proč ne rovnou něco jednoho: Pocket ID sám o sobě je jen poskytovatel identity, forward auth neumí. A tinyauth sám o sobě umí jen pár uživatelů v konfiguraci. Dohromady dají jednotné přihlášení s passkeys a paměťově se vejdou do desítek megabajtů — Authentik potřebuje řádově víc.

Docker Compose

services:
  pocket-id:
    image: ghcr.io/pocket-id/pocket-id:v2
    restart: unless-stopped
    environment:
      APP_URL: https://id.example.cz
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}            # povinné, min. 16 znaků
      TRUST_PROXY: "true"
      ALLOW_INSECURE_CALLBACK_URLS: "false"
    volumes:
      - ./pocket-id:/app/data
    networks: [web]

  tinyauth:
    image: ghcr.io/tinyauthapp/tinyauth:v5
    restart: unless-stopped
    environment:
      TINYAUTH_APPURL: https://auth.example.cz
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_NAME: Pocket ID
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTID: ${OIDC_ID}
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTSECRET: ${OIDC_SECRET}
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_AUTHURL: https://id.example.cz/authorize
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_TOKENURL: https://id.example.cz/api/oidc/token
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_USERINFOURL: https://id.example.cz/api/oidc/userinfo
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_REDIRECTURL: https://auth.example.cz/api/oauth/callback/pocketid
      TINYAUTH_OAUTH_PROVIDERS_POCKETID_SCOPES: openid email profile groups
      TINYAUTH_OAUTH_AUTOREDIRECT: pocketid
    networks: [web]

Hlídej si cestu k obrazu tinyauthu. Původní ghcr.io/steveiliop56/tinyauth je zamrzlá na verzi 5.0.7 — projekt se přestěhoval pod organizaci a novější vydání včetně bezpečnostních oprav vycházejí jen jako ghcr.io/tinyauthapp/tinyauth. Spousta návodů má ještě starou cestu a docker compose pull na ní aktualizaci nenajde, protože tam žádná novější není.

Obě služby postav za proxy jako každou jinou, Pocket ID uvnitř poslouchá na portu 1411.

Pocket ID musí běžet přes HTTPS. Passkeys stojí na WebAuthn a to funguje jen v zabezpečeném kontextu. Na http:// se nepřihlásíš ani na zkoušku.

Pár proměnných, na kterých se to láme:

  • ENCRYPTION_KEY je od verze 2 povinný. Bez něj Pocket ID nenastartuje.
  • TINYAUTH_SECRET do konfigurace nepatří — od verze 4 neexistuje a tiše se zahodí. Relace jsou v databázi a cookie nese jen neprůhledné číslo. Ve starších návodech ho najdeš, ignoruj to. Totéž platí pro TINYAUTH_PORT (dnes TINYAUTH_SERVER_PORT) a TINYAUTH_USERS (dnes TINYAUTH_AUTH_USERS).
  • TINYAUTH_OAUTH_AUTOREDIRECT: pocketid přeskočí přihlašovací stránku tinyauthu a pošle uživatele rovnou na Pocket ID. Když máš jen jednoho poskytovatele, chceš to.

Propojení

V administraci Pocket ID založ OIDC klienta a jako návratovou adresu (callback) mu dej přesně tu z REDIRECTURL výše. Tvar je vždycky <TINYAUTH_APPURL>/api/oauth/callback/<jméno-poskytovatele>, tedy pocketid musí sedět s tím, co máš v názvech proměnných. Vygenerovaný identifikátor a tajemství doplň do OIDC_ID a OIDC_SECRET.

Nově založený klient nepustí nikoho. Tohle je past, na které skončí většina lidí s dojmem, že něco špatně opsali: čerstvý OIDC klient nemá povolenou žádnou skupinu, takže se přes něj nepřihlásí ani administrátor. V detailu klienta buď klikni na Unrestrict, nebo mu přiřaď povolené skupiny uživatelů.

První přihlášení

Passkey si musí každý uživatel založit sám, admin to za něj udělat nemůže. Postup je takový, že mu v administraci vygeneruješ jednorázový kód (Users → login code, nebo docker compose exec pocket-id /app/pocket-id one-time-access-token <uživatel>), on ho zadá a při té příležitosti si passkey zaregistruje.

Stejnou cestou se řeší ztracený telefon. Počítej s tím, že jsi trvale poslední záchrana — kdo přijde o všechny passkeys, potřebuje tebe. Posílání kódů e-mailem sice jde zapnout, ale dokumentace to sama označuje za nebezpečné, protože pak stačí přístup do schránky.

Napojení na proxy

tinyauth má pro každou proxy vlastní koncový bod: /api/auth/traefik, /api/auth/caddy, /api/auth/nginx a /api/auth/envoy. Jiné jméno vrátí 400.

Caddy — vytvoř si úryvek a použij ho tam, kde chceš:

(chraneno) {
    forward_auth tinyauth:3000 {
        uri /api/auth/caddy
        copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
    }
}

nas.example.cz {
    import chraneno
    reverse_proxy 127.0.0.1:5000
}

Nic víc není potřeba. Caddy vrátí klientovi neúspěšnou odpověď tinyauthu i s přesměrováním, takže žádný handle_response nepiš — přidáním si to jen rozbiješ.

Traefik:

labels:
  - traefik.http.middlewares.tinyauth.forwardauth.address=http://tinyauth:3000/api/auth/traefik
  - traefik.http.middlewares.tinyauth.forwardauth.authResponseHeaders=Remote-User,Remote-Name,Remote-Email,Remote-Groups

authResponseHeaders uveď vždycky, jinak se identita k aplikaci nedostane. trustForwardHeader naopak nenastavuj — Traefik si hlavičky X-Forwarded-* vyrábí sám. Potřebuješ ho jen tehdy, když je Traefik sám za další proxy.

Nginx:

location / {
    auth_request /tinyauth;
    auth_request_set $user  $upstream_http_remote_user;
    auth_request_set $cil   $upstream_http_x_tinyauth_location;
    proxy_set_header Remote-User $user;
    error_page 401 403 = @prihlasit;
    proxy_pass http://127.0.0.1:5000;
}

location = /tinyauth {
    internal;
    proxy_pass http://tinyauth:3000/api/auth/nginx;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host  $http_host;
    proxy_set_header X-Forwarded-Uri   $request_uri;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}

location @prihlasit {
    return 302 $cil;
}

Přihlašovací adresu si nesestavuj sám. tinyauth ji pošle hotovou v hlavičce x-tinyauth-location a ty ji jen převezmeš. Návody, které tu adresu skládají ručně z parametrů, se rozbijí při první aktualizaci.

Kdo smí kam

Tady je past, na které to lidem tiše nefunguje, jak si myslí.

Kde se ověřuje, určuje proxy. Kdo smí projít, určuje tinyauth. To jsou dvě nezávislé věci. Když někam přidáš middleware, zapnul jsi ověření totožnosti — ale ve výchozím nastavení je politika allow, takže aplikace bez vlastního pravidla pustí kohokoliv přihlášeného. Pro rodinný Jellyfin to je v pořádku, pro rozhraní hypervizoru ne.

Omezení na konkrétní lidi nebo skupiny z Pocket ID se nastavuje v tinyauthu per aplikaci a aplikace se páruje podle hlavičky Host. Buď jí nastavíš config.domain, nebo se jméno aplikace musí rovnat první části doménového jména (nasnas.example.cz).

Omezení podle IP adresy (ip.allow, ip.block) navíc mlčky nedělá nic, dokud nenastavíš TINYAUTH_AUTH_TRUSTEDPROXIES. Bez toho tinyauth vidí jako zdroj adresu proxy, ne klienta.

Doména cookie

Platí tu obecné pravidlo forward auth ze schématu níž, ale tinyauth ho odvozuje po svém a stojí za to vědět jak: vezme hostname z TINYAUTH_APPURL a odřízne první část.

Z auth.example.cz tedy vznikne cookie pro example.cz, kterou dostanou všechny služby na *.example.cz. To chceš.

Dvě situace, kdy to nevyjde:

  • auth.sluzby.example.cz vyrobí cookie jen pro sluzby.example.cz. Služba na nas.example.cz ji nikdy nedostane a skončíš v nekonečné smyčce přihlášení → aplikace → přihlášení.
  • Doména od dynamického DNS. Když z auth.neco.duckdns.org vyjde duckdns.org, tinyauth vůbec nenastartuje a napíše, že doména je v seznamu veřejných přípon. Cookie pro duckdns.org by totiž dostal každý, kdo tam má subdoménu. Řešení je vlastní doména.

Když se ti přihlášení točí dokola, otevři v prohlížeči vývojářské nástroje a podívej se, jestli se cookie posílá i na doménu chráněné služby. Skoro vždycky je to tohle.

Kdy tuhle kombinaci nebrat

Poctivě — nehodí se vždycky:

  • Potřebuješ SAML. Neumí ho ani Pocket ID, ani tinyauth. (Pro pořádek: Authelia taky ne. SAML dnes reálně znamená Authentik nebo Keycloak.)
  • Aplikace se potřebuje připojit k LDAP serveru. Obě součásti umí LDAP jen číst — Pocket ID z něj synchronizuje uživatele, tinyauth proti němu umí ověřovat. Ani jedna ale není LDAP server, na který by se stará aplikace přihlásila. To je opět Authentik.
  • Někteří uživatelé passkeys nezvládnou. Pocket ID záměrně nemá hesla vůbec. Když víš, že to u tebe neprojde, chceš Authelii.
  • Chceš podmíněný přístup ve stylu „z domova stačí heslo, zvenku i druhý faktor", nebo pořádná role a oprávnění. tinyauth umí jen seznamy povolených a zakázaných; autor sám v dokumentaci píše, že na prostředí vyžadující RBAC nemá cílit.

Authelia

Nejrozšířenější řešení a dlouho výchozí volba. Přihlašovací stránka, dvoufaktor, pravidla podle domény a cesty. Vezmi ji tam, kde potřebuješ hesla nebo pravidla podle sítě.

services:
  authelia:
    image: authelia/authelia:latest
    volumes:
      - ./authelia:/config
    networks: [web]
    labels:
      - traefik.enable=true
      - traefik.http.routers.authelia.rule=Host(`auth.example.cz`)
      - traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.example.cz
      - traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true
      - traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email

Chráněnou službu pak označíš:

labels:
  - traefik.http.routers.app.middlewares=authelia

Pravidla v configuration.yml:

access_control:
  default_policy: deny
  rules:
    - domain: "verejne.example.cz"
      policy: bypass

    - domain: "nas.example.cz"
      policy: one_factor
      networks: [192.168.1.0/24]

    - domain: "nas.example.cz"
      policy: two_factor

    - domain: "*.example.cz"
      policy: two_factor
      subject: ["group:admin"]

Ta dvojice pravidel pro nas.example.cz je užitečný vzor: z domácí sítě stačí heslo, z internetu se vyžaduje i druhý faktor. Pořadí rozhoduje, první shoda vyhrává.

Uživatelé můžou být v souboru (users_database.yml) nebo v LDAP. Pro domácnost stačí soubor.

Authentik

Plnohodnotný poskytovatel identity. Umí OAuth2, OIDC, SAML, LDAP a proxy autentizaci.

Vezmi ho, když:

  • máš aplikace, které umí OIDC a chceš skutečné jednotné přihlášení, ne jen bránu
  • chceš uživatelské rozhraní pro správu uživatelů místo editace YAML
  • potřebuješ propojit s Google nebo GitHub účty

Cenou je podstatně vyšší spotřeba paměti a složitější nastavení. Pro tři služby je to kanón na vrabce.

Jak forward auth funguje

Užitečné vědět, protože se to špatně ladí naslepo. Schéma platí pro všechny brány, jména jsou z kombinace výše:

sequenceDiagram
    participant K as Klient
    participant P as Proxy
    participant T as tinyauth
    participant I as Pocket ID
    participant S as Služba
    K->>P: GET nas.example.cz
    P->>T: mám ho pustit?
    T->>P: ne, neověřený
    P->>K: přesměrování na přihlášení
    K->>I: přihlášení passkeyem
    I->>T: totožnost a skupiny (OIDC)
    T->>K: cookie pro example.cz
    K->>P: GET nas.example.cz (s cookie)
    P->>T: mám ho pustit?
    T->>P: ano, je to martin
    P->>S: GET / + hlavička Remote-User
    S->>K: obsah
Loading

Z toho plynou dvě věci, na kterých se to nejčastěji láme.

Cookie musí platit pro celou doménu. Proto musí být brána na subdoméně stejné domény jako chráněné služby. auth.example.cz a nas.example.cz ano, auth.jina.cz a nas.example.cz ne. Jak přesně tu doménu odvozuje tinyauth je výš.

Proxy se ptá při každém požadavku. Když je brána dole, nedostaneš se nikam. Počítej s tím a nedávej za ni věci, které potřebuješ, když se něco pokazí — třeba monitoring nebo rozhraní hypervizoru.

Předávání identity aplikaci

Některé aplikace umí důvěřovat hlavičce Remote-User a přihlásit uživatele automaticky. Nextcloud, Grafana a Jellyfin to zvládají.

tinyauth posílá dál pětici Remote-User, Remote-Name, Remote-Email, Remote-Groups a Remote-Sub. Dvě z nich jsou podmíněné: Remote-Groups dostaneš jen u přihlášení přes OIDC nebo LDAP, Remote-Sub jen u OIDC. Kdo se přihlásí lokálním účtem tinyauthu, přijde k aplikaci bez skupin — a aplikace, která podle nich přiděluje oprávnění, ho pak nikam nepustí.

Je to pohodlné, ale nebezpečné, pokud jde aplikace obejít. Kdyby se někdo dostal na aplikaci mimo proxy a poslal si Remote-User: admin, je uvnitř. Proto musí služba poslouchat jen na loopback a být dostupná výhradně přes proxy — viz Docker a firewall.

Co za autentizaci nedávat

Cokoliv, co používá mobilní aplikace nebo API. Forward auth je postavená na cookies a přesměrování v prohlížeči. Immich, Nextcloud a podobné mají vlastní přihlašování — nech ho a autentizaci před ně nedávej, jinak si rozbiješ synchronizaci.

Věci, které potřebuješ, když se to rozbije. Monitoring, konzole hypervizoru, IPMI. Ty dej za VPN.

Webhooky a callbacky. Zaplacení, notifikace z externích služeb. Musí mít výjimku (policy: bypass) na konkrétní cestu.

Časté chyby

Problém Příčina
Přihlášení se točí dokola cookie neplatí pro doménu služby — viz doména cookie
tinyauth vůbec nenastartuje odvozená doména je veřejná přípona, typicky dynamické DNS
Přihlásí se kdokoliv, i když jsi nastavil skupiny výchozí politika je allow, aplikace bez pravidla pustí každého
Pocket ID nepustí ani tebe nový OIDC klient nemá povolenou žádnou skupinu, chybí Unrestrict
Passkey nejde zaregistrovat Pocket ID neběží přes HTTPS
Aplikace nevidí uživatele u Traefiku chybí authResponseHeaders
Omezení podle IP nefunguje není nastavené TINYAUTH_AUTH_TRUSTEDPROXIES
Aktualizace nepřicházejí starý obraz steveiliop56/tinyauth, přejdi na tinyauthapp/tinyauth
Rozbila se mobilní aplikace forward auth nepatří před služby s vlastním klientem

Clone this wiki locally