Documentation ¶
Index ¶
- Constants
- Variables
- func IsNegativeDuration(in time.Duration) error
- func IsValidateFunc(name string) bool
- func ValidateCORSHTTPHeaderName(name string) error
- func ValidateHTTPHeaderName(name string) error
- func ValidateHTTPHeaderNameOrJwtClaimRoute(name string) error
- func ValidateHTTPHeaderOperationName(name string) error
- func ValidateHTTPHeaderValue(value string) error
- func ValidateHTTPHeaderWithAuthorityOperationName(name string) error
- func ValidateMaxServerConnectionAge(in time.Duration) error
- func ValidatePercent(val int32) error
- func ValidatePortName(name string) error
- func ValidateProtocol(protocolStr string) error
- func ValidateUnixAddress(addr string) error
- type AnalysisAwareError
- type HTTPRouteType
- type OverlappingMatchValidationForHTTPRoute
- type ParserState
- type ValidateFunc
- type Validation
- type Warning
Constants ¶
const ( IndependentRoute = iota RootRoute DelegateRoute )
const ( // UnixAddressPrefix is the prefix used to indicate an address is for a Unix Domain socket. It is used in // ServiceEntry.Endpoint.Address message. UnixAddressPrefix = "unix://" )
Constants for duration fields
Variables ¶
var ( // EmptyValidate is a Validate that does nothing and returns no error. EmptyValidate = RegisterValidateFunc("EmptyValidate", func(config.Config) (Warning, error) { return nil, nil }) )
var ValidateAuthorizationPolicy = RegisterValidateFunc("ValidateAuthorizationPolicy", func(cfg config.Config) (Warning, error) { in, ok := cfg.Spec.(*security_beta.AuthorizationPolicy) if !ok { return nil, fmt.Errorf("cannot cast to AuthorizationPolicy") } var errs error var warnings Warning selectorTypeValidation := validateOneOfSelectorType(in.GetSelector(), in.GetTargetRef(), in.GetTargetRefs()) workloadSelectorValidation := validateWorkloadSelector(in.GetSelector()) targetRefValidation := validatePolicyTargetReference(in.GetTargetRef()) targetRefsValidation := validatePolicyTargetReferences(in.GetTargetRefs()) errs = appendErrors(errs, selectorTypeValidation, workloadSelectorValidation, targetRefValidation, targetRefsValidation) warnings = appendErrors(warnings, workloadSelectorValidation.Warning) if in.Action == security_beta.AuthorizationPolicy_CUSTOM { if in.Rules == nil { errs = appendErrors(errs, fmt.Errorf("CUSTOM action without `rules` is meaningless as it will never be triggered, "+ "add an empty rule `{}` if you want it be triggered for every request")) } else { if in.GetProvider() == nil || in.GetProvider().GetName() == "" { errs = appendErrors(errs, fmt.Errorf("`provider.name` must not be empty")) } } for _, rule := range in.GetRules() { check := func(invalid bool, name string) error { if invalid { return fmt.Errorf("%s is currently not supported with CUSTOM action", name) } return nil } for _, from := range rule.GetFrom() { if src := from.GetSource(); src != nil { errs = appendErrors(errs, check(len(src.Namespaces) != 0, "From.Namespaces")) errs = appendErrors(errs, check(len(src.NotNamespaces) != 0, "From.NotNamespaces")) errs = appendErrors(errs, check(len(src.Principals) != 0, "From.Principals")) errs = appendErrors(errs, check(len(src.NotPrincipals) != 0, "From.NotPrincipals")) errs = appendErrors(errs, check(len(src.RequestPrincipals) != 0, "From.RequestPrincipals")) errs = appendErrors(errs, check(len(src.NotRequestPrincipals) != 0, "From.NotRequestPrincipals")) } } for _, when := range rule.GetWhen() { if when == nil { errs = appendErrors(errs, fmt.Errorf("when field cannot be nil")) continue } errs = appendErrors(errs, check(when.Key == "source.namespace", when.Key)) errs = appendErrors(errs, check(when.Key == "source.principal", when.Key)) errs = appendErrors(errs, check(strings.HasPrefix(when.Key, "request.auth."), when.Key)) } } } if in.GetProvider() != nil && in.Action != security_beta.AuthorizationPolicy_CUSTOM { errs = appendErrors(errs, fmt.Errorf("`provider` must not be with non CUSTOM action, found %s", in.Action)) } if in.Action == security_beta.AuthorizationPolicy_DENY && in.Rules == nil { errs = appendErrors(errs, fmt.Errorf("DENY action without `rules` is meaningless as it will never be triggered, "+ "add an empty rule `{}` if you want it be triggered for every request")) } for i, rule := range in.GetRules() { if rule == nil { errs = appendErrors(errs, fmt.Errorf("`rule` must not be nil, found at rule %d", i)) continue } if rule.From != nil && len(rule.From) == 0 { errs = appendErrors(errs, fmt.Errorf("`from` must not be empty, found at rule %d", i)) } tcpRulesInFrom := false tcpRulesInTo := false fromRuleExist := false toRuleExist := false for _, from := range rule.From { if from == nil { errs = appendErrors(errs, fmt.Errorf("`from` must not be nil, found at rule %d", i)) continue } if from.Source == nil { errs = appendErrors(errs, fmt.Errorf("`from.source` must not be nil, found at rule %d", i)) } else { fromRuleExist = true src := from.Source if len(src.Principals) == 0 && len(src.RequestPrincipals) == 0 && len(src.Namespaces) == 0 && len(src.IpBlocks) == 0 && len(src.RemoteIpBlocks) == 0 && len(src.NotPrincipals) == 0 && len(src.NotRequestPrincipals) == 0 && len(src.NotNamespaces) == 0 && len(src.NotIpBlocks) == 0 && len(src.NotRemoteIpBlocks) == 0 { errs = appendErrors(errs, fmt.Errorf("`from.source` must not be empty, found at rule %d", i)) } errs = appendErrors(errs, security.ValidateIPs(from.Source.GetIpBlocks())) errs = appendErrors(errs, security.ValidateIPs(from.Source.GetNotIpBlocks())) errs = appendErrors(errs, security.ValidateIPs(from.Source.GetRemoteIpBlocks())) errs = appendErrors(errs, security.ValidateIPs(from.Source.GetNotRemoteIpBlocks())) errs = appendErrors(errs, security.CheckEmptyValues("Principals", src.Principals)) errs = appendErrors(errs, security.CheckEmptyValues("RequestPrincipals", src.RequestPrincipals)) errs = appendErrors(errs, security.CheckEmptyValues("Namespaces", src.Namespaces)) errs = appendErrors(errs, security.CheckEmptyValues("IpBlocks", src.IpBlocks)) errs = appendErrors(errs, security.CheckEmptyValues("RemoteIpBlocks", src.RemoteIpBlocks)) errs = appendErrors(errs, security.CheckEmptyValues("NotPrincipals", src.NotPrincipals)) errs = appendErrors(errs, security.CheckEmptyValues("NotRequestPrincipals", src.NotRequestPrincipals)) errs = appendErrors(errs, security.CheckEmptyValues("NotNamespaces", src.NotNamespaces)) errs = appendErrors(errs, security.CheckEmptyValues("NotIpBlocks", src.NotIpBlocks)) errs = appendErrors(errs, security.CheckEmptyValues("NotRemoteIpBlocks", src.NotRemoteIpBlocks)) if src.NotPrincipals != nil || src.Principals != nil || src.IpBlocks != nil || src.NotIpBlocks != nil || src.Namespaces != nil || src.NotNamespaces != nil || src.RemoteIpBlocks != nil || src.NotRemoteIpBlocks != nil { tcpRulesInFrom = true } } } if rule.To != nil && len(rule.To) == 0 { errs = appendErrors(errs, fmt.Errorf("`to` must not be empty, found at rule %d", i)) } for _, to := range rule.To { if to == nil { errs = appendErrors(errs, fmt.Errorf("`to` must not be nil, found at rule %d", i)) continue } if to.Operation == nil { errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be nil, found at rule %d", i)) } else { toRuleExist = true op := to.Operation if len(op.Ports) == 0 && len(op.Methods) == 0 && len(op.Paths) == 0 && len(op.Hosts) == 0 && len(op.NotPorts) == 0 && len(op.NotMethods) == 0 && len(op.NotPaths) == 0 && len(op.NotHosts) == 0 { errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be empty, found at rule %d", i)) } errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetPorts())) errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetNotPorts())) errs = appendErrors(errs, security.CheckEmptyValues("Ports", op.Ports)) errs = appendErrors(errs, security.CheckEmptyValues("Methods", op.Methods)) errs = appendErrors(errs, security.CheckEmptyValues("Paths", op.Paths)) errs = appendErrors(errs, security.CheckEmptyValues("Hosts", op.Hosts)) errs = appendErrors(errs, security.CheckEmptyValues("NotPorts", op.NotPorts)) errs = appendErrors(errs, security.CheckEmptyValues("NotMethods", op.NotMethods)) errs = appendErrors(errs, security.CheckEmptyValues("NotPaths", op.NotPaths)) errs = appendErrors(errs, security.CheckEmptyValues("NotHosts", op.NotHosts)) errs = appendErrors(errs, security.CheckValidPathTemplate("Paths", op.Paths)) errs = appendErrors(errs, security.CheckValidPathTemplate("NotPaths", op.NotPaths)) if op.Ports != nil || op.NotPorts != nil { tcpRulesInTo = true } } } for _, condition := range rule.GetWhen() { key := condition.GetKey() if key == "" { errs = appendErrors(errs, fmt.Errorf("`key` must not be empty")) } else { if len(condition.GetValues()) == 0 && len(condition.GetNotValues()) == 0 { errs = appendErrors(errs, fmt.Errorf("at least one of `values` or `notValues` must be set for key %s", key)) } else { if err := security.ValidateAttribute(key, condition.GetValues()); err != nil { errs = appendErrors(errs, fmt.Errorf("invalid `value` for `key` %s: %v", key, err)) } if err := security.ValidateAttribute(key, condition.GetNotValues()); err != nil { errs = appendErrors(errs, fmt.Errorf("invalid `notValue` for `key` %s: %v", key, err)) } } } } if ((fromRuleExist && !toRuleExist && !tcpRulesInFrom) || (toRuleExist && !tcpRulesInTo)) && in.Action == security_beta.AuthorizationPolicy_DENY { warning := fmt.Errorf("configured AuthorizationPolicy will deny all traffic " + "to TCP ports under its scope due to the use of only HTTP attributes in a DENY rule; " + "it is recommended to explicitly specify the port") warnings = appendErrors(warnings, warning) } } return warnings, multierror.Prefix(errs, fmt.Sprintf("invalid policy %s.%s:", cfg.Name, cfg.Namespace)) })
ValidateAuthorizationPolicy checks that AuthorizationPolicy is well-formed.
var ValidateDestinationRule = RegisterValidateFunc("ValidateDestinationRule", func(cfg config.Config) (Warning, error) { rule, ok := cfg.Spec.(*networking.DestinationRule) if !ok { return nil, fmt.Errorf("cannot cast to destination rule") } v := Validation{} v = AppendValidation(v, agent.ValidateWildcardDomain(rule.Host), validateTrafficPolicy(rule.TrafficPolicy)) subsets := sets.String{} for _, subset := range rule.Subsets { if subset == nil { v = AppendValidation(v, errors.New("subset may not be null")) continue } if subsets.InsertContains(subset.Name) { v = AppendValidation(v, fmt.Errorf("duplicate subset names: %s", subset.Name)) } v = AppendValidation(v, validateSubset(subset)) } v = AppendValidation(v, validateExportTo(cfg.Namespace, rule.ExportTo, false, rule.GetWorkloadSelector() != nil)) v = AppendValidation(v, validateWorkloadSelector(rule.GetWorkloadSelector())) return v.Unwrap() })
ValidateDestinationRule checks proxy policies
var ValidateGateway = RegisterValidateFunc("ValidateGateway", func(cfg config.Config) (Warning, error) { name := cfg.Name gatewaySemantics := cfg.Annotations[constants.InternalGatewaySemantics] == constants.GatewaySemanticsGateway v := Validation{} if !labels.IsDNS1123Label(name) && !gatewaySemantics { v = AppendValidation(v, fmt.Errorf("invalid gateway name: %q", name)) } value, ok := cfg.Spec.(*networking.Gateway) if !ok { v = AppendValidation(v, fmt.Errorf("cannot cast to gateway: %#v", cfg.Spec)) return v.Unwrap() } if len(value.Servers) == 0 { v = AppendValidation(v, fmt.Errorf("gateway must have at least one server")) } else { for _, server := range value.Servers { v = AppendValidation(v, validateServer(server, gatewaySemantics)) } } portNames := make(map[string]bool) for _, s := range value.Servers { if s == nil { v = AppendValidation(v, fmt.Errorf("server may not be nil")) continue } if s.Port != nil { if portNames[s.Port.Name] { v = AppendValidation(v, fmt.Errorf("port names in servers must be unique: duplicate name %s", s.Port.Name)) } portNames[s.Port.Name] = true if !protocol.Parse(s.Port.Protocol).IsHTTP() && s.GetTls().GetHttpsRedirect() { v = AppendValidation(v, WrapWarning(fmt.Errorf("tls.httpsRedirect should only be used with http servers"))) } } } return v.Unwrap() })
ValidateGateway checks gateway specifications
var ValidatePeerAuthentication = RegisterValidateFunc("ValidatePeerAuthentication", func(cfg config.Config) (Warning, error) { in, ok := cfg.Spec.(*security_beta.PeerAuthentication) if !ok { return nil, errors.New("cannot cast to PeerAuthentication") } var errs error emptySelector := in.Selector == nil || len(in.Selector.MatchLabels) == 0 if emptySelector && len(in.PortLevelMtls) != 0 { errs = appendErrors(errs, fmt.Errorf("mesh/namespace peer authentication cannot have port level mTLS")) } if in.PortLevelMtls != nil && len(in.PortLevelMtls) == 0 { errs = appendErrors(errs, fmt.Errorf("port level mTLS, if defined, must have at least one element")) } for port := range in.PortLevelMtls { if port <= 0 || port > 65535 { errs = appendErrors(errs, fmt.Errorf("port must be in range 1..65535")) } } validation := validateWorkloadSelector(in.Selector) errs = appendErrors(errs, validation) return validation.Warning, errs })
ValidatePeerAuthentication checks that peer authentication spec is well-formed.
var ValidateProxyConfig = RegisterValidateFunc("ValidateProxyConfig", func(cfg config.Config) (Warning, error) { spec, ok := cfg.Spec.(*networkingv1beta1.ProxyConfig) if !ok { return nil, fmt.Errorf("cannot cast to proxyconfig") } errs := Validation{} errs = AppendValidation(errs, validateWorkloadSelector(spec.Selector), validateConcurrency(spec.Concurrency.GetValue()), ) return errs.Unwrap() })
ValidateProxyConfig validates a ProxyConfig CR (as opposed to the MeshConfig field).
var ValidateRequestAuthentication = RegisterValidateFunc("ValidateRequestAuthentication", func(cfg config.Config) (Warning, error) { in, ok := cfg.Spec.(*security_beta.RequestAuthentication) if !ok { return nil, errors.New("cannot cast to RequestAuthentication") } errs := Validation{} errs = AppendValidation(errs, validateOneOfSelectorType(in.GetSelector(), in.GetTargetRef(), in.GetTargetRefs()), validateWorkloadSelector(in.GetSelector()), validatePolicyTargetReference(in.GetTargetRef()), validatePolicyTargetReferences(in.GetTargetRefs()), ) for _, rule := range in.JwtRules { errs = AppendValidation(errs, validateJwtRule(rule)) } return errs.Unwrap() })
ValidateRequestAuthentication checks that request authentication spec is well-formed.
var ValidateServiceEntry = RegisterValidateFunc("ValidateServiceEntry", func(cfg config.Config) (Warning, error) { serviceEntry, ok := cfg.Spec.(*networking.ServiceEntry) if !ok { return nil, fmt.Errorf("cannot cast to service entry") } errs := Validation{} warning, err := ValidateAlphaWorkloadSelector(serviceEntry.WorkloadSelector) if err != nil { return nil, err } if warning != nil { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("service entry: %s, will be applied to all services in namespace", warning))) } if serviceEntry.WorkloadSelector != nil && serviceEntry.Endpoints != nil { errs = AppendValidation(errs, fmt.Errorf("only one of WorkloadSelector or Endpoints is allowed in Service Entry")) } if len(serviceEntry.Hosts) == 0 { errs = AppendValidation(errs, fmt.Errorf("service entry must have at least one host")) } for _, hostname := range serviceEntry.Hosts { if hostname == "*" { errs = AppendValidation(errs, fmt.Errorf("invalid host %s", hostname)) } else { errs = AppendValidation(errs, agent.ValidateWildcardDomain(hostname)) errs = AppendValidation(errs, WrapWarning(agent.ValidatePartialWildCard(hostname))) } } cidrFound := false for _, address := range serviceEntry.Addresses { cidrFound = cidrFound || strings.Contains(address, "/") errs = AppendValidation(errs, agent.ValidateIPSubnet(address)) } if cidrFound { if serviceEntry.Resolution != networking.ServiceEntry_NONE && serviceEntry.Resolution != networking.ServiceEntry_STATIC { errs = AppendValidation(errs, fmt.Errorf("CIDR addresses are allowed only for NONE/STATIC resolution types")) } } autoAllocation := serviceentry.ShouldV2AutoAllocateIPFromConfig(cfg) servicePortNumbers := sets.New[uint32]() servicePorts := sets.NewWithLength[string](len(serviceEntry.Ports)) for _, port := range serviceEntry.Ports { if port == nil { errs = AppendValidation(errs, fmt.Errorf("service entry port may not be null")) continue } if servicePorts.InsertContains(port.Name) { errs = AppendValidation(errs, fmt.Errorf("service entry port name %q already defined", port.Name)) } if servicePortNumbers.InsertContains(port.Number) { errs = AppendValidation(errs, fmt.Errorf("service entry port %d already defined", port.Number)) } if port.TargetPort != 0 { errs = AppendValidation(errs, agent.ValidatePort(int(port.TargetPort))) if serviceEntry.Resolution == networking.ServiceEntry_NONE && !features.PassthroughTargetPort { errs = AppendWarningf(errs, "targetPort has no effect when resolution mode is NONE") } } if len(serviceEntry.Addresses) == 0 && !autoAllocation { if port.Protocol == "" || port.Protocol == "TCP" { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("addresses are required for ports serving TCP (or unset) protocol "+ "when ISTIO_META_DNS_AUTO_ALLOCATE is not set on a proxy"))) } } errs = AppendValidation(errs, ValidatePortName(port.Name), ValidateProtocol(port.Protocol), agent.ValidatePort(int(port.Number))) } switch serviceEntry.Resolution { case networking.ServiceEntry_NONE: if len(serviceEntry.Endpoints) != 0 { errs = AppendValidation(errs, fmt.Errorf("no endpoints should be provided for resolution type none")) } if serviceEntry.WorkloadSelector != nil { errs = AppendWarningf(errs, "workloadSelector should not be set when resolution mode is NONE") } case networking.ServiceEntry_STATIC: for _, endpoint := range serviceEntry.Endpoints { if endpoint == nil { errs = AppendValidation(errs, errors.New("endpoint cannot be nil")) continue } errs = AppendValidation(errs, validateWorkloadEntry(endpoint, servicePorts, false)) } case networking.ServiceEntry_DNS, networking.ServiceEntry_DNS_ROUND_ROBIN: if len(serviceEntry.Endpoints) == 0 { for _, hostname := range serviceEntry.Hosts { if err := agent.ValidateFQDN(hostname); err != nil { errs = AppendValidation(errs, fmt.Errorf("hosts must be FQDN if no endpoints are provided for resolution mode %s", serviceEntry.Resolution)) } } } if serviceEntry.Resolution == networking.ServiceEntry_DNS_ROUND_ROBIN && len(serviceEntry.Endpoints) > 1 { errs = AppendValidation(errs, fmt.Errorf("there must only be 0 or 1 endpoint for resolution mode %s", serviceEntry.Resolution)) } for _, endpoint := range serviceEntry.Endpoints { if endpoint == nil { errs = AppendValidation(errs, errors.New("endpoint cannot be nil")) continue } if !netutil.IsValidIPAddress(endpoint.Address) { if err := agent.ValidateFQDN(endpoint.Address); err != nil { errs = AppendValidation(errs, fmt.Errorf("endpoint address %q is not a valid FQDN or an IP address", endpoint.Address)) } } errs = AppendValidation(errs, labels.Instance(endpoint.Labels).Validate()) for name, port := range endpoint.Ports { if !servicePorts.Contains(name) { errs = AppendValidation(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port)) } errs = AppendValidation(errs, ValidatePortName(name), agent.ValidatePort(int(port))) } } if serviceEntry.WorkloadSelector != nil { errs = AppendWarningf(errs, "workloadSelector should not be set when resolution mode is %v", serviceEntry.Resolution) } if len(serviceEntry.Addresses) > 0 { for _, port := range serviceEntry.Ports { if port == nil { errs = AppendValidation(errs, errors.New("ports cannot be nil")) continue } p := protocol.Parse(port.Protocol) if p.IsTCP() { if len(serviceEntry.Hosts) > 1 { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("service entry can not have more than one host specified "+ "simultaneously with address and tcp port"))) } break } } } default: errs = AppendValidation(errs, fmt.Errorf("unsupported resolution type %s", networking.ServiceEntry_Resolution_name[int32(serviceEntry.Resolution)])) } if serviceEntry.Resolution != networking.ServiceEntry_NONE && len(serviceEntry.Hosts) > 1 { for _, port := range serviceEntry.Ports { if port == nil { errs = AppendValidation(errs, errors.New("ports cannot be nil")) continue } p := protocol.Parse(port.Protocol) if !p.IsHTTP() && !p.IsTLS() { errs = AppendValidation(errs, fmt.Errorf("multiple hosts provided with non-HTTP, non-TLS ports")) break } } } errs = AppendValidation(errs, validateExportTo(cfg.Namespace, serviceEntry.ExportTo, true, false)) return errs.Unwrap() })
ValidateServiceEntry validates a service entry.
var ValidateSidecar = RegisterValidateFunc("ValidateSidecar", func(cfg config.Config) (Warning, error) { errs := Validation{} rule, ok := cfg.Spec.(*networking.Sidecar) if !ok { return nil, fmt.Errorf("cannot cast to Sidecar") } warning, err := ValidateAlphaWorkloadSelector(rule.WorkloadSelector) if err != nil { return nil, err } if warning != nil { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("sidecar: %s, will be applied to all services in namespace", warning))) } if len(rule.Egress) == 0 && len(rule.Ingress) == 0 && rule.OutboundTrafficPolicy == nil && rule.InboundConnectionPool == nil { return nil, fmt.Errorf("sidecar: empty configuration provided") } portMap := sets.Set[uint32]{} for _, i := range rule.Ingress { if i == nil { errs = AppendValidation(errs, fmt.Errorf("sidecar: ingress may not be null")) continue } if i.Port == nil { errs = AppendValidation(errs, fmt.Errorf("sidecar: port is required for ingress listeners")) continue } if i.Port.TargetPort > 0 { errs = AppendValidation(errs, fmt.Errorf("targetPort has no impact on Sidecars")) } bind := i.GetBind() errs = AppendValidation(errs, validateSidecarIngressPortAndBind(i.Port, bind)) if portMap.Contains(i.Port.Number) { errs = AppendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) } portMap.Insert(i.Port.Number) if len(i.DefaultEndpoint) != 0 { if strings.HasPrefix(i.DefaultEndpoint, UnixAddressPrefix) { errs = AppendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(i.DefaultEndpoint, UnixAddressPrefix))) } else { sHost, sPort, sErr := net.SplitHostPort(i.DefaultEndpoint) if sErr != nil { errs = AppendValidation(errs, sErr) } if sHost != "" && sHost != "127.0.0.1" && sHost != "0.0.0.0" && sHost != "::1" && sHost != "::" { errMsg := "sidecar: defaultEndpoint must be of form 127.0.0.1:<port>,0.0.0.0:<port>,[::1]:port,[::]:port,unix://filepath or unset" errs = AppendValidation(errs, errors.New(errMsg)) } port, err := strconv.Atoi(sPort) if err != nil { errs = AppendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint port (%s) is not a number: %v", sPort, err)) } else { errs = AppendValidation(errs, agent.ValidatePort(port)) } } } if i.Tls != nil { if len(i.Tls.SubjectAltNames) > 0 { errs = AppendValidation(errs, fmt.Errorf("sidecar: subjectAltNames is not supported in ingress tls")) } if i.Tls.HttpsRedirect { errs = AppendValidation(errs, fmt.Errorf("sidecar: httpsRedirect is not supported")) } if i.Tls.CredentialName != "" { errs = AppendValidation(errs, fmt.Errorf("sidecar: credentialName is not currently supported")) } if i.Tls.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL || i.Tls.Mode == networking.ServerTLSSettings_AUTO_PASSTHROUGH { errs = AppendValidation(errs, fmt.Errorf("configuration is invalid: cannot set mode to %s in sidecar ingress tls", i.Tls.Mode.String())) } protocol := protocol.Parse(i.Port.Protocol) if !protocol.IsTLS() { errs = AppendValidation(errs, fmt.Errorf("server cannot have TLS settings for non HTTPS/TLS ports")) } errs = AppendValidation(errs, validateTLSOptions(i.Tls)) } errs = AppendValidation(errs, validateConnectionPool(i.ConnectionPool)) if i.ConnectionPool != nil && i.ConnectionPool.Http != nil && i.Port != nil && !protocol.Parse(i.Port.Protocol).IsHTTP() { errs = AppendWarningf(errs, "sidecar: HTTP connection pool settings are configured for port %d (%q) but its protocol is not HTTP (%s); only TCP settings will apply", i.Port.Number, i.Port.Name, i.Port.Protocol) } } errs = AppendValidation(errs, validateConnectionPool(rule.InboundConnectionPool)) portMap = sets.Set[uint32]{} udsMap := sets.String{} catchAllEgressListenerFound := false for index, egress := range rule.Egress { if egress == nil { errs = AppendValidation(errs, errors.New("egress listener may not be null")) continue } if egress.Port == nil { if !catchAllEgressListenerFound { if index == len(rule.Egress)-1 { catchAllEgressListenerFound = true } else { errs = AppendValidation(errs, fmt.Errorf("sidecar: the egress listener with empty port should be the last listener in the list")) } } else { errs = AppendValidation(errs, fmt.Errorf("sidecar: egress can have only one listener with empty port")) continue } } else { if egress.Port.TargetPort > 0 { errs = AppendValidation(errs, fmt.Errorf("targetPort has no impact on Sidecars")) } bind := egress.GetBind() captureMode := egress.GetCaptureMode() errs = AppendValidation(errs, validateSidecarEgressPortBindAndCaptureMode(egress.Port, bind, captureMode)) if egress.Port.Number == 0 { if _, found := udsMap[bind]; found { errs = AppendValidation(errs, fmt.Errorf("sidecar: unix domain socket values for listeners must be unique")) } udsMap[bind] = struct{}{} } else { if portMap.Contains(egress.Port.Number) { errs = AppendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) } portMap.Insert(egress.Port.Number) } } if len(egress.Hosts) == 0 { errs = AppendValidation(errs, fmt.Errorf("sidecar: egress listener must contain at least one host")) } else { nssSvcs := map[string]map[string]bool{} for _, hostname := range egress.Hosts { parts := strings.SplitN(hostname, "/", 2) if len(parts) == 2 { ns := parts[0] svc := parts[1] if ns == "." { ns = cfg.Namespace } if _, ok := nssSvcs[ns]; !ok { nssSvcs[ns] = map[string]bool{} } if svc != "*" { if _, ok := nssSvcs[ns][svc]; ok || nssSvcs[ns]["*"] { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("duplicated egress host: %s", hostname))) } } else { if len(nssSvcs[ns]) != 0 { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("duplicated egress host: %s", hostname))) } } nssSvcs[ns][svc] = true } errs = AppendValidation(errs, agent.ValidateNamespaceSlashWildcardHostname(hostname, false, false)) } if nssSvcs["*"]["*"] && len(nssSvcs) != 1 { errs = AppendValidation(errs, WrapWarning(fmt.Errorf("`*/*` host select all resources, no other hosts can be added"))) } } } errs = AppendValidation(errs, validateSidecarOutboundTrafficPolicy(rule.OutboundTrafficPolicy)) return errs.Unwrap() })
ValidateSidecar checks sidecar config supplied by user
var ValidateTelemetry = RegisterValidateFunc("ValidateTelemetry", func(cfg config.Config) (Warning, error) { spec, ok := cfg.Spec.(*telemetry.Telemetry) if !ok { return nil, fmt.Errorf("cannot cast to telemetry") } errs := Validation{} errs = AppendValidation(errs, validateOneOfSelectorType(spec.GetSelector(), spec.GetTargetRef(), spec.GetTargetRefs()), validateWorkloadSelector(spec.GetSelector()), validatePolicyTargetReference(spec.GetTargetRef()), validatePolicyTargetReferences(spec.GetTargetRefs()), validateTelemetryMetrics(spec.Metrics), validateTelemetryTracing(spec.Tracing), validateTelemetryAccessLogging(spec.AccessLogging), ) return errs.Unwrap() })
ValidateTelemetry validates a Telemetry.
var ValidateVirtualService = RegisterValidateFunc("ValidateVirtualService", func(cfg config.Config) (Warning, error) { virtualService, ok := cfg.Spec.(*networking.VirtualService) if !ok { return nil, errors.New("cannot cast to virtual service") } errs := Validation{} if len(virtualService.Hosts) == 0 { if len(virtualService.Gateways) != 0 { errs = AppendValidation(errs, fmt.Errorf("delegate virtual service must have no gateways specified")) } if len(virtualService.Tls) != 0 { errs = AppendValidation(errs, fmt.Errorf("delegate virtual service must have no tls route specified")) } if len(virtualService.Tcp) != 0 { errs = AppendValidation(errs, fmt.Errorf("delegate virtual service must have no tcp route specified")) } } gatewaySemantics := cfg.Annotations[constants.InternalRouteSemantics] == constants.RouteSemanticsGateway appliesToMesh := false appliesToGateway := false if len(virtualService.Gateways) == 0 { appliesToMesh = true } else { errs = AppendValidation(errs, validateGatewayNames(virtualService.Gateways, gatewaySemantics)) appliesToGateway = isGateway(virtualService) appliesToMesh = !appliesToGateway } if !appliesToGateway { validateJWTClaimRoute := func(headers map[string]*networking.StringMatch) { for key := range headers { if jwt.ToRoutingClaim(key).Match { msg := fmt.Sprintf("JWT claim based routing (key: %s) is only supported for gateway, found no gateways: %v", key, virtualService.Gateways) errs = AppendValidation(errs, errors.New(msg)) } } } for _, http := range virtualService.GetHttp() { for _, m := range http.GetMatch() { validateJWTClaimRoute(m.GetHeaders()) validateJWTClaimRoute(m.GetWithoutHeaders()) } } } allHostsValid := true for _, virtualHost := range virtualService.Hosts { var err error if appliesToGateway { err = agent.ValidateWildcardDomainForVirtualServiceBoundToGateway(isSniHost(virtualService), virtualHost) } else { err = agent.ValidateWildcardDomain(virtualHost) } if err != nil { if !netutil.IsValidIPAddress(virtualHost) { errs = AppendValidation(errs, err) allHostsValid = false } } else if appliesToMesh && virtualHost == "*" { errs = AppendValidation(errs, fmt.Errorf("wildcard host * is not allowed for virtual services bound to the mesh gateway")) allHostsValid = false } } if allHostsValid { for i := 0; i < len(virtualService.Hosts); i++ { hostI := host.Name(virtualService.Hosts[i]) for j := i + 1; j < len(virtualService.Hosts); j++ { hostJ := host.Name(virtualService.Hosts[j]) if hostI.Matches(hostJ) { errs = AppendValidation(errs, fmt.Errorf("duplicate hosts in virtual service: %s & %s", hostI, hostJ)) } } } } if len(virtualService.Http) == 0 && len(virtualService.Tcp) == 0 && len(virtualService.Tls) == 0 { errs = AppendValidation(errs, errors.New("http, tcp or tls must be provided in virtual service")) } for _, httpRoute := range virtualService.Http { if httpRoute == nil { errs = AppendValidation(errs, errors.New("http route may not be null")) continue } errs = AppendValidation(errs, validateHTTPRoute(httpRoute, len(virtualService.Hosts) == 0, gatewaySemantics)) } for _, tlsRoute := range virtualService.Tls { errs = AppendValidation(errs, validateTLSRoute(tlsRoute, virtualService, gatewaySemantics)) } for _, tcpRoute := range virtualService.Tcp { errs = AppendValidation(errs, validateTCPRoute(tcpRoute, gatewaySemantics)) } errs = AppendValidation(errs, validateExportTo(cfg.Namespace, virtualService.ExportTo, false, false)) warnUnused := func(ruleno, reason string) { errs = AppendValidation(errs, WrapWarning(&AnalysisAwareError{ Type: "VirtualServiceUnreachableRule", Msg: fmt.Sprintf("virtualService rule %v not used (%s)", ruleno, reason), Parameters: []any{ruleno, reason}, })) } warnIneffective := func(ruleno, matchno, dupno string) { errs = AppendValidation(errs, WrapWarning(&AnalysisAwareError{ Type: "VirtualServiceIneffectiveMatch", Msg: fmt.Sprintf("virtualService rule %v match %v is not used (duplicate/overlapping match in rule %v)", ruleno, matchno, dupno), Parameters: []any{ruleno, matchno, dupno}, })) } analyzeUnreachableHTTPRules(virtualService.Http, warnUnused, warnIneffective) analyzeUnreachableTCPRules(virtualService.Tcp, warnUnused, warnIneffective) analyzeUnreachableTLSRules(virtualService.Tls, warnUnused, warnIneffective) return errs.Unwrap() })
ValidateVirtualService checks that a v1alpha3 route rule is well-formed.
var ValidateWasmPlugin = RegisterValidateFunc("ValidateWasmPlugin", func(cfg config.Config) (Warning, error) { spec, ok := cfg.Spec.(*extensions.WasmPlugin) if !ok { return nil, fmt.Errorf("cannot cast to wasmplugin") } errs := Validation{} errs = AppendValidation(errs, validateOneOfSelectorType(spec.GetSelector(), spec.GetTargetRef(), spec.GetTargetRefs()), validateWorkloadSelector(spec.GetSelector()), validatePolicyTargetReference(spec.GetTargetRef()), validatePolicyTargetReferences(spec.GetTargetRefs()), validateWasmPluginURL(spec.Url), validateWasmPluginSHA(spec), validateWasmPluginImagePullSecret(spec), validateWasmPluginName(spec), validateWasmPluginVMConfig(spec.VmConfig), validateWasmPluginMatch(spec.Match), ) return errs.Unwrap() })
ValidateWasmPlugin validates a WasmPlugin.
var ValidateWorkloadEntry = RegisterValidateFunc("ValidateWorkloadEntry", func(cfg config.Config) (Warning, error) { we, ok := cfg.Spec.(*networking.WorkloadEntry) if !ok { return nil, fmt.Errorf("cannot cast to workload entry") } return validateWorkloadEntry(we, nil, true).Unwrap() })
ValidateWorkloadEntry validates a workload entry.
var ValidateWorkloadGroup = RegisterValidateFunc("ValidateWorkloadGroup", func(cfg config.Config) (warnings Warning, errs error) { wg, ok := cfg.Spec.(*networking.WorkloadGroup) if !ok { return nil, fmt.Errorf("cannot cast to workload entry") } if wg.Template == nil { return nil, fmt.Errorf("template is required") } if wg.Metadata != nil { if err := labels.Instance(wg.Metadata.Labels).Validate(); err != nil { return nil, fmt.Errorf("invalid labels: %v", err) } } return nil, validateReadinessProbe(wg.Probe) })
ValidateWorkloadGroup validates a workload group.
Functions ¶
func IsNegativeDuration ¶
IsNegativeDuration check if the duration is negative
func IsValidateFunc ¶
IsValidateFunc indicates whether there is a validation function with the given name.
func ValidateCORSHTTPHeaderName ¶
ValidateCORSHTTPHeaderName validates a headers for CORS. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers#directives
func ValidateHTTPHeaderName ¶
ValidateHTTPHeaderName validates a header name
func ValidateHTTPHeaderNameOrJwtClaimRoute ¶
ValidateHTTPHeaderNameOrJwtClaimRoute validates a header name, allowing special @request.auth.claims syntax
func ValidateHTTPHeaderOperationName ¶
ValidateHTTPHeaderOperationName validates a header name when used to remove from request or modify response.
func ValidateHTTPHeaderValue ¶
ValidateHTTPHeaderValue validates a header value for Envoy Valid: "foo", "%HOSTNAME%", "100%%", "prefix %HOSTNAME% suffix" Invalid: "abc%123", "%START_TIME%%" We don't try to check that what is inside the %% is one of Envoy recognized values, we just prevent invalid config. See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html#custom-request-response-headers
func ValidateHTTPHeaderWithAuthorityOperationName ¶
ValidateHTTPHeaderWithAuthorityOperationName validates a header name when used to add/set in request.
func ValidateMaxServerConnectionAge ¶
ValidateMaxServerConnectionAge validate negative duration
func ValidatePercent ¶
ValidatePercent checks that percent is in range
func ValidatePortName ¶
ValidatePortName validates a port name to DNS-1123
func ValidateProtocol ¶
ValidateProtocol validates a portocol name is known
func ValidateUnixAddress ¶
ValidateUnixAddress validates that the string is a valid unix domain socket path.
Types ¶
type AnalysisAwareError ¶
func (*AnalysisAwareError) Error ¶
func (aae *AnalysisAwareError) Error() string
type HTTPRouteType ¶
type HTTPRouteType int
type OverlappingMatchValidationForHTTPRoute ¶
type OverlappingMatchValidationForHTTPRoute struct { RouteStr string MatchStr string Prefix string MatchPort uint32 MatchMethod string MatchAuthority string MatchHeaders map[string]string MatchQueryParams map[string]string MatchNonHeaders map[string]string }
OverlappingMatchValidationForHTTPRoute holds necessary information from virtualservice to do such overlapping match validation
type ParserState ¶
type ParserState int32
const ( LiteralParserState ParserState = iota // processing literal data VariableNameParserState // consuming a %VAR% name ExpectArrayParserState // expect starting [ in %VAR([...])% ExpectStringParserState // expect starting " in array of strings StringParserState // consuming an array element string ExpectArrayDelimiterOrEndParserState // expect array delimiter (,) or end of array (]) ExpectArgsEndParserState // expect closing ) in %VAR(...)% ExpectVariableEndParserState // expect closing % in %VAR(...)% )
type ValidateFunc ¶
ValidateFunc defines a validation func for an API proto.
func GetValidateFunc ¶
func GetValidateFunc(name string) ValidateFunc
GetValidateFunc returns the validation function with the given name, or null if it does not exist.
func RegisterValidateFunc ¶
func RegisterValidateFunc(name string, f ValidateFunc) ValidateFunc
type Validation ¶
type Validation = agent.Validation
func AppendValidation ¶
func AppendValidation(v Validation, vs ...error) Validation
func AppendWarningf ¶
func AppendWarningf(v Validation, format string, a ...any) Validation
AppendWarningf appends a formatted warning string nolint: unparam
func WrapWarning ¶
func WrapWarning(e error) Validation
WrapWarning turns an error into a Validation as a warning
type Warning ¶
func ValidateAlphaWorkloadSelector ¶
func ValidateAlphaWorkloadSelector(selector *networking.WorkloadSelector) (Warning, error)