github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/annotations.go (about) 1 package configs 2 3 import ( 4 "github.com/golang/glog" 5 ) 6 7 // JWTKeyAnnotation is the annotation where the Secret with a JWK is specified. 8 const JWTKeyAnnotation = "nginx.com/jwt-key" 9 10 // AppProtectPolicyAnnotation is where the NGINX App Protect policy is specified 11 const AppProtectPolicyAnnotation = "appprotect.f5.com/app-protect-policy" 12 13 // AppProtectLogConfAnnotation is where the NGINX AppProtect Log Configuration is specified 14 const AppProtectLogConfAnnotation = "appprotect.f5.com/app-protect-security-log" 15 16 // AppProtectLogConfDstAnnotation is where the NGINX AppProtect Log Configuration is specified 17 const AppProtectLogConfDstAnnotation = "appprotect.f5.com/app-protect-security-log-destination" 18 19 // nginxMeshInternalRoute specifies if the ingress resource is an internal route. 20 const nginxMeshInternalRouteAnnotation = "nsm.nginx.com/internal-route" 21 22 var masterBlacklist = map[string]bool{ 23 "nginx.org/rewrites": true, 24 "nginx.org/ssl-services": true, 25 "nginx.org/grpc-services": true, 26 "nginx.org/websocket-services": true, 27 "nginx.com/sticky-cookie-services": true, 28 "nginx.com/health-checks": true, 29 "nginx.com/health-checks-mandatory": true, 30 "nginx.com/health-checks-mandatory-queue": true, 31 } 32 33 var minionBlacklist = map[string]bool{ 34 "nginx.org/proxy-hide-headers": true, 35 "nginx.org/proxy-pass-headers": true, 36 "nginx.org/redirect-to-https": true, 37 "ingress.kubernetes.io/ssl-redirect": true, 38 "nginx.org/hsts": true, 39 "nginx.org/hsts-max-age": true, 40 "nginx.org/hsts-include-subdomains": true, 41 "nginx.org/server-tokens": true, 42 "nginx.org/listen-ports": true, 43 "nginx.org/listen-ports-ssl": true, 44 "nginx.org/server-snippets": true, 45 "appprotect.f5.com/app_protect_enable": true, 46 "appprotect.f5.com/app_protect_policy": true, 47 "appprotect.f5.com/app_protect_security_log_enable": true, 48 "appprotect.f5.com/app_protect_security_log": true, 49 } 50 51 var minionInheritanceList = map[string]bool{ 52 "nginx.org/proxy-connect-timeout": true, 53 "nginx.org/proxy-read-timeout": true, 54 "nginx.org/proxy-send-timeout": true, 55 "nginx.org/client-max-body-size": true, 56 "nginx.org/proxy-buffering": true, 57 "nginx.org/proxy-buffers": true, 58 "nginx.org/proxy-buffer-size": true, 59 "nginx.org/proxy-max-temp-file-size": true, 60 "nginx.org/upstream-zone-size": true, 61 "nginx.org/location-snippets": true, 62 "nginx.org/lb-method": true, 63 "nginx.org/keepalive": true, 64 "nginx.org/max-fails": true, 65 "nginx.org/max-conns": true, 66 "nginx.org/fail-timeout": true, 67 } 68 69 func parseAnnotations(ingEx *IngressEx, baseCfgParams *ConfigParams, isPlus bool, hasAppProtect bool, enableInternalRoutes bool) ConfigParams { 70 cfgParams := *baseCfgParams 71 72 if lbMethod, exists := ingEx.Ingress.Annotations["nginx.org/lb-method"]; exists { 73 if isPlus { 74 if parsedMethod, err := ParseLBMethodForPlus(lbMethod); err != nil { 75 glog.Errorf("Ingress %s/%s: Invalid value for the nginx.org/lb-method: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), lbMethod, err) 76 } else { 77 cfgParams.LBMethod = parsedMethod 78 } 79 } else { 80 if parsedMethod, err := ParseLBMethod(lbMethod); err != nil { 81 glog.Errorf("Ingress %s/%s: Invalid value for the nginx.org/lb-method: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), lbMethod, err) 82 } else { 83 cfgParams.LBMethod = parsedMethod 84 } 85 } 86 } 87 88 if healthCheckEnabled, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks", ingEx.Ingress); exists { 89 if err != nil { 90 glog.Error(err) 91 } 92 if isPlus { 93 cfgParams.HealthCheckEnabled = healthCheckEnabled 94 } else { 95 glog.Warning("Annotation 'nginx.com/health-checks' requires NGINX Plus") 96 } 97 } 98 99 if cfgParams.HealthCheckEnabled { 100 if healthCheckMandatory, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory", ingEx.Ingress); exists { 101 if err != nil { 102 glog.Error(err) 103 } 104 cfgParams.HealthCheckMandatory = healthCheckMandatory 105 } 106 } 107 108 if cfgParams.HealthCheckMandatory { 109 if healthCheckQueue, exists, err := GetMapKeyAsInt64(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory-queue", ingEx.Ingress); exists { 110 if err != nil { 111 glog.Error(err) 112 } 113 cfgParams.HealthCheckMandatoryQueue = healthCheckQueue 114 } 115 } 116 117 if slowStart, exists := ingEx.Ingress.Annotations["nginx.com/slow-start"]; exists { 118 if parsedSlowStart, err := ParseTime(slowStart); err != nil { 119 glog.Errorf("Ingress %s/%s: Invalid value nginx.org/slow-start: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), slowStart, err) 120 } else { 121 if isPlus { 122 cfgParams.SlowStart = parsedSlowStart 123 } else { 124 glog.Warning("Annotation 'nginx.com/slow-start' requires NGINX Plus") 125 } 126 } 127 } 128 129 if serverTokens, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/server-tokens", ingEx.Ingress); exists { 130 if err != nil { 131 if isPlus { 132 cfgParams.ServerTokens = ingEx.Ingress.Annotations["nginx.org/server-tokens"] 133 } else { 134 glog.Error(err) 135 } 136 } else { 137 cfgParams.ServerTokens = "off" 138 if serverTokens { 139 cfgParams.ServerTokens = "on" 140 } 141 } 142 } 143 144 if serverSnippets, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/server-snippets", ingEx.Ingress, "\n"); exists { 145 if err != nil { 146 glog.Error(err) 147 } else { 148 cfgParams.ServerSnippets = serverSnippets 149 } 150 } 151 152 if locationSnippets, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/location-snippets", ingEx.Ingress, "\n"); exists { 153 if err != nil { 154 glog.Error(err) 155 } else { 156 cfgParams.LocationSnippets = locationSnippets 157 } 158 } 159 160 if proxyConnectTimeout, exists := ingEx.Ingress.Annotations["nginx.org/proxy-connect-timeout"]; exists { 161 if parsedProxyConnectTimeout, err := ParseTime(proxyConnectTimeout); err != nil { 162 glog.Errorf("Ingress %s/%s: Invalid value nginx.org/proxy-connect-timeout: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), proxyConnectTimeout, err) 163 } else { 164 cfgParams.ProxyConnectTimeout = parsedProxyConnectTimeout 165 } 166 } 167 168 if proxyReadTimeout, exists := ingEx.Ingress.Annotations["nginx.org/proxy-read-timeout"]; exists { 169 if parsedProxyReadTimeout, err := ParseTime(proxyReadTimeout); err != nil { 170 glog.Errorf("Ingress %s/%s: Invalid value nginx.org/proxy-read-timeout: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), proxyReadTimeout, err) 171 } else { 172 cfgParams.ProxyReadTimeout = parsedProxyReadTimeout 173 } 174 } 175 176 if proxySendTimeout, exists := ingEx.Ingress.Annotations["nginx.org/proxy-send-timeout"]; exists { 177 if parsedProxySendTimeout, err := ParseTime(proxySendTimeout); err != nil { 178 glog.Errorf("Ingress %s/%s: Invalid value nginx.org/proxy-send-timeout: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), proxySendTimeout, err) 179 } else { 180 cfgParams.ProxySendTimeout = parsedProxySendTimeout 181 } 182 } 183 184 if proxyHideHeaders, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/proxy-hide-headers", ingEx.Ingress, ","); exists { 185 if err != nil { 186 glog.Error(err) 187 } else { 188 cfgParams.ProxyHideHeaders = proxyHideHeaders 189 } 190 } 191 192 if proxyPassHeaders, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/proxy-pass-headers", ingEx.Ingress, ","); exists { 193 if err != nil { 194 glog.Error(err) 195 } else { 196 cfgParams.ProxyPassHeaders = proxyPassHeaders 197 } 198 } 199 200 if clientMaxBodySize, exists := ingEx.Ingress.Annotations["nginx.org/client-max-body-size"]; exists { 201 cfgParams.ClientMaxBodySize = clientMaxBodySize 202 } 203 204 if redirectToHTTPS, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/redirect-to-https", ingEx.Ingress); exists { 205 if err != nil { 206 glog.Error(err) 207 } else { 208 cfgParams.RedirectToHTTPS = redirectToHTTPS 209 } 210 } 211 212 if sslRedirect, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "ingress.kubernetes.io/ssl-redirect", ingEx.Ingress); exists { 213 if err != nil { 214 glog.Error(err) 215 } else { 216 cfgParams.SSLRedirect = sslRedirect 217 } 218 } 219 220 if proxyBuffering, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/proxy-buffering", ingEx.Ingress); exists { 221 if err != nil { 222 glog.Error(err) 223 } else { 224 cfgParams.ProxyBuffering = proxyBuffering 225 } 226 } 227 228 if hsts, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/hsts", ingEx.Ingress); exists { 229 if err != nil { 230 glog.Error(err) 231 } else { 232 parsingErrors := false 233 234 hstsMaxAge, existsMA, err := GetMapKeyAsInt64(ingEx.Ingress.Annotations, "nginx.org/hsts-max-age", ingEx.Ingress) 235 if existsMA && err != nil { 236 glog.Error(err) 237 parsingErrors = true 238 } 239 hstsIncludeSubdomains, existsIS, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/hsts-include-subdomains", ingEx.Ingress) 240 if existsIS && err != nil { 241 glog.Error(err) 242 parsingErrors = true 243 } 244 hstsBehindProxy, existsBP, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/hsts-behind-proxy", ingEx.Ingress) 245 if existsBP && err != nil { 246 glog.Error(err) 247 parsingErrors = true 248 } 249 250 if parsingErrors { 251 glog.Errorf("Ingress %s/%s: There are configuration issues with hsts annotations, skipping annotions for all hsts settings", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName()) 252 } else { 253 cfgParams.HSTS = hsts 254 if existsMA { 255 cfgParams.HSTSMaxAge = hstsMaxAge 256 } 257 if existsIS { 258 cfgParams.HSTSIncludeSubdomains = hstsIncludeSubdomains 259 } 260 if existsBP { 261 cfgParams.HSTSBehindProxy = hstsBehindProxy 262 } 263 } 264 } 265 } 266 267 if proxyBuffers, exists := ingEx.Ingress.Annotations["nginx.org/proxy-buffers"]; exists { 268 cfgParams.ProxyBuffers = proxyBuffers 269 } 270 271 if proxyBufferSize, exists := ingEx.Ingress.Annotations["nginx.org/proxy-buffer-size"]; exists { 272 cfgParams.ProxyBufferSize = proxyBufferSize 273 } 274 275 if upstreamZoneSize, exists := ingEx.Ingress.Annotations["nginx.org/upstream-zone-size"]; exists { 276 cfgParams.UpstreamZoneSize = upstreamZoneSize 277 } 278 279 if proxyMaxTempFileSize, exists := ingEx.Ingress.Annotations["nginx.org/proxy-max-temp-file-size"]; exists { 280 cfgParams.ProxyMaxTempFileSize = proxyMaxTempFileSize 281 } 282 283 if isPlus { 284 if jwtRealm, exists := ingEx.Ingress.Annotations["nginx.com/jwt-realm"]; exists { 285 cfgParams.JWTRealm = jwtRealm 286 } 287 if jwtKey, exists := ingEx.Ingress.Annotations[JWTKeyAnnotation]; exists { 288 cfgParams.JWTKey = jwtKey 289 } 290 if jwtToken, exists := ingEx.Ingress.Annotations["nginx.com/jwt-token"]; exists { 291 cfgParams.JWTToken = jwtToken 292 } 293 if jwtLoginURL, exists := ingEx.Ingress.Annotations["nginx.com/jwt-login-url"]; exists { 294 cfgParams.JWTLoginURL = jwtLoginURL 295 } 296 } 297 298 if values, exists := ingEx.Ingress.Annotations["nginx.org/listen-ports"]; exists { 299 ports, err := ParsePortList(values) 300 if err != nil { 301 glog.Errorf("In %v nginx.org/listen-ports contains invalid declaration: %v, ignoring", ingEx.Ingress.Name, err) 302 } 303 if len(ports) > 0 { 304 cfgParams.Ports = ports 305 } 306 } 307 308 if values, exists := ingEx.Ingress.Annotations["nginx.org/listen-ports-ssl"]; exists { 309 sslPorts, err := ParsePortList(values) 310 if err != nil { 311 glog.Errorf("In %v nginx.org/listen-ports-ssl contains invalid declaration: %v, ignoring", ingEx.Ingress.Name, err) 312 } 313 if len(sslPorts) > 0 { 314 cfgParams.SSLPorts = sslPorts 315 } 316 } 317 318 if keepalive, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.org/keepalive", ingEx.Ingress); exists { 319 if err != nil { 320 glog.Error(err) 321 } else { 322 cfgParams.Keepalive = keepalive 323 } 324 } 325 326 if maxFails, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.org/max-fails", ingEx.Ingress); exists { 327 if err != nil { 328 glog.Error(err) 329 } else { 330 cfgParams.MaxFails = maxFails 331 } 332 } 333 334 if maxConns, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.org/max-conns", ingEx.Ingress); exists { 335 if err != nil { 336 glog.Error(err) 337 } else { 338 cfgParams.MaxConns = maxConns 339 } 340 } 341 342 if failTimeout, exists := ingEx.Ingress.Annotations["nginx.org/fail-timeout"]; exists { 343 if parsedFailTimeout, err := ParseTime(failTimeout); err != nil { 344 glog.Errorf("Ingress %s/%s: Invalid value nginx.org/fail-timeout: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), failTimeout, err) 345 } else { 346 cfgParams.FailTimeout = parsedFailTimeout 347 } 348 } 349 350 if hasAppProtect { 351 if appProtectEnable, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "appprotect.f5.com/app-protect-enable", ingEx.Ingress); exists { 352 if err != nil { 353 glog.Error(err) 354 } else { 355 if appProtectEnable { 356 cfgParams.AppProtectEnable = "on" 357 } else { 358 cfgParams.AppProtectEnable = "off" 359 } 360 } 361 } 362 363 if appProtectLogEnable, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "appprotect.f5.com/app-protect-security-log-enable", ingEx.Ingress); exists { 364 if err != nil { 365 glog.Error(err) 366 } else { 367 if appProtectLogEnable { 368 cfgParams.AppProtectLogEnable = "on" 369 } else { 370 cfgParams.AppProtectLogEnable = "off" 371 } 372 } 373 } 374 375 } 376 if enableInternalRoutes { 377 if spiffeServerCerts, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, nginxMeshInternalRouteAnnotation, ingEx.Ingress); exists { 378 if err != nil { 379 glog.Error(err) 380 } else { 381 cfgParams.SpiffeServerCerts = spiffeServerCerts 382 } 383 } 384 } 385 return cfgParams 386 } 387 388 func getWebsocketServices(ingEx *IngressEx) map[string]bool { 389 if value, exists := ingEx.Ingress.Annotations["nginx.org/websocket-services"]; exists { 390 return ParseServiceList(value) 391 } 392 return nil 393 } 394 395 func getRewrites(ingEx *IngressEx) map[string]string { 396 if value, exists := ingEx.Ingress.Annotations["nginx.org/rewrites"]; exists { 397 rewrites, err := ParseRewriteList(value) 398 if err != nil { 399 glog.Error(err) 400 } 401 return rewrites 402 } 403 return nil 404 } 405 406 func getSSLServices(ingEx *IngressEx) map[string]bool { 407 if value, exists := ingEx.Ingress.Annotations["nginx.org/ssl-services"]; exists { 408 return ParseServiceList(value) 409 } 410 return nil 411 } 412 413 func getGrpcServices(ingEx *IngressEx) map[string]bool { 414 if value, exists := ingEx.Ingress.Annotations["nginx.org/grpc-services"]; exists { 415 return ParseServiceList(value) 416 } 417 return nil 418 } 419 420 func getSessionPersistenceServices(ingEx *IngressEx) map[string]string { 421 if value, exists := ingEx.Ingress.Annotations["nginx.com/sticky-cookie-services"]; exists { 422 services, err := ParseStickyServiceList(value) 423 if err != nil { 424 glog.Error(err) 425 } 426 return services 427 } 428 return nil 429 } 430 431 func filterMasterAnnotations(annotations map[string]string) []string { 432 var removedAnnotations []string 433 434 for key := range annotations { 435 if _, notAllowed := masterBlacklist[key]; notAllowed { 436 removedAnnotations = append(removedAnnotations, key) 437 delete(annotations, key) 438 } 439 } 440 441 return removedAnnotations 442 } 443 444 func filterMinionAnnotations(annotations map[string]string) []string { 445 var removedAnnotations []string 446 447 for key := range annotations { 448 if _, notAllowed := minionBlacklist[key]; notAllowed { 449 removedAnnotations = append(removedAnnotations, key) 450 delete(annotations, key) 451 } 452 } 453 454 return removedAnnotations 455 } 456 457 func mergeMasterAnnotationsIntoMinion(minionAnnotations map[string]string, masterAnnotations map[string]string) { 458 for key, val := range masterAnnotations { 459 if _, exists := minionAnnotations[key]; !exists { 460 if _, allowed := minionInheritanceList[key]; allowed { 461 minionAnnotations[key] = val 462 } 463 } 464 } 465 }