add custom headers

Signed-off-by: Christian Groschupp <christian@groschupp.org>
This commit is contained in:
Christian Groschupp 2023-03-15 14:14:27 +01:00
parent d56aacdb31
commit 87f27d98ab
5 changed files with 97 additions and 1 deletions

View file

@ -20,6 +20,7 @@ import (
"dario.cat/mergo"
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
"k8s.io/ingress-nginx/internal/ingress/annotations/customheaders"
"k8s.io/ingress-nginx/internal/ingress/annotations/disableproxyintercepterrors"
"k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity"
"k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry"
@ -82,6 +83,7 @@ type Ingress struct {
Canary canary.Config
CertificateAuth authtls.Config
ClientBodyBufferSize string
CustomHeaders customheaders.Config
ConfigurationSnippet string
Connection connection.Config
CorsConfig cors.Config
@ -133,6 +135,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
"Canary": canary.NewParser(cfg),
"CertificateAuth": authtls.NewParser(cfg),
"ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg),
"ClientHeaders": customheaders.NewParser(cfg),
"ConfigurationSnippet": snippet.NewParser(cfg),
"Connection": connection.NewParser(cfg),
"CorsConfig": cors.NewParser(cfg),

View file

@ -0,0 +1,83 @@
/*
Copyright 2015 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 customheaders
import (
"fmt"
"regexp"
"k8s.io/klog/v2"
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
// Config returns external authentication configuration for an Ingress rule
type Config struct {
Headers map[string]string `json:"headers,omitempty"`
}
var (
headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`)
)
// ValidHeader checks is the provided string satisfies the header's name regex
func ValidHeader(header string) bool {
return headerRegexp.Match([]byte(header))
}
type customHeaders struct {
r resolver.Resolver
}
// NewParser creates a new authentication request annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return customHeaders{r}
}
// ParseAnnotations parses the annotations contained in the ingress
// rule used to use an Config URL as source for authentication
func (a customHeaders) Parse(ing *networking.Ingress) (interface{}, error) {
clientHeadersConfigMapName, err := parser.GetStringAnnotation("custom-headers", ing)
if err != nil {
klog.V(3).InfoS("client-headers annotation is undefined and will not be set")
}
var headers map[string]string
if clientHeadersConfigMapName != "" {
clientHeadersMapContents, err := a.r.GetConfigMap(clientHeadersConfigMapName)
if err != nil {
return nil, ing_errors.NewLocationDenied(fmt.Sprintf("unable to find configMap %q", clientHeadersConfigMapName))
}
for header := range clientHeadersMapContents.Data {
if !ValidHeader(header) {
return nil, ing_errors.NewLocationDenied("invalid client-headers in configmap")
}
}
headers = clientHeadersMapContents.Data
}
return &Config{
Headers: headers,
}, nil
}

View file

@ -1504,6 +1504,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress) {
loc.BasicDigestAuth = anns.BasicDigestAuth
loc.ClientBodyBufferSize = anns.ClientBodyBufferSize
loc.CustomHeaders = anns.CustomHeaders
loc.ConfigurationSnippet = anns.ConfigurationSnippet
loc.CorsConfig = anns.CorsConfig
loc.ExternalAuth = anns.ExternalAuth

View file

@ -27,6 +27,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
"k8s.io/ingress-nginx/internal/ingress/annotations/connection"
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
"k8s.io/ingress-nginx/internal/ingress/annotations/customheaders"
"k8s.io/ingress-nginx/internal/ingress/annotations/fastcgi"
"k8s.io/ingress-nginx/internal/ingress/annotations/globalratelimit"
"k8s.io/ingress-nginx/internal/ingress/annotations/ipallowlist"
@ -263,7 +264,8 @@ type Location struct {
BasicDigestAuth auth.Config `json:"basicDigestAuth,omitempty"`
// Denied returns an error when this location cannot not be allowed
// Requesting a denied location should return HTTP code 403.
Denied *string `json:"denied,omitempty"`
Denied *string `json:"denied,omitempty"`
CustomHeaders customheaders.Config `json:"customHeaders,omitempty"`
// CorsConfig returns the Cors Configuration for the ingress rule
// +optional
CorsConfig cors.Config `json:"corsConfig,omitempty"`

View file

@ -1489,6 +1489,13 @@ stream {
{{ $all.Cfg.LocationSnippet }}
{{ end }}
{{ if $location.CustomHeaders }}
# Custom Response Headers
{{ range $k, $v := $location.CustomHeaders.Headers }}
more_set_headers {{ printf "%s: %s" $k $v | quote }};
{{ end }}
{{ end }}
{{/* if we are sending the request to a custom default backend, we add the required headers */}}
{{ if (hasPrefix $location.Backend "custom-default-backend-") }}
proxy_set_header X-Code 503;