Skip to content

v1.1.0

Choose a tag to compare

@mxcd mxcd released this 30 Oct 23:19
· 10 commits to main since this release
b85c298

Security hardening from audit run 2 and impersonation as a library construct. Some changes break code or configuration that relied on the old behaviour; each is listed below and in docs/migration.md under "After v1.0.0". Decisions D42-D60 in docs/design.md.

New: impersonation (session.Impersonator, D60)

  • session.NewImpersonator(&session.ImpersonatorOptions{Guard, Load, ...}) is the only way to start or stop an impersonation: POST /auth/impersonation {"subject"} and DELETE /auth/impersonation behind the Guard, or Start/Stop in your own handlers.
  • Actor (default authorization.IsAdmin()) decides who may impersonate, Target (default Not(IsAdmin())) who may be impersonated. Always enforced: no nesting, active impersonator and target, no self-impersonation, a swap against the identity the Guard read.
  • An impersonation ends when stopped, with the session, or after MaximumDuration (default 1 h); the next request runs as the impersonator again.
  • JWTs of an impersonating identity carry the impersonator as the RFC 8693 act claim; Verify returns it as Identity.Impersonator (without roles).
  • New client codes already_impersonating, not_impersonating, unknown_impersonation_target, self_impersonation; outcomes impersonation_start, impersonation_stop; wide-event keys auth.impersonation.* and auth.impersonated_subject.

Breaking changes

  • session: SaveIdentity and SwapIdentity return an error for an identity or impersonator of another provider or subject than the session holds, and a login cannot save an impersonating identity. Replace code that set Identity.Impersonator itself with session.Impersonator; impersonations stored the old way end at their next request (D60).
  • jwt: Sign refuses an identity whose impersonator is inactive; authorization.Real(...) on a verified token now sees the act impersonator (D60). VerifierOptions.Audience is required (D54). JWKSURL must be https (plain http on loopback only) unless InsecureJWKSURL: true (D54). New MaximumKeyAge (default 24 h): keys are no longer trusted forever while the JWKS is unreachable (D55).
  • oidc: Issuer: oidc.GoogleIssuer needs AllowedEmailDomains, OnLogin or AllowAnyAccount: true; with Google, a non-consumer allowed domain also requires the hd claim, so only Workspace accounts of that domain pass (D56).
  • password: sessions are bound to a credential stamp; a password change or TOTP enable/disable ends the user's other sessions, and every password user logs in once more after the upgrade (D45).
  • session: ParseKey and NewStore refuse an encryption key that repeats one byte (all zero included) (D44); SaveIdentity and SwapIdentity no longer reset the Guard's refresh clock (D44).
  • authorization: every check except IsAuthenticated refuses an identity whose impersonator is inactive (D47).
  • forwardauth image: JWT_AUDIENCE required with JWT_ENABLED=true (D54); Google needs OIDC_ALLOWED_EMAIL_DOMAINS, an enforced KEYCLOAK_REQUIRED_* list or OIDC_ALLOW_ANY_ACCOUNT=true (D56); percent-encoded route variants answer 404 and /auth/me refuses inactive identities (D50); the checks refuse an identity whose impersonator is inactive (D60); request.client_ip is the peer address unless TRUSTED_PROXIES lists it (D51); a malformed SESSION_REDIS_ADDRESS fails validation at boot.

Security fixes (audit run 2)

  • Memory backend evicts through an expiry heap (O(log n)), so login floods no longer stall every request (D42).
  • Redis backend: an evicted session index reads as logged out, so a logout always takes effect (D43).
  • Guard: application saves no longer postpone the refresh; a failed refresh claim calls the provider only when it committed (D44).
  • OIDC access-token roles only for the same sub; raw Keycloak role claims are not copied into JWTs (D48); logout work bounded at 10 s; the Prometheus adapter never panics on invalid label values (D49).
  • GitHub OAuth tokens are revoked whenever the Guard or the provider ends a session (session.Revoker, D57).

Forward-auth image

  • Redis Sentinel (SESSION_REDIS_SENTINEL_*), TLS including mTLS (SESSION_REDIS_TLS_*) and an ACL username (SESSION_REDIS_USERNAME) (D52, D53).
  • TRUSTED_PROXIES (default none) decides whose X-Forwarded-For the logged client IP trusts (D51).
  • Published only after lint, tests, e2e and vulncheck passed, with a signed build provenance attestation; :latest, :1 and :1.1 move only for the newest stable tag (D59).

Image: ghcr.io/govengers/forwardauth:1.1.0.