github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/configurator.go (about) 1 package configs 2 3 import ( 4 "bytes" 5 "crypto" 6 "crypto/x509" 7 "encoding/json" 8 "encoding/pem" 9 "fmt" 10 "os" 11 "strings" 12 13 "github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets" 14 "github.com/nginxinc/nginx-prometheus-exporter/collector" 15 "github.com/spiffe/go-spiffe/workload" 16 17 "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" 18 conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" 19 20 "github.com/golang/glog" 21 api_v1 "k8s.io/api/core/v1" 22 networking "k8s.io/api/networking/v1beta1" 23 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 25 "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" 26 "github.com/nginxinc/kubernetes-ingress/internal/nginx" 27 conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 28 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 31 latCollector "github.com/nginxinc/kubernetes-ingress/internal/metrics/collectors" 32 ) 33 34 const ( 35 pemFileNameForWildcardTLSSecret = "/etc/nginx/secrets/wildcard" // #nosec G101 36 appProtectPolicyFolder = "/etc/nginx/waf/nac-policies/" 37 appProtectLogConfFolder = "/etc/nginx/waf/nac-logconfs/" 38 appProtectUserSigFolder = "/etc/nginx/waf/nac-usersigs/" 39 appProtectUserSigIndex = "/etc/nginx/waf/nac-usersigs/index.conf" 40 ) 41 42 // DefaultServerSecretPath is the full path to the Secret with a TLS cert and a key for the default server. #nosec G101 43 const DefaultServerSecretPath = "/etc/nginx/secrets/default" 44 45 // DefaultServerSecretName is the filename of the Secret with a TLS cert and a key for the default server. 46 const DefaultServerSecretName = "default" 47 48 // WildcardSecretName is the filename of the Secret with a TLS cert and a key for the ingress resources with TLS termination enabled but not secret defined. 49 const WildcardSecretName = "wildcard" 50 51 // JWTKeyKey is the key of the data field of a Secret where the JWK must be stored. 52 const JWTKeyKey = "jwk" 53 54 // CAKey is the key of the data field of a Secret where the cert must be stored. 55 const CAKey = "ca.crt" 56 57 // ClientSecretKey is the key of the data field of a Secret where the OIDC client secret must be stored. 58 const ClientSecretKey = "client-secret" 59 60 // SPIFFE filenames and modes 61 const ( 62 spiffeCertFileName = "spiffe_cert.pem" 63 spiffeKeyFileName = "spiffe_key.pem" 64 spiffeBundleFileName = "spiffe_rootca.pem" 65 spiffeCertsFileMode = os.FileMode(0644) 66 spiffeKeyFileMode = os.FileMode(0600) 67 ) 68 69 // ExtendedResources holds all extended configuration resources, for which Configurator configures NGINX. 70 type ExtendedResources struct { 71 IngressExes []*IngressEx 72 MergeableIngresses []*MergeableIngresses 73 VirtualServerExes []*VirtualServerEx 74 TransportServerExes []*TransportServerEx 75 } 76 77 type tlsPassthroughPair struct { 78 Host string 79 UnixSocket string 80 } 81 82 // metricLabelsIndex keeps the relations between Ingress Controller resources and NGINX configuration. 83 // Used to be able to add Prometheus Metrics variable labels grouped by resource key. 84 type metricLabelsIndex struct { 85 ingressUpstreams map[string][]string 86 virtualServerUpstreams map[string][]string 87 transportServerUpstreams map[string][]string 88 ingressServerZones map[string][]string 89 virtualServerServerZones map[string][]string 90 transportServerServerZones map[string][]string 91 ingressUpstreamPeers map[string][]string 92 virtualServerUpstreamPeers map[string][]string 93 transportServerUpstreamPeers map[string][]string 94 } 95 96 // Configurator configures NGINX. 97 type Configurator struct { 98 nginxManager nginx.Manager 99 staticCfgParams *StaticConfigParams 100 cfgParams *ConfigParams 101 templateExecutor *version1.TemplateExecutor 102 templateExecutorV2 *version2.TemplateExecutor 103 ingresses map[string]*IngressEx 104 minions map[string]map[string]bool 105 virtualServers map[string]*VirtualServerEx 106 tlsPassthroughPairs map[string]tlsPassthroughPair 107 isWildcardEnabled bool 108 isPlus bool 109 labelUpdater collector.LabelUpdater 110 metricLabelsIndex *metricLabelsIndex 111 isPrometheusEnabled bool 112 latencyCollector latCollector.LatencyCollector 113 isLatencyMetricsEnabled bool 114 } 115 116 // NewConfigurator creates a new Configurator. 117 func NewConfigurator(nginxManager nginx.Manager, staticCfgParams *StaticConfigParams, config *ConfigParams, 118 templateExecutor *version1.TemplateExecutor, templateExecutorV2 *version2.TemplateExecutor, isPlus bool, isWildcardEnabled bool, 119 labelUpdater collector.LabelUpdater, isPrometheusEnabled bool, latencyCollector latCollector.LatencyCollector, isLatencyMetricsEnabled bool) *Configurator { 120 metricLabelsIndex := &metricLabelsIndex{ 121 ingressUpstreams: make(map[string][]string), 122 virtualServerUpstreams: make(map[string][]string), 123 transportServerUpstreams: make(map[string][]string), 124 ingressServerZones: make(map[string][]string), 125 virtualServerServerZones: make(map[string][]string), 126 transportServerServerZones: make(map[string][]string), 127 ingressUpstreamPeers: make(map[string][]string), 128 virtualServerUpstreamPeers: make(map[string][]string), 129 transportServerUpstreamPeers: make(map[string][]string), 130 } 131 132 cnf := Configurator{ 133 nginxManager: nginxManager, 134 staticCfgParams: staticCfgParams, 135 cfgParams: config, 136 ingresses: make(map[string]*IngressEx), 137 virtualServers: make(map[string]*VirtualServerEx), 138 templateExecutor: templateExecutor, 139 templateExecutorV2: templateExecutorV2, 140 minions: make(map[string]map[string]bool), 141 tlsPassthroughPairs: make(map[string]tlsPassthroughPair), 142 isPlus: isPlus, 143 isWildcardEnabled: isWildcardEnabled, 144 labelUpdater: labelUpdater, 145 metricLabelsIndex: metricLabelsIndex, 146 isPrometheusEnabled: isPrometheusEnabled, 147 latencyCollector: latencyCollector, 148 isLatencyMetricsEnabled: isLatencyMetricsEnabled, 149 } 150 return &cnf 151 } 152 153 // AddOrUpdateDHParam creates a dhparam file with the content of the string. 154 func (cnf *Configurator) AddOrUpdateDHParam(content string) (string, error) { 155 return cnf.nginxManager.CreateDHParam(content) 156 } 157 158 func findRemovedKeys(currentKeys []string, newKeys map[string]bool) []string { 159 var removedKeys []string 160 for _, name := range currentKeys { 161 if _, exists := newKeys[name]; !exists { 162 removedKeys = append(removedKeys, name) 163 } 164 } 165 return removedKeys 166 } 167 168 func (cnf *Configurator) updateIngressMetricsLabels(ingEx *IngressEx, upstreams []version1.Upstream) { 169 upstreamServerLabels := make(map[string][]string) 170 newUpstreams := make(map[string]bool) 171 var newUpstreamsNames []string 172 173 upstreamServerPeerLabels := make(map[string][]string) 174 newPeers := make(map[string]bool) 175 var newPeersIPs []string 176 177 for _, u := range upstreams { 178 upstreamServerLabels[u.Name] = []string{u.UpstreamLabels.Service, u.UpstreamLabels.ResourceType, u.UpstreamLabels.ResourceName, u.UpstreamLabels.ResourceNamespace} 179 newUpstreams[u.Name] = true 180 newUpstreamsNames = append(newUpstreamsNames, u.Name) 181 for _, server := range u.UpstreamServers { 182 s := fmt.Sprintf("%v:%v", server.Address, server.Port) 183 podInfo := ingEx.PodsByIP[s] 184 labelKey := fmt.Sprintf("%v/%v", u.Name, s) 185 upstreamServerPeerLabels[labelKey] = []string{podInfo.Name} 186 if cnf.staticCfgParams.NginxServiceMesh { 187 ownerLabelVal := fmt.Sprintf("%s/%s", podInfo.OwnerType, podInfo.OwnerName) 188 upstreamServerPeerLabels[labelKey] = append(upstreamServerPeerLabels[labelKey], ownerLabelVal) 189 } 190 newPeers[labelKey] = true 191 newPeersIPs = append(newPeersIPs, labelKey) 192 } 193 } 194 195 key := fmt.Sprintf("%v/%v", ingEx.Ingress.Namespace, ingEx.Ingress.Name) 196 removedUpstreams := findRemovedKeys(cnf.metricLabelsIndex.ingressUpstreams[key], newUpstreams) 197 cnf.metricLabelsIndex.ingressUpstreams[key] = newUpstreamsNames 198 cnf.latencyCollector.UpdateUpstreamServerLabels(upstreamServerLabels) 199 cnf.latencyCollector.DeleteUpstreamServerLabels(removedUpstreams) 200 201 removedPeers := findRemovedKeys(cnf.metricLabelsIndex.ingressUpstreamPeers[key], newPeers) 202 cnf.metricLabelsIndex.ingressUpstreamPeers[key] = newPeersIPs 203 cnf.latencyCollector.UpdateUpstreamServerPeerLabels(upstreamServerPeerLabels) 204 cnf.latencyCollector.DeleteUpstreamServerPeerLabels(removedPeers) 205 cnf.latencyCollector.DeleteMetrics(removedPeers) 206 207 if cnf.isPlus { 208 cnf.labelUpdater.UpdateUpstreamServerLabels(upstreamServerLabels) 209 cnf.labelUpdater.DeleteUpstreamServerLabels(removedUpstreams) 210 cnf.labelUpdater.UpdateUpstreamServerPeerLabels(upstreamServerPeerLabels) 211 cnf.labelUpdater.DeleteUpstreamServerPeerLabels(removedPeers) 212 serverZoneLabels := make(map[string][]string) 213 newZones := make(map[string]bool) 214 var newZonesNames []string 215 for _, rule := range ingEx.Ingress.Spec.Rules { 216 serverZoneLabels[rule.Host] = []string{"ingress", ingEx.Ingress.Name, ingEx.Ingress.Namespace} 217 newZones[rule.Host] = true 218 newZonesNames = append(newZonesNames, rule.Host) 219 } 220 221 removedZones := findRemovedKeys(cnf.metricLabelsIndex.ingressServerZones[key], newZones) 222 cnf.metricLabelsIndex.ingressServerZones[key] = newZonesNames 223 cnf.labelUpdater.UpdateServerZoneLabels(serverZoneLabels) 224 cnf.labelUpdater.DeleteServerZoneLabels(removedZones) 225 } 226 } 227 228 func (cnf *Configurator) deleteIngressMetricsLabels(key string) { 229 cnf.latencyCollector.DeleteUpstreamServerLabels(cnf.metricLabelsIndex.ingressUpstreams[key]) 230 cnf.latencyCollector.DeleteUpstreamServerPeerLabels(cnf.metricLabelsIndex.ingressUpstreamPeers[key]) 231 cnf.latencyCollector.DeleteMetrics(cnf.metricLabelsIndex.ingressUpstreamPeers[key]) 232 233 if cnf.isPlus { 234 cnf.labelUpdater.DeleteUpstreamServerLabels(cnf.metricLabelsIndex.ingressUpstreams[key]) 235 cnf.labelUpdater.DeleteServerZoneLabels(cnf.metricLabelsIndex.ingressServerZones[key]) 236 cnf.labelUpdater.DeleteUpstreamServerPeerLabels(cnf.metricLabelsIndex.ingressUpstreamPeers[key]) 237 } 238 239 delete(cnf.metricLabelsIndex.ingressUpstreams, key) 240 delete(cnf.metricLabelsIndex.ingressServerZones, key) 241 delete(cnf.metricLabelsIndex.ingressUpstreamPeers, key) 242 } 243 244 // AddOrUpdateIngress adds or updates NGINX configuration for the Ingress resource. 245 func (cnf *Configurator) AddOrUpdateIngress(ingEx *IngressEx) (Warnings, error) { 246 warnings, err := cnf.addOrUpdateIngress(ingEx) 247 if err != nil { 248 return warnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 249 } 250 251 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 252 return warnings, fmt.Errorf("Error reloading NGINX for %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 253 } 254 255 return warnings, nil 256 } 257 258 func (cnf *Configurator) addOrUpdateIngress(ingEx *IngressEx) (Warnings, error) { 259 apResources := cnf.updateApResources(ingEx) 260 261 if jwtKey, exists := ingEx.Ingress.Annotations[JWTKeyAnnotation]; exists { 262 // LocalSecretStore will not set Path if the secret is not on the filesystem. 263 // However, NGINX configuration for an Ingress resource, to handle the case of a missing secret, 264 // relies on the path to be always configured. 265 ingEx.SecretRefs[jwtKey].Path = cnf.nginxManager.GetFilenameForSecret(ingEx.Ingress.Namespace + "-" + jwtKey) 266 } 267 268 isMinion := false 269 nginxCfg, warnings := generateNginxCfg(ingEx, apResources, isMinion, cnf.cfgParams, cnf.isPlus, cnf.IsResolverConfigured(), 270 cnf.staticCfgParams, cnf.isWildcardEnabled) 271 name := objectMetaToFileName(&ingEx.Ingress.ObjectMeta) 272 content, err := cnf.templateExecutor.ExecuteIngressConfigTemplate(&nginxCfg) 273 if err != nil { 274 return warnings, fmt.Errorf("Error generating Ingress Config %v: %w", name, err) 275 } 276 cnf.nginxManager.CreateConfig(name, content) 277 278 cnf.ingresses[name] = ingEx 279 if (cnf.isPlus && cnf.isPrometheusEnabled) || cnf.isLatencyMetricsEnabled { 280 cnf.updateIngressMetricsLabels(ingEx, nginxCfg.Upstreams) 281 } 282 return warnings, nil 283 } 284 285 // AddOrUpdateMergeableIngress adds or updates NGINX configuration for the Ingress resources with Mergeable Types. 286 func (cnf *Configurator) AddOrUpdateMergeableIngress(mergeableIngs *MergeableIngresses) (Warnings, error) { 287 warnings, err := cnf.addOrUpdateMergeableIngress(mergeableIngs) 288 if err != nil { 289 return warnings, fmt.Errorf("Error when adding or updating ingress %v/%v: %w", mergeableIngs.Master.Ingress.Namespace, mergeableIngs.Master.Ingress.Name, err) 290 } 291 292 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 293 return warnings, fmt.Errorf("Error reloading NGINX for %v/%v: %w", mergeableIngs.Master.Ingress.Namespace, mergeableIngs.Master.Ingress.Name, err) 294 } 295 296 return warnings, nil 297 } 298 299 func (cnf *Configurator) addOrUpdateMergeableIngress(mergeableIngs *MergeableIngresses) (Warnings, error) { 300 masterApResources := cnf.updateApResources(mergeableIngs.Master) 301 302 // LocalSecretStore will not set Path if the secret is not on the filesystem. 303 // However, NGINX configuration for an Ingress resource, to handle the case of a missing secret, 304 // relies on the path to be always configured. 305 if jwtKey, exists := mergeableIngs.Master.Ingress.Annotations[JWTKeyAnnotation]; exists { 306 mergeableIngs.Master.SecretRefs[jwtKey].Path = cnf.nginxManager.GetFilenameForSecret(mergeableIngs.Master.Ingress.Namespace + "-" + jwtKey) 307 } 308 for _, minion := range mergeableIngs.Minions { 309 if jwtKey, exists := minion.Ingress.Annotations[JWTKeyAnnotation]; exists { 310 minion.SecretRefs[jwtKey].Path = cnf.nginxManager.GetFilenameForSecret(minion.Ingress.Namespace + "-" + jwtKey) 311 } 312 } 313 314 nginxCfg, warnings := generateNginxCfgForMergeableIngresses(mergeableIngs, masterApResources, cnf.cfgParams, cnf.isPlus, 315 cnf.IsResolverConfigured(), cnf.staticCfgParams, cnf.isWildcardEnabled) 316 317 name := objectMetaToFileName(&mergeableIngs.Master.Ingress.ObjectMeta) 318 content, err := cnf.templateExecutor.ExecuteIngressConfigTemplate(&nginxCfg) 319 if err != nil { 320 return warnings, fmt.Errorf("Error generating Ingress Config %v: %w", name, err) 321 } 322 cnf.nginxManager.CreateConfig(name, content) 323 324 cnf.ingresses[name] = mergeableIngs.Master 325 cnf.minions[name] = make(map[string]bool) 326 for _, minion := range mergeableIngs.Minions { 327 minionName := objectMetaToFileName(&minion.Ingress.ObjectMeta) 328 cnf.minions[name][minionName] = true 329 } 330 if (cnf.isPlus && cnf.isPrometheusEnabled) || cnf.isLatencyMetricsEnabled { 331 cnf.updateIngressMetricsLabels(mergeableIngs.Master, nginxCfg.Upstreams) 332 } 333 334 return warnings, nil 335 } 336 337 func (cnf *Configurator) updateVirtualServerMetricsLabels(virtualServerEx *VirtualServerEx, upstreams []version2.Upstream) { 338 labels := make(map[string][]string) 339 newUpstreams := make(map[string]bool) 340 var newUpstreamsNames []string 341 342 upstreamServerPeerLabels := make(map[string][]string) 343 newPeers := make(map[string]bool) 344 var newPeersIPs []string 345 346 for _, u := range upstreams { 347 labels[u.Name] = []string{u.UpstreamLabels.Service, u.UpstreamLabels.ResourceType, u.UpstreamLabels.ResourceName, u.UpstreamLabels.ResourceNamespace} 348 newUpstreams[u.Name] = true 349 newUpstreamsNames = append(newUpstreamsNames, u.Name) 350 for _, server := range u.Servers { 351 podInfo := virtualServerEx.PodsByIP[server.Address] 352 labelKey := fmt.Sprintf("%v/%v", u.Name, server.Address) 353 upstreamServerPeerLabels[labelKey] = []string{podInfo.Name} 354 if cnf.staticCfgParams.NginxServiceMesh { 355 ownerLabelVal := fmt.Sprintf("%s/%s", podInfo.OwnerType, podInfo.OwnerName) 356 upstreamServerPeerLabels[labelKey] = append(upstreamServerPeerLabels[labelKey], ownerLabelVal) 357 } 358 newPeers[labelKey] = true 359 newPeersIPs = append(newPeersIPs, labelKey) 360 } 361 } 362 363 key := fmt.Sprintf("%v/%v", virtualServerEx.VirtualServer.Namespace, virtualServerEx.VirtualServer.Name) 364 365 removedPeers := findRemovedKeys(cnf.metricLabelsIndex.virtualServerUpstreamPeers[key], newPeers) 366 cnf.metricLabelsIndex.virtualServerUpstreamPeers[key] = newPeersIPs 367 cnf.latencyCollector.UpdateUpstreamServerPeerLabels(upstreamServerPeerLabels) 368 cnf.latencyCollector.DeleteUpstreamServerPeerLabels(removedPeers) 369 370 removedUpstreams := findRemovedKeys(cnf.metricLabelsIndex.virtualServerUpstreams[key], newUpstreams) 371 cnf.latencyCollector.UpdateUpstreamServerLabels(labels) 372 cnf.metricLabelsIndex.virtualServerUpstreams[key] = newUpstreamsNames 373 374 cnf.latencyCollector.DeleteUpstreamServerLabels(removedUpstreams) 375 cnf.latencyCollector.DeleteMetrics(removedPeers) 376 377 if cnf.isPlus { 378 cnf.labelUpdater.UpdateUpstreamServerPeerLabels(upstreamServerPeerLabels) 379 cnf.labelUpdater.DeleteUpstreamServerPeerLabels(removedPeers) 380 cnf.labelUpdater.UpdateUpstreamServerLabels(labels) 381 cnf.labelUpdater.DeleteUpstreamServerLabels(removedUpstreams) 382 383 serverZoneLabels := make(map[string][]string) 384 newZones := make(map[string]bool) 385 newZonesNames := []string{virtualServerEx.VirtualServer.Spec.Host} 386 387 serverZoneLabels[virtualServerEx.VirtualServer.Spec.Host] = []string{ 388 "virtualserver", virtualServerEx.VirtualServer.Name, virtualServerEx.VirtualServer.Namespace, 389 } 390 391 newZones[virtualServerEx.VirtualServer.Spec.Host] = true 392 393 removedZones := findRemovedKeys(cnf.metricLabelsIndex.virtualServerServerZones[key], newZones) 394 cnf.metricLabelsIndex.virtualServerServerZones[key] = newZonesNames 395 cnf.labelUpdater.UpdateServerZoneLabels(serverZoneLabels) 396 cnf.labelUpdater.DeleteServerZoneLabels(removedZones) 397 } 398 } 399 400 func (cnf *Configurator) deleteVirtualServerMetricsLabels(key string) { 401 cnf.latencyCollector.DeleteUpstreamServerLabels(cnf.metricLabelsIndex.virtualServerUpstreams[key]) 402 cnf.latencyCollector.DeleteUpstreamServerPeerLabels(cnf.metricLabelsIndex.virtualServerUpstreamPeers[key]) 403 cnf.latencyCollector.DeleteMetrics(cnf.metricLabelsIndex.virtualServerUpstreamPeers[key]) 404 405 if cnf.isPlus { 406 cnf.labelUpdater.DeleteUpstreamServerLabels(cnf.metricLabelsIndex.virtualServerUpstreams[key]) 407 cnf.labelUpdater.DeleteServerZoneLabels(cnf.metricLabelsIndex.virtualServerServerZones[key]) 408 cnf.labelUpdater.DeleteUpstreamServerPeerLabels(cnf.metricLabelsIndex.virtualServerUpstreamPeers[key]) 409 } 410 411 delete(cnf.metricLabelsIndex.virtualServerUpstreams, key) 412 delete(cnf.metricLabelsIndex.virtualServerServerZones, key) 413 delete(cnf.metricLabelsIndex.virtualServerUpstreamPeers, key) 414 } 415 416 // AddOrUpdateVirtualServer adds or updates NGINX configuration for the VirtualServer resource. 417 func (cnf *Configurator) AddOrUpdateVirtualServer(virtualServerEx *VirtualServerEx) (Warnings, error) { 418 warnings, err := cnf.addOrUpdateVirtualServer(virtualServerEx) 419 if err != nil { 420 return warnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", virtualServerEx.VirtualServer.Namespace, virtualServerEx.VirtualServer.Name, err) 421 } 422 423 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 424 return warnings, fmt.Errorf("Error reloading NGINX for VirtualServer %v/%v: %w", virtualServerEx.VirtualServer.Namespace, virtualServerEx.VirtualServer.Name, err) 425 } 426 427 return warnings, nil 428 } 429 430 func (cnf *Configurator) addOrUpdateOpenTracingTracerConfig(content string) error { 431 err := cnf.nginxManager.CreateOpenTracingTracerConfig(content) 432 return err 433 } 434 435 func (cnf *Configurator) addOrUpdateVirtualServer(virtualServerEx *VirtualServerEx) (Warnings, error) { 436 apResources := cnf.updateApResourcesForVs(virtualServerEx) 437 438 name := getFileNameForVirtualServer(virtualServerEx.VirtualServer) 439 440 vsc := newVirtualServerConfigurator(cnf.cfgParams, cnf.isPlus, cnf.IsResolverConfigured(), cnf.staticCfgParams) 441 vsCfg, warnings := vsc.GenerateVirtualServerConfig(virtualServerEx, apResources) 442 content, err := cnf.templateExecutorV2.ExecuteVirtualServerTemplate(&vsCfg) 443 if err != nil { 444 return warnings, fmt.Errorf("Error generating VirtualServer config: %v: %w", name, err) 445 } 446 cnf.nginxManager.CreateConfig(name, content) 447 448 cnf.virtualServers[name] = virtualServerEx 449 450 if (cnf.isPlus && cnf.isPrometheusEnabled) || cnf.isLatencyMetricsEnabled { 451 cnf.updateVirtualServerMetricsLabels(virtualServerEx, vsCfg.Upstreams) 452 } 453 return warnings, nil 454 } 455 456 // AddOrUpdateVirtualServers adds or updates NGINX configuration for multiple VirtualServer resources. 457 func (cnf *Configurator) AddOrUpdateVirtualServers(virtualServerExes []*VirtualServerEx) (Warnings, error) { 458 allWarnings := newWarnings() 459 460 for _, vsEx := range virtualServerExes { 461 warnings, err := cnf.addOrUpdateVirtualServer(vsEx) 462 if err != nil { 463 return allWarnings, err 464 } 465 allWarnings.Add(warnings) 466 } 467 468 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 469 return allWarnings, fmt.Errorf("Error when reloading NGINX when updating Policy: %w", err) 470 } 471 472 return allWarnings, nil 473 } 474 475 func (cnf *Configurator) updateTransportServerMetricsLabels(transportServerEx *TransportServerEx, upstreams []version2.StreamUpstream) { 476 labels := make(map[string][]string) 477 newUpstreams := make(map[string]bool) 478 var newUpstreamsNames []string 479 480 upstreamServerPeerLabels := make(map[string][]string) 481 newPeers := make(map[string]bool) 482 var newPeersIPs []string 483 484 for _, u := range upstreams { 485 labels[u.Name] = []string{u.UpstreamLabels.Service, u.UpstreamLabels.ResourceType, u.UpstreamLabels.ResourceName, u.UpstreamLabels.ResourceNamespace} 486 newUpstreams[u.Name] = true 487 newUpstreamsNames = append(newUpstreamsNames, u.Name) 488 489 for _, server := range u.Servers { 490 podName := transportServerEx.PodsByIP[server.Address] 491 labelKey := fmt.Sprintf("%v/%v", u.Name, server.Address) 492 upstreamServerPeerLabels[labelKey] = []string{podName} 493 494 newPeers[labelKey] = true 495 newPeersIPs = append(newPeersIPs, labelKey) 496 } 497 } 498 499 key := fmt.Sprintf("%v/%v", transportServerEx.TransportServer.Namespace, transportServerEx.TransportServer.Name) 500 501 removedPeers := findRemovedKeys(cnf.metricLabelsIndex.transportServerUpstreamPeers[key], newPeers) 502 cnf.metricLabelsIndex.transportServerUpstreamPeers[key] = newPeersIPs 503 504 removedUpstreams := findRemovedKeys(cnf.metricLabelsIndex.transportServerUpstreams[key], newUpstreams) 505 cnf.metricLabelsIndex.transportServerUpstreams[key] = newUpstreamsNames 506 cnf.labelUpdater.UpdateStreamUpstreamServerPeerLabels(upstreamServerPeerLabels) 507 cnf.labelUpdater.DeleteStreamUpstreamServerPeerLabels(removedPeers) 508 cnf.labelUpdater.UpdateStreamUpstreamServerLabels(labels) 509 cnf.labelUpdater.DeleteStreamUpstreamServerLabels(removedUpstreams) 510 511 streamServerZoneLabels := make(map[string][]string) 512 newZones := make(map[string]bool) 513 zoneName := transportServerEx.TransportServer.Spec.Listener.Name 514 515 if transportServerEx.TransportServer.Spec.Host != "" { 516 zoneName = transportServerEx.TransportServer.Spec.Host 517 } 518 519 newZonesNames := []string{zoneName} 520 521 streamServerZoneLabels[zoneName] = []string{ 522 "transportserver", transportServerEx.TransportServer.Name, transportServerEx.TransportServer.Namespace, 523 } 524 525 newZones[zoneName] = true 526 removedZones := findRemovedKeys(cnf.metricLabelsIndex.transportServerServerZones[key], newZones) 527 cnf.metricLabelsIndex.transportServerServerZones[key] = newZonesNames 528 cnf.labelUpdater.UpdateStreamServerZoneLabels(streamServerZoneLabels) 529 cnf.labelUpdater.DeleteStreamServerZoneLabels(removedZones) 530 } 531 532 func (cnf *Configurator) deleteTransportServerMetricsLabels(key string) { 533 cnf.labelUpdater.DeleteStreamUpstreamServerLabels(cnf.metricLabelsIndex.transportServerUpstreams[key]) 534 cnf.labelUpdater.DeleteStreamServerZoneLabels(cnf.metricLabelsIndex.transportServerServerZones[key]) 535 cnf.labelUpdater.DeleteStreamUpstreamServerPeerLabels(cnf.metricLabelsIndex.transportServerUpstreamPeers[key]) 536 537 delete(cnf.metricLabelsIndex.transportServerUpstreams, key) 538 delete(cnf.metricLabelsIndex.transportServerServerZones, key) 539 delete(cnf.metricLabelsIndex.transportServerUpstreamPeers, key) 540 } 541 542 // AddOrUpdateTransportServer adds or updates NGINX configuration for the TransportServer resource. 543 // It is a responsibility of the caller to check that the TransportServer references an existing listener. 544 func (cnf *Configurator) AddOrUpdateTransportServer(transportServerEx *TransportServerEx) error { 545 err := cnf.addOrUpdateTransportServer(transportServerEx) 546 if err != nil { 547 return fmt.Errorf("Error adding or updating TransportServer %v/%v: %w", transportServerEx.TransportServer.Namespace, transportServerEx.TransportServer.Name, err) 548 } 549 550 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 551 return fmt.Errorf("Error reloading NGINX for TransportServer %v/%v: %w", transportServerEx.TransportServer.Namespace, transportServerEx.TransportServer.Name, err) 552 } 553 554 return nil 555 } 556 557 func (cnf *Configurator) addOrUpdateTransportServer(transportServerEx *TransportServerEx) error { 558 name := getFileNameForTransportServer(transportServerEx.TransportServer) 559 560 tsCfg := generateTransportServerConfig(transportServerEx, transportServerEx.ListenerPort, cnf.isPlus) 561 562 content, err := cnf.templateExecutorV2.ExecuteTransportServerTemplate(tsCfg) 563 if err != nil { 564 return fmt.Errorf("Error generating TransportServer config %v: %w", name, err) 565 } 566 567 if cnf.isPlus && cnf.isPrometheusEnabled { 568 cnf.updateTransportServerMetricsLabels(transportServerEx, tsCfg.Upstreams) 569 } 570 571 cnf.nginxManager.CreateStreamConfig(name, content) 572 573 // update TLS Passthrough Hosts config in case we have a TLS Passthrough TransportServer 574 // only TLS Passthrough TransportServers have non-empty hosts 575 if transportServerEx.TransportServer.Spec.Host != "" { 576 key := generateNamespaceNameKey(&transportServerEx.TransportServer.ObjectMeta) 577 cnf.tlsPassthroughPairs[key] = tlsPassthroughPair{ 578 Host: transportServerEx.TransportServer.Spec.Host, 579 UnixSocket: generateUnixSocket(transportServerEx), 580 } 581 582 return cnf.updateTLSPassthroughHostsConfig() 583 } 584 585 return nil 586 } 587 588 // GetVirtualServerRoutesForVirtualServer returns the virtualServerRoutes that a virtualServer 589 // references, if that virtualServer exists 590 func (cnf *Configurator) GetVirtualServerRoutesForVirtualServer(key string) []*conf_v1.VirtualServerRoute { 591 vsFileName := getFileNameForVirtualServerFromKey(key) 592 if cnf.virtualServers[vsFileName] != nil { 593 return cnf.virtualServers[vsFileName].VirtualServerRoutes 594 } 595 return nil 596 } 597 598 func (cnf *Configurator) updateTLSPassthroughHostsConfig() error { 599 cfg := generateTLSPassthroughHostsConfig(cnf.tlsPassthroughPairs) 600 601 content, err := cnf.templateExecutorV2.ExecuteTLSPassthroughHostsTemplate(cfg) 602 if err != nil { 603 return fmt.Errorf("Error generating config for TLS Passthrough Unix Sockets map: %w", err) 604 } 605 606 cnf.nginxManager.CreateTLSPassthroughHostsConfig(content) 607 608 return nil 609 } 610 611 func generateTLSPassthroughHostsConfig(tlsPassthroughPairs map[string]tlsPassthroughPair) *version2.TLSPassthroughHostsConfig { 612 cfg := version2.TLSPassthroughHostsConfig{} 613 614 for _, pair := range tlsPassthroughPairs { 615 cfg[pair.Host] = pair.UnixSocket 616 } 617 618 return &cfg 619 } 620 621 func (cnf *Configurator) addOrUpdateCASecret(secret *api_v1.Secret) string { 622 name := objectMetaToFileName(&secret.ObjectMeta) 623 data := GenerateCAFileContent(secret) 624 return cnf.nginxManager.CreateSecret(name, data, nginx.TLSSecretFileMode) 625 } 626 627 func (cnf *Configurator) addOrUpdateJWKSecret(secret *api_v1.Secret) string { 628 name := objectMetaToFileName(&secret.ObjectMeta) 629 data := secret.Data[JWTKeyKey] 630 return cnf.nginxManager.CreateSecret(name, data, nginx.JWKSecretFileMode) 631 } 632 633 // AddOrUpdateResources adds or updates configuration for resources. 634 func (cnf *Configurator) AddOrUpdateResources(resources ExtendedResources) (Warnings, error) { 635 allWarnings := newWarnings() 636 637 for _, ingEx := range resources.IngressExes { 638 warnings, err := cnf.addOrUpdateIngress(ingEx) 639 if err != nil { 640 return allWarnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 641 } 642 allWarnings.Add(warnings) 643 } 644 645 for _, m := range resources.MergeableIngresses { 646 warnings, err := cnf.addOrUpdateMergeableIngress(m) 647 if err != nil { 648 return allWarnings, fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", m.Master.Ingress.Namespace, m.Master.Ingress.Name, err) 649 } 650 allWarnings.Add(warnings) 651 } 652 653 for _, vsEx := range resources.VirtualServerExes { 654 warnings, err := cnf.addOrUpdateVirtualServer(vsEx) 655 if err != nil { 656 return allWarnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name, err) 657 } 658 allWarnings.Add(warnings) 659 } 660 661 for _, tsEx := range resources.TransportServerExes { 662 err := cnf.addOrUpdateTransportServer(tsEx) 663 if err != nil { 664 return allWarnings, fmt.Errorf("Error adding or updating TransportServer %v/%v: %w", tsEx.TransportServer.Namespace, tsEx.TransportServer.Name, err) 665 } 666 } 667 668 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 669 return allWarnings, fmt.Errorf("Error when reloading NGINX when updating resources: %w", err) 670 } 671 672 return allWarnings, nil 673 } 674 675 func (cnf *Configurator) addOrUpdateTLSSecret(secret *api_v1.Secret) string { 676 name := objectMetaToFileName(&secret.ObjectMeta) 677 data := GenerateCertAndKeyFileContent(secret) 678 return cnf.nginxManager.CreateSecret(name, data, nginx.TLSSecretFileMode) 679 } 680 681 // AddOrUpdateSpecialTLSSecrets adds or updates a file with a TLS cert and a key from a Special TLS Secret (eg. DefaultServerSecret, WildcardTLSSecret). 682 func (cnf *Configurator) AddOrUpdateSpecialTLSSecrets(secret *api_v1.Secret, secretNames []string) error { 683 data := GenerateCertAndKeyFileContent(secret) 684 685 for _, secretName := range secretNames { 686 cnf.nginxManager.CreateSecret(secretName, data, nginx.TLSSecretFileMode) 687 } 688 689 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 690 return fmt.Errorf("Error when reloading NGINX when updating the special Secrets: %w", err) 691 } 692 693 return nil 694 } 695 696 // GenerateCertAndKeyFileContent generates a pem file content from the TLS secret. 697 func GenerateCertAndKeyFileContent(secret *api_v1.Secret) []byte { 698 var res bytes.Buffer 699 700 res.Write(secret.Data[api_v1.TLSCertKey]) 701 res.WriteString("\n") 702 res.Write(secret.Data[api_v1.TLSPrivateKeyKey]) 703 704 return res.Bytes() 705 } 706 707 // GenerateCAFileContent generates a pem file content from the TLS secret. 708 func GenerateCAFileContent(secret *api_v1.Secret) []byte { 709 var res bytes.Buffer 710 711 res.Write(secret.Data[CAKey]) 712 713 return res.Bytes() 714 } 715 716 // DeleteIngress deletes NGINX configuration for the Ingress resource. 717 func (cnf *Configurator) DeleteIngress(key string) error { 718 name := keyToFileName(key) 719 cnf.nginxManager.DeleteConfig(name) 720 721 delete(cnf.ingresses, name) 722 delete(cnf.minions, name) 723 724 if (cnf.isPlus && cnf.isPrometheusEnabled) || cnf.isLatencyMetricsEnabled { 725 cnf.deleteIngressMetricsLabels(key) 726 } 727 728 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 729 return fmt.Errorf("Error when removing ingress %v: %w", key, err) 730 } 731 732 return nil 733 } 734 735 // DeleteVirtualServer deletes NGINX configuration for the VirtualServer resource. 736 func (cnf *Configurator) DeleteVirtualServer(key string) error { 737 name := getFileNameForVirtualServerFromKey(key) 738 cnf.nginxManager.DeleteConfig(name) 739 740 delete(cnf.virtualServers, name) 741 if (cnf.isPlus && cnf.isPrometheusEnabled) || cnf.isLatencyMetricsEnabled { 742 cnf.deleteVirtualServerMetricsLabels(key) 743 } 744 745 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 746 return fmt.Errorf("Error when removing VirtualServer %v: %w", key, err) 747 } 748 749 return nil 750 } 751 752 // DeleteTransportServer deletes NGINX configuration for the TransportServer resource. 753 func (cnf *Configurator) DeleteTransportServer(key string) error { 754 if cnf.isPlus && cnf.isPrometheusEnabled { 755 cnf.deleteTransportServerMetricsLabels(key) 756 } 757 758 err := cnf.deleteTransportServer(key) 759 if err != nil { 760 return fmt.Errorf("Error when removing TransportServer %v: %w", key, err) 761 } 762 763 err = cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate) 764 if err != nil { 765 return fmt.Errorf("Error when removing TransportServer %v: %w", key, err) 766 } 767 768 return nil 769 } 770 771 func (cnf *Configurator) deleteTransportServer(key string) error { 772 name := getFileNameForTransportServerFromKey(key) 773 cnf.nginxManager.DeleteStreamConfig(name) 774 775 // update TLS Passthrough Hosts config in case we have a TLS Passthrough TransportServer 776 if _, exists := cnf.tlsPassthroughPairs[key]; exists { 777 delete(cnf.tlsPassthroughPairs, key) 778 779 return cnf.updateTLSPassthroughHostsConfig() 780 } 781 782 return nil 783 } 784 785 // UpdateEndpoints updates endpoints in NGINX configuration for the Ingress resources. 786 func (cnf *Configurator) UpdateEndpoints(ingExes []*IngressEx) error { 787 reloadPlus := false 788 789 for _, ingEx := range ingExes { 790 // It is safe to ignore warnings here as no new warnings should appear when updating Endpoints for Ingresses 791 _, err := cnf.addOrUpdateIngress(ingEx) 792 if err != nil { 793 return fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 794 } 795 796 if cnf.isPlus { 797 err := cnf.updatePlusEndpoints(ingEx) 798 if err != nil { 799 glog.Warningf("Couldn't update the endpoints via the API: %v; reloading configuration instead", err) 800 reloadPlus = true 801 } 802 } 803 } 804 805 if cnf.isPlus && !reloadPlus { 806 glog.V(3).Info("No need to reload nginx") 807 return nil 808 } 809 810 if err := cnf.nginxManager.Reload(nginx.ReloadForEndpointsUpdate); err != nil { 811 return fmt.Errorf("Error reloading NGINX when updating endpoints: %w", err) 812 } 813 814 return nil 815 } 816 817 // UpdateEndpointsMergeableIngress updates endpoints in NGINX configuration for a mergeable Ingress resource. 818 func (cnf *Configurator) UpdateEndpointsMergeableIngress(mergeableIngresses []*MergeableIngresses) error { 819 reloadPlus := false 820 821 for i := range mergeableIngresses { 822 // It is safe to ignore warnings here as no new warnings should appear when updating Endpoints for Ingresses 823 _, err := cnf.addOrUpdateMergeableIngress(mergeableIngresses[i]) 824 if err != nil { 825 return fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", mergeableIngresses[i].Master.Ingress.Namespace, mergeableIngresses[i].Master.Ingress.Name, err) 826 } 827 828 if cnf.isPlus { 829 for _, ing := range mergeableIngresses[i].Minions { 830 err = cnf.updatePlusEndpoints(ing) 831 if err != nil { 832 glog.Warningf("Couldn't update the endpoints via the API: %v; reloading configuration instead", err) 833 reloadPlus = true 834 } 835 } 836 } 837 } 838 839 if cnf.isPlus && !reloadPlus { 840 glog.V(3).Info("No need to reload nginx") 841 return nil 842 } 843 844 if err := cnf.nginxManager.Reload(nginx.ReloadForEndpointsUpdate); err != nil { 845 return fmt.Errorf("Error reloading NGINX when updating endpoints for %v: %w", mergeableIngresses, err) 846 } 847 848 return nil 849 } 850 851 // UpdateEndpointsForVirtualServers updates endpoints in NGINX configuration for the VirtualServer resources. 852 func (cnf *Configurator) UpdateEndpointsForVirtualServers(virtualServerExes []*VirtualServerEx) error { 853 reloadPlus := false 854 855 for _, vs := range virtualServerExes { 856 // It is safe to ignore warnings here as no new warnings should appear when updating Endpoints for VirtualServers 857 _, err := cnf.addOrUpdateVirtualServer(vs) 858 if err != nil { 859 return fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", vs.VirtualServer.Namespace, vs.VirtualServer.Name, err) 860 } 861 862 if cnf.isPlus { 863 err := cnf.updatePlusEndpointsForVirtualServer(vs) 864 if err != nil { 865 glog.Warningf("Couldn't update the endpoints via the API: %v; reloading configuration instead", err) 866 reloadPlus = true 867 } 868 } 869 } 870 871 if cnf.isPlus && !reloadPlus { 872 glog.V(3).Info("No need to reload nginx") 873 return nil 874 } 875 876 if err := cnf.nginxManager.Reload(nginx.ReloadForEndpointsUpdate); err != nil { 877 return fmt.Errorf("Error reloading NGINX when updating endpoints: %w", err) 878 } 879 880 return nil 881 } 882 883 func (cnf *Configurator) updatePlusEndpointsForVirtualServer(virtualServerEx *VirtualServerEx) error { 884 upstreams := createUpstreamsForPlus(virtualServerEx, cnf.cfgParams, cnf.staticCfgParams) 885 for _, upstream := range upstreams { 886 serverCfg := createUpstreamServersConfigForPlus(upstream) 887 888 endpoints := createEndpointsFromUpstream(upstream) 889 890 err := cnf.nginxManager.UpdateServersInPlus(upstream.Name, endpoints, serverCfg) 891 if err != nil { 892 return fmt.Errorf("Couldn't update the endpoints for %v: %w", upstream.Name, err) 893 } 894 } 895 896 return nil 897 } 898 899 // UpdateEndpointsForTransportServers updates endpoints in NGINX configuration for the TransportServer resources. 900 func (cnf *Configurator) UpdateEndpointsForTransportServers(transportServerExes []*TransportServerEx) error { 901 reloadPlus := false 902 903 for _, tsEx := range transportServerExes { 904 err := cnf.addOrUpdateTransportServer(tsEx) 905 if err != nil { 906 return fmt.Errorf("Error adding or updating TransportServer %v/%v: %w", tsEx.TransportServer.Namespace, tsEx.TransportServer.Name, err) 907 } 908 909 if cnf.isPlus { 910 err := cnf.updatePlusEndpointsForTransportServer(tsEx) 911 if err != nil { 912 glog.Warningf("Couldn't update the endpoints via the API: %v; reloading configuration instead", err) 913 reloadPlus = true 914 } 915 } 916 } 917 918 if cnf.isPlus && !reloadPlus { 919 glog.V(3).Info("No need to reload nginx") 920 return nil 921 } 922 923 if err := cnf.nginxManager.Reload(nginx.ReloadForEndpointsUpdate); err != nil { 924 return fmt.Errorf("Error reloading NGINX when updating endpoints: %w", err) 925 } 926 927 return nil 928 } 929 930 func (cnf *Configurator) updatePlusEndpointsForTransportServer(transportServerEx *TransportServerEx) error { 931 upstreamNamer := newUpstreamNamerForTransportServer(transportServerEx.TransportServer) 932 933 for _, u := range transportServerEx.TransportServer.Spec.Upstreams { 934 name := upstreamNamer.GetNameForUpstream(u.Name) 935 936 // subselector is not supported yet in TransportServer upstreams. That's why we pass "nil" here 937 endpointsKey := GenerateEndpointsKey(transportServerEx.TransportServer.Namespace, u.Service, nil, uint16(u.Port)) 938 endpoints := transportServerEx.Endpoints[endpointsKey] 939 940 err := cnf.nginxManager.UpdateStreamServersInPlus(name, endpoints) 941 if err != nil { 942 return fmt.Errorf("Couldn't update the endpoints for %v: %w", u.Name, err) 943 } 944 } 945 946 return nil 947 } 948 949 func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error { 950 ingCfg := parseAnnotations(ingEx, cnf.cfgParams, cnf.isPlus, cnf.staticCfgParams.MainAppProtectLoadModule, cnf.staticCfgParams.EnableInternalRoutes) 951 952 cfg := nginx.ServerConfig{ 953 MaxFails: ingCfg.MaxFails, 954 MaxConns: ingCfg.MaxConns, 955 FailTimeout: ingCfg.FailTimeout, 956 SlowStart: ingCfg.SlowStart, 957 } 958 959 if ingEx.Ingress.Spec.Backend != nil { 960 endps, exists := ingEx.Endpoints[ingEx.Ingress.Spec.Backend.ServiceName+ingEx.Ingress.Spec.Backend.ServicePort.String()] 961 if exists { 962 if _, isExternalName := ingEx.ExternalNameSvcs[ingEx.Ingress.Spec.Backend.ServiceName]; isExternalName { 963 glog.V(3).Infof("Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API", ingEx.Ingress.Spec.Backend.ServiceName) 964 } else { 965 name := getNameForUpstream(ingEx.Ingress, emptyHost, ingEx.Ingress.Spec.Backend) 966 err := cnf.nginxManager.UpdateServersInPlus(name, endps, cfg) 967 if err != nil { 968 return fmt.Errorf("Couldn't update the endpoints for %v: %w", name, err) 969 } 970 } 971 } 972 } 973 974 for _, rule := range ingEx.Ingress.Spec.Rules { 975 if rule.IngressRuleValue.HTTP == nil { 976 continue 977 } 978 979 for _, path := range rule.HTTP.Paths { 980 endps, exists := ingEx.Endpoints[path.Backend.ServiceName+path.Backend.ServicePort.String()] 981 if exists { 982 if _, isExternalName := ingEx.ExternalNameSvcs[path.Backend.ServiceName]; isExternalName { 983 glog.V(3).Infof("Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API", path.Backend.ServiceName) 984 continue 985 } 986 987 name := getNameForUpstream(ingEx.Ingress, rule.Host, &path.Backend) 988 err := cnf.nginxManager.UpdateServersInPlus(name, endps, cfg) 989 if err != nil { 990 return fmt.Errorf("Couldn't update the endpoints for %v: %w", name, err) 991 } 992 } 993 } 994 } 995 996 return nil 997 } 998 999 // UpdateConfig updates NGINX configuration parameters. 1000 func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, ingExes []*IngressEx, mergeableIngs []*MergeableIngresses, virtualServerExes []*VirtualServerEx) (Warnings, error) { 1001 cnf.cfgParams = cfgParams 1002 allWarnings := newWarnings() 1003 1004 if cnf.cfgParams.MainServerSSLDHParamFileContent != nil { 1005 fileName, err := cnf.nginxManager.CreateDHParam(*cnf.cfgParams.MainServerSSLDHParamFileContent) 1006 if err != nil { 1007 return allWarnings, fmt.Errorf("Error when updating dhparams: %w", err) 1008 } 1009 cfgParams.MainServerSSLDHParam = fileName 1010 } 1011 1012 if cfgParams.MainTemplate != nil { 1013 err := cnf.templateExecutor.UpdateMainTemplate(cfgParams.MainTemplate) 1014 if err != nil { 1015 return allWarnings, fmt.Errorf("Error when parsing the main template: %w", err) 1016 } 1017 } 1018 1019 if cfgParams.IngressTemplate != nil { 1020 err := cnf.templateExecutor.UpdateIngressTemplate(cfgParams.IngressTemplate) 1021 if err != nil { 1022 return allWarnings, fmt.Errorf("Error when parsing the ingress template: %w", err) 1023 } 1024 } 1025 1026 if cfgParams.VirtualServerTemplate != nil { 1027 err := cnf.templateExecutorV2.UpdateVirtualServerTemplate(cfgParams.VirtualServerTemplate) 1028 if err != nil { 1029 return allWarnings, fmt.Errorf("Error when parsing the VirtualServer template: %w", err) 1030 } 1031 } 1032 1033 mainCfg := GenerateNginxMainConfig(cnf.staticCfgParams, cfgParams) 1034 mainCfgContent, err := cnf.templateExecutor.ExecuteMainConfigTemplate(mainCfg) 1035 if err != nil { 1036 return allWarnings, fmt.Errorf("Error when writing main Config") 1037 } 1038 cnf.nginxManager.CreateMainConfig(mainCfgContent) 1039 1040 for _, ingEx := range ingExes { 1041 warnings, err := cnf.addOrUpdateIngress(ingEx) 1042 if err != nil { 1043 return allWarnings, err 1044 } 1045 allWarnings.Add(warnings) 1046 } 1047 for _, mergeableIng := range mergeableIngs { 1048 warnings, err := cnf.addOrUpdateMergeableIngress(mergeableIng) 1049 if err != nil { 1050 return allWarnings, err 1051 } 1052 allWarnings.Add(warnings) 1053 } 1054 for _, vsEx := range virtualServerExes { 1055 warnings, err := cnf.addOrUpdateVirtualServer(vsEx) 1056 if err != nil { 1057 return allWarnings, err 1058 } 1059 allWarnings.Add(warnings) 1060 } 1061 1062 if mainCfg.OpenTracingLoadModule { 1063 if err := cnf.addOrUpdateOpenTracingTracerConfig(mainCfg.OpenTracingTracerConfig); err != nil { 1064 return allWarnings, fmt.Errorf("Error when updating OpenTracing tracer config: %w", err) 1065 } 1066 } 1067 1068 cnf.nginxManager.SetOpenTracing(mainCfg.OpenTracingLoadModule) 1069 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1070 return allWarnings, fmt.Errorf("Error when updating config from ConfigMap: %w", err) 1071 } 1072 1073 return allWarnings, nil 1074 } 1075 1076 // UpdateTransportServers updates TransportServers. 1077 func (cnf *Configurator) UpdateTransportServers(updatedTSExes []*TransportServerEx, deletedKeys []string) error { 1078 for _, tsEx := range updatedTSExes { 1079 err := cnf.addOrUpdateTransportServer(tsEx) 1080 if err != nil { 1081 return fmt.Errorf("Error adding or updating TransportServer %v/%v: %w", tsEx.TransportServer.Namespace, tsEx.TransportServer.Name, err) 1082 } 1083 } 1084 1085 for _, key := range deletedKeys { 1086 err := cnf.deleteTransportServer(key) 1087 if err != nil { 1088 return fmt.Errorf("Error when removing TransportServer %v: %w", key, err) 1089 } 1090 } 1091 1092 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1093 return fmt.Errorf("Error when updating TransportServers: %w", err) 1094 } 1095 1096 return nil 1097 } 1098 1099 func keyToFileName(key string) string { 1100 return strings.Replace(key, "/", "-", -1) 1101 } 1102 1103 func objectMetaToFileName(meta *meta_v1.ObjectMeta) string { 1104 return meta.Namespace + "-" + meta.Name 1105 } 1106 1107 func generateNamespaceNameKey(objectMeta *meta_v1.ObjectMeta) string { 1108 return fmt.Sprintf("%s/%s", objectMeta.Namespace, objectMeta.Name) 1109 } 1110 1111 func getFileNameForVirtualServer(virtualServer *conf_v1.VirtualServer) string { 1112 return fmt.Sprintf("vs_%s_%s", virtualServer.Namespace, virtualServer.Name) 1113 } 1114 1115 func getFileNameForTransportServer(transportServer *conf_v1alpha1.TransportServer) string { 1116 return fmt.Sprintf("ts_%s_%s", transportServer.Namespace, transportServer.Name) 1117 } 1118 1119 func getFileNameForVirtualServerFromKey(key string) string { 1120 replaced := strings.Replace(key, "/", "_", -1) 1121 return fmt.Sprintf("vs_%s", replaced) 1122 } 1123 1124 func getFileNameForTransportServerFromKey(key string) string { 1125 replaced := strings.Replace(key, "/", "_", -1) 1126 return fmt.Sprintf("ts_%s", replaced) 1127 } 1128 1129 // HasIngress checks if the Ingress resource is present in NGINX configuration. 1130 func (cnf *Configurator) HasIngress(ing *networking.Ingress) bool { 1131 name := objectMetaToFileName(&ing.ObjectMeta) 1132 _, exists := cnf.ingresses[name] 1133 return exists 1134 } 1135 1136 // HasMinion checks if the minion Ingress resource of the master is present in NGINX configuration. 1137 func (cnf *Configurator) HasMinion(master *networking.Ingress, minion *networking.Ingress) bool { 1138 masterName := objectMetaToFileName(&master.ObjectMeta) 1139 1140 if _, exists := cnf.minions[masterName]; !exists { 1141 return false 1142 } 1143 1144 return cnf.minions[masterName][objectMetaToFileName(&minion.ObjectMeta)] 1145 } 1146 1147 // IsResolverConfigured checks if a DNS resolver is present in NGINX configuration. 1148 func (cnf *Configurator) IsResolverConfigured() bool { 1149 return len(cnf.cfgParams.ResolverAddresses) != 0 1150 } 1151 1152 // GetIngressCounts returns the total count of Ingress resources that are handled by the Ingress Controller grouped by their type 1153 func (cnf *Configurator) GetIngressCounts() map[string]int { 1154 counters := map[string]int{ 1155 "master": 0, 1156 "regular": 0, 1157 "minion": 0, 1158 } 1159 1160 // cnf.ingresses contains only master and regular Ingress Resources 1161 for _, ing := range cnf.ingresses { 1162 if ing.Ingress.Annotations["nginx.org/mergeable-ingress-type"] == "master" { 1163 counters["master"]++ 1164 } else { 1165 counters["regular"]++ 1166 } 1167 } 1168 1169 for _, min := range cnf.minions { 1170 counters["minion"] += len(min) 1171 } 1172 1173 return counters 1174 } 1175 1176 // GetVirtualServerCounts returns the total count of VS/VSR resources that are handled by the Ingress Controller 1177 func (cnf *Configurator) GetVirtualServerCounts() (vsCount int, vsrCount int) { 1178 vsCount = len(cnf.virtualServers) 1179 for _, vs := range cnf.virtualServers { 1180 vsrCount += len(vs.VirtualServerRoutes) 1181 } 1182 1183 return vsCount, vsrCount 1184 } 1185 1186 // AddOrUpdateSpiffeCerts writes Spiffe certs and keys to disk and reloads NGINX 1187 func (cnf *Configurator) AddOrUpdateSpiffeCerts(svidResponse *workload.X509SVIDs) error { 1188 svid := svidResponse.Default() 1189 privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(svid.PrivateKey.(crypto.PrivateKey)) 1190 if err != nil { 1191 return fmt.Errorf("error when marshaling private key: %w", err) 1192 } 1193 1194 cnf.nginxManager.CreateSecret(spiffeKeyFileName, createSpiffeKey(privateKeyBytes), spiffeKeyFileMode) 1195 cnf.nginxManager.CreateSecret(spiffeCertFileName, createSpiffeCert(svid.Certificates), spiffeCertsFileMode) 1196 cnf.nginxManager.CreateSecret(spiffeBundleFileName, createSpiffeCert(svid.TrustBundle), spiffeCertsFileMode) 1197 1198 err = cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate) 1199 if err != nil { 1200 return fmt.Errorf("error when reloading NGINX when updating the SPIFFE Certs: %w", err) 1201 } 1202 return nil 1203 } 1204 1205 func createSpiffeKey(content []byte) []byte { 1206 return pem.EncodeToMemory(&pem.Block{ 1207 Type: "EC PRIVATE KEY", 1208 Bytes: content, 1209 }) 1210 } 1211 1212 func createSpiffeCert(certs []*x509.Certificate) []byte { 1213 pemData := make([]byte, 0, len(certs)) 1214 for _, c := range certs { 1215 b := &pem.Block{ 1216 Type: "CERTIFICATE", 1217 Bytes: c.Raw, 1218 } 1219 pemData = append(pemData, pem.EncodeToMemory(b)...) 1220 } 1221 return pemData 1222 } 1223 1224 func (cnf *Configurator) updateApResources(ingEx *IngressEx) (apRes AppProtectResources) { 1225 if ingEx.AppProtectPolicy != nil { 1226 policyFileName := appProtectPolicyFileNameFromUnstruct(ingEx.AppProtectPolicy) 1227 policyContent := generateApResourceFileContent(ingEx.AppProtectPolicy) 1228 cnf.nginxManager.CreateAppProtectResourceFile(policyFileName, policyContent) 1229 apRes.AppProtectPolicy = policyFileName 1230 } 1231 1232 for _, logConf := range ingEx.AppProtectLogs { 1233 logConfFileName := appProtectLogConfFileNameFromUnstruct(logConf.LogConf) 1234 logConfContent := generateApResourceFileContent(logConf.LogConf) 1235 cnf.nginxManager.CreateAppProtectResourceFile(logConfFileName, logConfContent) 1236 apRes.AppProtectLogconfs = append(apRes.AppProtectLogconfs, logConfFileName+" "+logConf.Dest) 1237 } 1238 1239 return apRes 1240 } 1241 1242 func (cnf *Configurator) updateApResourcesForVs(vsEx *VirtualServerEx) map[string]string { 1243 apRes := make(map[string]string) 1244 1245 if vsEx.ApPolRefs != nil { 1246 for apPolKey, apPol := range vsEx.ApPolRefs { 1247 policyFileName := appProtectPolicyFileNameFromUnstruct(apPol) 1248 policyContent := generateApResourceFileContent(apPol) 1249 cnf.nginxManager.CreateAppProtectResourceFile(policyFileName, policyContent) 1250 apRes[apPolKey] = policyFileName 1251 } 1252 } 1253 1254 if vsEx.LogConfRefs != nil { 1255 for logConfKey, logConf := range vsEx.LogConfRefs { 1256 logConfFileName := appProtectLogConfFileNameFromUnstruct(logConf) 1257 logConfContent := generateApResourceFileContent(logConf) 1258 cnf.nginxManager.CreateAppProtectResourceFile(logConfFileName, logConfContent) 1259 apRes[logConfKey] = logConfFileName 1260 } 1261 } 1262 1263 return apRes 1264 } 1265 1266 func appProtectPolicyFileNameFromUnstruct(unst *unstructured.Unstructured) string { 1267 return fmt.Sprintf("%s%s_%s", appProtectPolicyFolder, unst.GetNamespace(), unst.GetName()) 1268 } 1269 1270 func appProtectLogConfFileNameFromUnstruct(unst *unstructured.Unstructured) string { 1271 return fmt.Sprintf("%s%s_%s", appProtectLogConfFolder, unst.GetNamespace(), unst.GetName()) 1272 } 1273 1274 func appProtectUserSigFileNameFromUnstruct(unst *unstructured.Unstructured) string { 1275 return fmt.Sprintf("%s%s_%s", appProtectUserSigFolder, unst.GetNamespace(), unst.GetName()) 1276 } 1277 1278 func generateApResourceFileContent(apResource *unstructured.Unstructured) []byte { 1279 // Safe to ignore errors since validation already checked those 1280 spec, _, _ := unstructured.NestedMap(apResource.Object, "spec") 1281 data, _ := json.Marshal(spec) 1282 return data 1283 } 1284 1285 // AddOrUpdateAppProtectResource updates Ingresses and VirtualServers that use App Protect Resources 1286 func (cnf *Configurator) AddOrUpdateAppProtectResource(resource *unstructured.Unstructured, ingExes []*IngressEx, mergeableIngresses []*MergeableIngresses, vsExes []*VirtualServerEx) (Warnings, error) { 1287 allWarnings := newWarnings() 1288 1289 for _, ingEx := range ingExes { 1290 warnings, err := cnf.addOrUpdateIngress(ingEx) 1291 if err != nil { 1292 return allWarnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 1293 } 1294 allWarnings.Add(warnings) 1295 } 1296 1297 for _, m := range mergeableIngresses { 1298 warnings, err := cnf.addOrUpdateMergeableIngress(m) 1299 if err != nil { 1300 return allWarnings, fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", m.Master.Ingress.Namespace, m.Master.Ingress.Name, err) 1301 } 1302 allWarnings.Add(warnings) 1303 } 1304 1305 for _, vs := range vsExes { 1306 warnings, err := cnf.addOrUpdateVirtualServer(vs) 1307 if err != nil { 1308 return allWarnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", vs.VirtualServer.Namespace, vs.VirtualServer.Name, err) 1309 } 1310 allWarnings.Add(warnings) 1311 } 1312 1313 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1314 return allWarnings, fmt.Errorf("Error when reloading NGINX when updating %v: %w", resource.GetKind(), err) 1315 } 1316 1317 return allWarnings, nil 1318 } 1319 1320 // DeleteAppProtectPolicy updates Ingresses and VirtualServers that use AP Policy after that policy is deleted 1321 func (cnf *Configurator) DeleteAppProtectPolicy(polNamespaceName string, ingExes []*IngressEx, mergeableIngresses []*MergeableIngresses, vsExes []*VirtualServerEx) (Warnings, error) { 1322 if len(ingExes)+len(mergeableIngresses)+len(vsExes) > 0 { 1323 fName := strings.Replace(polNamespaceName, "/", "_", 1) 1324 polFileName := appProtectPolicyFolder + fName 1325 cnf.nginxManager.DeleteAppProtectResourceFile(polFileName) 1326 } 1327 1328 allWarnings := newWarnings() 1329 1330 for _, ingEx := range ingExes { 1331 warnings, err := cnf.addOrUpdateIngress(ingEx) 1332 if err != nil { 1333 return allWarnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 1334 } 1335 allWarnings.Add(warnings) 1336 } 1337 1338 for _, m := range mergeableIngresses { 1339 warnings, err := cnf.addOrUpdateMergeableIngress(m) 1340 if err != nil { 1341 return allWarnings, fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", m.Master.Ingress.Namespace, m.Master.Ingress.Name, err) 1342 } 1343 allWarnings.Add(warnings) 1344 } 1345 1346 for _, v := range vsExes { 1347 warnings, err := cnf.addOrUpdateVirtualServer(v) 1348 if err != nil { 1349 return allWarnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", v.VirtualServer.Namespace, v.VirtualServer.Name, err) 1350 } 1351 allWarnings.Add(warnings) 1352 } 1353 1354 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1355 return allWarnings, fmt.Errorf("Error when reloading NGINX when removing App Protect Policy: %w", err) 1356 } 1357 1358 return allWarnings, nil 1359 } 1360 1361 // DeleteAppProtectLogConf updates Ingresses and VirtualServers that use AP Log Configuration after that policy is deleted 1362 func (cnf *Configurator) DeleteAppProtectLogConf(logConfNamespaceName string, ingExes []*IngressEx, mergeableIngresses []*MergeableIngresses, vsExes []*VirtualServerEx) (Warnings, error) { 1363 if len(ingExes)+len(mergeableIngresses)+len(vsExes) > 0 { 1364 fName := strings.Replace(logConfNamespaceName, "/", "_", 1) 1365 logConfFileName := appProtectLogConfFolder + fName 1366 cnf.nginxManager.DeleteAppProtectResourceFile(logConfFileName) 1367 } 1368 allWarnings := newWarnings() 1369 1370 for _, ingEx := range ingExes { 1371 warnings, err := cnf.addOrUpdateIngress(ingEx) 1372 if err != nil { 1373 return allWarnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 1374 } 1375 allWarnings.Add(warnings) 1376 } 1377 1378 for _, m := range mergeableIngresses { 1379 warnings, err := cnf.addOrUpdateMergeableIngress(m) 1380 if err != nil { 1381 return allWarnings, fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", m.Master.Ingress.Namespace, m.Master.Ingress.Name, err) 1382 } 1383 allWarnings.Add(warnings) 1384 } 1385 1386 for _, v := range vsExes { 1387 warnings, err := cnf.addOrUpdateVirtualServer(v) 1388 if err != nil { 1389 return allWarnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", v.VirtualServer.Namespace, v.VirtualServer.Name, err) 1390 } 1391 allWarnings.Add(warnings) 1392 } 1393 1394 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1395 return allWarnings, fmt.Errorf("Error when reloading NGINX when removing App Protect Log Configuration: %w", err) 1396 } 1397 1398 return allWarnings, nil 1399 } 1400 1401 // RefreshAppProtectUserSigs writes all valid UDS files to fs and reloads NGINX 1402 func (cnf *Configurator) RefreshAppProtectUserSigs( 1403 userSigs []*unstructured.Unstructured, delPols []string, ingExes []*IngressEx, mergeableIngresses []*MergeableIngresses, vsExes []*VirtualServerEx, 1404 ) (Warnings, error) { 1405 allWarnings := newWarnings() 1406 for _, ingEx := range ingExes { 1407 warnings, err := cnf.addOrUpdateIngress(ingEx) 1408 if err != nil { 1409 return allWarnings, fmt.Errorf("Error adding or updating ingress %v/%v: %w", ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) 1410 } 1411 allWarnings.Add(warnings) 1412 } 1413 1414 for _, m := range mergeableIngresses { 1415 warnings, err := cnf.addOrUpdateMergeableIngress(m) 1416 if err != nil { 1417 return allWarnings, fmt.Errorf("Error adding or updating mergeableIngress %v/%v: %w", m.Master.Ingress.Namespace, m.Master.Ingress.Name, err) 1418 } 1419 allWarnings.Add(warnings) 1420 } 1421 1422 for _, v := range vsExes { 1423 warnings, err := cnf.addOrUpdateVirtualServer(v) 1424 if err != nil { 1425 return allWarnings, fmt.Errorf("Error adding or updating VirtualServer %v/%v: %w", v.VirtualServer.Namespace, v.VirtualServer.Name, err) 1426 } 1427 allWarnings.Add(warnings) 1428 } 1429 1430 for _, file := range delPols { 1431 cnf.nginxManager.DeleteAppProtectResourceFile(file) 1432 } 1433 1434 var builder strings.Builder 1435 cnf.nginxManager.ClearAppProtectFolder(appProtectUserSigFolder) 1436 for _, sig := range userSigs { 1437 fName := appProtectUserSigFileNameFromUnstruct(sig) 1438 data := generateApResourceFileContent(sig) 1439 cnf.nginxManager.CreateAppProtectResourceFile(fName, data) 1440 fmt.Fprintf(&builder, "app_protect_user_defined_signatures %s;\n", fName) 1441 } 1442 cnf.nginxManager.CreateAppProtectResourceFile(appProtectUserSigIndex, []byte(builder.String())) 1443 return allWarnings, cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate) 1444 } 1445 1446 // AddInternalRouteConfig adds internal route server to NGINX Configuration and reloads NGINX 1447 func (cnf *Configurator) AddInternalRouteConfig() error { 1448 cnf.staticCfgParams.EnableInternalRoutes = true 1449 cnf.staticCfgParams.PodName = os.Getenv("POD_NAME") 1450 mainCfg := GenerateNginxMainConfig(cnf.staticCfgParams, cnf.cfgParams) 1451 mainCfgContent, err := cnf.templateExecutor.ExecuteMainConfigTemplate(mainCfg) 1452 if err != nil { 1453 return fmt.Errorf("Error when writing main Config: %w", err) 1454 } 1455 cnf.nginxManager.CreateMainConfig(mainCfgContent) 1456 if err := cnf.nginxManager.Reload(nginx.ReloadForOtherUpdate); err != nil { 1457 return fmt.Errorf("Error when reloading nginx: %w", err) 1458 } 1459 return nil 1460 } 1461 1462 // AddOrUpdateSecret adds or updates a secret. 1463 func (cnf *Configurator) AddOrUpdateSecret(secret *api_v1.Secret) string { 1464 switch secret.Type { 1465 case secrets.SecretTypeCA: 1466 return cnf.addOrUpdateCASecret(secret) 1467 case secrets.SecretTypeJWK: 1468 return cnf.addOrUpdateJWKSecret(secret) 1469 case secrets.SecretTypeOIDC: 1470 // OIDC ClientSecret is not required on the filesystem, it is written directly to the config file. 1471 return "" 1472 default: 1473 return cnf.addOrUpdateTLSSecret(secret) 1474 } 1475 } 1476 1477 // DeleteSecret deletes a secret. 1478 func (cnf *Configurator) DeleteSecret(key string) { 1479 cnf.nginxManager.DeleteSecret(keyToFileName(key)) 1480 }