Documentation ¶
Index ¶
- Constants
- Variables
- func IsNegativeDuration(in time.Duration) error
- func IsValidateFunc(name string) bool
- func ValidateConnectTimeout(timeout *durationpb.Duration) error
- func ValidateControlPlaneAuthPolicy(policy meshconfig.AuthenticationPolicy) error
- func ValidateDatadogCollector(d *meshconfig.Tracing_Datadog) error
- func ValidateDuration(pd *durationpb.Duration) error
- func ValidateDurationRange(dur, min, max time.Duration) error
- func ValidateExtensionProviderEnvoyExtAuthzGRPC(...) (errs error)
- func ValidateExtensionProviderEnvoyExtAuthzHTTP(...) (errs error)
- func ValidateExtensionProviderEnvoyHTTPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) (errs error)
- func ValidateExtensionProviderEnvoyOtelAls(...) (errs error)
- func ValidateExtensionProviderEnvoyTCPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) (errs error)
- func ValidateFQDN(fqdn string) error
- func ValidateHTTPHeaderName(name string) error
- func ValidateHTTPHeaderOperationName(name string) error
- func ValidateHTTPHeaderValue(value string) error
- func ValidateHTTPHeaderWithAuthorityOperationName(name string) error
- func ValidateHTTPHeaderWithHostOperationName(name string) error
- func ValidateIPAddress(addr string) error
- func ValidateIPSubnet(subnet string) error
- func ValidateLightstepCollector(ls *meshconfig.Tracing_Lightstep) error
- func ValidateMaxServerConnectionAge(in time.Duration) error
- func ValidateMeshConfig(mesh *meshconfig.MeshConfig) (errs error)
- func ValidateMeshConfigProxyConfig(config *meshconfig.ProxyConfig) (errs error)
- func ValidateMeshNetworks(meshnetworks *meshconfig.MeshNetworks) (errs error)
- func ValidateParentAndDrain(drainTime, parentShutdown *durationpb.Duration) (errs error)
- func ValidatePercent(val int32) error
- func ValidatePort(port int) error
- func ValidatePortName(name string) error
- func ValidateProtocol(protocolStr string) error
- func ValidateProtocolDetectionTimeout(timeout *durationpb.Duration) error
- func ValidateProxyAddress(hostAddr string) error
- func ValidateTrustDomain(domain string) error
- func ValidateUnixAddress(addr string) error
- func ValidateWildcardDomain(domain string) error
- func ValidateZipkinCollector(z *meshconfig.Tracing_Zipkin) error
- type AnalysisAwareError
- type HTTPRouteType
- type OverlappingMatchValidationForHTTPRoute
- 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 if err := validateWorkloadSelector(in.Selector); err != nil { errs = appendErrors(errs, err) } 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() { 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)) } 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 { 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 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 { 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)) } } 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)) } } } } } return nil, 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{} if features.EnableDestinationRuleInheritance { if rule.Host == "" { if rule.GetWorkloadSelector() != nil { v = appendValidation(v, fmt.Errorf("mesh/namespace destination rule cannot have workloadSelector configured")) } if len(rule.Subsets) != 0 { v = appendValidation(v, fmt.Errorf("mesh/namespace destination rule cannot have subsets")) } if len(rule.ExportTo) != 0 { v = appendValidation(v, fmt.Errorf("mesh/namespace destination rule cannot have exportTo configured")) } if rule.TrafficPolicy != nil && len(rule.TrafficPolicy.PortLevelSettings) != 0 { v = appendValidation(v, fmt.Errorf("mesh/namespace destination rule cannot have portLevelSettings configured")) } } else { v = appendValidation(v, ValidateWildcardDomain(rule.Host)) } } else { v = appendValidation(v, ValidateWildcardDomain(rule.Host)) } v = appendValidation(v, validateTrafficPolicy(rule.TrafficPolicy)) for _, subset := range rule.Subsets { if subset == nil { v = appendValidation(v, errors.New("subset may not be null")) continue } 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 ValidateEnvoyFilter = registerValidateFunc("ValidateEnvoyFilter", func(cfg config.Config) (Warning, error) { errs := Validation{} rule, ok := cfg.Spec.(*networking.EnvoyFilter) if !ok { return nil, fmt.Errorf("cannot cast to Envoy filter") } if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil { return nil, err } for _, cp := range rule.ConfigPatches { if cp == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: null config patch")) continue } if cp.ApplyTo == networking.EnvoyFilter_INVALID { errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing applyTo")) continue } if cp.Patch == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch")) continue } if cp.Patch.Operation == networking.EnvoyFilter_Patch_INVALID { errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch operation")) continue } if cp.Patch.Operation != networking.EnvoyFilter_Patch_REMOVE && cp.Patch.Value == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch value for non-remove operation")) continue } if cp.Match != nil && cp.Match.Proxy != nil && cp.Match.Proxy.ProxyVersion != "" { if _, err := regexp.Compile(cp.Match.Proxy.ProxyVersion); err != nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: invalid regex for proxy version, [%v]", err)) continue } } switch cp.ApplyTo { case networking.EnvoyFilter_LISTENER, networking.EnvoyFilter_FILTER_CHAIN, networking.EnvoyFilter_NETWORK_FILTER, networking.EnvoyFilter_HTTP_FILTER: if cp.Match != nil && cp.Match.ObjectTypes != nil { if cp.Match.GetListener() == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: applyTo for listener class objects cannot have non listener match")) continue } listenerMatch := cp.Match.GetListener() if listenerMatch.FilterChain != nil { if listenerMatch.FilterChain.Filter != nil { if cp.ApplyTo == networking.EnvoyFilter_LISTENER || cp.ApplyTo == networking.EnvoyFilter_FILTER_CHAIN { errs = appendValidation(errs, WrapWarning( fmt.Errorf("Envoy filter: filter match has no effect when used with %v", cp.ApplyTo))) } if listenerMatch.FilterChain.Filter.Name == "" { errs = appendValidation(errs, fmt.Errorf("Envoy filter: filter match has no name to match on")) continue } else if listenerMatch.FilterChain.Filter.SubFilter != nil { if cp.ApplyTo != networking.EnvoyFilter_HTTP_FILTER { errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match can be used with applyTo HTTP_FILTER only")) continue } if listenerMatch.FilterChain.Filter.Name != wellknown.HTTPConnectionManager && listenerMatch.FilterChain.Filter.Name != "envoy.http_connection_manager" { errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match requires filter match with %s", wellknown.HTTPConnectionManager)) continue } if listenerMatch.FilterChain.Filter.SubFilter.Name == "" { errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match has no name to match on")) continue } } errs = appendValidation(errs, validateListenerMatchName(listenerMatch.FilterChain.Filter.GetName())) errs = appendValidation(errs, validateListenerMatchName(listenerMatch.FilterChain.Filter.GetSubFilter().GetName())) } } } case networking.EnvoyFilter_ROUTE_CONFIGURATION, networking.EnvoyFilter_VIRTUAL_HOST, networking.EnvoyFilter_HTTP_ROUTE: if cp.Match != nil && cp.Match.ObjectTypes != nil { if cp.Match.GetRouteConfiguration() == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: applyTo for http route class objects cannot have non route configuration match")) } } case networking.EnvoyFilter_CLUSTER: if cp.Match != nil && cp.Match.ObjectTypes != nil { if cp.Match.GetCluster() == nil { errs = appendValidation(errs, fmt.Errorf("Envoy filter: applyTo for cluster class objects cannot have non cluster match")) } } } if _, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value, false); err != nil { errs = appendValidation(errs, err) } else { obj, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value, true) if err != nil { errs = appendValidation(errs, WrapWarning(err)) } if obj != nil { errs = appendValidation(errs, validateDeprecatedFilterTypes(obj)) errs = appendValidation(errs, validateMissingTypedConfigFilterTypes(obj)) } } } return errs.Unwrap() })
ValidateEnvoyFilter checks envoy filter config supplied by user
var ValidateGateway = registerValidateFunc("ValidateGateway", func(cfg config.Config) (Warning, error) { name := cfg.Name v := Validation{} if !labels.IsDNS1123Label(name) { 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)) } } 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 { errs = appendErrors(errs, fmt.Errorf("port cannot be 0")) } } errs = appendErrors(errs, validateWorkloadSelector(in.Selector)) return nil, 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") } var errs error errs = appendErrors(errs, validateWorkloadSelector(in.Selector)) for _, rule := range in.JwtRules { errs = appendErrors(errs, validateJwtRule(rule)) } return nil, errs })
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") } if err := validateAlphaWorkloadSelector(serviceEntry.WorkloadSelector); err != nil { return nil, err } errs := Validation{} 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, ValidateWildcardDomain(hostname)) } } cidrFound := false for _, address := range serviceEntry.Addresses { cidrFound = cidrFound || strings.Contains(address, "/") errs = appendValidation(errs, 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")) } } servicePortNumbers := make(map[uint32]bool) servicePorts := make(map[string]bool, 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[port.Name] { errs = appendValidation(errs, fmt.Errorf("service entry port name %q already defined", port.Name)) } servicePorts[port.Name] = true if servicePortNumbers[port.Number] { errs = appendValidation(errs, fmt.Errorf("service entry port %d already defined", port.Number)) } servicePortNumbers[port.Number] = true if port.TargetPort != 0 { errs = appendValidation(errs, ValidatePort(int(port.TargetPort))) } errs = appendValidation(errs, ValidatePortName(port.Name), ValidateProtocol(port.Protocol), 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")) } case networking.ServiceEntry_STATIC: unixEndpoint := false for _, endpoint := range serviceEntry.Endpoints { addr := endpoint.GetAddress() if strings.HasPrefix(addr, UnixAddressPrefix) { unixEndpoint = true errs = appendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(addr, UnixAddressPrefix))) if len(endpoint.Ports) != 0 { errs = appendValidation(errs, fmt.Errorf("unix endpoint %s must not include ports", addr)) } } else { errs = appendValidation(errs, ValidateIPAddress(addr)) for name, port := range endpoint.Ports { if !servicePorts[name] { errs = appendValidation(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port)) } } } errs = appendValidation(errs, labels.Instance(endpoint.Labels).Validate()) } if unixEndpoint && len(serviceEntry.Ports) != 1 { errs = appendValidation(errs, errors.New("exactly 1 service port required for unix endpoints")) } case networking.ServiceEntry_DNS, networking.ServiceEntry_DNS_ROUND_ROBIN: if len(serviceEntry.Endpoints) == 0 { for _, hostname := range serviceEntry.Hosts { if err := ValidateFQDN(hostname); err != nil { errs = appendValidation(errs, fmt.Errorf("hosts must be FQDN if no endpoints are provided for resolution mode %s", serviceEntry.Resolution)) } } } for _, endpoint := range serviceEntry.Endpoints { ipAddr := net.ParseIP(endpoint.Address) if ipAddr == nil { if err := 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[name] { errs = appendValidation(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port)) } errs = appendValidation(errs, ValidatePortName(name), ValidatePort(int(port))) } } if len(serviceEntry.Addresses) > 0 { for _, port := range serviceEntry.Ports { 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 { 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") } if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil { return nil, err } if len(rule.Egress) == 0 && len(rule.Ingress) == 0 && rule.OutboundTrafficPolicy == nil { return nil, fmt.Errorf("sidecar: empty configuration provided") } portMap := make(map[uint32]struct{}) 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 } bind := i.GetBind() errs = appendValidation(errs, validateSidecarIngressPortAndBind(i.Port, bind)) if _, found := portMap[i.Port.Number]; found { errs = appendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) } portMap[i.Port.Number] = struct{}{} if len(i.DefaultEndpoint) != 0 { if strings.HasPrefix(i.DefaultEndpoint, UnixAddressPrefix) { errs = appendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(i.DefaultEndpoint, UnixAddressPrefix))) } else { parts := strings.Split(i.DefaultEndpoint, ":") if len(parts) < 2 { errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>, 0.0.0.0:<port>, unix://filepath, or unset")) } else { if len(parts[0]) > 0 && parts[0] != "127.0.0.1" && parts[0] != "0.0.0.0" { errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>, 0.0.0.0:<port>, unix://filepath, or unset")) } port, err := strconv.Atoi(parts[1]) if err != nil { errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint port (%s) is not a number: %v", parts[1], err)) } else { errs = appendValidation(errs, 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)) } } portMap = make(map[uint32]struct{}) udsMap := make(map[string]struct{}) 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 { 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 _, found := portMap[egress.Port.Number]; found { errs = appendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) } portMap[egress.Port.Number] = struct{}{} } } 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, validateNamespaceSlashWildcardHostname(hostname, 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, validateWorkloadSelector(spec.Selector), 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")) } } appliesToMesh := false appliesToGateway := false if len(virtualService.Gateways) == 0 { appliesToMesh = true } else { errs = appendValidation(errs, validateGatewayNames(virtualService.Gateways)) for _, gatewayName := range virtualService.Gateways { if gatewayName == constants.IstioMeshGateway { appliesToMesh = true } else { appliesToGateway = true } } } if !appliesToGateway { validateJWTClaimRoute := func(headers map[string]*networking.StringMatch) { for key := range headers { if strings.HasPrefix(key, constant.HeaderJWTClaim) { 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 { if err := ValidateWildcardDomain(virtualHost); err != nil { ipAddr := net.ParseIP(virtualHost) if ipAddr == nil { 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)) } for _, tlsRoute := range virtualService.Tls { errs = appendValidation(errs, validateTLSRoute(tlsRoute, virtualService)) } for _, tcpRoute := range virtualService.Tcp { errs = appendValidation(errs, validateTCPRoute(tcpRoute)) } 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: []interface{}{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: []interface{}{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, validateWorkloadSelector(spec.Selector), validateWasmPluginURL(spec.Url), validateWasmPluginSHA(spec), validateWasmPluginVMConfig(spec.VmConfig), ) 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) })
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 ValidateConnectTimeout ¶
func ValidateConnectTimeout(timeout *durationpb.Duration) error
ValidateConnectTimeout validates the envoy connection timeout
func ValidateControlPlaneAuthPolicy ¶
func ValidateControlPlaneAuthPolicy(policy meshconfig.AuthenticationPolicy) error
func ValidateDatadogCollector ¶
func ValidateDatadogCollector(d *meshconfig.Tracing_Datadog) error
ValidateDatadogCollector validates the configuration for sending envoy spans to Datadog
func ValidateDuration ¶
func ValidateDuration(pd *durationpb.Duration) error
ValidateDuration checks that a proto duration is well-formed
func ValidateDurationRange ¶
ValidateDurationRange verifies range is in specified duration
func ValidateExtensionProviderEnvoyExtAuthzGRPC ¶
func ValidateExtensionProviderEnvoyExtAuthzGRPC(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider) (errs error)
func ValidateExtensionProviderEnvoyExtAuthzHTTP ¶
func ValidateExtensionProviderEnvoyExtAuthzHTTP(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) (errs error)
func ValidateExtensionProviderEnvoyHTTPAls ¶
func ValidateExtensionProviderEnvoyHTTPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) (errs error)
func ValidateExtensionProviderEnvoyOtelAls ¶
func ValidateExtensionProviderEnvoyOtelAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider) (errs error)
func ValidateExtensionProviderEnvoyTCPAls ¶
func ValidateExtensionProviderEnvoyTCPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) (errs error)
func ValidateFQDN ¶
ValidateFQDN checks a fully-qualified domain name
func ValidateHTTPHeaderName ¶
ValidateHTTPHeaderName validates a header name
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" 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 ValidateHTTPHeaderWithHostOperationName ¶
ValidateHTTPHeaderWithHostOperationName validates a header name when used to destination specific add/set in request. TODO(https://github.com/envoyproxy/envoy/issues/16775) merge with ValidateHTTPHeaderWithAuthorityOperationName
func ValidateIPAddress ¶
ValidateIPAddress validates that a string in "CIDR notation" or "Dot-decimal notation"
func ValidateIPSubnet ¶
ValidateIPSubnet checks that a string is in "CIDR notation" or "Dot-decimal notation"
func ValidateLightstepCollector ¶
func ValidateLightstepCollector(ls *meshconfig.Tracing_Lightstep) error
ValidateLightstepCollector validates the configuration for sending envoy spans to LightStep
func ValidateMaxServerConnectionAge ¶
ValidateMaxServerConnectionAge validate negative duration
func ValidateMeshConfig ¶
func ValidateMeshConfig(mesh *meshconfig.MeshConfig) (errs error)
ValidateMeshConfig checks that the mesh config is well-formed
func ValidateMeshConfigProxyConfig ¶
func ValidateMeshConfigProxyConfig(config *meshconfig.ProxyConfig) (errs error)
ValidateMeshConfigProxyConfig checks that the mesh config is well-formed
func ValidateMeshNetworks ¶
func ValidateMeshNetworks(meshnetworks *meshconfig.MeshNetworks) (errs error)
ValidateMeshNetworks validates meshnetworks.
func ValidateParentAndDrain ¶
func ValidateParentAndDrain(drainTime, parentShutdown *durationpb.Duration) (errs error)
ValidateParentAndDrain checks that parent and drain durations are valid
func ValidatePercent ¶
ValidatePercent checks that percent is in range
func ValidatePort ¶
ValidatePort checks that the network port is in range
func ValidatePortName ¶
ValidatePortName validates a port name to DNS-1123
func ValidateProtocol ¶
ValidateProtocol validates a portocol name is known
func ValidateProtocolDetectionTimeout ¶
func ValidateProtocolDetectionTimeout(timeout *durationpb.Duration) error
ValidateProtocolDetectionTimeout validates the envoy protocol detection timeout
func ValidateProxyAddress ¶
ValidateProxyAddress checks that a network address is well-formed
func ValidateTrustDomain ¶
validate the trust domain format
func ValidateUnixAddress ¶
ValidateUnixAddress validates that the string is a valid unix domain socket path.
func ValidateWildcardDomain ¶
ValidateWildcardDomain checks that a domain is a valid FQDN, but also allows wildcard prefixes.
func ValidateZipkinCollector ¶
func ValidateZipkinCollector(z *meshconfig.Tracing_Zipkin) error
ValidateZipkinCollector validates the configuration for sending envoy spans to Zipkin
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 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.
type Validation ¶
Validation holds errors and warnings. They can be joined with additional errors by called appendValidation
func Warningf ¶
func Warningf(format string, a ...interface{}) Validation
Warningf formats according to a format specifier and returns the string as a value that satisfies error. Like Errorf, but for warnings.
func WrapWarning ¶
func WrapWarning(e error) Validation
WrapWarning turns an error into a Validation as a warning
func (Validation) Error ¶
func (v Validation) Error() string
func (Validation) Unwrap ¶
func (v Validation) Unwrap() (Warning, error)