From 6a5f089300e17fddec333e68d72327782dab1ac5 Mon Sep 17 00:00:00 2001 From: Marc LeBlanc <7050295+marcleblanc2@users.noreply.github.com> Date: Tue, 29 Sep 2026 12:22:38 -0600 Subject: [PATCH 1/2] executor/k8s: create a dedicated ServiceAccount for job pods Job pods spawned by the Kubernetes executor ran as the namespace default ServiceAccount with its token automounted, sharing an identity with every other workload in the namespace. Add executor.kubernetesJob.serviceAccount values, render an sg-executor-job ServiceAccount with no RBAC bindings and automountServiceAccountToken: false, and pass the name and automount setting to the executor via KUBERNETES_JOB_SERVICE_ACCOUNT_NAME and KUBERNETES_JOB_AUTOMOUNT_SERVICE_ACCOUNT_TOKEN (sourcegraph/sourcegraph#16392). The ServiceAccount is rendered only when executor.configureRbac is true, so a second executor release in the same namespace does not try to own it. Amp-Thread-ID: https://ampcode.com/threads/T-01a0e96e-01bb-762e-a752-d2a3103593f0 Co-authored-by: Amp --- charts/sourcegraph-executor/k8s/README.md | 4 + .../k8s/templates/executor.ConfigMap.yaml | 2 + .../templates/executor.JobServiceAccount.yaml | 16 ++++ .../k8s/tests/executor_test.yaml | 91 +++++++++++++++++++ charts/sourcegraph-executor/k8s/values.yaml | 9 ++ 5 files changed, 122 insertions(+) create mode 100644 charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml diff --git a/charts/sourcegraph-executor/k8s/README.md b/charts/sourcegraph-executor/k8s/README.md index 1b5905a9..ea5836fc 100644 --- a/charts/sourcegraph-executor/k8s/README.md +++ b/charts/sourcegraph-executor/k8s/README.md @@ -84,6 +84,10 @@ In addition to the documented values, the `executor` and `private-docker-registr | executor.kubernetesJob.resources.requests.memory | string | `"1Gi"` | The requested memory for a job. | | executor.kubernetesJob.runAsGroup | int | `nil`; accepts [0, 2147483647] | The group ID to run Kubernetes jobs as. | | executor.kubernetesJob.runAsUser | int | `nil`; accepts [0, 2147483647] | The user ID to run Kubernetes jobs as. | +| executor.kubernetesJob.serviceAccount.annotations | object | `{}` | Annotations to add to the created ServiceAccount | +| executor.kubernetesJob.serviceAccount.automountToken | bool | `false` | Mount the ServiceAccount token into job pods. Jobs only talk to the Sourcegraph frontend and never need the Kubernetes API. Applied on both the created ServiceAccount and, on executor images that include sourcegraph/sourcegraph#16392, the job pod spec. | +| executor.kubernetesJob.serviceAccount.create | bool | `true` | Create a ServiceAccount for job pods, with no RBAC bindings. Rendered only when `executor.configureRbac` is also true, so a second executor release in the same namespace does not try to own it. Set to false to use an existing ServiceAccount named by `executor.kubernetesJob.serviceAccount.name`. | +| executor.kubernetesJob.serviceAccount.name | string | `"sg-executor-job"` | The ServiceAccount job pods run as. Must exist in `executor.namespace`. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. | | executor.log.format | string | `"condensed"` | | | executor.log.level | string | `"warn"` | Possible values are `dbug`, `info`, `warn`, `eror`, `crit`. | | executor.log.trace | string | `"false"` | | diff --git a/charts/sourcegraph-executor/k8s/templates/executor.ConfigMap.yaml b/charts/sourcegraph-executor/k8s/templates/executor.ConfigMap.yaml index 6e773607..a4e639dd 100644 --- a/charts/sourcegraph-executor/k8s/templates/executor.ConfigMap.yaml +++ b/charts/sourcegraph-executor/k8s/templates/executor.ConfigMap.yaml @@ -37,6 +37,8 @@ data: KUBERNETES_RUN_AS_USER: "{{ .Values.executor.kubernetesJob.runAsUser }}" KUBERNETES_RUN_AS_GROUP: "{{ .Values.executor.kubernetesJob.runAsGroup }}" KUBERNETES_FS_GROUP: "{{ .Values.executor.kubernetesJob.fsGroup }}" + KUBERNETES_JOB_SERVICE_ACCOUNT_NAME: "{{ .Values.executor.kubernetesJob.serviceAccount.name }}" + KUBERNETES_JOB_AUTOMOUNT_SERVICE_ACCOUNT_TOKEN: "{{ .Values.executor.kubernetesJob.serviceAccount.automountToken }}" EXECUTOR_DOCKER_ADD_HOST_GATEWAY: "{{.Values.executor.dockerAddHostGateway }}" KUBERNETES_KEEP_JOBS: "{{ .Values.executor.debug.keepJobs }}" diff --git a/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml b/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml new file mode 100644 index 00000000..bb105305 --- /dev/null +++ b/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.executor.configureRbac .Values.executor.kubernetesJob.serviceAccount.create .Values.executor.kubernetesJob.serviceAccount.name }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.executor.kubernetesJob.serviceAccount.name }} + labels: + category: rbac + deploy: sourcegraph + sourcegraph-resource-requires: cluster-admin + app.kubernetes.io/component: executor +{{- with .Values.executor.kubernetesJob.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +automountServiceAccountToken: {{ .Values.executor.kubernetesJob.serviceAccount.automountToken }} +{{- end }} diff --git a/charts/sourcegraph-executor/k8s/tests/executor_test.yaml b/charts/sourcegraph-executor/k8s/tests/executor_test.yaml index 256638de..a24e8ff3 100644 --- a/charts/sourcegraph-executor/k8s/tests/executor_test.yaml +++ b/charts/sourcegraph-executor/k8s/tests/executor_test.yaml @@ -3,6 +3,7 @@ templates: - executor.Deployment.yaml - executor.Service.yaml - executor.ConfigMap.yaml + - executor.JobServiceAccount.yaml tests: - it: should render the Deployment, Service, ConfigMap if executor is enabled set: @@ -125,3 +126,93 @@ tests: path: spec.template.spec.securityContext.runAsUser - isNull: path: spec.template.spec.securityContext.runAsGroup + + - it: should render the job ServiceAccount without a token mount by default + template: executor.JobServiceAccount.yaml + set: + executor: + queueName: "test" + asserts: + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + name: sg-executor-job + - equal: + path: automountServiceAccountToken + value: false + - isNull: + path: metadata.annotations + + - it: should pass the job ServiceAccount settings to the executor by default + template: executor.ConfigMap.yaml + set: + executor: + queueName: "test" + asserts: + - equal: + path: data.KUBERNETES_JOB_SERVICE_ACCOUNT_NAME + value: sg-executor-job + - equal: + path: data.KUBERNETES_JOB_AUTOMOUNT_SERVICE_ACCOUNT_TOKEN + value: "false" + + - it: should not render the job ServiceAccount when configureRbac is false + template: executor.JobServiceAccount.yaml + set: + executor: + queueName: "test" + configureRbac: false + asserts: + - hasDocuments: + count: 0 + + - it: should not render the job ServiceAccount when create is false but still pass the name + set: + executor: + queueName: "test" + kubernetesJob: + serviceAccount: + create: false + name: my-existing-job-sa + asserts: + - hasDocuments: + count: 0 + template: executor.JobServiceAccount.yaml + - equal: + path: data.KUBERNETES_JOB_SERVICE_ACCOUNT_NAME + value: my-existing-job-sa + template: executor.ConfigMap.yaml + + - it: should fall back to the namespace default ServiceAccount when name is empty + set: + executor: + queueName: "test" + kubernetesJob: + serviceAccount: + name: "" + asserts: + - hasDocuments: + count: 0 + template: executor.JobServiceAccount.yaml + - equal: + path: data.KUBERNETES_JOB_SERVICE_ACCOUNT_NAME + value: "" + template: executor.ConfigMap.yaml + + - it: should render job ServiceAccount annotations and token mount when set + template: executor.JobServiceAccount.yaml + set: + executor: + queueName: "test" + kubernetesJob: + serviceAccount: + automountToken: true + annotations: + iam.gke.io/gcp-service-account: jobs@example.iam.gserviceaccount.com + asserts: + - equal: + path: metadata.annotations["iam.gke.io/gcp-service-account"] + value: jobs@example.iam.gserviceaccount.com + - equal: + path: automountServiceAccountToken + value: true diff --git a/charts/sourcegraph-executor/k8s/values.yaml b/charts/sourcegraph-executor/k8s/values.yaml index 0bc1320e..717d0514 100644 --- a/charts/sourcegraph-executor/k8s/values.yaml +++ b/charts/sourcegraph-executor/k8s/values.yaml @@ -100,6 +100,15 @@ executor: runAsGroup: # -- The group ID which is set on the job PVC file system. fsGroup: "1000" + serviceAccount: + # -- Create a ServiceAccount for job pods, with no RBAC bindings. Rendered only when `executor.configureRbac` is also true, so a second executor release in the same namespace does not try to own it. Set to false to use an existing ServiceAccount named by `executor.kubernetesJob.serviceAccount.name`. + create: true + # -- The ServiceAccount job pods run as. Must exist in `executor.namespace`. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. + name: sg-executor-job + # -- Annotations to add to the created ServiceAccount + annotations: { } + # -- Mount the ServiceAccount token into job pods. Jobs only talk to the Sourcegraph frontend and never need the Kubernetes API. Applied on both the created ServiceAccount and, on executor images that include sourcegraph/sourcegraph#16392, the job pod spec. + automountToken: false resources: requests: # -- The requested CPU for a job. From d9017a09db55e1981135e449f2cd948315c6bfa7 Mon Sep 17 00:00:00 2001 From: Marc LeBlanc <7050295+marcleblanc2@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:53:06 -0600 Subject: [PATCH 2/2] executor/k8s: create the job ServiceAccount in executor.namespace A pod can only reference a ServiceAccount in its own namespace, so the job ServiceAccount must live where the job pods run, not in the release namespace. Amp-Thread-ID: https://ampcode.com/threads/T-01a0e96e-01bb-762e-a752-d2a3103593f0 Co-authored-by: Amp --- charts/sourcegraph-executor/k8s/README.md | 2 +- .../k8s/templates/executor.JobServiceAccount.yaml | 1 + .../sourcegraph-executor/k8s/tests/executor_test.yaml | 11 +++++++++++ charts/sourcegraph-executor/k8s/values.yaml | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/charts/sourcegraph-executor/k8s/README.md b/charts/sourcegraph-executor/k8s/README.md index ea5836fc..14b0fdff 100644 --- a/charts/sourcegraph-executor/k8s/README.md +++ b/charts/sourcegraph-executor/k8s/README.md @@ -87,7 +87,7 @@ In addition to the documented values, the `executor` and `private-docker-registr | executor.kubernetesJob.serviceAccount.annotations | object | `{}` | Annotations to add to the created ServiceAccount | | executor.kubernetesJob.serviceAccount.automountToken | bool | `false` | Mount the ServiceAccount token into job pods. Jobs only talk to the Sourcegraph frontend and never need the Kubernetes API. Applied on both the created ServiceAccount and, on executor images that include sourcegraph/sourcegraph#16392, the job pod spec. | | executor.kubernetesJob.serviceAccount.create | bool | `true` | Create a ServiceAccount for job pods, with no RBAC bindings. Rendered only when `executor.configureRbac` is also true, so a second executor release in the same namespace does not try to own it. Set to false to use an existing ServiceAccount named by `executor.kubernetesJob.serviceAccount.name`. | -| executor.kubernetesJob.serviceAccount.name | string | `"sg-executor-job"` | The ServiceAccount job pods run as. Must exist in `executor.namespace`. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. | +| executor.kubernetesJob.serviceAccount.name | string | `"sg-executor-job"` | The ServiceAccount job pods run as. Created in `executor.namespace`, where the job pods run. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. | | executor.log.format | string | `"condensed"` | | | executor.log.level | string | `"warn"` | Possible values are `dbug`, `info`, `warn`, `eror`, `crit`. | | executor.log.trace | string | `"false"` | | diff --git a/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml b/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml index bb105305..ecad3c94 100644 --- a/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml +++ b/charts/sourcegraph-executor/k8s/templates/executor.JobServiceAccount.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ .Values.executor.kubernetesJob.serviceAccount.name }} + namespace: {{ .Values.executor.namespace }} labels: category: rbac deploy: sourcegraph diff --git a/charts/sourcegraph-executor/k8s/tests/executor_test.yaml b/charts/sourcegraph-executor/k8s/tests/executor_test.yaml index a24e8ff3..a65425da 100644 --- a/charts/sourcegraph-executor/k8s/tests/executor_test.yaml +++ b/charts/sourcegraph-executor/k8s/tests/executor_test.yaml @@ -143,6 +143,17 @@ tests: - isNull: path: metadata.annotations + - it: should create the job ServiceAccount in the job namespace + template: executor.JobServiceAccount.yaml + set: + executor: + queueName: "test" + namespace: sourcegraph-executor-jobs + asserts: + - equal: + path: metadata.namespace + value: sourcegraph-executor-jobs + - it: should pass the job ServiceAccount settings to the executor by default template: executor.ConfigMap.yaml set: diff --git a/charts/sourcegraph-executor/k8s/values.yaml b/charts/sourcegraph-executor/k8s/values.yaml index 717d0514..923878bb 100644 --- a/charts/sourcegraph-executor/k8s/values.yaml +++ b/charts/sourcegraph-executor/k8s/values.yaml @@ -103,7 +103,7 @@ executor: serviceAccount: # -- Create a ServiceAccount for job pods, with no RBAC bindings. Rendered only when `executor.configureRbac` is also true, so a second executor release in the same namespace does not try to own it. Set to false to use an existing ServiceAccount named by `executor.kubernetesJob.serviceAccount.name`. create: true - # -- The ServiceAccount job pods run as. Must exist in `executor.namespace`. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. + # -- The ServiceAccount job pods run as. Created in `executor.namespace`, where the job pods run. Empty falls back to the namespace `default` ServiceAccount. Requires an executor image that includes sourcegraph/sourcegraph#16392; older executors ignore this setting. name: sg-executor-job # -- Annotations to add to the created ServiceAccount annotations: { }