@@ -3204,6 +3204,8 @@ type PodValidationOptions struct {
3204
3204
AllowInvalidPodDeletionCost bool
3205
3205
// Allow pod spec to use non-integer multiple of huge page unit size
3206
3206
AllowIndivisibleHugePagesValues bool
3207
+ // Allow hostProcess field to be set in windows security context
3208
+ AllowWindowsHostProcessField bool
3207
3209
}
3208
3210
3209
3211
// ValidatePodSingleHugePageResources checks if there are multiple huge
@@ -3327,6 +3329,7 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
3327
3329
allErrs = append (allErrs , validatePodDNSConfig (spec .DNSConfig , & spec .DNSPolicy , fldPath .Child ("dnsConfig" ))... )
3328
3330
allErrs = append (allErrs , validateReadinessGates (spec .ReadinessGates , fldPath .Child ("readinessGates" ))... )
3329
3331
allErrs = append (allErrs , validateTopologySpreadConstraints (spec .TopologySpreadConstraints , fldPath .Child ("topologySpreadConstraints" ))... )
3332
+ allErrs = append (allErrs , validateWindowsHostProcessPod (spec , fldPath , opts )... )
3330
3333
if len (spec .ServiceAccountName ) > 0 {
3331
3334
for _ , msg := range ValidateServiceAccountName (spec .ServiceAccountName , false ) {
3332
3335
allErrs = append (allErrs , field .Invalid (fldPath .Child ("serviceAccountName" ), spec .ServiceAccountName , msg ))
@@ -5974,6 +5977,91 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
5974
5977
return allErrs
5975
5978
}
5976
5979
5980
+ func validateWindowsHostProcessPod (podSpec * core.PodSpec , fieldPath * field.Path , opts PodValidationOptions ) field.ErrorList {
5981
+ allErrs := field.ErrorList {}
5982
+
5983
+ // Keep track of container and hostProcess container count for validate
5984
+ containerCount := 0
5985
+ hostProcessContainerCount := 0
5986
+
5987
+ var podHostProcess * bool
5988
+ if podSpec .SecurityContext != nil && podSpec .SecurityContext .WindowsOptions != nil {
5989
+ podHostProcess = podSpec .SecurityContext .WindowsOptions .HostProcess
5990
+ }
5991
+
5992
+ if ! opts .AllowWindowsHostProcessField && podHostProcess != nil {
5993
+ // Do not allow pods to persist data that sets hostProcess (true or false)
5994
+ errMsg := "not allowed when feature gate 'WindowsHostProcessContainers' is not enabled"
5995
+ allErrs = append (allErrs , field .Forbidden (fieldPath .Child ("securityContext" , "windowsOptions" , "hostProcess" ), errMsg ))
5996
+ return allErrs
5997
+ }
5998
+
5999
+ hostNetwork := false
6000
+ if podSpec .SecurityContext != nil {
6001
+ hostNetwork = podSpec .SecurityContext .HostNetwork
6002
+ }
6003
+
6004
+ podshelper .VisitContainersWithPath (podSpec , fieldPath , func (c * core.Container , cFieldPath * field.Path ) bool {
6005
+ containerCount ++
6006
+
6007
+ var containerHostProcess * bool = nil
6008
+ if c .SecurityContext != nil && c .SecurityContext .WindowsOptions != nil {
6009
+ containerHostProcess = c .SecurityContext .WindowsOptions .HostProcess
6010
+ }
6011
+
6012
+ if ! opts .AllowWindowsHostProcessField && containerHostProcess != nil {
6013
+ // Do not allow pods to persist data that sets hostProcess (true or false)
6014
+ errMsg := "not allowed when feature gate 'WindowsHostProcessContainers' is not enabled"
6015
+ allErrs = append (allErrs , field .Forbidden (cFieldPath .Child ("securityContext" , "windowsOptions" , "hostProcess" ), errMsg ))
6016
+ }
6017
+
6018
+ if podHostProcess != nil && containerHostProcess != nil && * podHostProcess != * containerHostProcess {
6019
+ errMsg := fmt .Sprintf ("pod hostProcess value must be identical if both are specified, was %v" , * podHostProcess )
6020
+ allErrs = append (allErrs , field .Invalid (cFieldPath .Child ("securityContext" , "windowsOptions" , "hostProcess" ), * containerHostProcess , errMsg ))
6021
+ }
6022
+
6023
+ switch {
6024
+ case containerHostProcess != nil && * containerHostProcess :
6025
+ // Container explitly sets hostProcess=true
6026
+ hostProcessContainerCount ++
6027
+ case containerHostProcess == nil && podHostProcess != nil && * podHostProcess :
6028
+ // Container inherits hostProcess=true from pod settings
6029
+ hostProcessContainerCount ++
6030
+ }
6031
+
6032
+ return true
6033
+ })
6034
+
6035
+ if hostProcessContainerCount > 0 {
6036
+ // Fail Pod validation if feature is not enabled (unless podspec already exists and contains HostProcess fields) instead of dropping fields based on PRR reivew.
6037
+ if ! opts .AllowWindowsHostProcessField {
6038
+ errMsg := "pod must not contain Windows hostProcess containers when feature gate 'WindowsHostProcessContainers' is not enabled"
6039
+ allErrs = append (allErrs , field .Forbidden (fieldPath , errMsg ))
6040
+ return allErrs
6041
+ }
6042
+
6043
+ // At present, if a Windows Pods contains any HostProcess containers than all containers must be
6044
+ // HostProcess containers (explicitly set or inherited).
6045
+ if hostProcessContainerCount != containerCount {
6046
+ errMsg := "If pod contains any hostProcess containers then all containers must be HostProcess containers"
6047
+ allErrs = append (allErrs , field .Invalid (fieldPath , "" , errMsg ))
6048
+ }
6049
+
6050
+ // At present Windows Pods which contain HostProcess containers must also set HostNetwork.
6051
+ if hostNetwork != true {
6052
+ errMsg := "hostNetwork must be true if pod contains any hostProcess containers"
6053
+ allErrs = append (allErrs , field .Invalid (fieldPath .Child ("hostNetwork" ), hostNetwork , errMsg ))
6054
+ }
6055
+
6056
+ if ! capabilities .Get ().AllowPrivileged {
6057
+ errMsg := "hostProcess containers are disallowed by cluster policy"
6058
+ allErrs = append (allErrs , field .Forbidden (fieldPath , errMsg ))
6059
+ }
6060
+ }
6061
+
6062
+ return allErrs
6063
+ }
6064
+
5977
6065
func ValidatePodLogOptions (opts * core.PodLogOptions ) field.ErrorList {
5978
6066
allErrs := field.ErrorList {}
5979
6067
if opts .TailLines != nil && * opts .TailLines < 0 {
0 commit comments