Skip to main content

Service accounts & API-key TTL ceiling (GY.C3a)

MOD supports a bounded, reviewable inventory of every non-human credential in the installation — the first slice of the strong authentication program. Wording is deliberate: these features support a strong-authentication posture; they do not by themselves make an installation "compliant".

What is a service account here​

  • API keys — sk_... credentials mapped to a Keycloak service account (client-credentials flow; short-lived JWTs, 1 hour). Includes app keys (GX.A3), which are machine credentials bound to an app's scope.
  • Keycloak service-account clients — the OIDC clients MOD creates for those keys (api-key-*, mod-app-*), reported from the existing Keycloak admin seam.
  • MFA-exempt service principals — machine users that never pass the browser flow (same exemption rule as the MFA surface: service-account-*, *@service.local, the seeded service users).

The inventory endpoint​

GET /v1/platform/compliance/service-accounts
  • Platform admin sees the whole platform; a tenant admin sees their own tenant only; everyone else gets 403.
  • Each API key row carries id, tenant, owner, description, prefix, scopes (read-only flag + capability allowlist), created / expires / last-used timestamps, and flags: no_expiry, expired, expiry_beyond_ceiling, unused (tunable via ?unused_days=N, default 90). The secret and its hash are never returned.
  • If Keycloak is unreachable, the response is still 200 — a partial result with a keycloak_error field instead of the client list.

The TTL ceiling​

Platform setting auth.api_key_max_ttl_days (default 0 = no ceiling, preserving the pre-existing behaviour where keys may be created without an expiry).

When the ceiling is set (> 0):

  • creating or rotating an API key without an expires_at → 422;
  • an expires_at beyond the ceiling → 422;
  • the refusal happens before the Keycloak client is created (no orphan clients); app keys are bounded by the ceiling when it is set; while the ceiling is 0 they keep the legacy unbounded behaviour (flagged in the inventory).

Existing keys that were minted before the ceiling is set are not retroactively expired — the inventory flags them (no_expiry / expiry_beyond_ceiling) so they can be rotated down.

Conformance check​

auth.service_accounts_bounded (supports NIST 800-53 IA-5, CMMC IA.L2-3.1.3, ISO 27001 A.8.5) reports:

  • not-applicable while the ceiling is 0;
  • pass when every live API key carries an expiry;
  • fail (warn) naming the offending key ids and prefixes (metadata only) when the ceiling is set and a key lacks an expiry.

It runs on the normal conformance schedule and on-demand runs, like the other GY.C12 checks.