diff --git a/CHANGELOG.md b/CHANGELOG.md index 974b913..8916384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`. ## [Unreleased] +## [1.1.16] - 2026-07-18 + +### Changed +- Public-release packaging: removed `"private": true` from `nodejs/package.json`, corrected the repository URL to `https://github.com/theta42/proxy.git`, and fixed the MIT `LICENSE` copyright line. +- Genericized committed defaults in `conf/base.js` and `conf/development.js`: LDAP now defaults to `ldap://localhost` with `dc=example,dc=com`, and OIDC endpoints default to `https://sso.example.com` instead of internal theta42 infrastructure. +- The bootstrap `proxyadmin2` account now gets a random, one-time password when `auth.localAdminPass` is unset, instead of the well-known default `proxyadmin2`. The password is printed to the log on first creation and can be made deterministic by setting `auth.localAdminPass` in the secrets file. + +### Fixed +- The global error handler no longer leaks `err.keys`, stack traces, or other internal details in JSON responses; only `name` and `message` are returned to clients. +- `DEPLOYMENT.md` and `docs/docker.md` now correctly describe the `CONF_SECRETS` env-var mechanism instead of the old symlink behavior. + ## [1.1.15] - 2026-07-18 ### Changed diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 496ec84..3a2575a 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -75,11 +75,12 @@ $EDITOR config/proxy-secrets.js # set oidc.clientId/clientSecret, ldap.bindP docker compose up -d --build ``` -`docker-entrypoint.sh` symlinks `/config/proxy-secrets.js` → `/app/conf/secrets.js` -so `@simpleworkjs/conf` reads it. No `app_*` env is passed — `app_*` env would -override the file (env beats secrets.js in `@simpleworkjs/conf`), so the file is -kept authoritative. `RESOLVER` / `REAL_IP_FROM` / `NODE_ENV` / `NODE_PORT` are -OpenResty-runtime / process env, not `app_*` config, so they stay in the compose. +`docker-entrypoint.sh` sets `CONF_SECRETS=/config/proxy-secrets.js` so +`@simpleworkjs/conf` reads it directly. No `app_*` env is passed — `app_*` env +would override the file (env beats secrets.js in `@simpleworkjs/conf`), so the +file is kept authoritative. `RESOLVER` / `REAL_IP_FROM` / `NODE_ENV` / +`NODE_PORT` are OpenResty-runtime / process env, not `app_*` config, so they +stay in the compose. > Running the unified `theta-env` stack? Its `setup.sh` generates > `./config/proxy-secrets.js` (+ `./config/sso-secrets.js`) for you and diff --git a/LICENSE b/LICENSE index 3f28a5f..134193a 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Copyright (c) 2026 theta42 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/docs/docker.md b/docs/docker.md index 7a60bc5..83b6a3c 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -39,11 +39,11 @@ which deep-merges, in order: 3. `conf/secrets.js` (gitignored) 4. **`app_*` environment variables** — the highest-precedence layer -The bundled `docker-compose.yml` mount `./config/proxy-secrets.js` at `/config`, -and `docker-entrypoint.sh` symlinks it into `/app/conf/secrets.js` so the app -reads the OIDC + LDAP + auth wiring from the file. **No `app_*` env is passed** — -`app_*` env beats `secrets.js`, so the file is authoritative only if the matching -`app_*` env is absent. See `secrets.js.example` for the shape. +The bundled `docker-compose.yml` mounts `./config/proxy-secrets.js` at `/config`, +and `docker-entrypoint.sh` sets `CONF_SECRETS=/config/proxy-secrets.js` so the +app reads the OIDC + LDAP + auth wiring from the file. **No `app_*` env is +passed** — `app_*` env beats `secrets.js`, so the file is authoritative only if +the matching `app_*` env is absent. See `secrets.js.example` for the shape. Any env var starting with `app_` overrides the merged config; the rest of the name splits on **double-underscore** (`__`) into a nested path. Values are diff --git a/nodejs/app.js b/nodejs/app.js index 2040bd9..547ff43 100755 --- a/nodejs/app.js +++ b/nodejs/app.js @@ -100,15 +100,21 @@ app.use(async function(req, res, next) { // Error handler. This is where `next()` will go on error app.use(async function(err, req, res, next) { - try{ - console.error(err.status || res.status, err.name, req.method, req.url); + try{ + const status = err.status || 500; + console.error(status, err.name, req.method, req.url); console.error(err.message); - console.error(err.stack); + if (err.stack) console.error(err.stack); console.error('========================================='); - res.status(err.status || 500); - res.json({name: err.name, message: err.message, keys: err.keys}); + res.status(status); + // Only expose safe, non-internal fields to the client. + const body = { name: err.name, message: err.message }; + res.json(body); }catch(error){ - console.log('error in the catch all error fn....', error); + console.error('error in the catch-all error handler', error); + if (!res.headersSent) { + res.status(500).json({ name: 'Error', message: 'Internal server error' }); + } } }); diff --git a/nodejs/conf/base.js b/nodejs/conf/base.js index cebbce7..cd92e08 100644 --- a/nodejs/conf/base.js +++ b/nodejs/conf/base.js @@ -6,10 +6,10 @@ module.exports = { logo: "/static/img/theta42.svg", // shown in the nav; point at your own file under public/ (or an absolute URL) to white-label userModel: 'redis', // pam, redis, ldap ldap: { - url: 'ldap://192.168.1.55:389', - bindDN: 'cn=ldapclient service,ou=people,dc=theta42,dc=com', + url: 'ldap://localhost', + bindDN: 'cn=ldapclient service,ou=people,dc=example,dc=com', bindPassword: '__IN SRECREST FILE__', - searchBase: 'ou=people,dc=theta42,dc=com', + searchBase: 'ou=people,dc=example,dc=com', userFilter: '(objectClass=inetOrgPerson)', userNameAttribute: 'uid' }, @@ -29,11 +29,11 @@ module.exports = { // redirectUri MUST be registered on the SSO client and match exactly. oidc: { enabled: true, - issuer: 'https://sso.theta42.com', - authorizationEndpoint: 'https://sso.theta42.com/oauth/authorize', - tokenEndpoint: 'https://sso.theta42.com/oauth/token', - userinfoEndpoint: 'https://sso.theta42.com/oauth/userinfo', - endSessionEndpoint: 'https://sso.theta42.com/oauth/logout', + issuer: 'https://sso.example.com', + authorizationEndpoint: 'https://sso.example.com/oauth/authorize', + tokenEndpoint: 'https://sso.example.com/oauth/token', + userinfoEndpoint: 'https://sso.example.com/oauth/userinfo', + endSessionEndpoint: 'https://sso.example.com/oauth/logout', clientId: '__SET_ME__', // Where the SSO sends the user back. Must be an absolute URL reachable // by the browser and registered on the SSO client. diff --git a/nodejs/conf/development.js b/nodejs/conf/development.js index afb275b..9902829 100644 --- a/nodejs/conf/development.js +++ b/nodejs/conf/development.js @@ -4,10 +4,10 @@ module.exports = { userModel: 'redis', // pam, redis, ldap ldap: { - url: 'ldap://192.168.1.55:389', - bindDN: 'cn=ldapclient service,ou=people,dc=theta42,dc=com', + url: 'ldap://localhost', + bindDN: 'cn=ldapclient service,ou=people,dc=example,dc=com', bindPassword: '__IN SRECREST FILE__', - searchBase: 'ou=people,dc=theta42,dc=com', + searchBase: 'ou=people,dc=example,dc=com', userFilter: '(objectClass=inetOrgPerson)', userNameAttribute: 'uid' }, diff --git a/nodejs/models/user_redis.js b/nodejs/models/user_redis.js index 90d64b8..3f5149f 100644 --- a/nodejs/models/user_redis.js +++ b/nodejs/models/user_redis.js @@ -90,10 +90,19 @@ User.register(); var defaultUser = 'proxyadmin2' // Optional: an orchestrator (e.g. theta-env's setup.sh) can set // auth.localAdminPass in proxy-secrets.js to a generated password so this - // bootstrap account isn't left at the well-known default (username == - // password == "proxyadmin2"). Only used on first creation -- once the - // account exists this is never read again, so it's safe to leave set. - var defaultPass = (conf.auth && conf.auth.localAdminPass) || defaultUser; + // bootstrap account isn't left at a well-known default. Only used on first + // creation -- once the account exists this is never read again, so it's + // safe to leave set. If unset, a random password is generated and printed + // once; save it from the log or set auth.localAdminPass explicitly. + var defaultPass = (conf.auth && conf.auth.localAdminPass); + if (!defaultPass) { + defaultPass = crypto.randomBytes(16).toString('hex'); + console.warn(`====================================================================`); + console.warn(`Bootstrap admin "${defaultUser}" created with random password:`); + console.warn(`${defaultPass}`); + console.warn(`Set auth.localAdminPass in your secrets file to make this deterministic.`); + console.warn(`====================================================================`); + } try{ let user = await User.get(defaultUser); }catch(error){ @@ -103,7 +112,7 @@ User.register(); password: defaultPass, created_by: defaultUser }); - console.log(defaultUser, 'created', user); + console.log(defaultUser, 'created'); }catch(error){ console.error(error) } diff --git a/nodejs/package.json b/nodejs/package.json index 1776872..1cc7cf1 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,7 +1,6 @@ { "name": "proxy-api", - "version": "1.1.15", - "private": true, + "version": "1.1.16", "author": [ { "name": "William Mantly", @@ -48,7 +47,7 @@ "license": "MIT", "repository": { "type": "git", - "url": "https://git.theta42.com/wmantly/proxy.git" + "url": "https://github.com/theta42/proxy.git" }, "devDependencies": { "nodemon": "^3.1.11"