istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/validation/agent/extensionprovider.go (about) 1 // Copyright 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 15 package agent 16 17 import ( 18 "fmt" 19 "net/url" 20 "strconv" 21 "strings" 22 23 envoytypev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" 24 "github.com/hashicorp/go-multierror" 25 26 meshconfig "istio.io/api/mesh/v1alpha1" 27 "istio.io/istio/pkg/util/sets" 28 ) 29 30 func validateExtensionProviderService(service string) error { 31 if service == "" { 32 return fmt.Errorf("service must not be empty") 33 } 34 parts := strings.Split(service, "/") 35 if len(parts) == 1 { 36 if err := ValidateFQDN(service); err != nil { 37 if err2 := ValidateIPAddress(service); err2 != nil { 38 return fmt.Errorf("invalid service fmt %s: %s", service, err2) 39 } 40 } 41 } else { 42 if err := ValidateNamespaceSlashWildcardHostname(service, false, false); err != nil { 43 return err 44 } 45 } 46 return nil 47 } 48 49 func validateExtensionProviderEnvoyExtAuthzStatusOnError(status string) error { 50 if status == "" { 51 return nil 52 } 53 code, err := strconv.ParseInt(status, 10, 32) 54 if err != nil { 55 return fmt.Errorf("invalid statusOnError value %s: %v", status, err) 56 } 57 if _, found := envoytypev3.StatusCode_name[int32(code)]; !found { 58 return fmt.Errorf("unsupported statusOnError value %s, supported values: %v", status, envoytypev3.StatusCode_name) 59 } 60 return nil 61 } 62 63 func ValidateExtensionProviderEnvoyExtAuthzHTTP(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) (errs error) { 64 if config == nil { 65 return fmt.Errorf("nil EnvoyExternalAuthorizationHttpProvider") 66 } 67 if err := ValidatePort(int(config.Port)); err != nil { 68 errs = AppendErrors(errs, err) 69 } 70 if err := validateExtensionProviderService(config.Service); err != nil { 71 errs = AppendErrors(errs, err) 72 } 73 if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil { 74 errs = AppendErrors(errs, err) 75 } 76 if config.PathPrefix != "" { 77 if _, err := url.Parse(config.PathPrefix); err != nil { 78 errs = AppendErrors(errs, fmt.Errorf("invalid pathPrefix %s: %v", config.PathPrefix, err)) 79 } 80 if !strings.HasPrefix(config.PathPrefix, "/") { 81 errs = AppendErrors(errs, fmt.Errorf("pathPrefix should begin with `/` but found %q", config.PathPrefix)) 82 } 83 } 84 return 85 } 86 87 func ValidateExtensionProviderEnvoyExtAuthzGRPC(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider) (errs error) { 88 if config == nil { 89 return fmt.Errorf("nil EnvoyExternalAuthorizationGrpcProvider") 90 } 91 if err := ValidatePort(int(config.Port)); err != nil { 92 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 93 } 94 if err := validateExtensionProviderService(config.Service); err != nil { 95 errs = AppendErrors(errs, err) 96 } 97 if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil { 98 errs = AppendErrors(errs, err) 99 } 100 return 101 } 102 103 func validateExtensionProviderTracingZipkin(config *meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider) (errs error) { 104 if config == nil { 105 return fmt.Errorf("nil TracingZipkinProvider") 106 } 107 if err := validateExtensionProviderService(config.Service); err != nil { 108 errs = AppendErrors(errs, err) 109 } 110 if err := ValidatePort(int(config.Port)); err != nil { 111 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 112 } 113 return 114 } 115 116 func validateExtensionProviderTracingLightStep(config *meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider) (errs error) { 117 if config == nil { 118 return fmt.Errorf("nil TracingLightStepProvider") 119 } 120 if err := validateExtensionProviderService(config.Service); err != nil { 121 errs = AppendErrors(errs, err) 122 } 123 if err := ValidatePort(int(config.Port)); err != nil { 124 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 125 } 126 if config.AccessToken == "" { 127 errs = AppendErrors(errs, fmt.Errorf("access token is required")) 128 } 129 return 130 } 131 132 func validateExtensionProviderTracingDatadog(config *meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider) (errs error) { 133 if config == nil { 134 return fmt.Errorf("nil TracingDatadogProvider") 135 } 136 if err := validateExtensionProviderService(config.Service); err != nil { 137 errs = AppendErrors(errs, err) 138 } 139 if err := ValidatePort(int(config.Port)); err != nil { 140 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 141 } 142 return 143 } 144 145 func validateExtensionProviderTracingOpenCensusAgent(config *meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider) (errs error) { 146 if config == nil { 147 return fmt.Errorf("nil OpenCensusAgent") 148 } 149 if err := validateExtensionProviderService(config.Service); err != nil { 150 errs = AppendErrors(errs, err) 151 } 152 if err := ValidatePort(int(config.Port)); err != nil { 153 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 154 } 155 return 156 } 157 158 func validateExtensionProviderTracingSkyWalking(config *meshconfig.MeshConfig_ExtensionProvider_SkyWalkingTracingProvider) (errs error) { 159 if config == nil { 160 return fmt.Errorf("nil TracingSkyWalkingProvider") 161 } 162 if err := validateExtensionProviderService(config.Service); err != nil { 163 errs = AppendErrors(errs, err) 164 } 165 if err := ValidatePort(int(config.Port)); err != nil { 166 errs = AppendErrors(errs, fmt.Errorf("invalid service port: %v", err)) 167 } 168 return 169 } 170 171 func validateExtensionProviderMetricsPrometheus(_ *meshconfig.MeshConfig_ExtensionProvider_PrometheusMetricsProvider) error { 172 return nil 173 } 174 175 func validateExtensionProviderStackdriver(_ *meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider) error { 176 return nil 177 } 178 179 func validateExtensionProviderEnvoyFileAccessLog(_ *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider) error { 180 return nil 181 } 182 183 func ValidateExtensionProviderEnvoyOtelAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider) (errs error) { 184 if provider == nil { 185 return fmt.Errorf("nil EnvoyOpenTelemetryLogProvider") 186 } 187 if err := ValidatePort(int(provider.Port)); err != nil { 188 errs = AppendErrors(errs, err) 189 } 190 if err := validateExtensionProviderService(provider.Service); err != nil { 191 errs = AppendErrors(errs, err) 192 } 193 return 194 } 195 196 func ValidateExtensionProviderTracingOpentelemetry(provider *meshconfig.MeshConfig_ExtensionProvider_OpenTelemetryTracingProvider) (errs error) { 197 if provider == nil { 198 return fmt.Errorf("nil OpenTelemetryTracingProvider") 199 } 200 if err := ValidatePort(int(provider.Port)); err != nil { 201 errs = AppendErrors(errs, err) 202 } 203 if err := validateExtensionProviderService(provider.Service); err != nil { 204 errs = AppendErrors(errs, err) 205 } 206 return 207 } 208 209 func ValidateExtensionProviderEnvoyHTTPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) (errs error) { 210 if provider == nil { 211 return fmt.Errorf("nil EnvoyHttpGrpcV3LogProvider") 212 } 213 if err := ValidatePort(int(provider.Port)); err != nil { 214 errs = AppendErrors(errs, err) 215 } 216 if err := validateExtensionProviderService(provider.Service); err != nil { 217 errs = AppendErrors(errs, err) 218 } 219 return 220 } 221 222 func ValidateExtensionProviderEnvoyTCPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) (errs error) { 223 if provider == nil { 224 return fmt.Errorf("nil EnvoyTcpGrpcV3LogProvider") 225 } 226 if err := ValidatePort(int(provider.Port)); err != nil { 227 errs = AppendErrors(errs, err) 228 } 229 if err := validateExtensionProviderService(provider.Service); err != nil { 230 errs = AppendErrors(errs, err) 231 } 232 return 233 } 234 235 func validateExtensionProvider(config *meshconfig.MeshConfig) (errs error) { 236 definedProviders := sets.String{} 237 for _, c := range config.ExtensionProviders { 238 var currentErrs error 239 // Provider name must be unique and not empty. 240 if c.Name == "" { 241 currentErrs = AppendErrors(currentErrs, fmt.Errorf("empty extension provider name")) 242 } else { 243 if definedProviders.Contains(c.Name) { 244 currentErrs = AppendErrors(currentErrs, fmt.Errorf("duplicate extension provider name %s", c.Name)) 245 } 246 definedProviders.Insert(c.Name) 247 } 248 249 switch provider := c.Provider.(type) { 250 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp: 251 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzHTTP(provider.EnvoyExtAuthzHttp)) 252 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc: 253 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzGRPC(provider.EnvoyExtAuthzGrpc)) 254 case *meshconfig.MeshConfig_ExtensionProvider_Zipkin: 255 currentErrs = AppendErrors(currentErrs, validateExtensionProviderTracingZipkin(provider.Zipkin)) 256 //nolint: staticcheck // Lightstep deprecated 257 case *meshconfig.MeshConfig_ExtensionProvider_Lightstep: 258 currentErrs = AppendErrors(currentErrs, validateExtensionProviderTracingLightStep(provider.Lightstep)) 259 case *meshconfig.MeshConfig_ExtensionProvider_Datadog: 260 currentErrs = AppendErrors(currentErrs, validateExtensionProviderTracingDatadog(provider.Datadog)) 261 //nolint: staticcheck 262 case *meshconfig.MeshConfig_ExtensionProvider_Opencensus: 263 currentErrs = AppendErrors(currentErrs, validateExtensionProviderTracingOpenCensusAgent(provider.Opencensus)) 264 case *meshconfig.MeshConfig_ExtensionProvider_Skywalking: 265 currentErrs = AppendErrors(currentErrs, validateExtensionProviderTracingSkyWalking(provider.Skywalking)) 266 case *meshconfig.MeshConfig_ExtensionProvider_Prometheus: 267 currentErrs = AppendErrors(currentErrs, validateExtensionProviderMetricsPrometheus(provider.Prometheus)) 268 case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: 269 currentErrs = AppendErrors(currentErrs, validateExtensionProviderStackdriver(provider.Stackdriver)) 270 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog: 271 currentErrs = AppendErrors(currentErrs, validateExtensionProviderEnvoyFileAccessLog(provider.EnvoyFileAccessLog)) 272 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls: 273 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderEnvoyOtelAls(provider.EnvoyOtelAls)) 274 case *meshconfig.MeshConfig_ExtensionProvider_Opentelemetry: 275 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderTracingOpentelemetry(provider.Opentelemetry)) 276 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls: 277 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderEnvoyHTTPAls(provider.EnvoyHttpAls)) 278 case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls: 279 currentErrs = AppendErrors(currentErrs, ValidateExtensionProviderEnvoyTCPAls(provider.EnvoyTcpAls)) 280 // TODO: add exhaustiveness test 281 default: 282 currentErrs = AppendErrors(currentErrs, fmt.Errorf("unsupported provider: %v of type %T", provider, provider)) 283 } 284 currentErrs = multierror.Prefix(currentErrs, fmt.Sprintf("invalid extension provider %s:", c.Name)) 285 errs = AppendErrors(errs, currentErrs) 286 } 287 return 288 }