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  }