Skip to content

security: enforce model-registry auth in release compose - #50

Merged
man4ish merged 1 commit into
mainfrom
security/hipaa-model-registry-release-auth-wiring
Aug 15, 2026
Merged

security: enforce model-registry auth in release compose#50
man4ish merged 1 commit into
mainfrom
security/hipaa-model-registry-release-auth-wiring

Conversation

@man4ish

@man4ish man4ish commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix a critical HIPAA security gap where the model-registry service was deployed without authentication and organization enforcement in the release Compose configuration.

The model-registry application already had the required Phase 2A–2E organization-ownership enforcement, but the release deployment did not enable it.

Before this fix, AUTH_ENABLED was unset in the release configuration and therefore defaulted to false.

The vulnerability was independently reproduced:

  • Unauthenticated POST /v1/register200
  • No Authorization header was required.
  • Model-registry was exposed on all interfaces in the release configuration.

Fix

Added the existing repository-standard authentication wiring to the model-registry service in both release Compose variants:

  • AUTH_ENABLED: "true"
  • JWT_SECRET: ${AUTH_SECRET_KEY:?AUTH_SECRET_KEY must be set}
  • IAM_URL: http://auth-service:8001
  • AUDIT_URL: http://security-audit:8004

Also:

  • Added model-registry to EXPECTED_SECRET_WIRING.
  • Added regression coverage requiring AUTH_ENABLED == "true" in both release files.
  • Corrected the inaccurate security documentation.
  • Did not modify the existing model-registry Phase 2A–2E ownership implementation.

Verification

Pre-fix vulnerability reproduced:

  • Unauthenticated POST /v1/register200

Post-fix verification:

  • Unauthenticated GET /v1/models401
  • Unauthenticated POST /v1/register401
  • Invalid/expired token → 401
  • Organization A can register/read its own model → 200
  • Organization B cannot read Organization A's model
  • Organization B cannot tag Organization A's model
  • Organization B cannot promote Organization A's model
  • Cross-organization responses retain the existing anti-enumeration behavior.

Configuration verification:

  • docker compose config confirms all required model-registry authentication variables.
  • Missing AUTH_SECRET_KEY causes Compose configuration to fail closed.
  • YAML validation passes.
  • Ruff passes on the changed test file.

Tests:

  • 75 passed — release/network configuration tests
  • 101 passed — non-integration Studio tests
  • Model-registry test suite passes with 95.76% coverage
  • Integration tests were not run successfully because they require the live stack; connection-refused failures are unrelated to this configuration change.

Scope

This PR intentionally does not modify:

  • omnibioai-model-registry
  • RAG
  • TES
  • dev-hub
  • Control Center public-read-only work
  • Existing organization ownership logic

Separate follow-up investigations are recommended for the release-auth configuration observed in dev-hub, RAG, and TES.

Review request

Please review specifically for:

  1. Correct release authentication wiring.
  2. Fail-closed behavior when AUTH_SECRET_KEY is missing.
  3. Regression-test coverage preventing the release configuration from reverting to unauthenticated mode.
  4. Preservation of existing model-registry organization isolation.

If CI is green and there are no substantive review concerns, this PR is GO for merge.

🤖 Generated with Claude Code

CRITICAL: both production/release compose files (docker-compose.release.yml
and docker-compose-release.yml) shipped model-registry with no
AUTH_ENABLED/JWT_SECRET/IAM_URL/AUDIT_URL at all. omnibioai-model-registry's
config.py defaults AUTH_ENABLED to false when unset, so every route's auth
dependency silently returned a synthetic unauthenticated identity -- no
token required, no permission check -- while the service was published on
${HOST_IP:-0.0.0.0}:8095 by default. The real Phase 2A-2E organization-
ownership enforcement in that repo never ran in a release deployment.
Reproduced live: an unauthenticated POST /v1/register (zero Authorization
header, AUTH_ENABLED unset) returned 200 and registered a model.

Fix mirrors the exact wiring docker-compose.yml (dev) already had for this
same service, and the established release-file convention already used by
control-center/dev-hub/security-audit (AUTH_ENABLED as a hardcoded "true"
literal, JWT_SECRET as ${AUTH_SECRET_KEY:?...} required-guard, IAM_URL/
AUDIT_URL as fixed internal-network URLs) -- no new pattern invented.

Changed:
- docker-compose.release.yml, docker-compose-release.yml: add
  IAM_URL/AUDIT_URL/JWT_SECRET/AUTH_ENABLED to model-registry's environment
  block. Verified via `docker compose config` that (a) the merged config
  now carries all four correctly and (b) omitting AUTH_SECRET_KEY makes
  compose refuse to start with "AUTH_SECRET_KEY must be set", naming
  model-registry's own JWT_SECRET line -- confirms this fails closed
  rather than silently reverting to open mode.
- tests/test_compose_release_config.py: add model-registry to
  EXPECTED_SECRET_WIRING (JWT_SECRET-sourcing regression coverage, same
  mechanism already protecting control-center/security-audit) and a new
  SERVICES_REQUIRING_AUTH_ENABLED set + test asserting AUTH_ENABLED is the
  literal "true" in both release files. Scoped to model-registry only --
  running the new AUTH_ENABLED test against dev-hub too surfaced an
  independent, pre-existing gap (docker-compose-release.yml's dev-hub
  block is missing JWT_SECRET/AUTH_ENABLED entirely) which is out of
  scope for this change and is flagged separately, not fixed here.
- SECURITY-COMPOSE-HARDENING.md: corrects the document's own prior claim
  that model-registry "does enforce IAM authorization" on its default
  0.0.0.0 binding -- that was false until this fix; documents the finding
  and marks the equivalent claim for workbench/tes/auth-service/rag as
  unverified pending the same audit.

release/linux-arm64-unpacked/ (gitignored local Electron build output, not
tracked in this repo) still contains a stale pre-fix compose copy on this
machine; it is not the repo's source of truth and will pick up this fix on
the next `npm run build:*`. Not hand-edited.

No changes to omnibioai-model-registry itself -- its Phase 2A-2E
organization-ownership enforcement was already correct; this was purely a
release deployment-configuration gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@man4ish
man4ish merged commit 755c4c2 into main Aug 15, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant