Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ name: Pull Request Checks

on:
pull_request:
paths:
- 'charts/jupiterone-integration-operator/**'
- 'charts/jupiterone-integration-runner/**'

jobs:
verify-operator-crds:
Expand All @@ -18,3 +15,36 @@ jobs:
run: make verify-crds
env:
GH_TOKEN: ${{ secrets.AUTO_GITHUB_PAT_TOKEN }}

chart-tests:
name: Chart Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: helm-charts

- name: Set up Helm
uses: azure/setup-helm@v4

- name: Lint charts
run: |
helm lint helm-charts/charts/jupiterone-integration-operator
helm lint helm-charts/charts/jupiterone-integration-runner \
--set accountID=ci --set apiToken=ci

- name: Run chart render tests
run: |
set -euo pipefail
status=0
for script in \
helm-charts/charts/jupiterone-integration-operator/tests/declared-values_test.sh \
helm-charts/charts/jupiterone-integration-operator/tests/private-registry-values_test.sh \
helm-charts/charts/jupiterone-integration-operator/tests/irsa-service-account_test.sh \
helm-charts/charts/jupiterone-integration-runner/tests/runner-values_test.sh; do
echo "::group::$script"
bash "$script" || status=1
echo "::endgroup::"
done
exit "$status"
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ sync-crds: ## Sync CRDs from the latest jupiterone-integration-operator release
tar xzf operator.tar.gz -C _operator_src && \
echo "Syncing CRDs to $(OPERATOR_CRD_DIR)..." && \
for f in _operator_src/*/config/crd/bases/*.yaml; do \
sed '1{/^---$$/d;}' "$$f" > "$(OPERATOR_CRD_DIR)/$$(basename $$f)"; \
sed '1{/^---$$/d;}' "$$f" | awk '/^ annotations:$$/ && !done { print; print " {{- if .Values.crd.keep }}"; print " \"helm.sh/resource-policy\": keep"; print " {{- end }}"; done=1; next } { print }' > "$(OPERATOR_CRD_DIR)/$$(basename $$f)"; \
echo " Synced $$(basename $$f)"; \
done && \
rm -rf _operator_src operator.tar.gz && \
Expand Down
4 changes: 2 additions & 2 deletions charts/jupiterone-integration-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ apiVersion: v2
name: jupiterone-integration-operator
description: JupiterOne Integration Operator for running integrations in Kubernetes
type: application
version: 1.3.4
appVersion: "v0.3.1"
version: 1.4.0
appVersion: "v0.4.0"
167 changes: 163 additions & 4 deletions charts/jupiterone-integration-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,106 @@ Refer to the [values.yaml](./values.yaml) for all available configuration option

| Parameter | Description | Default |
|---|---|---|
| `controllerManager.imageRegistry` | Image registry override for private registry environments. When set, integration job images are pulled from this registry instead of `ghcr.io`. | `""` |
| `controllerManager.imagePullSecrets` | Secrets for pulling images from private registries. Applied to both the operator Deployment and propagated to spawned integration job pods. | `[]` |
| `controllerManager.disableImageSignatureCheck` | Disable cosign image signature verification for integration job images. Set to `true` when using registries that don't mirror ghcr.io cosign signatures. | `false` |
| `controllerManager.jobResources` | Resource requests and limits applied to integration job containers. Configure to comply with cluster resource policies (e.g. Kyverno, OPA/Gatekeeper). | `{}` |
| `nameOverride` | Overrides the chart name used in the `app.kubernetes.io/name` label. | `""` |
| `controllerManager.replicas` | Manager replicas. Leader election is on, so extra replicas are standby only. | `1` |
| `controllerManager.container.image.repository` | Manager image repository. | `ghcr.io/jupiterone/jupiterone-integration-operator` |
| `controllerManager.container.image.tag` | Manager image tag. Empty uses the chart `appVersion`. | `""` |
| `controllerManager.container.args` | Manager command-line flags. | `--leader-elect`, `--metrics-bind-address=:8443`, `--health-probe-bind-address=:8081` |
| `controllerManager.container.env` | Extra environment variables on the manager as a `KEY: value` map (`JOB_TTL_SECONDS`, `JOB_ACTIVE_DEADLINE_SECONDS`, `ASM_CACHE_TTL_SECONDS`, `AWS_REGION`, `LOG_LEVEL`, `HTTP_PROXY`, ...). | `JOB_TTL_SECONDS: "604800"` |
| `controllerManager.container.resources` | Manager container requests and limits. | `100m`/`64Mi` requests, `500m`/`512Mi` limits |
| `controllerManager.container.livenessProbe` | Manager liveness probe. | `GET /healthz` on `8081` |
| `controllerManager.container.readinessProbe` | Manager readiness probe. | `GET /readyz` on `8081` |
| `controllerManager.container.securityContext` | Manager container security context. | `allowPrivilegeEscalation: false`, drop `ALL` |
| `controllerManager.securityContext` | Manager pod security context. | `runAsNonRoot: true`, seccomp `RuntimeDefault` |
| `controllerManager.pod.labels` | Extra labels on the manager pod. | `{}` |
| `controllerManager.terminationGracePeriodSeconds` | Manager pod termination grace period. | `10` |
| `controllerManager.serviceAccountName` | Name of the operator ServiceAccount. | `jupiterone-integration-operator-controller-manager` |
| `controllerManager.serviceAccount.annotations` | Annotations on the operator ServiceAccount. Set the IRSA role here when the operator reads credentials from AWS Secrets Manager. | `{}` |
| `controllerManager.imageRegistry` | Registry that replaces `ghcr.io` for integration job images (`<registry>/jupiterone/graph-<name>:latest`). Hostname only. | `""` |
| `controllerManager.disableImageSignatureCheck` | Skip cosign signature verification of integration job images. | `false` |
| `controllerManager.imagePullSecrets` | `imagePullSecrets` for the manager pod and every integration job pod. | `[]` |
| `controllerManager.jobResources` | Requests and limits applied to integration job containers. | `{}` |
| `rbac.enable` | Create the operator ServiceAccount, Roles and bindings. | `true` |
| `metrics.enable` | Create the metrics Service. Remove `--metrics-bind-address` from `args` when disabling. | `true` |
| `prometheus.enable` | Create a `ServiceMonitor` for the metrics Service. | `false` |
| `certmanager.enable` | Issue the metrics serving certificate with cert-manager. | `false` |
| `crd.keep` | Annotate the operator CRDs (and the metrics Certificate) with `helm.sh/resource-policy: keep` so `helm uninstall` leaves them, and every `IntegrationRunner`/`IntegrationInstance`/`IntegrationInstanceJob`, in place. | `false` |
| `networkPolicy.enable` | Create a NetworkPolicy allowing metrics scrapes from namespaces labeled `metrics: enabled`. | `false` |
| `integration.create` | Create the `kubernetes-managed` integration ServiceAccount, ClusterRole and ClusterRoleBinding. | `true` |
| `integration.serviceAccountName` | ServiceAccount used by `kubernetes-managed` integration job pods (`K8S_INTEGRATION_SERVICE_ACCOUNT`). | `jupiterone` |
| `integration.serviceAccountNamespace` | Namespace of that ServiceAccount. Changing it is not supported. | `jupiterone` |
| `integration.serviceAccount.annotations` | Annotations on the `kubernetes-managed` integration ServiceAccount. | `{}` |
| `integration.jobServiceAccount.create` | Create a ServiceAccount for all other integration job pods. | `false` |
| `integration.jobServiceAccount.name` | Name of that ServiceAccount. Defaults to `jupiterone-integration-job` when created. Set without `create` to reference one managed elsewhere. | `""` |
| `integration.jobServiceAccount.annotations` | Annotations on the integration job ServiceAccount. Set the IRSA role here to give job pods an AWS identity. | `{}` |

Environment variables the manager reads that have no dedicated value are set
through `controllerManager.container.env`:

| Variable | Description | Default |
|---|---|---|
| `JOB_TTL_SECONDS` | Seconds a finished `IntegrationInstanceJob` and its Job are kept. | `2592000`; the chart sets `604800` |
| `JOB_ACTIVE_DEADLINE_SECONDS` | `activeDeadlineSeconds` on each integration Job. | `86400` |
| `ASM_CACHE_TTL_SECONDS` | Cache TTL for AWS Secrets Manager lookups; `0` disables. | `60` |
| `AWS_REGION` | Region for the AWS SDK default chain when a CR omits `region`. | unset |
| `LOG_LEVEL` | `debug`, `info`, `warn` or `error`. | `info` |
| `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` | Proxy settings; also injected into integration job pods. | unset |

### ServiceAccount annotations (IRSA)

Every ServiceAccount the chart creates accepts annotations, so an IAM role can
be attached without editing rendered manifests:

```yaml
controllerManager:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/jupiterone-integration-operator
```

The operator needs that role only when a CR reads credentials from AWS Secrets
Manager (`apiTokenSource` / `secretSource` with `provider: awsSecretsManager`).
Integration job pods and the `kubernetes-managed` ServiceAccount take their
annotations from `integration.jobServiceAccount.annotations` and
`integration.serviceAccount.annotations`; see
[AWS Access for Integration Job Pods (IRSA)](#aws-access-for-integration-job-pods-irsa).
Trust policies and permission policies are in the operator README under
[AWS authentication (IRSA)](https://github.com/JupiterOne/jupiterone-integration-operator#aws-authentication-irsa).

### AWS Secrets Manager Example

```yaml
controllerManager:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/jupiterone-integration-operator
container:
env:
AWS_REGION: us-east-1
ASM_CACHE_TTL_SECONDS: "60"
```

Then reference the secret from the runner chart (`apiTokenSource`) or an
`IntegrationInstance` (`secretSource`).

### Metrics with Prometheus and cert-manager

```yaml
metrics:
enable: true
prometheus:
enable: true
certmanager:
enable: true
networkPolicy:
enable: true
```

cert-manager issues `metrics-server-cert` for
`jupiterone-integration-operator-metrics-service.<namespace>.svc`, the
ServiceMonitor scrapes it over TLS, and the NetworkPolicy admits scrapes only
from namespaces labeled `metrics: enabled`. Without `certmanager.enable` the
ServiceMonitor uses `insecureSkipVerify: true`.

### Private Registry Example

Expand All @@ -78,6 +174,69 @@ helm install integration-operator jupiterone/jupiterone-integration-operator \

> **Note:** `disableImageSignatureCheck` is independent of `imageRegistry`. Cosign verification may work through registry proxies since it resolves signatures against the original source. Only disable it if verification fails in your environment.

### AWS Access for Integration Job Pods (IRSA)

Integration job pods run under the `default` ServiceAccount, which normally has
no AWS identity. Integrations that call AWS -- for example SBOM for AWS ECR --
need one. Set `integration.jobServiceAccount` and the chart creates the
ServiceAccount, annotates it for IRSA, and points the operator at it through
`INTEGRATION_JOB_SERVICE_ACCOUNT`, so every integration job pod runs with that
identity.

```yaml
integration:
jobServiceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/jupiterone-integration-job
```

The `kubernetes-managed` integration keeps its own ServiceAccount (it is bound
to the in-cluster read ClusterRole) and is annotated separately:

```yaml
integration:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/jupiterone-kubernetes-managed
```

The operator itself needs a role only when a CR resolves credentials from AWS
Secrets Manager:

```yaml
controllerManager:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/jupiterone-integration-operator
```

**Same-account ECR.** Grant the job role ECR read directly
(`ecr:GetAuthorizationToken` on `*`, plus `ecr:BatchGetImage`,
`ecr:GetDownloadUrlForLayer`, `ecr:BatchCheckLayerAvailability`,
`ecr:DescribeRepositories`, `ecr:DescribeImages`, `ecr:ListImages`,
`ecr:ListTagsForResource` on the repository ARNs).

**Cross-account ECR.** Grant the job role only `sts:AssumeRole` on the role in
the registry's account; that role holds the ECR permissions and trusts the job
role. That trust is between two identities the customer owns: the `Principal`
is the customer's job pod role, not a JupiterOne AWS account. Several registries
means one such role per account and one `sts:AssumeRole` resource per target.

This ServiceAccount is shared by every integration job pod in the release, so
keep its own policy to the `sts:AssumeRole` targets it needs and leave the ECR
permissions on the assumed roles. Where a workload needs stronger separation,
install a second operator and runner in their own namespace with their own
`integration.jobServiceAccount`.

Trust policies, IAM policy documents and the step-by-step SBOM for AWS ECR
setup are in the operator repository:
[AWS authentication (IRSA)](https://github.com/JupiterOne/jupiterone-integration-operator#aws-authentication-irsa).

Requires the operator release that adds `INTEGRATION_JOB_SERVICE_ACCOUNT`
(`v0.4.0`). On older operators the env var is ignored and job pods keep using
the `default` ServiceAccount.

### Job Resources Example

If your cluster enforces resource policies (e.g. Kyverno `require-requests-limits`), configure resource requirements for integration job containers:
Expand Down
21 changes: 13 additions & 8 deletions charts/jupiterone-integration-operator/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
{{- define "chart.name" -}}
{{- if .Chart }}
{{- if .Chart.Name }}
{{- .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- else if .Values.nameOverride }}
{{ .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
jupiterone-integration-operator
{{- end }}
{{- if .Values.nameOverride }}
{{- .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- else if and .Chart .Chart.Name }}
{{- .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
jupiterone-integration-operator
{{- end }}
Expand Down Expand Up @@ -48,3 +44,12 @@ app.kubernetes.io/instance: {{ .Release.Name }}
$hasValidating = true }}{{- end }}
{{- end }}
{{ $hasValidating }}}}{{- end }}


{{- define "chart.integrationJobServiceAccountName" -}}
{{- if .Values.integration.jobServiceAccount.name -}}
{{- .Values.integration.jobServiceAccount.name -}}
{{- else if .Values.integration.jobServiceAccount.create -}}
jupiterone-integration-job
{{- end -}}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
{{- if .Values.crd.keep }}
"helm.sh/resource-policy": keep
{{- end }}
controller-gen.kubebuilder.io/version: v0.18.0
name: integrationinstancejobs.integrations.jupiterone.io
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
{{- if .Values.crd.keep }}
"helm.sh/resource-policy": keep
{{- end }}
controller-gen.kubebuilder.io/version: v0.18.0
name: integrationinstances.integrations.jupiterone.io
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
{{- if .Values.crd.keep }}
"helm.sh/resource-policy": keep
{{- end }}
controller-gen.kubebuilder.io/version: v0.18.0
name: integrationrunners.integrations.jupiterone.io
spec:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{- if and .Values.integration.jobServiceAccount.create (include "chart.integrationJobServiceAccountName" .) -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "chart.integrationJobServiceAccountName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "chart.labels" . | nindent 4 }}
{{- with .Values.integration.jobServiceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ metadata:
namespace: {{ .Values.integration.serviceAccountNamespace }}
labels:
{{- include "chart.labels" . | nindent 4 }}
{{- end }}
{{- with .Values.integration.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ spec:
control-plane: controller-manager
{{- if and .Values.controllerManager.pod .Values.controllerManager.pod.labels }}
{{- range $key, $value := .Values.controllerManager.pod.labels }}
{{ $key }}: {{ $value }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
spec:
Expand All @@ -45,6 +45,10 @@ spec:
fieldPath: metadata.namespace
- name: K8S_INTEGRATION_SERVICE_ACCOUNT
value: {{ .Values.integration.serviceAccountName }}
{{- with (include "chart.integrationJobServiceAccountName" .) }}
- name: INTEGRATION_JOB_SERVICE_ACCOUNT
value: {{ . | quote }}
{{- end }}
{{- if .Values.controllerManager.imageRegistry }}
- name: IMAGE_REGISTRY
value: {{ .Values.controllerManager.imageRegistry | quote }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ spec:
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
tlsConfig:
{{- if .Values.certmanager.enable }}
serverName: jupiterone-integration-operator-controller-manager-metrics-service.{{ .Release.Namespace }}.svc
serverName: jupiterone-integration-operator-metrics-service.{{ .Release.Namespace }}.svc
# Apply secure TLS configuration with cert-manager
insecureSkipVerify: false
ca:
Expand Down
Loading
Loading