From 28016376adba7c8e491899061152459972d7e3e8 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Sun, 19 Jul 2026 01:14:03 -0400 Subject: [PATCH] feat: propagate CFG_LDAPS_HOST through setup and document LDAPS networking (#71) Pass optional CFG_LDAPS_HOST from setup.env through setup.sh into the generated ./config/sso-secrets.js as ldap.ldapsHost. This lets operators advertise an internal-only LDAPS hostname (e.g. ldap.internal.example.com or sso-manager) on the SSO /integrations page instead of the public OAuth issuer, avoiding a public 636 port forward. - setup.env.example: add CFG_LDAPS_HOST - setup.sh: read/forward CFG_LDAPS_HOST into sso-secrets.js - config.example/sso-secrets.js.example: document ldapsHost/ldapsPort - .env.example: add LDAPS_HOST for legacy .env migrations - docker-compose.yml: comment warning against public 636 forwarding - README.md: explain CFG_LDAPS_HOST recommendation - CHANGELOG.md + bump version to 1.1.19 Co-authored-by: Claude --- .env.example | 8 +++++++- CHANGELOG.md | 25 +++++++++++++++++++++++++ README.md | 4 ++++ config.example/sso-secrets.js.example | 3 +++ docker-compose.yml | 2 ++ setup.env.example | 6 ++++++ setup.sh | 6 ++++++ 7 files changed, 53 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index e7fda17..d08e31f 100644 --- a/.env.example +++ b/.env.example @@ -76,4 +76,10 @@ MGMT_BIND=0.0.0.0 # Defaults to LDAP_DOMAIN. Set to the hostname the proxy connects via # (sso-manager inside the docker net uses the service name, which is in the # cert's SAN, so the default is usually fine). -LDAP_CERT_CN= \ No newline at end of file +LDAP_CERT_CN= + +# ── Optional: LDAPS hostname shown on the SSO /integrations page ──────────────── +# Leave blank to derive from the public SSO host (SSO_HOST). Set an internal-only +# name like 'ldap.internal.example.com' or 'sso-manager' so direct-LDAP clients +# don't need a public 636 port forward. See docs/ldap.md for network layouts. +LDAPS_HOST= \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 61fdb22..64af26a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,31 @@ for what changed inside the apps it composes. ## [Unreleased] +## [1.1.19] - 2026-07-18 + +### Bumped +- sso-manager-node -> [v1.1.17](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.17) + +sso-manager-node: + +### Added +- `conf.ldap.ldapsHost` and `conf.ldap.ldapsPort` config options for advertising a separate, internal-only LDAPS hostname on the `/integrations` page. Falls back to the public OAuth issuer host when unset. +- Contextual help panel on `/integrations` → LDAP explaining why LDAPS needs a hostname, why port 636 should not be forwarded publicly, and the recommended internal-DNS / Docker-internal alternatives. +- Tests for the `/integrations` route's LDAPS URL derivation and `ldapsHost` override. + +### Changed +- `nodejs/package.json` / `package-lock.json` version bumped to `1.1.17`. +- `routes/index.js` now derives the displayed LDAPS URL from `conf.ldap.ldapsHost`/`ldapsPort` with fallback to the OAuth issuer host. +- `docs/configuration.md`, `docs/ldap.md`, `DEPLOYMENT.md`, and `secrets.js.example` document the new `ldapsHost`/`ldapsPort` options and recommended network layouts. + +### theta-env own changes +- `setup.env.example` adds optional `CFG_LDAPS_HOST` for the internal LDAPS hostname. +- `setup.sh` passes `CFG_LDAPS_HOST` into the generated `./config/sso-secrets.js` as `ldap.ldapsHost`. +- `config.example/sso-secrets.js.example` documents `ldap.ldapsHost` / `ldap.ldapsPort`. +- `.env.example` adds `LDAPS_HOST` for legacy `.env` migrations. +- `docker-compose.yml` comments warn against forwarding 636 to the public internet. +- `README.md` explains the `CFG_LDAPS_HOST` recommendation in the port-forwarding section. + ## [1.1.18] - 2026-07-18 ### Bumped diff --git a/README.md b/README.md index 80a792b..25c315d 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,10 @@ Optional extra ports (only if you need them): - **636** (LDAPS) — only if a legacy app on another machine binds to LDAP directly over the network. The proxy itself reaches LDAP over the internal Docker network, so you do **not** need to expose 636 for the stack to work. + **Do not forward 636 to the public internet.** If you need LAN clients to bind + LDAP, set `CFG_LDAPS_HOST=ldap.internal.example.com` (or `sso-manager` for + same-host Docker clients) in `setup.env` and use an internal DNS record / cert + SAN. The default shows the public SSO hostname, which implies a public route. ### 4. Docker + Docker Compose diff --git a/config.example/sso-secrets.js.example b/config.example/sso-secrets.js.example index 03645d0..a39e288 100644 --- a/config.example/sso-secrets.js.example +++ b/config.example/sso-secrets.js.example @@ -17,6 +17,9 @@ module.exports = { bindPassword: 'CHANGE-ME', // slapd root + app bind password userBase: 'ou=people,dc=example,dc=com', groupBase: 'ou=groups,dc=example,dc=com', + // ldapsHost: 'ldap.internal.example.com', // optional: internal-only hostname + // shown on /integrations for direct LDAPS binds. Empty -> derive from issuer. + // ldapsPort: 636, }, smtp: { // optional; leave host '' to skip host: '', port: 587, secure: false, diff --git a/docker-compose.yml b/docker-compose.yml index aebf56e..de7a5a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,6 +46,8 @@ services: - "${SSO_BIND:-0.0.0.0}:${SSO_PORT:-3001}:3001" # LDAPS for EXTERNAL direct-LDAP clients (legacy apps). The proxy itself # reaches LDAPS over theta-net (sso-manager:636) without this host mapping. + # Prefer an internal-only hostname (set CFG_LDAPS_HOST in setup.env / ldapsHost + # in sso-secrets.js) and do NOT forward 636 to the public internet. - "${LDAPS_PORT:-636}:636" # Plain LDAP (389) is NOT mapped — direct-LDAP clients should use LDAPS. environment: diff --git a/setup.env.example b/setup.env.example index ba6d5bc..8aff21a 100644 --- a/setup.env.example +++ b/setup.env.example @@ -36,6 +36,12 @@ CFG_DOMAIN=example.com #CFG_ADMIN_UID=admin # initial SSO admin username #CFG_ADMIN_EMAIL=admin@proxy.example.com # defaults to admin@ #CFG_LDAP_CERT_CN= # LDAP TLS cert CN; empty -> defaults to the domain +# +# Hostname advertised on the SSO /integrations page for direct LDAPS binds. +# Leave blank to derive it from the public SSO host (same as oauth.issuer). +# Recommended: set an internal-only name like 'ldap.internal.example.com' or +# 'sso-manager' so clients don't need a public 636 port forward. See docs. +#CFG_LDAPS_HOST= # Optional SMTP (outbound email from the SSO app). Leave blank to disable: #CFG_SMTP_HOST=smtp.example.com diff --git a/setup.sh b/setup.sh index bb7e58e..800a755 100755 --- a/setup.sh +++ b/setup.sh @@ -240,6 +240,8 @@ module.exports = { bindPassword: $(js_str "$CFG_LDAP_ADMIN_PASS"), userBase: $(js_str "ou=people,${dn}"), groupBase: $(js_str "ou=groups,${dn}"), + ldapsHost: $(js_str "${CFG_LDAPS_HOST:-}"), + ldapsPort: 636, }, smtp: { host: $(js_str "${CFG_SMTP_HOST:-}"), @@ -355,6 +357,7 @@ ensure_config() { CFG_ADMIN_UID="${CFG_ADMIN_UID:-}" CFG_ADMIN_EMAIL="${CFG_ADMIN_EMAIL:-}" CFG_LDAP_CERT_CN="${CFG_LDAP_CERT_CN:-}" + CFG_LDAPS_HOST="${CFG_LDAPS_HOST:-}" CFG_CLIENT_ID="${CFG_CLIENT_ID:-}" CFG_CLIENT_SECRET="${CFG_CLIENT_SECRET:-}" CFG_LDAP_ADMIN_PASS="${CFG_LDAP_ADMIN_PASS:-}" @@ -383,6 +386,8 @@ ensure_config() { CFG_ADMIN_PASS="${BOOTSTRAP_ADMIN_PASS:-$CFG_ADMIN_PASS}" CFG_SVC_PASS="${LDAP_SERVICE_PASS:-$CFG_SVC_PASS}" CFG_LDAP_CERT_CN="${LDAP_CERT_CN:-$CFG_LDAP_CERT_CN}" + # .env has no legacy LDAPS_HOST key; this stays as set in setup.env/env. + CFG_LDAPS_HOST="${CFG_LDAPS_HOST:-}" CFG_SMTP_HOST="${SMTP_HOST:-${CFG_SMTP_HOST:-}}" CFG_SMTP_PORT="${SMTP_PORT:-${CFG_SMTP_PORT:-}}" CFG_SMTP_USER="${SMTP_USER:-${CFG_SMTP_USER:-}}" @@ -416,6 +421,7 @@ ensure_config() { CFG_ADMIN_UID="${CFG_ADMIN_UID:-admin}" CFG_ADMIN_EMAIL="${CFG_ADMIN_EMAIL:-admin@$CFG_PROXY_HOST}" CFG_LDAP_CERT_CN="${CFG_LDAP_CERT_CN:-}" + CFG_LDAPS_HOST="${CFG_LDAPS_HOST:-}" CFG_CLIENT_ID="${CFG_CLIENT_ID:-}" CFG_CLIENT_SECRET="${CFG_CLIENT_SECRET:-}" # Random secrets (generated fresh unless sourced/migrated above). These do