added proxy-intercept-errors config option
This commit is contained in:
parent
e7bee5308e
commit
cc0872cfab
10 changed files with 151 additions and 3 deletions
|
@ -49,6 +49,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
|
||||||
|[nginx.ingress.kubernetes.io/client-body-buffer-size](#client-body-buffer-size)|string|
|
|[nginx.ingress.kubernetes.io/client-body-buffer-size](#client-body-buffer-size)|string|
|
||||||
|[nginx.ingress.kubernetes.io/configuration-snippet](#configuration-snippet)|string|
|
|[nginx.ingress.kubernetes.io/configuration-snippet](#configuration-snippet)|string|
|
||||||
|[nginx.ingress.kubernetes.io/custom-http-errors](#custom-http-errors)|[]int|
|
|[nginx.ingress.kubernetes.io/custom-http-errors](#custom-http-errors)|[]int|
|
||||||
|
|[nginx.ingress.kubernetes.io/proxy-intercept-errors](#proxy-intercept-errors)|"true" or "false"|
|
||||||
|[nginx.ingress.kubernetes.io/default-backend](#default-backend)|string|
|
|[nginx.ingress.kubernetes.io/default-backend](#default-backend)|string|
|
||||||
|[nginx.ingress.kubernetes.io/enable-cors](#enable-cors)|"true" or "false"|
|
|[nginx.ingress.kubernetes.io/enable-cors](#enable-cors)|"true" or "false"|
|
||||||
|[nginx.ingress.kubernetes.io/cors-allow-origin](#enable-cors)|string|
|
|[nginx.ingress.kubernetes.io/cors-allow-origin](#enable-cors)|string|
|
||||||
|
@ -331,6 +332,17 @@ Example usage:
|
||||||
nginx.ingress.kubernetes.io/custom-http-errors: "404,415"
|
nginx.ingress.kubernetes.io/custom-http-errors: "404,415"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Proxy intercept Errors
|
||||||
|
|
||||||
|
Like the [`proxy-intercept-errors`](./configmap.md#proxy-intercept-errors) value in the ConfigMap, this annotation allows to disable NGINX `proxy-intercept-errors` when `custom-http-errors` are set, but only for the NGINX location associated with this ingress. If a [default backend annotation](#default-backend) is specified on the ingress, the errors will be routed to that annotation's default backend service (instead of the global default backend).
|
||||||
|
Different ingresses can specify different sets of errors codes and there are UseCases where NGINX shall not intercept all errors returned from upstream.
|
||||||
|
If `proxy-intercept-errors` is also specified globally, the annotation will override the global value for the given ingress' hostname and path.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
```
|
||||||
|
nginx.ingress.kubernetes.io/proxy-intercept-errors: "false"
|
||||||
|
```
|
||||||
|
|
||||||
### Default Backend
|
### Default Backend
|
||||||
|
|
||||||
This annotation is of the form `nginx.ingress.kubernetes.io/default-backend: <svc name>` to specify a custom default backend. This `<svc name>` is a reference to a service inside of the same namespace in which you are applying this annotation. This annotation overrides the global default backend. In case the service has [multiple ports](https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services), the first one is the one which will receive the backend traffic.
|
This annotation is of the form `nginx.ingress.kubernetes.io/default-backend: <svc name>` to specify a custom default backend. This `<svc name>` is a reference to a service inside of the same namespace in which you are applying this annotation. This annotation overrides the global default backend. In case the service has [multiple ports](https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services), the first one is the one which will receive the backend traffic.
|
||||||
|
|
|
@ -161,6 +161,7 @@ The following table shows a configuration option's name, type, and the default v
|
||||||
|[stream-snippet](#stream-snippet)|string|""|
|
|[stream-snippet](#stream-snippet)|string|""|
|
||||||
|[location-snippet](#location-snippet)|string|""|
|
|[location-snippet](#location-snippet)|string|""|
|
||||||
|[custom-http-errors](#custom-http-errors)|[]int|[]int{}|
|
|[custom-http-errors](#custom-http-errors)|[]int|[]int{}|
|
||||||
|
|[proxy-intercept-errors](#proxy-intercept-errors)|bool|"true"|
|
||||||
|[proxy-body-size](#proxy-body-size)|string|"1m"|
|
|[proxy-body-size](#proxy-body-size)|string|"1m"|
|
||||||
|[proxy-connect-timeout](#proxy-connect-timeout)|int|5|
|
|[proxy-connect-timeout](#proxy-connect-timeout)|int|5|
|
||||||
|[proxy-read-timeout](#proxy-read-timeout)|int|60|
|
|[proxy-read-timeout](#proxy-read-timeout)|int|60|
|
||||||
|
@ -1028,10 +1029,16 @@ You can not use this to add new locations that proxy to the Kubernetes pods, as
|
||||||
|
|
||||||
Enables which HTTP codes should be passed for processing with the [error_page directive](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page)
|
Enables which HTTP codes should be passed for processing with the [error_page directive](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page)
|
||||||
|
|
||||||
Setting at least one code also enables [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) which are required to process error_page.
|
Setting at least one code also enables [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) if not disabled with [proxy-intercept-errors](#proxy-intercept-errors).
|
||||||
|
|
||||||
Example usage: `custom-http-errors: 404,415`
|
Example usage: `custom-http-errors: 404,415`
|
||||||
|
|
||||||
|
## proxy-intercept-errors
|
||||||
|
|
||||||
|
Allows to disable proxy-intercept-errors if [custom-http-errors](#custom-http-errors) are set. Disabling [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) allows to pass upstream errors to client even if [custom-http-errors](#custom-http-errors) are set.
|
||||||
|
|
||||||
|
Example usage: `proxy-intercept-errors: "false"`
|
||||||
|
|
||||||
## proxy-body-size
|
## proxy-body-size
|
||||||
|
|
||||||
Sets the maximum allowed size of the client request body.
|
Sets the maximum allowed size of the client request body.
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/proxyintercepterrors"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/sslcipher"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/sslcipher"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/streamsnippet"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/streamsnippet"
|
||||||
|
@ -86,6 +87,7 @@ type Ingress struct {
|
||||||
Connection connection.Config
|
Connection connection.Config
|
||||||
CorsConfig cors.Config
|
CorsConfig cors.Config
|
||||||
CustomHTTPErrors []int
|
CustomHTTPErrors []int
|
||||||
|
ProxyInterceptErrors bool
|
||||||
DefaultBackend *apiv1.Service
|
DefaultBackend *apiv1.Service
|
||||||
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
|
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
|
||||||
FastCGI fastcgi.Config
|
FastCGI fastcgi.Config
|
||||||
|
@ -139,6 +141,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
|
||||||
"Connection": connection.NewParser(cfg),
|
"Connection": connection.NewParser(cfg),
|
||||||
"CorsConfig": cors.NewParser(cfg),
|
"CorsConfig": cors.NewParser(cfg),
|
||||||
"CustomHTTPErrors": customhttperrors.NewParser(cfg),
|
"CustomHTTPErrors": customhttperrors.NewParser(cfg),
|
||||||
|
"ProxyInterceptErrors": proxyintercepterrors.NewParser(cfg),
|
||||||
"DefaultBackend": defaultbackend.NewParser(cfg),
|
"DefaultBackend": defaultbackend.NewParser(cfg),
|
||||||
"FastCGI": fastcgi.NewParser(cfg),
|
"FastCGI": fastcgi.NewParser(cfg),
|
||||||
"ExternalAuth": authreq.NewParser(cfg),
|
"ExternalAuth": authreq.NewParser(cfg),
|
||||||
|
|
43
internal/ingress/annotations/proxyintercepterrors/main.go
Normal file
43
internal/ingress/annotations/proxyintercepterrors/main.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package proxyintercepterrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
networking "k8s.io/api/networking/v1"
|
||||||
|
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type proxyInterceptErrors struct {
|
||||||
|
r resolver.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParser creates a new proxyintercepterrors annotation parser
|
||||||
|
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||||
|
return proxyInterceptErrors{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s proxyInterceptErrors) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||||
|
defBackend := s.r.GetDefaultBackend()
|
||||||
|
|
||||||
|
val, err := parser.GetBoolAnnotation("proxy-intercept-errors", ing)
|
||||||
|
// A missing annotation is not a problem, just use the default
|
||||||
|
if err == errors.ErrMissingAnnotations {
|
||||||
|
return defBackend.ProxyInterceptErrors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package proxyintercepterrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
api "k8s.io/api/core/v1"
|
||||||
|
networking "k8s.io/api/networking/v1"
|
||||||
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildIngress() *networking.Ingress {
|
||||||
|
return &networking.Ingress{
|
||||||
|
ObjectMeta: meta_v1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
},
|
||||||
|
Spec: networking.IngressSpec{
|
||||||
|
DefaultBackend: &networking.IngressBackend{
|
||||||
|
Service: &networking.IngressServiceBackend{
|
||||||
|
Name: "default-backend",
|
||||||
|
Port: networking.ServiceBackendPort{
|
||||||
|
Number: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAnnotations(t *testing.T) {
|
||||||
|
ing := buildIngress()
|
||||||
|
|
||||||
|
_, err := NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := map[string]string{}
|
||||||
|
data[parser.GetAnnotationWithPrefix("proxy-intercept-errors")] = "true"
|
||||||
|
ing.SetAnnotations(data)
|
||||||
|
// test ingress using the annotation without a TLS section
|
||||||
|
_, err = NewParser(&resolver.Mock{}).Parse(ing)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error parsing ingress with proxy intercept errors")
|
||||||
|
}
|
||||||
|
}
|
|
@ -519,6 +519,10 @@ type Configuration struct {
|
||||||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version
|
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version
|
||||||
ProxyHTTPVersion string `json:"proxy-http-version"`
|
ProxyHTTPVersion string `json:"proxy-http-version"`
|
||||||
|
|
||||||
|
// Disables NGINX proxy-intercept-errors when error_page/custom-http-errors are set
|
||||||
|
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
||||||
|
ProxyInterceptErrors bool `json:"proxy-intercept-errors,omitempty"`
|
||||||
|
|
||||||
// Sets the ipv4 addresses on which the server will accept requests.
|
// Sets the ipv4 addresses on which the server will accept requests.
|
||||||
BindAddressIpv4 []string `json:"bind-address-ipv4,omitempty"`
|
BindAddressIpv4 []string `json:"bind-address-ipv4,omitempty"`
|
||||||
|
|
||||||
|
@ -873,6 +877,7 @@ func NewDefault() Configuration {
|
||||||
VariablesHashBucketSize: 256,
|
VariablesHashBucketSize: 256,
|
||||||
VariablesHashMaxSize: 2048,
|
VariablesHashMaxSize: 2048,
|
||||||
UseHTTP2: true,
|
UseHTTP2: true,
|
||||||
|
ProxyInterceptErrors: true,
|
||||||
ProxyStreamTimeout: "600s",
|
ProxyStreamTimeout: "600s",
|
||||||
ProxyStreamNextUpstream: true,
|
ProxyStreamNextUpstream: true,
|
||||||
ProxyStreamNextUpstreamTimeout: "600s",
|
ProxyStreamNextUpstreamTimeout: "600s",
|
||||||
|
@ -895,6 +900,7 @@ func NewDefault() Configuration {
|
||||||
PreserveTrailingSlash: false,
|
PreserveTrailingSlash: false,
|
||||||
SSLRedirect: true,
|
SSLRedirect: true,
|
||||||
CustomHTTPErrors: []int{},
|
CustomHTTPErrors: []int{},
|
||||||
|
ProxyInterceptErrors: true,
|
||||||
DenylistSourceRange: []string{},
|
DenylistSourceRange: []string{},
|
||||||
WhitelistSourceRange: []string{},
|
WhitelistSourceRange: []string{},
|
||||||
SkipAccessLogURLs: []string{},
|
SkipAccessLogURLs: []string{},
|
||||||
|
|
|
@ -34,6 +34,13 @@ type Backend struct {
|
||||||
// toggles whether or not to remove trailing slashes during TLS redirects
|
// toggles whether or not to remove trailing slashes during TLS redirects
|
||||||
PreserveTrailingSlash bool `json:"preserve-trailing-slash"`
|
PreserveTrailingSlash bool `json:"preserve-trailing-slash"`
|
||||||
|
|
||||||
|
// for use when using CustomHTTPErrors without intecepting service errors
|
||||||
|
// e.g. custom 404 and 503 when service-a does not exist or is not available
|
||||||
|
// but service-a can return 404 and 503 error codes without intercept
|
||||||
|
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
||||||
|
// By default this is enabled when CustomHTTPErrors is enabled
|
||||||
|
ProxyInterceptErrors bool `json:"proxy-intercept-errors"`
|
||||||
|
|
||||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
||||||
// Sets the maximum allowed size of the client request body
|
// Sets the maximum allowed size of the client request body
|
||||||
ProxyBodySize string `json:"proxy-body-size"`
|
ProxyBodySize string `json:"proxy-body-size"`
|
||||||
|
|
|
@ -349,6 +349,11 @@ type Location struct {
|
||||||
// CustomHTTPErrors specifies the error codes that should be intercepted.
|
// CustomHTTPErrors specifies the error codes that should be intercepted.
|
||||||
// +optional
|
// +optional
|
||||||
CustomHTTPErrors []int `json:"custom-http-errors"`
|
CustomHTTPErrors []int `json:"custom-http-errors"`
|
||||||
|
// ProxyInterceptErrors disables error intecepting when using CustomHTTPErrors
|
||||||
|
// e.g. custom 404 and 503 when service-a does not exist or is not available
|
||||||
|
// but service-a can return 404 and 503 error codes without intercept
|
||||||
|
// +optional
|
||||||
|
ProxyInterceptErrors bool `json:"proxy-intercept-errors"`
|
||||||
// ModSecurity allows to enable and configure modsecurity
|
// ModSecurity allows to enable and configure modsecurity
|
||||||
// +optional
|
// +optional
|
||||||
ModSecurity modsecurity.Config `json:"modsecurity"`
|
ModSecurity modsecurity.Config `json:"modsecurity"`
|
||||||
|
|
|
@ -472,6 +472,10 @@ func (l1 *Location) Equal(l2 *Location) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !l1.ProxyInterceptErrors != l2.ProxyInterceptErrors {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -470,7 +470,7 @@ http {
|
||||||
ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
||||||
ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
||||||
|
|
||||||
{{ if gt (len $cfg.CustomHTTPErrors) 0 }}
|
{{ if and (gt (len $cfg.CustomHTTPErrors) 0) $cfg.ProxyInterceptErrors }}
|
||||||
proxy_intercept_errors on;
|
proxy_intercept_errors on;
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -1450,7 +1450,7 @@ stream {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{/* if a location-specific error override is set, add the proxy_intercept here */}}
|
{{/* if a location-specific error override is set, add the proxy_intercept here */}}
|
||||||
{{ if $location.CustomHTTPErrors }}
|
{{ if and $location.CustomHTTPErrors $location.ProxyInterceptErrors }}
|
||||||
# Custom error pages per ingress
|
# Custom error pages per ingress
|
||||||
proxy_intercept_errors on;
|
proxy_intercept_errors on;
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Reference in a new issue