Files
member-console/docs/hosting.md
T
cgalo5758 ea4fee18b6 Fix five findings from security audit run 2
- Rotate the session token at the OIDC callback and restore the full
  lifetime; cap pre-auth sessions at 15 minutes and write no session
  for bare anonymous requests
- Treat db-dsn as a secret: accept db-dsn-file, log only host, port,
  database and user, and never echo a malformed DSN in an error
- Guard the logout callback with a state cookie so a forged visit
  cannot end a live session
- Collapse FedWiki site actions on a foreign tenant's domain to the
  not-found answer, as for a domain that does not exist
2026-09-09 20:53:31 -05:00

3.3 KiB

title, audience, summary
title audience summary
Hosting member-console
admin
Build the container image and generate the secrets needed to run a member-console instance.

Hosting member-console

Operational steps for running your own member-console instance. For how the console fits alongside a public site and identity provider, see deployment-architecture.md; for identity-provider configuration, see identity-provider-setup.md.

Building the image

The container image is built with Docker Buildx for both ARM64 and AMD64. Tag it for your own registry:

docker buildx build \
  --platform linux/arm64,linux/amd64 \
  -t your-registry.example/your-org/member-console:latest \
  -t your-registry.example/your-org/member-console:$(date +%Y-%m-%d) \
  --push \
  .

The canonical image is published to git.coopcloud.tech/wiki-cafe/member-console. Substitute your own registry when self-hosting.

Secrets

Three configuration keys are secrets, each with a -file variant that reads the value from a file at startup (docs/environment-reference.md):

  • oidc-sp-client-secret — the OIDC client's secret, issued by the identity provider.
  • valkey-password — the session store's password. Required; the console refuses to start without one.
  • temporal-oauth-client-secret — only when Temporal is behind OAuth.

None of them is generated by or for the console: each is a credential for something external, obtained from that system. Sessions are stored server-side in Valkey and carry no signing secret of their own; rotating any of the three means updating the deployment's configuration and restarting.

Session store limits

Every key member-console writes to the session store carries a TTL: a signed-in session lives for the deployment's configured lifetime, and a pre-sign-in session, the one /login creates to hold the OIDC state, nonce, and PKCE verifier, lives fifteen minutes. A bare, unauthenticated request no longer writes anything to the store. A flood of /login requests still writes one short-lived key per request, so the store's memory is still a resource a flood can spend.

Set maxmemory on the Valkey instance to a bound sized for the deployment's expected number of concurrent sessions, and set maxmemory-policy to volatile-lru. Under that policy, once the bound is reached, Valkey evicts the least recently used session first; that session can belong to a signed-in person, and losing it signs them out, so they sign in again. noeviction, Valkey's default, is worse: once maxmemory is reached it rejects new writes instead, and a write rejected during /login or /callback turns the flood into failed sign-ins for everyone hitting the store, not just whichever session would have been evicted.

Rate limit /login at the reverse proxy, since the memory bound above caps damage but does not stop a flood from reaching the store in the first place. With Caddy and the caddy-ratelimit plugin's rate_limit directive:

handle /login* {
    rate_limit {
        zone login {
            key {remote_host}
            events 20
            window 1m
        }
    }
    reverse_proxy member-console:8080
}

Any reverse proxy that can key a limit by client address and scope it to one path serves the same purpose.