mirror of
https://github.com/optim-enterprises-bv/kubernetes.git
synced 2025-11-01 10:48:15 +00:00
Windows: Adds RunAsUserName field in WindowsOptions
Adds the field RunAsUserName in the WindowsSecurityContextOptions type, which is used in PodSecurityContext and SecurityContext. This field needs to allow for a valid set of usernames allowed for Windows containers. It must have the format "U This commit also validates the runAsUserName field, making sure that it valid, having the format DOMAIN\USER (case insensitive), where DOMAIN\ is optional and has to be a valid NetBios or DNS domain name. For more information about the restrictions on the DOMAIN and USER parts, look here: [1] [2] Adds the WindowsRunAsUserName alpha feature gate. By default, it is disabled. If the feature gate is not enabled, the WindowsOptions.RunAsUserName field will be dropped from both the PodSecurityContext and container SecurityContext. Co-Authored-By: Claudiu Belu <cbelu@cloudbasesolutions.com> [1] https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and [2] https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/new-localuser?view=powershell-5.1
This commit is contained in:
committed by
Claudiu Belu
parent
5be1efe9bd
commit
e8b369ff3c
@@ -5360,9 +5360,37 @@ func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path) fiel
|
||||
// maxGMSACredentialSpecLength is the max length, in bytes, for the actual contents
|
||||
// of a GMSA cred spec. In general, those shouldn't be more than a few hundred bytes,
|
||||
// so we want to give plenty of room here while still providing an upper bound.
|
||||
// The runAsUserName field will be used to execute the given container's entrypoint, and
|
||||
// it can be formatted as "DOMAIN/USER", where the DOMAIN is optional, maxRunAsUserNameDomainLength
|
||||
// is the max character length for the user's DOMAIN, and maxRunAsUserNameUserLength
|
||||
// is the max character length for the USER itself. Both the DOMAIN and USER have their
|
||||
// own restrictions, and more information about them can be found here:
|
||||
// https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
|
||||
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/new-localuser?view=powershell-5.1
|
||||
const (
|
||||
maxGMSACredentialSpecLengthInKiB = 64
|
||||
maxGMSACredentialSpecLength = maxGMSACredentialSpecLengthInKiB * 1024
|
||||
maxRunAsUserNameDomainLength = 256
|
||||
maxRunAsUserNameUserLength = 21
|
||||
)
|
||||
|
||||
var (
|
||||
// control characters are not permitted in the runAsUserName field.
|
||||
ctrlRegex = regexp.MustCompile(`[[:cntrl:]]+`)
|
||||
|
||||
// a valid NetBios Domain name cannot start with a dot, has at least 1 character,
|
||||
// at most 15 characters, and it cannot the characters: \ / : * ? " < > |
|
||||
validNetBiosRegex = regexp.MustCompile(`^[^\\/:\*\?"<>|\.][^\\/:\*\?"<>|]{0,14}$`)
|
||||
|
||||
// a valid DNS name contains only alphanumeric characters, dots, and dashes.
|
||||
dnsLabelFormat = `[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?`
|
||||
dnsSubdomainFormat = fmt.Sprintf(`^%s(?:\.%s)*$`, dnsLabelFormat, dnsLabelFormat)
|
||||
validWindowsUserDomainDNSRegex = regexp.MustCompile(dnsSubdomainFormat)
|
||||
|
||||
// a username is invalid if it contains the characters: " / \ [ ] : ; | = , + * ? < > @
|
||||
// or it contains only dots or spaces.
|
||||
invalidUserNameCharsRegex = regexp.MustCompile(`["/\\:;|=,\+\*\?<>@\[\]]`)
|
||||
invalidUserNameDotsSpacesRegex = regexp.MustCompile(`^[\. ]+$`)
|
||||
)
|
||||
|
||||
func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityContextOptions, fieldPath *field.Path) field.ErrorList {
|
||||
@@ -5388,6 +5416,59 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
|
||||
}
|
||||
}
|
||||
|
||||
if windowsOptions.RunAsUserName != nil {
|
||||
if l := len(*windowsOptions.RunAsUserName); l == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, "runAsUserName cannot be an empty string"))
|
||||
} else if ctrlRegex.MatchString(*windowsOptions.RunAsUserName) {
|
||||
errMsg := fmt.Sprintf("runAsUserName cannot contain control characters")
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
} else if parts := strings.Split(*windowsOptions.RunAsUserName, "\\"); len(parts) > 2 {
|
||||
errMsg := fmt.Sprintf("runAsUserName cannot contain more than one backslash")
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
} else {
|
||||
var (
|
||||
hasDomain = false
|
||||
domain = ""
|
||||
user string
|
||||
)
|
||||
if len(parts) == 1 {
|
||||
user = parts[0]
|
||||
} else {
|
||||
hasDomain = true
|
||||
domain = parts[0]
|
||||
user = parts[1]
|
||||
}
|
||||
|
||||
if len(domain) >= maxRunAsUserNameDomainLength {
|
||||
errMsg := fmt.Sprintf("runAsUserName's Domain length must be under %d characters", maxRunAsUserNameDomainLength)
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
}
|
||||
|
||||
if hasDomain && !(validNetBiosRegex.MatchString(domain) || validWindowsUserDomainDNSRegex.MatchString(domain)) {
|
||||
errMsg := "runAsUserName's Domain doesn't match the NetBios nor the DNS format"
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
}
|
||||
|
||||
if l := len(user); l == 0 {
|
||||
errMsg := fmt.Sprintf("runAsUserName's User cannot be empty")
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
} else if l >= maxRunAsUserNameUserLength {
|
||||
errMsg := fmt.Sprintf("runAsUserName's User length must be under %d characters", maxRunAsUserNameUserLength)
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
}
|
||||
|
||||
if invalidUserNameDotsSpacesRegex.MatchString(user) {
|
||||
errMsg := `runAsUserName's User cannot contain only periods or spaces`
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
}
|
||||
|
||||
if invalidUserNameCharsRegex.MatchString(user) {
|
||||
errMsg := `runAsUserName's User cannot contain the following characters: "/\:;|=,+*?<>@[]`
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user