Skip to content

Commit 01c9a2b

Browse files
authored
Revert Implement pathType validation (#9511) (#9607)
Signed-off-by: James Strong <[email protected]>
1 parent 59d247d commit 01c9a2b

File tree

16 files changed

+211
-426
lines changed

16 files changed

+211
-426
lines changed

charts/ingress-nginx/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ Kubernetes: `>=1.20.0-0`
253253
| Key | Type | Default | Description |
254254
|-----|------|---------|-------------|
255255
| commonLabels | object | `{}` | |
256-
| 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 |
257256
| 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 |
258257
| controller.admissionWebhooks.annotations | object | `{}` | |
259258
| controller.admissionWebhooks.certManager.admissionCert.duration | string | `""` | |

charts/ingress-nginx/templates/controller-configmap.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ metadata:
1414
namespace: {{ .Release.Namespace }}
1515
data:
1616
allow-snippet-annotations: "{{ .Values.controller.allowSnippetAnnotations }}"
17-
enable-pathtype-validation: "{{ .Values.controller.EnablePathTypeValidation }}"
1817
{{- if .Values.controller.addHeaders }}
1918
add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers
2019
{{- end }}

charts/ingress-nginx/values.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,6 @@ controller:
8787
# Global snippets in ConfigMap are still respected
8888
allowSnippetAnnotations: true
8989

90-
# -- This configuration defines if Ingress Controller should validate pathType.
91-
# If false, special characters will be allowed on paths of any pathType.
92-
# If true, special characters are only allowed on paths with pathType = ImplementationSpecific
93-
EnablePathTypeValidation: false
94-
9590
# -- Required for use with CNI based kubernetes installations (such as ones set up by kubeadm),
9691
# since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920
9792
# is merged

internal/ingress/controller/config/config.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -782,18 +782,6 @@ type Configuration struct {
782782
// http://nginx.org/en/docs/ngx_core_module.html#debug_connection
783783
// Default: ""
784784
DebugConnections []string `json:"debug-connections"`
785-
786-
// EnablePathTypeValidation allows the admin to enable the pathType validation.
787-
// If EnablePathTypeValidation is enabled, the Controller will only allow alphanumeric
788-
// characters on path (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
789-
// to control what characters are allowed set them with PathAdditionalAllowedChars
790-
EnablePathTypeValidation bool `json:"enable-pathtype-validation"`
791-
792-
// PathAdditionalAllowedChars allows the admin to specify what are the additional
793-
// characters allowed in case of pathType=ImplementationSpecific.
794-
// Case enable-pathtype-validation=true, this characters will be only allowed on ImplementationSpecific.
795-
// Defaults to: "^%$[](){}*+?"
796-
PathAdditionalAllowedChars string `json:"path-additional-allowed-chars"`
797785
}
798786

799787
// NewDefault returns the default nginx configuration
@@ -829,8 +817,6 @@ func NewDefault() Configuration {
829817
ClientHeaderTimeout: 60,
830818
ClientBodyBufferSize: "8k",
831819
ClientBodyTimeout: 60,
832-
EnablePathTypeValidation: false,
833-
PathAdditionalAllowedChars: "^%$[](){}*+?|",
834820
EnableUnderscoresInHeaders: false,
835821
ErrorLogLevel: errorLevel,
836822
UseForwardedHeaders: false,

internal/ingress/controller/controller.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,6 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
325325

326326
k8s.SetDefaultNGINXPathType(ing)
327327

328-
if err := utilingress.ValidateIngressPath(ing, cfg.EnablePathTypeValidation, cfg.PathAdditionalAllowedChars); err != nil {
329-
return fmt.Errorf("ingress contains invalid characters: %s", err)
330-
}
331-
332328
allIngresses := n.store.ListIngresses()
333329

334330
filter := func(toCheck *ingress.Ingress) bool {

internal/ingress/controller/controller_test.go

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -201,97 +201,6 @@ func TestCheckIngress(t *testing.T) {
201201
},
202202
},
203203
}
204-
205-
t.Run("when validating pathType", func(t *testing.T) {
206-
t.Run("When ingress contains invalid path and pathType validation is enabled", func(t *testing.T) {
207-
nginx.store = fakeIngressStore{
208-
ingresses: []*ingress.Ingress{},
209-
configuration: ngx_config.Configuration{
210-
EnablePathTypeValidation: true,
211-
},
212-
}
213-
nginx.command = testNginxTestCommand{
214-
t: t,
215-
err: nil,
216-
expected: "",
217-
}
218-
nginx.cfg.IngressClassConfiguration = &ingressclass.IngressClassConfiguration{
219-
WatchWithoutClass: true,
220-
}
221-
ingPath := &networking.Ingress{
222-
ObjectMeta: metav1.ObjectMeta{
223-
Name: "test-ingress1",
224-
Namespace: "user-namespace1",
225-
Annotations: map[string]string{
226-
"kubernetes.io/ingress.class": "nginx",
227-
},
228-
},
229-
Spec: networking.IngressSpec{
230-
Rules: []networking.IngressRule{
231-
{
232-
Host: "example.com",
233-
IngressRuleValue: networking.IngressRuleValue{
234-
HTTP: &networking.HTTPIngressRuleValue{
235-
Paths: []networking.HTTPIngressPath{
236-
{
237-
Path: "/xpto/(a+)",
238-
PathType: &pathTypePrefix,
239-
},
240-
},
241-
},
242-
},
243-
},
244-
},
245-
},
246-
}
247-
248-
if nginx.CheckIngress(ingPath) == nil {
249-
t.Errorf("invalid path on pathTypePrefix and validation enabled should return an error")
250-
}
251-
})
252-
t.Run("When ingress contains invalid path and pathType validation is disabled", func(t *testing.T) {
253-
nginx.store = fakeIngressStore{
254-
ingresses: []*ingress.Ingress{},
255-
configuration: ngx_config.Configuration{
256-
EnablePathTypeValidation: false,
257-
PathAdditionalAllowedChars: "^%$[](){}*+?|",
258-
},
259-
}
260-
nginx.command = testNginxTestCommand{
261-
t: t,
262-
err: nil,
263-
expected: "_,example.com",
264-
}
265-
266-
ingPath := &networking.Ingress{
267-
ObjectMeta: metav1.ObjectMeta{
268-
Name: "test-ingress2",
269-
Namespace: "user-namespace2",
270-
},
271-
Spec: networking.IngressSpec{
272-
Rules: []networking.IngressRule{
273-
{
274-
Host: "example.com",
275-
IngressRuleValue: networking.IngressRuleValue{
276-
HTTP: &networking.HTTPIngressRuleValue{
277-
Paths: []networking.HTTPIngressPath{
278-
{
279-
Path: "/example(/|$)(.*)",
280-
PathType: &pathTypePrefix,
281-
},
282-
},
283-
},
284-
},
285-
},
286-
},
287-
},
288-
}
289-
290-
if nginx.CheckIngress(ingPath) != nil {
291-
t.Errorf("invalid path on pathTypePrefix and validation disabled should not return an error: %s", nginx.CheckIngress(ingPath))
292-
}
293-
})
294-
})
295204
t.Run("when the class is the nginx one", func(t *testing.T) {
296205
ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"] = "nginx"
297206
nginx.command = testNginxTestCommand{

internal/ingress/controller/store/store.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -846,11 +846,6 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
846846
copyIng := &networkingv1.Ingress{}
847847
ing.ObjectMeta.DeepCopyInto(&copyIng.ObjectMeta)
848848

849-
if err := ingressutils.ValidateIngressPath(ing, s.backendConfig.EnablePathTypeValidation, s.backendConfig.PathAdditionalAllowedChars); err != nil {
850-
klog.Errorf("ingress %s contains invalid path and will be skipped: %s", key, err)
851-
return
852-
}
853-
854849
if s.backendConfig.AnnotationValueWordBlocklist != "" {
855850
if err := checkBadAnnotationValue(copyIng.Annotations, s.backendConfig.AnnotationValueWordBlocklist); err != nil {
856851
klog.Warningf("skipping ingress %s: %s", key, err)
@@ -870,6 +865,10 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
870865
if path.Path == "" {
871866
copyIng.Spec.Rules[ri].HTTP.Paths[pi].Path = "/"
872867
}
868+
if !ingressutils.IsSafePath(copyIng, path.Path) {
869+
klog.Warningf("ingress %s contains invalid path %s", key, path.Path)
870+
return
871+
}
873872
}
874873
}
875874

internal/k8s/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ func SetDefaultNGINXPathType(ing *networkingv1.Ingress) {
180180
p.PathType = &defaultPathType
181181
}
182182

183+
if *p.PathType == networkingv1.PathTypeImplementationSpecific {
184+
p.PathType = &defaultPathType
185+
}
183186
}
184187
}
185188
}

magefiles/helm.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ func updateChartValue(key, value string) {
153153
Info("HELM Ingress Nginx Helm Chart update %s %s", key, value)
154154
}
155155

156+
func (Helm) Helmdocs() error {
157+
return runHelmDocs()
158+
}
156159
func runHelmDocs() error {
157160
err := installHelmDocs()
158161
if err != nil {

pkg/util/ingress/ingress.go

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,23 @@ import (
2323

2424
networkingv1 "k8s.io/api/networking/v1"
2525
"k8s.io/apimachinery/pkg/util/sets"
26+
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
2627
"k8s.io/ingress-nginx/internal/k8s"
2728
"k8s.io/ingress-nginx/internal/net/ssl"
2829
"k8s.io/ingress-nginx/pkg/apis/ingress"
2930
"k8s.io/klog/v2"
3031
)
3132

3233
const (
33-
alphaNumericChars = `A-Za-z0-9\-\.\_\~\/` // This is the default allowed set on paths
34+
alphaNumericChars = `\-\.\_\~a-zA-Z0-9/`
35+
regexEnabledChars = `\^\$\[\]\(\)\{\}\*\+`
3436
)
3537

3638
var (
37-
// pathAlphaNumeric is a regex validation that allows only (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
38-
pathAlphaNumericRegex = regexp.MustCompile("^[" + alphaNumericChars + "]*$").MatchString
39-
40-
// default path type is Prefix to not break existing definitions
41-
defaultPathType = networkingv1.PathTypePrefix
39+
// pathAlphaNumeric is a regex validation of something like "^/[a-zA-Z]+$" on path
40+
pathAlphaNumeric = regexp.MustCompile("^/[" + alphaNumericChars + "]*$").MatchString
41+
// pathRegexEnabled is a regex validation of paths that may contain regex.
42+
pathRegexEnabled = regexp.MustCompile("^/[" + alphaNumericChars + regexEnabledChars + "]*$").MatchString
4243
)
4344

4445
func GetRemovedHosts(rucfg, newcfg *ingress.Configuration) []string {
@@ -246,68 +247,12 @@ func BuildRedirects(servers []*ingress.Server) []*redirect {
246247
return redirectServers
247248
}
248249

249-
func ValidateIngressPath(copyIng *networkingv1.Ingress, enablePathTypeValidation bool, pathAdditionalAllowedChars string) error {
250-
251-
if copyIng == nil {
252-
return nil
253-
}
254-
255-
escapedPathAdditionalAllowedChars := regexp.QuoteMeta(pathAdditionalAllowedChars)
256-
regexPath, err := regexp.Compile("^[" + alphaNumericChars + escapedPathAdditionalAllowedChars + "]*$")
257-
if err != nil {
258-
return fmt.Errorf("ingress has misconfigured validation regex on configmap: %s - %w", pathAdditionalAllowedChars, err)
259-
}
260-
261-
for _, rule := range copyIng.Spec.Rules {
262-
263-
if rule.HTTP == nil {
264-
continue
265-
}
266-
267-
if err := checkPath(rule.HTTP.Paths, enablePathTypeValidation, regexPath); err != nil {
268-
return fmt.Errorf("error validating ingressPath: %w", err)
269-
}
270-
}
271-
return nil
272-
}
273-
274-
func checkPath(paths []networkingv1.HTTPIngressPath, enablePathTypeValidation bool, regexSpecificChars *regexp.Regexp) error {
275-
276-
for _, path := range paths {
277-
if path.PathType == nil {
278-
path.PathType = &defaultPathType
279-
}
280-
281-
klog.V(9).InfoS("PathType Validation", "enablePathTypeValidation", enablePathTypeValidation, "regexSpecificChars", regexSpecificChars.String(), "Path", path.Path)
282-
283-
switch pathType := *path.PathType; pathType {
284-
case networkingv1.PathTypeImplementationSpecific:
285-
if enablePathTypeValidation {
286-
//only match on regex chars per Ingress spec when path is implementation specific
287-
if !regexSpecificChars.MatchString(path.Path) {
288-
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
289-
}
290-
}
291-
292-
case networkingv1.PathTypeExact, networkingv1.PathTypePrefix:
293-
//enforce path type validation
294-
if enablePathTypeValidation {
295-
//only allow alphanumeric chars, no regex chars
296-
if !pathAlphaNumericRegex(path.Path) {
297-
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
298-
}
299-
continue
300-
} else {
301-
//path validation is disabled, so we check what regex chars are allowed by user
302-
if !regexSpecificChars.MatchString(path.Path) {
303-
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
304-
}
305-
continue
306-
}
307-
308-
default:
309-
return fmt.Errorf("unknown path type %v on path %v", *path.PathType, path.Path)
310-
}
250+
// IsSafePath verifies if the path used in ingress object contains only valid characters.
251+
// It will behave differently if regex is enabled or not
252+
func IsSafePath(copyIng *networkingv1.Ingress, path string) bool {
253+
isRegex, _ := parser.GetBoolAnnotation("use-regex", copyIng)
254+
if isRegex {
255+
return pathRegexEnabled(path)
311256
}
312-
return nil
257+
return pathAlphaNumeric(path)
313258
}

0 commit comments

Comments
 (0)