istio: istio.io/istio/pkg/config/validation Index | Files

package validation

import "istio.io/istio/pkg/config/validation"

Index

Package Files

validation.go

Constants

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(string, string, proto.Message) error {
            return nil
        })
)
var ValidateAuthenticationPolicy = registerValidateFunc("ValidateAuthenticationPolicy",
    func(name, namespace string, msg proto.Message) error {

        clusterScoped := namespace == ""
        in, ok := msg.(*authn.Policy)
        if !ok {
            return errors.New("cannot cast to AuthenticationPolicy")
        }
        var errs error

        if !clusterScoped {

            if len(in.Targets) == 0 && name != constants.DefaultAuthenticationPolicyName {
                errs = appendErrors(errs, fmt.Errorf("authentication policy with no target rules  must be named %q, found %q",
                    constants.DefaultAuthenticationPolicyName, name))
            }

            if len(in.Targets) > 0 && name == constants.DefaultAuthenticationPolicyName {
                errs = appendErrors(errs, fmt.Errorf("authentication policy with name %q must not have any target rules", name))
            }

            for _, target := range in.Targets {
                errs = appendErrors(errs, validateAuthNPolicyTarget(target))
            }
        } else {
            if name != constants.DefaultAuthenticationPolicyName {
                errs = appendErrors(errs, fmt.Errorf("cluster-scoped authentication policy name must be %q, found %q",
                    constants.DefaultAuthenticationPolicyName, name))
            }

            if len(in.Targets) > 0 {
                errs = appendErrors(errs, fmt.Errorf("cluster-scoped authentication policy must not have targets"))
            }
        }

        jwtIssuers := make(map[string]bool)
        for _, method := range in.Peers {

            if jwt := method.GetJwt(); jwt != nil {
                if _, jwtExist := jwtIssuers[jwt.Issuer]; jwtExist {
                    errs = appendErrors(errs, fmt.Errorf("jwt with issuer %q already defined", jwt.Issuer))
                } else {
                    jwtIssuers[jwt.Issuer] = true
                }
                errs = appendErrors(errs, validateJwt(jwt))
            }
        }

        for _, method := range in.Origins {
            if method == nil {
                errs = multierror.Append(errs, errors.New("origin cannot be empty"))
                continue
            }
            if method.Jwt == nil {
                errs = multierror.Append(errs, errors.New("jwt cannot be empty"))
                continue
            }
            if _, jwtExist := jwtIssuers[method.Jwt.Issuer]; jwtExist {
                errs = appendErrors(errs, fmt.Errorf("jwt with issuer %q already defined", method.Jwt.Issuer))
            } else {
                jwtIssuers[method.Jwt.Issuer] = true
            }
            errs = appendErrors(errs, validateJwt(method.Jwt))
        }

        return errs
    })

ValidateAuthenticationPolicy checks that AuthenticationPolicy is well-formed.

var ValidateAuthorizationPolicy = registerValidateFunc("ValidateAuthorizationPolicy",
    func(name, namespace string, msg proto.Message) error {
        in, ok := msg.(*security_beta.AuthorizationPolicy)
        if !ok {
            return fmt.Errorf("cannot cast to AuthorizationPolicy")
        }

        if err := validateWorkloadSelector(in.Selector); err != nil {
            return err
        }

        if in.Action == security_beta.AuthorizationPolicy_DENY && in.Rules == nil {
            return fmt.Errorf("a deny policy without `rules` is meaningless and has no effect, found in %s.%s", name, namespace)
        }

        var errs error
        for i, rule := range in.GetRules() {
            if rule.From != nil && len(rule.From) == 0 {
                errs = appendErrors(errs, fmt.Errorf("`from` must not be empty, found at rule %d in %s.%s", i, name, namespace))
            }
            for _, from := range rule.From {
                if from.Source == nil {
                    errs = appendErrors(errs, fmt.Errorf("`from.source` must not be nil, found at rule %d in %s.%s", i, name, namespace))
                } else {
                    src := from.Source
                    if len(src.Principals) == 0 && len(src.RequestPrincipals) == 0 && len(src.Namespaces) == 0 && len(src.IpBlocks) == 0 &&
                        len(src.NotPrincipals) == 0 && len(src.NotRequestPrincipals) == 0 && len(src.NotNamespaces) == 0 && len(src.NotIpBlocks) == 0 {
                        errs = appendErrors(errs, fmt.Errorf("`from.source` must not be empty, found at rule %d in %s.%s", i, name, namespace))
                    }
                    errs = appendErrors(errs, security.ValidateIPs(from.Source.GetIpBlocks()))
                }
            }
            if rule.To != nil && len(rule.To) == 0 {
                errs = appendErrors(errs, fmt.Errorf("`to` must not be empty, found at rule %d in %s.%s", i, name, namespace))
            }
            for _, to := range rule.To {
                if to.Operation == nil {
                    errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be nil, found at rule %d in %s.%s", i, name, namespace))
                } 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 in %s.%s", i, name, namespace))
                    }
                    errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetPorts()))
                }
            }
            for _, condition := range rule.GetWhen() {
                key := condition.GetKey()
                if key == "" {
                    errs = appendErrors(errs, fmt.Errorf("`key` must not be empty, found in %s.%s", name, namespace))
                } 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, found in %s.%s",
                            key, name, namespace))
                    } else {
                        if err := security.ValidateAttribute(key, condition.GetValues()); err != nil {
                            errs = appendErrors(errs, fmt.Errorf("invalid `value` for `key` %s: %v, found in %s.%s", key, err, name, namespace))
                        }
                        if err := security.ValidateAttribute(key, condition.GetNotValues()); err != nil {
                            errs = appendErrors(errs, fmt.Errorf("invalid `notValue` for `key` %s: %v, found in %s.%s", key, err, name, namespace))
                        }
                    }
                }
            }
        }
        return errs
    })

ValidateAuthorizationPolicy checks that AuthorizationPolicy is well-formed.

var ValidateClusterRbacConfig = registerValidateFunc("ValidateClusterRbacConfig",
    func(name, _ string, msg proto.Message) error {
        return checkRbacConfig(name, "ClusterRbacConfig", msg)
    })

ValidateClusterRbacConfig checks that ClusterRbacConfig is well-formed.

var ValidateDestinationRule = registerValidateFunc("ValidateDestinationRule",
    func(_, _ string, msg proto.Message) (errs error) {
        rule, ok := msg.(*networking.DestinationRule)
        if !ok {
            return fmt.Errorf("cannot cast to destination rule")
        }

        errs = appendErrors(errs,
            ValidateWildcardDomain(rule.Host),
            validateTrafficPolicy(rule.TrafficPolicy))

        for _, subset := range rule.Subsets {
            errs = appendErrors(errs, validateSubset(subset))
        }

        errs = appendErrors(errs, validateExportTo(rule.ExportTo))
        return
    })

ValidateDestinationRule checks proxy policies

var ValidateEnvoyFilter = registerValidateFunc("ValidateEnvoyFilter",
    func(_, _ string, msg proto.Message) (errs error) {
        rule, ok := msg.(*networking.EnvoyFilter)
        if !ok {
            return fmt.Errorf("cannot cast to Envoy filter")
        }

        if len(rule.Filters) > 0 {
            scope.Warn("Envoy filter: Filters is deprecated. use configPatches instead")
        }

        if rule.WorkloadLabels != nil {
            scope.Warn("Envoy filter: workloadLabels is deprecated. use workloadSelector instead")
        }

        if rule.WorkloadSelector != nil {
            if rule.WorkloadSelector.GetLabels() == nil {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: workloadSelector cannot have empty labels"))
            }
        }

        for _, f := range rule.Filters {
            if f.InsertPosition != nil {
                if f.InsertPosition.Index == networking.EnvoyFilter_InsertPosition_BEFORE ||
                    f.InsertPosition.Index == networking.EnvoyFilter_InsertPosition_AFTER {
                    if f.InsertPosition.RelativeTo == "" {
                        errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing relativeTo filter with BEFORE/AFTER index"))
                    }
                }
            }
            if f.FilterType == networking.EnvoyFilter_Filter_INVALID {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing filter type"))
            }
            if len(f.FilterName) == 0 {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing filter name"))
            }

            if f.FilterConfig == nil {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing filter config"))
            }
        }

        for _, cp := range rule.ConfigPatches {
            if cp.ApplyTo == networking.EnvoyFilter_INVALID {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing applyTo"))
                continue
            }
            if cp.Patch == nil {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing patch"))
                continue
            }
            if cp.Patch.Operation == networking.EnvoyFilter_Patch_INVALID {
                errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing patch operation"))
                continue
            }
            if cp.Patch.Operation != networking.EnvoyFilter_Patch_REMOVE && cp.Patch.Value == nil {
                errs = appendErrors(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 = appendErrors(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 = appendErrors(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 listenerMatch.FilterChain.Filter.Name == "" {
                                errs = appendErrors(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 = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match can be used with applyTo HTTP_FILTER only"))
                                    continue
                                }

                                if listenerMatch.FilterChain.Filter.Name != xdsUtil.HTTPConnectionManager {
                                    errs = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match requires filter match with %s",
                                        xdsUtil.HTTPConnectionManager))
                                    continue
                                }
                                if listenerMatch.FilterChain.Filter.SubFilter.Name == "" {
                                    errs = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match has no name to match on"))
                                    continue
                                }
                            }
                        }
                    }
                }
            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 = appendErrors(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 = appendErrors(errs, fmt.Errorf("Envoy filter: applyTo for cluster class objects cannot have non cluster match"))
                    }
                }
            }

            if _, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value); err != nil {
                errs = appendErrors(errs, err)
            }
        }

        return
    })

ValidateEnvoyFilter checks envoy filter config supplied by user

var ValidateGateway = registerValidateFunc("ValidateGateway",
    func(name, _ string, msg proto.Message) (errs error) {

        if !labels.IsDNS1123Label(name) {
            errs = appendErrors(errs, fmt.Errorf("invalid gateway name: %q", name))
        }
        value, ok := msg.(*networking.Gateway)
        if !ok {
            errs = appendErrors(errs, fmt.Errorf("cannot cast to gateway: %#v", msg))
            return
        }

        if len(value.Servers) == 0 {
            errs = appendErrors(errs, fmt.Errorf("gateway must have at least one server"))
        } else {
            for _, server := range value.Servers {
                errs = appendErrors(errs, validateServer(server))
            }
        }

        portNames := make(map[string]bool)

        for _, s := range value.Servers {
            if s.Port != nil {
                if portNames[s.Port.Name] {
                    errs = appendErrors(errs, fmt.Errorf("port names in servers must be unique: duplicate name %s", s.Port.Name))
                }
                portNames[s.Port.Name] = true
            }
        }

        return errs
    })

ValidateGateway checks gateway specifications

var ValidateHTTPAPISpec = registerValidateFunc("ValidateHTTPAPISpec",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*mccpb.HTTPAPISpec)
        if !ok {
            return errors.New("cannot case to HTTPAPISpec")
        }
        var errs error

        if in.Attributes != nil {
            if err := ValidateMixerAttributes(in.Attributes); err != nil {
                errs = multierror.Append(errs, err)
            }
        }
        if len(in.Patterns) == 0 {
            errs = multierror.Append(errs, errors.New("at least one pattern must be specified"))
        }
        for _, pattern := range in.Patterns {
            if err := ValidateMixerAttributes(in.Attributes); err != nil {
                errs = multierror.Append(errs, err)
            }
            if pattern.HttpMethod == "" {
                errs = multierror.Append(errs, errors.New("http_method cannot be empty"))
            }
            switch m := pattern.Pattern.(type) {
            case *mccpb.HTTPAPISpecPattern_UriTemplate:
                if m.UriTemplate == "" {
                    errs = multierror.Append(errs, errors.New("uri_template cannot be empty"))
                }
            case *mccpb.HTTPAPISpecPattern_Regex:
                if m.Regex == "" {
                    errs = multierror.Append(errs, errors.New("regex cannot be empty"))
                }
            }
        }
        for _, key := range in.ApiKeys {
            switch m := key.Key.(type) {
            case *mccpb.APIKey_Query:
                if m.Query == "" {
                    errs = multierror.Append(errs, errors.New("query cannot be empty"))
                }
            case *mccpb.APIKey_Header:
                if m.Header == "" {
                    errs = multierror.Append(errs, errors.New("header cannot be empty"))
                }
            case *mccpb.APIKey_Cookie:
                if m.Cookie == "" {
                    errs = multierror.Append(errs, errors.New("cookie cannot be empty"))
                }
            }
        }
        return errs
    })

ValidateHTTPAPISpec checks that HTTPAPISpec is well-formed.

var ValidateHTTPAPISpecBinding = registerValidateFunc("ValidateHTTPAPISpecBinding",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*mccpb.HTTPAPISpecBinding)
        if !ok {
            return errors.New("cannot case to HTTPAPISpecBinding")
        }
        var errs error
        if len(in.Services) == 0 {
            errs = multierror.Append(errs, errors.New("at least one service must be specified"))
        }
        for _, service := range in.Services {
            if err := ValidateMixerService(service); err != nil {
                errs = multierror.Append(errs, err)
            }
        }
        if len(in.ApiSpecs) == 0 {
            errs = multierror.Append(errs, errors.New("at least one spec must be specified"))
        }
        for _, spec := range in.ApiSpecs {
            if spec.Name == "" {
                errs = multierror.Append(errs, errors.New("name is mandatory for HTTPAPISpecReference"))
            }
            if spec.Namespace != "" && !labels.IsDNS1123Label(spec.Namespace) {
                errs = multierror.Append(errs, fmt.Errorf("namespace %q must be a valid label", spec.Namespace))
            }
        }
        return errs
    })

ValidateHTTPAPISpecBinding checks that HTTPAPISpecBinding is well-formed.

var ValidateQuotaSpec = registerValidateFunc("ValidateQuotaSpec",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*mccpb.QuotaSpec)
        if !ok {
            return errors.New("cannot case to HTTPAPISpecBinding")
        }
        var errs error
        if len(in.Rules) == 0 {
            errs = multierror.Append(errs, errors.New("a least one rule must be specified"))
        }
        for _, rule := range in.Rules {
            for _, match := range rule.Match {
                for name, clause := range match.Clause {
                    if clause == nil {
                        errs = multierror.Append(errs, errors.New("a clause cannot be empty"))
                        continue
                    }
                    switch matchType := clause.MatchType.(type) {
                    case *mccpb.StringMatch_Exact:
                        if matchType.Exact == "" {
                            errs = multierror.Append(errs,
                                fmt.Errorf("StringMatch_Exact for attribute %q cannot be empty", name))
                        }
                    case *mccpb.StringMatch_Prefix:
                        if matchType.Prefix == "" {
                            errs = multierror.Append(errs,
                                fmt.Errorf("StringMatch_Prefix for attribute %q cannot be empty", name))
                        }
                    case *mccpb.StringMatch_Regex:
                        if matchType.Regex == "" {
                            errs = multierror.Append(errs,
                                fmt.Errorf("StringMatch_Regex for attribute %q cannot be empty", name))
                        }
                    }
                }
            }
            if len(rule.Quotas) == 0 {
                errs = multierror.Append(errs, errors.New("a least one quota must be specified"))
            }
            for _, quota := range rule.Quotas {
                if quota.Quota == "" {
                    errs = multierror.Append(errs, errors.New("quota name cannot be empty"))
                }
                if quota.Charge <= 0 {
                    errs = multierror.Append(errs, errors.New("quota charge amount must be positive"))
                }
            }
        }
        return errs
    })

ValidateQuotaSpec checks that Quota is well-formed.

var ValidateQuotaSpecBinding = registerValidateFunc("ValidateQuotaSpecBinding",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*mccpb.QuotaSpecBinding)
        if !ok {
            return errors.New("cannot case to HTTPAPISpecBinding")
        }
        var errs error
        if len(in.Services) == 0 {
            errs = multierror.Append(errs, errors.New("at least one service must be specified"))
        }
        for _, service := range in.Services {
            if err := ValidateMixerService(service); err != nil {
                errs = multierror.Append(errs, err)
            }
        }
        if len(in.QuotaSpecs) == 0 {
            errs = multierror.Append(errs, errors.New("at least one spec must be specified"))
        }
        for _, spec := range in.QuotaSpecs {
            if spec.Name == "" {
                errs = multierror.Append(errs, errors.New("name is mandatory for QuotaSpecReference"))
            }
            if spec.Namespace != "" && !labels.IsDNS1123Label(spec.Namespace) {
                errs = multierror.Append(errs, fmt.Errorf("namespace %q must be a valid label", spec.Namespace))
            }
        }
        return errs
    })

ValidateQuotaSpecBinding checks that QuotaSpecBinding is well-formed.

var ValidateRbacConfig = registerValidateFunc("ValidateRbacConfig",
    func(name, _ string, msg proto.Message) error {
        scope.Warnf("RbacConfig is deprecated, use ClusterRbacConfig instead.")
        return checkRbacConfig(name, "RbacConfig", msg)
    })

ValidateRbacConfig checks that RbacConfig is well-formed.

var ValidateRequestAuthentication = registerValidateFunc("ValidateRequestAuthentication",
    func(name, namespace string, msg proto.Message) error {
        in, ok := msg.(*security_beta.RequestAuthentication)
        if !ok {
            return errors.New("cannot cast to RequestAuthentication")
        }

        var errs error
        emptySelector := in.Selector == nil || len(in.Selector.MatchLabels) == 0
        if name == constants.DefaultAuthenticationPolicyName && !emptySelector {
            errs = appendErrors(errs, fmt.Errorf("default request authentication cannot have workload selector"))
        } else if emptySelector && name != constants.DefaultAuthenticationPolicyName {
            errs = appendErrors(errs,
                fmt.Errorf("request authentication with empty workload selector must be named %q", constants.DefaultAuthenticationPolicyName))
        }

        errs = appendErrors(errs, validateWorkloadSelector(in.Selector))

        for _, rule := range in.JwtRules {
            errs = appendErrors(errs, validateJwtRule(rule))
        }
        return errs
    })

ValidateRequestAuthentication checks that request authentication spec is well-formed.

var ValidateServiceEntry = registerValidateFunc("ValidateServiceEntry",
    func(_, _ string, config proto.Message) (errs error) {
        serviceEntry, ok := config.(*networking.ServiceEntry)
        if !ok {
            return fmt.Errorf("cannot cast to service entry")
        }

        if len(serviceEntry.Hosts) == 0 {
            errs = appendErrors(errs, fmt.Errorf("service entry must have at least one host"))
        }
        for _, hostname := range serviceEntry.Hosts {

            if hostname == "*" {
                errs = appendErrors(errs, fmt.Errorf("invalid host %s", hostname))
            } else {
                errs = appendErrors(errs, ValidateWildcardDomain(hostname))
            }
        }

        cidrFound := false
        for _, address := range serviceEntry.Addresses {
            cidrFound = cidrFound || strings.Contains(address, "/")
            errs = appendErrors(errs, ValidateIPSubnet(address))
        }

        if cidrFound {
            if serviceEntry.Resolution != networking.ServiceEntry_NONE && serviceEntry.Resolution != networking.ServiceEntry_STATIC {
                errs = appendErrors(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 servicePorts[port.Name] {
                errs = appendErrors(errs, fmt.Errorf("service entry port name %q already defined", port.Name))
            }
            servicePorts[port.Name] = true
            if servicePortNumbers[port.Number] {
                errs = appendErrors(errs, fmt.Errorf("service entry port %d already defined", port.Number))
            }
            servicePortNumbers[port.Number] = true
        }

        switch serviceEntry.Resolution {
        case networking.ServiceEntry_NONE:
            if len(serviceEntry.Endpoints) != 0 {
                errs = appendErrors(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 = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(addr, UnixAddressPrefix)))
                    if len(endpoint.Ports) != 0 {
                        errs = appendErrors(errs, fmt.Errorf("unix endpoint %s must not include ports", addr))
                    }
                } else {
                    errs = appendErrors(errs, ValidateIPAddress(addr))

                    for name, port := range endpoint.Ports {
                        if !servicePorts[name] {
                            errs = appendErrors(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port))
                        }
                    }
                }
                errs = appendErrors(errs, labels.Instance(endpoint.Labels).Validate())

            }
            if unixEndpoint && len(serviceEntry.Ports) != 1 {
                errs = appendErrors(errs, errors.New("exactly 1 service port required for unix endpoints"))
            }
        case networking.ServiceEntry_DNS:
            if len(serviceEntry.Endpoints) == 0 {
                for _, hostname := range serviceEntry.Hosts {
                    if err := ValidateFQDN(hostname); err != nil {
                        errs = appendErrors(errs,
                            fmt.Errorf("hosts must be FQDN if no endpoints are provided for resolution mode DNS"))
                    }
                }
            }

            for _, endpoint := range serviceEntry.Endpoints {
                ipAddr := net.ParseIP(endpoint.Address)
                if ipAddr == nil {
                    if err := ValidateFQDN(endpoint.Address); err != nil {
                        errs = appendErrors(errs,
                            fmt.Errorf("endpoint address %q is not a valid FQDN or an IP address", endpoint.Address))
                    }
                }
                errs = appendErrors(errs,
                    labels.Instance(endpoint.Labels).Validate())
                for name, port := range endpoint.Ports {
                    if !servicePorts[name] {
                        errs = appendErrors(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port))
                    }
                    errs = appendErrors(errs,
                        ValidatePortName(name),
                        ValidatePort(int(port)))
                }
            }
        default:
            errs = appendErrors(errs, fmt.Errorf("unsupported resolution type %s",
                networking.ServiceEntry_Resolution_name[int32(serviceEntry.Resolution)]))
        }

        if serviceEntry.Resolution != networking.ServiceEntry_NONE && len(serviceEntry.Hosts) > 1 {
            canDifferentiate := true
            for _, port := range serviceEntry.Ports {
                p := protocol.Parse(port.Protocol)
                if !p.IsHTTP() && !p.IsTLS() {
                    canDifferentiate = false
                    break
                }
            }

            if !canDifferentiate {
                errs = appendErrors(errs, fmt.Errorf("multiple hosts provided with non-HTTP, non-TLS ports"))
            }
        }

        for _, port := range serviceEntry.Ports {
            errs = appendErrors(errs,
                ValidatePortName(port.Name),
                ValidateProtocol(port.Protocol),
                ValidatePort(int(port.Number)))
        }

        errs = appendErrors(errs, validateExportTo(serviceEntry.ExportTo))
        return
    })

ValidateServiceEntry validates a service entry.

var ValidateServiceRole = registerValidateFunc("ValidateServiceRole",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*rbac.ServiceRole)
        if !ok {
            return errors.New("cannot cast to ServiceRole")
        }
        var errs error
        if len(in.Rules) == 0 {
            errs = appendErrors(errs, fmt.Errorf("at least 1 rule must be specified"))
        }
        for i, rule := range in.Rules {

            sameAttributeKindError := "cannot have both regular and *not* attributes for the same kind (%s) for rule %d"
            if len(rule.Methods) > 0 && len(rule.NotMethods) > 0 {
                errs = appendErrors(errs, fmt.Errorf(sameAttributeKindError, "i.e. methods and not_methods", i))
            }
            if len(rule.Ports) > 0 && len(rule.NotPorts) > 0 {
                errs = appendErrors(errs, fmt.Errorf(sameAttributeKindError, "i.e. ports and not_ports", i))
            }
            if !ValidatePorts(rule.Ports) || !ValidatePorts(rule.NotPorts) {
                errs = appendErrors(errs, fmt.Errorf("at least one port is not in the range of [0, 65535]"))
            }
            for j, constraint := range rule.Constraints {
                if len(constraint.Key) == 0 {
                    errs = appendErrors(errs, fmt.Errorf("key cannot be empty for constraint %d in rule %d", j, i))
                }
                if len(constraint.Values) == 0 {
                    errs = appendErrors(errs, fmt.Errorf("at least 1 value must be specified for constraint %d in rule %d", j, i))
                }
                if hasExistingFirstClassFieldInRole(constraint.Key, rule) {
                    errs = appendErrors(errs, fmt.Errorf("cannot define %s for rule %d because a similar first-class field has been defined", constraint.Key, i))
                }
            }
        }
        return errs
    })

ValidateServiceRole checks that ServiceRole is well-formed.

var ValidateServiceRoleBinding = registerValidateFunc("ValidateServiceRoleBinding",
    func(_, _ string, msg proto.Message) error {
        in, ok := msg.(*rbac.ServiceRoleBinding)
        if !ok {
            return errors.New("cannot cast to ServiceRoleBinding")
        }
        return checkServiceRoleBinding(in)
    })

ValidateServiceRoleBinding checks that ServiceRoleBinding is well-formed.

var ValidateSidecar = registerValidateFunc("ValidateSidecar",
    func(_, _ string, msg proto.Message) (errs error) {
        rule, ok := msg.(*networking.Sidecar)
        if !ok {
            return fmt.Errorf("cannot cast to Sidecar")
        }

        if rule.WorkloadSelector != nil {
            if rule.WorkloadSelector.GetLabels() == nil {
                errs = appendErrors(errs, fmt.Errorf("sidecar: workloadSelector cannot have empty labels"))
            }
        }

        if len(rule.Egress) == 0 {
            return fmt.Errorf("sidecar: missing egress")
        }

        portMap := make(map[uint32]struct{})
        for _, i := range rule.Ingress {
            if i.Port == nil {
                errs = appendErrors(errs, fmt.Errorf("sidecar: port is required for ingress listeners"))
                continue
            }

            bind := i.GetBind()
            errs = appendErrors(errs, validateSidecarIngressPortAndBind(i.Port, bind))

            if _, found := portMap[i.Port.Number]; found {
                errs = appendErrors(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique"))
            }
            portMap[i.Port.Number] = struct{}{}

            if len(i.DefaultEndpoint) == 0 {
                errs = appendErrors(errs, fmt.Errorf("sidecar: default endpoint must be set for all ingress listeners"))
            } else {
                if strings.HasPrefix(i.DefaultEndpoint, UnixAddressPrefix) {
                    errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(i.DefaultEndpoint, UnixAddressPrefix)))
                } else {

                    parts := strings.Split(i.DefaultEndpoint, ":")
                    if len(parts) < 2 {
                        errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>"))
                    } else {
                        if len(parts[0]) > 0 && parts[0] != "127.0.0.1" {
                            errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>"))
                        }

                        port, err := strconv.Atoi(parts[1])
                        if err != nil {
                            errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint port (%s) is not a number: %v", parts[1], err))
                        } else {
                            errs = appendErrors(errs, ValidatePort(port))
                        }
                    }
                }
            }
        }

        portMap = make(map[uint32]struct{})
        udsMap := make(map[string]struct{})
        catchAllEgressListenerFound := false
        for index, i := range rule.Egress {

            if i.Port == nil {
                if !catchAllEgressListenerFound {
                    if index == len(rule.Egress)-1 {
                        catchAllEgressListenerFound = true
                    } else {
                        errs = appendErrors(errs, fmt.Errorf("sidecar: the egress listener with empty port should be the last listener in the list"))
                    }
                } else {
                    errs = appendErrors(errs, fmt.Errorf("sidecar: egress can have only one listener with empty port"))
                    continue
                }
            } else {
                bind := i.GetBind()
                captureMode := i.GetCaptureMode()
                errs = appendErrors(errs, validateSidecarEgressPortBindAndCaptureMode(i.Port, bind, captureMode))

                if i.Port.Number == 0 {
                    if _, found := udsMap[bind]; found {
                        errs = appendErrors(errs, fmt.Errorf("sidecar: unix domain socket values for listeners must be unique"))
                    }
                    udsMap[bind] = struct{}{}
                } else {
                    if _, found := portMap[i.Port.Number]; found {
                        errs = appendErrors(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique"))
                    }
                    portMap[i.Port.Number] = struct{}{}
                }
            }

            if len(i.Hosts) == 0 {
                errs = appendErrors(errs, fmt.Errorf("sidecar: egress listener must contain at least one host"))
            } else {
                for _, hostname := range i.Hosts {
                    errs = appendErrors(errs, validateNamespaceSlashWildcardHostname(hostname, false))
                }
            }
        }

        return
    })

ValidateSidecar checks sidecar config supplied by user

var ValidateSyntheticServiceEntry = registerValidateFunc("ValidateSyntheticServiceEntry",
    func(_, _ string, config proto.Message) (errs error) {
        return ValidateServiceEntry("", "", config)
    })

ValidateSyntheticServiceEntry validates a synthetic service entry.

var ValidateVirtualService = registerValidateFunc("ValidateVirtualService",
    func(_, _ string, msg proto.Message) (errs error) {
        virtualService, ok := msg.(*networking.VirtualService)
        if !ok {
            return errors.New("cannot cast to virtual service")
        }

        appliesToMesh := false
        if len(virtualService.Gateways) == 0 {
            appliesToMesh = true
        }

        errs = appendErrors(errs, validateGatewayNames(virtualService.Gateways))
        for _, gatewayName := range virtualService.Gateways {
            if gatewayName == constants.IstioMeshGateway {
                appliesToMesh = true
                break
            }
        }

        if len(virtualService.Hosts) == 0 {
            errs = appendErrors(errs, fmt.Errorf("virtual service must have at least one host"))
        }

        allHostsValid := true
        for _, virtualHost := range virtualService.Hosts {
            if err := ValidateWildcardDomain(virtualHost); err != nil {
                ipAddr := net.ParseIP(virtualHost)
                if ipAddr == nil {
                    errs = appendErrors(errs, err)
                    allHostsValid = false
                }
            } else if appliesToMesh && virtualHost == "*" {
                errs = appendErrors(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 = appendErrors(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 = appendErrors(errs, errors.New("http, tcp or tls must be provided in virtual service"))
        }
        for _, httpRoute := range virtualService.Http {
            errs = appendErrors(errs, validateHTTPRoute(httpRoute))
        }
        for _, tlsRoute := range virtualService.Tls {
            errs = appendErrors(errs, validateTLSRoute(tlsRoute, virtualService))
        }
        for _, tcpRoute := range virtualService.Tcp {
            errs = appendErrors(errs, validateTCPRoute(tcpRoute))
        }

        errs = appendErrors(errs, validateExportTo(virtualService.ExportTo))
        return
    })

ValidateVirtualService checks that a v1alpha3 route rule is well-formed.

func IsValidateFunc Uses

func IsValidateFunc(name string) bool

IsValidateFunc indicates whether there is a validation function with the given name.

func ValidateConnectTimeout Uses

func ValidateConnectTimeout(timeout *types.Duration) error

ValidateConnectTimeout validates the envoy conncection timeout

func ValidateDatadogCollector Uses

func ValidateDatadogCollector(d *meshconfig.Tracing_Datadog) error

ValidateDatadogCollector validates the configuration for sending envoy spans to Datadog

func ValidateDuration Uses

func ValidateDuration(pd *types.Duration) error

ValidateDuration checks that a proto duration is well-formed

func ValidateDurationGogo Uses

func ValidateDurationGogo(pd *types.Duration) error

ValidateDurationGogo checks that a gogo proto duration is well-formed

func ValidateDurationRange Uses

func ValidateDurationRange(dur, min, max time.Duration) error

ValidateDurationRange verifies range is in specified duration

func ValidateFQDN Uses

func ValidateFQDN(fqdn string) error

ValidateFQDN checks a fully-qualified domain name

func ValidateGogoDuration Uses

func ValidateGogoDuration(in *types.Duration) error

ValidateGogoDuration validates the variant of duration.

func ValidateHTTPHeaderName Uses

func ValidateHTTPHeaderName(name string) error

ValidateHTTPHeaderName validates a header name

func ValidateIPAddress Uses

func ValidateIPAddress(addr string) error

ValidateIPAddress validates that a string in "CIDR notation" or "Dot-decimal notation"

func ValidateIPSubnet Uses

func ValidateIPSubnet(subnet string) error

ValidateIPSubnet checks that a string is in "CIDR notation" or "Dot-decimal notation"

func ValidateLightstepCollector Uses

func ValidateLightstepCollector(ls *meshconfig.Tracing_Lightstep) error

ValidateLightstepCollector validates the configuration for sending envoy spans to LightStep

func ValidateMeshConfig Uses

func ValidateMeshConfig(mesh *meshconfig.MeshConfig) (errs error)

ValidateMeshConfig checks that the mesh config is well-formed

func ValidateMixerAttributes Uses

func ValidateMixerAttributes(msg proto.Message) error

ValidateMixerAttributes checks that Mixer attributes is well-formed.

func ValidateMixerService Uses

func ValidateMixerService(svc *mccpb.IstioService) (errs error)

ValidateMixerService checks for validity of a service reference

func ValidateParentAndDrain Uses

func ValidateParentAndDrain(drainTime, parentShutdown *types.Duration) (errs error)

ValidateParentAndDrain checks that parent and drain durations are valid

func ValidatePercent Uses

func ValidatePercent(val int32) error

ValidatePercent checks that percent is in range

func ValidatePort Uses

func ValidatePort(port int) error

ValidatePort checks that the network port is in range

func ValidatePortName Uses

func ValidatePortName(name string) error

func ValidatePorts Uses

func ValidatePorts(ports []int32) bool

ValidatePorts checks if all ports are in range [0, 65535]

func ValidateProtocol Uses

func ValidateProtocol(protocolStr string) error

func ValidateProtocolDetectionTimeout Uses

func ValidateProtocolDetectionTimeout(timeout *types.Duration) error

ValidateProtocolDetectionTimeout validates the envoy protocol detection timeout

func ValidateProxyAddress Uses

func ValidateProxyAddress(hostAddr string) error

ValidateProxyAddress checks that a network address is well-formed

func ValidateProxyConfig Uses

func ValidateProxyConfig(config *meshconfig.ProxyConfig) (errs error)

ValidateProxyConfig checks that the mesh config is well-formed

func ValidateUnixAddress Uses

func ValidateUnixAddress(addr string) error

ValidateUnixAddress validates that the string is a valid unix domain socket path.

func ValidateWildcardDomain Uses

func ValidateWildcardDomain(domain string) error

ValidateWildcardDomain checks that a domain is a valid FQDN, but also allows wildcard prefixes.

func ValidateZipkinCollector Uses

func ValidateZipkinCollector(z *meshconfig.Tracing_Zipkin) error

ValidateZipkinCollector validates the configuration for sending envoy spans to Zipkin

type ValidateFunc Uses

type ValidateFunc func(name, namespace string, config proto.Message) error

Validate defines a validation func for an API proto.

func GetValidateFunc Uses

func GetValidateFunc(name string) ValidateFunc

GetValidateFunc returns the validation function with the given name, or null if it does not exist.

Package validation imports 30 packages (graph) and is imported by 19 packages. Updated 2020-01-29. Refresh now. Tools for package owners.