Skip to content

feat(manifests): enable RBAC authorization in SaaS template#1667

Merged
mergify[bot] merged 5 commits into
mainfrom
jsell/chore/enable-authz
Jun 9, 2026
Merged

feat(manifests): enable RBAC authorization in SaaS template#1667
mergify[bot] merged 5 commits into
mainfrom
jsell/chore/enable-authz

Conversation

@jsell-rh

@jsell-rh jsell-rh commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • Live-patched on hcmais — api-server running with --enable-authz=true
  • Verify RBAC-protected endpoints return 403 for unauthorized users
  • Verify role-bound users can access their scoped resources

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • API server authorization is now enabled and the control plane uses OIDC-based credentials instead of the legacy API token.
  • Security

    • Credential handling now relies on the identity provider's JWKS/token endpoints; plaintext credential option removed.
  • Hardening

    • Key runtime containers now run with read-only root filesystems.
  • Documentation

    • Added deployment prerequisites: create OIDC client credentials and an API-server auth config before deploying.

Flip --enable-authz from false to true now that the RBAC enforcement
middleware has landed (PR #1660). Already live-patched on hcmais.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@jsell-rh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 11 minutes and 22 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 58e8ceec-8ff8-4f36-887b-13cd599ad37c

📥 Commits

Reviewing files that changed from the base of the PR and between 61894f1 and b19f762.

📒 Files selected for processing (1)
  • components/ambient-api-server/plugins/sessions/message_model.go
📝 Walkthrough

Walkthrough

Adds README prerequisites for Keycloak client and JWKS/configmap, replaces secret token envs with OIDC envs sourced from ambient-control-plane-oidc, enables API server authz and Keycloak JWK URL, removes plaintext credential envs, adds GRPC service account env, and sets readOnlyRootFilesystem on DB, migration initContainer, and API server.

Changes

Manifests OIDC wiring & pod hardening

Layer / File(s) Summary
Deployment prerequisites README
components/manifests/README.md
Adds a “Prerequisites for New Deployments” section with oc commands to create ambient-control-plane-oidc (Keycloak client credentials) and ambient-api-server-auth (JWKS certs + local ACL) in the target namespace.
Control-plane OIDC env patch
components/manifests/overlays/hcmais/control-plane-env-patch.yaml, components/manifests/templates/template-services.yaml
Replaces AMBIENT_API_TOKEN secret-based env with OIDC_TOKEN_URL, OIDC_CLIENT_ID, and OIDC_CLIENT_SECRET sourced from the ambient-control-plane-oidc secret.
API server auth flags & env updates
components/manifests/templates/template-services.yaml
Switches --enable-authz to true, adds --jwk-cert-url=${KEYCLOAK_REALM_URL}/protocol/openid-connect/certs while retaining --jwk-cert-file, removes JWK_CERT_URL and CREDENTIAL_ENCRYPTION_ALLOW_PLAINTEXT envs, and adds GRPC_SERVICE_ACCOUNT from ambient-control-plane-oidc.
Pod filesystem hardening
components/manifests/templates/template-services.yaml
Adds readOnlyRootFilesystem: true to the PostgreSQL container, the API server migration initContainer, and the API server container securityContext.

Possibly Related PRs

  • ambient-code/platform#1659: Modifies the same ambient-api-server deployment flags and pod security settings in the template.
  • ambient-code/platform#1658: Overlaps with changes around CREDENTIAL_ENCRYPTION_ALLOW_PLAINTEXT handling for the API server.
  • ambient-code/platform#1445: Previously added OIDC env vars to the control-plane deployment, overlapping with the control-plane OIDC wiring here.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Security And Secret Handling ❌ Error The README.md line 150 uses echo '...\\n...' which produces literal \\n instead of newlines, breaking ACL YAML format and preventing authz enforcement in Kubernetes manifests. Replace echo '- claim: email\\n pattern: ^.*$' with printf '%s\\n' '- claim: email' ' pattern: ^.*$' to ensure proper YAML parsing for ACL configuration.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title follows Conventional Commits format (feat(manifests):) and accurately captures the main change: enabling RBAC authorization in the SaaS template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Performance And Algorithmic Complexity ✅ Passed PR contains only Kubernetes manifest declarations and documentation—no algorithmic code, loops over large datasets, N+1 patterns, caching/buffering, or API pagination issues.
Kubernetes Resource Safety ✅ Passed Template passes all safety checks: no Jobs/Secrets/PVCs, all containers have resource limits, no RBAC rules, all resources namespaced, security contexts configured.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jsell/chore/enable-authz
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch jsell/chore/enable-authz

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/manifests/templates/template-services.yaml (1)

191-195: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add readOnlyRootFilesystem to the API server pod containers.

Both the migration initContainer and the api-server container still miss readOnlyRootFilesystem, so this manifest does not meet the repo's restricted container profile.

Suggested hardening
             securityContext:
+              readOnlyRootFilesystem: true
               allowPrivilegeEscalation: false
               capabilities:
                 drop:
                   - ALL
…
             securityContext:
+              readOnlyRootFilesystem: true
               allowPrivilegeEscalation: false
               capabilities:
                 drop:
                   - ALL

As per coding guidelines, "All containers must have restricted SecurityContext: runAsNonRoot, drop ALL capabilities, readOnlyRootFilesystem".

Also applies to: 297-301

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/manifests/templates/template-services.yaml` around lines 191 -
195, The manifest's containers are missing readOnlyRootFilesystem in their
securityContext; update the securityContext for the migration initContainer and
the api-server container to include readOnlyRootFilesystem: true (in addition to
the existing allowPrivilegeEscalation: false, capabilities.drop: [ALL]) and
ensure runAsNonRoot is set as well so both containers meet the repo's restricted
profile; apply the same change to the other container block referenced (the
second api-server/migration occurrence).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@components/manifests/templates/template-services.yaml`:
- Around line 191-195: The manifest's containers are missing
readOnlyRootFilesystem in their securityContext; update the securityContext for
the migration initContainer and the api-server container to include
readOnlyRootFilesystem: true (in addition to the existing
allowPrivilegeEscalation: false, capabilities.drop: [ALL]) and ensure
runAsNonRoot is set as well so both containers meet the repo's restricted
profile; apply the same change to the other container block referenced (the
second api-server/migration occurrence).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 033b8ddc-764c-4bec-b81b-0b42cb9c444a

📥 Commits

Reviewing files that changed from the base of the PR and between 1162d5d and 34fd7a2.

📒 Files selected for processing (1)
  • components/manifests/templates/template-services.yaml

@jsell-rh jsell-rh self-assigned this Jun 9, 2026
- Add --jwk-cert-url CLI flag pointing to Keycloak JWKS endpoint;
  without it the framework defaults to sso.redhat.com and rejects
  tokens signed by our Keycloak instance
- Remove unused JWK_CERT_URL env var (framework reads the CLI flag)
- Remove CREDENTIAL_ENCRYPTION_ALLOW_PLAINTEXT=true
- Add readOnlyRootFilesystem: true to all container securityContexts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for cheerful-kitten-f556a0 canceled.

Name Link
🔨 Latest commit 34fd7a2
🔍 Latest deploy log https://app.netlify.com/projects/cheerful-kitten-f556a0/deploys/6a282acdcb62e90008f92090

@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for cheerful-kitten-f556a0 canceled.

Name Link
🔨 Latest commit b19f762
🔍 Latest deploy log https://app.netlify.com/projects/cheerful-kitten-f556a0/deploys/6a2835f2cb62e90008fd33cb

jsell-rh and others added 3 commits June 9, 2026 11:28
…client credentials

Replace AMBIENT_API_TOKEN (static bearer token) with OIDC client
credentials (client_credentials grant) for control-plane → api-server
authentication. The control-plane exchanges a Keycloak client-id and
client-secret for short-lived JWTs, validated by the same JWKS path
the api-server already uses for user tokens.

- Update SaaS template: replace AMBIENT_API_TOKEN with OIDC_TOKEN_URL,
  OIDC_CLIENT_ID, OIDC_CLIENT_SECRET from ambient-control-plane-oidc secret
- Remove AMBIENT_API_TOKEN from api-server env (no longer needed)
- Update hcmais overlay with concrete Keycloak token URL
- Add deployment prerequisites section to manifests README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The gRPC interceptor needs to know the control-plane's OIDC client ID
to tag it as a service caller. Without this, gRPC watch streams
authenticate the JWT but don't grant service-caller privileges, so
session/project events are silently filtered out.

Read from the same ambient-control-plane-oidc secret to stay in sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The refactor from raw SQL to g2.Create(msg) in PR #1660 caused Gorm
to include seq=0 in INSERT statements, colliding with the unique
constraint when a seq=0 row already existed. The -> tag tells Gorm
to omit seq from writes and let the Postgres DEFAULT nextval() fire,
while still reading the value back via the RETURNING clause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/manifests/templates/template-services.yaml (1)

203-221: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Production startup can crash if credential-encryption-key is missing

With AMBIENT_ENV=production (Line 203) and optional keyring/version refs (Line 215, Line 221), the api-server can start without those envs set, then exit at runtime when encryption keyring is absent. This turns into CrashLoopBackOff on fresh deployments unless that secret is guaranteed.

Recommend making these secret refs required and documenting credential-encryption-key as a deployment prerequisite alongside the new OIDC prereqs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/manifests/templates/template-services.yaml` around lines 203 -
221, AMBIENT_ENV is set to production but the CREDENTIAL_ENCRYPTION_KEYRING and
CREDENTIAL_ENCRYPTION_KEY_VERSION envs reference secretKeyRef entries that are
marked optional: true, which can allow the pod to start and then fail at
runtime; remove the optional: true on the secretKeyRef blocks for
CREDENTIAL_ENCRYPTION_KEYRING and CREDENTIAL_ENCRYPTION_KEY_VERSION (keep the
same secret name credential-encryption-key and keys keyring/version) so the
manifest requires the secret at creation time, and add a short note in
deployment docs/prereqs documenting that the credential-encryption-key secret
must exist alongside the OIDC prereqs (and reference the
AMBIENT_ENV/GRPC_SERVICE_ACCOUNT env names for clarity).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/manifests/README.md`:
- Around line 149-150: The ACL ConfigMap command currently uses echo with a
literal '\n' which produces a single-line string; update the command that
generates acl.yml (the --from-file=acl.yml part) to emit a real multi-line YAML
string (for example use printf, echo -e, or a shell $'...' quoted string) so the
resulting acl.yml contains actual newlines and valid YAML mapping for the claim
and pattern; ensure the command still runs in a POSIX shell and produces content
matching "- claim: email\n  pattern: ^.*$" across two lines so ACL loading works
correctly.

---

Outside diff comments:
In `@components/manifests/templates/template-services.yaml`:
- Around line 203-221: AMBIENT_ENV is set to production but the
CREDENTIAL_ENCRYPTION_KEYRING and CREDENTIAL_ENCRYPTION_KEY_VERSION envs
reference secretKeyRef entries that are marked optional: true, which can allow
the pod to start and then fail at runtime; remove the optional: true on the
secretKeyRef blocks for CREDENTIAL_ENCRYPTION_KEYRING and
CREDENTIAL_ENCRYPTION_KEY_VERSION (keep the same secret name
credential-encryption-key and keys keyring/version) so the manifest requires the
secret at creation time, and add a short note in deployment docs/prereqs
documenting that the credential-encryption-key secret must exist alongside the
OIDC prereqs (and reference the AMBIENT_ENV/GRPC_SERVICE_ACCOUNT env names for
clarity).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d5faeb87-c976-4b35-9ce3-3cabcfee410a

📥 Commits

Reviewing files that changed from the base of the PR and between 3acdd02 and 61894f1.

📒 Files selected for processing (3)
  • components/manifests/README.md
  • components/manifests/overlays/hcmais/control-plane-env-patch.yaml
  • components/manifests/templates/template-services.yaml

Comment on lines +149 to +150
--from-file=jwks.json=<(curl -s <KEYCLOAK_REALM_URL>/protocol/openid-connect/certs) \
--from-file=acl.yml=<(echo '- claim: email\n pattern: ^.*$')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix ACL ConfigMap command: current echo likely writes literal \n

Line 150 uses echo with \n inside single quotes, which commonly produces a single-line literal string instead of valid YAML. That can break ACL loading for authz.

Suggested fix
-  --from-file=acl.yml=<(echo '- claim: email\n  pattern: ^.*$')
+  --from-file=acl.yml=<(printf '%s\n' '- claim: email' '  pattern: ^.*$')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/manifests/README.md` around lines 149 - 150, The ACL ConfigMap
command currently uses echo with a literal '\n' which produces a single-line
string; update the command that generates acl.yml (the --from-file=acl.yml part)
to emit a real multi-line YAML string (for example use printf, echo -e, or a
shell $'...' quoted string) so the resulting acl.yml contains actual newlines
and valid YAML mapping for the claim and pattern; ensure the command still runs
in a POSIX shell and produces content matching "- claim: email\n  pattern: ^.*$"
across two lines so ACL loading works correctly.

@mergify mergify Bot added the queued label Jun 9, 2026
@mergify

mergify Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-06-09 16:08 UTC · Rule: default
  • Checks skipped · PR is already up-to-date
  • Merged2026-06-09 16:08 UTC · at b19f762da34a2e8e4d6866a879a79c31cfe98d15 · squash

This pull request spent 19 seconds in the queue, including 4 seconds running CI.

Required conditions to merge

@mergify mergify Bot merged commit 13a0805 into main Jun 9, 2026
72 checks passed
@mergify mergify Bot deleted the jsell/chore/enable-authz branch June 9, 2026 16:08
@mergify mergify Bot removed the queued label Jun 9, 2026
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