// Process transforms Template object into List object. It generates // Parameter values using the defined set of generators first, and then it // substitutes all Parameter expression occurrences with their corresponding // values (currently in the containers' Environment variables only). func (p *Processor) Process(template *api.Template) fielderrors.ValidationErrorList { templateErrors := fielderrors.ValidationErrorList{} if err := p.GenerateParameterValues(template); err != nil { return append(templateErrors.Prefix("Template"), fielderrors.NewFieldInvalid("parameters", err, "failure to generate parameter value")) } for i, item := range template.Objects { if obj, ok := item.(*runtime.Unknown); ok { // TODO: use runtime.DecodeList when it returns ValidationErrorList obj, err := runtime.UnstructuredJSONScheme.Decode(obj.RawJSON) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("objects", err, "unable to handle object")) continue } item = obj } newItem, err := p.SubstituteParameters(template.Parameters, item) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldNotSupported("parameters", err)) } stripNamespace(newItem) if err := util.AddObjectLabels(newItem, template.ObjectLabels); err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("labels", err, "label could not be applied")) } template.Objects[i] = newItem } return templateErrors }
// Validate ensures that the specified values fall within the range of the strategy. func (s *mustRunAsRange) Validate(pod *api.Pod, container *api.Container) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if container.SecurityContext == nil { detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext", container.SecurityContext, detail)) return allErrs } if container.SecurityContext.RunAsUser == nil { detail := fmt.Sprintf("unable to validate nil RunAsUser for container %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext.runAsUser", container.SecurityContext.RunAsUser, detail)) return allErrs } if *container.SecurityContext.RunAsUser < *s.opts.UIDRangeMin || *container.SecurityContext.RunAsUser > *s.opts.UIDRangeMax { detail := fmt.Sprintf("UID on container %s does not match required range. Found %d, required min: %d max: %d", container.Name, *container.SecurityContext.RunAsUser, *s.opts.UIDRangeMin, *s.opts.UIDRangeMax) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext.runAsUser", *container.SecurityContext.RunAsUser, detail)) } return allErrs }
func ValidateIdentity(identity *api.Identity) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, kvalidation.ValidateObjectMeta(&identity.ObjectMeta, false, ValidateIdentityName).Prefix("metadata")...) if len(identity.ProviderName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("providerName")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderName); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerName", identity.ProviderName, msg)) } if len(identity.ProviderUserName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("providerUserName")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderUserName); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerUserName", identity.ProviderUserName, msg)) } if len(identity.ProviderName) > 0 && len(identity.ProviderUserName) > 0 { expectedIdentityName := identity.ProviderName + ":" + identity.ProviderUserName if identity.Name != expectedIdentityName { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.name", identity.User.Name, fmt.Sprintf("must be %s", expectedIdentityName))) } } if ok, msg := ValidateUserName(identity.User.Name, false); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.name", identity.User.Name, msg)) } if len(identity.User.Name) == 0 && len(identity.User.UID) != 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.uid", identity.User.UID, "may not be set if user.name is empty")) } if len(identity.User.Name) != 0 && len(identity.User.UID) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("user.uid")) } return allErrs }
func validateRollingParams(params *deployapi.RollingDeploymentStrategyParams) fielderrors.ValidationErrorList { errs := fielderrors.ValidationErrorList{} if params.IntervalSeconds != nil && *params.IntervalSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("intervalSeconds", *params.IntervalSeconds, "must be >0")) } if params.UpdatePeriodSeconds != nil && *params.UpdatePeriodSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("updatePeriodSeconds", *params.UpdatePeriodSeconds, "must be >0")) } if params.TimeoutSeconds != nil && *params.TimeoutSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("timeoutSeconds", *params.TimeoutSeconds, "must be >0")) } if params.UpdatePercent != nil { p := *params.UpdatePercent if p == 0 || p < -100 || p > 100 { errs = append(errs, fielderrors.NewFieldInvalid("updatePercent", *params.UpdatePercent, "must be between 1 and 100 or between -1 and -100 (inclusive)")) } } if params.Pre != nil { errs = append(errs, validateLifecycleHook(params.Pre).Prefix("pre")...) } if params.Post != nil { errs = append(errs, validateLifecycleHook(params.Post).Prefix("post")...) } return errs }
func ValidatePolicyBinding(policyBinding *authorizationapi.PolicyBinding, isNamespaced bool) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&policyBinding.ObjectMeta, isNamespaced, PolicyBindingNameValidator(policyBinding.PolicyRef.Namespace)).Prefix("metadata")...) if !isNamespaced { if len(policyBinding.PolicyRef.Namespace) > 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("policyRef.namespace", policyBinding.PolicyRef.Namespace, "may not reference another namespace")) } } for roleBindingKey, roleBinding := range policyBinding.RoleBindings { if roleBinding == nil { allErrs = append(allErrs, fielderrors.NewFieldRequired("roleBindings."+roleBindingKey)) } if roleBinding.RoleRef.Namespace != policyBinding.PolicyRef.Namespace { allErrs = append(allErrs, fielderrors.NewFieldInvalid("roleBindings."+roleBindingKey+".roleRef.namespace", policyBinding.PolicyRef.Namespace, "must be "+policyBinding.PolicyRef.Namespace)) } if roleBindingKey != roleBinding.Name { allErrs = append(allErrs, fielderrors.NewFieldInvalid("roleBindings."+roleBindingKey+".metadata.name", roleBinding.Name, "must be "+roleBindingKey)) } allErrs = append(allErrs, ValidateRoleBinding(roleBinding, isNamespaced).Prefix("roleBindings."+roleBindingKey)...) } return allErrs }
// ValidateRoute tests if required fields in the route are set. func ValidateRoute(route *routeapi.Route) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} //ensure meta is set properly result = append(result, kval.ValidateObjectMeta(&route.ObjectMeta, true, oapi.GetNameValidationFunc(kval.ValidatePodName)).Prefix("metadata")...) //host is not required but if it is set ensure it meets DNS requirements if len(route.Host) > 0 { if !util.IsDNS1123Subdomain(route.Host) { result = append(result, fielderrors.NewFieldInvalid("host", route.Host, "host must conform to DNS 952 subdomain conventions")) } } if len(route.Path) > 0 && !strings.HasPrefix(route.Path, "/") { result = append(result, fielderrors.NewFieldInvalid("path", route.Path, "path must begin with /")) } if len(route.ServiceName) == 0 { result = append(result, fielderrors.NewFieldRequired("serviceName")) } if errs := validateTLS(route); len(errs) != 0 { result = append(result, errs.Prefix("tls")...) } return result }
func validateOutput(output *buildapi.BuildOutput) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} // TODO: make part of a generic ValidateObjectReference method upstream. if output.To != nil { kind, name, namespace := output.To.Kind, output.To.Name, output.To.Namespace if len(kind) == 0 { kind = "ImageStream" output.To.Kind = kind } if kind != "ImageStream" { allErrs = append(allErrs, fielderrors.NewFieldInvalid("to.kind", kind, "the target of build output must be 'ImageStream'")) } if len(name) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("to.name")) } else if !util.IsDNS1123Subdomain(name) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("to.name", name, "name must be a valid subdomain")) } if len(namespace) != 0 && !util.IsDNS1123Subdomain(namespace) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("to.namespace", namespace, "namespace must be a valid subdomain")) } } allErrs = append(allErrs, validateSecretRef(output.PushSecret).Prefix("pushSecret")...) if len(output.DockerImageReference) != 0 { if _, err := imageapi.ParseDockerImageReference(output.DockerImageReference); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("dockerImageReference", output.DockerImageReference, err.Error())) } } return allErrs }
func ValidateProjectConfig(config api.ProjectConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if _, _, err := api.ParseNamespaceAndName(config.ProjectRequestTemplate); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("projectRequestTemplate", config.ProjectRequestTemplate, "must be in the form: namespace/templateName")) } if len(config.DefaultNodeSelector) > 0 { _, err := labelselector.Parse(config.DefaultNodeSelector) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("defaultNodeSelector", config.DefaultNodeSelector, "must be a valid label selector")) } } if alloc := config.SecurityAllocator; alloc != nil { if _, err := uid.ParseRange(alloc.UIDAllocatorRange); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("uidAllocatorRange", alloc.UIDAllocatorRange, err.Error())) } if _, err := mcs.ParseRange(alloc.MCSAllocatorRange); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("mcsAllocatorRange", alloc.MCSAllocatorRange, err.Error())) } if alloc.MCSLabelsPerProject <= 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("mcsLabelsPerProject", alloc.MCSLabelsPerProject, "must be a positive integer")) } } return allErrs }
func ValidateSessionConfig(config *api.SessionConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} // Validate session secrets file, if specified if len(config.SessionSecretsFile) > 0 { fileErrs := ValidateFile(config.SessionSecretsFile, "sessionSecretsFile") if len(fileErrs) != 0 { // Missing file allErrs = append(allErrs, fileErrs...) } else { // Validate file contents secrets, err := latest.ReadSessionSecrets(config.SessionSecretsFile) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("sessionSecretsFile", config.SessionSecretsFile, fmt.Sprintf("error reading file: %v", err))) } else { for _, err := range ValidateSessionSecrets(secrets) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("sessionSecretsFile", config.SessionSecretsFile, err.Error())) } } } } if len(config.SessionName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("sessionName")) } return allErrs }
func TestCheckInvalidErr(t *testing.T) { tests := []struct { err error expected string }{ { errors.NewInvalid("Invalid1", "invalidation", fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("Cause", "single", "details")}), `Error from server: Invalid1 "invalidation" is invalid: Cause: invalid value 'single', Details: details`, }, { errors.NewInvalid("Invalid2", "invalidation", fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("Cause", "multi1", "details"), fielderrors.NewFieldInvalid("Cause", "multi2", "details")}), `Error from server: Invalid2 "invalidation" is invalid: [Cause: invalid value 'multi1', Details: details, Cause: invalid value 'multi2', Details: details]`, }, { errors.NewInvalid("Invalid3", "invalidation", fielderrors.ValidationErrorList{}), `Error from server: Invalid3 "invalidation" is invalid: <nil>`, }, } var errReturned string errHandle := func(err string) { errReturned = err } for _, test := range tests { checkErr(test.err, errHandle) if errReturned != test.expected { t.Fatalf("Got: %s, expected: %s", errReturned, test.expected) } } }
// ValidateImageStreamMapping tests required fields for an ImageStreamMapping. func ValidateImageStreamMapping(mapping *api.ImageStreamMapping) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} result = append(result, validation.ValidateObjectMeta(&mapping.ObjectMeta, true, oapi.MinimalNameRequirements).Prefix("metadata")...) hasRepository := len(mapping.DockerImageRepository) != 0 hasName := len(mapping.Name) != 0 switch { case hasRepository: if _, err := api.ParseDockerImageReference(mapping.DockerImageRepository); err != nil { result = append(result, fielderrors.NewFieldInvalid("dockerImageRepository", mapping.DockerImageRepository, err.Error())) } case hasName: default: result = append(result, fielderrors.NewFieldRequired("name")) result = append(result, fielderrors.NewFieldRequired("dockerImageRepository")) } if ok, msg := validation.ValidateNamespaceName(mapping.Namespace, false); !ok { result = append(result, fielderrors.NewFieldInvalid("namespace", mapping.Namespace, msg)) } if len(mapping.Tag) == 0 { result = append(result, fielderrors.NewFieldRequired("tag")) } if errs := ValidateImage(&mapping.Image).Prefix("image"); len(errs) != 0 { result = append(result, errs...) } return result }
func validateToImageReference(reference *kapi.ObjectReference) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} kind, name, namespace := reference.Kind, reference.Name, reference.Namespace switch kind { case "ImageStreamTag": if len(name) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("name")) } else if _, _, ok := imageapi.SplitImageStreamTag(name); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", name, "ImageStreamTag object references must be in the form <name>:<tag>")) } if len(namespace) != 0 && !util.IsDNS1123Subdomain(namespace) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("namespace", namespace, "namespace must be a valid subdomain")) } case "DockerImage": if len(namespace) != 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("namespace", namespace, "namespace is not valid when used with a 'DockerImage'")) } if _, err := imageapi.ParseDockerImageReference(name); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", name, fmt.Sprintf("name is not a valid Docker pull specification: %v", err))) } case "": allErrs = append(allErrs, fielderrors.NewFieldRequired("kind")) default: allErrs = append(allErrs, fielderrors.NewFieldInvalid("kind", kind, "the target of build output must be an 'ImageStreamTag' or 'DockerImage'")) } return allErrs }
func validateImageChangeParams(params *deployapi.DeploymentTriggerImageChangeParams) fielderrors.ValidationErrorList { errs := fielderrors.ValidationErrorList{} if len(params.From.Name) != 0 { if len(params.From.Kind) == 0 { params.From.Kind = "ImageStream" } kinds := util.NewStringSet("ImageRepository", "ImageStream", "ImageStreamTag") if !kinds.Has(params.From.Kind) { msg := fmt.Sprintf("kind must be one of: %s", strings.Join(kinds.List(), ", ")) errs = append(errs, fielderrors.NewFieldInvalid("from.kind", params.From.Kind, msg)) } if !util.IsDNS1123Subdomain(params.From.Name) { errs = append(errs, fielderrors.NewFieldInvalid("from.name", params.From.Name, "name must be a valid subdomain")) } if len(params.From.Namespace) != 0 && !util.IsDNS1123Subdomain(params.From.Namespace) { errs = append(errs, fielderrors.NewFieldInvalid("from.namespace", params.From.Namespace, "namespace must be a valid subdomain")) } if len(params.RepositoryName) != 0 { errs = append(errs, fielderrors.NewFieldInvalid("repositoryName", params.RepositoryName, "only one of 'from', 'repository' name may be specified")) } } else { if len(params.RepositoryName) == 0 { errs = append(errs, fielderrors.NewFieldRequired("from")) } } if len(params.ContainerNames) == 0 { errs = append(errs, fielderrors.NewFieldRequired("containerNames")) } return errs }
func ValidateDeploymentConfigUpdate(newConfig *deployapi.DeploymentConfig, oldConfig *deployapi.DeploymentConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&newConfig.ObjectMeta, &oldConfig.ObjectMeta).Prefix("metadata")...) allErrs = append(allErrs, ValidateDeploymentConfig(newConfig)...) if newConfig.LatestVersion < oldConfig.LatestVersion { allErrs = append(allErrs, fielderrors.NewFieldInvalid("latestVersion", newConfig.LatestVersion, "latestVersion cannot be decremented")) } else if newConfig.LatestVersion > (oldConfig.LatestVersion + 1) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("latestVersion", newConfig.LatestVersion, "latestVersion can only be incremented by 1")) } return allErrs }
func ValidateSpecifiedIP(ipString string, field string) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} ip := net.ParseIP(ipString) if ip == nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid(field, ipString, "must be a valid IP")) } else if ip.IsUnspecified() { allErrs = append(allErrs, fielderrors.NewFieldInvalid(field, ipString, "cannot be an unspecified IP")) } return allErrs }
// ValidateEvent makes sure that the event makes sense. func ValidateEvent(event *api.Event) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} // TODO: There is no namespace required for minion if event.InvolvedObject.Kind != "Node" && event.Namespace != event.InvolvedObject.Namespace { allErrs = append(allErrs, errs.NewFieldInvalid("involvedObject.namespace", event.InvolvedObject.Namespace, "namespace does not match involvedObject")) } if !util.IsDNS1123Subdomain(event.Namespace) { allErrs = append(allErrs, errs.NewFieldInvalid("namespace", event.Namespace, "")) } return allErrs }
// ValidateHostSubnet tests fields for the host subnet, the host should be a network resolvable string, // and subnet should be a valid CIDR func ValidateHostSubnet(hs *sdnapi.HostSubnet) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&hs.ObjectMeta, false, oapi.MinimalNameRequirements).Prefix("metadata")...) _, _, err := net.ParseCIDR(hs.Subnet) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("subnet", hs.Subnet, err.Error())) } if net.ParseIP(hs.HostIP) == nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("hostIP", hs.HostIP, "invalid IP address")) } return allErrs }
func ValidateHTTPServingInfo(info api.HTTPServingInfo) fielderrors.ValidationErrorList { allErrs := ValidateServingInfo(info.ServingInfo) if info.MaxRequestsInFlight < 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("maxRequestsInFlight", info.MaxRequestsInFlight, "must be zero (no limit) or greater")) } if info.RequestTimeoutSeconds < -1 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("requestTimeoutSeconds", info.RequestTimeoutSeconds, "must be -1 (no timeout), 0 (default timeout), or greater")) } return allErrs }
func ValidateServiceAccountConfig(config api.ServiceAccountConfig, builtInKubernetes bool) ValidationResults { validationResults := ValidationResults{} managedNames := util.NewStringSet(config.ManagedNames...) if !managedNames.Has(bootstrappolicy.BuilderServiceAccountName) { validationResults.AddWarnings(fielderrors.NewFieldInvalid("managedNames", "", fmt.Sprintf("missing %q, which will require manual creation in each namespace before builds can run", bootstrappolicy.BuilderServiceAccountName))) } if !managedNames.Has(bootstrappolicy.DeployerServiceAccountName) { validationResults.AddWarnings(fielderrors.NewFieldInvalid("managedNames", "", fmt.Sprintf("missing %q, which will require manual creation in each namespace before deployments can run", bootstrappolicy.DeployerServiceAccountName))) } if builtInKubernetes && !managedNames.Has(bootstrappolicy.DefaultServiceAccountName) { validationResults.AddWarnings(fielderrors.NewFieldInvalid("managedNames", "", fmt.Sprintf("missing %q, which will prevent creation of pods that do not specify a valid service account", bootstrappolicy.DefaultServiceAccountName))) } for i, name := range config.ManagedNames { if ok, msg := kvalidation.ValidateServiceAccountName(name, false); !ok { validationResults.AddErrors(fielderrors.NewFieldInvalid(fmt.Sprintf("managedNames[%d]", i), name, msg)) } } if len(config.PrivateKeyFile) > 0 { if fileErrs := ValidateFile(config.PrivateKeyFile, "privateKeyFile"); len(fileErrs) > 0 { validationResults.AddErrors(fileErrs...) } else if privateKey, err := serviceaccount.ReadPrivateKey(config.PrivateKeyFile); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("privateKeyFile", config.PrivateKeyFile, err.Error())) } else if err := privateKey.Validate(); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("privateKeyFile", config.PrivateKeyFile, err.Error())) } } else if builtInKubernetes { validationResults.AddWarnings(fielderrors.NewFieldInvalid("privateKeyFile", "", "no service account tokens will be generated, which could prevent builds and deployments from working")) } if len(config.PublicKeyFiles) == 0 { validationResults.AddWarnings(fielderrors.NewFieldInvalid("publicKeyFiles", "", "no service account tokens will be accepted by the API, which will prevent builds and deployments from working")) } for i, publicKeyFile := range config.PublicKeyFiles { if fileErrs := ValidateFile(publicKeyFile, fmt.Sprintf("publicKeyFiles[%d]", i)); len(fileErrs) > 0 { validationResults.AddErrors(fileErrs...) } else if _, err := serviceaccount.ReadPublicKey(publicKeyFile); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid(fmt.Sprintf("publicKeyFiles[%d]", i), publicKeyFile, err.Error())) } } if len(config.MasterCA) > 0 { validationResults.AddErrors(ValidateFile(config.MasterCA, "masterCA")...) } else if builtInKubernetes { validationResults.AddWarnings(fielderrors.NewFieldInvalid("masterCA", "", "master CA information will not be automatically injected into pods, which will prevent verification of the API server from inside a pod")) } return validationResults }
// Validate ensures that the specified values fall within the range of the strategy. Validation // of this will pass if either the UID is not set, assuming that the image will provided the UID // or if the UID is set it is not root. In order to work properly this assumes that the kubelet // will populate an func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if container.SecurityContext == nil { detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext", container.SecurityContext, detail)) return allErrs } if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 { detail := fmt.Sprintf("running with the root UID is forbidden by the security context constraints %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext.runAsUser", *container.SecurityContext.RunAsUser, detail)) return allErrs } return allErrs }
// Ensure a container's SecurityContext is in compliance with the given constraints func (s *simpleProvider) ValidateSecurityContext(pod *api.Pod, container *api.Container) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if container.SecurityContext == nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext", container.SecurityContext, "No security context is set")) return allErrs } sc := container.SecurityContext allErrs = append(allErrs, s.runAsUserStrategy.Validate(pod, container)...) allErrs = append(allErrs, s.seLinuxStrategy.Validate(pod, container)...) if !s.scc.AllowPrivilegedContainer && *sc.Privileged { allErrs = append(allErrs, fielderrors.NewFieldInvalid("privileged", *sc.Privileged, "Privileged containers are not allowed")) } if sc.Capabilities != nil && len(sc.Capabilities.Add) > 0 { for _, cap := range sc.Capabilities.Add { found := false for _, allowedCap := range s.scc.AllowedCapabilities { if cap == allowedCap { found = true break } } if !found { allErrs = append(allErrs, fielderrors.NewFieldInvalid("capabilities.add", cap, "Capability is not allowed to be added")) } } } if !s.scc.AllowHostDirVolumePlugin { for _, v := range pod.Spec.Volumes { if v.HostPath != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("VolumeMounts", v.Name, "Host Volumes are not allowed to be used")) } } } if !s.scc.AllowHostNetwork && pod.Spec.HostNetwork { allErrs = append(allErrs, fielderrors.NewFieldInvalid("hostNetwork", pod.Spec.HostNetwork, "Host network is not allowed to be used")) } if !s.scc.AllowHostPorts { for idx, c := range pod.Spec.Containers { allErrs = append(allErrs, s.hasHostPort(&c).Prefix(fmt.Sprintf("containers.%d", idx))...) } } return allErrs }
func ValidateOAuthConfig(config *api.OAuthConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if len(config.MasterURL) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("masterURL")) } if _, urlErrs := ValidateURL(config.MasterPublicURL, "masterPublicURL"); len(urlErrs) > 0 { allErrs = append(allErrs, urlErrs...) } if len(config.AssetPublicURL) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("assetPublicURL")) } if config.SessionConfig != nil { allErrs = append(allErrs, ValidateSessionConfig(config.SessionConfig).Prefix("sessionConfig")...) } allErrs = append(allErrs, ValidateGrantConfig(config.GrantConfig).Prefix("grantConfig")...) providerNames := util.NewStringSet() redirectingIdentityProviders := []string{} for i, identityProvider := range config.IdentityProviders { if identityProvider.UseAsLogin { redirectingIdentityProviders = append(redirectingIdentityProviders, identityProvider.Name) if api.IsPasswordAuthenticator(identityProvider) { if config.SessionConfig == nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("sessionConfig", config, "sessionConfig is required if a password identity provider is used for browser based login")) } } } allErrs = append(allErrs, ValidateIdentityProvider(identityProvider).Prefix(fmt.Sprintf("identityProvider[%d]", i))...) if len(identityProvider.Name) > 0 { if providerNames.Has(identityProvider.Name) { allErrs = append(allErrs, fielderrors.NewFieldInvalid(fmt.Sprintf("identityProvider[%d].name", i), identityProvider.Name, "must have a unique name")) } providerNames.Insert(identityProvider.Name) } } if len(redirectingIdentityProviders) > 1 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("identityProviders", config.IdentityProviders, fmt.Sprintf("only one identity provider can support login for a browser, found: %v", redirectingIdentityProviders))) } return allErrs }
func ValidateIdentityUpdate(identity *api.Identity, old *api.Identity) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, kvalidation.ValidateObjectMetaUpdate(&identity.ObjectMeta, &old.ObjectMeta).Prefix("metadata")...) allErrs = append(allErrs, ValidateIdentity(identity)...) if identity.ProviderName != old.ProviderName { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerName", identity.ProviderName, "may not change providerName")) } if identity.ProviderUserName != old.ProviderUserName { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerUserName", identity.ProviderUserName, "may not change providerUserName")) } return allErrs }
func validateGitSource(git *buildapi.GitBuildSource) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if len(git.URI) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("uri")) } else if !isValidURL(git.URI) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("uri", git.URI, "uri is not a valid url")) } if len(git.HTTPProxy) != 0 && !isValidURL(git.HTTPProxy) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("httpproxy", git.HTTPProxy, "proxy is not a valid url")) } if len(git.HTTPSProxy) != 0 && !isValidURL(git.HTTPSProxy) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("httpsproxy", git.HTTPSProxy, "proxy is not a valid url")) } return allErrs }
func ValidateOpenIDIdentityProvider(provider *api.OpenIDIdentityProvider, identityProvider api.IdentityProvider) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, ValidateOAuthIdentityProvider(provider.ClientID, provider.ClientSecret, identityProvider.UseAsChallenger)...) // Communication with the Authorization Endpoint MUST utilize TLS // http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint _, urlErrs := ValidateSecureURL(provider.URLs.Authorize, "authorize") allErrs = append(allErrs, urlErrs.Prefix("provider.urls")...) // Communication with the Token Endpoint MUST utilize TLS // http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint _, urlErrs = ValidateSecureURL(provider.URLs.Token, "token") allErrs = append(allErrs, urlErrs.Prefix("provider.urls")...) if len(provider.URLs.UserInfo) != 0 { // Communication with the UserInfo Endpoint MUST utilize TLS // http://openid.net/specs/openid-connect-core-1_0.html#UserInfo _, urlErrs = ValidateSecureURL(provider.URLs.UserInfo, "userInfo") allErrs = append(allErrs, urlErrs.Prefix("provider.urls")...) } // At least one claim to use as the user id is required if len(provider.Claims.ID) == 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("provider.claims.id", "[]", "at least one id claim is required (OpenID standard identity claim is 'sub')")) } if len(provider.CA) != 0 { allErrs = append(allErrs, ValidateFile(provider.CA, "provider.ca")...) } return allErrs }
func ValidateSecureURL(urlString string, field string) (*url.URL, fielderrors.ValidationErrorList) { url, urlErrs := ValidateURL(urlString, field) if len(urlErrs) == 0 && url.Scheme != "https" { urlErrs = append(urlErrs, fielderrors.NewFieldInvalid(field, urlString, "must use https scheme")) } return url, urlErrs }
// ValidateClusterNetwork tests if required fields in the ClusterNetwork are set. func ValidateClusterNetwork(clusterNet *sdnapi.ClusterNetwork) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&clusterNet.ObjectMeta, false, oapi.MinimalNameRequirements).Prefix("metadata")...) _, ipnet, err := net.ParseCIDR(clusterNet.Network) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("network", clusterNet.Network, err.Error())) } else { ones, bitSize := ipnet.Mask.Size() if (bitSize - ones) <= clusterNet.HostSubnetLength { allErrs = append(allErrs, fielderrors.NewFieldInvalid("hostSubnetLength", clusterNet.HostSubnetLength, "subnet length is greater than cluster Mask")) } } return allErrs }
func ValidateURL(urlString string, field string) (*url.URL, fielderrors.ValidationErrorList) { allErrs := fielderrors.ValidationErrorList{} urlObj, err := url.Parse(urlString) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid(field, urlString, "must be a valid URL")) return nil, allErrs } if len(urlObj.Scheme) == 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid(field, urlString, "must contain a scheme (e.g. https://)")) } if len(urlObj.Host) == 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid(field, urlString, "must contain a host")) } return urlObj, allErrs }
// assignSecurityContext creates a security context for each container in the pod // and validates that the sc falls within the scc constraints. All containers must validate against // the same scc or is not considered valid. func assignSecurityContext(provider scc.SecurityContextConstraintsProvider, pod *kapi.Pod) fielderrors.ValidationErrorList { generatedSCs := make([]*kapi.SecurityContext, len(pod.Spec.Containers)) errs := fielderrors.ValidationErrorList{} for i, c := range pod.Spec.Containers { sc, err := provider.CreateSecurityContext(pod, &c) if err != nil { errs = append(errs, fielderrors.NewFieldInvalid(fmt.Sprintf("spec.containers[%d].securityContext", i), "", err.Error())) continue } generatedSCs[i] = sc c.SecurityContext = sc errs = append(errs, provider.ValidateSecurityContext(pod, &c).Prefix(fmt.Sprintf("spec.containers[%d].securityContext", i))...) } if len(errs) > 0 { return errs } // if we've reached this code then we've generated and validated an SC for every container in the // pod so let's apply what we generated for i, sc := range generatedSCs { pod.Spec.Containers[i].SecurityContext = sc } return nil }
func ValidateAssetConfig(config *api.AssetConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, ValidateHTTPServingInfo(config.ServingInfo).Prefix("servingInfo")...) if len(config.LogoutURL) > 0 { _, urlErrs := ValidateURL(config.LogoutURL, "logoutURL") if len(urlErrs) > 0 { allErrs = append(allErrs, urlErrs...) } } urlObj, urlErrs := ValidateURL(config.PublicURL, "publicURL") if len(urlErrs) > 0 { allErrs = append(allErrs, urlErrs...) } if urlObj != nil { if !strings.HasSuffix(urlObj.Path, "/") { allErrs = append(allErrs, fielderrors.NewFieldInvalid("publicURL", config.PublicURL, "must have a trailing slash in path")) } } if _, urlErrs := ValidateURL(config.MasterPublicURL, "masterPublicURL"); len(urlErrs) > 0 { allErrs = append(allErrs, urlErrs...) } return allErrs }