diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d3d92d..4c4a7a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this project are documented here. Format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`. +## [1.14.0] - 2026-08-01 + +### Changed +- **Secrets now load from OpenBao at boot** via + [@simpleworkjs/bao-conf](https://simpleworkjs.github.io/bao-conf/), which + deep-merges `secret/jump-host/conf` over the file-loaded config. The jump + host authenticates to OpenBao with a scoped `VAULT_TOKEN` (policy + `jump-host` — read-only on its own path), never the root token. Because the + OIDC `clientSecret` is captured at require time inside `createOidcClient` + (during `require('../models')`), `bin/www` now runs `bao-conf.init()` + **before** `require('../models')`. Fail-soft: if OpenBao is unreachable, + boot continues from `CONF_SECRETS`. The `config/jump-secrets.js` file is now + an operator-edit seed artifact (gitignored); OpenBao is authoritative. See + theta-env's [Secrets docs](https://theta42.github.io/theta-env/secrets/). +- Bumped package version to track the release tag. + ## [1.11.0] - 2026-07-30 ### Added diff --git a/README.md b/README.md index 9bb2c02..77298a0 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,22 @@ Config layers via [@simpleworkjs/conf](https://www.npmjs.com/package/@simplework `conf/base.js` < `conf/.js` < the `CONF_SECRETS` file < `app_*` env. See `secrets.js.example` for every key. +## Secrets + +At boot, [@simpleworkjs/bao-conf](https://simpleworkjs.github.io/bao-conf/) +deep-merges `secret/jump-host/conf` from **OpenBao** over the file-loaded +config. The jump host's OIDC `clientSecret` is captured at require time +(inside `createOidcClient` during `require('../models')`), so `bin/www` runs +`bao-conf.init()` **before** `require('../models')`. Fail-soft: if OpenBao is +unreachable, boot continues from `CONF_SECRETS`. The jump host authenticates to +OpenBao with the scoped `VAULT_TOKEN` (env, policy `jump-host` — read only +`secret/jump-host/conf`), never the root token. + +The `config/jump-secrets.js` file is an operator-edit seed artifact +(gitignored); the bootstrap writes the generated API token + OAuth client +into OpenBao, which is authoritative. For the full architecture see +theta-env's **[Secrets docs](https://theta42.github.io/theta-env/secrets/)**. + ## Development ``` diff --git a/nodejs/bin/www b/nodejs/bin/www index f37f312..2036ce8 100644 --- a/nodejs/bin/www +++ b/nodejs/bin/www @@ -9,32 +9,43 @@ const http = require('http'); const conf = require('@simpleworkjs/conf'); const { Server } = require('socket.io'); -require('../models'); +// @simpleworkjs/conf loads ./config/jump-secrets.js synchronously, then +// @simpleworkjs/bao-conf deep-merges secret/jump-host/conf from OpenBao over +// it. The OIDC clientSecret is captured at require time inside models (via +// createOidcClient), so the fetch MUST resolve before require('../models'). +// Fail-soft: if OpenBao is unreachable, init() leaves conf as the file-loaded +// fallback and boot continues from ./config/jump-secrets.js. +require('@simpleworkjs/bao-conf').init({ path: 'jump-host', conf }).then(() => { + require('../models'); -const app = require('../app'); -const middleware = require('../middleware/auth'); -const sshServer = require('../services/ssh_server'); + const app = require('../app'); + const middleware = require('../middleware/auth'); + const sshServer = require('../services/ssh_server'); -const webPort = (conf.web && conf.web.port) || 3002; -const server = http.createServer(app); + const webPort = (conf.web && conf.web.port) || 3002; + const server = http.createServer(app); -// Socket.IO — the client framework (app-base.js) opens an authenticated socket. -// We don't push anything yet, but serving /socket.io keeps the shared front-end -// working exactly as it does in the sibling apps. -const io = new Server(server); -io.use(middleware.authIO); -app.io = io; + // Socket.IO — the client framework (app-base.js) opens an authenticated socket. + // We don't push anything yet, but serving /socket.io keeps the shared front-end + // working exactly as it does in the sibling apps. + const io = new Server(server); + io.use(middleware.authIO); + app.io = io; -server.listen(webPort, () => { - console.log(`[web] jump-host UI/API on :${server.address().port}`); -}); + server.listen(webPort, () => { + console.log(`[web] jump-host UI/API on :${server.address().port}`); + }); -sshServer.start(); + sshServer.start(); -function shutdown() { - console.log('[jump-host] shutting down'); - server.close(); - process.exit(0); -} -process.on('SIGTERM', shutdown); -process.on('SIGINT', shutdown); + function shutdown() { + console.log('[jump-host] shutting down'); + server.close(); + process.exit(0); + } + process.on('SIGTERM', shutdown); + process.on('SIGINT', shutdown); +}).catch(err => { + console.error('boot failed:', err); + process.exit(1); +}); \ No newline at end of file diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index dad1fcc..d8fe784 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,16 +1,17 @@ { "name": "t42-jump-host", - "version": "1.9.0", + "version": "1.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "t42-jump-host", - "version": "1.9.0", + "version": "1.11.0", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-free": "^7.3.0", "@simpleworkjs/app-stack": "^1.0.0", + "@simpleworkjs/bao-conf": "^1.0.0", "@simpleworkjs/conf": "^1.2.0", "@simpleworkjs/directory-schema": "^1.0.0", "@simpleworkjs/frontend": "^0.2.6", @@ -156,6 +157,18 @@ "node": ">=18.0.0" } }, + "node_modules/@simpleworkjs/bao-conf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@simpleworkjs/bao-conf/-/bao-conf-1.0.0.tgz", + "integrity": "sha512-HxB2ohFuDKbwTfNh5dXCot0dd6qoP+3Ebz1xKH0eOhKNVNMjHU1p7XZv2VTe+VnHn+DV22Eh8lAgWxnX/pkXUw==", + "license": "MIT", + "dependencies": { + "extend": "^3.0.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@simpleworkjs/conf": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@simpleworkjs/conf/-/conf-1.2.0.tgz", diff --git a/nodejs/package.json b/nodejs/package.json index 6f57a1b..f4ad003 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "t42-jump-host", - "version": "1.11.0", + "version": "1.14.0", "description": "SSH jump host for the theta42 stack — LDAP-authenticated, directory-driven host bridging with audit and metrics", "author": [ { @@ -21,6 +21,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^7.3.0", "@simpleworkjs/app-stack": "^1.0.0", + "@simpleworkjs/bao-conf": "^1.0.0", "@simpleworkjs/conf": "^1.2.0", "@simpleworkjs/directory-schema": "^1.0.0", "@simpleworkjs/frontend": "^0.2.6",