Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e748bfa66 | |||
| a78db906e8 |
@@ -1,3 +1,18 @@
|
|||||||
|
# v1.30.2
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Outbound mail (test email, invites, password resets, OTP-by-email, notifications) could be rejected by the SMTP relay with `554 5.7.1 ... Sender is not same as SMTP authenticate username`.** Many authenticated relays require the `From` address to match the authenticated account or they refuse the send outright. `models/email.js` fell back to a hardcoded `noreply@theta42.com` when `smtp.from` wasn't set, which no relay ever authorized this account to send as. It now falls back to `smtp.user` first — the address the account can actually prove it owns — before the hardcoded placeholder.
|
||||||
|
- **Catalog page card titles read icon-then-name.** Swapped to name-then-icon so the resource name leads.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- `docs/configuration.md` didn't mention that OpenBao + the live Configuration UI sit above the four file/env config layers and win the merge — added.
|
||||||
|
- `docs/plugins.md` listed 3 of 4 discovery plugin types (missing `docker`) and didn't mention the `messaging` plugin category (`twilio`, `webhook`) at all — added both.
|
||||||
|
- `docs/vault.md` had no navigation (no frontmatter, no back-link, unreachable from the docs index) and described OpenBao as running in dev mode with API access via the root token — both wrong for a real deployment. Fixed navigation and corrected to describe the actual production setup (unsealed OpenBao, server-side scoped-token injection, personal API tokens for programmatic access).
|
||||||
|
- `docs/discovery.md` was unreachable from the docs index and missing its back-link — both fixed.
|
||||||
|
- `README.md`'s required-groups list was missing `app_sso_directory_admin` (gates Directory/Plugins/Agent admin).
|
||||||
|
|
||||||
# v1.30.1
|
# v1.30.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -200,7 +200,8 @@ If you are pointing the app at your own existing LDAP server, see
|
|||||||
`pw-sha2`, `ppolicy`, `memberof`, and `refint` modules plus a small custom
|
`pw-sha2`, `ppolicy`, `memberof`, and `refint` modules plus a small custom
|
||||||
schema. The bundled Docker image and `install.sh` set all of that up for you.
|
schema. The bundled Docker image and `install.sh` set all of that up for you.
|
||||||
Required groups: `app_sso_admin` (full admin), `app_sso_oauth_admin` (manage
|
Required groups: `app_sso_admin` (full admin), `app_sso_oauth_admin` (manage
|
||||||
OAuth clients only), `app_sso_invite` (invitation management) — see
|
OAuth clients only), `app_sso_invite` (invitation management),
|
||||||
|
`app_sso_directory_admin` (Directory/Plugins/Agent admin) — see
|
||||||
DEPLOYMENT.md for the full setup.
|
DEPLOYMENT.md for the full setup.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|||||||
@@ -16,13 +16,27 @@ deep-merges, in order (later wins):
|
|||||||
`localhost`, `SSO Manager`).
|
`localhost`, `SSO Manager`).
|
||||||
2. `conf/<NODE_ENV>.js` — optional, environment-specific.
|
2. `conf/<NODE_ENV>.js` — optional, environment-specific.
|
||||||
3. `conf/secrets.js` — gitignored; secrets + per-deployment values.
|
3. `conf/secrets.js` — gitignored; secrets + per-deployment values.
|
||||||
4. **`app_*` environment variables** — the highest-precedence layer.
|
4. **`app_*` environment variables** — the highest-precedence layer among these
|
||||||
|
four.
|
||||||
|
|
||||||
Any env var whose name starts with `app_` overrides the merged config. The rest
|
Any env var whose name starts with `app_` overrides the merged config. The rest
|
||||||
of the name splits on **double-underscore** (`__`) into a nested path. Values are
|
of the name splits on **double-underscore** (`__`) into a nested path. Values are
|
||||||
`JSON.parse`-coerced when possible (numbers, booleans, null, JSON) and kept as
|
`JSON.parse`-coerced when possible (numbers, booleans, null, JSON) and kept as
|
||||||
raw strings otherwise.
|
raw strings otherwise.
|
||||||
|
|
||||||
|
### A fifth, higher-precedence layer: OpenBao + the Configuration UI
|
||||||
|
|
||||||
|
In a theta-suite deployment, `@simpleworkjs/bao-conf`'s `init()` deep-merges
|
||||||
|
`secret/sso-manager/conf` (from OpenBao) over the four layers above at boot —
|
||||||
|
this is the layer `setup.sh`/theta-suite actually manages, and it wins over
|
||||||
|
everything else here. On top of that, the admin **Configuration** page in the
|
||||||
|
UI writes straight to `secret/sso-manager/conf` (via `routes/api_conf.js`)
|
||||||
|
and applies the change to the live `conf` object immediately
|
||||||
|
(`applyToLiveConf`) — no restart, and it bypasses `conf/secrets.js` entirely.
|
||||||
|
If a value isn't behaving the way `conf/secrets.js` says it should, check the
|
||||||
|
Configuration UI / OpenBao before assuming a file edit didn't take — it's
|
||||||
|
almost certainly OpenBao (or a live UI edit) winning the merge.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
| Env var | Sets | Type |
|
| Env var | Sets | Type |
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ nav_order: 6
|
|||||||
|
|
||||||
# Discovery & Inventory
|
# Discovery & Inventory
|
||||||
|
|
||||||
|
[← Back to Home](index.html)
|
||||||
|
|
||||||
The Directory holds two different kinds of thing, and the distinction matters
|
The Directory holds two different kinds of thing, and the distinction matters
|
||||||
for every consumer of the directory:
|
for every consumer of the directory:
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 332 KiB |
|
Before Width: | Height: | Size: 392 KiB After Width: | Height: | Size: 503 KiB |
|
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 320 KiB |
@@ -60,7 +60,10 @@ backend, that's the niche.
|
|||||||
run the pieces separately via `app_*` env config.
|
run the pieces separately via `app_*` env config.
|
||||||
- **Geo-Location Scaling** — built-in support for N-Way Multi-Master OpenLDAP [replication](replication.html) across physical sites.
|
- **Geo-Location Scaling** — built-in support for N-Way Multi-Master OpenLDAP [replication](replication.html) across physical sites.
|
||||||
- **[Directory & Inventory](directory.html)** — map sites, hosts, and services as a graph with rich metadata (IP/MAC, OS/kernel, ports, git repos), auto-provisioned access groups, and automatic registration from theta-env and ldap-client. Drives directory-aware tools like the [SSH jump host](https://theta42.github.io/jump-host/).
|
- **[Directory & Inventory](directory.html)** — map sites, hosts, and services as a graph with rich metadata (IP/MAC, OS/kernel, ports, git repos), auto-provisioned access groups, and automatic registration from theta-env and ldap-client. Drives directory-aware tools like the [SSH jump host](https://theta42.github.io/jump-host/).
|
||||||
|
- **[Discovery](discovery.html)** — the catalog-vs-discovered distinction, how scanned assets are matched/merged into existing resources, and how a discovery gets promoted into the catalog (and becomes reachable through the jump host).
|
||||||
- **[Theta Agent & Endpoint C2](agents.html)** — 2-way Go daemon (`theta-agent`) for real-time telemetry (CPU, RAM, Disk, ZFS, GPU), automated host discovery, SSSD/LDAP configuration, and local capability-controlled management operations.
|
- **[Theta Agent & Endpoint C2](agents.html)** — 2-way Go daemon (`theta-agent`) for real-time telemetry (CPU, RAM, Disk, ZFS, GPU), automated host discovery, SSSD/LDAP configuration, and local capability-controlled management operations.
|
||||||
|
- **[Vault secrets](vault.html)** — an OpenBao-backed key-value store built into the UI, for stashing passwords/API keys/credentials with encryption and access control.
|
||||||
|
- **[API tokens](concepts-api-tokens.html)** — self-service personal access tokens for calling the management API from scripts/CI without a browser session.
|
||||||
|
|
||||||
## Get it
|
## Get it
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,25 @@ needs theta-suite ≥ v1.30.1 (which grants the `sso-broker` OpenBao policy
|
|||||||
|
|
||||||
A plugin type is a module under `nodejs/plugins/<category>/<type>.js`. The
|
A plugin type is a module under `nodejs/plugins/<category>/<type>.js`. The
|
||||||
filename basename (without `.js`) is the `type`; the parent directory is the
|
filename basename (without `.js`) is the `type`; the parent directory is the
|
||||||
`category`. The built-ins ship under `plugins/discovery/`:
|
`category`. Two built-in categories ship today:
|
||||||
|
|
||||||
|
**`discovery`** — scheduled scans that sync external assets into the
|
||||||
|
directory catalog:
|
||||||
|
|
||||||
- `proxmox` — Proxmox VE (URL + API token)
|
- `proxmox` — Proxmox VE (URL + API token)
|
||||||
- `unifi` — UniFi Network controller (URL + username/password)
|
- `unifi` — UniFi Network controller (URL + username/password)
|
||||||
- `nmap` — nmap OS + port scan (a target range; no credentials)
|
- `nmap` — nmap OS + port scan (a target range; no credentials)
|
||||||
|
- `docker` — Docker daemon discovery (containers as directory resources)
|
||||||
|
|
||||||
|
**`messaging`** — on-demand delivery for alerts, 2FA codes, and
|
||||||
|
notifications:
|
||||||
|
|
||||||
|
- `twilio` — Twilio SMS
|
||||||
|
- `webhook` — universal REST webhook (custom JSON payload to Slack, Teams,
|
||||||
|
Discord, or any HTTP endpoint)
|
||||||
|
|
||||||
|
If no messaging plugin instance is enabled, the system falls back to the
|
||||||
|
legacy `voipms` integration configured directly in the SSO secrets.
|
||||||
|
|
||||||
### What the Proxmox plugin produces
|
### What the Proxmox plugin produces
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Vault Secrets
|
||||||
|
description: OpenBao-backed personal, shared, and external-app secret storage built into the SSO Manager UI.
|
||||||
|
---
|
||||||
|
|
||||||
# Vault Secrets Management
|
# Vault Secrets Management
|
||||||
|
|
||||||
|
[← Back to Home](index.html)
|
||||||
|
|
||||||
The Vault Secrets feature integrates with OpenBao to provide a secure key-value store for your environment. It allows you to store sensitive information like passwords, API keys, and credentials, ensuring they are encrypted and access-controlled.
|
The Vault Secrets feature integrates with OpenBao to provide a secure key-value store for your environment. It allows you to store sensitive information like passwords, API keys, and credentials, ensuring they are encrypted and access-controlled.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -26,7 +34,14 @@ You can access the Vault UI from the application's top navigation bar.
|
|||||||
|
|
||||||
### OpenBao Integration
|
### OpenBao Integration
|
||||||
|
|
||||||
The secrets are stored in an OpenBao backend configured in development mode. The default KV (Key-Value) version 2 engine is mounted at `secret/`. The built-in UI uses the `/api/vault/secret/` API endpoints to interact with OpenBao.
|
The secrets are stored in a real, initialized-and-unsealed OpenBao backend
|
||||||
|
(`setup.sh` handles init/unseal on first run) — not OpenBao's ephemeral dev
|
||||||
|
mode, which auto-unseals with an in-memory store and loses everything on
|
||||||
|
restart. The default KV (Key-Value) version 2 engine is mounted at `secret/`.
|
||||||
|
The built-in UI proxies through `/api/vault/secret/…`, authenticated the same
|
||||||
|
way as the rest of the app (session cookie or a personal API token) — the
|
||||||
|
server resolves your OpenBao access itself and injects the right scoped
|
||||||
|
token; you never see or handle a raw OpenBao token as a UI user.
|
||||||
|
|
||||||
## Apps tab (admin)
|
## Apps tab (admin)
|
||||||
|
|
||||||
@@ -48,9 +63,24 @@ The **Shared** tab lets you share a secret with another user (or app) without co
|
|||||||
|
|
||||||
## API Access
|
## API Access
|
||||||
|
|
||||||
If you need to programmatically access the secrets, you can interact directly with the OpenBao API using the root token (in dev mode):
|
To read your own secrets programmatically, call the `/api/vault` proxy with
|
||||||
|
a [personal API token](concepts-api-tokens.html) — **not** a raw OpenBao
|
||||||
|
token. The server authenticates the request, resolves your own scoped
|
||||||
|
OpenBao access, and injects the real `X-Vault-Token` itself:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Example: Read a secret via the API
|
# Example: Read a secret via the API (KV-v2, so the path includes /data/)
|
||||||
curl -H "X-Vault-Token: root" -H "Authorization: Bearer <your-sso-token>" http://<your-sso-host>/api/vault/secret/data/<your-secret-path>
|
curl -H "Authorization: Bearer sso_<id>_<secret>" \
|
||||||
|
https://<your-sso-host>/api/vault/secret/data/<your-secret-path>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
An **external app** reading its own config uses the scoped token minted for
|
||||||
|
it on the **Apps** tab instead of a personal token — see *Apps tab (admin)*
|
||||||
|
above for how that token is minted and what it's confined to.
|
||||||
|
|
||||||
|
Using the OpenBao **root token** directly (bypassing the SSO entirely) is
|
||||||
|
never the intended path for day-to-day secret access — it's an
|
||||||
|
operator/maintenance credential (seeding, disaster recovery), kept in
|
||||||
|
`setup.env` and never passed to a service container. See
|
||||||
|
[theta-env's Secrets doc](https://theta42.github.io/theta-env/secrets.html)
|
||||||
|
for the full token/policy model.
|
||||||
|
|||||||
@@ -33,8 +33,15 @@ Mail.send = function(to, subject, message, from){
|
|||||||
|
|
||||||
var transporter = nodemailer.createTransport(transportOpts);
|
var transporter = nodemailer.createTransport(transportOpts);
|
||||||
|
|
||||||
|
// Most authenticated SMTP relays (and this bit the field: "554 5.7.1
|
||||||
|
// ...: Sender is not same as SMTP authenticate username") require the
|
||||||
|
// envelope/header From to equal the authenticated user, or reject the
|
||||||
|
// send outright. If the operator hasn't set an explicit smtp.from,
|
||||||
|
// defaulting to the SMTP username is far more likely to actually send
|
||||||
|
// than a made-up noreply@theta42.com address that no relay authorized
|
||||||
|
// this account to send as.
|
||||||
var mailOpts = {
|
var mailOpts = {
|
||||||
from: from || conf.smtp.from || `${conf.name} Accounts <noreply@theta42.com>`,
|
from: from || conf.smtp.from || conf.smtp.user || `${conf.name} Accounts <noreply@theta42.com>`,
|
||||||
to: to,
|
to: to,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
html: message
|
html: message
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "t42-sso-manager",
|
"name": "t42-sso-manager",
|
||||||
"version": "1.30.1",
|
"version": "1.30.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "t42-sso-manager",
|
"name": "t42-sso-manager",
|
||||||
"version": "1.30.1",
|
"version": "1.30.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "t42-sso-manager",
|
"name": "t42-sso-manager",
|
||||||
"version": "1.30.1",
|
"version": "1.30.2",
|
||||||
"description": "A very simple LDAP management and SSO system",
|
"description": "A very simple LDAP management and SSO system",
|
||||||
"author": [
|
"author": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -206,8 +206,8 @@
|
|||||||
return '<div class="card shadow-sm service-card ' + (accessible ? 'border-success' : '') + '">'
|
return '<div class="card shadow-sm service-card ' + (accessible ? 'border-success' : '') + '">'
|
||||||
+ '<div class="card-body">'
|
+ '<div class="card-body">'
|
||||||
+ '<h5 class="card-title d-flex align-items-start gap-2">'
|
+ '<h5 class="card-title d-flex align-items-start gap-2">'
|
||||||
+ iconHtml
|
|
||||||
+ '<span>' + esc(r.name) + '</span>'
|
+ '<span>' + esc(r.name) + '</span>'
|
||||||
|
+ iconHtml
|
||||||
+ '</h5>'
|
+ '</h5>'
|
||||||
+ '<div class="mb-2"><span class="badge bg-secondary">' + esc(r.kind)
|
+ '<div class="mb-2"><span class="badge bg-secondary">' + esc(r.kind)
|
||||||
+ (md.subType ? ' · ' + esc(md.subType) : '') + '</span>' + badges + '</div>'
|
+ (md.subType ? ' · ' + esc(md.subType) : '') + '</span>' + badges + '</div>'
|
||||||
|
|||||||