Skip to content

feat(charts): integration job ServiceAccount IRSA, runner apiTokenSource, full values docs - #116

Merged
VDubber merged 12 commits into
mainfrom
feat/integration-job-irsa-service-account
Sep 22, 2026
Merged

VDubber merged 12 commits into
mainfrom
feat/integration-job-irsa-service-account

Conversation

@ryanmcafee

@ryanmcafee ryanmcafee commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Integration job pods run as default, and the chart could not annotate it -- so giving SBOM for AWS ECR an AWS identity to read a customer registry (same or cross account) meant a manual kubectl annotate that is lost whenever the namespace is rebuilt. This PR also absorbs the chart audit from #117 so the operator chart ships one release.

Operator chart 1.3.4 -> 1.4.0. Adds integration.jobServiceAccount (create/name/annotations): the chart creates the ServiceAccount, annotates it for IRSA and passes it to the operator as INTEGRATION_JOB_SERVICE_ACCOUNT; adds integration.serviceAccount.annotations for the kubernetes-managed SA. Declares nameOverride, controllerManager.pod.labels, crd.keep (read by templates, missing from values). Fixes ServiceMonitor serverName, which named a Service that does not exist so TLS scraping failed with prometheus.enable + certmanager.enable. README documents every value, the manager env vars, all three ServiceAccount annotation points, Secrets Manager and metrics setups. Defaults unchanged.

Runner chart 1.0.3 -> 1.1.0. Exposes apiTokenSource (Kubernetes Secret or AWS Secrets Manager), which the CRD already supported; refuses to render with placeholder accountID/apiToken; README rewritten (broken example, upgrade/uninstall acted on the operator release) and states the runner has no ServiceAccount -- job pod identity is operator-chart config.

Depends on JupiterOne/jupiterone-integration-operator#53 (operator side of the env var; older operators ignore it). Docs in jupiterone-integration-operator#52. helm lint clean; 60 render assertions across four test scripts.

https://jupiterone.atlassian.net/browse/TD-9103

…b service accounts

Integration job pods run under the `default` ServiceAccount, and the chart
had no way to annotate it, so giving a job pod an AWS identity meant
running `kubectl annotate` out of band and re-running it whenever the
namespace was rebuilt. Integrations that call AWS -- SBOM for AWS ECR
reading a customer registry in the same or another account -- need that
identity to exist as configuration, not as a manual step.

Adds `integration.jobServiceAccount` (create/name/annotations), which
creates the ServiceAccount and passes it to the operator via
INTEGRATION_JOB_SERVICE_ACCOUNT so every integration job pod runs under
it, and `integration.serviceAccount.annotations` for the kubernetes-managed
integration ServiceAccount. Both default off, so rendered output is
unchanged unless the values are set.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
@ryanmcafee
ryanmcafee requested a review from a team as a code owner September 16, 2026 19:17
… scope

The job ServiceAccount is shared by every integration job pod in a
release, so record what belongs on its role (assume-role targets only)
and how to separate workloads that need their own identity.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
ryanmcafee and others added 4 commits September 16, 2026 14:37
… the customer collector

On a self-hosted collector the customer cluster assumes the scan role, so
the trust Principal is the customer job pod role and sts:ExternalId is the
External ID on that customer integration instance, not a JupiterOne
account or the managed AWS integration External ID.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
…account ECR guidance

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
…examples page

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
… metrics TLS name

Operator chart (1.3.5): declare nameOverride, controllerManager.pod.labels
and crd.keep, which templates already read but values.yaml never listed;
fix the ServiceMonitor serverName, which pointed at a Service that does
not exist so TLS scraping failed whenever prometheus and cert-manager were
both enabled; document all values, the manager environment variables and
the ServiceAccount annotation, Secrets Manager and metrics setups in the
README; add a test that fails when a template reads an undeclared value.

Runner chart (1.1.0): expose apiTokenSource so the runner can read its API
token from AWS Secrets Manager or a named Kubernetes Secret, which the CRD
already supported; refuse to render with a placeholder accountID or
apiToken instead of registering a runner with a literal "<api-token>";
rewrite the README, whose example used values that do not exist and whose
upgrade and uninstall commands acted on the operator release, and state
that the runner has no ServiceAccount of its own so job pod identity is
configured on the operator chart; add render tests.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
…eat/integration-job-irsa-service-account

Brings the full values reference, runner apiTokenSource support and the
ServiceMonitor serverName fix into the integration ServiceAccount PR so the
operator chart ships one 1.4.0 release. Conflicts in Chart.yaml (keep
1.4.0) and README (full parameters table plus the integration
ServiceAccount rows) resolved by hand.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
@ryanmcafee ryanmcafee changed the title feat(jupiterone-integration-operator): IRSA support for integration job ServiceAccounts feat(charts): integration job ServiceAccount IRSA, runner apiTokenSource, full values docs Sep 16, 2026
The chart test scripts only ran when someone remembered to run them
locally. A Chart Tests job now lints both charts and runs every render
test script on pull requests touching either chart, failing the job on
any assertion failure. The scripts expect the repo checked out under a
helm-charts/ directory, so the job checks out to that path.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
Comment thread charts/jupiterone-integration-operator/values.yaml Outdated
ryanmcafee and others added 2 commits September 16, 2026 16:52
… in tests

crd.keep only annotated the metrics Certificate, but the flag exists to
keep the operator CRDs (and the custom resources under them) across helm
uninstall. sync-crds now injects the helm.sh/resource-policy: keep block
into each synced CRD, so verify-crds keeps passing and the annotation
follows every future sync.

Test helpers piped the rendered chart into grep -q, which exits on the
first match and leaves echo with a broken pipe; under pipefail a matched
assertion then reported failure, which is what broke the first CI run.
Assertions now read the haystack from a here-string, and needles are
passed after -- so a value starting with - is not read as a flag.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
Chart Tests and Verify Operator CRDs are required status checks on main.
A required check that only runs for some paths leaves every other PR
unmergeable, so the path filter goes; both jobs take under a minute.

Co-Authored-By: Ryan McAfee - Bot <ryan.mcafee+bot@jupiterone.com>
@VDubber

VDubber commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Validated live on EKS ✅

Deployed this chart (jupiterone-integration-operator-1.4.0) with an operator image built from jupiterone-integration-operator#53, on an EKS collector running SBOM for AWS ECR.

Configured path (integration.jobServiceAccount.create=true):

  • Chart created + IRSA-annotated the jupiterone-integration-job SA (carries meta.helm.sh/release-name) and wired INTEGRATION_JOB_SERVICE_ACCOUNT onto the manager.
  • Job pod ran under that SA, picked up its IRSA identity, default left unannotated, scan succeeded — no more shared-default hack.

Default path (create=false):

  • INTEGRATION_JOB_SERVICE_ACCOUNT absent; job pod reverted to default; scan still succeeded. Backward compatible.

Version-coupling caveat: appVersion: v0.3.1 predates operator#53 (still unmerged). Shipping the chart against v0.3.1 makes jobServiceAccount a silent no-op (SA created + annotated, but the operator ignores the env → pods stay on default). Merge order: operator#53 → operator release → bump this chart's appVersion → merge here.

Point the operator chart at v0.4.0, the release containing
jupiterone-integration-operator#53 (INTEGRATION_JOB_SERVICE_ACCOUNT).
Without this, integration.jobServiceAccount is a silent no-op: the chart
creates + annotates the ServiceAccount and sets the env, but the older
operator image ignores it and job pods stay on `default`.

Verified: ghcr.io/jupiterone/jupiterone-integration-operator:v0.4.0 is
published and contains jobServiceAccount(); end-to-end validated on EKS
with SBOM for AWS ECR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@VDubber

VDubber commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Bumped appVersion v0.3.1 → v0.4.0 (commit b0bd318). operator#53 is merged and released as v0.4.0; ghcr.io/jupiterone/jupiterone-integration-operator:v0.4.0 is published and contains jobServiceAccount(). The version-coupling gate is now cleared — the chart's integration.jobServiceAccount will hit an operator image that honors INTEGRATION_JOB_SERVICE_ACCOUNT instead of silently no-op'ing. Ready to merge; docs (jupiterone-integration-operator#52) after.

…fective

- manager.yaml: quote controllerManager.pod.labels values so non-string
  values (bool/number/version) render as strings; unquoted values produced
  invalid label maps that the Kubernetes API server rejects (422).
- _helpers.tpl: reorder chart.name so nameOverride actually overrides the
  app.kubernetes.io/name label. The prior helper checked .Chart.Name first
  (always set), leaving nameOverride permanently inert despite being
  declared in values.yaml and documented in the README.
- declared-values_test.sh: assert the quoted label value, and assert
  nameOverride takes effect (with default fallback) instead of codifying
  the dead behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@VDubber VDubber 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.

Review — Approve ✅

Multi-agent review (Senior DevOps + intent, adversarially cross-verified). Goal (TD-9103) fully accomplished: integration-job ServiceAccount + IRSA annotations wired to INTEGRATION_JOB_SERVICE_ACCOUNT (matches operator #53), serviceAccount.annotations, runner apiTokenSource + placeholder guards, ServiceMonitor serverName fix (now matches the real metrics Service + Certificate SANs), additive crd.keep blocks, docs. Defaults confirmed unchanged — new behavior opt-in and reversible.

Two findings — fixed in c6b411c

Severity File Issue Fix
Medium templates/manager/manager.yaml controllerManager.pod.labels rendered {{ $value }} unquoted — a non-string value (enabled=true, a number, or a version) produced a YAML bool/number, which the K8s API server rejects (label values must be strings) with no helm lint warning. {{ $value | quote }} → renders enabled: "true".
Low templates/_helpers.tpl nameOverride was declared in values.yaml + documented in the README but permanently inert: chart.name checked .Chart.Name first (always set), so the override branch was unreachable. Reordered to standard Helm precedence (nameOverride wins, nil-safe .Chart fallback).

Tests updated in declared-values_test.sh: quoted-label assertion, and test_name_override now asserts the override takes effect (+ default fallback) instead of codifying the dead behavior. All suites green (declared-values 10/10, irsa 12/12, private-registry 22/22); helm lint clean.

Remaining (optional, Info)

  • Runner placeholder guard (secret.yaml/runner.yaml) doesn't trim, so a whitespace-only apiToken/accountID slips through. Narrow; runtime auth fails clearly downstream. Optional hardening or a README note.
Dimension Score
Goal accomplishment 5/5
Security risk 2/5
Data volume impact 1/5
Performance risk 1/5

@VDubber
VDubber merged commit 4723644 into main Sep 22, 2026
4 checks passed
@VDubber
VDubber deleted the feat/integration-job-irsa-service-account branch September 22, 2026 17:02
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.

3 participants