feat(charts): integration job ServiceAccount IRSA, runner apiTokenSource, full values docs - #116
Conversation
…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>
… 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>
… 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>
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>
… 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>
Validated live on EKS ✅Deployed this chart ( Configured path (
Default path (
Version-coupling caveat: |
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>
|
Bumped |
…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
left a comment
There was a problem hiding this comment.
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'ttrim, so a whitespace-onlyapiToken/accountIDslips 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 |
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 manualkubectl annotatethat 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 asINTEGRATION_JOB_SERVICE_ACCOUNT; addsintegration.serviceAccount.annotationsfor thekubernetes-managedSA. DeclaresnameOverride,controllerManager.pod.labels,crd.keep(read by templates, missing from values). Fixes ServiceMonitorserverName, which named a Service that does not exist so TLS scraping failed withprometheus.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 placeholderaccountID/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 lintclean; 60 render assertions across four test scripts.https://jupiterone.atlassian.net/browse/TD-9103