feat(server): v2 secure-session-core Task 2 - auth rebuild
SPEC-002 Phase 1 Task 2 (specs/v2-secure-session-core), code-reviewed APPROVED. - DELETE the JWT-as-agent-key branch in relay validate_agent_api_key (audit CRITICAL): agent auth now = per-agent cak_ key (SHA-256 -> connect_agent_keys, revoked filtered) OR support code OR deprecated shared AGENT_API_KEY (warned). A user JWT can no longer authenticate an agent. - auth/agent_keys.rs: cak_ gen (OsRng 256-bit) + SHA-256 hash + verify. - auth/jwt.rs: ViewerClaims + create/validate_viewer_token (5-min TTL, purpose=viewer, session_id+tenant_id claims; non-interchangeable with login). - Admin key issuance: POST/GET/DELETE /api/machines/:agent_id/keys. - POST /api/sessions/:id/viewer-token mints a session-bound short-lived token. - Migration 005: organization/site/tags on connect_machines (fixes the silent update_machine_metadata write, coord todo faf39fe0). NOTE: viewer-token minting is gated by AuthenticatedUser only; the AUTHORIZATION check (admin/permission gate) that closes audit CRITICAL #1 lands in Task 3 (the viewer WS verification). The viewer WS path (relay/mod.rs:285) is untouched here. Not cargo-check-verified (no toolchain on the authoring host) - self-reviewed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,8 +169,9 @@ pub async fn agent_ws_handler(
|
||||
|
||||
// Validate API key if provided (for persistent agents)
|
||||
if let Some(ref key) = api_key {
|
||||
// For now, we'll accept API keys that match the JWT secret or a configured agent key
|
||||
// In production, this should validate against a database of registered agents
|
||||
// Agent-plane auth ONLY: a per-agent `cak_` key (hash-compared against
|
||||
// connect_agent_keys, rejecting revoked) or the deprecated shared
|
||||
// AGENT_API_KEY fallback. A dashboard/user JWT is NEVER accepted here.
|
||||
if !validate_agent_api_key(&state, key).await {
|
||||
warn!(
|
||||
"Agent connection rejected: {} from {} - invalid API key",
|
||||
@@ -220,21 +221,45 @@ pub async fn agent_ws_handler(
|
||||
}))
|
||||
}
|
||||
|
||||
/// Validate an agent API key
|
||||
/// Validate an agent key presented on the agent plane.
|
||||
///
|
||||
/// SECURITY (v2): a dashboard/user JWT is NEVER a valid agent credential — the
|
||||
/// old `jwt_config.validate_token` branch was the relay CRITICAL and is gone.
|
||||
/// Accepts, in order:
|
||||
/// 1. A per-agent `cak_` key: SHA-256 hash compared against
|
||||
/// `connect_agent_keys`; revoked keys are rejected (the DB query filters
|
||||
/// `revoked_at IS NULL`). This is the supported path.
|
||||
/// 2. The shared `AGENT_API_KEY` env value — DEPRECATED fallback, retained
|
||||
/// only for not-yet-migrated agents. Its use is logged at WARNING and it
|
||||
/// should be removed once all managed agents carry per-agent keys.
|
||||
///
|
||||
/// Never logs the presented key or any hash.
|
||||
async fn validate_agent_api_key(state: &AppState, api_key: &str) -> bool {
|
||||
// Check if API key is a valid JWT (allows using dashboard token for testing)
|
||||
if state.jwt_config.validate_token(api_key).is_ok() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check against configured agent API key if set
|
||||
if let Some(ref configured_key) = state.agent_api_key {
|
||||
if api_key == configured_key {
|
||||
// 1. Per-agent key (the supported path). Requires a database; without one,
|
||||
// only the deprecated shared-key fallback below can apply.
|
||||
if let Some(ref db) = state.db {
|
||||
if crate::auth::agent_keys::verify_agent_key(db.pool(), api_key)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. DEPRECATED shared-key fallback. Constant-time-ish equality is not
|
||||
// critical here (the key is high-entropy and this path is sunset), but
|
||||
// we still avoid logging the value.
|
||||
if let Some(ref configured_key) = state.agent_api_key {
|
||||
if api_key == configured_key {
|
||||
warn!(
|
||||
"[WARNING] Agent authenticated via the DEPRECATED shared AGENT_API_KEY \
|
||||
fallback. Migrate this agent to a per-agent cak_ key; the shared key \
|
||||
will be removed."
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// In future: validate against database of registered agents
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user