diff --git a/api/v1/archive_types.go b/api/v1/archive_types.go index 2e611ef1..05456dad 100644 --- a/api/v1/archive_types.go +++ b/api/v1/archive_types.go @@ -32,6 +32,9 @@ type InstanceSidecarConfiguration struct { // Resources allocated for the sidecar // +optional Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // SecurityContext for the sidecar container + // +optional + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` } // ArchiveSpec defines the desired state of Archive. diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 6923dacd..3cb43da0 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -127,6 +127,11 @@ func (in *InstanceSidecarConfiguration) DeepCopyInto(out *InstanceSidecarConfigu } } in.Resources.DeepCopyInto(&out.Resources) + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceSidecarConfiguration. diff --git a/config/crd/bases/pgbackrest.cnpg.opera.com_archives.yaml b/config/crd/bases/pgbackrest.cnpg.opera.com_archives.yaml index dce37860..082ea025 100644 --- a/config/crd/bases/pgbackrest.cnpg.opera.com_archives.yaml +++ b/config/crd/bases/pgbackrest.cnpg.opera.com_archives.yaml @@ -552,6 +552,198 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + securityContext: + description: SecurityContext for the sidecar container + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object type: object required: - configuration diff --git a/internal/cnpgi/operator/lifecycle.go b/internal/cnpgi/operator/lifecycle.go index 06ac7528..13aedefb 100644 --- a/internal/cnpgi/operator/lifecycle.go +++ b/internal/cnpgi/operator/lifecycle.go @@ -138,6 +138,21 @@ func (impl LifecycleImplementation) calculateSidecarResources( return nil } +func (impl LifecycleImplementation) calculateSidecarSecurityContext( + ctx context.Context, + archive *pgbackrestv1.Archive, +) *corev1.SecurityContext { + contextLogger := log.FromContext(ctx).WithName("lifecycle") + + if archive != nil && archive.Spec.InstanceSidecarConfiguration.SecurityContext != nil { + contextLogger.Info("Loading sidecar security context definition from the archive object.") + return archive.Spec.InstanceSidecarConfiguration.SecurityContext + } + + contextLogger.Info("Security context definition not found in the archive object, using default (no restrictions).") + return nil +} + func (impl LifecycleImplementation) getArchives( ctx context.Context, namespace string, @@ -223,8 +238,9 @@ func (impl LifecycleImplementation) reconcileJob( return nil, err } resources := impl.calculateSidecarResources(ctx, recoveryArchive) + securityContext := impl.calculateSidecarSecurityContext(ctx, recoveryArchive) - return reconcileJob(ctx, cluster, request, env, resources) + return reconcileJob(ctx, cluster, request, env, resources, securityContext) } func reconcileJob( @@ -233,6 +249,7 @@ func reconcileJob( request *lifecycle.OperatorLifecycleRequest, env []corev1.EnvVar, resources *corev1.ResourceRequirements, + securityContext *corev1.SecurityContext, ) (*lifecycle.OperatorLifecycleResponse, error) { contextLogger := log.FromContext(ctx).WithName("lifecycle") if pluginConfig := cluster.GetRecoverySourcePlugin(); pluginConfig == nil || pluginConfig.Name != metadata.PluginName { @@ -268,7 +285,7 @@ func reconcileJob( corev1.Container{ Args: []string{"restore"}, }, - env, resources, + env, resources, securityContext, ); err != nil { return nil, fmt.Errorf("while reconciling pod spec for job: %w", err) } @@ -299,8 +316,9 @@ func (impl LifecycleImplementation) reconcilePod( return nil, err } resources := impl.calculateSidecarResources(ctx, archive) + securityContext := impl.calculateSidecarSecurityContext(ctx, archive) - return reconcilePod(ctx, cluster, request, pluginConfiguration, env, resources) + return reconcilePod(ctx, cluster, request, pluginConfiguration, env, resources, securityContext) } func reconcilePod( @@ -310,6 +328,7 @@ func reconcilePod( pluginConfiguration *config.PluginConfiguration, env []corev1.EnvVar, resources *corev1.ResourceRequirements, + securityContext *corev1.SecurityContext, ) (*lifecycle.OperatorLifecycleResponse, error) { pod, err := decoder.DecodePodJSON(request.GetObjectDefinition()) if err != nil { @@ -329,7 +348,7 @@ func reconcilePod( corev1.Container{ Args: []string{"instance"}, }, - env, resources, + env, resources, securityContext, ); err != nil { return nil, fmt.Errorf("while reconciling pod spec for pod: %w", err) } @@ -355,6 +374,7 @@ func reconcilePodSpec( sidecarConfig corev1.Container, additionalEnvs []corev1.EnvVar, resources *corev1.ResourceRequirements, + securityContext *corev1.SecurityContext, ) error { envs := []corev1.EnvVar{ { @@ -428,6 +448,10 @@ func reconcilePodSpec( sidecarConfig.Resources = *resources } + if securityContext != nil { + sidecarConfig.SecurityContext = securityContext + } + if err := InjectPluginSidecarPodSpec(spec, &sidecarConfig, mainContainerName, true); err != nil { return err } diff --git a/internal/cnpgi/operator/lifecycle_test.go b/internal/cnpgi/operator/lifecycle_test.go index 4fd4299d..6e5ef2fc 100644 --- a/internal/cnpgi/operator/lifecycle_test.go +++ b/internal/cnpgi/operator/lifecycle_test.go @@ -18,6 +18,7 @@ limitations under the License. package operator import ( + "context" "encoding/json" cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" @@ -26,7 +27,9 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + pgbackrestv1 "github.com/operasoftware/cnpg-plugin-pgbackrest/api/v1" "github.com/operasoftware/cnpg-plugin-pgbackrest/internal/cnpgi/operator/config" . "github.com/onsi/ginkgo/v2" @@ -124,7 +127,7 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: jobJSON, } - response, err := reconcileJob(ctx, cluster, request, nil, nil) + response, err := reconcileJob(ctx, cluster, request, nil, nil, nil) Expect(err).NotTo(HaveOccurred()) Expect(response).NotTo(BeNil()) Expect(response.JsonPatch).NotTo(BeEmpty()) @@ -145,7 +148,7 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: jobJSON, } - response, err := reconcileJob(ctx, cluster, request, nil, nil) + response, err := reconcileJob(ctx, cluster, request, nil, nil, nil) Expect(err).NotTo(HaveOccurred()) Expect(response).To(BeNil()) }) @@ -155,7 +158,7 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: []byte("invalid-json"), } - response, err := reconcileJob(ctx, cluster, request, nil, nil) + response, err := reconcileJob(ctx, cluster, request, nil, nil, nil) Expect(err).To(HaveOccurred()) Expect(response).To(BeNil()) }) @@ -182,7 +185,7 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: jobJSON, } - response, err := reconcileJob(ctx, cluster, request, nil, nil) + response, err := reconcileJob(ctx, cluster, request, nil, nil, nil) Expect(err).NotTo(HaveOccurred()) Expect(response).To(BeNil()) }) @@ -202,7 +205,7 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: podJSON, } - response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil) + response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil, nil) Expect(err).NotTo(HaveOccurred()) Expect(response).NotTo(BeNil()) Expect(response.JsonPatch).NotTo(BeEmpty()) @@ -220,9 +223,275 @@ var _ = Describe("LifecycleImplementation", func() { ObjectDefinition: []byte("invalid-json"), } - response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil) + response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil, nil) Expect(err).To(HaveOccurred()) Expect(response).To(BeNil()) }) }) + + Describe("calculateSidecarSecurityContext", func() { + var ctx context.Context + + BeforeEach(func() { + ctx = context.Background() + }) + + It("returns nil when archive is nil", func() { + result := lifecycleImpl.calculateSidecarSecurityContext(ctx, nil) + Expect(result).To(BeNil()) + }) + + It("returns nil when archive has no security context", func() { + archive := &pgbackrestv1.Archive{ + Spec: pgbackrestv1.ArchiveSpec{ + InstanceSidecarConfiguration: pgbackrestv1.InstanceSidecarConfiguration{ + SecurityContext: nil, + }, + }, + } + + result := lifecycleImpl.calculateSidecarSecurityContext(ctx, archive) + Expect(result).To(BeNil()) + }) + + It("returns configured security context when present", func() { + expectedSecurityContext := &corev1.SecurityContext{ + AllowPrivilegeEscalation: ptr.To(false), + ReadOnlyRootFilesystem: ptr.To(true), + RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(int64(26)), + RunAsGroup: ptr.To(int64(26)), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + } + + archive := &pgbackrestv1.Archive{ + Spec: pgbackrestv1.ArchiveSpec{ + InstanceSidecarConfiguration: pgbackrestv1.InstanceSidecarConfiguration{ + SecurityContext: expectedSecurityContext, + }, + }, + } + + result := lifecycleImpl.calculateSidecarSecurityContext(ctx, archive) + Expect(result).To(Equal(expectedSecurityContext)) + }) + }) + + Describe("reconcilePod with security context", func() { + It("applies custom security context to sidecar when configured", func(ctx SpecContext) { + // Given + pod := &corev1.Pod{ + TypeMeta: podTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + }, + Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "postgres"}}}, + } + podJSON, _ := json.Marshal(pod) + request := &lifecycle.OperatorLifecycleRequest{ + ObjectDefinition: podJSON, + } + + customSecurityContext := &corev1.SecurityContext{ + AllowPrivilegeEscalation: ptr.To(false), + ReadOnlyRootFilesystem: ptr.To(true), + RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(int64(1000)), + RunAsGroup: ptr.To(int64(1000)), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + } + + response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil, customSecurityContext) + Expect(err).NotTo(HaveOccurred()) + Expect(response).NotTo(BeNil()) + Expect(response.JsonPatch).NotTo(BeEmpty()) + + var patch []map[string]interface{} + err = json.Unmarshal(response.JsonPatch, &patch) + Expect(err).NotTo(HaveOccurred()) + + var initContainersPatch map[string]interface{} + for _, p := range patch { + if p["path"] == "/spec/initContainers" && p["op"] == "add" { + initContainersPatch = p + break + } + } + Expect(initContainersPatch).NotTo(BeNil()) + + // Get the init containers patch + initContainers, ok := initContainersPatch["value"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(initContainers).To(HaveLen(1)) + + // Verify the init container contains the security context + container, ok := initContainers[0].(map[string]interface{}) + Expect(ok).To(BeTrue()) + Expect(container).To(HaveKey("securityContext")) + }) + + It("doesn't set security context when it's nil", func(ctx SpecContext) { + pod := &corev1.Pod{ + TypeMeta: podTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + }, + Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "postgres"}}}, + } + podJSON, _ := json.Marshal(pod) + request := &lifecycle.OperatorLifecycleRequest{ + ObjectDefinition: podJSON, + } + + response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(response).NotTo(BeNil()) + Expect(response.JsonPatch).NotTo(BeEmpty()) + + var patch []map[string]interface{} + err = json.Unmarshal(response.JsonPatch, &patch) + Expect(err).NotTo(HaveOccurred()) + + var initContainersPatch map[string]interface{} + for _, p := range patch { + if p["path"] == "/spec/initContainers" && p["op"] == "add" { + initContainersPatch = p + break + } + } + Expect(initContainersPatch).NotTo(BeNil()) + + // Get the init container patch + initContainers, ok := initContainersPatch["value"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(initContainers).To(HaveLen(1)) + + // Verify the init container doesn't contain the security context + container, ok := initContainers[0].(map[string]interface{}) + Expect(ok).To(BeTrue()) + Expect(container).NotTo(HaveKey("securityContext")) + }) + }) + + Describe("reconcileJob with security context", func() { + It("applies custom security context to job sidecar when configured", func(ctx SpecContext) { + job := &batchv1.Job{ + TypeMeta: jobTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Labels: map[string]string{}, + }, + Spec: batchv1.JobSpec{Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + utils.JobRoleLabelName: "full-recovery", + }, + }, + Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "full-recovery"}}}, + }}, + } + jobJSON, _ := json.Marshal(job) + request := &lifecycle.OperatorLifecycleRequest{ + ObjectDefinition: jobJSON, + } + + customSecurityContext := &corev1.SecurityContext{ + AllowPrivilegeEscalation: ptr.To(false), + ReadOnlyRootFilesystem: ptr.To(true), + RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(int64(1000)), + RunAsGroup: ptr.To(int64(1000)), + } + + response, err := reconcileJob(ctx, cluster, request, nil, nil, customSecurityContext) + Expect(err).NotTo(HaveOccurred()) + Expect(response).NotTo(BeNil()) + Expect(response.JsonPatch).NotTo(BeEmpty()) + + var patch []map[string]interface{} + err = json.Unmarshal(response.JsonPatch, &patch) + Expect(err).NotTo(HaveOccurred()) + Expect(patch).NotTo(BeEmpty()) + + var initContainersPatch map[string]interface{} + for _, p := range patch { + if p["path"] == "/spec/template/spec/initContainers" && p["op"] == "add" { + initContainersPatch = p + break + } + } + Expect(initContainersPatch).NotTo(BeNil()) + + // Get the init containers patch + initContainers, ok := initContainersPatch["value"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(initContainers).To(HaveLen(1)) + + // Verify the init container contains the security context + container, ok := initContainers[0].(map[string]interface{}) + Expect(ok).To(BeTrue()) + Expect(container).To(HaveKey("securityContext")) + }) + + It("doesn't set security context when it's nil", func(ctx SpecContext) { + job := &batchv1.Job{ + TypeMeta: jobTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Labels: map[string]string{}, + }, + Spec: batchv1.JobSpec{Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + utils.JobRoleLabelName: "full-recovery", + }, + }, + Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "full-recovery"}}}, + }}, + } + jobJSON, _ := json.Marshal(job) + request := &lifecycle.OperatorLifecycleRequest{ + ObjectDefinition: jobJSON, + } + + response, err := reconcileJob(ctx, cluster, request, nil, nil, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(response).NotTo(BeNil()) + Expect(response.JsonPatch).NotTo(BeEmpty()) + + var patch []map[string]interface{} + err = json.Unmarshal(response.JsonPatch, &patch) + Expect(err).NotTo(HaveOccurred()) + Expect(patch).NotTo(BeEmpty()) + + var initContainersPatch map[string]interface{} + for _, p := range patch { + if p["path"] == "/spec/template/spec/initContainers" && p["op"] == "add" { + initContainersPatch = p + break + } + } + Expect(initContainersPatch).NotTo(BeNil()) + + // Get the init containers patch + initContainers, ok := initContainersPatch["value"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(initContainers).To(HaveLen(1)) + + // Verify the init container doesn't contain the security context + container, ok := initContainers[0].(map[string]interface{}) + Expect(ok).To(BeTrue()) + Expect(container).NotTo(HaveKey("securityContext")) + }) + }) }) diff --git a/manifest.yaml b/manifest.yaml index 3dda961b..9e351b35 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -551,6 +551,198 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + securityContext: + description: SecurityContext for the sidecar container + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object type: object required: - configuration