istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/mesh/mesh.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 mesh 16 17 import ( 18 "fmt" 19 "os" 20 "time" 21 22 "github.com/hashicorp/go-multierror" 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/types/known/durationpb" 25 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 26 "sigs.k8s.io/yaml" 27 28 meshconfig "istio.io/api/mesh/v1alpha1" 29 "istio.io/api/networking/v1alpha3" 30 "istio.io/istio/pkg/config/constants" 31 "istio.io/istio/pkg/config/validation/agent" 32 "istio.io/istio/pkg/log" 33 "istio.io/istio/pkg/ptr" 34 "istio.io/istio/pkg/util/protomarshal" 35 "istio.io/istio/pkg/util/sets" 36 ) 37 38 // DefaultProxyConfig for individual proxies 39 func DefaultProxyConfig() *meshconfig.ProxyConfig { 40 // TODO: include revision based on REVISION env 41 // TODO: set default namespace based on POD_NAMESPACE env 42 return &meshconfig.ProxyConfig{ 43 ConfigPath: constants.ConfigPathDir, 44 ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: constants.ServiceClusterName}, 45 DrainDuration: durationpb.New(45 * time.Second), 46 TerminationDrainDuration: durationpb.New(5 * time.Second), 47 ProxyAdminPort: 15000, 48 ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, 49 DiscoveryAddress: "istiod.istio-system.svc:15012", 50 51 // Code defaults 52 BinaryPath: constants.BinaryPathFilename, 53 StatNameLength: 189, 54 StatusPort: 15020, 55 } 56 } 57 58 // DefaultMeshNetworks returns a default meshnetworks configuration. 59 // By default, it is empty. 60 func DefaultMeshNetworks() *meshconfig.MeshNetworks { 61 return ptr.Of(EmptyMeshNetworks()) 62 } 63 64 // DefaultMeshConfig returns the default mesh config. 65 // This is merged with values from the mesh config map. 66 func DefaultMeshConfig() *meshconfig.MeshConfig { 67 proxyConfig := DefaultProxyConfig() 68 69 // Defaults matching the standard install 70 // order matches the generated mesh config. 71 return &meshconfig.MeshConfig{ 72 EnableTracing: true, 73 AccessLogFile: "", 74 AccessLogEncoding: meshconfig.MeshConfig_TEXT, 75 AccessLogFormat: "", 76 EnableEnvoyAccessLogService: false, 77 ProtocolDetectionTimeout: durationpb.New(0), 78 IngressService: "istio-ingressgateway", 79 IngressControllerMode: meshconfig.MeshConfig_STRICT, 80 IngressClass: "istio", 81 TrustDomain: constants.DefaultClusterLocalDomain, 82 TrustDomainAliases: []string{}, 83 EnableAutoMtls: wrappers.Bool(true), 84 OutboundTrafficPolicy: &meshconfig.MeshConfig_OutboundTrafficPolicy{Mode: meshconfig.MeshConfig_OutboundTrafficPolicy_ALLOW_ANY}, 85 InboundTrafficPolicy: &meshconfig.MeshConfig_InboundTrafficPolicy{Mode: meshconfig.MeshConfig_InboundTrafficPolicy_PASSTHROUGH}, 86 LocalityLbSetting: &v1alpha3.LocalityLoadBalancerSetting{ 87 Enabled: wrappers.Bool(true), 88 }, 89 Certificates: []*meshconfig.Certificate{}, 90 DefaultConfig: proxyConfig, 91 92 RootNamespace: constants.IstioSystemNamespace, 93 ProxyListenPort: 15001, 94 ProxyInboundListenPort: 15006, 95 ConnectTimeout: durationpb.New(10 * time.Second), 96 DefaultServiceExportTo: []string{"*"}, 97 DefaultVirtualServiceExportTo: []string{"*"}, 98 DefaultDestinationRuleExportTo: []string{"*"}, 99 // DnsRefreshRate is only used when DNS requests fail (NXDOMAIN or SERVFAIL). For success, the TTL 100 // will be used. 101 // https://datatracker.ietf.org/doc/html/rfc2308#section-3 defines how negative DNS results should handle TTLs, 102 // but Envoy does not respect this (https://github.com/envoyproxy/envoy/issues/20885). 103 // To counter this, we bump up the default to 60s to avoid overloading DNS servers. 104 DnsRefreshRate: durationpb.New(60 * time.Second), 105 ServiceSettings: make([]*meshconfig.MeshConfig_ServiceSettings, 0), 106 107 EnablePrometheusMerge: wrappers.Bool(true), 108 DefaultProviders: &meshconfig.MeshConfig_DefaultProviders{}, 109 ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ 110 { 111 Name: "prometheus", 112 Provider: &meshconfig.MeshConfig_ExtensionProvider_Prometheus{ 113 Prometheus: &meshconfig.MeshConfig_ExtensionProvider_PrometheusMetricsProvider{}, 114 }, 115 }, 116 { 117 Name: "stackdriver", 118 Provider: &meshconfig.MeshConfig_ExtensionProvider_Stackdriver{ 119 Stackdriver: &meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider{}, 120 }, 121 }, 122 { 123 Name: "envoy", 124 Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ 125 EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ 126 Path: "/dev/stdout", 127 }, 128 }, 129 }, 130 }, 131 } 132 } 133 134 // ApplyProxyConfig applies the give proxy config yaml to a mesh config object. The passed in mesh config 135 // will not be modified. 136 func ApplyProxyConfig(yaml string, meshConfig *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { 137 mc := proto.Clone(meshConfig).(*meshconfig.MeshConfig) 138 pc, err := MergeProxyConfig(yaml, mc.DefaultConfig) 139 if err != nil { 140 return nil, err 141 } 142 mc.DefaultConfig = pc 143 return mc, nil 144 } 145 146 // MergeProxyConfig merges the given proxy config yaml with the given proxy config object. 147 func MergeProxyConfig(yaml string, proxyConfig *meshconfig.ProxyConfig) (*meshconfig.ProxyConfig, error) { 148 origMetadata := proxyConfig.ProxyMetadata 149 origProxyHeaders := proxyConfig.ProxyHeaders 150 if err := protomarshal.ApplyYAML(yaml, proxyConfig); err != nil { 151 return nil, fmt.Errorf("could not parse proxy config: %v", err) 152 } 153 newMetadata := proxyConfig.ProxyMetadata 154 proxyConfig.ProxyMetadata = mergeMap(origMetadata, newMetadata) 155 correctProxyHeaders(proxyConfig, origProxyHeaders) 156 return proxyConfig, nil 157 } 158 159 func correctProxyHeaders(proxyConfig *meshconfig.ProxyConfig, orig *meshconfig.ProxyConfig_ProxyHeaders) { 160 ph := proxyConfig.ProxyHeaders 161 if ph != nil && orig != nil { 162 ph.ForwardedClientCert = ptr.NonEmptyOrDefault(ph.ForwardedClientCert, orig.ForwardedClientCert) 163 ph.RequestId = ptr.NonEmptyOrDefault(ph.RequestId, orig.RequestId) 164 ph.AttemptCount = ptr.NonEmptyOrDefault(ph.AttemptCount, orig.AttemptCount) 165 ph.Server = ptr.NonEmptyOrDefault(ph.Server, orig.Server) 166 ph.EnvoyDebugHeaders = ptr.NonEmptyOrDefault(ph.EnvoyDebugHeaders, orig.EnvoyDebugHeaders) 167 } 168 } 169 170 func extractYamlField(key string, mp map[string]any) (string, error) { 171 proxyConfig := mp[key] 172 if proxyConfig == nil { 173 return "", nil 174 } 175 bytes, err := yaml.Marshal(proxyConfig) 176 if err != nil { 177 return "", err 178 } 179 return string(bytes), nil 180 } 181 182 func toMap(yamlText string) (map[string]any, error) { 183 mp := map[string]any{} 184 if err := yaml.Unmarshal([]byte(yamlText), &mp); err != nil { 185 return nil, err 186 } 187 return mp, nil 188 } 189 190 // ApplyMeshConfig returns a new MeshConfig decoded from the 191 // input YAML with the provided defaults applied to omitted configuration values. 192 func ApplyMeshConfig(yaml string, defaultConfig *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { 193 // We want to keep semantics that all fields are overrides, except proxy config is a merge. This allows 194 // decent customization while also not requiring users to redefine the entire proxy config if they want to override 195 // Note: if we want to add more structure in the future, we will likely need to revisit this idea. 196 197 // Store the current set proxy config so we don't wipe it out, we will configure this later 198 prevProxyConfig := defaultConfig.DefaultConfig 199 prevDefaultProvider := defaultConfig.DefaultProviders 200 prevExtensionProviders := defaultConfig.ExtensionProviders 201 prevTrustDomainAliases := defaultConfig.TrustDomainAliases 202 203 defaultConfig.DefaultConfig = DefaultProxyConfig() 204 if err := protomarshal.ApplyYAML(yaml, defaultConfig); err != nil { 205 return nil, multierror.Prefix(err, "failed to convert to proto.") 206 } 207 defaultConfig.DefaultConfig = prevProxyConfig 208 209 raw, err := toMap(yaml) 210 if err != nil { 211 return nil, err 212 } 213 // Get just the proxy config yaml 214 pc, err := extractYamlField("defaultConfig", raw) 215 if err != nil { 216 return nil, multierror.Prefix(err, "failed to extract proxy config") 217 } 218 if pc != "" { 219 pc, err := MergeProxyConfig(pc, defaultConfig.DefaultConfig) 220 if err != nil { 221 return nil, err 222 } 223 defaultConfig.DefaultConfig = pc 224 } 225 226 defaultConfig.DefaultProviders = prevDefaultProvider 227 dp, err := extractYamlField("defaultProviders", raw) 228 if err != nil { 229 return nil, multierror.Prefix(err, "failed to extract default providers") 230 } 231 if dp != "" { 232 if err := protomarshal.ApplyYAML(dp, defaultConfig.DefaultProviders); err != nil { 233 return nil, fmt.Errorf("could not parse default providers: %v", err) 234 } 235 } 236 237 newExtensionProviders := defaultConfig.ExtensionProviders 238 defaultConfig.ExtensionProviders = prevExtensionProviders 239 for _, p := range newExtensionProviders { 240 found := false 241 for _, e := range defaultConfig.ExtensionProviders { 242 if p.Name == e.Name { 243 e.Provider = p.Provider 244 found = true 245 break 246 } 247 } 248 if !found { 249 defaultConfig.ExtensionProviders = append(defaultConfig.ExtensionProviders, p) 250 } 251 } 252 253 defaultConfig.TrustDomainAliases = sets.SortedList(sets.New(append(defaultConfig.TrustDomainAliases, prevTrustDomainAliases...)...)) 254 255 warn, err := agent.ValidateMeshConfig(defaultConfig) 256 if err != nil { 257 return nil, err 258 } 259 if warn != nil { 260 log.Warnf("warnings occurred during mesh validation: %v", warn) 261 } 262 263 return defaultConfig, nil 264 } 265 266 func mergeMap(original map[string]string, merger map[string]string) map[string]string { 267 if original == nil && merger == nil { 268 return nil 269 } 270 if original == nil { 271 original = map[string]string{} 272 } 273 for k, v := range merger { 274 original[k] = v 275 } 276 return original 277 } 278 279 // ApplyMeshConfigDefaults returns a new MeshConfig decoded from the 280 // input YAML with defaults applied to omitted configuration values. 281 func ApplyMeshConfigDefaults(yaml string) (*meshconfig.MeshConfig, error) { 282 return ApplyMeshConfig(yaml, DefaultMeshConfig()) 283 } 284 285 func DeepCopyMeshConfig(mc *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { 286 j, err := protomarshal.ToJSON(mc) 287 if err != nil { 288 return nil, err 289 } 290 nmc := &meshconfig.MeshConfig{} 291 if err := protomarshal.ApplyJSON(j, nmc); err != nil { 292 return nil, err 293 } 294 return nmc, nil 295 } 296 297 // EmptyMeshNetworks configuration with no networks 298 func EmptyMeshNetworks() meshconfig.MeshNetworks { 299 return meshconfig.MeshNetworks{ 300 Networks: map[string]*meshconfig.Network{}, 301 } 302 } 303 304 // ParseMeshNetworks returns a new MeshNetworks decoded from the 305 // input YAML. 306 func ParseMeshNetworks(yaml string) (*meshconfig.MeshNetworks, error) { 307 out := EmptyMeshNetworks() 308 if err := protomarshal.ApplyYAML(yaml, &out); err != nil { 309 return nil, multierror.Prefix(err, "failed to convert to proto.") 310 } 311 312 if err := agent.ValidateMeshNetworks(&out); err != nil { 313 return nil, err 314 } 315 return &out, nil 316 } 317 318 // ReadMeshNetworks gets mesh networks configuration from a config file 319 func ReadMeshNetworks(filename string) (*meshconfig.MeshNetworks, error) { 320 yaml, err := os.ReadFile(filename) 321 if err != nil { 322 return nil, multierror.Prefix(err, "cannot read networks config file") 323 } 324 return ParseMeshNetworks(string(yaml)) 325 } 326 327 // ReadMeshConfig gets mesh configuration from a config file 328 func ReadMeshConfig(filename string) (*meshconfig.MeshConfig, error) { 329 yaml, err := os.ReadFile(filename) 330 if err != nil { 331 return nil, multierror.Prefix(err, "cannot read mesh config file") 332 } 333 return ApplyMeshConfigDefaults(string(yaml)) 334 } 335 336 // ReadMeshConfigData gets mesh configuration yaml from a config file 337 func ReadMeshConfigData(filename string) (string, error) { 338 yaml, err := os.ReadFile(filename) 339 if err != nil { 340 return "", multierror.Prefix(err, "cannot read mesh config file") 341 } 342 return string(yaml), nil 343 }