Persist Redis + config in bind-mounted ./config/ (no .env); add backup/restore (#8)

Part A — lossless upgrades:
- Persist both bundled Redis stores via AOF+RDB on named volumes (sso-data,
  proxy-data) so OAuth clients, Host records, perms, DNS creds, and auto-ssl
  Let's Encrypt certs survive rebuilds.
- setup.sh: backup_before_rebuild() snapshots ./config/ + LDAP (slapcat) +
  both Redis (BGSAVE + compose cp) to ./backups/<ts>/ before each rebuild,
  keeps last BACKUP_KEEP (default 5). First run is a no-op.
- Restore runbook (README + docs): full / Redis-only / LDAP-only, with the
  AOF-vs-RDB note (delete the AOF before restoring an RDB).

Part B — eliminate .env / proxy.env:
- All config + secrets live in bind-mounted ./config/ (gitignored), read by each
  app's @simpleworkjs/conf from a symlinked secrets.js. Compose passes only
  NODE_ENV + NODE_PORT (no app_* env, which would override secrets.js).
- ./config/sso-secrets.js: app secrets + orchestrator-only stack/bootstrap/
  serviceAccountPass keys (app ignores the ones it doesn't use).
- ./config/proxy-secrets.js: oidc (clientId/clientSecret filled in by the
  bootstrap), ldap (bind creds), auth (admin groups/users).
- setup.sh ensure_config(): generates ./config/ with random secrets on first
  run (then exits for editing); one-time migration from .env/proxy.env
  preserving existing secrets (LDAP admin pass, JWT, OAuth client, service
  pass) so a running deployment keeps its directory + tokens + OAuth client.
- bootstrap/bootstrap.js: reads /config/*.js (not process.env), registers the
  proxy as an OIDC client, and writes the SSO-generated client id+secret back
  into ./config/proxy-secrets.js (sso mounts ./config RW, proxy RO).
- config.example/ holds committed annotated templates for manual reference.
- .gitignore: add config/, backups/, *.rdb, *.ldif.

Bump both gitlinks to the merged submodule tips:
- sso-manager-node -> 6920a9f (PR #34)
- proxy -> 8e78604 (PR #118)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 13:16:17 -04:00
committed by GitHub
parent 2d2941e394
commit b5f24d40fc
13 changed files with 929 additions and 445 deletions
+45 -38
View File
@@ -11,9 +11,9 @@ title: Quickstart
- A Linux host with **Docker** + **Docker Compose** (the v2 plugin `docker
compose` or the v1 standalone `docker-compose` both work).
- Two hostnames that resolve to the host: one for the SSO UI (`SSO_HOST`), one
for the proxy mgmt UI (`PROXY_HOST`). On a real network add DNS records; for a
local try, add them to `/etc/hosts`.
- Two hostnames that resolve to the host: one for the SSO UI (your `stack.ssoHost`),
one for the proxy mgmt UI (your `stack.proxyHost`). On a real network add DNS
records; for a local try, add them to `/etc/hosts`.
- Port **80 + 443** reachable from the internet if you want Let's Encrypt
certs; otherwise the proxy serves a self-signed fallback (browsers warn —
expected for LAN use).
@@ -32,28 +32,32 @@ step. If you forgot it:
git submodule update --init --recursive
```
## 2. Configure `.env`
## 2. Configure `./config/`
```bash
cp .env.example .env
./setup.sh # generates ./config/ with random secrets, then exits
```
Edit `.env`. The **required** values:
The first `./setup.sh` generates `./config/sso-secrets.js` +
`./config/proxy-secrets.js` and **exits**, telling you to edit. Edit
`./config/sso-secrets.js` and at minimum set:
| Key | Example | Notes |
| Key (in `sso-secrets.js`) | Example | Notes |
|-----|---------|-------|
| `LDAP_BASE_DN` | `dc=lab,dc=local` | your directory base |
| `LDAP_ADMIN_PASS` | `...` | LDAP root password — **save it** |
| `JWT_SECRET` | _(blank)_ | leave blank to auto-generate + persist — **save it** |
| `SSO_HOST` | `sso.lab.local` | hostname the proxy serves the SSO UI at |
| `PROXY_HOST` | `proxy.lab.local` | hostname the proxy serves its own UI at |
| `BOOTSTRAP_ADMIN_UID` | `admin` | your first admin login |
| `BOOTSTRAP_ADMIN_PASS` | `...` | first admin password |
| `stack.ldapBaseDn` | `dc=lab,dc=local` | your directory base |
| `stack.ssoHost` | `sso.lab.local` | hostname the proxy serves the SSO UI at |
| `stack.proxyHost` | `proxy.lab.local` | hostname the proxy serves its own UI at |
| `bootstrap.adminUid` | `admin` | your first admin login |
| `bootstrap.adminPass` | `...` | first admin password |
Optional: `BOOTSTRAP_ADMIN_EMAIL`, `LDAP_SERVICE_PASS` (auto-generated if blank),
`SMTP_*` (for SSO password-reset/invite emails), `LDAP_CERT_CN`, and host port
overrides (`SSO_PORT`, `LDAPS_PORT`, `HTTP_PORT`, `HTTPS_PORT`,
`HTTPS_ALT_PORT`, `MGMT_PORT`). See `.env.example` for the full commented list.
Random secrets (`ldap.bindPassword`, `oauth.jwtSecret`, `serviceAccountPass`)
are generated for you — change them in the file if you like. Optional:
`bootstrap.adminEmail`, `smtp.*`, `stack.ldapCertCn`. See `config.example/` for
the full annotated shape, and each submodule's `secrets.js.example`.
> **Migrating from an older `.env`-based deployment?** If `.env`/`proxy.env`
> exist, `./setup.sh` migrates them into `./config/` preserving your existing
> secrets — no need to reconfigure.
## 3. Run
@@ -63,24 +67,22 @@ overrides (`SSO_PORT`, `LDAPS_PORT`, `HTTP_PORT`, `HTTPS_PORT`,
What happens:
1. Validates `.env` (copies from `.env.example` if missing, then exits so you
can edit it).
1. Snapshots state to `./backups/<timestamp>/` before rebuilding (a no-op on the
very first run).
2. Builds + starts **sso-manager**, waits for `/health`.
3. Runs the **bootstrap** inside the sso-manager container — creates the LDAP
service account, your first admin, and the proxy's OAuth client, and prints
the client id + secret.
4. Writes **`./proxy.env`** (the proxy's `app_*` config) from `.env` + the
bootstrap output.
5. Builds + starts **proxy**, waits for `/health`.
6. Prints your first-admin login + the public URLs.
service account, your first admin, and the proxy's OAuth client, and writes
the generated client id + secret into `./config/proxy-secrets.js`.
4. Builds + starts **proxy**, waits for `/health`.
5. Prints your first-admin login + the public URLs.
The first run builds two Docker images (a few minutes). Subsequent runs are
fast.
## 4. Point DNS at the host
`SSO_HOST` and `PROXY_HOST` must resolve to the host running the stack. Add DNS
records, or for a local try:
`stack.ssoHost` and `stack.proxyHost` (from `./config/sso-secrets.js`) must
resolve to the host running the stack. Add DNS records, or for a local try:
```bash
echo "127.0.0.1 sso.lab.local proxy.lab.local" | sudo tee -a /etc/hosts
@@ -92,7 +94,7 @@ serves a self-signed cert — browsers will warn, which is fine for home-lab use
## 5. Log in
Open `https://<SSO_HOST>` and log in as your bootstrap admin
(`BOOTSTRAP_ADMIN_UID` / `BOOTSTRAP_ADMIN_PASS`). From there you can add users,
(`bootstrap.adminUid` / `bootstrap.adminPass`). From there you can add users,
groups, and OAuth clients.
The proxy mgmt UI is at `https://<PROXY_HOST>` (same admin SSO login protects
@@ -103,10 +105,11 @@ First-run fallbacks (if DNS/TLS isn't ready yet): SSO UI at
## Re-running
`./setup.sh` is **idempotent** — safe to re-run after editing `.env`, after a
`docker compose down`, or after restoring from backup. It converges the stack to
your `.env` values (LDAP service account + admin passwords are reset to `.env`;
the OAuth client is left alone if `proxy.env` exists).
`./setup.sh` is **idempotent** — safe to re-run after editing `./config/`, after
a `docker compose down`, or after restoring from backup. It snapshots state,
then converges the stack to your `./config/` values (LDAP service account + admin
passwords are reset to the config; the OAuth client is kept if `proxy-secrets.js`
already holds its creds).
## Direct LDAP for legacy apps
@@ -121,16 +124,20 @@ ldapsearch -x -H ldaps://<host>:636 \
Use the `cn=ldapclient` service account (read-only, the bootstrap created it)
or the admin DN. Use LDAPS (636), not plain LDAP.
## Backups
## Backups and restore
`./setup.sh` auto-snapshots `./config/` + LDAP + both Redis to `./backups/<ts>/`
before each rebuild (keeps the last `BACKUP_KEEP`, default 5). For manual
backups and the full restore runbook (full / Redis-only / LDAP-only, with the
AOF-vs-RDB note), see the *Backups and restore* section of the
[README](https://github.com/theta42/theta-env#backups-and-restore). Quick LDAP
backup:
```bash
docker compose exec sso-manager slapcat -f /etc/openldap/slapd.conf \
-b "$LDAP_BASE_DN" > backup-$(date +%F).ldif
-b "<base>" > backup-$(date +%F).ldif
```
Keep `.env` + `proxy.env` alongside it. Restore is `ldapadd`/`ldapmodify` into a
fresh directory, then re-run `./setup.sh`.
## Next steps
- Add users / groups in the SSO UI.