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