Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8def1f6340 | |||
| 49cd134fb3 | |||
| 30146e9588 | |||
| 481602ae60 |
+22
-1
@@ -10,6 +10,25 @@ for what changed inside the apps it composes.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.17] - 2026-07-18
|
||||||
|
|
||||||
|
### Bumped
|
||||||
|
- proxy -> [v1.1.15](https://github.com/theta42/proxy/releases/tag/v1.1.15)
|
||||||
|
- sso-manager-node -> [v1.1.15](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.15)
|
||||||
|
|
||||||
|
Both apps' bare-metal `install.sh` now installs to `/opt/theta42/<app>` and seeds `/etc/<app>/secrets.js` on first run, matching a `wget -O - .../install.sh | sudo bash` one-line install for both (previously proxy-only); re-running it prints the version it's updating from/to. sso-manager-node's installer was rewritten from a flag-driven, copy-based script into the same idempotent git-clone pattern proxy already used, and now bootstraps OpenLDAP itself on first run instead of requiring the repo to already be checked out locally. None of this affects the Docker/unified-stack deployment this repo orchestrates — bare-metal-only.
|
||||||
|
|
||||||
|
## [1.1.16] - 2026-07-18
|
||||||
|
|
||||||
|
### Bumped
|
||||||
|
- proxy -> [v1.1.14](https://github.com/theta42/proxy/releases/tag/v1.1.14)
|
||||||
|
- sso-manager-node -> [v1.1.14](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.14)
|
||||||
|
|
||||||
|
Both: bumped `@simpleworkjs/conf` to 1.2.0 and `jq-repeat` to 2.2.0.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `./config/sso-secrets.js` and `./config/proxy-secrets.js` are now loaded via each app's `CONF_SECRETS` env var (set by the entrypoint) instead of being symlinked into `/app/conf/secrets.js` — neither container needs write access to its own `conf/` directory anymore. No change to the config file format or bind mounts; existing `./config/` directories keep working as-is.
|
||||||
|
|
||||||
## [1.1.15] - 2026-07-17
|
## [1.1.15] - 2026-07-17
|
||||||
|
|
||||||
### Bumped
|
### Bumped
|
||||||
@@ -195,7 +214,9 @@ First tagged release. Establishes the `vX.Y.Z` tag convention going forward.
|
|||||||
- proxy -> [v1.1.0](https://github.com/theta42/proxy/releases/tag/v1.1.0)
|
- proxy -> [v1.1.0](https://github.com/theta42/proxy/releases/tag/v1.1.0)
|
||||||
- sso-manager-node -> [v1.1.0](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.0)
|
- sso-manager-node -> [v1.1.0](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.0)
|
||||||
|
|
||||||
[Unreleased]: https://github.com/theta42/theta-env/compare/v1.1.15...HEAD
|
[Unreleased]: https://github.com/theta42/theta-env/compare/v1.1.17...HEAD
|
||||||
|
[1.1.17]: https://github.com/theta42/theta-env/compare/v1.1.16...v1.1.17
|
||||||
|
[1.1.16]: https://github.com/theta42/theta-env/compare/v1.1.15...v1.1.16
|
||||||
[1.1.15]: https://github.com/theta42/theta-env/compare/v1.1.14...v1.1.15
|
[1.1.15]: https://github.com/theta42/theta-env/compare/v1.1.14...v1.1.15
|
||||||
[1.1.14]: https://github.com/theta42/theta-env/compare/v1.1.13...v1.1.14
|
[1.1.14]: https://github.com/theta42/theta-env/compare/v1.1.13...v1.1.14
|
||||||
[1.1.13]: https://github.com/theta42/theta-env/compare/v1.1.12...v1.1.13
|
[1.1.13]: https://github.com/theta42/theta-env/compare/v1.1.12...v1.1.13
|
||||||
|
|||||||
@@ -172,7 +172,8 @@ operator-owned and `setup.env` is ignored.
|
|||||||
### Configuration — `./config/` (no `.env` files)
|
### Configuration — `./config/` (no `.env` files)
|
||||||
|
|
||||||
All config and secrets live in a bind-mounted `./config/` directory (gitignored),
|
All config and secrets live in a bind-mounted `./config/` directory (gitignored),
|
||||||
read by each app's `@simpleworkjs/conf` from a symlinked `secrets.js`:
|
read by each app's `@simpleworkjs/conf` via the `CONF_SECRETS` env var, which
|
||||||
|
the entrypoint points at the mounted file:
|
||||||
|
|
||||||
- **`./config/sso-secrets.js`** — SSO config: `ldap` (base, admin password,
|
- **`./config/sso-secrets.js`** — SSO config: `ldap` (base, admin password,
|
||||||
user/group bases), `oauth` (issuer, `jwtSecret`), `smtp`, `name`, plus
|
user/group bases), `oauth` (issuer, `jwtSecret`), `smtp`, `name`, plus
|
||||||
|
|||||||
+8
-7
@@ -13,11 +13,12 @@
|
|||||||
# Config + secrets live in bind-mounted ./config/ (gitignored):
|
# Config + secrets live in bind-mounted ./config/ (gitignored):
|
||||||
# ./config/sso-secrets.js — SSO app + orchestrator config
|
# ./config/sso-secrets.js — SSO app + orchestrator config
|
||||||
# ./config/proxy-secrets.js — proxy OIDC/LDAP/auth config
|
# ./config/proxy-secrets.js — proxy OIDC/LDAP/auth config
|
||||||
# Each app's entrypoint symlinks its file into /app/conf/secrets.js so
|
# Each app's entrypoint points CONF_SECRETS at its file so @simpleworkjs/conf
|
||||||
# @simpleworkjs/conf reads it. No app_* env is passed (app_* env would override
|
# (>= 1.2.0) reads it directly -- no app_* env is passed (app_* env would
|
||||||
# secrets.js). The sso-manager mounts ./config read-write so the bootstrap can
|
# override secrets.js), and no write access to /app/conf is needed. The
|
||||||
# write the generated OAuth client creds back into proxy-secrets.js; the proxy
|
# sso-manager mounts ./config read-write so the bootstrap can write the
|
||||||
# mounts it read-only.
|
# generated OAuth client creds back into proxy-secrets.js; the proxy mounts
|
||||||
|
# it read-only.
|
||||||
#
|
#
|
||||||
# Compose only interpolates the port defaults below — there is no .env file.
|
# Compose only interpolates the port defaults below — there is no .env file.
|
||||||
# First-run wiring (LDAP service account, first admin, OAuth client) is
|
# First-run wiring (LDAP service account, first admin, OAuth client) is
|
||||||
@@ -56,7 +57,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# Operator-edited SSO secrets (sso-secrets.js). Read-WRITE so the bootstrap
|
# Operator-edited SSO secrets (sso-secrets.js). Read-WRITE so the bootstrap
|
||||||
# can write the generated OAuth client creds into proxy-secrets.js. The
|
# can write the generated OAuth client creds into proxy-secrets.js. The
|
||||||
# entrypoint symlinks /config/sso-secrets.js -> /app/conf/secrets.js.
|
# entrypoint points CONF_SECRETS at /config/sso-secrets.js.
|
||||||
- ./config:/config
|
- ./config:/config
|
||||||
# Persist the LDAP database across container recreation.
|
# Persist the LDAP database across container recreation.
|
||||||
- ldap-data:/var/lib/ldap
|
- ldap-data:/var/lib/ldap
|
||||||
@@ -108,7 +109,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# Operator-edited proxy secrets (proxy-secrets.js). READ-ONLY — the proxy
|
# Operator-edited proxy secrets (proxy-secrets.js). READ-ONLY — the proxy
|
||||||
# only reads it; the sso-manager bootstrap writes the OAuth creds. The
|
# only reads it; the sso-manager bootstrap writes the OAuth creds. The
|
||||||
# entrypoint symlinks /config/proxy-secrets.js -> /app/conf/secrets.js.
|
# entrypoint points CONF_SECRETS at /config/proxy-secrets.js.
|
||||||
- ./config:/config:ro
|
- ./config:/config:ro
|
||||||
# Persist Redis (AOF + RDB) so Host records, permissions, DNS creds, local
|
# Persist Redis (AOF + RDB) so Host records, permissions, DNS creds, local
|
||||||
# users, AND the auto-ssl Let's Encrypt certs survive container recreation.
|
# users, AND the auto-ssl Let's Encrypt certs survive container recreation.
|
||||||
|
|||||||
+13
-12
@@ -102,9 +102,9 @@ inputs from the bind-mounted `./config/sso-secrets.js` + `./config/proxy-secrets
|
|||||||
read-only). If `proxy-secrets.js` already holds a `clientId`+`clientSecret`
|
read-only). If `proxy-secrets.js` already holds a `clientId`+`clientSecret`
|
||||||
matching an existing client, they are kept; if the client exists but the file
|
matching an existing client, they are kept; if the client exists but the file
|
||||||
has no usable secret, the secret is rotated and written back.
|
has no usable secret, the secret is rotated and written back.
|
||||||
6. **Build + start the proxy**, wait for `/health`. The proxy entrypoint symlinks
|
6. **Build + start the proxy**, wait for `/health`. The proxy entrypoint points
|
||||||
`./config/proxy-secrets.js` to `/app/conf/secrets.js`, so `@simpleworkjs/conf`
|
`CONF_SECRETS` at `./config/proxy-secrets.js`, so `@simpleworkjs/conf`
|
||||||
(≥1.1.0) reads the OAuth creds + LDAP bind creds from the file.
|
(≥1.2.0) reads the OAuth creds + LDAP bind creds from the file.
|
||||||
7. **Register `<SSO_HOST>` and `<PROXY_HOST>` as Host records in the proxy** —
|
7. **Register `<SSO_HOST>` and `<PROXY_HOST>` as Host records in the proxy** —
|
||||||
`setup.sh` runs a short script inside the proxy container that calls its
|
`setup.sh` runs a short script inside the proxy container that calls its
|
||||||
Host model directly (`Host.create({host, ip, targetPort, ...})`), rather
|
Host model directly (`Host.create({host, ip, targetPort, ...})`), rather
|
||||||
@@ -123,19 +123,20 @@ inputs from the bind-mounted `./config/sso-secrets.js` + `./config/proxy-secrets
|
|||||||
### How config reaches the apps (no `.env`)
|
### How config reaches the apps (no `.env`)
|
||||||
|
|
||||||
All config and secrets live in `./config/` (gitignored, bind-mounted). Each
|
All config and secrets live in `./config/` (gitignored, bind-mounted). Each
|
||||||
entrypoint symlinks its file to `/app/conf/secrets.js` early, before the app
|
entrypoint points the `CONF_SECRETS` env var (`@simpleworkjs/conf` >= 1.2.0)
|
||||||
starts:
|
at its file early, before the app starts:
|
||||||
|
|
||||||
```
|
```
|
||||||
./config/sso-secrets.js -> sso-manager:/app/conf/secrets.js (./config RW)
|
CONF_SECRETS=/config/sso-secrets.js (sso-manager, ./config RW)
|
||||||
./config/proxy-secrets.js -> proxy:/app/conf/secrets.js (./config RO)
|
CONF_SECRETS=/config/proxy-secrets.js (proxy, ./config RO)
|
||||||
```
|
```
|
||||||
|
|
||||||
`@simpleworkjs/conf` loads `conf/base.js → <env>.js → conf/secrets.js → app_*
|
`@simpleworkjs/conf` loads `conf/base.js → <env>.js → secrets file → app_*
|
||||||
env`, where **env beats `secrets.js`**. So compose passes **no `app_*` env vars**
|
env`, where **env beats the secrets file**. So compose passes **no `app_*` env
|
||||||
(only `NODE_ENV`, `NODE_PORT`) — that makes `secrets.js` authoritative. The SSO
|
vars** (only `NODE_ENV`, `NODE_PORT`) — that makes the secrets file
|
||||||
entrypoint reads the few values it needs at startup (LDAP base DN, admin
|
authoritative. The SSO entrypoint reads the few values it needs at startup
|
||||||
password, JWT secret, cert CN) from `secrets.js` via an in-container `node` call.
|
(LDAP base DN, admin password, JWT secret, cert CN) from `sso-secrets.js` via
|
||||||
|
an in-container `node` call.
|
||||||
|
|
||||||
### Why not `require` the SSO's internal models?
|
### Why not `require` the SSO's internal models?
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -25,18 +25,18 @@ mkdir -p config && cp secrets.js.example config/sso-secrets.js # edit it
|
|||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
The entrypoint symlinks `config/sso-secrets.js` to `nodejs/conf/secrets.js` so
|
The entrypoint points the `CONF_SECRETS` env var at `config/sso-secrets.js` so
|
||||||
`@simpleworkjs/conf` reads it. Set `ldap.bindPassword`, `oauth.jwtSecret`, and
|
`@simpleworkjs/conf` reads it. Set `ldap.bindPassword`, `oauth.jwtSecret`, and
|
||||||
the `stack`/`bootstrap` keys (the app ignores the ones it doesn't use). Pass
|
the `stack`/`bootstrap` keys (the app ignores the ones it doesn't use). Pass
|
||||||
**no `app_*` env** — env beats `secrets.js`, so `app_*` would silently override
|
**no `app_*` env** — env beats the secrets file, so `app_*` would silently
|
||||||
your file.
|
override your file.
|
||||||
|
|
||||||
- Web UI: `http://localhost:3001`
|
- Web UI: `http://localhost:3001`
|
||||||
- Health: `http://localhost:3001/health`
|
- Health: `http://localhost:3001/health`
|
||||||
- OIDC discovery: `http://localhost:3001/.well-known/openid-configuration`
|
- OIDC discovery: `http://localhost:3001/.well-known/openid-configuration`
|
||||||
- LDAPS: `ldaps://<host>:636`
|
- LDAPS: `ldaps://<host>:636`
|
||||||
|
|
||||||
Requires `@simpleworkjs/conf` >= 1.1.0. Full reference:
|
Requires `@simpleworkjs/conf` >= 1.2.0. Full reference:
|
||||||
[SSO Manager deployment docs](https://theta42.github.io/sso-manager-node/deployment.html).
|
[SSO Manager deployment docs](https://theta42.github.io/sso-manager-node/deployment.html).
|
||||||
|
|
||||||
### Bare metal
|
### Bare metal
|
||||||
@@ -62,11 +62,11 @@ mkdir -p config && cp secrets.js.example config/proxy-secrets.js # edit it
|
|||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
The entrypoint symlinks `config/proxy-secrets.js` to `nodejs/conf/secrets.js` so
|
The entrypoint points the `CONF_SECRETS` env var at `config/proxy-secrets.js`
|
||||||
`@simpleworkjs/conf` reads it. Fill in `oidc` (your SSO's endpoints +
|
so `@simpleworkjs/conf` reads it. Fill in `oidc` (your SSO's endpoints +
|
||||||
`clientId`/`clientSecret`/`redirectUri`), `ldap` (bind creds + search base), and
|
`clientId`/`clientSecret`/`redirectUri`), `ldap` (bind creds + search base), and
|
||||||
`auth` (admin groups/users). Pass **no `app_*` env** — env beats `secrets.js`,
|
`auth` (admin groups/users). Pass **no `app_*` env** — env beats the secrets
|
||||||
so `app_*` would silently override your file.
|
file, so `app_*` would silently override your file.
|
||||||
|
|
||||||
- Proxy (public, auto-SSL): `https://<host>/`
|
- Proxy (public, auto-SSL): `https://<host>/`
|
||||||
- Mgmt UI / API: `http://127.0.0.1:3000/`
|
- Mgmt UI / API: `http://127.0.0.1:3000/`
|
||||||
|
|||||||
+1
-1
Submodule proxy updated: b9bdf36638...c0e1aa666e
@@ -135,10 +135,12 @@ if [[ "${SKIP_SELF_UPDATE:-0}" != "1" && "${THETA_ENV_REEXECED:-0}" != "1" ]] \
|
|||||||
&& git rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1
|
&& git rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1
|
||||||
then
|
then
|
||||||
BEFORE_REV="$(git rev-parse HEAD)"
|
BEFORE_REV="$(git rev-parse HEAD)"
|
||||||
|
BEFORE_VER="$(git describe --tags "$BEFORE_REV" 2>/dev/null || echo "${BEFORE_REV:0:12}")"
|
||||||
if git pull --ff-only -q; then
|
if git pull --ff-only -q; then
|
||||||
AFTER_REV="$(git rev-parse HEAD)"
|
AFTER_REV="$(git rev-parse HEAD)"
|
||||||
if [[ "$BEFORE_REV" != "$AFTER_REV" ]]; then
|
if [[ "$BEFORE_REV" != "$AFTER_REV" ]]; then
|
||||||
info "Updated theta-env (${BEFORE_REV:0:12} -> ${AFTER_REV:0:12}) — restarting setup.sh with the new version..."
|
AFTER_VER="$(git describe --tags "$AFTER_REV" 2>/dev/null || echo "${AFTER_REV:0:12}")"
|
||||||
|
info "Updated theta-env (${BEFORE_VER} -> ${AFTER_VER}) — restarting setup.sh with the new version..."
|
||||||
THETA_ENV_REEXECED=1 exec "$0" "$@"
|
THETA_ENV_REEXECED=1 exec "$0" "$@"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -165,26 +167,32 @@ if [[ "${SKIP_SUBMODULE_UPDATE:-0}" != "1" ]]; then
|
|||||||
for sm in sso-manager-node proxy; do
|
for sm in sso-manager-node proxy; do
|
||||||
[[ -d "$sm" ]] || continue
|
[[ -d "$sm" ]] || continue
|
||||||
before_rev="$(git -C "$sm" rev-parse HEAD 2>/dev/null || true)"
|
before_rev="$(git -C "$sm" rev-parse HEAD 2>/dev/null || true)"
|
||||||
|
# Prefer the exact tag the submodule is currently pinned to; fall back
|
||||||
|
# to a short commit hash if it's on an untagged commit (shouldn't
|
||||||
|
# normally happen -- this repo only ever pins tagged releases).
|
||||||
|
before_tag="$(git -C "$sm" describe --tags --exact-match "$before_rev" 2>/dev/null || echo "${before_rev:0:12}")"
|
||||||
|
|
||||||
if ! git -C "$sm" fetch --tags -q 2>&1; then
|
if ! git -C "$sm" fetch --tags -q 2>&1; then
|
||||||
warn " ${sm}: could not fetch tags (offline?) — staying on the current pin."
|
warn " ${sm}: could not fetch tags (offline?) — staying on ${before_tag}."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
latest_tag="$(git -C "$sm" tag --list 'v*' --sort=-v:refname | head -n1)"
|
latest_tag="$(git -C "$sm" tag --list 'v*' --sort=-v:refname | head -n1)"
|
||||||
if [[ -z "$latest_tag" ]]; then
|
if [[ -z "$latest_tag" ]]; then
|
||||||
warn " ${sm}: no vX.Y.Z release tags found — staying on the current pin."
|
warn " ${sm}: no vX.Y.Z release tags found — staying on ${before_tag}."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! git -C "$sm" checkout -q "$latest_tag" 2>&1; then
|
if ! git -C "$sm" checkout -q "$latest_tag" 2>&1; then
|
||||||
warn " ${sm}: could not check out ${latest_tag} — staying on the current pin."
|
warn " ${sm}: could not check out ${latest_tag} — staying on ${before_tag}."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
after_rev="$(git -C "$sm" rev-parse HEAD 2>/dev/null || true)"
|
after_rev="$(git -C "$sm" rev-parse HEAD 2>/dev/null || true)"
|
||||||
if [[ "$before_rev" != "$after_rev" ]]; then
|
if [[ "$before_rev" != "$after_rev" ]]; then
|
||||||
info " ${sm}: updated to ${latest_tag} (${before_rev:0:12} -> ${after_rev:0:12})"
|
info " ${sm}: updated ${before_tag} -> ${latest_tag}"
|
||||||
|
else
|
||||||
|
info " ${sm}: already up to date (${latest_tag})"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
@@ -220,7 +228,7 @@ write_sso_secrets() {
|
|||||||
cat > "$CONFIG_DIR/sso-secrets.js" <<SSOEOF
|
cat > "$CONFIG_DIR/sso-secrets.js" <<SSOEOF
|
||||||
'use strict';
|
'use strict';
|
||||||
// Generated by setup.sh. Edit freely; re-run ./setup.sh to apply.
|
// Generated by setup.sh. Edit freely; re-run ./setup.sh to apply.
|
||||||
// The SSO app reads this via @simpleworkjs/conf (symlinked to conf/secrets.js).
|
// The SSO app reads this via @simpleworkjs/conf (CONF_SECRETS env var).
|
||||||
// The app ignores the extra stack/bootstrap/serviceAccountPass keys (read by
|
// The app ignores the extra stack/bootstrap/serviceAccountPass keys (read by
|
||||||
// the orchestrator). Back this file up off-host — it holds all SSO secrets.
|
// the orchestrator). Back this file up off-host — it holds all SSO secrets.
|
||||||
|
|
||||||
@@ -271,8 +279,8 @@ write_proxy_secrets() {
|
|||||||
local dn="$CFG_BASE_DN"
|
local dn="$CFG_BASE_DN"
|
||||||
cat > "$CONFIG_DIR/proxy-secrets.js" <<PROXYEOF
|
cat > "$CONFIG_DIR/proxy-secrets.js" <<PROXYEOF
|
||||||
'use strict';
|
'use strict';
|
||||||
// Generated by setup.sh. The proxy reads this via @simpleworkjs/conf (symlinked
|
// Generated by setup.sh. The proxy reads this via @simpleworkjs/conf (CONF_SECRETS
|
||||||
// to conf/secrets.js). clientId/clientSecret are filled in by the bootstrap
|
// env var). clientId/clientSecret are filled in by the bootstrap
|
||||||
// (run by ./setup.sh) — leave them as-is. ldap.bindPassword MUST equal
|
// (run by ./setup.sh) — leave them as-is. ldap.bindPassword MUST equal
|
||||||
// serviceAccountPass in sso-secrets.js (the proxy binds as that account).
|
// serviceAccountPass in sso-secrets.js (the proxy binds as that account).
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule sso-manager-node updated: 47a9f6c3ec...37f2ece172
Reference in New Issue
Block a user