diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 6a2bfa56aee81..78a17a62d9e70 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -10473,7 +10473,7 @@ "type": "string" }, "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. This field is beta-level and may be disabled with the WindowsRunAsUserName feature flag.", + "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" } }, diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 4e9fc774a08c6..951af288859d5 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -391,8 +391,6 @@ func dropDisabledFields( dropDisabledGMSAFields(podSpec, oldPodSpec) - dropDisabledRunAsUserNameFields(podSpec, oldPodSpec) - if !utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && !runtimeClassInUse(oldPodSpec) { // Set RuntimeClassName to nil only if feature is disabled and it is not used podSpec.RuntimeClassName = nil @@ -469,38 +467,6 @@ func dropDisabledGMSAFieldsFromContainers(containers []api.Container) { } } -// dropDisabledRunAsUserNameFields removes disabled fields related to WindowsOptions.RunAsUserName -// from the given PodSpec. -func dropDisabledRunAsUserNameFields(podSpec, oldPodSpec *api.PodSpec) { - if utilfeature.DefaultFeatureGate.Enabled(features.WindowsRunAsUserName) || - runAsUserNameFieldsInUse(oldPodSpec) { - return - } - - if podSpec.SecurityContext != nil { - dropDisabledRunAsUserNameFieldsFromWindowsSecurityOptions(podSpec.SecurityContext.WindowsOptions) - } - dropDisabledRunAsUserNameFieldsFromContainers(podSpec.Containers) - dropDisabledRunAsUserNameFieldsFromContainers(podSpec.InitContainers) -} - -// dropDisabledRunAsUserNameFieldsFromWindowsSecurityOptions removes disabled fields -// related to RunAsUserName from the given WindowsSecurityContextOptions. -func dropDisabledRunAsUserNameFieldsFromWindowsSecurityOptions(windowsOptions *api.WindowsSecurityContextOptions) { - if windowsOptions != nil { - windowsOptions.RunAsUserName = nil - } -} - -// dropDisabledRunAsUserNameFieldsFromContainers removes disabled fields -func dropDisabledRunAsUserNameFieldsFromContainers(containers []api.Container) { - for i := range containers { - if containers[i].SecurityContext != nil { - dropDisabledRunAsUserNameFieldsFromWindowsSecurityOptions(containers[i].SecurityContext.WindowsOptions) - } - } -} - // dropDisabledProcMountField removes disabled fields from PodSpec related // to ProcMount only if it is not already used by the old spec func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) { @@ -758,39 +724,6 @@ func gMSAFieldsInUseInAnyContainer(containers []api.Container) bool { return false } -// runAsUserNameFieldsInUse returns true if the pod spec is non-nil and has the RunAsUserName -// field set in the PodSecurityContext or any container's SecurityContext. -func runAsUserNameFieldsInUse(podSpec *api.PodSpec) bool { - if podSpec == nil { - return false - } - - if podSpec.SecurityContext != nil && runAsUserNameFieldsInUseInWindowsSecurityOptions(podSpec.SecurityContext.WindowsOptions) { - return true - } - - return runAsUserNameFieldsInUseInAnyContainer(podSpec.Containers) || - runAsUserNameFieldsInUseInAnyContainer(podSpec.InitContainers) -} - -// runAsUserNameFieldsInUseInWindowsSecurityOptions returns true if the given WindowsSecurityContextOptions is -// non-nil and its RunAsUserName field is set. -func runAsUserNameFieldsInUseInWindowsSecurityOptions(windowsOptions *api.WindowsSecurityContextOptions) bool { - return windowsOptions != nil && windowsOptions.RunAsUserName != nil -} - -// runAsUserNameFieldsInUseInAnyContainer returns true if any of the given Containers has its -// SecurityContext's RunAsUserName field set. -func runAsUserNameFieldsInUseInAnyContainer(containers []api.Container) bool { - for _, container := range containers { - if container.SecurityContext != nil && runAsUserNameFieldsInUseInWindowsSecurityOptions(container.SecurityContext.WindowsOptions) { - return true - } - } - - return false -} - // subpathExprInUse returns true if the pod spec is non-nil and has a volume mount that makes use of the subPathExpr feature func subpathExprInUse(podSpec *api.PodSpec) bool { if podSpec == nil { diff --git a/pkg/api/pod/util_test.go b/pkg/api/pod/util_test.go index 12485e681f297..7367122f5c860 100644 --- a/pkg/api/pod/util_test.go +++ b/pkg/api/pod/util_test.go @@ -1517,180 +1517,6 @@ func TestDropGMSAFields(t *testing.T) { } } -func TestDropWindowsRunAsUserNameFields(t *testing.T) { - defaultContainerSecurityContextFactory := func() *api.SecurityContext { - defaultProcMount := api.DefaultProcMount - return &api.SecurityContext{ProcMount: &defaultProcMount} - } - podWithoutWindowsOptionsFactory := func() *api.Pod { - return &api.Pod{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyNever, - SecurityContext: &api.PodSecurityContext{}, - Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: defaultContainerSecurityContextFactory()}}, - InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: defaultContainerSecurityContextFactory()}}, - }, - } - } - - type podFactoryInfo struct { - description string - hasRunAsUserNameField bool - // this factory should generate the input pod whose spec will be fed to dropDisabledFields - podFactory func() *api.Pod - // this factory should generate the expected pod after the RunAsUserName fields have been dropped - // we can't just use podWithoutWindowsOptionsFactory as is for this, since in some cases - // we'll be left with a WindowsSecurityContextOptions struct with no RunAsUserName field set, - // as oposed to a nil pointer in the pod generated by podWithoutWindowsOptionsFactory - // if this field is not set, it will default to the podFactory - strippedPodFactory func() *api.Pod - } - - toPtr := func(s string) *string { - return &s - } - - podFactoryInfos := []podFactoryInfo{ - { - description: "is nil", - hasRunAsUserNameField: false, - podFactory: func() *api.Pod { return nil }, - }, - { - description: "does not have any RunAsUserName field set", - hasRunAsUserNameField: false, - podFactory: podWithoutWindowsOptionsFactory, - }, - { - description: "has a pod-level WindowsSecurityContextOptions struct with no RunAsUserName field set", - hasRunAsUserNameField: false, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - { - description: "has a WindowsSecurityContextOptions struct with no RunAsUserName field set on a container", - hasRunAsUserNameField: false, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.Containers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - { - description: "has a WindowsSecurityContextOptions struct with no RunAsUserName field set on an init container", - hasRunAsUserNameField: false, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - { - description: "has RunAsUserName field set in the PodSecurityContext", - hasRunAsUserNameField: true, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{RunAsUserName: toPtr("foo-lish")} - return pod - }, - strippedPodFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - { - description: "has RunAsUserName field set in a container's SecurityContext", - hasRunAsUserNameField: true, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.Containers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{RunAsUserName: toPtr("foo-lish")} - return pod - }, - strippedPodFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.Containers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - { - description: "has RunAsUserName field set in an init container's PodSecurityContext", - hasRunAsUserNameField: true, - podFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{RunAsUserName: toPtr("foo-lish")} - return pod - }, - strippedPodFactory: func() *api.Pod { - pod := podWithoutWindowsOptionsFactory() - pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{} - return pod - }, - }, - } - - for _, enabled := range []bool{true, false} { - for _, oldPodFactoryInfo := range podFactoryInfos { - for _, newPodFactoryInfo := range podFactoryInfos { - newPodHasRunAsUserNameField, newPod := newPodFactoryInfo.hasRunAsUserNameField, newPodFactoryInfo.podFactory() - if newPod == nil { - continue - } - oldPodHasRunAsUserNameField, oldPod := oldPodFactoryInfo.hasRunAsUserNameField, oldPodFactoryInfo.podFactory() - - t.Run(fmt.Sprintf("feature enabled=%v, old pod %s, new pod %s", enabled, oldPodFactoryInfo.description, newPodFactoryInfo.description), func(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsRunAsUserName, enabled)() - - var oldPodSpec *api.PodSpec - if oldPod != nil { - oldPodSpec = &oldPod.Spec - } - dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil) - - // old pod should never be changed - if !reflect.DeepEqual(oldPod, oldPodFactoryInfo.podFactory()) { - t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodFactoryInfo.podFactory())) - } - - switch { - case enabled || oldPodHasRunAsUserNameField: - // new pod should not be changed if the feature is enabled, or if the old pod had the RunAsUserName field set - if !reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) { - t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodFactoryInfo.podFactory())) - } - case newPodHasRunAsUserNameField: - // new pod should be changed - if reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) { - t.Errorf("%v", oldPod) - t.Errorf("%v", newPod) - t.Errorf("new pod was not changed") - } - // new pod should not have the RunAsUserName field set - var expectedStrippedPod *api.Pod - if newPodFactoryInfo.strippedPodFactory == nil { - expectedStrippedPod = newPodFactoryInfo.podFactory() - } else { - expectedStrippedPod = newPodFactoryInfo.strippedPodFactory() - } - - if !reflect.DeepEqual(newPod, expectedStrippedPod) { - t.Errorf("new pod had some RunAsUserName field set: %v", diff.ObjectReflectDiff(newPod, expectedStrippedPod)) - } - default: - // new pod should not need to be changed - if !reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) { - t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodFactoryInfo.podFactory())) - } - } - }) - } - } - } -} - func TestDropPodSysctls(t *testing.T) { podWithSysctls := func() *api.Pod { return &api.Pod{ diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index 6fc9cfb17cfd6..6e69d886901c3 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -5090,7 +5090,6 @@ type WindowsSecurityContextOptions struct { // 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. - // This field is beta-level and may be disabled with the WindowsRunAsUserName feature flag. // +optional RunAsUserName *string } diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index ebf63761fbdd6..f32e2a2f144d2 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -432,6 +432,7 @@ const ( // owner: @bclau // alpha: v1.16 // beta: v1.17 + // GA: v1.18 // // Enables support for running container entrypoints as different usernames than their default ones. WindowsRunAsUserName featuregate.Feature = "WindowsRunAsUserName" @@ -609,7 +610,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS TTLAfterFinished: {Default: false, PreRelease: featuregate.Alpha}, KubeletPodResources: {Default: true, PreRelease: featuregate.Beta}, WindowsGMSA: {Default: true, PreRelease: featuregate.Beta}, - WindowsRunAsUserName: {Default: true, PreRelease: featuregate.Beta}, + WindowsRunAsUserName: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.20 ServiceLoadBalancerFinalizer: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, LocalStorageCapacityIsolationFSQuotaMonitoring: {Default: false, PreRelease: featuregate.Alpha}, NonPreemptingPriority: {Default: false, PreRelease: featuregate.Alpha}, diff --git a/staging/src/k8s.io/api/core/v1/generated.proto b/staging/src/k8s.io/api/core/v1/generated.proto index 1c2eef06f9aab..a7fe8dbca6755 100644 --- a/staging/src/k8s.io/api/core/v1/generated.proto +++ b/staging/src/k8s.io/api/core/v1/generated.proto @@ -5299,7 +5299,6 @@ message WindowsSecurityContextOptions { // 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. - // This field is beta-level and may be disabled with the WindowsRunAsUserName feature flag. // +optional optional string runAsUserName = 3; } diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index 9bfa384db2340..8586521101931 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -5837,7 +5837,6 @@ type WindowsSecurityContextOptions struct { // 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. - // This field is beta-level and may be disabled with the WindowsRunAsUserName feature flag. // +optional RunAsUserName *string `json:"runAsUserName,omitempty" protobuf:"bytes,3,opt,name=runAsUserName"` } diff --git a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go index 7d273bc8af62d..de8bbb076eeb5 100644 --- a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -2460,7 +2460,7 @@ var map_WindowsSecurityContextOptions = map[string]string{ "": "WindowsSecurityContextOptions contain Windows-specific options and credentials.", "gmsaCredentialSpecName": "GMSACredentialSpecName is the name of the GMSA credential spec to use. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", "gmsaCredentialSpec": "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. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", - "runAsUserName": "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. This field is beta-level and may be disabled with the WindowsRunAsUserName feature flag.", + "runAsUserName": "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.", } func (WindowsSecurityContextOptions) SwaggerDoc() map[string]string {