1// Copyright 2017 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package validation
16
17import (
18	"errors"
19	"fmt"
20	"net"
21	"net/http"
22	"path"
23	"regexp"
24	"strconv"
25	"strings"
26	"time"
27
28	xdsUtil "github.com/envoyproxy/go-control-plane/pkg/wellknown"
29	"github.com/gogo/protobuf/proto"
30	"github.com/gogo/protobuf/types"
31	"github.com/hashicorp/go-multierror"
32
33	authn "istio.io/api/authentication/v1alpha1"
34	meshconfig "istio.io/api/mesh/v1alpha1"
35	mpb "istio.io/api/mixer/v1"
36	mccpb "istio.io/api/mixer/v1/config/client"
37	networking "istio.io/api/networking/v1alpha3"
38	rbac "istio.io/api/rbac/v1alpha1"
39	security_beta "istio.io/api/security/v1beta1"
40	type_beta "istio.io/api/type/v1beta1"
41	"istio.io/pkg/log"
42
43	"istio.io/istio/pilot/pkg/features"
44	"istio.io/istio/pkg/config/constants"
45	"istio.io/istio/pkg/config/gateway"
46	"istio.io/istio/pkg/config/host"
47	"istio.io/istio/pkg/config/labels"
48	"istio.io/istio/pkg/config/protocol"
49	"istio.io/istio/pkg/config/security"
50	"istio.io/istio/pkg/config/visibility"
51	"istio.io/istio/pkg/config/xds"
52)
53
54// Constants for duration fields
55const (
56	connectTimeoutMax = time.Second * 30
57	connectTimeoutMin = time.Millisecond
58
59	drainTimeMax          = time.Hour
60	parentShutdownTimeMax = time.Hour
61
62	// UnixAddressPrefix is the prefix used to indicate an address is for a Unix Domain socket. It is used in
63	// ServiceEntry.Endpoint.Address message.
64	UnixAddressPrefix = "unix://"
65)
66
67var (
68	// envoy supported retry on header values
69	supportedRetryOnPolicies = map[string]bool{
70		// 'x-envoy-retry-on' supported policies:
71		// https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter.html#x-envoy-retry-on
72		"5xx":                    true,
73		"gateway-error":          true,
74		"reset":                  true,
75		"connect-failure":        true,
76		"retriable-4xx":          true,
77		"refused-stream":         true,
78		"retriable-status-codes": true,
79
80		// 'x-envoy-retry-grpc-on' supported policies:
81		// https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on
82		"cancelled":          true,
83		"deadline-exceeded":  true,
84		"internal":           true,
85		"resource-exhausted": true,
86		"unavailable":        true,
87	}
88
89	// golang supported methods: https://golang.org/src/net/http/method.go
90	supportedMethods = map[string]bool{
91		http.MethodGet:     true,
92		http.MethodHead:    true,
93		http.MethodPost:    true,
94		http.MethodPut:     true,
95		http.MethodPatch:   true,
96		http.MethodDelete:  true,
97		http.MethodConnect: true,
98		http.MethodOptions: true,
99		http.MethodTrace:   true,
100	}
101
102	scope = log.RegisterScope("validation", "CRD validation debugging", 0)
103
104	_ ValidateFunc = EmptyValidate
105
106	// EmptyValidate is a Validate that does nothing and returns no error.
107	EmptyValidate = registerValidateFunc("EmptyValidate",
108		func(string, string, proto.Message) error {
109			return nil
110		})
111
112	validateFuncs = make(map[string]ValidateFunc)
113)
114
115// Validate defines a validation func for an API proto.
116type ValidateFunc func(name, namespace string, config proto.Message) error
117
118// IsValidateFunc indicates whether there is a validation function with the given name.
119func IsValidateFunc(name string) bool {
120	return GetValidateFunc(name) != nil
121}
122
123// GetValidateFunc returns the validation function with the given name, or null if it does not exist.
124func GetValidateFunc(name string) ValidateFunc {
125	return validateFuncs[name]
126}
127
128func registerValidateFunc(name string, f ValidateFunc) ValidateFunc {
129	validateFuncs[name] = f
130	return f
131}
132
133// ValidatePort checks that the network port is in range
134func ValidatePort(port int) error {
135	if 1 <= port && port <= 65535 {
136		return nil
137	}
138	return fmt.Errorf("port number %d must be in the range 1..65535", port)
139}
140
141// ValidatePorts checks if all ports are in range [0, 65535]
142func ValidatePorts(ports []int32) bool {
143	for _, port := range ports {
144		if ValidatePort(int(port)) != nil {
145			return false
146		}
147	}
148	return true
149}
150
151// ValidateFQDN checks a fully-qualified domain name
152func ValidateFQDN(fqdn string) error {
153	if err := checkDNS1123Preconditions(fqdn); err != nil {
154		return err
155	}
156	return validateDNS1123Labels(fqdn)
157}
158
159// ValidateWildcardDomain checks that a domain is a valid FQDN, but also allows wildcard prefixes.
160func ValidateWildcardDomain(domain string) error {
161	if err := checkDNS1123Preconditions(domain); err != nil {
162		return err
163	}
164	// We only allow wildcards in the first label; split off the first label (parts[0]) from the rest of the host (parts[1])
165	parts := strings.SplitN(domain, ".", 2)
166	if !labels.IsWildcardDNS1123Label(parts[0]) {
167		return fmt.Errorf("domain name %q invalid (label %q invalid)", domain, parts[0])
168	} else if len(parts) > 1 {
169		return validateDNS1123Labels(parts[1])
170	}
171	return nil
172}
173
174// encapsulates DNS 1123 checks common to both wildcarded hosts and FQDNs
175func checkDNS1123Preconditions(name string) error {
176	if len(name) > 255 {
177		return fmt.Errorf("domain name %q too long (max 255)", name)
178	}
179	if len(name) == 0 {
180		return fmt.Errorf("empty domain name not allowed")
181	}
182	return nil
183}
184
185func validateDNS1123Labels(domain string) error {
186	parts := strings.Split(domain, ".")
187	topLevelDomain := parts[len(parts)-1]
188	if _, err := strconv.Atoi(topLevelDomain); err == nil {
189		return fmt.Errorf("domain name %q invalid (top level domain %q cannot be all-numeric)", domain, topLevelDomain)
190	}
191	for i, label := range parts {
192		// Allow the last part to be empty, for unambiguous names like `istio.io.`
193		if i == len(parts)-1 && label == "" {
194			return nil
195		}
196		if !labels.IsDNS1123Label(label) {
197			return fmt.Errorf("domain name %q invalid (label %q invalid)", domain, label)
198		}
199	}
200	return nil
201}
202
203// ValidateMixerService checks for validity of a service reference
204func ValidateMixerService(svc *mccpb.IstioService) (errs error) {
205	if svc.Name == "" && svc.Service == "" {
206		errs = multierror.Append(errs, errors.New("name or service is mandatory for a service reference"))
207	} else if svc.Service != "" && svc.Name != "" {
208		errs = multierror.Append(errs, errors.New("specify either name or service, not both"))
209	} else if svc.Service != "" {
210		if svc.Namespace != "" {
211			errs = multierror.Append(errs, errors.New("namespace is not valid when service is provided"))
212		}
213		if svc.Domain != "" {
214			errs = multierror.Append(errs, errors.New("domain is not valid when service is provided"))
215		}
216	} else if svc.Name != "" {
217		if !labels.IsDNS1123Label(svc.Name) {
218			errs = multierror.Append(errs, fmt.Errorf("name %q must be a valid label", svc.Name))
219		}
220	}
221
222	if svc.Namespace != "" && !labels.IsDNS1123Label(svc.Namespace) {
223		errs = multierror.Append(errs, fmt.Errorf("namespace %q must be a valid label", svc.Namespace))
224	}
225
226	if svc.Domain != "" {
227		if err := ValidateFQDN(svc.Domain); err != nil {
228			errs = multierror.Append(errs, err)
229		}
230	}
231
232	if err := labels.Instance(svc.Labels).Validate(); err != nil {
233		errs = multierror.Append(errs, err)
234	}
235
236	return
237}
238
239// ValidateHTTPHeaderName validates a header name
240func ValidateHTTPHeaderName(name string) error {
241	if name == "" {
242		return fmt.Errorf("header name cannot be empty")
243	}
244	return nil
245}
246
247// ValidatePercent checks that percent is in range
248func ValidatePercent(val int32) error {
249	if val < 0 || val > 100 {
250		return fmt.Errorf("percentage %v is not in range 0..100", val)
251	}
252	return nil
253}
254
255// validatePercentage checks if the specified fractional percentage is valid.
256func validatePercentage(percentage *networking.Percent) error {
257	if percentage != nil {
258		if percentage.Value < 0.0 || percentage.Value > 100.0 || (percentage.Value > 0.0 && percentage.Value < 0.0001) {
259			return fmt.Errorf("percentage %v is neither 0.0, nor in range [0.0001, 100.0]", percentage.Value)
260		}
261	}
262	return nil
263}
264
265// ValidateIPSubnet checks that a string is in "CIDR notation" or "Dot-decimal notation"
266func ValidateIPSubnet(subnet string) error {
267	// We expect a string in "CIDR notation" or "Dot-decimal notation"
268	// E.g., a.b.c.d/xx form or just a.b.c.d or 2001:1::1/64
269	if strings.Count(subnet, "/") == 1 {
270		// We expect a string in "CIDR notation", i.e. a.b.c.d/xx or 2001:1::1/64 form
271		ip, _, err := net.ParseCIDR(subnet)
272		if err != nil {
273			return fmt.Errorf("%v is not a valid CIDR block", subnet)
274		}
275		if ip.To4() == nil && ip.To16() == nil {
276			return fmt.Errorf("%v is not a valid IPv4 or IPv6 address", subnet)
277		}
278		return nil
279	}
280	return ValidateIPAddress(subnet)
281}
282
283// ValidateIPAddress validates that a string in "CIDR notation" or "Dot-decimal notation"
284func ValidateIPAddress(addr string) error {
285	ip := net.ParseIP(addr)
286	if ip == nil {
287		return fmt.Errorf("%v is not a valid IP", addr)
288	}
289
290	return nil
291}
292
293// ValidateUnixAddress validates that the string is a valid unix domain socket path.
294func ValidateUnixAddress(addr string) error {
295	if len(addr) == 0 {
296		return errors.New("unix address must not be empty")
297	}
298
299	// Allow unix abstract domain sockets whose names start with @
300	if strings.HasPrefix(addr, "@") {
301		return nil
302	}
303
304	// Note that we use path, not path/filepath even though a domain socket path is a file path.  We don't want the
305	// Pilot output to depend on which OS Pilot is run on, so we always use Unix-style forward slashes.
306	if !path.IsAbs(addr) || strings.HasSuffix(addr, "/") {
307		return fmt.Errorf("%s is not an absolute path to a file", addr)
308	}
309	return nil
310}
311
312// ValidateGateway checks gateway specifications
313var ValidateGateway = registerValidateFunc("ValidateGateway",
314	func(name, _ string, msg proto.Message) (errs error) {
315		// Gateway name must conform to the DNS label format (no dots)
316		if !labels.IsDNS1123Label(name) {
317			errs = appendErrors(errs, fmt.Errorf("invalid gateway name: %q", name))
318		}
319		value, ok := msg.(*networking.Gateway)
320		if !ok {
321			errs = appendErrors(errs, fmt.Errorf("cannot cast to gateway: %#v", msg))
322			return
323		}
324
325		if len(value.Servers) == 0 {
326			errs = appendErrors(errs, fmt.Errorf("gateway must have at least one server"))
327		} else {
328			for _, server := range value.Servers {
329				errs = appendErrors(errs, validateServer(server))
330			}
331		}
332
333		// Ensure unique port names
334		portNames := make(map[string]bool)
335
336		for _, s := range value.Servers {
337			if s.Port != nil {
338				if portNames[s.Port.Name] {
339					errs = appendErrors(errs, fmt.Errorf("port names in servers must be unique: duplicate name %s", s.Port.Name))
340				}
341				portNames[s.Port.Name] = true
342			}
343		}
344
345		return errs
346	})
347
348func validateServer(server *networking.Server) (errs error) {
349	if len(server.Hosts) == 0 {
350		errs = appendErrors(errs, fmt.Errorf("server config must contain at least one host"))
351	} else {
352		for _, hostname := range server.Hosts {
353			errs = appendErrors(errs, validateNamespaceSlashWildcardHostname(hostname, true))
354		}
355	}
356	portErr := validateServerPort(server.Port)
357	if portErr != nil {
358		errs = appendErrors(errs, portErr)
359	}
360	errs = appendErrors(errs, validateTLSOptions(server.Tls))
361
362	// If port is HTTPS or TLS, make sure that server has TLS options
363	if portErr == nil {
364		p := protocol.Parse(server.Port.Protocol)
365		if p.IsTLS() && server.Tls == nil {
366			errs = appendErrors(errs, fmt.Errorf("server must have TLS settings for HTTPS/TLS protocols"))
367		} else if !p.IsTLS() && server.Tls != nil {
368			// only tls redirect is allowed if this is a HTTP server
369			if p.IsHTTP() {
370				if !gateway.IsPassThroughServer(server) ||
371					server.Tls.CaCertificates != "" || server.Tls.PrivateKey != "" || server.Tls.ServerCertificate != "" {
372					errs = appendErrors(errs, fmt.Errorf("server cannot have TLS settings for plain text HTTP ports"))
373				}
374			} else {
375				errs = appendErrors(errs, fmt.Errorf("server cannot have TLS settings for non HTTPS/TLS ports"))
376			}
377		}
378	}
379	return errs
380}
381
382func validateServerPort(port *networking.Port) (errs error) {
383	if port == nil {
384		return appendErrors(errs, fmt.Errorf("port is required"))
385	}
386	if protocol.Parse(port.Protocol) == protocol.Unsupported {
387		errs = appendErrors(errs, fmt.Errorf("invalid protocol %q, supported protocols are HTTP, HTTP2, GRPC, GRPC-WEB, MONGO, REDIS, MYSQL, TCP", port.Protocol))
388	}
389	if port.Number > 0 {
390		errs = appendErrors(errs, ValidatePort(int(port.Number)))
391	}
392
393	if port.Name == "" {
394		errs = appendErrors(errs, fmt.Errorf("port name must be set: %v", port))
395	}
396	return
397}
398
399func validateTLSOptions(tls *networking.ServerTLSSettings) (errs error) {
400	if tls == nil {
401		// no tls config at all is valid
402		return
403	}
404
405	if tls.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL {
406		// ISTIO_MUTUAL TLS mode uses either SDS or default certificate mount paths
407		// therefore, we should fail validation if other TLS fields are set
408		if tls.ServerCertificate != "" {
409			errs = appendErrors(errs, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated server certificate"))
410		}
411		if tls.PrivateKey != "" {
412			errs = appendErrors(errs, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated private key"))
413		}
414		if tls.CaCertificates != "" {
415			errs = appendErrors(errs, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated CA bundle"))
416		}
417
418		return
419	}
420
421	if (tls.Mode == networking.ServerTLSSettings_SIMPLE || tls.Mode == networking.ServerTLSSettings_MUTUAL) && tls.CredentialName != "" {
422		// If tls mode is SIMPLE or MUTUAL, and CredentialName is specified, credentials are fetched
423		// remotely. ServerCertificate and CaCertificates fields are not required.
424		return
425	}
426	if tls.Mode == networking.ServerTLSSettings_SIMPLE {
427		if tls.ServerCertificate == "" {
428			errs = appendErrors(errs, fmt.Errorf("SIMPLE TLS requires a server certificate"))
429		}
430		if tls.PrivateKey == "" {
431			errs = appendErrors(errs, fmt.Errorf("SIMPLE TLS requires a private key"))
432		}
433	} else if tls.Mode == networking.ServerTLSSettings_MUTUAL {
434		if tls.ServerCertificate == "" {
435			errs = appendErrors(errs, fmt.Errorf("MUTUAL TLS requires a server certificate"))
436		}
437		if tls.PrivateKey == "" {
438			errs = appendErrors(errs, fmt.Errorf("MUTUAL TLS requires a private key"))
439		}
440		if tls.CaCertificates == "" {
441			errs = appendErrors(errs, fmt.Errorf("MUTUAL TLS requires a client CA bundle"))
442		}
443	}
444	return
445}
446
447// ValidateDestinationRule checks proxy policies
448var ValidateDestinationRule = registerValidateFunc("ValidateDestinationRule",
449	func(_, _ string, msg proto.Message) (errs error) {
450		rule, ok := msg.(*networking.DestinationRule)
451		if !ok {
452			return fmt.Errorf("cannot cast to destination rule")
453		}
454
455		errs = appendErrors(errs,
456			ValidateWildcardDomain(rule.Host),
457			validateTrafficPolicy(rule.TrafficPolicy))
458
459		for _, subset := range rule.Subsets {
460			errs = appendErrors(errs, validateSubset(subset))
461		}
462
463		errs = appendErrors(errs, validateExportTo(rule.ExportTo))
464		return
465	})
466
467func validateExportTo(exportTo []string) (errs error) {
468	if len(exportTo) > 0 {
469		if len(exportTo) > 1 {
470			errs = appendErrors(errs, fmt.Errorf("exportTo should have only one entry (. or *) in the current release"))
471		} else {
472			errs = appendErrors(errs, visibility.Instance(exportTo[0]).Validate())
473		}
474	}
475
476	return
477}
478
479func validateAlphaWorkloadSelector(selector *networking.WorkloadSelector) error {
480	var errs error
481	if selector != nil {
482		for k, v := range selector.Labels {
483			if k == "" {
484				errs = appendErrors(errs,
485					fmt.Errorf("empty key is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v)))
486			}
487			if strings.Contains(k, "*") || strings.Contains(v, "*") {
488				errs = appendErrors(errs,
489					fmt.Errorf("wildcard is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v)))
490			}
491		}
492	}
493
494	return errs
495}
496
497// ValidateEnvoyFilter checks envoy filter config supplied by user
498var ValidateEnvoyFilter = registerValidateFunc("ValidateEnvoyFilter",
499	func(_, _ string, msg proto.Message) (errs error) {
500		rule, ok := msg.(*networking.EnvoyFilter)
501		if !ok {
502			return fmt.Errorf("cannot cast to Envoy filter")
503		}
504
505		if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil {
506			return err
507		}
508
509		for _, cp := range rule.ConfigPatches {
510			if cp.ApplyTo == networking.EnvoyFilter_INVALID {
511				errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing applyTo")) // nolint: golint,stylecheck
512				continue
513			}
514			if cp.Patch == nil {
515				errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing patch")) // nolint: golint,stylecheck
516				continue
517			}
518			if cp.Patch.Operation == networking.EnvoyFilter_Patch_INVALID {
519				errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing patch operation")) // nolint: golint,stylecheck
520				continue
521			}
522			if cp.Patch.Operation != networking.EnvoyFilter_Patch_REMOVE && cp.Patch.Value == nil {
523				errs = appendErrors(errs, fmt.Errorf("Envoy filter: missing patch value for non-remove operation")) // nolint: golint,stylecheck
524				continue
525			}
526
527			// ensure that the supplied regex for proxy version compiles
528			if cp.Match != nil && cp.Match.Proxy != nil && cp.Match.Proxy.ProxyVersion != "" {
529				if _, err := regexp.Compile(cp.Match.Proxy.ProxyVersion); err != nil {
530					errs = appendErrors(errs, fmt.Errorf("Envoy filter: invalid regex for proxy version, [%v]", err)) // nolint: golint,stylecheck
531					continue
532				}
533			}
534			// ensure that applyTo, match and patch all line up
535			switch cp.ApplyTo {
536			case networking.EnvoyFilter_LISTENER,
537				networking.EnvoyFilter_FILTER_CHAIN,
538				networking.EnvoyFilter_NETWORK_FILTER,
539				networking.EnvoyFilter_HTTP_FILTER:
540				if cp.Match != nil && cp.Match.ObjectTypes != nil {
541					if cp.Match.GetListener() == nil {
542						errs = appendErrors(errs, fmt.Errorf("Envoy filter: applyTo for listener class objects cannot have non listener match")) // nolint: golint,stylecheck
543						continue
544					}
545					listenerMatch := cp.Match.GetListener()
546					if listenerMatch.FilterChain != nil {
547						if listenerMatch.FilterChain.Filter != nil {
548							// filter names are required if network filter matches are being made
549							if listenerMatch.FilterChain.Filter.Name == "" {
550								errs = appendErrors(errs, fmt.Errorf("Envoy filter: filter match has no name to match on")) // nolint: golint,stylecheck
551								continue
552							} else if listenerMatch.FilterChain.Filter.SubFilter != nil {
553								// sub filter match is supported only for applyTo HTTP_FILTER
554								if cp.ApplyTo != networking.EnvoyFilter_HTTP_FILTER {
555									errs = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match can be used with applyTo HTTP_FILTER only")) // nolint: golint,stylecheck
556									continue
557								}
558								// sub filter match requires the network filter to match to envoy http connection manager
559								if listenerMatch.FilterChain.Filter.Name != xdsUtil.HTTPConnectionManager {
560									errs = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match requires filter match with %s", // nolint: golint,stylecheck
561										xdsUtil.HTTPConnectionManager))
562									continue
563								}
564								if listenerMatch.FilterChain.Filter.SubFilter.Name == "" {
565									errs = appendErrors(errs, fmt.Errorf("Envoy filter: subfilter match has no name to match on")) // nolint: golint,stylecheck
566									continue
567								}
568							}
569						}
570					}
571				}
572			case networking.EnvoyFilter_ROUTE_CONFIGURATION, networking.EnvoyFilter_VIRTUAL_HOST, networking.EnvoyFilter_HTTP_ROUTE:
573				if cp.Match != nil && cp.Match.ObjectTypes != nil {
574					if cp.Match.GetRouteConfiguration() == nil {
575						errs = appendErrors(errs,
576							fmt.Errorf("Envoy filter: applyTo for http route class objects cannot have non route configuration match")) // nolint: golint,stylecheck
577					}
578				}
579
580			case networking.EnvoyFilter_CLUSTER:
581				if cp.Match != nil && cp.Match.ObjectTypes != nil {
582					if cp.Match.GetCluster() == nil {
583						errs = appendErrors(errs, fmt.Errorf("Envoy filter: applyTo for cluster class objects cannot have non cluster match")) // nolint: golint,stylecheck
584					}
585				}
586			}
587			// ensure that the struct is valid
588			if _, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value); err != nil {
589				errs = appendErrors(errs, err)
590			}
591		}
592
593		return
594	})
595
596// validates that hostname in ns/<hostname> is a valid hostname according to
597// API specs
598func validateSidecarOrGatewayHostnamePart(hostname string, isGateway bool) (errs error) {
599	// short name hosts are not allowed
600	if hostname != "*" && !strings.Contains(hostname, ".") {
601		errs = appendErrors(errs, fmt.Errorf("short names (non FQDN) are not allowed"))
602	}
603
604	if err := ValidateWildcardDomain(hostname); err != nil {
605		if !isGateway {
606			errs = appendErrors(errs, err)
607		}
608
609		// Gateway allows IP as the host string, as well
610		ipAddr := net.ParseIP(hostname)
611		if ipAddr == nil {
612			errs = appendErrors(errs, err)
613		}
614	}
615	return
616}
617
618func validateNamespaceSlashWildcardHostname(hostname string, isGateway bool) (errs error) {
619	parts := strings.SplitN(hostname, "/", 2)
620	if len(parts) != 2 {
621		if isGateway {
622			// Old style host in the gateway
623			return validateSidecarOrGatewayHostnamePart(hostname, true)
624		}
625		errs = appendErrors(errs, fmt.Errorf("host must be of form namespace/dnsName"))
626		return
627	}
628
629	if len(parts[0]) == 0 || len(parts[1]) == 0 {
630		errs = appendErrors(errs, fmt.Errorf("config namespace and dnsName in host entry cannot be empty"))
631	}
632
633	if !isGateway {
634		// namespace can be * or . or ~ or a valid DNS label in sidecars
635		if parts[0] != "*" && parts[0] != "." && parts[0] != "~" {
636			if !labels.IsDNS1123Label(parts[0]) {
637				errs = appendErrors(errs, fmt.Errorf("invalid namespace value %q in sidecar", parts[0]))
638			}
639		}
640	} else {
641		// namespace can be * or . or a valid DNS label in gateways
642		if parts[0] != "*" && parts[0] != "." {
643			if !labels.IsDNS1123Label(parts[0]) {
644				errs = appendErrors(errs, fmt.Errorf("invalid namespace value %q in gateway", parts[0]))
645			}
646		}
647	}
648	errs = appendErrors(errs, validateSidecarOrGatewayHostnamePart(parts[1], isGateway))
649	return
650}
651
652// ValidateSidecar checks sidecar config supplied by user
653var ValidateSidecar = registerValidateFunc("ValidateSidecar",
654	func(_, _ string, msg proto.Message) (errs error) {
655		rule, ok := msg.(*networking.Sidecar)
656		if !ok {
657			return fmt.Errorf("cannot cast to Sidecar")
658		}
659
660		if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil {
661			return err
662		}
663
664		if len(rule.Egress) == 0 {
665			return fmt.Errorf("sidecar: missing egress")
666		}
667
668		portMap := make(map[uint32]struct{})
669		for _, i := range rule.Ingress {
670			if i.Port == nil {
671				errs = appendErrors(errs, fmt.Errorf("sidecar: port is required for ingress listeners"))
672				continue
673			}
674
675			bind := i.GetBind()
676			errs = appendErrors(errs, validateSidecarIngressPortAndBind(i.Port, bind))
677
678			if _, found := portMap[i.Port.Number]; found {
679				errs = appendErrors(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique"))
680			}
681			portMap[i.Port.Number] = struct{}{}
682
683			if len(i.DefaultEndpoint) == 0 {
684				errs = appendErrors(errs, fmt.Errorf("sidecar: default endpoint must be set for all ingress listeners"))
685			} else {
686				if strings.HasPrefix(i.DefaultEndpoint, UnixAddressPrefix) {
687					errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(i.DefaultEndpoint, UnixAddressPrefix)))
688				} else {
689					// format should be 127.0.0.1:port or :port
690					parts := strings.Split(i.DefaultEndpoint, ":")
691					if len(parts) < 2 {
692						errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>"))
693					} else {
694						if len(parts[0]) > 0 && parts[0] != "127.0.0.1" {
695							errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:<port>"))
696						}
697
698						port, err := strconv.Atoi(parts[1])
699						if err != nil {
700							errs = appendErrors(errs, fmt.Errorf("sidecar: defaultEndpoint port (%s) is not a number: %v", parts[1], err))
701						} else {
702							errs = appendErrors(errs, ValidatePort(port))
703						}
704					}
705				}
706			}
707		}
708
709		portMap = make(map[uint32]struct{})
710		udsMap := make(map[string]struct{})
711		catchAllEgressListenerFound := false
712		for index, i := range rule.Egress {
713			// there can be only one catch all egress listener with empty port, and it should be the last listener.
714			if i.Port == nil {
715				if !catchAllEgressListenerFound {
716					if index == len(rule.Egress)-1 {
717						catchAllEgressListenerFound = true
718					} else {
719						errs = appendErrors(errs, fmt.Errorf("sidecar: the egress listener with empty port should be the last listener in the list"))
720					}
721				} else {
722					errs = appendErrors(errs, fmt.Errorf("sidecar: egress can have only one listener with empty port"))
723					continue
724				}
725			} else {
726				bind := i.GetBind()
727				captureMode := i.GetCaptureMode()
728				errs = appendErrors(errs, validateSidecarEgressPortBindAndCaptureMode(i.Port, bind, captureMode))
729
730				if i.Port.Number == 0 {
731					if _, found := udsMap[bind]; found {
732						errs = appendErrors(errs, fmt.Errorf("sidecar: unix domain socket values for listeners must be unique"))
733					}
734					udsMap[bind] = struct{}{}
735				} else {
736					if _, found := portMap[i.Port.Number]; found {
737						errs = appendErrors(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique"))
738					}
739					portMap[i.Port.Number] = struct{}{}
740				}
741			}
742
743			// validate that the hosts field is a slash separated value
744			// of form ns1/host, or */host, or */*, or ns1/*, or ns1/*.example.com
745			if len(i.Hosts) == 0 {
746				errs = appendErrors(errs, fmt.Errorf("sidecar: egress listener must contain at least one host"))
747			} else {
748				for _, hostname := range i.Hosts {
749					errs = appendErrors(errs, validateNamespaceSlashWildcardHostname(hostname, false))
750				}
751			}
752
753		}
754
755		errs = appendErrors(errs, validateSidecarOutboundTrafficPolicy(rule.OutboundTrafficPolicy))
756
757		return
758	})
759
760func validateSidecarOutboundTrafficPolicy(tp *networking.OutboundTrafficPolicy) (errs error) {
761	if tp == nil {
762		return
763	}
764	mode := tp.GetMode()
765	if tp.EgressProxy != nil {
766		if mode != networking.OutboundTrafficPolicy_ALLOW_ANY {
767			errs = appendErrors(errs, fmt.Errorf("sidecar: egress_proxy must be set only with ALLOW_ANY outbound_traffic_policy mode"))
768			return
769		}
770
771		errs = appendErrors(errs, ValidateFQDN(tp.EgressProxy.GetHost()))
772
773		if tp.EgressProxy.Port == nil {
774			errs = appendErrors(errs, fmt.Errorf("sidecar: egress_proxy port must be non-nil"))
775			return
776		}
777		errs = appendErrors(errs, validateDestination(tp.EgressProxy))
778	}
779	return
780}
781
782func validateSidecarEgressPortBindAndCaptureMode(port *networking.Port, bind string,
783	captureMode networking.CaptureMode) (errs error) {
784
785	// Port name is optional. Validate if exists.
786	if len(port.Name) > 0 {
787		errs = appendErrors(errs, ValidatePortName(port.Name))
788	}
789
790	// Handle Unix domain sockets
791	if port.Number == 0 {
792		// require bind to be a unix domain socket
793		errs = appendErrors(errs,
794			ValidateProtocol(port.Protocol))
795
796		if !strings.HasPrefix(bind, UnixAddressPrefix) {
797			errs = appendErrors(errs, fmt.Errorf("sidecar: ports with 0 value must have a unix domain socket bind address"))
798		} else {
799			errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(bind, UnixAddressPrefix)))
800		}
801
802		if captureMode != networking.CaptureMode_DEFAULT && captureMode != networking.CaptureMode_NONE {
803			errs = appendErrors(errs, fmt.Errorf("sidecar: captureMode must be DEFAULT/NONE for unix domain socket listeners"))
804		}
805	} else {
806		errs = appendErrors(errs,
807			ValidateProtocol(port.Protocol),
808			ValidatePort(int(port.Number)))
809
810		if len(bind) != 0 {
811			errs = appendErrors(errs, ValidateIPAddress(bind))
812		}
813	}
814
815	return
816}
817
818func validateSidecarIngressPortAndBind(port *networking.Port, bind string) (errs error) {
819
820	// Port name is optional. Validate if exists.
821	if len(port.Name) > 0 {
822		errs = appendErrors(errs, ValidatePortName(port.Name))
823	}
824
825	errs = appendErrors(errs,
826		ValidateProtocol(port.Protocol),
827		ValidatePort(int(port.Number)))
828
829	if len(bind) != 0 {
830		errs = appendErrors(errs, ValidateIPAddress(bind))
831	}
832
833	return
834}
835
836func validateTrafficPolicy(policy *networking.TrafficPolicy) error {
837	if policy == nil {
838		return nil
839	}
840	if policy.OutlierDetection == nil && policy.ConnectionPool == nil &&
841		policy.LoadBalancer == nil && policy.Tls == nil && policy.PortLevelSettings == nil {
842		return fmt.Errorf("traffic policy must have at least one field")
843	}
844
845	return appendErrors(validateOutlierDetection(policy.OutlierDetection),
846		validateConnectionPool(policy.ConnectionPool),
847		validateLoadBalancer(policy.LoadBalancer),
848		validateTLS(policy.Tls), validatePortTrafficPolicies(policy.PortLevelSettings))
849}
850
851func validateOutlierDetection(outlier *networking.OutlierDetection) (errs error) {
852	if outlier == nil {
853		return
854	}
855
856	if outlier.BaseEjectionTime != nil {
857		errs = appendErrors(errs, ValidateDurationGogo(outlier.BaseEjectionTime))
858	}
859	if outlier.ConsecutiveErrors < 0 {
860		errs = appendErrors(errs, fmt.Errorf("outlier detection consecutive errors cannot be negative"))
861	}
862	if outlier.Consecutive_5XxErrors != nil || outlier.ConsecutiveGatewayErrors != nil {
863		// ConsecutiveErrors is deprecated for Consecutive_5XxErrors and
864		// ConsecutiveGatewayErrors; they should not be set at the same time.
865		if outlier.ConsecutiveErrors > 0 {
866			errs = appendErrors(errs, fmt.Errorf("consecutive_errors should not be set with consecutive_5xx_errors or consecutive_gateway_errors"))
867		}
868	}
869	if outlier.Interval != nil {
870		errs = appendErrors(errs, ValidateDurationGogo(outlier.Interval))
871	}
872	errs = appendErrors(errs, ValidatePercent(outlier.MaxEjectionPercent), ValidatePercent(outlier.MinHealthPercent))
873
874	return
875}
876
877func validateConnectionPool(settings *networking.ConnectionPoolSettings) (errs error) {
878	if settings == nil {
879		return
880	}
881	if settings.Http == nil && settings.Tcp == nil {
882		return fmt.Errorf("connection pool must have at least one field")
883	}
884
885	if httpSettings := settings.Http; httpSettings != nil {
886		if httpSettings.Http1MaxPendingRequests < 0 {
887			errs = appendErrors(errs, fmt.Errorf("http1 max pending requests must be non-negative"))
888		}
889		if httpSettings.Http2MaxRequests < 0 {
890			errs = appendErrors(errs, fmt.Errorf("http2 max requests must be non-negative"))
891		}
892		if httpSettings.MaxRequestsPerConnection < 0 {
893			errs = appendErrors(errs, fmt.Errorf("max requests per connection must be non-negative"))
894		}
895		if httpSettings.MaxRetries < 0 {
896			errs = appendErrors(errs, fmt.Errorf("max retries must be non-negative"))
897		}
898		if httpSettings.IdleTimeout != nil {
899			errs = appendErrors(errs, ValidateDurationGogo(httpSettings.IdleTimeout))
900		}
901	}
902
903	if tcp := settings.Tcp; tcp != nil {
904		if tcp.MaxConnections < 0 {
905			errs = appendErrors(errs, fmt.Errorf("max connections must be non-negative"))
906		}
907		if tcp.ConnectTimeout != nil {
908			errs = appendErrors(errs, ValidateDurationGogo(tcp.ConnectTimeout))
909		}
910	}
911
912	return
913}
914
915func validateLoadBalancer(settings *networking.LoadBalancerSettings) (errs error) {
916	if settings == nil {
917		return
918	}
919
920	// simple load balancing is always valid
921
922	consistentHash := settings.GetConsistentHash()
923	if consistentHash != nil {
924		httpCookie := consistentHash.GetHttpCookie()
925		if httpCookie != nil {
926			if httpCookie.Name == "" {
927				errs = appendErrors(errs, fmt.Errorf("name required for HttpCookie"))
928			}
929			if httpCookie.Ttl == nil {
930				errs = appendErrors(errs, fmt.Errorf("ttl required for HttpCookie"))
931			}
932		}
933	}
934	if err := validateLocalityLbSetting(settings.LocalityLbSetting); err != nil {
935		errs = multierror.Append(errs, err)
936	}
937	return
938}
939
940func validateTLS(settings *networking.ClientTLSSettings) (errs error) {
941	if settings == nil {
942		return
943	}
944
945	if settings.Mode == networking.ClientTLSSettings_MUTUAL {
946		if settings.ClientCertificate == "" {
947			errs = appendErrors(errs, fmt.Errorf("client certificate required for mutual tls"))
948		}
949		if settings.PrivateKey == "" {
950			errs = appendErrors(errs, fmt.Errorf("private key required for mutual tls"))
951		}
952	}
953
954	return
955}
956
957func validateSubset(subset *networking.Subset) error {
958	return appendErrors(validateSubsetName(subset.Name),
959		labels.Instance(subset.Labels).Validate(),
960		validateTrafficPolicy(subset.TrafficPolicy))
961}
962
963func validatePortTrafficPolicies(pls []*networking.TrafficPolicy_PortTrafficPolicy) (errs error) {
964	for _, t := range pls {
965		if t.Port == nil {
966			errs = appendErrors(errs, fmt.Errorf("portTrafficPolicy must have valid port"))
967		}
968		if t.OutlierDetection == nil && t.ConnectionPool == nil &&
969			t.LoadBalancer == nil && t.Tls == nil {
970			errs = appendErrors(errs, fmt.Errorf("port traffic policy must have at least one field"))
971		} else {
972			errs = appendErrors(errs, validateOutlierDetection(t.OutlierDetection),
973				validateConnectionPool(t.ConnectionPool),
974				validateLoadBalancer(t.LoadBalancer),
975				validateTLS(t.Tls))
976		}
977	}
978	return
979}
980
981// ValidateProxyAddress checks that a network address is well-formed
982func ValidateProxyAddress(hostAddr string) error {
983	hostname, p, err := net.SplitHostPort(hostAddr)
984	if err != nil {
985		return fmt.Errorf("unable to split %q: %v", hostAddr, err)
986	}
987	port, err := strconv.Atoi(p)
988	if err != nil {
989		return fmt.Errorf("port (%s) is not a number: %v", p, err)
990	}
991	if err = ValidatePort(port); err != nil {
992		return err
993	}
994	if err = ValidateFQDN(hostname); err != nil {
995		ip := net.ParseIP(hostname)
996		if ip == nil {
997			return fmt.Errorf("%q is not a valid hostname or an IP address", hostname)
998		}
999	}
1000
1001	return nil
1002}
1003
1004// ValidateDurationGogo checks that a gogo proto duration is well-formed
1005func ValidateDurationGogo(pd *types.Duration) error {
1006	dur, err := types.DurationFromProto(pd)
1007	if err != nil {
1008		return err
1009	}
1010	if dur < time.Millisecond {
1011		return errors.New("duration must be greater than 1ms")
1012	}
1013	if dur%time.Millisecond != 0 {
1014		return errors.New("only durations to ms precision are supported")
1015	}
1016	return nil
1017}
1018
1019// ValidateDuration checks that a proto duration is well-formed
1020func ValidateDuration(pd *types.Duration) error {
1021	dur, err := types.DurationFromProto(pd)
1022	if err != nil {
1023		return err
1024	}
1025	if dur < time.Millisecond {
1026		return errors.New("duration must be greater than 1ms")
1027	}
1028	if dur%time.Millisecond != 0 {
1029		return errors.New("only durations to ms precision are supported")
1030	}
1031	return nil
1032}
1033
1034// ValidateGogoDuration validates the variant of duration.
1035func ValidateGogoDuration(in *types.Duration) error {
1036	return ValidateDuration(&types.Duration{
1037		Seconds: in.Seconds,
1038		Nanos:   in.Nanos,
1039	})
1040}
1041
1042// ValidateDurationRange verifies range is in specified duration
1043func ValidateDurationRange(dur, min, max time.Duration) error {
1044	if dur > max || dur < min {
1045		return fmt.Errorf("time %v must be >%v and <%v", dur.String(), min.String(), max.String())
1046	}
1047
1048	return nil
1049}
1050
1051// ValidateParentAndDrain checks that parent and drain durations are valid
1052func ValidateParentAndDrain(drainTime, parentShutdown *types.Duration) (errs error) {
1053	if err := ValidateDuration(drainTime); err != nil {
1054		errs = multierror.Append(errs, multierror.Prefix(err, "invalid drain duration:"))
1055	}
1056	if err := ValidateDuration(parentShutdown); err != nil {
1057		errs = multierror.Append(errs, multierror.Prefix(err, "invalid parent shutdown duration:"))
1058	}
1059	if errs != nil {
1060		return
1061	}
1062
1063	drainDuration, _ := types.DurationFromProto(drainTime)
1064	parentShutdownDuration, _ := types.DurationFromProto(parentShutdown)
1065
1066	if drainDuration%time.Second != 0 {
1067		errs = multierror.Append(errs,
1068			errors.New("drain time only supports durations to seconds precision"))
1069	}
1070	if parentShutdownDuration%time.Second != 0 {
1071		errs = multierror.Append(errs,
1072			errors.New("parent shutdown time only supports durations to seconds precision"))
1073	}
1074	if parentShutdownDuration <= drainDuration {
1075		errs = multierror.Append(errs,
1076			fmt.Errorf("parent shutdown time %v must be greater than drain time %v",
1077				parentShutdownDuration.String(), drainDuration.String()))
1078	}
1079
1080	if drainDuration > drainTimeMax {
1081		errs = multierror.Append(errs,
1082			fmt.Errorf("drain time %v must be <%v", drainDuration.String(), drainTimeMax.String()))
1083	}
1084
1085	if parentShutdownDuration > parentShutdownTimeMax {
1086		errs = multierror.Append(errs,
1087			fmt.Errorf("parent shutdown time %v must be <%v",
1088				parentShutdownDuration.String(), parentShutdownTimeMax.String()))
1089	}
1090
1091	return
1092}
1093
1094// ValidateLightstepCollector validates the configuration for sending envoy spans to LightStep
1095func ValidateLightstepCollector(ls *meshconfig.Tracing_Lightstep) error {
1096	var errs error
1097	if ls.GetAddress() == "" {
1098		errs = multierror.Append(errs, errors.New("address is required"))
1099	}
1100	if err := ValidateProxyAddress(ls.GetAddress()); err != nil {
1101		errs = multierror.Append(errs, multierror.Prefix(err, "invalid lightstep address:"))
1102	}
1103	if ls.GetAccessToken() == "" {
1104		errs = multierror.Append(errs, errors.New("access token is required"))
1105	}
1106	return errs
1107}
1108
1109// ValidateZipkinCollector validates the configuration for sending envoy spans to Zipkin
1110func ValidateZipkinCollector(z *meshconfig.Tracing_Zipkin) error {
1111	return ValidateProxyAddress(z.GetAddress())
1112}
1113
1114// ValidateDatadogCollector validates the configuration for sending envoy spans to Datadog
1115func ValidateDatadogCollector(d *meshconfig.Tracing_Datadog) error {
1116	// If the address contains $(HOST_IP), replace it with a valid IP before validation.
1117	return ValidateProxyAddress(strings.Replace(d.GetAddress(), "$(HOST_IP)", "127.0.0.1", 1))
1118}
1119
1120// ValidateConnectTimeout validates the envoy conncection timeout
1121func ValidateConnectTimeout(timeout *types.Duration) error {
1122	if err := ValidateDuration(timeout); err != nil {
1123		return err
1124	}
1125
1126	timeoutDuration, _ := types.DurationFromProto(timeout)
1127	err := ValidateDurationRange(timeoutDuration, connectTimeoutMin, connectTimeoutMax)
1128	return err
1129}
1130
1131// ValidateProtocolDetectionTimeout validates the envoy protocol detection timeout
1132func ValidateProtocolDetectionTimeout(timeout *types.Duration) error {
1133	dur, err := types.DurationFromProto(timeout)
1134	if err != nil {
1135		return err
1136	}
1137	// 0s is a valid value if trying to disable protocol detection timeout
1138	if dur == time.Second*0 {
1139		return nil
1140	}
1141	if dur%time.Millisecond != 0 {
1142		return errors.New("only durations to ms precision are supported")
1143	}
1144
1145	return nil
1146}
1147
1148// ValidateMeshConfig checks that the mesh config is well-formed
1149func ValidateMeshConfig(mesh *meshconfig.MeshConfig) (errs error) {
1150	if mesh.MixerCheckServer != "" {
1151		if err := ValidateProxyAddress(mesh.MixerCheckServer); err != nil {
1152			errs = multierror.Append(errs, multierror.Prefix(err, "invalid Policy Check Server address:"))
1153		}
1154	}
1155
1156	if mesh.MixerReportServer != "" {
1157		if err := ValidateProxyAddress(mesh.MixerReportServer); err != nil {
1158			errs = multierror.Append(errs, multierror.Prefix(err, "invalid Telemetry Server address:"))
1159		}
1160	}
1161
1162	if err := ValidatePort(int(mesh.ProxyListenPort)); err != nil {
1163		errs = multierror.Append(errs, multierror.Prefix(err, "invalid proxy listen port:"))
1164	}
1165
1166	if err := ValidateConnectTimeout(mesh.ConnectTimeout); err != nil {
1167		errs = multierror.Append(errs, multierror.Prefix(err, "invalid connect timeout:"))
1168	}
1169
1170	if err := ValidateProtocolDetectionTimeout(mesh.ProtocolDetectionTimeout); err != nil {
1171		errs = multierror.Append(errs, multierror.Prefix(err, "invalid protocol detection timeout:"))
1172	}
1173
1174	if mesh.DefaultConfig == nil {
1175		errs = multierror.Append(errs, errors.New("missing default config"))
1176	} else if err := ValidateProxyConfig(mesh.DefaultConfig); err != nil {
1177		errs = multierror.Append(errs, err)
1178	}
1179
1180	if err := validateLocalityLbSetting(mesh.LocalityLbSetting); err != nil {
1181		errs = multierror.Append(errs, err)
1182	}
1183
1184	if err := validateServiceSettings(mesh); err != nil {
1185		errs = multierror.Append(errs, err)
1186	}
1187
1188	return
1189}
1190
1191func validateServiceSettings(config *meshconfig.MeshConfig) (errs error) {
1192	for sIndex, s := range config.ServiceSettings {
1193		for _, h := range s.Hosts {
1194			if err := ValidateWildcardDomain(h); err != nil {
1195				errs = multierror.Append(errs, fmt.Errorf("serviceSettings[%d], host `%s`: %v", sIndex, h, err))
1196			}
1197		}
1198	}
1199	return
1200}
1201
1202// ValidateProxyConfig checks that the mesh config is well-formed
1203func ValidateProxyConfig(config *meshconfig.ProxyConfig) (errs error) {
1204	if config.ConfigPath == "" {
1205		errs = multierror.Append(errs, errors.New("config path must be set"))
1206	}
1207
1208	if config.BinaryPath == "" {
1209		errs = multierror.Append(errs, errors.New("binary path must be set"))
1210	}
1211
1212	if config.ServiceCluster == "" {
1213		errs = multierror.Append(errs, errors.New("service cluster must be set"))
1214	}
1215
1216	if err := ValidateParentAndDrain(config.DrainDuration, config.ParentShutdownDuration); err != nil {
1217		errs = multierror.Append(errs, multierror.Prefix(err, "invalid parent and drain time combination"))
1218	}
1219
1220	// discovery address is mandatory since mutual TLS relies on CDS.
1221	// strictly speaking, proxies can operate without RDS/CDS and with hot restarts
1222	// but that requires additional test validation
1223	if config.DiscoveryAddress == "" {
1224		errs = multierror.Append(errs, errors.New("discovery address must be set to the proxy discovery service"))
1225	} else if err := ValidateProxyAddress(config.DiscoveryAddress); err != nil {
1226		errs = multierror.Append(errs, multierror.Prefix(err, "invalid discovery address:"))
1227	}
1228
1229	if tracer := config.GetTracing().GetLightstep(); tracer != nil {
1230		if err := ValidateLightstepCollector(tracer); err != nil {
1231			errs = multierror.Append(errs, multierror.Prefix(err, "invalid lightstep config:"))
1232		}
1233	}
1234
1235	if tracer := config.GetTracing().GetZipkin(); tracer != nil {
1236		if err := ValidateZipkinCollector(tracer); err != nil {
1237			errs = multierror.Append(errs, multierror.Prefix(err, "invalid zipkin config:"))
1238		}
1239	}
1240
1241	if tracer := config.GetTracing().GetDatadog(); tracer != nil {
1242		if err := ValidateDatadogCollector(tracer); err != nil {
1243			errs = multierror.Append(errs, multierror.Prefix(err, "invalid datadog config:"))
1244		}
1245	}
1246
1247	if tracer := config.GetTracing().GetTlsSettings(); tracer != nil {
1248		if err := validateTLS(tracer); err != nil {
1249			errs = multierror.Append(errs, multierror.Prefix(err, "invalid tracing TLS config:"))
1250		}
1251	}
1252
1253	if config.StatsdUdpAddress != "" {
1254		if err := ValidateProxyAddress(config.StatsdUdpAddress); err != nil {
1255			errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid statsd udp address %q:", config.StatsdUdpAddress)))
1256		}
1257	}
1258
1259	if config.EnvoyMetricsServiceAddress != "" {
1260		if err := ValidateProxyAddress(config.EnvoyMetricsServiceAddress); err != nil {
1261			errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy metrics service address %q:", config.EnvoyMetricsServiceAddress)))
1262		} else {
1263			scope.Warnf("EnvoyMetricsServiceAddress is deprecated, use EnvoyMetricsService instead.") // nolint: golint,stylecheck
1264		}
1265	}
1266
1267	if config.EnvoyMetricsService != nil && config.EnvoyMetricsService.Address != "" {
1268		if err := ValidateProxyAddress(config.EnvoyMetricsService.Address); err != nil {
1269			errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy metrics service address %q:", config.EnvoyMetricsService.Address)))
1270		}
1271	}
1272
1273	if config.EnvoyAccessLogService != nil && config.EnvoyAccessLogService.Address != "" {
1274		if err := ValidateProxyAddress(config.EnvoyAccessLogService.Address); err != nil {
1275			errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy access log service address %q:", config.EnvoyAccessLogService.Address)))
1276		}
1277	}
1278
1279	if err := ValidatePort(int(config.ProxyAdminPort)); err != nil {
1280		errs = multierror.Append(errs, multierror.Prefix(err, "invalid proxy admin port:"))
1281	}
1282
1283	switch config.ControlPlaneAuthPolicy {
1284	case meshconfig.AuthenticationPolicy_NONE, meshconfig.AuthenticationPolicy_MUTUAL_TLS:
1285	default:
1286		errs = multierror.Append(errs,
1287			fmt.Errorf("unrecognized control plane auth policy %q", config.ControlPlaneAuthPolicy))
1288	}
1289
1290	return
1291}
1292
1293// ValidateMixerAttributes checks that Mixer attributes is
1294// well-formed.
1295func ValidateMixerAttributes(msg proto.Message) error {
1296	in, ok := msg.(*mpb.Attributes)
1297	if !ok {
1298		return errors.New("cannot case to attributes")
1299	}
1300	if in == nil || len(in.Attributes) == 0 {
1301		return errors.New("list of attributes is nil/empty")
1302	}
1303	var errs error
1304	for k, v := range in.Attributes {
1305		if v == nil {
1306			errs = multierror.Append(errs, errors.New("an attribute cannot be empty"))
1307			continue
1308		}
1309		switch val := v.Value.(type) {
1310		case *mpb.Attributes_AttributeValue_StringValue:
1311			if val.StringValue == "" {
1312				errs = multierror.Append(errs,
1313					fmt.Errorf("string attribute for %q should not be empty", k))
1314			}
1315		case *mpb.Attributes_AttributeValue_DurationValue:
1316			if val.DurationValue == nil {
1317				errs = multierror.Append(errs,
1318					fmt.Errorf("duration attribute for %q should not be nil", k))
1319			}
1320			if err := ValidateGogoDuration(val.DurationValue); err != nil {
1321				errs = multierror.Append(errs, err)
1322			}
1323		case *mpb.Attributes_AttributeValue_BytesValue:
1324			if len(val.BytesValue) == 0 {
1325				errs = multierror.Append(errs,
1326					fmt.Errorf("bytes attribute for %q should not be ", k))
1327			}
1328		case *mpb.Attributes_AttributeValue_TimestampValue:
1329			if val.TimestampValue == nil {
1330				errs = multierror.Append(errs,
1331					fmt.Errorf("timestamp attribute for %q should not be nil", k))
1332			}
1333			if _, err := types.TimestampFromProto(val.TimestampValue); err != nil {
1334				errs = multierror.Append(errs, err)
1335			}
1336		case *mpb.Attributes_AttributeValue_StringMapValue:
1337			if val.StringMapValue == nil || val.StringMapValue.Entries == nil {
1338				errs = multierror.Append(errs,
1339					fmt.Errorf("stringmap attribute for %q should not be nil", k))
1340			}
1341		}
1342	}
1343	return errs
1344}
1345
1346// ValidateHTTPAPISpec checks that HTTPAPISpec is well-formed.
1347var ValidateHTTPAPISpec = registerValidateFunc("ValidateHTTPAPISpec",
1348	func(_, _ string, msg proto.Message) error {
1349		in, ok := msg.(*mccpb.HTTPAPISpec)
1350		if !ok {
1351			return errors.New("cannot case to HTTPAPISpec")
1352		}
1353		var errs error
1354		// top-level list of attributes is optional
1355		if in.Attributes != nil {
1356			if err := ValidateMixerAttributes(in.Attributes); err != nil {
1357				errs = multierror.Append(errs, err)
1358			}
1359		}
1360		if len(in.Patterns) == 0 {
1361			errs = multierror.Append(errs, errors.New("at least one pattern must be specified"))
1362		}
1363		for _, pattern := range in.Patterns {
1364			if err := ValidateMixerAttributes(in.Attributes); err != nil {
1365				errs = multierror.Append(errs, err)
1366			}
1367			if pattern.HttpMethod == "" {
1368				errs = multierror.Append(errs, errors.New("http_method cannot be empty"))
1369			}
1370			switch m := pattern.Pattern.(type) {
1371			case *mccpb.HTTPAPISpecPattern_UriTemplate:
1372				if m.UriTemplate == "" {
1373					errs = multierror.Append(errs, errors.New("uri_template cannot be empty"))
1374				}
1375			case *mccpb.HTTPAPISpecPattern_Regex:
1376				if m.Regex == "" {
1377					errs = multierror.Append(errs, errors.New("regex cannot be empty"))
1378				}
1379			}
1380		}
1381		for _, key := range in.ApiKeys {
1382			switch m := key.Key.(type) {
1383			case *mccpb.APIKey_Query:
1384				if m.Query == "" {
1385					errs = multierror.Append(errs, errors.New("query cannot be empty"))
1386				}
1387			case *mccpb.APIKey_Header:
1388				if m.Header == "" {
1389					errs = multierror.Append(errs, errors.New("header cannot be empty"))
1390				}
1391			case *mccpb.APIKey_Cookie:
1392				if m.Cookie == "" {
1393					errs = multierror.Append(errs, errors.New("cookie cannot be empty"))
1394				}
1395			}
1396		}
1397		return errs
1398	})
1399
1400// ValidateHTTPAPISpecBinding checks that HTTPAPISpecBinding is well-formed.
1401var ValidateHTTPAPISpecBinding = registerValidateFunc("ValidateHTTPAPISpecBinding",
1402	func(_, _ string, msg proto.Message) error {
1403		in, ok := msg.(*mccpb.HTTPAPISpecBinding)
1404		if !ok {
1405			return errors.New("cannot case to HTTPAPISpecBinding")
1406		}
1407		var errs error
1408		if len(in.Services) == 0 {
1409			errs = multierror.Append(errs, errors.New("at least one service must be specified"))
1410		}
1411		for _, service := range in.Services {
1412			if err := ValidateMixerService(service); err != nil {
1413				errs = multierror.Append(errs, err)
1414			}
1415		}
1416		if len(in.ApiSpecs) == 0 {
1417			errs = multierror.Append(errs, errors.New("at least one spec must be specified"))
1418		}
1419		for _, spec := range in.ApiSpecs {
1420			if spec.Name == "" {
1421				errs = multierror.Append(errs, errors.New("name is mandatory for HTTPAPISpecReference"))
1422			}
1423			if spec.Namespace != "" && !labels.IsDNS1123Label(spec.Namespace) {
1424				errs = multierror.Append(errs, fmt.Errorf("namespace %q must be a valid label", spec.Namespace))
1425			}
1426		}
1427		return errs
1428	})
1429
1430// ValidateQuotaSpec checks that Quota is well-formed.
1431var ValidateQuotaSpec = registerValidateFunc("ValidateQuotaSpec",
1432	func(_, _ string, msg proto.Message) error {
1433		in, ok := msg.(*mccpb.QuotaSpec)
1434		if !ok {
1435			return errors.New("cannot case to HTTPAPISpecBinding")
1436		}
1437		var errs error
1438		if len(in.Rules) == 0 {
1439			errs = multierror.Append(errs, errors.New("a least one rule must be specified"))
1440		}
1441		for _, rule := range in.Rules {
1442			for _, match := range rule.Match {
1443				for name, clause := range match.Clause {
1444					if clause == nil {
1445						errs = multierror.Append(errs, errors.New("a clause cannot be empty"))
1446						continue
1447					}
1448					switch matchType := clause.MatchType.(type) {
1449					case *mccpb.StringMatch_Exact:
1450						if matchType.Exact == "" {
1451							errs = multierror.Append(errs,
1452								fmt.Errorf("StringMatch_Exact for attribute %q cannot be empty", name)) // nolint: golint
1453						}
1454					case *mccpb.StringMatch_Prefix:
1455						if matchType.Prefix == "" {
1456							errs = multierror.Append(errs,
1457								fmt.Errorf("StringMatch_Prefix for attribute %q cannot be empty", name)) // nolint: golint
1458						}
1459					case *mccpb.StringMatch_Regex:
1460						if matchType.Regex == "" {
1461							errs = multierror.Append(errs,
1462								fmt.Errorf("StringMatch_Regex for attribute %q cannot be empty", name)) // nolint: golint
1463						}
1464					}
1465				}
1466			}
1467			if len(rule.Quotas) == 0 {
1468				errs = multierror.Append(errs, errors.New("a least one quota must be specified"))
1469			}
1470			for _, quota := range rule.Quotas {
1471				if quota.Quota == "" {
1472					errs = multierror.Append(errs, errors.New("quota name cannot be empty"))
1473				}
1474				if quota.Charge <= 0 {
1475					errs = multierror.Append(errs, errors.New("quota charge amount must be positive"))
1476				}
1477			}
1478		}
1479		return errs
1480	})
1481
1482// ValidateQuotaSpecBinding checks that QuotaSpecBinding is well-formed.
1483var ValidateQuotaSpecBinding = registerValidateFunc("ValidateQuotaSpecBinding",
1484	func(_, _ string, msg proto.Message) error {
1485		in, ok := msg.(*mccpb.QuotaSpecBinding)
1486		if !ok {
1487			return errors.New("cannot case to HTTPAPISpecBinding")
1488		}
1489		var errs error
1490		if len(in.Services) == 0 {
1491			errs = multierror.Append(errs, errors.New("at least one service must be specified"))
1492		}
1493		for _, service := range in.Services {
1494			if err := ValidateMixerService(service); err != nil {
1495				errs = multierror.Append(errs, err)
1496			}
1497		}
1498		if len(in.QuotaSpecs) == 0 {
1499			errs = multierror.Append(errs, errors.New("at least one spec must be specified"))
1500		}
1501		for _, spec := range in.QuotaSpecs {
1502			if spec.Name == "" {
1503				errs = multierror.Append(errs, errors.New("name is mandatory for QuotaSpecReference"))
1504			}
1505			if spec.Namespace != "" && !labels.IsDNS1123Label(spec.Namespace) {
1506				errs = multierror.Append(errs, fmt.Errorf("namespace %q must be a valid label", spec.Namespace))
1507			}
1508		}
1509		return errs
1510	})
1511
1512// ValidateAuthenticationPolicy checks that AuthenticationPolicy is well-formed.
1513var ValidateAuthenticationPolicy = registerValidateFunc("ValidateAuthenticationPolicy",
1514	func(name, namespace string, msg proto.Message) error {
1515		// Empty namespace indicate policy is from cluster-scoped CRD.
1516		clusterScoped := namespace == ""
1517		in, ok := msg.(*authn.Policy)
1518		if !ok {
1519			return errors.New("cannot cast to AuthenticationPolicy")
1520		}
1521		var errs error
1522
1523		if !clusterScoped {
1524			// nolint: staticcheck
1525			if len(in.Targets) == 0 && name != constants.DefaultAuthenticationPolicyName {
1526				errs = appendErrors(errs, fmt.Errorf("authentication policy with no target rules  must be named %q, found %q",
1527					constants.DefaultAuthenticationPolicyName, name))
1528			}
1529			// nolint: staticcheck
1530			if len(in.Targets) > 0 && name == constants.DefaultAuthenticationPolicyName {
1531				errs = appendErrors(errs, fmt.Errorf("authentication policy with name %q must not have any target rules", name))
1532			}
1533			// nolint: staticcheck
1534			for _, target := range in.Targets {
1535				errs = appendErrors(errs, validateAuthNPolicyTarget(target))
1536			}
1537		} else {
1538			if name != constants.DefaultAuthenticationPolicyName {
1539				errs = appendErrors(errs, fmt.Errorf("cluster-scoped authentication policy name must be %q, found %q",
1540					constants.DefaultAuthenticationPolicyName, name))
1541			}
1542			// nolint: staticcheck
1543			if len(in.Targets) > 0 {
1544				errs = appendErrors(errs, fmt.Errorf("cluster-scoped authentication policy must not have targets"))
1545			}
1546		}
1547
1548		jwtIssuers := make(map[string]bool)
1549		for _, method := range in.Peers {
1550			// nolint: staticcheck
1551			if jwt := method.GetJwt(); jwt != nil {
1552				if _, jwtExist := jwtIssuers[jwt.Issuer]; jwtExist {
1553					errs = appendErrors(errs, fmt.Errorf("jwt with issuer %q already defined", jwt.Issuer))
1554				} else {
1555					jwtIssuers[jwt.Issuer] = true
1556				}
1557				errs = appendErrors(errs, validateJwt(jwt))
1558			}
1559		}
1560		// nolint: staticcheck
1561		for _, method := range in.Origins {
1562			if method == nil {
1563				errs = multierror.Append(errs, errors.New("origin cannot be empty"))
1564				continue
1565			}
1566			if method.Jwt == nil {
1567				errs = multierror.Append(errs, errors.New("jwt cannot be empty"))
1568				continue
1569			}
1570			if _, jwtExist := jwtIssuers[method.Jwt.Issuer]; jwtExist {
1571				errs = appendErrors(errs, fmt.Errorf("jwt with issuer %q already defined", method.Jwt.Issuer))
1572			} else {
1573				jwtIssuers[method.Jwt.Issuer] = true
1574			}
1575			errs = appendErrors(errs, validateJwt(method.Jwt))
1576		}
1577
1578		return errs
1579	})
1580
1581func validateWorkloadSelector(selector *type_beta.WorkloadSelector) error {
1582	var errs error
1583	if selector != nil {
1584		for k, v := range selector.MatchLabels {
1585			if k == "" {
1586				errs = appendErrors(errs,
1587					fmt.Errorf("empty key is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v)))
1588			}
1589			if strings.Contains(k, "*") || strings.Contains(v, "*") {
1590				errs = appendErrors(errs,
1591					fmt.Errorf("wildcard is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v)))
1592			}
1593		}
1594	}
1595
1596	return errs
1597}
1598
1599// ValidateAuthorizationPolicy checks that AuthorizationPolicy is well-formed.
1600var ValidateAuthorizationPolicy = registerValidateFunc("ValidateAuthorizationPolicy",
1601	func(name, namespace string, msg proto.Message) error {
1602		in, ok := msg.(*security_beta.AuthorizationPolicy)
1603		if !ok {
1604			return fmt.Errorf("cannot cast to AuthorizationPolicy")
1605		}
1606
1607		if err := validateWorkloadSelector(in.Selector); err != nil {
1608			return err
1609		}
1610
1611		if in.Action == security_beta.AuthorizationPolicy_DENY && in.Rules == nil {
1612			return fmt.Errorf("a deny policy without `rules` is meaningless and has no effect, found in %s.%s", name, namespace)
1613		}
1614
1615		var errs error
1616		for i, rule := range in.GetRules() {
1617			if rule.From != nil && len(rule.From) == 0 {
1618				errs = appendErrors(errs, fmt.Errorf("`from` must not be empty, found at rule %d in %s.%s", i, name, namespace))
1619			}
1620			for _, from := range rule.From {
1621				if from.Source == nil {
1622					errs = appendErrors(errs, fmt.Errorf("`from.source` must not be nil, found at rule %d in %s.%s", i, name, namespace))
1623				} else {
1624					src := from.Source
1625					if len(src.Principals) == 0 && len(src.RequestPrincipals) == 0 && len(src.Namespaces) == 0 && len(src.IpBlocks) == 0 &&
1626						len(src.NotPrincipals) == 0 && len(src.NotRequestPrincipals) == 0 && len(src.NotNamespaces) == 0 && len(src.NotIpBlocks) == 0 {
1627						errs = appendErrors(errs, fmt.Errorf("`from.source` must not be empty, found at rule %d in %s.%s", i, name, namespace))
1628					}
1629					errs = appendErrors(errs, security.ValidateIPs(from.Source.GetIpBlocks()))
1630					errs = appendErrors(errs, security.ValidateIPs(from.Source.GetNotIpBlocks()))
1631					errs = appendErrors(errs, security.CheckEmptyValues("Principals", src.Principals))
1632					errs = appendErrors(errs, security.CheckEmptyValues("RequestPrincipals", src.RequestPrincipals))
1633					errs = appendErrors(errs, security.CheckEmptyValues("Namespaces", src.Namespaces))
1634					errs = appendErrors(errs, security.CheckEmptyValues("IpBlocks", src.IpBlocks))
1635					errs = appendErrors(errs, security.CheckEmptyValues("NotPrincipals", src.NotPrincipals))
1636					errs = appendErrors(errs, security.CheckEmptyValues("NotRequestPrincipals", src.NotRequestPrincipals))
1637					errs = appendErrors(errs, security.CheckEmptyValues("NotNamespaces", src.NotNamespaces))
1638					errs = appendErrors(errs, security.CheckEmptyValues("NotIpBlocks", src.NotIpBlocks))
1639				}
1640			}
1641			if rule.To != nil && len(rule.To) == 0 {
1642				errs = appendErrors(errs, fmt.Errorf("`to` must not be empty, found at rule %d in %s.%s", i, name, namespace))
1643			}
1644			for _, to := range rule.To {
1645				if to.Operation == nil {
1646					errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be nil, found at rule %d in %s.%s", i, name, namespace))
1647				} else {
1648					op := to.Operation
1649					if len(op.Ports) == 0 && len(op.Methods) == 0 && len(op.Paths) == 0 && len(op.Hosts) == 0 &&
1650						len(op.NotPorts) == 0 && len(op.NotMethods) == 0 && len(op.NotPaths) == 0 && len(op.NotHosts) == 0 {
1651						errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be empty, found at rule %d in %s.%s", i, name, namespace))
1652					}
1653					errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetPorts()))
1654					errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetNotPorts()))
1655					errs = appendErrors(errs, security.CheckEmptyValues("Ports", op.Ports))
1656					errs = appendErrors(errs, security.CheckEmptyValues("Methods", op.Methods))
1657					errs = appendErrors(errs, security.CheckEmptyValues("Paths", op.Paths))
1658					errs = appendErrors(errs, security.CheckEmptyValues("Hosts", op.Hosts))
1659					errs = appendErrors(errs, security.CheckEmptyValues("NotPorts", op.NotPorts))
1660					errs = appendErrors(errs, security.CheckEmptyValues("NotMethods", op.NotMethods))
1661					errs = appendErrors(errs, security.CheckEmptyValues("NotPaths", op.NotPaths))
1662					errs = appendErrors(errs, security.CheckEmptyValues("NotHosts", op.NotHosts))
1663				}
1664			}
1665			for _, condition := range rule.GetWhen() {
1666				key := condition.GetKey()
1667				if key == "" {
1668					errs = appendErrors(errs, fmt.Errorf("`key` must not be empty, found in %s.%s", name, namespace))
1669				} else {
1670					if len(condition.GetValues()) == 0 && len(condition.GetNotValues()) == 0 {
1671						errs = appendErrors(errs, fmt.Errorf("at least one of `values` or `notValues` must be set for key %s, found in %s.%s",
1672							key, name, namespace))
1673					} else {
1674						if err := security.ValidateAttribute(key, condition.GetValues()); err != nil {
1675							errs = appendErrors(errs, fmt.Errorf("invalid `value` for `key` %s: %v, found in %s.%s", key, err, name, namespace))
1676						}
1677						if err := security.ValidateAttribute(key, condition.GetNotValues()); err != nil {
1678							errs = appendErrors(errs, fmt.Errorf("invalid `notValue` for `key` %s: %v, found in %s.%s", key, err, name, namespace))
1679						}
1680					}
1681				}
1682			}
1683		}
1684		return errs
1685	})
1686
1687// ValidateRequestAuthentication checks that request authentication spec is well-formed.
1688var ValidateRequestAuthentication = registerValidateFunc("ValidateRequestAuthentication",
1689	func(name, namespace string, msg proto.Message) error {
1690		in, ok := msg.(*security_beta.RequestAuthentication)
1691		if !ok {
1692			return errors.New("cannot cast to RequestAuthentication")
1693		}
1694
1695		var errs error
1696		errs = appendErrors(errs, validateWorkloadSelector(in.Selector))
1697
1698		for _, rule := range in.JwtRules {
1699			errs = appendErrors(errs, validateJwtRule(rule))
1700		}
1701		return errs
1702	})
1703
1704func validateJwtRule(rule *security_beta.JWTRule) (errs error) {
1705	if rule == nil {
1706		return nil
1707	}
1708	if len(rule.Issuer) == 0 {
1709		errs = multierror.Append(errs, errors.New("issuer must be set"))
1710	}
1711	for _, audience := range rule.Audiences {
1712		if len(audience) == 0 {
1713			errs = multierror.Append(errs, errors.New("audience must be non-empty string"))
1714		}
1715	}
1716
1717	if len(rule.JwksUri) != 0 {
1718		if _, err := security.ParseJwksURI(rule.JwksUri); err != nil {
1719			errs = multierror.Append(errs, err)
1720		}
1721	}
1722
1723	for _, location := range rule.FromHeaders {
1724		if len(location.Name) == 0 {
1725			errs = multierror.Append(errs, errors.New("location header name must be non-empty string"))
1726		}
1727	}
1728
1729	for _, location := range rule.FromParams {
1730		if len(location) == 0 {
1731			errs = multierror.Append(errs, errors.New("location query must be non-empty string"))
1732		}
1733	}
1734	return
1735}
1736
1737// ValidateAccept always returns true
1738func ValidateAccept(_, _ string, _ proto.Message) error {
1739	return nil
1740}
1741
1742// ValidatePeerAuthentication checks that peer authentication spec is well-formed.
1743var ValidatePeerAuthentication = registerValidateFunc("ValidatePeerAuthentication",
1744	func(name, namespace string, msg proto.Message) error {
1745		in, ok := msg.(*security_beta.PeerAuthentication)
1746		if !ok {
1747			return errors.New("cannot cast to PeerAuthentication")
1748		}
1749
1750		var errs error
1751		emptySelector := in.Selector == nil || len(in.Selector.MatchLabels) == 0
1752
1753		if emptySelector && len(in.PortLevelMtls) != 0 {
1754			errs = appendErrors(errs,
1755				fmt.Errorf("mesh/namespace peer authentication cannot have port level mTLS"))
1756		}
1757
1758		if in.PortLevelMtls != nil && len(in.PortLevelMtls) == 0 {
1759			errs = appendErrors(errs,
1760				fmt.Errorf("port level mTLS, if defined, must have at least one element"))
1761		}
1762
1763		for port := range in.PortLevelMtls {
1764			if port == 0 {
1765				errs = appendErrors(errs, fmt.Errorf("port cannot be 0"))
1766			}
1767		}
1768
1769		errs = appendErrors(errs, validateWorkloadSelector(in.Selector))
1770
1771		return errs
1772	})
1773
1774// ValidateServiceRole checks that ServiceRole is well-formed.
1775var ValidateServiceRole = registerValidateFunc("ValidateServiceRole",
1776	func(_, _ string, msg proto.Message) error {
1777		in, ok := msg.(*rbac.ServiceRole)
1778		if !ok {
1779			return errors.New("cannot cast to ServiceRole")
1780		}
1781		var errs error
1782		if len(in.Rules) == 0 {
1783			errs = appendErrors(errs, fmt.Errorf("at least 1 rule must be specified"))
1784		}
1785		for i, rule := range in.Rules {
1786			// Regular rules and not rules (e.g. methods and not_methods should not be defined together).
1787			sameAttributeKindError := "cannot have both regular and *not* attributes for the same kind (%s) for rule %d"
1788			if len(rule.Methods) > 0 && len(rule.NotMethods) > 0 {
1789				errs = appendErrors(errs, fmt.Errorf(sameAttributeKindError, "i.e. methods and not_methods", i))
1790			}
1791			if len(rule.Ports) > 0 && len(rule.NotPorts) > 0 {
1792				errs = appendErrors(errs, fmt.Errorf(sameAttributeKindError, "i.e. ports and not_ports", i))
1793			}
1794			if !ValidatePorts(rule.Ports) || !ValidatePorts(rule.NotPorts) {
1795				errs = appendErrors(errs, fmt.Errorf("at least one port is not in the range of [0, 65535]"))
1796			}
1797			for j, constraint := range rule.Constraints {
1798				if len(constraint.Key) == 0 {
1799					errs = appendErrors(errs, fmt.Errorf("key cannot be empty for constraint %d in rule %d", j, i))
1800				}
1801				if len(constraint.Values) == 0 {
1802					errs = appendErrors(errs, fmt.Errorf("at least 1 value must be specified for constraint %d in rule %d", j, i))
1803				}
1804				if hasExistingFirstClassFieldInRole(constraint.Key, rule) {
1805					errs = appendErrors(errs, fmt.Errorf("cannot define %s for rule %d because a similar first-class field has been defined", constraint.Key, i))
1806				}
1807			}
1808		}
1809		return errs
1810	})
1811
1812// Returns true if the user defines a constraint that already exists in the first-class fields, false
1813// if none has overlapped.
1814// First-class fields are the immediate-level fields right after the `rules` field in a ServiceRole, e.g.
1815// methods, services, etc., or after the `subjects` field in a binding, e.g. names, groups, etc. In shorts,
1816// they are not fields under Constraints (in ServiceRole) and Properties (in binding).
1817// This prevents the user from defining the same key, e.g. port of the serving service, in multiple places
1818// in a ServiceRole definition.
1819func hasExistingFirstClassFieldInRole(constraintKey string, rule *rbac.AccessRule) bool {
1820	// Same as authz.attrDestPort
1821	// Cannot use authz.attrDestPort since there is a import cycle. However, these constants can be
1822	// defined at another place and both authz and model package can access them.
1823	const attrDestPort = "destination.port"
1824	// Only check for port since we only have ports (or not_ports) as first-class field and in destination.port
1825	// in a ServiceRole definition.
1826	if constraintKey == attrDestPort && len(rule.Ports) > 0 {
1827		return true
1828	}
1829	if constraintKey == attrDestPort && len(rule.NotPorts) > 0 {
1830		return true
1831	}
1832	return false
1833}
1834
1835// checkServiceRoleBinding checks that ServiceRoleBinding is well-formed.
1836func checkServiceRoleBinding(in *rbac.ServiceRoleBinding) error {
1837	var errs error
1838	if len(in.Subjects) == 0 {
1839		errs = appendErrors(errs, fmt.Errorf("at least 1 subject must be specified"))
1840	}
1841	for i, subject := range in.Subjects {
1842		if isFirstClassFieldEmpty(subject) {
1843			errs = appendErrors(errs, fmt.Errorf("empty subjects are not allowed. Found an empty subject at index %d", i))
1844		}
1845		for propertyKey := range subject.Properties {
1846			if hasExistingFirstClassFieldInBinding(propertyKey, subject) {
1847				errs = appendErrors(errs, fmt.Errorf("cannot define %s for binding %d because a similar first-class field has been defined", propertyKey, i))
1848			}
1849		}
1850		// Since source.principal = "*" in binding properties is different than User: "*" from the old API,
1851		// we want to remove ambiguity when the user defines "*" in names or not_names
1852		if isStarInNames(subject.Names) || isStarInNames(subject.NotNames) {
1853			errs = appendErrors(errs, fmt.Errorf("do not use * for names or not_names (in rule %d)", i))
1854		}
1855	}
1856	roleFieldCount := 0
1857	if in.RoleRef != nil {
1858		roleFieldCount++
1859	}
1860	if len(in.Actions) > 0 {
1861		roleFieldCount++
1862	}
1863	if in.Role != "" {
1864		roleFieldCount++
1865	}
1866	if roleFieldCount != 1 {
1867		errs = appendErrors(errs, fmt.Errorf("exactly one of `roleRef`, `role`, or `actions` must be specified"))
1868	}
1869	if in.RoleRef != nil {
1870		expectKind := "ServiceRole"
1871		if in.RoleRef.Kind != expectKind {
1872			errs = appendErrors(errs, fmt.Errorf("kind set to %q, currently the only supported value is %q",
1873				in.RoleRef.Kind, expectKind))
1874		}
1875		if len(in.RoleRef.Name) == 0 {
1876			errs = appendErrors(errs, fmt.Errorf("`name` in `roleRef` cannot be empty"))
1877		}
1878	}
1879	if len(in.Actions) > 0 {
1880		inlineServiceRole := &rbac.ServiceRole{Rules: in.Actions}
1881		errs = appendErrors(errs, ValidateServiceRole("", "", inlineServiceRole))
1882	}
1883	if in.Role != "" {
1884		// Same as rootNamespacePrefix in rbac_v2.go
1885		const rootNamespacePrefix = "/"
1886		if in.Role == rootNamespacePrefix {
1887			errs = appendErrors(errs, fmt.Errorf("`role` cannot have an empty ServiceRole name"))
1888		}
1889	}
1890	return errs
1891}
1892
1893// ValidateServiceRoleBinding checks that ServiceRoleBinding is well-formed.
1894var ValidateServiceRoleBinding = registerValidateFunc("ValidateServiceRoleBinding",
1895	func(_, _ string, msg proto.Message) error {
1896		in, ok := msg.(*rbac.ServiceRoleBinding)
1897		if !ok {
1898			return errors.New("cannot cast to ServiceRoleBinding")
1899		}
1900		return checkServiceRoleBinding(in)
1901	})
1902
1903// isFirstClassFieldEmpty return false if there is at least one first class field (e.g. properties)
1904func isFirstClassFieldEmpty(subject *rbac.Subject) bool {
1905	return len(subject.User) == 0 && len(subject.Group) == 0 && len(subject.Properties) == 0 &&
1906		len(subject.Namespaces) == 0 && len(subject.NotNamespaces) == 0 && len(subject.Groups) == 0 &&
1907		len(subject.NotGroups) == 0 && len(subject.Ips) == 0 && len(subject.NotIps) == 0 &&
1908		len(subject.Names) == 0 && len(subject.NotNames) == 0
1909}
1910
1911// Returns true if the user defines a property that already exists in the first-class fields, false
1912// if none has overlapped.
1913// In the future when we introduce expression conditions in properties field, we might need to revisit
1914// this function.
1915func hasExistingFirstClassFieldInBinding(propertiesKey string, subject *rbac.Subject) bool {
1916	switch propertiesKey {
1917	case "source.principal":
1918		return len(subject.Names) > 0
1919	case "request.auth.claims[groups]":
1920		return len(subject.Groups) > 0
1921	case "source.namespace":
1922		return len(subject.Namespaces) > 0
1923	case "source.ip":
1924		return len(subject.Ips) > 0
1925	}
1926	return false
1927}
1928
1929// isStarInNames returns true if there is a "*" in the names slice.
1930func isStarInNames(names []string) bool {
1931	for _, name := range names {
1932		if name == "*" {
1933			return true
1934		}
1935	}
1936	return false
1937}
1938
1939func checkRbacConfig(name, typ string, msg proto.Message) error {
1940	in, ok := msg.(*rbac.RbacConfig)
1941	if !ok {
1942		return errors.New("cannot cast to " + typ)
1943	}
1944
1945	if name != constants.DefaultRbacConfigName {
1946		return fmt.Errorf("%s has invalid name(%s), name must be %q", typ, name, constants.DefaultRbacConfigName)
1947	}
1948
1949	if in.Mode == rbac.RbacConfig_ON_WITH_INCLUSION && in.Inclusion == nil {
1950		return errors.New("inclusion cannot be null (use 'inclusion: {}' for none)")
1951	}
1952
1953	if in.Mode == rbac.RbacConfig_ON_WITH_EXCLUSION && in.Exclusion == nil {
1954		return errors.New("exclusion cannot be null (use 'exclusion: {}' for none)")
1955	}
1956
1957	return nil
1958}
1959
1960// ValidateClusterRbacConfig checks that ClusterRbacConfig is well-formed.
1961var ValidateClusterRbacConfig = registerValidateFunc("ValidateClusterRbacConfig",
1962	func(name, _ string, msg proto.Message) error {
1963		return checkRbacConfig(name, "ClusterRbacConfig", msg)
1964	})
1965
1966// ValidateRbacConfig checks that RbacConfig is well-formed.
1967var ValidateRbacConfig = registerValidateFunc("ValidateRbacConfig",
1968	func(name, _ string, msg proto.Message) error {
1969		scope.Warnf("RbacConfig is deprecated, use ClusterRbacConfig instead.")
1970		return checkRbacConfig(name, "RbacConfig", msg)
1971	})
1972
1973func validateJwt(jwt *authn.Jwt) (errs error) {
1974	if jwt == nil {
1975		return nil
1976	}
1977	if jwt.Issuer == "" {
1978		errs = multierror.Append(errs, errors.New("issuer must be set"))
1979	}
1980	for _, audience := range jwt.Audiences {
1981		if audience == "" {
1982			errs = multierror.Append(errs, errors.New("audience must be non-empty string"))
1983		}
1984	}
1985	if jwt.JwksUri != "" {
1986		if _, err := security.ParseJwksURI(jwt.JwksUri); err != nil {
1987			errs = multierror.Append(errs, err)
1988		}
1989	}
1990
1991	for _, location := range jwt.JwtHeaders {
1992		if location == "" {
1993			errs = multierror.Append(errs, errors.New("location header must be non-empty string"))
1994		}
1995	}
1996
1997	for _, location := range jwt.JwtParams {
1998		if location == "" {
1999			errs = multierror.Append(errs, errors.New("location query must be non-empty string"))
2000		}
2001	}
2002	return
2003}
2004
2005func validateAuthNPolicyTarget(target *authn.TargetSelector) (errs error) {
2006	if target == nil {
2007		return
2008	}
2009
2010	// AuthN policy target (host)name must be a shortname
2011	if !labels.IsDNS1123Label(target.Name) {
2012		errs = multierror.Append(errs, fmt.Errorf("target name %q must be a valid label", target.Name))
2013	}
2014
2015	for _, port := range target.Ports {
2016		errs = appendErrors(errs, validateAuthNPortSelector(port))
2017	}
2018
2019	return
2020}
2021
2022// ValidateVirtualService checks that a v1alpha3 route rule is well-formed.
2023var ValidateVirtualService = registerValidateFunc("ValidateVirtualService",
2024	func(_, _ string, msg proto.Message) (errs error) {
2025		virtualService, ok := msg.(*networking.VirtualService)
2026		if !ok {
2027			return errors.New("cannot cast to virtual service")
2028		}
2029
2030		isDelegate := false
2031		if len(virtualService.Hosts) == 0 {
2032			if features.EnableVirtualServiceDelegate {
2033				isDelegate = true
2034			} else {
2035				errs = appendErrors(errs, fmt.Errorf("virtual service must have at least one host"))
2036			}
2037		}
2038
2039		if isDelegate {
2040			if len(virtualService.Gateways) != 0 {
2041				// meaningless to specify gateways in delegate
2042				errs = appendErrors(errs, fmt.Errorf("delegate virtual service must have no gateways specified"))
2043			}
2044			if len(virtualService.Tls) != 0 {
2045				// meaningless to specify tls in delegate, we donot support tls delegate
2046				errs = appendErrors(errs, fmt.Errorf("delegate virtual service must have no tls route specified"))
2047			}
2048			if len(virtualService.Tcp) != 0 {
2049				// meaningless to specify tls in delegate, we donot support tcp delegate
2050				errs = appendErrors(errs, fmt.Errorf("delegate virtual service must have no tcp route specified"))
2051			}
2052		}
2053
2054		appliesToMesh := false
2055		appliesToGateway := false
2056		if len(virtualService.Gateways) == 0 {
2057			appliesToMesh = true
2058		}
2059
2060		errs = appendErrors(errs, validateGatewayNames(virtualService.Gateways))
2061		for _, gatewayName := range virtualService.Gateways {
2062			if gatewayName == constants.IstioMeshGateway {
2063				appliesToMesh = true
2064			} else {
2065				appliesToGateway = true
2066			}
2067		}
2068
2069		allHostsValid := true
2070		for _, virtualHost := range virtualService.Hosts {
2071			if err := ValidateWildcardDomain(virtualHost); err != nil {
2072				ipAddr := net.ParseIP(virtualHost) // Could also be an IP
2073				if ipAddr == nil {
2074					errs = appendErrors(errs, err)
2075					allHostsValid = false
2076				}
2077			} else if appliesToMesh && virtualHost == "*" {
2078				errs = appendErrors(errs, fmt.Errorf("wildcard host * is not allowed for virtual services bound to the mesh gateway"))
2079				allHostsValid = false
2080			}
2081		}
2082
2083		// Check for duplicate hosts
2084		// Duplicates include literal duplicates as well as wildcard duplicates
2085		// E.g., *.foo.com, and *.com are duplicates in the same virtual service
2086		if allHostsValid {
2087			for i := 0; i < len(virtualService.Hosts); i++ {
2088				hostI := host.Name(virtualService.Hosts[i])
2089				for j := i + 1; j < len(virtualService.Hosts); j++ {
2090					hostJ := host.Name(virtualService.Hosts[j])
2091					if hostI.Matches(hostJ) {
2092						errs = appendErrors(errs, fmt.Errorf("duplicate hosts in virtual service: %s & %s", hostI, hostJ))
2093					}
2094				}
2095			}
2096		}
2097
2098		if len(virtualService.Http) == 0 && len(virtualService.Tcp) == 0 && len(virtualService.Tls) == 0 {
2099			errs = appendErrors(errs, errors.New("http, tcp or tls must be provided in virtual service"))
2100		}
2101		for _, httpRoute := range virtualService.Http {
2102			if !appliesToGateway && httpRoute.Delegate != nil {
2103				errs = appendErrors(errs, errors.New("http delegate only applies to gateway"))
2104			}
2105			errs = appendErrors(errs, validateHTTPRoute(httpRoute, isDelegate))
2106		}
2107		for _, tlsRoute := range virtualService.Tls {
2108			errs = appendErrors(errs, validateTLSRoute(tlsRoute, virtualService))
2109		}
2110		for _, tcpRoute := range virtualService.Tcp {
2111			errs = appendErrors(errs, validateTCPRoute(tcpRoute))
2112		}
2113
2114		errs = appendErrors(errs, validateExportTo(virtualService.ExportTo))
2115		return
2116	})
2117
2118func validateTLSRoute(tls *networking.TLSRoute, context *networking.VirtualService) (errs error) {
2119	if tls == nil {
2120		return nil
2121	}
2122	if len(tls.Match) == 0 {
2123		errs = appendErrors(errs, errors.New("TLS route must have at least one match condition"))
2124	}
2125	for _, match := range tls.Match {
2126		errs = appendErrors(errs, validateTLSMatch(match, context))
2127	}
2128	if len(tls.Route) == 0 {
2129		errs = appendErrors(errs, errors.New("TLS route is required"))
2130	}
2131	errs = appendErrors(errs, validateRouteDestinations(tls.Route))
2132	return
2133}
2134
2135func validateTLSMatch(match *networking.TLSMatchAttributes, context *networking.VirtualService) (errs error) {
2136	if len(match.SniHosts) == 0 {
2137		errs = appendErrors(errs, fmt.Errorf("TLS match must have at least one SNI host"))
2138	} else {
2139		for _, sniHost := range match.SniHosts {
2140			err := validateSniHost(sniHost, context)
2141			if err != nil {
2142				errs = appendErrors(errs, err)
2143			}
2144		}
2145	}
2146
2147	for _, destinationSubnet := range match.DestinationSubnets {
2148		errs = appendErrors(errs, ValidateIPSubnet(destinationSubnet))
2149	}
2150
2151	if match.Port != 0 {
2152		errs = appendErrors(errs, ValidatePort(int(match.Port)))
2153	}
2154	errs = appendErrors(errs, labels.Instance(match.SourceLabels).Validate())
2155	errs = appendErrors(errs, validateGatewayNames(match.Gateways))
2156	return
2157}
2158
2159func validateSniHost(sniHost string, context *networking.VirtualService) error {
2160	if err := ValidateWildcardDomain(sniHost); err != nil {
2161		ipAddr := net.ParseIP(sniHost) // Could also be an IP
2162		if ipAddr == nil {
2163			return err
2164		}
2165	}
2166	sniHostname := host.Name(sniHost)
2167	for _, hostname := range context.Hosts {
2168		if sniHostname.SubsetOf(host.Name(hostname)) {
2169			return nil
2170		}
2171	}
2172	return fmt.Errorf("SNI host %q is not a compatible subset of any of the virtual service hosts: [%s]",
2173		sniHost, strings.Join(context.Hosts, ", "))
2174}
2175
2176func validateTCPRoute(tcp *networking.TCPRoute) (errs error) {
2177	if tcp == nil {
2178		return nil
2179	}
2180	for _, match := range tcp.Match {
2181		errs = appendErrors(errs, validateTCPMatch(match))
2182	}
2183	if len(tcp.Route) == 0 {
2184		errs = appendErrors(errs, errors.New("TCP route is required"))
2185	}
2186	errs = appendErrors(errs, validateRouteDestinations(tcp.Route))
2187	return
2188}
2189
2190func validateTCPMatch(match *networking.L4MatchAttributes) (errs error) {
2191	for _, destinationSubnet := range match.DestinationSubnets {
2192		errs = appendErrors(errs, ValidateIPSubnet(destinationSubnet))
2193	}
2194	if match.Port != 0 {
2195		errs = appendErrors(errs, ValidatePort(int(match.Port)))
2196	}
2197	errs = appendErrors(errs, labels.Instance(match.SourceLabels).Validate())
2198	errs = appendErrors(errs, validateGatewayNames(match.Gateways))
2199	return
2200}
2201
2202func validateHTTPRoute(http *networking.HTTPRoute, delegate bool) (errs error) {
2203	if features.EnableVirtualServiceDelegate {
2204		if delegate {
2205			return validateDelegateHTTPRoute(http)
2206		}
2207		if http.Delegate != nil {
2208			return validateRootHTTPRoute(http)
2209		}
2210	}
2211
2212	// check for conflicts
2213	if http.Redirect != nil {
2214		if len(http.Route) > 0 {
2215			errs = appendErrors(errs, errors.New("HTTP route cannot contain both route and redirect"))
2216		}
2217
2218		if http.Fault != nil {
2219			errs = appendErrors(errs, errors.New("HTTP route cannot contain both fault and redirect"))
2220		}
2221
2222		if http.Rewrite != nil {
2223			errs = appendErrors(errs, errors.New("HTTP route rule cannot contain both rewrite and redirect"))
2224		}
2225	} else if len(http.Route) == 0 {
2226		errs = appendErrors(errs, errors.New("HTTP route or redirect is required"))
2227	}
2228
2229	// header manipulation
2230	for name := range http.Headers.GetRequest().GetAdd() {
2231		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2232	}
2233	for name := range http.Headers.GetRequest().GetSet() {
2234		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2235	}
2236	for _, name := range http.Headers.GetRequest().GetRemove() {
2237		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2238	}
2239	for name := range http.Headers.GetResponse().GetAdd() {
2240		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2241	}
2242	for name := range http.Headers.GetResponse().GetSet() {
2243		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2244	}
2245	for _, name := range http.Headers.GetResponse().GetRemove() {
2246		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2247	}
2248
2249	errs = appendErrors(errs, validateCORSPolicy(http.CorsPolicy))
2250	errs = appendErrors(errs, validateHTTPFaultInjection(http.Fault))
2251
2252	for _, match := range http.Match {
2253		if match != nil {
2254			for name, header := range match.Headers {
2255				if header == nil {
2256					errs = appendErrors(errs, fmt.Errorf("header match %v cannot be null", name))
2257				}
2258				errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2259				errs = appendErrors(errs, validateStringMatchRegexp(header, "headers"))
2260			}
2261
2262			if match.Port != 0 {
2263				errs = appendErrors(errs, ValidatePort(int(match.Port)))
2264			}
2265			errs = appendErrors(errs, labels.Instance(match.SourceLabels).Validate())
2266			errs = appendErrors(errs, validateGatewayNames(match.Gateways))
2267			errs = appendErrors(errs, validateStringMatchRegexp(match.GetUri(), "uri"))
2268			errs = appendErrors(errs, validateStringMatchRegexp(match.GetScheme(), "scheme"))
2269			errs = appendErrors(errs, validateStringMatchRegexp(match.GetMethod(), "method"))
2270			errs = appendErrors(errs, validateStringMatchRegexp(match.GetAuthority(), "authority"))
2271			for _, qp := range match.GetQueryParams() {
2272				errs = appendErrors(errs, validateStringMatchRegexp(qp, "queryParams"))
2273			}
2274		}
2275	}
2276
2277	if http.MirrorPercent != nil {
2278		if value := http.MirrorPercent.GetValue(); value > 100 {
2279			errs = appendErrors(errs, fmt.Errorf("mirror_percent must have a max value of 100 (it has %d)", value))
2280		}
2281	}
2282
2283	if http.MirrorPercentage != nil {
2284		if value := http.MirrorPercentage.GetValue(); value > 100 {
2285			errs = appendErrors(errs, fmt.Errorf("mirror_percentage must have a max value of 100 (it has %f)", value))
2286		}
2287	}
2288
2289	errs = appendErrors(errs, validateDestination(http.Mirror))
2290	errs = appendErrors(errs, validateHTTPRedirect(http.Redirect))
2291	errs = appendErrors(errs, validateHTTPRetry(http.Retries))
2292	errs = appendErrors(errs, validateHTTPRewrite(http.Rewrite))
2293	errs = appendErrors(errs, validateHTTPRouteDestinations(http.Route))
2294	if http.Timeout != nil {
2295		errs = appendErrors(errs, ValidateDurationGogo(http.Timeout))
2296	}
2297
2298	return
2299}
2300
2301func validateStringMatchRegexp(sm *networking.StringMatch, where string) error {
2302	re := sm.GetRegex()
2303	if re == "" {
2304		return nil
2305	}
2306
2307	_, err := regexp.Compile(re)
2308	if err == nil {
2309		return nil
2310	}
2311
2312	return fmt.Errorf("%q: %w; Istio uses RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax)", where, err)
2313}
2314
2315func validateGatewayNames(gatewayNames []string) (errs error) {
2316	for _, gatewayName := range gatewayNames {
2317		parts := strings.SplitN(gatewayName, "/", 2)
2318		if len(parts) != 2 {
2319			// deprecated
2320			// Old style spec with FQDN gateway name
2321			errs = appendErrors(errs, ValidateFQDN(gatewayName))
2322			return
2323		}
2324
2325		if len(parts[0]) == 0 || len(parts[1]) == 0 {
2326			errs = appendErrors(errs, fmt.Errorf("config namespace and gateway name cannot be empty"))
2327		}
2328
2329		// namespace and name must be DNS labels
2330		if !labels.IsDNS1123Label(parts[0]) {
2331			errs = appendErrors(errs, fmt.Errorf("invalid value for namespace: %q", parts[0]))
2332		}
2333
2334		if !labels.IsDNS1123Label(parts[1]) {
2335			errs = appendErrors(errs, fmt.Errorf("invalid value for gateway name: %q", parts[1]))
2336		}
2337	}
2338	return
2339}
2340
2341func validateHTTPRouteDestinations(weights []*networking.HTTPRouteDestination) (errs error) {
2342	var totalWeight int32
2343	for _, weight := range weights {
2344		if weight.Destination == nil {
2345			errs = multierror.Append(errs, errors.New("destination is required"))
2346		}
2347
2348		// header manipulations
2349		for name := range weight.Headers.GetRequest().GetAdd() {
2350			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2351		}
2352		for name := range weight.Headers.GetRequest().GetSet() {
2353			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2354		}
2355		for _, name := range weight.Headers.GetRequest().GetRemove() {
2356			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2357		}
2358		for name := range weight.Headers.GetResponse().GetAdd() {
2359			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2360		}
2361		for name := range weight.Headers.GetResponse().GetSet() {
2362			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2363		}
2364		for _, name := range weight.Headers.GetResponse().GetRemove() {
2365			errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2366		}
2367
2368		errs = appendErrors(errs, validateDestination(weight.Destination))
2369		errs = appendErrors(errs, ValidatePercent(weight.Weight))
2370		totalWeight += weight.Weight
2371	}
2372	if len(weights) > 1 && totalWeight != 100 {
2373		errs = appendErrors(errs, fmt.Errorf("total destination weight %v != 100", totalWeight))
2374	}
2375	return
2376}
2377
2378func validateRouteDestinations(weights []*networking.RouteDestination) (errs error) {
2379	var totalWeight int32
2380	for _, weight := range weights {
2381		if weight.Destination == nil {
2382			errs = multierror.Append(errs, errors.New("destination is required"))
2383		}
2384		errs = appendErrors(errs, validateDestination(weight.Destination))
2385		errs = appendErrors(errs, ValidatePercent(weight.Weight))
2386		totalWeight += weight.Weight
2387	}
2388	if len(weights) > 1 && totalWeight != 100 {
2389		errs = appendErrors(errs, fmt.Errorf("total destination weight %v != 100", totalWeight))
2390	}
2391	return
2392}
2393
2394func validateCORSPolicy(policy *networking.CorsPolicy) (errs error) {
2395	if policy == nil {
2396		return
2397	}
2398
2399	for _, origin := range policy.AllowOrigins {
2400		errs = appendErrors(errs, validateAllowOrigins(origin))
2401	}
2402
2403	for _, method := range policy.AllowMethods {
2404		errs = appendErrors(errs, validateHTTPMethod(method))
2405	}
2406
2407	for _, name := range policy.AllowHeaders {
2408		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2409	}
2410
2411	for _, name := range policy.ExposeHeaders {
2412		errs = appendErrors(errs, ValidateHTTPHeaderName(name))
2413	}
2414
2415	if policy.MaxAge != nil {
2416		errs = appendErrors(errs, ValidateDurationGogo(policy.MaxAge))
2417		if policy.MaxAge.Nanos > 0 {
2418			errs = multierror.Append(errs, errors.New("max_age duration is accurate only to seconds precision"))
2419		}
2420	}
2421
2422	return
2423}
2424
2425func validateAllowOrigins(origin *networking.StringMatch) error {
2426	var match string
2427	switch origin.MatchType.(type) {
2428	case *networking.StringMatch_Exact:
2429		match = origin.GetExact()
2430	case *networking.StringMatch_Prefix:
2431		match = origin.GetPrefix()
2432	case *networking.StringMatch_Regex:
2433		match = origin.GetRegex()
2434	}
2435	if match == "" {
2436		return fmt.Errorf("'%v' is not a valid match type for CORS allow origins", match)
2437	}
2438	return validateStringMatchRegexp(origin, "corsPolicy.allowOrigins")
2439}
2440
2441func validateHTTPMethod(method string) error {
2442	if !supportedMethods[method] {
2443		return fmt.Errorf("%q is not a supported HTTP method", method)
2444	}
2445	return nil
2446}
2447
2448func validateHTTPFaultInjection(fault *networking.HTTPFaultInjection) (errs error) {
2449	if fault == nil {
2450		return
2451	}
2452
2453	if fault.Abort == nil && fault.Delay == nil {
2454		errs = multierror.Append(errs, errors.New("HTTP fault injection must have an abort and/or a delay"))
2455	}
2456
2457	errs = appendErrors(errs, validateHTTPFaultInjectionAbort(fault.Abort))
2458	errs = appendErrors(errs, validateHTTPFaultInjectionDelay(fault.Delay))
2459
2460	return
2461}
2462
2463func validateHTTPFaultInjectionAbort(abort *networking.HTTPFaultInjection_Abort) (errs error) {
2464	if abort == nil {
2465		return
2466	}
2467
2468	errs = appendErrors(errs, validatePercentage(abort.Percentage))
2469
2470	switch abort.ErrorType.(type) {
2471	case *networking.HTTPFaultInjection_Abort_GrpcStatus:
2472		// TODO: gRPC status validation
2473		errs = multierror.Append(errs, errors.New("gRPC abort fault injection not supported yet"))
2474	case *networking.HTTPFaultInjection_Abort_Http2Error:
2475		// TODO: HTTP2 error validation
2476		errs = multierror.Append(errs, errors.New("HTTP/2 abort fault injection not supported yet"))
2477	case *networking.HTTPFaultInjection_Abort_HttpStatus:
2478		errs = appendErrors(errs, validateHTTPStatus(abort.GetHttpStatus()))
2479	}
2480
2481	return
2482}
2483
2484func validateHTTPStatus(status int32) error {
2485	if status < 200 || status > 600 {
2486		return fmt.Errorf("HTTP status %d is not in range 200-599", status)
2487	}
2488	return nil
2489}
2490
2491func validateHTTPFaultInjectionDelay(delay *networking.HTTPFaultInjection_Delay) (errs error) {
2492	if delay == nil {
2493		return
2494	}
2495
2496	errs = appendErrors(errs, validatePercentage(delay.Percentage))
2497
2498	switch v := delay.HttpDelayType.(type) {
2499	case *networking.HTTPFaultInjection_Delay_FixedDelay:
2500		errs = appendErrors(errs, ValidateDurationGogo(v.FixedDelay))
2501	case *networking.HTTPFaultInjection_Delay_ExponentialDelay:
2502		errs = appendErrors(errs, ValidateDurationGogo(v.ExponentialDelay))
2503		errs = multierror.Append(errs, fmt.Errorf("exponentialDelay not supported yet"))
2504	}
2505
2506	return
2507}
2508
2509func validateDestination(destination *networking.Destination) (errs error) {
2510	if destination == nil {
2511		return
2512	}
2513
2514	hostname := destination.Host
2515	if hostname == "*" {
2516		errs = appendErrors(errs, fmt.Errorf("invalid destination host %s", hostname))
2517	} else {
2518		errs = appendErrors(errs, ValidateWildcardDomain(hostname))
2519	}
2520	if destination.Subset != "" {
2521		errs = appendErrors(errs, validateSubsetName(destination.Subset))
2522	}
2523	if destination.Port != nil {
2524		errs = appendErrors(errs, validatePortSelector(destination.Port))
2525	}
2526
2527	return
2528}
2529
2530func validateSubsetName(name string) error {
2531	if len(name) == 0 {
2532		return fmt.Errorf("subset name cannot be empty")
2533	}
2534	if !labels.IsDNS1123Label(name) {
2535		return fmt.Errorf("subset name is invalid: %s", name)
2536	}
2537	return nil
2538}
2539
2540func validatePortSelector(selector *networking.PortSelector) (errs error) {
2541	if selector == nil {
2542		return nil
2543	}
2544
2545	// port must be a number
2546	number := int(selector.GetNumber())
2547	errs = appendErrors(errs, ValidatePort(number))
2548	return
2549}
2550
2551func validateAuthNPortSelector(selector *authn.PortSelector) error {
2552	if selector == nil {
2553		return nil
2554	}
2555
2556	// port selector is either a name or a number
2557	name := selector.GetName()
2558	number := int(selector.GetNumber())
2559	if name == "" && number == 0 {
2560		// an unset value is indistinguishable from a zero value, so return both errors
2561		return appendErrors(validateSubsetName(name), ValidatePort(number))
2562	} else if number != 0 {
2563		return ValidatePort(number)
2564	}
2565	return validateSubsetName(name)
2566}
2567
2568func validateHTTPRetry(retries *networking.HTTPRetry) (errs error) {
2569	if retries == nil {
2570		return
2571	}
2572
2573	if retries.Attempts < 0 {
2574		errs = multierror.Append(errs, errors.New("attempts cannot be negative"))
2575	}
2576
2577	if retries.Attempts == 0 && (retries.PerTryTimeout != nil || retries.RetryOn != "" || retries.RetryRemoteLocalities != nil) {
2578		errs = appendErrors(errs, errors.New("http retry policy configured when attempts are set to 0 (disabled)"))
2579	}
2580
2581	if retries.PerTryTimeout != nil {
2582		errs = appendErrors(errs, ValidateDurationGogo(retries.PerTryTimeout))
2583	}
2584	if retries.RetryOn != "" {
2585		retryOnPolicies := strings.Split(retries.RetryOn, ",")
2586		for _, policy := range retryOnPolicies {
2587			// Try converting it to an integer to see if it's a valid HTTP status code.
2588			i, _ := strconv.Atoi(policy)
2589
2590			if http.StatusText(i) == "" && !supportedRetryOnPolicies[policy] {
2591				errs = appendErrors(errs, fmt.Errorf("%q is not a valid retryOn policy", policy))
2592			}
2593		}
2594	}
2595
2596	return
2597}
2598
2599func validateHTTPRedirect(redirect *networking.HTTPRedirect) error {
2600	if redirect != nil && redirect.Uri == "" && redirect.Authority == "" {
2601		return errors.New("redirect must specify URI, authority, or both")
2602	}
2603
2604	if redirect != nil && redirect.RedirectCode != 0 {
2605		if redirect.RedirectCode < 300 || redirect.RedirectCode > 399 {
2606			return fmt.Errorf("%d is not a valid redirect code, must be 3xx", redirect.RedirectCode)
2607		}
2608	}
2609	return nil
2610}
2611
2612func validateHTTPRewrite(rewrite *networking.HTTPRewrite) error {
2613	if rewrite != nil && rewrite.Uri == "" && rewrite.Authority == "" {
2614		return errors.New("rewrite must specify URI, authority, or both")
2615	}
2616	return nil
2617}
2618
2619// ValidateWorkloadEntry validates a workload entry.
2620var ValidateWorkloadEntry = registerValidateFunc("ValidateWorkloadEntry",
2621	func(_, _ string, config proto.Message) (errs error) {
2622		we, ok := config.(*networking.WorkloadEntry)
2623		if !ok {
2624			return fmt.Errorf("cannot cast to workload entry")
2625		}
2626		if we.Address == "" {
2627			return fmt.Errorf("address must be set")
2628		}
2629		// TODO: add better validation. The tricky thing is that we don't know if its meant to be
2630		// DNS or STATIC type without association with a ServiceEntry
2631		return nil
2632	})
2633
2634// ValidateServiceEntry validates a service entry.
2635var ValidateServiceEntry = registerValidateFunc("ValidateServiceEntry",
2636	func(_, _ string, config proto.Message) (errs error) {
2637		serviceEntry, ok := config.(*networking.ServiceEntry)
2638		if !ok {
2639			return fmt.Errorf("cannot cast to service entry")
2640		}
2641
2642		if err := validateAlphaWorkloadSelector(serviceEntry.WorkloadSelector); err != nil {
2643			return err
2644		}
2645
2646		if serviceEntry.WorkloadSelector != nil && serviceEntry.Endpoints != nil {
2647			errs = appendErrors(errs, fmt.Errorf("only one of WorkloadSelector or Endpoints is allowed in Service Entry"))
2648		}
2649
2650		if len(serviceEntry.Hosts) == 0 {
2651			errs = appendErrors(errs, fmt.Errorf("service entry must have at least one host"))
2652		}
2653		for _, hostname := range serviceEntry.Hosts {
2654			// Full wildcard is not allowed in the service entry.
2655			if hostname == "*" {
2656				errs = appendErrors(errs, fmt.Errorf("invalid host %s", hostname))
2657			} else {
2658				errs = appendErrors(errs, ValidateWildcardDomain(hostname))
2659			}
2660		}
2661
2662		cidrFound := false
2663		for _, address := range serviceEntry.Addresses {
2664			cidrFound = cidrFound || strings.Contains(address, "/")
2665			errs = appendErrors(errs, ValidateIPSubnet(address))
2666		}
2667
2668		if cidrFound {
2669			if serviceEntry.Resolution != networking.ServiceEntry_NONE && serviceEntry.Resolution != networking.ServiceEntry_STATIC {
2670				errs = appendErrors(errs, fmt.Errorf("CIDR addresses are allowed only for NONE/STATIC resolution types"))
2671			}
2672		}
2673
2674		servicePortNumbers := make(map[uint32]bool)
2675		servicePorts := make(map[string]bool, len(serviceEntry.Ports))
2676		for _, port := range serviceEntry.Ports {
2677			if servicePorts[port.Name] {
2678				errs = appendErrors(errs, fmt.Errorf("service entry port name %q already defined", port.Name))
2679			}
2680			servicePorts[port.Name] = true
2681			if servicePortNumbers[port.Number] {
2682				errs = appendErrors(errs, fmt.Errorf("service entry port %d already defined", port.Number))
2683			}
2684			servicePortNumbers[port.Number] = true
2685		}
2686
2687		switch serviceEntry.Resolution {
2688		case networking.ServiceEntry_NONE:
2689			if len(serviceEntry.Endpoints) != 0 {
2690				errs = appendErrors(errs, fmt.Errorf("no endpoints should be provided for resolution type none"))
2691			}
2692		case networking.ServiceEntry_STATIC:
2693			unixEndpoint := false
2694			for _, endpoint := range serviceEntry.Endpoints {
2695				addr := endpoint.GetAddress()
2696				if strings.HasPrefix(addr, UnixAddressPrefix) {
2697					unixEndpoint = true
2698					errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(addr, UnixAddressPrefix)))
2699					if len(endpoint.Ports) != 0 {
2700						errs = appendErrors(errs, fmt.Errorf("unix endpoint %s must not include ports", addr))
2701					}
2702				} else {
2703					errs = appendErrors(errs, ValidateIPAddress(addr))
2704
2705					for name, port := range endpoint.Ports {
2706						if !servicePorts[name] {
2707							errs = appendErrors(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port))
2708						}
2709					}
2710				}
2711				errs = appendErrors(errs, labels.Instance(endpoint.Labels).Validate())
2712
2713			}
2714			if unixEndpoint && len(serviceEntry.Ports) != 1 {
2715				errs = appendErrors(errs, errors.New("exactly 1 service port required for unix endpoints"))
2716			}
2717		case networking.ServiceEntry_DNS:
2718			if len(serviceEntry.Endpoints) == 0 {
2719				for _, hostname := range serviceEntry.Hosts {
2720					if err := ValidateFQDN(hostname); err != nil {
2721						errs = appendErrors(errs,
2722							fmt.Errorf("hosts must be FQDN if no endpoints are provided for resolution mode DNS"))
2723					}
2724				}
2725			}
2726
2727			for _, endpoint := range serviceEntry.Endpoints {
2728				ipAddr := net.ParseIP(endpoint.Address) // Typically it is an IP address
2729				if ipAddr == nil {
2730					if err := ValidateFQDN(endpoint.Address); err != nil { // Otherwise could be an FQDN
2731						errs = appendErrors(errs,
2732							fmt.Errorf("endpoint address %q is not a valid FQDN or an IP address", endpoint.Address))
2733					}
2734				}
2735				errs = appendErrors(errs,
2736					labels.Instance(endpoint.Labels).Validate())
2737				for name, port := range endpoint.Ports {
2738					if !servicePorts[name] {
2739						errs = appendErrors(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port))
2740					}
2741					errs = appendErrors(errs,
2742						ValidatePortName(name),
2743						ValidatePort(int(port)))
2744				}
2745			}
2746		default:
2747			errs = appendErrors(errs, fmt.Errorf("unsupported resolution type %s",
2748				networking.ServiceEntry_Resolution_name[int32(serviceEntry.Resolution)]))
2749		}
2750
2751		// multiple hosts and TCP is invalid unless the resolution type is NONE.
2752		// depending on the protocol, we can differentiate between hosts when proxying:
2753		// - with HTTP, the authority header can be used
2754		// - with HTTPS/TLS with SNI, the ServerName can be used
2755		// however, for plain TCP there is no way to differentiate between the
2756		// hosts so we consider it invalid, unless the resolution type is NONE
2757		// (because the hosts are ignored).
2758		if serviceEntry.Resolution != networking.ServiceEntry_NONE && len(serviceEntry.Hosts) > 1 {
2759			canDifferentiate := true
2760			for _, port := range serviceEntry.Ports {
2761				p := protocol.Parse(port.Protocol)
2762				if !p.IsHTTP() && !p.IsTLS() {
2763					canDifferentiate = false
2764					break
2765				}
2766			}
2767
2768			if !canDifferentiate {
2769				errs = appendErrors(errs, fmt.Errorf("multiple hosts provided with non-HTTP, non-TLS ports"))
2770			}
2771		}
2772
2773		for _, port := range serviceEntry.Ports {
2774			errs = appendErrors(errs,
2775				ValidatePortName(port.Name),
2776				ValidateProtocol(port.Protocol),
2777				ValidatePort(int(port.Number)))
2778		}
2779
2780		errs = appendErrors(errs, validateExportTo(serviceEntry.ExportTo))
2781		return
2782	})
2783
2784func ValidatePortName(name string) error {
2785	if !labels.IsDNS1123Label(name) {
2786		return fmt.Errorf("invalid port name: %s", name)
2787	}
2788	return nil
2789}
2790
2791func ValidateProtocol(protocolStr string) error {
2792	// Empty string is used for protocol sniffing.
2793	if protocolStr != "" && protocol.Parse(protocolStr) == protocol.Unsupported {
2794		return fmt.Errorf("unsupported protocol: %s", protocolStr)
2795	}
2796	return nil
2797}
2798
2799// wrapper around multierror.Append that enforces the invariant that if all input errors are nil, the output
2800// error is nil (allowing validation without branching).
2801func appendErrors(err error, errs ...error) error {
2802	appendError := func(err, err2 error) error {
2803		if err == nil {
2804			return err2
2805		} else if err2 == nil {
2806			return err
2807		}
2808		return multierror.Append(err, err2)
2809	}
2810
2811	for _, err2 := range errs {
2812		err = appendError(err, err2)
2813	}
2814	return err
2815}
2816
2817// validateLocalityLbSetting checks the LocalityLbSetting of MeshConfig
2818func validateLocalityLbSetting(lb *networking.LocalityLoadBalancerSetting) error {
2819	if lb == nil {
2820		return nil
2821	}
2822
2823	if len(lb.GetDistribute()) > 0 && len(lb.GetFailover()) > 0 {
2824		return fmt.Errorf("can not simultaneously specify 'distribute' and 'failover'")
2825	}
2826
2827	srcLocalities := make([]string, 0)
2828	for _, locality := range lb.GetDistribute() {
2829		srcLocalities = append(srcLocalities, locality.From)
2830		var totalWeight uint32
2831		destLocalities := make([]string, 0)
2832		for loc, weight := range locality.To {
2833			destLocalities = append(destLocalities, loc)
2834			if weight == 0 {
2835				return fmt.Errorf("locality weight must be in range [1, 100]")
2836			}
2837			totalWeight += weight
2838		}
2839		if totalWeight != 100 {
2840			return fmt.Errorf("total locality weight %v != 100", totalWeight)
2841		}
2842		if err := validateLocalities(destLocalities); err != nil {
2843			return err
2844		}
2845	}
2846
2847	if err := validateLocalities(srcLocalities); err != nil {
2848		return err
2849	}
2850
2851	for _, failover := range lb.GetFailover() {
2852		if failover.From == failover.To {
2853			return fmt.Errorf("locality lb failover settings must specify different regions")
2854		}
2855		if strings.Contains(failover.To, "*") {
2856			return fmt.Errorf("locality lb failover region should not contain '*' wildcard")
2857		}
2858	}
2859
2860	return nil
2861}
2862
2863func validateLocalities(localities []string) error {
2864	regionZoneSubZoneMap := map[string]map[string]map[string]bool{}
2865
2866	for _, locality := range localities {
2867		if n := strings.Count(locality, "*"); n > 0 {
2868			if n > 1 || !strings.HasSuffix(locality, "*") {
2869				return fmt.Errorf("locality %s wildcard '*' number can not exceed 1 and must be in the end", locality)
2870			}
2871		}
2872
2873		items := strings.SplitN(locality, "/", 3)
2874		for _, item := range items {
2875			if item == "" {
2876				return fmt.Errorf("locality %s must not contain empty region/zone/subzone info", locality)
2877			}
2878		}
2879		if _, ok := regionZoneSubZoneMap["*"]; ok {
2880			return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2881		}
2882		switch len(items) {
2883		case 1:
2884			if _, ok := regionZoneSubZoneMap[items[0]]; ok {
2885				return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2886			}
2887			regionZoneSubZoneMap[items[0]] = map[string]map[string]bool{"*": {"*": true}}
2888		case 2:
2889			if _, ok := regionZoneSubZoneMap[items[0]]; ok {
2890				if _, ok := regionZoneSubZoneMap[items[0]]["*"]; ok {
2891					return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2892				}
2893				if _, ok := regionZoneSubZoneMap[items[0]][items[1]]; ok {
2894					return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2895				}
2896				regionZoneSubZoneMap[items[0]][items[1]] = map[string]bool{"*": true}
2897			} else {
2898				regionZoneSubZoneMap[items[0]] = map[string]map[string]bool{items[1]: {"*": true}}
2899			}
2900		case 3:
2901			if _, ok := regionZoneSubZoneMap[items[0]]; ok {
2902				if _, ok := regionZoneSubZoneMap[items[0]]["*"]; ok {
2903					return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2904				}
2905				if _, ok := regionZoneSubZoneMap[items[0]][items[1]]; ok {
2906					if regionZoneSubZoneMap[items[0]][items[1]]["*"] {
2907						return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2908					}
2909					if regionZoneSubZoneMap[items[0]][items[1]][items[2]] {
2910						return fmt.Errorf("locality %s overlap with previous specified ones", locality)
2911					}
2912					regionZoneSubZoneMap[items[0]][items[1]][items[2]] = true
2913				} else {
2914					regionZoneSubZoneMap[items[0]][items[1]] = map[string]bool{items[2]: true}
2915				}
2916			} else {
2917				regionZoneSubZoneMap[items[0]] = map[string]map[string]bool{items[1]: {items[2]: true}}
2918			}
2919		}
2920	}
2921
2922	return nil
2923}
2924
2925// ValidateMeshNetworks validates meshnetworks.
2926func ValidateMeshNetworks(meshnetworks *meshconfig.MeshNetworks) (errs error) {
2927	for name, network := range meshnetworks.Networks {
2928		if err := validateNetwork(network); err != nil {
2929			errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid network %v:", name)))
2930		}
2931	}
2932	return
2933}
2934
2935func validateNetwork(network *meshconfig.Network) (errs error) {
2936	for _, n := range network.Endpoints {
2937		switch e := n.Ne.(type) {
2938		case *meshconfig.Network_NetworkEndpoints_FromCidr:
2939			if err := ValidateIPSubnet(e.FromCidr); err != nil {
2940				errs = multierror.Append(errs, err)
2941			}
2942		case *meshconfig.Network_NetworkEndpoints_FromRegistry:
2943			if ok := labels.IsDNS1123Label(e.FromRegistry); !ok {
2944				errs = multierror.Append(errs, fmt.Errorf("invalid registry name: %v", e.FromRegistry))
2945			}
2946		}
2947
2948	}
2949	for _, n := range network.Gateways {
2950		switch g := n.Gw.(type) {
2951		case *meshconfig.Network_IstioNetworkGateway_RegistryServiceName:
2952			if err := ValidateFQDN(g.RegistryServiceName); err != nil {
2953				errs = multierror.Append(errs, err)
2954			}
2955		case *meshconfig.Network_IstioNetworkGateway_Address:
2956			if err := ValidateIPAddress(g.Address); err != nil {
2957				errs = multierror.Append(errs, err)
2958			}
2959		}
2960		if err := ValidatePort(int(n.Port)); err != nil {
2961			errs = multierror.Append(errs, err)
2962		}
2963	}
2964	return
2965}
2966