Revert Implement pathType validation (#9511) (#9607)

Signed-off-by: James Strong <strong.james.e@gmail.com>
This commit is contained in:
James Strong 2023-02-13 07:57:29 +01:00 committed by GitHub
parent 59d247dd74
commit 01c9a2bf25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 211 additions and 426 deletions

View file

@ -253,7 +253,6 @@ Kubernetes: `>=1.20.0-0`
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| commonLabels | object | `{}` | |
| controller.EnablePathTypeValidation | bool | `false` | This configuration defines if Ingress Controller should validate pathType. If false, special characters will be allowed on paths of any pathType. If true, special characters are only allowed on paths with pathType = ImplementationSpecific |
| controller.addHeaders | object | `{}` | Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers |
| controller.admissionWebhooks.annotations | object | `{}` | |
| controller.admissionWebhooks.certManager.admissionCert.duration | string | `""` | |

View file

@ -14,7 +14,6 @@ metadata:
namespace: {{ .Release.Namespace }}
data:
allow-snippet-annotations: "{{ .Values.controller.allowSnippetAnnotations }}"
enable-pathtype-validation: "{{ .Values.controller.EnablePathTypeValidation }}"
{{- if .Values.controller.addHeaders }}
add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers
{{- end }}

View file

@ -87,11 +87,6 @@ controller:
# Global snippets in ConfigMap are still respected
allowSnippetAnnotations: true
# -- This configuration defines if Ingress Controller should validate pathType.
# If false, special characters will be allowed on paths of any pathType.
# If true, special characters are only allowed on paths with pathType = ImplementationSpecific
EnablePathTypeValidation: false
# -- 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

View file

@ -782,18 +782,6 @@ type Configuration struct {
// http://nginx.org/en/docs/ngx_core_module.html#debug_connection
// Default: ""
DebugConnections []string `json:"debug-connections"`
// EnablePathTypeValidation allows the admin to enable the pathType validation.
// If EnablePathTypeValidation is enabled, the Controller will only allow alphanumeric
// characters on path (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
// to control what characters are allowed set them with PathAdditionalAllowedChars
EnablePathTypeValidation bool `json:"enable-pathtype-validation"`
// PathAdditionalAllowedChars allows the admin to specify what are the additional
// characters allowed in case of pathType=ImplementationSpecific.
// Case enable-pathtype-validation=true, this characters will be only allowed on ImplementationSpecific.
// Defaults to: "^%$[](){}*+?"
PathAdditionalAllowedChars string `json:"path-additional-allowed-chars"`
}
// NewDefault returns the default nginx configuration
@ -829,8 +817,6 @@ func NewDefault() Configuration {
ClientHeaderTimeout: 60,
ClientBodyBufferSize: "8k",
ClientBodyTimeout: 60,
EnablePathTypeValidation: false,
PathAdditionalAllowedChars: "^%$[](){}*+?|",
EnableUnderscoresInHeaders: false,
ErrorLogLevel: errorLevel,
UseForwardedHeaders: false,

View file

@ -325,10 +325,6 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
k8s.SetDefaultNGINXPathType(ing)
if err := utilingress.ValidateIngressPath(ing, cfg.EnablePathTypeValidation, cfg.PathAdditionalAllowedChars); err != nil {
return fmt.Errorf("ingress contains invalid characters: %s", err)
}
allIngresses := n.store.ListIngresses()
filter := func(toCheck *ingress.Ingress) bool {

View file

@ -201,97 +201,6 @@ func TestCheckIngress(t *testing.T) {
},
},
}
t.Run("when validating pathType", func(t *testing.T) {
t.Run("When ingress contains invalid path and pathType validation is enabled", func(t *testing.T) {
nginx.store = fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
EnablePathTypeValidation: true,
},
}
nginx.command = testNginxTestCommand{
t: t,
err: nil,
expected: "",
}
nginx.cfg.IngressClassConfiguration = &ingressclass.IngressClassConfiguration{
WatchWithoutClass: true,
}
ingPath := &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ingress1",
Namespace: "user-namespace1",
Annotations: map[string]string{
"kubernetes.io/ingress.class": "nginx",
},
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/xpto/(a+)",
PathType: &pathTypePrefix,
},
},
},
},
},
},
},
}
if nginx.CheckIngress(ingPath) == nil {
t.Errorf("invalid path on pathTypePrefix and validation enabled should return an error")
}
})
t.Run("When ingress contains invalid path and pathType validation is disabled", func(t *testing.T) {
nginx.store = fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
EnablePathTypeValidation: false,
PathAdditionalAllowedChars: "^%$[](){}*+?|",
},
}
nginx.command = testNginxTestCommand{
t: t,
err: nil,
expected: "_,example.com",
}
ingPath := &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ingress2",
Namespace: "user-namespace2",
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/example(/|$)(.*)",
PathType: &pathTypePrefix,
},
},
},
},
},
},
},
}
if nginx.CheckIngress(ingPath) != nil {
t.Errorf("invalid path on pathTypePrefix and validation disabled should not return an error: %s", nginx.CheckIngress(ingPath))
}
})
})
t.Run("when the class is the nginx one", func(t *testing.T) {
ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"] = "nginx"
nginx.command = testNginxTestCommand{

View file

@ -846,11 +846,6 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
copyIng := &networkingv1.Ingress{}
ing.ObjectMeta.DeepCopyInto(&copyIng.ObjectMeta)
if err := ingressutils.ValidateIngressPath(ing, s.backendConfig.EnablePathTypeValidation, s.backendConfig.PathAdditionalAllowedChars); err != nil {
klog.Errorf("ingress %s contains invalid path and will be skipped: %s", key, err)
return
}
if s.backendConfig.AnnotationValueWordBlocklist != "" {
if err := checkBadAnnotationValue(copyIng.Annotations, s.backendConfig.AnnotationValueWordBlocklist); err != nil {
klog.Warningf("skipping ingress %s: %s", key, err)
@ -870,6 +865,10 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
if path.Path == "" {
copyIng.Spec.Rules[ri].HTTP.Paths[pi].Path = "/"
}
if !ingressutils.IsSafePath(copyIng, path.Path) {
klog.Warningf("ingress %s contains invalid path %s", key, path.Path)
return
}
}
}

View file

@ -180,6 +180,9 @@ func SetDefaultNGINXPathType(ing *networkingv1.Ingress) {
p.PathType = &defaultPathType
}
if *p.PathType == networkingv1.PathTypeImplementationSpecific {
p.PathType = &defaultPathType
}
}
}
}

View file

@ -153,6 +153,9 @@ func updateChartValue(key, value string) {
Info("HELM Ingress Nginx Helm Chart update %s %s", key, value)
}
func (Helm) Helmdocs() error {
return runHelmDocs()
}
func runHelmDocs() error {
err := installHelmDocs()
if err != nil {

View file

@ -23,6 +23,7 @@ import (
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/net/ssl"
"k8s.io/ingress-nginx/pkg/apis/ingress"
@ -30,15 +31,15 @@ import (
)
const (
alphaNumericChars = `A-Za-z0-9\-\.\_\~\/` // This is the default allowed set on paths
alphaNumericChars = `\-\.\_\~a-zA-Z0-9/`
regexEnabledChars = `\^\$\[\]\(\)\{\}\*\+`
)
var (
// pathAlphaNumeric is a regex validation that allows only (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
pathAlphaNumericRegex = regexp.MustCompile("^[" + alphaNumericChars + "]*$").MatchString
// default path type is Prefix to not break existing definitions
defaultPathType = networkingv1.PathTypePrefix
// pathAlphaNumeric is a regex validation of something like "^/[a-zA-Z]+$" on path
pathAlphaNumeric = regexp.MustCompile("^/[" + alphaNumericChars + "]*$").MatchString
// pathRegexEnabled is a regex validation of paths that may contain regex.
pathRegexEnabled = regexp.MustCompile("^/[" + alphaNumericChars + regexEnabledChars + "]*$").MatchString
)
func GetRemovedHosts(rucfg, newcfg *ingress.Configuration) []string {
@ -246,68 +247,12 @@ func BuildRedirects(servers []*ingress.Server) []*redirect {
return redirectServers
}
func ValidateIngressPath(copyIng *networkingv1.Ingress, enablePathTypeValidation bool, pathAdditionalAllowedChars string) error {
if copyIng == nil {
return nil
// IsSafePath verifies if the path used in ingress object contains only valid characters.
// It will behave differently if regex is enabled or not
func IsSafePath(copyIng *networkingv1.Ingress, path string) bool {
isRegex, _ := parser.GetBoolAnnotation("use-regex", copyIng)
if isRegex {
return pathRegexEnabled(path)
}
escapedPathAdditionalAllowedChars := regexp.QuoteMeta(pathAdditionalAllowedChars)
regexPath, err := regexp.Compile("^[" + alphaNumericChars + escapedPathAdditionalAllowedChars + "]*$")
if err != nil {
return fmt.Errorf("ingress has misconfigured validation regex on configmap: %s - %w", pathAdditionalAllowedChars, err)
}
for _, rule := range copyIng.Spec.Rules {
if rule.HTTP == nil {
continue
}
if err := checkPath(rule.HTTP.Paths, enablePathTypeValidation, regexPath); err != nil {
return fmt.Errorf("error validating ingressPath: %w", err)
}
}
return nil
}
func checkPath(paths []networkingv1.HTTPIngressPath, enablePathTypeValidation bool, regexSpecificChars *regexp.Regexp) error {
for _, path := range paths {
if path.PathType == nil {
path.PathType = &defaultPathType
}
klog.V(9).InfoS("PathType Validation", "enablePathTypeValidation", enablePathTypeValidation, "regexSpecificChars", regexSpecificChars.String(), "Path", path.Path)
switch pathType := *path.PathType; pathType {
case networkingv1.PathTypeImplementationSpecific:
if enablePathTypeValidation {
//only match on regex chars per Ingress spec when path is implementation specific
if !regexSpecificChars.MatchString(path.Path) {
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
}
}
case networkingv1.PathTypeExact, networkingv1.PathTypePrefix:
//enforce path type validation
if enablePathTypeValidation {
//only allow alphanumeric chars, no regex chars
if !pathAlphaNumericRegex(path.Path) {
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
}
continue
} else {
//path validation is disabled, so we check what regex chars are allowed by user
if !regexSpecificChars.MatchString(path.Path) {
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
}
continue
}
default:
return fmt.Errorf("unknown path type %v on path %v", *path.PathType, path.Path)
}
}
return nil
return pathAlphaNumeric(path)
}

View file

@ -17,10 +17,13 @@ limitations under the License.
package ingress
import (
"fmt"
"testing"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/pkg/apis/ingress"
)
@ -133,172 +136,81 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
}
}
func generateDumbIngressforPathTest(pathType *networkingv1.PathType, path string) *networkingv1.Ingress {
func generateDumbIngressforPathTest(regexEnabled bool) *networkingv1.Ingress {
var annotations = make(map[string]string)
regexAnnotation := fmt.Sprintf("%s/use-regex", parser.AnnotationsPrefix)
if regexEnabled {
annotations[regexAnnotation] = "true"
}
return &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "dumb",
Namespace: "default",
},
Spec: networkingv1.IngressSpec{
Rules: []networkingv1.IngressRule{
{
Host: "test.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: pathType,
Path: path,
},
},
},
},
},
},
Name: "dumb",
Namespace: "default",
Annotations: annotations,
},
}
}
func generateComplexIngress(ing *networkingv1.Ingress) *networkingv1.Ingress {
oldRules := ing.Spec.DeepCopy().Rules
ing.Spec.Rules = []networkingv1.IngressRule{
{
Host: "test1.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: &pathTypeExact,
Path: "/xpto",
},
},
},
},
},
{
Host: "test2.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: &pathTypeExact,
Path: "/someotherpath",
},
{
PathType: &pathTypePrefix,
Path: "/someprefix/~xpto/lala123",
},
},
},
},
},
}
// we want to invert the order to test better :)
ing.Spec.Rules = append(ing.Spec.Rules, oldRules...)
return ing
}
var (
pathTypeExact = networkingv1.PathTypeExact
pathTypePrefix = networkingv1.PathTypePrefix
pathTypeImplSpecific = networkingv1.PathTypeImplementationSpecific
)
const (
defaultAdditionalChars = "^%$[](){}*+?"
)
func TestValidateIngressPath(t *testing.T) {
func TestIsSafePath(t *testing.T) {
tests := []struct {
name string
copyIng *networkingv1.Ingress
EnablePathTypeValidation bool
additionalChars string
wantErr bool
name string
copyIng *networkingv1.Ingress
path string
want bool
}{
{
name: "should return nil when ingress = nil",
wantErr: false,
copyIng: nil,
name: "should accept valid path with regex disabled",
want: true,
copyIng: generateDumbIngressforPathTest(false),
path: "/xpto/~user/t-e_st.exe",
},
{
name: "should accept valid path on pathType Exact",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypeExact, "/xpto/~user9/t-e_st.exe"),
name: "should accept valid path / with regex disabled",
want: true,
copyIng: generateDumbIngressforPathTest(false),
path: "/",
},
{
name: "should accept valid path on pathType Prefix",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypePrefix, "/xpto/~user9/t-e_st.exe"),
name: "should reject invalid path with invalid chars",
want: false,
copyIng: generateDumbIngressforPathTest(false),
path: "/foo/bar/;xpto",
},
{
name: "should accept valid simple path on pathType Impl Specific",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/xpto/~user9/t-e_st.exe"),
name: "should reject regex path when regex is disabled",
want: false,
copyIng: generateDumbIngressforPathTest(false),
path: "/foo/bar/(.+)",
},
{
name: "should accept valid path on pathType nil",
wantErr: false,
copyIng: generateDumbIngressforPathTest(nil, "/xpto/~user/t-e_st.exe"),
name: "should accept valid path / with regex enabled",
want: true,
copyIng: generateDumbIngressforPathTest(true),
path: "/",
},
{
name: "should accept empty path",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypePrefix, ""),
name: "should accept regex path when regex is enabled",
want: true,
copyIng: generateDumbIngressforPathTest(true),
path: "/foo/bar/(.+)",
},
{
name: "should deny path with bad characters and pathType not implementationSpecific",
wantErr: true,
additionalChars: "()",
copyIng: generateDumbIngressforPathTest(&pathTypeExact, "/foo/bar/(.+)"),
name: "should reject regex path when regex is enabled but the path is invalid",
want: false,
copyIng: generateDumbIngressforPathTest(true),
path: "/foo/bar/;xpto",
},
{
name: "should accept path with regex characters and pathType implementationSpecific",
wantErr: false,
additionalChars: defaultAdditionalChars,
EnablePathTypeValidation: false,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.+)"),
},
{
name: "should accept path with regex characters and pathType exact, but pathType validation disabled",
wantErr: false,
additionalChars: defaultAdditionalChars,
EnablePathTypeValidation: false,
copyIng: generateDumbIngressforPathTest(&pathTypeExact, "/foo/bar/(.+)"),
},
{
name: "should reject path when the allowed additional set does not match",
wantErr: true,
additionalChars: "().?",
EnablePathTypeValidation: true,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.+)"),
},
{
name: "should accept path when the allowed additional set does match",
wantErr: false,
additionalChars: "().?",
EnablePathTypeValidation: false,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.?)"),
},
{
name: "should block if at least one path is bad",
wantErr: true,
EnablePathTypeValidation: false,
copyIng: generateComplexIngress(generateDumbIngressforPathTest(&pathTypeExact, "/foo/bar/(.?)")),
},
{
name: "should block if at least one path is bad",
wantErr: true,
EnablePathTypeValidation: true,
copyIng: generateComplexIngress(generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.?)")),
name: "should reject regex path when regex is enabled but the path is invalid",
want: false,
copyIng: generateDumbIngressforPathTest(true),
path: ";xpto",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateIngressPath(tt.copyIng, tt.EnablePathTypeValidation, tt.additionalChars); (err != nil) != tt.wantErr {
t.Errorf("ValidateIngressPath() error = %v, wantErr %v", err, tt.wantErr)
if got := IsSafePath(tt.copyIng, tt.path); got != tt.want {
t.Errorf("IsSafePath() = %v, want %v", got, tt.want)
}
})
}

View file

@ -30,14 +30,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
networkingv1 "k8s.io/api/networking/v1"
)
var (
pathExact = networkingv1.PathTypeExact
pathPrefix = networkingv1.PathTypePrefix
pathImplSpecific = networkingv1.PathTypeImplementationSpecific
)
var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
@ -160,35 +152,7 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid annotation value should return an error")
})
ginkgo.It("ADMISSION should not validate characters on ingress when validation of pathType is disabled", func() {
host := "admission-test"
f.UpdateNginxConfigMapData("enable-pathtype-validation", "false")
firstIngress := framework.NewSingleIngress("first-ingress", "/xpto*", host, f.Namespace, framework.EchoService, 80, nil)
firstIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathPrefix
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with regex chars on path and pathType validation disabled should be accepted")
})
ginkgo.It("ADMISSION should reject ingress with bad characters and pathType != ImplementationSpecific", func() {
host := "admission-test"
f.UpdateNginxConfigMapData("enable-pathtype-validation", "true")
firstIngress := framework.NewSingleIngress("first-ingress", "/xpto*", host, f.Namespace, framework.EchoService, 80, nil)
firstIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathPrefix
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{})
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid path value should return an error")
secondIngress := framework.NewSingleIngress("second-ingress", "/abc123*", host, f.Namespace, framework.EchoService, 80, nil)
secondIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImplSpecific
_, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), secondIngress, metav1.CreateOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with regex on path and pathType ImplementationSpecific should not return an error")
})
ginkgo.It("ADMISSION should return an error if there is a forbidden value in some annotation", func() {
ginkgo.It("should return an error if there is a forbidden value in some annotation", func() {
host := "admission-test"
annotations := map[string]string{

View file

@ -35,8 +35,6 @@ import (
var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
f := framework.NewDefaultFramework("affinity")
pathImpl := networking.PathTypeImplementationSpecific
ginkgo.BeforeEach(func() {
f.NewEchoDeployment(framework.WithDeploymentReplicas(2))
})
@ -278,8 +276,6 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
annotations["nginx.ingress.kubernetes.io/session-cookie-path"] = "/foo/bar"
ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -303,8 +299,6 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
annotations["nginx.ingress.kubernetes.io/use-regex"] = "true"
ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,

View file

@ -720,51 +720,6 @@ http {
})
})
ginkgo.Context("when external authentication is configured along with CORS enabled", func() {
host := "auth"
var annotations map[string]string
var ing *networking.Ingress
ginkgo.BeforeEach(func() {
f.NewHttpbinDeployment()
var httpbinIP string
err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBinService, f.Namespace, 1)
assert.Nil(ginkgo.GinkgoT(), err)
e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBinService, metav1.GetOptions{})
assert.Nil(ginkgo.GinkgoT(), err)
httpbinIP = e.Subsets[0].Addresses[0].IP
annotations = map[string]string{
"nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbinIP),
"nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start",
"nginx.ingress.kubernetes.io/enable-cors": "true",
}
ing = framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, "server_name auth")
})
})
ginkgo.It("should redirect to signin url when not signed in along With CORS headers in response", func() {
f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusFound).
Header("Access-Control-Allow-Origin").Equal(fmt.Sprintf("*"))
})
})
ginkgo.Context("when external authentication with caching is configured", func() {
thisHost := "auth"
thatHost := "different"

View file

@ -24,15 +24,12 @@ import (
"github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-log", func() {
f := framework.NewDefaultFramework("rewrite")
pathImpl := networking.PathTypeImplementationSpecific
ginkgo.BeforeEach(func() {
f.NewEchoDeployment()
})
@ -129,7 +126,6 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend",
}
ing = framework.NewSingleIngress("regex", "/foo.+", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -172,8 +168,6 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend",
}
ing = framework.NewSingleIngress("regex", "/foo/bar/[a-z]{3}", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -202,8 +196,6 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend/$1",
}
ing := framework.NewSingleIngress("regex", "/foo/bar/(.+)", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,

View file

@ -0,0 +1,134 @@
/*
Copyright 2022 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 security
import (
"fmt"
"net/http"
"strings"
"github.com/onsi/ginkgo/v2"
"k8s.io/ingress-nginx/test/e2e/framework"
)
const (
validPath = "/xpto/~user/t-e_st.exe"
invalidPath = "/foo/bar/;xpto"
regexPath = "/foo/bar/(.+)"
host = "securitytest.com"
)
var (
annotationRegex = map[string]string{
"nginx.ingress.kubernetes.io/use-regex": "true",
}
)
var _ = framework.IngressNginxDescribe("[Security] validate path fields", func() {
f := framework.NewDefaultFramework("validate-path")
ginkgo.BeforeEach(func() {
f.NewEchoDeployment()
})
ginkgo.It("should accept an ingress with valid path", func() {
ing := framework.NewSingleIngress(host, validPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(validPath).
WithHeader("Host", host).
Expect().
Status(http.StatusOK)
})
ginkgo.It("should drop an ingress with invalid path", func() {
ing := framework.NewSingleIngress(host, invalidPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(invalidPath).
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
ginkgo.It("should drop an ingress with regex path and regex disabled", func() {
ing := framework.NewSingleIngress(host, regexPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET("/foo/bar/lalala").
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
ginkgo.It("should accept an ingress with regex path and regex enabled", func() {
ing := framework.NewSingleIngress(host, regexPath, host, f.Namespace, framework.EchoService, 80, annotationRegex)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET("/foo/bar/lalala").
WithHeader("Host", host).
Expect().
Status(http.StatusOK)
})
ginkgo.It("should reject an ingress with invalid path and regex enabled", func() {
ing := framework.NewSingleIngress(host, invalidPath, host, f.Namespace, framework.EchoService, 80, annotationRegex)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(invalidPath).
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
})