Add LDAP-over-HTTPS API, agent secrets/IAM engines, and join key management

See CHANGELOG.md for the full breakdown. Summary:

- POST /api/v1/ldap/{bind,search}: LDAP-over-HTTPS so a client stops
  speaking raw LDAP and instead calls the SSO, which binds/searches its
  own OpenLDAP on the caller's behalf (DESIGN.md §3).
- LDAP byte-pump relay (utils/ldap_tunnel.js): forwards raw LDAP bytes
  from an agent's local socket into OpenLDAP over the existing agent WSS
  channel; the SSO never parses LDAP (DESIGN.md §4).
- POST /api/v1/agent/secrets: node-scoped OpenBao secret fetch for
  agents, enforced to each agent's own secret/data/nodes/<id>/* prefix
  (DESIGN.md §5).
- iam_apply signed command: push node-scoped IAM config (sudo rules, SSH
  keys, access control, revocation) to an agent (DESIGN.md §6).
- Agent capability badges on the Directory Metrics tab, sourced from the
  agent's own discovery frame.
- Join key management: GET /api/agent/join-keys/:id/agents (which hosts
  enrolled through a key) plus a Manage join keys table in the Install
  Agent modal with Revoke/Delete actions, confirmed inline per-row rather
  than a blocking native confirm() or the shared app.messages.confirm()
  banner (which desyncs across concurrent rows -- see CHANGELOG).
- docs/agents.md: capability matrix updated for the three new
  capabilities, a full secrets-engine walkthrough with screenshots
  (bash + Node consuming a rendered secret, plus the direct-API
  alternative), and the join-key reuse/UI/audit questions answered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 17:03:41 -04:00
parent 6e748bfa66
commit 181ca8c9cb
17 changed files with 1091 additions and 7 deletions
+10
View File
@@ -112,6 +112,16 @@ app.use('/api/api-token', middleware.auth, require('./routes/api_token'));
// WebSocket handler (routes/api_agent.initAgentWebSockets) still runs on onListen.
app.use('/api/agent', require('./routes/api_agent'));
// LDAP-over-HTTPS API (DESIGN.md §3). Bearer-authed (agent token or PAT); the
// SSO performs the real LDAP bind/search against its own OpenLDAP. Mounted
// synchronously for the same reason as /api/agent — it must sit before the 404
// catch-all.
app.use('/api/v1/ldap', require('./routes/api_ldap'));
// Agent-facing operations (DESIGN.md §5, §6): node-scoped secrets, IAM. The
// caller is the agent itself (Bearer agent token), not an admin session.
app.use('/api/v1/agent', require('./routes/api_agent_ops'));
// OAuth 2.0 / OpenID Connect
app.use('/oauth', oauthRouter);
app.use('/api/oauth', middleware.auth, oauthApiRouter);