Skip to content

feat(helm): per-role ServiceAccounts — least-privilege IAM seam (IRSA) - #556

Merged
jensholdgaard merged 7 commits into
mainfrom
helm-per-role-irsa
Jul 20, 2026
Merged

feat(helm): per-role ServiceAccounts — least-privilege IAM seam (IRSA)#556
jensholdgaard merged 7 commits into
mainfrom
helm-per-role-irsa

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jul 19, 2026

Copy link
Copy Markdown
Owner

What

Closes #555 — the least-privilege object-store story, as chart support + documentation:

  1. Per-role ServiceAccounts (receiver/querier/compactor .serviceAccount.{create,annotations,name}, default off → shared SA, fully backwards-compatible). With IRSA each role carries its own IAM identity; name-only binds an existing SA. Chart version → 0.4.0.
  2. Render-time validation generalized: the existing IRSA/static-keys mutual-exclusion and the "role-arn without create has no effect" check now cover the shared and per-role accounts.
  3. README "Per-role IAM" section: the permission table (querier read-only / receiver no-delete / compactor sole DeleteObject holder), IRSA install example, IAM policy shape with prefix scoping, S3-versioning as the independent second control on deletes, non-AWS equivalents, and the SPIRE/cert-reload rotation seam (RFC 0030) with an honest note that chart-level TLS values aren't exposed yet.

Verified

  • Claims checked against the code, not assumed: production deletes exist only in compaction.rs; the querier's RFC 0033 cache write-through (and v1-key cleanup) is best-effort by contract — documented explicitly, with a scoped-key PutObject/DeleteObject alternative (audit/tenant_id=*/template_map*) for operators who want the cache warm under a read-only querier.
  • helm lint clean; render matrix exercised: default (shared SA, unchanged output), full per-role IRSA split (3 SAs with arns, each workload binding its own), name-only fallback, and both failure modes (arn-without-create per role; secret + any arn) firing with actionable messages.

Invariants / hazards

No data-path change. The compactor-is-the-only-sweeper invariant is untouched; the delete split enforces hazard-#4's ownership boundary at the IAM layer rather than only by convention.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

Summary by CodeRabbit

  • New Features
    • Added optional role-specific ServiceAccounts for the receiver, querier, and compactor.
    • Enabled least-privilege IAM separation per workload, including compactor-only delete permission.
    • Updated credential validation to support per-role IRSA and to prevent incompatible static S3 credentials/Secrets with web-identity usage.
  • Documentation
    • Expanded guidance for per-role IAM, cache permission behavior, ServiceAccount/IRSA configuration, and security hardening.
  • Chores
    • Bumped the Helm chart version to 0.4.0.

…IRSA)

Each role opts into its own ServiceAccount (falling back to the shared
one), so with IRSA the three roles carry three IAM identities: querier
read-only, receiver Get/List/Put with no delete, compactor the sole
s3:DeleteObject holder. Verified against the code: production deletes
exist only in compaction, plus the querier's best-effort RFC 0033
cache write-through — documented with a scoped-key policy alternative
since a publish failure never fails a query by contract.

The IRSA/static-keys mutual-exclusion render check generalizes over the
shared + per-role accounts, and a role-arn on a non-created role SA
fails render with the same guidance as the shared case.

README gains the Per-role IAM section: the permission table, the IRSA
install example, the IAM policy shape, S3-versioning as the independent
second control on deletes, and the SPIRE/cert-reload rotation seam.

Closes #555.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
@jensholdgaard
jensholdgaard requested a review from Copilot July 19, 2026 23:28
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf319d0a-d7e8-4366-8894-d3f6c24a2404

📥 Commits

Reviewing files that changed from the base of the PR and between e08c3dc and 3c21522.

📒 Files selected for processing (3)
  • deploy/helm/ourios/README.md
  • deploy/helm/ourios/templates/_helpers.tpl
  • deploy/helm/ourios/values.yaml
📝 Walkthrough

Walkthrough

The Helm chart adds optional role-specific ServiceAccounts for receiver, querier, and compactor workloads, expands IRSA credential validation, wires workloads to role-specific accounts, documents IAM policies, and bumps the chart version.

Changes

Per-role IAM support

Layer / File(s) Summary
Role configuration and documentation
deploy/helm/ourios/values.yaml, deploy/helm/ourios/README.md, deploy/helm/ourios/Chart.yaml
Adds receiver, querier, and compactor ServiceAccount settings, documents role-specific IAM and IRSA configuration, and updates the chart version to 0.4.0.
ServiceAccount rendering and validation
deploy/helm/ourios/templates/_helpers.tpl, deploy/helm/ourios/templates/serviceaccount.yaml
Adds role-specific ServiceAccount name resolution and renders configured role accounts; IRSA validation now checks shared and per-role annotations, creation flags, and existing credentials.
Role-specific workload wiring
deploy/helm/ourios/templates/receiver-statefulset.yaml, deploy/helm/ourios/templates/querier-deployment.yaml, deploy/helm/ourios/templates/compactor-deployment.yaml
Configures each workload to use its corresponding role-specific ServiceAccount helper.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HelmValues
  participant HelmHelpers
  participant ServiceAccountTemplate
  participant WorkloadTemplates
  HelmValues->>HelmHelpers: provide per-role ServiceAccount settings
  HelmHelpers->>ServiceAccountTemplate: resolve names and validate IRSA configuration
  ServiceAccountTemplate->>WorkloadTemplates: expose role-specific ServiceAccounts
  WorkloadTemplates->>WorkloadTemplates: assign receiver, querier, and compactor accounts
Loading

Possibly related PRs

  • jensholdgaard/ourios#304: Adds the existing IRSA and S3 credential validation that this change extends to role-specific ServiceAccounts.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly reflects the main change: per-role ServiceAccounts for least-privilege IRSA in the Helm chart.
Description check ✅ Passed The description is detailed and covers the change, related issue, verification, and invariants; it is mostly complete.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch helm-per-role-irsa

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.

Copilot AI 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.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds Helm chart support and documentation for least-privilege, per-role ServiceAccounts (IRSA seam), plus generalized render-time validation around IRSA vs static S3 keys.

Changes:

  • Introduces optional per-role ServiceAccount values for receiver/querier/compactor and updates workloads to use role-scoped SA names with fallback to the shared SA.
  • Extends Helm render-time validation to cover both shared and per-role IRSA role-arn annotations (and maintains mutual exclusion with static S3 secrets).
  • Documents the per-role IAM model and bumps chart version to 0.4.0.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
deploy/helm/ourios/values.yaml Adds per-role ServiceAccount value blocks and clarifies shared SA comments for IRSA usage.
deploy/helm/ourios/templates/serviceaccount.yaml Renders optional role-scoped ServiceAccounts when enabled per role.
deploy/helm/ourios/templates/receiver-statefulset.yaml Switches receiver pods to use role-aware ServiceAccount name helper.
deploy/helm/ourios/templates/querier-deployment.yaml Switches querier pods to use role-aware ServiceAccount name helper.
deploy/helm/ourios/templates/compactor-deployment.yaml Switches compactor pods to use role-aware ServiceAccount name helper.
deploy/helm/ourios/templates/_helpers.tpl Adds role SA name helper and expands IRSA/static-keys validation across per-role SAs.
deploy/helm/ourios/README.md Documents per-role IAM split and updates values table accordingly.
deploy/helm/ourios/Chart.yaml Bumps Helm chart version to 0.4.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread deploy/helm/ourios/README.md
Comment thread deploy/helm/ourios/values.yaml
Comment thread deploy/helm/ourios/README.md
Comment thread deploy/helm/ourios/templates/serviceaccount.yaml Outdated
… shared-SA opt-out example

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
deploy/helm/ourios/templates/_helpers.tpl (1)

70-89: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Generate unique names for role-specific ServiceAccounts.

When a role sets serviceAccount.create=true without serviceAccount.name, falling back to ourios.serviceAccountName gives every role the same name. The per-role rendering loop then emits multiple ServiceAccounts with an identical name, and workloads cannot receive distinct IRSA identities. Use a generated role-scoped default such as <fullname>-receiver, while retaining the shared fallback only when per-role creation is disabled.

Suggested resolution
-# create=true with no custom name falls back to the shared ServiceAccount name
+# create=true with no custom name uses a role-scoped generated name
+printf "%s-%s" (include "ourios.fullname" $root) $role
🤖 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 `@deploy/helm/ourios/templates/_helpers.tpl` around lines 70 - 89, The
roleServiceAccountName helper must generate a unique role-scoped name when the
role’s serviceAccount.create is true but no name is provided. Update that branch
to use the fullname plus role suffix default, while preserving the explicitly
configured name and shared ourios.serviceAccountName fallback when role-specific
creation is disabled.
🤖 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 `@deploy/helm/ourios/templates/_helpers.tpl`:
- Around line 70-89: The roleServiceAccountName helper must generate a unique
role-scoped name when the role’s serviceAccount.create is true but no name is
provided. Update that branch to use the fullname plus role suffix default, while
preserving the explicitly configured name and shared ourios.serviceAccountName
fallback when role-specific creation is disabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c17cce3c-a1cb-4b57-975d-0670fe2cbea3

📥 Commits

Reviewing files that changed from the base of the PR and between e456b30 and e08c3dc.

📒 Files selected for processing (4)
  • deploy/helm/ourios/README.md
  • deploy/helm/ourios/templates/_helpers.tpl
  • deploy/helm/ourios/templates/serviceaccount.yaml
  • deploy/helm/ourios/values.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • deploy/helm/ourios/templates/serviceaccount.yaml
  • deploy/helm/ourios/values.yaml
  • deploy/helm/ourios/README.md

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread deploy/helm/ourios/README.md

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

deploy/helm/ourios/values.yaml:113

  • The receiver serviceAccount comment says create=true + empty name renders <release>-ourios-receiver, but the actual rendered name is <ourios.fullname>-receiver. When the Helm release name already contains the chart name (common for a release named ourios/ourios-prod), ourios.fullname becomes the release name and the generated ServiceAccount is <release>-receiver, not <release>-ourios-receiver. Updating the comment avoids misconfiguring IRSA trust-policy subjects.
  # that role needs Get/List/Put but NOT s3:DeleteObject. `name`:
  # create=true + empty name renders `<release>-ourios-receiver`;
  # create=true + name overrides that; create=false + name binds an
  # EXISTING ServiceAccount of that name (managed out-of-band).

Comment thread deploy/helm/ourios/templates/_helpers.tpl
Comment thread deploy/helm/ourios/README.md
Comment thread deploy/helm/ourios/README.md Outdated
…e>-<role>

Truncate the base before joining the role suffix so a maximal fullname
can never collapse the three role ServiceAccounts into one name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread deploy/helm/ourios/values.yaml Outdated
Comment thread deploy/helm/ourios/README.md
Comment thread deploy/helm/ourios/README.md Outdated
…compaction

Verified in code: the ingester's production store calls are put_blocking
alone (the one list_blocking is in a test); Manifest publish-CAS lives in
the compaction path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread deploy/helm/ourios/README.md Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@jensholdgaard
jensholdgaard merged commit 780114e into main Jul 20, 2026
25 checks passed
@jensholdgaard
jensholdgaard deleted the helm-per-role-irsa branch July 20, 2026 04:23
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.

docs(helm): document IRSA / short-lived per-role S3 credentials + SPIRE-compatible cert rotation

2 participants