Implement annotation validation (#9673)
* Add validation to all annotations * Add annotation validation for fcgi * Fix reviews and fcgi e2e * Add flag to disable cross namespace validation * Add risk, flag for validation, tests * Add missing formating * Enable validation by default on tests * Test validation flag * remove ajp from list * Finalize validation changes * Add validations to CI * Update helm docs * Fix code review * Use a better name for annotation risk
This commit is contained in:
parent
86c00a2310
commit
c5f348ea2e
109 changed files with 4320 additions and 586 deletions
38
test/e2e-image/namespace-overlays/validations/values.yaml
Normal file
38
test/e2e-image/namespace-overlays/validations/values.yaml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# TODO: remove the need to use fullnameOverride
|
||||
fullnameOverride: nginx-ingress
|
||||
controller:
|
||||
image:
|
||||
repository: ingress-controller/controller
|
||||
chroot: true
|
||||
tag: 1.0.0-dev
|
||||
digest:
|
||||
digestChroot:
|
||||
containerPort:
|
||||
http: "1080"
|
||||
https: "1443"
|
||||
|
||||
extraArgs:
|
||||
http-port: "1080"
|
||||
https-port: "1443"
|
||||
# e2e tests do not require information about ingress status
|
||||
update-status: "false"
|
||||
|
||||
scope:
|
||||
enabled: true
|
||||
|
||||
config:
|
||||
worker-processes: "1"
|
||||
service:
|
||||
type: NodePort
|
||||
|
||||
admissionWebhooks:
|
||||
enabled: true
|
||||
certificate: "/usr/local/certificates/cert"
|
||||
key: "/usr/local/certificates/key"
|
||||
|
||||
defaultBackend:
|
||||
enabled: false
|
||||
|
||||
rbac:
|
||||
create: true
|
||||
scope: true
|
||||
|
|
@ -75,7 +75,7 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() {
|
|||
Namespace: f.Namespace,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"SCRIPT_FILENAME": "/home/www/scripts/php$fastcgi_script_name",
|
||||
"SCRIPT_FILENAME": "$fastcgi_script_name",
|
||||
"REDIRECT_STATUS": "200",
|
||||
},
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() {
|
|||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"/home/www/scripts/php$fastcgi_script_name\";") &&
|
||||
return strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"$fastcgi_script_name\";") &&
|
||||
strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,19 +24,19 @@ import (
|
|||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = framework.DescribeAnnotation("whitelist-source-range", func() {
|
||||
f := framework.NewDefaultFramework("ipwhitelist")
|
||||
var _ = framework.DescribeAnnotation("allowlist-source-range", func() {
|
||||
f := framework.NewDefaultFramework("ipallowlist")
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
f.NewEchoDeployment()
|
||||
})
|
||||
|
||||
ginkgo.It("should set valid ip whitelist range", func() {
|
||||
host := "ipwhitelist.foo.com"
|
||||
ginkgo.It("should set valid ip allowlist range", func() {
|
||||
host := "ipallowlist.foo.com"
|
||||
nameSpace := f.Namespace
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/whitelist-source-range": "18.0.0.0/8, 56.0.0.0/8",
|
||||
"nginx.ingress.kubernetes.io/allowlist-source-range": "18.0.0.0/8, 56.0.0.0/8",
|
||||
}
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, nameSpace, framework.EchoService, 80, annotations)
|
||||
|
|
@ -47,6 +47,8 @@ import (
|
|||
_ "k8s.io/ingress-nginx/test/e2e/settings"
|
||||
_ "k8s.io/ingress-nginx/test/e2e/settings/modsecurity"
|
||||
_ "k8s.io/ingress-nginx/test/e2e/settings/ocsp"
|
||||
_ "k8s.io/ingress-nginx/test/e2e/settings/validations"
|
||||
|
||||
_ "k8s.io/ingress-nginx/test/e2e/ssl"
|
||||
_ "k8s.io/ingress-nginx/test/e2e/status"
|
||||
_ "k8s.io/ingress-nginx/test/e2e/tcpudp"
|
||||
|
|
|
|||
|
|
@ -116,7 +116,12 @@ func (f *Framework) newIngressController(namespace string, namespaceOverlay stri
|
|||
if !ok {
|
||||
isChroot = "false"
|
||||
}
|
||||
cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot)
|
||||
|
||||
enableAnnotationValidations, ok := os.LookupEnv("ENABLE_VALIDATIONS")
|
||||
if !ok {
|
||||
enableAnnotationValidations = "false"
|
||||
}
|
||||
cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot, enableAnnotationValidations)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error waiting for ingress controller deployment: %v.\nLogs:\n%v", err, string(out))
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ kubectl run --rm \
|
|||
--env="E2E_NODES=${E2E_NODES}" \
|
||||
--env="FOCUS=${FOCUS}" \
|
||||
--env="IS_CHROOT=${IS_CHROOT:-false}"\
|
||||
--env="ENABLE_VALIDATIONS=${ENABLE_VALIDATIONS:-false}"\
|
||||
--env="E2E_CHECK_LEAKS=${E2E_CHECK_LEAKS}" \
|
||||
--env="NGINX_BASE_IMAGE=${NGINX_BASE_IMAGE}" \
|
||||
--env="HTTPBUN_IMAGE=${HTTPBUN_IMAGE}" \
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ fi
|
|||
|
||||
KIND_LOG_LEVEL="1"
|
||||
IS_CHROOT="${IS_CHROOT:-false}"
|
||||
ENABLE_VALIDATIONS="${ENABLE_VALIDATIONS:-false}"
|
||||
export KIND_CLUSTER_NAME=${KIND_CLUSTER_NAME:-ingress-nginx-dev}
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
# Use 1.0.0-dev to make sure we use the latest configuration in the helm template
|
||||
|
|
|
|||
86
test/e2e/settings/validations/validations.go
Normal file
86
test/e2e/settings/validations/validations.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright 2023 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 annotations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = framework.IngressNginxDescribeSerial("annotation validations", func() {
|
||||
f := framework.NewDefaultFramework("validations")
|
||||
|
||||
ginkgo.It("should allow ingress based on their risk on webhooks", func() {
|
||||
host := "annotation-validations"
|
||||
|
||||
// Low and Medium Risk annotations should be allowed, the rest should be denied
|
||||
f.UpdateNginxConfigMapData("annotations-risk-level", "Medium")
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/default-backend": "default/bla", // low risk
|
||||
"nginx.ingress.kubernetes.io/denylist-source-range": "1.1.1.1/32", // medium risk
|
||||
}
|
||||
|
||||
ginkgo.By("allow ingress with low/medium risk annotations")
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), ing, metav1.CreateOptions{})
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating ingress with allowed annotations should not trigger an error")
|
||||
|
||||
ginkgo.By("block ingress with risky annotations")
|
||||
annotations["nginx.ingress.kubernetes.io/modsecurity-transaction-id"] = "bla123" // High should be blocked
|
||||
annotations["nginx.ingress.kubernetes.io/modsecurity-snippet"] = "some random stuff;" // High should be blocked
|
||||
ing = framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
_, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Update(context.TODO(), ing, metav1.UpdateOptions{})
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating ingress with risky annotations should trigger an error")
|
||||
|
||||
})
|
||||
|
||||
ginkgo.It("should allow ingress based on their risk on webhooks", func() {
|
||||
host := "annotation-validations"
|
||||
|
||||
// Low and Medium Risk annotations should be allowed, the rest should be denied
|
||||
f.UpdateNginxConfigMapData("annotations-risk-level", "Medium")
|
||||
// Sleep a while just to guarantee that the configmap is applied
|
||||
framework.Sleep()
|
||||
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/default-backend": "default/bla", // low risk
|
||||
"nginx.ingress.kubernetes.io/denylist-source-range": "1.1.1.1/32", // medium risk
|
||||
}
|
||||
|
||||
ginkgo.By("allow ingress with low/medium risk annotations")
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), ing, metav1.CreateOptions{})
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating ingress with allowed annotations should not trigger an error")
|
||||
|
||||
ginkgo.By("block ingress with risky annotations")
|
||||
annotations["nginx.ingress.kubernetes.io/modsecurity-transaction-id"] = "bla123" // High should be blocked
|
||||
annotations["nginx.ingress.kubernetes.io/modsecurity-snippet"] = "some random stuff;" // High should be blocked
|
||||
ing = framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
_, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Update(context.TODO(), ing, metav1.UpdateOptions{})
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating ingress with risky annotations should trigger an error")
|
||||
|
||||
})
|
||||
})
|
||||
|
|
@ -24,6 +24,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||
export NAMESPACE=$1
|
||||
export NAMESPACE_OVERLAY=$2
|
||||
export IS_CHROOT=$3
|
||||
export ENABLE_VALIDATIONS=$4
|
||||
|
||||
echo "deploying NGINX Ingress controller in namespace $NAMESPACE"
|
||||
|
||||
|
|
@ -68,6 +69,7 @@ else
|
|||
# TODO: remove the need to use fullnameOverride
|
||||
fullnameOverride: nginx-ingress
|
||||
controller:
|
||||
enableAnnotationValidations: ${ENABLE_VALIDATIONS}
|
||||
image:
|
||||
repository: ingress-controller/controller
|
||||
chroot: ${IS_CHROOT}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue