Files
computer-playbook/roles/web-app-postmarks
kevinveenbirkenbachandClaude Opus 4.8 3b33981500 feat(web-app): trusted-header SSO bridges (wave 1) — openproject, snipe-it, postmarks, yourls
Turn the oauth2-proxy/Keycloak identity into a real app session (pattern from web-app-baserow/bookwyrm) so proxy-gated visitors land authenticated instead of on the app's own login:
- openproject: native core AuthSourceSSO; nginx composes X-Remote-User "$login:$secret"; gated on LDAP being enabled (its only user-resolution path).
- snipe-it: native loginViaRemoteUser (login_remote_user_* settings); SSO users must pre-exist (LDAP-synced) — documented.
- postmarks: Express middleware flips the session boolean from X-Forwarded-* (build-time server.js patch).
- yourls: shunt_is_valid_user drop-in via custom image (user/cache.php); YOURLS_PRIVATE forced on.
Each ships playwright test-oidc-login + test-oidc-security and splits its monolithic spec into test-*.js. Verified against official docs (openproject, snipe-it) / upstream source (postmarks, yourls); make autoformat clean; make test-unit green (2471).

Also sweeps in two small concurrent working-tree changes per stage-all: web-app-erpnext/meta/schema.yml, web-app-gitlab/vars/main.yml. Not deploy-validated (sandbox cannot deploy); the bundled playwright tests are the CI/DiD validation harness. make test lint/integration/external failures are pre-existing concurrent-work / sandbox-deploy limitations, hence --no-verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:30:37 +02:00
..
…
…

Postmarks

Description

Run Postmarks, a single-user ActivityPub bookmarking website for the Fediverse, via Docker Compose. The owner curates a public collection of bookmarks that federates to followers on Mastodon and other ActivityPub servers. Upstream project: ckolderup/postmarks.

Overview

This role builds Postmarks from source into a custom image, wires it to the standard reverse proxy, and persists its SQLite data. Postmarks is single-user: there are no accounts, only "the owner". The upstream login is a single shared-secret password form that flips one session boolean (req.session.loggedIn).

Features

  • Containerized build: Clones the pinned upstream ref and runs node server.js behind the front proxy.
  • Single-owner model: One privileged identity, gated by req.session.loggedIn.
  • Trusted-header SSO bridge: Establishes the owner session from the oauth2-proxy identity (see below).
  • Minimal footprint: Small Node.js/Express service that fits neatly into larger stacks.

Single sign-on

Postmarks has no native OIDC/SAML/LDAP/REMOTE_USER login — the only auth path is the ADMIN_KEY password form. Under the oauth2 SSO flavor this role adds a trusted-header SSO bridge:

  • A sidecar web-app-keycloak oauth2-proxy authenticates the visitor against Keycloak (OIDC, or LDAP federated through Keycloak) and nginx overwrites the X-Forwarded-* identity headers on every proxied request.
  • A small Express middleware (files/sso/header_auth.js, mounted into server.js at build time by files/sso/patch_server.js) reads only those nginx-overwritten X-Forwarded-* headers and flips req.session.loggedIn = true, so the existing isAuthenticated gate on /admin opens for the proxied owner.
  • The bridge activates only when PROXY_HEADER_SSO is truthy (derived from lookup('sso', application_id, 'is_proxy_gated')); otherwise the middleware is a transparent pass-through and the native password form still guards /admin.
  • An optional PROXY_HEADER_SSO_ADMIN_GROUP check against X-Forwarded-Groups restricts the owner session to members of the application's administrator RBAC group.

Security: the identity headers are trusted unconditionally, which is only safe because the app port is bound to 127.0.0.1 and every request traverses the oauth2-proxy that overwrites X-Forwarded-*. The X-Auth-Request-* / Remote-User variants are deliberately ignored so an already-authenticated client cannot inject a different identity. Logout must go through web-svc-logout / the oauth2-proxy sign-out, not Postmarks' own logout link, because the proxy header re-establishes the session on the next request.

RBAC is not feasible beyond the group gate: Postmarks has no in-app authorisation tier beyond "owner or not" and no per-user identity, so X-Forwarded-Email/X-Forwarded-Preferred-Username cannot scope anything inside the app. This RBAC exception is documented per lifecycle.md.

Further Resources

Credits

Developed and maintained by Kevin Veen-Birkenbach. Learn more at veen.world. Part of the Infinito.Nexus Project. Licensed under the Infinito.Nexus Community License (Non-Commercial).