Add option to force enabling snippet directives (#7665)
Signed-off-by: Ricardo Pchevuzinske Katz <ricardo.katz@gmail.com>
This commit is contained in:
parent
314cc6c2dc
commit
5e6ab651ec
12 changed files with 459 additions and 24 deletions
|
@ -4,6 +4,7 @@ controller:
|
|||
tag: 1.0.0-dev
|
||||
digest: null
|
||||
kind: DaemonSet
|
||||
enableSnippetDirectives: false
|
||||
admissionWebhooks:
|
||||
enabled: false
|
||||
service:
|
||||
|
|
|
@ -5,6 +5,7 @@ controller:
|
|||
digest: null
|
||||
config:
|
||||
use-proxy-protocol: "true"
|
||||
enableSnippetDirectives: false
|
||||
admissionWebhooks:
|
||||
enabled: false
|
||||
service:
|
||||
|
|
|
@ -10,6 +10,7 @@ metadata:
|
|||
name: {{ include "ingress-nginx.controller.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
enable-snippet-directives: "{{ .Values.controller.enableSnippetDirectives }}"
|
||||
{{- if .Values.controller.addHeaders }}
|
||||
add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers
|
||||
{{- end }}
|
||||
|
|
|
@ -69,6 +69,12 @@ controller:
|
|||
# Process IngressClass per name (additionally as per spec.controller)
|
||||
ingressClassByName: false
|
||||
|
||||
# This configuration defines if Ingress Controller should allow users to set
|
||||
# their own *-snippet directives/annotations, otherwise this is forbidden / dropped
|
||||
# when users add those annotations.
|
||||
# Global snippets in ConfigMap are still respected
|
||||
enableSnippetDirectives: true
|
||||
|
||||
# Required for use with CNI based kubernetes installations (such as ones set up by kubeadm),
|
||||
# since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920
|
||||
# is merged
|
||||
|
|
|
@ -46,6 +46,7 @@ The following table shows a configuration option's name, type, and the default v
|
|||
|[disable-access-log](#disable-access-log)|bool|false|
|
||||
|[disable-ipv6](#disable-ipv6)|bool|false|
|
||||
|[disable-ipv6-dns](#disable-ipv6-dns)|bool|false|
|
||||
|[enable-snippet-directives](#enable-snippet-directives)|bool|true|
|
||||
|[enable-underscores-in-headers](#enable-underscores-in-headers)|bool|false|
|
||||
|[enable-ocsp](#enable-ocsp)|bool|false|
|
||||
|[ignore-invalid-headers](#ignore-invalid-headers)|bool|true|
|
||||
|
@ -316,6 +317,12 @@ Disable listening on IPV6. _**default:**_ `false`; IPv6 listening is enabled
|
|||
|
||||
Disable IPV6 for nginx DNS resolver. _**default:**_ `false`; IPv6 resolving enabled.
|
||||
|
||||
## enable-snippet-directives
|
||||
|
||||
Enables Ingress to parse and add *-snippet annotations/directives created by the user. _**default:**_ `true`;
|
||||
Obs.: We recommend enabling this option only if you TRUST users with permission to create Ingress objects, as this
|
||||
may allow a user to add restricted configurations to the final nginx.conf file
|
||||
|
||||
## enable-underscores-in-headers
|
||||
|
||||
Enables underscores in header names. _**default:**_ is disabled
|
||||
|
|
|
@ -93,6 +93,10 @@ const (
|
|||
type Configuration struct {
|
||||
defaults.Backend `json:",squash"`
|
||||
|
||||
// EnableSnippetDirectives enable users to add their own snippets via ingress annotation.
|
||||
// If disabled, only snippets added via ConfigMap are added to ingress.
|
||||
EnableSnippetDirectives bool `json:"enable-snippet-directives"`
|
||||
|
||||
// Sets the name of the configmap that contains the headers to pass to the client
|
||||
AddHeaders string `json:"add-headers,omitempty"`
|
||||
|
||||
|
@ -757,6 +761,7 @@ func NewDefault() Configuration {
|
|||
defGlobalExternalAuth := GlobalExternalAuth{"", "", "", "", "", append(defResponseHeaders, ""), "", "", "", []string{}, map[string]string{}}
|
||||
|
||||
cfg := Configuration{
|
||||
EnableSnippetDirectives: true,
|
||||
AllowBackendServerHeader: false,
|
||||
AccessLogPath: "/var/log/nginx/access.log",
|
||||
AccessLogParams: "",
|
||||
|
|
|
@ -234,27 +234,28 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
|
|||
return fmt.Errorf("This deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.backend' or set DisableCatchAll flag to false.")
|
||||
}
|
||||
|
||||
if parser.AnnotationsPrefix != parser.DefaultAnnotationsPrefix {
|
||||
for key := range ing.ObjectMeta.GetAnnotations() {
|
||||
cfg := n.store.GetBackendConfiguration()
|
||||
cfg.Resolver = n.resolver
|
||||
|
||||
for key := range ing.ObjectMeta.GetAnnotations() {
|
||||
if parser.AnnotationsPrefix != parser.DefaultAnnotationsPrefix {
|
||||
if strings.HasPrefix(key, fmt.Sprintf("%s/", parser.DefaultAnnotationsPrefix)) {
|
||||
return fmt.Errorf("This deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix)
|
||||
}
|
||||
}
|
||||
|
||||
if !cfg.EnableSnippetDirectives && strings.HasSuffix(key, "-snippet") {
|
||||
return fmt.Errorf("%s annotation cannot be used. Snippet directives are disabled by the Ingress administrator", key)
|
||||
}
|
||||
|
||||
if len(cfg.GlobalRateLimitMemcachedHost) == 0 && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
|
||||
return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
k8s.SetDefaultNGINXPathType(ing)
|
||||
|
||||
cfg := n.store.GetBackendConfiguration()
|
||||
cfg.Resolver = n.resolver
|
||||
|
||||
if len(cfg.GlobalRateLimitMemcachedHost) == 0 {
|
||||
for key := range ing.ObjectMeta.GetAnnotations() {
|
||||
if strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
|
||||
return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allIngresses := n.store.ListIngresses()
|
||||
|
||||
filter := func(toCheck *ingress.Ingress) bool {
|
||||
|
@ -511,6 +512,30 @@ func (n *NGINXController) getConfiguration(ingresses []*ingress.Ingress) (sets.S
|
|||
}
|
||||
}
|
||||
|
||||
func dropSnippetDirectives(anns *annotations.Ingress, ingKey string) {
|
||||
if anns != nil {
|
||||
if anns.ConfigurationSnippet != "" {
|
||||
klog.V(3).Infof("Ingress %q tried to use configuration-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
|
||||
anns.ConfigurationSnippet = ""
|
||||
}
|
||||
if anns.ServerSnippet != "" {
|
||||
klog.V(3).Infof("Ingress %q tried to use server-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
|
||||
anns.ServerSnippet = ""
|
||||
}
|
||||
|
||||
if anns.ModSecurity.Snippet != "" {
|
||||
klog.V(3).Infof("Ingress %q tried to use modsecurity-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
|
||||
anns.ModSecurity.Snippet = ""
|
||||
}
|
||||
|
||||
if anns.ExternalAuth.AuthSnippet != "" {
|
||||
klog.V(3).Infof("Ingress %q tried to use auth-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
|
||||
anns.ExternalAuth.AuthSnippet = ""
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// getBackendServers returns a list of Upstream and Server to be used by the
|
||||
// backend. An upstream can be used in multiple servers if the namespace,
|
||||
// service name and port are the same.
|
||||
|
@ -525,6 +550,10 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
|
|||
ingKey := k8s.MetaNamespaceKey(ing)
|
||||
anns := ing.ParsedAnnotations
|
||||
|
||||
if !n.store.GetBackendConfiguration().EnableSnippetDirectives {
|
||||
dropSnippetDirectives(anns, ingKey)
|
||||
}
|
||||
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
host := rule.Host
|
||||
if host == "" {
|
||||
|
@ -801,6 +830,10 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
|
|||
ingKey := k8s.MetaNamespaceKey(ing)
|
||||
anns := ing.ParsedAnnotations
|
||||
|
||||
if !n.store.GetBackendConfiguration().EnableSnippetDirectives {
|
||||
dropSnippetDirectives(anns, ingKey)
|
||||
}
|
||||
|
||||
var defBackend string
|
||||
if ing.Spec.DefaultBackend != nil && ing.Spec.DefaultBackend.Service != nil {
|
||||
defBackend = upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service)
|
||||
|
@ -1091,6 +1124,10 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
ingKey := k8s.MetaNamespaceKey(ing)
|
||||
anns := ing.ParsedAnnotations
|
||||
|
||||
if !n.store.GetBackendConfiguration().EnableSnippetDirectives {
|
||||
dropSnippetDirectives(anns, ingKey)
|
||||
}
|
||||
|
||||
// default upstream name
|
||||
un := du.Name
|
||||
|
||||
|
@ -1167,6 +1204,10 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
ingKey := k8s.MetaNamespaceKey(ing)
|
||||
anns := ing.ParsedAnnotations
|
||||
|
||||
if !n.store.GetBackendConfiguration().EnableSnippetDirectives {
|
||||
dropSnippetDirectives(anns, ingKey)
|
||||
}
|
||||
|
||||
if anns.Canary.Enabled {
|
||||
klog.V(2).Infof("Ingress %v is marked as Canary, ignoring", ingKey)
|
||||
continue
|
||||
|
|
|
@ -42,6 +42,7 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/sessionaffinity"
|
||||
|
@ -57,11 +58,12 @@ import (
|
|||
)
|
||||
|
||||
type fakeIngressStore struct {
|
||||
ingresses []*ingress.Ingress
|
||||
ingresses []*ingress.Ingress
|
||||
configuration ngx_config.Configuration
|
||||
}
|
||||
|
||||
func (fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
|
||||
return ngx_config.Configuration{}
|
||||
func (fis fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
|
||||
return fis.configuration
|
||||
}
|
||||
|
||||
func (fakeIngressStore) GetConfigMap(key string) (*corev1.ConfigMap, error) {
|
||||
|
@ -235,6 +237,9 @@ func TestCheckIngress(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("When the default annotation prefix is used despite an override", func(t *testing.T) {
|
||||
defer func() {
|
||||
parser.AnnotationsPrefix = "nginx.ingress.kubernetes.io"
|
||||
}()
|
||||
parser.AnnotationsPrefix = "ingress.kubernetes.io"
|
||||
ing.ObjectMeta.Annotations["nginx.ingress.kubernetes.io/backend-protocol"] = "GRPC"
|
||||
nginx.command = testNginxTestCommand{
|
||||
|
@ -246,6 +251,23 @@ func TestCheckIngress(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("When snippets are disabled and user tries to use snippet annotation", func(t *testing.T) {
|
||||
nginx.store = fakeIngressStore{
|
||||
ingresses: []*ingress.Ingress{},
|
||||
configuration: ngx_config.Configuration{
|
||||
EnableSnippetDirectives: false,
|
||||
},
|
||||
}
|
||||
nginx.command = testNginxTestCommand{
|
||||
t: t,
|
||||
err: nil,
|
||||
}
|
||||
ing.ObjectMeta.Annotations["nginx.ingress.kubernetes.io/server-snippet"] = "bla"
|
||||
if err := nginx.CheckIngress(ing); err == nil {
|
||||
t.Errorf("with a snippet annotation, ingresses using the default should be rejected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("When a new catch-all ingress is being created despite catch-alls being disabled ", func(t *testing.T) {
|
||||
backendBefore := ing.Spec.DefaultBackend
|
||||
disableCatchAllBefore := nginx.cfg.DisableCatchAll
|
||||
|
@ -275,6 +297,9 @@ func TestCheckIngress(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("When the ingress is in a different namespace than the watched one", func(t *testing.T) {
|
||||
defer func() {
|
||||
nginx.cfg.Namespace = "test-namespace"
|
||||
}()
|
||||
nginx.command = testNginxTestCommand{
|
||||
t: t,
|
||||
err: fmt.Errorf("test error"),
|
||||
|
@ -2211,6 +2236,84 @@ func TestGetBackendServers(t *testing.T) {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Ingresses: []*ingress.Ingress{
|
||||
{
|
||||
Ingress: networking.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "not-allowed-snippet",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
"nginx.ingress.kubernetes.io/server-snippet": "bla",
|
||||
"nginx.ingress.kubernetes.io/configuration-snippet": "blo",
|
||||
"nginx.ingress.kubernetes.io/whitelist-source-range": "10.0.0.0/24",
|
||||
},
|
||||
},
|
||||
Spec: networking.IngressSpec{
|
||||
Rules: []networking.IngressRule{
|
||||
{
|
||||
Host: "example.com",
|
||||
IngressRuleValue: networking.IngressRuleValue{
|
||||
HTTP: &networking.HTTPIngressRuleValue{
|
||||
Paths: []networking.HTTPIngressPath{
|
||||
{
|
||||
Path: "/path1",
|
||||
PathType: &pathTypePrefix,
|
||||
Backend: networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
Name: "path1-svc",
|
||||
Port: networking.ServiceBackendPort{
|
||||
Number: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ParsedAnnotations: &annotations.Ingress{
|
||||
Whitelist: ipwhitelist.SourceRange{CIDR: []string{"10.0.0.0/24"}},
|
||||
ServerSnippet: "bla",
|
||||
ConfigurationSnippet: "blo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Validate: func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server) {
|
||||
if len(servers) != 2 {
|
||||
t.Errorf("servers count should be 2, got %d", len(servers))
|
||||
return
|
||||
}
|
||||
s := servers[1]
|
||||
|
||||
if s.ServerSnippet != "" {
|
||||
t.Errorf("server snippet should be empty, got '%s'", s.ServerSnippet)
|
||||
}
|
||||
|
||||
if s.Locations[0].ConfigurationSnippet != "" {
|
||||
t.Errorf("config snippet should be empty, got '%s'", s.Locations[0].ConfigurationSnippet)
|
||||
}
|
||||
|
||||
if len(s.Locations[0].Whitelist.CIDR) != 1 || s.Locations[0].Whitelist.CIDR[0] != "10.0.0.0/24" {
|
||||
t.Errorf("allow list was incorrectly dropped, len should be 1 and contain 10.0.0.0/24")
|
||||
}
|
||||
|
||||
},
|
||||
SetConfigMap: func(ns string) *v1.ConfigMap {
|
||||
return &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"enable-snippet-directives": "false",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
|
|
@ -282,7 +282,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() {
|
|||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return true
|
||||
return strings.Contains(server, "SecRequestBodyAccess On")
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
|
@ -292,4 +292,44 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() {
|
|||
Expect().
|
||||
Status(http.StatusForbidden)
|
||||
})
|
||||
|
||||
ginkgo.It("should enable modsecurity through the config map but ignore snippet as disabled by admin", func() {
|
||||
host := "modsecurity.foo.com"
|
||||
nameSpace := f.Namespace
|
||||
|
||||
snippet := `SecRequestBodyAccess On
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogParts ABIJDEFHZ
|
||||
SecAuditLog /dev/stdout
|
||||
SecAuditLogType Serial
|
||||
SecRule REQUEST_HEADERS:User-Agent \"block-ua\" \"log,deny,id:107,status:403,msg:\'UA blocked\'\"`
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/modsecurity-snippet": snippet,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, nameSpace, framework.EchoService, 80, annotations)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
expectedComment := "SecRuleEngine On"
|
||||
|
||||
f.SetNginxConfigMapData(map[string]string{
|
||||
"enable-modsecurity": "true",
|
||||
"enable-owasp-modsecurity-crs": "true",
|
||||
"enable-snippet-directives": "false",
|
||||
"modsecurity-snippet": expectedComment,
|
||||
})
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return !strings.Contains(server, "block-ua")
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
WithHeader("User-Agent", "block-ua").
|
||||
Expect().
|
||||
Status(http.StatusOK)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package annotations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
|
@ -35,8 +36,8 @@ var _ = framework.DescribeAnnotation("server-snippet", func() {
|
|||
host := "serversnippet.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/server-snippet": `
|
||||
more_set_headers "Content-Length: $content_length";
|
||||
more_set_headers "Content-Type: $content_type";`,
|
||||
more_set_headers "Foo: Bar";
|
||||
more_set_headers "Xpto: Lalala";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
|
@ -44,8 +45,50 @@ var _ = framework.DescribeAnnotation("server-snippet", func() {
|
|||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Content-Length: $content_length`) &&
|
||||
strings.Contains(server, `more_set_headers "Content-Type: $content_type";`)
|
||||
return strings.Contains(server, `more_set_headers "Foo: Bar`) &&
|
||||
strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Foo", []string{"Bar"}).
|
||||
ValueEqual("Xpto", []string{"Lalala"})
|
||||
})
|
||||
|
||||
ginkgo.It(`drops server snippet if disabled by the administrator`, func() {
|
||||
host := "noserversnippet.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/server-snippet": `
|
||||
more_set_headers "Foo: Bar";
|
||||
more_set_headers "Xpto: Lalala";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.UpdateNginxConfigMapData("enable-snippet-directives", "false")
|
||||
defer func() {
|
||||
// Return to the original value
|
||||
f.UpdateNginxConfigMapData("enable-snippet-directives", "true")
|
||||
}()
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return !strings.Contains(server, `more_set_headers "Foo: Bar`) &&
|
||||
!strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
NotContainsKey("Foo").
|
||||
NotContainsKey("Xpto")
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package annotations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
|
@ -31,11 +32,11 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() {
|
|||
f.NewEchoDeployment()
|
||||
})
|
||||
|
||||
ginkgo.It(`set snippet "more_set_headers "Request-Id: $req_id";" in all locations"`, func() {
|
||||
ginkgo.It(`set snippet "more_set_headers "Foo1: Bar1";" in all locations"`, func() {
|
||||
host := "configurationsnippet.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/configuration-snippet": `
|
||||
more_set_headers "Request-Id: $req_id";`,
|
||||
more_set_headers "Foo1: Bar1";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
|
@ -43,7 +44,44 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() {
|
|||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Request-Id: $req_id";`)
|
||||
return strings.Contains(server, `more_set_headers "Foo1: Bar1";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Foo1", []string{"Bar1"})
|
||||
})
|
||||
|
||||
ginkgo.It(`drops snippet "more_set_headers "Foo1: Bar1";" in all locations if disabled by admin"`, func() {
|
||||
host := "noconfigurationsnippet.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/configuration-snippet": `
|
||||
more_set_headers "Foo1: Bar1";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.UpdateNginxConfigMapData("enable-snippet-directives", "false")
|
||||
defer func() {
|
||||
// Return to the original value
|
||||
f.UpdateNginxConfigMapData("enable-snippet-directives", "true")
|
||||
}()
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return !strings.Contains(server, `more_set_headers "Foo1: Bar1";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
NotContainsKey("Foo1")
|
||||
})
|
||||
})
|
||||
|
|
149
test/e2e/settings/server_snippet.go
Normal file
149
test/e2e/settings/server_snippet.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright 2021 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 settings
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = framework.DescribeSetting("configmap server-snippet", func() {
|
||||
f := framework.NewDefaultFramework("cm-server-snippet")
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
f.NewEchoDeployment()
|
||||
})
|
||||
|
||||
ginkgo.It("should add value of server-snippet setting to all ingress config", func() {
|
||||
host := "serverglobalsnippet1.foo.com"
|
||||
hostAnnots := "serverannotssnippet1.foo.com"
|
||||
|
||||
f.SetNginxConfigMapData(map[string]string{
|
||||
"server-snippet": `
|
||||
more_set_headers "Globalfoo: Foooo";`,
|
||||
})
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/server-snippet": `
|
||||
more_set_headers "Foo: Bar";
|
||||
more_set_headers "Xpto: Lalala";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
ing1 := framework.NewSingleIngress(hostAnnots, "/", hostAnnots, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.EnsureIngress(ing1)
|
||||
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Globalfoo: Foooo`) &&
|
||||
!strings.Contains(server, `more_set_headers "Foo: Bar";`) &&
|
||||
!strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.WaitForNginxServer(hostAnnots,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Globalfoo: Foooo`) &&
|
||||
strings.Contains(server, `more_set_headers "Foo: Bar";`) &&
|
||||
strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Globalfoo", []string{"Foooo"}).
|
||||
NotContainsKey("Foo").
|
||||
NotContainsKey("Xpto")
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", hostAnnots).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Foo", []string{"Bar"}).
|
||||
ValueEqual("Xpto", []string{"Lalala"}).
|
||||
ValueEqual("Globalfoo", []string{"Foooo"})
|
||||
})
|
||||
|
||||
ginkgo.It("should add global server-snippet and drop annotations per admin config", func() {
|
||||
host := "serverglobalsnippet2.foo.com"
|
||||
hostAnnots := "serverannotssnippet2.foo.com"
|
||||
|
||||
f.SetNginxConfigMapData(map[string]string{
|
||||
"enable-snippet-directives": "false",
|
||||
"server-snippet": `
|
||||
more_set_headers "Globalfoo: Foooo";`,
|
||||
})
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/server-snippet": `
|
||||
more_set_headers "Foo: Bar";
|
||||
more_set_headers "Xpto: Lalala";`,
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
ing1 := framework.NewSingleIngress(hostAnnots, "/", hostAnnots, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.EnsureIngress(ing1)
|
||||
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Globalfoo: Foooo`) &&
|
||||
!strings.Contains(server, `more_set_headers "Foo: Bar";`) &&
|
||||
!strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.WaitForNginxServer(hostAnnots,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, `more_set_headers "Globalfoo: Foooo`) &&
|
||||
!strings.Contains(server, `more_set_headers "Foo: Bar";`) &&
|
||||
!strings.Contains(server, `more_set_headers "Xpto: Lalala";`)
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Globalfoo", []string{"Foooo"}).
|
||||
NotContainsKey("Foo").
|
||||
NotContainsKey("Xpto")
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", hostAnnots).
|
||||
Expect().
|
||||
Status(http.StatusOK).Headers().
|
||||
ValueEqual("Globalfoo", []string{"Foooo"}).
|
||||
NotContainsKey("Foo").
|
||||
NotContainsKey("Xpto")
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue