setup.sh: register SSO + proxy hostnames as Host records in the proxy

The proxy routes every hostname it serves purely off a Host record
(ops/nginx_conf/proxy.conf has no default/self route — targetinfo.lua
does a Redis lookup per request, full stop). Nothing created these for
the SSO's own UI or the proxy's own management UI, so on a fresh
install https://<SSO_HOST> and https://<PROXY_HOST> both 404 despite
setup.sh's summary claiming they're "fronted by the proxy under TLS".

Add a step after the proxy is healthy that runs a short script inside
the proxy container calling its Host model directly (no HTTP API call,
since no authenticated session exists yet at this point in the run):
- <SSO_HOST> -> sso-manager:3001 (the Docker service)
- <PROXY_HOST> -> 127.0.0.1:3000 (the proxy's own management app)

Both created with sso_enabled: false — each app already gates its own
login, and SSO-gating the SSO's own login page would be circular.
Idempotent: skips a host that already exists.
This commit is contained in:
2026-07-14 00:53:17 -04:00
parent b43c3d0d1d
commit 425d92a137
4 changed files with 75 additions and 5 deletions
+6 -1
View File
@@ -151,7 +151,12 @@ operator-owned and `setup.env` is ignored.
- registers the proxy as an OIDC client in the SSO and **writes the generated
client id + secret back into `./config/proxy-secrets.js`**.
4. Builds + starts the proxy container, waits for it to be healthy.
5. Prints your first admin login + the public URLs.
5. Registers `<SSO_HOST>` and `<PROXY_HOST>` as Host records in the proxy
(directly via its Host model, inside the proxy container) — the proxy
routes every hostname it serves off a Host record, including its own
management UI and the SSO's UI, so without this step those two URLs
would 404. Idempotent; skips a host that already exists.
6. Prints your first admin login + the public URLs.
### Configuration — `./config/` (no `.env` files)
+12
View File
@@ -104,6 +104,18 @@ inputs from the bind-mounted `./config/sso-secrets.js` + `./config/proxy-secrets
6. **Build + start the proxy**, wait for `/health`. The proxy entrypoint symlinks
`./config/proxy-secrets.js` to `/app/conf/secrets.js`, so `@simpleworkjs/conf`
(≥1.1.0) reads the OAuth creds + LDAP bind creds from the file.
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
Host model directly (`Host.create({host, ip, targetPort, ...})`), rather
than the proxy's own HTTP API, since no authenticated session exists yet at
this point in the run. The proxy routes every hostname purely off a Host
record (`ops/nginx_conf/proxy.conf` has no default/self route), so without
this step neither URL resolves to anything. `<SSO_HOST>` targets
`sso-manager:3001` (the Docker service), `<PROXY_HOST>` targets
`127.0.0.1:3000` (the proxy's own management app, same container). Both
are created with `sso_enabled: false` — each app already gates its own
login, and SSO-gating the SSO's own login page would be circular. Skips a
host that already exists, so re-running `setup.sh` is a no-op here.
`setup.sh` then prints the first-admin login + the public URLs.
+4 -1
View File
@@ -89,7 +89,10 @@ What happens:
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.
5. Registers `<SSO_HOST>` and `<PROXY_HOST>` as Host records in the proxy —
every hostname the proxy serves, including its own UI and the SSO's,
needs one of these or it 404s. Idempotent.
6. Prints your first-admin login + the public URLs.
The first run builds two Docker images (a few minutes). Subsequent runs are
fast.
+53 -3
View File
@@ -12,7 +12,8 @@
# state before rebuild, (re)starts the SSO Manager, runs the bootstrap (which
# converges the LDAP service account / first admin / OAuth client to the ./config
# values and writes the generated OAuth client creds into proxy-secrets.js),
# then starts the proxy.
# then starts the proxy and registers the SSO's + proxy's own hostnames as
# Host records in it (otherwise the proxy has no route for either).
#
# What it does, in order:
# 1. Update the git submodules to the latest of their tracked remote branch
@@ -35,7 +36,11 @@
# writes the OAuth client creds into ./config/proxy-secrets.js; prints
# CLIENT_ID / CLIENT_SECRET / ALREADY_CONFIGURED on stdout.
# 6. docker compose up -d --build proxy; wait for /health.
# 7. Print the first-admin login + the public URLs.
# 7. Register <SSO_HOST> and <PROXY_HOST> as Host records in the proxy (via
# `docker compose exec proxy node`, calling the proxy's Host model
# directly) so the proxy actually routes those hostnames somewhere —
# nothing else creates them. Idempotent; skips a host that already exists.
# 8. Print the first-admin login + the public URLs.
#
# Requires: git, docker + docker compose (v1 standalone or v2 plugin).
@@ -548,7 +553,52 @@ for i in $(seq 1 60); do
sleep 2
done
# ── 7. Summary ───────────────────────────────────────────────────────────────
# ── 7. Register the SSO + proxy UIs as Host records in the proxy ──────────────
# The proxy routes EVERY hostname it serves — including its own management UI
# and the SSO's UI — off a Host record (ops/nginx_conf/proxy.conf has no
# default/self route; targetinfo.lua does a lookup for every request, full
# stop). Nothing else creates these two, so without this step https://<SSO_HOST>
# and https://<PROXY_HOST> 404 on first run. sso_enabled is left false on both:
# each app gates its own login already, and SSO-gating the SSO's own login page
# would be circular. Idempotent — skips a host that already exists.
info "Registering ${SSO_HOST} and ${PROXY_HOST} with the proxy..."
HOSTS_OUT=$("${COMPOSE[@]}" exec -T proxy node <<NODEEOF
const {Host} = require('/app/models').models;
async function ensureHost(host, ip, targetPort) {
try {
await Host.get(host);
console.log('SKIP ' + host + ' (already exists)');
} catch (error) {
if (error.name !== 'EntryNotFound') throw error;
await Host.create({
host: host,
ip: ip,
targetPort: targetPort,
forcessl: true,
targetssl: false,
sso_enabled: false,
created_by: 'setup.sh',
});
console.log('CREATED ' + host + ' -> ' + ip + ':' + targetPort);
}
}
(async () => {
try {
await ensureHost($(js_str "$SSO_HOST"), 'sso-manager', 3001);
await ensureHost($(js_str "$PROXY_HOST"), '127.0.0.1', 3000);
process.exit(0);
} catch (error) {
console.error('ERROR', error.message);
process.exit(1);
}
})();
NODEEOF
) || die "Registering hosts with the proxy failed:\n${HOSTS_OUT}"
echo "$HOSTS_OUT" | sed 's/^/[setup] /'
# ── 8. Summary ───────────────────────────────────────────────────────────────
echo
info "\033[1;32mDone. Your SSO + proxy stack is up.\033[0m"
echo