From 3c027c27a70bb8c329b1a29c6ea0b9f570717266 Mon Sep 17 00:00:00 2001 From: Craig Squire Date: Sun, 10 May 2020 12:04:38 -0500 Subject: [PATCH 1/2] Add support for separate kubernetes containers security contexts --- common/config.go | 281 ++++++++++---- common/config_test.go | 471 ++++++++++++++++++++++++ docs/executors/kubernetes.md | 55 ++- executors/kubernetes/kubernetes.go | 117 +++--- executors/kubernetes/kubernetes_test.go | 57 +++ 5 files changed, 845 insertions(+), 136 deletions(-) diff --git a/common/config.go b/common/config.go index 1b01f634bb..d9eae1baf4 100644 --- a/common/config.go +++ b/common/config.go @@ -281,81 +281,84 @@ func (p KubernetesDNSPolicy) Get() (api.DNSPolicy, error) { //nolint:lll type KubernetesConfig struct { - Host string `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"` - CertFile string `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"` - KeyFile string `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"` - CAFile string `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"` - BearerTokenOverwriteAllowed bool `toml:"bearer_token_overwrite_allowed" json:"bearer_token_overwrite_allowed" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."` - BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."` - Image string `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"` - Namespace string `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"` - NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed" json:"namespace_overwrite_allowed" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"` - Privileged *bool `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"` - AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."` - CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"` - CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."` - CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"` - CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."` - MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"` - MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."` - MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"` - MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."` - EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"` - EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."` - EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"` - EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."` - ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"` - ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."` - ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"` - ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."` - ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"` - ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."` - ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"` - ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."` - ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"` - ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."` - ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"` - ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."` - HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"` - HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."` - HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"` - HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."` - HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"` - HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."` - HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"` - HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."` - HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"` - HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."` - HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"` - HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."` - AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"` - AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"` - PullPolicy StringOrArray `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"` - NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."` - NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."` - Affinity KubernetesAffinity `toml:"affinity,omitempty" json:"affinity" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"` - ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"` - HelperImage string `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"` - HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"` - TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."` + Host string `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"` + CertFile string `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"` + KeyFile string `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"` + CAFile string `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"` + BearerTokenOverwriteAllowed bool `toml:"bearer_token_overwrite_allowed" json:"bearer_token_overwrite_allowed" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."` + BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."` + Image string `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"` + Namespace string `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"` + NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed" json:"namespace_overwrite_allowed" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"` + Privileged *bool `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."` + CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"` + CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."` + CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"` + CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."` + MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"` + MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."` + MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"` + MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."` + EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"` + EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."` + EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"` + EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."` + ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"` + ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."` + ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"` + ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."` + ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"` + ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."` + ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"` + ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."` + ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"` + ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."` + ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"` + ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."` + HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"` + HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."` + HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"` + HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."` + HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"` + HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."` + HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"` + HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."` + HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"` + HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."` + HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"` + HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."` + AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"` + AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"` + PullPolicy StringOrArray `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"` + NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."` + NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."` + Affinity KubernetesAffinity `toml:"affinity,omitempty" json:"affinity" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"` + ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"` + HelperImage string `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"` + HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"` + TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal.DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."` PodTerminationGracePeriodSeconds *int64 `toml:"pod_termination_grace_period_seconds,omitzero" json:"pod_termination_grace_period_seconds" long:"pod_termination_grace_period_seconds" env:"KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS" description:"Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` CleanupGracePeriodSeconds *int64 `toml:"cleanup_grace_period_seconds,omitzero" json:"cleanup_grace_period_seconds" long:"cleanup_grace_period_seconds" env:"KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS" description:"When cleaning up a pod on completion of a job, the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` - PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"` - PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"` - PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."` - ServiceAccount string `toml:"service_account,omitempty" json:"service_account" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"` - ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed" json:"service_account_overwrite_allowed" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"` - PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"` - PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed" json:"pod_annotations_overwrite_allowed" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"` - PodSecurityContext KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"` - Volumes KubernetesVolumes `toml:"volumes"` - HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases" long:"host_aliases" description:"Add a custom host-to-IP mapping"` - Services []Service `toml:"services,omitempty" json:"services" description:"Add service that is started with container"` - CapAdd []string `toml:"cap_add" json:"cap_add" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"` - CapDrop []string `toml:"cap_drop" json:"cap_drop" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"` - DNSPolicy KubernetesDNSPolicy `toml:"dns_policy,omitempty" json:"dns_policy" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"` - DNSConfig KubernetesDNSConfig `toml:"dns_config" json:"dns_config" description:"Pod DNS config"` - ContainerLifecycle KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"` + PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"` + PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"` + PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."` + ServiceAccount string `toml:"service_account,omitempty" json:"service_account" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"` + ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed" json:"service_account_overwrite_allowed" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"` + PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"` + PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed" json:"pod_annotations_overwrite_allowed" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"` + PodSecurityContext KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"` + BuildContainerSecurityContext KubernetesBuildContainerSecurityContext `toml:"build_container_security_context,omitempty" namespace:"build-container-security-context" description:"A security context attached to the build container inside the build pod"` + HelperContainerSecurityContext KubernetesHelperContainerSecurityContext `toml:"helper_container_security_context,omitempty" namespace:"helper-container-security-context" description:"A security context attached to the helper container inside the build pod"` + ServiceContainerSecurityContext KubernetesServiceContainerSecurityContext `toml:"service_container_security_context,omitempty"` + Volumes KubernetesVolumes `toml:"volumes"` + HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases" long:"host_aliases" description:"Add a custom host-to-IP mapping"` + Services []Service `toml:"services,omitempty" json:"services" description:"Add service that is started with container"` + CapAdd []string `toml:"cap_add" json:"cap_add" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"` + CapDrop []string `toml:"cap_drop" json:"cap_drop" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"` + DNSPolicy KubernetesDNSPolicy `toml:"dns_policy,omitempty" json:"dns_policy" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"` + DNSConfig KubernetesDNSConfig `toml:"dns_config" json:"dns_config" description:"Pod DNS config"` + ContainerLifecycle KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"` } //nolint:lll @@ -442,6 +445,138 @@ type KubernetesPodSecurityContext struct { SupplementalGroups []int64 `toml:"supplemental_groups,omitempty" long:"supplemental-groups" description:"A list of groups applied to the first process run in each container, in addition to the container's primary GID"` } +type KubernetesBuildContainerCapabilities struct { + Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the build container"` + Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the build container"` +} + +type KubernetesServiceContainerCapabilities struct { + Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the service container"` + Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the service container"` +} + +type KubernetesHelperContainerCapabilities struct { + Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the helper container"` + Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the helper container"` +} + +type KubernetesBuildContainerSecurityContext struct { + Capabilities *KubernetesBuildContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` + Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` + RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` + RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` + RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` + ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` +} + +type KubernetesServiceContainerSecurityContext struct { + Capabilities *KubernetesServiceContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` + Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` + RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` + RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` + RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` + ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` +} + +type KubernetesHelperContainerSecurityContext struct { + Capabilities *KubernetesHelperContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` + Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` + RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` + RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` + RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` + ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` +} + +func (c *KubernetesConfig) GetBuildContainerSecurityContext() *api.SecurityContext { + securityContext := c.BuildContainerSecurityContext + + return &api.SecurityContext{ + Capabilities: c.getBuildContainerCapabilities(), + Privileged: c.getPrivilegedEffective(securityContext.Privileged), + RunAsGroup: securityContext.RunAsGroup, + RunAsNonRoot: securityContext.RunAsNonRoot, + RunAsUser: securityContext.RunAsUser, + ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, + AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, + } +} + +func (c *KubernetesConfig) GetServiceContainerSecurityContext() *api.SecurityContext { + securityContext := c.ServiceContainerSecurityContext + + return &api.SecurityContext{ + Capabilities: c.getServiceContainerCapabilities(), + Privileged: c.getPrivilegedEffective(securityContext.Privileged), + RunAsGroup: securityContext.RunAsGroup, + RunAsNonRoot: securityContext.RunAsNonRoot, + RunAsUser: securityContext.RunAsUser, + ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, + AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, + } +} + +func (c *KubernetesConfig) GetHelperContainerSecurityContext() *api.SecurityContext { + securityContext := c.HelperContainerSecurityContext + + return &api.SecurityContext{ + Capabilities: c.getHelperContainerCapabilities(), + Privileged: c.getPrivilegedEffective(securityContext.Privileged), + RunAsGroup: securityContext.RunAsGroup, + RunAsNonRoot: securityContext.RunAsNonRoot, + RunAsUser: securityContext.RunAsUser, + ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, + AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, + } +} + +// getPrivilegedEffective gets the priviledged flag from Kubernetes config. The containerPrivileged argument which can be specific for each container takes precedence +func (c *KubernetesConfig) getPrivilegedEffective(containerPrivileged *bool) *bool { + if containerPrivileged != nil { + return containerPrivileged + } + + return c.Privileged +} + +func (c *KubernetesConfig) getBuildContainerCapabilities() *api.Capabilities { + capabilities := c.BuildContainerSecurityContext.Capabilities + if capabilities == nil { + return nil + } + + return &api.Capabilities{ + Add: capabilities.Add, + Drop: capabilities.Drop, + } +} + +func (c *KubernetesConfig) getServiceContainerCapabilities() *api.Capabilities { + capabilities := c.ServiceContainerSecurityContext.Capabilities + if capabilities == nil { + return nil + } + + return &api.Capabilities{ + Add: capabilities.Add, + Drop: capabilities.Drop, + } +} + +func (c *KubernetesConfig) getHelperContainerCapabilities() *api.Capabilities { + capabilities := c.HelperContainerSecurityContext.Capabilities + if capabilities == nil { + return nil + } + + return &api.Capabilities{ + Add: capabilities.Add, + Drop: capabilities.Drop, + } +} + //nolint:lll type KubernetesAffinity struct { NodeAffinity *KubernetesNodeAffinity `toml:"node_affinity,omitempty" json:"node_affinity" long:"node-affinity" description:"Node affinity is conceptually similar to nodeSelector -- it allows you to constrain which nodes your pod is eligible to be scheduled on, based on labels on the node."` diff --git a/common/config_test.go b/common/config_test.go index 2b36ee7027..712be1e3d1 100644 --- a/common/config_test.go +++ b/common/config_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" api "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags" "gitlab.com/gitlab-org/gitlab-runner/helpers/process" @@ -1306,6 +1307,476 @@ func TestRunnerSettings_IsFeatureFlagOn(t *testing.T) { } } +func TestEffectivePrivilege(t *testing.T) { + tests := map[string]struct { + pod bool + container bool + expected bool + }{ + "pod and container privileged": { + pod: true, + container: true, + expected: true, + }, + "pod privileged": { + pod: true, + container: false, + expected: false, + }, + "container privileged": { + pod: false, + container: true, + expected: true, + }, + "all unprivileged": { + pod: false, + container: false, + expected: false, + }, + } + + for tn, tt := range tests { + t.Run(tn, func(t *testing.T) { + k8sConfig := KubernetesConfig{ + Privileged: &tt.pod, + } + effectivePrivileged := k8sConfig.getPrivilegedEffective(&tt.container) + require.NotNil(t, effectivePrivileged) + assert.Equal(t, tt.expected, *effectivePrivileged) + }) + } +} + +func TestContainerSecurityContext(t *testing.T) { + boolPtr := func(v bool) *bool { + return &v + } + + tests := map[string]struct { + assignSecurityContextFn func(c *KubernetesConfig) + getSecurityContext func(c *KubernetesConfig) *v1.SecurityContext + getExpectedContainerSecurityContext func() *v1.SecurityContext + }{ + "no build container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) {}, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetBuildContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + return &v1.SecurityContext{ + Privileged: &privileged, + } + }, + }, + "run as user - build container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + runAsUser := int64(1000) + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetBuildContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(1000) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + "no service container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) {}, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetServiceContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + return &v1.SecurityContext{ + Privileged: &privileged, + } + }, + }, + "run as user - service container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + runAsUser := int64(900) + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetServiceContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(900) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + "no helper container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) {}, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetHelperContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + return &v1.SecurityContext{ + Privileged: &privileged, + } + }, + }, + "run as user - helper container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + runAsUser := int64(65535) + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetHelperContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(65535) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + "privileged - helper container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetHelperContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := true + return &v1.SecurityContext{ + Privileged: &privileged, + } + }, + }, + "privileged - service container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetServiceContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := true + return &v1.SecurityContext{ + Privileged: &privileged, + } + }, + }, + "privileged - build container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetBuildContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + return &v1.SecurityContext{ + Privileged: boolPtr(true), + } + }, + }, + "container privileged override - helper container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + + privileged := false + runAsUser := int64(65535) + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetHelperContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(65535) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + "container privileged override - service container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + + privileged := false + runAsUser := int64(65535) + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetServiceContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(65535) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + "container privileged override - build container security context": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.Privileged = boolPtr(true) + + privileged := false + runAsUser := int64(65535) + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { + return c.GetBuildContainerSecurityContext() + }, + getExpectedContainerSecurityContext: func() *v1.SecurityContext { + privileged := false + runAsUser := int64(65535) + return &v1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsUser, + } + }, + }, + } + + for tn, tt := range tests { + t.Run(tn, func(t *testing.T) { + config := new(KubernetesConfig) + tt.assignSecurityContextFn(config) + + tt.assignSecurityContextFn(config) + scExpected := tt.getExpectedContainerSecurityContext() + scActual := tt.getSecurityContext(config) + assert.Equal(t, scExpected, scActual) + }) + } +} + +func TestContainerSecurityCapabilities(t *testing.T) { + tests := map[string]struct { + assignSecurityContextFn func(c *KubernetesConfig) + getCapabilitiesFn func(c *KubernetesConfig) *v1.Capabilities + expectedCapabilities *v1.Capabilities + }{ + "build container add": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ + Capabilities: &KubernetesBuildContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getBuildContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: nil, + }, + }, + "build container drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ + Capabilities: &KubernetesBuildContainerCapabilities{ + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getBuildContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: nil, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "build container add and drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ + Capabilities: &KubernetesBuildContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getBuildContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "build container empty": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{} + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getBuildContainerCapabilities() + }, + expectedCapabilities: nil, + }, + "helper container add": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + Capabilities: &KubernetesHelperContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getHelperContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: nil, + }, + }, + "helper container drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + Capabilities: &KubernetesHelperContainerCapabilities{ + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getHelperContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: nil, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "helper container add and drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + Capabilities: &KubernetesHelperContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getHelperContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "helper container empty": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{} + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getHelperContainerCapabilities() + }, + expectedCapabilities: nil, + }, + "service container add": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ + Capabilities: &KubernetesServiceContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getServiceContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: nil, + }, + }, + "service container drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ + Capabilities: &KubernetesServiceContainerCapabilities{ + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getServiceContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: nil, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "service container add and drop": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ + Capabilities: &KubernetesServiceContainerCapabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + } + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getServiceContainerCapabilities() + }, + + expectedCapabilities: &v1.Capabilities{ + Add: []v1.Capability{"SYS_TIME"}, + Drop: []v1.Capability{"SYS_TIME"}, + }, + }, + "service container empty": { + assignSecurityContextFn: func(c *KubernetesConfig) { + c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{} + }, + getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { + return c.getServiceContainerCapabilities() + }, + expectedCapabilities: nil, + }, + } + + for tn, tt := range tests { + t.Run(tn, func(t *testing.T) { + config := new(KubernetesConfig) + tt.assignSecurityContextFn(config) + + c := tt.getCapabilitiesFn(config) + assert.Equal(t, tt.expectedCapabilities, c) + }) + } +} + func TestKubernetesTerminationPeriod(t *testing.T) { tests := map[string]struct { cfg KubernetesConfig diff --git a/docs/executors/kubernetes.md b/docs/executors/kubernetes.md index 365b32bdff..acb9911d4a 100644 --- a/docs/executors/kubernetes.md +++ b/docs/executors/kubernetes.md @@ -165,6 +165,9 @@ The following settings help to define the behavior of GitLab Runner within Kuber | `pod_annotations_overwrite_allowed` | Regular expression to validate the contents of the pod annotations overwrite environment variable. When empty, it disables the pod annotations overwrite feature. | | `pod_labels` | A set of labels to be added to each build pod created by the runner. The value of these can include environment variables for expansion. | | `pod_security_context` | Configured through the configuration file, this sets a pod security context for the build pod. [Read more about security context](#using-security-context). | +| `build_container_security_context` | Sets a container security context for the build container. [Read more about security context](#using-security-context). | +| `helper_container_security_context` | Sets a container security context for the helper container. [Read more about security context](#using-security-context). | +| `service_container_security_context` | Sets a container security context for the service containers. [Read more about security context](#using-security-context). | | `pod_termination_grace_period_seconds` | Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if `terminationGracePeriodSeconds` is specified. | | `poll_interval` | How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status (default = 3). | | `poll_timeout` | The amount of time, in seconds, that needs to pass before the runner will time out attempting to connect to the container it has just created. Useful for queueing more builds that the cluster can handle at a time (default = 180). | @@ -516,9 +519,9 @@ concurrent = 4 | Option | Type | Required | Description | |----------------------|------------|----------|-------------| | `fs_group` | `int` | No | A special supplemental group that applies to all containers in a pod. | -| `run_as_group` | `int` | No | The GID to run the entrypoint of the container process. | +| `run_as_group` | `int` | No | The GID to run the entry point of the container process. | | `run_as_non_root` | boolean | No | Indicates that the container must run as a non-root user. | -| `run_as_user` | `int` | No | The UID to run the entrypoint of the container process. | +| `run_as_user` | `int` | No | The UID to run the entry point of the container process. | | `supplemental_groups`| `int` list | No | A list of groups applied to the first process run in each container, in addition to the container's primary GID. | Assigning a security context to pods provides security to your Kubernetes cluster. For this to work you'll need to provide a helper @@ -553,14 +556,60 @@ check_interval = 30 url = "gitlab.example.com" executor = "kubernetes" [runners.kubernetes] - helper_image = "gitlab-registy.example.com/helper:latest" + helper_image = "gitlab-registry.example.com/helper:latest" + [runners.kubernetes.pod_security_context] + run_as_non_root = true + run_as_user = 59417 + run_as_group = 59417 + fs_group = 59417 +``` + +### Container security context + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1507) in GitLab runner 14.3 + +The [container security context](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) configuration configures the executor to set a container security policy on the build, helper or service pods. + +| Option | Type | Required | Description | +|-----------------------|-------------|----------|-------------| +| `run_as_group` | int | no | The GID to run the entry point of the container process | +| `run_as_non_root` | boolean | no | Indicates that the container must run as a non-root user | +| `run_as_user` | int | no | The UID to run the entry point of the container process | +| `capabilities.add` | string list | no | The capabilities to add when running the container | +| `capabilities.drop` | string list | no | The capabilities to drop when running the container | + +The example below sets a pod security context, then overrides `run_as_user` and `run_as_group` for both the build and helper containers. In the example, all service containers would inherit `run_as_user` and `run_as_group` from the pod security context. + +Example of setting pod security context in your `config.toml`: + +```toml +concurrent = 4 +check_interval = 30 + [[runners]] + name = "myRunner" + url = "gitlab.example.com" + executor = "kubernetes" + [runners.kubernetes] + helper_image = "gitlab-registry.example.com/helper:latest" [runners.kubernetes.pod_security_context] run_as_non_root = true run_as_user = 59417 run_as_group = 59417 fs_group = 59417 + [runners.kubernetes.build_container_security_context] + run_as_user = 65534 + run_as_group = 65534 + [runners.kubernetes.build_container_security_context.capabilities] + add = ["NET_ADMIN"] + [runners.kubernetes.helper_container_security_context] + run_as_user = 1000 + run_as_group = 1000 + [runners.kubernetes.service_container_security_context] + run_as_user = 1000 + run_as_group = 1000 ``` + ## Using services > - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4470) in GitLab Runner 12.5. diff --git a/executors/kubernetes/kubernetes.go b/executors/kubernetes/kubernetes.go index dafd37fa11..5f669d77a9 100644 --- a/executors/kubernetes/kubernetes.go +++ b/executors/kubernetes/kubernetes.go @@ -117,6 +117,16 @@ type kubernetesOptions struct { Services common.Services } +type containerBuildOpts struct { + name string + image string + imageDefinition common.Image + requests api.ResourceList + limits api.ResourceList + securityContext *api.SecurityContext + command []string +} + type executor struct { executors.AbstractExecutor @@ -691,12 +701,7 @@ func (s *executor) cleanupResources() { } //nolint:funlen -func (s *executor) buildContainer( - name, image string, - imageDefinition common.Image, - requests, limits api.ResourceList, - containerCommand ...string, -) (api.Container, error) { +func (s *executor) buildContainer(opts containerBuildOpts) (api.Container, error) { // check if the image/service is allowed internalImages := []string{ s.Config.Kubernetes.Image, @@ -707,16 +712,16 @@ func (s *executor) buildContainer( optionName string allowedImages []string ) - if strings.HasPrefix(name, "svc-") { + if strings.HasPrefix(opts.name, "svc-") { optionName = "services" allowedImages = s.Config.Kubernetes.AllowedServices - } else if name == buildContainerName { + } else if opts.name == buildContainerName { optionName = "images" allowedImages = s.Config.Kubernetes.AllowedImages } verifyAllowedImageOptions := common.VerifyAllowedImageOptions{ - Image: image, + Image: opts.image, OptionName: optionName, AllowedImages: allowedImages, InternalImages: internalImages, @@ -726,58 +731,50 @@ func (s *executor) buildContainer( return api.Container{}, err } - containerPorts := make([]api.ContainerPort, len(imageDefinition.Ports)) - proxyPorts := make([]proxy.Port, len(imageDefinition.Ports)) + containerPorts := make([]api.ContainerPort, len(opts.imageDefinition.Ports)) + proxyPorts := make([]proxy.Port, len(opts.imageDefinition.Ports)) - for i, port := range imageDefinition.Ports { + for i, port := range opts.imageDefinition.Ports { proxyPorts[i] = proxy.Port{Name: port.Name, Number: port.Number, Protocol: port.Protocol} containerPorts[i] = api.ContainerPort{ContainerPort: int32(port.Number)} } if len(proxyPorts) > 0 { - serviceName := imageDefinition.Alias + serviceName := opts.imageDefinition.Alias if serviceName == "" { - serviceName = name - if name != buildContainerName { - serviceName = fmt.Sprintf("proxy-%s", name) + serviceName = opts.name + if opts.name != buildContainerName { + serviceName = fmt.Sprintf("proxy-%s", opts.name) } } s.ProxyPool[serviceName] = s.newProxy(serviceName, proxyPorts) } - pullPolicy, err := s.pullManager.GetPullPolicyFor(image) + pullPolicy, err := s.pullManager.GetPullPolicyFor(opts.image) if err != nil { return api.Container{}, err } - command, args := s.getCommandAndArgs(imageDefinition, containerCommand...) + command, args := s.getCommandAndArgs(opts.imageDefinition, opts.command...) container := api.Container{ - Name: name, - Image: image, + Name: opts.name, + Image: opts.image, ImagePullPolicy: pullPolicy, Command: command, Args: args, Env: buildVariables(s.Build.GetAllVariables().PublicOrInternal()), Resources: api.ResourceRequirements{ - Limits: limits, - Requests: requests, - }, - Ports: containerPorts, - VolumeMounts: s.getVolumeMounts(), - SecurityContext: &api.SecurityContext{ - Privileged: s.Config.Kubernetes.Privileged, - AllowPrivilegeEscalation: s.Config.Kubernetes.AllowPrivilegeEscalation, - Capabilities: getCapabilities( - GetDefaultCapDrop(s.helperImageInfo.OSType), - s.Config.Kubernetes.CapAdd, - s.Config.Kubernetes.CapDrop, - ), + Limits: opts.limits, + Requests: opts.requests, }, - Lifecycle: s.prepareLifecycleHooks(), - Stdin: true, + Ports: containerPorts, + VolumeMounts: s.getVolumeMounts(), + SecurityContext: opts.securityContext, + Lifecycle: s.prepareLifecycleHooks(), + Stdin: true, } return container, nil @@ -1229,13 +1226,14 @@ func (s *executor) setupBuildPod(initContainers []api.Container) error { for i, service := range s.options.Services { resolvedImage := s.Build.GetAllVariables().ExpandValue(service.Name) var err error - podServices[i], err = s.buildContainer( - fmt.Sprintf("svc-%d", i), - resolvedImage, - service, - s.configurationOverwrites.serviceRequests, - s.configurationOverwrites.serviceLimits, - ) + podServices[i], err = s.buildContainer(containerBuildOpts{ + name: fmt.Sprintf("svc-%d", i), + image: resolvedImage, + imageDefinition: service, + requests: s.configurationOverwrites.serviceRequests, + limits: s.configurationOverwrites.serviceLimits, + securityContext: s.Config.Kubernetes.GetServiceContainerSecurityContext(), + }) if err != nil { return err } @@ -1308,34 +1306,33 @@ func (s *executor) preparePodConfig( hostAliases []api.HostAlias, initContainers []api.Container, ) (api.Pod, error) { - buildImage := s.Build.GetAllVariables().ExpandValue(s.options.Image.Name) - dockerCmdForBuildContainer := s.BuildShell.DockerCommand if s.Build.IsFeatureFlagOn(featureflags.KubernetesHonorEntrypoint) && !s.Build.IsFeatureFlagOn(featureflags.UseLegacyKubernetesExecutionStrategy) { dockerCmdForBuildContainer = []string{} } - buildContainer, err := s.buildContainer( - buildContainerName, - buildImage, - s.options.Image, - s.configurationOverwrites.buildRequests, - s.configurationOverwrites.buildLimits, - dockerCmdForBuildContainer..., - ) + buildContainer, err := s.buildContainer(containerBuildOpts{ + name: buildContainerName, + image: s.Build.GetAllVariables().ExpandValue(s.options.Image.Name), + imageDefinition: s.options.Image, + requests: s.configurationOverwrites.buildRequests, + limits: s.configurationOverwrites.buildLimits, + securityContext: s.Config.Kubernetes.GetBuildContainerSecurityContext(), + command: dockerCmdForBuildContainer, + }) if err != nil { return api.Pod{}, fmt.Errorf("building build container: %w", err) } - helperContainer, err := s.buildContainer( - helperContainerName, - s.getHelperImage(), - common.Image{}, - s.configurationOverwrites.helperRequests, - s.configurationOverwrites.helperLimits, - s.BuildShell.DockerCommand..., - ) + helperContainer, err := s.buildContainer(containerBuildOpts{ + name: helperContainerName, + image: s.getHelperImage(), + requests: s.configurationOverwrites.helperRequests, + limits: s.configurationOverwrites.helperLimits, + securityContext: s.Config.Kubernetes.GetHelperContainerSecurityContext(), + command: s.BuildShell.DockerCommand, + }) if err != nil { return api.Pod{}, fmt.Errorf("building helper container: %w", err) } diff --git a/executors/kubernetes/kubernetes_test.go b/executors/kubernetes/kubernetes_test.go index a44cf6b0ee..5fe6df2132 100644 --- a/executors/kubernetes/kubernetes_test.go +++ b/executors/kubernetes/kubernetes_test.go @@ -4743,3 +4743,60 @@ func TestLifecyclePrepare(t *testing.T) { }) } } + +func TestBuildContainerSecurityContext(t *testing.T) { + tests := map[string]struct { + getSecurityContext func() *api.SecurityContext + }{ + "build security context": { + getSecurityContext: func() *api.SecurityContext { + runAsNonRoot := true + readOnlyRootFileSystem := true + privileged := false + allowPrivilageEscalation := false + var uid int64 = 1000 + var gid int64 = 1000 + return &api.SecurityContext{ + RunAsNonRoot: &runAsNonRoot, + ReadOnlyRootFilesystem: &readOnlyRootFileSystem, + Privileged: &privileged, + AllowPrivilegeEscalation: &allowPrivilageEscalation, + RunAsUser: &uid, + RunAsGroup: &gid, + Capabilities: &api.Capabilities{ + Drop: []api.Capability{"ALL"}, + }, + } + }, + }, + "no security context": { + getSecurityContext: func() *api.SecurityContext { + return nil + }, + }, + } + + mockPullManager := &pull.MockManager{} + mockPullManager.On("GetPullPolicyFor", mock.Anything). + Return(api.PullAlways, nil). + Times(2) + + defer mockPullManager.AssertExpectations(t) + + executor := newExecutor() + executor.pullManager = mockPullManager + executor.Build = new(common.Build) + executor.Config.Kubernetes = new(common.KubernetesConfig) + + for tn, tt := range tests { + t.Run(tn, func(t *testing.T) { + opts := containerBuildOpts{ + name: buildContainerName, + securityContext: tt.getSecurityContext(), + } + container, err := executor.buildContainer(opts) + require.NoError(t, err) + assert.Equal(t, tt.getSecurityContext(), container.SecurityContext) + }) + } +} -- GitLab From 45af896fd1511424e67b4f360852ba1112170248 Mon Sep 17 00:00:00 2001 From: ggeorgiev_ Date: Tue, 31 Aug 2021 16:43:58 +0300 Subject: [PATCH 2/2] Add support for AllowPrivilegeEscalation and CapAdd/CapDrop for containers security context Review and pipeline fixes fix security context config integration confix Refactor security context add namespaced security context tests format config.go fix tests and linter fix indentation apply docs suggestions remove println in tests Use strings.Replacer in register integration tests --- commands/register_integration_test.go | 99 +++-- common/config.go | 329 +++++++-------- common/config_test.go | 564 ++++++++++---------------- docs/executors/kubernetes.md | 15 +- executors/kubernetes/kubernetes.go | 56 +-- executors/kubernetes/util.go | 36 -- executors/kubernetes/util_test.go | 102 ----- 7 files changed, 489 insertions(+), 712 deletions(-) diff --git a/commands/register_integration_test.go b/commands/register_integration_test.go index c1e1f6217d..1b44e41815 100644 --- a/commands/register_integration_test.go +++ b/commands/register_integration_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/urfave/cli" - clihelpers "gitlab.com/gitlab-org/golang-cli-helpers" "gitlab.com/gitlab-org/gitlab-runner/commands" "gitlab.com/gitlab-org/gitlab-runner/common" @@ -27,10 +26,17 @@ import ( "gitlab.com/gitlab-org/gitlab-runner/helpers" "gitlab.com/gitlab-org/gitlab-runner/helpers/ssh" "gitlab.com/gitlab-org/gitlab-runner/shells" + clihelpers "gitlab.com/gitlab-org/golang-cli-helpers" ) const osTypeWindows = "windows" +var spaceReplacer = strings.NewReplacer(" ", "", "\t", "") + +type kv struct { + key, value string +} + func TestAccessLevelSetting(t *testing.T) { tests := map[string]struct { accessLevel commands.AccessLevel @@ -70,7 +76,7 @@ func TestAccessLevelSetting(t *testing.T) { "--access-level", string(testCase.accessLevel), } - _, output, err := testRegisterCommandRun(t, network, arguments...) + _, output, err := testRegisterCommandRun(t, network, nil, arguments...) if testCase.failureExpected { assert.EqualError(t, err, "command error: Given access-level is not valid. "+ @@ -111,6 +117,7 @@ func TestAskRunnerOverrideDefaultsForExecutors(t *testing.T) { func testRegisterCommandRun( t *testing.T, network common.Network, + env []kv, args ...string, ) (content, output string, err error) { config := &common.RunnerConfig{ @@ -122,6 +129,19 @@ func testRegisterCommandRun( }, } + for _, kv := range env { + err := os.Setenv(kv.key, kv.value) + if err != nil { + return "", "", err + } + } + + defer func() { + for _, kv := range env { + _ = os.Unsetenv(kv.key) + } + }() + hook := test.NewGlobal() defer func() { @@ -654,6 +674,9 @@ check_interval = 0 pod_annotations_overwrite_allowed = "" [runners.kubernetes.affinity] [runners.kubernetes.pod_security_context] + [runners.kubernetes.build_container_security_context] + [runners.kubernetes.helper_container_security_context] + [runners.kubernetes.service_container_security_context] [runners.kubernetes.volumes] [[runners.kubernetes.volumes.empty_dir]] @@ -704,7 +727,7 @@ check_interval = 0 tt.networkAssertions(network) - fileContent, _, err := testRegisterCommandRun(t, network, args...) + fileContent, _, err := testRegisterCommandRun(t, network, nil, args...) if tt.errExpected { require.Error(t, err) return @@ -796,9 +819,10 @@ func TestUnregisterOnFailure(t *testing.T) { func TestRegisterCommand(t *testing.T) { type testCase struct { - condition func() bool - arguments []string - expectedConfig string + condition func() bool + arguments []string + environment []kv + expectedConfigs []string } testCases := map[string]testCase{ @@ -808,11 +832,9 @@ func TestRegisterCommand(t *testing.T) { "--feature-flags", "FF_TEST_1:true", "--feature-flags", "FF_TEST_2:false", }, - expectedConfig: ` - [runners.feature_flags] - FF_TEST_1 = true - FF_TEST_2 = false -`, + expectedConfigs: []string{`[runners.feature_flags] + FF_TEST_1 = true + FF_TEST_2 = false`}, }, "shell defaults to pwsh on Windows with shell executor": { condition: func() bool { return runtime.GOOS == osTypeWindows }, @@ -820,9 +842,7 @@ func TestRegisterCommand(t *testing.T) { "--name", "test-runner", "--executor", "shell", }, - expectedConfig: ` - shell = "pwsh" -`, + expectedConfigs: []string{`shell = "pwsh"`}, }, "shell defaults to pwsh on Windows with docker-windows executor": { condition: func() bool { return runtime.GOOS == osTypeWindows }, @@ -831,9 +851,7 @@ func TestRegisterCommand(t *testing.T) { "--executor", "docker-windows", "--docker-image", "abc", }, - expectedConfig: ` - shell = "pwsh" -`, + expectedConfigs: []string{`shell = "pwsh"`}, }, "shell can be overridden to powershell on Windows with shell executor": { condition: func() bool { return runtime.GOOS == osTypeWindows }, @@ -842,9 +860,7 @@ func TestRegisterCommand(t *testing.T) { "--executor", "shell", "--shell", "powershell", }, - expectedConfig: ` - shell = "powershell" -`, + expectedConfigs: []string{`shell = "powershell"`}, }, "shell can be overridden to powershell on Windows with docker-windows executor": { condition: func() bool { return runtime.GOOS == osTypeWindows }, @@ -854,9 +870,40 @@ func TestRegisterCommand(t *testing.T) { "--shell", "powershell", "--docker-image", "abc", }, - expectedConfig: ` - shell = "powershell" -`, + expectedConfigs: []string{`shell = "powershell"`}, + }, + "kubernetes security context namespace": { + arguments: []string{ + "--executor", "kubernetes", + }, + environment: []kv{ + { + key: "KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_PRIVILEGED", + value: "true", + }, + { + key: "KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER", + value: "1000", + }, + { + key: "KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT", + value: "true", + }, + { + key: "KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD", + value: "NET_RAW, NET_RAW1", + }, + }, + expectedConfigs: []string{` + [runners.kubernetes.build_container_security_context] + privileged = true`, ` + [runners.kubernetes.helper_container_security_context] + run_as_user = 1000`, ` + [runners.kubernetes.service_container_security_context] + run_as_non_root = true`, ` + [runners.kubernetes.service_container_security_context.capabilities] + add = ["NET_RAW, NET_RAW1"]`, + }, }, } @@ -875,10 +922,12 @@ func TestRegisterCommand(t *testing.T) { }). Once() - gotConfig, _, err := testRegisterCommandRun(t, network, tc.arguments...) + gotConfig, _, err := testRegisterCommandRun(t, network, tc.environment, tc.arguments...) require.NoError(t, err) - assert.Contains(t, gotConfig, tc.expectedConfig) + for _, expectedConfig := range tc.expectedConfigs { + assert.Contains(t, spaceReplacer.Replace(gotConfig), spaceReplacer.Replace(expectedConfig)) + } }) } } diff --git a/common/config.go b/common/config.go index d9eae1baf4..0e59679264 100644 --- a/common/config.go +++ b/common/config.go @@ -281,84 +281,84 @@ func (p KubernetesDNSPolicy) Get() (api.DNSPolicy, error) { //nolint:lll type KubernetesConfig struct { - Host string `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"` - CertFile string `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"` - KeyFile string `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"` - CAFile string `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"` - BearerTokenOverwriteAllowed bool `toml:"bearer_token_overwrite_allowed" json:"bearer_token_overwrite_allowed" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."` - BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."` - Image string `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"` - Namespace string `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"` - NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed" json:"namespace_overwrite_allowed" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"` - Privileged *bool `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"` - AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."` - CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"` - CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."` - CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"` - CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."` - MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"` - MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."` - MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"` - MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."` - EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"` - EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."` - EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"` - EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."` - ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"` - ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."` - ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"` - ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."` - ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"` - ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."` - ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"` - ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."` - ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"` - ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."` - ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"` - ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."` - HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"` - HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."` - HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"` - HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."` - HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"` - HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."` - HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"` - HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."` - HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"` - HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."` - HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"` - HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."` - AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"` - AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"` - PullPolicy StringOrArray `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"` - NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."` - NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."` - Affinity KubernetesAffinity `toml:"affinity,omitempty" json:"affinity" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"` - ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"` - HelperImage string `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"` - HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"` - TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal.DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."` - PodTerminationGracePeriodSeconds *int64 `toml:"pod_termination_grace_period_seconds,omitzero" json:"pod_termination_grace_period_seconds" long:"pod_termination_grace_period_seconds" env:"KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS" description:"Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` - CleanupGracePeriodSeconds *int64 `toml:"cleanup_grace_period_seconds,omitzero" json:"cleanup_grace_period_seconds" long:"cleanup_grace_period_seconds" env:"KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS" description:"When cleaning up a pod on completion of a job, the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` - PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"` - PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"` - PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."` - ServiceAccount string `toml:"service_account,omitempty" json:"service_account" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"` - ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed" json:"service_account_overwrite_allowed" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"` - PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"` - PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed" json:"pod_annotations_overwrite_allowed" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"` - PodSecurityContext KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"` - BuildContainerSecurityContext KubernetesBuildContainerSecurityContext `toml:"build_container_security_context,omitempty" namespace:"build-container-security-context" description:"A security context attached to the build container inside the build pod"` - HelperContainerSecurityContext KubernetesHelperContainerSecurityContext `toml:"helper_container_security_context,omitempty" namespace:"helper-container-security-context" description:"A security context attached to the helper container inside the build pod"` - ServiceContainerSecurityContext KubernetesServiceContainerSecurityContext `toml:"service_container_security_context,omitempty"` - Volumes KubernetesVolumes `toml:"volumes"` - HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases" long:"host_aliases" description:"Add a custom host-to-IP mapping"` - Services []Service `toml:"services,omitempty" json:"services" description:"Add service that is started with container"` - CapAdd []string `toml:"cap_add" json:"cap_add" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"` - CapDrop []string `toml:"cap_drop" json:"cap_drop" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"` - DNSPolicy KubernetesDNSPolicy `toml:"dns_policy,omitempty" json:"dns_policy" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"` - DNSConfig KubernetesDNSConfig `toml:"dns_config" json:"dns_config" description:"Pod DNS config"` - ContainerLifecycle KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"` + Host string `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"` + CertFile string `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"` + KeyFile string `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"` + CAFile string `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"` + BearerTokenOverwriteAllowed bool `toml:"bearer_token_overwrite_allowed" json:"bearer_token_overwrite_allowed" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."` + BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."` + Image string `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"` + Namespace string `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"` + NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed" json:"namespace_overwrite_allowed" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"` + Privileged *bool `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."` + CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"` + CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."` + CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"` + CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."` + MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"` + MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."` + MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"` + MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."` + EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"` + EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."` + EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"` + EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."` + ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"` + ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."` + ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"` + ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."` + ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"` + ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."` + ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"` + ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."` + ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"` + ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."` + ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"` + ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."` + HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"` + HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."` + HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"` + HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."` + HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"` + HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."` + HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"` + HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."` + HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"` + HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."` + HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"` + HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."` + AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"` + AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"` + PullPolicy StringOrArray `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"` + NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."` + NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."` + Affinity KubernetesAffinity `toml:"affinity,omitempty" json:"affinity" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"` + ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"` + HelperImage string `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"` + HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"` + TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal.DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."` + PodTerminationGracePeriodSeconds *int64 `toml:"pod_termination_grace_period_seconds,omitzero" json:"pod_termination_grace_period_seconds" long:"pod_termination_grace_period_seconds" env:"KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS" description:"Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` + CleanupGracePeriodSeconds *int64 `toml:"cleanup_grace_period_seconds,omitzero" json:"cleanup_grace_period_seconds" long:"cleanup_grace_period_seconds" env:"KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS" description:"When cleaning up a pod on completion of a job, the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."` + PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"` + PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"` + PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."` + ServiceAccount string `toml:"service_account,omitempty" json:"service_account" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"` + ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed" json:"service_account_overwrite_allowed" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"` + PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"` + PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed" json:"pod_annotations_overwrite_allowed" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"` + PodSecurityContext KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"` + BuildContainerSecurityContext KubernetesContainerSecurityContext `toml:"build_container_security_context,omitempty" namespace:"build_container_security_context" description:"A security context attached to the build container inside the build pod"` + HelperContainerSecurityContext KubernetesContainerSecurityContext `toml:"helper_container_security_context,omitempty" namespace:"helper_container_security_context" description:"A security context attached to the helper container inside the build pod"` + ServiceContainerSecurityContext KubernetesContainerSecurityContext `toml:"service_container_security_context,omitempty" namespace:"service_container_security_context" description:"A security context attached to the service containers inside the build pod"` + Volumes KubernetesVolumes `toml:"volumes"` + HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases" long:"host_aliases" description:"Add a custom host-to-IP mapping"` + Services []Service `toml:"services,omitempty" json:"services" description:"Add service that is started with container"` + CapAdd []string `toml:"cap_add" json:"cap_add" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"` + CapDrop []string `toml:"cap_drop" json:"cap_drop" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"` + DNSPolicy KubernetesDNSPolicy `toml:"dns_policy,omitempty" json:"dns_policy" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"` + DNSConfig KubernetesDNSConfig `toml:"dns_config" json:"dns_config" description:"Pod DNS config"` + ContainerLifecycle KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"` } //nolint:lll @@ -445,128 +445,113 @@ type KubernetesPodSecurityContext struct { SupplementalGroups []int64 `toml:"supplemental_groups,omitempty" long:"supplemental-groups" description:"A list of groups applied to the first process run in each container, in addition to the container's primary GID"` } -type KubernetesBuildContainerCapabilities struct { - Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the build container"` - Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the build container"` -} - -type KubernetesServiceContainerCapabilities struct { - Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the service container"` - Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the service container"` +//nolint:lll +type KubernetesContainerCapabilities struct { + Add []api.Capability `toml:"add" long:"add" env:"@ADD" description:"List of capabilities to add to the build container"` + Drop []api.Capability `toml:"drop" long:"drop" env:"@DROP" description:"List of capabilities to drop from the build container"` } -type KubernetesHelperContainerCapabilities struct { - Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the helper container"` - Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the helper container"` +//nolint:lll +type KubernetesContainerSecurityContext struct { + Capabilities *KubernetesContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` + Privileged *bool `toml:"privileged" long:"privileged" env:"@PRIVILEGED" description:"Run container in privileged mode"` + RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"@RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` + RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"@RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` + RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"@RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` + ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"@READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` + AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"@ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` } -type KubernetesBuildContainerSecurityContext struct { - Capabilities *KubernetesBuildContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` - Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` - RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` - RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` - RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` - ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` - AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` -} +func (c *KubernetesConfig) getCapabilities(defaultCapDrop []string) *api.Capabilities { + enabled := make(map[string]bool) -type KubernetesServiceContainerSecurityContext struct { - Capabilities *KubernetesServiceContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` - Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` - RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` - RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` - RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` - ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` - AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` -} + for _, v := range defaultCapDrop { + enabled[v] = false + } -type KubernetesHelperContainerSecurityContext struct { - Capabilities *KubernetesHelperContainerCapabilities `toml:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"` - Privileged *bool `toml:"privileged" long:"privileged" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_PRIVILEGED" description:"Run container in privileged mode"` - RunAsUser *int64 `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"` - RunAsGroup *int64 `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"` - RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"` - ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."` - AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"` -} + for _, v := range c.CapAdd { + enabled[v] = true + } -func (c *KubernetesConfig) GetBuildContainerSecurityContext() *api.SecurityContext { - securityContext := c.BuildContainerSecurityContext + for _, v := range c.CapDrop { + enabled[v] = false + } - return &api.SecurityContext{ - Capabilities: c.getBuildContainerCapabilities(), - Privileged: c.getPrivilegedEffective(securityContext.Privileged), - RunAsGroup: securityContext.RunAsGroup, - RunAsNonRoot: securityContext.RunAsNonRoot, - RunAsUser: securityContext.RunAsUser, - ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, - AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, + if len(enabled) < 1 { + return nil } + + return buildCapabilities(enabled) } -func (c *KubernetesConfig) GetServiceContainerSecurityContext() *api.SecurityContext { - securityContext := c.ServiceContainerSecurityContext +func buildCapabilities(enabled map[string]bool) *api.Capabilities { + capabilities := new(api.Capabilities) - return &api.SecurityContext{ - Capabilities: c.getServiceContainerCapabilities(), - Privileged: c.getPrivilegedEffective(securityContext.Privileged), - RunAsGroup: securityContext.RunAsGroup, - RunAsNonRoot: securityContext.RunAsNonRoot, - RunAsUser: securityContext.RunAsUser, - ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, - AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, + for c, add := range enabled { + if add { + capabilities.Add = append(capabilities.Add, api.Capability(c)) + continue + } + capabilities.Drop = append(capabilities.Drop, api.Capability(c)) } -} -func (c *KubernetesConfig) GetHelperContainerSecurityContext() *api.SecurityContext { - securityContext := c.HelperContainerSecurityContext + return capabilities +} +func (c *KubernetesConfig) GetContainerSecurityContext( + securityContext KubernetesContainerSecurityContext, + defaultCapDrop ...string, +) *api.SecurityContext { return &api.SecurityContext{ - Capabilities: c.getHelperContainerCapabilities(), - Privileged: c.getPrivilegedEffective(securityContext.Privileged), - RunAsGroup: securityContext.RunAsGroup, - RunAsNonRoot: securityContext.RunAsNonRoot, - RunAsUser: securityContext.RunAsUser, - ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, - AllowPrivilegeEscalation: securityContext.AllowPrivilegeEscalation, - } -} + Capabilities: mergeCapabilitiesAddDrop( + c.getCapabilities(defaultCapDrop), + securityContext.getCapabilities(), + ), + Privileged: getContainerSecurityContextEffectiveFlagValue(securityContext.Privileged, c.Privileged), + AllowPrivilegeEscalation: getContainerSecurityContextEffectiveFlagValue( + securityContext.AllowPrivilegeEscalation, + c.AllowPrivilegeEscalation, + ), + RunAsGroup: securityContext.RunAsGroup, + RunAsNonRoot: securityContext.RunAsNonRoot, + RunAsUser: securityContext.RunAsUser, + ReadOnlyRootFilesystem: securityContext.ReadOnlyRootFilesystem, + } +} + +func mergeCapabilitiesAddDrop(capabilities ...*api.Capabilities) *api.Capabilities { + merged := &api.Capabilities{} + for _, c := range capabilities { + if c == nil { + continue + } -// getPrivilegedEffective gets the priviledged flag from Kubernetes config. The containerPrivileged argument which can be specific for each container takes precedence -func (c *KubernetesConfig) getPrivilegedEffective(containerPrivileged *bool) *bool { - if containerPrivileged != nil { - return containerPrivileged - } + if c.Add != nil { + merged.Add = c.Add + } - return c.Privileged -} + if c.Drop != nil { + merged.Drop = c.Drop + } + } -func (c *KubernetesConfig) getBuildContainerCapabilities() *api.Capabilities { - capabilities := c.BuildContainerSecurityContext.Capabilities - if capabilities == nil { + if merged.Add == nil && merged.Drop == nil { return nil } - return &api.Capabilities{ - Add: capabilities.Add, - Drop: capabilities.Drop, - } + return merged } -func (c *KubernetesConfig) getServiceContainerCapabilities() *api.Capabilities { - capabilities := c.ServiceContainerSecurityContext.Capabilities - if capabilities == nil { - return nil +func getContainerSecurityContextEffectiveFlagValue(containerValue, fallbackValue *bool) *bool { + if containerValue == nil { + return fallbackValue } - return &api.Capabilities{ - Add: capabilities.Add, - Drop: capabilities.Drop, - } + return containerValue } -func (c *KubernetesConfig) getHelperContainerCapabilities() *api.Capabilities { - capabilities := c.HelperContainerSecurityContext.Capabilities +func (c *KubernetesContainerSecurityContext) getCapabilities() *api.Capabilities { + capabilities := c.Capabilities if capabilities == nil { return nil } diff --git a/common/config_test.go b/common/config_test.go index 712be1e3d1..66d1528caf 100644 --- a/common/config_test.go +++ b/common/config_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" api "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" "gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags" "gitlab.com/gitlab-org/gitlab-runner/helpers/process" @@ -1337,10 +1336,7 @@ func TestEffectivePrivilege(t *testing.T) { for tn, tt := range tests { t.Run(tn, func(t *testing.T) { - k8sConfig := KubernetesConfig{ - Privileged: &tt.pod, - } - effectivePrivileged := k8sConfig.getPrivilegedEffective(&tt.container) + effectivePrivileged := getContainerSecurityContextEffectiveFlagValue(&tt.container, &tt.pod) require.NotNil(t, effectivePrivileged) assert.Equal(t, tt.expected, *effectivePrivileged) }) @@ -1352,211 +1348,85 @@ func TestContainerSecurityContext(t *testing.T) { return &v } + int64Ptr := func(v int64) *int64 { + return &v + } + tests := map[string]struct { - assignSecurityContextFn func(c *KubernetesConfig) - getSecurityContext func(c *KubernetesConfig) *v1.SecurityContext - getExpectedContainerSecurityContext func() *v1.SecurityContext + getSecurityContext func(c *KubernetesConfig) *api.SecurityContext + getExpectedContainerSecurityContext func() *api.SecurityContext }{ - "no build container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) {}, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetBuildContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - return &v1.SecurityContext{ - Privileged: &privileged, - } - }, - }, - "run as user - build container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - runAsUser := int64(1000) - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ - RunAsUser: &runAsUser, - } - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetBuildContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - runAsUser := int64(1000) - return &v1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } - }, - }, - "no service container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) {}, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetServiceContainerSecurityContext() + "no container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{}) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - return &v1.SecurityContext{ - Privileged: &privileged, - } - }, - }, - "run as user - service container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - runAsUser := int64(900) - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ - RunAsUser: &runAsUser, - } - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetServiceContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - runAsUser := int64(900) - return &v1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } + getExpectedContainerSecurityContext: func() *api.SecurityContext { + return &api.SecurityContext{} }, }, - "no helper container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) {}, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetHelperContainerSecurityContext() + "run as user - container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + RunAsUser: int64Ptr(1000), + }) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - return &v1.SecurityContext{ - Privileged: &privileged, - } - }, - }, - "run as user - helper container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - runAsUser := int64(65535) - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ + getExpectedContainerSecurityContext: func() *api.SecurityContext { + runAsUser := int64(1000) + return &api.SecurityContext{ RunAsUser: &runAsUser, } }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetHelperContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - runAsUser := int64(65535) - return &v1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } - }, }, - "privileged - helper container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.Privileged = boolPtr(true) - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetHelperContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := true - return &v1.SecurityContext{ - Privileged: &privileged, - } - }, - }, - "privileged - service container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.Privileged = boolPtr(true) - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetServiceContainerSecurityContext() - }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := true - return &v1.SecurityContext{ - Privileged: &privileged, - } - }, - }, - "privileged - build container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.Privileged = boolPtr(true) - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetBuildContainerSecurityContext() + "privileged - container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Privileged: boolPtr(true), + }) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - return &v1.SecurityContext{ + getExpectedContainerSecurityContext: func() *api.SecurityContext { + return &api.SecurityContext{ Privileged: boolPtr(true), } }, }, - "container privileged override - helper container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { + "container privileged override - container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { c.Privileged = boolPtr(true) - - privileged := false - runAsUser := int64(65535) - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetHelperContainerSecurityContext() + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Privileged: boolPtr(false), + RunAsUser: int64Ptr(65535), + }) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false + getExpectedContainerSecurityContext: func() *api.SecurityContext { runAsUser := int64(65535) - return &v1.SecurityContext{ - Privileged: &privileged, + return &api.SecurityContext{ + Privileged: boolPtr(false), RunAsUser: &runAsUser, } }, }, - "container privileged override - service container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.Privileged = boolPtr(true) - - privileged := false - runAsUser := int64(65535) - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetServiceContainerSecurityContext() + "allow privilege escalation - not set on container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + AllowPrivilegeEscalation: boolPtr(true), + }) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - runAsUser := int64(65535) - return &v1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, + getExpectedContainerSecurityContext: func() *api.SecurityContext { + return &api.SecurityContext{ + AllowPrivilegeEscalation: boolPtr(true), } }, }, - "container privileged override - build container security context": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.Privileged = boolPtr(true) - - privileged := false - runAsUser := int64(65535) - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, - } - }, - getSecurityContext: func(c *KubernetesConfig) *v1.SecurityContext { - return c.GetBuildContainerSecurityContext() + "allow privilege escalation - set on container security context": { + getSecurityContext: func(c *KubernetesConfig) *api.SecurityContext { + c.AllowPrivilegeEscalation = boolPtr(true) + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + AllowPrivilegeEscalation: boolPtr(false), + }) }, - getExpectedContainerSecurityContext: func() *v1.SecurityContext { - privileged := false - runAsUser := int64(65535) - return &v1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsUser, + getExpectedContainerSecurityContext: func() *api.SecurityContext { + return &api.SecurityContext{ + AllowPrivilegeEscalation: boolPtr(false), } }, }, @@ -1565,9 +1435,6 @@ func TestContainerSecurityContext(t *testing.T) { for tn, tt := range tests { t.Run(tn, func(t *testing.T) { config := new(KubernetesConfig) - tt.assignSecurityContextFn(config) - - tt.assignSecurityContextFn(config) scExpected := tt.getExpectedContainerSecurityContext() scActual := tt.getSecurityContext(config) assert.Equal(t, scExpected, scActual) @@ -1577,202 +1444,209 @@ func TestContainerSecurityContext(t *testing.T) { func TestContainerSecurityCapabilities(t *testing.T) { tests := map[string]struct { - assignSecurityContextFn func(c *KubernetesConfig) - getCapabilitiesFn func(c *KubernetesConfig) *v1.Capabilities - expectedCapabilities *v1.Capabilities + getCapabilitiesFn func(c *KubernetesConfig) *api.Capabilities + expectedCapabilities *api.Capabilities }{ - "build container add": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ - Capabilities: &KubernetesBuildContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, + "container add": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Capabilities: &KubernetesContainerCapabilities{ + Add: []api.Capability{"SYS_TIME"}, }, - } - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getBuildContainerCapabilities() + }).Capabilities }, - - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, + expectedCapabilities: &api.Capabilities{ + Add: []api.Capability{"SYS_TIME"}, Drop: nil, }, }, - "build container drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ - Capabilities: &KubernetesBuildContainerCapabilities{ - Drop: []v1.Capability{"SYS_TIME"}, + "container drop": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Capabilities: &KubernetesContainerCapabilities{ + Drop: []api.Capability{"SYS_TIME"}, }, - } + }).Capabilities }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getBuildContainerCapabilities() - }, - - expectedCapabilities: &v1.Capabilities{ + expectedCapabilities: &api.Capabilities{ Add: nil, - Drop: []v1.Capability{"SYS_TIME"}, + Drop: []api.Capability{"SYS_TIME"}, }, }, - "build container add and drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{ - Capabilities: &KubernetesBuildContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, + "container add and drop": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Capabilities: &KubernetesContainerCapabilities{ + Add: []api.Capability{"SYS_TIME"}, + Drop: []api.Capability{"SYS_TIME"}, }, - } - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getBuildContainerCapabilities() + }).Capabilities }, - - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, + expectedCapabilities: &api.Capabilities{ + Add: []api.Capability{"SYS_TIME"}, + Drop: []api.Capability{"SYS_TIME"}, }, }, - "build container empty": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.BuildContainerSecurityContext = KubernetesBuildContainerSecurityContext{} - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getBuildContainerCapabilities() + "container empty": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{}).Capabilities }, - expectedCapabilities: nil, }, - "helper container add": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ - Capabilities: &KubernetesHelperContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, - }, - } + "container when capAdd and capDrop exist": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + c.CapAdd = []string{"add"} + c.CapDrop = []string{"drop"} + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{}).Capabilities }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getHelperContainerCapabilities() - }, - - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: nil, + expectedCapabilities: &api.Capabilities{ + Add: []api.Capability{"add"}, + Drop: []api.Capability{"drop"}, }, }, - "helper container drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ - Capabilities: &KubernetesHelperContainerCapabilities{ - Drop: []v1.Capability{"SYS_TIME"}, + "container when capAdd and container capabilities exist": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + c.CapAdd = []string{"add"} + c.CapDrop = []string{"drop"} + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Capabilities: &KubernetesContainerCapabilities{ + Add: []api.Capability{"add container"}, }, - } + }).Capabilities }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getHelperContainerCapabilities() - }, - - expectedCapabilities: &v1.Capabilities{ - Add: nil, - Drop: []v1.Capability{"SYS_TIME"}, + expectedCapabilities: &api.Capabilities{ + Add: []api.Capability{"add container"}, + Drop: []api.Capability{"drop"}, }, }, - "helper container add and drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{ - Capabilities: &KubernetesHelperContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, + "container when capDrop and container capabilities exist": { + getCapabilitiesFn: func(c *KubernetesConfig) *api.Capabilities { + c.CapAdd = []string{"add"} + c.CapDrop = []string{"drop"} + return c.GetContainerSecurityContext(KubernetesContainerSecurityContext{ + Capabilities: &KubernetesContainerCapabilities{ + Drop: []api.Capability{"drop container"}, }, - } + }).Capabilities }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getHelperContainerCapabilities() + expectedCapabilities: &api.Capabilities{ + Add: []api.Capability{"add"}, + Drop: []api.Capability{"drop container"}, }, - - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, - }, - }, - "helper container empty": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.HelperContainerSecurityContext = KubernetesHelperContainerSecurityContext{} - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getHelperContainerCapabilities() - }, - expectedCapabilities: nil, }, - "service container add": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ - Capabilities: &KubernetesServiceContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, - }, - } - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getServiceContainerCapabilities() - }, - - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: nil, - }, - }, - "service container drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ - Capabilities: &KubernetesServiceContainerCapabilities{ - Drop: []v1.Capability{"SYS_TIME"}, - }, - } - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getServiceContainerCapabilities() - }, + } - expectedCapabilities: &v1.Capabilities{ - Add: nil, - Drop: []v1.Capability{"SYS_TIME"}, - }, - }, - "service container add and drop": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{ - Capabilities: &KubernetesServiceContainerCapabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, - }, - } - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getServiceContainerCapabilities() - }, + for tn, tt := range tests { + t.Run(tn, func(t *testing.T) { + config := new(KubernetesConfig) + c := tt.getCapabilitiesFn(config) + assert.Equal(t, tt.expectedCapabilities, c) + }) + } +} - expectedCapabilities: &v1.Capabilities{ - Add: []v1.Capability{"SYS_TIME"}, - Drop: []v1.Capability{"SYS_TIME"}, - }, - }, - "service container empty": { - assignSecurityContextFn: func(c *KubernetesConfig) { - c.ServiceContainerSecurityContext = KubernetesServiceContainerSecurityContext{} - }, - getCapabilitiesFn: func(c *KubernetesConfig) *v1.Capabilities { - return c.getServiceContainerCapabilities() +func TestGetCapabilities(t *testing.T) { + tests := map[string]struct { + defaultCapDrop []string + capAdd []string + capDrop []string + assertCapabilities func(t *testing.T, a *api.Capabilities) + }{ + "no data provided": { + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + assert.Nil(t, a) + }, + }, + "only default_cap_drop provided": { + defaultCapDrop: []string{"CAP_1", "CAP_2"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Empty(t, a.Add) + assert.Len(t, a.Drop, 2) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + assert.Contains(t, a.Drop, api.Capability("CAP_2")) + }, + }, + "only custom cap_add provided": { + capAdd: []string{"CAP_1", "CAP_2"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Len(t, a.Add, 2) + assert.Contains(t, a.Add, api.Capability("CAP_1")) + assert.Contains(t, a.Add, api.Capability("CAP_2")) + assert.Empty(t, a.Drop) + }, + }, + "only custom cap_drop provided": { + capDrop: []string{"CAP_1", "CAP_2"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Empty(t, a.Add) + assert.Len(t, a.Drop, 2) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + assert.Contains(t, a.Drop, api.Capability("CAP_2")) + }, + }, + "default_cap_drop and custom cap_drop sums": { + defaultCapDrop: []string{"CAP_1", "CAP_2"}, + capDrop: []string{"CAP_3", "CAP_4"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Empty(t, a.Add) + assert.Len(t, a.Drop, 4) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + assert.Contains(t, a.Drop, api.Capability("CAP_2")) + assert.Contains(t, a.Drop, api.Capability("CAP_3")) + assert.Contains(t, a.Drop, api.Capability("CAP_4")) + }, + }, + "default_cap_drop and custom cap_drop duplicate": { + defaultCapDrop: []string{"CAP_1", "CAP_2"}, + capDrop: []string{"CAP_2", "CAP_3"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Empty(t, a.Add) + assert.Len(t, a.Drop, 3) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + assert.Contains(t, a.Drop, api.Capability("CAP_2")) + assert.Contains(t, a.Drop, api.Capability("CAP_3")) + }, + }, + "default_cap_drop and custom cap_add intersect": { + defaultCapDrop: []string{"CAP_1", "CAP_2"}, + capAdd: []string{"CAP_2", "CAP_3"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Len(t, a.Add, 2) + assert.Contains(t, a.Add, api.Capability("CAP_2")) + assert.Contains(t, a.Add, api.Capability("CAP_3")) + assert.Len(t, a.Drop, 1) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + }, + }, + "default_cap_drop and custom cap_add intersect and cap_drop forces": { + defaultCapDrop: []string{"CAP_1", "CAP_2"}, + capAdd: []string{"CAP_2", "CAP_3"}, + capDrop: []string{"CAP_2", "CAP_4"}, + assertCapabilities: func(t *testing.T, a *api.Capabilities) { + require.NotNil(t, a) + assert.Len(t, a.Add, 1) + assert.Contains(t, a.Add, api.Capability("CAP_3")) + assert.Len(t, a.Drop, 3) + assert.Contains(t, a.Drop, api.Capability("CAP_1")) + assert.Contains(t, a.Drop, api.Capability("CAP_2")) + assert.Contains(t, a.Drop, api.Capability("CAP_4")) }, - expectedCapabilities: nil, }, } for tn, tt := range tests { t.Run(tn, func(t *testing.T) { - config := new(KubernetesConfig) - tt.assignSecurityContextFn(config) + c := KubernetesConfig{ + CapAdd: tt.capAdd, + CapDrop: tt.capDrop, + } - c := tt.getCapabilitiesFn(config) - assert.Equal(t, tt.expectedCapabilities, c) + tt.assertCapabilities(t, c.getCapabilities(tt.defaultCapDrop)) }) } } diff --git a/docs/executors/kubernetes.md b/docs/executors/kubernetes.md index acb9911d4a..1947e5c6a5 100644 --- a/docs/executors/kubernetes.md +++ b/docs/executors/kubernetes.md @@ -566,17 +566,17 @@ check_interval = 30 ### Container security context -> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1507) in GitLab runner 14.3 +> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/3116) in GitLab Runner 14.5. -The [container security context](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) configuration configures the executor to set a container security policy on the build, helper or service pods. +Use the [container security context](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) configuration to configure the executor to set a container security policy on the build, helper, or service pods. | Option | Type | Required | Description | |-----------------------|-------------|----------|-------------| -| `run_as_group` | int | no | The GID to run the entry point of the container process | -| `run_as_non_root` | boolean | no | Indicates that the container must run as a non-root user | -| `run_as_user` | int | no | The UID to run the entry point of the container process | -| `capabilities.add` | string list | no | The capabilities to add when running the container | -| `capabilities.drop` | string list | no | The capabilities to drop when running the container | +| `run_as_group` | int | No | The GID to run the entry point of the container process. | +| `run_as_non_root` | boolean | No | Indicates that the container must run as a non-root user. | +| `run_as_user` | int | No | The UID to run the entry point of the container process. | +| `capabilities.add` | string list | No | The capabilities to add when running the container. | +| `capabilities.drop` | string list | No | The capabilities to drop when running the container. | The example below sets a pod security context, then overrides `run_as_user` and `run_as_group` for both the build and helper containers. In the example, all service containers would inherit `run_as_user` and `run_as_group` from the pod security context. @@ -609,7 +609,6 @@ check_interval = 30 run_as_group = 1000 ``` - ## Using services > - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4470) in GitLab Runner 12.5. diff --git a/executors/kubernetes/kubernetes.go b/executors/kubernetes/kubernetes.go index 5f669d77a9..7e1ab64115 100644 --- a/executors/kubernetes/kubernetes.go +++ b/executors/kubernetes/kubernetes.go @@ -75,21 +75,6 @@ var ( errIncorrectShellType = fmt.Errorf("kubernetes executor incorrect shell type") ) -// GetDefaultCapDrop returns the default capabilities that should be dropped -// from a build container. -func GetDefaultCapDrop(os string) []string { - // windows does not support security context capabilities - if os == helperimage.OSTypeWindows { - return nil - } - - return []string{ - // Reasons for disabling NET_RAW by default were - // discussed in https://gitlab.com/gitlab-org/gitlab-runner/-/issues/26833 - "NET_RAW", - } -} - type commandTerminatedError struct { exitCode int } @@ -1232,7 +1217,10 @@ func (s *executor) setupBuildPod(initContainers []api.Container) error { imageDefinition: service, requests: s.configurationOverwrites.serviceRequests, limits: s.configurationOverwrites.serviceLimits, - securityContext: s.Config.Kubernetes.GetServiceContainerSecurityContext(), + securityContext: s.Config.Kubernetes.GetContainerSecurityContext( + s.Config.Kubernetes.ServiceContainerSecurityContext, + s.defaultCapDrop()..., + ), }) if err != nil { return err @@ -1298,6 +1286,20 @@ func (s *executor) setupBuildPod(initContainers []api.Container) error { return nil } +func (s *executor) defaultCapDrop() []string { + os := s.helperImageInfo.OSType + // windows does not support security context capabilities + if os == helperimage.OSTypeWindows { + return nil + } + + return []string{ + // Reasons for disabling NET_RAW by default were + // discussed in https://gitlab.com/gitlab-org/gitlab-runner/-/issues/26833 + "NET_RAW", + } +} + //nolint:funlen func (s *executor) preparePodConfig( labels, annotations map[string]string, @@ -1318,20 +1320,26 @@ func (s *executor) preparePodConfig( imageDefinition: s.options.Image, requests: s.configurationOverwrites.buildRequests, limits: s.configurationOverwrites.buildLimits, - securityContext: s.Config.Kubernetes.GetBuildContainerSecurityContext(), - command: dockerCmdForBuildContainer, + securityContext: s.Config.Kubernetes.GetContainerSecurityContext( + s.Config.Kubernetes.BuildContainerSecurityContext, + s.defaultCapDrop()..., + ), + command: dockerCmdForBuildContainer, }) if err != nil { return api.Pod{}, fmt.Errorf("building build container: %w", err) } helperContainer, err := s.buildContainer(containerBuildOpts{ - name: helperContainerName, - image: s.getHelperImage(), - requests: s.configurationOverwrites.helperRequests, - limits: s.configurationOverwrites.helperLimits, - securityContext: s.Config.Kubernetes.GetHelperContainerSecurityContext(), - command: s.BuildShell.DockerCommand, + name: helperContainerName, + image: s.getHelperImage(), + requests: s.configurationOverwrites.helperRequests, + limits: s.configurationOverwrites.helperLimits, + securityContext: s.Config.Kubernetes.GetContainerSecurityContext( + s.Config.Kubernetes.HelperContainerSecurityContext, + s.defaultCapDrop()..., + ), + command: s.BuildShell.DockerCommand, }) if err != nil { return api.Pod{}, fmt.Errorf("building helper container: %w", err) diff --git a/executors/kubernetes/util.go b/executors/kubernetes/util.go index 904ecf94d3..86a140eea9 100644 --- a/executors/kubernetes/util.go +++ b/executors/kubernetes/util.go @@ -309,42 +309,6 @@ func buildVariables(bv common.JobVariables) []api.EnvVar { return e } -func getCapabilities(defaultCapDrop []string, capAdd []string, capDrop []string) *api.Capabilities { - enabled := make(map[string]bool) - - for _, v := range defaultCapDrop { - enabled[v] = false - } - - for _, v := range capAdd { - enabled[v] = true - } - - for _, v := range capDrop { - enabled[v] = false - } - - if len(enabled) < 1 { - return nil - } - - return buildCapabilities(enabled) -} - -func buildCapabilities(enabled map[string]bool) *api.Capabilities { - capabilities := new(api.Capabilities) - - for c, add := range enabled { - if add { - capabilities.Add = append(capabilities.Add, api.Capability(c)) - continue - } - capabilities.Drop = append(capabilities.Drop, api.Capability(c)) - } - - return capabilities -} - // Sanitize labels to match Kubernetes restrictions from https://kubernetes.io/ // /docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set //nolint:gocognit diff --git a/executors/kubernetes/util_test.go b/executors/kubernetes/util_test.go index 9fefcd4ec5..b103f8420d 100644 --- a/executors/kubernetes/util_test.go +++ b/executors/kubernetes/util_test.go @@ -503,108 +503,6 @@ func testVersionAndCodec() (version string, codec runtime.Codec) { return } -func TestGetCapabilities(t *testing.T) { - tests := map[string]struct { - defaultCapDrop []string - capAdd []string - capDrop []string - assertCapabilities func(t *testing.T, a *api.Capabilities) - }{ - "no data provided": { - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - assert.Nil(t, a) - }, - }, - "only default_cap_drop provided": { - defaultCapDrop: []string{"CAP_1", "CAP_2"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Empty(t, a.Add) - assert.Len(t, a.Drop, 2) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - assert.Contains(t, a.Drop, api.Capability("CAP_2")) - }, - }, - "only custom cap_add provided": { - capAdd: []string{"CAP_1", "CAP_2"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Len(t, a.Add, 2) - assert.Contains(t, a.Add, api.Capability("CAP_1")) - assert.Contains(t, a.Add, api.Capability("CAP_2")) - assert.Empty(t, a.Drop) - }, - }, - "only custom cap_drop provided": { - capDrop: []string{"CAP_1", "CAP_2"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Empty(t, a.Add) - assert.Len(t, a.Drop, 2) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - assert.Contains(t, a.Drop, api.Capability("CAP_2")) - }, - }, - "default_cap_drop and custom cap_drop sums": { - defaultCapDrop: []string{"CAP_1", "CAP_2"}, - capDrop: []string{"CAP_3", "CAP_4"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Empty(t, a.Add) - assert.Len(t, a.Drop, 4) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - assert.Contains(t, a.Drop, api.Capability("CAP_2")) - assert.Contains(t, a.Drop, api.Capability("CAP_3")) - assert.Contains(t, a.Drop, api.Capability("CAP_4")) - }, - }, - "default_cap_drop and custom cap_drop duplicate": { - defaultCapDrop: []string{"CAP_1", "CAP_2"}, - capDrop: []string{"CAP_2", "CAP_3"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Empty(t, a.Add) - assert.Len(t, a.Drop, 3) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - assert.Contains(t, a.Drop, api.Capability("CAP_2")) - assert.Contains(t, a.Drop, api.Capability("CAP_3")) - }, - }, - "default_cap_drop and custom cap_add intersect": { - defaultCapDrop: []string{"CAP_1", "CAP_2"}, - capAdd: []string{"CAP_2", "CAP_3"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Len(t, a.Add, 2) - assert.Contains(t, a.Add, api.Capability("CAP_2")) - assert.Contains(t, a.Add, api.Capability("CAP_3")) - assert.Len(t, a.Drop, 1) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - }, - }, - "default_cap_drop and custom cap_add intersect and cap_drop forces": { - defaultCapDrop: []string{"CAP_1", "CAP_2"}, - capAdd: []string{"CAP_2", "CAP_3"}, - capDrop: []string{"CAP_2", "CAP_4"}, - assertCapabilities: func(t *testing.T, a *api.Capabilities) { - require.NotNil(t, a) - assert.Len(t, a.Add, 1) - assert.Contains(t, a.Add, api.Capability("CAP_3")) - assert.Len(t, a.Drop, 3) - assert.Contains(t, a.Drop, api.Capability("CAP_1")) - assert.Contains(t, a.Drop, api.Capability("CAP_2")) - assert.Contains(t, a.Drop, api.Capability("CAP_4")) - }, - }, - } - - for tn, tt := range tests { - t.Run(tn, func(t *testing.T) { - tt.assertCapabilities(t, getCapabilities(tt.defaultCapDrop, tt.capAdd, tt.capDrop)) - }) - } -} - func TestSanitizeLabel(t *testing.T) { tests := []struct { Name string -- GitLab