diff --git a/.gcloudignore b/.gcloudignore new file mode 100644 index 000000000..e69de29bb diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 0e892d626..406a7ad0a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,16 +7,6 @@ assignees: '' --- - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e3781887a..c1c815678 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,6 @@ + ## What this PR does / why we need it: @@ -33,36 +34,3 @@ fixes # - [ ] I've read the [CONTRIBUTION](https://github.com/kubernetes/ingress-nginx/blob/main/CONTRIBUTING.md) guide - [ ] I have added unit and/or e2e tests to cover my changes. - [ ] All new and existing tests passed. -- [ ] Added Release Notes. - -## Does my pull request need a release note? -Any user-visible or operator-visible change qualifies for a release note. This could be a: - -- CLI change -- API change -- UI change -- configuration schema change -- behavioral change -- change in non-functional attributes such as efficiency or availability, availability of a new platform -- a warning about a deprecation -- fix of a previous Known Issue -- fix of a vulnerability (CVE) - -No release notes are required for changes to the following: - -- Tests -- Build infrastructure -- Fixes for unreleased bugs - -For more tips on writing good release notes, check out the [Release Notes Handbook](https://github.com/kubernetes/sig-release/tree/master/release-team/role-handbooks/release-notes) - - -```release-note -PLACE RELEASE NOTES HERE -``` diff --git a/.github/actions/mkdocs/Dockerfile b/.github/actions/mkdocs/Dockerfile index b5b564d3e..378128978 100644 --- a/.github/actions/mkdocs/Dockerfile +++ b/.github/actions/mkdocs/Dockerfile @@ -1,6 +1,4 @@ -FROM squidfunk/mkdocs-material:6.2.4 - -RUN pip install mkdocs-awesome-pages-plugin +FROM squidfunk/mkdocs-material:9.4.5 COPY action.sh /action.sh diff --git a/.github/actions/mkdocs/action.sh b/.github/actions/mkdocs/action.sh index 07ddbddb9..d9f8ecbfc 100644 --- a/.github/actions/mkdocs/action.sh +++ b/.github/actions/mkdocs/action.sh @@ -16,7 +16,7 @@ set -e -REQUIREMENTS="${GITHUB_WORKSPACE}/requirements.txt" +REQUIREMENTS="${GITHUB_WORKSPACE}/docs/requirements.txt" if [ -f "${REQUIREMENTS}" ]; then pip install -r "${REQUIREMENTS}" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 77232aa88..1203be01b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,6 +7,7 @@ on: paths-ignore: - 'docs/**' - 'deploy/**' + - '**.md' push: branches: @@ -14,6 +15,7 @@ on: paths-ignore: - 'docs/**' - 'deploy/**' + - '**.md' workflow_dispatch: inputs: @@ -40,7 +42,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: filter @@ -60,60 +62,6 @@ jobs: - 'charts/ingress-nginx/**/*' - 'NGINX_BASE' - - security: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - - - name: Run Gosec Security Scanner - uses: securego/gosec@a459eb0ba387d9bd083d5c2e2354dbeef2465038 # v2.15.0 - with: - # G601 for zz_generated.deepcopy.go - # G306 TODO: Expect WriteFile permissions to be 0600 or less - # G307 TODO: Deferring unsafe method "Close" - args: -exclude=G109,G601,G104,G204,G304,G306,G307 -tests=false -exclude-dir=test -exclude-dir=images/ -exclude-dir=docs/ ./... - - lint: - runs-on: ubuntu-latest - needs: changes - if: | - (needs.changes.outputs.go == 'true') - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - - - name: Set up Go - id: go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 - with: - go-version: '1.20' - check-latest: true - - - name: Run Lint - run: ./hack/verify-golint.sh - - gofmt: - runs-on: ubuntu-latest - needs: changes - if: | - (needs.changes.outputs.go == 'true') - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - - - name: Set up Go - id: go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 - with: - go-version: '1.20' - check-latest: true - - - name: Run go-fmt - run: ./hack/verify-gofmt.sh - test-go: runs-on: ubuntu-latest needs: changes @@ -121,13 +69,13 @@ jobs: (needs.changes.outputs.go == 'true') steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Go id: go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: '1.20' + go-version: '1.21.3' check-latest: true - name: Run test @@ -142,21 +90,21 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Go id: go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: '1.20' + go-version: '1.21.3' check-latest: true - name: Set up QEMU - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 + uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 with: version: latest @@ -165,9 +113,7 @@ jobs: - name: Prepare Host run: | - sudo apt-get -qq update || true - sudo apt-get install -y pigz - curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.25.5/bin/linux/amd64/kubectl + curl -LO https://dl.k8s.io/release/v1.27.3/bin/linux/amd64/kubectl chmod +x ./kubectl sudo mv ./kubectl /usr/local/bin/kubectl @@ -186,10 +132,10 @@ jobs: nginx-ingress-controller:e2e \ ingress-controller/controller:1.0.0-dev \ ingress-controller/controller-chroot:1.0.0-dev \ - | pigz > docker.tar.gz + | gzip > docker.tar.gz - name: cache - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: docker.tar.gz path: docker.tar.gz @@ -205,16 +151,16 @@ jobs: strategy: matrix: - k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1] + k8s: [v1.25.11, v1.26.6, v1.27.3, v1.28.0] steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: '1.20' + go-version: '1.21.3' check-latest: true - name: cache @@ -258,7 +204,7 @@ jobs: - name: Load images from cache run: | echo "loading docker images..." - pigz -dc docker.tar.gz | docker load + gzip -dc docker.tar.gz | docker load - name: Test env: @@ -280,11 +226,11 @@ jobs: strategy: matrix: - k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1] + k8s: [v1.25.11, v1.26.6, v1.27.3, v1.28.0] steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: cache uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 @@ -299,7 +245,7 @@ jobs: - name: Load images from cache run: | echo "loading docker images..." - pigz -dc docker.tar.gz | docker load + gzip -dc docker.tar.gz | docker load - name: Run e2e tests env: @@ -310,8 +256,57 @@ jobs: kind get kubeconfig > $HOME/.kube/kind-config-kind make kind-e2e-test - - name: Uplaod e2e junit-reports - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + - name: Upload e2e junit-reports + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + if: success() || failure() + with: + name: e2e-test-reports-${{ matrix.k8s }} + path: 'test/junitreports/report*.xml' + + kubernetes-validations: + name: Kubernetes with Validations + runs-on: ubuntu-latest + needs: + - changes + - build + if: | + (needs.changes.outputs.go == 'true') || ${{ inputs.run_e2e }} + + strategy: + matrix: + k8s: [v1.25.11, v1.26.6, v1.27.3, v1.28.0] + + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: cache + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: docker.tar.gz + + - name: Create Kubernetes ${{ matrix.k8s }} cluster + id: kind + run: | + kind create cluster --image=kindest/node:${{ matrix.k8s }} --config test/e2e/kind.yaml + + - name: Load images from cache + run: | + echo "loading docker images..." + gzip -dc docker.tar.gz | docker load + + - name: Run e2e tests + env: + KIND_CLUSTER_NAME: kind + SKIP_CLUSTER_CREATION: true + SKIP_IMAGE_CREATION: true + ENABLE_VALIDATIONS: true + run: | + kind get kubeconfig > $HOME/.kube/kind-config-kind + make kind-e2e-test + + - name: Upload e2e junit-reports + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 if: success() || failure() with: name: e2e-test-reports-${{ matrix.k8s }} @@ -329,12 +324,12 @@ jobs: strategy: matrix: - k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1] + k8s: [v1.25.11, v1.26.6, v1.27.3, v1.28.0] steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: cache uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 @@ -349,7 +344,7 @@ jobs: - name: Load images from cache run: | echo "loading docker images..." - pigz -dc docker.tar.gz | docker load + gzip -dc docker.tar.gz | docker load - name: Run e2e tests env: @@ -362,12 +357,48 @@ jobs: make kind-e2e-test - name: Upload e2e junit-reports - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 if: success() || failure() with: name: e2e-test-reports-chroot-${{ matrix.k8s }} path: 'test/junitreports/report*.xml' + test-nginx-image-build: + permissions: + contents: read # for dorny/paths-filter to fetch a list of changed files + pull-requests: read # for dorny/paths-filter to read pull requests + runs-on: ubuntu-latest + env: + PLATFORMS: linux/amd64,linux/arm64 + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 + id: filter-images + with: + token: ${{ secrets.GITHUB_TOKEN }} + filters: | + nginx-base: + - 'images/nginx/**' + - name: nginx-base-image + if: ${{ steps.filter-images.outputs.nginx-base == 'true' }} + run: | + cd images/nginx/rootfs && docker build -t docker.io/nginx-test-workflow/nginx:${{ github.sha }} . + - name: Run Trivy on NGINX Image + if: ${{ steps.filter-images.outputs.nginx-base == 'true' }} + uses: aquasecurity/trivy-action@master + with: + image-ref: 'docker.io/nginx-test-workflow/nginx:${{ github.sha }}' + format: 'sarif' + ignore-unfixed: true + output: 'trivy-results.sarif' + - name: Upload Trivy scan results to GitHub Security tab + if: ${{ steps.filter-images.outputs.nginx-base == 'true' && always() }} + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results.sarif' + test-image-build: permissions: @@ -378,7 +409,7 @@ jobs: PLATFORMS: linux/amd64,linux/arm64 steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: filter-images @@ -401,7 +432,6 @@ jobs: - 'images/kube-webhook-certgen/**' ext-auth-example-authsvc: - 'images/ext-auth-example-authsvc/**' - - name: custom-error-pages image build if: ${{ steps.filter-images.outputs.custom-error-pages == 'true' }} run: | @@ -447,11 +477,11 @@ jobs: strategy: matrix: - k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1] + k8s: [v1.25.11, v1.26.6, v1.27.3, v1.28.0] steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: filter-images @@ -470,12 +500,13 @@ jobs: - name: Set up Go id: go if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }} - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: '1.20' + go-version: '1.21.3' check-latest: true - name: kube-webhook-certgen image build if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }} run: | cd images/kube-webhook-certgen && make test test-e2e + diff --git a/.github/workflows/depreview.yaml b/.github/workflows/depreview.yaml index de9243f16..1eefe5939 100644 --- a/.github/workflows/depreview.yaml +++ b/.github/workflows/depreview.yaml @@ -9,6 +9,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: 'Dependency Review' - uses: actions/dependency-review-action@f46c48ed6d4f1227fb2d9ea62bf6bcbed315589e # v3.0.4 + uses: actions/dependency-review-action@7bbfa034e752445ea40215fff1c3bf9597993d3f # v3.1.3 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 1a2ceaa83..ab315b3d3 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: filter @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout master - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Deploy uses: ./.github/actions/mkdocs diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 000000000..d3118d469 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,34 @@ +name: golangci-lint + +on: + pull_request: + push: + branches: + - main + paths-ignore: + - 'docs/**' + - 'deploy/**' + - '**.md' + +permissions: + contents: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + id: go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version: '1.21.3' + check-latest: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 + with: + version: v1.53 diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml index 2a82fa124..c6cb31320 100644 --- a/.github/workflows/helm.yaml +++ b/.github/workflows/helm.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Artifact Hub lint run: | @@ -61,7 +61,7 @@ jobs: steps: - name: Checkout master - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: # Fetch entire history. Required for chart-releaser; see https://github.com/helm/chart-releaser-action/issues/13#issuecomment-602063896 fetch-depth: 0 @@ -73,9 +73,9 @@ jobs: git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Helm Chart Releaser - uses: helm/chart-releaser-action@be16258da8010256c6e82849661221415f031968 # v1.5.0 + uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 env: - CR_SKIP_EXISTING: "false" + CR_SKIP_EXISTING: true CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" CR_RELEASE_NAME_TEMPLATE: "helm-chart-{{ .Version }}" with: diff --git a/.github/workflows/junit-reports.yaml b/.github/workflows/junit-reports.yaml index eb25bbeca..62745a53b 100644 --- a/.github/workflows/junit-reports.yaml +++ b/.github/workflows/junit-reports.yaml @@ -9,7 +9,7 @@ jobs: report: runs-on: ubuntu-latest steps: - - uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 + - uses: dorny/test-reporter@afe6793191b75b608954023a46831a3fe10048d4 # v1.7.0 with: artifact: /e2e-test-reports-(.*)/ name: JEST Tests $1 # Name of the check run which will be created diff --git a/.github/workflows/perftest.yaml b/.github/workflows/perftest.yaml index 9e87bf1b2..7f62fedf4 100644 --- a/.github/workflows/perftest.yaml +++ b/.github/workflows/perftest.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install K6 run: | diff --git a/.github/workflows/plugin.yaml b/.github/workflows/plugin.yaml index f77c59937..ab88454fb 100644 --- a/.github/workflows/plugin.yaml +++ b/.github/workflows/plugin.yaml @@ -4,10 +4,8 @@ on: push: branches: - "main" - paths: - - "cmd/plugin/**" tags: - - "v*" + - 'v*.*.*\+plugin' permissions: contents: write # for goreleaser/goreleaser-action @@ -17,21 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: 1.20 + go-version: '1.21.3' check-latest: true - name: Run GoReleaser - uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0 + uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 with: version: latest - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 72acf608a..ac99dc449 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -27,12 +27,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3 + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: results.sarif results_format: sarif @@ -51,7 +51,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/vulnerability-scans.yaml b/.github/workflows/vulnerability-scans.yaml index 069c9f974..226586030 100644 --- a/.github/workflows/vulnerability-scans.yaml +++ b/.github/workflows/vulnerability-scans.yaml @@ -22,7 +22,7 @@ jobs: versions: ${{ steps.version.outputs.TAGS }} steps: - name: Checkout code - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 @@ -52,7 +52,7 @@ jobs: versions: ${{ fromJSON(needs.version.outputs.versions) }} steps: - name: Checkout code - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - shell: bash id: test @@ -60,7 +60,7 @@ jobs: - name: Scan image with AquaSec/Trivy id: scan - uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 # v0.10.0 + uses: aquasecurity/trivy-action@2b6a709cf9c4025c5438138008beaddbb02086f0 # v0.14.0 with: image-ref: registry.k8s.io/ingress-nginx/controller:${{ matrix.versions }} format: 'sarif' diff --git a/.gitignore b/.gitignore index 2c0accad7..60bf6a304 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ cmd/plugin/release/ingress-nginx.yaml cmd/plugin/release/*.tar.gz cmd/plugin/release/LICENSE tmp/ +test/junitreports/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..5da8f0399 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,241 @@ +run: + timeout: 10m + allow-parallel-runners: true + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 +linters: + disable-all: true + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - contextcheck + - decorder + - dogsled + - dupl + - durationcheck + - errcheck + - errchkjson + - errname + - execinquery + - ginkgolinter + - gocheckcompilerdirectives + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - gofumpt + - goheader + - goimports + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + - importas + - ineffassign + - loggercheck + - makezero + - misspell + - musttag + - nakedret + - nolintlint + - nosprintfhostport + - prealloc + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - tenv + - testableexamples + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + - whitespace + # - containedctx + # - cyclop + # - dupword + # - errorlint + # - exhaustive + # - exhaustruct + # - exportloopref + # - forbidigo + # - forcetypeassert + # - funlen + # - gci + # - gochecknoglobals + # - gochecknoinits + # - gocognit + # - godot + # - goerr113 + # - gomnd + # - interfacebloat + # - ireturn + # - lll + # - maintidx + # - nestif + # - nilerr + # - nilnil + # - nlreturn + # - noctx + # - nonamedreturns + # - paralleltest + # - tagliatelle + # - testpackage + # - thelper + # - tparallel + # - varnamelen + # - wastedassign + # - wrapcheck + # - wsl +linters-settings: + gocyclo: + min-complexity: 40 + godox: + keywords: + - BUG + - FIXME + - HACK + errcheck: + check-type-assertions: true + check-blank: true + gocritic: + enabled-checks: + # Diagnostic + - appendAssign + - argOrder + - badCall + - badCond + - badLock + - badRegexp + - badSorting + - builtinShadowDecl + - caseOrder + - codegenComment + - commentedOutCode + - deferInLoop + - deprecatedComment + - dupArg + - dupBranchBody + - dupCase + - dupSubExpr + - dynamicFmtString + - emptyDecl + - evalOrder + - exitAfterDefer + - externalErrorReassign + - filepathJoin + - flagDeref + - flagName + - mapKey + - nilValReturn + - offBy1 + - regexpPattern + - returnAfterHttpError + - sloppyReassign + - sloppyTypeAssert + - sortSlice + - sprintfQuotedString + - sqlQuery + - syncMapLoadAndDelete + - truncateCmp + - unnecessaryDefer + - weakCond + + # Performance + - appendCombine + - equalFold + - hugeParam + - indexAlloc + - preferDecodeRune + - preferFprint + - preferStringWriter + - preferWriteByte + - rangeExprCopy + - rangeValCopy + - sliceClear + - stringXbytes + + # Style + - assignOp + - boolExprSimplify + - captLocal + - commentFormatting + - commentedOutImport + - defaultCaseOrder + - deferUnlambda + - docStub + - dupImport + - elseif + - emptyFallthrough + - emptyStringTest + - exposedSyncMutex + - hexLiteral + - httpNoBody + - ifElseChain + - methodExprCall + - newDeref + - octalLiteral + - preferFilepathJoin + - redundantSprint + - regexpMust + - regexpSimplify + - ruleguard + - singleCaseSwitch + - sloppyLen + - stringConcatSimplify + - stringsCompare + - switchTrue + - timeCmpSimplify + - timeExprSimplify + - todoCommentWithoutDetail + - tooManyResultsChecker + - typeAssertChain + - typeDefFirst + - typeSwitchVar + - underef + - unlabelStmt + - unlambda + - unslice + - valSwap + - whyNoLint + - wrapperFunc + - yodaStyleExpr + + # Opinionated + - builtinShadow + - importShadow + - initClause + - nestingReduce + - paramTypeCombine + - ptrToRefParam + - typeUnparen + - unnamedResult + - unnecessaryBlock + nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: false + # Disable to ensure that nolint directives don't have a leading space. Default is true. + # TODO(lint): Enforce machine-readable `nolint` directives + allow-leading-space: true + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + # TODO(lint): Enforce explanations for `nolint` directives + require-explanation: false + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true diff --git a/Makefile b/Makefile index fc40a39bb..cbda4dafb 100644 --- a/Makefile +++ b/Makefile @@ -128,6 +128,12 @@ static-check: ## Run verification script for boilerplate, codegen, gofmt, golint MAC_OS=$(MAC_OS) \ hack/verify-all.sh +.PHONY: golint-check +golint-check: + @build/run-in-docker.sh \ + MAC_OS=$(MAC_OS) \ + hack/verify-golint.sh + ############################### # Tests for ingress-nginx ############################### @@ -262,3 +268,8 @@ release: ensure-buildx clean --build-arg COMMIT_SHA="$(COMMIT_SHA)" \ --build-arg BUILD_ID="$(BUILD_ID)" \ -t $(REGISTRY)/controller-chroot:$(TAG) rootfs -f rootfs/Dockerfile-chroot + +.PHONY: build-docs +build-docs: + pip install -r docs/requirements.txt + mkdocs build --config-file mkdocs.yml diff --git a/NGINX_BASE b/NGINX_BASE index 5f1751bbf..d6cecf76b 100644 --- a/NGINX_BASE +++ b/NGINX_BASE @@ -1 +1 @@ -registry.k8s.io/ingress-nginx/nginx:91057c439cf07ffb62887b8a8bda66ce3cbe39ca@sha256:3b650123c755392f8c0eb9a356b12716327106e624ab5f5b43bc25ab130978fb \ No newline at end of file +registry.k8s.io/ingress-nginx/nginx:v20231011-8b53cabe0@sha256:34881d62f71e8573fb765c40585dba28a1148206fbbe2c3871ad3f4e8c6e360f diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index cd9001dd2..5423743db 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -26,10 +26,12 @@ aliases: ingress-nginx-helm-maintainers: - cpanato + - Gacko - strongjz ingress-nginx-helm-reviewers: - cpanato + - Gacko - strongjz ingress-nginx-docs-maintainers: diff --git a/README.md b/README.md index 86f95cb9b..29ad2929d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Overview -ingress-nginx is an Ingress controller for Kubernetes using [NGINX](https://www.nginx.org/) as a reverse proxy and load +ingress-nginx is an Ingress controller for Kubernetes using [NGINX](https://www.nginx.org/) as a reverse proxy and load balancer. [Learn more about Ingress on the main Kubernetes documentation site](https://kubernetes.io/docs/concepts/services-networking/ingress/). @@ -20,66 +20,63 @@ See the [Getting Started](https://kubernetes.github.io/ingress-nginx/deploy/) do ## Troubleshooting -If you encounter issues, review the [troubleshooting docs](docs/troubleshooting.md), -[file an issue](https://github.com/kubernetes/ingress-nginx/issues), or talk to us on the +If you encounter issues, review the [troubleshooting docs](docs/troubleshooting.md), +[file an issue](https://github.com/kubernetes/ingress-nginx/issues), or talk to us on the [#ingress-nginx channel](https://kubernetes.slack.com/messages/ingress-nginx) on the Kubernetes Slack server. ## Changelog -See [the list of releases](https://github.com/kubernetes/ingress-nginx/releases) to find out about feature changes. -For detailed changes for each release; please check the [Changelog.md](Changelog.md) file. -For detailed changes on the `ingress-nginx` helm chart, please check the following -[CHANGELOG.md](charts/ingress-nginx/CHANGELOG.md) file. +See [the list of releases](https://github.com/kubernetes/ingress-nginx/releases) for all changes. +For detailed changes for each release, please check the [changelog-$version.md](./changelog) file for the release version. +For detailed changes on the `ingress-nginx` helm chart, please check the changelog folder for a specific version +[CHANGELOG-$current-version.md](./charts/ingress-nginx/changelog) file. -### Supported Versions table +### Supported Versions table Supported versions for the ingress-nginx project mean that we have completed E2E tests, and they are passing for -the versions listed. Ingress-Nginx versions may work on older versions but the project does not make that guarantee. +the versions listed. Ingress-Nginx versions **may** work on older versions, but the project does not make that guarantee. -| | Ingress-NGINX version | k8s supported version | Alpine Version | Nginx Version | Helm Chart Version | -|:-----:|------------------------|------------------------------|----------------|---------------|--------------------| -| πŸ”„ | **v1.7.1** | 1.27,1.26, 1.25, 1.24 | 3.17.2 | 1.21.6 | 4.6.* | -| πŸ”„ | **v1.7.0** | 1.26, 1.25, 1.24 | 3.17.2 | 1.21.6 | 4.6.* | -| πŸ”„ | **v1.6.4** | 1.26, 1.25, 1.24, 1.23 | 3.17.0 | 1.21.6 | 4.5.* | -| πŸ”„| **v1.5.1** | 1.25, 1.24, 1.23 | 3.16.2 | 1.21.6 | 4.4.* | -| | v1.4.0 | 1.25, 1.24, 1.23, 1.22 | 3.16.2 | 1.19.10† | 4.3.0 | -| | v1.3.1 | 1.24, 1.23, 1.22, 1.21, 1.20 | 3.16.2 | 1.19.10† | 4.2.5 | -| | v1.3.0 | 1.24, 1.23, 1.22, 1.21, 1.20 | 3.16.0 | 1.19.10† | 4.2.3 | -| | v1.2.1 | 1.23, 1.22, 1.21, 1.20, 1.19 | 3.14.6 | 1.19.10† | 4.1.4 | -| | v1.1.3 | 1.23, 1.22, 1.21, 1.20, 1.19 | 3.14.4 | 1.19.10† | 4.0.19 | -| | v1.1.2 | 1.23, 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.18 | -| | v1.1.1 | 1.23, 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.17 | -| | v1.1.0 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.13 | -| | v1.0.5 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.9 | -| | v1.0.4 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.6 | -| | v1.0.3 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.5 | -| | v1.0.2 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.3 | -| | v1.0.1 | 1.22, 1.21, 1.20, 1.19 | 3.14.2 | 1.19.9† | 4.0.2 | -| | v1.0.0 | 1.22, 1.21, 1.20, 1.19 | 3.13.5 | 1.20.1 | 4.0.1 | +| Supported | Ingress-NGINX version | k8s supported version | Alpine Version | Nginx Version | Helm Chart Version | +|:--:|-----------------------|------------------------------|----------------|---------------|------------------------------| +| πŸ”„ | **v1.9.4** | 1.28, 1.27,1.26, 1.25 | 3.18.4 | 1.21.6 | 4.8.3* | +| πŸ”„ | **v1.9.3** | 1.28, 1.27,1.26, 1.25 | 3.18.4 | 1.21.6 | 4.8.* | +| πŸ”„ | **v1.9.1** | 1.28, 1.27,1.26, 1.25 | 3.18.4 | 1.21.6 | 4.8.* | +| πŸ”„ | **v1.9.0** | 1.28, 1.27,1.26, 1.25 | 3.18.2 | 1.21.6 | 4.8.* | +| πŸ”„ | **v1.8.4** | 1.27,1.26, 1.25, 1.24 | 3.18.2 | 1.21.6 | 4.7.* | +| πŸ”„ | **v1.8.2** | 1.27,1.26, 1.25, 1.24 | 3.18.2 | 1.21.6 | 4.7.* | +| πŸ”„ | **v1.8.1** | 1.27,1.26, 1.25, 1.24 | 3.18.2 | 1.21.6 | 4.7.* | +| πŸ”„ | **v1.8.0** | 1.27,1.26, 1.25, 1.24 | 3.18.0 | 1.21.6 | 4.7.* | +| πŸ”„ | **v1.7.1** | 1.27,1.26, 1.25, 1.24 | 3.17.2 | 1.21.6 | 4.6.* | +| πŸ”„ | **v1.7.0** | 1.26, 1.25, 1.24 | 3.17.2 | 1.21.6 | 4.6.* | +| | v1.6.4 | 1.26, 1.25, 1.24, 1.23 | 3.17.0 | 1.21.6 | 4.5.* | +| | v1.5.1 | 1.25, 1.24, 1.23 | 3.16.2 | 1.21.6 | 4.4.* | +| | v1.4.0 | 1.25, 1.24, 1.23, 1.22 | 3.16.2 | 1.19.10† | 4.3.0 | +| | v1.3.1 | 1.24, 1.23, 1.22, 1.21, 1.20 | 3.16.2 | 1.19.10† | 4.2.5 | +| | v1.3.0 | 1.24, 1.23, 1.22, 1.21, 1.20 | 3.16.0 | 1.19.10† | 4.2.3 | - -† _This build is -[patched against CVE-2021-23017](https://github.com/openresty/openresty/commit/4b5ec7edd78616f544abc194308e0cf4b788725b#diff-42ef841dc27fe0b5aa2d06bd31308bb63a59cdcddcbcddd917248349d22020a3)._ - -See [this article](https://kubernetes.io/blog/2021/07/26/update-with-ingress-nginx/) if you want upgrade to the stable -Ingress API. +See [this article](https://kubernetes.io/blog/2021/07/26/update-with-ingress-nginx/) if you want upgrade to the stable +Ingress API. ## Get Involved Thanks for taking the time to join our community and start contributing! -- This project adheres to the [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md). +- This project adheres to the [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md). By participating in this project, you agree to abide by its terms. -- **Contributing**: Contributions of all kind are welcome! - - - Read [`CONTRIBUTING.md`](CONTRIBUTING.md) for information about setting up your environment, the workflow that we +- **Contributing**: Contributions of all kinds are welcome! + + - Read [`CONTRIBUTING.md`](CONTRIBUTING.md) for information about setting up your environment, the workflow that we expect, and instructions on the developer certificate of origin that we require. - Join our Kubernetes Slack channel for developer discussion : [#ingress-nginx-dev](https://kubernetes.slack.com/archives/C021E147ZA4). - - Submit GitHub issues for any feature enhancements, bugs or documentation problems. Please make sure to read the [Issue Reporting Checklist](https://github.com/kubernetes/ingress-nginx/blob/main/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines **may be closed immediately**. - - **Support**: Join the [#ingress-nginx-users](https://kubernetes.slack.com/messages/CANQGM8BA/) channel inside the [Kubernetes Slack](http://slack.kubernetes.io/) to ask questions or get support from the maintainers and other users. + - Submit GitHub issues for any feature enhancements, bugs, or documentation problems. + - Please make sure to read the [Issue Reporting Checklist](https://github.com/kubernetes/ingress-nginx/blob/main/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines **may be closed immediately**. + - Join our [ingress-nginx-dev mailing list](https://groups.google.com/a/kubernetes.io/g/ingress-nginx-dev/c/ebbBMo-zX-w) + +- **Support**: + - Join the [#ingress-nginx-users](https://kubernetes.slack.com/messages/CANQGM8BA/) channel inside the [Kubernetes Slack](http://slack.kubernetes.io/) to ask questions or get support from the maintainers and other users. - The [GitHub issues](https://github.com/kubernetes/ingress-nginx/issues) in the repository are **exclusively** for bug reports and feature requests. - - **Discuss**: Tweet using the `#IngressNginx` hashtag. + - **Discuss**: Tweet using the `#IngressNginx` hashtag or sharing with us [@IngressNginx](https://twitter.com/IngressNGINX). ## License diff --git a/TAG b/TAG index 668c7899c..c4e620172 100644 --- a/TAG +++ b/TAG @@ -1,2 +1 @@ -v1.7.1 - +v1.9.4 diff --git a/build/dev-env.sh b/build/dev-env.sh index 3d21b7e15..93ed3de40 100755 --- a/build/dev-env.sh +++ b/build/dev-env.sh @@ -45,14 +45,16 @@ if ! command -v helm &> /dev/null; then exit 1 fi +function ver { printf "%d%03d%03d" $(echo "$1" | tr '.' ' '); } + HELM_VERSION=$(helm version 2>&1 | cut -f1 -d"," | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') || true echo $HELM_VERSION -if [[ ${HELM_VERSION} -lt 3.10.0 ]]; then +if [[ $(ver $HELM_VERSION) -lt $(ver "3.10.0") ]]; then echo "Please upgrade helm to v3.10.0 or higher" exit 1 fi -KUBE_CLIENT_VERSION=$(kubectl version --client --short 2>/dev/null | grep Client | awk '{print $3}' | cut -d. -f2) || true +KUBE_CLIENT_VERSION=$(kubectl version --client -oyaml 2>/dev/null | grep "minor:" | awk '{print $2}' | tr -d '"') || true if [[ ${KUBE_CLIENT_VERSION} -lt 24 ]]; then echo "Please update kubectl to 1.24.2 or higher" exit 1 diff --git a/build/run-in-docker.sh b/build/run-in-docker.sh index 3a6aeb43d..bec825291 100755 --- a/build/run-in-docker.sh +++ b/build/run-in-docker.sh @@ -44,7 +44,7 @@ function cleanup { } trap cleanup EXIT -E2E_IMAGE=${E2E_IMAGE:-registry.k8s.io/ingress-nginx/e2e-test-runner:v20230314-helm-chart-4.5.2-32-g520384b11@sha256:754c62f9a5efd1ee515ee908ecc16c0c4d1dda96a8cc8019667182a55f3a9035} +E2E_IMAGE=${E2E_IMAGE:-registry.k8s.io/ingress-nginx/e2e-test-runner:v20231011-8b53cabe0@sha256:ed0dad805c635e66469b4ac376010eebdd0b3fe62d753f58db1632d6f12f451d} if [[ "$RUNTIME" == podman ]]; then # Podman does not support both tag and digest @@ -87,7 +87,7 @@ if [[ "$DOCKER_IN_DOCKER_ENABLED" == "true" ]]; then echo "..reached DIND check TRUE block, inside run-in-docker.sh" echo "FLAGS=$FLAGS" #go env - go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@v2.9.0 + go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@v2.13.0 find / -type f -name ginkgo 2>/dev/null which ginkgo /bin/bash -c "${FLAGS}" diff --git a/changelog/Changelog-1.8.0.md b/changelog/Changelog-1.8.0.md new file mode 100644 index 000000000..a109c68ba --- /dev/null +++ b/changelog/Changelog-1.8.0.md @@ -0,0 +1,67 @@ +# Changelog + +### 1.8.0 +Images: + +* registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 +* registry.k8s.io/ingress-nginx/controller-chroot:v1.8.0@sha256:a45e41cd2b7670adf829759878f512d4208d0aec1869dae593a0fecd09a5e49e + +### Important Changes: + +* Validate path types (#9967) +* images: upgrade to Alpine 3.18 (#9997) +* Update documentation to reflect project name; Ingress-Nginx Controller + +For improving security on our 1.8.0 release includes a +[new, **optional** validation ](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#strict-validate-path-type) +that limits the characters accepted on ".spec paths.path" when pathType=Exact or pathType=Prefix, +to alphanumeric characters only. More information can be found on our +[Google doc](https://docs.google.com/document/d/1HPvaEwHRuMSkXYkVIJ-w7IpijKdHfNynm_4N2Akt0CQ/edit?usp=sharing) +, our new [ingress-nginx-dev mailing list](https://groups.google.com/a/kubernetes.io/g/ingress-nginx-dev/c/ebbBMo-zX-w) +or in our [docs](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#strict-validate-path-type) + +### Community Updates + +We are now posting updates and release to our twitter handle, [@IngressNginx](https://twitter.com/IngressNGINX) and +on our new [ingress-nginx-dev mailing list](https://groups.google.com/a/kubernetes.io/g/ingress-nginx-dev/c/ebbBMo-zX-w) + +### All Changes: + +* Add legacy to OpenTelemetry migration doc (#10011) +* changed tagsha to recent builds (#10001) +* change to alpine318 baseimage (#10000) +* images: upgrade to Alpine 3.18 (#9997) +* openssl CVE fix (#9996) +* PodDisruptionBudget spec logic update (#9904) +* Admission warning (#9975) +* Add OPA examples on pathType restrictions (#9992) +* updated testrunner image tag+sha (#9987) +* bumped ginkgo to v2.9.5 (#9985) +* helm: Fix opentelemetry module installation for daemonset (#9792) +* OpenTelemetry default config (#9978) +* Correct annotations in monitoring docs (#9976) +* fix: avoid builds and tests for changes to markdown (#9962) +* Validate path types (#9967) +* HPA: Use capabilites & align manifests. (#9521) +* Use dl.k8s.io instead of hardcoded GCS URIs (#9946) +* add option for annotations in PodDisruptionBudget (#9843) +* chore: update httpbin to httpbun (#9919) +* image_update (#9942) +* Add geoname id value into $geoip2_*_geoname_id variables (#9527) +* Update annotations.md (#9933) +* Update charts/* to keep project name display aligned (#9931) +* Keep project name display aligned (#9920) + +### Dependencies updates: +* Bump github.com/imdario/mergo from 0.3.15 to 0.3.16 (#10008) +* Bump github.com/prometheus/common from 0.43.0 to 0.44.0 (#10007) +* Bump k8s.io/klog/v2 from 2.90.1 to 2.100.1 (#9913) +* Bump github.com/onsi/ginkgo/v2 from 2.9.0 to 2.9.5 (#9980) +* Bump golang.org/x/crypto from 0.8.0 to 0.9.0 (#9982) +* Bump actions/setup-go from 4.0.0 to 4.0.1 (#9984) +* Bump securego/gosec from 2.15.0 to 2.16.0 (#9983) +* Bump github.com/prometheus/common from 0.42.0 to 0.43.0 (#9981) +* Bump github.com/prometheus/client_model from 0.3.0 to 0.4.0 (#9937) +* Bump google.golang.org/grpc from 1.54.0 to 1.55.0 (#9936) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.7.1...controller-controller-v1.8.0 \ No newline at end of file diff --git a/changelog/Changelog-1.8.1.md b/changelog/Changelog-1.8.1.md new file mode 100644 index 000000000..c84a7ce18 --- /dev/null +++ b/changelog/Changelog-1.8.1.md @@ -0,0 +1,67 @@ +# Changelog + +### 1.8.1 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.8.1@sha256:e5c4824e7375fcf2a393e1c03c293b69759af37a9ca6abdb91b13d78a93da8bd + * registry.k8s.io/ingress-nginx/controller-chroot:v1.8.1@sha256:e0d4121e3c5e39de9122e55e331a32d5ebf8d4d257227cb93ab54a1b912a7627 + +### All Changes: + +* netlify: Only trigger preview when there are changes in docs. (#10144) +* changed to updated baseimage and reverted tag (#10143) +* Fix loadBalancerClass value (#10139) +* Added a doc line to the missing helm value service.internal.loadBalancerIP (#9406) +* Set grpc :authority header from request header (#8912) +* bump pinned golang to 1.20.5 (#10127) +* update test runner (#10125) +* chore: remove echo from snippet tests (#10110) +* Update typo in docs for lb scheme (#10117) +* golang 1.20.5 bump (#10120) +* feat(helm): Add loadBalancerClass (#9562) +* chore: remove echo friom canary tests (#10089) +* fix: obsolete warnings (#10029) +* docs: change Dockefile url ref main (#10087) +* Revert "Remove fastcgi feature" (#10081) +* docs: add netlify configuration (#10073) +* add distroless otel init (#10035) +* chore: move httpbun to be part of framework (#9955) +* Remove fastcgi feature (#9864) +* Fix mirror-target values without path separator and port (#9889) +* Adding feature to upgrade Oracle Cloud Infrastructure's Flexible Load Balancer and adjusting Health Check that were critical in the previous configuration (#9961) +* add support for keda fallback settings (#9993) +* unnecessary use of fmt.Sprint (S1039) (#10049) +* chore: pkg imported more than once (#10048) +* tracing: upgrade to dd-opentracing-cpp v1.3.7 (#10031) +* fix: add canary to sidebar in examples (#10068) +* docs: add lua testing documentation (#10060) +* docs: canary weighted deployments example (#10067) +* Update Internal Load Balancer docs (#10062) +* fix broken kubernetes.io/user-guide/ docs links (#10055) +* docs: Updated the content of deploy/rbac.md (#10054) +* ensured hpa mem spec before cpu spec (#10043) +* Fix typo in controller_test (#10034) +* chore(dep): upgrade github.com/emicklei/go-restful/v3 to 3.10 (#10028) +* Upgrade to Golang 1.20.4 (#10016) +* perf: avoid unnecessary byte/string conversion (#10012) +* added note on dns for localtesting (#10021) +* added helmshowvalues example (#10019) +* release controller 1.8.0 and chart 4.7.0 (#10017) + +### Dependencies updates: +* Bump ossf/scorecard-action from 2.1.3 to 2.2.0 (#10133) +* Bump google.golang.org/grpc from 1.56.0 to 1.56.1 (#10134) +* Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#10106) +* Bump golang.org/x/crypto from 0.9.0 to 0.10.0 (#10105) +* Bump google.golang.org/grpc from 1.55.0 to 1.56.0 (#10103) +* Bump goreleaser/goreleaser-action from 4.2.0 to 4.3.0 (#10101) +* Bump docker/setup-buildx-action from 2.6.0 to 2.7.0 (#10102) +* Bump actions/checkout from 3.5.2 to 3.5.3 (#10076) +* Bump docker/setup-qemu-action from 2.1.0 to 2.2.0 (#10075) +* Bump aquasecurity/trivy-action from 0.10.0 to 0.11.2 (#10078) +* Bump docker/setup-buildx-action from 2.5.0 to 2.6.0 (#10077) +* Bump actions/dependency-review-action from 3.0.4 to 3.0.6 (#10042) +* Bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#10041) +* Bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#10005) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.8.0...controller-controller-v1.8.1 diff --git a/changelog/Changelog-1.8.2.md b/changelog/Changelog-1.8.2.md new file mode 100644 index 000000000..12a273af2 --- /dev/null +++ b/changelog/Changelog-1.8.2.md @@ -0,0 +1,19 @@ +# Changelog + +### 1.8.2 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.8.2@sha256:74834d3d25b336b62cabeb8bf7f1d788706e2cf1cfd64022de4137ade8881ff2 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.8.2@sha256:1317a563219f755a6094d990057c78e5c4dcea5e31f4ce1db8641e732a7d6133 + +### All Changes: + +* Release v1.8.2 and Update Go to v1.21.1 (#10379) +* Making auth access logs optional (#10380) +* [release-1.8] Disable Modsecurity from internal processing which affects large ingresses (#10375) +* promote distroless otel init image (#10270) +* [release-1.8] Update images tags after adding git data in gcloud (#10233) +* [release-1.8] Golang 1.20.6 for test runner (#10231) + +### Dependencies updates: +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.8.1...controller-controller-v1.8.2 diff --git a/changelog/Changelog-1.9.0-beta.0.md b/changelog/Changelog-1.9.0-beta.0.md new file mode 100644 index 000000000..d1ffe148f --- /dev/null +++ b/changelog/Changelog-1.9.0-beta.0.md @@ -0,0 +1,93 @@ +# Changelog + +### 1.9.0-beta.0 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.9.0-beta.0@sha256:531377e4cc9dc62af40d742402222603259673f5a755a64d74122f256dfad8f9 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.9.0-beta.0@sha256:60b4c95349ce2a81a3b2a76423ee483b847b89d3fa8cb148468434f606f3fa0c + +### All Changes: + +* Rework mage (#10418) +* Start release of v1.9.0 beta0 (#10407) +* Update k8s versions on CI (#10406) +* Add a flag to enable or disable aio_write (#10394) +* Update external-articles.md - advanced setup with GKE/Cloud Armor/IAP (#10372) +* Fix e2e test suite doc (#10396) +* Disable user snippets per default (#10393) +* Deployment/DaemonSet: Fix templating & value. (#10240) +* Fix deferInLoop error (#10387) +* Remove gofmt (#10385) +* Deployment/DaemonSet: Template `topologySpreadConstraints`. (#10259) +* release notes 1.8.2 (#10389) +* fix: remove curl on base container #9716 (#10306) +* Fix http default backend test (#10382) +* Add golangci github action and replace the deprecated golint (#10187) +* BUGFIX incorrect indentation (#10254) +* Upgrade OpenTelemetry to v1.11.0 and gRPC to v1.57.0 (#10352) +* fix: path with sepecial characters warning #10281 #10308 (#10330) +* Fix golangci-lint errors (#10196) +* chore(build): Fix Run make dev-env syntax error (#10294) +* Add firewall configuration to quick start documentation (#10357) +* Making auth access logs optional (#10335) +* Fix β€œdev-env” Makefile target to work with kubectl 1.28+ (#10350) +* fix: update action file to auto release plugin #10197 (#10321) +* Use gzip instead of pigz in CI (#10348) +* Disable Modsecurity from internal processing which affects large ingresses (#10316) +* fix: add /etc/mime.types #10309 (#10310) +* Remove curl dependencies in e2e tests #9716 (#10296) +* docs: swap explanation to match example (#10220) +* ci(helm): fix Helm Chart release action 422 error (#10237) +* helm: Use .Release.Namespace as default for ServiceMonitor namespace (#10249) +* Updated index.md - Fix typos (#10256) +* Handle request_id variable correctly in auth requests (#9219) +* test kind updates (#10272) +* promote distroless otel init image (#10257) +* [helm] configure allow to configure hostAliases (#10180) +* Add rolling update strategy to each static deployment file (#10129) +* Implement annotation validation (#9673) +* Golang 1.20.6 for test runner (#10230) +* [helm] pass service annotations through helm tpl engine (#10084) +* Ignore deployment template's replicas if KEDA is enabled (#9534) +* chore: bump OpenResty to v1.21.4.2 (#10219) +* Scanning port 10247 lead to tcp connection 502 error (#9815) +* revise Datadog trace sampling configuration (#10151) +* Clarify TCP/UDP service docs (#10146) +* Exposed continent data as variable in the case of Maxmind city files (#10157) +* Cleanup errcheck code (#10166) +* Fix golang-ci linter errors (#10128) +* Deprecate and remove AJP support (#10158) +* release notes 1.8.1 (#10161) + +### Dependencies updates: +* Bump github.com/onsi/ginkgo/v2 from 2.9.5 to 2.12.0 (#10355) +* Bump golang.org/x/crypto from 0.12.0 to 0.13.0 (#10399) +* Bump actions/setup-go from 4.0.1 to 4.1.0 (#10403) +* Bump goreleaser/goreleaser-action from 4.4.0 to 4.6.0 (#10402) +* Bump actions/upload-artifact from 3.1.2 to 3.1.3 (#10404) +* Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#10400) +* Bump google.golang.org/grpc from 1.57.0 to 1.58.0 (#10398) +* Bump actions/dependency-review-action from 3.0.8 to 3.1.0 (#10401) +* Bump github.com/cyphar/filepath-securejoin from 0.2.3 to 0.2.4 (#10373) +* Bump github.com/cyphar/filepath-securejoin in /magefiles (#10374) +* Bump Go version to 1.21.1 (#10377) +* Bump Go version to 1.21.1 on testrunner (#10378) +* Bump aquasecurity/trivy-action from 0.11.2 to 0.12.0 (#10365) +* Bump docker/setup-buildx-action from 2.9.1 to 2.10.0 (#10353) +* Bump actions/checkout from 3.5.3 to 3.6.0 (#10354) +* Bump actions/dependency-review-action from 3.0.6 to 3.0.8 (#10333) +* Bump actions/setup-go from 4.0.1 to 4.1.0 (#10313) +* Bump securego/gosec from 2.16.0 to 2.17.0 (#10332) +* Bump goreleaser/goreleaser-action from 4.3.0 to 4.4.0 (#10314) +* Bump github.com/opencontainers/runc from 1.1.8 to 1.1.9 (#10298) +* Bump k8s.io/component-base from 0.26.4 to 0.27.4 (Replace Topology Aware Hints with Topology Aware Routing) (#10282) +* Bump google.golang.org/grpc from 1.56.2 to 1.57.0 (#10258) +* Bump golang.org/x/crypto from 0.11.0 to 0.12.0 (#10280) +* Bump github.com/opencontainers/runc from 1.1.7 to 1.1.8 (#10244) +* Bump google.golang.org/grpc from 1.56.1 to 1.56.2 (#10193) +* Bump docker/setup-buildx-action from 2.9.0 to 2.9.1 (#10207) +* Bump golang.org/x/crypto from 0.10.0 to 0.11.0 (#10192) +* Bump docker/setup-buildx-action from 2.8.0 to 2.9.0 (#10191) +* Bump docker/setup-buildx-action from 2.7.0 to 2.8.0 (#10165) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-release-1.8...controller-controller-v1.9.0-beta.0 diff --git a/changelog/Changelog-1.9.0.md b/changelog/Changelog-1.9.0.md new file mode 100644 index 000000000..cd5396f05 --- /dev/null +++ b/changelog/Changelog-1.9.0.md @@ -0,0 +1,93 @@ +# Changelog + +### 1.9.0 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.9.0@sha256:c15d1a617858d90fb8f8a2dd60b0676f2bb85c54e3ed11511794b86ec30c8c60 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.9.0@sha256:d9fa7a136de2104fb2ecfcf1666978bfab927f4a125b75c0fb471e6104366ab5 + +### All Changes: + +* Rework mage (#10418) +* Start release of v1.9.0 beta0 (#10407) +* Update k8s versions on CI (#10406) +* Add a flag to enable or disable aio_write (#10394) +* Update external-articles.md - advanced setup with GKE/Cloud Armor/IAP (#10372) +* Fix e2e test suite doc (#10396) +* Disable user snippets per default (#10393) +* Deployment/DaemonSet: Fix templating & value. (#10240) +* Fix deferInLoop error (#10387) +* Remove gofmt (#10385) +* Deployment/DaemonSet: Template `topologySpreadConstraints`. (#10259) +* release notes 1.8.2 (#10389) +* fix: remove curl on base container #9716 (#10306) +* Fix http default backend test (#10382) +* Add golangci github action and replace the deprecated golint (#10187) +* BUGFIX incorrect indentation (#10254) +* Upgrade OpenTelemetry to v1.11.0 and gRPC to v1.57.0 (#10352) +* fix: path with sepecial characters warning #10281 #10308 (#10330) +* Fix golangci-lint errors (#10196) +* chore(build): Fix Run make dev-env syntax error (#10294) +* Add firewall configuration to quick start documentation (#10357) +* Making auth access logs optional (#10335) +* Fix β€œdev-env” Makefile target to work with kubectl 1.28+ (#10350) +* fix: update action file to auto release plugin #10197 (#10321) +* Use gzip instead of pigz in CI (#10348) +* Disable Modsecurity from internal processing which affects large ingresses (#10316) +* fix: add /etc/mime.types #10309 (#10310) +* Remove curl dependencies in e2e tests #9716 (#10296) +* docs: swap explanation to match example (#10220) +* ci(helm): fix Helm Chart release action 422 error (#10237) +* helm: Use .Release.Namespace as default for ServiceMonitor namespace (#10249) +* Updated index.md - Fix typos (#10256) +* Handle request_id variable correctly in auth requests (#9219) +* test kind updates (#10272) +* promote distroless otel init image (#10257) +* [helm] configure allow to configure hostAliases (#10180) +* Add rolling update strategy to each static deployment file (#10129) +* Implement annotation validation (#9673) +* Golang 1.20.6 for test runner (#10230) +* [helm] pass service annotations through helm tpl engine (#10084) +* Ignore deployment template's replicas if KEDA is enabled (#9534) +* chore: bump OpenResty to v1.21.4.2 (#10219) +* Scanning port 10247 lead to tcp connection 502 error (#9815) +* revise Datadog trace sampling configuration (#10151) +* Clarify TCP/UDP service docs (#10146) +* Exposed continent data as variable in the case of Maxmind city files (#10157) +* Cleanup errcheck code (#10166) +* Fix golang-ci linter errors (#10128) +* Deprecate and remove AJP support (#10158) +* release notes 1.8.1 (#10161) + +### Dependencies updates: +* Bump github.com/onsi/ginkgo/v2 from 2.9.5 to 2.12.0 (#10355) +* Bump golang.org/x/crypto from 0.12.0 to 0.13.0 (#10399) +* Bump actions/setup-go from 4.0.1 to 4.1.0 (#10403) +* Bump goreleaser/goreleaser-action from 4.4.0 to 4.6.0 (#10402) +* Bump actions/upload-artifact from 3.1.2 to 3.1.3 (#10404) +* Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#10400) +* Bump google.golang.org/grpc from 1.57.0 to 1.58.0 (#10398) +* Bump actions/dependency-review-action from 3.0.8 to 3.1.0 (#10401) +* Bump github.com/cyphar/filepath-securejoin from 0.2.3 to 0.2.4 (#10373) +* Bump github.com/cyphar/filepath-securejoin in /magefiles (#10374) +* Bump Go version to 1.21.1 (#10377) +* Bump Go version to 1.21.1 on testrunner (#10378) +* Bump aquasecurity/trivy-action from 0.11.2 to 0.12.0 (#10365) +* Bump docker/setup-buildx-action from 2.9.1 to 2.10.0 (#10353) +* Bump actions/checkout from 3.5.3 to 3.6.0 (#10354) +* Bump actions/dependency-review-action from 3.0.6 to 3.0.8 (#10333) +* Bump actions/setup-go from 4.0.1 to 4.1.0 (#10313) +* Bump securego/gosec from 2.16.0 to 2.17.0 (#10332) +* Bump goreleaser/goreleaser-action from 4.3.0 to 4.4.0 (#10314) +* Bump github.com/opencontainers/runc from 1.1.8 to 1.1.9 (#10298) +* Bump k8s.io/component-base from 0.26.4 to 0.27.4 (Replace Topology Aware Hints with Topology Aware Routing) (#10282) +* Bump google.golang.org/grpc from 1.56.2 to 1.57.0 (#10258) +* Bump golang.org/x/crypto from 0.11.0 to 0.12.0 (#10280) +* Bump github.com/opencontainers/runc from 1.1.7 to 1.1.8 (#10244) +* Bump google.golang.org/grpc from 1.56.1 to 1.56.2 (#10193) +* Bump docker/setup-buildx-action from 2.9.0 to 2.9.1 (#10207) +* Bump golang.org/x/crypto from 0.10.0 to 0.11.0 (#10192) +* Bump docker/setup-buildx-action from 2.8.0 to 2.9.0 (#10191) +* Bump docker/setup-buildx-action from 2.7.0 to 2.8.0 (#10165) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-release-1.8...controller-controller-v1.9.0 diff --git a/changelog/Changelog-1.9.1.md b/changelog/Changelog-1.9.1.md new file mode 100644 index 000000000..07790098f --- /dev/null +++ b/changelog/Changelog-1.9.1.md @@ -0,0 +1,19 @@ +# Changelog + +### 1.9.1 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.9.1@sha256:605a737877de78969493a4b1213b21de4ee425d2926906857b98050f57a95b25 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.9.1@sha256:2ac744ef08850ee86ad7162451a6879f47c1a41c6a757f6b6f913c52103b8836 + +### All Changes: + +* upgrade owasp modsecurity core rule set to v3.3.5 (#10437) +* Start v1.9.1 release (#10463) +* Accept backend protocol on any case (#10461) +* Chart: Rework network policies. (#10438) + +### Dependencies updates: +* Bump google.golang.org/grpc from 1.58.0 to 1.58.1 (#10436) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.9.0...controller-controller-v1.9.1 diff --git a/changelog/Changelog-1.9.3.md b/changelog/Changelog-1.9.3.md new file mode 100644 index 000000000..45dfaa371 --- /dev/null +++ b/changelog/Changelog-1.9.3.md @@ -0,0 +1,27 @@ +# Changelog + +### 1.9.3 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.9.3@sha256:8fd21d59428507671ce0fb47f818b1d859c92d2ad07bb7c947268d433030ba98 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.9.3@sha256:df4931fd6859fbf1a71e785f02a44b2f9a16f010ae852c442e9bb779cbefdc86 + +### All Changes: + +* update nginx base, httpbun, e2e, helm webhook cert gen (#10506) +* added warning for configuration-snippets usage (#10492) +* Remove legacy GeoIP from controller (#10495) +* add upstream patch for CVE-2023-44487 (#10494) +* Revert "Remove curl from nginx base image (#10477)" (#10479) +* update error and otel to have all the arch we support (#10476) +* Remove curl from nginx base image (#10477) + +### Dependencies updates: +* Bump x/net (#10514) +* Bump curl and Go version (#10503) +* Bump google.golang.org/grpc from 1.58.2 to 1.58.3 (#10496) +* Bump github.com/prometheus/client_model (#10486) +* Bump ossf/scorecard-action from 2.2.0 to 2.3.0 (#10487) +* Bump golang.org/x/crypto from 0.13.0 to 0.14.0 (#10485) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.9.1...controller-controller-v1.9.3 diff --git a/changelog/Changelog-1.9.4.md b/changelog/Changelog-1.9.4.md new file mode 100644 index 000000000..5550a9d69 --- /dev/null +++ b/changelog/Changelog-1.9.4.md @@ -0,0 +1,13 @@ +# Changelog + +### 1.9.4 +Images: + + * registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 + * registry.k8s.io/ingress-nginx/controller-chroot:v1.9.4@sha256:5976b1067cfbca8a21d0ba53d71f83543a73316a61ea7f7e436d6cf84ddf9b26 + +### All Changes: + +* Cherry pick fcgi fix and release v1.9.4 (#10544) + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/controller-controller-v1.9.3...controller-controller-v1.9.4 diff --git a/charts/ingress-nginx/Chart.yaml b/charts/ingress-nginx/Chart.yaml index 81ebfc875..30ca8afaa 100644 --- a/charts/ingress-nginx/Chart.yaml +++ b/charts/ingress-nginx/Chart.yaml @@ -1,14 +1,11 @@ annotations: - artifacthub.io/changes: | - - "[helm] Support custom port configuration for internal service (#9846)" - - "Adding resource type to default HPA configuration to resolve issues with Terraform helm chart usage (#9803)" - - "Update Ingress-Nginx version controller-v1.7.1" + artifacthub.io/changes: |- + - "Update Ingress-Nginx version controller-v1.9.4" artifacthub.io/prerelease: "false" apiVersion: v2 -appVersion: 1.7.1 +appVersion: 1.9.4 description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer -engine: gotpl home: https://github.com/kubernetes/ingress-nginx icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png keywords: @@ -22,4 +19,4 @@ maintainers: name: ingress-nginx sources: - https://github.com/kubernetes/ingress-nginx -version: 4.6.1 +version: 4.8.3 diff --git a/charts/ingress-nginx/README.md b/charts/ingress-nginx/README.md index a48dfa15d..c7004922f 100644 --- a/charts/ingress-nginx/README.md +++ b/charts/ingress-nginx/README.md @@ -2,7 +2,7 @@ [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer -![Version: 4.6.1](https://img.shields.io/badge/Version-4.6.1-informational?style=flat-square) ![AppVersion: 1.7.1](https://img.shields.io/badge/AppVersion-1.7.1-informational?style=flat-square) +![Version: 4.8.3](https://img.shields.io/badge/Version-4.8.3-informational?style=flat-square) ![AppVersion: 1.9.4](https://img.shields.io/badge/AppVersion-1.9.4-informational?style=flat-square) To use, add `ingressClassName: nginx` spec field or the `kubernetes.io/ingress.class: nginx` annotation to your Ingress resources. @@ -143,8 +143,10 @@ controller: internal: enabled: true annotations: - # Create internal ELB - service.beta.kubernetes.io/aws-load-balancer-internal: "true" + # Create internal NLB + service.beta.kubernetes.io/aws-load-balancer-scheme: "internal" + # Create internal ELB(Deprecated) + # service.beta.kubernetes.io/aws-load-balancer-internal: "true" # Any other annotation can be declared here. ``` @@ -187,6 +189,8 @@ controller: # Any other annotation can be declared here. ``` +The load balancer annotations of more cloud service providers can be found: [Internal load balancer](https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer). + An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. @@ -236,33 +240,33 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.admissionWebhooks.certManager.enabled | bool | `false` | | | controller.admissionWebhooks.certManager.rootCert.duration | string | `""` | | | controller.admissionWebhooks.certificate | string | `"/usr/local/certificates/cert"` | | +| controller.admissionWebhooks.createSecretJob.name | string | `"create"` | | | controller.admissionWebhooks.createSecretJob.resources | object | `{}` | | -| controller.admissionWebhooks.createSecretJob.securityContext.allowPrivilegeEscalation | bool | `false` | | +| controller.admissionWebhooks.createSecretJob.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":65532,"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for secret creation containers | | controller.admissionWebhooks.enabled | bool | `true` | | | controller.admissionWebhooks.existingPsp | string | `""` | Use an existing PSP instead of creating one | | controller.admissionWebhooks.extraEnvs | list | `[]` | Additional environment variables to set | | controller.admissionWebhooks.failurePolicy | string | `"Fail"` | Admission Webhook failure policy to use | | controller.admissionWebhooks.key | string | `"/usr/local/certificates/key"` | | | controller.admissionWebhooks.labels | object | `{}` | Labels to be added to admission webhooks | +| controller.admissionWebhooks.name | string | `"admission"` | | | controller.admissionWebhooks.namespaceSelector | object | `{}` | | -| controller.admissionWebhooks.networkPolicyEnabled | bool | `false` | | | controller.admissionWebhooks.objectSelector | object | `{}` | | | controller.admissionWebhooks.patch.enabled | bool | `true` | | -| controller.admissionWebhooks.patch.image.digest | string | `"sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f"` | | +| controller.admissionWebhooks.patch.image.digest | string | `"sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80"` | | | controller.admissionWebhooks.patch.image.image | string | `"ingress-nginx/kube-webhook-certgen"` | | | controller.admissionWebhooks.patch.image.pullPolicy | string | `"IfNotPresent"` | | | controller.admissionWebhooks.patch.image.registry | string | `"registry.k8s.io"` | | -| controller.admissionWebhooks.patch.image.tag | string | `"v20230312-helm-chart-4.5.2-28-g66a760794"` | | +| controller.admissionWebhooks.patch.image.tag | string | `"v20231011-8b53cabe0"` | | | controller.admissionWebhooks.patch.labels | object | `{}` | Labels to be added to patch job resources | | controller.admissionWebhooks.patch.nodeSelector."kubernetes.io/os" | string | `"linux"` | | | controller.admissionWebhooks.patch.podAnnotations | object | `{}` | | | controller.admissionWebhooks.patch.priorityClassName | string | `""` | Provide a priority class name to the webhook patching job # | -| controller.admissionWebhooks.patch.securityContext.fsGroup | int | `2000` | | -| controller.admissionWebhooks.patch.securityContext.runAsNonRoot | bool | `true` | | -| controller.admissionWebhooks.patch.securityContext.runAsUser | int | `2000` | | +| controller.admissionWebhooks.patch.securityContext | object | `{}` | Security context for secret creation & webhook patch pods | | controller.admissionWebhooks.patch.tolerations | list | `[]` | | +| controller.admissionWebhooks.patchWebhookJob.name | string | `"patch"` | | | controller.admissionWebhooks.patchWebhookJob.resources | object | `{}` | | -| controller.admissionWebhooks.patchWebhookJob.securityContext.allowPrivilegeEscalation | bool | `false` | | +| controller.admissionWebhooks.patchWebhookJob.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":65532,"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for webhook patch containers | | controller.admissionWebhooks.port | int | `8443` | | | controller.admissionWebhooks.service.annotations | object | `{}` | | | controller.admissionWebhooks.service.externalIPs | list | `[]` | | @@ -270,10 +274,9 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.admissionWebhooks.service.servicePort | int | `443` | | | controller.admissionWebhooks.service.type | string | `"ClusterIP"` | | | controller.affinity | object | `{}` | Affinity and anti-affinity rules for server scheduling to nodes # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity # | -| controller.allowSnippetAnnotations | bool | `true` | This configuration defines if Ingress Controller should allow users to set their own *-snippet annotations, otherwise this is forbidden / dropped when users add those annotations. Global snippets in ConfigMap are still respected | +| controller.allowSnippetAnnotations | bool | `false` | This configuration defines if Ingress Controller should allow users to set their own *-snippet annotations, otherwise this is forbidden / dropped when users add those annotations. Global snippets in ConfigMap are still respected | | controller.annotations | object | `{}` | Annotations to be added to the controller Deployment or DaemonSet # | | controller.autoscaling.annotations | object | `{}` | | -| controller.autoscaling.apiVersion | string | `"autoscaling/v2"` | | | controller.autoscaling.behavior | object | `{}` | | | controller.autoscaling.enabled | bool | `false` | | | controller.autoscaling.maxReplicas | int | `11` | | @@ -286,13 +289,15 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.configMapNamespace | string | `""` | Allows customization of the configmap / nginx-configmap namespace; defaults to $(POD_NAMESPACE) | | controller.containerName | string | `"controller"` | Configures the controller container name | | controller.containerPort | object | `{"http":80,"https":443}` | Configures the ports that the nginx-controller listens on | +| controller.containerSecurityContext | object | `{}` | Security context for controller containers | | controller.customTemplate.configMapKey | string | `""` | | | controller.customTemplate.configMapName | string | `""` | | | controller.dnsConfig | object | `{}` | Optionally customize the pod dnsConfig. | | controller.dnsPolicy | string | `"ClusterFirst"` | Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. | | controller.electionID | string | `""` | Election ID to use for status update, by default it uses the controller name combined with a suffix of 'leader' | +| controller.enableAnnotationValidations | bool | `false` | | | controller.enableMimalloc | bool | `true` | Enable mimalloc as a drop-in replacement for malloc. # ref: https://github.com/microsoft/mimalloc # | -| controller.enableTopologyAwareRouting | bool | `false` | This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-aware-hints="auto" Defaults to false | +| controller.enableTopologyAwareRouting | bool | `false` | This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-mode="auto" Defaults to false | | controller.existingPsp | string | `""` | Use an existing PSP instead of creating one | | controller.extraArgs | object | `{}` | Additional command line arguments to pass to Ingress-Nginx Controller E.g. to specify the default SSL certificate you can use | | controller.extraContainers | list | `[]` | Additional containers to be added to the controller pod. See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. | @@ -303,20 +308,24 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.extraVolumes | list | `[]` | Additional volumes to the controller pod. | | controller.healthCheckHost | string | `""` | Address to bind the health check endpoint. It is better to set this option to the internal node address if the Ingress-Nginx Controller is running in the `hostNetwork: true` mode. | | controller.healthCheckPath | string | `"/healthz"` | Path of the health check endpoint. All requests received on the port defined by the healthz-port parameter are forwarded internally to this path. | +| controller.hostAliases | list | `[]` | Optionally customize the pod hostAliases. | | controller.hostNetwork | bool | `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 | | controller.hostPort.enabled | bool | `false` | Enable 'hostPort' or not | | controller.hostPort.ports.http | int | `80` | 'hostPort' http port | | controller.hostPort.ports.https | int | `443` | 'hostPort' https port | | controller.hostname | object | `{}` | Optionally customize the pod hostname. | -| controller.image.allowPrivilegeEscalation | bool | `true` | | +| controller.image.allowPrivilegeEscalation | bool | `false` | | | controller.image.chroot | bool | `false` | | -| controller.image.digest | string | `"sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407"` | | -| controller.image.digestChroot | string | `"sha256:e35d5ab487861b9d419c570e3530589229224a0762c7b4d2e2222434abb8d988"` | | +| controller.image.digest | string | `"sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3"` | | +| controller.image.digestChroot | string | `"sha256:5976b1067cfbca8a21d0ba53d71f83543a73316a61ea7f7e436d6cf84ddf9b26"` | | | controller.image.image | string | `"ingress-nginx/controller"` | | | controller.image.pullPolicy | string | `"IfNotPresent"` | | +| controller.image.readOnlyRootFilesystem | bool | `false` | | | controller.image.registry | string | `"registry.k8s.io"` | | +| controller.image.runAsNonRoot | bool | `true` | | | controller.image.runAsUser | int | `101` | | -| controller.image.tag | string | `"v1.7.1"` | | +| controller.image.seccompProfile.type | string | `"RuntimeDefault"` | | +| controller.image.tag | string | `"v1.9.4"` | | | controller.ingressClass | string | `"nginx"` | For backwards compatibility with ingress.class annotation, use ingressClass. Algorithm is as follows, first ingressClassName is considered, if not present, controller looks for ingress.class annotation | | controller.ingressClassByName | bool | `false` | Process IngressClass per name (additionally as per spec.controller). | | controller.ingressClassResource.controllerValue | string | `"k8s.io/ingress-nginx"` | Controller-value of the controller that is processing this ingressClass | @@ -353,7 +362,7 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.metrics.prometheusRule.enabled | bool | `false` | | | controller.metrics.prometheusRule.rules | list | `[]` | | | controller.metrics.service.annotations | object | `{}` | | -| controller.metrics.service.externalIPs | list | `[]` | List of IP addresses at which the stats-exporter service is available # Ref: https://kubernetes.io/docs/user-guide/services/#external-ips # | +| controller.metrics.service.externalIPs | list | `[]` | List of IP addresses at which the stats-exporter service is available # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips # | | controller.metrics.service.labels | object | `{}` | Labels to be added to the metrics service resource | | controller.metrics.service.loadBalancerSourceRanges | list | `[]` | | | controller.metrics.service.servicePort | int | `10254` | | @@ -366,19 +375,31 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.metrics.serviceMonitor.relabelings | list | `[]` | | | controller.metrics.serviceMonitor.scrapeInterval | string | `"30s"` | | | controller.metrics.serviceMonitor.targetLabels | list | `[]` | | -| controller.minAvailable | int | `1` | Define either 'minAvailable' or 'maxUnavailable', never both. | +| controller.minAvailable | int | `1` | Minimum available pods set in PodDisruptionBudget. Define either 'minAvailable' or 'maxUnavailable', never both. | | controller.minReadySeconds | int | `0` | `minReadySeconds` to avoid killing pods before we are ready # | | controller.name | string | `"controller"` | | -| controller.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for controller pod assignment # Ref: https://kubernetes.io/docs/user-guide/node-selection/ # | +| controller.networkPolicy.enabled | bool | `false` | Enable 'networkPolicy' or not | +| controller.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for controller pod assignment # Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ # | | controller.opentelemetry.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | +| controller.opentelemetry.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | +| controller.opentelemetry.containerSecurityContext.readOnlyRootFilesystem | bool | `true` | | +| controller.opentelemetry.containerSecurityContext.runAsNonRoot | bool | `true` | | +| controller.opentelemetry.containerSecurityContext.runAsUser | int | `65532` | The image's default user, inherited from its base image `cgr.dev/chainguard/static`. | +| controller.opentelemetry.containerSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | | +| controller.opentelemetry.distroless | bool | `true` | | | controller.opentelemetry.enabled | bool | `false` | | +| controller.opentelemetry.image | string | `"registry.k8s.io/ingress-nginx/opentelemetry:v20230721-3e2062ee5@sha256:13bee3f5223883d3ca62fee7309ad02d22ec00ff0d7033e3e9aca7a9f60fd472"` | | +| controller.opentelemetry.name | string | `"opentelemetry"` | | +| controller.opentelemetry.resources | object | `{}` | | | controller.opentelemetry.image.digest | string | `"sha256:40f766ac4a9832f36f217bb0e98d44c8d38faeccbfe861fbc1a76af7e9ab257f"` | | | controller.opentelemetry.image.image | string | `"ingress-nginx/opentelemetry"` | | | controller.opentelemetry.image.registry | string | `"registry.k8s.io"` | | | controller.opentelemetry.image.tag | string | `"v20230312-helm-chart-4.5.2-28-g66a760794"` | | + + | controller.podAnnotations | object | `{}` | Annotations to be added to controller pods # | | controller.podLabels | object | `{}` | Labels to add to the pod container metadata | -| controller.podSecurityContext | object | `{}` | Security Context policies for controller pods | +| controller.podSecurityContext | object | `{}` | Security context for controller pods | | controller.priorityClassName | string | `""` | | | controller.proxySetHeaders | object | `{}` | Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/custom-headers | | controller.publishService | object | `{"enabled":true,"pathOverride":""}` | Allows customization of the source of the IP address or FQDN to report in the ingress status field. By default, it reads the information provided by the service. If disable, the status field reports the IP address of the node or nodes where an ingress controller pod is running. | @@ -399,21 +420,23 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.scope.enabled | bool | `false` | Enable 'scope' or not | | controller.scope.namespace | string | `""` | Namespace to limit the controller to; defaults to $(POD_NAMESPACE) | | controller.scope.namespaceSelector | string | `""` | When scope.enabled == false, instead of watching all namespaces, we watching namespaces whose labels only match with namespaceSelector. Format like foo=bar. Defaults to empty, means watching all namespaces. | -| controller.service.annotations | object | `{}` | | +| controller.service.annotations | object | `{}` | Annotations are mandatory for the load balancer to come up. Varies with the cloud service. Values passed through helm tpl engine. | | controller.service.appProtocol | bool | `true` | If enabled is adding an appProtocol option for Kubernetes service. An appProtocol field replacing annotations that were using for setting a backend protocol. Here is an example for AWS: service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http It allows choosing the protocol for each backend specified in the Kubernetes service. See the following GitHub issue for more details about the purpose: https://github.com/kubernetes/kubernetes/issues/40244 Will be ignored for Kubernetes versions older than 1.20 # | | controller.service.enableHttp | bool | `true` | | | controller.service.enableHttps | bool | `true` | | | controller.service.enabled | bool | `true` | | | controller.service.external.enabled | bool | `true` | | -| controller.service.externalIPs | list | `[]` | List of IP addresses at which the controller services are available # Ref: https://kubernetes.io/docs/user-guide/services/#external-ips # | -| controller.service.internal.annotations | object | `{}` | Annotations are mandatory for the load balancer to come up. Varies with the cloud service. | +| controller.service.externalIPs | list | `[]` | List of IP addresses at which the controller services are available # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips # | +| controller.service.internal.annotations | object | `{}` | Annotations are mandatory for the load balancer to come up. Varies with the cloud service. Values passed through helm tpl engine. | | controller.service.internal.enabled | bool | `false` | Enables an additional internal load balancer (besides the external one). | +| controller.service.internal.loadBalancerIP | string | `""` | Used by cloud providers to connect the resulting internal LoadBalancer to a pre-existing static IP. Make sure to add to the service the needed annotation to specify the subnet which the static IP belongs to. For instance, `networking.gke.io/internal-load-balancer-subnet` for GCP and `service.beta.kubernetes.io/aws-load-balancer-subnets` for AWS. | | controller.service.internal.loadBalancerSourceRanges | list | `[]` | Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. | | controller.service.internal.ports | object | `{}` | Custom port mapping for internal service | | controller.service.internal.targetPorts | object | `{}` | Custom target port mapping for internal service | | controller.service.ipFamilies | list | `["IPv4"]` | List of IP families (e.g. IPv4, IPv6) assigned to the service. This field is usually assigned automatically based on cluster configuration and the ipFamilyPolicy field. # Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/ | | controller.service.ipFamilyPolicy | string | `"SingleStack"` | Represents the dual-stack-ness requested or required by this Service. Possible values are SingleStack, PreferDualStack or RequireDualStack. The ipFamilies and clusterIPs fields depend on the value of this field. # Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/ | | controller.service.labels | object | `{}` | | +| controller.service.loadBalancerClass | string | `""` | Used by cloud providers to select a load balancer implementation other than the cloud provider default. https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-class | | controller.service.loadBalancerIP | string | `""` | Used by cloud providers to connect the resulting `LoadBalancer` to a pre-existing static IP according to https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer | | controller.service.loadBalancerSourceRanges | list | `[]` | | | controller.service.nodePorts.http | string | `""` | | @@ -426,7 +449,7 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.service.targetPorts.https | string | `"https"` | | | controller.service.type | string | `"LoadBalancer"` | | | controller.shareProcessNamespace | bool | `false` | | -| controller.sysctls | object | `{}` | See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls | +| controller.sysctls | object | `{}` | sysctls for controller pods # Ref: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ | | controller.tcp.annotations | object | `{}` | Annotations to be added to the tcp config configmap | | controller.tcp.configMapNamespace | string | `""` | Allows customization of the tcp-services-configmap; defaults to $(POD_NAMESPACE) | | controller.terminationGracePeriodSeconds | int | `300` | `terminationGracePeriodSeconds` to avoid killing pods before we are ready # wait up to five minutes for the drain of connections # | @@ -438,13 +461,12 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | controller.watchIngressWithoutClass | bool | `false` | Process Ingress objects without ingressClass annotation/ingressClassName field Overrides value for --watch-ingress-without-class flag of the controller binary Defaults to false | | defaultBackend.affinity | object | `{}` | | | defaultBackend.autoscaling.annotations | object | `{}` | | -| defaultBackend.autoscaling.apiVersion | string | `"autoscaling/v2"` | | | defaultBackend.autoscaling.enabled | bool | `false` | | | defaultBackend.autoscaling.maxReplicas | int | `2` | | | defaultBackend.autoscaling.minReplicas | int | `1` | | | defaultBackend.autoscaling.targetCPUUtilizationPercentage | int | `50` | | | defaultBackend.autoscaling.targetMemoryUtilizationPercentage | int | `50` | | -| defaultBackend.containerSecurityContext | object | `{}` | Security Context policies for controller main container. See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls # | +| defaultBackend.containerSecurityContext | object | `{}` | Security context for default backend containers | | defaultBackend.enabled | bool | `false` | | | defaultBackend.existingPsp | string | `""` | Use an existing PSP instead of creating one | | defaultBackend.extraArgs | object | `{}` | | @@ -458,6 +480,7 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | defaultBackend.image.registry | string | `"registry.k8s.io"` | | | defaultBackend.image.runAsNonRoot | bool | `true` | | | defaultBackend.image.runAsUser | int | `65534` | | +| defaultBackend.image.seccompProfile.type | string | `"RuntimeDefault"` | | | defaultBackend.image.tag | string | `"1.5"` | | | defaultBackend.labels | object | `{}` | Labels to be added to the default backend resources | | defaultBackend.livenessProbe.failureThreshold | int | `3` | | @@ -468,10 +491,11 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | defaultBackend.minAvailable | int | `1` | | | defaultBackend.minReadySeconds | int | `0` | `minReadySeconds` to avoid killing pods before we are ready # | | defaultBackend.name | string | `"defaultbackend"` | | -| defaultBackend.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for default backend pod assignment # Ref: https://kubernetes.io/docs/user-guide/node-selection/ # | +| defaultBackend.networkPolicy.enabled | bool | `false` | Enable 'networkPolicy' or not | +| defaultBackend.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for default backend pod assignment # Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ # | | defaultBackend.podAnnotations | object | `{}` | Annotations to be added to default backend pods # | | defaultBackend.podLabels | object | `{}` | Labels to add to the pod container metadata | -| defaultBackend.podSecurityContext | object | `{}` | Security Context policies for controller pods See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls # | +| defaultBackend.podSecurityContext | object | `{}` | Security context for default backend pods | | defaultBackend.port | int | `8080` | | | defaultBackend.priorityClassName | string | `""` | | | defaultBackend.readinessProbe.failureThreshold | int | `6` | | @@ -482,7 +506,7 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | defaultBackend.replicaCount | int | `1` | | | defaultBackend.resources | object | `{}` | | | defaultBackend.service.annotations | object | `{}` | | -| defaultBackend.service.externalIPs | list | `[]` | List of IP addresses at which the default backend service is available # Ref: https://kubernetes.io/docs/user-guide/services/#external-ips # | +| defaultBackend.service.externalIPs | list | `[]` | List of IP addresses at which the default backend service is available # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips # | | defaultBackend.service.loadBalancerSourceRanges | list | `[]` | | | defaultBackend.service.servicePort | int | `80` | | | defaultBackend.service.type | string | `"ClusterIP"` | | @@ -493,6 +517,7 @@ As of version `1.26.0` of this chart, by simply not providing any clusterIP valu | defaultBackend.updateStrategy | object | `{}` | The update strategy to apply to the Deployment or DaemonSet # | | dhParam | string | `""` | A base64-encoded Diffie-Hellman parameter. This can be generated with: `openssl dhparam 4096 2> /dev/null | base64` # Ref: https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/ssl-dh-param | | imagePullSecrets | list | `[]` | Optional array of imagePullSecrets containing private registry credentials # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | +| namespaceOverride | string | `""` | Override the deployment namespace; defaults to .Release.Namespace | | podSecurityPolicy.enabled | bool | `false` | | | portNamePrefix | string | `""` | Prefix for TCP and UDP ports names in ingress controller service # Some cloud providers, like Yandex Cloud may have a requirements for a port name regex to support cloud load balancer integration | | rbac.create | bool | `true` | | diff --git a/charts/ingress-nginx/README.md.gotmpl b/charts/ingress-nginx/README.md.gotmpl index 4a35a40b3..17b029bbf 100644 --- a/charts/ingress-nginx/README.md.gotmpl +++ b/charts/ingress-nginx/README.md.gotmpl @@ -140,8 +140,10 @@ controller: internal: enabled: true annotations: - # Create internal ELB - service.beta.kubernetes.io/aws-load-balancer-internal: "true" + # Create internal NLB + service.beta.kubernetes.io/aws-load-balancer-scheme: "internal" + # Create internal ELB(Deprecated) + # service.beta.kubernetes.io/aws-load-balancer-internal: "true" # Any other annotation can be declared here. ``` @@ -184,6 +186,8 @@ controller: # Any other annotation can be declared here. ``` +The load balancer annotations of more cloud service providers can be found: [Internal load balancer](https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer). + An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. diff --git a/charts/ingress-nginx/changelog/Changelog-4.7.0.md b/charts/ingress-nginx/changelog/Changelog-4.7.0.md new file mode 100644 index 000000000..7399da777 --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.7.0.md @@ -0,0 +1,14 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.7.0 + +* helm: Fix opentelemetry module installation for daemonset (#9792) +* Update charts/* to keep project name display aligned (#9931) +* HPA: Use capabilites & align manifests. (#9521) +* PodDisruptionBudget spec logic update (#9904) +* add option for annotations in PodDisruptionBudget (#9843) +* Update Ingress-Nginx version controller-v1.8.0 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.6.1...helm-chart-4.7.0 diff --git a/charts/ingress-nginx/changelog/Changelog-4.7.1.md b/charts/ingress-nginx/changelog/Changelog-4.7.1.md new file mode 100644 index 000000000..4d69a7117 --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.7.1.md @@ -0,0 +1,12 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.7.1 + +* Added a doc line to the missing helm value service.internal.loadBalancerIP (#9406) +* feat(helm): Add loadBalancerClass (#9562) +* added helmshowvalues example (#10019) +* Update Ingress-Nginx version controller-v1.8.1 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.7.0...helm-chart-4.7.1 diff --git a/charts/ingress-nginx/changelog/Changelog-4.7.2.md b/charts/ingress-nginx/changelog/Changelog-4.7.2.md new file mode 100644 index 000000000..57b17b982 --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.7.2.md @@ -0,0 +1,9 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.7.2 + +* Update Ingress-Nginx version controller-v1.8.2 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.7.1...helm-chart-4.7.2 diff --git a/charts/ingress-nginx/changelog/Changelog-4.8.0-beta.0.md b/charts/ingress-nginx/changelog/Changelog-4.8.0-beta.0.md new file mode 100644 index 000000000..9072a75b4 --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.8.0-beta.0.md @@ -0,0 +1,13 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.8.0-beta.0 + +* ci(helm): fix Helm Chart release action 422 error (#10237) +* helm: Use .Release.Namespace as default for ServiceMonitor namespace (#10249) +* [helm] configure allow to configure hostAliases (#10180) +* [helm] pass service annotations through helm tpl engine (#10084) +* Update Ingress-Nginx version controller-v1.9.0-beta.0 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.7.2...helm-chart-4.8.0-beta.0 diff --git a/charts/ingress-nginx/changelog/Changelog-4.8.0.md b/charts/ingress-nginx/changelog/Changelog-4.8.0.md new file mode 100644 index 000000000..af8f1241f --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.8.0.md @@ -0,0 +1,13 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.8.0 + +* ci(helm): fix Helm Chart release action 422 error (#10237) +* helm: Use .Release.Namespace as default for ServiceMonitor namespace (#10249) +* [helm] configure allow to configure hostAliases (#10180) +* [helm] pass service annotations through helm tpl engine (#10084) +* Update Ingress-Nginx version controller-v1.9.0 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.7.2...helm-chart-4.8.0 diff --git a/charts/ingress-nginx/changelog/Changelog-4.8.1.md b/charts/ingress-nginx/changelog/Changelog-4.8.1.md new file mode 100644 index 000000000..53a4493de --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.8.1.md @@ -0,0 +1,9 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.8.1 + +* Update Ingress-Nginx version controller-v1.9.1 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.8.0...helm-chart-4.8.1 diff --git a/charts/ingress-nginx/changelog/Changelog-4.8.2.md b/charts/ingress-nginx/changelog/Changelog-4.8.2.md new file mode 100644 index 000000000..3fbb19f53 --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.8.2.md @@ -0,0 +1,10 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.8.2 + +* - "update nginx base, httpbun, e2e, helm webhook cert gen (#10506)" +* - "Update Ingress-Nginx version controller-v1.9.3" + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.8.1...helm-chart-4.8.2 diff --git a/charts/ingress-nginx/changelog/Changelog-4.8.3.md b/charts/ingress-nginx/changelog/Changelog-4.8.3.md new file mode 100644 index 000000000..ca1815bbb --- /dev/null +++ b/charts/ingress-nginx/changelog/Changelog-4.8.3.md @@ -0,0 +1,8 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.8.3 +* Update Ingress-Nginx version controller-v1.9.4 + +**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.8.2...helm-chart-4.8.3 diff --git a/charts/ingress-nginx/templates/NOTES.txt b/charts/ingress-nginx/templates/NOTES.txt index 9fe35c785..f4923007e 100644 --- a/charts/ingress-nginx/templates/NOTES.txt +++ b/charts/ingress-nginx/templates/NOTES.txt @@ -6,24 +6,24 @@ Get the application URL by running these commands: {{- if (not (empty .Values.controller.service.nodePorts.http)) }} export HTTP_NODE_PORT={{ .Values.controller.service.nodePorts.http }} {{- else }} - export HTTP_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[0].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) + export HTTP_NODE_PORT=$(kubectl get service --namespace {{ include "ingress-nginx.namespace" . }} {{ include "ingress-nginx.controller.fullname" . }} --output jsonpath="{.spec.ports[0].nodePort}") {{- end }} {{- if (not (empty .Values.controller.service.nodePorts.https)) }} export HTTPS_NODE_PORT={{ .Values.controller.service.nodePorts.https }} {{- else }} - export HTTPS_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[1].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) + export HTTPS_NODE_PORT=$(kubectl get service --namespace {{ include "ingress-nginx.namespace" . }} {{ include "ingress-nginx.controller.fullname" . }} --output jsonpath="{.spec.ports[1].nodePort}") {{- end }} - export NODE_IP=$(kubectl --namespace {{ .Release.Namespace }} get nodes -o jsonpath="{.items[0].status.addresses[1].address}") + export NODE_IP="$(kubectl get nodes --output jsonpath="{.items[0].status.addresses[1].address}")" - echo "Visit http://$NODE_IP:$HTTP_NODE_PORT to access your application via HTTP." - echo "Visit https://$NODE_IP:$HTTPS_NODE_PORT to access your application via HTTPS." + echo "Visit http://${NODE_IP}:${HTTP_NODE_PORT} to access your application via HTTP." + echo "Visit https://${NODE_IP}:${HTTPS_NODE_PORT} to access your application via HTTPS." {{- else if contains "LoadBalancer" .Values.controller.service.type }} -It may take a few minutes for the LoadBalancer IP to be available. -You can watch the status by running 'kubectl --namespace {{ .Release.Namespace }} get services -o wide -w {{ include "ingress-nginx.controller.fullname" . }}' +It may take a few minutes for the load balancer IP to be available. +You can watch the status by running 'kubectl get service --namespace {{ include "ingress-nginx.namespace" . }} {{ include "ingress-nginx.controller.fullname" . }} --output wide --watch' {{- else if contains "ClusterIP" .Values.controller.service.type }} Get the application URL by running these commands: - export POD_NAME=$(kubectl --namespace {{ .Release.Namespace }} get pods -o jsonpath="{.items[0].metadata.name}" -l "app={{ template "ingress-nginx.name" . }},component={{ .Values.controller.name }},release={{ .Release.Name }}") - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 + export POD_NAME="$(kubectl get pods --namespace {{ include "ingress-nginx.namespace" . }} --selector app.kubernetes.io/name={{ include "ingress-nginx.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=controller --output jsonpath="{.items[0].metadata.name}")" + kubectl port-forward --namespace {{ include "ingress-nginx.namespace" . }} "${POD_NAME}" 8080:80 echo "Visit http://127.0.0.1:8080 to access your application." {{- end }} diff --git a/charts/ingress-nginx/templates/_helpers.tpl b/charts/ingress-nginx/templates/_helpers.tpl index 7db5b2ca8..c936dab79 100644 --- a/charts/ingress-nginx/templates/_helpers.tpl +++ b/charts/ingress-nginx/templates/_helpers.tpl @@ -30,25 +30,40 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this {{- end -}} {{- end -}} +{{/* +Expand the namespace of the release. +Allows overriding it for multi-namespace deployments in combined charts. +*/}} +{{- define "ingress-nginx.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} {{/* -Container SecurityContext. +Controller container security context. */}} -{{- define "controller.containerSecurityContext" -}} +{{- define "ingress-nginx.controller.containerSecurityContext" -}} {{- if .Values.controller.containerSecurityContext -}} {{- toYaml .Values.controller.containerSecurityContext -}} {{- else -}} +runAsNonRoot: {{ .Values.controller.image.runAsNonRoot }} +runAsUser: {{ .Values.controller.image.runAsUser }} +allowPrivilegeEscalation: {{ or .Values.controller.image.allowPrivilegeEscalation .Values.controller.image.chroot }} +{{- if .Values.controller.image.seccompProfile }} +seccompProfile: {{ toYaml .Values.controller.image.seccompProfile | nindent 2 }} +{{- end }} capabilities: drop: - ALL add: - NET_BIND_SERVICE {{- if .Values.controller.image.chroot }} + {{- if .Values.controller.image.seccompProfile }} + - SYS_ADMIN + {{- end }} - SYS_CHROOT {{- end }} -runAsUser: {{ .Values.controller.image.runAsUser }} -allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} -{{- end }} +readOnlyRootFilesystem: {{ .Values.controller.image.readOnlyRootFilesystem }} +{{- end -}} {{- end -}} {{/* @@ -102,7 +117,6 @@ By convention this will simply use the / to match th service generated. Users can provide an override for an explicit service they want bound via `.Values.controller.publishService.pathOverride` - */}} {{- define "ingress-nginx.controller.publishServicePath" -}} {{- $defServiceName := printf "%s/%s" "$(POD_NAMESPACE)" (include "ingress-nginx.controller.fullname" .) -}} @@ -110,14 +124,6 @@ Users can provide an override for an explicit service they want bound via `.Valu {{- print $servicePath | trimSuffix "-" -}} {{- end -}} -{{/* -Create a default fully qualified default backend name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "ingress-nginx.defaultBackend.fullname" -}} -{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.defaultBackend.name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - {{/* Common labels */}} @@ -153,6 +159,38 @@ Create the name of the controller service account to use {{- end -}} {{- end -}} +{{/* +Create a default fully qualified admission webhook name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.admissionWebhooks.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.controller.admissionWebhooks.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified admission webhook secret creation job name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.admissionWebhooks.createSecretJob.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.admissionWebhooks.fullname" .) .Values.controller.admissionWebhooks.createSecretJob.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified admission webhook patch job name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.admissionWebhooks.patchWebhookJob.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.admissionWebhooks.fullname" .) .Values.controller.admissionWebhooks.patchWebhookJob.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified default backend name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.defaultBackend.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.defaultBackend.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + {{/* Create the name of the backend service account to use - only used when podsecuritypolicy is also enabled */}} @@ -164,6 +202,26 @@ Create the name of the backend service account to use - only used when podsecuri {{- end -}} {{- end -}} +{{/* +Default backend container security context. +*/}} +{{- define "ingress-nginx.defaultBackend.containerSecurityContext" -}} +{{- if .Values.defaultBackend.containerSecurityContext -}} +{{- toYaml .Values.defaultBackend.containerSecurityContext -}} +{{- else -}} +runAsNonRoot: {{ .Values.defaultBackend.image.runAsNonRoot }} +runAsUser: {{ .Values.defaultBackend.image.runAsUser }} +allowPrivilegeEscalation: {{ .Values.defaultBackend.image.allowPrivilegeEscalation }} +{{- if .Values.defaultBackend.image.seccompProfile }} +seccompProfile: {{ toYaml .Values.defaultBackend.image.seccompProfile | nindent 2 }} +{{- end }} +capabilities: + drop: + - ALL +readOnlyRootFilesystem: {{ .Values.defaultBackend.image.readOnlyRootFilesystem }} +{{- end -}} +{{- end -}} + {{/* Return the appropriate apiGroup for PodSecurityPolicy. */}} @@ -198,15 +256,23 @@ IngressClass parameters. Extra modules. */}} {{- define "extraModules" -}} - - name: {{ .name }} image: {{ .image }} - command: ['sh', '-c', '/usr/local/bin/init_module.sh'] - {{- if (.containerSecurityContext) }} - securityContext: {{ .containerSecurityContext | toYaml | nindent 4 }} + command: + {{- if .distroless }} + - /init_module + {{- else }} + - sh + - -c + - /usr/local/bin/init_module.sh + {{- end }} + {{- if .containerSecurityContext }} + securityContext: {{ toYaml .containerSecurityContext | nindent 4 }} + {{- end }} + {{- if .resources }} + resources: {{ toYaml .resources | nindent 4 }} {{- end }} volumeMounts: - - name: {{ toYaml "modules"}} - mountPath: {{ toYaml "/modules_mount"}} - + - name: modules + mountPath: /modules_mount {{- end -}} diff --git a/charts/ingress-nginx/templates/_params.tpl b/charts/ingress-nginx/templates/_params.tpl index a1aef01ae..47d024e85 100644 --- a/charts/ingress-nginx/templates/_params.tpl +++ b/charts/ingress-nginx/templates/_params.tpl @@ -1,5 +1,8 @@ {{- define "ingress-nginx.params" -}} - /nginx-ingress-controller +{{- if .Values.controller.enableAnnotationValidations }} +- --enable-annotation-validation=true +{{- end }} {{- if .Values.defaultBackend.enabled }} - --default-backend-service=$(POD_NAMESPACE)/{{ include "ingress-nginx.defaultBackend.fullname" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml b/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml index 55fab471c..db2946c3d 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml @@ -6,7 +6,7 @@ apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: {{ include "ingress-nginx.fullname" . }}-self-signed-issuer - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: selfSigned: {} --- @@ -15,7 +15,7 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: {{ include "ingress-nginx.fullname" . }}-root-cert - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: secretName: {{ include "ingress-nginx.fullname" . }}-root-cert duration: {{ .Values.controller.admissionWebhooks.certManager.rootCert.duration | default "43800h0m0s" | quote }} @@ -32,7 +32,7 @@ apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: {{ include "ingress-nginx.fullname" . }}-root-issuer - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: ca: secretName: {{ include "ingress-nginx.fullname" . }}-root-cert @@ -42,10 +42,10 @@ spec: apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: - secretName: {{ include "ingress-nginx.fullname" . }}-admission + secretName: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} duration: {{ .Values.controller.admissionWebhooks.certManager.admissionCert.duration | default "8760h0m0s" | quote }} issuerRef: {{- if .Values.controller.admissionWebhooks.certManager.issuerRef }} @@ -55,8 +55,8 @@ spec: {{- end }} dnsNames: - {{ include "ingress-nginx.controller.fullname" . }}-admission - - {{ include "ingress-nginx.controller.fullname" . }}-admission.{{ .Release.Namespace }} - - {{ include "ingress-nginx.controller.fullname" . }}-admission.{{ .Release.Namespace }}.svc + - {{ include "ingress-nginx.controller.fullname" . }}-admission.{{ include "ingress-nginx.namespace" . }} + - {{ include "ingress-nginx.controller.fullname" . }}-admission.{{ include "ingress-nginx.namespace" . }}.svc subject: organizations: - ingress-nginx-admission diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml index f9ec70974..8271dc404 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -21,14 +21,13 @@ rules: - get - update {{- if .Values.podSecurityPolicy.enabled }} - - apiGroups: ['extensions'] - resources: ['podsecuritypolicies'] - verbs: ['use'] - resourceNames: + - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] + resources: ['podsecuritypolicies'] + verbs: ['use'] {{- with .Values.controller.admissionWebhooks.existingPsp }} - - {{ . }} + resourceNames: [{{ . }}] {{- else }} - - {{ include "ingress-nginx.fullname" . }}-admission + resourceNames: [{{ include "ingress-nginx.admissionWebhooks.fullname" . }}] {{- end }} {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml index 871953261..3fe842d1f 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -15,9 +15,9 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} subjects: - kind: ServiceAccount - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace | quote }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml index d93433ecd..c29083f2b 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -2,8 +2,8 @@ apiVersion: batch/v1 kind: Job metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-create - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.createSecretJob.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -23,7 +23,7 @@ spec: {{- end }} template: metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-create + name: {{ include "ingress-nginx.admissionWebhooks.createSecretJob.fullname" . }} {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} {{- end }} @@ -50,7 +50,7 @@ spec: - create - --host={{ include "ingress-nginx.controller.fullname" . }}-admission,{{ include "ingress-nginx.controller.fullname" . }}-admission.$(POD_NAMESPACE).svc - --namespace=$(POD_NAMESPACE) - - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + - --secret-name={{ include "ingress-nginx.admissionWebhooks.fullname" . }} env: - name: POD_NAMESPACE valueFrom: @@ -66,15 +66,14 @@ spec: resources: {{ toYaml .Values.controller.admissionWebhooks.createSecretJob.resources | nindent 12 }} {{- end }} restartPolicy: OnFailure - serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + serviceAccountName: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} {{- end }} {{- if .Values.controller.admissionWebhooks.patch.tolerations }} tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} {{- end }} - {{- if .Values.controller.admissionWebhooks.patch.securityContext }} - securityContext: - {{- toYaml .Values.controller.admissionWebhooks.patch.securityContext | nindent 8 }} - {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.securityContext }} + securityContext: {{ toYaml .Values.controller.admissionWebhooks.patch.securityContext | nindent 8 }} + {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml index 0fa3ff9a2..cbc4a7619 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -2,8 +2,8 @@ apiVersion: batch/v1 kind: Job metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-patch - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.patchWebhookJob.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -23,7 +23,7 @@ spec: {{- end }} template: metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-patch + name: {{ include "ingress-nginx.admissionWebhooks.patchWebhookJob.fullname" . }} {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} {{- end }} @@ -48,10 +48,10 @@ spec: imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} args: - patch - - --webhook-name={{ include "ingress-nginx.fullname" . }}-admission + - --webhook-name={{ include "ingress-nginx.admissionWebhooks.fullname" . }} - --namespace=$(POD_NAMESPACE) - --patch-mutating=false - - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + - --secret-name={{ include "ingress-nginx.admissionWebhooks.fullname" . }} - --patch-failure-policy={{ .Values.controller.admissionWebhooks.failurePolicy }} env: - name: POD_NAMESPACE @@ -68,15 +68,14 @@ spec: resources: {{ toYaml .Values.controller.admissionWebhooks.patchWebhookJob.resources | nindent 12 }} {{- end }} restartPolicy: OnFailure - serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + serviceAccountName: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} {{- end }} {{- if .Values.controller.admissionWebhooks.patch.tolerations }} tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} {{- end }} - {{- if .Values.controller.admissionWebhooks.patch.securityContext }} - securityContext: - {{- toYaml .Values.controller.admissionWebhooks.patch.securityContext | nindent 8 }} - {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.securityContext }} + securityContext: {{ toYaml .Values.controller.admissionWebhooks.patch.securityContext | nindent 8 }} + {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/networkpolicy.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/networkpolicy.yaml index 08b32257c..142e56aeb 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/networkpolicy.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/networkpolicy.yaml @@ -1,9 +1,9 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.networkPolicyEnabled }} +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled (not .Values.controller.admissionWebhooks.certManager.enabled) -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -16,11 +16,11 @@ metadata: spec: podSelector: matchLabels: - {{- include "ingress-nginx.labels" . | nindent 6 }} + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: admission-webhook policyTypes: - - Ingress - - Egress + - Ingress + - Egress egress: - - {} + - {} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml index e19c95572..8e5dc72ac 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml @@ -1,12 +1,13 @@ {{- if (semverCompare "<1.25.0-0" .Capabilities.KubeVersion.Version) }} -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled .Values.podSecurityPolicy.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}} +{{- if and .Values.podSecurityPolicy.enabled .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}} apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + seccomp.security.alpha.kubernetes.io/allowedProfileNames: "*" labels: {{- include "ingress-nginx.labels" . | nindent 4 }} app.kubernetes.io/component: admission-webhook @@ -14,28 +15,38 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - allowPrivilegeEscalation: false + privileged: false + hostPID: false + hostIPC: false + hostNetwork: false + volumes: + - configMap + - downwardAPI + - emptyDir + - secret + - projected fsGroup: - ranges: - - max: 65535 - min: 1 rule: MustRunAs - requiredDropCapabilities: - - ALL + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: true runAsUser: rule: MustRunAsNonRoot + runAsGroup: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + supplementalGroups: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL seLinux: rule: RunAsAny - supplementalGroups: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml index ea7c20818..5b05d9b00 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml @@ -2,8 +2,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml index 60c3f4ff0..48a175566 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml @@ -2,8 +2,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded @@ -16,9 +16,9 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} subjects: - kind: ServiceAccount - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace | quote }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml index 00be54ec5..91bbf22bb 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml @@ -2,8 +2,8 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} annotations: "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded diff --git a/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml b/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml index f27244dc9..4cd36a62e 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml @@ -6,8 +6,8 @@ kind: ValidatingWebhookConfiguration metadata: annotations: {{- if .Values.controller.admissionWebhooks.certManager.enabled }} - certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s-admission" .Release.Namespace (include "ingress-nginx.fullname" .) | quote }} - cert-manager.io/inject-ca-from: {{ printf "%s/%s-admission" .Release.Namespace (include "ingress-nginx.fullname" .) | quote }} + certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s" (include "ingress-nginx.namespace" .) (include "ingress-nginx.admissionWebhooks.fullname" .) | quote }} + cert-manager.io/inject-ca-from: {{ printf "%s/%s" (include "ingress-nginx.namespace" .) (include "ingress-nginx.admissionWebhooks.fullname" .) | quote }} {{- end }} {{- if .Values.controller.admissionWebhooks.annotations }} {{- toYaml .Values.controller.admissionWebhooks.annotations | nindent 4 }} @@ -18,7 +18,7 @@ metadata: {{- with .Values.controller.admissionWebhooks.labels }} {{- toYaml . | nindent 4 }} {{- end }} - name: {{ include "ingress-nginx.fullname" . }}-admission + name: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} webhooks: - name: validate.nginx.ingress.kubernetes.io matchPolicy: Equivalent @@ -38,8 +38,8 @@ webhooks: - v1 clientConfig: service: - namespace: {{ .Release.Namespace | quote }} name: {{ include "ingress-nginx.controller.fullname" . }}-admission + namespace: {{ include "ingress-nginx.namespace" . }} path: /networking/v1/ingresses {{- if .Values.controller.admissionWebhooks.timeoutSeconds }} timeoutSeconds: {{ .Values.controller.admissionWebhooks.timeoutSeconds }} diff --git a/charts/ingress-nginx/templates/clusterrolebinding.yaml b/charts/ingress-nginx/templates/clusterrolebinding.yaml index acbbd8b10..8f91aac80 100644 --- a/charts/ingress-nginx/templates/clusterrolebinding.yaml +++ b/charts/ingress-nginx/templates/clusterrolebinding.yaml @@ -15,5 +15,5 @@ roleRef: subjects: - kind: ServiceAccount name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml b/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml index dfd49a126..4e4bd1310 100644 --- a/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml +++ b/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml @@ -9,6 +9,6 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-custom-add-headers - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: {{ toYaml .Values.controller.addHeaders | nindent 2 }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml b/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml index 38feb721f..0a22600db 100644 --- a/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml +++ b/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml @@ -9,6 +9,6 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-custom-proxy-headers - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: {{ toYaml .Values.controller.proxySetHeaders | nindent 2 }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-configmap-tcp.yaml b/charts/ingress-nginx/templates/controller-configmap-tcp.yaml index 0f6088ea9..131a9ad51 100644 --- a/charts/ingress-nginx/templates/controller-configmap-tcp.yaml +++ b/charts/ingress-nginx/templates/controller-configmap-tcp.yaml @@ -12,6 +12,6 @@ metadata: annotations: {{ toYaml .Values.controller.tcp.annotations | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-tcp - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: {{ tpl (toYaml .Values.tcp) . | nindent 2 }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-configmap-udp.yaml b/charts/ingress-nginx/templates/controller-configmap-udp.yaml index 3772ec514..7137da9ad 100644 --- a/charts/ingress-nginx/templates/controller-configmap-udp.yaml +++ b/charts/ingress-nginx/templates/controller-configmap-udp.yaml @@ -12,6 +12,6 @@ metadata: annotations: {{ toYaml .Values.controller.udp.annotations | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-udp - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: {{ tpl (toYaml .Values.udp) . | nindent 2 }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-configmap.yaml b/charts/ingress-nginx/templates/controller-configmap.yaml index 9ec2b8369..662a16204 100644 --- a/charts/ingress-nginx/templates/controller-configmap.yaml +++ b/charts/ingress-nginx/templates/controller-configmap.yaml @@ -11,17 +11,17 @@ metadata: annotations: {{ toYaml .Values.controller.configAnnotations | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: allow-snippet-annotations: "{{ .Values.controller.allowSnippetAnnotations }}" {{- if .Values.controller.addHeaders }} - add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers + add-headers: {{ include "ingress-nginx.namespace" . }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers {{- end }} {{- if .Values.controller.proxySetHeaders }} - proxy-set-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-proxy-headers + proxy-set-headers: {{ include "ingress-nginx.namespace" . }}/{{ include "ingress-nginx.fullname" . }}-custom-proxy-headers {{- end }} {{- if .Values.dhParam }} - ssl-dh-param: {{ .Release.Namespace }}/{{ include "ingress-nginx.controller.fullname" . }} + ssl-dh-param: {{ include "ingress-nginx.namespace" . }}/{{ include "ingress-nginx.controller.fullname" . }} {{- end }} {{- range $key, $value := .Values.controller.config }} {{- $key | nindent 2 }}: {{ $value | quote }} diff --git a/charts/ingress-nginx/templates/controller-daemonset.yaml b/charts/ingress-nginx/templates/controller-daemonset.yaml index 94cb52a1f..5ac0f5dd0 100644 --- a/charts/ingress-nginx/templates/controller-daemonset.yaml +++ b/charts/ingress-nginx/templates/controller-daemonset.yaml @@ -1,4 +1,4 @@ -{{- if or (eq .Values.controller.kind "DaemonSet") (eq .Values.controller.kind "Both") -}} +{{- if eq .Values.controller.kind "DaemonSet" -}} {{- include "isControllerTagValid" . -}} apiVersion: apps/v1 kind: DaemonSet @@ -10,7 +10,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- if .Values.controller.annotations }} annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} {{- end }} @@ -45,6 +45,9 @@ spec: {{- if .Values.controller.dnsConfig }} dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} {{- end }} + {{- if .Values.controller.hostAliases }} + hostAliases: {{ tpl (toYaml .Values.controller.hostAliases) $ | nindent 8 }} + {{- end }} {{- if .Values.controller.hostname }} hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} {{- end }} @@ -53,20 +56,20 @@ spec: imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} {{- end }} {{- if .Values.controller.priorityClassName }} - priorityClassName: {{ .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName | quote }} {{- end }} {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} securityContext: - {{- end }} - {{- if .Values.controller.podSecurityContext }} + {{- if .Values.controller.podSecurityContext }} {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} - {{- end }} - {{- if .Values.controller.sysctls }} + {{- end }} + {{- if .Values.controller.sysctls }} sysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - name: {{ $sysctl | quote }} - value: {{ $value | quote }} - {{- end }} + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} {{- end }} {{- if .Values.controller.shareProcessNamespace }} shareProcessNamespace: {{ .Values.controller.shareProcessNamespace }} @@ -80,9 +83,8 @@ spec: {{- if .Values.controller.lifecycle }} lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} {{- end }} - args: - {{- include "ingress-nginx.params" . | nindent 12 }} - securityContext: {{ include "controller.containerSecurityContext" . | nindent 12 }} + args: {{ include "ingress-nginx.params" . | nindent 12 }} + securityContext: {{ include "ingress-nginx.controller.containerSecurityContext" . | nindent 12 }} env: - name: POD_NAME valueFrom: @@ -143,11 +145,15 @@ spec: hostPort: {{ $key }} {{- end }} {{- end }} - {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraModules) }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraModules .Values.controller.opentelemetry.enabled) }} volumeMounts: - {{- if .Values.controller.extraModules }} + {{- if (or .Values.controller.extraModules .Values.controller.opentelemetry.enabled) }} - name: modules + {{- if .Values.controller.image.chroot }} + mountPath: /chroot/modules_mount + {{- else }} mountPath: /modules_mount + {{- end }} {{- end }} {{- if .Values.controller.customTemplate.configMapName }} - mountPath: /etc/nginx/template @@ -167,26 +173,29 @@ spec: resources: {{ toYaml .Values.controller.resources | nindent 12 }} {{- end }} {{- if .Values.controller.extraContainers }} - {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- toYaml .Values.controller.extraContainers | nindent 8 }} {{- end }} - - - {{- if (or .Values.controller.extraInitContainers .Values.controller.extraModules) }} + {{- if (or .Values.controller.extraInitContainers .Values.controller.extraModules .Values.controller.opentelemetry.enabled) }} initContainers: {{- if .Values.controller.extraInitContainers }} - {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- toYaml .Values.controller.extraInitContainers | nindent 8 }} {{- end }} {{- if .Values.controller.extraModules }} {{- range .Values.controller.extraModules }} - {{ $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} -{{ include "extraModules" (dict "name" .name "image" .image "containerSecurityContext" $containerSecurityContext) | indent 8 }} + {{- $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{- include "extraModules" (dict "name" .name "image" .image "distroless" .distroless "containerSecurityContext" $containerSecurityContext "resources" .resources) | nindent 8 }} {{- end }} {{- end }} - {{- if .Values.controller.opentelemetry.enabled}} - {{ $otelContainerSecurityContext := $.Values.controller.opentelemetry.containerSecurityContext | default $.Values.controller.containerSecurityContext }} - {{ $fullImage := printf "%s/%s:%s@%s" .Values.controller.opentelemetry.image.registry .Values.controller.opentelemetry.image.image .Values.controller.opentelemetry.image.tag .Values.controller.opentelemetry.image.digest }} - {{- include "extraModules" (dict "name" "opentelemetry" "image" .Values.controller.opentelemetry.image "containerSecurityContext" $otelContainerSecurityContext) | nindent 8}} - {{- end}} + {{- if .Values.controller.opentelemetry.enabled }} + {{- with .Values.controller.opentelemetry }} + {{- $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{- include "extraModules" (dict "name" .name "image" .image "distroless" .distroless "containerSecurityContext" $containerSecurityContext "resources" .resources) | nindent 8 }} + {{- end }} + + {{ $otelContainerSecurityContext := $.Values.controller.opentelemetry.containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{ $fullImage := printf "%s/%s:%s@%s" .Values.controller.opentelemetry.image.registry .Values.controller.opentelemetry.image.image .Values.controller.opentelemetry.image.tag .Values.controller.opentelemetry.image.digest }} + {{- include "extraModules" (dict "name" "opentelemetry" "image" .Values.controller.opentelemetry.image "containerSecurityContext" $otelContainerSecurityContext) | nindent 8}} + {{- end }} {{- end }} {{- if .Values.controller.hostNetwork }} hostNetwork: {{ .Values.controller.hostNetwork }} @@ -201,7 +210,7 @@ spec: affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} {{- end }} {{- if .Values.controller.topologySpreadConstraints }} - topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + topologySpreadConstraints: {{ tpl (toYaml .Values.controller.topologySpreadConstraints) $ | nindent 8 }} {{- end }} serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} @@ -222,7 +231,7 @@ spec: {{- if .Values.controller.admissionWebhooks.enabled }} - name: webhook-cert secret: - secretName: {{ include "ingress-nginx.fullname" . }}-admission + secretName: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} {{- if .Values.controller.admissionWebhooks.certManager.enabled }} items: - key: tls.crt diff --git a/charts/ingress-nginx/templates/controller-deployment.yaml b/charts/ingress-nginx/templates/controller-deployment.yaml index 878b21a78..00f7bba2a 100644 --- a/charts/ingress-nginx/templates/controller-deployment.yaml +++ b/charts/ingress-nginx/templates/controller-deployment.yaml @@ -1,4 +1,4 @@ -{{- if or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both") -}} +{{- if eq .Values.controller.kind "Deployment" -}} {{- include "isControllerTagValid" . -}} apiVersion: apps/v1 kind: Deployment @@ -10,7 +10,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- if .Values.controller.annotations }} annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} {{- end }} @@ -19,13 +19,12 @@ spec: matchLabels: {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: controller - {{- if not .Values.controller.autoscaling.enabled }} + {{- if not (or .Values.controller.autoscaling.enabled .Values.controller.keda.enabled) }} replicas: {{ .Values.controller.replicaCount }} {{- end }} revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} {{- if .Values.controller.updateStrategy }} - strategy: - {{ toYaml .Values.controller.updateStrategy | nindent 4 }} + strategy: {{ toYaml .Values.controller.updateStrategy | nindent 4 }} {{- end }} minReadySeconds: {{ .Values.controller.minReadySeconds }} template: @@ -49,6 +48,9 @@ spec: {{- if .Values.controller.dnsConfig }} dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} {{- end }} + {{- if .Values.controller.hostAliases }} + hostAliases: {{ tpl (toYaml .Values.controller.hostAliases) $ | nindent 8 }} + {{- end }} {{- if .Values.controller.hostname }} hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} {{- end }} @@ -61,16 +63,16 @@ spec: {{- end }} {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} securityContext: - {{- end }} - {{- if .Values.controller.podSecurityContext }} + {{- if .Values.controller.podSecurityContext }} {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} - {{- end }} - {{- if .Values.controller.sysctls }} + {{- end }} + {{- if .Values.controller.sysctls }} sysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - name: {{ $sysctl | quote }} - value: {{ $value | quote }} - {{- end }} + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} {{- end }} {{- if .Values.controller.shareProcessNamespace }} shareProcessNamespace: {{ .Values.controller.shareProcessNamespace }} @@ -84,9 +86,8 @@ spec: {{- if .Values.controller.lifecycle }} lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} {{- end }} - args: - {{- include "ingress-nginx.params" . | nindent 12 }} - securityContext: {{ include "controller.containerSecurityContext" . | nindent 12 }} + args: {{ include "ingress-nginx.params" . | nindent 12 }} + securityContext: {{ include "ingress-nginx.controller.containerSecurityContext" . | nindent 12 }} env: - name: POD_NAME valueFrom: @@ -151,11 +152,11 @@ spec: volumeMounts: {{- if (or .Values.controller.extraModules .Values.controller.opentelemetry.enabled) }} - name: modules - {{ if .Values.controller.image.chroot }} + {{- if .Values.controller.image.chroot }} mountPath: /chroot/modules_mount - {{ else }} + {{- else }} mountPath: /modules_mount - {{ end }} + {{- end }} {{- end }} {{- if .Values.controller.customTemplate.configMapName }} - mountPath: /etc/nginx/template @@ -175,24 +176,29 @@ spec: resources: {{ toYaml .Values.controller.resources | nindent 12 }} {{- end }} {{- if .Values.controller.extraContainers }} - {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- toYaml .Values.controller.extraContainers | nindent 8 }} {{- end }} {{- if (or .Values.controller.extraInitContainers .Values.controller.extraModules .Values.controller.opentelemetry.enabled) }} initContainers: {{- if .Values.controller.extraInitContainers }} - {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- toYaml .Values.controller.extraInitContainers | nindent 8 }} {{- end }} {{- if .Values.controller.extraModules }} {{- range .Values.controller.extraModules }} - {{ $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} -{{ include "extraModules" (dict "name" .name "image" .image "containerSecurityContext" $containerSecurityContext) | indent 8 }} + {{- $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{- include "extraModules" (dict "name" .name "image" .image "distroless" .distroless "containerSecurityContext" $containerSecurityContext "resources" .resources) | nindent 8 }} {{- end }} {{- end }} - {{- if .Values.controller.opentelemetry.enabled}} - {{- $otelContainerSecurityContext := $.Values.controller.opentelemetry.containerSecurityContext | default $.Values.controller.containerSecurityContext }} - {{- $fullImage := printf "%s/%s:%s@%s" .Values.controller.opentelemetry.image.registry .Values.controller.opentelemetry.image.image .Values.controller.opentelemetry.image.tag .Values.controller.opentelemetry.image.digest }} - {{- include "extraModules" (dict "name" "opentelemetry" "image" $fullImage "containerSecurityContext" $otelContainerSecurityContext) | nindent 8}} - {{- end}} + {{- if .Values.controller.opentelemetry.enabled }} + {{- with .Values.controller.opentelemetry }} + {{- $containerSecurityContext := .containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{- include "extraModules" (dict "name" .name "image" .image "distroless" .distroless "containerSecurityContext" $containerSecurityContext "resources" .resources) | nindent 8 }} + {{- end }} + + {{- $otelContainerSecurityContext := $.Values.controller.opentelemetry.containerSecurityContext | default $.Values.controller.containerSecurityContext }} + {{- $fullImage := printf "%s/%s:%s@%s" .Values.controller.opentelemetry.image.registry .Values.controller.opentelemetry.image.image .Values.controller.opentelemetry.image.tag .Values.controller.opentelemetry.image.digest }} + {{- include "extraModules" (dict "name" "opentelemetry" "image" $fullImage "containerSecurityContext" $otelContainerSecurityContext) | nindent 8}} + {{- end }} {{- end }} {{- if .Values.controller.hostNetwork }} hostNetwork: {{ .Values.controller.hostNetwork }} @@ -207,7 +213,7 @@ spec: affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} {{- end }} {{- if .Values.controller.topologySpreadConstraints }} - topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + topologySpreadConstraints: {{ tpl (toYaml .Values.controller.topologySpreadConstraints) $ | nindent 8 }} {{- end }} serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} @@ -228,7 +234,7 @@ spec: {{- if .Values.controller.admissionWebhooks.enabled }} - name: webhook-cert secret: - secretName: {{ include "ingress-nginx.fullname" . }}-admission + secretName: {{ include "ingress-nginx.admissionWebhooks.fullname" . }} {{- if .Values.controller.admissionWebhooks.certManager.enabled }} items: - key: tls.crt diff --git a/charts/ingress-nginx/templates/controller-hpa.yaml b/charts/ingress-nginx/templates/controller-hpa.yaml index d1e78bdfc..ec9ad7380 100644 --- a/charts/ingress-nginx/templates/controller-hpa.yaml +++ b/charts/ingress-nginx/templates/controller-hpa.yaml @@ -1,12 +1,9 @@ -{{- if and .Values.controller.autoscaling.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} -{{- if not .Values.controller.keda.enabled }} - -apiVersion: {{ .Values.controller.autoscaling.apiVersion }} +{{- if and (eq .Values.controller.kind "Deployment") .Values.controller.autoscaling.enabled (not .Values.controller.keda.enabled) -}} +apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} kind: HorizontalPodAutoscaler metadata: - annotations: {{- with .Values.controller.autoscaling.annotations }} - {{- toYaml . | trimSuffix "\n" | nindent 4 }} + annotations: {{ toYaml . | nindent 4 }} {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} @@ -15,7 +12,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: scaleTargetRef: apiVersion: apps/v1 @@ -48,5 +45,3 @@ spec: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} -{{- end }} - diff --git a/charts/ingress-nginx/templates/controller-keda.yaml b/charts/ingress-nginx/templates/controller-keda.yaml index 875157ea4..6ff9c0b22 100644 --- a/charts/ingress-nginx/templates/controller-keda.yaml +++ b/charts/ingress-nginx/templates/controller-keda.yaml @@ -1,6 +1,4 @@ -{{- if and .Values.controller.keda.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} -# https://keda.sh/docs/ - +{{- if and .Values.controller.keda.enabled (eq .Values.controller.kind "Deployment") -}} apiVersion: {{ .Values.controller.keda.apiVersion }} kind: ScaledObject metadata: @@ -11,6 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- if .Values.controller.keda.scaledObject.annotations }} annotations: {{ toYaml .Values.controller.keda.scaledObject.annotations | nindent 4 }} {{- end }} @@ -25,6 +24,11 @@ spec: cooldownPeriod: {{ .Values.controller.keda.cooldownPeriod }} minReplicaCount: {{ .Values.controller.keda.minReplicas }} maxReplicaCount: {{ .Values.controller.keda.maxReplicas }} +{{- with .Values.controller.keda.fallback }} + fallback: + failureThreshold: {{ .failureThreshold | default 3 }} + replicas: {{ .replicas | default $.Values.controller.keda.maxReplicas }} +{{- end }} triggers: {{- with .Values.controller.keda.triggers }} {{ toYaml . | indent 2 }} diff --git a/charts/ingress-nginx/templates/controller-networkpolicy.yaml b/charts/ingress-nginx/templates/controller-networkpolicy.yaml new file mode 100644 index 000000000..e68f9916d --- /dev/null +++ b/charts/ingress-nginx/templates/controller-networkpolicy.yaml @@ -0,0 +1,45 @@ +{{- if .Values.controller.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} +spec: + podSelector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + policyTypes: + - Ingress + - Egress + ingress: + - ports: + {{- range $key, $value := .Values.controller.containerPort }} + - protocol: TCP + port: {{ $value }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + - protocol: TCP + port: {{ .Values.controller.metrics.port }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - protocol: TCP + port: {{ .Values.controller.admissionWebhooks.port }} + {{- end }} + {{- range $key, $value := .Values.tcp }} + - protocol: TCP + port: {{ $key }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - protocol: UDP + port: {{ $key }} + {{- end }} + egress: + - {} +{{- end }} diff --git a/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml b/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml index 230307cd7..8cb7d4b97 100644 --- a/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml +++ b/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- if .Values.controller.annotations }} annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} {{- end }} @@ -18,7 +18,7 @@ spec: matchLabels: {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: controller - {{- if .Values.controller.minAvailable }} + {{- if and .Values.controller.minAvailable (not (hasKey .Values.controller "maxUnavailable")) }} minAvailable: {{ .Values.controller.minAvailable }} {{- else if .Values.controller.maxUnavailable }} maxUnavailable: {{ .Values.controller.maxUnavailable }} diff --git a/charts/ingress-nginx/templates/controller-prometheusrules.yaml b/charts/ingress-nginx/templates/controller-prometheusrules.yaml index 78b5362e8..f0b93cc8c 100644 --- a/charts/ingress-nginx/templates/controller-prometheusrules.yaml +++ b/charts/ingress-nginx/templates/controller-prometheusrules.yaml @@ -4,7 +4,9 @@ kind: PrometheusRule metadata: name: {{ include "ingress-nginx.controller.fullname" . }} {{- if .Values.controller.metrics.prometheusRule.namespace }} - namespace: {{ .Values.controller.metrics.prometheusRule.namespace | quote }} + namespace: {{ .Values.controller.metrics.prometheusRule.namespace }} +{{- else }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} diff --git a/charts/ingress-nginx/templates/controller-psp.yaml b/charts/ingress-nginx/templates/controller-psp.yaml index 3c499b9d4..aad1d2736 100644 --- a/charts/ingress-nginx/templates/controller-psp.yaml +++ b/charts/ingress-nginx/templates/controller-psp.yaml @@ -4,6 +4,8 @@ apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: {{ include "ingress-nginx.fullname" . }} + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: "*" labels: {{- include "ingress-nginx.labels" . | nindent 4 }} app.kubernetes.io/component: controller @@ -11,84 +13,88 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - allowedCapabilities: - - NET_BIND_SERVICE - {{- if .Values.controller.image.chroot }} - - SYS_CHROOT - {{- end }} -{{- if .Values.controller.sysctls }} - allowedUnsafeSysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - {{ $sysctl }} - {{- end }} -{{- end }} privileged: false - allowPrivilegeEscalation: true - # Allow core volume types. - volumes: - - 'configMap' - - 'emptyDir' - - 'projected' - - 'secret' - - 'downwardAPI' -{{- if .Values.controller.hostNetwork }} + hostPID: false + hostIPC: false hostNetwork: {{ .Values.controller.hostNetwork }} -{{- end }} {{- if or .Values.controller.hostNetwork .Values.controller.hostPort.enabled }} hostPorts: -{{- if .Values.controller.hostNetwork }} -{{- range $key, $value := .Values.controller.containerPort }} - # {{ $key }} - - min: {{ $value }} - max: {{ $value }} + {{- if .Values.controller.hostNetwork }} + {{- range $key, $value := .Values.controller.containerPort }} + # controller.containerPort.{{ $key }} + - min: {{ $value }} + max: {{ $value }} + {{- end }} + {{- else if .Values.controller.hostPort.enabled }} + {{- range $key, $value := .Values.controller.hostPort.ports }} + # controller.hostPort.ports.{{ $key }} + - min: {{ $value }} + max: {{ $value }} + {{- end }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + # controller.metrics.port + - min: {{ .Values.controller.metrics.port }} + max: {{ .Values.controller.metrics.port }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + # controller.admissionWebhooks.port + - min: {{ .Values.controller.admissionWebhooks.port }} + max: {{ .Values.controller.admissionWebhooks.port }} + {{- end }} + {{- range $key, $value := .Values.tcp }} + # tcp.{{ $key }} + - min: {{ $key }} + max: {{ $key }} + {{- end }} + {{- range $key, $value := .Values.udp }} + # udp.{{ $key }} + - min: {{ $key }} + max: {{ $key }} + {{- end }} {{- end }} -{{- else if .Values.controller.hostPort.enabled }} -{{- range $key, $value := .Values.controller.hostPort.ports }} - # {{ $key }} - - min: {{ $value }} - max: {{ $value }} -{{- end }} -{{- end }} -{{- if .Values.controller.metrics.enabled }} - # metrics - - min: {{ .Values.controller.metrics.port }} - max: {{ .Values.controller.metrics.port }} -{{- end }} -{{- if .Values.controller.admissionWebhooks.enabled }} - # admission webhooks - - min: {{ .Values.controller.admissionWebhooks.port }} - max: {{ .Values.controller.admissionWebhooks.port }} -{{- end }} -{{- range $key, $value := .Values.tcp }} - # {{ $key }}-tcp - - min: {{ $key }} - max: {{ $key }} -{{- end }} -{{- range $key, $value := .Values.udp }} - # {{ $key }}-udp - - min: {{ $key }} - max: {{ $key }} -{{- end }} -{{- end }} - hostIPC: false - hostPID: false - runAsUser: - # Require the container to run without root privileges. - rule: 'MustRunAsNonRoot' - supplementalGroups: - rule: 'MustRunAs' - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 + volumes: + - configMap + - downwardAPI + - emptyDir + - secret + - projected fsGroup: - rule: 'MustRunAs' + rule: MustRunAs ranges: - # Forbid adding the root group. - min: 1 max: 65535 readOnlyRootFilesystem: false + runAsUser: + rule: MustRunAsNonRoot + runAsGroup: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + supplementalGroups: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + allowPrivilegeEscalation: {{ or .Values.controller.image.allowPrivilegeEscalation .Values.controller.image.chroot }} + requiredDropCapabilities: + - ALL + allowedCapabilities: + - NET_BIND_SERVICE + {{- if .Values.controller.image.chroot }} + {{- if .Values.controller.image.seccompProfile }} + - SYS_ADMIN + {{- end }} + - SYS_CHROOT + {{- end }} seLinux: - rule: 'RunAsAny' + rule: RunAsAny +{{- if .Values.controller.sysctls }} + allowedUnsafeSysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - {{ $sysctl }} + {{- end }} +{{- end }} {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-role.yaml b/charts/ingress-nginx/templates/controller-role.yaml index d1aa9aac7..f6217a29a 100644 --- a/charts/ingress-nginx/templates/controller-role.yaml +++ b/charts/ingress-nginx/templates/controller-role.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} rules: - apiGroups: - "" diff --git a/charts/ingress-nginx/templates/controller-rolebinding.yaml b/charts/ingress-nginx/templates/controller-rolebinding.yaml index e846a1183..153430aa2 100644 --- a/charts/ingress-nginx/templates/controller-rolebinding.yaml +++ b/charts/ingress-nginx/templates/controller-rolebinding.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -17,5 +17,5 @@ roleRef: subjects: - kind: ServiceAccount name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-secret.yaml b/charts/ingress-nginx/templates/controller-secret.yaml index f3744232f..f20f53469 100644 --- a/charts/ingress-nginx/templates/controller-secret.yaml +++ b/charts/ingress-nginx/templates/controller-secret.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} data: dhparam.pem: {{ .Values.dhParam }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-service-internal.yaml b/charts/ingress-nginx/templates/controller-service-internal.yaml index 87146b746..b69a09f67 100644 --- a/charts/ingress-nginx/templates/controller-service-internal.yaml +++ b/charts/ingress-nginx/templates/controller-service-internal.yaml @@ -4,7 +4,7 @@ kind: Service metadata: annotations: {{- range $key, $value := .Values.controller.service.internal.annotations }} - {{ $key }}: {{ $value | quote }} + {{ $key }}: {{ tpl ($value | toString) $ | quote }} {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} @@ -13,9 +13,12 @@ metadata: {{- toYaml .Values.controller.service.labels | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }}-internal - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: type: "{{ .Values.controller.service.type }}" +{{- if hasKey .Values.controller.service.internal "allocateLoadBalancerNodePorts" }} + allocateLoadBalancerNodePorts: {{ .Values.controller.service.internal.allocateLoadBalancerNodePorts }} +{{- end }} {{- if .Values.controller.service.internal.loadBalancerIP }} loadBalancerIP: {{ .Values.controller.service.internal.loadBalancerIP }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-service-metrics.yaml b/charts/ingress-nginx/templates/controller-service-metrics.yaml index b178401c9..7c153295f 100644 --- a/charts/ingress-nginx/templates/controller-service-metrics.yaml +++ b/charts/ingress-nginx/templates/controller-service-metrics.yaml @@ -12,7 +12,7 @@ metadata: {{- toYaml .Values.controller.metrics.service.labels | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }}-metrics - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: type: {{ .Values.controller.metrics.service.type }} {{- if .Values.controller.metrics.service.clusterIP }} diff --git a/charts/ingress-nginx/templates/controller-service-webhook.yaml b/charts/ingress-nginx/templates/controller-service-webhook.yaml index 2aae24fcf..2d02e23aa 100644 --- a/charts/ingress-nginx/templates/controller-service-webhook.yaml +++ b/charts/ingress-nginx/templates/controller-service-webhook.yaml @@ -12,7 +12,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }}-admission - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: type: {{ .Values.controller.admissionWebhooks.service.type }} {{- if .Values.controller.admissionWebhooks.service.clusterIP }} diff --git a/charts/ingress-nginx/templates/controller-service.yaml b/charts/ingress-nginx/templates/controller-service.yaml index 2b28196de..d81635014 100644 --- a/charts/ingress-nginx/templates/controller-service.yaml +++ b/charts/ingress-nginx/templates/controller-service.yaml @@ -4,7 +4,7 @@ kind: Service metadata: annotations: {{- range $key, $value := .Values.controller.service.annotations }} - {{ $key }}: {{ $value | quote }} + {{ $key }}: {{ tpl ($value | toString) $ | quote }} {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} @@ -13,9 +13,12 @@ metadata: {{- toYaml .Values.controller.service.labels | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: type: {{ .Values.controller.service.type }} +{{- if hasKey .Values.controller.service "allocateLoadBalancerNodePorts" }} + allocateLoadBalancerNodePorts: {{ .Values.controller.service.allocateLoadBalancerNodePorts }} +{{- end }} {{- if .Values.controller.service.clusterIP }} clusterIP: {{ .Values.controller.service.clusterIP }} {{- end }} @@ -28,6 +31,9 @@ spec: {{- if .Values.controller.service.loadBalancerSourceRanges }} loadBalancerSourceRanges: {{ toYaml .Values.controller.service.loadBalancerSourceRanges | nindent 4 }} {{- end }} +{{- if .Values.controller.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.controller.service.loadBalancerClass }} +{{- end }} {{- if .Values.controller.service.externalTrafficPolicy }} externalTrafficPolicy: {{ .Values.controller.service.externalTrafficPolicy }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-serviceaccount.yaml b/charts/ingress-nginx/templates/controller-serviceaccount.yaml index e6e776d09..df83de3d0 100644 --- a/charts/ingress-nginx/templates/controller-serviceaccount.yaml +++ b/charts/ingress-nginx/templates/controller-serviceaccount.yaml @@ -9,10 +9,9 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- if .Values.serviceAccount.annotations }} - annotations: - {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} + annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }} {{- end }} automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/charts/ingress-nginx/templates/controller-servicemonitor.yaml index 8ab16f0b2..585fd0cb7 100644 --- a/charts/ingress-nginx/templates/controller-servicemonitor.yaml +++ b/charts/ingress-nginx/templates/controller-servicemonitor.yaml @@ -4,7 +4,9 @@ kind: ServiceMonitor metadata: name: {{ include "ingress-nginx.controller.fullname" . }} {{- if .Values.controller.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.controller.metrics.serviceMonitor.namespace | quote }} + namespace: {{ .Values.controller.metrics.serviceMonitor.namespace }} +{{- else }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} @@ -33,7 +35,7 @@ spec: {{- else }} namespaceSelector: matchNames: - - {{ .Release.Namespace }} + - {{ include "ingress-nginx.namespace" . }} {{- end }} {{- if .Values.controller.metrics.serviceMonitor.targetLabels }} targetLabels: diff --git a/charts/ingress-nginx/templates/controller-webhooks-networkpolicy.yaml b/charts/ingress-nginx/templates/controller-webhooks-networkpolicy.yaml deleted file mode 100644 index f74c2fbf3..000000000 --- a/charts/ingress-nginx/templates/controller-webhooks-networkpolicy.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- if .Values.controller.admissionWebhooks.enabled }} -{{- if .Values.controller.admissionWebhooks.networkPolicyEnabled }} - -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "ingress-nginx.fullname" . }}-webhooks-allow - namespace: {{ .Release.Namespace }} -spec: - ingress: - - {} - podSelector: - matchLabels: - app.kubernetes.io/name: {{ include "ingress-nginx.name" . }} - policyTypes: - - Ingress - -{{- end }} -{{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-deployment.yaml b/charts/ingress-nginx/templates/default-backend-deployment.yaml index 87aced49d..ed88e6bc3 100644 --- a/charts/ingress-nginx/templates/default-backend-deployment.yaml +++ b/charts/ingress-nginx/templates/default-backend-deployment.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: selector: matchLabels: @@ -65,14 +65,7 @@ spec: {{- end }} {{- end }} {{- end }} - securityContext: - capabilities: - drop: - - ALL - runAsUser: {{ .Values.defaultBackend.image.runAsUser }} - runAsNonRoot: {{ .Values.defaultBackend.image.runAsNonRoot }} - allowPrivilegeEscalation: {{ .Values.defaultBackend.image.allowPrivilegeEscalation }} - readOnlyRootFilesystem: {{ .Values.defaultBackend.image.readOnlyRootFilesystem}} + securityContext: {{ include "ingress-nginx.defaultBackend.containerSecurityContext" . | nindent 12 }} {{- if .Values.defaultBackend.extraEnvs }} env: {{ toYaml .Values.defaultBackend.extraEnvs | nindent 12 }} {{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-hpa.yaml b/charts/ingress-nginx/templates/default-backend-hpa.yaml index 924125f07..699323897 100644 --- a/charts/ingress-nginx/templates/default-backend-hpa.yaml +++ b/charts/ingress-nginx/templates/default-backend-hpa.yaml @@ -1,37 +1,40 @@ {{- if and .Values.defaultBackend.enabled .Values.defaultBackend.autoscaling.enabled }} -apiVersion: {{ .Values.defaultBackend.autoscaling.apiVersion }} +apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} kind: HorizontalPodAutoscaler metadata: + {{- with .Values.defaultBackend.autoscaling.annotations }} + annotations: {{ toYaml . | nindent 4 }} + {{- end }} labels: {{- include "ingress-nginx.labels" . | nindent 4 }} app.kubernetes.io/component: default-backend {{- with .Values.defaultBackend.labels }} {{- toYaml . | nindent 4 }} {{- end }} - name: {{ template "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ template "ingress-nginx.defaultBackend.fullname" . }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} minReplicas: {{ .Values.defaultBackend.autoscaling.minReplicas }} maxReplicas: {{ .Values.defaultBackend.autoscaling.maxReplicas }} metrics: -{{- with .Values.defaultBackend.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ . }} -{{- end }} -{{- with .Values.defaultBackend.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ . }} -{{- end }} + {{- with .Values.defaultBackend.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.defaultBackend.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-networkpolicy.yaml b/charts/ingress-nginx/templates/default-backend-networkpolicy.yaml new file mode 100644 index 000000000..90b3c2ba0 --- /dev/null +++ b/charts/ingress-nginx/templates/default-backend-networkpolicy.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ include "ingress-nginx.namespace" . }} +spec: + podSelector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: default-backend + policyTypes: + - Ingress + - Egress + ingress: + - ports: + - protocol: TCP + port: {{ .Values.defaultBackend.port }} +{{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml b/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml index 00891cee5..f869e4530 100644 --- a/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml +++ b/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml @@ -10,7 +10,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: selector: matchLabels: diff --git a/charts/ingress-nginx/templates/default-backend-psp.yaml b/charts/ingress-nginx/templates/default-backend-psp.yaml index c144c8fbf..424109109 100644 --- a/charts/ingress-nginx/templates/default-backend-psp.yaml +++ b/charts/ingress-nginx/templates/default-backend-psp.yaml @@ -4,6 +4,8 @@ apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: {{ include "ingress-nginx.fullname" . }}-backend + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: "*" labels: {{- include "ingress-nginx.labels" . | nindent 4 }} app.kubernetes.io/component: default-backend @@ -11,28 +13,38 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - allowPrivilegeEscalation: false + privileged: false + hostPID: false + hostIPC: false + hostNetwork: false + volumes: + - configMap + - downwardAPI + - emptyDir + - secret + - projected fsGroup: - ranges: - - max: 65535 - min: 1 rule: MustRunAs - requiredDropCapabilities: - - ALL + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: true runAsUser: rule: MustRunAsNonRoot + runAsGroup: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + supplementalGroups: + rule: MustRunAs + ranges: + - min: 1 + max: 65535 + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL seLinux: rule: RunAsAny - supplementalGroups: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-role.yaml b/charts/ingress-nginx/templates/default-backend-role.yaml index a2b457c36..dd7868aa0 100644 --- a/charts/ingress-nginx/templates/default-backend-role.yaml +++ b/charts/ingress-nginx/templates/default-backend-role.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-backend - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} rules: - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] resources: ['podsecuritypolicies'] diff --git a/charts/ingress-nginx/templates/default-backend-rolebinding.yaml b/charts/ingress-nginx/templates/default-backend-rolebinding.yaml index dbaa516b9..3203b6f57 100644 --- a/charts/ingress-nginx/templates/default-backend-rolebinding.yaml +++ b/charts/ingress-nginx/templates/default-backend-rolebinding.yaml @@ -9,7 +9,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.fullname" . }}-backend - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -17,5 +17,5 @@ roleRef: subjects: - kind: ServiceAccount name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} + namespace: {{ include "ingress-nginx.namespace" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/default-backend-service.yaml b/charts/ingress-nginx/templates/default-backend-service.yaml index 5f1d09a95..2cccd6e9e 100644 --- a/charts/ingress-nginx/templates/default-backend-service.yaml +++ b/charts/ingress-nginx/templates/default-backend-service.yaml @@ -12,7 +12,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} spec: type: {{ .Values.defaultBackend.service.type }} {{- if .Values.defaultBackend.service.clusterIP }} diff --git a/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml b/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml index b45a95ad2..2afaf0c04 100644 --- a/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml +++ b/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml @@ -9,6 +9,6 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ include "ingress-nginx.namespace" . }} automountServiceAccountToken: {{ .Values.defaultBackend.serviceAccount.automountServiceAccountToken }} {{- end }} diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml index a33db6e1e..0de4a4d3f 100644 --- a/charts/ingress-nginx/values.yaml +++ b/charts/ingress-nginx/values.yaml @@ -7,6 +7,9 @@ # nameOverride: # fullnameOverride: +# -- Override the deployment namespace; defaults to .Release.Namespace +namespaceOverride: "" + ## Labels to apply to all resources ## commonLabels: {} @@ -15,6 +18,7 @@ commonLabels: {} controller: name: controller + enableAnnotationValidations: false image: ## Keep false as default for now! chroot: false @@ -23,13 +27,17 @@ controller: ## for backwards compatibility consider setting the full image url via the repository value below ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail ## repository: - tag: "v1.7.1" - digest: sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 - digestChroot: sha256:e35d5ab487861b9d419c570e3530589229224a0762c7b4d2e2222434abb8d988 + tag: "v1.9.4" + digest: sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 + digestChroot: sha256:5976b1067cfbca8a21d0ba53d71f83543a73316a61ea7f7e436d6cf84ddf9b26 pullPolicy: IfNotPresent + runAsNonRoot: true # www-data -> uid 101 runAsUser: 101 - allowPrivilegeEscalation: true + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + readOnlyRootFilesystem: false # -- Use an existing PSP instead of creating one existingPsp: "" # -- Configures the controller container name @@ -48,6 +56,16 @@ controller: addHeaders: {} # -- Optionally customize the pod dnsConfig. dnsConfig: {} + # -- Optionally customize the pod hostAliases. + hostAliases: [] + # - ip: 127.0.0.1 + # hostnames: + # - foo.local + # - bar.local + # - ip: 10.1.2.3 + # hostnames: + # - foo.remote + # - bar.remote # -- Optionally customize the pod hostname. hostname: {} # -- Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. @@ -63,14 +81,14 @@ controller: watchIngressWithoutClass: false # -- Process IngressClass per name (additionally as per spec.controller). ingressClassByName: false - # -- This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-aware-hints="auto" + # -- This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-mode="auto" # Defaults to false enableTopologyAwareRouting: false # -- This configuration defines if Ingress Controller should allow users to set # their own *-snippet annotations, otherwise this is forbidden / dropped # when users add those annotations. # Global snippets in ConfigMap are still respected - allowSnippetAnnotations: true + allowSnippetAnnotations: 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 @@ -85,6 +103,10 @@ controller: http: 80 # -- 'hostPort' https port https: 443 + # NetworkPolicy for controller component. + networkPolicy: + # -- Enable 'networkPolicy' or not + enabled: false # -- Election ID to use for status update, by default it uses the controller name combined with a suffix of 'leader' electionID: "" ## This section refers to the creation of the IngressClass resource @@ -109,12 +131,15 @@ controller: podLabels: {} # key: value - # -- Security Context policies for controller pods + # -- Security context for controller pods podSecurityContext: {} - # -- See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls + # -- sysctls for controller pods + ## Ref: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ sysctls: {} # sysctls: # "net.core.somaxconn": "8192" + # -- Security context for controller containers + containerSecurityContext: {} # -- Allows customization of the source of the IP address or FQDN to report # in the ingress status field. By default, it reads the information provided @@ -155,6 +180,9 @@ controller: extraArgs: {} ## extraArgs: ## default-ssl-certificate: "/" + ## time-buckets: "0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10" + ## length-buckets: "10,20,30,40,50,60,70,80,90,100" + ## size-buckets: "10,100,1000,10000,100000,1e+06,1e+07" # -- Additional environment variables to set extraEnvs: [] @@ -245,19 +273,29 @@ controller: ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ ## topologySpreadConstraints: [] - # - maxSkew: 1 - # topologyKey: topology.kubernetes.io/zone - # whenUnsatisfiable: DoNotSchedule - # labelSelector: + # - labelSelector: # matchLabels: - # app.kubernetes.io/instance: ingress-nginx-internal + # app.kubernetes.io/name: '{{ include "ingress-nginx.name" . }}' + # app.kubernetes.io/instance: '{{ .Release.Name }}' + # app.kubernetes.io/component: controller + # topologyKey: topology.kubernetes.io/zone + # maxSkew: 1 + # whenUnsatisfiable: ScheduleAnyway + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: '{{ include "ingress-nginx.name" . }}' + # app.kubernetes.io/instance: '{{ .Release.Name }}' + # app.kubernetes.io/component: controller + # topologyKey: kubernetes.io/hostname + # maxSkew: 1 + # whenUnsatisfiable: ScheduleAnyway # -- `terminationGracePeriodSeconds` to avoid killing pods before we are ready ## wait up to five minutes for the drain of connections ## terminationGracePeriodSeconds: 300 # -- Node labels for controller pod assignment - ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ ## nodeSelector: kubernetes.io/os: linux @@ -308,9 +346,10 @@ controller: ## podAnnotations: {} replicaCount: 1 - # -- Define either 'minAvailable' or 'maxUnavailable', never both. + # -- Minimum available pods set in PodDisruptionBudget. + # Define either 'minAvailable' or 'maxUnavailable', never both. minAvailable: 1 - # -- Define either 'minAvailable' or 'maxUnavailable', never both. + # -- Maximum unavailable pods set in PodDisruptionBudget. If set, 'minAvailable' is ignored. # maxUnavailable: 1 ## Define requests resources to avoid probe issues due to CPU utilization in busy nodes @@ -326,7 +365,6 @@ controller: memory: 90Mi # Mutually exclusive with keda autoscaling autoscaling: - apiVersion: autoscaling/v2 enabled: false annotations: {} minReplicas: 1 @@ -368,6 +406,9 @@ controller: maxReplicas: 11 pollingInterval: 30 cooldownPeriod: 300 + # fallback: + # failureThreshold: 3 + # replicas: 11 restoreToOriginalReplicaCount: false scaledObject: annotations: {} @@ -412,17 +453,23 @@ controller: # Will be ignored for Kubernetes versions older than 1.20 ## appProtocol: true + # -- Annotations are mandatory for the load balancer to come up. Varies with the cloud service. Values passed through helm tpl engine. annotations: {} labels: {} # clusterIP: "" # -- List of IP addresses at which the controller services are available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips ## externalIPs: [] + # -- Set to false to disable loadbalancer node port allocation + # See https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation + # allocateLoadBalancerNodePorts: true # -- Used by cloud providers to connect the resulting `LoadBalancer` to a pre-existing static IP according to https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer loadBalancerIP: "" loadBalancerSourceRanges: [] + # -- Used by cloud providers to select a load balancer implementation other than the cloud provider default. https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-class + loadBalancerClass: "" enableHttp: true enableHttps: true ## Set external traffic policy to: "Local" to preserve source IP on providers supporting it. @@ -471,10 +518,13 @@ controller: internal: # -- Enables an additional internal load balancer (besides the external one). enabled: false - # -- Annotations are mandatory for the load balancer to come up. Varies with the cloud service. + # -- Annotations are mandatory for the load balancer to come up. Varies with the cloud service. Values passed through helm tpl engine. annotations: {} - # loadBalancerIP: "" - + # -- Set to false to disable loadbalancer node port allocation + # See https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation + # allocateLoadBalancerNodePorts: true + # -- Used by cloud providers to connect the resulting internal LoadBalancer to a pre-existing static IP. Make sure to add to the service the needed annotation to specify the subnet which the static IP belongs to. For instance, `networking.gke.io/internal-load-balancer-subnet` for GCP and `service.beta.kubernetes.io/aws-load-balancer-subnets` for AWS. + loadBalancerIP: "" # -- Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. loadBalancerSourceRanges: [] ## Set external traffic policy to: "Local" to preserve source IP on @@ -538,8 +588,18 @@ controller: extraModules: [] # - name: mytestmodule # image: registry.k8s.io/ingress-nginx/mytestmodule + # distroless: false # containerSecurityContext: + # runAsNonRoot: true + # runAsUser: # allowPrivilegeEscalation: false + # seccompProfile: + # type: RuntimeDefault + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # resources: {} # # The image must contain a `/usr/local/bin/init_module.sh` executable, which # will be executed as initContainers, to move its config files within the @@ -547,14 +607,32 @@ controller: opentelemetry: enabled: false + name: opentelemetry + image: registry.k8s.io/ingress-nginx/opentelemetry:v20230721-3e2062ee5@sha256:13bee3f5223883d3ca62fee7309ad02d22ec00ff0d7033e3e9aca7a9f60fd472 + + image: registry: registry.k8s.io image: ingress-nginx/opentelemetry tag: "v20230312-helm-chart-4.5.2-28-g66a760794" digest: sha256:40f766ac4a9832f36f217bb0e98d44c8d38faeccbfe861fbc1a76af7e9ab257f + + + distroless: true containerSecurityContext: + runAsNonRoot: true + # -- The image's default user, inherited from its base image `cgr.dev/chainguard/static`. + runAsUser: 65532 allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + resources: {} admissionWebhooks: + name: admission annotations: {} # ignore-check.kube-linter.io/no-read-only-rootfs: "This deployment needs write access to root filesystem". @@ -582,7 +660,6 @@ controller: labels: {} # -- Use an existing PSP instead of creating one existingPsp: "" - networkPolicyEnabled: false service: annotations: {} # clusterIP: "" @@ -592,8 +669,18 @@ controller: servicePort: 443 type: ClusterIP createSecretJob: + name: create + # -- Security context for secret creation containers securityContext: + runAsNonRoot: true + runAsUser: 65532 allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true resources: {} # limits: # cpu: 10m @@ -602,8 +689,18 @@ controller: # cpu: 10m # memory: 20Mi patchWebhookJob: + name: patch + # -- Security context for webhook patch containers securityContext: + runAsNonRoot: true + runAsUser: 65532 allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true resources: {} patch: enabled: true @@ -613,8 +710,8 @@ controller: ## for backwards compatibility consider setting the full image url via the repository value below ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail ## repository: - tag: v20230312-helm-chart-4.5.2-28-g66a760794 - digest: sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + tag: v20231011-8b53cabe0 + digest: sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 pullPolicy: IfNotPresent # -- Provide a priority class name to the webhook patching job ## @@ -625,10 +722,8 @@ controller: tolerations: [] # -- Labels to be added to patch job resources labels: {} - securityContext: - runAsNonRoot: true - runAsUser: 2000 - fsGroup: 2000 + # -- Security context for secret creation & webhook patch pods + securityContext: {} # Use certmanager to generate webhook certs certManager: enabled: false @@ -656,7 +751,7 @@ controller: # clusterIP: "" # -- List of IP addresses at which the stats-exporter service is available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips ## externalIPs: [] # loadBalancerIP: "" @@ -672,7 +767,7 @@ controller: ## jobLabel: "app.kubernetes.io/name" namespace: "" namespaceSelector: {} - ## Default: scrape .Release.Namespace only + ## Default: scrape .Release.Namespace or namespaceOverride only ## To scrape all, use the following: ## namespaceSelector: ## any: true @@ -695,8 +790,13 @@ controller: # annotations: # description: bad ingress config - nginx config test failed # summary: uninstall the latest ingress changes to allow config reloads to resume + # # By default a fake self-signed certificate is generated as default and + # # it is fine if it expires. If `--default-ssl-certificate` flag is used + # # and a valid certificate passed please do not filter for `host` label! + # # (i.e. delete `{host!="_"}` so also the default SSL certificate is + # # checked for expiration) # - alert: NGINXCertificateExpiry - # expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800 + # expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds{host!="_"}) by (host) - time()) < 604800 # for: 1s # labels: # severity: critical @@ -749,11 +849,13 @@ defaultBackend: ## repository: tag: "1.5" pullPolicy: IfNotPresent + runAsNonRoot: true # nobody user -> uid 65534 runAsUser: 65534 - runAsNonRoot: true - readOnlyRootFilesystem: true allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + readOnlyRootFilesystem: true # -- Use an existing PSP instead of creating one existingPsp: "" extraArgs: {} @@ -799,22 +901,16 @@ defaultBackend: # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" affinity: {} - # -- Security Context policies for controller pods - # See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for - # notes on enabling and using sysctls - ## + # -- Security context for default backend pods podSecurityContext: {} - # -- Security Context policies for controller main container. - # See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for - # notes on enabling and using sysctls - ## + # -- Security context for default backend containers containerSecurityContext: {} # -- Labels to add to the pod container metadata podLabels: {} # key: value # -- Node labels for default backend pod assignment - ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ ## nodeSelector: kubernetes.io/os: linux @@ -842,19 +938,22 @@ defaultBackend: # emptyDir: {} autoscaling: - apiVersion: autoscaling/v2 annotations: {} enabled: false minReplicas: 1 maxReplicas: 2 targetCPUUtilizationPercentage: 50 targetMemoryUtilizationPercentage: 50 + # NetworkPolicy for default backend component. + networkPolicy: + # -- Enable 'networkPolicy' or not + enabled: false service: annotations: {} # clusterIP: "" # -- List of IP addresses at which the default backend service is available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips ## externalIPs: [] # loadBalancerIP: "" diff --git a/cmd/dataplane/main.go b/cmd/dataplane/main.go index 6fd559e4d..65f898df6 100644 --- a/cmd/dataplane/main.go +++ b/cmd/dataplane/main.go @@ -18,10 +18,8 @@ package main import ( "fmt" - "math/rand" // #nosec "net/http" "os" - "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" @@ -41,8 +39,6 @@ import ( func main() { klog.InitFlags(nil) - rand.Seed(time.Now().UnixNano()) - fmt.Println(version.String()) var err error showVersion, conf, err := ingressflags.ParseFlags() diff --git a/cmd/dbg/main.go b/cmd/dbg/main.go index 41a3c2772..b63bcf5fe 100644 --- a/cmd/dbg/main.go +++ b/cmd/dbg/main.go @@ -114,7 +114,6 @@ func main() { fmt.Println(err) os.Exit(1) } - } func backendsAll() { @@ -155,10 +154,16 @@ func backendsList() { fmt.Println(unmarshalErr) return } - backends := f.([]interface{}) + backends, ok := f.([]interface{}) + if !ok { + fmt.Printf("unexpected type: %T", f) + } for _, backendi := range backends { - backend := backendi.(map[string]interface{}) + backend, ok := backendi.(map[string]interface{}) + if !ok { + fmt.Printf("unexpected type: %T", backendi) + } fmt.Println(backend["name"].(string)) } } @@ -180,12 +185,22 @@ func backendsGet(name string) { fmt.Println(unmarshalErr) return } - backends := f.([]interface{}) + backends, ok := f.([]interface{}) + if !ok { + fmt.Printf("unexpected type: %T", f) + } for _, backendi := range backends { - backend := backendi.(map[string]interface{}) + backend, ok := backendi.(map[string]interface{}) + if !ok { + fmt.Printf("unexpected type: %T", backendi) + } if backend["name"].(string) == name { - printed, _ := json.MarshalIndent(backend, "", " ") + printed, err := json.MarshalIndent(backend, "", " ") + if err != nil { + fmt.Println(err) + return + } fmt.Println(string(printed)) return } @@ -213,18 +228,7 @@ func certGet(host string) { } func general() { - //TODO: refactor to obtain ingress-nginx pod count from the api server - /* - statusCode, body, requestErr := nginx.NewGetStatusRequest(generalPath) - if requestErr != nil { - fmt.Println(requestErr) - return - } - if statusCode != 200 { - fmt.Printf("Nginx returned code %v\n", statusCode) - return - } - */ + // TODO: refactor to obtain ingress-nginx pod count from the api server var prettyBuffer bytes.Buffer indentErr := json.Indent(&prettyBuffer, []byte("{}"), "", " ") diff --git a/cmd/nginx/logger.go b/cmd/nginx/logger.go index 13ec095fa..323085c83 100644 --- a/cmd/nginx/logger.go +++ b/cmd/nginx/logger.go @@ -47,5 +47,4 @@ func logger(address string) { server.Wait() klog.Infof("Stopping logger") - } diff --git a/cmd/nginx/main.go b/cmd/nginx/main.go index 48dd933dc..1d30091ef 100644 --- a/cmd/nginx/main.go +++ b/cmd/nginx/main.go @@ -19,7 +19,6 @@ package main import ( "context" "fmt" - "math/rand" // #nosec "net/http" "os" "path/filepath" @@ -54,8 +53,6 @@ import ( func main() { klog.InitFlags(nil) - rand.Seed(time.Now().UnixNano()) - fmt.Println(version.String()) showVersion, conf, err := ingressflags.ParseFlags() @@ -156,7 +153,6 @@ func main() { if errExists == nil { conf.IsChroot = true go logger(conf.InternalLoggerAddress) - } go metrics.StartHTTPServer(conf.HealthCheckHost, conf.ListenPorts.Health, mux) @@ -285,10 +281,10 @@ func checkService(key string, kubeClient *kubernetes.Clientset) error { } if errors.IsNotFound(err) { - return fmt.Errorf("No service with name %v found in namespace %v: %v", name, ns, err) + return fmt.Errorf("no service with name %v found in namespace %v: %v", name, ns, err) } - return fmt.Errorf("Unexpected error searching service with name %v in namespace %v: %v", name, ns, err) + return fmt.Errorf("unexpected error searching service with name %v in namespace %v: %v", name, ns, err) } return nil diff --git a/cmd/nginx/main_test.go b/cmd/nginx/main_test.go index f57c02c5e..13f1e9eec 100644 --- a/cmd/nginx/main_test.go +++ b/cmd/nginx/main_test.go @@ -47,7 +47,7 @@ func TestCreateApiserverClient(t *testing.T) { func init() { // the default value of nginx.TemplatePath assumes the template exists in // the root filesystem and not in the rootfs directory - path, err := filepath.Abs(filepath.Join("../../rootfs/", nginx.TemplatePath)) + path, err := filepath.Abs(filepath.Join("..", "..", "rootfs", nginx.TemplatePath)) if err == nil { nginx.TemplatePath = path } @@ -87,14 +87,14 @@ func TestHandleSigterm(t *testing.T) { ingressflags.ResetForTesting(func() { t.Fatal("bad parse") }) - os.Setenv("POD_NAME", podName) - os.Setenv("POD_NAMESPACE", namespace) + t.Setenv("POD_NAME", podName) + t.Setenv("POD_NAMESPACE", namespace) oldArgs := os.Args defer func() { - os.Setenv("POD_NAME", "") - os.Setenv("POD_NAMESPACE", "") + t.Setenv("POD_NAME", "") + t.Setenv("POD_NAMESPACE", "") os.Args = oldArgs }() diff --git a/cmd/plugin/commands/backends/backends.go b/cmd/plugin/commands/backends/backends.go index afc98e4d6..ff44fd9c2 100644 --- a/cmd/plugin/commands/backends/backends.go +++ b/cmd/plugin/commands/backends/backends.go @@ -63,13 +63,14 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { return cmd } -func backends(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, backend string, onlyList bool) error { +func backends(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container, backend string, onlyList bool) error { var command []string - if onlyList { + switch { + case onlyList: command = []string{"/dbg", "backends", "list"} - } else if backend != "" { + case backend != "": command = []string{"/dbg", "backends", "get", backend} - } else { + default: command = []string{"/dbg", "backends", "all"} } diff --git a/cmd/plugin/commands/certs/certs.go b/cmd/plugin/commands/certs/certs.go index 88b721ee3..ee27cf1b1 100644 --- a/cmd/plugin/commands/certs/certs.go +++ b/cmd/plugin/commands/certs/certs.go @@ -18,6 +18,7 @@ package certs import ( "fmt" + "os" "github.com/spf13/cobra" @@ -46,7 +47,10 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { } cmd.Flags().String("host", "", "Get the cert for this hostname") - cobra.MarkFlagRequired(cmd.Flags(), "host") + if err := cobra.MarkFlagRequired(cmd.Flags(), "host"); err != nil { + util.PrintError(err) + os.Exit(1) + } pod = util.AddPodFlag(cmd) deployment = util.AddDeploymentFlag(cmd) selector = util.AddSelectorFlag(cmd) @@ -55,7 +59,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { return cmd } -func certs(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, host string) error { +func certs(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container, host string) error { command := []string{"/dbg", "certs", "get", host} pod, err := request.ChoosePod(flags, podName, deployment, selector) diff --git a/cmd/plugin/commands/conf/conf.go b/cmd/plugin/commands/conf/conf.go index a7f03a062..090fb40bb 100644 --- a/cmd/plugin/commands/conf/conf.go +++ b/cmd/plugin/commands/conf/conf.go @@ -55,7 +55,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { return cmd } -func conf(flags *genericclioptions.ConfigFlags, host string, podName string, deployment string, selector string, container string) error { +func conf(flags *genericclioptions.ConfigFlags, host, podName, deployment, selector, container string) error { pod, err := request.ChoosePod(flags, podName, deployment, selector) if err != nil { return err diff --git a/cmd/plugin/commands/exec/exec.go b/cmd/plugin/commands/exec/exec.go index f06aaeb23..8e853c534 100644 --- a/cmd/plugin/commands/exec/exec.go +++ b/cmd/plugin/commands/exec/exec.go @@ -55,7 +55,7 @@ type execFlags struct { Stdin bool } -func exec(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, cmd []string, opts execFlags) error { +func exec(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string, cmd []string, opts execFlags) error { pod, err := request.ChoosePod(flags, podName, deployment, selector) if err != nil { return err diff --git a/cmd/plugin/commands/general/general.go b/cmd/plugin/commands/general/general.go index fa6c1301f..cea403562 100644 --- a/cmd/plugin/commands/general/general.go +++ b/cmd/plugin/commands/general/general.go @@ -47,7 +47,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { return cmd } -func general(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string) error { +func general(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string) error { pod, err := request.ChoosePod(flags, podName, deployment, selector) if err != nil { return err diff --git a/cmd/plugin/commands/ingresses/ingresses.go b/cmd/plugin/commands/ingresses/ingresses.go index dff967103..8a25418db 100644 --- a/cmd/plugin/commands/ingresses/ingresses.go +++ b/cmd/plugin/commands/ingresses/ingresses.go @@ -74,9 +74,9 @@ func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces if host != "" { rowsWithHost := make([]ingressRow, 0) - for _, row := range rows { - if row.Host == host { - rowsWithHost = append(rowsWithHost, row) + for i := range rows { + if rows[i].Host == host { + rowsWithHost = append(rowsWithHost, rows[i]) } } rows = rowsWithHost @@ -91,7 +91,8 @@ func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces fmt.Fprintln(printer, "INGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT\tENDPOINTS") } - for _, row := range rows { + for i := range rows { + row := &rows[i] var tlsMsg string if row.TLS { tlsMsg = "YES" @@ -134,8 +135,8 @@ type ingressRow struct { func getIngressRows(ingresses *[]networking.Ingress) []ingressRow { rows := make([]ingressRow, 0) - for _, ing := range *ingresses { - + for i := range *ingresses { + ing := &(*ingresses)[i] address := "" for _, lbIng := range ing.Status.LoadBalancer.Ingress { if len(lbIng.IP) > 0 { @@ -182,7 +183,7 @@ func getIngressRows(ingresses *[]networking.Ingress) []ingressRow { for _, rule := range ing.Spec.Rules { _, hasTLS := tlsHosts[rule.Host] - //Handle ingress with no paths + // Handle ingress with no paths if rule.HTTP == nil { row := ingressRow{ Namespace: ing.Namespace, diff --git a/cmd/plugin/commands/ingresses/ingresses_test.go b/cmd/plugin/commands/ingresses/ingresses_test.go index 6a8d8837f..7a90efe46 100644 --- a/cmd/plugin/commands/ingresses/ingresses_test.go +++ b/cmd/plugin/commands/ingresses/ingresses_test.go @@ -24,7 +24,6 @@ import ( ) func TestGetIngressInformation(t *testing.T) { - testcases := map[string]struct { ServiceBackend *networking.IngressServiceBackend wantName string diff --git a/cmd/plugin/commands/lint/main.go b/cmd/plugin/commands/lint/main.go index 2daf8eb87..847fe86c9 100644 --- a/cmd/plugin/commands/lint/main.go +++ b/cmd/plugin/commands/lint/main.go @@ -111,11 +111,13 @@ type lintOptions struct { } func (opts *lintOptions) Validate() error { + //nolint:dogsled // Ignore 3 blank identifiers _, _, _, err := util.ParseVersionString(opts.versionFrom) if err != nil { return err } + //nolint:dogsled // Ignore 3 blank identifiers _, _, _, err = util.ParseVersionString(opts.versionTo) if err != nil { return err @@ -131,9 +133,9 @@ type lint interface { Version() string } -func checkObjectArray(lints []lint, objects []kmeta.Object, opts lintOptions) { +func checkObjectArray(allLints []lint, objects []kmeta.Object, opts lintOptions) { usedLints := make([]lint, 0) - for _, lint := range lints { + for _, lint := range allLints { lintVersion := lint.Version() if lint.Version() == "" { lintVersion = "0.0.0" @@ -189,7 +191,7 @@ func ingresses(opts lintOptions) error { return err } - var iLints []lints.IngressLint = lints.GetIngressLints() + iLints := lints.GetIngressLints() genericLints := make([]lint, len(iLints)) for i := range iLints { genericLints[i] = iLints[i] @@ -216,7 +218,7 @@ func deployments(opts lintOptions) error { return err } - var iLints []lints.DeploymentLint = lints.GetDeploymentLints() + iLints := lints.GetDeploymentLints() genericLints := make([]lint, len(iLints)) for i := range iLints { genericLints[i] = iLints[i] diff --git a/cmd/plugin/commands/logs/logs.go b/cmd/plugin/commands/logs/logs.go index 56f4fc640..22068d469 100644 --- a/cmd/plugin/commands/logs/logs.go +++ b/cmd/plugin/commands/logs/logs.go @@ -95,7 +95,7 @@ func (o *logsFlags) toStrings() []string { return r } -func logs(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, opts logsFlags) error { +func logs(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string, opts logsFlags) error { pod, err := request.ChoosePod(flags, podName, deployment, selector) if err != nil { return err diff --git a/cmd/plugin/commands/ssh/ssh.go b/cmd/plugin/commands/ssh/ssh.go index fe1b3e9fe..a4762d781 100644 --- a/cmd/plugin/commands/ssh/ssh.go +++ b/cmd/plugin/commands/ssh/ssh.go @@ -45,7 +45,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command { return cmd } -func ssh(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string) error { +func ssh(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string) error { pod, err := request.ChoosePod(flags, podName, deployment, selector) if err != nil { return err diff --git a/cmd/plugin/krew.yaml b/cmd/plugin/krew.yaml index e68b09f9a..0ef537d34 100644 --- a/cmd/plugin/krew.yaml +++ b/cmd/plugin/krew.yaml @@ -21,7 +21,7 @@ spec: arch: amd64 {{addURIAndSha "https://github.com/kubernetes/ingress-nginx/releases/download/{{ .TagName }}/kubectl-ingress-nginx_darwin_amd64.tar.gz" .TagName }} bin: kubectl-ingress-nginx - selector: + - selector: matchLabels: os: linux arch: amd64 diff --git a/cmd/plugin/kubectl/kubectl.go b/cmd/plugin/kubectl/kubectl.go index 3f31a2104..cb33243fc 100644 --- a/cmd/plugin/kubectl/kubectl.go +++ b/cmd/plugin/kubectl/kubectl.go @@ -38,11 +38,11 @@ func PodExecString(flags *genericclioptions.ConfigFlags, pod *apiv1.Pod, contain // ExecToString runs a kubectl subcommand and returns stdout as a string func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, error) { - kArgs := getKubectlConfigFlags(flags) - kArgs = append(kArgs, args...) + kubectlArgs := getKubectlConfigFlags(flags) + kubectlArgs = append(kubectlArgs, args...) buf := bytes.NewBuffer(make([]byte, 0)) - err := execToWriter(append([]string{"kubectl"}, kArgs...), buf) + err := execToWriter(append([]string{"kubectl"}, kubectlArgs...), buf) if err != nil { return "", err } @@ -51,9 +51,9 @@ func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, // Exec replaces the current process with a kubectl invocation func Exec(flags *genericclioptions.ConfigFlags, args []string) error { - kArgs := getKubectlConfigFlags(flags) - kArgs = append(kArgs, args...) - return execCommand(append([]string{"kubectl"}, kArgs...)) + kubectlArgs := getKubectlConfigFlags(flags) + kubectlArgs = append(kubectlArgs, args...) + return execCommand(append([]string{"kubectl"}, kubectlArgs...)) } // Replaces the currently running process with the given command @@ -70,6 +70,7 @@ func execCommand(args []string) error { // Runs a command and returns stdout func execToWriter(args []string, writer io.Writer) error { + //nolint:gosec // Ignore G204 error cmd := exec.Command(args[0], args[1:]...) op, err := cmd.StdoutPipe() @@ -77,7 +78,9 @@ func execToWriter(args []string, writer io.Writer) error { return err } - go io.Copy(writer, op) + go func() { + io.Copy(writer, op) //nolint:errcheck // Ignore the error + }() err = cmd.Run() if err != nil { return err @@ -104,7 +107,6 @@ func getKubectlConfigFlags(flags *genericclioptions.ConfigFlags) []string { appendStringFlag(o, flags.Password, "password") appendStringFlag(o, flags.ClusterName, "cluster") appendStringFlag(o, flags.AuthInfoName, "user") - //appendStringFlag(o, flags.Namespace, "namespace") appendStringFlag(o, flags.Context, "context") appendStringFlag(o, flags.APIServer, "server") appendBoolFlag(o, flags.Insecure, "insecure-skip-tls-verify") @@ -126,7 +128,7 @@ func appendBoolFlag(out *[]string, in *bool, flag string) { } } -func appendStringArrayFlag(out *[]string, in *[]string, flag string) { +func appendStringArrayFlag(out, in *[]string, flag string) { if in != nil && len(*in) > 0 { *out = append(*out, fmt.Sprintf("--%v=%v'", flag, strings.Join(*in, ","))) } diff --git a/cmd/plugin/lints/deployment.go b/cmd/plugin/lints/deployment.go index a1c473f1e..ce1712284 100644 --- a/cmd/plugin/lints/deployment.go +++ b/cmd/plugin/lints/deployment.go @@ -35,7 +35,10 @@ type DeploymentLint struct { // Check returns true if the lint detects an issue func (lint DeploymentLint) Check(obj kmeta.Object) bool { - cmp := obj.(*v1.Deployment) + cmp, ok := obj.(*v1.Deployment) + if !ok { + util.PrintError(fmt.Errorf("unexpected type: %T", obj)) + } return lint.f(*cmp) } @@ -72,11 +75,11 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint { issue: issueNumber, version: version, f: func(dep v1.Deployment) bool { - if !isIngressNginxDeployment(dep) { + if !isIngressNginxDeployment(&dep) { return false } - args := getNginxArgs(dep) + args := getNginxArgs(&dep) for _, arg := range args { if strings.HasPrefix(arg, fmt.Sprintf("--%v", flag)) { return true @@ -88,8 +91,9 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint { } } -func getNginxArgs(dep v1.Deployment) []string { - for _, container := range dep.Spec.Template.Spec.Containers { +func getNginxArgs(dep *v1.Deployment) []string { + for i := range dep.Spec.Template.Spec.Containers { + container := &dep.Spec.Template.Spec.Containers[i] if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" { return container.Args } @@ -97,10 +101,10 @@ func getNginxArgs(dep v1.Deployment) []string { return make([]string, 0) } -func isIngressNginxDeployment(dep v1.Deployment) bool { +func isIngressNginxDeployment(dep *v1.Deployment) bool { containers := dep.Spec.Template.Spec.Containers - for _, container := range containers { - if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" { + for i := range containers { + if len(containers[i].Args) > 0 && containers[i].Args[0] == "/nginx-ingress-controller" { return true } } diff --git a/cmd/plugin/lints/ingress.go b/cmd/plugin/lints/ingress.go index ea08bfd8b..d5ad42e2c 100644 --- a/cmd/plugin/lints/ingress.go +++ b/cmd/plugin/lints/ingress.go @@ -30,13 +30,16 @@ type IngressLint struct { message string issue int version string - f func(ing networking.Ingress) bool + f func(ing *networking.Ingress) bool } // Check returns true if the lint detects an issue func (lint IngressLint) Check(obj kmeta.Object) bool { - ing := obj.(*networking.Ingress) - return lint.f(*ing) + ing, ok := obj.(*networking.Ingress) + if !ok { + util.PrintError(fmt.Errorf("unexpected type: %T", obj)) + } + return lint.f(ing) } // Message is a description of the lint @@ -94,7 +97,7 @@ func GetIngressLints() []IngressLint { } } -func xForwardedPrefixIsBool(ing networking.Ingress) bool { +func xForwardedPrefixIsBool(ing *networking.Ingress) bool { for name, val := range ing.Annotations { if strings.HasSuffix(name, "/x-forwarded-prefix") && (val == "true" || val == "false") { return true @@ -103,7 +106,7 @@ func xForwardedPrefixIsBool(ing networking.Ingress) bool { return false } -func annotationPrefixIsNginxCom(ing networking.Ingress) bool { +func annotationPrefixIsNginxCom(ing *networking.Ingress) bool { for name := range ing.Annotations { if strings.HasPrefix(name, "nginx.com/") { return true @@ -112,7 +115,7 @@ func annotationPrefixIsNginxCom(ing networking.Ingress) bool { return false } -func annotationPrefixIsNginxOrg(ing networking.Ingress) bool { +func annotationPrefixIsNginxOrg(ing *networking.Ingress) bool { for name := range ing.Annotations { if strings.HasPrefix(name, "nginx.org/") { return true @@ -121,7 +124,7 @@ func annotationPrefixIsNginxOrg(ing networking.Ingress) bool { return false } -func rewriteTargetWithoutCaptureGroup(ing networking.Ingress) bool { +func rewriteTargetWithoutCaptureGroup(ing *networking.Ingress) bool { for name, val := range ing.Annotations { if strings.HasSuffix(name, "/rewrite-target") && !strings.Contains(val, "$1") { return true @@ -135,7 +138,7 @@ func removedAnnotation(annotationName string, issueNumber int, version string) I message: fmt.Sprintf("Contains the removed %v annotation.", annotationName), issue: issueNumber, version: version, - f: func(ing networking.Ingress) bool { + f: func(ing *networking.Ingress) bool { for annotation := range ing.Annotations { if strings.HasSuffix(annotation, "/"+annotationName) { return true @@ -146,7 +149,7 @@ func removedAnnotation(annotationName string, issueNumber int, version string) I } } -func satisfyDirective(ing networking.Ingress) bool { +func satisfyDirective(ing *networking.Ingress) bool { for name, val := range ing.Annotations { if strings.HasSuffix(name, "/configuration-snippet") { return strings.Contains(val, "satisfy") diff --git a/cmd/plugin/main.go b/cmd/plugin/main.go index f3a809715..e9a8ea59a 100644 --- a/cmd/plugin/main.go +++ b/cmd/plugin/main.go @@ -24,7 +24,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" - //Just importing this is supposed to allow cloud authentication + // Just importing this is supposed to allow cloud authentication // eg GCP, AWS, Azure ... _ "k8s.io/client-go/plugin/pkg/client/auth" diff --git a/cmd/plugin/request/request.go b/cmd/plugin/request/request.go index fd47564a9..55df85d5e 100644 --- a/cmd/plugin/request/request.go +++ b/cmd/plugin/request/request.go @@ -35,7 +35,7 @@ import ( ) // ChoosePod finds a pod either by deployment or by name -func ChoosePod(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string) (apiv1.Pod, error) { +func ChoosePod(flags *genericclioptions.ConfigFlags, podName, deployment, selector string) (apiv1.Pod, error) { if podName != "" { return GetNamedPod(flags, podName) } @@ -54,9 +54,9 @@ func GetNamedPod(flags *genericclioptions.ConfigFlags, name string) (apiv1.Pod, return apiv1.Pod{}, err } - for _, pod := range allPods { - if pod.Name == name { - return pod, nil + for i := range allPods { + if allPods[i].Name == name { + return allPods[i], nil } } @@ -132,7 +132,7 @@ func GetIngressDefinitions(flags *genericclioptions.ConfigFlags, namespace strin } // GetNumEndpoints counts the number of endpointslices adresses for the service with the given name -func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, serviceName string) (*int, error) { +func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace, serviceName string) (*int, error) { epss, err := GetEndpointSlicesByName(flags, namespace, serviceName) if err != nil { return nil, err @@ -143,25 +143,26 @@ func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, ser } ret := 0 - for _, eps := range epss { - for _, ep := range eps.Endpoints { - ret += len(ep.Addresses) + for i := range epss { + eps := &epss[i] + for j := range eps.Endpoints { + ret += len(eps.Endpoints[j].Addresses) } } return &ret, nil } // GetEndpointSlicesByName returns the endpointSlices for the service with the given name -func GetEndpointSlicesByName(flags *genericclioptions.ConfigFlags, namespace string, name string) ([]discoveryv1.EndpointSlice, error) { +func GetEndpointSlicesByName(flags *genericclioptions.ConfigFlags, namespace, name string) ([]discoveryv1.EndpointSlice, error) { allEndpointsSlices, err := getEndpointSlices(flags, namespace) if err != nil { return nil, err } var eps []discoveryv1.EndpointSlice - for _, slice := range allEndpointsSlices { - if svcName, ok := slice.ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok { + for i := range allEndpointsSlices { + if svcName, ok := allEndpointsSlices[i].ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok { if svcName == name { - eps = append(eps, slice) + eps = append(eps, allEndpointsSlices[i]) } } } @@ -182,7 +183,7 @@ func getEndpointSlices(flags *genericclioptions.ConfigFlags, namespace string) ( tryAllNamespacesEndpointSlicesCache(flags) } - cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(flags, namespace) + cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(namespace) if cachedEndpointSlices != nil { return *cachedEndpointSlices, nil @@ -217,13 +218,13 @@ func tryAllNamespacesEndpointSlicesCache(flags *genericclioptions.ConfigFlags) { } } -func tryFilteringEndpointSlicesFromAllNamespacesCache(flags *genericclioptions.ConfigFlags, namespace string) *[]discoveryv1.EndpointSlice { +func tryFilteringEndpointSlicesFromAllNamespacesCache(namespace string) *[]discoveryv1.EndpointSlice { allEndpointSlices := endpointSlicesCache[""] if allEndpointSlices != nil { endpointSlices := make([]discoveryv1.EndpointSlice, 0) - for _, slice := range *allEndpointSlices { - if slice.Namespace == namespace { - endpointSlices = append(endpointSlices, slice) + for i := range *allEndpointSlices { + if (*allEndpointSlices)[i].Namespace == namespace { + endpointSlices = append(endpointSlices, (*allEndpointSlices)[i]) } } endpointSlicesCache[namespace] = &endpointSlices @@ -242,9 +243,9 @@ func GetServiceByName(flags *genericclioptions.ConfigFlags, name string, service services = &servicesArray } - for _, svc := range *services { - if svc.Name == name { - return svc, nil + for i := range *services { + if (*services)[i].Name == name { + return (*services)[i], nil } } @@ -288,7 +289,6 @@ func getLabeledPods(flags *genericclioptions.ConfigFlags, label string) ([]apiv1 pods, err := api.Pods(namespace).List(context.TODO(), metav1.ListOptions{ LabelSelector: label, }) - if err != nil { return make([]apiv1.Pod, 0), err } @@ -303,9 +303,9 @@ func getDeploymentPods(flags *genericclioptions.ConfigFlags, deployment string) } ingressPods := make([]apiv1.Pod, 0) - for _, pod := range pods { - if util.PodInDeployment(pod, deployment) { - ingressPods = append(ingressPods, pod) + for i := range pods { + if util.PodInDeployment(&pods[i], deployment) { + ingressPods = append(ingressPods, pods[i]) } } @@ -331,5 +331,4 @@ func getServices(flags *genericclioptions.ConfigFlags) ([]apiv1.Service, error) } return services.Items, nil - } diff --git a/cmd/plugin/util/util.go b/cmd/plugin/util/util.go index e1910140d..7457b8c53 100644 --- a/cmd/plugin/util/util.go +++ b/cmd/plugin/util/util.go @@ -29,8 +29,8 @@ import ( // The default deployment and service names for ingress-nginx const ( - DefaultIngressDeploymentName = "ingress-nginx-controller" - DefaultIngressServiceName = "ingress-nginx-controller" + DefaultIngressDeploymentName = "ingress-nginx-controller" //#nosec G101 + DefaultIngressServiceName = "ingress-nginx-controller" //#nosec G101 DefaultIngressContainerName = "controller" ) @@ -47,17 +47,25 @@ func PrintError(e error) { } // ParseVersionString returns the major, minor, and patch numbers of a version string -func ParseVersionString(v string) (int, int, int, error) { +func ParseVersionString(v string) (major, minor, patch int, err error) { parts := versionRegex.FindStringSubmatch(v) if len(parts) != 4 { return 0, 0, 0, fmt.Errorf("could not parse %v as a version string (like 0.20.3)", v) } - major, _ := strconv.Atoi(parts[1]) - minor, _ := strconv.Atoi(parts[2]) - patch, _ := strconv.Atoi(parts[3]) - + major, err = strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, 0, err + } + minor, err = strconv.Atoi(parts[2]) + if err != nil { + return 0, 0, 0, err + } + patch, err = strconv.Atoi(parts[3]) + if err != nil { + return 0, 0, 0, err + } return major, minor, patch, nil } @@ -90,7 +98,7 @@ func isVersionLessThan(a, b string) bool { // PodInDeployment returns whether a pod is part of a deployment with the given name // a pod is considered to be in {deployment} if it is owned by a replicaset with a name of format {deployment}-otherchars -func PodInDeployment(pod apiv1.Pod, deployment string) bool { +func PodInDeployment(pod *apiv1.Pod, deployment string) bool { for _, owner := range pod.OwnerReferences { if owner.Controller == nil || !*owner.Controller || owner.Kind != "ReplicaSet" { continue @@ -138,7 +146,7 @@ func AddContainerFlag(cmd *cobra.Command) *string { // GetNamespace takes a set of kubectl flag values and returns the namespace we should be operating in func GetNamespace(flags *genericclioptions.ConfigFlags) string { namespace, _, err := flags.ToRawKubeConfigLoader().Namespace() - if err != nil || len(namespace) == 0 { + if err != nil || namespace == "" { namespace = apiv1.NamespaceDefault } return namespace diff --git a/deploy/static/provider/aws/deploy.yaml b/deploy/static/provider/aws/deploy.yaml index 41006c016..356967315 100644 --- a/deploy/static/provider/aws/deploy.yaml +++ b/deploy/static/provider/aws/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" kind: ConfigMap metadata: labels: @@ -328,7 +328,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -344,7 +344,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -377,7 +377,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -400,7 +400,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -411,6 +411,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -418,7 +422,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -442,7 +446,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -514,7 +518,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -525,7 +529,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -539,7 +543,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -561,7 +565,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -572,7 +576,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -588,7 +592,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -610,11 +614,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -623,7 +650,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml b/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml index 4e4f9ca9b..a5d179953 100644 --- a/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml +++ b/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" http-snippet: | server { listen 2443; @@ -335,7 +335,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -353,7 +353,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -386,7 +386,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -409,7 +409,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -420,6 +420,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -427,7 +431,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -451,7 +455,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -526,7 +530,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -537,7 +541,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -551,7 +555,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -573,7 +577,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -584,7 +588,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -600,7 +604,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -622,11 +626,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -635,7 +662,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/baremetal/deploy.yaml b/deploy/static/provider/baremetal/deploy.yaml index 3f015f001..42fcb3250 100644 --- a/deploy/static/provider/baremetal/deploy.yaml +++ b/deploy/static/provider/baremetal/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" kind: ConfigMap metadata: labels: @@ -328,7 +328,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -340,7 +340,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -372,7 +372,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -395,7 +395,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -406,6 +406,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -413,7 +417,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -436,7 +440,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -508,7 +512,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -519,7 +523,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -533,7 +537,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -555,7 +559,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -566,7 +570,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -582,7 +586,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -604,11 +608,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -617,7 +644,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/cloud/deploy.yaml b/deploy/static/provider/cloud/deploy.yaml index a914aca5e..6844384b1 100644 --- a/deploy/static/provider/cloud/deploy.yaml +++ b/deploy/static/provider/cloud/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" kind: ConfigMap metadata: labels: @@ -328,7 +328,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -340,7 +340,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -373,7 +373,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -396,7 +396,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -407,6 +407,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -414,7 +418,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -438,7 +442,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -510,7 +514,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -521,7 +525,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -535,7 +539,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -557,7 +561,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -568,7 +572,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -584,7 +588,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -606,11 +610,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -619,7 +646,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/do/deploy.yaml b/deploy/static/provider/do/deploy.yaml index 676ae1229..322e5788c 100644 --- a/deploy/static/provider/do/deploy.yaml +++ b/deploy/static/provider/do/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" use-proxy-protocol: "true" kind: ConfigMap metadata: @@ -329,7 +329,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -343,7 +343,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -376,7 +376,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -399,7 +399,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -410,6 +410,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -417,7 +421,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -441,7 +445,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -513,7 +517,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -524,7 +528,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -538,7 +542,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -560,7 +564,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -571,7 +575,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -587,7 +591,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -609,11 +613,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -622,7 +649,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/exoscale/deploy.yaml b/deploy/static/provider/exoscale/deploy.yaml index e9d3522f0..5112ac3cf 100644 --- a/deploy/static/provider/exoscale/deploy.yaml +++ b/deploy/static/provider/exoscale/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" kind: ConfigMap metadata: labels: @@ -328,7 +328,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -349,7 +349,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -382,7 +382,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -405,7 +405,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -423,7 +423,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -447,7 +447,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -510,6 +510,10 @@ spec: - name: webhook-cert secret: secretName: ingress-nginx-admission + updateStrategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate --- apiVersion: batch/v1 kind: Job @@ -519,7 +523,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -530,7 +534,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -544,7 +548,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -566,7 +570,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -577,7 +581,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -593,7 +597,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -615,11 +619,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -628,7 +655,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/kind/deploy.yaml b/deploy/static/provider/kind/deploy.yaml index 6a327b97f..f53d4bb80 100644 --- a/deploy/static/provider/kind/deploy.yaml +++ b/deploy/static/provider/kind/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" kind: ConfigMap metadata: labels: @@ -328,7 +328,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -340,7 +340,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -372,7 +372,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -395,7 +395,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -417,7 +417,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -442,7 +442,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -524,7 +524,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -535,7 +535,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -549,7 +549,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -571,7 +571,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -582,7 +582,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -598,7 +598,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -620,11 +620,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -633,7 +656,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/deploy/static/provider/oracle/deploy.yaml b/deploy/static/provider/oracle/deploy.yaml new file mode 100644 index 000000000..2205c5293 --- /dev/null +++ b/deploy/static/provider/oracle/deploy.yaml @@ -0,0 +1,676 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx + namespace: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - ingress-nginx-leader + resources: + - leases + verbs: + - get + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets + - namespaces + verbs: + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission +rules: +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx + namespace: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: v1 +data: + allow-snippet-annotations: "false" +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-controller + namespace: ingress-nginx +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + service.beta.kubernetes.io/oci-load-balancer-shape: flexible + service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "100" + service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10" + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-controller + namespace: ingress-nginx +spec: + externalTrafficPolicy: Local + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - appProtocol: http + name: http + port: 80 + protocol: TCP + targetPort: http + - appProtocol: https + name: https + port: 443 + protocol: TCP + targetPort: https + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: LoadBalancer +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-controller-admission + namespace: ingress-nginx +spec: + ports: + - appProtocol: https + name: https-webhook + port: 443 + targetPort: webhook + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-controller + namespace: ingress-nginx +spec: + minReadySeconds: 0 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + spec: + containers: + - args: + - /nginx-ingress-controller + - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + - --election-id=ingress-nginx-leader + - --controller-class=k8s.io/ingress-nginx + - --ingress-class=nginx + - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + - --validating-webhook=:8443 + - --validating-webhook-certificate=/usr/local/certificates/cert + - --validating-webhook-key=/usr/local/certificates/key + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: controller + ports: + - containerPort: 80 + name: http + protocol: TCP + - containerPort: 443 + name: https + protocol: TCP + - containerPort: 8443 + name: webhook + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 90Mi + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + runAsUser: 101 + volumeMounts: + - mountPath: /usr/local/certificates/ + name: webhook-cert + readOnly: true + dnsPolicy: ClusterFirst + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: ingress-nginx + terminationGracePeriodSeconds: 300 + volumes: + - name: webhook-cert + secret: + secretName: ingress-nginx-admission +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission-create + namespace: ingress-nginx +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission-create + spec: + containers: + - args: + - create + - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc + - --namespace=$(POD_NAMESPACE) + - --secret-name=ingress-nginx-admission + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 + imagePullPolicy: IfNotPresent + name: create + securityContext: + allowPrivilegeEscalation: false + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission-patch + namespace: ingress-nginx +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission-patch + spec: + containers: + - args: + - patch + - --webhook-name=ingress-nginx-admission + - --namespace=$(POD_NAMESPACE) + - --patch-mutating=false + - --secret-name=ingress-nginx-admission + - --patch-failure-policy=Fail + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 + imagePullPolicy: IfNotPresent + name: patch + securityContext: + allowPrivilegeEscalation: false + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: nginx +spec: + controller: k8s.io/ingress-nginx +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: ingress-nginx-controller-admission + namespace: ingress-nginx + path: /networking/v1/ingresses + failurePolicy: Fail + matchPolicy: Equivalent + name: validate.nginx.ingress.kubernetes.io + rules: + - apiGroups: + - networking.k8s.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - ingresses + sideEffects: None diff --git a/deploy/static/provider/oracle/kustomization.yaml b/deploy/static/provider/oracle/kustomization.yaml new file mode 100644 index 000000000..5c1dcff96 --- /dev/null +++ b/deploy/static/provider/oracle/kustomization.yaml @@ -0,0 +1,11 @@ +# NOTE: kustomize is not supported. This file exists only to be able to reference it from bases. +# https://kubectl.docs.kubernetes.io/references/kustomize/bases/ +# +# ``` +# namespace: ingress-nginx +# bases: +# - github.com/kubernetes/ingress-nginx/tree/main/deploy/static/provider/oracle +# ``` + +resources: + - deploy.yaml diff --git a/deploy/static/provider/scw/deploy.yaml b/deploy/static/provider/scw/deploy.yaml index 389e2a920..e08ebc990 100644 --- a/deploy/static/provider/scw/deploy.yaml +++ b/deploy/static/provider/scw/deploy.yaml @@ -15,7 +15,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx --- @@ -27,7 +27,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx --- @@ -39,7 +39,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx rules: @@ -129,7 +129,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx rules: @@ -148,7 +148,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx rules: - apiGroups: @@ -230,7 +230,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission rules: - apiGroups: @@ -249,7 +249,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx namespace: ingress-nginx roleRef: @@ -269,7 +269,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission namespace: ingress-nginx roleRef: @@ -288,7 +288,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io @@ -307,7 +307,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission roleRef: apiGroup: rbac.authorization.k8s.io @@ -320,7 +320,7 @@ subjects: --- apiVersion: v1 data: - allow-snippet-annotations: "true" + allow-snippet-annotations: "false" use-proxy-protocol: "true" kind: ConfigMap metadata: @@ -329,7 +329,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx --- @@ -343,7 +343,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -376,7 +376,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller-admission namespace: ingress-nginx spec: @@ -399,7 +399,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-controller namespace: ingress-nginx spec: @@ -410,6 +410,10 @@ spec: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate template: metadata: labels: @@ -417,7 +421,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 spec: containers: - args: @@ -441,7 +445,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407 + image: registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -513,7 +517,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create namespace: ingress-nginx spec: @@ -524,7 +528,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-create spec: containers: @@ -538,7 +542,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: create securityContext: @@ -560,7 +564,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch namespace: ingress-nginx spec: @@ -571,7 +575,7 @@ spec: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission-patch spec: containers: @@ -587,7 +591,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794@sha256:01d181618f270f2a96c04006f33b2699ad3ccb02da48d0f89b22abce084b292f + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80 imagePullPolicy: IfNotPresent name: patch securityContext: @@ -609,11 +613,34 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: nginx spec: controller: k8s.io/ingress-nginx --- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.9.4 + name: ingress-nginx-admission + namespace: ingress-nginx +spec: + egress: + - {} + podSelector: + matchLabels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + policyTypes: + - Ingress + - Egress +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: @@ -622,7 +649,7 @@ metadata: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx - app.kubernetes.io/version: 1.7.1 + app.kubernetes.io/version: 1.9.4 name: ingress-nginx-admission webhooks: - admissionReviewVersions: diff --git a/docs/deploy/index.md b/docs/deploy/index.md index e33ee5dbe..ae28c2502 100644 --- a/docs/deploy/index.md +++ b/docs/deploy/index.md @@ -59,10 +59,16 @@ It will install the controller in the `ingress-nginx` namespace, creating that n - if the ingress controller is not installed, it will install it, - if the ingress controller is already installed, it will upgrade it. +**If you want a full list of values that you can set, while installing with Helm,** then run: + +```console +helm show values ingress-nginx --repo https://kubernetes.github.io/ingress-nginx +``` + **If you don't have Helm** or if you prefer to use a YAML manifest, you can run the following command instead: ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml ``` !!! info @@ -70,10 +76,15 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/cont resources as if you had used Helm to install the controller. !!! attention - If you are running an old version of Kubernetes (1.18 or earlier), please read - [this paragraph](#running-on-Kubernetes-versions-older-than-1.19) for specific instructions. - Because of api deprecations, the default manifest may not work on your cluster. - Specific manifests for supported Kubernetes versions are available within a sub-folder of each provider. + If you are running an old version of Kubernetes (1.18 or earlier), please read [this paragraph](#running-on-Kubernetes-versions-older-than-1.19) for specific instructions. + Because of api deprecations, the default manifest may not work on your cluster. + Specific manifests for supported Kubernetes versions are available within a sub-folder of each provider. + +### Firewall configuration + +To check which ports are used by your installation of ingress-nginx, look at the output of `kubectl -n ingress-nginx get pod -o yaml`. In general, you need: +- Port 8443 open between all hosts on which the kubernetes nodes are running. This is used for the ingress-nginx [admission controller](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/). +- Port 80 (for HTTP) and/or 443 (for HTTPS) open to the public on the kubernetes nodes to which the DNS of your apps are pointing. ### Pre-flight check @@ -93,6 +104,7 @@ kubectl wait --namespace ingress-nginx \ --timeout=120s ``` + ### Local testing Let's create a simple web server and the associated service: @@ -115,7 +127,19 @@ Now, forward a local port to the ingress controller: kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80 ``` -At this point, if you access http://demo.localdev.me:8080/, you should see an HTML page telling you "It works!". +!!! info + A note on DNS & network-connection. + This documentation assumes that a user has awareness of the DNS and the network routing aspects involved in using ingress. + The port-forwarding mentioned above, is the easiest way to demo the working of ingress. The "kubectl port-forward..." command above has forwarded the port number 8080, on the localhost's tcp/ip stack, where the command was typed, to the port number 80, of the service created by the installation of ingress-nginx controller. So now, the traffic sent to port number 8080 on localhost will reach the port number 80, of the ingress-controller's service. + Port-forwarding is not for a production environment use-case. But here we use port-forwarding, to simulate a HTTP request, originating from outside the cluster, to reach the service of the ingress-nginx controller, that is exposed to receive traffic from outside the cluster. + [This issue](https://github.com/kubernetes/ingress-nginx/issues/10014#issuecomment-1567791549described) shows a typical DNS problem and its solution. + +At this point, you can access your deployment using curl ; +```console +curl --resolve demo.localdev.me:8080:127.0.0.1 http://demo.localdev.me:8080 +``` + +You should see a HTML response containing text like **"It works!"**. ### Online testing @@ -225,7 +249,7 @@ In AWS, we use a Network load balancer (NLB) to expose the Ingress-Nginx Control ##### Network Load Balancer (NLB) ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/aws/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/aws/deploy.yaml ``` ##### TLS termination in AWS Load Balancer (NLB) @@ -233,10 +257,10 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/cont By default, TLS is terminated in the ingress controller. But it is also possible to terminate TLS in the Load Balancer. This section explains how to do that on AWS using an NLB. -1. Download the [deploy.yaml](https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml) template +1. Download the [deploy.yaml](https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml) template ```console - wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml + wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml ``` 2. Edit the file and change the VPC CIDR in use for the Kubernetes cluster: @@ -282,7 +306,7 @@ Then, the ingress controller can be installed like this: ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml ``` !!! warning @@ -299,7 +323,7 @@ Proxy-protocol is supported in GCE check the [Official Documentations on how to #### Azure ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml ``` More information with regard to Azure annotations for ingress controller can be found in the [official AKS documentation](https://docs.microsoft.com/en-us/azure/aks/ingress-internal-ip#create-an-ingress-controller). @@ -307,7 +331,7 @@ More information with regard to Azure annotations for ingress controller can be #### Digital Ocean ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/do/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/do/deploy.yaml ``` - By default the service object of the ingress-nginx-controller for Digital-Ocean, only configures one annotation. Its this one `service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true"`. While this makes the service functional, it was reported that the Digital-Ocean LoadBalancer graphs shows `no data`, unless a few other annotations are also configured. Some of these other annotations require values that can not be generic and hence not forced in a out-of-the-box installation. These annotations and a discussion on them is well documented in [this issue](https://github.com/kubernetes/ingress-nginx/issues/8965). Please refer to the issue to add annotations, with values specific to user, to get graphs of the DO-LB populated with data. @@ -315,7 +339,7 @@ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/cont #### Scaleway ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/scw/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/scw/deploy.yaml ``` #### Exoscale @@ -330,7 +354,7 @@ The full list of annotations supported by Exoscale is available in the Exoscale #### Oracle Cloud Infrastructure ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml ``` A @@ -357,7 +381,7 @@ For quick testing, you can use a This should work on almost every cluster, but it will typically use a port in the range 30000-32767. ```console -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/baremetal/deploy.yaml +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/baremetal/deploy.yaml ``` For more information about bare metal deployments (and how to use port 80 instead of a random port in the 30000-32767 range), diff --git a/docs/deploy/rbac.md b/docs/deploy/rbac.md index 8c36d19a7..70af8ba92 100644 --- a/docs/deploy/rbac.md +++ b/docs/deploy/rbac.md @@ -29,39 +29,38 @@ namespace specific permissions defined by the `Role` named `ingress-nginx`. These permissions are granted in order for the ingress-nginx-controller to be able to function as an ingress across the cluster. These permissions are -granted to the ClusterRole named `ingress-nginx` +granted to the `ClusterRole` named `ingress-nginx` * `configmaps`, `endpoints`, `nodes`, `pods`, `secrets`: list, watch * `nodes`: get -* `services`, `ingresses`: get, list, watch +* `services`, `ingresses`, `ingressclasses`, `endpointslices`: get, list, watch * `events`: create, patch * `ingresses/status`: update +* `leases`: list, watch ### Namespace Permissions These permissions are granted specific to the ingress-nginx namespace. These -permissions are granted to the Role named `ingress-nginx` +permissions are granted to the `Role` named `ingress-nginx` * `configmaps`, `pods`, `secrets`: get * `endpoints`: get Furthermore to support leader-election, the ingress-nginx-controller needs to -have access to a `configmap` using the resourceName `ingress-controller-leader-nginx` +have access to a `leases` using the resourceName `ingress-nginx-leader` > Note that resourceNames can NOT be used to limit requests using the β€œcreate” > verb because authorizers only have access to information that can be obtained > from the request URL, method, and headers (resource names in a β€œcreate” request > are part of the request body). -* `configmaps`: get, update (for resourceName `ingress-controller-leader-nginx`) -* `configmaps`: create +* `leases`: get, update (for resourceName `ingress-controller-leader`) +* `leases`: create -This resourceName is the concatenation of the `election-id` and the -`ingress-class` as defined by the ingress-controller, which defaults to: +This resourceName is the `election-id` defined by the ingress-controller, which defaults to: * `election-id`: `ingress-controller-leader` -* `ingress-class`: `nginx` -* `resourceName` : `-` +* `resourceName` : `` Please adapt accordingly if you overwrite either parameter when launching the ingress-nginx-controller. diff --git a/docs/e2e-tests.md b/docs/e2e-tests.md index 9b3c5f2ff..8418d7b31 100644 --- a/docs/e2e-tests.md +++ b/docs/e2e-tests.md @@ -6,901 +6,553 @@ Do not try to edit it manually. # e2e test suite for [Ingress NGINX Controller](https://github.com/kubernetes/ingress-nginx/tree/main/) - -### [[Admission] admission controller](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L35) - -- [reject ingress with global-rate-limit annotations when memcached is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L43) -- [should not allow overlaps of host and paths without canary annotations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L70) -- [should allow overlaps of host and paths with canary annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L87) -- [should block ingress with invalid path](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L108) -- [should return an error if there is an error validating the ingress definition](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L125) -- [should return an error if there is an invalid value in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L136) -- [should return an error if there is a forbidden value in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L150) -- [should not return an error if the Ingress V1 definition is valid with Ingress Class](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L164) -- [should not return an error if the Ingress V1 definition is valid with IngressClass annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L180) -- [should return an error if the Ingress V1 definition contains invalid annotations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L196) -- [should not return an error for an invalid Ingress when it has unknown class](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/admission/admission.go#L207) - -### [affinitymode](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinitymode.go#L31) - -- [Balanced affinity mode should balance](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinitymode.go#L34) -- [Check persistent affinity mode](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinitymode.go#L67) - -### [server-alias](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/alias.go#L29) - -- [should return status code 200 for host 'foo' and 404 for 'bar'](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/alias.go#L36) -- [should return status code 200 for host 'foo' and 'bar'](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/alias.go#L62) -- [should return status code 200 for hosts defined in two ingresses, different path with one alias](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/alias.go#L87) - -### [app-root](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/approot.go#L28) - -- [should redirect to /foo](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/approot.go#L35) - -### [auth-tls-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L29) - -- [should set sslClientCertificate, sslVerifyClient and sslVerifyDepth with auth-tls-secret](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L36) -- [should set valid auth-tls-secret, sslVerify to off, and sslVerifyDepth to 2](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L84) -- [should 302 redirect to error page instead of 400 when auth-tls-error-page is set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L114) -- [should pass URL-encoded certificate to upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L161) -- [should validate auth-tls-verify-client](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L206) -- [should return 403 using auth-tls-match-cn with no matching CN from client](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L266) -- [should return 200 using auth-tls-match-cn with matching CN from client](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L295) -- [should return 200 using auth-tls-match-cn where atleast one of the regex options matches CN from client](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/authtls.go#L324) - -### [backend-protocol](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L27) - -- [should set backend protocol to https:// and use proxy_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L34) -- [should set backend protocol to $scheme:// and use proxy_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L49) -- [should set backend protocol to grpc:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L64) -- [should set backend protocol to grpcs:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L79) -- [should set backend protocol to '' and use fastcgi_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L94) -- [should set backend protocol to '' and use ajp_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L109) - -### [client-body-buffer-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L28) - -- [should set client_body_buffer_size to 1000](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L35) -- [should set client_body_buffer_size to 1K](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L57) -- [should set client_body_buffer_size to 1k](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L79) -- [should set client_body_buffer_size to 1m](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L101) -- [should set client_body_buffer_size to 1M](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L123) -- [should not set client_body_buffer_size to invalid 1b](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/clientbodybuffersize.go#L145) - -### [connection-proxy-header](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/connection.go#L29) - -- [set connection header to keep-alive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/connection.go#L36) - -### [cors-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L28) - -- [should enable cors](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L35) -- [should set cors methods to only allow POST, GET](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L62) -- [should set cors max-age](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L78) -- [should disable cors allow credentials](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L94) -- [should allow origin for cors](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L110) -- [should allow headers for cors](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L137) -- [should expose headers for cors](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L153) -- [should allow - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L169) -- [should not allow - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L196) -- [should allow correct origins - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L216) -- [should not break functionality](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L267) -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L291) -- [should not break functionality with extra domain](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L314) -- [should not match](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L338) -- [should allow - single origin with required port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L358) -- [should not allow - single origin with port and origin without port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L386) -- [should not allow - single origin without port and origin with required port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L405) -- [should allow - matching origin with wildcard origin (2 subdomains)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L425) -- [should not allow - unmatching origin with wildcard origin (2 subdomains)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L468) -- [should allow - matching origin+port with wildcard origin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L488) -- [should not allow - portless origin with wildcard origin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L515) -- [should allow correct origins - missing subdomain + origin with wildcard origin and correct origin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L535) -- [should allow - missing origins (should allow all origins)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/cors.go#L571) - -### [custom-http-errors](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/customhttperrors.go#L34) - -- [configures Nginx correctly](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/customhttperrors.go#L41) - -### [default-backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/default_backend.go#L29) - -- [should use a custom default backend as upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/default_backend.go#L37) - -### [disable-access-log disable-http-access-log disable-stream-access-log](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L28) - -- [disable-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L35) -- [disable-http-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L53) -- [disable-stream-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L71) - -### [force-ssl-redirect](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/forcesslredirect.go#L27) - -- [should redirect to https](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/forcesslredirect.go#L34) - -### [from-to-www-redirect](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fromtowwwredirect.go#L31) - -- [should redirect from www HTTP to HTTP](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fromtowwwredirect.go#L38) -- [should redirect from www HTTPS to HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fromtowwwredirect.go#L64) - -### [annotation-global-rate-limit](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/globalratelimit.go#L30) - -- [generates correct configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/globalratelimit.go#L38) - -### [backend-protocol - GRPC](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/grpc.go#L40) - -- [should use grpc_pass in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/grpc.go#L43) -- [should return OK for service with backend protocol GRPC](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/grpc.go#L68) -- [authorization metadata should be overwritten by external auth response headers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/grpc.go#L126) -- [should return OK for service with backend protocol GRPCS](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/grpc.go#L199) - -### [http2-push-preload](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/http2pushpreload.go#L27) - -- [enable the http2-push-preload directive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/http2pushpreload.go#L34) - -### [whitelist-source-range](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipwhitelist.go#L27) - -- [should set valid ip whitelist range](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipwhitelist.go#L34) - -### [Annotation - limit-connections](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/limitconnections.go#L31) - -- [should limit-connections](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/limitconnections.go#L38) - -### [limit-rate](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/limitrate.go#L29) - -- [Check limit-rate annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/limitrate.go#L37) - -### [enable-access-log enable-rewrite-log](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/log.go#L27) - -- [set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/log.go#L34) -- [set rewrite_log on](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/log.go#L49) - -### [mirror-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/mirror.go#L28) - -- [should set mirror-target to http://localhost/mirror](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/mirror.go#L36) -- [should set mirror-target to https://test.env.com/$request_uri](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/mirror.go#L51) -- [should disable mirror-request-body](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/mirror.go#L67) - -### [modsecurity owasp](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L28) - -- [should enable modsecurity](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L35) -- [should enable modsecurity with transaction ID and OWASP rules](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L53) -- [should disable modsecurity](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L74) -- [should enable modsecurity with snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L91) -- [should enable modsecurity without using 'modsecurity on;'](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L110) -- [should disable modsecurity using 'modsecurity off;'](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L132) -- [should enable modsecurity with snippet and block requests](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L153) -- [should enable modsecurity globally and with modsecurity-snippet block requests](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L189) -- [should enable modsecurity when enable-owasp-modsecurity-crs is set to true](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L225) -- [should enable modsecurity through the config map](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L264) -- [should enable modsecurity through the config map but ignore snippet as disabled by admin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L305) -- [should disable default modsecurity conf setting when modsecurity-snippet is specified](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/modsecurity/modsecurity.go#L347) - -### [preserve-trailing-slash](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/preservetrailingslash.go#L27) - -- [should allow preservation of trailing slashes](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/preservetrailingslash.go#L34) - -### [proxy-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L28) - -- [should set proxy_redirect to off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L36) -- [should set proxy_redirect to default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L52) -- [should set proxy_redirect to hello.com goodbye.com](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L68) -- [should set proxy client-max-body-size to 8m](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L85) -- [should not set proxy client-max-body-size to incorrect value](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L100) -- [should set valid proxy timeouts](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L115) -- [should not set invalid proxy timeouts](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L136) -- [should turn on proxy-buffering](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L157) -- [should turn off proxy-request-buffering](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L179) -- [should build proxy next upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L194) -- [should setup proxy cookies](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L215) -- [should change the default proxy HTTP version](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxy.go#L233) - -### [proxy-ssl-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L30) - -- [should set valid proxy-ssl-secret](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L37) -- [should set valid proxy-ssl-secret, proxy-ssl-verify to on, proxy-ssl-verify-depth to 2, and proxy-ssl-server-name to on](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L64) -- [should set valid proxy-ssl-secret, proxy-ssl-ciphers to HIGH:!AES](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L94) -- [should set valid proxy-ssl-secret, proxy-ssl-protocols](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L122) -- [proxy-ssl-location-only flag should change the nginx config server part](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/proxyssl.go#L150) - -### [permanent-redirect permanent-redirect-code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/redirect.go#L30) - -- [should respond with a standard redirect code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/redirect.go#L33) -- [should respond with a custom redirect code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/redirect.go#L61) - -### [satisfy](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/satisfy.go#L35) - -- [should configure satisfy directive correctly](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/satisfy.go#L42) -- [should allow multiple auth with satisfy any](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/satisfy.go#L84) - -### [server-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serversnippet.go#L28) - -- [add valid directives to server via server snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serversnippet.go#L35) -- [drops server snippet if disabled by the administrator](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serversnippet.go#L61) - -### [service-upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serviceupstream.go#L32) - -- [should use the Service Cluster IP and Port ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serviceupstream.go#L41) -- [should use the Service Cluster IP and Port ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serviceupstream.go#L70) -- [should not use the Service Cluster IP and Port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/serviceupstream.go#L99) - -### [configuration-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/snippet.go#L28) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/snippet.go#L35) -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/snippet.go#L58) - -### [ssl-ciphers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/sslciphers.go#L28) - -- [should change ssl ciphers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/sslciphers.go#L35) - -### [stream-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/streamsnippet.go#L34) - -- [should add value of stream-snippet to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/streamsnippet.go#L41) -- [should add stream-snippet and drop annotations per admin config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/streamsnippet.go#L85) - -### [upstream-hash-by-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/upstreamhashby.go#L76) - -- [should connect to the same pod](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/upstreamhashby.go#L83) -- [should connect to the same subset of pods](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/upstreamhashby.go#L92) - -### [upstream-vhost](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/upstreamvhost.go#L27) - -- [set host to upstreamvhost.bar.com](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/upstreamvhost.go#L34) - -### [x-forwarded-prefix](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/xforwardedprefix.go#L28) - -- [should set the X-Forwarded-Prefix to the annotation value](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/xforwardedprefix.go#L35) -- [should not add X-Forwarded-Prefix if the annotation value is empty](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/xforwardedprefix.go#L57) - -### [denylist-source-range](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L28) - -- [only deny explicitly denied IPs, allow all others](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L35) -- [only allow explicitly allowed IPs, deny all others](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L86) - -### [affinity session-cookie-name](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L35) - -- [should set sticky cookie SERVERID](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L42) -- [should change cookie name on ingress definition change](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L64) -- [should set the path to /something on the generated cookie](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L99) -- [does not set the path to / on the generated cookie if there's more than one rule referring to the same backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L121) -- [should set cookie with expires](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L194) -- [should set cookie with domain](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L225) -- [should not set cookie without domain annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L248) -- [should work with use-regex annotation and session-cookie-path](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L270) -- [should warn user when use-regex is true and session-cookie-path is not set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L294) -- [should not set affinity across all server locations when using separate ingresses](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L320) -- [should set sticky cookie without host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L352) -- [should work with server-alias annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L372) -- [should set secure in cookie with provided true annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L412) -- [should not set secure in cookie with provided false annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L435) -- [should set secure in cookie with provided false annotation on https](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L458) - -### [rewrite-target use-regex enable-rewrite-log](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L30) - -- [should write rewrite logs](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L37) -- [should use correct longest path match](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L66) -- [should use ~* location modifier if regex annotation is present](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L111) -- [should fail to use longest match for documented warning](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L158) -- [should allow for custom rewrite parameters](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L190) - -### [backend-protocol - FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L30) - -- [should use fastcgi_pass in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L37) -- [should add fastcgi_index in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L54) -- [should add fastcgi_param in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L71) -- [should return OK for service with backend protocol FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L102) - -### [auth-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L39) - -- [should return status code 200 when no authentication is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L46) -- [should return status code 503 when authentication is configured with an invalid secret](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L65) -- [should return status code 401 when authentication is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L89) -- [should return status code 401 when authentication is configured and Authorization header is sent with invalid credentials](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L116) -- [should return status code 401 and cors headers when authentication and cors is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L144) -- [should return status code 200 when authentication is configured and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L172) -- [should return status code 200 when authentication is configured with a map and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L199) -- [should return status code 401 when authentication is configured with invalid content and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L227) -- [ when external auth is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L266) -- [ when external auth is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L284) -- [ when auth-headers are set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L301) -- [should set cache_key when external auth cache is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L322) -- [user retains cookie by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L411) -- [user does not retain cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L422) -- [user with annotated ingress retains cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L433) -- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L485) -- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L494) -- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L505) -- [should overwrite Foo header with auth response](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L529) -- [should not create additional upstream block when auth-keepalive is not set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L552) -- [should not create additional upstream block when host part of auth-url contains a variable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L570) -- [should not create additional upstream block when auth-keepalive is negative](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L590) -- [should not create additional upstream block when auth-keepalive is set with HTTP/2](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L609) -- [should create additional upstream block when auth-keepalive is set with HTTP/1.x](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L623) -- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L678) -- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L687) -- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L698) -- [should return status code 200 when signed in after auth backend is deleted ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L772) -- [should deny login for different location on same server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L792) -- [should deny login for different servers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L820) -- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L849) -- [should return 503 (location was denied)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L879) -- [should add error to the config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L887) - -### [canary-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L36) - -- [should response with a 200 status from the mainline upstream when requests are made to the mainline ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L48) -- [should return 404 status for requests to the canary if no matching ingress is found](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L80) -- [should return the correct status codes when endpoints are unavailable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L107) -- [should route requests to the correct upstream if mainline ingress is created before the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L161) -- [should route requests to the correct upstream if mainline ingress is created after the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L206) -- [should route requests to the correct upstream if the mainline ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L250) -- [should route requests to the correct upstream if the canary ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L307) -- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L372) -- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L426) -- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L490) -- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L532) -- [should routes to mainline upstream when the given Regex causes error](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L566) -- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L604) -- [respects always and never values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L643) -- [should route requests only to mainline if canary weight is 0](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L705) -- [should route requests only to canary if canary weight is 100](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L743) -- [should route requests only to canary if canary weight is equal to canary weight total](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L775) -- [should route requests split between mainline and canary if canary weight is 50](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L808) -- [should route requests split between mainline and canary if canary weight is 100 and weight total is 200](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L834) -- [should not use canary as a catch-all server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L863) -- [should not use canary with domain as a server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L891) -- [does not crash when canary ingress has multiple paths to the same non-matching backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L915) -- [always routes traffic to canary if first request was affinitized to canary (default behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L943) -- [always routes traffic to canary if first request was affinitized to canary (explicit sticky behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L1000) -- [routes traffic to either mainline or canary backend (legacy behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L1058) - -### [Debug CLI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L29) - -- [should list the backend servers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L37) -- [should get information for a specific backend server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L56) -- [should produce valid JSON for /dbg general](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L85) - -### [[Default Backend] custom service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/custom_default_backend.go#L33) - -- [uses custom default backend that returns 200 as status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/custom_default_backend.go#L36) - -### [[Default Backend]](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/default_backend.go#L30) - -- [should return 404 sending requests when only a default backend is running](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/default_backend.go#L33) -- [enables access logging for default backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/default_backend.go#L88) -- [disables access logging for default backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/default_backend.go#L105) - -### [[Default Backend] SSL](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/ssl.go#L26) - -- [should return a self generated SSL certificate](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/ssl.go#L29) - -### [[Default Backend] change default settings](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/with_hosts.go#L30) - -- [should apply the annotation to the default backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/defaultbackend/with_hosts.go#L38) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/e2e_test.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/e2e_test.go#L) - -### [[Endpointslices] long service name](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/endpointslices/longname.go#L29) - -- [should return 200 when service name has max allowed number of characters 63](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/endpointslices/longname.go#L38) - -### [[TopologyHints] topology aware routing](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/endpointslices/topology.go#L35) - -- [should return 200 when service has topology hints](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/endpointslices/topology.go#L43) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/grpc_fortune_teller.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/grpc_fortune_teller.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/array.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/array.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/chain.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/chain.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/cookie.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/cookie.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/match.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/match.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/object.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/object.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/reporter.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/reporter.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/response.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/response.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/string.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/string.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/value.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/value.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/request.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/httpexpect/request.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/logs.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/logs.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/ssl.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/ssl.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/test_context.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/test_context.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/util.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/util.go#L) - -### [[Setting] ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L194) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/healthz.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/healthz.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/k8s.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/k8s.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/metrics.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/metrics.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/deployment.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/deployment.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/exec.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/exec.go#L) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/fastcgi_helloserver.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/fastcgi_helloserver.go#L) - -### [[Shutdown] Grace period shutdown](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/grace_period.go#L32) - -- [/healthz should return status code 500 during shutdown grace period](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/grace_period.go#L35) - -### [[Shutdown] ingress controller](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/shutdown.go#L30) - -- [should shutdown in less than 60 secons without pending connections](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/shutdown.go#L40) -- [should shutdown after waiting 60 seconds for pending connections to be closed](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/shutdown.go#L61) -- [should shutdown after waiting 150 seconds for pending connections to be closed](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/shutdown.go#L106) - -### [[Shutdown] Graceful shutdown with pending request](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/slow_requests.go#L25) - -- [should let slow requests finish before shutting down](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/slow_requests.go#L33) - -### [[Ingress] DeepInspection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/deep_inspection.go#L27) - -- [should drop whole ingress if one path matches invalid regex](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/deep_inspection.go#L34) - -### [single ingress - multiple hosts](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/multiple_rules.go#L30) - -- [should set the correct $service_name NGINX variable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/multiple_rules.go#L38) - -### [[Ingress] [PathType] exact](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_exact.go#L30) - -- [should choose exact location for /exact](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_exact.go#L37) - -### [[Ingress] [PathType] mix Exact and Prefix paths](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_mixed.go#L30) - -- [should choose the correct location](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_mixed.go#L39) - -### [[Ingress] [PathType] prefix checks](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_prefix.go#L28) - -- [should return 404 when prefix /aaa does not match request /aaaccc](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/pathtype_prefix.go#L35) - -### [[Ingress] definition without host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/without_host.go#L31) - -- [should set ingress details variables for ingresses without a host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/without_host.go#L34) -- [should set ingress details variables for ingresses with host without IngressRuleValue, only Backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ingress/without_host.go#L55) - -### [[Memory Leak] Dynamic Certificates](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/leaks/lua_ssl.go#L35) - -- [should not leak memory from ingress SSL certificates or configuration updates](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/leaks/lua_ssl.go#L42) - -### [[Load Balancer] load-balance](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/configmap.go#L28) - -- [should apply the configmap load-balance setting](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/configmap.go#L35) - -### [[Load Balancer] EWMA](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/ewma.go#L31) - -- [does not fail requests](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/ewma.go#L42) - -### [[Load Balancer] round-robin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/round_robin.go#L31) - -- [should evenly distribute requests with round-robin (default algorithm)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/loadbalance/round_robin.go#L39) - -### [[Lua] dynamic certificates](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L37) - -- [picks up the certificate when we add TLS spec to existing ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L45) -- [picks up the previously missing secret for a given ingress without reloading](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L70) -- [supports requests with domain with trailing dot](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L145) -- [picks up the updated certificate without reloading](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L149) -- [falls back to using default certificate when secret gets deleted without reloading](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L185) -- [picks up a non-certificate only change](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L218) -- [removes HTTPS configuration when we delete TLS spec](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_certificates.go#L233) - -### [[Lua] dynamic configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L42) - -- [configures balancer Lua middleware correctly](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L50) -- [handles endpoints only changes](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L62) -- [handles endpoints only changes (down scaling of replicas)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L87) -- [handles endpoints only changes consistently (down scaling of replicas vs. empty service)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L125) -- [handles an annotation change](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/lua/dynamic_configuration.go#L171) - -### [nginx-configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/nginx/nginx.go#L99) - -- [start nginx with default configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/nginx/nginx.go#L102) -- [fails when using alias directive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/nginx/nginx.go#L115) -- [fails when using root directive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/nginx/nginx.go#L124) - -### [[Security] request smuggling](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/security/request_smuggling.go#L32) - -- [should not return body content from error_page](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/security/request_smuggling.go#L39) - -### [[Service] backend status code 503](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_backend.go#L33) - -- [should return 503 when backend service does not exist](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_backend.go#L36) -- [should return 503 when all backend service endpoints are unavailable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_backend.go#L54) - -### [[Service] Type ExternalName](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L59) - -- [works with external name set to incomplete fqdn](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L62) -- [should return 200 for service type=ExternalName without a port defined](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L95) -- [should return 200 for service type=ExternalName with a port defined](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L129) -- [should return status 502 for service type=ExternalName with an invalid host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L153) -- [should return 200 for service type=ExternalName using a port name](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L184) -- [should return 200 for service type=ExternalName using FQDN with trailing dot](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L217) -- [should update the external name after a service update](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L248) -- [should sync ingress on external name service addition/deletion](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_externalname.go#L311) - -### [[Service] Nil Service Backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_nil_backend.go#L31) - -- [should return 404 when backend service is nil](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/servicebackend/service_nil_backend.go#L38) - -### [access-log](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L27) - -- [use the default configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L32) -- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L42) -- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L54) -- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L67) -- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/access_log.go#L80) - -### [Bad annotation values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/badannotationvalues.go#L29) - -- [[BAD_ANNOTATIONS] should drop an ingress if there is an invalid character in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/badannotationvalues.go#L36) -- [[BAD_ANNOTATIONS] should drop an ingress if there is a forbidden word in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/badannotationvalues.go#L67) -- [[BAD_ANNOTATIONS] should allow an ingress if there is a default blocklist config in place](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/badannotationvalues.go#L102) -- [[BAD_ANNOTATIONS] should drop an ingress if there is a custom blocklist config in place and allow others to pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/badannotationvalues.go#L133) - -### [brotli](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/brotli.go#L30) - -- [ condition](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/brotli.go#L39) - -### [add-headers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/custom_header.go#L30) - -- [Add a custom header](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/custom_header.go#L40) -- [Add multiple custom headers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/custom_header.go#L65) - -### [[SSL] [Flag] default-ssl-certificate](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/default_ssl_certificate.go#L33) - -- [uses default ssl certificate for catch-all ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/default_ssl_certificate.go#L64) -- [uses default ssl certificate for host based ingress when configured certificate does not match host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/default_ssl_certificate.go#L80) - -### [[Flag] disable-catch-all](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_catch_all.go#L33) - -- [should ignore catch all Ingress with backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_catch_all.go#L50) -- [should ignore catch all Ingress with backend and rules](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_catch_all.go#L69) -- [should delete Ingress updated to catch-all](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_catch_all.go#L81) -- [should allow Ingress with rules](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_catch_all.go#L123) - -### [[Flag] disable-service-external-name](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_service_external_name.go#L35) - -- [should ignore services of external-name type](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_service_external_name.go#L52) - -### [enable-real-ip](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/enable_real_ip.go#L30) - -- [trusts X-Forwarded-For header only when setting is true](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/enable_real_ip.go#L40) -- [should not trust X-Forwarded-For header when setting is false](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/enable_real_ip.go#L78) - -### [use-forwarded-headers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/forwarded_headers.go#L30) - -- [should trust X-Forwarded headers when setting is true](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/forwarded_headers.go#L40) -- [should not trust X-Forwarded headers when setting is false](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/forwarded_headers.go#L92) - -### [Geoip2](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/geoip2.go#L37) - -- [should include geoip2 line in config when enabled and db file exists](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/geoip2.go#L46) -- [should only allow requests from specific countries](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/geoip2.go#L70) - -### [[Security] block-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_access_block.go#L28) - -- [should block CIDRs defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_access_block.go#L38) -- [should block User-Agents defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_access_block.go#L55) -- [should block Referers defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_access_block.go#L88) - -### [[Security] global-auth-url](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L34) - -- [should return status code 401 when request any protected service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L85) -- [should return status code 200 when request whitelisted (via no-auth-locations) service and 401 when request protected service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L102) -- [should return status code 200 when request whitelisted (via ingress annotation) service and 401 when request protected service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L126) -- [should still return status code 200 after auth backend is deleted using cache](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L155) -- [should proxy_method method when global-auth-method is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L197) -- [should add custom error page when global-auth-signin url is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L210) -- [should add auth headers when global-auth-response-headers is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L223) -- [should set request-redirect when global-auth-request-redirect is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L237) -- [should set snippet when global external auth is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L250) -- [user retains cookie by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L326) -- [user does not retain cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L337) -- [user with global-auth-always-set-cookie key in configmap retains cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_external_auth.go#L348) - -### [global-options](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_options.go#L28) - -- [should have worker_rlimit_nofile option](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_options.go#L31) -- [should have worker_rlimit_nofile option and be independent on amount of worker processes](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/global_options.go#L38) - -### [settings-global-rate-limit](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/globalratelimit.go#L30) - -- [generates correct NGINX configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/globalratelimit.go#L38) - -### [hash size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L27) - -- [should set server_names_hash_bucket_size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L40) -- [should set server_names_hash_max_size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L48) -- [should set proxy-headers-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L60) -- [should set proxy-headers-hash-max-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L68) -- [should set variables-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L80) -- [should set variables-hash-max-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L88) -- [should set vmap-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/hash-size.go#L100) - -### [[Flag] ingress-class](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L39) - -- [should ignore Ingress with a different class annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L68) -- [should ignore Ingress with different controller class](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L104) -- [should accept both Ingresses with default IngressClassName and IngressClass annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L132) -- [should ignore Ingress without IngressClass configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L164) -- [should delete Ingress when class is removed](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L192) -- [should serve Ingress when class is added](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L257) -- [should serve Ingress when class is updated between annotation and ingressClassName](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L323) -- [should ignore Ingress with no class and accept the correctly configured Ingresses](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L413) -- [should watch Ingress with no class and ignore ingress with a different class](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L482) -- [should watch Ingress that uses the class name even if spec is different](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L539) -- [should watch Ingress with correct annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L631) -- [should ignore Ingress with only IngressClassName](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ingress_class.go#L652) - -### [keep-alive keep-alive-requests](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L28) - -- [should set keepalive_timeout](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L40) -- [should set keepalive_requests](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L48) -- [should set keepalive connection to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L59) -- [should set keep alive connection timeout to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L68) -- [should set keepalive time to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L77) -- [should set the request count to upstream server through one keep alive connection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/keep-alive.go#L86) - -### [Configmap - limit-rate](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/limit_rate.go#L28) - -- [Check limit-rate config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/limit_rate.go#L36) - -### [[Flag] custom HTTP and HTTPS ports](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/listen_nondefault_ports.go#L32) - -- [should set X-Forwarded-Port headers accordingly when listening on a non-default HTTP port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/listen_nondefault_ports.go#L48) -- [should set X-Forwarded-Port header to 443](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/listen_nondefault_ports.go#L70) -- [should set the X-Forwarded-Port header to 443](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/listen_nondefault_ports.go#L100) - -### [log-format-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L28) - -- [should not configure log-format escape by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L40) -- [should enable the log-format-escape-json](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L47) -- [should disable the log-format-escape-json](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L55) -- [should enable the log-format-escape-none](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L63) -- [should disable the log-format-escape-none](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L71) -- [log-format-escape-json enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L82) -- [log-format default escape](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L105) -- [log-format-escape-none enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/log-format.go#L128) - -### [[Lua] lua-shared-dicts](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/lua_shared_dicts.go#L26) - -- [configures lua shared dicts](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/lua_shared_dicts.go#L29) - -### [main-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/main_snippet.go#L27) - -- [should add value of main-snippet setting to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/main_snippet.go#L31) - -### [[Security] modsecurity-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/modsecurity/modsecurity_snippet.go#L27) - -- [should add value of modsecurity-snippet setting to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/modsecurity/modsecurity_snippet.go#L30) - -### [enable-multi-accept](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/multi_accept.go#L27) - -- [should be enabled by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/multi_accept.go#L31) -- [should be enabled when set to true](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/multi_accept.go#L39) -- [should be disabled when set to false](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/multi_accept.go#L49) - -### [Add no tls redirect locations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_tls_redirect_locations.go#L28) - -- [Check no tls redirect locations config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_tls_redirect_locations.go#L31) - -### [OCSP](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ocsp/ocsp.go#L42) - -- [should enable OCSP and contain stapling information in the connection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ocsp/ocsp.go#L49) - -### [Configure OpenTracing](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L48) - -- [should not exists opentracing directive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L58) -- [should exists opentracing directive when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L71) -- [should include opentracing_trust_incoming_span off directive when disabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L85) -- [should not exists opentracing_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L100) -- [should exists opentracing_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L115) -- [should not exists opentracing_location_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L130) -- [should exists opentracing_location_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L145) -- [should enable opentracing using zipkin](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L160) -- [should enable opentracing using jaeger](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L172) -- [should enable opentracing using jaeger with sampler host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L184) -- [should propagate the w3c header when configured with jaeger](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L197) -- [should enable opentracing using jaeger with an HTTP endpoint](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L228) -- [should enable opentracing using datadog](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentracing.go#L241) - -### [plugins](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/plugins.go#L28) - -- [should exist a x-hello-world header](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/plugins.go#L35) - -### [[Security] Pod Security Policies](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/pod_security_policy.go#L41) - -- [should be running with a Pod Security Policy](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/pod_security_policy.go#L44) - -### [[Security] Pod Security Policies with volumes](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/pod_security_policy_volumes.go#L37) - -- [should be running with a Pod Security Policy](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/pod_security_policy_volumes.go#L40) - -### [proxy-connect-timeout](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_connect_timeout.go#L28) - -- [should set valid proxy timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_connect_timeout.go#L36) -- [should not set invalid proxy timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_connect_timeout.go#L52) - -### [Dynamic $proxy_host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_host.go#L28) - -- [should exist a proxy_host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_host.go#L36) -- [should exist a proxy_host using the upstream-vhost annotation value](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_host.go#L57) - -### [proxy-next-upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_next_upstream.go#L28) - -- [should build proxy next upstream using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_next_upstream.go#L36) - -### [use-proxy-protocol](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_protocol.go#L36) - -- [should respect port passed by the PROXY Protocol](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_protocol.go#L46) -- [should respect proto passed by the PROXY Protocol server port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_protocol.go#L79) -- [should enable PROXY Protocol for HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_protocol.go#L112) -- [should enable PROXY Protocol for TCP](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_protocol.go#L155) - -### [proxy-read-timeout](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_read_timeout.go#L28) - -- [should set valid proxy read timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_read_timeout.go#L36) -- [should not set invalid proxy read timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_read_timeout.go#L52) - -### [proxy-send-timeout](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_send_timeout.go#L28) - -- [should set valid proxy send timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_send_timeout.go#L36) -- [should not set invalid proxy send timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/proxy_send_timeout.go#L52) - -### [reuse-port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/reuse-port.go#L27) - -- [reuse port should be enabled by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/reuse-port.go#L38) -- [reuse port should be disabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/reuse-port.go#L44) -- [reuse port should be enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/reuse-port.go#L52) - -### [configmap server-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_snippet.go#L28) - -- [should add value of server-snippet setting to all ingress config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_snippet.go#L35) -- [should add global server-snippet and drop annotations per admin config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_snippet.go#L92) - -### [server-tokens](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_tokens.go#L29) - -- [should not exists Server header in the response](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_tokens.go#L38) -- [should exists Server header in the response when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/server_tokens.go#L50) - -### [ssl-ciphers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ssl_ciphers.go#L28) - -- [Add ssl ciphers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ssl_ciphers.go#L31) - -### [configmap stream-snippet](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/stream_snippet.go#L35) - -- [should add value of stream-snippet via config map to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/stream_snippet.go#L42) - -### [[SSL] TLS protocols, ciphers and headers)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L31) - -- [setting cipher suite](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L65) -- [setting max-age parameter](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L111) -- [setting includeSubDomains parameter](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L127) -- [setting preload parameter](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L146) -- [overriding what's set from the upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L166) -- [should not use ports during the HTTP to HTTPS redirection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L187) -- [should not use ports or X-Forwarded-Host during the HTTP to HTTPS redirection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/tls.go#L205) - -### [[Flag] disable-sync-events](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_sync_events.go#L32) - -- [should create sync events (default)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_sync_events.go#L35) -- [should create sync events](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_sync_events.go#L53) -- [should not create sync events](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/disable_sync_events.go#L80) - -### [gzip](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L29) - -- [should be disabled by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L32) -- [should be enabled with default settings](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L39) -- [should set gzip_comp_level to 4](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L56) -- [should set gzip_disable to msie6](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L67) -- [should set gzip_min_length to 100](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L78) -- [should set gzip_types to application/javascript](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/gzip.go#L89) - -### [Configmap change](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/configmap_change.go#L29) - -- [should reload after an update in the configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/configmap_change.go#L36) - -### [[Flag] watch namespace selector](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L30) - -- [should ingore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L63) - -### [With enable-ssl-passthrough enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ssl_passthrough.go#L36) - -- [should enable ssl-passthrough-proxy-port on a different port](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ssl_passthrough.go#L56) -- [should pass unknown traffic to default backend and handle known traffic](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/ssl_passthrough.go#L79) - -### [[Security] no-auth-locations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L33) - -- [should return status code 401 when accessing '/' unauthentication](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L54) -- [should return status code 200 when accessing '/' authentication](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L68) -- [should return status code 200 when accessing '/noauth' unauthenticated](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L82) - -### [Configure Opentelemetry](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L37) - -- [should not exists opentelemetry directive](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L47) -- [should exists opentelemetry directive when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L60) -- [should include opentelemetry_trust_incoming_spans on directive when enabled](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L74) -- [should not exists opentelemetry_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L89) -- [should exists opentelemetry_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/opentelemetry.go#L104) - -### [[SSL] redirect to HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ssl/http_redirect.go#L29) - -- [should redirect from HTTP to HTTPS when secret is missing](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ssl/http_redirect.go#L36) - -### [[SSL] secret update](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ssl/secret_update.go#L33) - -- [should not appear references to secret updates not used in ingress rules](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ssl/secret_update.go#L40) -- [should return the fake SSL certificate if the secret is invalid](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/ssl/secret_update.go#L82) - -### [[Status] status update](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/status/update.go#L38) - -- [should update status field after client-go reconnection](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/status/update.go#L43) - -### [[TCP] tcp-services](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/tcpudp/tcp.go#L38) - -- [should expose a TCP service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/tcpudp/tcp.go#L46) -- [should expose an ExternalName TCP service](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/tcpudp/tcp.go#L80) -- [should reload after an update in the configuration](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/tcpudp/tcp.go#L169) - -### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/e2e.go#L) - -- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/e2e.go#L) - -### [[metrics] exported prometheus metrics](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/metrics/metrics.go#L36) - -- [exclude socket request metrics are absent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/metrics/metrics.go#L50) -- [exclude socket request metrics are present](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/metrics/metrics.go#L72) \ No newline at end of file +### [[Admission] admission controller](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L39) +- [reject ingress with global-rate-limit annotations when memcached is not configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L47) +- [should not allow overlaps of host and paths without canary annotations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L74) +- [should allow overlaps of host and paths with canary annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L91) +- [should block ingress with invalid path](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L112) +- [should return an error if there is an error validating the ingress definition](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L129) +- [should return an error if there is an invalid value in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L149) +- [should return an error if there is a forbidden value in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L163) +- [should return an error if there is an invalid path and wrong pathType is set](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L177) +- [should not return an error if the Ingress V1 definition is valid with Ingress Class](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L211) +- [should not return an error if the Ingress V1 definition is valid with IngressClass annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L227) +- [should return an error if the Ingress V1 definition contains invalid annotations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L243) +- [should not return an error for an invalid Ingress when it has unknown class](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/admission/admission.go#L263) +### [affinity session-cookie-name](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L43) +- [should set sticky cookie SERVERID](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L50) +- [should change cookie name on ingress definition change](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L72) +- [should set the path to /something on the generated cookie](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L107) +- [does not set the path to / on the generated cookie if there's more than one rule referring to the same backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L129) +- [should set cookie with expires](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L202) +- [should set cookie with domain](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L234) +- [should not set cookie without domain annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L257) +- [should work with use-regex annotation and session-cookie-path](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L279) +- [should warn user when use-regex is true and session-cookie-path is not set](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L303) +- [should not set affinity across all server locations when using separate ingresses](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L329) +- [should set sticky cookie without host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L361) +- [should work with server-alias annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L381) +- [should set secure in cookie with provided true annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L421) +- [should not set secure in cookie with provided false annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L444) +- [should set secure in cookie with provided false annotation on https](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinity.go#L467) +### [affinitymode](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinitymode.go#L33) +- [Balanced affinity mode should balance](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinitymode.go#L36) +- [Check persistent affinity mode](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/affinitymode.go#L69) +### [server-alias](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/alias.go#L31) +- [should return status code 200 for host 'foo' and 404 for 'bar'](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/alias.go#L38) +- [should return status code 200 for host 'foo' and 'bar'](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/alias.go#L64) +- [should return status code 200 for hosts defined in two ingresses, different path with one alias](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/alias.go#L89) +### [app-root](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/approot.go#L28) +- [should redirect to /foo](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/approot.go#L35) +### [auth-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L45) +- [should return status code 200 when no authentication is configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L52) +- [should return status code 503 when authentication is configured with an invalid secret](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L71) +- [should return status code 401 when authentication is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L95) +- [should return status code 401 when authentication is configured and Authorization header is sent with invalid credentials](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L122) +- [should return status code 401 and cors headers when authentication and cors is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L150) +- [should return status code 200 when authentication is configured and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L178) +- [should return status code 200 when authentication is configured with a map and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L205) +- [should return status code 401 when authentication is configured with invalid content and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L233) +- [proxy_set_header My-Custom-Header 42;](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L272) +- [proxy_set_header My-Custom-Header 42;](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L298) +- [proxy_set_header 'My-Custom-Header' '42';](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L324) +- [user retains cookie by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L432) +- [user does not retain cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L443) +- [user with annotated ingress retains cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L454) +- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L493) +- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L502) +- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L513) +- [should overwrite Foo header with auth response](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L537) +- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L713) +- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L722) +- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L733) +- [should return status code 200 when signed in after auth backend is deleted ](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L792) +- [should deny login for different location on same server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L812) +- [should deny login for different servers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L840) +- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L869) +- [should return 503 (location was denied)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L899) +- [should add error to the config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/auth.go#L907) +### [auth-tls-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L31) +- [should set sslClientCertificate, sslVerifyClient and sslVerifyDepth with auth-tls-secret](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L38) +- [should set valid auth-tls-secret, sslVerify to off, and sslVerifyDepth to 2](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L86) +- [should 302 redirect to error page instead of 400 when auth-tls-error-page is set](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L116) +- [should pass URL-encoded certificate to upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L163) +- [should validate auth-tls-verify-client](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L208) +- [should return 403 using auth-tls-match-cn with no matching CN from client](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L267) +- [should return 200 using auth-tls-match-cn with matching CN from client](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L296) +- [should return 200 using auth-tls-match-cn where atleast one of the regex options matches CN from client](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/authtls.go#L325) +### [backend-protocol](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L29) +- [should set backend protocol to https:// and use proxy_pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L36) +- [should set backend protocol to https:// and use proxy_pass with lowercase annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L51) +- [should set backend protocol to $scheme:// and use proxy_pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L66) +- [should set backend protocol to grpc:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L81) +- [should set backend protocol to grpcs:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L96) +- [should set backend protocol to '' and use fastcgi_pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/backendprotocol.go#L111) +### [canary-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L36) +- [should response with a 200 status from the mainline upstream when requests are made to the mainline ingress](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L45) +- [should return 404 status for requests to the canary if no matching ingress is found](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L89) +- [should return the correct status codes when endpoints are unavailable](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L120) +- [should route requests to the correct upstream if mainline ingress is created before the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L174) +- [should route requests to the correct upstream if mainline ingress is created after the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L232) +- [should route requests to the correct upstream if the mainline ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L289) +- [should route requests to the correct upstream if the canary ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L363) +- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L445) +- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L513) +- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L594) +- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L647) +- [should routes to mainline upstream when the given Regex causes error](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L692) +- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L741) +- [respects always and never values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L790) +- [should route requests only to mainline if canary weight is 0](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L862) +- [should route requests only to canary if canary weight is 100](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L910) +- [should route requests only to canary if canary weight is equal to canary weight total](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L952) +- [should route requests split between mainline and canary if canary weight is 50](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L995) +- [should route requests split between mainline and canary if canary weight is 100 and weight total is 200](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1031) +- [should not use canary as a catch-all server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1070) +- [should not use canary with domain as a server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1104) +- [does not crash when canary ingress has multiple paths to the same non-matching backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1138) +- [always routes traffic to canary if first request was affinitized to canary (default behavior)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1175) +- [always routes traffic to canary if first request was affinitized to canary (explicit sticky behavior)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1242) +- [routes traffic to either mainline or canary backend (legacy behavior)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/canary.go#L1310) +### [client-body-buffer-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L30) +- [should set client_body_buffer_size to 1000](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L37) +- [should set client_body_buffer_size to 1K](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L59) +- [should set client_body_buffer_size to 1k](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L81) +- [should set client_body_buffer_size to 1m](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L103) +- [should set client_body_buffer_size to 1M](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L125) +- [should not set client_body_buffer_size to invalid 1b](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/clientbodybuffersize.go#L147) +### [connection-proxy-header](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/connection.go#L28) +- [set connection header to keep-alive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/connection.go#L35) +### [cors-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L33) +- [should enable cors](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L40) +- [should set cors methods to only allow POST, GET](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L67) +- [should set cors max-age](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L83) +- [should disable cors allow credentials](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L99) +- [should allow origin for cors](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L115) +- [should allow headers for cors](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L142) +- [should expose headers for cors](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L158) +- [should allow - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L174) +- [should not allow - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L201) +- [should allow correct origins - single origin for multiple cors values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L221) +- [should not break functionality](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L272) +- [should not break functionality - without `*`](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L296) +- [should not break functionality with extra domain](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L319) +- [should not match](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L343) +- [should allow - single origin with required port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L363) +- [should not allow - single origin with port and origin without port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L391) +- [should not allow - single origin without port and origin with required port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L410) +- [should allow - matching origin with wildcard origin (2 subdomains)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L430) +- [should not allow - unmatching origin with wildcard origin (2 subdomains)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L473) +- [should allow - matching origin+port with wildcard origin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L493) +- [should not allow - portless origin with wildcard origin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L520) +- [should allow correct origins - missing subdomain + origin with wildcard origin and correct origin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L540) +- [should allow - missing origins (should allow all origins)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/cors.go#L576) +### [custom-http-errors](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/customhttperrors.go#L34) +- [configures Nginx correctly](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/customhttperrors.go#L41) +### [default-backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/default_backend.go#L29) +- [should use a custom default backend as upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/default_backend.go#L37) +### [disable-access-log disable-http-access-log disable-stream-access-log](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/disableaccesslog.go#L28) +- [disable-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/disableaccesslog.go#L35) +- [disable-http-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/disableaccesslog.go#L53) +- [disable-stream-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/disableaccesslog.go#L71) +### [backend-protocol - FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fastcgi.go#L30) +- [should use fastcgi_pass in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fastcgi.go#L37) +- [should add fastcgi_index in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fastcgi.go#L54) +- [should add fastcgi_param in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fastcgi.go#L71) +- [should return OK for service with backend protocol FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fastcgi.go#L102) +### [force-ssl-redirect](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/forcesslredirect.go#L27) +- [should redirect to https](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/forcesslredirect.go#L34) +### [from-to-www-redirect](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fromtowwwredirect.go#L31) +- [should redirect from www HTTP to HTTP](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fromtowwwredirect.go#L38) +- [should redirect from www HTTPS to HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/fromtowwwredirect.go#L64) +### [annotation-global-rate-limit](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/globalratelimit.go#L30) +- [generates correct configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/globalratelimit.go#L38) +### [backend-protocol - GRPC](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/grpc.go#L40) +- [should use grpc_pass in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/grpc.go#L43) +- [should return OK for service with backend protocol GRPC](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/grpc.go#L68) +- [authorization metadata should be overwritten by external auth response headers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/grpc.go#L128) +- [should return OK for service with backend protocol GRPCS](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/grpc.go#L189) +### [http2-push-preload](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/http2pushpreload.go#L27) +- [enable the http2-push-preload directive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/http2pushpreload.go#L34) +### [allowlist-source-range](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/ipallowlist.go#L27) +- [should set valid ip allowlist range](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/ipallowlist.go#L34) +### [denylist-source-range](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/ipdenylist.go#L28) +- [only deny explicitly denied IPs, allow all others](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/ipdenylist.go#L35) +- [only allow explicitly allowed IPs, deny all others](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/ipdenylist.go#L86) +### [Annotation - limit-connections](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/limitconnections.go#L31) +- [should limit-connections](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/limitconnections.go#L38) +### [limit-rate](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/limitrate.go#L29) +- [Check limit-rate annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/limitrate.go#L37) +### [enable-access-log enable-rewrite-log](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/log.go#L27) +- [set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/log.go#L34) +- [set rewrite_log on](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/log.go#L49) +### [mirror-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/mirror.go#L28) +- [should set mirror-target to http://localhost/mirror](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/mirror.go#L36) +- [should set mirror-target to https://test.env.com/$request_uri](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/mirror.go#L51) +- [should disable mirror-request-body](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/mirror.go#L67) +### [modsecurity owasp](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L39) +- [should enable modsecurity](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L46) +- [should enable modsecurity with transaction ID and OWASP rules](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L64) +- [should disable modsecurity](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L85) +- [should enable modsecurity with snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L102) +- [should enable modsecurity without using 'modsecurity on;'](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L130) +- [should disable modsecurity using 'modsecurity off;'](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L153) +- [should enable modsecurity with snippet and block requests](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L175) +- [should enable modsecurity globally and with modsecurity-snippet block requests](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L214) +- [should enable modsecurity when enable-owasp-modsecurity-crs is set to true](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L253) +- [should enable modsecurity through the config map](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L292) +- [should enable modsecurity through the config map but ignore snippet as disabled by admin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L338) +- [should disable default modsecurity conf setting when modsecurity-snippet is specified](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/modsecurity/modsecurity.go#L380) +### [preserve-trailing-slash](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/preservetrailingslash.go#L27) +- [should allow preservation of trailing slashes](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/preservetrailingslash.go#L34) +### [proxy-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L30) +- [should set proxy_redirect to off](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L38) +- [should set proxy_redirect to default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L54) +- [should set proxy_redirect to hello.com goodbye.com](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L70) +- [should set proxy client-max-body-size to 8m](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L87) +- [should not set proxy client-max-body-size to incorrect value](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L102) +- [should set valid proxy timeouts](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L117) +- [should not set invalid proxy timeouts](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L138) +- [should turn on proxy-buffering](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L159) +- [should turn off proxy-request-buffering](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L181) +- [should build proxy next upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L196) +- [should setup proxy cookies](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L217) +- [should change the default proxy HTTP version](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxy.go#L235) +### [proxy-ssl-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L32) +- [should set valid proxy-ssl-secret](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L39) +- [should set valid proxy-ssl-secret, proxy-ssl-verify to on, proxy-ssl-verify-depth to 2, and proxy-ssl-server-name to on](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L66) +- [should set valid proxy-ssl-secret, proxy-ssl-ciphers to HIGH:!AES](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L96) +- [should set valid proxy-ssl-secret, proxy-ssl-protocols](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L124) +- [proxy-ssl-location-only flag should change the nginx config server part](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/proxyssl.go#L152) +### [permanent-redirect permanent-redirect-code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/redirect.go#L30) +- [should respond with a standard redirect code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/redirect.go#L33) +- [should respond with a custom redirect code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/redirect.go#L61) +### [rewrite-target use-regex enable-rewrite-log](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L32) +- [should write rewrite logs](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L39) +- [should use correct longest path match](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L68) +- [should use ~* location modifier if regex annotation is present](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L113) +- [should fail to use longest match for documented warning](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L160) +- [should allow for custom rewrite parameters](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/rewrite.go#L192) +### [satisfy](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/satisfy.go#L33) +- [should configure satisfy directive correctly](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/satisfy.go#L40) +- [should allow multiple auth with satisfy any](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/satisfy.go#L82) +### [server-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/serversnippet.go#L28) +### [service-upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/serviceupstream.go#L32) +- [should use the Service Cluster IP and Port ](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/serviceupstream.go#L41) +- [should use the Service Cluster IP and Port ](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/serviceupstream.go#L69) +- [should not use the Service Cluster IP and Port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/serviceupstream.go#L97) +### [configuration-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/snippet.go#L28) +- [set snippet more_set_headers in all locations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/snippet.go#L34) +- [drops snippet more_set_header in all locations if disabled by admin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/snippet.go#L73) +### [ssl-ciphers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/sslciphers.go#L28) +- [should change ssl ciphers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/sslciphers.go#L35) +### [stream-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/streamsnippet.go#L34) +- [should add value of stream-snippet to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/streamsnippet.go#L41) +- [should add stream-snippet and drop annotations per admin config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/streamsnippet.go#L94) +### [upstream-hash-by-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/upstreamhashby.go#L79) +- [should connect to the same pod](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/upstreamhashby.go#L86) +- [should connect to the same subset of pods](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/upstreamhashby.go#L95) +### [upstream-vhost](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/upstreamvhost.go#L27) +- [set host to upstreamvhost.bar.com](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/upstreamvhost.go#L34) +### [x-forwarded-prefix](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/xforwardedprefix.go#L28) +- [should set the X-Forwarded-Prefix to the annotation value](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/xforwardedprefix.go#L35) +- [should not add X-Forwarded-Prefix if the annotation value is empty](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/annotations/xforwardedprefix.go#L57) +### [Debug CLI](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/dbg/main.go#L29) +- [should list the backend servers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/dbg/main.go#L37) +- [should get information for a specific backend server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/dbg/main.go#L56) +- [should produce valid JSON for /dbg general](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/dbg/main.go#L85) +### [[Default Backend] custom service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/custom_default_backend.go#L33) +- [uses custom default backend that returns 200 as status code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/custom_default_backend.go#L36) +### [[Default Backend]](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/default_backend.go#L30) +- [should return 404 sending requests when only a default backend is running](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/default_backend.go#L33) +- [enables access logging for default backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/default_backend.go#L88) +- [disables access logging for default backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/default_backend.go#L105) +### [[Default Backend] SSL](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/ssl.go#L26) +- [should return a self generated SSL certificate](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/ssl.go#L29) +### [[Default Backend] change default settings](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/with_hosts.go#L30) +- [should apply the annotation to the default backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/defaultbackend/with_hosts.go#L38) +### [[Endpointslices] long service name](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/endpointslices/longname.go#L29) +- [should return 200 when service name has max allowed number of characters 63](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/endpointslices/longname.go#L38) +### [[TopologyHints] topology aware routing](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/endpointslices/topology.go#L34) +- [should return 200 when service has topology hints](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/endpointslices/topology.go#L42) +### [[Shutdown] Grace period shutdown](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/grace_period.go#L32) +- [/healthz should return status code 500 during shutdown grace period](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/grace_period.go#L35) +### [[Shutdown] ingress controller](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/shutdown.go#L30) +- [should shutdown in less than 60 secons without pending connections](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/shutdown.go#L40) +### [[Shutdown] Graceful shutdown with pending request](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/slow_requests.go#L25) +- [should let slow requests finish before shutting down](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/gracefulshutdown/slow_requests.go#L33) +### [[Ingress] DeepInspection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/deep_inspection.go#L27) +- [should drop whole ingress if one path matches invalid regex](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/deep_inspection.go#L34) +### [single ingress - multiple hosts](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/multiple_rules.go#L30) +- [should set the correct $service_name NGINX variable](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/multiple_rules.go#L38) +### [[Ingress] [PathType] exact](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_exact.go#L30) +- [should choose exact location for /exact](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_exact.go#L37) +### [[Ingress] [PathType] mix Exact and Prefix paths](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_mixed.go#L30) +- [should choose the correct location](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_mixed.go#L39) +### [[Ingress] [PathType] prefix checks](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_prefix.go#L28) +- [should return 404 when prefix /aaa does not match request /aaaccc](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/pathtype_prefix.go#L35) +### [[Ingress] definition without host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/without_host.go#L31) +- [should set ingress details variables for ingresses without a host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/without_host.go#L34) +- [should set ingress details variables for ingresses with host without IngressRuleValue, only Backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ingress/without_host.go#L55) +### [[Memory Leak] Dynamic Certificates](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/leaks/lua_ssl.go#L35) +- [should not leak memory from ingress SSL certificates or configuration updates](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/leaks/lua_ssl.go#L42) +### [[Load Balancer] load-balance](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/configmap.go#L30) +- [should apply the configmap load-balance setting](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/configmap.go#L37) +### [[Load Balancer] EWMA](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/ewma.go#L31) +- [does not fail requests](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/ewma.go#L43) +### [[Load Balancer] round-robin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/round_robin.go#L31) +- [should evenly distribute requests with round-robin (default algorithm)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/loadbalance/round_robin.go#L39) +### [[Lua] dynamic certificates](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L37) +- [picks up the certificate when we add TLS spec to existing ingress](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L45) +- [picks up the previously missing secret for a given ingress without reloading](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L70) +- [supports requests with domain with trailing dot](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L145) +- [picks up the updated certificate without reloading](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L149) +- [falls back to using default certificate when secret gets deleted without reloading](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L185) +- [picks up a non-certificate only change](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L218) +- [removes HTTPS configuration when we delete TLS spec](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_certificates.go#L233) +### [[Lua] dynamic configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L41) +- [configures balancer Lua middleware correctly](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L49) +- [handles endpoints only changes](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L61) +- [handles endpoints only changes (down scaling of replicas)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L86) +- [handles endpoints only changes consistently (down scaling of replicas vs. empty service)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L124) +- [handles an annotation change](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/lua/dynamic_configuration.go#L170) +### [[metrics] exported prometheus metrics](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/metrics/metrics.go#L36) +- [exclude socket request metrics are absent](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/metrics/metrics.go#L50) +- [exclude socket request metrics are present](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/metrics/metrics.go#L72) +### [nginx-configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/nginx/nginx.go#L99) +- [start nginx with default configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/nginx/nginx.go#L102) +- [fails when using alias directive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/nginx/nginx.go#L114) +- [fails when using root directive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/nginx/nginx.go#L121) +### [[Security] request smuggling](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/security/request_smuggling.go#L32) +- [should not return body content from error_page](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/security/request_smuggling.go#L39) +### [[Service] backend status code 503](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_backend.go#L34) +- [should return 503 when backend service does not exist](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_backend.go#L37) +- [should return 503 when all backend service endpoints are unavailable](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_backend.go#L55) +### [[Service] Type ExternalName](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L38) +- [works with external name set to incomplete fqdn](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L41) +- [should return 200 for service type=ExternalName without a port defined](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L78) +- [should return 200 for service type=ExternalName with a port defined](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L118) +- [should return status 502 for service type=ExternalName with an invalid host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L148) +- [should return 200 for service type=ExternalName using a port name](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L184) +- [should return 200 for service type=ExternalName using FQDN with trailing dot](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L225) +- [should update the external name after a service update](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L261) +- [should sync ingress on external name service addition/deletion](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_externalname.go#L344) +### [[Service] Nil Service Backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_nil_backend.go#L31) +- [should return 404 when backend service is nil](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/servicebackend/service_nil_backend.go#L38) +### [access-log](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L27) +- [use the default configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L31) +- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L41) +- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L52) +- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L64) +- [use the specified configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/access_log.go#L76) +### [aio-write](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/aio_write.go#L27) +- [should be enabled by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/aio_write.go#L30) +- [should be enabled when setting is true](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/aio_write.go#L37) +- [should be disabled when setting is false](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/aio_write.go#L46) +### [Bad annotation values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/badannotationvalues.go#L29) +- [[BAD_ANNOTATIONS] should drop an ingress if there is an invalid character in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/badannotationvalues.go#L36) +- [[BAD_ANNOTATIONS] should drop an ingress if there is a forbidden word in some annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/badannotationvalues.go#L75) +- [[BAD_ANNOTATIONS] should allow an ingress if there is a default blocklist config in place](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/badannotationvalues.go#L119) +- [[BAD_ANNOTATIONS] should drop an ingress if there is a custom blocklist config in place and allow others to pass](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/badannotationvalues.go#L157) +### [brotli](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/brotli.go#L30) +- [should only compress responses that meet the `brotli-min-length` condition](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/brotli.go#L38) +### [Configmap change](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/configmap_change.go#L29) +- [should reload after an update in the configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/configmap_change.go#L36) +### [add-headers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/custom_header.go#L30) +- [Add a custom header](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/custom_header.go#L40) +- [Add multiple custom headers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/custom_header.go#L65) +### [[SSL] [Flag] default-ssl-certificate](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/default_ssl_certificate.go#L35) +- [uses default ssl certificate for catch-all ingress](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/default_ssl_certificate.go#L66) +- [uses default ssl certificate for host based ingress when configured certificate does not match host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/default_ssl_certificate.go#L82) +### [[Flag] disable-catch-all](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_catch_all.go#L33) +- [should ignore catch all Ingress with backend](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_catch_all.go#L50) +- [should ignore catch all Ingress with backend and rules](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_catch_all.go#L69) +- [should delete Ingress updated to catch-all](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_catch_all.go#L81) +- [should allow Ingress with rules](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_catch_all.go#L123) +### [[Flag] disable-service-external-name](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_service_external_name.go#L35) +- [should ignore services of external-name type](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_service_external_name.go#L55) +### [[Flag] disable-sync-events](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_sync_events.go#L32) +- [should create sync events (default)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_sync_events.go#L35) +- [should create sync events](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_sync_events.go#L54) +- [should not create sync events](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/disable_sync_events.go#L82) +### [enable-real-ip](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/enable_real_ip.go#L30) +- [trusts X-Forwarded-For header only when setting is true](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/enable_real_ip.go#L40) +- [should not trust X-Forwarded-For header when setting is false](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/enable_real_ip.go#L78) +### [use-forwarded-headers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/forwarded_headers.go#L31) +- [should trust X-Forwarded headers when setting is true](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/forwarded_headers.go#L41) +- [should not trust X-Forwarded headers when setting is false](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/forwarded_headers.go#L93) +### [Geoip2](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/geoip2.go#L36) +- [should include geoip2 line in config when enabled and db file exists](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/geoip2.go#L45) +- [should only allow requests from specific countries](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/geoip2.go#L69) +### [[Security] block-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_access_block.go#L28) +- [should block CIDRs defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_access_block.go#L38) +- [should block User-Agents defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_access_block.go#L55) +- [should block Referers defined in the ConfigMap](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_access_block.go#L88) +### [[Security] global-auth-url](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L39) +- [should return status code 401 when request any protected service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L91) +- [should return status code 200 when request whitelisted (via no-auth-locations) service and 401 when request protected service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L107) +- [should return status code 200 when request whitelisted (via ingress annotation) service and 401 when request protected service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L130) +- [should still return status code 200 after auth backend is deleted using cache](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L158) +- [user retains cookie by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L322) +- [user does not retain cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L333) +- [user with global-auth-always-set-cookie key in configmap retains cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_external_auth.go#L344) +### [global-options](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_options.go#L28) +- [should have worker_rlimit_nofile option](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_options.go#L31) +- [should have worker_rlimit_nofile option and be independent on amount of worker processes](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/global_options.go#L37) +### [settings-global-rate-limit](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/globalratelimit.go#L30) +- [generates correct NGINX configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/globalratelimit.go#L38) +### [gzip](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L29) +- [should be disabled by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L32) +- [should be enabled with default settings](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L39) +- [should set gzip_comp_level to 4](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L56) +- [should set gzip_disable to msie6](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L67) +- [should set gzip_min_length to 100](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L78) +- [should set gzip_types to application/javascript](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/gzip.go#L89) +### [hash size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L27) +- [should set server_names_hash_bucket_size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L39) +- [should set server_names_hash_max_size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L47) +- [should set proxy-headers-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L57) +- [should set proxy-headers-hash-max-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L65) +- [should set variables-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L75) +- [should set variables-hash-max-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L83) +- [should set vmap-hash-bucket-size](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/hash-size.go#L93) +### [[Flag] ingress-class](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L41) +- [should ignore Ingress with a different class annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L70) +- [should ignore Ingress with different controller class](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L106) +- [should accept both Ingresses with default IngressClassName and IngressClass annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L134) +- [should ignore Ingress without IngressClass configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L166) +- [should delete Ingress when class is removed](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L194) +- [should serve Ingress when class is added](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L259) +- [should serve Ingress when class is updated between annotation and ingressClassName](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L325) +- [should ignore Ingress with no class and accept the correctly configured Ingresses](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L414) +- [should watch Ingress with no class and ignore ingress with a different class](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L482) +- [should watch Ingress that uses the class name even if spec is different](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L538) +- [should watch Ingress with correct annotation](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L628) +- [should ignore Ingress with only IngressClassName](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ingress_class.go#L648) +### [keep-alive keep-alive-requests](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L28) +- [should set keepalive_timeout](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L40) +- [should set keepalive_requests](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L48) +- [should set keepalive connection to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L58) +- [should set keep alive connection timeout to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L68) +- [should set keepalive time to upstream server](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L78) +- [should set the request count to upstream server through one keep alive connection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/keep-alive.go#L88) +### [Configmap - limit-rate](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/limit_rate.go#L28) +- [Check limit-rate config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/limit_rate.go#L36) +### [[Flag] custom HTTP and HTTPS ports](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/listen_nondefault_ports.go#L30) +- [should set X-Forwarded-Port headers accordingly when listening on a non-default HTTP port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/listen_nondefault_ports.go#L45) +- [should set X-Forwarded-Port header to 443](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/listen_nondefault_ports.go#L65) +- [should set the X-Forwarded-Port header to 443](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/listen_nondefault_ports.go#L93) +### [log-format-*](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L28) +- [should not configure log-format escape by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L39) +- [should enable the log-format-escape-json](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L46) +- [should disable the log-format-escape-json](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L54) +- [should enable the log-format-escape-none](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L62) +- [should disable the log-format-escape-none](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L70) +- [log-format-escape-json enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L80) +- [log-format default escape](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L103) +- [log-format-escape-none enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/log-format.go#L126) +### [[Lua] lua-shared-dicts](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/lua_shared_dicts.go#L26) +- [configures lua shared dicts](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/lua_shared_dicts.go#L29) +### [main-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/main_snippet.go#L27) +- [should add value of main-snippet setting to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/main_snippet.go#L31) +### [[Security] modsecurity-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/modsecurity/modsecurity_snippet.go#L27) +- [should add value of modsecurity-snippet setting to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/modsecurity/modsecurity_snippet.go#L30) +### [enable-multi-accept](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/multi_accept.go#L27) +- [should be enabled by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/multi_accept.go#L31) +- [should be enabled when set to true](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/multi_accept.go#L39) +- [should be disabled when set to false](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/multi_accept.go#L49) +### [[Flag] watch namespace selector](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/namespace_selector.go#L30) +- [should ignore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/namespace_selector.go#L62) +### [[Security] no-auth-locations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_auth_locations.go#L33) +- [should return status code 401 when accessing '/' unauthentication](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_auth_locations.go#L54) +- [should return status code 200 when accessing '/' authentication](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_auth_locations.go#L68) +- [should return status code 200 when accessing '/noauth' unauthenticated](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_auth_locations.go#L82) +### [Add no tls redirect locations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_tls_redirect_locations.go#L27) +- [Check no tls redirect locations config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/no_tls_redirect_locations.go#L30) +### [OCSP](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ocsp/ocsp.go#L42) +- [should enable OCSP and contain stapling information in the connection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ocsp/ocsp.go#L49) +### [Configure Opentelemetry](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L39) +- [should not exists opentelemetry directive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L49) +- [should exists opentelemetry directive when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L62) +- [should include opentelemetry_trust_incoming_spans on directive when enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L76) +- [should not exists opentelemetry_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L91) +- [should exists opentelemetry_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentelemetry.go#L106) +### [Configure OpenTracing](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L52) +- [should not exists opentracing directive](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L62) +- [should exists opentracing directive when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L75) +- [should include opentracing_trust_incoming_span off directive when disabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L89) +- [should not exists opentracing_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L104) +- [should exists opentracing_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L119) +- [should not exists opentracing_location_operation_name directive when is empty](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L134) +- [should exists opentracing_location_operation_name directive when is configured](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L149) +- [should enable opentracing using zipkin](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L164) +- [should enable opentracing using jaeger](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L176) +- [should enable opentracing using jaeger with sampler host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L188) +- [should propagate the w3c header when configured with jaeger](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L201) +- [should enable opentracing using jaeger with an HTTP endpoint](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L232) +- [should enable opentracing using datadog](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/opentracing.go#L245) +### [plugins](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/plugins.go#L28) +- [should exist a x-hello-world header](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/plugins.go#L35) +### [[Security] Pod Security Policies](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/pod_security_policy.go#L41) +- [should be running with a Pod Security Policy](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/pod_security_policy.go#L44) +### [[Security] Pod Security Policies with volumes](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/pod_security_policy_volumes.go#L37) +- [should be running with a Pod Security Policy](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/pod_security_policy_volumes.go#L40) +### [proxy-connect-timeout](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_connect_timeout.go#L29) +- [should set valid proxy timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_connect_timeout.go#L37) +- [should not set invalid proxy timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_connect_timeout.go#L53) +### [Dynamic $proxy_host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_host.go#L28) +- [should exist a proxy_host](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_host.go#L36) +- [should exist a proxy_host using the upstream-vhost annotation value](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_host.go#L65) +### [proxy-next-upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_next_upstream.go#L28) +- [should build proxy next upstream using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_next_upstream.go#L36) +### [use-proxy-protocol](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_protocol.go#L38) +- [should respect port passed by the PROXY Protocol](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_protocol.go#L48) +- [should respect proto passed by the PROXY Protocol server port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_protocol.go#L85) +- [should enable PROXY Protocol for HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_protocol.go#L121) +- [should enable PROXY Protocol for TCP](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_protocol.go#L164) +### [proxy-read-timeout](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_read_timeout.go#L29) +- [should set valid proxy read timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_read_timeout.go#L37) +- [should not set invalid proxy read timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_read_timeout.go#L53) +### [proxy-send-timeout](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_send_timeout.go#L29) +- [should set valid proxy send timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_send_timeout.go#L37) +- [should not set invalid proxy send timeouts using configmap values](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/proxy_send_timeout.go#L53) +### [reuse-port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/reuse-port.go#L27) +- [reuse port should be enabled by default](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/reuse-port.go#L38) +- [reuse port should be disabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/reuse-port.go#L44) +- [reuse port should be enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/reuse-port.go#L52) +### [configmap server-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_snippet.go#L28) +- [should add value of server-snippet setting to all ingress config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_snippet.go#L35) +- [should add global server-snippet and drop annotations per admin config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_snippet.go#L98) +### [server-tokens](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_tokens.go#L29) +- [should not exists Server header in the response](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_tokens.go#L38) +- [should exists Server header in the response when is enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/server_tokens.go#L50) +### [ssl-ciphers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_ciphers.go#L28) +- [Add ssl ciphers](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_ciphers.go#L31) +### [[Flag] enable-ssl-passthrough](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_passthrough.go#L36) +### [With enable-ssl-passthrough enabled](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_passthrough.go#L55) +- [should enable ssl-passthrough-proxy-port on a different port](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_passthrough.go#L56) +- [should pass unknown traffic to default backend and handle known traffic](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/ssl_passthrough.go#L78) +### [configmap stream-snippet](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/stream_snippet.go#L35) +- [should add value of stream-snippet via config map to nginx config](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/stream_snippet.go#L42) +### [[SSL] TLS protocols, ciphers and headers)](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L31) +- [setting cipher suite](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L65) +- [setting max-age parameter](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L109) +- [setting includeSubDomains parameter](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L125) +- [setting preload parameter](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L144) +- [overriding what's set from the upstream](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L164) +- [should not use ports during the HTTP to HTTPS redirection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L186) +- [should not use ports or X-Forwarded-Host during the HTTP to HTTPS redirection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/tls.go#L204) +### [annotation validations](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/validations/validations.go#L30) +- [should allow ingress based on their risk on webhooks](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/validations/validations.go#L33) +- [should allow ingress based on their risk on webhooks](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/settings/validations/validations.go#L68) +### [[SSL] redirect to HTTPS](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ssl/http_redirect.go#L29) +- [should redirect from HTTP to HTTPS when secret is missing](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ssl/http_redirect.go#L36) +### [[SSL] secret update](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ssl/secret_update.go#L33) +- [should not appear references to secret updates not used in ingress rules](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ssl/secret_update.go#L40) +- [should return the fake SSL certificate if the secret is invalid](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/ssl/secret_update.go#L83) +### [[Status] status update](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/status/update.go#L38) +- [should update status field after client-go reconnection](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/status/update.go#L43) +### [[TCP] tcp-services](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/tcpudp/tcp.go#L38) +- [should expose a TCP service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/tcpudp/tcp.go#L46) +- [should expose an ExternalName TCP service](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/tcpudp/tcp.go#L80) +- [should reload after an update in the configuration](https://github.com/kubernetes/ingress-nginx/tree/main//test/e2e/tcpudp/tcp.go#L169) \ No newline at end of file diff --git a/docs/enhancements/20231001-split-containers.md b/docs/enhancements/20231001-split-containers.md new file mode 100644 index 000000000..3c2e85094 --- /dev/null +++ b/docs/enhancements/20231001-split-containers.md @@ -0,0 +1,110 @@ +# Proposal to split containers + +* All the NGINX files should live on one container + * No file other than NGINX files should exist on this container + * This includes not mounting the service account +* All the controller files should live on a different container + * Controller container should have bare minimum to work (just go program) + * ServiceAccount should be mounted just on controller + +* Inside nginx container, there should be a really small http listener just able +to start, stop and reload NGINX + +## Roadmap (what needs to be done) +* Map what needs to be done to mount the SA just on controller container +* Map all the required files for NGINX to work +* Map all the required network calls between controller and NGINX + * eg.: Dynamic lua reconfiguration +* Map problematic features that will need attention + * SSLPassthrough today happens on controller process and needs to happen on NGINX + +### Ports and endpoints on NGINX container +* Public HTTP/HTTPs port - 80 and 443 +* Lua configuration port - 10246 (HTTP) and 10247 (Stream) +* 3333 (temp) - Dataplane controller http server + * /reload - (POST) Reloads the configuration. + * "config" argument is the location of temporary file that should be used / moved to nginx.conf + * /test - (POST) Test the configuration of a given file location + * "config" argument is the location of temporary file that should be tested + +### Mounting empty SA on controller container + +```yaml +kind: Pod +apiVersion: v1 +metadata: + name: test +spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 + - name: othernginx + image: alpine:latest + command: ["/bin/sh"] + args: ["-c", "while true; do date; sleep 3; done"] + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: emptysecret + volumes: + - name: emptysecret + emptyDir: + sizeLimit: 1Mi +``` + +### Mapped folders on NGINX configuration +**WARNING** We need to be aware of inter mount containers and inode problems. If we +mount a file instead of a directory, it may take time to reflect the file value on +the target container + +* "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;"; - Lua scripts +* "/var/log/nginx" - NGINX logs +* "/tmp/nginx (nginx.pid)" - NGINX pid directory / file, fcgi socket, etc +* " /etc/nginx/geoip" - GeoIP database directory - OK - /etc/ingress-controller/geoip +* /etc/nginx/mime.types - Mime types +* /etc/ingress-controller/ssl - SSL directory (fake cert, auth cert) +* /etc/ingress-controller/auth - Authentication files +* /etc/nginx/modsecurity - Modsecurity configuration +* /etc/nginx/owasp-modsecurity-crs - Modsecurity rules +* /etc/nginx/tickets.key - SSL tickets - OK - /etc/ingress-controller/tickets.key +* /etc/nginx/opentelemetry.toml - OTEL config - OK - /etc/ingress-controller/telemetry +* /etc/nginx/opentracing.json - Opentracing config - OK - /etc/ingress-controller/telemetry +* /etc/nginx/modules - NGINX modules +* /etc/nginx/fastcgi_params (maybe) - fcgi params +* /etc/nginx/template - Template, may be used by controller only + +##### List of modules +``` +ngx_http_auth_digest_module.so ngx_http_modsecurity_module.so +ngx_http_brotli_filter_module.so ngx_http_opentracing_module.so +ngx_http_brotli_static_module.so ngx_stream_geoip2_module.so +ngx_http_geoip2_module.so +``` + +##### List of files that may be removed +``` +-rw-r--r-- 1 www-data www-data 1077 Jun 23 19:44 fastcgi.conf +-rw-r--r-- 1 www-data www-data 1077 Jun 23 19:44 fastcgi.conf.default +-rw-r--r-- 1 www-data www-data 1007 Jun 23 19:44 fastcgi_params +-rw-r--r-- 1 www-data www-data 1007 Jun 23 19:44 fastcgi_params.default +drwxr-xr-x 2 www-data www-data 4096 Jun 23 19:34 geoip +-rw-r--r-- 1 www-data www-data 2837 Jun 23 19:44 koi-utf +-rw-r--r-- 1 www-data www-data 2223 Jun 23 19:44 koi-win +drwxr-xr-x 6 www-data www-data 4096 Sep 19 14:13 lua +-rw-r--r-- 1 www-data www-data 5349 Jun 23 19:44 mime.types +-rw-r--r-- 1 www-data www-data 5349 Jun 23 19:44 mime.types.default +drwxr-xr-x 2 www-data www-data 4096 Jun 23 19:44 modsecurity +drwxr-xr-x 2 www-data www-data 4096 Jun 23 19:44 modules +-rw-r--r-- 1 www-data www-data 18275 Oct 1 21:28 nginx.conf +-rw-r--r-- 1 www-data www-data 2656 Jun 23 19:44 nginx.conf.default +-rwx------ 1 www-data www-data 420 Oct 1 21:28 opentelemetry.toml +-rw-r--r-- 1 www-data www-data 2 Oct 1 21:28 opentracing.json +drwxr-xr-x 7 www-data www-data 4096 Jun 23 19:44 owasp-modsecurity-crs +-rw-r--r-- 1 www-data www-data 636 Jun 23 19:44 scgi_params +-rw-r--r-- 1 www-data www-data 636 Jun 23 19:44 scgi_params.default +drwxr-xr-x 2 www-data www-data 4096 Sep 19 14:13 template +-rw-r--r-- 1 www-data www-data 664 Jun 23 19:44 uwsgi_params +-rw-r--r-- 1 www-data www-data 664 Jun 23 19:44 uwsgi_params.default +-rw-r--r-- 1 www-data www-data 3610 Jun 23 19:44 win-utf +``` diff --git a/docs/examples/canary/README.md b/docs/examples/canary/README.md new file mode 100644 index 000000000..4124faf6f --- /dev/null +++ b/docs/examples/canary/README.md @@ -0,0 +1,231 @@ +# Canary + +Ingress Nginx Has the ability to handle canary routing by setting specific +annotations, the following is an example of how to configure a canary +deployment with weighted canary routing. + +## Create your main deployment and service + +This is the main deployment of your application with the service that will be +used to route to it + +```bash +echo " +--- +# Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: production + labels: + app: production +spec: + replicas: 1 + selector: + matchLabels: + app: production + template: + metadata: + labels: + app: production + spec: + containers: + - name: production + image: registry.k8s.io/ingress-nginx/e2e-test-echo@sha256:6fc5aa2994c86575975bb20a5203651207029a0d28e3f491d8a127d08baadab4 + ports: + - containerPort: 80 + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP +--- +# Service +apiVersion: v1 +kind: Service +metadata: + name: production + labels: + app: production +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: production +" | kubectl apply -f - +``` + +## Create the canary deployment and service + +This is the canary deployment that will take a weighted amount of requests +instead of the main deployment + +```bash +echo " +--- +# Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: canary + labels: + app: canary +spec: + replicas: 1 + selector: + matchLabels: + app: canary + template: + metadata: + labels: + app: canary + spec: + containers: + - name: canary + image: registry.k8s.io/ingress-nginx/e2e-test-echo@sha256:6fc5aa2994c86575975bb20a5203651207029a0d28e3f491d8a127d08baadab4 + ports: + - containerPort: 80 + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP +--- +# Service +apiVersion: v1 +kind: Service +metadata: + name: canary + labels: + app: canary +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: canary +" | kubectl apply -f - +``` + +## Create Ingress Pointing To Your Main Deployment + +Next you will need to expose your main deployment with an ingress resource, +note there are no canary specific annotations on this ingress + +```bash +echo " +--- +# Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: production + annotations: +spec: + ingressClassName: nginx + rules: + - host: echo.prod.mydomain.com + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: production + port: + number: 80 +" | kubectl apply -f - +``` + +## Create Ingress Pointing To Your Canary Deployment + +You will then create an Ingress that has the canary specific configuration, +please pay special notice of the following: + +- The host name is identical to the main ingress host name +- The `nginx.ingress.kubernetes.io/canary: "true"` annotation is required and + defines this as a canary annotation (if you do not have this the Ingresses + will clash) +- The `nginx.ingress.kubernetes.io/canary-weight: "50"` annotation dictates the + weight of the routing, in this case there is a "50%" chance a request will + hit the canary deployment over the main deployment +```bash +echo " +--- +# Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: canary + annotations: + nginx.ingress.kubernetes.io/canary: \"true\" + nginx.ingress.kubernetes.io/canary-weight: \"50\" +spec: + ingressClassName: nginx + rules: + - host: echo.prod.mydomain.com + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: canary + port: + number: 80 +" | kubectl apply -f - +``` + +## Testing your setup + +You can use the following command to test your setup (replacing +INGRESS_CONTROLLER_IP with your ingresse controllers IP Address) + +```bash +for i in $(seq 1 10); do curl -s --resolve echo.prod.mydomain.com:80:$INGRESS_CONTROLLER_IP echo.prod.mydomain.com | grep "Hostname"; done +``` + +You will get the following output showing that your canary setup is working as +expected: + +```bash +Hostname: production-5c5f65d859-phqzc +Hostname: canary-6697778457-zkfjf +Hostname: canary-6697778457-zkfjf +Hostname: production-5c5f65d859-phqzc +Hostname: canary-6697778457-zkfjf +Hostname: production-5c5f65d859-phqzc +Hostname: production-5c5f65d859-phqzc +Hostname: production-5c5f65d859-phqzc +Hostname: canary-6697778457-zkfjf +Hostname: production-5c5f65d859-phqzc +``` diff --git a/docs/examples/customization/custom-errors/custom-default-backend.helm.values.yaml b/docs/examples/customization/custom-errors/custom-default-backend.helm.values.yaml index 807da71a4..708a93831 100644 --- a/docs/examples/customization/custom-errors/custom-default-backend.helm.values.yaml +++ b/docs/examples/customization/custom-errors/custom-default-backend.helm.values.yaml @@ -6,7 +6,7 @@ defaultBackend: image: registry: registry.k8s.io image: ingress-nginx/nginx-errors - tag: "v20230312-helm-chart-4.5.2-28-g66a760794@sha256:332be6ff8c4e93e8845963932f98839dfd52ae49829c29e06475368a3e4fbd9e" + tag: "v20230505@sha256:3600dcd1bbd0d05959bb01af4b272714e94d22d24a64e91838e7183c80e53f7f" extraVolumes: - name: custom-error-pages configMap: diff --git a/docs/examples/customization/custom-errors/custom-default-backend.yaml b/docs/examples/customization/custom-errors/custom-default-backend.yaml index 2af64896f..e606c5b62 100644 --- a/docs/examples/customization/custom-errors/custom-default-backend.yaml +++ b/docs/examples/customization/custom-errors/custom-default-backend.yaml @@ -36,7 +36,7 @@ spec: spec: containers: - name: nginx-error-server - image: registry.k8s.io/ingress-nginx/nginx-errors:v20230404-helm-chart-4.6.0-11-gc76179c04@sha256:aabd7a001f6a0a07ed6ea8f6da87e928bfa8f971eba2bef708f3e8504fc5cc9b + image: registry.k8s.io/ingress-nginx/nginx-errors:v20230505@sha256:3600dcd1bbd0d05959bb01af4b272714e94d22d24a64e91838e7183c80e53f7f ports: - containerPort: 8080 # Setting the environment variable DEBUG we can see the headers sent diff --git a/docs/examples/customization/external-auth-headers/echo-service.yaml b/docs/examples/customization/external-auth-headers/echo-service.yaml index 57df4d628..fc4461cd8 100644 --- a/docs/examples/customization/external-auth-headers/echo-service.yaml +++ b/docs/examples/customization/external-auth-headers/echo-service.yaml @@ -18,7 +18,7 @@ spec: terminationGracePeriodSeconds: 60 containers: - name: echo-service - image: registry.k8s.io/ingress-nginx/e2e-test-echo:v20230318-helm-chart-4.5.2-44-gfec1dbe3a@sha256:4938d1d91a2b7d19454460a8c1b010b89f6ff92d2987fd889ac3e8fc3b70d91a + image: registry.k8s.io/ingress-nginx/e2e-test-echo:v20230527@sha256:6fc5aa2994c86575975bb20a5203651207029a0d28e3f491d8a127d08baadab4 ports: - containerPort: 8080 resources: diff --git a/docs/examples/customization/sysctl/patch.json b/docs/examples/customization/sysctl/patch.json index 3f736197b..e87c9affa 100644 --- a/docs/examples/customization/sysctl/patch.json +++ b/docs/examples/customization/sysctl/patch.json @@ -4,7 +4,7 @@ "spec": { "initContainers": [{ "name": "sysctl", - "image": "alpine:3.17.0", + "image": "alpine:3.18", "securityContext": { "privileged": true }, diff --git a/docs/examples/grpc/README.md b/docs/examples/grpc/README.md index cf4597fcd..f6bd96e20 100644 --- a/docs/examples/grpc/README.md +++ b/docs/examples/grpc/README.md @@ -9,6 +9,7 @@ This example demonstrates how to route traffic to a gRPC service through the Ing 3. You have the ingress-nginx-controller installed as per docs. 4. You have a backend application running a gRPC server listening for TCP traffic. If you want, you can use as an example. 5. You're also responsible for provisioning an SSL certificate for the ingress. So you need to have a valid SSL certificate, deployed as a Kubernetes secret of type `tls`, in the same namespace as the gRPC application. + - Note: To use gRPC with ingress-nginx, TLS _must_ be terminated by the ingress or gRPC server (using the `backend-protocol: "GRPCS"` annotation described below). ### Step 1: Create a Kubernetes `Deployment` for gRPC app @@ -20,7 +21,7 @@ This example demonstrates how to route traffic to a gRPC service through the Ing - As an example gRPC application, we can use this app . -- To create a container image for this app, you can use [this Dockerfile](https://github.com/kubernetes/ingress-nginx/blob/5a52d99ae85cfe5ef9535291b8326b0006e75066/images/go-grpc-greeter-server/rootfs/Dockerfile). +- To create a container image for this app, you can use [this Dockerfile](https://github.com/kubernetes/ingress-nginx/blob/main/images/go-grpc-greeter-server/rootfs/Dockerfile). - If you use the Dockerfile mentioned above, to create a image, then you can use the following example Kubernetes manifest to create a deployment resource that uses that image. If necessary edit this manifest to suit your needs. diff --git a/docs/examples/index.md b/docs/examples/index.md index 8a5fd5f51..59745e014 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -23,6 +23,7 @@ Customization | [External authentication with response header propagation](custo Customization | [Sysctl tuning](customization/sysctl/README.md) | TODO | TODO Features | [Rewrite](rewrite/README.md) | TODO | TODO Features | [Session stickiness](affinity/cookie/README.md) | route requests consistently to the same endpoint | Advanced +Features | [Canary Deployments](canary/README.md) | weighted canary routing to a seperate deployment | Intermediate Scaling | [Static IP](static-ip/README.md) | a single ingress gets a single static IP | Intermediate TLS | [Multi TLS certificate termination](multi-tls/README.md) | TODO | TODO TLS | [TLS termination](tls-termination/README.md) | TODO | TODO diff --git a/docs/examples/openpolicyagent/README.md b/docs/examples/openpolicyagent/README.md new file mode 100644 index 000000000..2d653a90c --- /dev/null +++ b/docs/examples/openpolicyagent/README.md @@ -0,0 +1,25 @@ +# OpenPolicyAgent and pathType enforcing + +Ingress API allows users to specify different [pathType](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) +on Ingress object. + +While pathType `Exact` and `Prefix` should allow only a small set of characters, pathType `ImplementationSpecific` +allows any characters, as it may contain regexes, variables and other features that may be specific of the Ingress +Controller being used. + +This means that the Ingress Admins (the persona who deployed the Ingress Controller) should trust the users +allowed to use `pathType: ImplementationSpecific`, as this may allow arbitrary configuration, and this +configuration may end on the proxy (aka Nginx) configuration. + +## Example +The example in this repo uses [Gatekeeper](https://open-policy-agent.github.io/gatekeeper/website/) to block the usage of `pathType: ImplementationSpecific`, +allowing just a specific list of namespaces to use it. + +It is recommended that the admin modifies this rules to enforce a specific set of characters when the usage of ImplementationSpecific +is allowed, or in ways that best suits their needs. + +First, the `ConstraintTemplate` from [template.yaml](template.yaml) will define a rule that validates if the Ingress object +is being created on an excempted namespace, and case not, will validate its pathType. + +Then, the rule `K8sBlockIngressPathType` contained in [rule.yaml](rule.yaml) will define the parameters: what kind of +object should be verified (Ingress), what are the excempted namespaces, and what kinds of pathType are blocked. diff --git a/docs/examples/openpolicyagent/rule.yaml b/docs/examples/openpolicyagent/rule.yaml new file mode 100644 index 000000000..fce305241 --- /dev/null +++ b/docs/examples/openpolicyagent/rule.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sBlockIngressPathType +metadata: + name: implspecificisblocked +spec: + match: + kinds: + - apiGroups: ["networking.k8s.io"] + kinds: ["Ingress"] + parameters: + namespacesExceptions: + - "privileged" + blockedTypes: + - "ImplementationSpecific" diff --git a/docs/examples/openpolicyagent/template.yaml b/docs/examples/openpolicyagent/template.yaml new file mode 100644 index 000000000..ed2a6ba1c --- /dev/null +++ b/docs/examples/openpolicyagent/template.yaml @@ -0,0 +1,40 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sblockingresspathtype + annotations: + metadata.gatekeeper.sh/title: "Block a pathType usage" + description: >- + Users should not be able to use specific pathTypes +spec: + crd: + spec: + names: + kind: K8sBlockIngressPathType + validation: + openAPIV3Schema: + type: object + properties: + blockedTypes: + type: array + items: + type: string + namespacesExceptions: + type: array + items: + type: string + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package K8sBlockIngressPathType + + violation[{"msg": msg}] { + input.review.kind.kind == "Ingress" + ns := input.review.object.metadata.namespace + excemptNS := [good | excempts = input.parameters.namespacesExceptions[_] ; good = excempts == ns] + not any(excemptNS) + pathType := object.get(input.review.object.spec.rules[_].http.paths[_], "pathType", "") + blockedPath := [blocked | blockedTypes = input.parameters.blockedTypes[_] ; blocked = blockedTypes == pathType] + any(blockedPath) + msg := sprintf("pathType '%v' is not allowed in this namespace", [pathType]) + } diff --git a/docs/examples/openpolicyagent/tests/should-allow-ns-except.yaml b/docs/examples/openpolicyagent/tests/should-allow-ns-except.yaml new file mode 100644 index 000000000..974e83555 --- /dev/null +++ b/docs/examples/openpolicyagent/tests/should-allow-ns-except.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + creationTimestamp: null + name: simple + namespace: privileged +spec: + rules: + - host: foo1.com + http: + paths: + - backend: + service: + name: svc1 + port: + number: 8080 + path: /bar + pathType: ImplementationSpecific diff --git a/docs/examples/openpolicyagent/tests/should-allow.yaml b/docs/examples/openpolicyagent/tests/should-allow.yaml new file mode 100644 index 000000000..854aff14c --- /dev/null +++ b/docs/examples/openpolicyagent/tests/should-allow.yaml @@ -0,0 +1,17 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + creationTimestamp: null + name: simple +spec: + rules: + - host: foo.com + http: + paths: + - backend: + service: + name: svc1 + port: + number: 8080 + path: /bar + pathType: Exact diff --git a/docs/examples/openpolicyagent/tests/should-deny.yaml b/docs/examples/openpolicyagent/tests/should-deny.yaml new file mode 100644 index 000000000..b732fdc89 --- /dev/null +++ b/docs/examples/openpolicyagent/tests/should-deny.yaml @@ -0,0 +1,17 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + creationTimestamp: null + name: simple +spec: + rules: + - host: foo2.com + http: + paths: + - backend: + service: + name: svc1 + port: + number: 8080 + path: /bar + pathType: ImplementationSpecific diff --git a/docs/examples/rewrite/README.md b/docs/examples/rewrite/README.md index 5b42e1fc7..16889e0bc 100644 --- a/docs/examples/rewrite/README.md +++ b/docs/examples/rewrite/README.md @@ -30,6 +30,9 @@ Rewriting can be controlled using the following annotations: !!! note [Captured groups](https://www.regular-expressions.info/refcapture.html) are saved in numbered placeholders, chronologically, in the form `$1`, `$2` ... `$n`. These placeholders can be used as parameters in the `rewrite-target` annotation. +!!! note + Please see the [FAQ](../../faq.md#validation-of-path) for Validation Of __`path`__ + Create an Ingress rule with a rewrite annotation: ```console @@ -49,7 +52,7 @@ spec: http: paths: - path: /something(/|$)(.*) - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: http-svc diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 000000000..253378c65 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,33 @@ + +# FAQ + +## Retaining Client IPAddress + +Please read [Retain Client IPAddress Guide here](./user-guide/retaining-client-ipaddress.md). + +## Kubernetes v1.22 Migration + +If you are using Ingress objects in your cluster (running Kubernetes older than v1.22), and you plan to upgrade your Kubernetes version to K8S 1.22 or above, then please read [the migration guide here](./user-guide/k8s-122-migration.md). + +## Validation Of __`path`__ + +- For improving security and also following desired standards on Kubernetes API spec, the next release, scheduled for v1.8.0, will include a new & optional feature of validating the value for the key `ingress.spec.rules.http.paths.path` . + +- This behavior will be disabled by default on the 1.8.0 release and enabled by default on the next breaking change release, set for 2.0.0. + +- When "`ingress.spec.rules.http.pathType=Exact`" or "`pathType=Prefix`", this validation will limit the characters accepted on the field "`ingress.spec.rules.http.paths.path`", to "`alphanumeric characters`", and `"/," "_," "-."` Also, in this case, the path should start with `"/."` + +- When the ingress resource path contains other characters (like on rewrite configurations), the pathType value should be "`ImplementationSpecific`". + +- API Spec on pathType is documented [here](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) + +- When this option is enabled, the validation will happen on the Admission Webhook. So if any new ingress object contains characters other than "`alphanumeric characters`", and `"/," "_," "-."` , in the `path` field, but is not using `pathType` value as `ImplementationSpecific`, then the ingress object will be denied admission. + +- The cluster admin should establish validation rules using mechanisms like "`Open Policy Agent`", to validate that only authorized users can use ImplementationSpecific pathType and that only the authorized characters can be used. [The configmap value is here](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#strict-validate-path-type) + +- A complete example of an Openpolicyagent gatekeeper rule is available [here](https://kubernetes.github.io/ingress-nginx/examples/openpolicyagent/) + +- If you have any issues or concerns, please do one of the following: + - Open a GitHub issue + - Comment in our Dev Slack Channel + - Open a thread in our Google Group ingress-nginx-dev@kubernetes.io diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 161803210..4542921c7 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -58,13 +58,13 @@ In a relatively big cluster with frequently deploying apps this feature saves si ### Avoiding outage from wrong configuration -Because the ingress controller works using the [synchronization loop pattern](https://coreos.com/kubernetes/docs/latest/replication-controller.html#the-reconciliation-loop-in-detail), it is applying the configuration for all matching objects. In case some Ingress objects have a broken configuration, for example a syntax error in the `nginx.ingress.kubernetes.io/configuration-snippet` annotation, the generated configuration becomes invalid, does not reload and hence no more ingresses will be taken into account. +Because the ingress controller works using the [synchronization loop pattern][1], it is applying the configuration for all matching objects. In case some Ingress objects have a broken configuration, for example a syntax error in the `nginx.ingress.kubernetes.io/configuration-snippet` annotation, the generated configuration becomes invalid, does not reload and hence no more ingresses will be taken into account. To prevent this situation to happen, the Ingress-Nginx Controller optionally exposes a [validating admission webhook server][8] to ensure the validity of incoming ingress objects. This webhook appends the incoming ingress objects to the list of ingresses, generates the configuration and calls nginx to ensure the configuration has no syntax errors. [0]: https://github.com/openresty/lua-nginx-module/pull/1259 -[1]: https://coreos.com/kubernetes/docs/latest/replication-controller.html#the-reconciliation-loop-in-detail +[1]: https://github.com/coreos/docs/blob/master/kubernetes/replication-controller.md#the-reconciliation-loop-in-detail [2]: https://godoc.org/k8s.io/client-go/informers#NewFilteredSharedInformerFactory [3]: https://godoc.org/k8s.io/client-go/tools/cache#ResourceEventHandlerFuncs [4]: https://github.com/kubernetes/ingress-nginx/blob/main/internal/task/queue.go#L38 diff --git a/docs/index.md b/docs/index.md index bcf4a4018..bd6a825e1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,14 +4,9 @@ This is the documentation for the Ingress NGINX Controller. It is built around the [Kubernetes Ingress resource](https://kubernetes.io/docs/concepts/services-networking/ingress/), using a [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) to store the controller configuration. -You can learn more about using [Ingress](http://kubernetes.io/docs/user-guide/ingress/) in the official [Kubernetes documentation](https://docs.k8s.io). +You can learn more about using [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) in the official [Kubernetes documentation](https://docs.k8s.io). -## Getting Started +# Getting Started See [Deployment](./deploy/) for a whirlwind tour that will get you started. - -# FAQ - Kubernetes 1.22 Migration - -If you are using Ingress objects in your cluster (running Kubernetes older than v1.22), -and you plan to upgrade to Kubernetes v1.22, please read [the migration guide here](./user-guide/k8s-122-migration.md). diff --git a/docs/lua_tests.md b/docs/lua_tests.md new file mode 100644 index 000000000..4d3d1fe70 --- /dev/null +++ b/docs/lua_tests.md @@ -0,0 +1,19 @@ +# Lua Tests + +## Running the Lua Tests + +To run the Lua tests you can run the following from the root directory: + +```bash +make lua-test +``` + +This command makes use of docker hence does not need any dependency +installations besides docker + +## Where are the Lua Tests? + +Lua Tests can be found in the [rootfs/etc/nginx/lua/test](../rootfs/etc/nginx/lua/test) directory + + +[1]: https://openresty.org/en/installation.html diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..5d5943b84 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +mkdocs-material==9.4.5 +mkdocs-awesome-pages-plugin==2.9.2 +mkdocs-minify-plugin==0.7.1 +mkdocs-redirects==1.2.1 \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 4b9820200..0d2004092 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -137,7 +137,7 @@ The Ingress controller needs information from apiserver. Therefore, authenticati * _Kubeconfig file:_ In some Kubernetes environments service accounts are not available. In this case a manual configuration is required. The Ingress controller binary can be started with the `--kubeconfig` flag. The value of the flag is a path to a file specifying how to connect to the API server. Using the `--kubeconfig` does not requires the flag `--apiserver-host`. The format of the file is identical to `~/.kube/config` which is used by kubectl to connect to the API server. See 'kubeconfig' section for details. -* _Using the flag `--apiserver-host`:_ Using this flag `--apiserver-host=http://localhost:8080` it is possible to specify an unsecured API server or reach a remote kubernetes cluster using [kubectl proxy](https://kubernetes.io/docs/user-guide/kubectl/kubectl_proxy/). +* _Using the flag `--apiserver-host`:_ Using this flag `--apiserver-host=http://localhost:8080` it is possible to specify an unsecured API server or reach a remote kubernetes cluster using [kubectl proxy](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#proxy). Please do not use this approach in production. In the diagram below you can see the full authentication flow with all options, starting with the browser @@ -230,7 +230,7 @@ If it is not working, there are two possible reasons: More information: -- [User Guide: Service Accounts](http://kubernetes.io/docs/user-guide/service-accounts/) +- [User Guide: Service Accounts](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) - [Cluster Administrator Guide: Managing Service Accounts](http://kubernetes.io/docs/admin/service-accounts-admin/) ## Kube-Config @@ -419,6 +419,7 @@ spec: operator: "Exists" effect: NoSchedule ``` + * update the namespace if applicable/desired * replace `##_NODE_NAME_##` with the problematic node (or remove nodeSelector section if problem is not confined to one node) * replace `##_CONTROLLER_IMAGE_##` with the same image as in use by your ingress-nginx deployment @@ -445,6 +446,7 @@ $ capsh --decode=0000000000000400 ## Create a test pod as root (Note, this may be restricted by PodSecurityPolicy, PodSecurityAdmission/Standards, OPA Gatekeeper, etc. in which case you will need to do the appropriate workaround for testing, e.g. deploy in a new namespace without the restrictions.) To test further you may want to install additional utilities, etc. Modify the pod yaml by: + * changing runAsUser from 101 to 0 * removing the "drop..ALL" section from the capabilities. diff --git a/docs/user-guide/cli-arguments.md b/docs/user-guide/cli-arguments.md index bc0894a52..07a17a271 100644 --- a/docs/user-guide/cli-arguments.md +++ b/docs/user-guide/cli-arguments.md @@ -15,6 +15,7 @@ They are set in the container spec of the `ingress-nginx-controller` Deployment | `--default-backend-service` | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form "namespace/name". The controller configures NGINX to forward requests to the first port of this Service. | | `--default-server-port` | Port to use for exposing the default server (catch-all). (default 8181) | | `--default-ssl-certificate` | Secret containing a SSL certificate to be used by the default HTTPS server (catch-all). Takes the form "namespace/name". | +| `--enable-annotation-validation` | If true, will enable the annotation validation feature. This value will be defaulted to true on a future release. | | `--disable-catch-all` | Disable support for catch-all Ingresses. (default false) | | `--disable-full-test` | Disable full test of all merged ingresses at the admission stage and tests the template of the ingress being created or updated (full test of all ingresses is enabled by default). | | `--disable-svc-external-name` | Disable support for Services of type ExternalName. (default false) | @@ -24,7 +25,7 @@ They are set in the container spec of the `ingress-nginx-controller` Deployment | `--enable-metrics` | Enables the collection of NGINX metrics. (default true) | | `--enable-ssl-chain-completion` | Autocomplete SSL certificate chains with missing intermediate CA certificates. Certificates uploaded to Kubernetes must have the "Authority Information Access" X.509 v3 extension for this to succeed. (default false)| | `--enable-ssl-passthrough` | Enable SSL Passthrough. (default false) | -| `--enable-topology-aware-routing` | Enable topology aware hints feature, needs service object annotation service.kubernetes.io/topology-aware-hints sets to auto. (default false) | +| `--enable-topology-aware-routing` | Enable topology aware routing feature, needs service object annotation service.kubernetes.io/topology-mode sets to auto. (default false) | | `--exclude-socket-metrics` | Set of socket request metrics to exclude which won't be exported nor being calculated. The possible socket request metrics to exclude are documented in the monitoring guide e.g. 'nginx_ingress_controller_request_duration_seconds,nginx_ingress_controller_response_size'| | `--health-check-path` | URL path of the health check endpoint. Configured inside the NGINX status server. All requests received on the port defined by the healthz-port parameter are forwarded internally to this path. (default "/healthz") | | `--health-check-timeout` | Time limit, in seconds, for a probe to health-check-path to succeed. (default 10) | diff --git a/docs/user-guide/exposing-tcp-udp-services.md b/docs/user-guide/exposing-tcp-udp-services.md index 63293f0e5..dbc143ff1 100644 --- a/docs/user-guide/exposing-tcp-udp-services.md +++ b/docs/user-guide/exposing-tcp-udp-services.md @@ -1,7 +1,9 @@ # Exposing TCP and UDP services -Ingress does not support TCP or UDP services. For this reason this Ingress controller uses the flags `--tcp-services-configmap` and `--udp-services-configmap` to point to an existing config map where the key is the external port to use and the value indicates the service to expose using the format: -`::[PROXY]:[PROXY]` +While the Kubernetes Ingress resource only officially supports routing external HTTP(s) traffic to services, ingress-nginx can be configured to receive external TCP/UDP traffic from non-HTTP protocols and route them to internal services using TCP/UDP port mappings that are specified within a ConfigMap. + +To support this, the `--tcp-services-configmap` and `--udp-services-configmap` flags can be used to point to an existing config map where the key is the external port to use and the value indicates the service to expose using the format: +`::[PROXY]:[PROXY]` It is also possible to use a number or the name of the port. The two last fields are optional. Adding `PROXY` in either or both of the two last fields we can use [Proxy Protocol](https://www.nginx.com/resources/admin-guide/proxy-protocol) decoding (listen) and/or encoding (proxy_pass) in a TCP service. diff --git a/docs/user-guide/external-articles.md b/docs/user-guide/external-articles.md index 27a3f4447..8a60ad567 100644 --- a/docs/user-guide/external-articles.md +++ b/docs/user-guide/external-articles.md @@ -4,3 +4,4 @@ - [Accessing Kubernetes Pods from Outside of the Cluster](http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster) - [Kubernetes - Redirect HTTP to HTTPS with ELB and the Ingress-Nginx Controller](https://dev.to/tomhoule/kubernetes---redirect-http-to-https-with-elb-and-the-nginx-ingress-controller) - [Configure Nginx Ingress Controller for TLS termination on Kubernetes on Azure](https://blogs.technet.microsoft.com/livedevopsinjapan/2017/02/28/configure-nginx-ingress-controller-for-tls-termination-on-kubernetes-on-azure-2/) +- [Secure your Nginx Ingress controller behind Google Cloud Armor or Identity-Aware Proxy (IAP)](https://medium.com/google-cloud/secure-your-nginx-ingress-controller-behind-cloud-armor-805d6109af86?sk=f64029bb5624b4ad5cd2828f4c358af3) diff --git a/docs/user-guide/ingress-path-matching.md b/docs/user-guide/ingress-path-matching.md index 321ddfa8e..dd618e08b 100644 --- a/docs/user-guide/ingress-path-matching.md +++ b/docs/user-guide/ingress-path-matching.md @@ -5,6 +5,9 @@ !!! important Regular expressions and wild cards are not supported in the `spec.rules.host` field. Full hostnames must be used. +!!! note + Please see the [FAQ](../faq.md#validation-of-path) for Validation Of __`path`__ + The ingress controller supports **case insensitive** regular expressions in the `spec.rules.http.paths.path` field. This can be enabled by setting the `nginx.ingress.kubernetes.io/use-regex` annotation to `true` (the default is false). @@ -28,7 +31,7 @@ spec: http: paths: - path: /foo/.* - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: test @@ -95,7 +98,7 @@ spec: http: paths: - path: /foo/bar/(.+) - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: service3 @@ -160,7 +163,7 @@ spec: port: number: 80 - path: /foo/bar/[A-Z0-9]{3} - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: test diff --git a/docs/user-guide/monitoring.md b/docs/user-guide/monitoring.md index 1e30e556f..28608e850 100644 --- a/docs/user-guide/monitoring.md +++ b/docs/user-guide/monitoring.md @@ -39,10 +39,9 @@ This tutorial will show you how to install [Prometheus](https://prometheus.io/) controller: metrics: enabled: true - service: - annotations: - prometheus.io/port: "10254" - prometheus.io/scrape: "true" + podAnnotations: + prometheus.io/port: "10254" + prometheus.io/scrape: "true" .. ``` - If you are **not using helm**, you will have to edit your manifests like this: @@ -50,10 +49,6 @@ This tutorial will show you how to install [Prometheus](https://prometheus.io/) ``` apiVersion: v1 kind: Service - metadata: - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "10254" .. spec: ports: @@ -67,16 +62,20 @@ This tutorial will show you how to install [Prometheus](https://prometheus.io/) ``` apiVersion: v1 kind: Deployment - metadata: - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "10254" .. spec: - ports: - - name: prometheus - containerPort: 10254 - .. + template: + metadata: + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "10254" + spec: + containers: + - name: controller + ports: + - name: prometheus + containerPort: 10254 + .. ``` diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md index b515a9f3b..41a876abf 100755 --- a/docs/user-guide/nginx-configuration/annotations.md +++ b/docs/user-guide/nginx-configuration/annotations.md @@ -33,12 +33,13 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz |[nginx.ingress.kubernetes.io/auth-cache-key](#external-authentication)|string| |[nginx.ingress.kubernetes.io/auth-cache-duration](#external-authentication)|string| |[nginx.ingress.kubernetes.io/auth-keepalive](#external-authentication)|number| +|[nginx.ingress.kubernetes.io/auth-keepalive-share-vars](#external-authentication)|"true" or "false"| |[nginx.ingress.kubernetes.io/auth-keepalive-requests](#external-authentication)|number| |[nginx.ingress.kubernetes.io/auth-keepalive-timeout](#external-authentication)|number| |[nginx.ingress.kubernetes.io/auth-proxy-set-headers](#external-authentication)|string| |[nginx.ingress.kubernetes.io/auth-snippet](#external-authentication)|string| |[nginx.ingress.kubernetes.io/enable-global-auth](#external-authentication)|"true" or "false"| -|[nginx.ingress.kubernetes.io/backend-protocol](#backend-protocol)|string|HTTP,HTTPS,GRPC,GRPCS,AJP| +|[nginx.ingress.kubernetes.io/backend-protocol](#backend-protocol)|string|HTTP,HTTPS,GRPC,GRPCS| |[nginx.ingress.kubernetes.io/canary](#canary)|"true" or "false"| |[nginx.ingress.kubernetes.io/canary-by-header](#canary)|string| |[nginx.ingress.kubernetes.io/canary-by-header-value](#canary)|string| @@ -49,6 +50,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz |[nginx.ingress.kubernetes.io/client-body-buffer-size](#client-body-buffer-size)|string| |[nginx.ingress.kubernetes.io/configuration-snippet](#configuration-snippet)|string| |[nginx.ingress.kubernetes.io/custom-http-errors](#custom-http-errors)|[]int| +|[nginx.ingress.kubernetes.io/disable-proxy-intercept-errors](#disable-proxy-intercept-errors)|"true" or "false"| |[nginx.ingress.kubernetes.io/default-backend](#default-backend)|string| |[nginx.ingress.kubernetes.io/enable-cors](#enable-cors)|"true" or "false"| |[nginx.ingress.kubernetes.io/cors-allow-origin](#enable-cors)|string| @@ -315,7 +317,8 @@ nginx.ingress.kubernetes.io/configuration-snippet: | more_set_headers "Request-Id: $req_id"; ``` -Be aware this can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. The recommended mitigation for this threat is to disable this feature, so it may not work for you. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. +!!! attention +Since version 1.9.0, `"configuration-snippet"` annotation is disabled by default and has to be explicitly enabled, see [allow-snippet-annotations](./configmap.md#allow-snippet-annotations). Enabling it can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. ### Custom HTTP Errors @@ -328,6 +331,17 @@ Example usage: nginx.ingress.kubernetes.io/custom-http-errors: "404,415" ``` +## Disable Proxy intercept Errors + +Like the [`disable-proxy-intercept-errors`](./configmap.md#disable-proxy-intercept-errors) value in the ConfigMap, this annotation allows to disable NGINX `proxy-intercept-errors` when `custom-http-errors` are set, but only for the NGINX location associated with this ingress. If a [default backend annotation](#default-backend) is specified on the ingress, the errors will be routed to that annotation's default backend service (instead of the global default backend). +Different ingresses can specify different sets of errors codes and there are UseCases where NGINX shall not intercept all errors returned from upstream. +If `disable-proxy-intercept-errors` is also specified globally, the annotation will override the global value for the given ingress' hostname and path. + +Example usage: +``` +nginx.ingress.kubernetes.io/disable-proxy-intercept-errors: "false" +``` + ### Default Backend This annotation is of the form `nginx.ingress.kubernetes.io/default-backend: ` to specify a custom default backend. This `` is a reference to a service inside of the same namespace in which you are applying this annotation. This annotation overrides the global default backend. In case the service has [multiple ports](https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services), the first one is the one which will receive the backend traffic. @@ -427,6 +441,9 @@ metadata: } ``` +!!! attention +Since version 1.9.0, `"server-snippet"` annotation is disabled by default and has to be explicitly enabled, see [allow-snippet-annotations](./configmap.md#allow-snippet-annotations). Enabling it can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. + !!! attention This annotation can be used only once per host. @@ -467,6 +484,9 @@ Additionally it is possible to set: > Note: does not work with HTTP/2 listener because of a limitation in Lua [subrequests](https://github.com/openresty/lua-nginx-module#spdy-mode-not-fully-supported). > [UseHTTP2](./configmap.md#use-http2) configuration should be disabled! +* `nginx.ingress.kubernetes.io/auth-keepalive-share-vars`: + Whether to share Nginx variables among the current request and the auth request. Example use case is to track requests: when set to "true" X-Request-ID HTTP header will be the same for the backend and the auth request. + Defaults to "false". * `nginx.ingress.kubernetes.io/auth-keepalive-requests`: `` to specify the maximum number of requests that can be served through one keepalive connection. Defaults to `1000` and only applied if `auth-keepalive` is set to higher than `0`. @@ -501,12 +521,15 @@ nginx.ingress.kubernetes.io/auth-snippet: | ``` > Note: `nginx.ingress.kubernetes.io/auth-snippet` is an optional annotation. However, it may only be used in conjunction with `nginx.ingress.kubernetes.io/auth-url` and will be ignored if `nginx.ingress.kubernetes.io/auth-url` is not set +!!! attention +Since version 1.9.0, `"auth-snippet"` annotation is disabled by default and has to be explicitly enabled, see [allow-snippet-annotations](./configmap.md#allow-snippet-annotations). Enabling it can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. + !!! example Please check the [external-auth](../../examples/auth/external-auth/README.md) example. #### Global External Authentication -By default the controller redirects all requests to an existing service that provides authentication if `global-auth-url` is set in the NGINX ConfigMap. If you want to disable this behavior for that ingress, you can use `enable-global-auth: "false"` in the NGINX ConfigMap. +By default the controller redirects all requests to an existing service that provides authentication if `global-auth-url` is set in the NGINX ConfigMap. If you want to disable this behavior for a specific ingress, you can use the annotation `nginx.ingress.kubernetes.io/enable-global-auth: "false"`. `nginx.ingress.kubernetes.io/enable-global-auth`: indicates if GlobalExternalAuth configuration should be applied or not to this Ingress rule. Default values is set to `"true"`. @@ -891,10 +914,13 @@ nginx.ingress.kubernetes.io/modsecurity-snippet: | Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf ``` +!!! attention +Since version 1.9.0, `"modsecurity-snippet"` annotation is disabled by default and has to be explicitly enabled, see [allow-snippet-annotations](./configmap.md#allow-snippet-annotations). Enabling it can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. + ### Backend Protocol Using `backend-protocol` annotations is possible to indicate how NGINX should communicate with the backend service. (Replaces `secure-backends` in older versions) -Valid Values: HTTP, HTTPS, GRPC, GRPCS, AJP and FCGI +Valid Values: HTTP, HTTPS, GRPC, GRPCS and FCGI By default NGINX uses `HTTP`. @@ -980,3 +1006,6 @@ metadata: proxy_pass 127.0.0.1:80; } ``` + +!!! attention +Since version 1.9.0, `"stream-snippet"` annotation is disabled by default and has to be explicitly enabled, see [allow-snippet-annotations](./configmap.md#allow-snippet-annotations). Enabling it can be dangerous in multi-tenant clusters, as it can lead to people with otherwise limited permissions being able to retrieve all secrets on the cluster. See CVE-2021-25742 and the [related issue on github](https://github.com/kubernetes/ingress-nginx/issues/7837) for more information. diff --git a/docs/user-guide/nginx-configuration/configmap.md b/docs/user-guide/nginx-configuration/configmap.md index ccb115a0f..7038ca90d 100644 --- a/docs/user-guide/nginx-configuration/configmap.md +++ b/docs/user-guide/nginx-configuration/configmap.md @@ -25,210 +25,217 @@ data: The following table shows a configuration option's name, type, and the default value: -|name|type|default| -|:---|:---|:------| -|[add-headers](#add-headers)|string|""| -|[allow-backend-server-header](#allow-backend-server-header)|bool|"false"| -|[allow-snippet-annotations](#allow-snippet-annotations)|bool|true| -|[annotation-value-word-blocklist](#annotation-value-word-blocklist)|string array|""| -|[hide-headers](#hide-headers)|string array|empty| -|[access-log-params](#access-log-params)|string|""| -|[access-log-path](#access-log-path)|string|"/var/log/nginx/access.log"| -|[http-access-log-path](#http-access-log-path)|string|""| -|[stream-access-log-path](#stream-access-log-path)|string|""| -|[enable-access-log-for-default-backend](#enable-access-log-for-default-backend)|bool|"false"| -|[error-log-path](#error-log-path)|string|"/var/log/nginx/error.log"| -|[enable-modsecurity](#enable-modsecurity)|bool|"false"| -|[modsecurity-snippet](#modsecurity-snippet)|string|""| -|[enable-owasp-modsecurity-crs](#enable-owasp-modsecurity-crs)|bool|"false"| -|[client-header-buffer-size](#client-header-buffer-size)|string|"1k"| -|[client-header-timeout](#client-header-timeout)|int|60| -|[client-body-buffer-size](#client-body-buffer-size)|string|"8k"| -|[client-body-timeout](#client-body-timeout)|int|60| -|[disable-access-log](#disable-access-log)|bool|false| -|[disable-ipv6](#disable-ipv6)|bool|false| -|[disable-ipv6-dns](#disable-ipv6-dns)|bool|false| -|[enable-underscores-in-headers](#enable-underscores-in-headers)|bool|false| -|[enable-ocsp](#enable-ocsp)|bool|false| -|[ignore-invalid-headers](#ignore-invalid-headers)|bool|true| -|[retry-non-idempotent](#retry-non-idempotent)|bool|"false"| -|[error-log-level](#error-log-level)|string|"notice"| -|[http2-max-field-size](#http2-max-field-size)|string|"4k"| -|[http2-max-header-size](#http2-max-header-size)|string|"16k"| -|[http2-max-requests](#http2-max-requests)|int|1000| -|[http2-max-concurrent-streams](#http2-max-concurrent-streams)|int|128| -|[hsts](#hsts)|bool|"true"| -|[hsts-include-subdomains](#hsts-include-subdomains)|bool|"true"| -|[hsts-max-age](#hsts-max-age)|string|"15724800"| -|[hsts-preload](#hsts-preload)|bool|"false"| -|[keep-alive](#keep-alive)|int|75| -|[keep-alive-requests](#keep-alive-requests)|int|1000| -|[large-client-header-buffers](#large-client-header-buffers)|string|"4 8k"| -|[log-format-escape-none](#log-format-escape-none)|bool|"false"| -|[log-format-escape-json](#log-format-escape-json)|bool|"false"| -|[log-format-upstream](#log-format-upstream)|string|`$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id`| -|[log-format-stream](#log-format-stream)|string|`[$remote_addr] [$time_local] $protocol $status $bytes_sent $bytes_received $session_time`| -|[enable-multi-accept](#enable-multi-accept)|bool|"true"| -|[max-worker-connections](#max-worker-connections)|int|16384| -|[max-worker-open-files](#max-worker-open-files)|int|0| -|[map-hash-bucket-size](#max-hash-bucket-size)|int|64| -|[nginx-status-ipv4-whitelist](#nginx-status-ipv4-whitelist)|[]string|"127.0.0.1"| -|[nginx-status-ipv6-whitelist](#nginx-status-ipv6-whitelist)|[]string|"::1"| -|[proxy-real-ip-cidr](#proxy-real-ip-cidr)|[]string|"0.0.0.0/0"| -|[proxy-set-headers](#proxy-set-headers)|string|""| -|[server-name-hash-max-size](#server-name-hash-max-size)|int|1024| -|[server-name-hash-bucket-size](#server-name-hash-bucket-size)|int|`` -|[proxy-headers-hash-max-size](#proxy-headers-hash-max-size)|int|512| -|[proxy-headers-hash-bucket-size](#proxy-headers-hash-bucket-size)|int|64| -|[plugins](#plugins)|[]string| | -|[reuse-port](#reuse-port)|bool|"true"| -|[server-tokens](#server-tokens)|bool|"false"| -|[ssl-ciphers](#ssl-ciphers)|string|"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"| -|[ssl-ecdh-curve](#ssl-ecdh-curve)|string|"auto"| -|[ssl-dh-param](#ssl-dh-param)|string|""| -|[ssl-protocols](#ssl-protocols)|string|"TLSv1.2 TLSv1.3"| -|[ssl-session-cache](#ssl-session-cache)|bool|"true"| -|[ssl-session-cache-size](#ssl-session-cache-size)|string|"10m"| -|[ssl-session-tickets](#ssl-session-tickets)|bool|"false"| -|[ssl-session-ticket-key](#ssl-session-ticket-key)|string|`` -|[ssl-session-timeout](#ssl-session-timeout)|string|"10m"| -|[ssl-buffer-size](#ssl-buffer-size)|string|"4k"| -|[use-proxy-protocol](#use-proxy-protocol)|bool|"false"| -|[proxy-protocol-header-timeout](#proxy-protocol-header-timeout)|string|"5s"| -|[use-gzip](#use-gzip)|bool|"false"| -|[use-geoip](#use-geoip)|bool|"true"| -|[use-geoip2](#use-geoip2)|bool|"false"| -|[enable-brotli](#enable-brotli)|bool|"false"| -|[brotli-level](#brotli-level)|int|4| -|[brotli-min-length](#brotli-min-length)|int|20| -|[brotli-types](#brotli-types)|string|"application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"| -|[use-http2](#use-http2)|bool|"true"| -|[gzip-disable](#gzip-disable)|string|""| -|[gzip-level](#gzip-level)|int|1| -|[gzip-min-length](#gzip-min-length)|int|256| -|[gzip-types](#gzip-types)|string|"application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"| -|[worker-processes](#worker-processes)|string|``| -|[worker-cpu-affinity](#worker-cpu-affinity)|string|""| -|[worker-shutdown-timeout](#worker-shutdown-timeout)|string|"240s"| -|[load-balance](#load-balance)|string|"round_robin"| -|[variables-hash-bucket-size](#variables-hash-bucket-size)|int|128| -|[variables-hash-max-size](#variables-hash-max-size)|int|2048| -|[upstream-keepalive-connections](#upstream-keepalive-connections)|int|320| -|[upstream-keepalive-time](#upstream-keepalive-time)|string|"1h"| -|[upstream-keepalive-timeout](#upstream-keepalive-timeout)|int|60| -|[upstream-keepalive-requests](#upstream-keepalive-requests)|int|10000| -|[limit-conn-zone-variable](#limit-conn-zone-variable)|string|"$binary_remote_addr"| -|[proxy-stream-timeout](#proxy-stream-timeout)|string|"600s"| -|[proxy-stream-next-upstream](#proxy-stream-next-upstream)|bool|"true"| -|[proxy-stream-next-upstream-timeout](#proxy-stream-next-upstream-timeout)|string|"600s"| -|[proxy-stream-next-upstream-tries](#proxy-stream-next-upstream-tries)|int|3| -|[proxy-stream-responses](#proxy-stream-responses)|int|1| -|[bind-address](#bind-address)|[]string|""| -|[use-forwarded-headers](#use-forwarded-headers)|bool|"false"| -|[enable-real-ip](#enable-real-ip)|bool|"false"| -|[forwarded-for-header](#forwarded-for-header)|string|"X-Forwarded-For"| -|[compute-full-forwarded-for](#compute-full-forwarded-for)|bool|"false"| -|[proxy-add-original-uri-header](#proxy-add-original-uri-header)|bool|"false"| -|[generate-request-id](#generate-request-id)|bool|"true"| -|[enable-opentracing](#enable-opentracing)|bool|"false"| -|[opentracing-operation-name](#opentracing-operation-name)|string|""| -|[opentracing-location-operation-name](#opentracing-location-operation-name)|string|""| -|[zipkin-collector-host](#zipkin-collector-host)|string|""| -|[zipkin-collector-port](#zipkin-collector-port)|int|9411| -|[zipkin-service-name](#zipkin-service-name)|string|"nginx"| -|[zipkin-sample-rate](#zipkin-sample-rate)|float|1.0| -|[jaeger-collector-host](#jaeger-collector-host)|string|""| -|[jaeger-collector-port](#jaeger-collector-port)|int|6831| -|[jaeger-endpoint](#jaeger-endpoint)|string|""| -|[jaeger-service-name](#jaeger-service-name)|string|"nginx"| -|[jaeger-propagation-format](#jaeger-propagation-format)|string|"jaeger"| -|[jaeger-sampler-type](#jaeger-sampler-type)|string|"const"| -|[jaeger-sampler-param](#jaeger-sampler-param)|string|"1"| -|[jaeger-sampler-host](#jaeger-sampler-host)|string|"http://127.0.0.1"| -|[jaeger-sampler-port](#jaeger-sampler-port)|int|5778| -|[jaeger-trace-context-header-name](#jaeger-trace-context-header-name)|string|uber-trace-id| -|[jaeger-debug-header](#jaeger-debug-header)|string|uber-debug-id| -|[jaeger-baggage-header](#jaeger-baggage-header)|string|jaeger-baggage| -|[jaeger-trace-baggage-header-prefix](#jaeger-trace-baggage-header-prefix)|string|uberctx-| -|[datadog-collector-host](#datadog-collector-host)|string|""| -|[datadog-collector-port](#datadog-collector-port)|int|8126| -|[datadog-service-name](#datadog-service-name)|string|"nginx"| -|[datadog-environment](#datadog-environment)|string|"prod"| -|[datadog-operation-name-override](#datadog-operation-name-override)|string|"nginx.handle"| -|[datadog-priority-sampling](#datadog-priority-sampling)|bool|"true"| -|[datadog-sample-rate](#datadog-sample-rate)|float|1.0| -|[enable-opentelemetry](#enable-opentelemetry)|bool|"false"| -|[opentelemetry-trust-incoming-span](#opentelemetry-trust-incoming-span)|bool|"true"| -|[opentelemetry-operation-name](#opentelemetry-operation-name)|string|""| -|[opentelemetry-config](#/etc/nginx/opentelemetry.toml)|string|"/etc/nginx/opentelemetry.toml"| -|[otlp-collector-host](#otlp-collector-host)|string|""| -|[otlp-collector-port](#otlp-collector-port)|int|4317| -|[otel-max-queuesize](#otel-max-queuesize)|int|| -|[otel-schedule-delay-millis](#otel-schedule-delay-millis)|int|| -|[otel-max-export-batch-size](#otel-max-export-batch-size)|int|| -|[otel-service-name](#otel-service-name)|string|"nginx"| -|[otel-sampler](#otel-sampler)|string|"AlwaysOff"| -|[otel-sampler-parent-based](#otel-sampler-parent-based)|bool|"false"| -|[otel-sampler-ratio](#otel-sampler-ratio)|float|0.01| -|[main-snippet](#main-snippet)|string|""| -|[http-snippet](#http-snippet)|string|""| -|[server-snippet](#server-snippet)|string|""| -|[stream-snippet](#stream-snippet)|string|""| -|[location-snippet](#location-snippet)|string|""| -|[custom-http-errors](#custom-http-errors)|[]int|[]int{}| -|[proxy-body-size](#proxy-body-size)|string|"1m"| -|[proxy-connect-timeout](#proxy-connect-timeout)|int|5| -|[proxy-read-timeout](#proxy-read-timeout)|int|60| -|[proxy-send-timeout](#proxy-send-timeout)|int|60| -|[proxy-buffers-number](#proxy-buffers-number)|int|4| -|[proxy-buffer-size](#proxy-buffer-size)|string|"4k"| -|[proxy-cookie-path](#proxy-cookie-path)|string|"off"| -|[proxy-cookie-domain](#proxy-cookie-domain)|string|"off"| -|[proxy-next-upstream](#proxy-next-upstream)|string|"error timeout"| -|[proxy-next-upstream-timeout](#proxy-next-upstream-timeout)|int|0| -|[proxy-next-upstream-tries](#proxy-next-upstream-tries)|int|3| -|[proxy-redirect-from](#proxy-redirect-from)|string|"off"| -|[proxy-request-buffering](#proxy-request-buffering)|string|"on"| -|[ssl-redirect](#ssl-redirect)|bool|"true"| -|[force-ssl-redirect](#force-ssl-redirect)|bool|"false"| -|[denylist-source-range](#denylist-source-range)|[]string|[]string{}| -|[whitelist-source-range](#whitelist-source-range)|[]string|[]string{}| -|[skip-access-log-urls](#skip-access-log-urls)|[]string|[]string{}| -|[limit-rate](#limit-rate)|int|0| -|[limit-rate-after](#limit-rate-after)|int|0| -|[lua-shared-dicts](#lua-shared-dicts)|string|""| -|[http-redirect-code](#http-redirect-code)|int|308| -|[proxy-buffering](#proxy-buffering)|string|"off"| -|[limit-req-status-code](#limit-req-status-code)|int|503| -|[limit-conn-status-code](#limit-conn-status-code)|int|503| -|[enable-syslog](#enable-syslog)|bool|false| -|[syslog-host](#syslog-host)|string|""| -|[syslog-port](#syslog-port)|int|514| -|[no-tls-redirect-locations](#no-tls-redirect-locations)|string|"/.well-known/acme-challenge"| -|[global-auth-url](#global-auth-url)|string|""| -|[global-auth-method](#global-auth-method)|string|""| -|[global-auth-signin](#global-auth-signin)|string|""| -|[global-auth-signin-redirect-param](#global-auth-signin-redirect-param)|string|"rd"| -|[global-auth-response-headers](#global-auth-response-headers)|string|""| -|[global-auth-request-redirect](#global-auth-request-redirect)|string|""| -|[global-auth-snippet](#global-auth-snippet)|string|""| -|[global-auth-cache-key](#global-auth-cache-key)|string|""| -|[global-auth-cache-duration](#global-auth-cache-duration)|string|"200 202 401 5m"| -|[no-auth-locations](#no-auth-locations)|string|"/.well-known/acme-challenge"| -|[block-cidrs](#block-cidrs)|[]string|""| -|[block-user-agents](#block-user-agents)|[]string|""| -|[block-referers](#block-referers)|[]string|""| -|[proxy-ssl-location-only](#proxy-ssl-location-only)|bool|"false"| -|[default-type](#default-type)|string|"text/html"| -|[global-rate-limit-memcached-host](#global-rate-limit)|string|""| -|[global-rate-limit-memcached-port](#global-rate-limit)|int|11211| -|[global-rate-limit-memcached-connect-timeout](#global-rate-limit)|int|50| -|[global-rate-limit-memcached-max-idle-timeout](#global-rate-limit)|int|10000| -|[global-rate-limit-memcached-pool-size](#global-rate-limit)|int|50| -|[global-rate-limit-status-code](#global-rate-limit)|int|429| -|[service-upstream](#service-upstream)|bool|"false"| -|[ssl-reject-handshake](#ssl-reject-handshake)|bool|"false"| -|[debug-connections](#debug-connections)|[]string|"127.0.0.1,1.1.1.1/24"| +|name|type|default|notes| +|:---|:---|:------|:----| +|[add-headers](#add-headers)|string|""|| +|[allow-backend-server-header](#allow-backend-server-header)|bool|"false"|| +|[allow-cross-namespace-resources](#allow-cross-namespace-resources)|bool|"true"|| +|[allow-snippet-annotations](#allow-snippet-annotations)|bool|false|| +|[annotations-risk-level](#annotations-risk-level)|string|Critical|| +|[annotation-value-word-blocklist](#annotation-value-word-blocklist)|string array|""|| +|[hide-headers](#hide-headers)|string array|empty|| +|[access-log-params](#access-log-params)|string|""|| +|[access-log-path](#access-log-path)|string|"/var/log/nginx/access.log"|| +|[http-access-log-path](#http-access-log-path)|string|""|| +|[stream-access-log-path](#stream-access-log-path)|string|""|| +|[enable-access-log-for-default-backend](#enable-access-log-for-default-backend)|bool|"false"|| +|[enable-auth-access-log](#enable-auth-access-log)|bool|"false"|| +|[error-log-path](#error-log-path)|string|"/var/log/nginx/error.log"|| +|[enable-modsecurity](#enable-modsecurity)|bool|"false"|| +|[modsecurity-snippet](#modsecurity-snippet)|string|""|| +|[enable-owasp-modsecurity-crs](#enable-owasp-modsecurity-crs)|bool|"false"|| +|[client-header-buffer-size](#client-header-buffer-size)|string|"1k"|| +|[client-header-timeout](#client-header-timeout)|int|60|| +|[client-body-buffer-size](#client-body-buffer-size)|string|"8k"|| +|[client-body-timeout](#client-body-timeout)|int|60|| +|[disable-access-log](#disable-access-log)|bool|false|| +|[disable-ipv6](#disable-ipv6)|bool|false|| +|[disable-ipv6-dns](#disable-ipv6-dns)|bool|false|| +|[enable-underscores-in-headers](#enable-underscores-in-headers)|bool|false|| +|[enable-ocsp](#enable-ocsp)|bool|false|| +|[ignore-invalid-headers](#ignore-invalid-headers)|bool|true|| +|[retry-non-idempotent](#retry-non-idempotent)|bool|"false"|| +|[error-log-level](#error-log-level)|string|"notice"|| +|[http2-max-field-size](#http2-max-field-size)|string|""|DEPRECATED in favour of [large_client_header_buffers](#large-client-header-buffers)| +|[http2-max-header-size](#http2-max-header-size)|string|""|DEPRECATED in favour of [large_client_header_buffers](#large-client-header-buffers)| +|[http2-max-requests](#http2-max-requests)|int|0|DEPRECATED in favour of [keepalive_requests](#keepalive-requests)| +|[http2-max-concurrent-streams](#http2-max-concurrent-streams)|int|128|| +|[hsts](#hsts)|bool|"true"|| +|[hsts-include-subdomains](#hsts-include-subdomains)|bool|"true"|| +|[hsts-max-age](#hsts-max-age)|string|"31536000"|| +|[hsts-preload](#hsts-preload)|bool|"false"|| +|[keep-alive](#keep-alive)|int|75|| +|[keep-alive-requests](#keep-alive-requests)|int|1000|| +|[large-client-header-buffers](#large-client-header-buffers)|string|"4 8k"|| +|[log-format-escape-none](#log-format-escape-none)|bool|"false"|| +|[log-format-escape-json](#log-format-escape-json)|bool|"false"|| +|[log-format-upstream](#log-format-upstream)|string|`$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id`|| +|[log-format-stream](#log-format-stream)|string|`[$remote_addr] [$time_local] $protocol $status $bytes_sent $bytes_received $session_time`|| +|[enable-multi-accept](#enable-multi-accept)|bool|"true"|| +|[max-worker-connections](#max-worker-connections)|int|16384|| +|[max-worker-open-files](#max-worker-open-files)|int|0|| +|[map-hash-bucket-size](#max-hash-bucket-size)|int|64|| +|[nginx-status-ipv4-whitelist](#nginx-status-ipv4-whitelist)|[]string|"127.0.0.1"|| +|[nginx-status-ipv6-whitelist](#nginx-status-ipv6-whitelist)|[]string|"::1"|| +|[proxy-real-ip-cidr](#proxy-real-ip-cidr)|[]string|"0.0.0.0/0"|| +|[proxy-set-headers](#proxy-set-headers)|string|""|| +|[server-name-hash-max-size](#server-name-hash-max-size)|int|1024|| +|[server-name-hash-bucket-size](#server-name-hash-bucket-size)|int|``| +|[proxy-headers-hash-max-size](#proxy-headers-hash-max-size)|int|512|| +|[proxy-headers-hash-bucket-size](#proxy-headers-hash-bucket-size)|int|64|| +|[plugins](#plugins)|[]string| || +|[reuse-port](#reuse-port)|bool|"true"|| +|[server-tokens](#server-tokens)|bool|"false"|| +|[ssl-ciphers](#ssl-ciphers)|string|"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"|| +|[ssl-ecdh-curve](#ssl-ecdh-curve)|string|"auto"|| +|[ssl-dh-param](#ssl-dh-param)|string|""|| +|[ssl-protocols](#ssl-protocols)|string|"TLSv1.2 TLSv1.3"|| +|[ssl-session-cache](#ssl-session-cache)|bool|"true"|| +|[ssl-session-cache-size](#ssl-session-cache-size)|string|"10m"|| +|[ssl-session-tickets](#ssl-session-tickets)|bool|"false"|| +|[ssl-session-ticket-key](#ssl-session-ticket-key)|string|``| +|[ssl-session-timeout](#ssl-session-timeout)|string|"10m"|| +|[ssl-buffer-size](#ssl-buffer-size)|string|"4k"|| +|[use-proxy-protocol](#use-proxy-protocol)|bool|"false"|| +|[proxy-protocol-header-timeout](#proxy-protocol-header-timeout)|string|"5s"|| +|[enable-aio-write](#enable-aio-write)|bool|"true"|| +|[use-gzip](#use-gzip)|bool|"false"|| +|[use-geoip](#use-geoip)|bool|"true"|| +|[use-geoip2](#use-geoip2)|bool|"false"|| +|[enable-brotli](#enable-brotli)|bool|"false"|| +|[brotli-level](#brotli-level)|int|4|| +|[brotli-min-length](#brotli-min-length)|int|20|| +|[brotli-types](#brotli-types)|string|"application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"|| +|[use-http2](#use-http2)|bool|"true"|| +|[gzip-disable](#gzip-disable)|string|""|| +|[gzip-level](#gzip-level)|int|1|| +|[gzip-min-length](#gzip-min-length)|int|256|| +|[gzip-types](#gzip-types)|string|"application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"|| +|[worker-processes](#worker-processes)|string|``|| +|[worker-cpu-affinity](#worker-cpu-affinity)|string|""|| +|[worker-shutdown-timeout](#worker-shutdown-timeout)|string|"240s"|| +|[load-balance](#load-balance)|string|"round_robin"|| +|[variables-hash-bucket-size](#variables-hash-bucket-size)|int|128|| +|[variables-hash-max-size](#variables-hash-max-size)|int|2048|| +|[upstream-keepalive-connections](#upstream-keepalive-connections)|int|320|| +|[upstream-keepalive-time](#upstream-keepalive-time)|string|"1h"|| +|[upstream-keepalive-timeout](#upstream-keepalive-timeout)|int|60|| +|[upstream-keepalive-requests](#upstream-keepalive-requests)|int|10000|| +|[limit-conn-zone-variable](#limit-conn-zone-variable)|string|"$binary_remote_addr"|| +|[proxy-stream-timeout](#proxy-stream-timeout)|string|"600s"|| +|[proxy-stream-next-upstream](#proxy-stream-next-upstream)|bool|"true"|| +|[proxy-stream-next-upstream-timeout](#proxy-stream-next-upstream-timeout)|string|"600s"|| +|[proxy-stream-next-upstream-tries](#proxy-stream-next-upstream-tries)|int|3|| +|[proxy-stream-responses](#proxy-stream-responses)|int|1|| +|[bind-address](#bind-address)|[]string|""|| +|[use-forwarded-headers](#use-forwarded-headers)|bool|"false"|| +|[enable-real-ip](#enable-real-ip)|bool|"false"|| +|[forwarded-for-header](#forwarded-for-header)|string|"X-Forwarded-For"|| +|[compute-full-forwarded-for](#compute-full-forwarded-for)|bool|"false"|| +|[proxy-add-original-uri-header](#proxy-add-original-uri-header)|bool|"false"|| +|[generate-request-id](#generate-request-id)|bool|"true"|| +|[enable-opentracing](#enable-opentracing)|bool|"false"|| +|[opentracing-operation-name](#opentracing-operation-name)|string|""|| +|[opentracing-location-operation-name](#opentracing-location-operation-name)|string|""|| +|[zipkin-collector-host](#zipkin-collector-host)|string|""|| +|[zipkin-collector-port](#zipkin-collector-port)|int|9411|| +|[zipkin-service-name](#zipkin-service-name)|string|"nginx"|| +|[zipkin-sample-rate](#zipkin-sample-rate)|float|1.0|| +|[jaeger-collector-host](#jaeger-collector-host)|string|""|| +|[jaeger-collector-port](#jaeger-collector-port)|int|6831|| +|[jaeger-endpoint](#jaeger-endpoint)|string|""|| +|[jaeger-service-name](#jaeger-service-name)|string|"nginx"|| +|[jaeger-propagation-format](#jaeger-propagation-format)|string|"jaeger"|| +|[jaeger-sampler-type](#jaeger-sampler-type)|string|"const"|| +|[jaeger-sampler-param](#jaeger-sampler-param)|string|"1"|| +|[jaeger-sampler-host](#jaeger-sampler-host)|string|"http://127.0.0.1"|| +|[jaeger-sampler-port](#jaeger-sampler-port)|int|5778|| +|[jaeger-trace-context-header-name](#jaeger-trace-context-header-name)|string|uber-trace-id|| +|[jaeger-debug-header](#jaeger-debug-header)|string|uber-debug-id|| +|[jaeger-baggage-header](#jaeger-baggage-header)|string|jaeger-baggage|| +|[jaeger-trace-baggage-header-prefix](#jaeger-trace-baggage-header-prefix)|string|uberctx-|| +|[datadog-collector-host](#datadog-collector-host)|string|""|| +|[datadog-collector-port](#datadog-collector-port)|int|8126|| +|[datadog-service-name](#datadog-service-name)|string|"nginx"|| +|[datadog-environment](#datadog-environment)|string|"prod"|| +|[datadog-operation-name-override](#datadog-operation-name-override)|string|"nginx.handle"|| +|[datadog-priority-sampling](#datadog-priority-sampling)|bool|"true"|| +|[datadog-sample-rate](#datadog-sample-rate)|float|1.0|| +|[enable-opentelemetry](#enable-opentelemetry)|bool|"false"|| +|[opentelemetry-trust-incoming-span](#opentelemetry-trust-incoming-span)|bool|"true"|| +|[opentelemetry-operation-name](#opentelemetry-operation-name)|string|""|| +|[opentelemetry-config](#/etc/ingress-controller/telemetry/opentelemetry.toml)|string|"/etc/ingress-controller/telemetry/opentelemetry.toml"|| +|[otlp-collector-host](#otlp-collector-host)|string|""|| +|[otlp-collector-port](#otlp-collector-port)|int|4317|| +|[otel-max-queuesize](#otel-max-queuesize)|int||| +|[otel-schedule-delay-millis](#otel-schedule-delay-millis)|int||| +|[otel-max-export-batch-size](#otel-max-export-batch-size)|int||| +|[otel-service-name](#otel-service-name)|string|"nginx"|| +|[otel-sampler](#otel-sampler)|string|"AlwaysOff"|| +|[otel-sampler-parent-based](#otel-sampler-parent-based)|bool|"false"|| +|[otel-sampler-ratio](#otel-sampler-ratio)|float|0.01|| +|[main-snippet](#main-snippet)|string|""|| +|[http-snippet](#http-snippet)|string|""|| +|[server-snippet](#server-snippet)|string|""|| +|[stream-snippet](#stream-snippet)|string|""|| +|[location-snippet](#location-snippet)|string|""|| +|[custom-http-errors](#custom-http-errors)|[]int|[]int{}|| +|[disable-proxy-intercept-errors](#disable-proxy-intercept-errors)|bool|"false"| +|[proxy-body-size](#proxy-body-size)|string|"1m"|| +|[proxy-connect-timeout](#proxy-connect-timeout)|int|5|| +|[proxy-read-timeout](#proxy-read-timeout)|int|60|| +|[proxy-send-timeout](#proxy-send-timeout)|int|60|| +|[proxy-buffers-number](#proxy-buffers-number)|int|4|| +|[proxy-buffer-size](#proxy-buffer-size)|string|"4k"|| +|[proxy-cookie-path](#proxy-cookie-path)|string|"off"|| +|[proxy-cookie-domain](#proxy-cookie-domain)|string|"off"|| +|[proxy-next-upstream](#proxy-next-upstream)|string|"error timeout"|| +|[proxy-next-upstream-timeout](#proxy-next-upstream-timeout)|int|0|| +|[proxy-next-upstream-tries](#proxy-next-upstream-tries)|int|3|| +|[proxy-redirect-from](#proxy-redirect-from)|string|"off"|| +|[proxy-request-buffering](#proxy-request-buffering)|string|"on"|| +|[ssl-redirect](#ssl-redirect)|bool|"true"|| +|[force-ssl-redirect](#force-ssl-redirect)|bool|"false"|| +|[denylist-source-range](#denylist-source-range)|[]string|[]string{}|| +|[whitelist-source-range](#whitelist-source-range)|[]string|[]string{}|| +|[skip-access-log-urls](#skip-access-log-urls)|[]string|[]string{}|| +|[limit-rate](#limit-rate)|int|0|| +|[limit-rate-after](#limit-rate-after)|int|0|| +|[lua-shared-dicts](#lua-shared-dicts)|string|""|| +|[http-redirect-code](#http-redirect-code)|int|308|| +|[proxy-buffering](#proxy-buffering)|string|"off"|| +|[limit-req-status-code](#limit-req-status-code)|int|503|| +|[limit-conn-status-code](#limit-conn-status-code)|int|503|| +|[enable-syslog](#enable-syslog)|bool|false|| +|[syslog-host](#syslog-host)|string|""|| +|[syslog-port](#syslog-port)|int|514|| +|[no-tls-redirect-locations](#no-tls-redirect-locations)|string|"/.well-known/acme-challenge"|| +|[global-auth-url](#global-auth-url)|string|""|| +|[global-auth-method](#global-auth-method)|string|""|| +|[global-auth-signin](#global-auth-signin)|string|""|| +|[global-auth-signin-redirect-param](#global-auth-signin-redirect-param)|string|"rd"|| +|[global-auth-response-headers](#global-auth-response-headers)|string|""|| +|[global-auth-request-redirect](#global-auth-request-redirect)|string|""|| +|[global-auth-snippet](#global-auth-snippet)|string|""|| +|[global-auth-cache-key](#global-auth-cache-key)|string|""|| +|[global-auth-cache-duration](#global-auth-cache-duration)|string|"200 202 401 5m"|| +|[global-auth-always-set-cookie](#global-auth-always-set-cookie)|bool|"false"|| +|[no-auth-locations](#no-auth-locations)|string|"/.well-known/acme-challenge"|| +|[block-cidrs](#block-cidrs)|[]string|""|| +|[block-user-agents](#block-user-agents)|[]string|""|| +|[block-referers](#block-referers)|[]string|""|| +|[proxy-ssl-location-only](#proxy-ssl-location-only)|bool|"false"|| +|[default-type](#default-type)|string|"text/html"|| +|[global-rate-limit-memcached-host](#global-rate-limit)|string|""|| +|[global-rate-limit-memcached-port](#global-rate-limit)|int|11211|| +|[global-rate-limit-memcached-connect-timeout](#global-rate-limit)|int|50|| +|[global-rate-limit-memcached-max-idle-timeout](#global-rate-limit)|int|10000|| +|[global-rate-limit-memcached-pool-size](#global-rate-limit)|int|50|| +|[global-rate-limit-status-code](#global-rate-limit)|int|429|| +|[service-upstream](#service-upstream)|bool|"false"|| +|[ssl-reject-handshake](#ssl-reject-handshake)|bool|"false"|| +|[debug-connections](#debug-connections)|[]string|"127.0.0.1,1.1.1.1/24"|| +|[strict-validate-path-type](#strict-validate-path-type)|bool|"false" (v1.7.x)|| ## add-headers @@ -238,13 +245,37 @@ Sets custom headers from named configmap before sending traffic to the client. S Enables the return of the header Server from the backend instead of the generic nginx string. _**default:**_ is disabled +## allow-cross-namespace-resources + +Enables users to consume cross namespace resource on annotations, when was previously enabled . _**default:**_ true + +**Annotations that may be impacted with this change**: +* `auth-secret` +* `auth-proxy-set-header` +* `auth-tls-secret` +* `fastcgi-params-configmap` +* `proxy-ssl-secret` + + +**This option will be defaulted to false in the next major release** + ## allow-snippet-annotations -Enables Ingress to parse and add *-snippet annotations/directives created by the user. _**default:**_ `true` +Enables Ingress to parse and add *-snippet annotations/directives created by the user. _**default:**_ `false` Warning: 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 +**This option is defaulted to false since v1.9.0** + +## annotations-risk-level + +Represents the risk accepted on an annotation. If the risk is, for instance `Medium`, annotations with risk High and Critical will not be accepted. + +Accepted values are `Critical`, `High`, `Medium` and `Low`. + +Defaults to `Critical` but will be changed to `High` on the next minor release + ## annotation-value-word-blocklist Contains a comma-separated value of chars/words that are well known of being used to abuse Ingress configuration @@ -298,6 +329,10 @@ __Note:__ If not specified, the `access-log-path` will be used. Enables logging access to default backend. _**default:**_ is disabled. +## enable-auth-access-log + +Enables logging access to the authentication endpoint. _**default:**_ is disabled. + ## error-log-path Error log path. Goes to `/var/log/nginx/error.log` by default. @@ -682,12 +717,19 @@ Enables or disables the [PROXY protocol](https://www.nginx.com/resources/admin-g Sets the timeout value for receiving the proxy-protocol headers. The default of 5 seconds prevents the TLS passthrough handler from waiting indefinitely on a dropped connection. _**default:**_ 5s +## enable-aio-write + +Enables or disables the directive [aio_write](https://nginx.org/en/docs/http/ngx_http_core_module.html#aio_write) that writes files asynchronously. _**default:**_ true + ## use-gzip Enables or disables compression of HTTP responses using the ["gzip" module](https://nginx.org/en/docs/http/ngx_http_gzip_module.html). MIME types to compress are controlled by [gzip-types](#gzip-types). _**default:**_ false ## use-geoip +!!! attention + GeoIP is deprecated and removed on v1.10. Users willing to use GeoIP should use GeoIP2 + Enables or disables ["geoip" module](https://nginx.org/en/docs/http/ngx_http_geoip_module.html) that creates variables with values depending on the client IP address, using the precompiled MaxMind databases. _**default:**_ true @@ -1088,10 +1130,18 @@ You can not use this to add new locations that proxy to the Kubernetes pods, as Enables which HTTP codes should be passed for processing with the [error_page directive](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) -Setting at least one code also enables [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) which are required to process error_page. +Setting at least one code also enables [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) if not disabled with [disable-proxy-intercept-errors](#disable-proxy-intercept-errors). Example usage: `custom-http-errors: 404,415` +## disable-proxy-intercept-errors + +Allows to disable [proxy-intercept-errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors). + +Disabling [proxy_intercept_errors](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) allows to pass upstream errors to client even if [custom-http-errors](#custom-http-errors) are set. + +Example usage: `disable-proxy-intercept-errors: "true"` + ## proxy-body-size Sets the maximum allowed size of the client request body. @@ -1249,7 +1299,7 @@ _**default:**_ "/.well-known/acme-challenge" A url to an existing service that provides authentication for all the locations. Similar to the Ingress rule annotation `nginx.ingress.kubernetes.io/auth-url`. -Locations that should not get authenticated can be listed using `no-auth-locations` See [no-auth-locations](#no-auth-locations). In addition, each service can be excluded from authentication via annotation `enable-global-auth` set to "false". +Locations that should not get authenticated can be listed using `no-auth-locations` See [no-auth-locations](#no-auth-locations). In addition, each service can be excluded from authentication via annotation `nginx.ingress.kubernetes.io/enable-global-auth` set to "false". _**default:**_ "" _References:_ [https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#external-authentication](https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#external-authentication) @@ -1379,3 +1429,17 @@ _**default:**_ "" _References:_ [http://nginx.org/en/docs/ngx_core_module.html#debug_connection](http://nginx.org/en/docs/ngx_core_module.html#debug_connection) + +## strict-validate-path-type +Ingress objects contains a field called pathType that defines the proxy behavior. It can be `Exact`, `Prefix` and `ImplementationSpecific`. + +When pathType is configured as `Exact` or `Prefix`, there should be a more strict validation, allowing only paths starting with "/" and +containing only alphanumeric characters and "-", "_" and additional "/". + +When this option is enabled, the validation will happen on the Admission Webhook, making any Ingress not using pathType `ImplementationSpecific` +and containing invalid characters to be denied. + +This means that Ingress objects that rely on paths containing regex characters should use `ImplementationSpecific` pathType. + +The cluster admin should establish validation rules using mechanisms like [Open Policy Agent](https://www.openpolicyagent.org/) to +validate that only authorized users can use `ImplementationSpecific` pathType and that only the authorized characters can be used. diff --git a/docs/user-guide/retaining-client-ipaddress.md b/docs/user-guide/retaining-client-ipaddress.md new file mode 100644 index 000000000..237bd3004 --- /dev/null +++ b/docs/user-guide/retaining-client-ipaddress.md @@ -0,0 +1,44 @@ + +## Retaining Client IPAddress + +Please read this https://kubernetes.github.io/ingress-nginx/user-guide/miscellaneous/#source-ip-address , to get details of retaining the client IPAddress. + +### Using proxy-protocol + +Please read this https://kubernetes.github.io/ingress-nginx/user-guide/miscellaneous/#proxy-protocol , to use proxy-protocol for retaining client IPAddress + + +### Using the K8S spec service.spec.externalTrafficPolicy + +``` +% kubectl explain service.spec.externalTrafficPolicy +KIND: Service +VERSION: v1 + +FIELD: externalTrafficPolicy + +DESCRIPTION: + externalTrafficPolicy describes how nodes distribute service traffic they + receive on one of the Service's "externally-facing" addresses (NodePorts, + ExternalIPs, and LoadBalancer IPs). If set to "Local", the proxy will + configure the service in a way that assumes that external load balancers + will take care of balancing the service traffic between nodes, and so each + node will deliver traffic only to the node-local endpoints of the service, + without masquerading the client source IP. (Traffic mistakenly sent to a + node with no endpoints will be dropped.) The default value, "Cluster", uses + the standard behavior of routing to all endpoints evenly (possibly modified + by topology and other features). Note that traffic sent to an External IP or + LoadBalancer IP from within the cluster will always get "Cluster" semantics, + but clients sending to a NodePort from within the cluster may need to take + traffic policy into account when picking a node. + + Possible enum values: + - `"Cluster"` routes traffic to all endpoints. + - `"Local"` preserves the source IP of the traffic by routing only to + endpoints on the same node as the traffic was received on (dropping the + traffic if there are no local endpoints). + +``` + + +- Setting the field `externalTrafficPolicy`, in the ingress-controller service, to a value of `Local` retains the client's ipaddress, within the scope explained above diff --git a/docs/user-guide/third-party-addons/opentelemetry.md b/docs/user-guide/third-party-addons/opentelemetry.md index 1b9b6536e..a4bd8d060 100644 --- a/docs/user-guide/third-party-addons/opentelemetry.md +++ b/docs/user-guide/third-party-addons/opentelemetry.md @@ -5,6 +5,18 @@ Enables requests served by NGINX for distributed telemetry via The OpenTelemetry Using the third party module [opentelemetry-cpp-contrib/nginx](https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/nginx) the Ingress-Nginx Controller can configure NGINX to enable [OpenTelemetry](http://opentelemetry.io) instrumentation. By default this feature is disabled. +Check out this demo showcasing OpenTelemetry in Ingress NGINX. The video provides an overview and +practical demonstration of how OpenTelemetry can be utilized in Ingress NGINX for observability +and monitoring purposes. + +

+ + Video Thumbnail + +

+ +

Demo: OpenTelemetry in Ingress NGINX.

+ ## Usage To enable the instrumentation we must enable OpenTelemetry in the configuration ConfigMap: @@ -140,7 +152,7 @@ To install the example and collectors run: ```yaml opentelemetry: enabled: true - image: registry.k8s.io/ingress-nginx/opentelemetry:v20230107-helm-chart-4.4.2-2-g96b3d2165@sha256:331b9bebd6acfcd2d3048abbdd86555f5be76b7e3d0b5af4300b04235c6056c9 + image: registry.k8s.io/ingress-nginx/opentelemetry:v20230527@sha256:fd7ec835f31b7b37187238eb4fdad4438806e69f413a203796263131f4f02ed0 containerSecurityContext: allowPrivilegeEscalation: false ``` @@ -153,7 +165,7 @@ To install the example and collectors run: kind: ConfigMap data: enable-opentelemetry: "true" - opentelemetry-config: "/etc/nginx/opentelemetry.toml" + opentelemetry-config: "/etc/ingress-controller/telemetry/opentelemetry.toml" opentelemetry-operation-name: "HTTP $request_method $service_name $uri" opentelemetry-trust-incoming-span: "true" otlp-collector-host: "otel-coll-collector.otel.svc" @@ -258,3 +270,45 @@ To install the example and collectors run: ``` In the Zipkin interface we can see the details: ![zipkin screenshot](../../images/otel-zipkin-demo.png "zipkin screenshot") + +## Migration from OpenTracing, Jaeger, Zipkin and Datadog + +If you are migrating from OpenTracing, Jaeger, Zipkin, or Datadog to OpenTelemetry, +you may need to update various annotations and configurations. Here are the mappings +for common annotations and configurations: + +### Annotations + +| Legacy | OpenTelemetry | +|--------------------------------------------------|--------------------------------------------------| +| `nginx.ingress.kubernetes.io/enable-opentracing` | `nginx.ingress.kubernetes.io/enable-opentelemetry` | +| `opentracing-trust-incoming-span` | `opentracing-trust-incoming-span` | + +### Configs + +| Legacy | OpenTelemetry | +|---------------------------------------|----------------------------------------------| +| `opentracing-operation-name` | `opentelemetry-operation-name` | +| `opentracing-location-operation-name` | `opentelemetry-operation-name` | +| `opentracing-trust-incoming-span` | `opentelemetry-trust-incoming-span` | +| `zipkin-collector-port` | `otlp-collector-port` | +| `zipkin-service-name` | `otel-service-name` | +| `zipkin-sample-rate` | `otel-sampler-ratio` | +| `jaeger-collector-port` | `otlp-collector-port` | +| `jaeger-endpoint` | `otlp-collector-port`, `otlp-collector-host` | +| `jaeger-service-name` | `otel-service-name` | +| `jaeger-propagation-format` | `N/A` | +| `jaeger-sampler-type` | `otel-sampler` | +| `jaeger-sampler-param` | `otel-sampler` | +| `jaeger-sampler-host` | `N/A` | +| `jaeger-sampler-port` | `N/A` | +| `jaeger-trace-context-header-name` | `N/A` | +| `jaeger-debug-header` | `N/A` | +| `jaeger-baggage-header` | `N/A` | +| `jaeger-tracer-baggage-header-prefix` | `N/A` | +| `datadog-collector-port` | `otlp-collector-port` | +| `datadog-service-name` | `otel-service-name` | +| `datadog-environment` | `N/A` | +| `datadog-operation-name-override` | `N/A` | +| `datadog-priority-sampling` | `otel-sampler` | +| `datadog-sample-rate` | `otel-sampler-ratio` | diff --git a/docs/user-guide/tls.md b/docs/user-guide/tls.md index 11338981b..f6fee6895 100644 --- a/docs/user-guide/tls.md +++ b/docs/user-guide/tls.md @@ -127,6 +127,9 @@ ingress-nginx defaults to using TLS 1.2 and 1.3 only, with a [secure set of TLS ### Legacy TLS +!!! warning + TLSv1, TLSv1.1 are not supported in ingress-nginx v1.6.0 and above. + The default configuration, though secure, does not support some older browsers and operating systems. For instance, TLS 1.1+ is only enabled by default from Android 5.0 on. At the time of writing, diff --git a/go.mod b/go.mod index 9de23ba1a..87e4d8873 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module k8s.io/ingress-nginx -go 1.20 +go 1.21.3 require ( - github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a + dario.cat/mergo v1.0.0 + github.com/armon/go-proxyproto v0.1.0 github.com/eapache/channels v1.1.0 - github.com/fsnotify/fsnotify v1.6.0 - github.com/imdario/mergo v0.3.15 + github.com/fsnotify/fsnotify v1.7.0 github.com/json-iterator/go v1.1.12 github.com/kylelemons/godebug v1.1.0 github.com/mitchellh/go-ps v1.0.0 @@ -14,55 +14,55 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/moul/pb v0.0.0-20220425114252-bca18df4138c github.com/ncabatoff/process-exporter v0.7.10 - github.com/onsi/ginkgo/v2 v2.9.0 - github.com/opencontainers/runc v1.1.7 + github.com/onsi/ginkgo/v2 v2.13.1 + github.com/opencontainers/runc v1.1.10 github.com/pmezard/go-difflib v1.0.0 - github.com/prometheus/client_golang v1.15.0 - github.com/prometheus/client_model v0.3.0 - github.com/prometheus/common v0.42.0 - github.com/spf13/cobra v1.7.0 + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_model v0.5.0 + github.com/prometheus/common v0.45.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/yudai/gojsondiff v1.0.0 - github.com/zakjan/cert-chain-resolver v0.0.0-20211122211144-c6b0b792af9a - golang.org/x/crypto v0.8.0 - google.golang.org/grpc v1.54.0 + github.com/zakjan/cert-chain-resolver v0.0.0-20221221105603-fcedb00c5b30 + golang.org/x/crypto v0.15.0 + google.golang.org/grpc v1.59.0 google.golang.org/grpc/examples v0.0.0-20221220003428-4f16fbe410f7 gopkg.in/go-playground/pool.v3 v3.1.1 gopkg.in/mcuadros/go-syslog.v2 v2.3.0 - k8s.io/api v0.26.4 - k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery v0.26.4 - k8s.io/apiserver v0.26.4 - k8s.io/cli-runtime v0.26.4 - k8s.io/client-go v0.26.4 - k8s.io/code-generator v0.26.4 - k8s.io/component-base v0.26.4 - k8s.io/klog/v2 v2.90.1 + k8s.io/api v0.27.6 + k8s.io/apiextensions-apiserver v0.27.6 + k8s.io/apimachinery v0.27.6 + k8s.io/apiserver v0.27.6 + k8s.io/cli-runtime v0.27.6 + k8s.io/client-go v0.27.6 + k8s.io/code-generator v0.27.6 + k8s.io/component-base v0.27.6 + k8s.io/klog/v2 v2.100.1 pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732 sigs.k8s.io/controller-runtime v0.14.6 sigs.k8s.io/mdtoc v1.1.0 ) require ( - github.com/BurntSushi/toml v1.0.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/coreos/go-systemd/v22 v22.3.2 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect + github.com/coreos/go-systemd/v22 v22.4.0 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eapache/queue v1.1.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fullsailor/pkcs7 v0.0.0-20160414161337-2585af45975b // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -70,18 +70,19 @@ require ( github.com/gomarkdown/markdown v0.0.0-20210514010506-3b9f47219fe7 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mmarkdown/mmark v2.0.40+incompatible // indirect github.com/moby/sys/mountinfo v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -93,34 +94,34 @@ require ( github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect github.com/sergi/go-diff v1.1.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/tools v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.2 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index da99bd248..a325a9c26 100644 --- a/go.sum +++ b/go.sum @@ -30,18 +30,20 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= -github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a h1:AP/vsCIvJZ129pdm9Ek7bH7yutN3hByqsMoNrWAxRQc= -github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU= +github.com/armon/go-proxyproto v0.1.0 h1:TWWcSsjco7o2itn6r25/5AqKBiWmsiuzsUDLT/MTl7k= +github.com/armon/go-proxyproto v0.1.0/go.mod h1:Xj90dce2VKbHzRAeiVQAMBtj4M5oidoXJ8lmgyW21mw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -57,12 +59,12 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU= +github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -71,8 +73,8 @@ github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4 github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -83,12 +85,13 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fullsailor/pkcs7 v0.0.0-20160414161337-2585af45975b h1:074/xhloHUBOpTZwlIzQ28rbPY8pNJvzY7Gcx5KnNOk= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullsailor/pkcs7 v0.0.0-20160414161337-2585af45975b/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -100,20 +103,20 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -169,8 +172,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -183,13 +186,13 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= @@ -198,9 +201,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -226,6 +228,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -234,17 +237,15 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -275,7 +276,6 @@ github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 h1:t4WWQ9I797y7QU github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833/go.mod h1:0CznHmXSjMEqs5Tezj/w2emQoM41wzYM9KpDKUHPYag= github.com/ncabatoff/process-exporter v0.7.10 h1:+Ere7+3se6QqP54gg7aBRagWcL8bq3u5zNi/GRSWeKQ= github.com/ncabatoff/process-exporter v0.7.10/go.mod h1:DHZRZjqxw9LCOpLlX0DjBuyn6d5plh41Jv6Tmttj7Ek= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -283,13 +283,14 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= +github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= -github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= -github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= +github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -305,42 +306,43 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/exporter-toolkit v0.7.0/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -358,8 +360,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli v1.17.1-0.20160602030128-01a33823596e/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= @@ -373,8 +376,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zakjan/cert-chain-resolver v0.0.0-20211122211144-c6b0b792af9a h1:CbXWHAnmrtTKgX+yMVVANuRJP8ld88ELbAYAYnBdLJ4= -github.com/zakjan/cert-chain-resolver v0.0.0-20211122211144-c6b0b792af9a/go.mod h1:/Hzu8ych2oXCs1iNI+MeASyFzWTncQ6nlu/wgqbqC2A= +github.com/zakjan/cert-chain-resolver v0.0.0-20221221105603-fcedb00c5b30 h1:rzHvkiukOVYcf840FqAsHqBMhfLofvQIxWtczkGRklU= +github.com/zakjan/cert-chain-resolver v0.0.0-20221221105603-fcedb00c5b30/go.mod h1:/Hzu8ych2oXCs1iNI+MeASyFzWTncQ6nlu/wgqbqC2A= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -383,8 +386,11 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -393,8 +399,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -425,8 +431,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -460,16 +466,16 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -497,7 +503,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -525,21 +530,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -589,13 +594,14 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -650,8 +656,8 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -664,8 +670,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/examples v0.0.0-20221220003428-4f16fbe410f7 h1:pPsdyuBif+uoyUoL19yuj/TCfUPsmpJHJZhWQ98JGLU= google.golang.org/grpc/examples v0.0.0-20221220003428-4f16fbe410f7/go.mod h1:8pQa1yxxkh+EsxUK8/455D5MSbv3vgmEJqKCH3y17mI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -680,13 +686,12 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -720,31 +725,31 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= -k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= -k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= -k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= -k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= -k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/apiserver v0.26.4 h1:3Oq4mnJv0mzVX7BR/Nod+8KjlELf/3Ljvu9ZWDyLUoA= -k8s.io/apiserver v0.26.4/go.mod h1:yAY3O1vBM4/0OIGAGeWcdfzQvgdwJ188VirLcuSAVnw= -k8s.io/cli-runtime v0.26.4 h1:MgSU871KDzBDX7V9GtuqS6Ai9lhQCHgRzkurnXOWtZ0= -k8s.io/cli-runtime v0.26.4/go.mod h1:MjJ2DXMChw2zcG0/agzm17xwKpfVxOfuoCdfY9iOCOE= -k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= -k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= -k8s.io/code-generator v0.26.4 h1:zgDD0qX13p/jtrAoYRRiYeQ5ibnriwmo2cMkMZAtJxc= -k8s.io/code-generator v0.26.4/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI= -k8s.io/component-base v0.26.4 h1:Bg2xzyXNKL3eAuiTEu3XE198d6z22ENgFgGQv2GGOUk= -k8s.io/component-base v0.26.4/go.mod h1:lTuWL1Xz/a4e80gmIC3YZG2JCO4xNwtKWHJWeJmsq20= +k8s.io/api v0.27.6 h1:PBWu/lywJe2qQcshMjubzcBg7+XDZOo7O8JJAWuYtUo= +k8s.io/api v0.27.6/go.mod h1:AQYj0UsFCp3qJE7bOVnUuy4orCsXVkvHefnbYQiNWgk= +k8s.io/apiextensions-apiserver v0.27.6 h1:mOwSBJtThZhpJr+8gEkc3wFDIjq87E3JspR5mtZxIg8= +k8s.io/apiextensions-apiserver v0.27.6/go.mod h1:AVNlLYRrESG5Poo6ASRUhY2pvoKPcNt8y/IuZ4lx3o8= +k8s.io/apimachinery v0.27.6 h1:mGU8jmBq5o8mWBov+mLjdTBcU+etTE19waies4AQ6NE= +k8s.io/apimachinery v0.27.6/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/apiserver v0.27.6 h1:r/eHN8r3lG2buggHrVMy++kKhHlHn1HWSX1dqDtes54= +k8s.io/apiserver v0.27.6/go.mod h1:Xeo9OEXn2kDLK5pqspjdXQx7YKgDyKSpwIB4p0BmpAQ= +k8s.io/cli-runtime v0.27.6 h1:ik1+20C0AvxYcqEzwebH2PHOlqBTKUHZnOuPtG2CCl8= +k8s.io/cli-runtime v0.27.6/go.mod h1:+qSBK08EJL1fFvsfVNlETzsgGtxIhascIGZSuaQXQro= +k8s.io/client-go v0.27.6 h1:vzI8804gpUtpMCNaFjIFyJrifH7u//LJCJPy8fQuYQg= +k8s.io/client-go v0.27.6/go.mod h1:PMsXcDKiJTW7PHJ64oEsIUJF319wm+EFlCj76oE5QXM= +k8s.io/code-generator v0.27.6 h1:1zkSDvylcA11s91aYg5U7fZ24EXMZ+KIDOj/Z3Ti4c8= +k8s.io/code-generator v0.27.6/go.mod h1:DPung1sI5vBgn4AGKtlPRQAyagj/ir/4jI55ipZHVww= +k8s.io/component-base v0.27.6 h1:hF5WxX7Tpi9/dXAbLjPVkIA6CA6Pi6r9JOHyo0uCDYI= +k8s.io/component-base v0.27.6/go.mod h1:NvjLtaneUeb0GgMPpCBF+4LNB9GuhDHi16uUTjBhQfU= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732 h1:SAElp8THCfmBdM+4lmWX5gebiSSkEr7PAYDVF91qpfg= pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732/go.mod h1:lpvCfhqEHNJSSpG5R5A2EgsVzG8RTt4RfPoQuRAcDmg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -752,12 +757,12 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= -sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= -sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= +sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= +sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= +sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= sigs.k8s.io/mdtoc v1.1.0 h1:q3YtqYzmC2e0hgLXRIOm7/QLuPux1CX3ZHCwlbABxZo= sigs.k8s.io/mdtoc v1.1.0/go.mod h1:QZLVEdHH2iNIR4uHAZyvFRtjloHgVItk8lo/mzCtq3w= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/go.work b/go.work new file mode 100644 index 000000000..249976119 --- /dev/null +++ b/go.work @@ -0,0 +1,7 @@ +go 1.21.3 + +use ( + . + ./images/kube-webhook-certgen/rootfs + ./magefiles +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 000000000..f1f07229e --- /dev/null +++ b/go.work.sum @@ -0,0 +1,329 @@ +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= +cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= +cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= +cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= +cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= +cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= +cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= +cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= +cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= +cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= +cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= +cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= +cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= +cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= +cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= +cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= +cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= +cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= +cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= +cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= +cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= +cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= +cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= +cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= +cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= +cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= +cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= +cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= +cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= +cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= +cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= +cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= +cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= +cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= +cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= +cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= +cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= +cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= +cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= +cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= +cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= +cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= +cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= +cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= +cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= +cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= +cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= +cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= +cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= +cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= +cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= +cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= +cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= +cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= +cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= +cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= +cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= +cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= +cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= +cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= +cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= +cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= +cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= +cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= +cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= +cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= +cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= +cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= +cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= +cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= +cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= +cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= +cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= +cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= +cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= +cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= +cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= +cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= +cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= +cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= +cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= +cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= +cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= +cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= +cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= +cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= +cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= +cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= +github.com/google/cel-go v0.12.7/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= +github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.etcd.io/etcd/pkg/v3 v3.5.7/go.mod h1:kcOfWt3Ov9zgYdOiJ/o1Y9zFfLhQjylTgL4Lru8opRo= +go.etcd.io/etcd/raft/v3 v3.5.7/go.mod h1:TflkAb/8Uy6JFBxcRaH2Fr6Slm9mCPVdI2efzxY96yU= +go.etcd.io/etcd/server/v3 v3.5.7/go.mod h1:gxBgT84issUVBRpZ3XkW1T55NjOb4vZZRI4wVvNhf4A= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.1/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= +go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= +go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= +go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= +go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kms v0.27.6/go.mod h1:9YQuCFa+n88RWokHkl+4RHFQ9DATSip/ihBqxlDUBuw= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2/go.mod h1:+qG7ISXqCDVVcyO8hLn12AKVYYUjM7ftlqsqmrhMZE0= diff --git a/hack/generate-e2e-suite-doc.sh b/hack/generate-e2e-suite-doc.sh deleted file mode 100755 index a4ccc8fc2..000000000 --- a/hack/generate-e2e-suite-doc.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright 2020 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. - -if [ -n "$DEBUG" ]; then - set -x -fi - -URL="https://github.com/kubernetes/ingress-nginx/tree/main/" -DIR=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) - -echo " - -# e2e test suite for [Ingress NGINX Controller]($URL) - -" - -for FILE in $(find $DIR/test/e2e -name "*.go");do - # describe definition - DESCRIBE=$(cat $FILE | grep -n -oP 'Describe.*') - # line number - DESCRIBE_LINE=$(echo $DESCRIBE | cut -f1 -d ':') - # clean describe, extracting the string - DESCRIBE=$(echo $DESCRIBE | sed -En 's/.*("|`)(.*)("|`).*/\2/p') - - FILE_URL=$(echo $FILE | sed "s|${DIR}/|${URL}|g") - echo " -### [$DESCRIBE]($FILE_URL#L$DESCRIBE_LINE) -" - - # extract Tests - ITS=$(cat $FILE | grep -n -oP 'It\(.*') - while IFS= read -r line; do - IT_LINE=$(echo $line | cut -f1 -d ':') - IT=$(echo $line | sed -En 's/.*("|`)(.*)("|`).*/\2/p') - echo "- [$IT]($FILE_URL#L$IT_LINE)" - done <<< "$ITS" -done diff --git a/hack/manifest-templates/provider/aws/nlb-with-tls-termination/values.yaml b/hack/manifest-templates/provider/aws/nlb-with-tls-termination/values.yaml index 5b36b3dd2..46f8417c7 100644 --- a/hack/manifest-templates/provider/aws/nlb-with-tls-termination/values.yaml +++ b/hack/manifest-templates/provider/aws/nlb-with-tls-termination/values.yaml @@ -1,5 +1,9 @@ # AWS NLB with TLS termination controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/manifest-templates/provider/aws/values.yaml b/hack/manifest-templates/provider/aws/values.yaml index 743721fc4..37eac03c2 100644 --- a/hack/manifest-templates/provider/aws/values.yaml +++ b/hack/manifest-templates/provider/aws/values.yaml @@ -1,5 +1,9 @@ # AWS - NLB controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/manifest-templates/provider/baremetal/values.yaml b/hack/manifest-templates/provider/baremetal/values.yaml index 3c5a0840b..205a7430c 100644 --- a/hack/manifest-templates/provider/baremetal/values.yaml +++ b/hack/manifest-templates/provider/baremetal/values.yaml @@ -1,5 +1,9 @@ # Baremetal controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: NodePort diff --git a/hack/manifest-templates/provider/cloud/values.yaml b/hack/manifest-templates/provider/cloud/values.yaml index 7d8266c0f..edc5662dd 100644 --- a/hack/manifest-templates/provider/cloud/values.yaml +++ b/hack/manifest-templates/provider/cloud/values.yaml @@ -1,4 +1,8 @@ controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/manifest-templates/provider/do/values.yaml b/hack/manifest-templates/provider/do/values.yaml index 2b0578414..aeee47ad6 100644 --- a/hack/manifest-templates/provider/do/values.yaml +++ b/hack/manifest-templates/provider/do/values.yaml @@ -1,5 +1,9 @@ # Digital Ocean controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/manifest-templates/provider/exoscale/values.yaml b/hack/manifest-templates/provider/exoscale/values.yaml index 17458cac9..867ecc57e 100644 --- a/hack/manifest-templates/provider/exoscale/values.yaml +++ b/hack/manifest-templates/provider/exoscale/values.yaml @@ -1,6 +1,10 @@ # Exoscale controller: kind: DaemonSet + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/manifest-templates/provider/oracle/kustomization.yaml b/hack/manifest-templates/provider/oracle/kustomization.yaml new file mode 100644 index 000000000..cd6ef95be --- /dev/null +++ b/hack/manifest-templates/provider/oracle/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../../common diff --git a/hack/manifest-templates/provider/oracle/values.yaml b/hack/manifest-templates/provider/oracle/values.yaml new file mode 100644 index 000000000..600dbfe5c --- /dev/null +++ b/hack/manifest-templates/provider/oracle/values.yaml @@ -0,0 +1,12 @@ +controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + service: + type: LoadBalancer + externalTrafficPolicy: Local + annotations: + service.beta.kubernetes.io/oci-load-balancer-shape: "flexible" + service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10" + service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "100" diff --git a/hack/manifest-templates/provider/scw/values.yaml b/hack/manifest-templates/provider/scw/values.yaml index cee5e2b1e..56d351dcd 100644 --- a/hack/manifest-templates/provider/scw/values.yaml +++ b/hack/manifest-templates/provider/scw/values.yaml @@ -1,5 +1,9 @@ # Scaleway controller: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 service: type: LoadBalancer externalTrafficPolicy: Local diff --git a/hack/verify-golint.sh b/hack/verify-golint.sh index 7c9284880..17bcedd9f 100755 --- a/hack/verify-golint.sh +++ b/hack/verify-golint.sh @@ -22,19 +22,13 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. cd "${KUBE_ROOT}" -GOLINT=${GOLINT:-"golint"} -PACKAGES=($(go list ./internal/... | grep -v /vendor/)) -bad_files=() -for package in "${PACKAGES[@]}"; do - out=$("${GOLINT}" -min_confidence=0.9 "${package}" | grep -v -E '(should not use dot imports|internal/file/bindata.go)' || :) - if [[ -n "${out}" ]]; then - bad_files+=("${out}") - fi -done -if [[ "${#bad_files[@]}" -ne 0 ]]; then - echo "!!! '$GOLINT' problems: " - echo "${bad_files[@]}" - exit 1 +LINT=${LINT:-golangci-lint} + +if [[ -z "$(command -v ${LINT})" ]]; then + echo "${LINT} is missing. Installing it now." + # See: https://golangci-lint.run/usage/install/#local-installation + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.3 + LINT=$(go env GOPATH)/bin/golangci-lint fi -# ex: ts=2 sw=2 et filetype=sh +${LINT} run diff --git a/images/cfssl/Makefile b/images/cfssl/Makefile index 8d8e597a4..31c37fbc5 100644 --- a/images/cfssl/Makefile +++ b/images/cfssl/Makefile @@ -23,8 +23,6 @@ TAG ?=v$(shell date +%Y%m%d)-$(SHORT_SHA) REGISTRY ?= local - - IMAGE = $(REGISTRY)/e2e-test-cfssl # required to enable buildx diff --git a/images/cfssl/cloudbuild.yaml b/images/cfssl/cloudbuild.yaml index 4976a3f67..5fed3b712 100644 --- a/images/cfssl/cloudbuild.yaml +++ b/images/cfssl/cloudbuild.yaml @@ -6,8 +6,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -17,6 +15,3 @@ steps: - | gcloud auth configure-docker \ && cd images/cfssl && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/cfssl/rootfs/Dockerfile b/images/cfssl/rootfs/Dockerfile index a50c715ea..4ad69d362 100644 --- a/images/cfssl/rootfs/Dockerfile +++ b/images/cfssl/rootfs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM alpine:3.17.2 +FROM alpine:3.18.4 RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories diff --git a/images/custom-error-pages/Makefile b/images/custom-error-pages/Makefile index 810736cca..98f7104a7 100644 --- a/images/custom-error-pages/Makefile +++ b/images/custom-error-pages/Makefile @@ -31,7 +31,7 @@ IMAGE = $(REGISTRY)/nginx-errors export DOCKER_CLI_EXPERIMENTAL=enabled # build with buildx -PLATFORMS?=linux/amd64 +PLATFORMS?=linux/amd64,linux/arm,linux/arm64,linux/s390x OUTPUT= PROGRESS=plain diff --git a/images/custom-error-pages/cloudbuild.yaml b/images/custom-error-pages/cloudbuild.yaml index e38d4bcdc..a443d2b11 100644 --- a/images/custom-error-pages/cloudbuild.yaml +++ b/images/custom-error-pages/cloudbuild.yaml @@ -6,8 +6,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -17,6 +15,3 @@ steps: - | gcloud auth configure-docker \ && cd images/custom-error-pages && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/custom-error-pages/rootfs/Dockerfile b/images/custom-error-pages/rootfs/Dockerfile index b34bb6977..54b2a5cc7 100755 --- a/images/custom-error-pages/rootfs/Dockerfile +++ b/images/custom-error-pages/rootfs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.20.1-alpine3.17 as builder +FROM golang:1.21.3-alpine3.18 as builder RUN apk update \ && apk upgrade && apk add git @@ -32,6 +32,7 @@ FROM gcr.io/distroless/static:nonroot COPY --from=builder /go/src/k8s.io/ingress-nginx/images/custom-error-pages/nginx-errors / COPY --from=builder /go/src/k8s.io/ingress-nginx/images/custom-error-pages/www /www +COPY --from=builder /go/src/k8s.io/ingress-nginx/images/custom-error-pages/etc /etc USER nonroot:nonroot CMD ["/nginx-errors"] diff --git a/images/custom-error-pages/rootfs/go.mod b/images/custom-error-pages/rootfs/go.mod index 89e47a4b5..50cfc2a19 100644 --- a/images/custom-error-pages/rootfs/go.mod +++ b/images/custom-error-pages/rootfs/go.mod @@ -1,6 +1,6 @@ module k8s.io/ingress-nginx/custom-error-pages -go 1.20 +go 1.21.3 require github.com/prometheus/client_golang v1.11.1 diff --git a/images/echo/cloudbuild.yaml b/images/echo/cloudbuild.yaml index f07be825a..811d8d3f0 100644 --- a/images/echo/cloudbuild.yaml +++ b/images/echo/cloudbuild.yaml @@ -6,8 +6,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -17,6 +15,3 @@ steps: - | gcloud auth configure-docker \ && cd images/echo && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/ext-auth-example-authsvc/Makefile b/images/ext-auth-example-authsvc/Makefile index 85d2f8e6b..63f261ec3 100644 --- a/images/ext-auth-example-authsvc/Makefile +++ b/images/ext-auth-example-authsvc/Makefile @@ -18,7 +18,9 @@ SHELL=/bin/bash -o pipefail -o errexit DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))) INIT_BUILDX=$(DIR)/../../hack/init-buildx.sh -TAG ?=v1.0.0 +SHORT_SHA ?=$(shell git rev-parse --short HEAD) +TAG ?=v$(shell date +%Y%m%d)-$(SHORT_SHA) + REGISTRY ?= local IMAGE = $(REGISTRY)/ext-auth-example-authsvc diff --git a/images/ext-auth-example-authsvc/cloudbuild.yaml b/images/ext-auth-example-authsvc/cloudbuild.yaml index c5bb5db6a..4a436e012 100644 --- a/images/ext-auth-example-authsvc/cloudbuild.yaml +++ b/images/ext-auth-example-authsvc/cloudbuild.yaml @@ -8,8 +8,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -19,6 +17,3 @@ steps: - | gcloud auth configure-docker \ && cd images/ext-auth-example-authsvc && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/ext-auth-example-authsvc/rootfs/Dockerfile b/images/ext-auth-example-authsvc/rootfs/Dockerfile index c604651d4..96da802ce 100644 --- a/images/ext-auth-example-authsvc/rootfs/Dockerfile +++ b/images/ext-auth-example-authsvc/rootfs/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.1-alpine3.17 as builder +FROM golang:1.21.3-alpine3.18 as builder RUN mkdir /authsvc WORKDIR /authsvc COPY . ./ diff --git a/images/ext-auth-example-authsvc/rootfs/go.mod b/images/ext-auth-example-authsvc/rootfs/go.mod index cc5124072..e910e360c 100644 --- a/images/ext-auth-example-authsvc/rootfs/go.mod +++ b/images/ext-auth-example-authsvc/rootfs/go.mod @@ -1,6 +1,6 @@ module example.com/authsvc -go 1.20 +go 1.21.3 require k8s.io/apimachinery v0.23.1 diff --git a/images/fastcgi-helloserver/cloudbuild.yaml b/images/fastcgi-helloserver/cloudbuild.yaml index a4c9d5eff..80623a197 100644 --- a/images/fastcgi-helloserver/cloudbuild.yaml +++ b/images/fastcgi-helloserver/cloudbuild.yaml @@ -6,8 +6,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -17,6 +15,4 @@ steps: - | gcloud auth configure-docker \ && cd images/fastcgi-helloserver && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" + diff --git a/images/fastcgi-helloserver/rootfs/Dockerfile b/images/fastcgi-helloserver/rootfs/Dockerfile index 7d9220ddf..92a5e8196 100755 --- a/images/fastcgi-helloserver/rootfs/Dockerfile +++ b/images/fastcgi-helloserver/rootfs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.20.1-alpine3.17 as builder +FROM golang:1.21.3-alpine3.18 as builder WORKDIR /go/src/k8s.io/ingress-nginx/images/fastcgi diff --git a/images/fastcgi-helloserver/rootfs/main.go b/images/fastcgi-helloserver/rootfs/main.go index 91db60c26..710f90f2c 100644 --- a/images/fastcgi-helloserver/rootfs/main.go +++ b/images/fastcgi-helloserver/rootfs/main.go @@ -16,15 +16,17 @@ func hello(w http.ResponseWriter, r *http.Request) { } key := keys[0] - fmt.Fprintf(w, "Hello "+string(key)+"!") + fmt.Fprintf(w, "Hello "+key+"!") } func main() { http.HandleFunc("/hello", hello) - l, err := net.Listen("tcp", "0.0.0.0:9000") + l, err := net.Listen("tcp", "0.0.0.0:9000") //nolint:gosec // Ignore the gosec error since it's a hello server if err != nil { panic(err) } - fcgi.Serve(l, nil) + if err := fcgi.Serve(l, nil); err != nil { + panic(err) + } } diff --git a/images/go-grpc-greeter-server/cloudbuild.yaml b/images/go-grpc-greeter-server/cloudbuild.yaml index 6f32bcf50..20740eb27 100644 --- a/images/go-grpc-greeter-server/cloudbuild.yaml +++ b/images/go-grpc-greeter-server/cloudbuild.yaml @@ -8,8 +8,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -19,6 +17,3 @@ steps: - | gcloud auth configure-docker \ && cd images/go-grpc-greeter-server && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/go-grpc-greeter-server/rootfs/Dockerfile b/images/go-grpc-greeter-server/rootfs/Dockerfile index 67aed038e..33cb35923 100644 --- a/images/go-grpc-greeter-server/rootfs/Dockerfile +++ b/images/go-grpc-greeter-server/rootfs/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.1-alpine3.17 as build +FROM golang:1.21.3-alpine3.18 as build WORKDIR /go/src/greeter-server diff --git a/images/go-grpc-greeter-server/rootfs/main.go b/images/go-grpc-greeter-server/rootfs/main.go index 569273dfd..d7fc36208 100644 --- a/images/go-grpc-greeter-server/rootfs/main.go +++ b/images/go-grpc-greeter-server/rootfs/main.go @@ -41,7 +41,7 @@ type hwServer struct { } // SayHello implements helloworld.GreeterServer -func (s *hwServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) { +func (s *hwServer) SayHello(_ context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) { return &hwpb.HelloReply{Message: "Hello " + in.Name}, nil } @@ -49,7 +49,7 @@ type ecServer struct { ecpb.UnimplementedEchoServer } -func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) { +func (s *ecServer) UnaryEcho(_ context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) { return &ecpb.EchoResponse{Message: req.Message}, nil } diff --git a/images/httpbun/cloudbuild.yaml b/images/httpbun/cloudbuild.yaml index 9e3af5935..68afbe873 100644 --- a/images/httpbun/cloudbuild.yaml +++ b/images/httpbun/cloudbuild.yaml @@ -8,8 +8,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -19,6 +17,3 @@ steps: - | gcloud auth configure-docker \ && cd images/httpbun && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" diff --git a/images/httpbun/rootfs/Dockerfile b/images/httpbun/rootfs/Dockerfile index a1775d303..c84f17fd1 100644 --- a/images/httpbun/rootfs/Dockerfile +++ b/images/httpbun/rootfs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.20 AS builder +FROM golang:1.21.3 AS builder ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 diff --git a/images/kube-webhook-certgen/cloudbuild.yaml b/images/kube-webhook-certgen/cloudbuild.yaml index bf0bd3ce1..88d7400d0 100644 --- a/images/kube-webhook-certgen/cloudbuild.yaml +++ b/images/kube-webhook-certgen/cloudbuild.yaml @@ -21,8 +21,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -32,6 +30,3 @@ steps: - | gcloud auth configure-docker \ && cd images/kube-webhook-certgen && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "main" diff --git a/images/kube-webhook-certgen/rootfs/Dockerfile b/images/kube-webhook-certgen/rootfs/Dockerfile index 40a2c31ac..846e37aee 100644 --- a/images/kube-webhook-certgen/rootfs/Dockerfile +++ b/images/kube-webhook-certgen/rootfs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=$BUILDPLATFORM golang:1.20.1 as builder +FROM --platform=$BUILDPLATFORM golang:1.21.3 as builder ARG BUILDPLATFORM ARG TARGETARCH diff --git a/images/kube-webhook-certgen/rootfs/go.mod b/images/kube-webhook-certgen/rootfs/go.mod index 49f95828f..b30fa5f27 100644 --- a/images/kube-webhook-certgen/rootfs/go.mod +++ b/images/kube-webhook-certgen/rootfs/go.mod @@ -1,48 +1,63 @@ module github.com/jet/kube-webhook-certgen -go 1.20 +go 1.21.3 require ( github.com/onrik/logrus v0.9.0 - github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.1.3 - k8s.io/api v0.22.6 - k8s.io/apimachinery v0.22.6 - k8s.io/client-go v0.22.6 + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.7.0 + k8s.io/api v0.27.6 + k8s.io/apimachinery v0.27.6 + k8s.io/client-go v0.27.6 k8s.io/kube-aggregator v0.22.6 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/evanphx/json-patch v4.11.0+incompatible // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.5 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect - github.com/imdario/mergo v0.3.5 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect + github.com/onsi/gomega v1.27.10 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/tidwall/gjson v1.14.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - google.golang.org/appengine v1.6.5 // indirect - google.golang.org/protobuf v1.26.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.14.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect - k8s.io/klog/v2 v2.9.0 // indirect - k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect - k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/images/kube-webhook-certgen/rootfs/go.sum b/images/kube-webhook-certgen/rootfs/go.sum index 7817b7f29..004bd7d96 100644 --- a/images/kube-webhook-certgen/rootfs/go.sum +++ b/images/kube-webhook-certgen/rootfs/go.sum @@ -77,6 +77,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -89,20 +90,22 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -117,15 +120,25 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -158,19 +171,23 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -180,13 +197,16 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -218,18 +238,22 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -244,6 +268,9 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -252,6 +279,8 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -271,16 +300,16 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onrik/logrus v0.9.0 h1:oT7VstCUxWBoX7fswYK61fi9bzRBSpROq5CR2b7wxQo= @@ -288,16 +317,19 @@ github.com/onrik/logrus v0.9.0/go.mod h1:qfe9NeZVAJfIxviw3cYkZo3kvBtLoPRJriAO8zl github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -329,7 +361,11 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -337,8 +373,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -347,8 +384,9 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -358,13 +396,19 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -483,14 +527,15 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -545,13 +590,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -559,14 +605,15 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -607,10 +654,11 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -626,8 +674,9 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -675,14 +724,16 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -691,7 +742,6 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -706,8 +756,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -716,33 +766,43 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.6 h1:acjE5ABt0KpsBI9QCtLqaQEPSF94jOtE/LoFxSYasSE= k8s.io/api v0.22.6/go.mod h1:q1F7IfaNrbi/83ebLy3YFQYLjPSNyunZ/IXQxMmbwCg= -k8s.io/apimachinery v0.22.6 h1:z7vxNRkFX0NToA+8D17kzLZ/T4t+DqwzUlqqbqRepRs= +k8s.io/api v0.27.6 h1:PBWu/lywJe2qQcshMjubzcBg7+XDZOo7O8JJAWuYtUo= +k8s.io/api v0.27.6/go.mod h1:AQYj0UsFCp3qJE7bOVnUuy4orCsXVkvHefnbYQiNWgk= k8s.io/apimachinery v0.22.6/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU= +k8s.io/apimachinery v0.27.6 h1:mGU8jmBq5o8mWBov+mLjdTBcU+etTE19waies4AQ6NE= +k8s.io/apimachinery v0.27.6/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.22.6/go.mod h1:OlL1rGa2kKWGj2JEXnwBcul/BwC9Twe95gm4ohtiIIs= -k8s.io/client-go v0.22.6 h1:ugAXeC312xeGXsn7zTRz+btgtLBnW3qYhtUUpVQL7YE= k8s.io/client-go v0.22.6/go.mod h1:TffU4AV2idZGeP+g3kdFZP+oHVHWPL1JYFySOALriw0= +k8s.io/client-go v0.27.6 h1:vzI8804gpUtpMCNaFjIFyJrifH7u//LJCJPy8fQuYQg= +k8s.io/client-go v0.27.6/go.mod h1:PMsXcDKiJTW7PHJ64oEsIUJF319wm+EFlCj76oE5QXM= k8s.io/code-generator v0.22.6/go.mod h1:iOZwYADSgFPNGWfqHFfg1V0TNJnl1t0WyZluQp4baqU= k8s.io/component-base v0.22.6/go.mod h1:ngHLefY4J5fq2fApNdbWyj4yh0lvw36do4aAjNN8rc8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-aggregator v0.22.6 h1:/iaXzOWia2dqOQkIA9eJtkmfTveJMLz3Dci9ZA/WgmU= k8s.io/kube-aggregator v0.22.6/go.mod h1:0RSTzxqiwsj5HUlov195Z72ZKyE4qgedKXCl6sLKAjM= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.27/go.mod h1:tq2nT0Kx7W+/f2JVE+zxYtUhdjuELJkVpNz+x/QN5R4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/images/kube-webhook-certgen/rootfs/pkg/k8s/k8s_test.go b/images/kube-webhook-certgen/rootfs/pkg/k8s/k8s_test.go index deaeb3540..f11bef981 100644 --- a/images/kube-webhook-certgen/rootfs/pkg/k8s/k8s_test.go +++ b/images/kube-webhook-certgen/rootfs/pkg/k8s/k8s_test.go @@ -3,8 +3,8 @@ package k8s import ( "bytes" "context" + "crypto/rand" "errors" - "math/rand" "testing" "time" diff --git a/images/nginx/TAG b/images/nginx/TAG new file mode 100644 index 000000000..4e379d2bf --- /dev/null +++ b/images/nginx/TAG @@ -0,0 +1 @@ +0.0.2 diff --git a/images/nginx/cloudbuild.yaml b/images/nginx/cloudbuild.yaml index c581700dd..71051f26d 100644 --- a/images/nginx/cloudbuild.yaml +++ b/images/nginx/cloudbuild.yaml @@ -8,8 +8,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx - HOME=/root args: @@ -17,7 +15,3 @@ steps: - | gcloud auth configure-docker \ && cd images/nginx && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" - _PULL_BASE_SHA: '12345' diff --git a/images/nginx/rootfs/Dockerfile b/images/nginx/rootfs/Dockerfile index a968eb1b3..95f7a0288 100644 --- a/images/nginx/rootfs/Dockerfile +++ b/images/nginx/rootfs/Dockerfile @@ -11,7 +11,7 @@ # 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. -FROM alpine:3.17.2 as builder +FROM alpine:3.18.4 as builder COPY . / @@ -21,7 +21,7 @@ RUN apk update \ && /build.sh # Use a multi-stage build -FROM alpine:3.17.2 +FROM alpine:3.18.4 ENV PATH=$PATH:/usr/local/luajit/bin:/usr/local/nginx/sbin:/usr/local/nginx/bin @@ -39,8 +39,6 @@ RUN apk update \ openssl \ pcre \ zlib \ - geoip \ - curl \ ca-certificates \ patch \ yajl \ diff --git a/images/nginx/rootfs/build.sh b/images/nginx/rootfs/build.sh index 44c7b20f8..118dee1be 100755 --- a/images/nginx/rootfs/build.sh +++ b/images/nginx/rootfs/build.sh @@ -18,16 +18,16 @@ set -o errexit set -o nounset set -o pipefail -export NGINX_VERSION=1.21.6 +export NGINX_VERSION=1.25.3 -# Check for recent changes: https://github.com/vision5/ngx_devel_kit/compare/v0.3.1...master -export NDK_VERSION=0.3.1 +# Check for recent changes: https://github.com/vision5/ngx_devel_kit/compare/v0.3.2...master +export NDK_VERSION=0.3.2 # Check for recent changes: https://github.com/openresty/set-misc-nginx-module/compare/v0.33...master export SETMISC_VERSION=0.33 -# Check for recent changes: https://github.com/openresty/headers-more-nginx-module/compare/v0.33...master -export MORE_HEADERS_VERSION=0.33 +# Check for recent changes: https://github.com/openresty/headers-more-nginx-module/compare/v0.34...master +export MORE_HEADERS_VERSION=0.34 # Check for recent changes: https://github.com/atomx/nginx-http-auth-digest/compare/v1.0.0...atomx:master export NGINX_DIGEST_AUTH=1.0.0 @@ -53,8 +53,8 @@ export JAEGER_VERSION=0.7.0 # Check for recent changes: https://github.com/msgpack/msgpack-c/compare/cpp-3.3.0...master export MSGPACK_VERSION=3.3.0 -# Check for recent changes: https://github.com/DataDog/dd-opentracing-cpp/compare/v1.3.2...master -export DATADOG_CPP_VERSION=1.3.2 +# Check for recent changes: https://github.com/DataDog/dd-opentracing-cpp/compare/v1.3.7...master +export DATADOG_CPP_VERSION=1.3.7 # Check for recent changes: https://github.com/SpiderLabs/ModSecurity-nginx/compare/v1.0.3...master export MODSECURITY_VERSION=1.0.3 @@ -63,60 +63,55 @@ export MODSECURITY_VERSION=1.0.3 export MODSECURITY_LIB_VERSION=e9a7ba4a60be48f761e0328c6dfcc668d70e35a0 # Check for recent changes: https://github.com/coreruleset/coreruleset/compare/v3.3.2...v3.3/master -export OWASP_MODSECURITY_CRS_VERSION=v3.3.4 +export OWASP_MODSECURITY_CRS_VERSION=v3.3.5 -# Check for recent changes: https://github.com/openresty/lua-nginx-module/compare/v0.10.21...master -export LUA_NGX_VERSION=0.10.21 +# Check for recent changes: https://github.com/openresty/lua-nginx-module/compare/v0.10.25...master +export LUA_NGX_VERSION=0.10.25 -# Check for recent changes: https://github.com/openresty/stream-lua-nginx-module/compare/v0.0.11...master -export LUA_STREAM_NGX_VERSION=0.0.11 +# Check for recent changes: https://github.com/openresty/stream-lua-nginx-module/compare/v0.0.13...master +export LUA_STREAM_NGX_VERSION=0.0.13 # Check for recent changes: https://github.com/openresty/lua-upstream-nginx-module/compare/8aa93ead98ba2060d4efd594ae33a35d153589bf...master export LUA_UPSTREAM_VERSION=8aa93ead98ba2060d4efd594ae33a35d153589bf -# Check for recent changes: https://github.com/openresty/lua-cjson/compare/2.1.0.10...openresty:master -export LUA_CJSON_VERSION=2.1.0.10 +# Check for recent changes: https://github.com/openresty/lua-cjson/compare/2.1.0.11...openresty:master +export LUA_CJSON_VERSION=2.1.0.11 -# Check for recent changes: https://github.com/leev/ngx_http_geoip2_module/compare/3.3...master -export GEOIP2_VERSION=a26c6beed77e81553686852dceb6c7fdacc5970d +# Check for recent changes: https://github.com/leev/ngx_http_geoip2_module/compare/3.4...master +export GEOIP2_VERSION=a607a41a8115fecfc05b5c283c81532a3d605425 -# Check for recent changes: https://github.com/msva/nginx_ajp_module/compare/fcbb2ccca4901d317ecd7a9dabb3fec9378ff40f...master -# This is a fork from https://github.com/yaoweibin/nginx_ajp_module -# Since it has not been updated and is not compatible with NGINX 1.21 -export NGINX_AJP_VERSION=fcbb2ccca4901d317ecd7a9dabb3fec9378ff40f - -# Check for recent changes: https://github.com/openresty/luajit2/compare/v2.1-20220411...v2.1-agentzh -export LUAJIT_VERSION=2.1-20220411 +# Check for recent changes: https://github.com/openresty/luajit2/compare/v2.1-20231117...v2.1-agentzh +export LUAJIT_VERSION=2.1-20231117 # Check for recent changes: https://github.com/openresty/lua-resty-balancer/compare/v0.04...master export LUA_RESTY_BALANCER=0.04 -# Check for recent changes: https://github.com/openresty/lua-resty-lrucache/compare/v0.11...master -export LUA_RESTY_CACHE=0.11 +# Check for recent changes: https://github.com/openresty/lua-resty-lrucache/compare/v0.13...master +export LUA_RESTY_CACHE=0.13 -# Check for recent changes: https://github.com/openresty/lua-resty-core/compare/v0.1.23...master -export LUA_RESTY_CORE=0.1.23 +# Check for recent changes: https://github.com/openresty/lua-resty-core/compare/v0.1.27...master +export LUA_RESTY_CORE=0.1.27 -# Check for recent changes: https://github.com/cloudflare/lua-resty-cookie/compare/v0.1.0...master -export LUA_RESTY_COOKIE_VERSION=303e32e512defced053a6484bc0745cf9dc0d39e +# Check for recent changes: https://github.com/utix/lua-resty-cookie/compare/9533f47...master +export LUA_RESTY_COOKIE_VERSION=9533f479371663107b515590fc9daf00d61ebf11 # Check for recent changes: https://github.com/openresty/lua-resty-dns/compare/v0.22...master export LUA_RESTY_DNS=0.22 -# Check for recent changes: https://github.com/ledgetech/lua-resty-http/compare/v0.16.1...master -export LUA_RESTY_HTTP=0ce55d6d15da140ecc5966fa848204c6fd9074e8 +# Check for recent changes: https://github.com/ledgetech/lua-resty-http/compare/v0.17.1...master +export LUA_RESTY_HTTP=0.17.1 -# Check for recent changes: https://github.com/openresty/lua-resty-lock/compare/v0.08...master -export LUA_RESTY_LOCK=0.08 +# Check for recent changes: https://github.com/openresty/lua-resty-lock/compare/v0.09...master +export LUA_RESTY_LOCK=0.09 -# Check for recent changes: https://github.com/openresty/lua-resty-upload/compare/v0.10...master -export LUA_RESTY_UPLOAD_VERSION=0.10 +# Check for recent changes: https://github.com/openresty/lua-resty-upload/compare/v0.11...master +export LUA_RESTY_UPLOAD_VERSION=0.11 # Check for recent changes: https://github.com/openresty/lua-resty-string/compare/v0.15...master export LUA_RESTY_STRING_VERSION=0.15 -# Check for recent changes: https://github.com/openresty/lua-resty-memcached/compare/v0.16...master -export LUA_RESTY_MEMCACHED_VERSION=0.16 +# Check for recent changes: https://github.com/openresty/lua-resty-memcached/compare/v0.17...master +export LUA_RESTY_MEMCACHED_VERSION=0.17 # Check for recent changes: https://github.com/openresty/lua-resty-redis/compare/v0.30...master export LUA_RESTY_REDIS_VERSION=0.30 @@ -169,7 +164,6 @@ apk add \ linux-headers \ libxslt-dev \ gd-dev \ - geoip-dev \ perl-dev \ libedit-dev \ mercurial \ @@ -201,16 +195,16 @@ mkdir --verbose -p "$BUILD_PATH" cd "$BUILD_PATH" # download, verify and extract the source files -get_src 66dc7081488811e9f925719e34d1b4504c2801c81dee2920e5452a86b11405ae \ +get_src 64c5b975ca287939e828303fa857d22f142b251f17808dfe41733512d9cded86 \ "https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz" -get_src 0e971105e210d272a497567fa2e2c256f4e39b845a5ba80d373e26ba1abfbd85 \ - "https://github.com/simpl/ngx_devel_kit/archive/v$NDK_VERSION.tar.gz" +get_src aa961eafb8317e0eb8da37eb6e2c9ff42267edd18b56947384e719b85188f58b \ + "https://github.com/vision5/ngx_devel_kit/archive/v$NDK_VERSION.tar.gz" get_src cd5e2cc834bcfa30149e7511f2b5a2183baf0b70dc091af717a89a64e44a2985 \ "https://github.com/openresty/set-misc-nginx-module/archive/v$SETMISC_VERSION.tar.gz" -get_src a3dcbab117a9c103bc1ea5200fc00a7b7d2af97ff7fd525f16f8ac2632e30fbf \ +get_src 0c0d2ced2ce895b3f45eb2b230cd90508ab2a773299f153de14a43e44c1209b3 \ "https://github.com/openresty/headers-more-nginx-module/archive/v$MORE_HEADERS_VERSION.tar.gz" get_src f09851e6309560a8ff3e901548405066c83f1f6ff88aa7171e0763bd9514762b \ @@ -246,10 +240,10 @@ get_src 7d5f3439c8df56046d0564b5857fd8a30296ab1bd6df0f048aed7afb56a0a4c2 \ get_src 99c47c75c159795c9faf76bbb9fa58e5a50b75286c86565ffcec8514b1c74bf9 \ "https://github.com/openresty/stream-lua-nginx-module/archive/v$LUA_STREAM_NGX_VERSION.tar.gz" else -get_src 9db756000578efaecb43bea4fc6cf631aaa80988d86ffe5d3afeb9927895ffad \ +get_src bc764db42830aeaf74755754b900253c233ad57498debe7a441cee2c6f4b07c2 \ "https://github.com/openresty/lua-nginx-module/archive/v$LUA_NGX_VERSION.tar.gz" -get_src c7924f28cb014a99636e747ea907724dd55f60e180cb92cde6e8ed48d2278f27 \ +get_src 01b715754a8248cc7228e0c8f97f7488ae429d90208de0481394e35d24cef32f \ "https://github.com/openresty/stream-lua-nginx-module/archive/v$LUA_STREAM_NGX_VERSION.tar.gz" fi @@ -261,20 +255,17 @@ if [[ ${ARCH} == "s390x" ]]; then get_src 266ed1abb70a9806d97cb958537a44b67db6afb33d3b32292a2d68a2acedea75 \ "https://github.com/openresty/luajit2/archive/$LUAJIT_VERSION.tar.gz" else -get_src d3f2c870f8f88477b01726b32accab30f6e5d57ae59c5ec87374ff73d0794316 \ +get_src cc92968c57c00303eb9eaebf65cc8b29a0f851670f16bb514896ab5057ae381f \ "https://github.com/openresty/luajit2/archive/v$LUAJIT_VERSION.tar.gz" fi -get_src 586f92166018cc27080d34e17c59d68219b85af745edf3cc9fe41403fc9b4ac6 \ +get_src 8d39c6b23f941a2d11571daaccc04e69539a3fcbcc50a631837560d5861a7b96 \ "https://github.com/DataDog/dd-opentracing-cpp/archive/v$DATADOG_CPP_VERSION.tar.gz" -get_src 4c1933434572226942c65b2f2b26c8a536ab76aa771a3c7f6c2629faa764976b \ +get_src b6c9c09fd43eb34a71e706ad780b2ead26549a9a9f59280fe558f5b7b980b7c6 \ "https://github.com/leev/ngx_http_geoip2_module/archive/$GEOIP2_VERSION.tar.gz" -get_src 778fcca851bd69dabfb382dc827d2ee07662f7eca36b5e66e67d5512bad75ef8 \ - "https://github.com/msva/nginx_ajp_module/archive/$NGINX_AJP_VERSION.tar.gz" - -get_src 5d16e623d17d4f42cc64ea9cfb69ca960d313e12f5d828f785dd227cc483fcbd \ +get_src deb4ab1ffb9f3d962c4b4a2c4bdff692b86a209e3835ae71ebdf3b97189e40a9 \ "https://github.com/openresty/lua-resty-upload/archive/v$LUA_RESTY_UPLOAD_VERSION.tar.gz" get_src bdbf271003d95aa91cab0a92f24dca129e99b33f79c13ebfcdbbcbb558129491 \ @@ -287,29 +278,29 @@ if [[ ${ARCH} == "s390x" ]]; then get_src 8f5f76d2689a3f6b0782f0a009c56a65e4c7a4382be86422c9b3549fe95b0dc4 \ "https://github.com/openresty/lua-resty-core/archive/v$LUA_RESTY_CORE.tar.gz" else -get_src efd6b51520429e64b1bcc10f477d370ebed1631c190f7e4dc270d959a743ad7d \ +get_src 39baab9e2b31cc48cecf896cea40ef6e80559054fd8a6e440cc804a858ea84d4 \ "https://github.com/openresty/lua-resty-core/archive/v$LUA_RESTY_CORE.tar.gz" fi -get_src 0c551d6898f89f876e48730f9b55790d0ba07d5bc0aa6c76153277f63c19489f \ +get_src a77b9de160d81712f2f442e1de8b78a5a7ef0d08f13430ff619f79235db974d4 \ "https://github.com/openresty/lua-cjson/archive/$LUA_CJSON_VERSION.tar.gz" -get_src 5ed48c36231e2622b001308622d46a0077525ac2f751e8cc0c9905914254baa4 \ - "https://github.com/cloudflare/lua-resty-cookie/archive/$LUA_RESTY_COOKIE_VERSION.tar.gz" +get_src a404c790553617424d743b82a9f01feccd0d2930b306b370c665ca3b7c09ccb6 \ + "https://github.com/utix/lua-resty-cookie/archive/$LUA_RESTY_COOKIE_VERSION.tar.gz" -get_src e810ed124fe788b8e4aac2c8960dda1b9a6f8d0ca94ce162f28d3f4d877df8af \ +get_src 573184006b98ccee2594b0d134fa4d05e5d2afd5141cbad315051ccf7e9b6403 \ "https://github.com/openresty/lua-resty-lrucache/archive/v$LUA_RESTY_CACHE.tar.gz" -get_src 2b4683f9abe73e18ca00345c65010c9056777970907a311d6e1699f753141de2 \ +get_src b4ddcd47db347e9adf5c1e1491a6279a6ae2a3aff3155ef77ea0a65c998a69c1 \ "https://github.com/openresty/lua-resty-lock/archive/v$LUA_RESTY_LOCK.tar.gz" get_src 70e9a01eb32ccade0d5116a25bcffde0445b94ad35035ce06b94ccd260ad1bf0 \ "https://github.com/openresty/lua-resty-dns/archive/v$LUA_RESTY_DNS.tar.gz" -get_src 9fcb6db95bc37b6fce77d3b3dc740d593f9d90dce0369b405eb04844d56ac43f \ - "https://github.com/ledgetech/lua-resty-http/archive/$LUA_RESTY_HTTP.tar.gz" +get_src f52a0538b20a2d45025f318c18a138b78f6f88f07713dd0ed502183cb3d16f17 \ + "https://github.com/ledgetech/lua-resty-http/archive/v$LUA_RESTY_HTTP.tar.gz" -get_src 42893da0e3de4ec180c9bf02f82608d78787290a70c5644b538f29d243147396 \ +get_src 02733575c4aed15f6cab662378e4b071c0a4a4d07940c4ef19a7319e9be943d4 \ "https://github.com/openresty/lua-resty-memcached/archive/v$LUA_RESTY_MEMCACHED_VERSION.tar.gz" get_src c15aed1a01c88a3a6387d9af67a957dff670357f5fdb4ee182beb44635eef3f1 \ @@ -474,7 +465,7 @@ make install # Get Brotli source and deps cd "$BUILD_PATH" -git clone --depth=1 https://github.com/google/ngx_brotli.git +git clone --depth=100 https://github.com/google/ngx_brotli.git cd ngx_brotli git submodule init git submodule update @@ -589,7 +580,6 @@ WITH_FLAGS="--with-debug \ --with-http_realip_module \ --with-http_auth_request_module \ --with-http_addition_module \ - --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_sub_module \ --with-http_v2_module \ @@ -633,7 +623,6 @@ WITH_MODULES=" \ --add-module=$BUILD_PATH/lua-nginx-module-$LUA_NGX_VERSION \ --add-module=$BUILD_PATH/stream-lua-nginx-module-$LUA_STREAM_NGX_VERSION \ --add-module=$BUILD_PATH/lua-upstream-nginx-module-$LUA_UPSTREAM_VERSION \ - --add-module=$BUILD_PATH/nginx_ajp_module-${NGINX_AJP_VERSION} \ --add-dynamic-module=$BUILD_PATH/nginx-http-auth-digest-$NGINX_DIGEST_AUTH \ --add-dynamic-module=$BUILD_PATH/nginx-opentracing-$NGINX_OPENTRACING_VERSION/opentracing \ --add-dynamic-module=$BUILD_PATH/ModSecurity-nginx-$MODSECURITY_VERSION \ diff --git a/images/nginx/rootfs/etc/nginx/geoip/GeoIP.dat b/images/nginx/rootfs/etc/nginx/geoip/GeoIP.dat deleted file mode 100644 index be8b031f7..000000000 Binary files a/images/nginx/rootfs/etc/nginx/geoip/GeoIP.dat and /dev/null differ diff --git a/images/nginx/rootfs/etc/nginx/geoip/GeoIPASNum.dat b/images/nginx/rootfs/etc/nginx/geoip/GeoIPASNum.dat deleted file mode 100644 index 85c2cb329..000000000 Binary files a/images/nginx/rootfs/etc/nginx/geoip/GeoIPASNum.dat and /dev/null differ diff --git a/images/nginx/rootfs/etc/nginx/geoip/GeoLiteCity.dat b/images/nginx/rootfs/etc/nginx/geoip/GeoLiteCity.dat deleted file mode 100644 index 1adb8c3c4..000000000 Binary files a/images/nginx/rootfs/etc/nginx/geoip/GeoLiteCity.dat and /dev/null differ diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-balancer_status_code.patch b/images/nginx/rootfs/patches/nginx-1.21.4-balancer_status_code.patch deleted file mode 100644 index c4d87e2fb..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-balancer_status_code.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c -index f8d5707d..6efe0047 100644 ---- a/src/http/ngx_http_upstream.c -+++ b/src/http/ngx_http_upstream.c -@@ -1515,6 +1515,11 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) - return; - } - -+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { -+ ngx_http_upstream_finalize_request(r, u, rc); -+ return; -+ } -+ - u->state->peer = u->peer.name; - - if (rc == NGX_BUSY) { -diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h -index 3e714e5b..dfbb25e0 100644 ---- a/src/http/ngx_http_upstream.h -+++ b/src/http/ngx_http_upstream.h -@@ -427,4 +427,9 @@ extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[]; - extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[]; - - -+#ifndef HAVE_BALANCER_STATUS_CODE_PATCH -+#define HAVE_BALANCER_STATUS_CODE_PATCH -+#endif -+ -+ - #endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */ -diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h -index 09d24593..d8b4b584 100644 ---- a/src/stream/ngx_stream.h -+++ b/src/stream/ngx_stream.h -@@ -27,6 +27,7 @@ typedef struct ngx_stream_session_s ngx_stream_session_t; - - - #define NGX_STREAM_OK 200 -+#define NGX_STREAM_SPECIAL_RESPONSE 300 - #define NGX_STREAM_BAD_REQUEST 400 - #define NGX_STREAM_FORBIDDEN 403 - #define NGX_STREAM_INTERNAL_SERVER_ERROR 500 -diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c -index 818d7329..329dcdc6 100644 ---- a/src/stream/ngx_stream_proxy_module.c -+++ b/src/stream/ngx_stream_proxy_module.c -@@ -691,6 +691,11 @@ ngx_stream_proxy_connect(ngx_stream_session_t *s) - return; - } - -+ if (rc >= NGX_STREAM_SPECIAL_RESPONSE) { -+ ngx_stream_proxy_finalize(s, rc); -+ return; -+ } -+ - u->state->peer = u->peer.name; - - if (rc == NGX_BUSY) { -diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h -index 73947f46..21bc0ad7 100644 ---- a/src/stream/ngx_stream_upstream.h -+++ b/src/stream/ngx_stream_upstream.h -@@ -151,4 +151,9 @@ ngx_stream_upstream_srv_conf_t *ngx_stream_upstream_add(ngx_conf_t *cf, - extern ngx_module_t ngx_stream_upstream_module; - - -+#ifndef HAVE_BALANCER_STATUS_CODE_PATCH -+#define HAVE_BALANCER_STATUS_CODE_PATCH -+#endif -+ -+ - #endif /* _NGX_STREAM_UPSTREAM_H_INCLUDED_ */ diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-cache_manager_exit.patch b/images/nginx/rootfs/patches/nginx-1.21.4-cache_manager_exit.patch deleted file mode 100644 index 91ee63a26..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-cache_manager_exit.patch +++ /dev/null @@ -1,19 +0,0 @@ -# HG changeset patch -# User Yichun Zhang -# Date 1383598130 28800 -# Node ID f64218e1ac963337d84092536f588b8e0d99bbaa -# Parent dea321e5c0216efccbb23e84bbce7cf3e28f130c -Cache: gracefully exit the cache manager process. - -diff -r dea321e5c021 -r f64218e1ac96 src/os/unix/ngx_process_cycle.c ---- a/src/os/unix/ngx_process_cycle.c Thu Oct 31 18:23:49 2013 +0400 -+++ b/src/os/unix/ngx_process_cycle.c Mon Nov 04 12:48:50 2013 -0800 -@@ -1134,7 +1134,7 @@ - - if (ngx_terminate || ngx_quit) { - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); -- exit(0); -+ ngx_worker_process_exit(cycle); - } - - if (ngx_reopen) { diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-delayed_posted_events.patch b/images/nginx/rootfs/patches/nginx-1.21.4-delayed_posted_events.patch deleted file mode 100644 index 687584324..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-delayed_posted_events.patch +++ /dev/null @@ -1,98 +0,0 @@ -diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c -index 57af8132..4853945f 100644 ---- a/src/event/ngx_event.c -+++ b/src/event/ngx_event.c -@@ -196,6 +196,9 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle) - ngx_uint_t flags; - ngx_msec_t timer, delta; - -+ ngx_queue_t *q; -+ ngx_event_t *ev; -+ - if (ngx_timer_resolution) { - timer = NGX_TIMER_INFINITE; - flags = 0; -@@ -215,6 +218,13 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle) - #endif - } - -+ if (!ngx_queue_empty(&ngx_posted_delayed_events)) { -+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, -+ "posted delayed event queue not empty" -+ " making poll timeout 0"); -+ timer = 0; -+ } -+ - if (ngx_use_accept_mutex) { - if (ngx_accept_disabled > 0) { - ngx_accept_disabled--; -@@ -257,6 +267,35 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle) - } - - ngx_event_process_posted(cycle, &ngx_posted_events); -+ -+ while (!ngx_queue_empty(&ngx_posted_delayed_events)) { -+ q = ngx_queue_head(&ngx_posted_delayed_events); -+ -+ ev = ngx_queue_data(q, ngx_event_t, queue); -+ if (ev->delayed) { -+ /* start of newly inserted nodes */ -+ for (/* void */; -+ q != ngx_queue_sentinel(&ngx_posted_delayed_events); -+ q = ngx_queue_next(q)) -+ { -+ ev = ngx_queue_data(q, ngx_event_t, queue); -+ ev->delayed = 0; -+ -+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, -+ "skipping delayed posted event %p," -+ " till next iteration", ev); -+ } -+ -+ break; -+ } -+ -+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, -+ "delayed posted event %p", ev); -+ -+ ngx_delete_posted_event(ev); -+ -+ ev->handler(ev); -+ } - } - - -@@ -600,6 +639,7 @@ ngx_event_process_init(ngx_cycle_t *cycle) - - ngx_queue_init(&ngx_posted_accept_events); - ngx_queue_init(&ngx_posted_events); -+ ngx_queue_init(&ngx_posted_delayed_events); - - if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { - return NGX_ERROR; -diff --git a/src/event/ngx_event_posted.c b/src/event/ngx_event_posted.c -index d851f3d1..b6cea009 100644 ---- a/src/event/ngx_event_posted.c -+++ b/src/event/ngx_event_posted.c -@@ -12,6 +12,7 @@ - - ngx_queue_t ngx_posted_accept_events; - ngx_queue_t ngx_posted_events; -+ngx_queue_t ngx_posted_delayed_events; - - - void -diff --git a/src/event/ngx_event_posted.h b/src/event/ngx_event_posted.h -index 145d30fe..6c388553 100644 ---- a/src/event/ngx_event_posted.h -+++ b/src/event/ngx_event_posted.h -@@ -43,6 +43,9 @@ void ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted); - - extern ngx_queue_t ngx_posted_accept_events; - extern ngx_queue_t ngx_posted_events; -+extern ngx_queue_t ngx_posted_delayed_events; -+ -+#define HAVE_POSTED_DELAYED_EVENTS_PATCH - - - #endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */ diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-hash_overflow.patch b/images/nginx/rootfs/patches/nginx-1.21.4-hash_overflow.patch deleted file mode 100644 index 449d214ba..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-hash_overflow.patch +++ /dev/null @@ -1,20 +0,0 @@ -# HG changeset patch -# User Yichun Zhang -# Date 1412276417 25200 -# Thu Oct 02 12:00:17 2014 -0700 -# Node ID 4032b992f23b054c1a2cfb0be879330d2c6708e5 -# Parent 1ff0f68d9376e3d184d65814a6372856bf65cfcd -Hash: buffer overflow might happen when exceeding the pre-configured limits. - -diff -r 1ff0f68d9376 -r 4032b992f23b src/core/ngx_hash.c ---- a/src/core/ngx_hash.c Tue Sep 30 15:50:28 2014 -0700 -+++ b/src/core/ngx_hash.c Thu Oct 02 12:00:17 2014 -0700 -@@ -312,6 +312,8 @@ ngx_hash_init(ngx_hash_init_t *hinit, ng - continue; - } - -+ size--; -+ - ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0, - "could not build optimal %s, you should increase " - "either %s_max_size: %i or %s_bucket_size: %i; " diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-init_cycle_pool_release.patch b/images/nginx/rootfs/patches/nginx-1.21.4-init_cycle_pool_release.patch deleted file mode 100644 index 9cfa4f7cb..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-init_cycle_pool_release.patch +++ /dev/null @@ -1,59 +0,0 @@ -diff -rup nginx-1.21.4/src/core/nginx.c nginx-1.21.4-patched/src/core/nginx.c ---- nginx-1.21.4/src/core/nginx.c 2017-12-17 00:00:38.136470108 -0800 -+++ nginx-1.21.4-patched/src/core/nginx.c 2017-12-16 23:59:51.680958322 -0800 -@@ -186,6 +186,7 @@ static u_char *ngx_prefix; - static u_char *ngx_conf_file; - static u_char *ngx_conf_params; - static char *ngx_signal; -+ngx_pool_t *saved_init_cycle_pool = NULL; - - - static char **ngx_os_environ; -@@ -253,6 +254,8 @@ main(int argc, char *const *argv) - return 1; - } - -+ saved_init_cycle_pool = init_cycle.pool; -+ - if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { - return 1; - } -diff -rup nginx-1.21.4/src/core/ngx_core.h nginx-1.21.4-patched/src/core/ngx_core.h ---- nginx-1.21.4/src/core/ngx_core.h 2017-10-10 08:22:51.000000000 -0700 -+++ nginx-1.21.4-patched/src/core/ngx_core.h 2017-12-16 23:59:51.679958370 -0800 -@@ -108,4 +108,6 @@ void ngx_cpuinfo(void); - #define NGX_DISABLE_SYMLINKS_NOTOWNER 2 - #endif - -+extern ngx_pool_t *saved_init_cycle_pool; -+ - #endif /* _NGX_CORE_H_INCLUDED_ */ -diff -rup nginx-1.21.4/src/core/ngx_cycle.c nginx-1.21.4-patched/src/core/ngx_cycle.c ---- nginx-1.21.4/src/core/ngx_cycle.c 2017-10-10 08:22:51.000000000 -0700 -+++ nginx-1.21.4-patched/src/core/ngx_cycle.c 2017-12-16 23:59:51.678958419 -0800 -@@ -748,6 +748,10 @@ old_shm_zone_done: - - if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) { - -+ if (ngx_is_init_cycle(old_cycle)) { -+ saved_init_cycle_pool = NULL; -+ } -+ - ngx_destroy_pool(old_cycle->pool); - cycle->old_cycle = NULL; - -diff -rup nginx-1.21.4/src/os/unix/ngx_process_cycle.c nginx-1.21.4-patched/src/os/unix/ngx_process_cycle.c ---- nginx-1.21.4/src/os/unix/ngx_process_cycle.c 2017-12-17 00:00:38.142469762 -0800 -+++ nginx-1.21.4-patched/src/os/unix/ngx_process_cycle.c 2017-12-16 23:59:51.691957791 -0800 -@@ -687,6 +692,11 @@ ngx_master_process_exit(ngx_cycle_t *cyc - ngx_exit_cycle.files_n = ngx_cycle->files_n; - ngx_cycle = &ngx_exit_cycle; - -+ if (saved_init_cycle_pool != NULL && saved_init_cycle_pool != cycle->pool) { -+ ngx_destroy_pool(saved_init_cycle_pool); -+ saved_init_cycle_pool = NULL; -+ } -+ - ngx_destroy_pool(cycle->pool); - - exit(0); diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-larger_max_error_str.patch b/images/nginx/rootfs/patches/nginx-1.21.4-larger_max_error_str.patch deleted file mode 100644 index c89032c9f..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-larger_max_error_str.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- nginx-1.21.4/src/core/ngx_log.h 2013-10-08 05:07:14.000000000 -0700 -+++ nginx-1.21.4-patched/src/core/ngx_log.h 2013-12-05 20:35:35.996236720 -0800 -@@ -64,7 +64,9 @@ struct ngx_log_s { - }; - - --#define NGX_MAX_ERROR_STR 2048 -+#ifndef NGX_MAX_ERROR_STR -+#define NGX_MAX_ERROR_STR 4096 -+#endif - - - /*********************************/ diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-no_Werror.patch b/images/nginx/rootfs/patches/nginx-1.21.4-no_Werror.patch deleted file mode 100644 index f4d6fd0e5..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-no_Werror.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff -urp nginx-1.21.4/auto/cc/clang nginx-1.21.4-patched/auto/cc/clang ---- nginx-1.21.4/auto/cc/clang 2014-03-04 03:39:24.000000000 -0800 -+++ nginx-1.21.4-patched/auto/cc/clang 2014-03-13 20:54:26.241413360 -0700 -@@ -89,7 +89,7 @@ CFLAGS="$CFLAGS -Wconditional-uninitiali - CFLAGS="$CFLAGS -Wno-unused-parameter" - - # stop on warning --CFLAGS="$CFLAGS -Werror" -+#CFLAGS="$CFLAGS -Werror" - - # debug - CFLAGS="$CFLAGS -g" -diff -urp nginx-1.21.4/auto/cc/gcc nginx-1.21.4-patched/auto/cc/gcc ---- nginx-1.21.4/auto/cc/gcc 2014-03-04 03:39:24.000000000 -0800 -+++ nginx-1.21.4-patched/auto/cc/gcc 2014-03-13 20:54:13.301355329 -0700 -@@ -168,7 +168,7 @@ esac - - - # stop on warning --CFLAGS="$CFLAGS -Werror" -+#CFLAGS="$CFLAGS -Werror" - - # debug - CFLAGS="$CFLAGS -g" -diff -urp nginx-1.21.4/auto/cc/icc nginx-1.21.4-patched/auto/cc/icc ---- nginx-1.21.4/auto/cc/icc 2014-03-04 03:39:24.000000000 -0800 -+++ nginx-1.21.4-patched/auto/cc/icc 2014-03-13 20:54:13.301355329 -0700 -@@ -115,7 +115,7 @@ case "$NGX_ICC_VER" in - esac - - # stop on warning --CFLAGS="$CFLAGS -Werror" -+#CFLAGS="$CFLAGS -Werror" - - # debug - CFLAGS="$CFLAGS -g" diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-proxy_host_port_vars.patch b/images/nginx/rootfs/patches/nginx-1.21.4-proxy_host_port_vars.patch deleted file mode 100644 index 01cebd65a..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-proxy_host_port_vars.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- nginx-1.21.4/src/http/modules/ngx_http_proxy_module.c 2017-07-16 14:02:51.000000000 +0800 -+++ nginx-1.21.4-patched/src/http/modules/ngx_http_proxy_module.c 2017-07-16 14:02:51.000000000 +0800 -@@ -793,13 +793,13 @@ static ngx_keyval_t ngx_http_proxy_cach - static ngx_http_variable_t ngx_http_proxy_vars[] = { - - { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0, -- NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, -+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, - - { ngx_string("proxy_port"), NULL, ngx_http_proxy_port_variable, 0, -- NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, -+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, - - { ngx_string("proxy_add_x_forwarded_for"), NULL, -- ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, -+ ngx_http_proxy_add_x_forwarded_for_variable, 0, 0, 0 }, - - #if 0 - { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 }, diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-resolver_conf_parsing.patch b/images/nginx/rootfs/patches/nginx-1.21.4-resolver_conf_parsing.patch deleted file mode 100644 index 8638cdf2a..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-resolver_conf_parsing.patch +++ /dev/null @@ -1,263 +0,0 @@ -diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c -index cd55520c..dade1846 100644 ---- a/src/core/ngx_resolver.c -+++ b/src/core/ngx_resolver.c -@@ -9,12 +9,26 @@ - #include - #include - -+#if !(NGX_WIN32) -+#include -+#endif -+ - - #define NGX_RESOLVER_UDP_SIZE 4096 - - #define NGX_RESOLVER_TCP_RSIZE (2 + 65535) - #define NGX_RESOLVER_TCP_WSIZE 8192 - -+#if !(NGX_WIN32) -+/* -+ * note that 2KB should be more than enough for majority of the -+ * resolv.conf files out there. it also acts as a safety guard to prevent -+ * abuse. -+ */ -+#define NGX_RESOLVER_FILE_BUF_SIZE 2048 -+#define NGX_RESOLVER_FILE_NAME "/etc/resolv.conf" -+#endif -+ - - typedef struct { - u_char ident_hi; -@@ -131,6 +145,191 @@ static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r, - #endif - - -+#if !(NGX_WIN32) -+static ngx_int_t -+ngx_resolver_read_resolv_conf(ngx_conf_t *cf, ngx_resolver_t *r, u_char *path, -+ size_t path_len) -+{ -+ ngx_url_t u; -+ ngx_resolver_connection_t *rec; -+ ngx_fd_t fd; -+ ngx_file_t file; -+ u_char buf[NGX_RESOLVER_FILE_BUF_SIZE]; -+ u_char ipv6_buf[NGX_INET6_ADDRSTRLEN]; -+ ngx_uint_t address = 0, j, total = 0; -+ ssize_t n, i; -+ enum { -+ sw_nameserver, -+ sw_spaces, -+ sw_address, -+ sw_skip -+ } state; -+ -+ file.name.data = path; -+ file.name.len = path_len; -+ -+ if (ngx_conf_full_name(cf->cycle, &file.name, 1) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, -+ NGX_FILE_OPEN, 0); -+ -+ if (fd == NGX_INVALID_FILE) { -+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, -+ ngx_open_file_n " \"%s\" failed", file.name.data); -+ -+ return NGX_ERROR; -+ } -+ -+ ngx_memzero(&file, sizeof(ngx_file_t)); -+ -+ file.fd = fd; -+ file.log = cf->log; -+ -+ state = sw_nameserver; -+ -+ n = ngx_read_file(&file, buf, NGX_RESOLVER_FILE_BUF_SIZE, 0); -+ -+ if (n == NGX_ERROR) { -+ ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno, -+ ngx_read_file_n " \"%s\" failed", file.name.data); -+ } -+ -+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { -+ ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno, -+ ngx_close_file_n " \"%s\" failed", file.name.data); -+ } -+ -+ if (n == NGX_ERROR) { -+ return NGX_ERROR; -+ } -+ -+ if (n == 0) { -+ return NGX_OK; -+ } -+ -+ for (i = 0; i < n && total < MAXNS; /* void */) { -+ if (buf[i] == '#' || buf[i] == ';') { -+ state = sw_skip; -+ } -+ -+ switch (state) { -+ -+ case sw_nameserver: -+ -+ if ((size_t) n - i >= sizeof("nameserver") - 1 -+ && ngx_memcmp(buf + i, "nameserver", -+ sizeof("nameserver") - 1) == 0) -+ { -+ state = sw_spaces; -+ i += sizeof("nameserver") - 1; -+ -+ continue; -+ } -+ -+ break; -+ -+ case sw_spaces: -+ if (buf[i] != '\t' && buf[i] != ' ') { -+ address = i; -+ state = sw_address; -+ } -+ -+ break; -+ -+ case sw_address: -+ -+ if (buf[i] == CR || buf[i] == LF || i == n - 1) { -+ ngx_memzero(&u, sizeof(ngx_url_t)); -+ -+ u.url.data = buf + address; -+ -+ if (i == n - 1 && buf[i] != CR && buf[i] != LF) { -+ u.url.len = n - address; -+ -+ } else { -+ u.url.len = i - address; -+ } -+ -+ u.default_port = 53; -+ -+ /* IPv6? */ -+ if (ngx_strlchr(u.url.data, u.url.data + u.url.len, -+ ':') != NULL) -+ { -+ if (u.url.len + 2 > sizeof(ipv6_buf)) { -+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, -+ "IPv6 resolver address is too long:" -+ " \"%V\"", &u.url); -+ -+ return NGX_ERROR; -+ } -+ -+ ipv6_buf[0] = '['; -+ ngx_memcpy(ipv6_buf + 1, u.url.data, u.url.len); -+ ipv6_buf[u.url.len + 1] = ']'; -+ -+ u.url.data = ipv6_buf; -+ u.url.len = u.url.len + 2; -+ } -+ -+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) { -+ if (u.err) { -+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, -+ "%s in resolver \"%V\"", -+ u.err, &u.url); -+ } -+ -+ return NGX_ERROR; -+ } -+ -+ rec = ngx_array_push_n(&r->connections, u.naddrs); -+ if (rec == NULL) { -+ return NGX_ERROR; -+ } -+ -+ ngx_memzero(rec, u.naddrs * sizeof(ngx_resolver_connection_t)); -+ -+ for (j = 0; j < u.naddrs; j++) { -+ rec[j].sockaddr = u.addrs[j].sockaddr; -+ rec[j].socklen = u.addrs[j].socklen; -+ rec[j].server = u.addrs[j].name; -+ rec[j].resolver = r; -+ } -+ -+ total++; -+ -+#if (NGX_DEBUG) -+ /* -+ * logs with level below NGX_LOG_NOTICE will not be printed -+ * in this early phase -+ */ -+ ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, -+ "parsed a resolver: \"%V\"", &u.url); -+#endif -+ -+ state = sw_nameserver; -+ } -+ -+ break; -+ -+ case sw_skip: -+ if (buf[i] == CR || buf[i] == LF) { -+ state = sw_nameserver; -+ } -+ -+ break; -+ } -+ -+ i++; -+ } -+ -+ return NGX_OK; -+} -+#endif -+ -+ - ngx_resolver_t * - ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) - { -@@ -246,6 +445,39 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) - } - #endif - -+#if !(NGX_WIN32) -+ if (ngx_strncmp(names[i].data, "local=", 6) == 0) { -+ -+ if (ngx_strcmp(&names[i].data[6], "on") == 0) { -+ if (ngx_resolver_read_resolv_conf(cf, r, -+ (u_char *) -+ NGX_RESOLVER_FILE_NAME, -+ sizeof(NGX_RESOLVER_FILE_NAME) -+ - 1) -+ != NGX_OK) -+ { -+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, -+ "unable to parse local resolver"); -+ return NULL; -+ } -+ -+ } else if (ngx_strcmp(&names[i].data[6], "off") != 0) { -+ if (ngx_resolver_read_resolv_conf(cf, r, -+ &names[i].data[6], -+ names[i].len - 6) -+ != NGX_OK) -+ { -+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, -+ "unable to parse local resolver"); -+ return NULL; -+ } -+ -+ } -+ -+ continue; -+ } -+#endif -+ - ngx_memzero(&u, sizeof(ngx_url_t)); - - u.url = names[i]; diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-reuseport_close_unused_fds.patch b/images/nginx/rootfs/patches/nginx-1.21.4-reuseport_close_unused_fds.patch deleted file mode 100644 index ff4a36fd2..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-reuseport_close_unused_fds.patch +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c ---- a/src/core/ngx_connection.c -+++ b/src/core/ngx_connection.c -@@ -1118,6 +1118,12 @@ ngx_close_listening_sockets(ngx_cycle_t *cycle) - ls = cycle->listening.elts; - for (i = 0; i < cycle->listening.nelts; i++) { - -+#if (NGX_HAVE_REUSEPORT) -+ if (ls[i].fd == (ngx_socket_t) -1) { -+ continue; -+ } -+#endif -+ - c = ls[i].connection; - - if (c) { -diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c ---- a/src/event/ngx_event.c -+++ b/src/event/ngx_event.c -@@ -775,6 +775,18 @@ ngx_event_process_init(ngx_cycle_t *cycle) - - #if (NGX_HAVE_REUSEPORT) - if (ls[i].reuseport && ls[i].worker != ngx_worker) { -+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, -+ "closing unused fd:%d listening on %V", -+ ls[i].fd, &ls[i].addr_text); -+ -+ if (ngx_close_socket(ls[i].fd) == -1) { -+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, -+ ngx_close_socket_n " %V failed", -+ &ls[i].addr_text); -+ } -+ -+ ls[i].fd = (ngx_socket_t) -1; -+ - continue; - } - #endif diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-single_process_graceful_exit.patch b/images/nginx/rootfs/patches/nginx-1.21.4-single_process_graceful_exit.patch deleted file mode 100644 index 2754fc2fe..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-single_process_graceful_exit.patch +++ /dev/null @@ -1,75 +0,0 @@ -diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c -index 15680237..12a8c687 100644 ---- a/src/os/unix/ngx_process.c -+++ b/src/os/unix/ngx_process.c -@@ -362,8 +362,15 @@ ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext) - break; - - case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): -- ngx_reconfigure = 1; -- action = ", reconfiguring"; -+ if (ngx_process == NGX_PROCESS_SINGLE) { -+ ngx_terminate = 1; -+ action = ", exiting"; -+ -+ } else { -+ ngx_reconfigure = 1; -+ action = ", reconfiguring"; -+ } -+ - break; - - case ngx_signal_value(NGX_REOPEN_SIGNAL): -diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c -index 5817a2c2..f3d58e97 100644 ---- a/src/os/unix/ngx_process_cycle.c -+++ b/src/os/unix/ngx_process_cycle.c -@@ -305,11 +305,26 @@ ngx_single_process_cycle(ngx_cycle_t *cycle) - } - - for ( ;; ) { -+ if (ngx_exiting) { -+ if (ngx_event_no_timers_left() == NGX_OK) { -+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); -+ -+ for (i = 0; cycle->modules[i]; i++) { -+ if (cycle->modules[i]->exit_process) { -+ cycle->modules[i]->exit_process(cycle); -+ } -+ } -+ -+ ngx_master_process_exit(cycle); -+ } -+ } -+ - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); - - ngx_process_events_and_timers(cycle); - -- if (ngx_terminate || ngx_quit) { -+ if (ngx_terminate) { -+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); - - for (i = 0; cycle->modules[i]; i++) { - if (cycle->modules[i]->exit_process) { -@@ -320,6 +335,20 @@ ngx_single_process_cycle(ngx_cycle_t *cycle) - ngx_master_process_exit(cycle); - } - -+ if (ngx_quit) { -+ ngx_quit = 0; -+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, -+ "gracefully shutting down"); -+ ngx_setproctitle("process is shutting down"); -+ -+ if (!ngx_exiting) { -+ ngx_exiting = 1; -+ ngx_set_shutdown_timer(cycle); -+ ngx_close_listening_sockets(cycle); -+ ngx_close_idle_connections(cycle); -+ } -+ } -+ - if (ngx_reconfigure) { - ngx_reconfigure = 0; - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-socket_cloexec.patch b/images/nginx/rootfs/patches/nginx-1.21.4-socket_cloexec.patch deleted file mode 100644 index 8ffe4c167..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-socket_cloexec.patch +++ /dev/null @@ -1,185 +0,0 @@ -diff --git a/auto/unix b/auto/unix -index 10835f6c..b5b33bb3 100644 ---- a/auto/unix -+++ b/auto/unix -@@ -990,3 +990,27 @@ ngx_feature_test='struct addrinfo *res; - if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1; - freeaddrinfo(res)' - . auto/feature -+ -+ngx_feature="SOCK_CLOEXEC support" -+ngx_feature_name="NGX_HAVE_SOCKET_CLOEXEC" -+ngx_feature_run=no -+ngx_feature_incs="#include -+ #include " -+ngx_feature_path= -+ngx_feature_libs= -+ngx_feature_test="int fd; -+ fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);" -+. auto/feature -+ -+ngx_feature="FD_CLOEXEC support" -+ngx_feature_name="NGX_HAVE_FD_CLOEXEC" -+ngx_feature_run=no -+ngx_feature_incs="#include -+ #include -+ #include " -+ngx_feature_path= -+ngx_feature_libs= -+ngx_feature_test="int fd; -+ fd = socket(AF_INET, SOCK_STREAM, 0); -+ fcntl(fd, F_SETFD, FD_CLOEXEC);" -+. auto/feature -diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c -index cd55520c..438e0806 100644 ---- a/src/core/ngx_resolver.c -+++ b/src/core/ngx_resolver.c -@@ -4466,8 +4466,14 @@ ngx_tcp_connect(ngx_resolver_connection_t *rec) - ngx_event_t *rev, *wev; - ngx_connection_t *c; - -+#if (NGX_HAVE_SOCKET_CLOEXEC) -+ s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0); -+ -+#else - s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0); - -+#endif -+ - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); - - if (s == (ngx_socket_t) -1) { -@@ -4494,6 +4500,15 @@ ngx_tcp_connect(ngx_resolver_connection_t *rec) - goto failed; - } - -+#if (NGX_HAVE_FD_CLOEXEC) -+ if (ngx_cloexec(s) == -1) { -+ ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, -+ ngx_cloexec_n " failed"); -+ -+ goto failed; -+ } -+#endif -+ - rev = c->read; - wev = c->write; - -diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h -index 19fec68..8c2f01a 100644 ---- a/src/event/ngx_event.h -+++ b/src/event/ngx_event.h -@@ -73,6 +73,9 @@ struct ngx_event_s { - /* to test on worker exit */ - unsigned channel:1; - unsigned resolver:1; -+#if (HAVE_SOCKET_CLOEXEC_PATCH) -+ unsigned skip_socket_leak_check:1; -+#endif - - unsigned cancelable:1; - -diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c -index 77563709..5827b9d0 100644 ---- a/src/event/ngx_event_accept.c -+++ b/src/event/ngx_event_accept.c -@@ -62,7 +62,9 @@ ngx_event_accept(ngx_event_t *ev) - - #if (NGX_HAVE_ACCEPT4) - if (use_accept4) { -- s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK); -+ s = accept4(lc->fd, &sa.sockaddr, &socklen, -+ SOCK_NONBLOCK | SOCK_CLOEXEC); -+ - } else { - s = accept(lc->fd, &sa.sockaddr, &socklen); - } -@@ -202,6 +204,16 @@ ngx_event_accept(ngx_event_t *ev) - ngx_close_accepted_connection(c); - return; - } -+ -+#if (NGX_HAVE_FD_CLOEXEC) -+ if (ngx_cloexec(s) == -1) { -+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno, -+ ngx_cloexec_n " failed"); -+ ngx_close_accepted_connection(c); -+ return; -+ } -+#endif -+ - } - } - -diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c -index c5bb8068..cf33b1d2 100644 ---- a/src/event/ngx_event_connect.c -+++ b/src/event/ngx_event_connect.c -@@ -38,8 +38,15 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) - - type = (pc->type ? pc->type : SOCK_STREAM); - -+#if (NGX_HAVE_SOCKET_CLOEXEC) -+ s = ngx_socket(pc->sockaddr->sa_family, type | SOCK_CLOEXEC, 0); -+ -+#else - s = ngx_socket(pc->sockaddr->sa_family, type, 0); - -+#endif -+ -+ - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d", - (type == SOCK_STREAM) ? "stream" : "dgram", s); - -@@ -80,6 +87,15 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) - goto failed; - } - -+#if (NGX_HAVE_FD_CLOEXEC) -+ if (ngx_cloexec(s) == -1) { -+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, -+ ngx_cloexec_n " failed"); -+ -+ goto failed; -+ } -+#endif -+ - if (pc->local) { - - #if (NGX_HAVE_TRANSPARENT_PROXY) -diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c -index c4376a5..48e8fa8 100644 ---- a/src/os/unix/ngx_process_cycle.c -+++ b/src/os/unix/ngx_process_cycle.c -@@ -960,6 +1029,9 @@ ngx_worker_process_exit(ngx_cycle_t *cycle) - for (i = 0; i < cycle->connection_n; i++) { - if (c[i].fd != -1 - && c[i].read -+#if (HAVE_SOCKET_CLOEXEC_PATCH) -+ && !c[i].read->skip_socket_leak_check -+#endif - && !c[i].read->accept - && !c[i].read->channel - && !c[i].read->resolver) -diff --git a/src/os/unix/ngx_socket.h b/src/os/unix/ngx_socket.h -index fcc51533..d1eebf47 100644 ---- a/src/os/unix/ngx_socket.h -+++ b/src/os/unix/ngx_socket.h -@@ -38,6 +38,17 @@ int ngx_blocking(ngx_socket_t s); - - #endif - -+#if (NGX_HAVE_FD_CLOEXEC) -+ -+#define ngx_cloexec(s) fcntl(s, F_SETFD, FD_CLOEXEC) -+#define ngx_cloexec_n "fcntl(FD_CLOEXEC)" -+ -+/* at least FD_CLOEXEC is required to ensure connection fd is closed -+ * after execve */ -+#define HAVE_SOCKET_CLOEXEC_PATCH 1 -+ -+#endif -+ - int ngx_tcp_nopush(ngx_socket_t s); - int ngx_tcp_push(ngx_socket_t s); - diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-ssl_cert_cb_yield.patch b/images/nginx/rootfs/patches/nginx-1.21.4-ssl_cert_cb_yield.patch deleted file mode 100644 index 89773c05e..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-ssl_cert_cb_yield.patch +++ /dev/null @@ -1,64 +0,0 @@ -# HG changeset patch -# User Yichun Zhang -# Date 1451762084 28800 -# Sat Jan 02 11:14:44 2016 -0800 -# Node ID 449f0461859c16e95bdb18e8be6b94401545d3dd -# Parent 78b4e10b4367b31367aad3c83c9c3acdd42397c4 -SSL: handled SSL_CTX_set_cert_cb() callback yielding. - -OpenSSL 1.0.2+ introduces SSL_CTX_set_cert_cb() to allow custom -callbacks to serve the SSL certificiates and private keys dynamically -and lazily. The callbacks may yield for nonblocking I/O or sleeping. -Here we added support for such usage in NGINX 3rd-party modules -(like ngx_lua) in NGINX's event handlers for downstream SSL -connections. - -diff -r 78b4e10b4367 -r 449f0461859c src/event/ngx_event_openssl.c ---- a/src/event/ngx_event_openssl.c Thu Dec 17 16:39:15 2015 +0300 -+++ b/src/event/ngx_event_openssl.c Sat Jan 02 11:14:44 2016 -0800 -@@ -1445,6 +1445,23 @@ ngx_ssl_handshake(ngx_connection_t *c) - return NGX_AGAIN; - } - -+#if OPENSSL_VERSION_NUMBER >= 0x10002000L -+ if (sslerr == SSL_ERROR_WANT_X509_LOOKUP) { -+ c->read->handler = ngx_ssl_handshake_handler; -+ c->write->handler = ngx_ssl_handshake_handler; -+ -+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ return NGX_AGAIN; -+ } -+#endif -+ - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - - c->ssl->no_wait_shutdown = 1; -@@ -1558,6 +1575,21 @@ ngx_ssl_try_early_data(ngx_connection_t *c) - return NGX_AGAIN; - } - -+ if (sslerr == SSL_ERROR_WANT_X509_LOOKUP) { -+ c->read->handler = ngx_ssl_handshake_handler; -+ c->write->handler = ngx_ssl_handshake_handler; -+ -+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ return NGX_AGAIN; -+ } -+ - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - - c->ssl->no_wait_shutdown = 1; diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-ssl_sess_cb_yield.patch b/images/nginx/rootfs/patches/nginx-1.21.4-ssl_sess_cb_yield.patch deleted file mode 100644 index ac5fe65eb..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-ssl_sess_cb_yield.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c ---- a/src/event/ngx_event_openssl.c -+++ b/src/event/ngx_event_openssl.c -@@ -1446,7 +1446,12 @@ ngx_ssl_handshake(ngx_connection_t *c) - } - - #if OPENSSL_VERSION_NUMBER >= 0x10002000L -- if (sslerr == SSL_ERROR_WANT_X509_LOOKUP) { -+ if (sslerr == SSL_ERROR_WANT_X509_LOOKUP -+# ifdef SSL_ERROR_PENDING_SESSION -+ || sslerr == SSL_ERROR_PENDING_SESSION -+# endif -+ ) -+ { - c->read->handler = ngx_ssl_handshake_handler; - c->write->handler = ngx_ssl_handshake_handler; - -@@ -1575,6 +1580,23 @@ ngx_ssl_try_early_data(ngx_connection_t *c) - return NGX_AGAIN; - } - -+#ifdef SSL_ERROR_PENDING_SESSION -+ if (sslerr == SSL_ERROR_PENDING_SESSION) { -+ c->read->handler = ngx_ssl_handshake_handler; -+ c->write->handler = ngx_ssl_handshake_handler; -+ -+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ return NGX_AGAIN; -+ } -+#endif -+ - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - - c->ssl->no_wait_shutdown = 1; diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-stream_proxy_get_next_upstream_tries.patch b/images/nginx/rootfs/patches/nginx-1.21.4-stream_proxy_get_next_upstream_tries.patch deleted file mode 100644 index cb881f070..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-stream_proxy_get_next_upstream_tries.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h -index 09d2459..de92724 100644 ---- a/src/stream/ngx_stream.h -+++ b/src/stream/ngx_stream.h -@@ -303,4 +303,7 @@ typedef ngx_int_t (*ngx_stream_filter_pt)(ngx_stream_session_t *s, - extern ngx_stream_filter_pt ngx_stream_top_filter; - - -+#define HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH 1 -+ -+ - #endif /* _NGX_STREAM_H_INCLUDED_ */ -diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c -index 0afde1c..3254ce1 100644 ---- a/src/stream/ngx_stream_proxy_module.c -+++ b/src/stream/ngx_stream_proxy_module.c -@@ -2156,3 +2156,14 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) - - return NGX_CONF_OK; - } -+ -+ -+ngx_uint_t -+ngx_stream_proxy_get_next_upstream_tries(ngx_stream_session_t *s) -+{ -+ ngx_stream_proxy_srv_conf_t *pscf; -+ -+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); -+ -+ return pscf->next_upstream_tries; -+} diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-stream_ssl_preread_no_skip.patch b/images/nginx/rootfs/patches/nginx-1.21.4-stream_ssl_preread_no_skip.patch deleted file mode 100644 index e45e9f69a..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-stream_ssl_preread_no_skip.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c -index e3d11fd9..3717b5fe 100644 ---- a/src/stream/ngx_stream_ssl_preread_module.c -+++ b/src/stream/ngx_stream_ssl_preread_module.c -@@ -159,7 +159,7 @@ ngx_stream_ssl_preread_handler(ngx_stream_session_t *s) - - rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); - if (rc != NGX_AGAIN) { -- return rc; -+ return rc == NGX_OK ? NGX_DECLINED : rc; - } - - p += len; diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-upstream_pipelining.patch b/images/nginx/rootfs/patches/nginx-1.21.4-upstream_pipelining.patch deleted file mode 100644 index aed80365a..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-upstream_pipelining.patch +++ /dev/null @@ -1,23 +0,0 @@ -commit f9907b72a76a21ac5413187b83177a919475c75f -Author: Yichun Zhang (agentzh) -Date: Wed Feb 10 16:05:08 2016 -0800 - - bugfix: upstream: keep sending request data after the first write attempt. - - See - http://mailman.nginx.org/pipermail/nginx-devel/2012-March/002040.html - for more details on the issue. - -diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c -index 69019417..92b7c97f 100644 ---- a/src/http/ngx_http_upstream.c -+++ b/src/http/ngx_http_upstream.c -@@ -2239,7 +2239,7 @@ ngx_http_upstream_send_request_handler(ngx_http_request_t *r, - - #endif - -- if (u->header_sent && !u->conf->preserve_output) { -+ if (u->request_body_sent && !u->conf->preserve_output) { - u->write_event_handler = ngx_http_upstream_dummy_handler; - - (void) ngx_handle_write_event(c->write, 0); diff --git a/images/nginx/rootfs/patches/nginx-1.21.4-upstream_timeout_fields.patch b/images/nginx/rootfs/patches/nginx-1.21.4-upstream_timeout_fields.patch deleted file mode 100644 index 2314ddf80..000000000 --- a/images/nginx/rootfs/patches/nginx-1.21.4-upstream_timeout_fields.patch +++ /dev/null @@ -1,112 +0,0 @@ -diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c -index 69019417..2265d8f7 100644 ---- a/src/http/ngx_http_upstream.c -+++ b/src/http/ngx_http_upstream.c -@@ -509,12 +509,19 @@ void - ngx_http_upstream_init(ngx_http_request_t *r) - { - ngx_connection_t *c; -+ ngx_http_upstream_t *u; - - c = r->connection; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http init upstream, client timer: %d", c->read->timer_set); - -+ u = r->upstream; -+ -+ u->connect_timeout = u->conf->connect_timeout; -+ u->send_timeout = u->conf->send_timeout; -+ u->read_timeout = u->conf->read_timeout; -+ - #if (NGX_HTTP_V2) - if (r->stream) { - ngx_http_upstream_init_request(r); -@@ -1626,7 +1633,7 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) - u->request_body_blocked = 0; - - if (rc == NGX_AGAIN) { -- ngx_add_timer(c->write, u->conf->connect_timeout); -+ ngx_add_timer(c->write, u->connect_timeout); - return; - } - -@@ -1704,7 +1711,7 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, - if (rc == NGX_AGAIN) { - - if (!c->write->timer_set) { -- ngx_add_timer(c->write, u->conf->connect_timeout); -+ ngx_add_timer(c->write, u->connect_timeout); - } - - c->ssl->handler = ngx_http_upstream_ssl_handshake_handler; -@@ -2022,7 +2029,7 @@ ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, - - if (rc == NGX_AGAIN) { - if (!c->write->ready || u->request_body_blocked) { -- ngx_add_timer(c->write, u->conf->send_timeout); -+ ngx_add_timer(c->write, u->send_timeout); - - } else if (c->write->timer_set) { - ngx_del_timer(c->write); -@@ -2084,7 +2091,7 @@ ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, - return; - } - -- ngx_add_timer(c->read, u->conf->read_timeout); -+ ngx_add_timer(c->read, u->read_timeout); - - if (c->read->ready) { - ngx_http_upstream_process_header(r, u); -@@ -3213,7 +3220,7 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) - p->cyclic_temp_file = 0; - } - -- p->read_timeout = u->conf->read_timeout; -+ p->read_timeout = u->read_timeout; - p->send_timeout = clcf->send_timeout; - p->send_lowat = clcf->send_lowat; - -@@ -3458,7 +3465,7 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r, - } - - if (upstream->write->active && !upstream->write->ready) { -- ngx_add_timer(upstream->write, u->conf->send_timeout); -+ ngx_add_timer(upstream->write, u->send_timeout); - - } else if (upstream->write->timer_set) { - ngx_del_timer(upstream->write); -@@ -3470,7 +3477,7 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r, - } - - if (upstream->read->active && !upstream->read->ready) { -- ngx_add_timer(upstream->read, u->conf->read_timeout); -+ ngx_add_timer(upstream->read, u->read_timeout); - - } else if (upstream->read->timer_set) { - ngx_del_timer(upstream->read); -@@ -3664,7 +3671,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, - } - - if (upstream->read->active && !upstream->read->ready) { -- ngx_add_timer(upstream->read, u->conf->read_timeout); -+ ngx_add_timer(upstream->read, u->read_timeout); - - } else if (upstream->read->timer_set) { - ngx_del_timer(upstream->read); -diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h -index c2f4dc0b..b9eef118 100644 ---- a/src/http/ngx_http_upstream.h -+++ b/src/http/ngx_http_upstream.h -@@ -333,6 +333,11 @@ struct ngx_http_upstream_s { - ngx_array_t *caches; - #endif - -+#define HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS 1 -+ ngx_msec_t connect_timeout; -+ ngx_msec_t send_timeout; -+ ngx_msec_t read_timeout; -+ - ngx_http_upstream_headers_in_t headers_in; - - ngx_http_upstream_resolved_t *resolved; diff --git a/images/opentelemetry/Makefile b/images/opentelemetry/Makefile index 07a66beed..03d92a298 100644 --- a/images/opentelemetry/Makefile +++ b/images/opentelemetry/Makefile @@ -32,7 +32,7 @@ IMAGE = $(REGISTRY)/opentelemetry export DOCKER_CLI_EXPERIMENTAL=enabled # build with buildx -PLATFORMS?=linux/amd64,linux/arm64 +PLATFORMS?=linux/amd64,linux/arm,linux/arm64,linux/s390x OUTPUT= PROGRESS=plain build: ensure-buildx diff --git a/images/opentelemetry/cloudbuild.yaml b/images/opentelemetry/cloudbuild.yaml index 9556ccba9..baf29a051 100644 --- a/images/opentelemetry/cloudbuild.yaml +++ b/images/opentelemetry/cloudbuild.yaml @@ -8,8 +8,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -19,6 +17,3 @@ steps: - | gcloud auth configure-docker \ && cd images/opentelemetry && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "main" diff --git a/images/opentelemetry/rootfs/CMakeLists.txt b/images/opentelemetry/rootfs/CMakeLists.txt index ef90b5805..2a816ebf7 100644 --- a/images/opentelemetry/rootfs/CMakeLists.txt +++ b/images/opentelemetry/rootfs/CMakeLists.txt @@ -25,6 +25,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "-O2") +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON" FORCE) set(CMAKE_BUILD_TYPE Release @@ -82,12 +83,18 @@ ExternalProject_Add( -DgRPC_BUILD_TESTS=OFF -DBUILD_SHARED_LIBS=OFF -DgRPC_INSTALL=ON + -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF + -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF + -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF + -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=OFF + -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF CMAKE_CACHE_ARGS -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} - TEST_AFTER_INSTALL 0 - DOWNLOAD_NO_PROGRESS 1 - LOG_CONFIGURE 1 - LOG_BUILD 0 - LOG_INSTALL 1) + TEST_AFTER_INSTALL FALSE + USES_TERMINAL_BUILD TRUE + DOWNLOAD_NO_PROGRESS TRUE + LOG_CONFIGURE TRUE + LOG_BUILD TRUE + LOG_INSTALL TRUE) install( DIRECTORY ${STAGED_INSTALL_PREFIX}/ diff --git a/images/opentelemetry/rootfs/Dockerfile b/images/opentelemetry/rootfs/Dockerfile index 33f5bcb36..b46b116e0 100644 --- a/images/opentelemetry/rootfs/Dockerfile +++ b/images/opentelemetry/rootfs/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. -FROM alpine:3.17.2 as base +FROM alpine:3.18.4 as base RUN mkdir -p /opt/third_party/install COPY . /opt/third_party/ @@ -21,17 +21,24 @@ COPY . /opt/third_party/ # install build tools RUN apk update \ && apk upgrade \ - && apk add -U bash cmake \ + && apk add -U bash \ && bash /opt/third_party/build.sh -p +ENV NINJA_STATUS "[%p/%f/%t] " + # install gRPC FROM base as grpc -RUN bash /opt/third_party/build.sh -g v1.49.2 +RUN bash /opt/third_party/build.sh -g v1.57.0 + +# install abseil-cpp +FROM base as absl-cpp +RUN bash /opt/third_party/build.sh -a 20230802.0 # install OpenTelemetry-cpp FROM base as otel-cpp COPY --from=grpc /opt/third_party/install/ /usr -RUN bash /opt/third_party/build.sh -o v1.8.1 +COPY --from=absl-cpp /opt/third_party/install/ /usr +RUN bash /opt/third_party/build.sh -o v1.11.0 # install otel_ngx_module.so FROM base as nginx @@ -39,7 +46,16 @@ COPY --from=grpc /opt/third_party/install/ /usr COPY --from=otel-cpp /opt/third_party/install/ /usr RUN bash /opt/third_party/build.sh -n -FROM alpine:3.17.2 as final -COPY --from=base /opt/third_party/init_module.sh /usr/local/bin/init_module.sh +FROM cgr.dev/chainguard/go:latest as build-init + +WORKDIR /go/src/app +COPY . . + +RUN go mod download +RUN CGO_ENABLED=0 go build -o /go/bin/init_module + +FROM cgr.dev/chainguard/static as final +COPY --from=build-init /go/bin/init_module / COPY --from=nginx /etc/nginx/modules /etc/nginx/modules -COPY --from=nginx /opt/third_party/install/lib /etc/nginx/modules + +CMD ["/init_module"] diff --git a/images/opentelemetry/rootfs/build.sh b/images/opentelemetry/rootfs/build.sh index 6ad4601c6..ebf95e219 100755 --- a/images/opentelemetry/rootfs/build.sh +++ b/images/opentelemetry/rootfs/build.sh @@ -21,6 +21,7 @@ set -o pipefail export GRPC_GIT_TAG=${GRPC_GIT_TAG:="v1.43.2"} # Check for recent changes: https://github.com/open-telemetry/opentelemetry-cpp/compare/v1.2.0...main export OPENTELEMETRY_CPP_VERSION=${OPENTELEMETRY_CPP_VERSION:="1.2.0"} +export ABSL_CPP_VERSION=${ABSL_CPP_VERSION:="20230802.0"} export INSTAL_DIR=/opt/third_party/install # improve compilation times CORES=$(($(grep -c ^processor /proc/cpuinfo) - 1)) @@ -54,6 +55,8 @@ prepare() { apk add \ linux-headers \ + cmake \ + ninja \ openssl \ curl-dev \ openssl-dev \ @@ -70,14 +73,33 @@ install_grpc() mkdir -p $BUILD_PATH/grpc cd ${BUILD_PATH}/grpc cmake -DCMAKE_INSTALL_PREFIX=${INSTAL_DIR} \ - -DGRPC_GIT_TAG=${GRPC_GIT_TAG} /opt/third_party \ - -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF \ - -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF \ - -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \ - -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \ - -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=OFF \ - -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF - cmake --build . -j ${CORES} --target all install + -G Ninja \ + -DGRPC_GIT_TAG=${GRPC_GIT_TAG} /opt/third_party + + cmake --build . -j ${CORES} --target all install --verbose +} + +install_absl() +{ + cd ${BUILD_PATH} + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+LD_LIBRARY_PATH:}${INSTAL_DIR}/lib:/usr/local" + export PATH="${PATH}:${INSTAL_DIR}/bin" + git clone --recurse-submodules -j ${CORES} --depth=1 -b \ + ${ABSL_CPP_VERSION} https://github.com/abseil/abseil-cpp.git abseil-cpp-${ABSL_CPP_VERSION} + cd "abseil-cpp-${ABSL_CPP_VERSION}" + mkdir -p .build + cd .build + + cmake -DCMAKE_BUILD_TYPE=Release \ + -G Ninja \ + -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ + -DBUILD_TESTING=OFF \ + -DCMAKE_INSTALL_PREFIX=${INSTAL_DIR} \ + -DABSL_PROPAGATE_CXX_STD=ON \ + -DBUILD_SHARED_LIBS=OFF \ + .. + cmake --build . -j ${CORES} --target install } install_otel() @@ -92,17 +114,20 @@ install_otel() cd .build cmake -DCMAKE_BUILD_TYPE=Release \ + -G Ninja \ + -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ -DWITH_ZIPKIN=OFF \ - -DWITH_JAEGER=OFF \ -DCMAKE_INSTALL_PREFIX=${INSTAL_DIR} \ -DBUILD_TESTING=OFF \ + -DWITH_BENCHMARK=OFF \ + -DWITH_FUNC_TESTS=OFF \ -DBUILD_SHARED_LIBS=OFF \ - -DWITH_OTLP=ON \ -DWITH_OTLP_GRPC=ON \ -DWITH_OTLP_HTTP=OFF \ - -DWITH_ABSEIL=OFF \ + -DWITH_ABSEIL=ON \ -DWITH_EXAMPLES=OFF \ + -DWITH_NO_DEPRECATED_CODE=ON \ .. cmake --build . -j ${CORES} --target install } @@ -126,7 +151,7 @@ install_nginx() export NGINX_VERSION=1.21.6 # Check for recent changes: https://github.com/open-telemetry/opentelemetry-cpp-contrib/compare/2656a4...main - export OPENTELEMETRY_CONTRIB_COMMIT=1ec94c82095bab61f06c7393b6f3272469d285af + export OPENTELEMETRY_CONTRIB_COMMIT=aaa51e2297bcb34297f3c7aa44fa790497d2f7f3 mkdir -p /etc/nginx cd "$BUILD_PATH" @@ -143,19 +168,19 @@ install_nginx() mkdir -p build cd build cmake -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=${INSTAL_DIR} \ - -DBUILD_SHARED_LIBS=ON \ - -DNGINX_VERSION=${NGINX_VERSION} \ - .. + -G Ninja \ + -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_INSTALL_PREFIX=${INSTAL_DIR} \ + -DBUILD_SHARED_LIBS=ON \ + -DNGINX_VERSION=${NGINX_VERSION} \ + .. cmake --build . -j ${CORES} --target install mkdir -p /etc/nginx/modules cp ${INSTAL_DIR}/otel_ngx_module.so /etc/nginx/modules/otel_ngx_module.so - - mkdir -p ${INSTAL_DIR}/lib } -while getopts ":hpng:o:" option; do +while getopts ":pha:g:o:n" option; do case $option in h) # display Help Help @@ -174,6 +199,10 @@ while getopts ":hpng:o:" option; do n) # install nginx install_nginx exit;; + a) # install abseil + ABSL_CPP_VERSION=${OPTARG} + install_absl + exit;; \?) Help exit;; diff --git a/images/opentelemetry/rootfs/go.mod b/images/opentelemetry/rootfs/go.mod new file mode 100644 index 000000000..0f48b0988 --- /dev/null +++ b/images/opentelemetry/rootfs/go.mod @@ -0,0 +1,3 @@ +module init-otel + +go 1.21.3 diff --git a/images/opentelemetry/rootfs/init_module.go b/images/opentelemetry/rootfs/init_module.go new file mode 100644 index 000000000..bebec728f --- /dev/null +++ b/images/opentelemetry/rootfs/init_module.go @@ -0,0 +1,104 @@ +/* +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 main + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +func main() { + // Enable error handling for all operations + err := run() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func run() error { + // Create the target directory if it doesn't exist + targetDir := "/modules_mount/etc/nginx/modules/otel" + err := os.MkdirAll(targetDir, os.ModePerm) + if err != nil { + return fmt.Errorf("failed to create target directory: %w", err) + } + + // Copy files from source directory to target directory + sourceDir := "/etc/nginx/modules/" + err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Skip directories + if info.IsDir() { + return nil + } + + // Calculate the destination path + relPath, err := filepath.Rel(sourceDir, path) + if err != nil { + return err + } + destPath := filepath.Join(targetDir, relPath) + + // Create the destination directory if it doesn't exist + destDir := filepath.Dir(destPath) + err = os.MkdirAll(destDir, os.ModePerm) + if err != nil { + return err + } + + // Copy the file + err = copyFile(path, destPath) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return fmt.Errorf("failed to copy files: %w", err) + } + + return nil +} + +func copyFile(sourcePath, destPath string) error { + sourceFile, err := os.Open(sourcePath) + if err != nil { + return err + } + defer sourceFile.Close() + + destFile, err := os.Create(destPath) + if err != nil { + return err + } + defer destFile.Close() + + _, err = io.Copy(destFile, sourceFile) + if err != nil { + return err + } + + return nil +} diff --git a/images/opentelemetry/rootfs/init_module.sh b/images/opentelemetry/rootfs/init_module.sh deleted file mode 100755 index 5a675aa2b..000000000 --- a/images/opentelemetry/rootfs/init_module.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -# 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. - -set -o errexit -set -o nounset -set -o pipefail - -mkdir -p /modules_mount/etc/nginx/modules/otel -cp -R /etc/nginx/modules/* /modules_mount/etc/nginx/modules/otel diff --git a/images/test-runner/Makefile b/images/test-runner/Makefile index 562625cff..b70c57b7b 100644 --- a/images/test-runner/Makefile +++ b/images/test-runner/Makefile @@ -43,7 +43,7 @@ image: --pull \ --push \ --build-arg BASE_IMAGE=${NGINX_BASE_IMAGE} \ - --build-arg GOLANG_VERSION=1.20.1 \ + --build-arg GOLANG_VERSION=1.21.3 \ --build-arg ETCD_VERSION=3.4.3-0 \ --build-arg K8S_RELEASE=v1.26.0 \ --build-arg RESTY_CLI_VERSION=0.27 \ @@ -54,7 +54,7 @@ image: --build-arg YAML_LINT_VERSION=1.27.1 \ --build-arg YAMALE_VERSION=4.0.4 \ --build-arg HELM_VERSION=3.11.2 \ - --build-arg GINKGO_VERSION=2.9.0 \ + --build-arg GINKGO_VERSION=2.13.0 \ --build-arg GOLINT_VERSION=latest \ -t ${IMAGE}:${TAG} rootfs @@ -64,7 +64,7 @@ build: ensure-buildx --progress=${PROGRESS} \ --pull \ --build-arg BASE_IMAGE=${NGINX_BASE_IMAGE} \ - --build-arg GOLANG_VERSION=1.20.1 \ + --build-arg GOLANG_VERSION=1.21.3 \ --build-arg ETCD_VERSION=3.4.3-0 \ --build-arg K8S_RELEASE=v1.26.0 \ --build-arg RESTY_CLI_VERSION=0.27 \ @@ -75,7 +75,7 @@ build: ensure-buildx --build-arg YAML_LINT_VERSION=1.27.1 \ --build-arg YAMALE_VERSION=4.0.4 \ --build-arg HELM_VERSION=3.11.2 \ - --build-arg GINKGO_VERSION=2.9.0 \ + --build-arg GINKGO_VERSION=2.13.0 \ --build-arg GOLINT_VERSION=latest \ -t ${IMAGE}:${TAG} rootfs diff --git a/images/test-runner/cloudbuild.yaml b/images/test-runner/cloudbuild.yaml index 761ea7f28..b17de9b31 100644 --- a/images/test-runner/cloudbuild.yaml +++ b/images/test-runner/cloudbuild.yaml @@ -6,8 +6,6 @@ steps: entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - - SHORT_SHA=$SHORT_SHA - - BASE_REF=$_PULL_BASE_REF - REGISTRY=gcr.io/k8s-staging-ingress-nginx # default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx # set the home to /root explicitly to if using docker buildx @@ -17,7 +15,3 @@ steps: - | gcloud auth configure-docker \ && cd images/test-runner && make push -substitutions: - _GIT_TAG: "12345" - _PULL_BASE_REF: "master" - _PULL_BASE_SHA: '12345' \ No newline at end of file diff --git a/images/test-runner/rootfs/Dockerfile b/images/test-runner/rootfs/Dockerfile index 0b691a7d2..161b6caf4 100644 --- a/images/test-runner/rootfs/Dockerfile +++ b/images/test-runner/rootfs/Dockerfile @@ -15,7 +15,7 @@ ARG BASE_IMAGE ARG GOLANG_VERSION ARG ETCD_VERSION -FROM golang:${GOLANG_VERSION}-alpine3.17 as GO +FROM golang:${GOLANG_VERSION}-alpine3.18 as GO FROM registry.k8s.io/etcd:${ETCD_VERSION} as etcd FROM ${BASE_IMAGE} @@ -65,7 +65,6 @@ RUN apk update && apk upgrade && apk add --no-cache \ unzip \ openssl \ cfssl@testing \ - curl \ tzdata \ libc6-compat @@ -92,11 +91,11 @@ RUN luarocks install busted \ && luarocks install luacheck RUN wget -qO /usr/local/bin/kubectl \ - https://storage.googleapis.com/kubernetes-release/release/${K8S_RELEASE}/bin/linux/${TARGETARCH}/kubectl \ + https://dl.k8s.io/release/${K8S_RELEASE}/bin/linux/${TARGETARCH}/kubectl \ && chmod +x /usr/local/bin/kubectl RUN wget -qO /usr/local/bin/kube-apiserver \ - https://storage.googleapis.com/kubernetes-release/release/${K8S_RELEASE}/bin/linux/${TARGETARCH}/kube-apiserver \ + https://dl.k8s.io/release/${K8S_RELEASE}/bin/linux/${TARGETARCH}/kube-apiserver \ && chmod +x /usr/local/bin/kube-apiserver RUN wget -qO /tmp/ct-${CHART_TESTING_VERSION}-linux-${TARGETARCH}.tar.gz \ diff --git a/internal/admission/controller/main.go b/internal/admission/controller/main.go index 645f298b4..888818c89 100644 --- a/internal/admission/controller/main.go +++ b/internal/admission/controller/main.go @@ -33,6 +33,7 @@ import ( // contains invalid instructions type Checker interface { CheckIngress(ing *networking.Ingress) error + CheckWarning(ing *networking.Ingress) ([]string, error) } // IngressAdmission implements the AdmissionController interface @@ -41,19 +42,16 @@ type IngressAdmission struct { Checker Checker } -var ( - ingressResource = metav1.GroupVersionKind{ - Group: networking.GroupName, - Version: "v1", - Kind: "Ingress", - } -) +var ingressResource = metav1.GroupVersionKind{ + Group: networking.GroupName, + Version: "v1", + Kind: "Ingress", +} // HandleAdmission populates the admission Response // with Allowed=false if the Object is an ingress that would prevent nginx to reload the configuration // with Allowed=true otherwise func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object, error) { - review, isV1 := obj.(*admissionv1.AdmissionReview) if !isV1 { return nil, fmt.Errorf("request is not of type AdmissionReview v1 or v1beta1") @@ -85,6 +83,15 @@ func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object, return review, nil } + // Adds the warnings regardless of operation being allowed or not + warning, err := ia.Checker.CheckWarning(&ingress) + if err != nil { + klog.ErrorS(err, "failed to get ingress warnings") + } + if len(warning) > 0 { + status.Warnings = warning + } + if err := ia.Checker.CheckIngress(&ingress); err != nil { klog.ErrorS(err, "invalid ingress configuration", "ingress", fmt.Sprintf("%v/%v", review.Request.Namespace, review.Request.Name)) status.Allowed = false diff --git a/internal/admission/controller/main_test.go b/internal/admission/controller/main_test.go index 7cc3cd7b4..1d223cc9e 100644 --- a/internal/admission/controller/main_test.go +++ b/internal/admission/controller/main_test.go @@ -33,11 +33,16 @@ type failTestChecker struct { t *testing.T } -func (ftc failTestChecker) CheckIngress(ing *networking.Ingress) error { +func (ftc failTestChecker) CheckIngress(_ *networking.Ingress) error { ftc.t.Error("checker should not be called") return nil } +func (ftc failTestChecker) CheckWarning(_ *networking.Ingress) ([]string, error) { + ftc.t.Error("checker should not be called") + return nil, nil +} + type testChecker struct { t *testing.T err error @@ -50,12 +55,19 @@ func (tc testChecker) CheckIngress(ing *networking.Ingress) error { return tc.err } +func (tc testChecker) CheckWarning(ing *networking.Ingress) ([]string, error) { + if ing.ObjectMeta.Name != testIngressName { + tc.t.Errorf("CheckWarning should be called with %v ingress, but got %v", testIngressName, ing.ObjectMeta.Name) + } + return nil, tc.err +} + func TestHandleAdmission(t *testing.T) { adm := &IngressAdmission{ Checker: failTestChecker{t: t}, } - result, err := adm.HandleAdmission(&admissionv1.AdmissionReview{ + _, err := adm.HandleAdmission(&admissionv1.AdmissionReview{ Request: &admissionv1.AdmissionRequest{ Kind: v1.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, }, @@ -64,12 +76,12 @@ func TestHandleAdmission(t *testing.T) { t.Fatalf("with a non ingress resource, the check should not pass") } - result, err = adm.HandleAdmission(nil) + _, err = adm.HandleAdmission(nil) if err == nil { t.Fatalf("with a nil AdmissionReview request, the check should not pass") } - result, err = adm.HandleAdmission(&admissionv1.AdmissionReview{ + result, err := adm.HandleAdmission(&admissionv1.AdmissionReview{ Request: &admissionv1.AdmissionRequest{ Kind: v1.GroupVersionKind{Group: networking.GroupName, Version: "v1", Kind: "Ingress"}, Object: runtime.RawExtension{ @@ -102,7 +114,9 @@ func TestHandleAdmission(t *testing.T) { err: fmt.Errorf("this is a test error"), } - adm.HandleAdmission(review) + if _, err := adm.HandleAdmission(review); err != nil { + t.Errorf("unexpected error: %v", err) + } if review.Response.Allowed { t.Fatalf("when the checker returns an error, the request should not be allowed") } @@ -112,7 +126,9 @@ func TestHandleAdmission(t *testing.T) { err: nil, } - adm.HandleAdmission(review) + if _, err := adm.HandleAdmission(review); err != nil { + t.Errorf("unexpected error: %v", err) + } if !review.Response.Allowed { t.Fatalf("when the checker returns no error, the request should be allowed") } diff --git a/internal/admission/controller/server.go b/internal/admission/controller/server.go index 29449de50..7fc61bcbb 100644 --- a/internal/admission/controller/server.go +++ b/internal/admission/controller/server.go @@ -26,12 +26,12 @@ import ( "k8s.io/klog/v2" ) -var ( - scheme = runtime.NewScheme() -) +var scheme = runtime.NewScheme() func init() { - admissionv1.AddToScheme(scheme) + if err := admissionv1.AddToScheme(scheme); err != nil { + klog.ErrorS(err, "Failed to add scheme") + } } // AdmissionController checks if an object diff --git a/internal/ingress/annotations/alias/main.go b/internal/ingress/annotations/alias/main.go index bd2067c9f..7a33e648e 100644 --- a/internal/ingress/annotations/alias/main.go +++ b/internal/ingress/annotations/alias/main.go @@ -27,19 +27,44 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + serverAliasAnnotation = "server-alias" +) + +var aliasAnnotation = parser.Annotation{ + Group: "alias", + Annotations: parser.AnnotationFields{ + serverAliasAnnotation: { + Validator: parser.ValidateArrayOfServerName, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, // High as this allows regex chars + Documentation: `this annotation can be used to define additional server + aliases for this Ingress`, + }, + }, +} + type alias struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new Alias annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return alias{r} + return alias{ + r: r, + annotationConfig: aliasAnnotation, + } +} + +func (a alias) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations } // Parse parses the annotations contained in the ingress rule // used to add an alias to the provided hosts func (a alias) Parse(ing *networking.Ingress) (interface{}, error) { - val, err := parser.GetStringAnnotation("server-alias", ing) + val, err := parser.GetStringAnnotation(serverAliasAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return []string{}, err } @@ -47,7 +72,7 @@ func (a alias) Parse(ing *networking.Ingress) (interface{}, error) { aliases := sets.NewString() for _, alias := range strings.Split(val, ",") { alias = strings.TrimSpace(alias) - if len(alias) == 0 { + if alias == "" { continue } @@ -61,3 +86,8 @@ func (a alias) Parse(ing *networking.Ingress) (interface{}, error) { return l, nil } + +func (a alias) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, aliasAnnotation.Annotations) +} diff --git a/internal/ingress/annotations/alias/main_test.go b/internal/ingress/annotations/alias/main_test.go index 8e6fca447..f1d68c1f7 100644 --- a/internal/ingress/annotations/alias/main_test.go +++ b/internal/ingress/annotations/alias/main_test.go @@ -27,7 +27,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) -var annotation = parser.GetAnnotationWithPrefix("server-alias") +var annotation = parser.GetAnnotationWithPrefix(serverAliasAnnotation) func TestParse(t *testing.T) { ap := NewParser(&resolver.Mock{}) @@ -36,16 +36,20 @@ func TestParse(t *testing.T) { } testCases := []struct { - annotations map[string]string - expected []string + annotations map[string]string + expected []string + skipValidation bool + wantErr bool }{ - {map[string]string{annotation: "a.com, b.com, , c.com"}, []string{"a.com", "b.com", "c.com"}}, - {map[string]string{annotation: "www.example.com"}, []string{"www.example.com"}}, - {map[string]string{annotation: "*.example.com,www.example.*"}, []string{"*.example.com", "www.example.*"}}, - {map[string]string{annotation: `~^www\d+\.example\.com$`}, []string{`~^www\d+\.example\.com$`}}, - {map[string]string{annotation: ""}, []string{}}, - {map[string]string{}, []string{}}, - {nil, []string{}}, + {map[string]string{annotation: "a.com, b.com, , c.com"}, []string{"a.com", "b.com", "c.com"}, false, false}, + {map[string]string{annotation: "www.example.com"}, []string{"www.example.com"}, false, false}, + {map[string]string{annotation: "*.example.com,www.example.*"}, []string{"*.example.com", "www.example.*"}, false, false}, + {map[string]string{annotation: `~^www\d+\.example\.com$`}, []string{`~^www\d+\.example\.com$`}, false, false}, + {map[string]string{annotation: `www.xpto;lala`}, []string{}, false, true}, + {map[string]string{annotation: `www.xpto;lala`}, []string{"www.xpto;lala"}, true, false}, // When we skip validation no error should happen + {map[string]string{annotation: ""}, []string{}, false, true}, + {map[string]string{}, []string{}, false, true}, + {nil, []string{}, false, true}, } ing := &networking.Ingress{ @@ -58,7 +62,16 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) + if testCase.skipValidation { + parser.EnableAnnotationValidation = false + } + t.Cleanup(func() { + parser.EnableAnnotationValidation = true + }) + result, err := ap.Parse(ing) + if (err != nil) != testCase.wantErr { + t.Errorf("ParseAliasAnnotation() annotation: %s, error = %v, wantErr %v", testCase.annotations, err, testCase.wantErr) + } if !reflect.DeepEqual(result, testCase.expected) { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) } diff --git a/internal/ingress/annotations/annotations.go b/internal/ingress/annotations/annotations.go index 5bb2bf5e6..302f0b4b1 100644 --- a/internal/ingress/annotations/annotations.go +++ b/internal/ingress/annotations/annotations.go @@ -17,8 +17,10 @@ limitations under the License. package annotations import ( - "github.com/imdario/mergo" + "dario.cat/mergo" + "k8s.io/ingress-nginx/internal/ingress/annotations/canary" + "k8s.io/ingress-nginx/internal/ingress/annotations/disableproxyintercepterrors" "k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity" "k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry" "k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl" @@ -44,12 +46,11 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/fastcgi" "k8s.io/ingress-nginx/internal/ingress/annotations/globalratelimit" "k8s.io/ingress-nginx/internal/ingress/annotations/http2pushpreload" + "k8s.io/ingress-nginx/internal/ingress/annotations/ipallowlist" "k8s.io/ingress-nginx/internal/ingress/annotations/ipdenylist" - "k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist" "k8s.io/ingress-nginx/internal/ingress/annotations/loadbalancing" "k8s.io/ingress-nginx/internal/ingress/annotations/log" "k8s.io/ingress-nginx/internal/ingress/annotations/mirror" - "k8s.io/ingress-nginx/internal/ingress/annotations/opentracing" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/portinredirect" "k8s.io/ingress-nginx/internal/ingress/annotations/proxy" @@ -75,48 +76,47 @@ const DeniedKeyName = "Denied" // Ingress defines the valid annotations present in one NGINX Ingress rule type Ingress struct { metav1.ObjectMeta - BackendProtocol string - Aliases []string - BasicDigestAuth auth.Config - Canary canary.Config - CertificateAuth authtls.Config - ClientBodyBufferSize string - ConfigurationSnippet string - Connection connection.Config - CorsConfig cors.Config - CustomHTTPErrors []int - DefaultBackend *apiv1.Service - //TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved - FastCGI fastcgi.Config - Denied *string - ExternalAuth authreq.Config - EnableGlobalAuth bool - HTTP2PushPreload bool - Opentracing opentracing.Config - Opentelemetry opentelemetry.Config - Proxy proxy.Config - ProxySSL proxyssl.Config - RateLimit ratelimit.Config - GlobalRateLimit globalratelimit.Config - Redirect redirect.Config - Rewrite rewrite.Config - Satisfy string - ServerSnippet string - ServiceUpstream bool - SessionAffinity sessionaffinity.Config - SSLPassthrough bool - UsePortInRedirects bool - UpstreamHashBy upstreamhashby.Config - LoadBalancing string - UpstreamVhost string - Whitelist ipwhitelist.SourceRange - Denylist ipdenylist.SourceRange - XForwardedPrefix string - SSLCipher sslcipher.Config - Logs log.Config - ModSecurity modsecurity.Config - Mirror mirror.Config - StreamSnippet string + BackendProtocol string + Aliases []string + BasicDigestAuth auth.Config + Canary canary.Config + CertificateAuth authtls.Config + ClientBodyBufferSize string + ConfigurationSnippet string + Connection connection.Config + CorsConfig cors.Config + CustomHTTPErrors []int + DisableProxyInterceptErrors bool + DefaultBackend *apiv1.Service + FastCGI fastcgi.Config + Denied *string + ExternalAuth authreq.Config + EnableGlobalAuth bool + HTTP2PushPreload bool + Opentelemetry opentelemetry.Config + Proxy proxy.Config + ProxySSL proxyssl.Config + RateLimit ratelimit.Config + GlobalRateLimit globalratelimit.Config + Redirect redirect.Config + Rewrite rewrite.Config + Satisfy string + ServerSnippet string + ServiceUpstream bool + SessionAffinity sessionaffinity.Config + SSLPassthrough bool + UsePortInRedirects bool + UpstreamHashBy upstreamhashby.Config + LoadBalancing string + UpstreamVhost string + Denylist ipdenylist.SourceRange + XForwardedPrefix string + SSLCipher sslcipher.Config + Logs log.Config + ModSecurity modsecurity.Config + Mirror mirror.Config + StreamSnippet string + Allowlist ipallowlist.SourceRange } // Extractor defines the annotation parsers to be used in the extraction of annotations @@ -128,61 +128,68 @@ type Extractor struct { func NewAnnotationExtractor(cfg resolver.Resolver) Extractor { return Extractor{ map[string]parser.IngressAnnotation{ - "Aliases": alias.NewParser(cfg), - "BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg), - "Canary": canary.NewParser(cfg), - "CertificateAuth": authtls.NewParser(cfg), - "ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg), - "ConfigurationSnippet": snippet.NewParser(cfg), - "Connection": connection.NewParser(cfg), - "CorsConfig": cors.NewParser(cfg), - "CustomHTTPErrors": customhttperrors.NewParser(cfg), - "DefaultBackend": defaultbackend.NewParser(cfg), - "FastCGI": fastcgi.NewParser(cfg), - "ExternalAuth": authreq.NewParser(cfg), - "EnableGlobalAuth": authreqglobal.NewParser(cfg), - "HTTP2PushPreload": http2pushpreload.NewParser(cfg), - "Opentracing": opentracing.NewParser(cfg), - "Opentelemetry": opentelemetry.NewParser(cfg), - "Proxy": proxy.NewParser(cfg), - "ProxySSL": proxyssl.NewParser(cfg), - "RateLimit": ratelimit.NewParser(cfg), - "GlobalRateLimit": globalratelimit.NewParser(cfg), - "Redirect": redirect.NewParser(cfg), - "Rewrite": rewrite.NewParser(cfg), - "Satisfy": satisfy.NewParser(cfg), - "ServerSnippet": serversnippet.NewParser(cfg), - "ServiceUpstream": serviceupstream.NewParser(cfg), - "SessionAffinity": sessionaffinity.NewParser(cfg), - "SSLPassthrough": sslpassthrough.NewParser(cfg), - "UsePortInRedirects": portinredirect.NewParser(cfg), - "UpstreamHashBy": upstreamhashby.NewParser(cfg), - "LoadBalancing": loadbalancing.NewParser(cfg), - "UpstreamVhost": upstreamvhost.NewParser(cfg), - "Whitelist": ipwhitelist.NewParser(cfg), - "Denylist": ipdenylist.NewParser(cfg), - "XForwardedPrefix": xforwardedprefix.NewParser(cfg), - "SSLCipher": sslcipher.NewParser(cfg), - "Logs": log.NewParser(cfg), - "BackendProtocol": backendprotocol.NewParser(cfg), - "ModSecurity": modsecurity.NewParser(cfg), - "Mirror": mirror.NewParser(cfg), - "StreamSnippet": streamsnippet.NewParser(cfg), + "Aliases": alias.NewParser(cfg), + "BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg), + "Canary": canary.NewParser(cfg), + "CertificateAuth": authtls.NewParser(cfg), + "ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg), + "ConfigurationSnippet": snippet.NewParser(cfg), + "Connection": connection.NewParser(cfg), + "CorsConfig": cors.NewParser(cfg), + "CustomHTTPErrors": customhttperrors.NewParser(cfg), + "DisableProxyInterceptErrors": disableproxyintercepterrors.NewParser(cfg), + "DefaultBackend": defaultbackend.NewParser(cfg), + "FastCGI": fastcgi.NewParser(cfg), + "ExternalAuth": authreq.NewParser(cfg), + "EnableGlobalAuth": authreqglobal.NewParser(cfg), + "HTTP2PushPreload": http2pushpreload.NewParser(cfg), + "Opentelemetry": opentelemetry.NewParser(cfg), + "Proxy": proxy.NewParser(cfg), + "ProxySSL": proxyssl.NewParser(cfg), + "RateLimit": ratelimit.NewParser(cfg), + "GlobalRateLimit": globalratelimit.NewParser(cfg), + "Redirect": redirect.NewParser(cfg), + "Rewrite": rewrite.NewParser(cfg), + "Satisfy": satisfy.NewParser(cfg), + "ServerSnippet": serversnippet.NewParser(cfg), + "ServiceUpstream": serviceupstream.NewParser(cfg), + "SessionAffinity": sessionaffinity.NewParser(cfg), + "SSLPassthrough": sslpassthrough.NewParser(cfg), + "UsePortInRedirects": portinredirect.NewParser(cfg), + "UpstreamHashBy": upstreamhashby.NewParser(cfg), + "LoadBalancing": loadbalancing.NewParser(cfg), + "UpstreamVhost": upstreamvhost.NewParser(cfg), + "Allowlist": ipallowlist.NewParser(cfg), + "Denylist": ipdenylist.NewParser(cfg), + "XForwardedPrefix": xforwardedprefix.NewParser(cfg), + "SSLCipher": sslcipher.NewParser(cfg), + "Logs": log.NewParser(cfg), + "BackendProtocol": backendprotocol.NewParser(cfg), + "ModSecurity": modsecurity.NewParser(cfg), + "Mirror": mirror.NewParser(cfg), + "StreamSnippet": streamsnippet.NewParser(cfg), }, } } // Extract extracts the annotations from an Ingress -func (e Extractor) Extract(ing *networking.Ingress) *Ingress { +func (e Extractor) Extract(ing *networking.Ingress) (*Ingress, error) { pia := &Ingress{ ObjectMeta: ing.ObjectMeta, } data := make(map[string]interface{}) for name, annotationParser := range e.annotations { + if err := annotationParser.Validate(ing.GetAnnotations()); err != nil { + return nil, errors.NewRiskyAnnotations(name) + } val, err := annotationParser.Parse(ing) klog.V(5).InfoS("Parsing Ingress annotation", "name", name, "ingress", klog.KObj(ing), "value", val) if err != nil { + if errors.IsValidationError(err) { + klog.ErrorS(err, "ingress contains invalid annotation value") + return nil, err + } if errors.IsMissingAnnotations(err) { continue } @@ -220,5 +227,5 @@ func (e Extractor) Extract(ing *networking.Ingress) *Ingress { klog.ErrorS(err, "unexpected error merging extracted annotations") } - return pia + return pia, nil } diff --git a/internal/ingress/annotations/annotations_test.go b/internal/ingress/annotations/annotations_test.go index d792801bc..5f8128e0d 100644 --- a/internal/ingress/annotations/annotations_test.go +++ b/internal/ingress/annotations/annotations_test.go @@ -64,7 +64,11 @@ func (m mockCfg) GetService(name string) (*apiv1.Service, error) { } func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { - if secret, _ := m.GetSecret(name); secret != nil { + secret, err := m.GetSecret(name) + if err != nil { + return nil, err + } + if secret != nil { return &resolver.AuthSSLCert{ Secret: name, CAFileName: "/opt/ca.pem", @@ -134,8 +138,11 @@ func TestSSLPassthrough(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Extract(ing).SSLPassthrough - if r != foo.er { + r, err := ec.Extract(ing) + if err != nil { + t.Errorf("Errors should be null: %v", err) + } + if r.SSLPassthrough != foo.er { t.Errorf("Returned %v but expected %v", r, foo.er) } } @@ -158,8 +165,11 @@ func TestUpstreamHashBy(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Extract(ing).UpstreamHashBy.UpstreamHashBy - if r != foo.er { + r, err := ec.Extract(ing) + if err != nil { + t.Errorf("error should be null: %v", err) + } + if r.UpstreamHashBy.UpstreamHashBy != foo.er { t.Errorf("Returned %v but expected %v", r, foo.er) } } @@ -185,7 +195,11 @@ func TestAffinitySession(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Extract(ing).SessionAffinity + rann, err := ec.Extract(ing) + if err != nil { + t.Errorf("error should be null: %v", err) + } + r := rann.SessionAffinity t.Logf("Testing pass %v %v", foo.affinitytype, foo.cookiename) if r.Type != foo.affinitytype { @@ -228,7 +242,11 @@ func TestCors(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Extract(ing).CorsConfig + rann, err := ec.Extract(ing) + if err != nil { + t.Errorf("error should be null: %v", err) + } + r := rann.CorsConfig t.Logf("Testing pass %v %v %v %v %v", foo.corsenabled, foo.methods, foo.headers, foo.origin, foo.credentials) if r.CorsEnabled != foo.corsenabled { @@ -256,9 +274,9 @@ func TestCors(t *testing.T) { if r.CorsAllowCredentials != foo.credentials { t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials) } - } } + func TestCustomHTTPErrors(t *testing.T) { ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() @@ -277,7 +295,11 @@ func TestCustomHTTPErrors(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Extract(ing).CustomHTTPErrors + rann, err := ec.Extract(ing) + if err != nil { + t.Errorf("error should be null: %v", err) + } + r := rann.CustomHTTPErrors // Check that expected codes were created for i := range foo.er { diff --git a/internal/ingress/annotations/auth/main.go b/internal/ingress/annotations/auth/main.go index 58486c6e8..7c7fde7b8 100644 --- a/internal/ingress/annotations/auth/main.go +++ b/internal/ingress/annotations/auth/main.go @@ -32,13 +32,56 @@ import ( "k8s.io/ingress-nginx/pkg/util/file" ) +const ( + authSecretTypeAnnotation = "auth-secret-type" //#nosec G101 + authRealmAnnotation = "auth-realm" + authTypeAnnotation = "auth-type" + // This should be exported as it is imported by other packages + AuthSecretAnnotation = "auth-secret" //#nosec G101 +) + var ( - authTypeRegex = regexp.MustCompile(`basic|digest`) + authTypeRegex = regexp.MustCompile(`basic|digest`) + authSecretTypeRegex = regexp.MustCompile(`auth-file|auth-map`) + // AuthDirectory default directory used to store files // to authenticate request AuthDirectory = "/etc/ingress-controller/auth" ) +var AuthSecretConfig = parser.AnnotationConfig{ + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars + Documentation: `This annotation defines the name of the Secret that contains the usernames and passwords which are granted access to the paths defined in the Ingress rules. `, +} + +var authSecretAnnotations = parser.Annotation{ + Group: "authentication", + Annotations: parser.AnnotationFields{ + AuthSecretAnnotation: AuthSecretConfig, + authSecretTypeAnnotation: { + Validator: parser.ValidateRegex(authSecretTypeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation what is the format of auth-secret value. Can be "auth-file" that defines the content of an htpasswd file, or "auth-map" where each key + is a user and each value is the password.`, + }, + authRealmAnnotation: { + Validator: parser.ValidateRegex(parser.CharsWithSpace, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars + Documentation: `This annotation defines the realm (message) that should be shown to user when authentication is requested.`, + }, + authTypeAnnotation: { + Validator: parser.ValidateRegex(authTypeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines the basic authentication type. Should be "basic" or "digest"`, + }, + }, +} + const ( fileAuth = "auth-file" mapAuth = "auth-map" @@ -85,13 +128,18 @@ func (bd1 *Config) Equal(bd2 *Config) bool { } type auth struct { - r resolver.Resolver - authDirectory string + r resolver.Resolver + authDirectory string + annotationConfig parser.Annotation } // NewParser creates a new authentication annotation parser func NewParser(authDirectory string, r resolver.Resolver) parser.IngressAnnotation { - return auth{r, authDirectory} + return auth{ + r: r, + authDirectory: authDirectory, + annotationConfig: authSecretAnnotations, + } } // Parse parses the annotations contained in the ingress @@ -99,7 +147,7 @@ func NewParser(authDirectory string, r resolver.Resolver) parser.IngressAnnotati // and generated an htpasswd compatible file to be used as source // during the authentication process func (a auth) Parse(ing *networking.Ingress) (interface{}, error) { - at, err := parser.GetStringAnnotation("auth-type", ing) + at, err := parser.GetStringAnnotation(authTypeAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return nil, err } @@ -109,21 +157,24 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) { } var secretType string - secretType, err = parser.GetStringAnnotation("auth-secret-type", ing) + secretType, err = parser.GetStringAnnotation(authSecretTypeAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + return nil, err + } secretType = fileAuth } - s, err := parser.GetStringAnnotation("auth-secret", ing) + s, err := parser.GetStringAnnotation(AuthSecretAnnotation, ing, a.annotationConfig.Annotations) if err != nil { - return nil, ing_errors.LocationDenied{ + return nil, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("error reading secret name from annotation: %w", err), } } sns, sname, err := cache.SplitMetaNamespaceKey(s) if err != nil { - return nil, ing_errors.LocationDenied{ + return nil, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("error reading secret name from annotation: %w", err), } } @@ -131,16 +182,26 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) { if sns == "" { sns = ing.Namespace } + secCfg := a.r.GetSecurityConfiguration() + // We don't accept different namespaces for secrets. + if !secCfg.AllowCrossNamespaceResources && sns != ing.Namespace { + return nil, ing_errors.LocationDeniedError{ + Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"), + } + } name := fmt.Sprintf("%v/%v", sns, sname) secret, err := a.r.GetSecret(name) if err != nil { - return nil, ing_errors.LocationDenied{ + return nil, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("unexpected error reading secret %s: %w", name, err), } } - realm, _ := parser.GetStringAnnotation("auth-realm", ing) + realm, err := parser.GetStringAnnotation(authRealmAnnotation, ing, a.annotationConfig.Annotations) + if ing_errors.IsValidationError(err) { + return nil, err + } passFilename := fmt.Sprintf("%v/%v-%v-%v.passwd", a.authDirectory, ing.GetNamespace(), ing.UID, secret.UID) @@ -156,7 +217,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) { return nil, err } default: - return nil, ing_errors.LocationDenied{ + return nil, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("invalid auth-secret-type in annotation, must be 'auth-file' or 'auth-map': %w", err), } } @@ -177,14 +238,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) { func dumpSecretAuthFile(filename string, secret *api.Secret) error { val, ok := secret.Data["auth"] if !ok { - return ing_errors.LocationDenied{ + return ing_errors.LocationDeniedError{ Reason: fmt.Errorf("the secret %s does not contain a key with value auth", secret.Name), } } err := os.WriteFile(filename, val, file.ReadWriteByUser) if err != nil { - return ing_errors.LocationDenied{ + return ing_errors.LocationDeniedError{ Reason: fmt.Errorf("unexpected error creating password file: %w", err), } } @@ -197,16 +258,25 @@ func dumpSecretAuthMap(filename string, secret *api.Secret) error { for user, pass := range secret.Data { builder.WriteString(user) builder.WriteString(":") - builder.WriteString(string(pass)) + builder.Write(pass) builder.WriteString("\n") } err := os.WriteFile(filename, []byte(builder.String()), file.ReadWriteByUser) if err != nil { - return ing_errors.LocationDenied{ + return ing_errors.LocationDeniedError{ Reason: fmt.Errorf("unexpected error creating password file: %w", err), } } return nil } + +func (a auth) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a auth) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, authSecretAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/auth/main_test.go b/internal/ingress/annotations/auth/main_test.go index d4ec53459..868f31a05 100644 --- a/internal/ingress/annotations/auth/main_test.go +++ b/internal/ingress/annotations/auth/main_test.go @@ -26,11 +26,21 @@ import ( api "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/cache" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +//nolint:gosec // Ignore hardcoded credentials error in testing +const ( + authType = "basic" + authRealm = "-realm-" + defaultDemoSecret = "default/demo-secret" + othernsDemoSecret = "otherns/demo-secret" + demoSecret = "demo-secret" +) + func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ @@ -79,14 +89,19 @@ type mockSecret struct { } func (m mockSecret) GetSecret(name string) (*api.Secret, error) { - if name != "default/demo-secret" { + if name != defaultDemoSecret && name != othernsDemoSecret { return nil, fmt.Errorf("there is no secret with name %v", name) } + ns, _, err := cache.SplitMetaNamespaceKey(name) + if err != nil { + return nil, err + } + return &api.Secret{ ObjectMeta: meta_v1.ObjectMeta{ - Namespace: api.NamespaceDefault, - Name: "demo-secret", + Namespace: ns, + Name: demoSecret, }, Data: map[string][]byte{"auth": []byte("foo:$apr1$OFG3Xybp$ckL0FHDAkoXYIlH9.cysT0")}, }, nil @@ -106,13 +121,91 @@ func TestIngressAuthBadAuthType(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-type")] = "invalid" + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "invalid" ing.SetAnnotations(data) _, dir, _ := dummySecretContent(t) defer os.RemoveAll(dir) - expected := ing_errors.NewLocationDenied("invalid authentication type") + expected := ing_errors.NewValidationError("nginx.ingress.kubernetes.io/auth-type") + _, err := NewParser(dir, &mockSecret{}).Parse(ing) + if err.Error() != expected.Error() { + t.Errorf("expected '%v' but got '%v'", expected, err) + } +} + +func TestIngressInvalidRealm(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "something weird ; location trying to { break }" + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + expected := ing_errors.NewValidationError("nginx.ingress.kubernetes.io/auth-realm") + _, err := NewParser(dir, &mockSecret{}).Parse(ing) + if err.Error() != expected.Error() { + t.Errorf("expected '%v' but got '%v'", expected, err) + } +} + +func TestIngressInvalidDifferentNamespace(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + expected := ing_errors.LocationDeniedError{ + Reason: errors.New("cross namespace usage of secrets is not allowed"), + } + _, err := NewParser(dir, &mockSecret{}).Parse(ing) + if err.Error() != expected.Error() { + t.Errorf("expected '%v' but got '%v'", expected, err) + } +} + +func TestIngressInvalidDifferentNamespaceAllowed(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + r := mockSecret{} + r.AllowCrossNamespace = true + _, err := NewParser(dir, r).Parse(ing) + if err != nil { + t.Errorf("not expecting an error") + } +} + +func TestIngressInvalidSecretName(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret;xpto" + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + expected := ing_errors.LocationDeniedError{ + Reason: errors.New("error reading secret name from annotation: annotation nginx.ingress.kubernetes.io/auth-secret contains invalid value"), + } _, err := NewParser(dir, &mockSecret{}).Parse(ing) if err.Error() != expected.Error() { t.Errorf("expected '%v' but got '%v'", expected, err) @@ -123,13 +216,13 @@ func TestInvalidIngressAuthNoSecret(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-type")] = "basic" + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType ing.SetAnnotations(data) _, dir, _ := dummySecretContent(t) defer os.RemoveAll(dir) - expected := ing_errors.LocationDenied{ + expected := ing_errors.LocationDeniedError{ Reason: errors.New("error reading secret name from annotation: ingress rule without annotations"), } _, err := NewParser(dir, &mockSecret{}).Parse(ing) @@ -142,9 +235,9 @@ func TestIngressAuth(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-type")] = "basic" - data[parser.GetAnnotationWithPrefix("auth-secret")] = "demo-secret" - data[parser.GetAnnotationWithPrefix("auth-realm")] = "-realm-" + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret + data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm ing.SetAnnotations(data) _, dir, _ := dummySecretContent(t) @@ -158,10 +251,10 @@ func TestIngressAuth(t *testing.T) { if !ok { t.Errorf("expected a BasicDigest type") } - if auth.Type != "basic" { + if auth.Type != authType { t.Errorf("Expected basic as auth type but returned %s", auth.Type) } - if auth.Realm != "-realm-" { + if auth.Realm != authRealm { t.Errorf("Expected -realm- as realm but returned %s", auth.Realm) } if !auth.Secured { @@ -173,9 +266,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-type")] = "basic" - data[parser.GetAnnotationWithPrefix("auth-secret")] = "invalid-secret" - data[parser.GetAnnotationWithPrefix("auth-realm")] = "-realm-" + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "invalid-secret" + data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm ing.SetAnnotations(data) _, dir, _ := dummySecretContent(t) @@ -191,10 +284,10 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-type")] = "basic" - data[parser.GetAnnotationWithPrefix("auth-secret")] = "demo-secret" - data[parser.GetAnnotationWithPrefix("auth-secret-type")] = "invalid-type" - data[parser.GetAnnotationWithPrefix("auth-realm")] = "-realm-" + data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType + data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret + data[parser.GetAnnotationWithPrefix(authSecretTypeAnnotation)] = "invalid-type" + data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm ing.SetAnnotations(data) _, dir, _ := dummySecretContent(t) @@ -206,7 +299,7 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) { } } -func dummySecretContent(t *testing.T) (string, string, *api.Secret) { +func dummySecretContent(t *testing.T) (fileName, dir string, s *api.Secret) { dir, err := os.MkdirTemp("", fmt.Sprintf("%v", time.Now().Unix())) if err != nil { t.Error(err) @@ -217,7 +310,10 @@ func dummySecretContent(t *testing.T) (string, string, *api.Secret) { t.Error(err) } defer tmpfile.Close() - s, _ := mockSecret{}.GetSecret("default/demo-secret") + s, err = mockSecret{}.GetSecret(defaultDemoSecret) + if err != nil { + t.Errorf("unexpected error: %v", err) + } return tmpfile.Name(), dir, s } diff --git a/internal/ingress/annotations/authreq/main.go b/internal/ingress/annotations/authreq/main.go index 6a8a4611a..c66b0ed47 100644 --- a/internal/ingress/annotations/authreq/main.go +++ b/internal/ingress/annotations/authreq/main.go @@ -24,6 +24,7 @@ import ( "k8s.io/klog/v2" networking "k8s.io/api/networking/v1" + "k8s.io/client-go/tools/cache" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" @@ -31,6 +32,125 @@ import ( "k8s.io/ingress-nginx/pkg/util/sets" ) +const ( + authReqURLAnnotation = "auth-url" + authReqMethodAnnotation = "auth-method" + authReqSigninAnnotation = "auth-signin" + authReqSigninRedirParamAnnotation = "auth-signin-redirect-param" + authReqSnippetAnnotation = "auth-snippet" + authReqCacheKeyAnnotation = "auth-cache-key" + authReqKeepaliveAnnotation = "auth-keepalive" + authReqKeepaliveShareVarsAnnotation = "auth-keepalive-share-vars" + authReqKeepaliveRequestsAnnotation = "auth-keepalive-requests" + authReqKeepaliveTimeout = "auth-keepalive-timeout" + authReqCacheDuration = "auth-cache-duration" + authReqResponseHeadersAnnotation = "auth-response-headers" + authReqProxySetHeadersAnnotation = "auth-proxy-set-headers" + authReqRequestRedirectAnnotation = "auth-request-redirect" + authReqAlwaysSetCookieAnnotation = "auth-always-set-cookie" + + // This should be exported as it is imported by other packages + AuthSecretAnnotation = "auth-secret" +) + +var authReqAnnotations = parser.Annotation{ + Group: "authentication", + Annotations: parser.AnnotationFields{ + authReqURLAnnotation: { + Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation allows to indicate the URL where the HTTP request should be sent`, + }, + authReqMethodAnnotation: { + Validator: parser.ValidateRegex(methodsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows to specify the HTTP method to use`, + }, + authReqSigninAnnotation: { + Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation allows to specify the location of the error page`, + }, + authReqSigninRedirParamAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows to specify the URL parameter in the error page which should contain the original URL for a failed signin request`, + }, + authReqSnippetAnnotation: { + Validator: parser.ValidateNull, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskCritical, + Documentation: `This annotation allows to specify a custom snippet to use with external authentication`, + }, + authReqCacheKeyAnnotation: { + Validator: parser.ValidateRegex(parser.NGINXVariable, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation enables caching for auth requests.`, + }, + authReqKeepaliveAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation specifies the maximum number of keepalive connections to auth-url. Only takes effect when no variables are used in the host part of the URL`, + }, + authReqKeepaliveShareVarsAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation specifies whether to share Nginx variables among the current request and the auth request`, + }, + authReqKeepaliveRequestsAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines the maximum number of requests that can be served through one keepalive connection`, + }, + authReqKeepaliveTimeout: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation specifies a duration in seconds which an idle keepalive connection to an upstream server will stay open`, + }, + authReqCacheDuration: { + Validator: parser.ValidateRegex(parser.ExtendedCharsRegex, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows to specify a caching time for auth responses based on their response codes, e.g. 200 202 30m`, + }, + authReqResponseHeadersAnnotation: { + Validator: parser.ValidateRegex(parser.HeadersVariable, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation sets the headers to pass to backend once authentication request completes. They should be separated by comma.`, + }, + authReqProxySetHeadersAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation sets the name of a ConfigMap that specifies headers to pass to the authentication service. + Only ConfigMaps on the same namespace are allowed`, + }, + authReqRequestRedirectAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows to specify the X-Auth-Request-Redirect header value`, + }, + authReqAlwaysSetCookieAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables setting a cookie returned by auth request. + By default, the cookie will be set only if an upstream reports with the code 200, 201, 204, 206, 301, 302, 303, 304, 307, or 308`, + }, + }, +} + // Config returns external authentication configuration for an Ingress rule type Config struct { URL string `json:"url"` @@ -45,6 +165,7 @@ type Config struct { AuthCacheKey string `json:"authCacheKey"` AuthCacheDuration []string `json:"authCacheDuration"` KeepaliveConnections int `json:"keepaliveConnections"` + KeepaliveShareVars bool `json:"keepaliveShareVars"` KeepaliveRequests int `json:"keepaliveRequests"` KeepaliveTimeout int `json:"keepaliveTimeout"` ProxySetHeaders map[string]string `json:"proxySetHeaders,omitempty"` @@ -57,6 +178,7 @@ const DefaultCacheDuration = "200 202 401 5m" // fallback values when no keepalive parameters are set const ( defaultKeepaliveConnections = 0 + defaultKeepaliveShareVars = false defaultKeepaliveRequests = 1000 defaultKeepaliveTimeout = 60 ) @@ -105,6 +227,10 @@ func (e1 *Config) Equal(e2 *Config) bool { return false } + if e1.KeepaliveShareVars != e2.KeepaliveShareVars { + return false + } + if e1.KeepaliveRequests != e2.KeepaliveRequests { return false } @@ -121,29 +247,20 @@ func (e1 *Config) Equal(e2 *Config) bool { } var ( - methods = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "CONNECT", "OPTIONS", "TRACE"} + methodsRegex = regexp.MustCompile("(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)") headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`) - statusCodeRegex = regexp.MustCompile(`^[\d]{3}$`) - durationRegex = regexp.MustCompile(`^[\d]+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html + statusCodeRegex = regexp.MustCompile(`^\d{3}$`) + durationRegex = regexp.MustCompile(`^\d+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html ) // ValidMethod checks is the provided string a valid HTTP method func ValidMethod(method string) bool { - if len(method) == 0 { - return false - } - - for _, m := range methods { - if method == m { - return true - } - } - return false + return methodsRegex.MatchString(method) } // ValidHeader checks is the provided string satisfies the header's name regex func ValidHeader(header string) bool { - return headerRegexp.Match([]byte(header)) + return headerRegexp.MatchString(header) } // ValidCacheDuration checks if the provided string is a valid cache duration @@ -156,16 +273,16 @@ func ValidCacheDuration(duration string) bool { seenDuration := false for _, element := range elements { - if len(element) == 0 { + if element == "" { continue } - if statusCodeRegex.Match([]byte(element)) { + if statusCodeRegex.MatchString(element) { if seenDuration { return false // code after duration } continue } - if durationRegex.Match([]byte(element)) { + if durationRegex.MatchString(element) { seenDuration = true } } @@ -173,55 +290,72 @@ func ValidCacheDuration(duration string) bool { } type authReq struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new authentication request annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return authReq{r} + return authReq{ + r: r, + annotationConfig: authReqAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress // rule used to use an Config URL as source for authentication +// +//nolint:gocyclo // Ignore function complexity error func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { // Required Parameters - urlString, err := parser.GetStringAnnotation("auth-url", ing) + urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return nil, err } authURL, err := parser.StringToURL(urlString) if err != nil { - return nil, ing_errors.LocationDenied{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)} + return nil, ing_errors.LocationDeniedError{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)} } - authMethod, _ := parser.GetStringAnnotation("auth-method", ing) - if len(authMethod) != 0 && !ValidMethod(authMethod) { - return nil, ing_errors.NewLocationDenied("invalid HTTP method") + authMethod, err := parser.GetStringAnnotation(authReqMethodAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + if ing_errors.IsValidationError(err) { + return nil, ing_errors.NewLocationDenied("invalid HTTP method") + } } // Optional Parameters - signIn, err := parser.GetStringAnnotation("auth-signin", ing) + signIn, err := parser.GetStringAnnotation(authReqSigninAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("%s value is invalid: %s", authReqSigninAnnotation, err) + } klog.V(3).InfoS("auth-signin annotation is undefined and will not be set") } - signInRedirectParam, err := parser.GetStringAnnotation("auth-signin-redirect-param", ing) + signInRedirectParam, err := parser.GetStringAnnotation(authReqSigninRedirParamAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("%s value is invalid: %s", authReqSigninRedirParamAnnotation, err) + } klog.V(3).Infof("auth-signin-redirect-param annotation is undefined and will not be set") } - authSnippet, err := parser.GetStringAnnotation("auth-snippet", ing) + authSnippet, err := parser.GetStringAnnotation(authReqSnippetAnnotation, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("auth-snippet annotation is undefined and will not be set") } - authCacheKey, err := parser.GetStringAnnotation("auth-cache-key", ing) + authCacheKey, err := parser.GetStringAnnotation(authReqCacheKeyAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("%s value is invalid: %s", authReqCacheKeyAnnotation, err) + } klog.V(3).InfoS("auth-cache-key annotation is undefined and will not be set") } - keepaliveConnections, err := parser.GetIntAnnotation("auth-keepalive", ing) + keepaliveConnections, err := parser.GetIntAnnotation(authReqKeepaliveAnnotation, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("auth-keepalive annotation is undefined and will be set to its default value") keepaliveConnections = defaultKeepaliveConnections @@ -238,9 +372,15 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { } } - keepaliveRequests, err := parser.GetIntAnnotation("auth-keepalive-requests", ing) + keepaliveShareVars, err := parser.GetBoolAnnotation(authReqKeepaliveShareVarsAnnotation, ing, a.annotationConfig.Annotations) if err != nil { - klog.V(3).InfoS("auth-keepalive-requests annotation is undefined and will be set to its default value") + klog.V(3).InfoS("auth-keepalive-share-vars annotation is undefined and will be set to its default value") + keepaliveShareVars = defaultKeepaliveShareVars + } + + keepaliveRequests, err := parser.GetIntAnnotation(authReqKeepaliveRequestsAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + klog.V(3).InfoS("auth-keepalive-requests annotation is undefined or invalid and will be set to its default value") keepaliveRequests = defaultKeepaliveRequests } if keepaliveRequests <= 0 { @@ -248,7 +388,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { keepaliveConnections = 0 } - keepaliveTimeout, err := parser.GetIntAnnotation("auth-keepalive-timeout", ing) + keepaliveTimeout, err := parser.GetIntAnnotation(authReqKeepaliveTimeout, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("auth-keepalive-timeout annotation is undefined and will be set to its default value") keepaliveTimeout = defaultKeepaliveTimeout @@ -258,15 +398,21 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { keepaliveConnections = 0 } - durstr, _ := parser.GetStringAnnotation("auth-cache-duration", ing) + durstr, err := parser.GetStringAnnotation(authReqCacheDuration, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsValidationError(err) { + return nil, fmt.Errorf("%s contains invalid value", authReqCacheDuration) + } authCacheDuration, err := ParseStringToCacheDurations(durstr) if err != nil { return nil, err } responseHeaders := []string{} - hstr, _ := parser.GetStringAnnotation("auth-response-headers", ing) - if len(hstr) != 0 { + hstr, err := parser.GetStringAnnotation(authReqResponseHeadersAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsValidationError(err) { + return nil, ing_errors.NewLocationDenied("validation error") + } + if hstr != "" { harr := strings.Split(hstr, ",") for _, header := range harr { header = strings.TrimSpace(header) @@ -279,9 +425,28 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { } } - proxySetHeaderMap, err := parser.GetStringAnnotation("auth-proxy-set-headers", ing) + proxySetHeaderMap, err := parser.GetStringAnnotation(authReqProxySetHeadersAnnotation, ing, a.annotationConfig.Annotations) if err != nil { - klog.V(3).InfoS("auth-set-proxy-headers annotation is undefined and will not be set") + klog.V(3).InfoS("auth-set-proxy-headers annotation is undefined and will not be set", "err", err) + } + + cns, _, err := cache.SplitMetaNamespaceKey(proxySetHeaderMap) + if err != nil { + return nil, ing_errors.LocationDeniedError{ + Reason: fmt.Errorf("error reading configmap name %s from annotation: %w", proxySetHeaderMap, err), + } + } + + if cns == "" { + cns = ing.Namespace + } + + secCfg := a.r.GetSecurityConfiguration() + // We don't accept different namespaces for secrets. + if !secCfg.AllowCrossNamespaceResources && cns != ing.Namespace { + return nil, ing_errors.LocationDeniedError{ + Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"), + } } var proxySetHeaders map[string]string @@ -301,9 +466,15 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { proxySetHeaders = proxySetHeadersMapContents.Data } - requestRedirect, _ := parser.GetStringAnnotation("auth-request-redirect", ing) + requestRedirect, err := parser.GetStringAnnotation(authReqRequestRedirectAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsValidationError(err) { + return nil, fmt.Errorf("%s is invalid: %w", authReqRequestRedirectAnnotation, err) + } - alwaysSetCookie, _ := parser.GetBoolAnnotation("auth-always-set-cookie", ing) + alwaysSetCookie, err := parser.GetBoolAnnotation(authReqAlwaysSetCookieAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsValidationError(err) { + return nil, fmt.Errorf("%s is invalid: %w", authReqAlwaysSetCookieAnnotation, err) + } return &Config{ URL: urlString, @@ -317,6 +488,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { AuthCacheKey: authCacheKey, AuthCacheDuration: authCacheDuration, KeepaliveConnections: keepaliveConnections, + KeepaliveShareVars: keepaliveShareVars, KeepaliveRequests: keepaliveRequests, KeepaliveTimeout: keepaliveTimeout, ProxySetHeaders: proxySetHeaders, @@ -329,7 +501,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { // It will always return at least one duration (the default duration) func ParseStringToCacheDurations(input string) ([]string, error) { authCacheDuration := []string{} - if len(input) != 0 { + if input != "" { arr := strings.Split(input, ",") for _, duration := range arr { duration = strings.TrimSpace(duration) @@ -348,3 +520,12 @@ func ParseStringToCacheDurations(input string) ([]string, error) { } return authCacheDuration, nil } + +func (a authReq) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a authReq) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, authReqAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/authreq/main_test.go b/internal/ingress/annotations/authreq/main_test.go index e1325235b..c261795c6 100644 --- a/internal/ingress/annotations/authreq/main_test.go +++ b/internal/ingress/annotations/authreq/main_test.go @@ -17,7 +17,6 @@ limitations under the License. package authreq import ( - "fmt" "reflect" "testing" @@ -100,20 +99,20 @@ func TestAnnotations(t *testing.T) { {"no scheme", "bar", "bar", "", "", "", "", "", false, true}, {"invalid host", "http://", "http://", "", "", "", "", "", false, true}, {"invalid host (multiple dots)", "http://foo..bar.com", "http://foo..bar.com", "", "", "", "", "", false, true}, - {"valid URL", "http://bar.foo.com/external-auth", "http://bar.foo.com/external-auth", "", "", "", "", "", false, false}, - {"valid URL - send body", "http://foo.com/external-auth", "http://foo.com/external-auth", "", "POST", "", "", "", false, false}, - {"valid URL - send body", "http://foo.com/external-auth", "http://foo.com/external-auth", "", "GET", "", "", "", false, false}, - {"valid URL - request redirect", "http://foo.com/external-auth", "http://foo.com/external-auth", "", "GET", "http://foo.com/redirect-me", "", "", false, false}, - {"auth snippet", "http://foo.com/external-auth", "http://foo.com/external-auth", "", "", "", "proxy_set_header My-Custom-Header 42;", "", false, false}, - {"auth cache ", "http://foo.com/external-auth", "http://foo.com/external-auth", "", "", "", "", "$foo$bar", false, false}, - {"redirect param", "http://bar.foo.com/external-auth", "http://bar.foo.com/external-auth", "origUrl", "", "", "", "", true, false}, + {"valid URL", "http://bar.foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://bar.foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "", "", "", "", false, false}, + {"valid URL - send body", "http://foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "POST", "", "", "", false, false}, + {"valid URL - send body", "http://foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "GET", "", "", "", false, false}, + {"valid URL - request redirect", "http://foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "GET", "http://foo.com/redirect-me", "", "", false, false}, + {"auth snippet", "http://foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "", "", "proxy_set_header My-Custom-Header 42;", "", false, false}, + {"auth cache ", "http://foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "", "", "", "", "$foo$bar", false, false}, + {"redirect param", "http://bar.foo.com/external-auth/auth?allowed_groups=snow-group,rain-group", "http://bar.foo.com/external-auth/start?rd=https://$host$escaped_request_uri", "origUrl", "", "", "", "", true, false}, } for _, test := range tests { data[parser.GetAnnotationWithPrefix("auth-url")] = test.url data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL data[parser.GetAnnotationWithPrefix("auth-signin-redirect-param")] = test.signinURLRedirectParam - data[parser.GetAnnotationWithPrefix("auth-method")] = fmt.Sprintf("%v", test.method) + data[parser.GetAnnotationWithPrefix("auth-method")] = test.method data[parser.GetAnnotationWithPrefix("auth-request-redirect")] = test.requestRedirect data[parser.GetAnnotationWithPrefix("auth-snippet")] = test.authSnippet data[parser.GetAnnotationWithPrefix("auth-cache-key")] = test.authCacheKey @@ -192,11 +191,13 @@ func TestHeaderAnnotations(t *testing.T) { i, err := NewParser(&resolver.Mock{}).Parse(ing) if test.expErr { if err == nil { - t.Error("expected error but retuned nil") + t.Errorf("%v expected error but retuned nil", test.title) } continue } - + if err != nil { + t.Errorf("no error was expected but %v happened in %s", err, test.title) + } u, ok := i.(*Config) if !ok { t.Errorf("%v: expected an External type", test.title) @@ -266,28 +267,31 @@ func TestKeepaliveAnnotations(t *testing.T) { title string url string keepaliveConnections string + keepaliveShareVars string keepaliveRequests string keepaliveTimeout string expectedConnections int + expectedShareVars bool expectedRequests int expectedTimeout int }{ - {"all set", "http://goog.url", "5", "500", "50", 5, 500, 50}, - {"no annotation", "http://goog.url", "", "", "", defaultKeepaliveConnections, defaultKeepaliveRequests, defaultKeepaliveTimeout}, - {"default for connections", "http://goog.url", "x", "500", "50", defaultKeepaliveConnections, 500, 50}, - {"default for requests", "http://goog.url", "5", "x", "50", 5, defaultKeepaliveRequests, 50}, - {"default for invalid timeout", "http://goog.url", "5", "500", "x", 5, 500, defaultKeepaliveTimeout}, - {"variable in host", "http://$host:5000/a/b", "5", "", "", 0, defaultKeepaliveRequests, defaultKeepaliveTimeout}, - {"variable in path", "http://goog.url:5000/$path", "5", "", "", 5, defaultKeepaliveRequests, defaultKeepaliveTimeout}, - {"negative connections", "http://goog.url", "-2", "", "", 0, defaultKeepaliveRequests, defaultKeepaliveTimeout}, - {"negative requests", "http://goog.url", "5", "-1", "", 0, -1, defaultKeepaliveTimeout}, - {"negative timeout", "http://goog.url", "5", "", "-1", 0, defaultKeepaliveRequests, -1}, - {"negative request and timeout", "http://goog.url", "5", "-2", "-3", 0, -2, -3}, + {"all set", "http://goog.url", "5", "false", "500", "50", 5, false, 500, 50}, + {"no annotation", "http://goog.url", "", "", "", "", defaultKeepaliveConnections, defaultKeepaliveShareVars, defaultKeepaliveRequests, defaultKeepaliveTimeout}, + {"default for connections", "http://goog.url", "x", "true", "500", "50", defaultKeepaliveConnections, true, 500, 50}, + {"default for requests", "http://goog.url", "5", "x", "dummy", "50", 5, defaultKeepaliveShareVars, defaultKeepaliveRequests, 50}, + {"default for invalid timeout", "http://goog.url", "5", "t", "500", "x", 5, true, 500, defaultKeepaliveTimeout}, + {"variable in host", "http://$host:5000/a/b", "5", "1", "", "", 0, true, defaultKeepaliveRequests, defaultKeepaliveTimeout}, + {"variable in path", "http://goog.url:5000/$path", "5", "t", "", "", 5, true, defaultKeepaliveRequests, defaultKeepaliveTimeout}, + {"negative connections", "http://goog.url", "-2", "f", "", "", 0, false, defaultKeepaliveRequests, defaultKeepaliveTimeout}, + {"negative requests", "http://goog.url", "5", "True", "-1", "", 0, true, -1, defaultKeepaliveTimeout}, + {"negative timeout", "http://goog.url", "5", "0", "", "-1", 0, false, defaultKeepaliveRequests, -1}, + {"negative request and timeout", "http://goog.url", "5", "False", "-2", "-3", 0, false, -2, -3}, } for _, test := range tests { data[parser.GetAnnotationWithPrefix("auth-url")] = test.url data[parser.GetAnnotationWithPrefix("auth-keepalive")] = test.keepaliveConnections + data[parser.GetAnnotationWithPrefix("auth-keepalive-share-vars")] = test.keepaliveShareVars data[parser.GetAnnotationWithPrefix("auth-keepalive-timeout")] = test.keepaliveTimeout data[parser.GetAnnotationWithPrefix("auth-keepalive-requests")] = test.keepaliveRequests @@ -311,6 +315,10 @@ func TestKeepaliveAnnotations(t *testing.T) { t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.expectedConnections, u.KeepaliveConnections) } + if u.KeepaliveShareVars != test.expectedShareVars { + t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.expectedShareVars, u.KeepaliveShareVars) + } + if u.KeepaliveRequests != test.expectedRequests { t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.expectedRequests, u.KeepaliveRequests) } @@ -322,7 +330,6 @@ func TestKeepaliveAnnotations(t *testing.T) { } func TestParseStringToCacheDurations(t *testing.T) { - tests := []struct { title string duration string @@ -337,7 +344,6 @@ func TestParseStringToCacheDurations(t *testing.T) { } for _, test := range tests { - dur, err := ParseStringToCacheDurations(test.duration) if test.expErr { if err == nil { diff --git a/internal/ingress/annotations/authreqglobal/main.go b/internal/ingress/annotations/authreqglobal/main.go index 78dd7d6a5..e8a259047 100644 --- a/internal/ingress/annotations/authreqglobal/main.go +++ b/internal/ingress/annotations/authreqglobal/main.go @@ -23,23 +23,51 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + enableGlobalAuthAnnotation = "enable-global-auth" +) + +var globalAuthAnnotations = parser.Annotation{ + Group: "authentication", + Annotations: parser.AnnotationFields{ + enableGlobalAuthAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `Defines if the global external authentication should be enabled.`, + }, + }, +} + type authReqGlobal struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new authentication request annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return authReqGlobal{r} + return authReqGlobal{ + r: r, + annotationConfig: globalAuthAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress // rule used to enable or disable global external authentication func (a authReqGlobal) Parse(ing *networking.Ingress) (interface{}, error) { - - enableGlobalAuth, err := parser.GetBoolAnnotation("enable-global-auth", ing) + enableGlobalAuth, err := parser.GetBoolAnnotation(enableGlobalAuthAnnotation, ing, a.annotationConfig.Annotations) if err != nil { enableGlobalAuth = true } return enableGlobalAuth, nil } + +func (a authReqGlobal) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a authReqGlobal) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, globalAuthAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/authreqglobal/main_test.go b/internal/ingress/annotations/authreqglobal/main_test.go index 0313edcf5..734f97c0f 100644 --- a/internal/ingress/annotations/authreqglobal/main_test.go +++ b/internal/ingress/annotations/authreqglobal/main_test.go @@ -77,7 +77,10 @@ func TestAnnotation(t *testing.T) { data[parser.GetAnnotationWithPrefix("enable-global-auth")] = "false" ing.SetAnnotations(data) - i, _ := NewParser(&resolver.Mock{}).Parse(ing) + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } u, ok := i.(bool) if !ok { t.Errorf("expected a Config type") diff --git a/internal/ingress/annotations/authtls/main.go b/internal/ingress/annotations/authtls/main.go index 2efd6d176..adedb084a 100644 --- a/internal/ingress/annotations/authtls/main.go +++ b/internal/ingress/annotations/authtls/main.go @@ -18,11 +18,10 @@ package authtls import ( "fmt" + "regexp" networking "k8s.io/api/networking/v1" - "regexp" - "k8s.io/ingress-nginx/internal/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" @@ -32,13 +31,64 @@ import ( const ( defaultAuthTLSDepth = 1 defaultAuthVerifyClient = "on" + + annotationAuthTLSSecret = "auth-tls-secret" //#nosec G101 + annotationAuthTLSVerifyClient = "auth-tls-verify-client" + annotationAuthTLSVerifyDepth = "auth-tls-verify-depth" + annotationAuthTLSErrorPage = "auth-tls-error-page" + annotationAuthTLSPassCertToUpstream = "auth-tls-pass-certificate-to-upstream" //#nosec G101 + annotationAuthTLSMatchCN = "auth-tls-match-cn" ) var ( + regexChars = regexp.QuoteMeta(`()|=`) authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`) - commonNameRegex = regexp.MustCompile(`CN=`) + commonNameRegex = regexp.MustCompile(`^CN=[/\-.\_\~a-zA-Z0-9` + regexChars + `]*$`) + redirectRegex = regexp.MustCompile(`^((https?://)?[A-Za-z0-9\-.]*(:\d+)?/[A-Za-z0-9\-.]*)?$`) ) +var authTLSAnnotations = parser.Annotation{ + Group: "authentication", + Annotations: parser.AnnotationFields{ + annotationAuthTLSSecret: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars + Documentation: `This annotation defines the secret that contains the certificate chain of allowed certs`, + }, + annotationAuthTLSVerifyClient: { + Validator: parser.ValidateRegex(authVerifyClientRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars + Documentation: `This annotation enables verification of client certificates. Can be "on", "off", "optional" or "optional_no_ca"`, + }, + annotationAuthTLSVerifyDepth: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines validation depth between the provided client certificate and the Certification Authority chain.`, + }, + annotationAuthTLSErrorPage: { + Validator: parser.ValidateRegex(redirectRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation defines the URL/Page that user should be redirected in case of a Certificate Authentication Error`, + }, + annotationAuthTLSPassCertToUpstream: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`, + }, + annotationAuthTLSMatchCN: { + Validator: parser.ValidateRegex(commonNameRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation adds a sanity check for the CN of the client certificate that is sent over using a string / regex starting with "CN="`, + }, + }, +} + // Config contains the AuthSSLCert used for mutual authentication // and the configured ValidationDepth type Config struct { @@ -79,12 +129,16 @@ func (assl1 *Config) Equal(assl2 *Config) bool { } // NewParser creates a new TLS authentication annotation parser -func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { - return authTLS{resolver} +func NewParser(r resolver.Resolver) parser.IngressAnnotation { + return authTLS{ + r: r, + annotationConfig: authTLSAnnotations, + } } type authTLS struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Parse parses the annotations contained in the ingress @@ -93,47 +147,75 @@ func (a authTLS) Parse(ing *networking.Ingress) (interface{}, error) { var err error config := &Config{} - tlsauthsecret, err := parser.GetStringAnnotation("auth-tls-secret", ing) + tlsauthsecret, err := parser.GetStringAnnotation(annotationAuthTLSSecret, ing, a.annotationConfig.Annotations) if err != nil { return &Config{}, err } - _, _, err = k8s.ParseNameNS(tlsauthsecret) + ns, _, err := k8s.ParseNameNS(tlsauthsecret) if err != nil { return &Config{}, ing_errors.NewLocationDenied(err.Error()) } + if ns == "" { + ns = ing.Namespace + } + secCfg := a.r.GetSecurityConfiguration() + // We don't accept different namespaces for secrets. + if !secCfg.AllowCrossNamespaceResources && ns != ing.Namespace { + return &Config{}, ing_errors.NewLocationDenied("cross namespace secrets are not supported") + } authCert, err := a.r.GetAuthCertificate(tlsauthsecret) if err != nil { e := fmt.Errorf("error obtaining certificate: %w", err) - return &Config{}, ing_errors.LocationDenied{Reason: e} + return &Config{}, ing_errors.LocationDeniedError{Reason: e} } config.AuthSSLCert = *authCert - config.VerifyClient, err = parser.GetStringAnnotation("auth-tls-verify-client", ing) + config.VerifyClient, err = parser.GetStringAnnotation(annotationAuthTLSVerifyClient, ing, a.annotationConfig.Annotations) + // We can set a default value here in case of validation error if err != nil || !authVerifyClientRegex.MatchString(config.VerifyClient) { config.VerifyClient = defaultAuthVerifyClient } - config.ValidationDepth, err = parser.GetIntAnnotation("auth-tls-verify-depth", ing) + config.ValidationDepth, err = parser.GetIntAnnotation(annotationAuthTLSVerifyDepth, ing, a.annotationConfig.Annotations) + // We can set a default value here in case of validation error if err != nil || config.ValidationDepth == 0 { config.ValidationDepth = defaultAuthTLSDepth } - config.ErrorPage, err = parser.GetStringAnnotation("auth-tls-error-page", ing) + config.ErrorPage, err = parser.GetStringAnnotation(annotationAuthTLSErrorPage, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + return &Config{}, err + } config.ErrorPage = "" } - config.PassCertToUpstream, err = parser.GetBoolAnnotation("auth-tls-pass-certificate-to-upstream", ing) + config.PassCertToUpstream, err = parser.GetBoolAnnotation(annotationAuthTLSPassCertToUpstream, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + return &Config{}, err + } config.PassCertToUpstream = false } - config.MatchCN, err = parser.GetStringAnnotation("auth-tls-match-cn", ing) - if err != nil || !commonNameRegex.MatchString(config.MatchCN) { + config.MatchCN, err = parser.GetStringAnnotation(annotationAuthTLSMatchCN, ing, a.annotationConfig.Annotations) + if err != nil { + if ing_errors.IsValidationError(err) { + return &Config{}, err + } config.MatchCN = "" } return config, nil } + +func (a authTLS) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a authTLS) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, authTLSAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/authtls/main_test.go b/internal/ingress/annotations/authtls/main_test.go index 569f3865b..0dd442e4f 100644 --- a/internal/ingress/annotations/authtls/main_test.go +++ b/internal/ingress/annotations/authtls/main_test.go @@ -27,6 +27,11 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + defaultDemoSecret = "default/demo-secret" + off = "off" +) + func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ @@ -77,23 +82,22 @@ type mockSecret struct { // GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { - if name != "default/demo-secret" { + if name != defaultDemoSecret { return nil, errors.Errorf("there is no secret with name %v", name) } return &resolver.AuthSSLCert{ - Secret: "default/demo-secret", + Secret: defaultDemoSecret, CAFileName: "/ssl/ca.crt", CASHA: "abc", }, nil - } func TestAnnotations(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("auth-tls-secret")] = "default/demo-secret" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = defaultDemoSecret ing.SetAnnotations(data) @@ -108,7 +112,7 @@ func TestAnnotations(t *testing.T) { t.Errorf("expected *Config but got %v", u) } - secret, err := fakeSecret.GetAuthCertificate("default/demo-secret") + secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret) if err != nil { t.Errorf("unexpected error getting secret %v", err) } @@ -132,11 +136,11 @@ func TestAnnotations(t *testing.T) { t.Errorf("expected empty string, but got %v", u.MatchCN) } - data[parser.GetAnnotationWithPrefix("auth-tls-verify-client")] = "off" - data[parser.GetAnnotationWithPrefix("auth-tls-verify-depth")] = "2" - data[parser.GetAnnotationWithPrefix("auth-tls-error-page")] = "ok.com/error" - data[parser.GetAnnotationWithPrefix("auth-tls-pass-certificate-to-upstream")] = "true" - data[parser.GetAnnotationWithPrefix("auth-tls-match-cn")] = "CN=hello-app" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = off + data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "CN=(hello-app|ok|goodbye)" ing.SetAnnotations(data) @@ -153,8 +157,8 @@ func TestAnnotations(t *testing.T) { if u.AuthSSLCert.Secret != secret.Secret { t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret) } - if u.VerifyClient != "off" { - t.Errorf("expected %v but got %v", "off", u.VerifyClient) + if u.VerifyClient != off { + t.Errorf("expected %v but got %v", off, u.VerifyClient) } if u.ValidationDepth != 2 { t.Errorf("expected %v but got %v", 2, u.ValidationDepth) @@ -165,8 +169,8 @@ func TestAnnotations(t *testing.T) { if u.PassCertToUpstream != true { t.Errorf("expected %v but got %v", true, u.PassCertToUpstream) } - if u.MatchCN != "CN=hello-app" { - t.Errorf("expected %v but got %v", "CN=hello-app", u.MatchCN) + if u.MatchCN != "CN=(hello-app|ok|goodbye)" { + t.Errorf("expected %v but got %v", "CN=(hello-app|ok|goodbye)", u.MatchCN) } } @@ -182,15 +186,24 @@ func TestInvalidAnnotations(t *testing.T) { } // Invalid NameSpace - data[parser.GetAnnotationWithPrefix("auth-tls-secret")] = "demo-secret" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "demo-secret" ing.SetAnnotations(data) _, err = NewParser(fakeSecret).Parse(ing) if err == nil { t.Errorf("Expected error with ingress but got nil") } + // Invalid Cross NameSpace + data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "nondefault/demo-secret" + ing.SetAnnotations(data) + _, err = NewParser(fakeSecret).Parse(ing) + expErr := errors.NewLocationDenied("cross namespace secrets are not supported") + if err.Error() != expErr.Error() { + t.Errorf("received error is different from cross namespace error: %s Expected %s", err, expErr) + } + // Invalid Auth Certificate - data[parser.GetAnnotationWithPrefix("auth-tls-secret")] = "default/invalid-demo-secret" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "default/invalid-demo-secret" ing.SetAnnotations(data) _, err = NewParser(fakeSecret).Parse(ing) if err == nil { @@ -198,11 +211,38 @@ func TestInvalidAnnotations(t *testing.T) { } // Invalid optional Annotations - data[parser.GetAnnotationWithPrefix("auth-tls-secret")] = "default/demo-secret" - data[parser.GetAnnotationWithPrefix("auth-tls-verify-client")] = "w00t" - data[parser.GetAnnotationWithPrefix("auth-tls-verify-depth")] = "abcd" - data[parser.GetAnnotationWithPrefix("auth-tls-pass-certificate-to-upstream")] = "nahh" - data[parser.GetAnnotationWithPrefix("auth-tls-match-cn")] = "" + data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "default/demo-secret" + + data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = "w00t" + ing.SetAnnotations(data) + _, err = NewParser(fakeSecret).Parse(ing) + if err != nil { + t.Errorf("Error should be nil and verify client should be defaulted") + } + + data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "abcd" + ing.SetAnnotations(data) + _, err = NewParser(fakeSecret).Parse(ing) + if err != nil { + t.Errorf("Error should be nil and verify depth should be defaulted") + } + + data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "nahh" + ing.SetAnnotations(data) + _, err = NewParser(fakeSecret).Parse(ing) + if err == nil { + t.Errorf("Expected error with ingress but got nil") + } + delete(data, parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)) + + data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "" + ing.SetAnnotations(data) + _, err = NewParser(fakeSecret).Parse(ing) + if err == nil { + t.Errorf("Expected error with ingress CN but got nil") + } + delete(data, parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)) + ing.SetAnnotations(data) i, err := NewParser(fakeSecret).Parse(ing) @@ -226,28 +266,21 @@ func TestInvalidAnnotations(t *testing.T) { if u.MatchCN != "" { t.Errorf("expected empty string but got %v", u.MatchCN) } - } func TestEquals(t *testing.T) { cfg1 := &Config{} cfg2 := &Config{} - // Same config - result := cfg1.Equal(cfg1) - if result != true { - t.Errorf("Expected true") - } - // compare nil - result = cfg1.Equal(nil) + result := cfg1.Equal(nil) if result != false { t.Errorf("Expected false") } // Different Certs sslCert1 := resolver.AuthSSLCert{ - Secret: "default/demo-secret", + Secret: defaultDemoSecret, CAFileName: "/ssl/ca.crt", CASHA: "abc", } @@ -266,7 +299,7 @@ func TestEquals(t *testing.T) { // Different Verify Client cfg1.VerifyClient = "on" - cfg2.VerifyClient = "off" + cfg2.VerifyClient = off result = cfg1.Equal(cfg2) if result != false { t.Errorf("Expected false") diff --git a/internal/ingress/annotations/backendprotocol/main.go b/internal/ingress/annotations/backendprotocol/main.go index d8ea72386..62674c4e5 100644 --- a/internal/ingress/annotations/backendprotocol/main.go +++ b/internal/ingress/annotations/backendprotocol/main.go @@ -17,49 +17,70 @@ limitations under the License. package backendprotocol import ( - "regexp" - "strings" - networking "k8s.io/api/networking/v1" "k8s.io/klog/v2" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) -// HTTP protocol -const HTTP = "HTTP" +var validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"} -var ( - validProtocols = regexp.MustCompile(`^(AUTO_HTTP|HTTP|HTTPS|AJP|GRPC|GRPCS|FCGI)$`) +const ( + http = "HTTP" + backendProtocolAnnotation = "backend-protocol" ) +var backendProtocolConfig = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + backendProtocolAnnotation: { + Validator: parser.ValidateOptions(validProtocols, false, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `this annotation can be used to define which protocol should + be used to communicate with backends`, + }, + }, +} + type backendProtocol struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new backend protocol annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return backendProtocol{r} + return backendProtocol{ + r: r, + annotationConfig: backendProtocolConfig, + } +} + +func (a backendProtocol) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations } // ParseAnnotations parses the annotations contained in the ingress // rule used to indicate the backend protocol. func (a backendProtocol) Parse(ing *networking.Ingress) (interface{}, error) { if ing.GetAnnotations() == nil { - return HTTP, nil + return http, nil } - proto, err := parser.GetStringAnnotation("backend-protocol", ing) + proto, err := parser.GetStringAnnotation(backendProtocolAnnotation, ing, a.annotationConfig.Annotations) if err != nil { - return HTTP, nil - } - - proto = strings.TrimSpace(strings.ToUpper(proto)) - if !validProtocols.MatchString(proto) { - klog.Warningf("Protocol %v is not a valid value for the backend-protocol annotation. Using HTTP as protocol", proto) - return HTTP, nil + if errors.IsValidationError(err) { + klog.Warningf("validation error %s. Using HTTP as protocol", err) + } + return http, nil } return proto, nil } + +func (a backendProtocol) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, backendProtocolConfig.Annotations) +} diff --git a/internal/ingress/annotations/backendprotocol/main_test.go b/internal/ingress/annotations/backendprotocol/main_test.go index e8c018998..cb1ec779f 100644 --- a/internal/ingress/annotations/backendprotocol/main_test.go +++ b/internal/ingress/annotations/backendprotocol/main_test.go @@ -44,6 +44,7 @@ func buildIngress() *networking.Ingress { }, } } + func TestParseInvalidAnnotations(t *testing.T) { ing := buildIngress() @@ -56,7 +57,7 @@ func TestParseInvalidAnnotations(t *testing.T) { if !ok { t.Errorf("expected a string type") } - if val != "HTTP" { + if val != http { t.Errorf("expected HTTPS but %v returned", val) } @@ -72,12 +73,12 @@ func TestParseInvalidAnnotations(t *testing.T) { if !ok { t.Errorf("expected a string type") } - if val != "HTTP" { + if val != http { t.Errorf("expected HTTPS but %v returned", val) } // Test invalid annotation set - data[parser.GetAnnotationWithPrefix("backend-protocol")] = "INVALID" + data[parser.GetAnnotationWithPrefix(backendProtocolAnnotation)] = "INVALID" ing.SetAnnotations(data) i, err = NewParser(&resolver.Mock{}).Parse(ing) @@ -88,7 +89,7 @@ func TestParseInvalidAnnotations(t *testing.T) { if !ok { t.Errorf("expected a string type") } - if val != "HTTP" { + if val != http { t.Errorf("expected HTTPS but %v returned", val) } } @@ -97,7 +98,7 @@ func TestParseAnnotations(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("backend-protocol")] = "HTTPS" + data[parser.GetAnnotationWithPrefix(backendProtocolAnnotation)] = " HTTPS " ing.SetAnnotations(data) i, err := NewParser(&resolver.Mock{}).Parse(ing) diff --git a/internal/ingress/annotations/canary/main.go b/internal/ingress/annotations/canary/main.go index d9e53b3b8..be5761675 100644 --- a/internal/ingress/annotations/canary/main.go +++ b/internal/ingress/annotations/canary/main.go @@ -18,14 +18,82 @@ package canary import ( networking "k8s.io/api/networking/v1" + "k8s.io/klog/v2" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + canaryAnnotation = "canary" + canaryWeightAnnotation = "canary-weight" + canaryWeightTotalAnnotation = "canary-weight-total" + canaryByHeaderAnnotation = "canary-by-header" + canaryByHeaderValueAnnotation = "canary-by-header-value" + canaryByHeaderPatternAnnotation = "canary-by-header-pattern" + canaryByCookieAnnotation = "canary-by-cookie" +) + +var CanaryAnnotations = parser.Annotation{ + Group: "canary", + Annotations: parser.AnnotationFields{ + canaryAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables the Ingress spec to act as an alternative service for requests to route to depending on the rules applied`, + }, + canaryWeightAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines the integer based (0 - ) percent of random requests that should be routed to the service specified in the canary Ingress`, + }, + canaryWeightTotalAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`, + }, + canaryByHeaderAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the header that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress. + When the request header is set to 'always', it will be routed to the canary. When the header is set to 'never', it will never be routed to the canary. + For any other value, the header will be ignored and the request compared against the other canary rules by precedence`, + }, + canaryByHeaderValueAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the header value to match for notifying the Ingress to route the request to the service specified in the Canary Ingress. + When the request header is set to this value, it will be routed to the canary. For any other header value, the header will be ignored and the request compared against the other canary rules by precedence. + This annotation has to be used together with 'canary-by-header'. The annotation is an extension of the 'canary-by-header' to allow customizing the header value instead of using hardcoded values. + It doesn't have any effect if the 'canary-by-header' annotation is not defined`, + }, + canaryByHeaderPatternAnnotation: { + Validator: parser.ValidateRegex(parser.IsValidRegex, false), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation works the same way as canary-by-header-value except it does PCRE Regex matching. + Note that when 'canary-by-header-value' is set this annotation will be ignored. + When the given Regex causes error during request processing, the request will be considered as not matching.`, + }, + canaryByCookieAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the cookie that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress. + When the cookie is set to 'always', it will be routed to the canary. When the cookie is set to 'never', it will never be routed to the canary`, + }, + }, +} + type canary struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config returns the configuration rules for setting up the Canary @@ -41,7 +109,10 @@ type Config struct { // NewParser parses the ingress for canary related annotations func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return canary{r} + return canary{ + r: r, + annotationConfig: CanaryAnnotations, + } } // Parse parses the annotations contained in the ingress @@ -50,45 +121,75 @@ func (c canary) Parse(ing *networking.Ingress) (interface{}, error) { config := &Config{} var err error - config.Enabled, err = parser.GetBoolAnnotation("canary", ing) + config.Enabled, err = parser.GetBoolAnnotation(canaryAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to 'false'", canaryAnnotation) + } config.Enabled = false } - config.Weight, err = parser.GetIntAnnotation("canary-weight", ing) + config.Weight, err = parser.GetIntAnnotation(canaryWeightAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to '0'", canaryWeightAnnotation) + } config.Weight = 0 } - config.WeightTotal, err = parser.GetIntAnnotation("canary-weight-total", ing) + config.WeightTotal, err = parser.GetIntAnnotation(canaryWeightTotalAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to '100'", canaryWeightTotalAnnotation) + } config.WeightTotal = 100 } - config.Header, err = parser.GetStringAnnotation("canary-by-header", ing) + config.Header, err = parser.GetStringAnnotation(canaryByHeaderAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderAnnotation) + } config.Header = "" } - config.HeaderValue, err = parser.GetStringAnnotation("canary-by-header-value", ing) + config.HeaderValue, err = parser.GetStringAnnotation(canaryByHeaderValueAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderValueAnnotation) + } config.HeaderValue = "" } - config.HeaderPattern, err = parser.GetStringAnnotation("canary-by-header-pattern", ing) + config.HeaderPattern, err = parser.GetStringAnnotation(canaryByHeaderPatternAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderPatternAnnotation) + } config.HeaderPattern = "" } - config.Cookie, err = parser.GetStringAnnotation("canary-by-cookie", ing) + config.Cookie, err = parser.GetStringAnnotation(canaryByCookieAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%s is invalid, defaulting to ''", canaryByCookieAnnotation) + } config.Cookie = "" } if !config.Enabled && (config.Weight > 0 || len(config.Header) > 0 || len(config.HeaderValue) > 0 || len(config.Cookie) > 0 || len(config.HeaderPattern) > 0) { - return nil, errors.NewInvalidAnnotationConfiguration("canary", "configured but not enabled") + return nil, errors.NewInvalidAnnotationConfiguration(canaryAnnotation, "configured but not enabled") } return config, nil } + +func (c canary) GetDocumentation() parser.AnnotationFields { + return c.annotationConfig.Annotations +} + +func (c canary) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/canary/main_test.go b/internal/ingress/annotations/canary/main_test.go index ddfc0a9c4..1787ddb85 100644 --- a/internal/ingress/annotations/canary/main_test.go +++ b/internal/ingress/annotations/canary/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package canary import ( + "strconv" "testing" api "k8s.io/api/core/v1" @@ -24,8 +25,6 @@ import ( metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" - "strconv" - "k8s.io/ingress-nginx/internal/ingress/resolver" ) @@ -93,7 +92,6 @@ func TestCanaryInvalid(t *testing.T) { if val.Weight != 0 { t.Errorf("Expected %v but got %v", 0, val.Weight) } - } func TestAnnotations(t *testing.T) { @@ -133,10 +131,9 @@ func TestAnnotations(t *testing.T) { } continue - } else { - if err != nil { - t.Errorf("%v: expected nil but returned error %v", test.title, err) - } + } + if err != nil { + t.Errorf("%v: expected nil but returned error %v", test.title, err) } canaryConfig, ok := i.(*Config) diff --git a/internal/ingress/annotations/clientbodybuffersize/main.go b/internal/ingress/annotations/clientbodybuffersize/main.go index 9020ee594..c0fa79713 100644 --- a/internal/ingress/annotations/clientbodybuffersize/main.go +++ b/internal/ingress/annotations/clientbodybuffersize/main.go @@ -23,17 +23,49 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + clientBodyBufferSizeAnnotation = "client-body-buffer-size" +) + +var clientBodyBufferSizeConfig = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + clientBodyBufferSizeAnnotation: { + Validator: parser.ValidateRegex(parser.SizeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Sets buffer size for reading client request body per location. + In case the request body is larger than the buffer, the whole body or only its part is written to a temporary file. + By default, buffer size is equal to two memory pages. This is 8K on x86, other 32-bit platforms, and x86-64. + It is usually 16K on other 64-bit platforms. This annotation is applied to each location provided in the ingress rule.`, + }, + }, +} + type clientBodyBufferSize struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new clientBodyBufferSize annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return clientBodyBufferSize{r} + return clientBodyBufferSize{ + r: r, + annotationConfig: clientBodyBufferSizeConfig, + } +} + +func (cbbs clientBodyBufferSize) GetDocumentation() parser.AnnotationFields { + return cbbs.annotationConfig.Annotations } // Parse parses the annotations contained in the ingress rule // used to add an client-body-buffer-size to the provided locations func (cbbs clientBodyBufferSize) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("client-body-buffer-size", ing) + return parser.GetStringAnnotation(clientBodyBufferSizeAnnotation, ing, cbbs.annotationConfig.Annotations) +} + +func (cbbs clientBodyBufferSize) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(cbbs.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, clientBodyBufferSizeConfig.Annotations) } diff --git a/internal/ingress/annotations/clientbodybuffersize/main_test.go b/internal/ingress/annotations/clientbodybuffersize/main_test.go index 9932f8314..62257aeb9 100644 --- a/internal/ingress/annotations/clientbodybuffersize/main_test.go +++ b/internal/ingress/annotations/clientbodybuffersize/main_test.go @@ -39,6 +39,9 @@ func TestParse(t *testing.T) { }{ {map[string]string{annotation: "8k"}, "8k"}, {map[string]string{annotation: "16k"}, "16k"}, + {map[string]string{annotation: "10000"}, "10000"}, + {map[string]string{annotation: "16R"}, ""}, + {map[string]string{annotation: "16kkk"}, ""}, {map[string]string{annotation: ""}, ""}, {map[string]string{}, ""}, {nil, ""}, @@ -54,6 +57,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/annotations/connection/main.go b/internal/ingress/annotations/connection/main.go index e9b0c1865..b0242ac23 100644 --- a/internal/ingress/annotations/connection/main.go +++ b/internal/ingress/annotations/connection/main.go @@ -17,12 +17,32 @@ limitations under the License. package connection import ( + "regexp" + networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + connectionProxyHeaderAnnotation = "connection-proxy-header" +) + +var validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`) + +var connectionHeadersAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + connectionProxyHeaderAnnotation: { + Validator: parser.ValidateRegex(validConnectionHeaderValue, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows setting a specific value for "proxy_set_header Connection" directive. Right now it is restricted to "close" or "keep-alive"`, + }, + }, +} + // Config returns the connection header configuration for an Ingress rule type Config struct { Header string `json:"header"` @@ -30,18 +50,22 @@ type Config struct { } type connection struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new port in redirect annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return connection{r} + return connection{ + r: r, + annotationConfig: connectionHeadersAnnotations, + } } // Parse parses the annotations contained in the ingress // rule used to indicate if the connection header should be overridden. func (a connection) Parse(ing *networking.Ingress) (interface{}, error) { - cp, err := parser.GetStringAnnotation("connection-proxy-header", ing) + cp, err := parser.GetStringAnnotation(connectionProxyHeaderAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return &Config{ Enabled: false, @@ -70,3 +94,12 @@ func (r1 *Config) Equal(r2 *Config) bool { return true } + +func (a connection) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a connection) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, connectionHeadersAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/connection/main_test.go b/internal/ingress/annotations/connection/main_test.go index 011a2948c..1b9361f31 100644 --- a/internal/ingress/annotations/connection/main_test.go +++ b/internal/ingress/annotations/connection/main_test.go @@ -37,10 +37,12 @@ func TestParse(t *testing.T) { testCases := []struct { annotations map[string]string expected *Config + expectErr bool }{ - {map[string]string{annotation: "keep-alive"}, &Config{Enabled: true, Header: "keep-alive"}}, - {map[string]string{}, &Config{Enabled: false}}, - {nil, &Config{Enabled: false}}, + {map[string]string{annotation: "keep-alive"}, &Config{Enabled: true, Header: "keep-alive"}, false}, + {map[string]string{annotation: "not-allowed-value"}, &Config{Enabled: false}, true}, + {map[string]string{}, &Config{Enabled: false}, true}, + {nil, &Config{Enabled: false}, true}, } ing := &networking.Ingress{ @@ -53,9 +55,14 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - i, _ := ap.Parse(ing) - p, _ := i.(*Config) - + i, err := ap.Parse(ing) + if (err != nil) != testCase.expectErr { + t.Fatalf("expected error: %t got error: %t err value: %s. %+v", testCase.expectErr, err != nil, err, testCase.annotations) + } + p, ok := i.(*Config) + if !ok { + t.Fatalf("expected a Config type") + } if !p.Equal(testCase.expected) { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, p, testCase.annotations) } diff --git a/internal/ingress/annotations/cors/main.go b/internal/ingress/annotations/cors/main.go index 3888f2909..d6e92b34d 100644 --- a/internal/ingress/annotations/cors/main.go +++ b/internal/ingress/annotations/cors/main.go @@ -24,6 +24,7 @@ import ( "k8s.io/klog/v2" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) @@ -38,20 +39,87 @@ var ( // Regex are defined here to prevent information leak, if user tries to set anything not valid // that could cause the Response to contain some internal value/variable (like returning $pid, $upstream_addr, etc) // Origin must contain a http/s Origin (including or not the port) or the value '*' - corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?|\*)?$`) + // This Regex is composed of the following: + // * Sets a group that can be (https?://)?*?.something.com:port? + // * Allows this to be repeated as much as possible, and separated by comma + // Otherwise it should be '*' + corsOriginRegexValidator = regexp.MustCompile(`^((((https?://)?(\*\.)?[A-Za-z0-9\-.]*(:\d+)?,?)+)|\*)?$`) + // corsOriginRegex defines the regex for validation inside Parse + corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-.]*(:\d+)?|\*)?$`) // Method must contain valid methods list (PUT, GET, POST, BLA) // May contain or not spaces between each verb corsMethodsRegex = regexp.MustCompile(`^([A-Za-z]+,?\s?)+$`) - // Headers must contain valid values only (X-HEADER12, X-ABC) - // May contain or not spaces between each Header - corsHeadersRegex = regexp.MustCompile(`^([A-Za-z0-9\-\_]+,?\s?)+$`) // Expose Headers must contain valid values only (*, X-HEADER12, X-ABC) // May contain or not spaces between each Header corsExposeHeadersRegex = regexp.MustCompile(`^(([A-Za-z0-9\-\_]+|\*),?\s?)+$`) ) +const ( + corsEnableAnnotation = "enable-cors" + corsAllowOriginAnnotation = "cors-allow-origin" + corsAllowHeadersAnnotation = "cors-allow-headers" + corsAllowMethodsAnnotation = "cors-allow-methods" + corsAllowCredentialsAnnotation = "cors-allow-credentials" //#nosec G101 + corsExposeHeadersAnnotation = "cors-expose-headers" + corsMaxAgeAnnotation = "cors-max-age" +) + +var corsAnnotation = parser.Annotation{ + Group: "cors", + Annotations: parser.AnnotationFields{ + corsEnableAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables Cross-Origin Resource Sharing (CORS) in an Ingress rule`, + }, + corsAllowOriginAnnotation: { + Validator: parser.ValidateRegex(corsOriginRegexValidator, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation controls what's the accepted Origin for CORS. + This is a multi-valued field, separated by ','. It must follow this format: http(s)://origin-site.com or http(s)://origin-site.com:port + It also supports single level wildcard subdomains and follows this format: http(s)://*.foo.bar, http(s)://*.bar.foo:8080 or http(s)://*.abc.bar.foo:9000`, + }, + corsAllowHeadersAnnotation: { + Validator: parser.ValidateRegex(parser.HeadersVariable, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation controls which headers are accepted. + This is a multi-valued field, separated by ',' and accepts letters, numbers, _ and -`, + }, + corsAllowMethodsAnnotation: { + Validator: parser.ValidateRegex(corsMethodsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation controls which methods are accepted. + This is a multi-valued field, separated by ',' and accepts only letters (upper and lower case)`, + }, + corsAllowCredentialsAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation controls if credentials can be passed during CORS operations.`, + }, + corsExposeHeadersAnnotation: { + Validator: parser.ValidateRegex(corsExposeHeadersRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation controls which headers are exposed to response. + This is a multi-valued field, separated by ',' and accepts letters, numbers, _, - and *.`, + }, + corsMaxAgeAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation controls how long, in seconds, preflight requests can be cached.`, + }, + }, +} + type cors struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config contains the Cors configuration to be used in the Ingress @@ -67,7 +135,10 @@ type Config struct { // NewParser creates a new CORS annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return cors{r} + return cors{ + r: r, + annotationConfig: corsAnnotation, + } } // Equal tests for equality between two External types @@ -116,17 +187,24 @@ func (c cors) Parse(ing *networking.Ingress) (interface{}, error) { var err error config := &Config{} - config.CorsEnabled, err = parser.GetBoolAnnotation("enable-cors", ing) + config.CorsEnabled, err = parser.GetBoolAnnotation(corsEnableAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("enable-cors is invalid, defaulting to 'false'") + } config.CorsEnabled = false } config.CorsAllowOrigin = []string{} - unparsedOrigins, err := parser.GetStringAnnotation("cors-allow-origin", ing) + unparsedOrigins, err := parser.GetStringAnnotation(corsAllowOriginAnnotation, ing, c.annotationConfig.Annotations) if err == nil { origins := strings.Split(unparsedOrigins, ",") for _, origin := range origins { origin = strings.TrimSpace(origin) + if origin == "" { + continue + } + if origin == "*" { config.CorsAllowOrigin = []string{"*"} break @@ -140,33 +218,53 @@ func (c cors) Parse(ing *networking.Ingress) (interface{}, error) { klog.Infof("Current config.corsAllowOrigin %v", config.CorsAllowOrigin) } } else { + if errors.IsValidationError(err) { + klog.Warningf("cors-allow-origin is invalid, defaulting to '*'") + } config.CorsAllowOrigin = []string{"*"} } - config.CorsAllowHeaders, err = parser.GetStringAnnotation("cors-allow-headers", ing) - if err != nil || !corsHeadersRegex.MatchString(config.CorsAllowHeaders) { + config.CorsAllowHeaders, err = parser.GetStringAnnotation(corsAllowHeadersAnnotation, ing, c.annotationConfig.Annotations) + if err != nil || !parser.HeadersVariable.MatchString(config.CorsAllowHeaders) { config.CorsAllowHeaders = defaultCorsHeaders } - config.CorsAllowMethods, err = parser.GetStringAnnotation("cors-allow-methods", ing) + config.CorsAllowMethods, err = parser.GetStringAnnotation(corsAllowMethodsAnnotation, ing, c.annotationConfig.Annotations) if err != nil || !corsMethodsRegex.MatchString(config.CorsAllowMethods) { config.CorsAllowMethods = defaultCorsMethods } - config.CorsAllowCredentials, err = parser.GetBoolAnnotation("cors-allow-credentials", ing) + config.CorsAllowCredentials, err = parser.GetBoolAnnotation(corsAllowCredentialsAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + if errors.IsValidationError(err) { + klog.Warningf("cors-allow-credentials is invalid, defaulting to 'true'") + } + } config.CorsAllowCredentials = true } - config.CorsExposeHeaders, err = parser.GetStringAnnotation("cors-expose-headers", ing) + config.CorsExposeHeaders, err = parser.GetStringAnnotation(corsExposeHeadersAnnotation, ing, c.annotationConfig.Annotations) if err != nil || !corsExposeHeadersRegex.MatchString(config.CorsExposeHeaders) { config.CorsExposeHeaders = "" } - config.CorsMaxAge, err = parser.GetIntAnnotation("cors-max-age", ing) + config.CorsMaxAge, err = parser.GetIntAnnotation(corsMaxAgeAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("cors-max-age is invalid, defaulting to %d", defaultCorsMaxAge) + } config.CorsMaxAge = defaultCorsMaxAge } return config, nil } + +func (c cors) GetDocumentation() parser.AnnotationFields { + return c.annotationConfig.Annotations +} + +func (c cors) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, corsAnnotation.Annotations) +} diff --git a/internal/ingress/annotations/cors/main_test.go b/internal/ingress/annotations/cors/main_test.go index 086a59d89..a69390a17 100644 --- a/internal/ingress/annotations/cors/main_test.go +++ b/internal/ingress/annotations/cors/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package cors import ( + "reflect" "testing" api "k8s.io/api/core/v1" @@ -75,13 +76,13 @@ func TestIngressCorsConfigValid(t *testing.T) { data := map[string]string{} // Valid - data[parser.GetAnnotationWithPrefix("enable-cors")] = "true" - data[parser.GetAnnotationWithPrefix("cors-allow-headers")] = "DNT,X-CustomHeader, Keep-Alive,User-Agent" - data[parser.GetAnnotationWithPrefix("cors-allow-credentials")] = "false" - data[parser.GetAnnotationWithPrefix("cors-allow-methods")] = "GET, PATCH" - data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "https://origin123.test.com:4443" - data[parser.GetAnnotationWithPrefix("cors-expose-headers")] = "*, X-CustomResponseHeader" - data[parser.GetAnnotationWithPrefix("cors-max-age")] = "600" + data[parser.GetAnnotationWithPrefix(corsEnableAnnotation)] = "true" + data[parser.GetAnnotationWithPrefix(corsAllowHeadersAnnotation)] = "DNT,X-CustomHeader, Keep-Alive,User-Agent" + data[parser.GetAnnotationWithPrefix(corsAllowCredentialsAnnotation)] = "false" + data[parser.GetAnnotationWithPrefix(corsAllowMethodsAnnotation)] = "GET, PATCH" + data[parser.GetAnnotationWithPrefix(corsAllowOriginAnnotation)] = "https://origin123.test.com:4443" + data[parser.GetAnnotationWithPrefix(corsExposeHeadersAnnotation)] = "*, X-CustomResponseHeader" + data[parser.GetAnnotationWithPrefix(corsMaxAgeAnnotation)] = "600" ing.SetAnnotations(data) corst, err := NewParser(&resolver.Mock{}).Parse(ing) @@ -95,31 +96,31 @@ func TestIngressCorsConfigValid(t *testing.T) { } if !nginxCors.CorsEnabled { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("enable-cors")], nginxCors.CorsEnabled) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsEnableAnnotation)], nginxCors.CorsEnabled) } if nginxCors.CorsAllowCredentials { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-credentials")], nginxCors.CorsAllowCredentials) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsAllowCredentialsAnnotation)], nginxCors.CorsAllowCredentials) } if nginxCors.CorsAllowHeaders != "DNT,X-CustomHeader, Keep-Alive,User-Agent" { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-headers")], nginxCors.CorsAllowHeaders) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsAllowHeadersAnnotation)], nginxCors.CorsAllowHeaders) } if nginxCors.CorsAllowMethods != "GET, PATCH" { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-methods")], nginxCors.CorsAllowMethods) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsAllowMethodsAnnotation)], nginxCors.CorsAllowMethods) } if nginxCors.CorsAllowOrigin[0] != "https://origin123.test.com:4443" { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-origin")], nginxCors.CorsAllowOrigin) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsAllowOriginAnnotation)], nginxCors.CorsAllowOrigin) } if nginxCors.CorsExposeHeaders != "*, X-CustomResponseHeader" { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-expose-headers")], nginxCors.CorsExposeHeaders) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsExposeHeadersAnnotation)], nginxCors.CorsExposeHeaders) } if nginxCors.CorsMaxAge != 600 { - t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-max-age")], nginxCors.CorsMaxAge) + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsMaxAgeAnnotation)], nginxCors.CorsMaxAge) } } @@ -129,13 +130,13 @@ func TestIngressCorsConfigInvalid(t *testing.T) { data := map[string]string{} // Valid - data[parser.GetAnnotationWithPrefix("enable-cors")] = "yes" - data[parser.GetAnnotationWithPrefix("cors-allow-headers")] = "@alright, #ingress" - data[parser.GetAnnotationWithPrefix("cors-allow-credentials")] = "no" - data[parser.GetAnnotationWithPrefix("cors-allow-methods")] = "GET, PATCH, $nginx" - data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "origin123.test.com:4443" - data[parser.GetAnnotationWithPrefix("cors-expose-headers")] = "@alright, #ingress" - data[parser.GetAnnotationWithPrefix("cors-max-age")] = "abcd" + data[parser.GetAnnotationWithPrefix(corsEnableAnnotation)] = "yes" + data[parser.GetAnnotationWithPrefix(corsAllowHeadersAnnotation)] = "@alright, #ingress" + data[parser.GetAnnotationWithPrefix(corsAllowCredentialsAnnotation)] = "no" + data[parser.GetAnnotationWithPrefix(corsAllowMethodsAnnotation)] = "GET, PATCH, $nginx" + data[parser.GetAnnotationWithPrefix(corsAllowOriginAnnotation)] = "origin123.test.com:4443" + data[parser.GetAnnotationWithPrefix(corsExposeHeadersAnnotation)] = "@alright, #ingress" + data[parser.GetAnnotationWithPrefix(corsMaxAgeAnnotation)] = "abcd" ing.SetAnnotations(data) corst, err := NewParser(&resolver.Mock{}).Parse(ing) @@ -172,3 +173,33 @@ func TestIngressCorsConfigInvalid(t *testing.T) { t.Errorf("expected %v but returned %v", defaultCorsMaxAge, nginxCors.CorsMaxAge) } } + +func TestIngresCorsConfigAllowOriginWithTrailingComma(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(corsEnableAnnotation)] = "true" + + // Include a trailing comma and an empty value between the commas. + data[parser.GetAnnotationWithPrefix(corsAllowOriginAnnotation)] = "https://origin123.test.com:4443, ,https://origin321.test.com:4443," + ing.SetAnnotations(data) + + corst, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("error parsing annotations: %v", err) + } + + nginxCors, ok := corst.(*Config) + if !ok { + t.Errorf("expected a Config type but returned %t", corst) + } + + if !nginxCors.CorsEnabled { + t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix(corsEnableAnnotation)], nginxCors.CorsEnabled) + } + + expectedCorsAllowOrigins := []string{"https://origin123.test.com:4443", "https://origin321.test.com:4443"} + if !reflect.DeepEqual(nginxCors.CorsAllowOrigin, expectedCorsAllowOrigins) { + t.Errorf("expected %v but returned %v", expectedCorsAllowOrigins, nginxCors.CorsAllowOrigin) + } +} diff --git a/internal/ingress/annotations/customhttperrors/main.go b/internal/ingress/annotations/customhttperrors/main.go index a05fb16c8..f3c72a22f 100644 --- a/internal/ingress/annotations/customhttperrors/main.go +++ b/internal/ingress/annotations/customhttperrors/main.go @@ -17,6 +17,7 @@ limitations under the License. package customhttperrors import ( + "regexp" "strconv" "strings" @@ -26,25 +27,50 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + customHTTPErrorsAnnotation = "custom-http-errors" +) + +// We accept anything between 400 and 599, on a comma separated. +var arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5]\d{2},?)*$`) + +var customHTTPErrorsAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + customHTTPErrorsAnnotation: { + Validator: parser.ValidateRegex(arrayOfHTTPErrors, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `If a default backend annotation is specified on the ingress, the errors code specified on this annotation + will be routed to that annotation's default backend service. Otherwise they will be routed to the global default backend. + A comma-separated list of error codes is accepted (anything between 400 and 599, like 403, 503)`, + }, + }, +} + type customhttperrors struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new custom http errors annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return customhttperrors{r} + return customhttperrors{ + r: r, + annotationConfig: customHTTPErrorsAnnotations, + } } // Parse parses the annotations contained in the ingress to use // custom http errors func (e customhttperrors) Parse(ing *networking.Ingress) (interface{}, error) { - c, err := parser.GetStringAnnotation("custom-http-errors", ing) + c, err := parser.GetStringAnnotation(customHTTPErrorsAnnotation, ing, e.annotationConfig.Annotations) if err != nil { return nil, err } cSplit := strings.Split(c, ",") - var codes []int + codes := make([]int, 0, len(cSplit)) for _, i := range cSplit { num, err := strconv.Atoi(i) if err != nil { @@ -55,3 +81,12 @@ func (e customhttperrors) Parse(ing *networking.Ingress) (interface{}, error) { return codes, nil } + +func (e customhttperrors) GetDocumentation() parser.AnnotationFields { + return e.annotationConfig.Annotations +} + +func (e customhttperrors) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(e.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, customHTTPErrorsAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/defaultbackend/main.go b/internal/ingress/annotations/defaultbackend/main.go index b1685015e..9ae44f052 100644 --- a/internal/ingress/annotations/defaultbackend/main.go +++ b/internal/ingress/annotations/defaultbackend/main.go @@ -25,28 +25,58 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + defaultBackendAnnotation = "default-backend" +) + +var defaultBackendAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + defaultBackendAnnotation: { + Validator: parser.ValidateServiceName, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This service will be used to handle the response when the configured service in the Ingress rule does not have any active endpoints. + It will also be used to handle the error responses if both this annotation and the custom-http-errors annotation are set.`, + }, + }, +} + type backend struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new default backend annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return backend{r} + return backend{ + r: r, + annotationConfig: defaultBackendAnnotations, + } } // Parse parses the annotations contained in the ingress to use // a custom default backend -func (db backend) Parse(ing *networking.Ingress) (interface{}, error) { - s, err := parser.GetStringAnnotation("default-backend", ing) +func (b backend) Parse(ing *networking.Ingress) (interface{}, error) { + s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, b.annotationConfig.Annotations) if err != nil { return nil, err } name := fmt.Sprintf("%v/%v", ing.Namespace, s) - svc, err := db.r.GetService(name) + svc, err := b.r.GetService(name) if err != nil { return nil, fmt.Errorf("unexpected error reading service %s: %w", name, err) } return svc, nil } + +func (b backend) GetDocumentation() parser.AnnotationFields { + return b.annotationConfig.Annotations +} + +func (b backend) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(b.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, defaultBackendAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/defaultbackend/main_test.go b/internal/ingress/annotations/defaultbackend/main_test.go index ec23d32c2..214d07803 100644 --- a/internal/ingress/annotations/defaultbackend/main_test.go +++ b/internal/ingress/annotations/defaultbackend/main_test.go @@ -91,21 +91,51 @@ func (m mockService) GetService(name string) (*api.Service, error) { func TestAnnotations(t *testing.T) { ing := buildIngress() - data := map[string]string{} - data[parser.GetAnnotationWithPrefix("default-backend")] = "demo-service" - ing.SetAnnotations(data) - - fakeService := &mockService{} - i, err := NewParser(fakeService).Parse(ing) - if err != nil { - t.Errorf("unexpected error %v", err) + tests := map[string]struct { + expectErr bool + serviceName string + }{ + "valid name": { + serviceName: "demo-service", + expectErr: false, + }, + "not in backend": { + serviceName: "demo1-service", + expectErr: true, + }, + "invalid dns name": { + serviceName: "demo-service.something.tld", + expectErr: true, + }, + "invalid name": { + serviceName: "something/xpto", + expectErr: true, + }, + "invalid characters": { + serviceName: "something;xpto", + expectErr: true, + }, } - svc, ok := i.(*api.Service) - if !ok { - t.Errorf("expected *api.Service but got %v", svc) - } - if svc.Name != "demo-service" { - t.Errorf("expected %v but got %v", "demo-service", svc.Name) + for _, test := range tests { + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(defaultBackendAnnotation)] = test.serviceName + ing.SetAnnotations(data) + + fakeService := &mockService{} + i, err := NewParser(fakeService).Parse(ing) + if (err != nil) != test.expectErr { + t.Errorf("expected error: %t got error: %t err value: %s. %+v", test.expectErr, err != nil, err, i) + } + + if !test.expectErr { + svc, ok := i.(*api.Service) + if !ok { + t.Errorf("expected *api.Service but got %v", svc) + } + if svc.Name != test.serviceName { + t.Errorf("expected %v but got %v", test.serviceName, svc.Name) + } + } } } diff --git a/internal/ingress/annotations/disableproxyintercepterrors/main.go b/internal/ingress/annotations/disableproxyintercepterrors/main.go new file mode 100644 index 000000000..650d29707 --- /dev/null +++ b/internal/ingress/annotations/disableproxyintercepterrors/main.go @@ -0,0 +1,76 @@ +/* +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 disableproxyintercepterrors + +import ( + networking "k8s.io/api/networking/v1" + + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" + "k8s.io/ingress-nginx/internal/ingress/resolver" +) + +const ( + disableProxyInterceptErrorsAnnotation = "disable-proxy-intercept-errors" +) + +var disableProxyInterceptErrorsAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + disableProxyInterceptErrorsAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows to disable NGINX proxy-intercept-errors when custom-http-errors are set. + If a default backend annotation is specified on the ingress, the errors will be routed to that annotation's default backend service (instead of the global default backend). + Different ingresses can specify different sets of errors codes and there are UseCases where NGINX shall not intercept all errors returned from upstream.`, + }, + }, +} + +type disableProxyInterceptErrors struct { + r resolver.Resolver + annotationConfig parser.Annotation +} + +func (pie disableProxyInterceptErrors) GetDocumentation() parser.AnnotationFields { + return pie.annotationConfig.Annotations +} + +func (pie disableProxyInterceptErrors) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(pie.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, disableProxyInterceptErrorsAnnotations.Annotations) +} + +// NewParser creates a new disableProxyInterceptErrors annotation parser +func NewParser(r resolver.Resolver) parser.IngressAnnotation { + return disableProxyInterceptErrors{ + r: r, + annotationConfig: disableProxyInterceptErrorsAnnotations, + } +} + +func (pie disableProxyInterceptErrors) Parse(ing *networking.Ingress) (interface{}, error) { + val, err := parser.GetBoolAnnotation(disableProxyInterceptErrorsAnnotation, ing, pie.annotationConfig.Annotations) + + // A missing annotation is not a problem, just use the default + if err == errors.ErrMissingAnnotations { + return false, nil // default is false + } + + return val, nil +} diff --git a/internal/ingress/annotations/disableproxyintercepterrors/main_test.go b/internal/ingress/annotations/disableproxyintercepterrors/main_test.go new file mode 100644 index 000000000..1238bb4e0 --- /dev/null +++ b/internal/ingress/annotations/disableproxyintercepterrors/main_test.go @@ -0,0 +1,64 @@ +/* +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 disableproxyintercepterrors + +import ( + "testing" + + api "k8s.io/api/core/v1" + networking "k8s.io/api/networking/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/resolver" +) + +func buildIngress() *networking.Ingress { + return &networking.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: networking.IngressSpec{ + DefaultBackend: &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "default-backend", + Port: networking.ServiceBackendPort{ + Number: 80, + }, + }, + }, + }, + } +} + +func TestParseAnnotations(t *testing.T) { + ing := buildIngress() + + _, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("disable-proxy-intercept-errors")] = "true" + ing.SetAnnotations(data) + // test ingress using the annotation without a TLS section + _, err = NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing ingress with disable-proxy-intercept-errors") + } +} diff --git a/internal/ingress/annotations/fastcgi/main.go b/internal/ingress/annotations/fastcgi/main.go index 84bac4109..9e7ab3c61 100644 --- a/internal/ingress/annotations/fastcgi/main.go +++ b/internal/ingress/annotations/fastcgi/main.go @@ -19,17 +19,50 @@ package fastcgi import ( "fmt" "reflect" + "regexp" networking "k8s.io/api/networking/v1" "k8s.io/client-go/tools/cache" + "k8s.io/klog/v2" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + fastCGIIndexAnnotation = "fastcgi-index" + fastCGIParamsAnnotation = "fastcgi-params-configmap" //#nosec G101 +) + +// fast-cgi valid parameters is just a single file name (like index.php) +var ( + regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9.\-\_]+$`) + validFCGIValue = regexp.MustCompile(`^[A-Za-z0-9\-\_\$\{\}/.]*$`) +) + +var fastCGIAnnotations = parser.Annotation{ + Group: "fastcgi", + Annotations: parser.AnnotationFields{ + fastCGIIndexAnnotation: { + Validator: parser.ValidateRegex(regexValidIndexAnnotationAndKey, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation can be used to specify an index file`, + }, + fastCGIParamsAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation can be used to specify a ConfigMap containing the fastcgi parameters as a key/value. + Only ConfigMaps on the same namespace of ingress can be used. They key and value from ConfigMap are validated for unauthorized characters.`, + }, + }, +} + type fastcgi struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config describes the per location fastcgi config @@ -57,50 +90,78 @@ func (l1 *Config) Equal(l2 *Config) bool { // NewParser creates a new fastcgiConfig protocol annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return fastcgi{r} + return fastcgi{ + r: r, + annotationConfig: fastCGIAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress // rule used to indicate the fastcgiConfig. func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) { - fcgiConfig := Config{} if ing.GetAnnotations() == nil { return fcgiConfig, nil } - index, err := parser.GetStringAnnotation("fastcgi-index", ing) + index, err := parser.GetStringAnnotation(fastCGIIndexAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + return fcgiConfig, err + } index = "" } + fcgiConfig.Index = index - cm, err := parser.GetStringAnnotation("fastcgi-params-configmap", ing) + cm, err := parser.GetStringAnnotation(fastCGIParamsAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + return fcgiConfig, err + } return fcgiConfig, nil } cmns, cmn, err := cache.SplitMetaNamespaceKey(cm) if err != nil { - return fcgiConfig, ing_errors.LocationDenied{ + return fcgiConfig, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("error reading configmap name from annotation: %w", err), } } + secCfg := a.r.GetSecurityConfiguration() - if cmns != "" && cmns != ing.Namespace { + // We don't accept different namespaces for secrets. + if cmns != "" && !secCfg.AllowCrossNamespaceResources && cmns != ing.Namespace { return fcgiConfig, fmt.Errorf("different namespace is not supported on fast_cgi param configmap") } cm = fmt.Sprintf("%v/%v", ing.Namespace, cmn) cmap, err := a.r.GetConfigMap(cm) if err != nil { - return fcgiConfig, ing_errors.LocationDenied{ + return fcgiConfig, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err), } } + for k, v := range cmap.Data { + if !regexValidIndexAnnotationAndKey.MatchString(k) || !validFCGIValue.MatchString(v) { + klog.ErrorS(fmt.Errorf("fcgi contains invalid key or value"), "fcgi annotation error", "configmap", cmap.Name, "namespace", cmap.Namespace, "key", k, "value", v) + return fcgiConfig, ing_errors.NewValidationError(fastCGIParamsAnnotation) + } + } + + fcgiConfig.Index = index fcgiConfig.Params = cmap.Data return fcgiConfig, nil } + +func (a fastcgi) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a fastcgi) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, fastCGIAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/fastcgi/main_test.go b/internal/ingress/annotations/fastcgi/main_test.go index 35c5bbc12..f0bbc9d65 100644 --- a/internal/ingress/annotations/fastcgi/main_test.go +++ b/internal/ingress/annotations/fastcgi/main_test.go @@ -18,6 +18,7 @@ package fastcgi import ( "fmt" + "reflect" "testing" api "k8s.io/api/core/v1" @@ -49,10 +50,16 @@ func buildIngress() *networking.Ingress { type mockConfigMap struct { resolver.Mock + extraConfigMap map[string]map[string]string } func (m mockConfigMap) GetConfigMap(name string) (*api.ConfigMap, error) { - if name != "default/demo-configmap" && name != "otherns/demo-configmap" { + if m.extraConfigMap == nil { + m.extraConfigMap = make(map[string]map[string]string) + } + cmdata, ok := m.extraConfigMap[name] + + if name != "default/demo-configmap" && name != "otherns/demo-configmap" && !ok { return nil, fmt.Errorf("there is no configmap with name %v", name) } @@ -61,12 +68,17 @@ func (m mockConfigMap) GetConfigMap(name string) (*api.ConfigMap, error) { return nil, fmt.Errorf("invalid configmap name") } + data := map[string]string{"REDIRECT_STATUS": "200", "SERVER_NAME": "$server_name"} + if ok { + data = cmdata + } + return &api.ConfigMap{ ObjectMeta: meta_v1.ObjectMeta{ Namespace: cmns, Name: cmn, }, - Data: map[string]string{"REDIRECT_STATUS": "200", "SERVER_NAME": "$server_name"}, + Data: data, }, nil } @@ -143,7 +155,6 @@ func TestParseFastCGIInvalidParamsConfigMapAnnotation(t *testing.T) { invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"} for _, configmap := range invalidConfigMapList { - data := map[string]string{} data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap ing.SetAnnotations(data) @@ -227,11 +238,9 @@ func TestParseFastCGIParamsConfigMapAnnotationWithDifferentNS(t *testing.T) { if err == nil { t.Errorf("Different namespace configmap should return an error") } - } func TestConfigEquality(t *testing.T) { - var nilConfig *Config config := Config{ @@ -283,3 +292,121 @@ func TestConfigEquality(t *testing.T) { t.Errorf("config4 should be equal to config") } } + +func Test_fastcgi_Parse(t *testing.T) { + tests := []struct { + name string + index string + configmapname string + configmap map[string]string + want interface{} + wantErr bool + }{ + { + name: "valid configuration", + index: "indexxpto-92123.php", + configmapname: "default/fcgiconfig", + configmap: map[string]string{ + "REQUEST_METHOD": "$request_method", + "SCRIPT_FILENAME": "$document_root$fastcgi_script_name", + }, + want: Config{ + Index: "indexxpto-92123.php", + Params: map[string]string{ + "REQUEST_METHOD": "$request_method", + "SCRIPT_FILENAME": "$document_root$fastcgi_script_name", + }, + }, + }, + { + name: "invalid index name", + index: "indexxpto-92123$xx.php", + configmapname: "default/fcgiconfig", + configmap: map[string]string{ + "REQUEST_METHOD": "$request_method", + "SCRIPT_FILENAME": "$document_root$fastcgi_script_name", + }, + want: Config{}, + wantErr: true, + }, + { + name: "invalid configmap namespace", + index: "indexxpto-92123.php", + configmapname: "otherns/fcgiconfig", + configmap: map[string]string{ + "REQUEST_METHOD": "$request_method", + "SCRIPT_FILENAME": "$document_root$fastcgi_script_name", + }, + want: Config{Index: "indexxpto-92123.php"}, + wantErr: true, + }, + { + name: "invalid configmap namespace name", + index: "indexxpto-92123.php", + configmapname: "otherns/fcgicon;{fig", + configmap: map[string]string{ + "REQUEST_METHOD": "$request_method", + "SCRIPT_FILENAME": "$document_root$fastcgi_script_name", + }, + want: Config{Index: "indexxpto-92123.php"}, + wantErr: true, + }, + { + name: "invalid configmap values key", + index: "indexxpto-92123.php", + configmapname: "default/fcgiconfig", + configmap: map[string]string{ + "REQUEST_METHOD$XPTO": "$request_method", + }, + want: Config{Index: "indexxpto-92123.php"}, + wantErr: true, + }, + { + name: "invalid configmap values val", + index: "indexxpto-92123.php", + configmapname: "default/fcgiconfig", + configmap: map[string]string{ + "REQUEST_METHOD_XPTO": "$request_method{test};a", + }, + want: Config{Index: "indexxpto-92123.php"}, + wantErr: true, + }, + { + name: "invalid configmap values val", + index: "indexxpto-92123.php", + configmapname: "default/fcgiconfig", + configmap: map[string]string{ + "SCRIPT_FILENAME": "/app/src/index.php", + }, + want: Config{Index: "indexxpto-92123.php", Params: map[string]string{ + "SCRIPT_FILENAME": "/app/src/index.php", + }}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("fastcgi-index")] = tt.index + data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = tt.configmapname + ing.SetAnnotations(data) + + m := &mockConfigMap{ + extraConfigMap: map[string]map[string]string{ + tt.configmapname: tt.configmap, + }, + } + + got, err := NewParser(m).Parse(ing) + if (err != nil) != tt.wantErr { + t.Errorf("fastcgi.Parse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("fastcgi.Parse() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/ingress/annotations/globalratelimit/main.go b/internal/ingress/annotations/globalratelimit/main.go index ea9fc4678..0aec29f66 100644 --- a/internal/ingress/annotations/globalratelimit/main.go +++ b/internal/ingress/annotations/globalratelimit/main.go @@ -22,6 +22,7 @@ import ( "time" networking "k8s.io/api/networking/v1" + "k8s.io/klog/v2" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" @@ -32,6 +33,46 @@ import ( const defaultKey = "$remote_addr" +const ( + globalRateLimitAnnotation = "global-rate-limit" + globalRateLimitWindowAnnotation = "global-rate-limit-window" + globalRateLimitKeyAnnotation = "global-rate-limit-key" + globalRateLimitIgnoredCidrsAnnotation = "global-rate-limit-ignored-cidrs" +) + +var globalRateLimitAnnotationConfig = parser.Annotation{ + Group: "ratelimit", + Annotations: parser.AnnotationFields{ + globalRateLimitAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation configures maximum allowed number of requests per window`, + }, + globalRateLimitWindowAnnotation: { + Validator: parser.ValidateDuration, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `Configures a time window (i.e 1m) that the limit is applied`, + }, + globalRateLimitKeyAnnotation: { + Validator: parser.ValidateRegex(parser.NGINXVariable, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation Configures a key for counting the samples. Defaults to $remote_addr. + You can also combine multiple NGINX variables here, like ${remote_addr}-${http_x_api_client} which would mean the limit will be applied to + requests coming from the same API client (indicated by X-API-Client HTTP request header) with the same source IP address`, + }, + globalRateLimitIgnoredCidrsAnnotation: { + Validator: parser.ValidateCIDRs, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines a comma separated list of IPs and CIDRs to match client IP against. + When there's a match request is not considered for rate limiting.`, + }, + }, +} + // Config encapsulates all global rate limit attributes type Config struct { Namespace string `json:"namespace"` @@ -63,12 +104,16 @@ func (l *Config) Equal(r *Config) bool { } type globalratelimit struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new globalratelimit annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return globalratelimit{r} + return globalratelimit{ + r: r, + annotationConfig: globalRateLimitAnnotationConfig, + } } // Parse extracts globalratelimit annotations from the given ingress @@ -76,32 +121,46 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation { func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) { config := &Config{} - limit, _ := parser.GetIntAnnotation("global-rate-limit", ing) - rawWindowSize, _ := parser.GetStringAnnotation("global-rate-limit-window", ing) + limit, err := parser.GetIntAnnotation(globalRateLimitAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsInvalidContent(err) { + return nil, err + } + rawWindowSize, err := parser.GetStringAnnotation(globalRateLimitWindowAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsValidationError(err) { + return config, ing_errors.LocationDeniedError{ + Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err), + } + } - if limit == 0 || len(rawWindowSize) == 0 { + if limit == 0 || rawWindowSize == "" { return config, nil } windowSize, err := time.ParseDuration(rawWindowSize) if err != nil { - return config, ing_errors.LocationDenied{ + return config, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err), } } - key, _ := parser.GetStringAnnotation("global-rate-limit-key", ing) - if len(key) == 0 { + key, err := parser.GetStringAnnotation(globalRateLimitKeyAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + klog.Warningf("invalid %s, defaulting to %s", globalRateLimitKeyAnnotation, defaultKey) + } + if key == "" { key = defaultKey } - rawIgnoredCIDRs, _ := parser.GetStringAnnotation("global-rate-limit-ignored-cidrs", ing) + rawIgnoredCIDRs, err := parser.GetStringAnnotation(globalRateLimitIgnoredCidrsAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && ing_errors.IsInvalidContent(err) { + return nil, err + } ignoredCIDRs, err := net.ParseCIDRs(rawIgnoredCIDRs) if err != nil { return nil, err } - config.Namespace = strings.Replace(string(ing.UID), "-", "", -1) + config.Namespace = strings.ReplaceAll(string(ing.UID), "-", "") config.Limit = limit config.WindowSize = int(windowSize.Seconds()) config.Key = key @@ -109,3 +168,12 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) { return config, nil } + +func (a globalratelimit) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a globalratelimit) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, globalRateLimitAnnotationConfig.Annotations) +} diff --git a/internal/ingress/annotations/globalratelimit/main_test.go b/internal/ingress/annotations/globalratelimit/main_test.go index 815d6cfff..b1a7ab71b 100644 --- a/internal/ingress/annotations/globalratelimit/main_test.go +++ b/internal/ingress/annotations/globalratelimit/main_test.go @@ -30,8 +30,10 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) -const UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e" -const expectedUID = "31285d47b1504dcfbd6f12c46d769f6e" +const ( + UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e" + expectedUID = "31285d47b1504dcfbd6f12c46d769f6e" +) func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ @@ -149,6 +151,22 @@ func TestGlobalRateLimiting(t *testing.T) { }, nil, }, + { + "global-rate-limit-complex-key", + map[string]string{ + annRateLimit: "100", + annRateLimitWindow: "2m", + annRateLimitKey: "${http_x_api_user}${otherinfo}", + }, + &Config{ + Namespace: expectedUID, + Limit: 100, + WindowSize: 120, + Key: "${http_x_api_user}${otherinfo}", + IgnoredCIDRs: make([]string, 0), + }, + nil, + }, { "incorrect duration for window", map[string]string{ @@ -157,8 +175,8 @@ func TestGlobalRateLimiting(t *testing.T) { annRateLimitKey: "$http_x_api_user", }, &Config{}, - ing_errors.LocationDenied{ - Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: time: unknown unit \"mb\" in duration \"2mb\""), + ing_errors.ValidationError{ + Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: annotation nginx.ingress.kubernetes.io/global-rate-limit-window contains invalid value"), }, }, } @@ -168,16 +186,25 @@ func TestGlobalRateLimiting(t *testing.T) { i, actualErr := NewParser(mockBackend{}).Parse(ing) if (testCase.expectedErr == nil || actualErr == nil) && testCase.expectedErr != actualErr { - t.Errorf("expected error 'nil' but got '%v'", actualErr) + t.Errorf("%s expected error '%v' but got '%v'", testCase.title, testCase.expectedErr, actualErr) } else if testCase.expectedErr != nil && actualErr != nil && testCase.expectedErr.Error() != actualErr.Error() { t.Errorf("expected error '%v' but got '%v'", testCase.expectedErr, actualErr) } - actualConfig := i.(*Config) + actualConfig, ok := i.(*Config) + if !ok { + t.Errorf("expected Config type but got %T", i) + } if !testCase.expectedConfig.Equal(actualConfig) { - expectedJSON, _ := json.Marshal(testCase.expectedConfig) - actualJSON, _ := json.Marshal(actualConfig) + expectedJSON, err := json.Marshal(testCase.expectedConfig) + if err != nil { + t.Errorf("failed to marshal expected config: %v", err) + } + actualJSON, err := json.Marshal(actualConfig) + if err != nil { + t.Errorf("failed to marshal actual config: %v", err) + } t.Errorf("%v: expected config '%s' but got '%s'", testCase.title, expectedJSON, actualJSON) } } diff --git a/internal/ingress/annotations/http2pushpreload/main.go b/internal/ingress/annotations/http2pushpreload/main.go index 27d3368f4..123940d9d 100644 --- a/internal/ingress/annotations/http2pushpreload/main.go +++ b/internal/ingress/annotations/http2pushpreload/main.go @@ -23,17 +23,46 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + http2PushPreloadAnnotation = "http2-push-preload" +) + +var http2PushPreloadAnnotations = parser.Annotation{ + Group: "http2", + Annotations: parser.AnnotationFields{ + http2PushPreloadAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `Enables automatic conversion of preload links specified in the β€œLink” response header fields into push requests`, + }, + }, +} + type http2PushPreload struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new http2PushPreload annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return http2PushPreload{r} + return http2PushPreload{ + r: r, + annotationConfig: http2PushPreloadAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to add http2 push preload to the server func (h2pp http2PushPreload) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetBoolAnnotation("http2-push-preload", ing) + return parser.GetBoolAnnotation(http2PushPreloadAnnotation, ing, h2pp.annotationConfig.Annotations) +} + +func (h2pp http2PushPreload) GetDocumentation() parser.AnnotationFields { + return h2pp.annotationConfig.Annotations +} + +func (h2pp http2PushPreload) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(h2pp.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, http2PushPreloadAnnotations.Annotations) } diff --git a/internal/ingress/annotations/http2pushpreload/main_test.go b/internal/ingress/annotations/http2pushpreload/main_test.go index bb98af93f..eb6e9111d 100644 --- a/internal/ingress/annotations/http2pushpreload/main_test.go +++ b/internal/ingress/annotations/http2pushpreload/main_test.go @@ -23,11 +23,12 @@ import ( networking "k8s.io/api/networking/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("http2-push-preload") + annotation := parser.GetAnnotationWithPrefix(http2PushPreloadAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { t.Fatalf("expected a parser.IngressAnnotation but returned nil") @@ -36,12 +37,14 @@ func TestParse(t *testing.T) { testCases := []struct { annotations map[string]string expected bool + expectErr bool }{ - {map[string]string{annotation: "true"}, true}, - {map[string]string{annotation: "1"}, true}, - {map[string]string{annotation: ""}, false}, - {map[string]string{}, false}, - {nil, false}, + {map[string]string{annotation: "true"}, true, false}, + {map[string]string{annotation: "1"}, true, false}, + {map[string]string{annotation: "xpto"}, false, true}, + {map[string]string{annotation: ""}, false, false}, + {map[string]string{}, false, false}, + {nil, false, false}, } ing := &networking.Ingress{ @@ -54,7 +57,10 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) + result, err := ap.Parse(ing) + if ((err != nil) != testCase.expectErr) && !errors.IsInvalidContent(err) && !errors.IsMissingAnnotations(err) { + t.Fatalf("expected error: %t got error: %t err value: %s. %+v", testCase.expectErr, err != nil, err, testCase.annotations) + } if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) } diff --git a/internal/ingress/annotations/ipwhitelist/main.go b/internal/ingress/annotations/ipallowlist/main.go similarity index 52% rename from internal/ingress/annotations/ipwhitelist/main.go rename to internal/ingress/annotations/ipallowlist/main.go index 63c049fef..23a3bd9e7 100644 --- a/internal/ingress/annotations/ipwhitelist/main.go +++ b/internal/ingress/annotations/ipallowlist/main.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package ipwhitelist +package ipallowlist import ( "fmt" @@ -30,6 +30,24 @@ import ( "k8s.io/ingress-nginx/pkg/util/sets" ) +const ( + ipWhitelistAnnotation = "whitelist-source-range" + ipAllowlistAnnotation = "allowlist-source-range" +) + +var allowlistAnnotations = parser.Annotation{ + Group: "acl", + Annotations: parser.AnnotationFields{ + ipAllowlistAnnotation: { + Validator: parser.ValidateCIDRs, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Failure on parsing this may cause undesired access + Documentation: `This annotation allows setting a list of IPs and networks allowed to access this Location`, + AnnotationAliases: []string{ipWhitelistAnnotation}, + }, + }, +} + // SourceRange returns the CIDR type SourceRange struct { CIDR []string `json:"cidr,omitempty"` @@ -47,36 +65,46 @@ func (sr1 *SourceRange) Equal(sr2 *SourceRange) bool { return sets.StringElementsMatch(sr1.CIDR, sr2.CIDR) } -type ipwhitelist struct { - r resolver.Resolver +type ipallowlist struct { + r resolver.Resolver + annotationConfig parser.Annotation } -// NewParser creates a new whitelist annotation parser +// NewParser creates a new ipallowlist annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return ipwhitelist{r} + return ipallowlist{ + r: r, + annotationConfig: allowlistAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress // rule used to limit access to certain client addresses or networks. // Multiple ranges can specified using commas as separator // e.g. `18.0.0.0/8,56.0.0.0/8` -func (a ipwhitelist) Parse(ing *networking.Ingress) (interface{}, error) { +func (a ipallowlist) Parse(ing *networking.Ingress) (interface{}, error) { defBackend := a.r.GetDefaultBackend() - defaultWhitelistSourceRange := make([]string, len(defBackend.WhitelistSourceRange)) - copy(defaultWhitelistSourceRange, defBackend.WhitelistSourceRange) - sort.Strings(defaultWhitelistSourceRange) + defaultAllowlistSourceRange := make([]string, len(defBackend.WhitelistSourceRange)) + copy(defaultAllowlistSourceRange, defBackend.WhitelistSourceRange) + sort.Strings(defaultAllowlistSourceRange) - val, err := parser.GetStringAnnotation("whitelist-source-range", ing) + val, err := parser.GetStringAnnotation(ipAllowlistAnnotation, ing, a.annotationConfig.Annotations) // A missing annotation is not a problem, just use the default - if err == ing_errors.ErrMissingAnnotations { - return &SourceRange{CIDR: defaultWhitelistSourceRange}, nil + if err != nil { + if err == ing_errors.ErrMissingAnnotations { + return &SourceRange{CIDR: defaultAllowlistSourceRange}, nil + } + + return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{ + Reason: err, + } } values := strings.Split(val, ",") ipnets, ips, err := net.ParseIPNets(values...) if err != nil && len(ips) == 0 { - return &SourceRange{CIDR: defaultWhitelistSourceRange}, ing_errors.LocationDenied{ + return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err), } } @@ -93,3 +121,12 @@ func (a ipwhitelist) Parse(ing *networking.Ingress) (interface{}, error) { return &SourceRange{cidrs}, nil } + +func (a ipallowlist) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a ipallowlist) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, allowlistAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/ipwhitelist/main_test.go b/internal/ingress/annotations/ipallowlist/main_test.go similarity index 64% rename from internal/ingress/annotations/ipwhitelist/main_test.go rename to internal/ingress/annotations/ipallowlist/main_test.go index 5042bb200..b16b25a5b 100644 --- a/internal/ingress/annotations/ipwhitelist/main_test.go +++ b/internal/ingress/annotations/ipallowlist/main_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package ipwhitelist +package ipallowlist import ( "testing" @@ -86,12 +86,12 @@ func TestParseAnnotations(t *testing.T) { "test parse a invalid net": { net: "ww", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ww", + errOut: "annotation nginx.ingress.kubernetes.io/allowlist-source-range contains invalid value", }, "test parse a empty net": { net: "", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ", + errOut: "the annotation nginx.ingress.kubernetes.io/allowlist-source-range does not contain a valid value ()", }, "test parse multiple valid cidr": { net: "2.2.2.2/32,1.1.1.1/32,3.3.3.0/24", @@ -102,16 +102,16 @@ func TestParseAnnotations(t *testing.T) { for testName, test := range tests { data := map[string]string{} - data[parser.GetAnnotationWithPrefix("whitelist-source-range")] = test.net + data[parser.GetAnnotationWithPrefix(ipAllowlistAnnotation)] = test.net ing.SetAnnotations(data) p := NewParser(&resolver.Mock{}) i, err := p.Parse(ing) - if err != nil && !test.expectErr { - t.Errorf("%v:unexpected error: %v", testName, err) + if (err != nil) != test.expectErr { + t.Errorf("%s expected error: %t got error: %t err value: %s. %+v", testName, test.expectErr, err != nil, err, i) } - if test.expectErr { + if test.expectErr && err != nil { if err.Error() != test.errOut { - t.Errorf("%v:expected error: %v but %v return", testName, test.errOut, err.Error()) + t.Errorf("expected error %s but got %s", test.errOut, err) } } if !test.expectErr { @@ -137,7 +137,7 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend { } } -// Test that when we have a whitelist set on the Backend that is used when we +// Test that when we have a allowlist set on the Backend that is used when we // don't have the annotation func TestParseAnnotationsWithDefaultConfig(t *testing.T) { ing := buildIngress() @@ -158,12 +158,12 @@ func TestParseAnnotationsWithDefaultConfig(t *testing.T) { "test parse a invalid net": { net: "ww", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ww", + errOut: "annotation nginx.ingress.kubernetes.io/allowlist-source-range contains invalid value", }, "test parse a empty net": { net: "", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ", + errOut: "the annotation nginx.ingress.kubernetes.io/allowlist-source-range does not contain a valid value ()", }, "test parse multiple valid cidr": { net: "2.2.2.2/32,1.1.1.1/32,3.3.3.0/24", @@ -174,16 +174,67 @@ func TestParseAnnotationsWithDefaultConfig(t *testing.T) { for testName, test := range tests { data := map[string]string{} - data[parser.GetAnnotationWithPrefix("whitelist-source-range")] = test.net + data[parser.GetAnnotationWithPrefix(ipAllowlistAnnotation)] = test.net ing.SetAnnotations(data) p := NewParser(mockBackend) i, err := p.Parse(ing) - if err != nil && !test.expectErr { - t.Errorf("%v:unexpected error: %v", testName, err) + if (err != nil) != test.expectErr { + t.Errorf("expected error: %t got error: %t err value: %s. %+v", test.expectErr, err != nil, err, i) } - if test.expectErr { + if test.expectErr && err != nil { if err.Error() != test.errOut { - t.Errorf("%v:expected error: %v but %v return", testName, test.errOut, err.Error()) + t.Errorf("expected error %s but got %s", test.errOut, err) + } + } + if !test.expectErr { + sr, ok := i.(*SourceRange) + if !ok { + t.Errorf("%v:expected a SourceRange type", testName) + } + if !strsEquals(sr.CIDR, test.expectCidr) { + t.Errorf("%v:expected %v CIDR but %v returned", testName, test.expectCidr, sr.CIDR) + } + } + } +} + +// Test that when we have a whitelist set on the Backend that is used when we +// don't have the annotation +func TestLegacyAnnotation(t *testing.T) { + ing := buildIngress() + + mockBackend := mockBackend{} + + tests := map[string]struct { + net string + expectCidr []string + expectErr bool + errOut string + }{ + "test parse a valid net": { + net: "10.0.0.0/24", + expectCidr: []string{"10.0.0.0/24"}, + expectErr: false, + }, + "test parse multiple valid cidr": { + net: "2.2.2.2/32,1.1.1.1/32,3.3.3.0/24", + expectCidr: []string{"1.1.1.1/32", "2.2.2.2/32", "3.3.3.0/24"}, + expectErr: false, + }, + } + + for testName, test := range tests { + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(ipWhitelistAnnotation)] = test.net + ing.SetAnnotations(data) + p := NewParser(mockBackend) + i, err := p.Parse(ing) + if (err != nil) != test.expectErr { + t.Errorf("expected error: %t got error: %t err value: %s. %+v", test.expectErr, err != nil, err, i) + } + if test.expectErr && err != nil { + if err.Error() != test.errOut { + t.Errorf("expected error %s but got %s", test.errOut, err) } } if !test.expectErr { diff --git a/internal/ingress/annotations/ipdenylist/main.go b/internal/ingress/annotations/ipdenylist/main.go index f6a0e10f1..e8f02fe50 100644 --- a/internal/ingress/annotations/ipdenylist/main.go +++ b/internal/ingress/annotations/ipdenylist/main.go @@ -30,6 +30,22 @@ import ( "k8s.io/ingress-nginx/pkg/util/sets" ) +const ( + ipDenylistAnnotation = "denylist-source-range" +) + +var denylistAnnotations = parser.Annotation{ + Group: "acl", + Annotations: parser.AnnotationFields{ + ipDenylistAnnotation: { + Validator: parser.ValidateCIDRs, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Failure on parsing this may cause undesired access + Documentation: `This annotation allows setting a list of IPs and networks that should be blocked to access this Location`, + }, + }, +} + // SourceRange returns the CIDR type SourceRange struct { CIDR []string `json:"cidr,omitempty"` @@ -48,12 +64,16 @@ func (sr1 *SourceRange) Equal(sr2 *SourceRange) bool { } type ipdenylist struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new denylist annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return ipdenylist{r} + return ipdenylist{ + r: r, + annotationConfig: denylistAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -67,16 +87,21 @@ func (a ipdenylist) Parse(ing *networking.Ingress) (interface{}, error) { copy(defaultDenylistSourceRange, defBackend.DenylistSourceRange) sort.Strings(defaultDenylistSourceRange) - val, err := parser.GetStringAnnotation("denylist-source-range", ing) - // A missing annotation is not a problem, just use the default - if err == ing_errors.ErrMissingAnnotations { - return &SourceRange{CIDR: defaultDenylistSourceRange}, nil + val, err := parser.GetStringAnnotation(ipDenylistAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + if err == ing_errors.ErrMissingAnnotations { + return &SourceRange{CIDR: defaultDenylistSourceRange}, nil + } + + return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{ + Reason: err, + } } values := strings.Split(val, ",") ipnets, ips, err := net.ParseIPNets(values...) if err != nil && len(ips) == 0 { - return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{ + return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{ Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err), } } @@ -93,3 +118,12 @@ func (a ipdenylist) Parse(ing *networking.Ingress) (interface{}, error) { return &SourceRange{cidrs}, nil } + +func (a ipdenylist) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a ipdenylist) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, denylistAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/ipdenylist/main_test.go b/internal/ingress/annotations/ipdenylist/main_test.go index eb69aa520..ebd81179a 100644 --- a/internal/ingress/annotations/ipdenylist/main_test.go +++ b/internal/ingress/annotations/ipdenylist/main_test.go @@ -86,17 +86,17 @@ func TestParseAnnotations(t *testing.T) { "test parse a invalid net": { net: "ww", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ww", + errOut: "annotation nginx.ingress.kubernetes.io/denylist-source-range contains invalid value", }, "test parse a empty net": { net: "", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ", + errOut: "the annotation nginx.ingress.kubernetes.io/denylist-source-range does not contain a valid value ()", }, "test parse a malicious escaped string": { net: `10.0.0.0/8"rm /tmp",11.0.0.0/8`, expectErr: true, - errOut: `the annotation does not contain a valid IP address or network: invalid CIDR address: 10.0.0.0/8"rm /tmp"`, + errOut: `annotation nginx.ingress.kubernetes.io/denylist-source-range contains invalid value`, }, "test parse multiple valid cidr": { net: "2.2.2.2/32,1.1.1.1/32,3.3.3.0/24", @@ -107,16 +107,16 @@ func TestParseAnnotations(t *testing.T) { for testName, test := range tests { data := map[string]string{} - data[parser.GetAnnotationWithPrefix("denylist-source-range")] = test.net + data[parser.GetAnnotationWithPrefix(ipDenylistAnnotation)] = test.net ing.SetAnnotations(data) p := NewParser(&resolver.Mock{}) i, err := p.Parse(ing) - if err != nil && !test.expectErr { - t.Errorf("%v:unexpected error: %v", testName, err) + if (err != nil) != test.expectErr { + t.Errorf("expected error: %t got error: %t err value: %s. %+v", test.expectErr, err != nil, err, i) } - if test.expectErr { + if test.expectErr && err != nil { if err.Error() != test.errOut { - t.Errorf("%v:expected error: %v but %v return", testName, test.errOut, err.Error()) + t.Errorf("expected error %s but got %s", test.errOut, err) } } if !test.expectErr { @@ -163,12 +163,12 @@ func TestParseAnnotationsWithDefaultConfig(t *testing.T) { "test parse a invalid net": { net: "ww", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ww", + errOut: "annotation nginx.ingress.kubernetes.io/denylist-source-range contains invalid value", }, "test parse a empty net": { net: "", expectErr: true, - errOut: "the annotation does not contain a valid IP address or network: invalid CIDR address: ", + errOut: "the annotation nginx.ingress.kubernetes.io/denylist-source-range does not contain a valid value ()", }, "test parse multiple valid cidr": { net: "2.2.2.2/32,1.1.1.1/32,3.3.3.0/24", @@ -179,16 +179,16 @@ func TestParseAnnotationsWithDefaultConfig(t *testing.T) { for testName, test := range tests { data := map[string]string{} - data[parser.GetAnnotationWithPrefix("denylist-source-range")] = test.net + data[parser.GetAnnotationWithPrefix(ipDenylistAnnotation)] = test.net ing.SetAnnotations(data) p := NewParser(mockBackend) i, err := p.Parse(ing) - if err != nil && !test.expectErr { - t.Errorf("%v:unexpected error: %v", testName, err) + if (err != nil) != test.expectErr { + t.Errorf("expected error: %t got error: %t err value: %s. %+v", test.expectErr, err != nil, err, i) } - if test.expectErr { + if test.expectErr && err != nil { if err.Error() != test.errOut { - t.Errorf("%v:expected error: %v but %v return", testName, test.errOut, err.Error()) + t.Errorf("expected error %s but got %s", test.errOut, err) } } if !test.expectErr { diff --git a/internal/ingress/annotations/loadbalancing/main.go b/internal/ingress/annotations/loadbalancing/main.go index a8b4335e6..ee89d2c1b 100644 --- a/internal/ingress/annotations/loadbalancing/main.go +++ b/internal/ingress/annotations/loadbalancing/main.go @@ -23,18 +23,52 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) -type loadbalancing struct { - r resolver.Resolver +// LB Alghorithms are defined in https://github.com/kubernetes/ingress-nginx/blob/d3e75b056f77be54e01bdb18675f1bb46caece31/rootfs/etc/nginx/lua/balancer.lua#L28 + +const ( + loadBalanceAlghoritmAnnotation = "load-balance" +) + +var loadBalanceAlghoritms = []string{"round_robin", "chash", "chashsubset", "sticky_balanced", "sticky_persistent", "ewma"} + +var loadBalanceAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + loadBalanceAlghoritmAnnotation: { + Validator: parser.ValidateOptions(loadBalanceAlghoritms, true, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows setting the load balancing alghorithm that should be used. If none is specified, defaults to + the default configured by Ingress admin, otherwise to round_robin`, + }, + }, } -// NewParser creates a new CORS annotation parser +type loadbalancing struct { + r resolver.Resolver + annotationConfig parser.Annotation +} + +// NewParser creates a new Load Balancer annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return loadbalancing{r} + return loadbalancing{ + r: r, + annotationConfig: loadBalanceAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to indicate if the location/s contains a fragment of // configuration to be included inside the paths of the rules func (a loadbalancing) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("load-balance", ing) + return parser.GetStringAnnotation(loadBalanceAlghoritmAnnotation, ing, a.annotationConfig.Annotations) +} + +func (a loadbalancing) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a loadbalancing) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, loadBalanceAnnotations.Annotations) } diff --git a/internal/ingress/annotations/loadbalancing/main_test.go b/internal/ingress/annotations/loadbalancing/main_test.go index e2be5c0ae..a5205e523 100644 --- a/internal/ingress/annotations/loadbalancing/main_test.go +++ b/internal/ingress/annotations/loadbalancing/main_test.go @@ -38,7 +38,8 @@ func TestParse(t *testing.T) { annotations map[string]string expected string }{ - {map[string]string{annotation: "ip_hash"}, "ip_hash"}, + {map[string]string{annotation: "ewma"}, "ewma"}, + {map[string]string{annotation: "ip_hash"}, ""}, // This is invalid and should not return anything {map[string]string{}, ""}, {nil, ""}, } @@ -53,6 +54,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/annotations/log/main.go b/internal/ingress/annotations/log/main.go index 4bc76dcf7..82d50bac3 100644 --- a/internal/ingress/annotations/log/main.go +++ b/internal/ingress/annotations/log/main.go @@ -23,8 +23,32 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + enableAccessLogAnnotation = "enable-access-log" + enableRewriteLogAnnotation = "enable-rewrite-log" +) + +var logAnnotations = parser.Annotation{ + Group: "log", + Annotations: parser.AnnotationFields{ + enableAccessLogAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This configuration setting allows you to control if this location should generate an access_log`, + }, + enableRewriteLogAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This configuration setting allows you to control if this location should generate logs from the rewrite feature usage`, + }, + }, +} + type log struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config contains the configuration to be used in the Ingress @@ -48,7 +72,10 @@ func (bd1 *Config) Equal(bd2 *Config) bool { // NewParser creates a new log annotations parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return log{r} + return log{ + r: r, + annotationConfig: logAnnotations, + } } // Parse parses the annotations contained in the ingress @@ -57,15 +84,24 @@ func (l log) Parse(ing *networking.Ingress) (interface{}, error) { var err error config := &Config{} - config.Access, err = parser.GetBoolAnnotation("enable-access-log", ing) + config.Access, err = parser.GetBoolAnnotation(enableAccessLogAnnotation, ing, l.annotationConfig.Annotations) if err != nil { config.Access = true } - config.Rewrite, err = parser.GetBoolAnnotation("enable-rewrite-log", ing) + config.Rewrite, err = parser.GetBoolAnnotation(enableRewriteLogAnnotation, ing, l.annotationConfig.Annotations) if err != nil { config.Rewrite = false } return config, nil } + +func (l log) GetDocumentation() parser.AnnotationFields { + return l.annotationConfig.Annotations +} + +func (l log) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(l.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, logAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/log/main_test.go b/internal/ingress/annotations/log/main_test.go index c4632b010..950612b5b 100644 --- a/internal/ingress/annotations/log/main_test.go +++ b/internal/ingress/annotations/log/main_test.go @@ -73,10 +73,13 @@ func TestIngressAccessLogConfig(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-access-log")] = "false" + data[parser.GetAnnotationWithPrefix(enableAccessLogAnnotation)] = "false" ing.SetAnnotations(data) - log, _ := NewParser(&resolver.Mock{}).Parse(ing) + log, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } nginxLogs, ok := log.(*Config) if !ok { t.Errorf("expected a Config type") @@ -91,10 +94,13 @@ func TestIngressRewriteLogConfig(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-rewrite-log")] = "true" + data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "true" ing.SetAnnotations(data) - log, _ := NewParser(&resolver.Mock{}).Parse(ing) + log, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing annotations %v", err) + } nginxLogs, ok := log.(*Config) if !ok { t.Errorf("expected a Config type") @@ -104,3 +110,24 @@ func TestIngressRewriteLogConfig(t *testing.T) { t.Errorf("expected rewrite log to be enabled but it is disabled") } } + +func TestInvalidBoolConfig(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "blo" + ing.SetAnnotations(data) + + log, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + nginxLogs, ok := log.(*Config) + if !ok { + t.Errorf("expected a Config type") + } + + if !nginxLogs.Access { + t.Errorf("expected access log to be enabled due to invalid config, but it is disabled") + } +} diff --git a/internal/ingress/annotations/mirror/main.go b/internal/ingress/annotations/mirror/main.go index cd54a9826..d51bdd27c 100644 --- a/internal/ingress/annotations/mirror/main.go +++ b/internal/ingress/annotations/mirror/main.go @@ -18,13 +18,48 @@ package mirror import ( "fmt" + "regexp" "strings" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" + "k8s.io/klog/v2" ) +const ( + mirrorRequestBodyAnnotation = "mirror-request-body" + mirrorTargetAnnotation = "mirror-target" + mirrorHostAnnotation = "mirror-host" +) + +var OnOffRegex = regexp.MustCompile(`^(on|off)$`) + +var mirrorAnnotation = parser.Annotation{ + Group: "mirror", + Annotations: parser.AnnotationFields{ + mirrorRequestBodyAnnotation: { + Validator: parser.ValidateRegex(OnOffRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines if the request-body should be sent to the mirror backend. Can be 'on' or 'off'`, + }, + mirrorTargetAnnotation: { + Validator: parser.ValidateServerName, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation enables a request to be mirrored to a mirror backend.`, + }, + mirrorHostAnnotation: { + Validator: parser.ValidateServerName, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation defines if a specific Host header should be set for mirrored request.`, + }, + }, +} + // Config returns the mirror to use in a given location type Config struct { Source string `json:"source"` @@ -63,12 +98,16 @@ func (m1 *Config) Equal(m2 *Config) bool { } type mirror struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new mirror configuration annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return mirror{r} + return mirror{ + r: r, + annotationConfig: mirrorAnnotation, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -79,29 +118,49 @@ func (a mirror) Parse(ing *networking.Ingress) (interface{}, error) { } var err error - config.RequestBody, err = parser.GetStringAnnotation("mirror-request-body", ing) + config.RequestBody, err = parser.GetStringAnnotation(mirrorRequestBodyAnnotation, ing, a.annotationConfig.Annotations) if err != nil || config.RequestBody != "off" { + if errors.IsValidationError(err) { + klog.Warningf("annotation %s contains invalid value", mirrorRequestBodyAnnotation) + } config.RequestBody = "on" } - config.Target, err = parser.GetStringAnnotation("mirror-target", ing) + config.Target, err = parser.GetStringAnnotation(mirrorTargetAnnotation, ing, a.annotationConfig.Annotations) if err != nil { - config.Target = "" - config.Source = "" + if errors.IsValidationError(err) { + klog.Warningf("annotation %s contains invalid value, defaulting", mirrorTargetAnnotation) + } else { + config.Target = "" + config.Source = "" + } } - config.Host, err = parser.GetStringAnnotation("mirror-host", ing) + config.Host, err = parser.GetStringAnnotation(mirrorHostAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("annotation %s contains invalid value, defaulting", mirrorHostAnnotation) + } if config.Target != "" { - url, err := parser.StringToURL(config.Target) + target := strings.Split(config.Target, "$") + + url, err := parser.StringToURL(target[0]) if err != nil { config.Host = "" } else { - hostname := strings.Split(url.Hostname(), "$") - config.Host = hostname[0] + config.Host = url.Hostname() } } } return config, nil } + +func (a mirror) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a mirror) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, mirrorAnnotation.Annotations) +} diff --git a/internal/ingress/annotations/mirror/main_test.go b/internal/ingress/annotations/mirror/main_test.go index f744ab552..1f6b44d61 100644 --- a/internal/ingress/annotations/mirror/main_test.go +++ b/internal/ingress/annotations/mirror/main_test.go @@ -48,6 +48,24 @@ func TestParse(t *testing.T) { Target: "https://test.env.com/$request_uri", Host: "test.env.com", }}, + {map[string]string{backendURL: "https://test.env.com$request_uri"}, &Config{ + Source: ngxURI, + RequestBody: "on", + Target: "https://test.env.com$request_uri", + Host: "test.env.com", + }}, + {map[string]string{backendURL: "https://test.env.com:8080$request_uri"}, &Config{ + Source: ngxURI, + RequestBody: "on", + Target: "https://test.env.com:8080$request_uri", + Host: "test.env.com", + }}, + {map[string]string{backendURL: "https://test.env.com:8080/$request_uri"}, &Config{ + Source: ngxURI, + RequestBody: "on", + Target: "https://test.env.com:8080/$request_uri", + Host: "test.env.com", + }}, {map[string]string{requestBody: "off"}, &Config{ Source: "", RequestBody: "off", @@ -76,13 +94,13 @@ func TestParse(t *testing.T) { Source: ngxURI, RequestBody: "on", Target: "http://some.test.env.com", - Host: "someInvalidParm.%^&*()_=!@#'\"", + Host: "some.test.env.com", }}, {map[string]string{backendURL: "http://some.test.env.com", host: "_sbrubles-i\"@xpto:12345"}, &Config{ Source: ngxURI, RequestBody: "on", Target: "http://some.test.env.com", - Host: "_sbrubles-i\"@xpto:12345", + Host: "some.test.env.com", }}, } @@ -97,9 +115,12 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) + result, err := ap.Parse(ing) + if err != nil { + t.Errorf(err.Error()) + } if !reflect.DeepEqual(result, testCase.expected) { - t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) + t.Errorf("expected %+v but returned %+v, annotations: %s", testCase.expected, result, testCase.annotations) } } } diff --git a/internal/ingress/annotations/modsecurity/main.go b/internal/ingress/annotations/modsecurity/main.go index c53739441..80ae78937 100644 --- a/internal/ingress/annotations/modsecurity/main.go +++ b/internal/ingress/annotations/modsecurity/main.go @@ -19,9 +19,48 @@ package modsecurity import ( networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" + "k8s.io/klog/v2" ) +const ( + modsecEnableAnnotation = "enable-modsecurity" + modsecEnableOwaspCoreAnnotation = "enable-owasp-core-rules" + modesecTransactionIDAnnotation = "modsecurity-transaction-id" + modsecSnippetAnnotation = "modsecurity-snippet" +) + +var modsecurityAnnotation = parser.Annotation{ + Group: "modsecurity", + Annotations: parser.AnnotationFields{ + modsecEnableAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables ModSecurity`, + }, + modsecEnableOwaspCoreAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables the OWASP Core Rule Set`, + }, + modesecTransactionIDAnnotation: { + Validator: parser.ValidateRegex(parser.NGINXVariable, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation enables passing an NGINX variable to ModSecurity.`, + }, + modsecSnippetAnnotation: { + Validator: parser.ValidateNull, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskCritical, + Documentation: `This annotation enables adding a specific snippet configuration for ModSecurity`, + }, + }, +} + // Config contains ModSecurity Configuration items type Config struct { Enable bool `json:"enable-modsecurity"` @@ -59,12 +98,16 @@ func (modsec1 *Config) Equal(modsec2 *Config) bool { } // NewParser creates a new ModSecurity annotation parser -func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { - return modSecurity{resolver} +func NewParser(r resolver.Resolver) parser.IngressAnnotation { + return modSecurity{ + r: r, + annotationConfig: modsecurityAnnotation, + } } type modSecurity struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Parse parses the annotations contained in the ingress @@ -74,26 +117,44 @@ func (a modSecurity) Parse(ing *networking.Ingress) (interface{}, error) { config := &Config{} config.EnableSet = true - config.Enable, err = parser.GetBoolAnnotation("enable-modsecurity", ing) + config.Enable, err = parser.GetBoolAnnotation(modsecEnableAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsInvalidContent(err) { + klog.Warningf("annotation %s contains invalid directive, defaulting to false", modsecEnableAnnotation) + } config.Enable = false config.EnableSet = false } - config.OWASPRules, err = parser.GetBoolAnnotation("enable-owasp-core-rules", ing) + config.OWASPRules, err = parser.GetBoolAnnotation(modsecEnableOwaspCoreAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsInvalidContent(err) { + klog.Warningf("annotation %s contains invalid directive, defaulting to false", modsecEnableOwaspCoreAnnotation) + } config.OWASPRules = false } - config.TransactionID, err = parser.GetStringAnnotation("modsecurity-transaction-id", ing) + config.TransactionID, err = parser.GetStringAnnotation(modesecTransactionIDAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsInvalidContent(err) { + klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIDAnnotation) + } config.TransactionID = "" } - config.Snippet, err = parser.GetStringAnnotation("modsecurity-snippet", ing) + config.Snippet, err = parser.GetStringAnnotation("modsecurity-snippet", ing, a.annotationConfig.Annotations) if err != nil { config.Snippet = "" } return config, nil } + +func (a modSecurity) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a modSecurity) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, modsecurityAnnotation.Annotations) +} diff --git a/internal/ingress/annotations/modsecurity/main_test.go b/internal/ingress/annotations/modsecurity/main_test.go index 2ddbdf7e3..5cb05f201 100644 --- a/internal/ingress/annotations/modsecurity/main_test.go +++ b/internal/ingress/annotations/modsecurity/main_test.go @@ -67,11 +67,17 @@ func TestParse(t *testing.T) { Spec: networking.IngressSpec{}, } - for _, testCase := range testCases { + for i, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) - config := result.(*Config) - if !config.Equal(&testCase.expected) { + result, err := ap.Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + config, ok := result.(*Config) + if !ok { + t.Errorf("unexpected type: %T", result) + } + if !config.Equal(&testCases[i].expected) { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) } } diff --git a/internal/ingress/annotations/opentelemetry/main.go b/internal/ingress/annotations/opentelemetry/main.go index 7dd292322..ca9108548 100644 --- a/internal/ingress/annotations/opentelemetry/main.go +++ b/internal/ingress/annotations/opentelemetry/main.go @@ -17,14 +17,51 @@ limitations under the License. package opentelemetry import ( + "regexp" + networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + enableOpenTelemetryAnnotation = "enable-opentelemetry" + otelTrustSpanAnnotation = "opentelemetry-trust-incoming-span" + otelOperationNameAnnotation = "opentelemetry-operation-name" +) + +var regexOperationName = regexp.MustCompile(`^[A-Za-z0-9_\-]*$`) + +var otelAnnotations = parser.Annotation{ + Group: "opentelemetry", + Annotations: parser.AnnotationFields{ + enableOpenTelemetryAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines if Open Telemetry collector should be enable for this location. OpenTelemetry should + already be configured by Ingress administrator`, + }, + otelTrustSpanAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables or disables using spans from incoming requests as parent for created ones`, + }, + otelOperationNameAnnotation: { + Validator: parser.ValidateRegex(regexOperationName, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines what operation name should be added to the span`, + }, + }, +} + type opentelemetry struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config contains the configuration to be used in the Ingress @@ -38,7 +75,6 @@ type Config struct { // Equal tests for equality between two Config types func (bd1 *Config) Equal(bd2 *Config) bool { - if bd1.Set != bd2.Set { return false } @@ -64,13 +100,16 @@ func (bd1 *Config) Equal(bd2 *Config) bool { // NewParser creates a new serviceUpstream annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return opentelemetry{r} + return opentelemetry{ + r: r, + annotationConfig: otelAnnotations, + } } // Parse parses the annotations to look for opentelemetry configurations func (c opentelemetry) Parse(ing *networking.Ingress) (interface{}, error) { cfg := Config{} - enabled, err := parser.GetBoolAnnotation("enable-opentelemetry", ing) + enabled, err := parser.GetBoolAnnotation(enableOpenTelemetryAnnotation, ing, c.annotationConfig.Annotations) if err != nil { return &cfg, nil } @@ -80,10 +119,13 @@ func (c opentelemetry) Parse(ing *networking.Ingress) (interface{}, error) { return &cfg, nil } - trustEnabled, err := parser.GetBoolAnnotation("opentelemetry-trust-incoming-span", ing) + trustEnabled, err := parser.GetBoolAnnotation(otelTrustSpanAnnotation, ing, c.annotationConfig.Annotations) if err != nil { - operationName, err := parser.GetStringAnnotation("opentelemetry-operation-name", ing) + operationName, err := parser.GetStringAnnotation(otelOperationNameAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + return nil, err + } return &cfg, nil } cfg.OperationName = operationName @@ -92,10 +134,22 @@ func (c opentelemetry) Parse(ing *networking.Ingress) (interface{}, error) { cfg.TrustSet = true cfg.TrustEnabled = trustEnabled - operationName, err := parser.GetStringAnnotation("opentelemetry-operation-name", ing) + operationName, err := parser.GetStringAnnotation(otelOperationNameAnnotation, ing, c.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + return nil, err + } return &cfg, nil } cfg.OperationName = operationName return &cfg, nil } + +func (c opentelemetry) GetDocumentation() parser.AnnotationFields { + return c.annotationConfig.Annotations +} + +func (c opentelemetry) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, otelAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/opentelemetry/main_test.go b/internal/ingress/annotations/opentelemetry/main_test.go index 619108fa6..e5c1de0b3 100644 --- a/internal/ingress/annotations/opentelemetry/main_test.go +++ b/internal/ingress/annotations/opentelemetry/main_test.go @@ -26,6 +26,8 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const enableAnnotation = "true" + func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ @@ -73,10 +75,13 @@ func TestIngressAnnotationOpentelemetrySetTrue(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-opentelemetry")] = "true" + data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } openTelemetry, ok := val.(*Config) if !ok { t.Errorf("expected a Config type") @@ -100,10 +105,13 @@ func TestIngressAnnotationOpentelemetrySetFalse(t *testing.T) { // Test with explicitly set to false data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-opentelemetry")] = "false" + data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "false" ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } openTelemetry, ok := val.(*Config) if !ok { t.Errorf("expected a Config type") @@ -123,12 +131,15 @@ func TestIngressAnnotationOpentelemetryTrustSetTrue(t *testing.T) { data := map[string]string{} opName := "foo-op" - data[parser.GetAnnotationWithPrefix("enable-opentelemetry")] = "true" - data[parser.GetAnnotationWithPrefix("opentelemetry-trust-incoming-span")] = "true" - data[parser.GetAnnotationWithPrefix("opentelemetry-operation-name")] = opName + data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation + data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = enableAnnotation + data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Fatal(err) + } openTelemetry, ok := val.(*Config) if !ok { t.Errorf("expected a Config type") @@ -155,6 +166,21 @@ func TestIngressAnnotationOpentelemetryTrustSetTrue(t *testing.T) { } } +func TestIngressAnnotationOpentelemetryWithBadOpName(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + opName := "fooxpto_123$la;" + data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation + data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName + ing.SetAnnotations(data) + + _, err := NewParser(&resolver.Mock{}).Parse(ing) + if err == nil { + t.Fatalf("This operation should return an error but no error was returned") + } +} + func TestIngressAnnotationOpentelemetryUnset(t *testing.T) { ing := buildIngress() @@ -162,7 +188,10 @@ func TestIngressAnnotationOpentelemetryUnset(t *testing.T) { data := map[string]string{} ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } _, ok := val.(*Config) if !ok { t.Errorf("expected a Config type") diff --git a/internal/ingress/annotations/opentracing/main.go b/internal/ingress/annotations/opentracing/main.go deleted file mode 100644 index 17ba7eb9f..000000000 --- a/internal/ingress/annotations/opentracing/main.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2019 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 opentracing - -import ( - networking "k8s.io/api/networking/v1" - - "k8s.io/ingress-nginx/internal/ingress/annotations/parser" - "k8s.io/ingress-nginx/internal/ingress/resolver" -) - -type opentracing struct { - r resolver.Resolver -} - -// Config contains the configuration to be used in the Ingress -type Config struct { - Enabled bool `json:"enabled"` - Set bool `json:"set"` - TrustEnabled bool `json:"trust-enabled"` - TrustSet bool `json:"trust-set"` -} - -// Equal tests for equality between two Config types -func (bd1 *Config) Equal(bd2 *Config) bool { - if bd1.Set != bd2.Set { - return false - } - - if bd1.Enabled != bd2.Enabled { - return false - } - - if bd1.TrustSet != bd2.TrustSet { - return false - } - - if bd1.TrustEnabled != bd2.TrustEnabled { - return false - } - - return true -} - -// NewParser creates a new serviceUpstream annotation parser -func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return opentracing{r} -} - -func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) { - enabled, err := parser.GetBoolAnnotation("enable-opentracing", ing) - if err != nil { - return &Config{}, nil - } - - trustSpan, err := parser.GetBoolAnnotation("opentracing-trust-incoming-span", ing) - if err != nil { - return &Config{Set: true, Enabled: enabled}, nil - } - - return &Config{Set: true, Enabled: enabled, TrustSet: true, TrustEnabled: trustSpan}, nil -} diff --git a/internal/ingress/annotations/opentracing/main_test.go b/internal/ingress/annotations/opentracing/main_test.go deleted file mode 100644 index 7bd9d31ff..000000000 --- a/internal/ingress/annotations/opentracing/main_test.go +++ /dev/null @@ -1,144 +0,0 @@ -/* -Copyright 2019 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 opentracing - -import ( - "testing" - - api "k8s.io/api/core/v1" - networking "k8s.io/api/networking/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/ingress-nginx/internal/ingress/annotations/parser" - "k8s.io/ingress-nginx/internal/ingress/resolver" -) - -func buildIngress() *networking.Ingress { - defaultBackend := networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "default-backend", - Port: networking.ServiceBackendPort{ - Number: 80, - }, - }, - } - - return &networking.Ingress{ - ObjectMeta: meta_v1.ObjectMeta{ - Name: "foo", - Namespace: api.NamespaceDefault, - }, - Spec: networking.IngressSpec{ - DefaultBackend: &networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "default-backend", - Port: networking.ServiceBackendPort{ - Number: 80, - }, - }, - }, - Rules: []networking.IngressRule{ - { - Host: "foo.bar.com", - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: []networking.HTTPIngressPath{ - { - Path: "/foo", - Backend: defaultBackend, - }, - }, - }, - }, - }, - }, - }, - } -} - -func TestIngressAnnotationOpentracingSetTrue(t *testing.T) { - ing := buildIngress() - - data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-opentracing")] = "true" - ing.SetAnnotations(data) - - val, _ := NewParser(&resolver.Mock{}).Parse(ing) - openTracing, ok := val.(*Config) - if !ok { - t.Errorf("expected a Config type") - } - - if !openTracing.Enabled { - t.Errorf("expected annotation value to be true, got false") - } -} - -func TestIngressAnnotationOpentracingSetFalse(t *testing.T) { - ing := buildIngress() - - // Test with explicitly set to false - data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-opentracing")] = "false" - ing.SetAnnotations(data) - - val, _ := NewParser(&resolver.Mock{}).Parse(ing) - openTracing, ok := val.(*Config) - if !ok { - t.Errorf("expected a Config type") - } - - if openTracing.Enabled { - t.Errorf("expected annotation value to be false, got true") - } -} - -func TestIngressAnnotationOpentracingTrustSetTrue(t *testing.T) { - ing := buildIngress() - - data := map[string]string{} - data[parser.GetAnnotationWithPrefix("enable-opentracing")] = "true" - data[parser.GetAnnotationWithPrefix("opentracing-trust-incoming-span")] = "true" - ing.SetAnnotations(data) - - val, _ := NewParser(&resolver.Mock{}).Parse(ing) - openTracing, ok := val.(*Config) - if !ok { - t.Errorf("expected a Config type") - } - - if !openTracing.Enabled { - t.Errorf("expected annotation value to be true, got false") - } - - if !openTracing.TrustEnabled { - t.Errorf("expected annotation value to be true, got false") - } -} - -func TestIngressAnnotationOpentracingUnset(t *testing.T) { - ing := buildIngress() - - // Test with no annotation specified - data := map[string]string{} - ing.SetAnnotations(data) - - val, _ := NewParser(&resolver.Mock{}).Parse(ing) - _, ok := val.(*Config) - if !ok { - t.Errorf("expected a Config type") - } -} diff --git a/internal/ingress/annotations/parser/main.go b/internal/ingress/annotations/parser/main.go index 107a278b0..554ad9dde 100644 --- a/internal/ingress/annotations/parser/main.go +++ b/internal/ingress/annotations/parser/main.go @@ -29,20 +29,79 @@ import ( ) // DefaultAnnotationsPrefix defines the common prefix used in the nginx ingress controller -const DefaultAnnotationsPrefix = "nginx.ingress.kubernetes.io" +const ( + DefaultAnnotationsPrefix = "nginx.ingress.kubernetes.io" + DefaultEnableAnnotationValidation = true +) var ( // AnnotationsPrefix is the mutable attribute that the controller explicitly refers to AnnotationsPrefix = DefaultAnnotationsPrefix + // Enable is the mutable attribute for enabling or disabling the validation functions + EnableAnnotationValidation = DefaultEnableAnnotationValidation ) +// AnnotationGroup defines the group that this annotation may belong +// eg.: Security, Snippets, Rewrite, etc +type AnnotationGroup string + +// AnnotationScope defines which scope this annotation applies. May be to the whole +// ingress, per location, etc +type AnnotationScope string + +var ( + AnnotationScopeLocation AnnotationScope = "location" + AnnotationScopeIngress AnnotationScope = "ingress" +) + +// AnnotationRisk is a subset of risk that an annotation may represent. +// Based on the Risk, the admin will be able to allow or disallow users to set it +// on their ingress objects +type AnnotationRisk int + +type AnnotationFields map[string]AnnotationConfig + +// AnnotationConfig defines the configuration that a single annotation field +// has, with the Validator and the documentation of this field. +type AnnotationConfig struct { + // Validator defines a function to validate the annotation value + Validator AnnotationValidator + // Documentation defines a user facing documentation for this annotation. This + // field will be used to auto generate documentations + Documentation string + // Risk defines a risk of this annotation being exposed to the user. Annotations + // with bool fields, or to set timeout are usually low risk. Annotations that allows + // string input without a limited set of options may represent a high risk + Risk AnnotationRisk + + // Scope defines which scope this annotation applies, may be to location, to an Ingress object, etc + Scope AnnotationScope + + // AnnotationAliases defines other names this annotation may have. + AnnotationAliases []string +} + +// Annotation defines an annotation feature an Ingress may have. +// It should contain the internal resolver, and all the annotations +// with configs and Validators that should be used for each Annotation +type Annotation struct { + // Annotations contains all the annotations that belong to this feature + Annotations AnnotationFields + // Group defines which annotation group this feature belongs to + Group AnnotationGroup +} + // IngressAnnotation has a method to parse annotations located in Ingress type IngressAnnotation interface { Parse(ing *networking.Ingress) (interface{}, error) + GetDocumentation() AnnotationFields + Validate(anns map[string]string) error } type ingAnnotations map[string]string +// TODO: We already parse all of this on checkAnnotation and can just do a parse over the +// value func (a ingAnnotations) parseBool(name string) (bool, error) { val, ok := a[name] if ok { @@ -59,7 +118,7 @@ func (a ingAnnotations) parseString(name string) (string, error) { val, ok := a[name] if ok { s := normalizeString(val) - if len(s) == 0 { + if s == "" { return "", errors.NewInvalidAnnotationContent(name, val) } @@ -92,21 +151,9 @@ func (a ingAnnotations) parseFloat32(name string) (float32, error) { return 0, errors.ErrMissingAnnotations } -func checkAnnotation(name string, ing *networking.Ingress) error { - if ing == nil || len(ing.GetAnnotations()) == 0 { - return errors.ErrMissingAnnotations - } - if name == "" { - return errors.ErrInvalidAnnotationName - } - - return nil -} - // GetBoolAnnotation extracts a boolean from an Ingress annotation -func GetBoolAnnotation(name string, ing *networking.Ingress) (bool, error) { - v := GetAnnotationWithPrefix(name) - err := checkAnnotation(v, ing) +func GetBoolAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (bool, error) { + v, err := checkAnnotation(name, ing, fields) if err != nil { return false, err } @@ -114,9 +161,8 @@ func GetBoolAnnotation(name string, ing *networking.Ingress) (bool, error) { } // GetStringAnnotation extracts a string from an Ingress annotation -func GetStringAnnotation(name string, ing *networking.Ingress) (string, error) { - v := GetAnnotationWithPrefix(name) - err := checkAnnotation(v, ing) +func GetStringAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (string, error) { + v, err := checkAnnotation(name, ing, fields) if err != nil { return "", err } @@ -125,9 +171,8 @@ func GetStringAnnotation(name string, ing *networking.Ingress) (string, error) { } // GetIntAnnotation extracts an int from an Ingress annotation -func GetIntAnnotation(name string, ing *networking.Ingress) (int, error) { - v := GetAnnotationWithPrefix(name) - err := checkAnnotation(v, ing) +func GetIntAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (int, error) { + v, err := checkAnnotation(name, ing, fields) if err != nil { return 0, err } @@ -135,9 +180,8 @@ func GetIntAnnotation(name string, ing *networking.Ingress) (int, error) { } // GetFloatAnnotation extracts a float32 from an Ingress annotation -func GetFloatAnnotation(name string, ing *networking.Ingress) (float32, error) { - v := GetAnnotationWithPrefix(name) - err := checkAnnotation(v, ing) +func GetFloatAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (float32, error) { + v, err := checkAnnotation(name, ing, fields) if err != nil { return 0, err } @@ -149,6 +193,23 @@ func GetAnnotationWithPrefix(suffix string) string { return fmt.Sprintf("%v/%v", AnnotationsPrefix, suffix) } +func TrimAnnotationPrefix(annotation string) string { + return strings.TrimPrefix(annotation, AnnotationsPrefix+"/") +} + +func StringRiskToRisk(risk string) AnnotationRisk { + switch strings.ToLower(risk) { + case "critical": + return AnnotationRiskCritical + case "high": + return AnnotationRiskHigh + case "medium": + return AnnotationRiskMedium + default: + return AnnotationRiskLow + } +} + func normalizeString(input string) string { trimmedContent := []string{} for _, line := range strings.Split(input, "\n") { @@ -187,13 +248,14 @@ func StringToURL(input string) (*url.URL, error) { return nil, fmt.Errorf("%v is not a valid URL: %v", input, err) } - if parsedURL.Scheme == "" { + switch { + case parsedURL.Scheme == "": return nil, fmt.Errorf("url scheme is empty") - } else if parsedURL.Host == "" { + case parsedURL.Host == "": return nil, fmt.Errorf("url host is empty") - } else if strings.Contains(parsedURL.Host, "..") { + case strings.Contains(parsedURL.Host, ".."): return nil, fmt.Errorf("invalid url host") + default: + return parsedURL, nil } - - return parsedURL, nil } diff --git a/internal/ingress/annotations/parser/main_test.go b/internal/ingress/annotations/parser/main_test.go index 318e024d3..0f7ecb459 100644 --- a/internal/ingress/annotations/parser/main_test.go +++ b/internal/ingress/annotations/parser/main_test.go @@ -38,7 +38,7 @@ func buildIngress() *networking.Ingress { func TestGetBoolAnnotation(t *testing.T) { ing := buildIngress() - _, err := GetBoolAnnotation("", nil) + _, err := GetBoolAnnotation("", nil, nil) if err == nil { t.Errorf("expected error but retuned nil") } @@ -59,8 +59,8 @@ func TestGetBoolAnnotation(t *testing.T) { for _, test := range tests { data[GetAnnotationWithPrefix(test.field)] = test.value - - u, err := GetBoolAnnotation(test.field, ing) + ing.SetAnnotations(data) + u, err := GetBoolAnnotation(test.field, ing, nil) if test.expErr { if err == nil { t.Errorf("%v: expected error but retuned nil", test.name) @@ -68,7 +68,7 @@ func TestGetBoolAnnotation(t *testing.T) { continue } if u != test.exp { - t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.name, test.exp, u) + t.Errorf("%v: expected \"%v\" but \"%v\" was returned, %+v", test.name, test.exp, u, ing) } delete(data, test.field) @@ -78,7 +78,7 @@ func TestGetBoolAnnotation(t *testing.T) { func TestGetStringAnnotation(t *testing.T) { ing := buildIngress() - _, err := GetStringAnnotation("", nil) + _, err := GetStringAnnotation("", nil, nil) if err == nil { t.Errorf("expected error but none returned") } @@ -93,14 +93,16 @@ func TestGetStringAnnotation(t *testing.T) { {"valid - A", "string", "A ", "A", false}, {"valid - B", "string", " B", "B", false}, {"empty", "string", " ", "", true}, - {"valid multiline", "string", ` + { + "valid multiline", "string", ` rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break; `, ` rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break; `, - false}, + false, + }, } data := map[string]string{} @@ -109,7 +111,7 @@ rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/serv for _, test := range tests { data[GetAnnotationWithPrefix(test.field)] = test.value - s, err := GetStringAnnotation(test.field, ing) + s, err := GetStringAnnotation(test.field, ing, nil) if test.expErr { if err == nil { t.Errorf("%v: expected error but none returned", test.name) @@ -133,7 +135,7 @@ rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/serv func TestGetFloatAnnotation(t *testing.T) { ing := buildIngress() - _, err := GetFloatAnnotation("", nil) + _, err := GetFloatAnnotation("", nil, nil) if err == nil { t.Errorf("expected error but retuned nil") } @@ -156,7 +158,7 @@ func TestGetFloatAnnotation(t *testing.T) { for _, test := range tests { data[GetAnnotationWithPrefix(test.field)] = test.value - s, err := GetFloatAnnotation(test.field, ing) + s, err := GetFloatAnnotation(test.field, ing, nil) if test.expErr { if err == nil { t.Errorf("%v: expected error but retuned nil", test.name) @@ -174,7 +176,7 @@ func TestGetFloatAnnotation(t *testing.T) { func TestGetIntAnnotation(t *testing.T) { ing := buildIngress() - _, err := GetIntAnnotation("", nil) + _, err := GetIntAnnotation("", nil, nil) if err == nil { t.Errorf("expected error but retuned nil") } @@ -196,7 +198,7 @@ func TestGetIntAnnotation(t *testing.T) { for _, test := range tests { data[GetAnnotationWithPrefix(test.field)] = test.value - s, err := GetIntAnnotation(test.field, ing) + s, err := GetIntAnnotation(test.field, ing, nil) if test.expErr { if err == nil { t.Errorf("%v: expected error but retuned nil", test.name) @@ -213,8 +215,10 @@ func TestGetIntAnnotation(t *testing.T) { func TestStringToURL(t *testing.T) { validURL := "http://bar.foo.com/external-auth" - validParsedURL, _ := url.Parse(validURL) - + validParsedURL, err := url.Parse(validURL) + if err != nil { + t.Errorf("unexpected error: %v", err) + } tests := []struct { title string url string @@ -224,6 +228,7 @@ func TestStringToURL(t *testing.T) { }{ {"empty", "", "url scheme is empty", nil, true}, {"no scheme", "bar", "url scheme is empty", nil, true}, + {"invalid parse", "://lala.com", "://lala.com is not a valid URL: parse \"://lala.com\": missing protocol scheme", nil, true}, {"invalid host", "http://", "url host is empty", nil, true}, {"invalid host (multiple dots)", "http://foo..bar.com", "invalid url host", nil, true}, {"valid URL", validURL, "", validParsedURL, false}, diff --git a/internal/ingress/annotations/parser/validators.go b/internal/ingress/annotations/parser/validators.go new file mode 100644 index 000000000..f66f3c0bc --- /dev/null +++ b/internal/ingress/annotations/parser/validators.go @@ -0,0 +1,239 @@ +/* +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 parser + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "time" + + networking "k8s.io/api/networking/v1" + machineryvalidation "k8s.io/apimachinery/pkg/api/validation" + ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" + "k8s.io/ingress-nginx/internal/net" + "k8s.io/klog/v2" +) + +type AnnotationValidator func(string) error + +const ( + AnnotationRiskLow AnnotationRisk = iota + AnnotationRiskMedium + AnnotationRiskHigh + AnnotationRiskCritical +) + +var ( + alphaNumericChars = `\-\.\_\~a-zA-Z0-9\/:` + extendedAlphaNumeric = alphaNumericChars + ", " + regexEnabledChars = regexp.QuoteMeta(`^$[](){}*+?|&=\`) + urlEnabledChars = regexp.QuoteMeta(`:?&=`) +) + +// IsValidRegex checks if the tested string can be used as a regex, but without any weird character. +// It includes regex characters for paths that may contain regexes +var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$") + +// SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M +var SizeRegex = regexp.MustCompile(`^(?i)\d+[bkmg]?$`) + +// URLRegex is used to validate a URL but with only a specific set of characters: +// It is alphanumericChar + ":", "?", "&" +// A valid URL would be proto://something.com:port/something?arg=param +var ( + // URLIsValidRegex is used on full URLs, containing query strings (:, ? and &) + URLIsValidRegex = regexp.MustCompile("^[" + alphaNumericChars + urlEnabledChars + "]*$") + // BasicChars is alphanumeric and ".", "-", "_", "~" and ":", usually used on simple host:port/path composition. + // This combination can also be used on fields that may contain characters like / (as ns/name) + BasicCharsRegex = regexp.MustCompile("^[/" + alphaNumericChars + "]*$") + // ExtendedChars is alphanumeric and ".", "-", "_", "~" and ":" plus "," and spaces, usually used on simple host:port/path composition + ExtendedCharsRegex = regexp.MustCompile("^[/" + extendedAlphaNumeric + "]*$") + // CharsWithSpace is like basic chars, but includes the space character + CharsWithSpace = regexp.MustCompile("^[/" + alphaNumericChars + " ]*$") + // NGINXVariable allows entries with alphanumeric characters, -, _ and the special "$" + NGINXVariable = regexp.MustCompile(`^[A-Za-z0-9\-\_\$\{\}]*$`) + // RegexPathWithCapture allows entries that SHOULD start with "/" and may contain alphanumeric + capture + // character for regex based paths, like /something/$1/anything/$2 + RegexPathWithCapture = regexp.MustCompile(`^/?[` + alphaNumericChars + `\/\$]*$`) + // HeadersVariable defines a regex that allows headers separated by comma + HeadersVariable = regexp.MustCompile(`^[A-Za-z0-9-_, ]*$`) + // URLWithNginxVariableRegex defines a url that can contain nginx variables. + // It is a risky operation + URLWithNginxVariableRegex = regexp.MustCompile("^[" + extendedAlphaNumeric + urlEnabledChars + "$]*$") +) + +// ValidateArrayOfServerName validates if all fields on a Server name annotation are +// regexes. They can be *.something*, ~^www\d+\.example\.com$ but not fancy character +func ValidateArrayOfServerName(value string) error { + for _, fqdn := range strings.Split(value, ",") { + if err := ValidateServerName(fqdn); err != nil { + return err + } + } + return nil +} + +// ValidateServerName validates if the passed value is an acceptable server name. The server name +// can contain regex characters, as those are accepted values on nginx configuration +func ValidateServerName(value string) error { + value = strings.TrimSpace(value) + if !IsValidRegex.MatchString(value) { + return fmt.Errorf("value %s is invalid server name", value) + } + return nil +} + +// ValidateRegex receives a regex as an argument and uses it to validate +// the value of the field. +// Annotation can define if the spaces should be trimmed before validating the value +func ValidateRegex(regex *regexp.Regexp, removeSpace bool) AnnotationValidator { + return func(s string) error { + if removeSpace { + s = strings.ReplaceAll(s, " ", "") + } + if !regex.MatchString(s) { + return fmt.Errorf("value %s is invalid", s) + } + return nil + } +} + +// ValidateOptions receives an array of valid options that can be the value of annotation. +// If no valid option is found, it will return an error +func ValidateOptions(options []string, caseSensitive, trimSpace bool) AnnotationValidator { + return func(s string) error { + if trimSpace { + s = strings.TrimSpace(s) + } + if !caseSensitive { + s = strings.ToLower(s) + } + for _, option := range options { + if s == option { + return nil + } + } + return fmt.Errorf("value does not match any valid option") + } +} + +// ValidateBool validates if the specified value is a bool +func ValidateBool(value string) error { + _, err := strconv.ParseBool(value) + return err +} + +// ValidateInt validates if the specified value is an integer +func ValidateInt(value string) error { + _, err := strconv.Atoi(value) + return err +} + +// ValidateCIDRs validates if the specified value is an array of IPs and CIDRs +func ValidateCIDRs(value string) error { + _, err := net.ParseCIDRs(value) + return err +} + +// ValidateDuration validates if the specified value is a valid time +func ValidateDuration(value string) error { + _, err := time.ParseDuration(value) + return err +} + +// ValidateNull always return null values and should not be widely used. +// It is used on the "snippet" annotations, as it is up to the admin to allow its +// usage, knowing it can be critical! +func ValidateNull(_ string) error { + return nil +} + +// ValidateServiceName validates if a provided service name is a valid string +func ValidateServiceName(value string) error { + errs := machineryvalidation.NameIsDNS1035Label(value, false) + if len(errs) != 0 { + return fmt.Errorf("annotation does not contain a valid service name: %+v", errs) + } + return nil +} + +// checkAnnotations will check each annotation for: +// 1 - Does it contain the internal validation and docs config? +// 2 - Does the ingress contains annotations? (validate null pointers) +// 3 - Does it contains a validator? Should it contain a validator (not containing is a bug!) +// 4 - Does the annotation contain aliases? So we should use if the alias is defined an the annotation not. +// 4 - Runs the validator on the value +// It will return the full annotation name if all is fine +func checkAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (string, error) { + var validateFunc AnnotationValidator + if fields != nil { + config, ok := fields[name] + if !ok { + return "", fmt.Errorf("annotation does not contain a valid internal configuration, this is an Ingress Controller issue! Please raise an issue on github.com/kubernetes/ingress-nginx") + } + validateFunc = config.Validator + } + + if ing == nil || len(ing.GetAnnotations()) == 0 { + return "", ing_errors.ErrMissingAnnotations + } + + annotationFullName := GetAnnotationWithPrefix(name) + if annotationFullName == "" { + return "", ing_errors.ErrInvalidAnnotationName + } + + annotationValue := ing.GetAnnotations()[annotationFullName] + if fields != nil { + if validateFunc == nil { + return "", fmt.Errorf("annotation does not contain a validator. This is an ingress-controller bug. Please open an issue") + } + if annotationValue == "" { + for _, annotationAlias := range fields[name].AnnotationAliases { + tempAnnotationFullName := GetAnnotationWithPrefix(annotationAlias) + if aliasVal := ing.GetAnnotations()[tempAnnotationFullName]; aliasVal != "" { + annotationValue = aliasVal + annotationFullName = tempAnnotationFullName + break + } + } + } + // We don't run validation against empty values + if EnableAnnotationValidation && annotationValue != "" { + if err := validateFunc(annotationValue); err != nil { + klog.Warningf("validation error on ingress %s/%s: annotation %s contains invalid value %s", ing.GetNamespace(), ing.GetName(), name, annotationValue) + return "", ing_errors.NewValidationError(annotationFullName) + } + } + } + + return annotationFullName, nil +} + +func CheckAnnotationRisk(annotations map[string]string, maxrisk AnnotationRisk, config AnnotationFields) error { + var err error + for annotation := range annotations { + annPure := TrimAnnotationPrefix(annotation) + if cfg, ok := config[annPure]; ok && cfg.Risk > maxrisk { + err = errors.Join(err, fmt.Errorf("annotation %s is too risky for environment", annotation)) + } + } + return err +} diff --git a/internal/ingress/annotations/parser/validators_test.go b/internal/ingress/annotations/parser/validators_test.go new file mode 100644 index 000000000..e7aeb15ca --- /dev/null +++ b/internal/ingress/annotations/parser/validators_test.go @@ -0,0 +1,309 @@ +/* +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 parser + +import ( + "fmt" + "testing" + + networking "k8s.io/api/networking/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestValidateArrayOfServerName(t *testing.T) { + tests := []struct { + name string + value string + wantErr bool + }{ + { + name: "should accept common name", + value: "something.com,anything.com", + wantErr: false, + }, + { + name: "should accept wildcard name", + value: "*.something.com,otherthing.com", + wantErr: false, + }, + { + name: "should allow names with spaces between array and some regexes", + value: `~^www\d+\.example\.com$,something.com`, + wantErr: false, + }, + { + name: "should allow names with regexes", + value: `http://some.test.env.com:2121/$someparam=1&$someotherparam=2`, + wantErr: false, + }, + { + name: "should allow names with wildcard in middle common name", + value: "*.so*mething.com,bla.com", + wantErr: false, + }, + { + name: "should deny names with weird characters", + value: "something.com,lolo;xpto.com,nothing.com", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateArrayOfServerName(tt.value); (err != nil) != tt.wantErr { + t.Errorf("ValidateArrayOfServerName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_checkAnnotation(t *testing.T) { + type args struct { + name string + ing *networking.Ingress + fields AnnotationFields + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "null ingress should error", + want: "", + args: args{ + name: "some-random-annotation", + }, + wantErr: true, + }, + { + name: "not having a validator for a specific annotation is a bug", + want: "", + args: args{ + name: "some-new-invalid-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + GetAnnotationWithPrefix("some-new-invalid-annotation"): "xpto", + }, + }, + }, + fields: AnnotationFields{ + "otherannotation": AnnotationConfig{ + Validator: func(value string) error { return nil }, + }, + }, + }, + wantErr: true, + }, + { + name: "annotationconfig found and no validation func defined on annotation is a bug", + want: "", + args: args{ + name: "some-new-invalid-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + GetAnnotationWithPrefix("some-new-invalid-annotation"): "xpto", + }, + }, + }, + fields: AnnotationFields{ + "some-new-invalid-annotation": AnnotationConfig{}, + }, + }, + wantErr: true, + }, + { + name: "no annotation can turn into a null pointer and should fail", + want: "", + args: args{ + name: "some-new-invalid-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{}, + }, + fields: AnnotationFields{ + "some-new-invalid-annotation": AnnotationConfig{}, + }, + }, + wantErr: true, + }, + { + name: "no AnnotationField config should bypass validations", + want: GetAnnotationWithPrefix("some-valid-annotation"), + args: args{ + name: "some-valid-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + GetAnnotationWithPrefix("some-valid-annotation"): "xpto", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "annotation with invalid value should fail", + want: "", + args: args{ + name: "some-new-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + GetAnnotationWithPrefix("some-new-annotation"): "xpto1", + }, + }, + }, + fields: AnnotationFields{ + "some-new-annotation": AnnotationConfig{ + Validator: func(value string) error { + if value != "xpto" { + return fmt.Errorf("this is an error") + } + return nil + }, + }, + }, + }, + wantErr: true, + }, + { + name: "annotation with valid value should pass", + want: GetAnnotationWithPrefix("some-other-annotation"), + args: args{ + name: "some-other-annotation", + ing: &networking.Ingress{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + GetAnnotationWithPrefix("some-other-annotation"): "xpto", + }, + }, + }, + fields: AnnotationFields{ + "some-other-annotation": AnnotationConfig{ + Validator: func(value string) error { + if value != "xpto" { + return fmt.Errorf("this is an error") + } + return nil + }, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := checkAnnotation(tt.args.name, tt.args.ing, tt.args.fields) + if (err != nil) != tt.wantErr { + t.Errorf("checkAnnotation() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("checkAnnotation() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCheckAnnotationRisk(t *testing.T) { + tests := []struct { + name string + annotations map[string]string + maxrisk AnnotationRisk + config AnnotationFields + wantErr bool + }{ + { + name: "high risk should not be accepted with maximum medium", + maxrisk: AnnotationRiskMedium, + annotations: map[string]string{ + "nginx.ingress.kubernetes.io/bla": "blo", + "nginx.ingress.kubernetes.io/bli": "bl3", + }, + config: AnnotationFields{ + "bla": { + Risk: AnnotationRiskHigh, + }, + "bli": { + Risk: AnnotationRiskMedium, + }, + }, + wantErr: true, + }, + { + name: "high risk should be accepted with maximum critical", + maxrisk: AnnotationRiskCritical, + annotations: map[string]string{ + "nginx.ingress.kubernetes.io/bla": "blo", + "nginx.ingress.kubernetes.io/bli": "bl3", + }, + config: AnnotationFields{ + "bla": { + Risk: AnnotationRiskHigh, + }, + "bli": { + Risk: AnnotationRiskMedium, + }, + }, + wantErr: false, + }, + { + name: "low risk should be accepted with maximum low", + maxrisk: AnnotationRiskLow, + annotations: map[string]string{ + "nginx.ingress.kubernetes.io/bla": "blo", + "nginx.ingress.kubernetes.io/bli": "bl3", + }, + config: AnnotationFields{ + "bla": { + Risk: AnnotationRiskLow, + }, + "bli": { + Risk: AnnotationRiskLow, + }, + }, + wantErr: false, + }, + { + name: "critical risk should be accepted with maximum critical", + maxrisk: AnnotationRiskCritical, + annotations: map[string]string{ + "nginx.ingress.kubernetes.io/bla": "blo", + "nginx.ingress.kubernetes.io/bli": "bl3", + }, + config: AnnotationFields{ + "bla": { + Risk: AnnotationRiskCritical, + }, + "bli": { + Risk: AnnotationRiskCritical, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := CheckAnnotationRisk(tt.annotations, tt.maxrisk, tt.config); (err != nil) != tt.wantErr { + t.Errorf("CheckAnnotationRisk() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/ingress/annotations/portinredirect/main.go b/internal/ingress/annotations/portinredirect/main.go index 25d665558..7392ea3a6 100644 --- a/internal/ingress/annotations/portinredirect/main.go +++ b/internal/ingress/annotations/portinredirect/main.go @@ -23,22 +23,51 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + portsInRedirectAnnotation = "use-port-in-redirects" +) + +var portsInRedirectAnnotations = parser.Annotation{ + Group: "redirect", + Annotations: parser.AnnotationFields{ + portsInRedirectAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Enables or disables specifying the port in absolute redirects issued by nginx.`, + }, + }, +} + type portInRedirect struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new port in redirect annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return portInRedirect{r} + return portInRedirect{ + r: r, + annotationConfig: portsInRedirectAnnotations, + } } // Parse parses the annotations contained in the ingress // rule used to indicate if the redirects must func (a portInRedirect) Parse(ing *networking.Ingress) (interface{}, error) { - up, err := parser.GetBoolAnnotation("use-port-in-redirects", ing) + up, err := parser.GetBoolAnnotation(portsInRedirectAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return a.r.GetDefaultBackend().UsePortInRedirects, nil } return up, nil } + +func (a portInRedirect) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a portInRedirect) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, portsInRedirectAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/portinredirect/main_test.go b/internal/ingress/annotations/portinredirect/main_test.go index 71afd4cdf..f5806e01a 100644 --- a/internal/ingress/annotations/portinredirect/main_test.go +++ b/internal/ingress/annotations/portinredirect/main_test.go @@ -17,7 +17,6 @@ limitations under the License. package portinredirect import ( - "fmt" "testing" api "k8s.io/api/core/v1" @@ -84,23 +83,24 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend { func TestPortInRedirect(t *testing.T) { tests := []struct { title string - usePort *bool + usePort string def bool exp bool }{ - {"false - default false", newFalse(), false, false}, - {"false - default true", newFalse(), true, false}, - {"no annotation - default false", nil, false, false}, - {"no annotation - default true", nil, true, true}, - {"true - default true", newTrue(), true, true}, + {"false - default false", "false", false, false}, + {"false - default true", "false", true, false}, + {"no annotation - default false", "", false, false}, + {"no annotation - default false", "not-a-bool", false, false}, + {"no annotation - default true", "", true, true}, + {"true - default true", "true", true, true}, } for _, test := range tests { ing := buildIngress() data := map[string]string{} - if test.usePort != nil { - data[parser.GetAnnotationWithPrefix("use-port-in-redirects")] = fmt.Sprintf("%v", *test.usePort) + if test.usePort != "" { + data[parser.GetAnnotationWithPrefix(portsInRedirectAnnotation)] = test.usePort } ing.SetAnnotations(data) @@ -118,13 +118,3 @@ func TestPortInRedirect(t *testing.T) { } } } - -func newTrue() *bool { - b := true - return &b -} - -func newFalse() *bool { - b := false - return &b -} diff --git a/internal/ingress/annotations/proxy/main.go b/internal/ingress/annotations/proxy/main.go index 3a89b8855..9d2646261 100644 --- a/internal/ingress/annotations/proxy/main.go +++ b/internal/ingress/annotations/proxy/main.go @@ -17,12 +17,148 @@ limitations under the License. package proxy import ( + "regexp" + networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + proxyConnectTimeoutAnnotation = "proxy-connect-timeout" + proxySendTimeoutAnnotation = "proxy-send-timeout" + proxyReadTimeoutAnnotation = "proxy-read-timeout" + proxyBuffersNumberAnnotation = "proxy-buffers-number" + proxyBufferSizeAnnotation = "proxy-buffer-size" + proxyCookiePathAnnotation = "proxy-cookie-path" + proxyCookieDomainAnnotation = "proxy-cookie-domain" + proxyBodySizeAnnotation = "proxy-body-size" + proxyNextUpstreamAnnotation = "proxy-next-upstream" + proxyNextUpstreamTimeoutAnnotation = "proxy-next-upstream-timeout" + proxyNextUpstreamTriesAnnotation = "proxy-next-upstream-tries" + proxyRequestBufferingAnnotation = "proxy-request-buffering" + proxyRedirectFromAnnotation = "proxy-redirect-from" + proxyRedirectToAnnotation = "proxy-redirect-to" + proxyBufferingAnnotation = "proxy-buffering" + proxyHTTPVersionAnnotation = "proxy-http-version" + proxyMaxTempFileSizeAnnotation = "proxy-max-temp-file-size" //#nosec G101 +) + +var validUpstreamAnnotation = regexp.MustCompile(`^((error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off)\s?)+$`) + +var proxyAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + proxyConnectTimeoutAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows setting the timeout in seconds of the connect operation to the backend.`, + }, + proxySendTimeoutAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows setting the timeout in seconds of the send operation to the backend.`, + }, + proxyReadTimeoutAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation allows setting the timeout in seconds of the read operation to the backend.`, + }, + proxyBuffersNumberAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation sets the number of the buffers in proxy_buffers used for reading the first part of the response received from the proxied server. + By default proxy buffers number is set as 4`, + }, + proxyBufferSizeAnnotation: { + Validator: parser.ValidateRegex(parser.SizeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation sets the size of the buffer proxy_buffer_size used for reading the first part of the response received from the proxied server. + By default proxy buffer size is set as "4k".`, + }, + proxyCookiePathAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation sets a text that should be changed in the path attribute of the "Set-Cookie" header fields of a proxied server response.`, + }, + proxyCookieDomainAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation ets a text that should be changed in the domain attribute of the "Set-Cookie" header fields of a proxied server response.`, + }, + proxyBodySizeAnnotation: { + Validator: parser.ValidateRegex(parser.SizeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows setting the maximum allowed size of a client request body.`, + }, + proxyNextUpstreamAnnotation: { + Validator: parser.ValidateRegex(validUpstreamAnnotation, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines when the next upstream should be used. + This annotation reflect the directive https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream + and only the allowed values on upstream are allowed here.`, + }, + proxyNextUpstreamTimeoutAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation limits the time during which a request can be passed to the next server`, + }, + proxyNextUpstreamTriesAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation limits the number of possible tries for passing a request to the next server`, + }, + proxyRequestBufferingAnnotation: { + Validator: parser.ValidateOptions([]string{"on", "off"}, true, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables or disables buffering of a client request body.`, + }, + proxyRedirectFromAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`, + }, + proxyRedirectToAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`, + }, + proxyBufferingAnnotation: { + Validator: parser.ValidateOptions([]string{"on", "off"}, true, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables or disables buffering of responses from the proxied server. It can be "on" or "off"`, + }, + proxyHTTPVersionAnnotation: { + Validator: parser.ValidateOptions([]string{"1.0", "1.1"}, true, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotations sets the HTTP protocol version for proxying. Can be "1.0" or "1.1".`, + }, + proxyMaxTempFileSizeAnnotation: { + Validator: parser.ValidateRegex(parser.SizeRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines the maximum size of a temporary file when buffering responses.`, + }, + }, +} + // Config returns the proxy timeout to use in the upstream server/s type Config struct { BodySize string `json:"bodySize"` @@ -109,12 +245,16 @@ func (l1 *Config) Equal(l2 *Config) bool { } type proxy struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new reverse proxy configuration annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return proxy{r} + return proxy{ + r: r, + annotationConfig: proxyAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -125,90 +265,99 @@ func (a proxy) Parse(ing *networking.Ingress) (interface{}, error) { var err error - config.ConnectTimeout, err = parser.GetIntAnnotation("proxy-connect-timeout", ing) + config.ConnectTimeout, err = parser.GetIntAnnotation(proxyConnectTimeoutAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ConnectTimeout = defBackend.ProxyConnectTimeout } - config.SendTimeout, err = parser.GetIntAnnotation("proxy-send-timeout", ing) + config.SendTimeout, err = parser.GetIntAnnotation(proxySendTimeoutAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.SendTimeout = defBackend.ProxySendTimeout } - config.ReadTimeout, err = parser.GetIntAnnotation("proxy-read-timeout", ing) + config.ReadTimeout, err = parser.GetIntAnnotation(proxyReadTimeoutAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ReadTimeout = defBackend.ProxyReadTimeout } - config.BuffersNumber, err = parser.GetIntAnnotation("proxy-buffers-number", ing) + config.BuffersNumber, err = parser.GetIntAnnotation(proxyBuffersNumberAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.BuffersNumber = defBackend.ProxyBuffersNumber } - config.BufferSize, err = parser.GetStringAnnotation("proxy-buffer-size", ing) + config.BufferSize, err = parser.GetStringAnnotation(proxyBufferSizeAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.BufferSize = defBackend.ProxyBufferSize } - config.CookiePath, err = parser.GetStringAnnotation("proxy-cookie-path", ing) + config.CookiePath, err = parser.GetStringAnnotation(proxyCookiePathAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.CookiePath = defBackend.ProxyCookiePath } - config.CookieDomain, err = parser.GetStringAnnotation("proxy-cookie-domain", ing) + config.CookieDomain, err = parser.GetStringAnnotation(proxyCookieDomainAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.CookieDomain = defBackend.ProxyCookieDomain } - config.BodySize, err = parser.GetStringAnnotation("proxy-body-size", ing) + config.BodySize, err = parser.GetStringAnnotation(proxyBodySizeAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.BodySize = defBackend.ProxyBodySize } - config.NextUpstream, err = parser.GetStringAnnotation("proxy-next-upstream", ing) + config.NextUpstream, err = parser.GetStringAnnotation(proxyNextUpstreamAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.NextUpstream = defBackend.ProxyNextUpstream } - config.NextUpstreamTimeout, err = parser.GetIntAnnotation("proxy-next-upstream-timeout", ing) + config.NextUpstreamTimeout, err = parser.GetIntAnnotation(proxyNextUpstreamTimeoutAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.NextUpstreamTimeout = defBackend.ProxyNextUpstreamTimeout } - config.NextUpstreamTries, err = parser.GetIntAnnotation("proxy-next-upstream-tries", ing) + config.NextUpstreamTries, err = parser.GetIntAnnotation(proxyNextUpstreamTriesAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.NextUpstreamTries = defBackend.ProxyNextUpstreamTries } - config.RequestBuffering, err = parser.GetStringAnnotation("proxy-request-buffering", ing) + config.RequestBuffering, err = parser.GetStringAnnotation(proxyRequestBufferingAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.RequestBuffering = defBackend.ProxyRequestBuffering } - config.ProxyRedirectFrom, err = parser.GetStringAnnotation("proxy-redirect-from", ing) + config.ProxyRedirectFrom, err = parser.GetStringAnnotation(proxyRedirectFromAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ProxyRedirectFrom = defBackend.ProxyRedirectFrom } - config.ProxyRedirectTo, err = parser.GetStringAnnotation("proxy-redirect-to", ing) + config.ProxyRedirectTo, err = parser.GetStringAnnotation(proxyRedirectToAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ProxyRedirectTo = defBackend.ProxyRedirectTo } - config.ProxyBuffering, err = parser.GetStringAnnotation("proxy-buffering", ing) + config.ProxyBuffering, err = parser.GetStringAnnotation(proxyBufferingAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ProxyBuffering = defBackend.ProxyBuffering } - config.ProxyHTTPVersion, err = parser.GetStringAnnotation("proxy-http-version", ing) + config.ProxyHTTPVersion, err = parser.GetStringAnnotation(proxyHTTPVersionAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ProxyHTTPVersion = defBackend.ProxyHTTPVersion } - config.ProxyMaxTempFileSize, err = parser.GetStringAnnotation("proxy-max-temp-file-size", ing) + config.ProxyMaxTempFileSize, err = parser.GetStringAnnotation(proxyMaxTempFileSizeAnnotation, ing, a.annotationConfig.Annotations) if err != nil { config.ProxyMaxTempFileSize = defBackend.ProxyMaxTempFileSize } return config, nil } + +func (a proxy) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a proxy) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, proxyAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/proxy/main_test.go b/internal/ingress/annotations/proxy/main_test.go index e377ccb19..9446ae970 100644 --- a/internal/ingress/annotations/proxy/main_test.go +++ b/internal/ingress/annotations/proxy/main_test.go @@ -28,6 +28,12 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + off = "off" + proxyHTTPVersion = "1.0" + proxyMaxTempFileSize = "128k" +) + func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ @@ -87,7 +93,7 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend { ProxyNextUpstreamTimeout: 0, ProxyNextUpstreamTries: 3, ProxyRequestBuffering: "on", - ProxyBuffering: "off", + ProxyBuffering: off, ProxyHTTPVersion: "1.1", ProxyMaxTempFileSize: "1024m", } @@ -103,13 +109,13 @@ func TestProxy(t *testing.T) { data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8" data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k" data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k" - data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off" + data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = off data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5" data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3" - data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off" + data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = off data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on" - data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0" - data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k" + data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion + data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize ing.SetAnnotations(data) i, err := NewParser(mockBackend{}).Parse(ing) @@ -138,7 +144,75 @@ func TestProxy(t *testing.T) { if p.BodySize != "2k" { t.Errorf("expected 2k as body-size but returned %v", p.BodySize) } - if p.NextUpstream != "off" { + if p.NextUpstream != off { + t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream) + } + if p.NextUpstreamTimeout != 5 { + t.Errorf("expected 5 as next-upstream-timeout but returned %v", p.NextUpstreamTimeout) + } + if p.NextUpstreamTries != 3 { + t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries) + } + if p.RequestBuffering != off { + t.Errorf("expected off as request-buffering but returned %v", p.RequestBuffering) + } + if p.ProxyBuffering != "on" { + t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering) + } + if p.ProxyHTTPVersion != proxyHTTPVersion { + t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion) + } + if p.ProxyMaxTempFileSize != proxyMaxTempFileSize { + t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize) + } +} + +func TestProxyComplex(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("proxy-connect-timeout")] = "1" + data[parser.GetAnnotationWithPrefix("proxy-send-timeout")] = "2" + data[parser.GetAnnotationWithPrefix("proxy-read-timeout")] = "3" + data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8" + data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k" + data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k" + data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "error http_502" + data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5" + data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3" + data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off" + data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on" + data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion + data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize + ing.SetAnnotations(data) + + i, err := NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Fatalf("unexpected error parsing a valid") + } + p, ok := i.(*Config) + if !ok { + t.Fatalf("expected a Config type") + } + if p.ConnectTimeout != 1 { + t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout) + } + if p.SendTimeout != 2 { + t.Errorf("expected 2 as send-timeout but returned %v", p.SendTimeout) + } + if p.ReadTimeout != 3 { + t.Errorf("expected 3 as read-timeout but returned %v", p.ReadTimeout) + } + if p.BuffersNumber != 8 { + t.Errorf("expected 8 as proxy-buffers-number but returned %v", p.BuffersNumber) + } + if p.BufferSize != "1k" { + t.Errorf("expected 1k as buffer-size but returned %v", p.BufferSize) + } + if p.BodySize != "2k" { + t.Errorf("expected 2k as body-size but returned %v", p.BodySize) + } + if p.NextUpstream != "error http_502" { t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream) } if p.NextUpstreamTimeout != 5 { @@ -153,10 +227,10 @@ func TestProxy(t *testing.T) { if p.ProxyBuffering != "on" { t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering) } - if p.ProxyHTTPVersion != "1.0" { + if p.ProxyHTTPVersion != proxyHTTPVersion { t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion) } - if p.ProxyMaxTempFileSize != "128k" { + if p.ProxyMaxTempFileSize != proxyMaxTempFileSize { t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize) } } diff --git a/internal/ingress/annotations/proxyssl/main.go b/internal/ingress/annotations/proxyssl/main.go index 22f49b3eb..0e854cd21 100644 --- a/internal/ingress/annotations/proxyssl/main.go +++ b/internal/ingress/annotations/proxyssl/main.go @@ -27,6 +27,7 @@ import ( ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/k8s" + "k8s.io/klog/v2" ) const ( @@ -39,9 +40,73 @@ const ( var ( proxySSLOnOffRegex = regexp.MustCompile(`^(on|off)$`) - proxySSLProtocolRegex = regexp.MustCompile(`^(SSLv2|SSLv3|TLSv1|TLSv1\.1|TLSv1\.2|TLSv1\.3)$`) + proxySSLProtocolRegex = regexp.MustCompile(`^(SSLv2|SSLv3|TLSv1|TLSv1\.1|TLSv1\.2|TLSv1\.3| )*$`) + proxySSLCiphersRegex = regexp.MustCompile(`^[A-Za-z0-9\+:\_\-!]*$`) ) +const ( + proxySSLSecretAnnotation = "proxy-ssl-secret" + proxySSLCiphersAnnotation = "proxy-ssl-ciphers" + proxySSLProtocolsAnnotation = "proxy-ssl-protocols" + proxySSLNameAnnotation = "proxy-ssl-name" + proxySSLVerifyAnnotation = "proxy-ssl-verify" + proxySSLVerifyDepthAnnotation = "proxy-ssl-verify-depth" + proxySSLServerNameAnnotation = "proxy-ssl-server-name" +) + +var proxySSLAnnotation = parser.Annotation{ + Group: "proxy", + Annotations: parser.AnnotationFields{ + proxySSLSecretAnnotation: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation specifies a Secret with the certificate tls.crt, key tls.key in PEM format used for authentication to a proxied HTTPS server. + It should also contain trusted CA certificates ca.crt in PEM format used to verify the certificate of the proxied HTTPS server. + This annotation expects the Secret name in the form "namespace/secretName" + Just secrets on the same namespace of the ingress can be used.`, + }, + proxySSLCiphersAnnotation: { + Validator: parser.ValidateRegex(proxySSLCiphersRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation Specifies the enabled ciphers for requests to a proxied HTTPS server. + The ciphers are specified in the format understood by the OpenSSL library.`, + }, + proxySSLProtocolsAnnotation: { + Validator: parser.ValidateRegex(proxySSLProtocolRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables the specified protocols for requests to a proxied HTTPS server.`, + }, + proxySSLNameAnnotation: { + Validator: parser.ValidateServerName, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskHigh, + Documentation: `This annotation allows to set proxy_ssl_name. This allows overriding the server name used to verify the certificate of the proxied HTTPS server. + This value is also passed through SNI when a connection is established to the proxied HTTPS server.`, + }, + proxySSLVerifyAnnotation: { + Validator: parser.ValidateRegex(proxySSLOnOffRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables or disables verification of the proxied HTTPS server certificate. (default: off)`, + }, + proxySSLVerifyDepthAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation Sets the verification depth in the proxied HTTPS server certificates chain. (default: 1).`, + }, + proxySSLServerNameAnnotation: { + Validator: parser.ValidateRegex(proxySSLOnOffRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.`, + }, + }, +} + // Config contains the AuthSSLCert used for mutual authentication // and the configured VerifyDepth type Config struct { @@ -84,12 +149,16 @@ func (pssl1 *Config) Equal(pssl2 *Config) bool { } // NewParser creates a new TLS authentication annotation parser -func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { - return proxySSL{resolver} +func NewParser(r resolver.Resolver) parser.IngressAnnotation { + return proxySSL{ + r: r, + annotationConfig: proxySSLAnnotation, + } } type proxySSL struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } func sortProtocols(protocols string) string { @@ -120,54 +189,78 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) { var err error config := &Config{} - proxysslsecret, err := parser.GetStringAnnotation("proxy-ssl-secret", ing) + proxysslsecret, err := parser.GetStringAnnotation(proxySSLSecretAnnotation, ing, p.annotationConfig.Annotations) if err != nil { return &Config{}, err } - _, _, err = k8s.ParseNameNS(proxysslsecret) + ns, _, err := k8s.ParseNameNS(proxysslsecret) if err != nil { return &Config{}, ing_errors.NewLocationDenied(err.Error()) } + secCfg := p.r.GetSecurityConfiguration() + // We don't accept different namespaces for secrets. + if !secCfg.AllowCrossNamespaceResources && ns != ing.Namespace { + return &Config{}, ing_errors.NewLocationDenied("cross namespace secrets are not supported") + } + proxyCert, err := p.r.GetAuthCertificate(proxysslsecret) if err != nil { e := fmt.Errorf("error obtaining certificate: %w", err) - return &Config{}, ing_errors.LocationDenied{Reason: e} + return &Config{}, ing_errors.LocationDeniedError{Reason: e} } config.AuthSSLCert = *proxyCert - config.Ciphers, err = parser.GetStringAnnotation("proxy-ssl-ciphers", ing) + config.Ciphers, err = parser.GetStringAnnotation(proxySSLCiphersAnnotation, ing, p.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("invalid value passed to proxy-ssl-ciphers, defaulting to %s", defaultProxySSLCiphers) + } config.Ciphers = defaultProxySSLCiphers } - config.Protocols, err = parser.GetStringAnnotation("proxy-ssl-protocols", ing) + config.Protocols, err = parser.GetStringAnnotation(proxySSLProtocolsAnnotation, ing, p.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("invalid value passed to proxy-ssl-protocols, defaulting to %s", defaultProxySSLProtocols) + } config.Protocols = defaultProxySSLProtocols } else { config.Protocols = sortProtocols(config.Protocols) } - config.ProxySSLName, err = parser.GetStringAnnotation("proxy-ssl-name", ing) + config.ProxySSLName, err = parser.GetStringAnnotation(proxySSLNameAnnotation, ing, p.annotationConfig.Annotations) if err != nil { + if ing_errors.IsValidationError(err) { + klog.Warningf("invalid value passed to proxy-ssl-name, defaulting to empty") + } config.ProxySSLName = "" } - config.Verify, err = parser.GetStringAnnotation("proxy-ssl-verify", ing) + config.Verify, err = parser.GetStringAnnotation(proxySSLVerifyAnnotation, ing, p.annotationConfig.Annotations) if err != nil || !proxySSLOnOffRegex.MatchString(config.Verify) { config.Verify = defaultProxySSLVerify } - config.VerifyDepth, err = parser.GetIntAnnotation("proxy-ssl-verify-depth", ing) + config.VerifyDepth, err = parser.GetIntAnnotation(proxySSLVerifyDepthAnnotation, ing, p.annotationConfig.Annotations) if err != nil || config.VerifyDepth == 0 { config.VerifyDepth = defaultProxySSLVerifyDepth } - config.ProxySSLServerName, err = parser.GetStringAnnotation("proxy-ssl-server-name", ing) + config.ProxySSLServerName, err = parser.GetStringAnnotation(proxySSLServerNameAnnotation, ing, p.annotationConfig.Annotations) if err != nil || !proxySSLOnOffRegex.MatchString(config.ProxySSLServerName) { config.ProxySSLServerName = defaultProxySSLServerName } return config, nil } + +func (p proxySSL) GetDocumentation() parser.AnnotationFields { + return p.annotationConfig.Annotations +} + +func (p proxySSL) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(p.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, proxySSLAnnotation.Annotations) +} diff --git a/internal/ingress/annotations/proxyssl/main_test.go b/internal/ingress/annotations/proxyssl/main_test.go index 29949796c..a20c6813d 100644 --- a/internal/ingress/annotations/proxyssl/main_test.go +++ b/internal/ingress/annotations/proxyssl/main_test.go @@ -27,6 +27,14 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + defaultDemoSecret = "default/demo-secret" + proxySslCiphers = "HIGH:-SHA" + off = "off" + sslServerName = "w00t" + defaultProtocol = "SSLv2 TLSv1 TLSv1.2 TLSv1.3" +) + func buildIngress() *networking.Ingress { defaultBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ @@ -77,28 +85,27 @@ type mockSecret struct { // GetAuthCertificate from mockSecret mocks the GetAuthCertificate for backend certificate authentication func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { - if name != "default/demo-secret" { + if name != defaultDemoSecret { return nil, errors.Errorf("there is no secret with name %v", name) } return &resolver.AuthSSLCert{ - Secret: "default/demo-secret", + Secret: defaultDemoSecret, CAFileName: "/ssl/ca.crt", CASHA: "abc", }, nil - } func TestAnnotations(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = "default/demo-secret" - data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = "HIGH:-SHA" + data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = defaultDemoSecret + data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = proxySslCiphers data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host" data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv1.3 SSLv2 TLSv1 TLSv1.2" data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "on" - data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "off" + data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = off data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "3" @@ -115,7 +122,7 @@ func TestAnnotations(t *testing.T) { t.Errorf("expected *Config but got %v", u) } - secret, err := fakeSecret.GetAuthCertificate("default/demo-secret") + secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret) if err != nil { t.Errorf("unexpected error getting secret %v", err) } @@ -123,11 +130,11 @@ func TestAnnotations(t *testing.T) { if u.AuthSSLCert.Secret != secret.Secret { t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret) } - if u.Ciphers != "HIGH:-SHA" { - t.Errorf("expected %v but got %v", "HIGH:-SHA", u.Ciphers) + if u.Ciphers != proxySslCiphers { + t.Errorf("expected %v but got %v", proxySslCiphers, u.Ciphers) } - if u.Protocols != "SSLv2 TLSv1 TLSv1.2 TLSv1.3" { - t.Errorf("expected %v but got %v", "SSLv2 TLSv1 TLSv1.2 TLSv1.3", u.Protocols) + if u.Protocols != defaultProtocol { + t.Errorf("expected %v but got %v", defaultProtocol, u.Protocols) } if u.Verify != "on" { t.Errorf("expected %v but got %v", "on", u.Verify) @@ -141,7 +148,6 @@ func TestAnnotations(t *testing.T) { if u.ProxySSLServerName != "on" { t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName) } - } func TestInvalidAnnotations(t *testing.T) { @@ -172,11 +178,11 @@ func TestInvalidAnnotations(t *testing.T) { } // Invalid optional Annotations - data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = "default/demo-secret" + data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = defaultDemoSecret data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv111 SSLv1" - data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "w00t" - data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "w00t" - data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "w00t" + data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = sslServerName + data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = sslServerName + data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = sslServerName data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "abcd" ing.SetAnnotations(data) @@ -207,21 +213,15 @@ func TestEquals(t *testing.T) { cfg1 := &Config{} cfg2 := &Config{} - // Same config - result := cfg1.Equal(cfg1) - if result != true { - t.Errorf("Expected true") - } - // compare nil - result = cfg1.Equal(nil) + result := cfg1.Equal(nil) if result != false { t.Errorf("Expected false") } // Different Certs sslCert1 := resolver.AuthSSLCert{ - Secret: "default/demo-secret", + Secret: defaultDemoSecret, CAFileName: "/ssl/ca.crt", CASHA: "abc", } @@ -240,7 +240,7 @@ func TestEquals(t *testing.T) { // Different Ciphers cfg1.Ciphers = "DEFAULT" - cfg2.Ciphers = "HIGH:-SHA" + cfg2.Ciphers = proxySslCiphers result = cfg1.Equal(cfg2) if result != false { t.Errorf("Expected false") @@ -248,22 +248,22 @@ func TestEquals(t *testing.T) { cfg2.Ciphers = "DEFAULT" // Different Protocols - cfg1.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3" + cfg1.Protocols = defaultProtocol cfg2.Protocols = "SSLv3 TLSv1 TLSv1.2 TLSv1.3" result = cfg1.Equal(cfg2) if result != false { t.Errorf("Expected false") } - cfg2.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3" + cfg2.Protocols = defaultProtocol // Different Verify - cfg1.Verify = "off" + cfg1.Verify = off cfg2.Verify = "on" result = cfg1.Equal(cfg2) if result != false { t.Errorf("Expected false") } - cfg2.Verify = "off" + cfg2.Verify = off // Different VerifyDepth cfg1.VerifyDepth = 1 @@ -275,13 +275,13 @@ func TestEquals(t *testing.T) { cfg2.VerifyDepth = 1 // Different ProxySSLServerName - cfg1.ProxySSLServerName = "off" + cfg1.ProxySSLServerName = off cfg2.ProxySSLServerName = "on" result = cfg1.Equal(cfg2) if result != false { t.Errorf("Expected false") } - cfg2.ProxySSLServerName = "off" + cfg2.ProxySSLServerName = off // Equal Configs result = cfg1.Equal(cfg2) diff --git a/internal/ingress/annotations/ratelimit/main.go b/internal/ingress/annotations/ratelimit/main.go index 84a5f10f0..e79c698bf 100644 --- a/internal/ingress/annotations/ratelimit/main.go +++ b/internal/ingress/annotations/ratelimit/main.go @@ -24,6 +24,7 @@ import ( networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/net" "k8s.io/ingress-nginx/pkg/util/sets" @@ -58,7 +59,7 @@ type Config struct { ID string `json:"id"` - Whitelist []string `json:"whitelist"` + Allowlist []string `json:"allowlist"` } // Equal tests for equality between two RateLimit types @@ -90,11 +91,11 @@ func (rt1 *Config) Equal(rt2 *Config) bool { if rt1.Name != rt2.Name { return false } - if len(rt1.Whitelist) != len(rt2.Whitelist) { + if len(rt1.Allowlist) != len(rt2.Allowlist) { return false } - return sets.StringElementsMatch(rt1.Whitelist, rt2.Whitelist) + return sets.StringElementsMatch(rt1.Allowlist, rt2.Allowlist) } // Zone returns information about the NGINX rate limit (limit_req_zone) @@ -131,43 +132,121 @@ func (z1 *Zone) Equal(z2 *Zone) bool { return true } +const ( + limitRateAnnotation = "limit-rate" + limitRateAfterAnnotation = "limit-rate-after" + limitRateRPMAnnotation = "limit-rpm" + limitRateRPSAnnotation = "limit-rps" + limitRateConnectionsAnnotation = "limit-connections" + limitRateBurstMultiplierAnnotation = "limit-burst-multiplier" + limitWhitelistAnnotation = "limit-whitelist" // This annotation is an alias for limit-allowlist + limitAllowlistAnnotation = "limit-allowlist" +) + +var rateLimitAnnotations = parser.Annotation{ + Group: "rate-limit", + Annotations: parser.AnnotationFields{ + limitRateAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Limits the rate of response transmission to a client. The rate is specified in bytes per second. + The zero value disables rate limiting. The limit is set per a request, and so if a client simultaneously opens two connections, the overall rate will be twice as much as the specified limit. + References: https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate`, + }, + limitRateAfterAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Sets the initial amount after which the further transmission of a response to a client will be rate limited.`, + }, + limitRateRPMAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Requests per minute that will be allowed.`, + }, + limitRateRPSAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Requests per second that will be allowed.`, + }, + limitRateConnectionsAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Number of connections that will be allowed`, + }, + limitRateBurstMultiplierAnnotation: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `Burst multiplier for a limit-rate enabled location.`, + }, + limitAllowlistAnnotation: { + Validator: parser.ValidateCIDRs, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `List of CIDR/IP addresses that will not be rate-limited.`, + AnnotationAliases: []string{limitWhitelistAnnotation}, + }, + }, +} + type ratelimit struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new ratelimit annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return ratelimit{r} + return ratelimit{ + r: r, + annotationConfig: rateLimitAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress // rule used to rewrite the defined paths func (a ratelimit) Parse(ing *networking.Ingress) (interface{}, error) { defBackend := a.r.GetDefaultBackend() - lr, err := parser.GetIntAnnotation("limit-rate", ing) + lr, err := parser.GetIntAnnotation(limitRateAnnotation, ing, a.annotationConfig.Annotations) if err != nil { lr = defBackend.LimitRate } - lra, err := parser.GetIntAnnotation("limit-rate-after", ing) + lra, err := parser.GetIntAnnotation(limitRateAfterAnnotation, ing, a.annotationConfig.Annotations) if err != nil { lra = defBackend.LimitRateAfter } - rpm, _ := parser.GetIntAnnotation("limit-rpm", ing) - rps, _ := parser.GetIntAnnotation("limit-rps", ing) - conn, _ := parser.GetIntAnnotation("limit-connections", ing) - burstMultiplier, err := parser.GetIntAnnotation("limit-burst-multiplier", ing) + rpm, err := parser.GetIntAnnotation(limitRateRPMAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && errors.IsValidationError(err) { + return nil, err + } + rps, err := parser.GetIntAnnotation(limitRateRPSAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && errors.IsValidationError(err) { + return nil, err + } + conn, err := parser.GetIntAnnotation(limitRateConnectionsAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && errors.IsValidationError(err) { + return nil, err + } + burstMultiplier, err := parser.GetIntAnnotation(limitRateBurstMultiplierAnnotation, ing, a.annotationConfig.Annotations) if err != nil { burstMultiplier = defBurst } - val, _ := parser.GetStringAnnotation("limit-whitelist", ing) - - cidrs, err := net.ParseCIDRs(val) - if err != nil { + val, err := parser.GetStringAnnotation(limitAllowlistAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && errors.IsValidationError(err) { return nil, err } + cidrs, errCidr := net.ParseCIDRs(val) + if errCidr != nil { + return nil, errCidr + } + if rpm == 0 && rps == 0 && conn == 0 { return &Config{ Connections: Zone{}, @@ -203,11 +282,20 @@ func (a ratelimit) Parse(ing *networking.Ingress) (interface{}, error) { LimitRateAfter: lra, Name: zoneName, ID: encode(zoneName), - Whitelist: cidrs, + Allowlist: cidrs, }, nil } func encode(s string) string { str := base64.URLEncoding.EncodeToString([]byte(s)) - return strings.Replace(str, "=", "", -1) + return strings.ReplaceAll(str, "=", "") +} + +func (a ratelimit) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a ratelimit) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, rateLimitAnnotations.Annotations) } diff --git a/internal/ingress/annotations/ratelimit/main_test.go b/internal/ingress/annotations/ratelimit/main_test.go index 9f101cc3b..d3a2cc0e9 100644 --- a/internal/ingress/annotations/ratelimit/main_test.go +++ b/internal/ingress/annotations/ratelimit/main_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/defaults" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) @@ -85,8 +86,8 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend { func TestWithoutAnnotations(t *testing.T) { ing := buildIngress() _, err := NewParser(mockBackend{}).Parse(ing) - if err != nil { - t.Error("unexpected error with ingress without annotations") + if err != nil && !errors.IsMissingAnnotations(err) { + t.Errorf("unexpected error with ingress without annotations: %s", err) } } @@ -94,22 +95,22 @@ func TestRateLimiting(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("limit-connections")] = "0" - data[parser.GetAnnotationWithPrefix("limit-rps")] = "0" - data[parser.GetAnnotationWithPrefix("limit-rpm")] = "0" + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "0" + data[parser.GetAnnotationWithPrefix(limitRateRPSAnnotation)] = "0" + data[parser.GetAnnotationWithPrefix(limitRateRPMAnnotation)] = "0" ing.SetAnnotations(data) _, err := NewParser(mockBackend{}).Parse(ing) if err != nil { - t.Errorf("unexpected error with invalid limits (0)") + t.Errorf("unexpected error with invalid limits (0): %s", err) } data = map[string]string{} - data[parser.GetAnnotationWithPrefix("limit-connections")] = "5" - data[parser.GetAnnotationWithPrefix("limit-rps")] = "100" - data[parser.GetAnnotationWithPrefix("limit-rpm")] = "10" - data[parser.GetAnnotationWithPrefix("limit-rate-after")] = "100" - data[parser.GetAnnotationWithPrefix("limit-rate")] = "10" + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "5" + data[parser.GetAnnotationWithPrefix(limitRateRPSAnnotation)] = "100" + data[parser.GetAnnotationWithPrefix(limitRateRPMAnnotation)] = "10" + data[parser.GetAnnotationWithPrefix(limitRateAfterAnnotation)] = "100" + data[parser.GetAnnotationWithPrefix(limitRateAnnotation)] = "10" ing.SetAnnotations(data) @@ -147,12 +148,12 @@ func TestRateLimiting(t *testing.T) { } data = map[string]string{} - data[parser.GetAnnotationWithPrefix("limit-connections")] = "5" - data[parser.GetAnnotationWithPrefix("limit-rps")] = "100" - data[parser.GetAnnotationWithPrefix("limit-rpm")] = "10" - data[parser.GetAnnotationWithPrefix("limit-rate-after")] = "100" - data[parser.GetAnnotationWithPrefix("limit-rate")] = "10" - data[parser.GetAnnotationWithPrefix("limit-burst-multiplier")] = "3" + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "5" + data[parser.GetAnnotationWithPrefix(limitRateRPSAnnotation)] = "100" + data[parser.GetAnnotationWithPrefix(limitRateRPMAnnotation)] = "10" + data[parser.GetAnnotationWithPrefix(limitRateAfterAnnotation)] = "100" + data[parser.GetAnnotationWithPrefix(limitRateAnnotation)] = "10" + data[parser.GetAnnotationWithPrefix(limitRateBurstMultiplierAnnotation)] = "3" ing.SetAnnotations(data) @@ -189,3 +190,61 @@ func TestRateLimiting(t *testing.T) { t.Errorf("expected 10 in limit by limitrate but %v was returned", rateLimit.LimitRate) } } + +func TestAnnotationCIDR(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "5" + data[parser.GetAnnotationWithPrefix(limitAllowlistAnnotation)] = "192.168.0.5, 192.168.50.32/24" + ing.SetAnnotations(data) + + i, err := NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + rateLimit, ok := i.(*Config) + if !ok { + t.Errorf("expected a RateLimit type") + } + if len(rateLimit.Allowlist) != 2 { + t.Errorf("expected 2 cidrs in limit by ip but %v was returned", len(rateLimit.Allowlist)) + } + + data = map[string]string{} + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "5" + data[parser.GetAnnotationWithPrefix(limitWhitelistAnnotation)] = "192.168.0.5, 192.168.50.32/24, 10.10.10.1" + ing.SetAnnotations(data) + + i, err = NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + rateLimit, ok = i.(*Config) + if !ok { + t.Errorf("expected a RateLimit type") + } + if len(rateLimit.Allowlist) != 3 { + t.Errorf("expected 3 cidrs in limit by ip but %v was returned", len(rateLimit.Allowlist)) + } + + // Parent annotation surpasses any alias + data = map[string]string{} + data[parser.GetAnnotationWithPrefix(limitRateConnectionsAnnotation)] = "5" + data[parser.GetAnnotationWithPrefix(limitWhitelistAnnotation)] = "192.168.0.5, 192.168.50.32/24, 10.10.10.1" + data[parser.GetAnnotationWithPrefix(limitAllowlistAnnotation)] = "192.168.0.9" + ing.SetAnnotations(data) + + i, err = NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + rateLimit, ok = i.(*Config) + if !ok { + t.Errorf("expected a RateLimit type") + } + if len(rateLimit.Allowlist) != 1 { + t.Errorf("expected 1 cidrs in limit by ip but %v was returned", len(rateLimit.Allowlist)) + } +} diff --git a/internal/ingress/annotations/redirect/redirect.go b/internal/ingress/annotations/redirect/redirect.go index 11b08a4a2..b58e35171 100644 --- a/internal/ingress/annotations/redirect/redirect.go +++ b/internal/ingress/annotations/redirect/redirect.go @@ -37,13 +37,56 @@ type Config struct { FromToWWW bool `json:"fromToWWW"` } +const ( + fromToWWWRedirAnnotation = "from-to-www-redirect" + temporalRedirectAnnotation = "temporal-redirect" + permanentRedirectAnnotation = "permanent-redirect" + permanentRedirectAnnotationCode = "permanent-redirect-code" +) + +var redirectAnnotations = parser.Annotation{ + Group: "redirect", + Annotations: parser.AnnotationFields{ + fromToWWWRedirAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `In some scenarios is required to redirect from www.domain.com to domain.com or vice versa. To enable this feature use this annotation.`, + }, + temporalRedirectAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated + Documentation: `This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream. + For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`, + }, + permanentRedirectAnnotation: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated + Documentation: `This annotation allows to return a permanent redirect (Return Code 301) instead of sending data to the upstream. + For example setting this annotation https://www.google.com would redirect everything to Google with a code 301`, + }, + permanentRedirectAnnotationCode: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options + Documentation: `This annotation allows you to modify the status code used for permanent redirects.`, + }, + }, +} + type redirect struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new redirect annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return redirect{r} + return redirect{ + r: r, + annotationConfig: redirectAnnotations, + } } // Parse parses the annotations contained in the ingress @@ -51,9 +94,12 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation { // If the Ingress contains both annotations the execution order is // temporal and then permanent func (r redirect) Parse(ing *networking.Ingress) (interface{}, error) { - r3w, _ := parser.GetBoolAnnotation("from-to-www-redirect", ing) + r3w, err := parser.GetBoolAnnotation(fromToWWWRedirAnnotation, ing, r.annotationConfig.Annotations) + if err != nil && !errors.IsMissingAnnotations(err) { + return nil, err + } - tr, err := parser.GetStringAnnotation("temporal-redirect", ing) + tr, err := parser.GetStringAnnotation(temporalRedirectAnnotation, ing, r.annotationConfig.Annotations) if err != nil && !errors.IsMissingAnnotations(err) { return nil, err } @@ -70,12 +116,12 @@ func (r redirect) Parse(ing *networking.Ingress) (interface{}, error) { }, nil } - pr, err := parser.GetStringAnnotation("permanent-redirect", ing) + pr, err := parser.GetStringAnnotation(permanentRedirectAnnotation, ing, r.annotationConfig.Annotations) if err != nil && !errors.IsMissingAnnotations(err) { return nil, err } - prc, err := parser.GetIntAnnotation("permanent-redirect-code", ing) + prc, err := parser.GetIntAnnotation(permanentRedirectAnnotationCode, ing, r.annotationConfig.Annotations) if err != nil && !errors.IsMissingAnnotations(err) { return nil, err } @@ -127,3 +173,12 @@ func isValidURL(s string) error { return nil } + +func (r redirect) GetDocumentation() parser.AnnotationFields { + return r.annotationConfig.Annotations +} + +func (r redirect) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(r.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, redirectAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/redirect/redirect_test.go b/internal/ingress/annotations/redirect/redirect_test.go index b5a87a5d3..bd2f98211 100644 --- a/internal/ingress/annotations/redirect/redirect_test.go +++ b/internal/ingress/annotations/redirect/redirect_test.go @@ -43,7 +43,7 @@ func TestPermanentRedirectWithDefaultCode(t *testing.T) { ing := new(networking.Ingress) data := make(map[string]string, 1) - data[parser.GetAnnotationWithPrefix("permanent-redirect")] = defRedirectURL + data[parser.GetAnnotationWithPrefix(permanentRedirectAnnotation)] = defRedirectURL ing.SetAnnotations(data) i, err := rp.Parse(ing) @@ -81,8 +81,8 @@ func TestPermanentRedirectWithCustomCode(t *testing.T) { ing := new(networking.Ingress) data := make(map[string]string, 2) - data[parser.GetAnnotationWithPrefix("permanent-redirect")] = defRedirectURL - data[parser.GetAnnotationWithPrefix("permanent-redirect-code")] = strconv.Itoa(tc.input) + data[parser.GetAnnotationWithPrefix(permanentRedirectAnnotation)] = defRedirectURL + data[parser.GetAnnotationWithPrefix(permanentRedirectAnnotationCode)] = strconv.Itoa(tc.input) ing.SetAnnotations(data) i, err := rp.Parse(ing) @@ -112,8 +112,8 @@ func TestTemporalRedirect(t *testing.T) { ing := new(networking.Ingress) data := make(map[string]string, 1) - data[parser.GetAnnotationWithPrefix("from-to-www-redirect")] = "true" - data[parser.GetAnnotationWithPrefix("temporal-redirect")] = defRedirectURL + data[parser.GetAnnotationWithPrefix(fromToWWWRedirAnnotation)] = "true" + data[parser.GetAnnotationWithPrefix(temporalRedirectAnnotation)] = defRedirectURL ing.SetAnnotations(data) i, err := rp.Parse(ing) @@ -136,7 +136,6 @@ func TestTemporalRedirect(t *testing.T) { } func TestIsValidURL(t *testing.T) { - invalid := "ok.com" urlParse, err := url.Parse(invalid) if err != nil { diff --git a/internal/ingress/annotations/rewrite/main.go b/internal/ingress/annotations/rewrite/main.go index f92d508dc..cd9ed3993 100644 --- a/internal/ingress/annotations/rewrite/main.go +++ b/internal/ingress/annotations/rewrite/main.go @@ -27,6 +27,59 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + rewriteTargetAnnotation = "rewrite-target" + sslRedirectAnnotation = "ssl-redirect" + preserveTrailingSlashAnnotation = "preserve-trailing-slash" + forceSSLRedirectAnnotation = "force-ssl-redirect" + useRegexAnnotation = "use-regex" + appRootAnnotation = "app-root" +) + +var rewriteAnnotations = parser.Annotation{ + Group: "rewrite", + Annotations: parser.AnnotationFields{ + rewriteTargetAnnotation: { + Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows to specify the target URI where the traffic must be redirected. It can contain regular characters and captured + groups specified as '$1', '$2', etc.`, + }, + sslRedirectAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines if the location section is only accessible via SSL`, + }, + preserveTrailingSlashAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines if the trailing slash should be preserved in the URI with 'ssl-redirect'`, + }, + forceSSLRedirectAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation forces the redirection to HTTPS even if the Ingress is not TLS Enabled`, + }, + useRegexAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines if the paths defined on an Ingress use regular expressions. To use regex on path + the pathType should also be defined as 'ImplementationSpecific'.`, + }, + appRootAnnotation: { + Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the Application Root that the Controller must redirect if it's in / context`, + }, + }, +} + // Config describes the per location redirect config type Config struct { // Target URI where the traffic must be redirected @@ -71,12 +124,16 @@ func (r1 *Config) Equal(r2 *Config) bool { } type rewrite struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new rewrite annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return rewrite{r} + return rewrite{ + r: r, + annotationConfig: rewriteAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -85,24 +142,45 @@ func (a rewrite) Parse(ing *networking.Ingress) (interface{}, error) { var err error config := &Config{} - config.Target, _ = parser.GetStringAnnotation("rewrite-target", ing) - config.SSLRedirect, err = parser.GetBoolAnnotation("ssl-redirect", ing) + config.Target, err = parser.GetStringAnnotation(rewriteTargetAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%sis invalid, defaulting to empty", rewriteTargetAnnotation) + } + config.Target = "" + } + config.SSLRedirect, err = parser.GetBoolAnnotation(sslRedirectAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%sis invalid, defaulting to '%s'", sslRedirectAnnotation, a.r.GetDefaultBackend().SSLRedirect) + } config.SSLRedirect = a.r.GetDefaultBackend().SSLRedirect } - config.PreserveTrailingSlash, err = parser.GetBoolAnnotation("preserve-trailing-slash", ing) + config.PreserveTrailingSlash, err = parser.GetBoolAnnotation(preserveTrailingSlashAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%sis invalid, defaulting to '%s'", preserveTrailingSlashAnnotation, a.r.GetDefaultBackend().PreserveTrailingSlash) + } config.PreserveTrailingSlash = a.r.GetDefaultBackend().PreserveTrailingSlash } - config.ForceSSLRedirect, err = parser.GetBoolAnnotation("force-ssl-redirect", ing) + config.ForceSSLRedirect, err = parser.GetBoolAnnotation(forceSSLRedirectAnnotation, ing, a.annotationConfig.Annotations) if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%sis invalid, defaulting to '%s'", forceSSLRedirectAnnotation, a.r.GetDefaultBackend().ForceSSLRedirect) + } config.ForceSSLRedirect = a.r.GetDefaultBackend().ForceSSLRedirect } - config.UseRegex, _ = parser.GetBoolAnnotation("use-regex", ing) + config.UseRegex, err = parser.GetBoolAnnotation(useRegexAnnotation, ing, a.annotationConfig.Annotations) + if err != nil { + if errors.IsValidationError(err) { + klog.Warningf("%sis invalid, defaulting to 'false'", useRegexAnnotation) + } + config.UseRegex = false + } - config.AppRoot, err = parser.GetStringAnnotation("app-root", ing) + config.AppRoot, err = parser.GetStringAnnotation(appRootAnnotation, ing, a.annotationConfig.Annotations) if err != nil { if !errors.IsMissingAnnotations(err) && !errors.IsInvalidContent(err) { klog.Warningf("Annotation app-root contains an invalid value: %v", err) @@ -126,3 +204,12 @@ func (a rewrite) Parse(ing *networking.Ingress) (interface{}, error) { return config, nil } + +func (a rewrite) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a rewrite) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, rewriteAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/rewrite/main_test.go b/internal/ingress/annotations/rewrite/main_test.go index c2cb42c78..b68b901b4 100644 --- a/internal/ingress/annotations/rewrite/main_test.go +++ b/internal/ingress/annotations/rewrite/main_test.go @@ -120,7 +120,10 @@ func TestSSLRedirect(t *testing.T) { data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute ing.SetAnnotations(data) - i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) + i, err := NewParser(mockBackend{redirect: true}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } redirect, ok := i.(*Config) if !ok { t.Errorf("expected a Redirect type") @@ -129,10 +132,43 @@ func TestSSLRedirect(t *testing.T) { t.Errorf("Expected true but returned false") } + data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/$1/abc/$2" + ing.SetAnnotations(data) + + i, err = NewParser(mockBackend{redirect: true}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + redirect, ok = i.(*Config) + if !ok { + t.Errorf("expected a Redirect type") + } + if redirect.Target != "/xpto/$1/abc/$2" { + t.Errorf("Expected /xpto/$1/abc/$2 but returned %s", redirect.Target) + } + + data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/xas{445}" + ing.SetAnnotations(data) + + i, err = NewParser(mockBackend{redirect: true}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + redirect, ok = i.(*Config) + if !ok { + t.Errorf("expected a Redirect type") + } + if redirect.Target != "" { + t.Errorf("Expected empty rewrite target but returned %s", redirect.Target) + } + data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false" ing.SetAnnotations(data) - i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) + i, err = NewParser(mockBackend{redirect: false}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } redirect, ok = i.(*Config) if !ok { t.Errorf("expected a Redirect type") @@ -149,7 +185,10 @@ func TestForceSSLRedirect(t *testing.T) { data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute ing.SetAnnotations(data) - i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) + i, err := NewParser(mockBackend{redirect: true}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } redirect, ok := i.(*Config) if !ok { t.Errorf("expected a Redirect type") @@ -161,7 +200,10 @@ func TestForceSSLRedirect(t *testing.T) { data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true" ing.SetAnnotations(data) - i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) + i, err = NewParser(mockBackend{redirect: false}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } redirect, ok = i.(*Config) if !ok { t.Errorf("expected a Redirect type") @@ -170,6 +212,7 @@ func TestForceSSLRedirect(t *testing.T) { t.Errorf("Expected true but returned false") } } + func TestAppRoot(t *testing.T) { ap := NewParser(mockBackend{redirect: true}) @@ -217,7 +260,10 @@ func TestUseRegex(t *testing.T) { data[parser.GetAnnotationWithPrefix("use-regex")] = "true" ing.SetAnnotations(data) - i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) + i, err := NewParser(mockBackend{redirect: true}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } redirect, ok := i.(*Config) if !ok { t.Errorf("expected a App Context") diff --git a/internal/ingress/annotations/satisfy/main.go b/internal/ingress/annotations/satisfy/main.go index 0d4fd4ff6..13a4532c1 100644 --- a/internal/ingress/annotations/satisfy/main.go +++ b/internal/ingress/annotations/satisfy/main.go @@ -23,18 +23,40 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + satisfyAnnotation = "satisfy" +) + +var satisfyAnnotations = parser.Annotation{ + Group: "authentication", + Annotations: parser.AnnotationFields{ + satisfyAnnotation: { + Validator: parser.ValidateOptions([]string{"any", "all"}, true, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `By default, a request would need to satisfy all authentication requirements in order to be allowed. + By using this annotation, requests that satisfy either any or all authentication requirements are allowed, based on the configuration value. + Valid options are "all" and "any"`, + }, + }, +} + type satisfy struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new SATISFY annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return satisfy{r} + return satisfy{ + r: r, + annotationConfig: satisfyAnnotations, + } } // Parse parses annotation contained in the ingress func (s satisfy) Parse(ing *networking.Ingress) (interface{}, error) { - satisfy, err := parser.GetStringAnnotation("satisfy", ing) + satisfy, err := parser.GetStringAnnotation(satisfyAnnotation, ing, s.annotationConfig.Annotations) if err != nil || (satisfy != "any" && satisfy != "all") { satisfy = "" @@ -42,3 +64,12 @@ func (s satisfy) Parse(ing *networking.Ingress) (interface{}, error) { return satisfy, nil } + +func (s satisfy) GetDocumentation() parser.AnnotationFields { + return s.annotationConfig.Annotations +} + +func (s satisfy) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, satisfyAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/satisfy/main_test.go b/internal/ingress/annotations/satisfy/main_test.go index b45205d9f..c8d5782f9 100644 --- a/internal/ingress/annotations/satisfy/main_test.go +++ b/internal/ingress/annotations/satisfy/main_test.go @@ -83,7 +83,7 @@ func TestSatisfyParser(t *testing.T) { annotations := map[string]string{} for input, expected := range data { - annotations[parser.GetAnnotationWithPrefix("satisfy")] = input + annotations[parser.GetAnnotationWithPrefix(satisfyAnnotation)] = input ing.SetAnnotations(annotations) satisfyt, err := NewParser(&resolver.Mock{}).Parse(ing) diff --git a/internal/ingress/annotations/serversnippet/main.go b/internal/ingress/annotations/serversnippet/main.go index 70f0af8e5..aa15608d0 100644 --- a/internal/ingress/annotations/serversnippet/main.go +++ b/internal/ingress/annotations/serversnippet/main.go @@ -23,18 +23,47 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + serverSnippetAnnotation = "server-snippet" +) + +var serverSnippetAnnotations = parser.Annotation{ + Group: "snippets", + Annotations: parser.AnnotationFields{ + serverSnippetAnnotation: { + Validator: parser.ValidateNull, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskCritical, // Critical, this annotation is not validated at all and allows arbitrary configutations + Documentation: `This annotation allows setting a custom NGINX configuration on a server block. This annotation does not contain any validation and it's usage is not recommended!`, + }, + }, +} + type serverSnippet struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new server snippet annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return serverSnippet{r} + return serverSnippet{ + r: r, + annotationConfig: serverSnippetAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to indicate if the location/s contains a fragment of // configuration to be included inside the paths of the rules func (a serverSnippet) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("server-snippet", ing) + return parser.GetStringAnnotation(serverSnippetAnnotation, ing, a.annotationConfig.Annotations) +} + +func (a serverSnippet) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a serverSnippet) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, serverSnippetAnnotations.Annotations) } diff --git a/internal/ingress/annotations/serversnippet/main_test.go b/internal/ingress/annotations/serversnippet/main_test.go index c9e0979ad..72e757dc1 100644 --- a/internal/ingress/annotations/serversnippet/main_test.go +++ b/internal/ingress/annotations/serversnippet/main_test.go @@ -27,7 +27,7 @@ import ( ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("server-snippet") + annotation := parser.GetAnnotationWithPrefix(serverSnippetAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { @@ -54,6 +54,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/annotations/serviceupstream/main.go b/internal/ingress/annotations/serviceupstream/main.go index 4a4879682..d1851bc7b 100644 --- a/internal/ingress/annotations/serviceupstream/main.go +++ b/internal/ingress/annotations/serviceupstream/main.go @@ -24,19 +24,39 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + serviceUpstreamAnnotation = "service-upstream" +) + +var serviceUpstreamAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + serviceUpstreamAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, // Critical, this annotation is not validated at all and allows arbitrary configutations + Documentation: `This annotation makes NGINX use Service's Cluster IP and Port instead of Endpoints as the backend endpoints`, + }, + }, +} + type serviceUpstream struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new serviceUpstream annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return serviceUpstream{r} + return serviceUpstream{ + r: r, + annotationConfig: serviceUpstreamAnnotations, + } } func (s serviceUpstream) Parse(ing *networking.Ingress) (interface{}, error) { defBackend := s.r.GetDefaultBackend() - val, err := parser.GetBoolAnnotation("service-upstream", ing) + val, err := parser.GetBoolAnnotation(serviceUpstreamAnnotation, ing, s.annotationConfig.Annotations) // A missing annotation is not a problem, just use the default if err == errors.ErrMissingAnnotations { return defBackend.ServiceUpstream, nil @@ -44,3 +64,12 @@ func (s serviceUpstream) Parse(ing *networking.Ingress) (interface{}, error) { return val, nil } + +func (s serviceUpstream) GetDocumentation() parser.AnnotationFields { + return s.annotationConfig.Annotations +} + +func (s serviceUpstream) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, serviceUpstreamAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/serviceupstream/main_test.go b/internal/ingress/annotations/serviceupstream/main_test.go index b773e9723..e6216ee5a 100644 --- a/internal/ingress/annotations/serviceupstream/main_test.go +++ b/internal/ingress/annotations/serviceupstream/main_test.go @@ -74,10 +74,13 @@ func TestIngressAnnotationServiceUpstreamEnabled(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("service-upstream")] = "true" + data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "true" ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } enabled, ok := val.(bool) if !ok { t.Errorf("expected a bool type") @@ -93,10 +96,13 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) { // Test with explicitly set to false data := map[string]string{} - data[parser.GetAnnotationWithPrefix("service-upstream")] = "false" + data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false" ing.SetAnnotations(data) - val, _ := NewParser(&resolver.Mock{}).Parse(ing) + val, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } enabled, ok := val.(bool) if !ok { t.Errorf("expected a bool type") @@ -110,7 +116,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) { data = map[string]string{} ing.SetAnnotations(data) - val, _ = NewParser(&resolver.Mock{}).Parse(ing) + val, err = NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } enabled, ok = val.(bool) if !ok { t.Errorf("expected a bool type") @@ -137,7 +146,10 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend { func TestParseAnnotationsWithDefaultConfig(t *testing.T) { ing := buildIngress() - val, _ := NewParser(mockBackend{}).Parse(ing) + val, err := NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } enabled, ok := val.(bool) if !ok { @@ -155,10 +167,13 @@ func TestParseAnnotationsOverridesDefaultConfig(t *testing.T) { ing := buildIngress() data := map[string]string{} - data[parser.GetAnnotationWithPrefix("service-upstream")] = "false" + data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false" ing.SetAnnotations(data) - val, _ := NewParser(mockBackend{}).Parse(ing) + val, err := NewParser(mockBackend{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error: %v", err) + } enabled, ok := val.(bool) if !ok { diff --git a/internal/ingress/annotations/sessionaffinity/main.go b/internal/ingress/annotations/sessionaffinity/main.go index 98a0d64f8..bee4a2094 100644 --- a/internal/ingress/annotations/sessionaffinity/main.go +++ b/internal/ingress/annotations/sessionaffinity/main.go @@ -63,11 +63,95 @@ const ( // This is used to control the cookie change after request failure annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure" + + cookieAffinity = "cookie" ) -var ( - affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`) -) +var sessionAffinityAnnotations = parser.Annotation{ + Group: "affinity", + Annotations: parser.AnnotationFields{ + annotationAffinityType: { + Validator: parser.ValidateOptions([]string{cookieAffinity}, true, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation enables and sets the affinity type in all Upstreams of an Ingress. This way, a request will always be directed to the same upstream server. The only affinity type available for NGINX is cookie`, + }, + annotationAffinityMode: { + Validator: parser.ValidateOptions([]string{"balanced", "persistent"}, true, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the stickiness of a session. + Setting this to balanced (default) will redistribute some sessions if a deployment gets scaled up, therefore rebalancing the load on the servers. + Setting this to persistent will not rebalance sessions to new servers, therefore providing maximum stickiness.`, + }, + annotationAffinityCanaryBehavior: { + Validator: parser.ValidateOptions([]string{"sticky", "legacy"}, true, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation defines the behavior of canaries when session affinity is enabled. + Setting this to sticky (default) will ensure that users that were served by canaries, will continue to be served by canaries. + Setting this to legacy will restore original canary behavior, when session affinity was ignored.`, + }, + annotationAffinityCookieName: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation allows to specify the name of the cookie that will be used to route the requests`, + }, + annotationAffinityCookieSecure: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation set the cookie as secure regardless the protocol of the incoming request`, + }, + annotationAffinityCookieExpires: { + Validator: parser.ValidateRegex(affinityCookieExpiresRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation is a legacy version of "session-cookie-max-age" for compatibility with older browsers, generates an "Expires" cookie directive by adding the seconds to the current date`, + }, + annotationAffinityCookieMaxAge: { + Validator: parser.ValidateRegex(affinityCookieExpiresRegex, false), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation sets the time until the cookie expires`, + }, + annotationAffinityCookiePath: { + Validator: parser.ValidateRegex(parser.URLIsValidRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the Path that will be set on the cookie (required if your Ingress paths use regular expressions)`, + }, + annotationAffinityCookieDomain: { + Validator: parser.ValidateRegex(parser.BasicCharsRegex, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation defines the Domain attribute of the sticky cookie.`, + }, + annotationAffinityCookieSameSite: { + Validator: parser.ValidateOptions([]string{"none", "lax", "strict"}, false, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation is used to apply a SameSite attribute to the sticky cookie. + Browser accepted values are None, Lax, and Strict`, + }, + annotationAffinityCookieConditionalSameSiteNone: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation is used to omit SameSite=None from browsers with SameSite attribute incompatibilities`, + }, + annotationAffinityCookieChangeOnFailure: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation, when set to false will send request to upstream pointed by sticky cookie even if previous attempt failed. + When set to true and previous attempt failed, sticky cookie will be changed to point to another upstream.`, + }, + }, +} + +var affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`) // Config describes the per ingress session affinity config type Config struct { @@ -102,6 +186,11 @@ type Cookie struct { ConditionalSameSiteNone bool `json:"conditional-samesite-none"` } +type affinity struct { + r resolver.Resolver + annotationConfig parser.Annotation +} + // cookieAffinityParse gets the annotation values related to Cookie Affinity // It also sets default values when no value or incorrect value is found func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie { @@ -109,50 +198,50 @@ func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie { cookie := &Cookie{} - cookie.Name, err = parser.GetStringAnnotation(annotationAffinityCookieName, ing) + cookie.Name, err = parser.GetStringAnnotation(annotationAffinityCookieName, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieName, "default", defaultAffinityCookieName) cookie.Name = defaultAffinityCookieName } - cookie.Expires, err = parser.GetStringAnnotation(annotationAffinityCookieExpires, ing) + cookie.Expires, err = parser.GetStringAnnotation(annotationAffinityCookieExpires, ing, a.annotationConfig.Annotations) if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.Expires) { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieExpires) cookie.Expires = "" } - cookie.MaxAge, err = parser.GetStringAnnotation(annotationAffinityCookieMaxAge, ing) + cookie.MaxAge, err = parser.GetStringAnnotation(annotationAffinityCookieMaxAge, ing, a.annotationConfig.Annotations) if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.MaxAge) { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieMaxAge) cookie.MaxAge = "" } - cookie.Path, err = parser.GetStringAnnotation(annotationAffinityCookiePath, ing) + cookie.Path, err = parser.GetStringAnnotation(annotationAffinityCookiePath, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookiePath) } - cookie.Domain, err = parser.GetStringAnnotation(annotationAffinityCookieDomain, ing) + cookie.Domain, err = parser.GetStringAnnotation(annotationAffinityCookieDomain, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieDomain) } - cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing) + cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieSameSite) } - cookie.Secure, err = parser.GetBoolAnnotation(annotationAffinityCookieSecure, ing) + cookie.Secure, err = parser.GetBoolAnnotation(annotationAffinityCookieSecure, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieSecure) } - cookie.ConditionalSameSiteNone, err = parser.GetBoolAnnotation(annotationAffinityCookieConditionalSameSiteNone, ing) + cookie.ConditionalSameSiteNone, err = parser.GetBoolAnnotation(annotationAffinityCookieConditionalSameSiteNone, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieConditionalSameSiteNone) } - cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing) + cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing, a.annotationConfig.Annotations) if err != nil { klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieChangeOnFailure) } @@ -162,11 +251,10 @@ func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie { // NewParser creates a new Affinity annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return affinity{r} -} - -type affinity struct { - r resolver.Resolver + return affinity{ + r: r, + annotationConfig: sessionAffinityAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -174,28 +262,27 @@ type affinity struct { func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) { cookie := &Cookie{} // Check the type of affinity that will be used - at, err := parser.GetStringAnnotation(annotationAffinityType, ing) + at, err := parser.GetStringAnnotation(annotationAffinityType, ing, a.annotationConfig.Annotations) if err != nil { at = "" } // Check the affinity mode that will be used - am, err := parser.GetStringAnnotation(annotationAffinityMode, ing) + am, err := parser.GetStringAnnotation(annotationAffinityMode, ing, a.annotationConfig.Annotations) if err != nil { am = "" } - cb, err := parser.GetStringAnnotation(annotationAffinityCanaryBehavior, ing) + cb, err := parser.GetStringAnnotation(annotationAffinityCanaryBehavior, ing, a.annotationConfig.Annotations) if err != nil { cb = "" } switch at { - case "cookie": + case cookieAffinity: cookie = a.cookieAffinityParse(ing) default: klog.V(3).InfoS("No default affinity found", "ingress", ing.Name) - } return &Config{ @@ -205,3 +292,12 @@ func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) { Cookie: *cookie, }, nil } + +func (a affinity) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a affinity) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, sessionAffinityAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/sessionaffinity/main_test.go b/internal/ingress/annotations/sessionaffinity/main_test.go index cffe57fec..4b7ea5e61 100644 --- a/internal/ingress/annotations/sessionaffinity/main_test.go +++ b/internal/ingress/annotations/sessionaffinity/main_test.go @@ -79,11 +79,16 @@ func TestIngressAffinityCookieConfig(t *testing.T) { data[parser.GetAnnotationWithPrefix(annotationAffinityCookieMaxAge)] = "3000" data[parser.GetAnnotationWithPrefix(annotationAffinityCookiePath)] = "/foo" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieDomain)] = "foo.bar" + data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSameSite)] = "Strict" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieChangeOnFailure)] = "true" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true" ing.SetAnnotations(data) - affin, _ := NewParser(&resolver.Mock{}).Parse(ing) + affin, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing annotations: %v", err) + } + nginxAffinity, ok := affin.(*Config) if !ok { t.Errorf("expected a Config type") @@ -117,6 +122,10 @@ func TestIngressAffinityCookieConfig(t *testing.T) { t.Errorf("expected foo.bar as session-cookie-domain but returned %v", nginxAffinity.Cookie.Domain) } + if nginxAffinity.Cookie.SameSite != "Strict" { + t.Errorf("expected Strict as session-cookie-same-site but returned %v", nginxAffinity.Cookie.SameSite) + } + if !nginxAffinity.Cookie.ChangeOnFailure { t.Errorf("expected change of failure parameter set to true but returned %v", nginxAffinity.Cookie.ChangeOnFailure) } diff --git a/internal/ingress/annotations/snippet/main.go b/internal/ingress/annotations/snippet/main.go index 93ec70cf9..2406093c5 100644 --- a/internal/ingress/annotations/snippet/main.go +++ b/internal/ingress/annotations/snippet/main.go @@ -23,18 +23,47 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + configurationSnippetAnnotation = "configuration-snippet" +) + +var configurationSnippetAnnotations = parser.Annotation{ + Group: "snippets", + Annotations: parser.AnnotationFields{ + configurationSnippetAnnotation: { + Validator: parser.ValidateNull, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskCritical, // Critical, this annotation is not validated at all and allows arbitrary configutations + Documentation: `This annotation allows setting a custom NGINX configuration on a location block. This annotation does not contain any validation and it's usage is not recommended!`, + }, + }, +} + type snippet struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new CORS annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return snippet{r} + return snippet{ + r: r, + annotationConfig: configurationSnippetAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to indicate if the location/s contains a fragment of // configuration to be included inside the paths of the rules func (a snippet) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("configuration-snippet", ing) + return parser.GetStringAnnotation(configurationSnippetAnnotation, ing, a.annotationConfig.Annotations) +} + +func (a snippet) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a snippet) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, configurationSnippetAnnotations.Annotations) } diff --git a/internal/ingress/annotations/snippet/main_test.go b/internal/ingress/annotations/snippet/main_test.go index 0defc3c1f..b29b2950d 100644 --- a/internal/ingress/annotations/snippet/main_test.go +++ b/internal/ingress/annotations/snippet/main_test.go @@ -27,7 +27,7 @@ import ( ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("configuration-snippet") + annotation := parser.GetAnnotationWithPrefix(configurationSnippetAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { @@ -54,6 +54,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/annotations/sslcipher/main.go b/internal/ingress/annotations/sslcipher/main.go index e4e5baad2..685ef90bf 100644 --- a/internal/ingress/annotations/sslcipher/main.go +++ b/internal/ingress/annotations/sslcipher/main.go @@ -17,14 +17,45 @@ limitations under the License. package sslcipher import ( + "regexp" + networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + sslPreferServerCipherAnnotation = "ssl-prefer-server-ciphers" + sslCipherAnnotation = "ssl-ciphers" +) + +// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" +var regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`) + +var sslCipherAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + sslPreferServerCipherAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `The following annotation will set the ssl_prefer_server_ciphers directive at the server level. + This configuration specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.`, + }, + sslCipherAnnotation: { + Validator: parser.ValidateRegex(regexValidSSLCipher, true), + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, + Documentation: `Using this annotation will set the ssl_ciphers directive at the server level. This configuration is active for all the paths in the host.`, + }, + }, +} + type sslCipher struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config contains the ssl-ciphers & ssl-prefer-server-ciphers configuration @@ -35,7 +66,10 @@ type Config struct { // NewParser creates a new sslCipher annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return sslCipher{r} + return sslCipher{ + r: r, + annotationConfig: sslCipherAnnotations, + } } // Parse parses the annotations contained in the ingress rule @@ -45,7 +79,7 @@ func (sc sslCipher) Parse(ing *networking.Ingress) (interface{}, error) { var err error var sslPreferServerCiphers bool - sslPreferServerCiphers, err = parser.GetBoolAnnotation("ssl-prefer-server-ciphers", ing) + sslPreferServerCiphers, err = parser.GetBoolAnnotation(sslPreferServerCipherAnnotation, ing, sc.annotationConfig.Annotations) if err != nil { config.SSLPreferServerCiphers = "" } else { @@ -56,7 +90,19 @@ func (sc sslCipher) Parse(ing *networking.Ingress) (interface{}, error) { } } - config.SSLCiphers, _ = parser.GetStringAnnotation("ssl-ciphers", ing) + config.SSLCiphers, err = parser.GetStringAnnotation(sslCipherAnnotation, ing, sc.annotationConfig.Annotations) + if err != nil && !errors.IsInvalidContent(err) && !errors.IsMissingAnnotations(err) { + return config, err + } return config, nil } + +func (sc sslCipher) GetDocumentation() parser.AnnotationFields { + return sc.annotationConfig.Annotations +} + +func (sc sslCipher) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(sc.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, sslCipherAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/sslcipher/main_test.go b/internal/ingress/annotations/sslcipher/main_test.go index 6eb9ec0c2..20cb4d7af 100644 --- a/internal/ingress/annotations/sslcipher/main_test.go +++ b/internal/ingress/annotations/sslcipher/main_test.go @@ -33,22 +33,27 @@ func TestParse(t *testing.T) { t.Fatalf("expected a parser.IngressAnnotation but returned nil") } - annotationSSLCiphers := parser.GetAnnotationWithPrefix("ssl-ciphers") - annotationSSLPreferServerCiphers := parser.GetAnnotationWithPrefix("ssl-prefer-server-ciphers") + annotationSSLCiphers := parser.GetAnnotationWithPrefix(sslCipherAnnotation) + annotationSSLPreferServerCiphers := parser.GetAnnotationWithPrefix(sslPreferServerCipherAnnotation) testCases := []struct { annotations map[string]string expected Config + expectErr bool }{ - {map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", ""}}, - {map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"}, - Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""}}, - {map[string]string{annotationSSLCiphers: ""}, Config{"", ""}}, - {map[string]string{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}}, - {map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}}, - {map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", annotationSSLPreferServerCiphers: "true"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", "on"}}, - {map[string]string{}, Config{"", ""}}, - {nil, Config{"", ""}}, + {map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", ""}, false}, + { + map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"}, + Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""}, + false, + }, + {map[string]string{annotationSSLCiphers: ""}, Config{"", ""}, false}, + {map[string]string{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}, false}, + {map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}, false}, + {map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", annotationSSLPreferServerCiphers: "true"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", "on"}, false}, + {map[string]string{annotationSSLCiphers: "ALL:SOMETHING:;locationXPTO"}, Config{"", ""}, true}, + {map[string]string{}, Config{"", ""}, false}, + {nil, Config{"", ""}, false}, } ing := &networking.Ingress{ @@ -59,10 +64,13 @@ func TestParse(t *testing.T) { Spec: networking.IngressSpec{}, } - for _, testCase := range testCases { + for i, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) - if !reflect.DeepEqual(result, &testCase.expected) { + result, err := ap.Parse(ing) + if (err != nil) != testCase.expectErr { + t.Fatalf("expected error: %t got error: %t err value: %s. %+v", testCase.expectErr, err != nil, err, testCase.annotations) + } + if !reflect.DeepEqual(result, &testCases[i].expected) { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) } } diff --git a/internal/ingress/annotations/sslpassthrough/main.go b/internal/ingress/annotations/sslpassthrough/main.go index d1def7172..c06db8715 100644 --- a/internal/ingress/annotations/sslpassthrough/main.go +++ b/internal/ingress/annotations/sslpassthrough/main.go @@ -24,13 +24,33 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + sslPassthroughAnnotation = "ssl-passthrough" +) + +var sslPassthroughAnnotations = parser.Annotation{ + Group: "", // TBD + Annotations: parser.AnnotationFields{ + sslPassthroughAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskLow, // Low, as it allows regexes but on a very limited set + Documentation: `This annotation instructs the controller to send TLS connections directly to the backend instead of letting NGINX decrypt the communication.`, + }, + }, +} + type sslpt struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new SSL passthrough annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return sslpt{r} + return sslpt{ + r: r, + annotationConfig: sslPassthroughAnnotations, + } } // ParseAnnotations parses the annotations contained in the ingress @@ -40,5 +60,14 @@ func (a sslpt) Parse(ing *networking.Ingress) (interface{}, error) { return false, ing_errors.ErrMissingAnnotations } - return parser.GetBoolAnnotation("ssl-passthrough", ing) + return parser.GetBoolAnnotation(sslPassthroughAnnotation, ing, a.annotationConfig.Annotations) +} + +func (a sslpt) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a sslpt) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, sslPassthroughAnnotations.Annotations) } diff --git a/internal/ingress/annotations/sslpassthrough/main_test.go b/internal/ingress/annotations/sslpassthrough/main_test.go index 5cf2f979a..b712fda19 100644 --- a/internal/ingress/annotations/sslpassthrough/main_test.go +++ b/internal/ingress/annotations/sslpassthrough/main_test.go @@ -54,7 +54,7 @@ func TestParseAnnotations(t *testing.T) { } data := map[string]string{} - data[parser.GetAnnotationWithPrefix("ssl-passthrough")] = "true" + data[parser.GetAnnotationWithPrefix(sslPassthroughAnnotation)] = "true" ing.SetAnnotations(data) // test ingress using the annotation without a TLS section _, err = NewParser(&resolver.Mock{}).Parse(ing) diff --git a/internal/ingress/annotations/streamsnippet/main.go b/internal/ingress/annotations/streamsnippet/main.go index fb22f754c..71ff3b140 100644 --- a/internal/ingress/annotations/streamsnippet/main.go +++ b/internal/ingress/annotations/streamsnippet/main.go @@ -23,18 +23,47 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + streamSnippetAnnotation = "stream-snippet" +) + +var streamSnippetAnnotations = parser.Annotation{ + Group: "snippets", + Annotations: parser.AnnotationFields{ + streamSnippetAnnotation: { + Validator: parser.ValidateNull, + Scope: parser.AnnotationScopeIngress, + Risk: parser.AnnotationRiskCritical, // Critical, this annotation is not validated at all and allows arbitrary configutations + Documentation: `This annotation allows setting a custom NGINX configuration on a stream block. This annotation does not contain any validation and it's usage is not recommended!`, + }, + }, +} + type streamSnippet struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new server snippet annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return streamSnippet{r} + return streamSnippet{ + r: r, + annotationConfig: streamSnippetAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to indicate if the location/s contains a fragment of // configuration to be included inside the paths of the rules func (a streamSnippet) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("stream-snippet", ing) + return parser.GetStringAnnotation("stream-snippet", ing, a.annotationConfig.Annotations) +} + +func (a streamSnippet) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a streamSnippet) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, streamSnippetAnnotations.Annotations) } diff --git a/internal/ingress/annotations/streamsnippet/main_test.go b/internal/ingress/annotations/streamsnippet/main_test.go index 0b8e3e3aa..e8a1ff8d0 100644 --- a/internal/ingress/annotations/streamsnippet/main_test.go +++ b/internal/ingress/annotations/streamsnippet/main_test.go @@ -27,7 +27,7 @@ import ( ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("stream-snippet") + annotation := parser.GetAnnotationWithPrefix(streamSnippetAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { @@ -38,7 +38,8 @@ func TestParse(t *testing.T) { annotations map[string]string expected string }{ - {map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"}, + { + map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"}, "server { listen: 8000; proxy_pass 127.0.0.1:80}", }, {map[string]string{annotation: "false"}, "false"}, @@ -56,6 +57,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/annotations/upstreamhashby/main.go b/internal/ingress/annotations/upstreamhashby/main.go index e6bbca6c3..25cc88b8e 100644 --- a/internal/ingress/annotations/upstreamhashby/main.go +++ b/internal/ingress/annotations/upstreamhashby/main.go @@ -17,14 +17,54 @@ limitations under the License. package upstreamhashby import ( + "regexp" + networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + upstreamHashByAnnotation = "upstream-hash-by" + upstreamHashBySubsetAnnotation = "upstream-hash-by-subset" + upstreamHashBySubsetSize = "upstream-hash-by-subset-size" +) + +var ( + specialChars = regexp.QuoteMeta("_${}") + hashByRegex = regexp.MustCompilePOSIX(`^[A-Za-z0-9\-` + specialChars + `]*$`) +) + +var upstreamHashByAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + upstreamHashByAnnotation: { + Validator: parser.ValidateRegex(hashByRegex, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskHigh, // High, this annotation allows accessing NGINX variables + Documentation: `This annotation defines the nginx variable, text value or any combination thereof to use for consistent hashing. + For example: nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri" or nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri$host" or nginx.ingress.kubernetes.io/upstream-hash-by: "${request_uri}-text-value" to consistently hash upstream requests by the current request URI.`, + }, + upstreamHashBySubsetAnnotation: { + Validator: parser.ValidateBool, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation maps requests to subset of nodes instead of a single one.`, + }, + upstreamHashBySubsetSize: { + Validator: parser.ValidateInt, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, + Documentation: `This annotation determines the size of each subset (default 3)`, + }, + }, +} + type upstreamhashby struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // Config contains the Consistent hash configuration to be used in the Ingress @@ -36,14 +76,26 @@ type Config struct { // NewParser creates a new UpstreamHashBy annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return upstreamhashby{r} + return upstreamhashby{ + r: r, + annotationConfig: upstreamHashByAnnotations, + } } // Parse parses the annotations contained in the ingress rule func (a upstreamhashby) Parse(ing *networking.Ingress) (interface{}, error) { - upstreamHashBy, _ := parser.GetStringAnnotation("upstream-hash-by", ing) - upstreamHashBySubset, _ := parser.GetBoolAnnotation("upstream-hash-by-subset", ing) - upstreamHashbySubsetSize, _ := parser.GetIntAnnotation("upstream-hash-by-subset-size", ing) + upstreamHashBy, err := parser.GetStringAnnotation(upstreamHashByAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && !errors.IsMissingAnnotations(err) { + return nil, err + } + upstreamHashBySubset, err := parser.GetBoolAnnotation(upstreamHashBySubsetAnnotation, ing, a.annotationConfig.Annotations) + if err != nil && !errors.IsMissingAnnotations(err) { + return nil, err + } + upstreamHashbySubsetSize, err := parser.GetIntAnnotation(upstreamHashBySubsetSize, ing, a.annotationConfig.Annotations) + if err != nil && !errors.IsMissingAnnotations(err) { + return nil, err + } if upstreamHashbySubsetSize == 0 { upstreamHashbySubsetSize = 3 @@ -51,3 +103,12 @@ func (a upstreamhashby) Parse(ing *networking.Ingress) (interface{}, error) { return &Config{upstreamHashBy, upstreamHashBySubset, upstreamHashbySubsetSize}, nil } + +func (a upstreamhashby) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a upstreamhashby) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, upstreamHashByAnnotations.Annotations) +} diff --git a/internal/ingress/annotations/upstreamhashby/main_test.go b/internal/ingress/annotations/upstreamhashby/main_test.go index d2c2644ca..bdbd9c350 100644 --- a/internal/ingress/annotations/upstreamhashby/main_test.go +++ b/internal/ingress/annotations/upstreamhashby/main_test.go @@ -27,7 +27,7 @@ import ( ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("upstream-hash-by") + annotation := parser.GetAnnotationWithPrefix(upstreamHashByAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { @@ -37,12 +37,15 @@ func TestParse(t *testing.T) { testCases := []struct { annotations map[string]string expected string + expectErr bool }{ - {map[string]string{annotation: "$request_uri"}, "$request_uri"}, - {map[string]string{annotation: "$request_uri$scheme"}, "$request_uri$scheme"}, - {map[string]string{annotation: "false"}, "false"}, - {map[string]string{}, ""}, - {nil, ""}, + {map[string]string{annotation: "$request_URI"}, "$request_URI", false}, + {map[string]string{annotation: "$request_uri$scheme"}, "$request_uri$scheme", false}, + {map[string]string{annotation: "xpto;[]"}, "", true}, + {map[string]string{annotation: "lalal${scheme_test}"}, "lalal${scheme_test}", false}, + {map[string]string{annotation: "false"}, "false", false}, + {map[string]string{}, "", false}, + {nil, "", false}, } ing := &networking.Ingress{ @@ -55,14 +58,19 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) - result, _ := ap.Parse(ing) - uc, ok := result.(*Config) - if !ok { - t.Fatalf("expected a Config type") + result, err := ap.Parse(ing) + if (err != nil) != testCase.expectErr { + t.Fatalf("expected error: %t got error: %t err value: %s. %+v", testCase.expectErr, err != nil, err, testCase.annotations) } + if !testCase.expectErr { + uc, ok := result.(*Config) + if !ok { + t.Fatalf("expected a Config type") + } - if uc.UpstreamHashBy != testCase.expected { - t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) + if uc.UpstreamHashBy != testCase.expected { + t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) + } } } } diff --git a/internal/ingress/annotations/upstreamvhost/main.go b/internal/ingress/annotations/upstreamvhost/main.go index 2eed5607e..052ca2344 100644 --- a/internal/ingress/annotations/upstreamvhost/main.go +++ b/internal/ingress/annotations/upstreamvhost/main.go @@ -23,18 +23,48 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + upstreamVhostAnnotation = "upstream-vhost" +) + +var upstreamVhostAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + upstreamVhostAnnotation: { + Validator: parser.ValidateServerName, + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskLow, // Low, as it allows regexes but on a very limited set + Documentation: `This configuration setting allows you to control the value for host in the following statement: proxy_set_header Host $host, which forms part of the location block. + This is useful if you need to call the upstream server by something other than $host`, + }, + }, +} + type upstreamVhost struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new upstream VHost annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return upstreamVhost{r} + return upstreamVhost{ + r: r, + annotationConfig: upstreamVhostAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to indicate if the location/s contains a fragment of // configuration to be included inside the paths of the rules func (a upstreamVhost) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("upstream-vhost", ing) + return parser.GetStringAnnotation(upstreamVhostAnnotation, ing, a.annotationConfig.Annotations) +} + +func (a upstreamVhost) GetDocumentation() parser.AnnotationFields { + return a.annotationConfig.Annotations +} + +func (a upstreamVhost) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, upstreamVhostAnnotations.Annotations) } diff --git a/internal/ingress/annotations/upstreamvhost/main_test.go b/internal/ingress/annotations/upstreamvhost/main_test.go index 130d745ee..87324b181 100644 --- a/internal/ingress/annotations/upstreamvhost/main_test.go +++ b/internal/ingress/annotations/upstreamvhost/main_test.go @@ -36,7 +36,7 @@ func TestParse(t *testing.T) { } data := map[string]string{} - data[parser.GetAnnotationWithPrefix("upstream-vhost")] = "ok.com" + data[parser.GetAnnotationWithPrefix(upstreamVhostAnnotation)] = "ok.com" ing.SetAnnotations(data) diff --git a/internal/ingress/annotations/xforwardedprefix/main.go b/internal/ingress/annotations/xforwardedprefix/main.go index 60eed8773..54bb7199a 100644 --- a/internal/ingress/annotations/xforwardedprefix/main.go +++ b/internal/ingress/annotations/xforwardedprefix/main.go @@ -23,17 +23,47 @@ import ( "k8s.io/ingress-nginx/internal/ingress/resolver" ) +const ( + xForwardedForPrefixAnnotation = "x-forwarded-prefix" +) + +var xForwardedForAnnotations = parser.Annotation{ + Group: "backend", + Annotations: parser.AnnotationFields{ + xForwardedForPrefixAnnotation: { + Validator: parser.ValidateRegex(parser.RegexPathWithCapture, true), + Scope: parser.AnnotationScopeLocation, + Risk: parser.AnnotationRiskMedium, + Documentation: `This annotation can be used to add the non-standard X-Forwarded-Prefix header to the upstream request with a string value. It can + contain regular characters and captured groups specified as '$1', '$2', etc.`, + }, + }, +} + type xforwardedprefix struct { - r resolver.Resolver + r resolver.Resolver + annotationConfig parser.Annotation } // NewParser creates a new xforwardedprefix annotation parser func NewParser(r resolver.Resolver) parser.IngressAnnotation { - return xforwardedprefix{r} + return xforwardedprefix{ + r: r, + annotationConfig: xForwardedForAnnotations, + } } // Parse parses the annotations contained in the ingress rule // used to add an x-forwarded-prefix header to the request -func (cbbs xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) { - return parser.GetStringAnnotation("x-forwarded-prefix", ing) +func (x xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) { + return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, x.annotationConfig.Annotations) +} + +func (x xforwardedprefix) GetDocumentation() parser.AnnotationFields { + return x.annotationConfig.Annotations +} + +func (x xforwardedprefix) Validate(anns map[string]string) error { + maxrisk := parser.StringRiskToRisk(x.r.GetSecurityConfiguration().AnnotationsRiskLevel) + return parser.CheckAnnotationRisk(anns, maxrisk, xForwardedForAnnotations.Annotations) } diff --git a/internal/ingress/annotations/xforwardedprefix/main_test.go b/internal/ingress/annotations/xforwardedprefix/main_test.go index a78c63d04..977e7d372 100644 --- a/internal/ingress/annotations/xforwardedprefix/main_test.go +++ b/internal/ingress/annotations/xforwardedprefix/main_test.go @@ -27,7 +27,7 @@ import ( ) func TestParse(t *testing.T) { - annotation := parser.GetAnnotationWithPrefix("x-forwarded-prefix") + annotation := parser.GetAnnotationWithPrefix(xForwardedForPrefixAnnotation) ap := NewParser(&resolver.Mock{}) if ap == nil { t.Fatalf("expected a parser.IngressAnnotation but returned nil") @@ -40,6 +40,7 @@ func TestParse(t *testing.T) { {map[string]string{annotation: "true"}, "true"}, {map[string]string{annotation: "1"}, "1"}, {map[string]string{annotation: ""}, ""}, + {map[string]string{annotation: "/$1"}, "/$1"}, {map[string]string{}, ""}, {nil, ""}, } @@ -54,6 +55,7 @@ func TestParse(t *testing.T) { for _, testCase := range testCases { ing.SetAnnotations(testCase.annotations) + //nolint:errcheck // Ignore the error since invalid cases will be checked with expected results result, _ := ap.Parse(ing) if result != testCase.expected { t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) diff --git a/internal/ingress/controller/certificate.go b/internal/ingress/controller/certificate.go index e8707c716..76aafec3c 100644 --- a/internal/ingress/controller/certificate.go +++ b/internal/ingress/controller/certificate.go @@ -100,7 +100,7 @@ func matchHostnames(pattern, host string) bool { host = strings.TrimSuffix(host, ".") pattern = strings.TrimSuffix(pattern, ".") - if len(pattern) == 0 || len(host) == 0 { + if pattern == "" || host == "" { return false } diff --git a/internal/ingress/controller/checker.go b/internal/ingress/controller/checker.go index 3229778bb..d1bf19ddf 100644 --- a/internal/ingress/controller/checker.go +++ b/internal/ingress/controller/checker.go @@ -29,7 +29,7 @@ import ( ) // Name returns the healthcheck name -func (n NGINXController) Name() string { +func (n *NGINXController) Name() string { return "nginx-ingress-controller" } diff --git a/internal/ingress/controller/checker_test.go b/internal/ingress/controller/checker_test.go index a0d2baafa..5fb6b09fd 100644 --- a/internal/ingress/controller/checker_test.go +++ b/internal/ingress/controller/checker_test.go @@ -32,7 +32,7 @@ import ( ) func TestNginxCheck(t *testing.T) { - var tests = []struct { + tests := []struct { healthzPath string }{ {"/healthz"}, @@ -42,7 +42,6 @@ func TestNginxCheck(t *testing.T) { for _, tt := range tests { testName := fmt.Sprintf("health path: %s", tt.healthzPath) t.Run(testName, func(t *testing.T) { - mux := http.NewServeMux() listener, err := tryListen("tcp", fmt.Sprintf(":%v", nginx.StatusPort)) @@ -50,7 +49,7 @@ func TestNginxCheck(t *testing.T) { t.Fatalf("creating tcp listener: %s", err) } defer listener.Close() - + //nolint:gosec // Ignore not configured ReadHeaderTimeout in testing server := &httptest.Server{ Listener: listener, Config: &http.Server{ @@ -76,7 +75,10 @@ func TestNginxCheck(t *testing.T) { }) // create pid file - os.MkdirAll("/tmp/nginx", file.ReadWriteByUser) + if err := os.MkdirAll("/tmp/nginx", file.ReadWriteByUser); err != nil { + t.Errorf("unexpected error creating pid file: %v", err) + } + pidFile, err := os.Create(nginx.PID) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -90,14 +92,23 @@ func TestNginxCheck(t *testing.T) { // start dummy process to use the PID cmd := exec.Command("sleep", "3600") - cmd.Start() + if err := cmd.Start(); err != nil { + t.Errorf("unexpected error: %v", err) + } pid := cmd.Process.Pid - defer cmd.Process.Kill() + defer func() { + if err := cmd.Process.Kill(); err != nil { + t.Errorf("unexpected error killing the process: %v", err) + } + }() go func() { - cmd.Wait() + cmd.Wait() //nolint:errcheck // Ignore the error }() - pidFile.Write([]byte(fmt.Sprintf("%v", pid))) + if _, err := fmt.Fprintf(pidFile, "%v", pid); err != nil { + t.Errorf("unexpected error writing the pid file: %v", err) + } + pidFile.Close() healthz.InstallPathHandler(mux, tt.healthzPath, n) @@ -109,7 +120,7 @@ func TestNginxCheck(t *testing.T) { }) // pollute pid file - pidFile.Write([]byte(fmt.Sprint("999999"))) + pidFile.WriteString("999999") //nolint:errcheck // Ignore the error pidFile.Close() t.Run("bad pid", func(t *testing.T) { @@ -122,7 +133,7 @@ func TestNginxCheck(t *testing.T) { } func callHealthz(expErr bool, healthzPath string, mux *http.ServeMux) error { - req, err := http.NewRequest(http.MethodGet, healthzPath, nil) + req, err := http.NewRequest(http.MethodGet, healthzPath, http.NoBody) if err != nil { return fmt.Errorf("healthz error: %v", err) } diff --git a/internal/ingress/controller/config/config.go b/internal/ingress/controller/config/config.go index da37ea3c9..bad82b8b0 100644 --- a/internal/ingress/controller/config/config.go +++ b/internal/ingress/controller/config/config.go @@ -29,10 +29,8 @@ import ( "k8s.io/ingress-nginx/pkg/util/runtime" ) -var ( - // EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates. - EnableSSLChainCompletion = false -) +// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates. +var EnableSSLChainCompletion = false const ( // http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size @@ -48,7 +46,7 @@ const ( // that tell browsers that it should only be communicated with using HTTPS, instead of using HTTP. // https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security // max-age is the time, in seconds, that the browser should remember that this site is only to be accessed using HTTPS. - hstsMaxAge = "15724800" + hstsMaxAge = "31536000" gzipTypes = "application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component" @@ -91,12 +89,23 @@ const ( // Configuration represents the content of nginx.conf file type Configuration struct { - defaults.Backend `json:",squash"` + defaults.Backend `json:",squash"` //nolint:staticcheck // Ignore unknown JSON option "squash" error // AllowSnippetAnnotations enable users to add their own snippets via ingress annotation. // If disabled, only snippets added via ConfigMap are added to ingress. AllowSnippetAnnotations bool `json:"allow-snippet-annotations"` + // AllowCrossNamespaceResources enables users to consume cross namespace resource on annotations + // Case disabled, attempts to use secrets or configmaps from a namespace different from Ingress will + // be denied + // This value will default to `false` on future releases + AllowCrossNamespaceResources bool `json:"allow-cross-namespace-resources"` + + // AnnotationsRiskLevel represents the risk accepted on an annotation. If the risk is, for instance `Medium`, annotations + // with risk High and Critical will not be accepted. + // Default Risk is Critical by default, but this may be changed in future releases + AnnotationsRiskLevel string `json:"annotations-risk-level"` + // AnnotationValueWordBlocklist defines words that should not be part of an user annotation value // (can be used to run arbitrary code or configs, for example) and that should be dropped. // This list should be separated by "," character @@ -120,15 +129,19 @@ type Configuration struct { // By default this is disabled EnableAccessLogForDefaultBackend bool `json:"enable-access-log-for-default-backend"` + // EnableAuthAccessLog enable auth access log + // By default this is disabled + EnableAuthAccessLog bool `json:"enable-auth-access-log"` + // AccessLogPath sets the path of the access logs for both http and stream contexts if enabled // http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log // http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log // By default access logs go to /var/log/nginx/access.log AccessLogPath string `json:"access-log-path,omitempty"` - // HttpAccessLogPath sets the path of the access logs for http context globally if enabled + // HTTPAccessLogPath sets the path of the access logs for http context globally if enabled // http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log - HttpAccessLogPath string `json:"http-access-log-path,omitempty"` + HTTPAccessLogPath string `json:"http-access-log-path,omitempty"` // StreamAccessLogPath sets the path of the access logs for stream context globally if enabled // http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log @@ -215,16 +228,19 @@ type Configuration struct { // https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_field_size // HTTP2MaxFieldSize Limits the maximum size of an HPACK-compressed request header field + // Deprecated: HTTP2MaxFieldSize is deprecated. HTTP2MaxFieldSize string `json:"http2-max-field-size,omitempty"` // https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_header_size // HTTP2MaxHeaderSize Limits the maximum size of the entire request header list after HPACK decompression + // Deprecated: HTTP2MaxHeaderSize is deprecated. HTTP2MaxHeaderSize string `json:"http2-max-header-size,omitempty"` // http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests // HTTP2MaxRequests Sets the maximum number of requests (including push requests) that can be served // through one HTTP/2 connection, after which the next client request will lead to connection closing // and the need of establishing a new connection. + // Deprecated: HTTP2MaxRequests is deprecated. HTTP2MaxRequests int `json:"http2-max-requests,omitempty"` // http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams @@ -408,14 +424,14 @@ type Configuration struct { // Example '60s' ProxyProtocolHeaderTimeout time.Duration `json:"proxy-protocol-header-timeout,omitempty"` + // Enables or disables the directive aio_write that writes files files asynchronously + // https://nginx.org/en/docs/http/ngx_http_core_module.html#aio_write + EnableAioWrite bool `json:"enable-aio-write,omitempty"` + // Enables or disables the use of the nginx module that compresses responses using the "gzip" method // http://nginx.org/en/docs/http/ngx_http_gzip_module.html UseGzip bool `json:"use-gzip,omitempty"` - // Enables or disables the use of the nginx geoip module that creates variables with values depending on the client IP - // http://nginx.org/en/docs/http/ngx_http_geoip_module.html - UseGeoIP bool `json:"use-geoip,omitempty"` - // UseGeoIP2 enables the geoip2 module for NGINX // By default this is disabled UseGeoIP2 bool `json:"use-geoip2,omitempty"` @@ -524,6 +540,10 @@ type Configuration struct { // http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version ProxyHTTPVersion string `json:"proxy-http-version"` + // Disables NGINX proxy-intercept-errors when error_page/custom-http-errors are set + // https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors + DisableProxyInterceptErrors bool `json:"disable-proxy-intercept-errors,omitempty"` + // Sets the ipv4 addresses on which the server will accept requests. BindAddressIpv4 []string `json:"bind-address-ipv4,omitempty"` @@ -534,7 +554,7 @@ type Configuration struct { UseForwardedHeaders bool `json:"use-forwarded-headers"` // Sets whether to enable the real ip module - EnableRealIp bool `json:"enable-real-ip"` + EnableRealIP bool `json:"enable-real-ip"` // Sets the header field for identifying the originating IP address of a client // Default is X-Forwarded-For @@ -552,22 +572,6 @@ type Configuration struct { // Default: true ProxyAddOriginalURIHeader bool `json:"proxy-add-original-uri-header"` - // EnableOpentracing enables the nginx Opentracing extension - // https://github.com/opentracing-contrib/nginx-opentracing - // By default this is disabled - EnableOpentracing bool `json:"enable-opentracing"` - - // OpentracingOperationName specifies a custom name for the server span - OpentracingOperationName string `json:"opentracing-operation-name"` - - // OpentracingOperationName specifies a custom name for the location span - OpentracingLocationOperationName string `json:"opentracing-location-operation-name"` - - // OpentracingTrustIncomingSpan sets whether or not to trust incoming trace spans - // If false, incoming span headers will be rejected - // Default: true - OpentracingTrustIncomingSpan bool `json:"opentracing-trust-incoming-span"` - // EnableOpentelemetry enables the nginx Opentelemetry extension // By default this is disabled EnableOpentelemetry bool `json:"enable-opentelemetry"` @@ -596,121 +600,29 @@ type Configuration struct { OtelServiceName string `json:"otel-service-name"` // OtelSampler specifies the sampler to use for any traces created - // Default: AlwaysOff + // Default: AlwaysOn OtelSampler string `json:"otel-sampler"` // OtelSamplerRatio specifies the sampler ratio to use for any traces created // Default: 0.01 OtelSamplerRatio float32 `json:"otel-sampler-ratio"` - //OtelSamplerParentBased specifies the parent based sampler to be use for any traces created - // Default: false + // OtelSamplerParentBased specifies the parent based sampler to be use for any traces created + // Default: true OtelSamplerParentBased bool `json:"otel-sampler-parent-based"` // MaxQueueSize specifies the max queue size for uploading traces + // Default: 2048 OtelMaxQueueSize int32 `json:"otel-max-queuesize"` // ScheduleDelayMillis specifies the max delay between uploading traces + // Default: 5000 OtelScheduleDelayMillis int32 `json:"otel-schedule-delay-millis"` // MaxExportBatchSize specifies the max export batch size to used when uploading traces + // Default: 512 OtelMaxExportBatchSize int32 `json:"otel-max-export-batch-size"` - // ZipkinCollectorHost specifies the host to use when uploading traces - ZipkinCollectorHost string `json:"zipkin-collector-host"` - - // ZipkinCollectorPort specifies the port to use when uploading traces - // Default: 9411 - ZipkinCollectorPort int `json:"zipkin-collector-port"` - - // ZipkinServiceName specifies the service name to use for any traces created - // Default: nginx - ZipkinServiceName string `json:"zipkin-service-name"` - - // ZipkinSampleRate specifies sampling rate for traces - // Default: 1.0 - ZipkinSampleRate float32 `json:"zipkin-sample-rate"` - - // JaegerCollectorHost specifies the host to use when uploading traces - JaegerCollectorHost string `json:"jaeger-collector-host"` - - // JaegerCollectorPort specifies the port to use when uploading traces - // Default: 6831 - JaegerCollectorPort int `json:"jaeger-collector-port"` - - // JaegerEndpoint specifies the enpoint to use when uploading traces to a collector over TCP - JaegerEndpoint string `json:"jaeger-endpoint"` - - // JaegerServiceName specifies the service name to use for any traces created - // Default: nginx - JaegerServiceName string `json:"jaeger-service-name"` - - // JaegerPropagationFormat specifies the traceparent/tracestate propagation format - JaegerPropagationFormat string `json:"jaeger-propagation-format"` - - // JaegerSamplerType specifies the sampler to be used when sampling traces. - // The available samplers are: const, probabilistic, ratelimiting, remote - // Default: const - JaegerSamplerType string `json:"jaeger-sampler-type"` - - // JaegerSamplerParam specifies the argument to be passed to the sampler constructor - // Default: 1 - JaegerSamplerParam string `json:"jaeger-sampler-param"` - - // JaegerSamplerHost specifies the host used for remote sampling consultation - // Default: http://127.0.0.1 - JaegerSamplerHost string `json:"jaeger-sampler-host"` - - // JaegerSamplerHost specifies the host used for remote sampling consultation - // Default: 5778 - JaegerSamplerPort int `json:"jaeger-sampler-port"` - - // JaegerTraceContextHeaderName specifies the header name used for passing trace context - // Default: uber-trace-id - JaegerTraceContextHeaderName string `json:"jaeger-trace-context-header-name"` - - // JaegerDebugHeader specifies the header name used for force sampling - // Default: jaeger-debug-id - JaegerDebugHeader string `json:"jaeger-debug-header"` - - // JaegerBaggageHeader specifies the header name used to submit baggage if there is no root span - // Default: jaeger-baggage - JaegerBaggageHeader string `json:"jaeger-baggage-header"` - - // TraceBaggageHeaderPrefix specifies the header prefix used to propagate baggage - // Default: uberctx- - JaegerTraceBaggageHeaderPrefix string `json:"jaeger-tracer-baggage-header-prefix"` - - // DatadogCollectorHost specifies the datadog agent host to use when uploading traces - DatadogCollectorHost string `json:"datadog-collector-host"` - - // DatadogCollectorPort specifies the port to use when uploading traces - // Default: 8126 - DatadogCollectorPort int `json:"datadog-collector-port"` - - // DatadogEnvironment specifies the environment this trace belongs to. - // Default: prod - DatadogEnvironment string `json:"datadog-environment"` - - // DatadogServiceName specifies the service name to use for any traces created - // Default: nginx - DatadogServiceName string `json:"datadog-service-name"` - - // DatadogOperationNameOverride overrides the operation naem to use for any traces crated - // Default: nginx.handle - DatadogOperationNameOverride string `json:"datadog-operation-name-override"` - - // DatadogPrioritySampling specifies to use client-side sampling - // If true disables client-side sampling (thus ignoring sample_rate) and enables distributed - // priority sampling, where traces are sampled based on a combination of user-assigned - // Default: true - DatadogPrioritySampling bool `json:"datadog-priority-sampling"` - - // DatadogSampleRate specifies sample rate for any traces created. - // This is effective only when datadog-priority-sampling is false - // Default: 1.0 - DatadogSampleRate float32 `json:"datadog-sample-rate"` - // MainSnippet adds custom configuration to the main section of the nginx configuration MainSnippet string `json:"main-snippet"` @@ -830,6 +742,12 @@ type Configuration struct { // http://nginx.org/en/docs/ngx_core_module.html#debug_connection // Default: "" DebugConnections []string `json:"debug-connections"` + + // StrictValidatePathType enable the strict validation of Ingress Paths + // It enforces that pathType of type Exact or Prefix should start with / and contain only + // alphanumeric chars, "-", "_", "/".In case of additional characters, + // like used on Rewrite configurations the user should use pathType as ImplementationSpecific + StrictValidatePathType bool `json:"strict-validate-path-type"` } // NewDefault returns the default nginx configuration @@ -847,12 +765,15 @@ func NewDefault() Configuration { defGlobalExternalAuth := GlobalExternalAuth{"", "", "", "", "", append(defResponseHeaders, ""), "", "", "", []string{}, map[string]string{}, false} cfg := Configuration{ - AllowSnippetAnnotations: true, + AllowSnippetAnnotations: false, + AllowCrossNamespaceResources: true, AllowBackendServerHeader: false, AnnotationValueWordBlocklist: "", + AnnotationsRiskLevel: "Critical", AccessLogPath: "/var/log/nginx/access.log", AccessLogParams: "", EnableAccessLogForDefaultBackend: false, + EnableAuthAccessLog: false, WorkerCPUAffinity: "", ErrorLogPath: "/var/log/nginx/error.log", BlockCIDRs: defBlockEntity, @@ -868,14 +789,14 @@ func NewDefault() Configuration { EnableUnderscoresInHeaders: false, ErrorLogLevel: errorLevel, UseForwardedHeaders: false, - EnableRealIp: false, + EnableRealIP: false, ForwardedForHeader: "X-Forwarded-For", ComputeFullForwardedFor: false, ProxyAddOriginalURIHeader: false, GenerateRequestID: true, - HTTP2MaxFieldSize: "4k", - HTTP2MaxHeaderSize: "16k", - HTTP2MaxRequests: 1000, + HTTP2MaxFieldSize: "", + HTTP2MaxHeaderSize: "", + HTTP2MaxRequests: 0, HTTP2MaxConcurrentStreams: 128, HTTPRedirectCode: 308, HSTS: true, @@ -917,45 +838,47 @@ func NewDefault() Configuration { SSLSessionTickets: false, SSLSessionTimeout: sslSessionTimeout, EnableBrotli: false, + EnableAioWrite: true, UseGzip: false, - UseGeoIP: true, UseGeoIP2: false, WorkerProcesses: strconv.Itoa(runtime.NumCPU()), WorkerShutdownTimeout: "240s", VariablesHashBucketSize: 256, VariablesHashMaxSize: 2048, UseHTTP2: true, + DisableProxyInterceptErrors: false, ProxyStreamTimeout: "600s", ProxyStreamNextUpstream: true, ProxyStreamNextUpstreamTimeout: "600s", ProxyStreamNextUpstreamTries: 3, Backend: defaults.Backend{ - ProxyBodySize: bodySize, - ProxyConnectTimeout: 5, - ProxyReadTimeout: 60, - ProxySendTimeout: 60, - ProxyBuffersNumber: 4, - ProxyBufferSize: "4k", - ProxyCookieDomain: "off", - ProxyCookiePath: "off", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: 0, - ProxyNextUpstreamTries: 3, - ProxyRequestBuffering: "on", - ProxyRedirectFrom: "off", - ProxyRedirectTo: "off", - PreserveTrailingSlash: false, - SSLRedirect: true, - CustomHTTPErrors: []int{}, - DenylistSourceRange: []string{}, - WhitelistSourceRange: []string{}, - SkipAccessLogURLs: []string{}, - LimitRate: 0, - LimitRateAfter: 0, - ProxyBuffering: "off", - ProxyHTTPVersion: "1.1", - ProxyMaxTempFileSize: "1024m", - ServiceUpstream: false, + ProxyBodySize: bodySize, + ProxyConnectTimeout: 5, + ProxyReadTimeout: 60, + ProxySendTimeout: 60, + ProxyBuffersNumber: 4, + ProxyBufferSize: "4k", + ProxyCookieDomain: "off", + ProxyCookiePath: "off", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: 0, + ProxyNextUpstreamTries: 3, + ProxyRequestBuffering: "on", + ProxyRedirectFrom: "off", + ProxyRedirectTo: "off", + PreserveTrailingSlash: false, + SSLRedirect: true, + CustomHTTPErrors: []int{}, + DisableProxyInterceptErrors: false, + DenylistSourceRange: []string{}, + WhitelistSourceRange: []string{}, + SkipAccessLogURLs: []string{}, + LimitRate: 0, + LimitRateAfter: 0, + ProxyBuffering: "off", + ProxyHTTPVersion: "1.1", + ProxyMaxTempFileSize: "1024m", + ServiceUpstream: false, }, UpstreamKeepaliveConnections: 320, UpstreamKeepaliveTime: "1h", @@ -964,30 +887,16 @@ func NewDefault() Configuration { LimitConnZoneVariable: defaultLimitConnZoneVariable, BindAddressIpv4: defBindAddress, BindAddressIpv6: defBindAddress, - OpentracingTrustIncomingSpan: true, OpentelemetryTrustIncomingSpan: true, - OpentelemetryConfig: "/etc/nginx/opentelemetry.toml", + OpentelemetryConfig: "/etc/ingress-controller/telemetry/opentelemetry.toml", OtlpCollectorPort: "4317", OtelServiceName: "nginx", - OtelSampler: "AlwaysOff", + OtelSampler: "AlwaysOn", OtelSamplerRatio: 0.01, - OtelSamplerParentBased: false, - ZipkinCollectorPort: 9411, - ZipkinServiceName: "nginx", - ZipkinSampleRate: 1.0, - JaegerCollectorPort: 6831, - JaegerPropagationFormat: "jaeger", - JaegerServiceName: "nginx", - JaegerSamplerType: "const", - JaegerSamplerParam: "1", - JaegerSamplerPort: 5778, - JaegerSamplerHost: "http://127.0.0.1", - DatadogServiceName: "nginx", - DatadogEnvironment: "prod", - DatadogCollectorPort: 8126, - DatadogOperationNameOverride: "nginx.handle", - DatadogSampleRate: 1.0, - DatadogPrioritySampling: true, + OtelSamplerParentBased: true, + OtelScheduleDelayMillis: 5000, + OtelMaxExportBatchSize: 512, + OtelMaxQueueSize: 2048, LimitReqStatusCode: 503, LimitConnStatusCode: 503, SyslogPort: 514, @@ -1002,6 +911,7 @@ func NewDefault() Configuration { GlobalRateLimitMemcachedPoolSize: 50, GlobalRateLimitStatucCode: 429, DebugConnections: []string{}, + StrictValidatePathType: false, // TODO: This will be true in future releases } if klog.V(5).Enabled() { @@ -1013,41 +923,41 @@ func NewDefault() Configuration { // TemplateConfig contains the nginx configuration to render the file nginx.conf type TemplateConfig struct { - ProxySetHeaders map[string]string - AddHeaders map[string]string - BacklogSize int - Backends []*ingress.Backend - PassthroughBackends []*ingress.SSLPassthroughBackend - Servers []*ingress.Server - TCPBackends []ingress.L4Service - UDPBackends []ingress.L4Service - HealthzURI string - Cfg Configuration - IsIPV6Enabled bool - IsSSLPassthroughEnabled bool - NginxStatusIpv4Whitelist []string - NginxStatusIpv6Whitelist []string - RedirectServers interface{} - ListenPorts *ListenPorts - PublishService *apiv1.Service - EnableMetrics bool - MaxmindEditionFiles *[]string - MonitorMaxBatchSize int - PID string - StatusPath string - StatusPort int - StreamPort int - StreamSnippets []string + ProxySetHeaders map[string]string `json:"ProxySetHeaders"` + AddHeaders map[string]string `json:"AddHeaders"` + BacklogSize int `json:"BacklogSize"` + Backends []*ingress.Backend `json:"Backends"` + PassthroughBackends []*ingress.SSLPassthroughBackend `json:"PassthroughBackends"` + Servers []*ingress.Server `json:"Servers"` + TCPBackends []ingress.L4Service `json:"TCPBackends"` + UDPBackends []ingress.L4Service `json:"UDPBackends"` + HealthzURI string `json:"HealthzURI"` + Cfg Configuration `json:"Cfg"` + IsIPV6Enabled bool `json:"IsIPV6Enabled"` + IsSSLPassthroughEnabled bool `json:"IsSSLPassthroughEnabled"` + NginxStatusIpv4Whitelist []string `json:"NginxStatusIpv4Whitelist"` + NginxStatusIpv6Whitelist []string `json:"NginxStatusIpv6Whitelist"` + RedirectServers interface{} `json:"RedirectServers"` + ListenPorts *ListenPorts `json:"ListenPorts"` + PublishService *apiv1.Service `json:"PublishService"` + EnableMetrics bool `json:"EnableMetrics"` + MaxmindEditionFiles *[]string `json:"MaxmindEditionFiles"` + MonitorMaxBatchSize int `json:"MonitorMaxBatchSize"` + PID string `json:"PID"` + StatusPath string `json:"StatusPath"` + StatusPort int `json:"StatusPort"` + StreamPort int `json:"StreamPort"` + StreamSnippets []string `json:"StreamSnippets"` } // ListenPorts describe the ports required to run the // NGINX Ingress controller type ListenPorts struct { - HTTP int - HTTPS int - Health int - Default int - SSLProxy int + HTTP int `json:"HTTP"` + HTTPS int `json:"HTTPS"` + Health int `json:"Health"` + Default int `json:"Default"` + SSLProxy int `json:"SSLProxy"` } // GlobalExternalAuth describe external authentication configuration for the diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 80b5b587b..cb8d3712c 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -114,7 +114,7 @@ type Configuration struct { DisableCatchAll bool - IngressClassConfiguration *ingressclass.IngressClassConfiguration + IngressClassConfiguration *ingressclass.Configuration ValidationWebhook string ValidationWebhookCertPath string @@ -142,8 +142,8 @@ type Configuration struct { func getIngressPodZone(svc *apiv1.Service) string { svcKey := k8s.MetaNamespaceKey(svc) - if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyAwareHints]; ok { - if strings.ToLower(svcZoneAnnotation) == "auto" { + if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyMode]; ok { + if strings.EqualFold(svcZoneAnnotation, "auto") { if foundZone, ok := k8s.IngressNodeDetails.GetLabels()[apiv1.LabelTopologyZone]; ok { klog.V(3).Infof("Svc has topology aware annotation enabled, try to use zone %q where controller pod is running for Service %q ", foundZone, svcKey) return foundZone @@ -154,7 +154,7 @@ func getIngressPodZone(svc *apiv1.Service) string { } // GetPublishService returns the Service used to set the load-balancer status of Ingresses. -func (n NGINXController) GetPublishService() *apiv1.Service { +func (n *NGINXController) GetPublishService() *apiv1.Service { s, err := n.store.GetService(n.cfg.PublishService) if err != nil { return nil @@ -189,13 +189,16 @@ func (n *NGINXController) syncIngress(interface{}) error { if !utilingress.IsDynamicConfigurationEnough(pcfg, n.runningConfig) { klog.InfoS("Configuration changes detected, backend reload required") - hash, _ := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{ + hash, err := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{ TagName: "json", }) + if err != nil { + klog.Errorf("unexpected error hashing configuration: %v", err) + } pcfg.ConfigurationChecksum = fmt.Sprintf("%v", hash) - err := n.OnUpdate(*pcfg) + err = n.OnUpdate(*pcfg) if err != nil { n.metricCollector.IncReloadErrorCount() n.metricCollector.ConfigSuccess(hash, false) @@ -256,6 +259,52 @@ func (n *NGINXController) syncIngress(interface{}) error { return nil } +// GetWarnings returns a list of warnings an Ingress gets when being created. +// The warnings are going to be used in an admission webhook, and they represent +// a list of messages that users need to be aware (like deprecation notices) +// when creating a new ingress object +func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) { + warnings := make([]string, 0) + + deprecatedAnnotations := sets.NewString() + deprecatedAnnotations.Insert( + "enable-influxdb", + "influxdb-measurement", + "influxdb-port", + "influxdb-host", + "influxdb-server-name", + "secure-verify-ca-secret", + ) + + // Skip checks if the ingress is marked as deleted + if !ing.DeletionTimestamp.IsZero() { + return warnings, nil + } + + anns := ing.GetAnnotations() + for k := range anns { + trimmedkey := strings.TrimPrefix(k, parser.AnnotationsPrefix+"/") + if deprecatedAnnotations.Has(trimmedkey) { + warnings = append(warnings, fmt.Sprintf("annotation %s is deprecated", k)) + } + } + + // Add each validation as a single warning + // rikatz: I know this is somehow a duplicated code from CheckIngress, but my goal was to deliver fast warning on this behavior. We + // can and should, tho, simplify this in the near future + if err := inspector.ValidatePathType(ing); err != nil { + if errs, is := err.(interface{ Unwrap() []error }); is { + for _, errW := range errs.Unwrap() { + warnings = append(warnings, errW.Error()) + } + } else { + warnings = append(warnings, err.Error()) + } + } + + return warnings, nil +} + // CheckIngress returns an error in case the provided ingress, when added // to the current configuration, generates an invalid configuration func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { @@ -270,11 +319,13 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { if !ing.DeletionTimestamp.IsZero() { return nil } + if n.cfg.DeepInspector { if err := inspector.DeepInspect(ing); err != nil { return fmt.Errorf("invalid object: %w", err) } } + // Do not attempt to validate an ingress that's not meant to be controlled by the current instance of the controller. if ingressClass, err := n.store.GetIngressClass(ing, n.cfg.IngressClassConfiguration); ingressClass == "" { klog.Warningf("ignoring ingress %v in %v based on annotation %v: %v", ing.Name, ing.ObjectMeta.Namespace, ingressClass, err) @@ -287,12 +338,19 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { } if n.cfg.DisableCatchAll && ing.Spec.DefaultBackend != nil { - return fmt.Errorf("This deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false.") + return fmt.Errorf("this deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false") } startRender := time.Now().UnixNano() / 1000000 cfg := n.store.GetBackendConfiguration() cfg.Resolver = n.resolver + // Adds the pathType Validation + if cfg.StrictValidatePathType { + if err := inspector.ValidatePathType(ing); err != nil { + return fmt.Errorf("ingress contains invalid paths: %w", err) + } + } + var arrayBadWords []string if cfg.AnnotationValueWordBlocklist != "" { @@ -300,10 +358,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { } for key, value := 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) + return fmt.Errorf("this deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix) } } @@ -319,10 +376,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { 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")) { + if cfg.GlobalRateLimitMemcachedHost == "" && 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) @@ -334,14 +390,19 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error { toCheck.ObjectMeta.Name == ing.ObjectMeta.Name } ings := store.FilterIngresses(allIngresses, filter) + parsed, err := annotations.NewAnnotationExtractor(n.store).Extract(ing) + if err != nil { + n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name) + return err + } ings = append(ings, &ingress.Ingress{ Ingress: *ing, - ParsedAnnotations: annotations.NewAnnotationExtractor(n.store).Extract(ing), + ParsedAnnotations: parsed, }) startTest := time.Now().UnixNano() / 1000000 _, servers, pcfg := n.getConfiguration(ings) - err := checkOverlap(ing, allIngresses, servers) + err = checkOverlap(ing, servers) if err != nil { n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name) return err @@ -392,7 +453,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr return []ingress.L4Service{} } - var svcs []ingress.L4Service + svcs := make([]ingress.L4Service, 0, len(configmap.Data)) var svcProxyProtocol ingress.ProxyProtocol rp := []int{ @@ -429,10 +490,10 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr svcProxyProtocol.Encode = false // Proxy Protocol is only compatible with TCP Services if len(nsSvcPort) >= 3 && proto == apiv1.ProtocolTCP { - if len(nsSvcPort) >= 3 && strings.ToUpper(nsSvcPort[2]) == "PROXY" { + if len(nsSvcPort) >= 3 && strings.EqualFold(nsSvcPort[2], "PROXY") { svcProxyProtocol.Decode = true } - if len(nsSvcPort) == 4 && strings.ToUpper(nsSvcPort[3]) == "PROXY" { + if len(nsSvcPort) == 4 && strings.EqualFold(nsSvcPort[3], "PROXY") { svcProxyProtocol.Encode = true } } @@ -472,6 +533,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr klog.V(3).Infof("Searching Endpoints with %v port number %d for Service %q", proto, targetPort, nsName) for i := range svc.Spec.Ports { sp := svc.Spec.Ports[i] + //nolint:gosec // Ignore G109 error if sp.Port == int32(targetPort) { if sp.Protocol == proto { endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices) @@ -514,7 +576,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend { } svcKey := n.cfg.DefaultService - if len(svcKey) == 0 { + if svcKey == "" { upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint()) return upstream } @@ -630,13 +692,14 @@ func dropSnippetDirectives(anns *annotations.Ingress, ingKey string) { klog.V(3).Infof("Ingress %q tried to use stream-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey) anns.StreamSnippet = "" } - } } // 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. +// +//nolint:gocyclo // Ignore function complexity error func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*ingress.Backend, []*ingress.Server) { du := n.getDefaultUpstream() upstreams := n.createUpstreams(ingresses, du) @@ -970,7 +1033,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B // configure traffic shaping for canary if anns.Canary.Enabled { upstreams[defBackend].NoServer = true - upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary) + upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary) } if len(upstreams[defBackend].Endpoints) == 0 { @@ -994,7 +1057,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B continue } - for _, path := range rule.HTTP.Paths { + for i, path := range rule.HTTP.Paths { if path.Backend.Service == nil { // skip non-service backends klog.V(3).Infof("Ingress %q and path %q does not contain a service backend, using default backend", ingKey, path.Path) @@ -1024,7 +1087,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B // add the service ClusterIP as a single Endpoint instead of individual Endpoints if anns.ServiceUpstream { - endpoint, err := n.getServiceClusterEndpoint(svcKey, &path.Backend) + endpoint, err := n.getServiceClusterEndpoint(svcKey, &rule.HTTP.Paths[i].Backend) if err != nil { klog.Errorf("Failed to determine a suitable ClusterIP Endpoint for Service %q: %v", svcKey, err) } else { @@ -1035,7 +1098,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B // configure traffic shaping for canary if anns.Canary.Enabled { upstreams[name].NoServer = true - upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary) + upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary) } if len(upstreams[name].Endpoints) == 0 { @@ -1146,7 +1209,6 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres if strconv.Itoa(int(servicePort.Port)) == backendPort || servicePort.TargetPort.String() == backendPort || servicePort.Name == backendPort { - endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices) if len(endps) == 0 { klog.Warningf("Service %q does not have any active Endpoint.", svcKey) @@ -1179,8 +1241,8 @@ func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert { // one root location, which uses a default backend if left unspecified. func (n *NGINXController) createServers(data []*ingress.Ingress, upstreams map[string]*ingress.Backend, - du *ingress.Backend) map[string]*ingress.Server { - + du *ingress.Backend, +) map[string]*ingress.Server { servers := make(map[string]*ingress.Server, len(data)) allAliases := make(map[string][]string, len(data)) @@ -1222,7 +1284,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress, Rewrite: false, }, }, - }} + }, + } // initialize all other servers for _, ing := range data { @@ -1444,7 +1507,6 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress) loc.ExternalAuth = anns.ExternalAuth loc.EnableGlobalAuth = anns.EnableGlobalAuth loc.HTTP2PushPreload = anns.HTTP2PushPreload - loc.Opentracing = anns.Opentracing loc.Opentelemetry = anns.Opentelemetry loc.Proxy = anns.Proxy loc.ProxySSL = anns.ProxySSL @@ -1454,7 +1516,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress) loc.Rewrite = anns.Rewrite loc.UpstreamVhost = anns.UpstreamVhost loc.Denylist = anns.Denylist - loc.Whitelist = anns.Whitelist + loc.Allowlist = anns.Allowlist loc.Denied = anns.Denied loc.XForwardedPrefix = anns.XForwardedPrefix loc.UsePortInRedirects = anns.UsePortInRedirects @@ -1464,6 +1526,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress) loc.BackendProtocol = anns.BackendProtocol loc.FastCGI = anns.FastCGI loc.CustomHTTPErrors = anns.CustomHTTPErrors + loc.DisableProxyInterceptErrors = anns.DisableProxyInterceptErrors loc.ModSecurity = anns.ModSecurity loc.Satisfy = anns.Satisfy loc.Mirror = anns.Mirror @@ -1472,7 +1535,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress) } // OK to merge canary ingresses iff there exists one or more ingresses to potentially merge into -func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ingress.Ingress) bool { +func nonCanaryIngressExists(ingresses, canaryIngresses []*ingress.Ingress) bool { return len(ingresses)-len(canaryIngresses) > 0 } @@ -1480,12 +1543,12 @@ func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ing // 1) names of backends do not match and canary doesn't merge into itself // 2) primary name is not the default upstream // 3) the primary has a server -func canMergeBackend(primary *ingress.Backend, alternative *ingress.Backend) bool { +func canMergeBackend(primary, alternative *ingress.Backend) bool { return alternative != nil && primary.Name != alternative.Name && primary.Name != defUpstreamName && !primary.NoServer } // Performs the merge action and checks to ensure that one two alternative backends do not merge into each other -func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altUps *ingress.Backend) bool { +func mergeAlternativeBackend(ing *ingress.Ingress, priUps, altUps *ingress.Backend) bool { if priUps.NoServer { klog.Warningf("unable to merge alternative backend %v into primary backend %v because %v is a primary backend", altUps.Name, priUps.Name, priUps.Name) @@ -1503,8 +1566,7 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU priUps.SessionAffinity.DeepCopyInto(&altUps.SessionAffinity) } - priUps.AlternativeBackends = - append(priUps.AlternativeBackends, altUps.Name) + priUps.AlternativeBackends = append(priUps.AlternativeBackends, altUps.Name) return true } @@ -1514,8 +1576,8 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU // to a backend's alternative list. // If no match is found, then the serverless backend is deleted. func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingress.Backend, - servers map[string]*ingress.Server) { - + servers map[string]*ingress.Server, +) { // merge catch-all alternative backends if ing.Spec.DefaultBackend != nil { upsName := upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service) @@ -1525,7 +1587,6 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres if altUps == nil { klog.Warningf("alternative backend %s has already been removed", upsName) } else { - merged := false altEqualsPri := false @@ -1616,8 +1677,8 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres // extractTLSSecretName returns the name of the Secret containing a SSL // certificate for the given host name, or an empty string. func extractTLSSecretName(host string, ing *ingress.Ingress, - getLocalSSLCert func(string) (*ingress.SSLCert, error)) string { - + getLocalSSLCert func(string) (*ingress.SSLCert, error), +) string { if ing == nil { return "" } @@ -1634,7 +1695,6 @@ func extractTLSSecretName(host string, ing *ingress.Ingress, // no TLS host matching host name, try each TLS host for matching SAN or CN for _, tls := range ing.Spec.TLS { - if tls.SecretName == "" { // There's no secretName specified, so it will never be available continue @@ -1693,6 +1753,7 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort { } for _, svcPort := range svc.Spec.Ports { + //nolint:gosec // Ignore G109 error if svcPort.Port != int32(port) { continue } @@ -1711,13 +1772,14 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort { // ExternalName without port return &apiv1.ServicePort{ - Protocol: "TCP", + Protocol: "TCP", + //nolint:gosec // Ignore G109 error Port: int32(port), TargetPort: intstr.FromInt(port), } } -func checkOverlap(ing *networking.Ingress, ingresses []*ingress.Ingress, servers []*ingress.Server) error { +func checkOverlap(ing *networking.Ingress, servers []*ingress.Server) error { for _, rule := range ing.Spec.Rules { if rule.HTTP == nil { continue @@ -1753,9 +1815,9 @@ func checkOverlap(ing *networking.Ingress, ingresses []*ingress.Ingress, servers } // path overlap. Check if one of the ingresses has a canary annotation - isCanaryEnabled, annotationErr := parser.GetBoolAnnotation("canary", ing) + isCanaryEnabled, annotationErr := parser.GetBoolAnnotation("canary", ing, canary.CanaryAnnotations.Annotations) for _, existing := range existingIngresses { - isExistingCanaryEnabled, existingAnnotationErr := parser.GetBoolAnnotation("canary", existing) + isExistingCanaryEnabled, existingAnnotationErr := parser.GetBoolAnnotation("canary", existing, canary.CanaryAnnotations.Annotations) if isCanaryEnabled && isExistingCanaryEnabled { return fmt.Errorf(`host "%s" and path "%s" is already defined in ingress %s/%s`, rule.Host, path.Path, existing.Namespace, existing.Name) @@ -1782,7 +1844,7 @@ func ingressForHostPath(hostname, path string, servers []*ingress.Server) []*net continue } - for _, location := range server.Locations { + for i, location := range server.Locations { if location.Path != path { continue } @@ -1791,7 +1853,7 @@ func ingressForHostPath(hostname, path string, servers []*ingress.Server) []*net continue } - ingresses = append(ingresses, &location.Ingress.Ingress) + ingresses = append(ingresses, &server.Locations[i].Ingress.Ingress) } } @@ -1810,7 +1872,7 @@ func (n *NGINXController) getStreamSnippets(ingresses []*ingress.Ingress) []stri } // newTrafficShapingPolicy creates new ingress.TrafficShapingPolicy instance using canary configuration -func newTrafficShapingPolicy(cfg canary.Config) ingress.TrafficShapingPolicy { +func newTrafficShapingPolicy(cfg *canary.Config) ingress.TrafficShapingPolicy { return ingress.TrafficShapingPolicy{ Weight: cfg.Weight, WeightTotal: cfg.WeightTotal, diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go index 8cca10385..c07fcfadf 100644 --- a/internal/ingress/controller/controller_test.go +++ b/internal/ingress/controller/controller_test.go @@ -33,7 +33,7 @@ import ( "github.com/eapache/channels" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" networking "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -44,11 +44,10 @@ import ( "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/ipallowlist" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl" "k8s.io/ingress-nginx/internal/ingress/annotations/sessionaffinity" - "k8s.io/ingress-nginx/internal/ingress/controller/config" ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config" "k8s.io/ingress-nginx/internal/ingress/controller/ingressclass" "k8s.io/ingress-nginx/internal/ingress/controller/store" @@ -61,44 +60,56 @@ import ( "k8s.io/ingress-nginx/pkg/util/file" ) +const ( + exampleBackend = "example-http-svc-1-80" + TRUE = "true" +) + type fakeIngressStore struct { ingresses []*ingress.Ingress configuration ngx_config.Configuration } -func (fakeIngressStore) GetIngressClass(ing *networking.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) { +func (fakeIngressStore) GetIngressClass(_ *networking.Ingress, _ *ingressclass.Configuration) (string, error) { return "nginx", nil } -func (fis fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration { +func (fis *fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration { return fis.configuration } -func (fakeIngressStore) GetConfigMap(key string) (*corev1.ConfigMap, error) { +func (fis *fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration { + return defaults.SecurityConfiguration{ + AnnotationsRiskLevel: fis.configuration.AnnotationsRiskLevel, + AllowCrossNamespaceResources: fis.configuration.AllowCrossNamespaceResources, + } +} + +func (fakeIngressStore) GetConfigMap(_ string) (*corev1.ConfigMap, error) { return nil, fmt.Errorf("test error") } -func (fakeIngressStore) GetSecret(key string) (*corev1.Secret, error) { +func (fakeIngressStore) GetSecret(_ string) (*corev1.Secret, error) { return nil, fmt.Errorf("test error") } -func (fakeIngressStore) GetService(key string) (*corev1.Service, error) { +func (fakeIngressStore) GetService(_ string) (*corev1.Service, error) { return nil, fmt.Errorf("test error") } -func (fakeIngressStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) { +func (fakeIngressStore) GetServiceEndpointsSlices(_ string) ([]*discoveryv1.EndpointSlice, error) { return nil, fmt.Errorf("test error") } -func (fis fakeIngressStore) ListIngresses() []*ingress.Ingress { +func (fis *fakeIngressStore) ListIngresses() []*ingress.Ingress { return fis.ingresses } -func (fis fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, filterFunc store.IngressFilterFunc) []*ingress.Ingress { +func (fis *fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, _ store.IngressFilterFunc) []*ingress.Ingress { return ingresses } -func (fakeIngressStore) GetLocalSSLCert(name string) (*ingress.SSLCert, error) { +func (fakeIngressStore) GetLocalSSLCert(_ string) (*ingress.SSLCert, error) { return nil, fmt.Errorf("test error") } @@ -114,7 +125,7 @@ func (fakeIngressStore) GetDefaultBackend() defaults.Backend { return defaults.Backend{} } -func (fakeIngressStore) Run(stopCh chan struct{}) {} +func (fakeIngressStore) Run(_ chan struct{}) {} type testNginxTestCommand struct { t *testing.T @@ -123,7 +134,7 @@ type testNginxTestCommand struct { err error } -func (ntc testNginxTestCommand) ExecCommand(args ...string) *exec.Cmd { +func (ntc testNginxTestCommand) ExecCommand(_ ...string) *exec.Cmd { return nil } @@ -146,7 +157,7 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) { type fakeTemplate struct{} -func (fakeTemplate) Write(conf config.TemplateConfig) ([]byte, error) { +func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) { r := []byte{} for _, s := range conf.Servers { if len(r) > 0 { @@ -159,7 +170,7 @@ func (fakeTemplate) Write(conf config.TemplateConfig) ([]byte, error) { func TestCheckIngress(t *testing.T) { defer func() { - filepath.Walk(os.TempDir(), func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(os.TempDir(), func(path string, info os.FileInfo, err error) error { if info.IsDir() && os.TempDir() != path { return filepath.SkipDir } @@ -168,6 +179,9 @@ func TestCheckIngress(t *testing.T) { } return nil }) + if err != nil { + t.Errorf("unexpected error: %v", err) + } }() err := file.CreateRequiredDirectories() @@ -177,13 +191,17 @@ func TestCheckIngress(t *testing.T) { // Ensure no panic with wrong arguments var nginx *NGINXController - nginx.CheckIngress(nil) + if err := nginx.CheckIngress(nil); err != nil { + t.Errorf("unexpected error: %v", err) + } nginx = newNGINXController(t) - nginx.CheckIngress(nil) + if err := nginx.CheckIngress(nil); err != nil { + t.Errorf("unexpected error: %v", err) + } nginx.metricCollector = metric.DummyCollector{} nginx.t = fakeTemplate{} - nginx.store = fakeIngressStore{ + nginx.store = &fakeIngressStore{ ingresses: []*ingress.Ingress{}, } @@ -213,7 +231,7 @@ func TestCheckIngress(t *testing.T) { } t.Run("When the hostname is updated", func(t *testing.T) { - nginx.store = fakeIngressStore{ + nginx.store = &fakeIngressStore{ ingresses: []*ingress.Ingress{ { Ingress: *ing, @@ -260,7 +278,7 @@ 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{ + nginx.store = &fakeIngressStore{ ingresses: []*ingress.Ingress{}, configuration: ngx_config.Configuration{ AllowSnippetAnnotations: false, @@ -277,7 +295,7 @@ func TestCheckIngress(t *testing.T) { }) t.Run("When invalid directives are used in annotation values", func(t *testing.T) { - nginx.store = fakeIngressStore{ + nginx.store = &fakeIngressStore{ ingresses: []*ingress.Ingress{}, configuration: ngx_config.Configuration{ AnnotationValueWordBlocklist: "invalid_directive, another_directive", @@ -352,6 +370,112 @@ func TestCheckIngress(t *testing.T) { }) } +func TestCheckWarning(t *testing.T) { + // Ensure no panic with wrong arguments + nginx := &NGINXController{} + + nginx.t = fakeTemplate{} + nginx.store = &fakeIngressStore{ + ingresses: []*ingress.Ingress{}, + } + + ing := &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-warning", + Namespace: "user-namespace", + Annotations: map[string]string{}, + }, + Spec: networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + Host: "example.com", + }, + }, + }, + } + t.Run("when a deprecated annotation is used a warning should be returned", func(t *testing.T) { + ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE + defer func() { + ing.ObjectMeta.Annotations = map[string]string{} + }() + + warnings, err := nginx.CheckWarning(ing) + if err != nil { + t.Errorf("no error should be returned, but %s was returned", err) + } + if len(warnings) != 1 { + t.Errorf("expected 1 warning to occur but %d occurred", len(warnings)) + } else { + t.Logf("got warning %s correctly", warnings[0]) + } + }) + + t.Run("When an invalid pathType is used, a warning should be returned", func(t *testing.T) { + rules := ing.Spec.DeepCopy().Rules + ing.Spec.Rules = []networking.IngressRule{ + { + Host: "example.com", + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + Path: "/xpto{$2}", + PathType: &pathTypePrefix, + }, + { + Path: "/ok", + PathType: &pathTypeExact, + }, + }, + }, + }, + }, + } + defer func() { + ing.Spec.Rules = rules + }() + + warnings, err := nginx.CheckWarning(ing) + if err != nil { + t.Errorf("no error should be returned, but %s was returned", err) + } + if len(warnings) != 1 { + t.Errorf("expected 1 warning to occur but %d occurred", len(warnings)) + } else { + t.Logf("got warnings %v correctly", warnings) + } + + t.Run("adding invalid annotations increases the warning count", func(t *testing.T) { + ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE + ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = TRUE + ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla" + defer func() { + ing.ObjectMeta.Annotations = map[string]string{} + }() + warnings, err := nginx.CheckWarning(ing) + if err != nil { + t.Errorf("no error should be returned, but %s was returned", err) + } + if len(warnings) != 4 { + t.Errorf("expected 4 warning to occur but %d occurred", len(warnings)) + } else { + t.Logf("got warnings %v correctly", warnings) + } + }) + }) + + t.Run("When the ingress is marked as deleted", func(t *testing.T) { + ing.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + + if warnings, err := nginx.CheckWarning(ing); err != nil || len(warnings) != 0 { + t.Errorf("when the ingress is marked as deleted, no warning should be returned") + } + }) +} + +//nolint:dupl // Ignore dupl errors for similar test case func TestMergeAlternativeBackends(t *testing.T) { testCases := map[string]struct { ingress *ingress.Ingress @@ -1417,12 +1541,12 @@ func TestExtractTLSSecretName(t *testing.T) { } } +//nolint:gocyclo // Ignore function complexity error func TestGetBackendServers(t *testing.T) { - testCases := []struct { Ingresses []*ingress.Ingress Validate func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server) - SetConfigMap func(namespace string) *v1.ConfigMap + SetConfigMap func(namespace string) *corev1.ConfigMap }{ { Ingresses: []*ingress.Ingress{ @@ -1958,7 +2082,7 @@ func TestGetBackendServers(t *testing.T) { t.Errorf("server hostname should be 'example.com', got '%s'", s.Hostname) } - if s.Locations[0].Backend != "example-http-svc-1-80" || s.Locations[1].Backend != "example-http-svc-1-80" || s.Locations[2].Backend != "example-http-svc-1-80" { + if s.Locations[0].Backend != exampleBackend || s.Locations[1].Backend != exampleBackend || s.Locations[2].Backend != exampleBackend { t.Errorf("all location backend should be 'example-http-svc-1-80'") } @@ -1967,7 +2091,7 @@ func TestGetBackendServers(t *testing.T) { return } - if upstreams[0].Name != "example-http-svc-1-80" { + if upstreams[0].Name != exampleBackend { t.Errorf("example-http-svc-1-80 should be first upstream, got %s", upstreams[0].Name) return } @@ -1981,6 +2105,7 @@ func TestGetBackendServers(t *testing.T) { SetConfigMap: testConfigMap, }, { + //nolint:dupl // Ignore dupl errors for similar test case Ingresses: []*ingress.Ingress{ { Ingress: networking.Ingress{ @@ -2088,6 +2213,7 @@ func TestGetBackendServers(t *testing.T) { SetConfigMap: testConfigMap, }, { + //nolint:dupl // Ignore dupl errors for similar test case Ingresses: []*ingress.Ingress{ { Ingress: networking.Ingress{ @@ -2192,14 +2318,14 @@ func TestGetBackendServers(t *testing.T) { t.Errorf("location cafilename should be '%s', got '%s'", ingresses[1].ParsedAnnotations.ProxySSL.CAFileName, s.Locations[0].ProxySSL.CAFileName) } }, - SetConfigMap: func(ns string) *v1.ConfigMap { - return &v1.ConfigMap{ + SetConfigMap: func(ns string) *corev1.ConfigMap { + return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "config", SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), }, Data: map[string]string{ - "proxy-ssl-location-only": "true", + "proxy-ssl-location-only": TRUE, }, } }, @@ -2253,14 +2379,14 @@ func TestGetBackendServers(t *testing.T) { t.Errorf("backend should be upstream-default-backend, got '%s'", s.Locations[0].Backend) } }, - SetConfigMap: func(ns string) *v1.ConfigMap { - return &v1.ConfigMap{ + SetConfigMap: func(ns string) *corev1.ConfigMap { + return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "config", SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), }, Data: map[string]string{ - "proxy-ssl-location-only": "true", + "proxy-ssl-location-only": TRUE, }, } }, @@ -2305,7 +2431,7 @@ func TestGetBackendServers(t *testing.T) { }, }, ParsedAnnotations: &annotations.Ingress{ - Whitelist: ipwhitelist.SourceRange{CIDR: []string{"10.0.0.0/24"}}, + Allowlist: ipallowlist.SourceRange{CIDR: []string{"10.0.0.0/24"}}, ServerSnippet: "bla", ConfigurationSnippet: "blo", }, @@ -2326,13 +2452,12 @@ func TestGetBackendServers(t *testing.T) { 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" { + if len(s.Locations[0].Allowlist.CIDR) != 1 || s.Locations[0].Allowlist.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{ + SetConfigMap: func(ns string) *corev1.ConfigMap { + return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "config", SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), @@ -2352,8 +2477,8 @@ func TestGetBackendServers(t *testing.T) { } } -func testConfigMap(ns string) *v1.ConfigMap { - return &v1.ConfigMap{ +func testConfigMap(ns string) *corev1.ConfigMap { + return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "config", SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), @@ -2362,11 +2487,11 @@ func testConfigMap(ns string) *v1.ConfigMap { } func newNGINXController(t *testing.T) *NGINXController { - ns := v1.NamespaceDefault + ns := corev1.NamespaceDefault clientSet := fake.NewSimpleClientset() - configMap := &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "config", SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), @@ -2400,7 +2525,7 @@ func newNGINXController(t *testing.T) *NGINXController { channels.NewRingChannel(10), false, true, - &ingressclass.IngressClassConfiguration{ + &ingressclass.Configuration{ Controller: "k8s.io/ingress-nginx", AnnotationValue: "nginx", }, @@ -2433,8 +2558,8 @@ func fakeX509Cert(dnsNames []string) *x509.Certificate { } } -func newDynamicNginxController(t *testing.T, setConfigMap func(string) *v1.ConfigMap) *NGINXController { - ns := v1.NamespaceDefault +func newDynamicNginxController(t *testing.T, setConfigMap func(string) *corev1.ConfigMap) *NGINXController { + ns := corev1.NamespaceDefault clientSet := fake.NewSimpleClientset() configMap := setConfigMap(ns) @@ -2466,7 +2591,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *v1.Confi channels.NewRingChannel(10), false, true, - &ingressclass.IngressClassConfiguration{ + &ingressclass.Configuration{ Controller: "k8s.io/ingress-nginx", AnnotationValue: "nginx", }, diff --git a/internal/ingress/controller/endpointslices.go b/internal/ingress/controller/endpointslices.go index ca6e595c8..4f98e3ce7 100644 --- a/internal/ingress/controller/endpointslices.go +++ b/internal/ingress/controller/endpointslices.go @@ -36,8 +36,8 @@ import ( // getEndpointsFromSlices returns a list of Endpoint structs for a given service/target port combination. func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol, zoneForHints string, - getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error)) []ingress.Endpoint { - + getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error), +) []ingress.Endpoint { upsServers := []ingress.Endpoint{} if s == nil || port == nil { @@ -94,7 +94,7 @@ func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto c if !reflect.DeepEqual(*epPort.Protocol, proto) { continue } - var targetPort int32 = 0 + var targetPort int32 if port.Name == "" { // port.Name is optional if there is only one port targetPort = *epPort.Port diff --git a/internal/ingress/controller/endpointslices_test.go b/internal/ingress/controller/endpointslices_test.go index b61e9a4f3..69ef3ef1b 100644 --- a/internal/ingress/controller/endpointslices_test.go +++ b/internal/ingress/controller/endpointslices_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/ingress-nginx/pkg/apis/ingress" ) +//nolint:dupl // Ignore dupl errors for similar test case func TestGetEndpointsFromSlices(t *testing.T) { tests := []struct { name string diff --git a/internal/ingress/controller/ingressclass/ingressclass.go b/internal/ingress/controller/ingressclass/ingressclass.go index 95bd98d0f..90c6a4d67 100644 --- a/internal/ingress/controller/ingressclass/ingressclass.go +++ b/internal/ingress/controller/ingressclass/ingressclass.go @@ -29,9 +29,9 @@ const ( DefaultAnnotationValue = "nginx" ) -// IngressClassConfiguration defines the various aspects of IngressClass parsing +// Configuration defines the various aspects of IngressClass parsing // and how the controller should behave in each case -type IngressClassConfiguration struct { +type Configuration struct { // Controller defines the controller value this daemon watch to. // Defaults to "k8s.io/ingress-nginx" defined in flags Controller string @@ -45,7 +45,7 @@ type IngressClassConfiguration struct { // IgnoreIngressClass defines if Controller should ignore the IngressClass Object if no permissions are // granted on IngressClass IgnoreIngressClass bool - //IngressClassByName defines if the Controller should watch for Ingress Classes by + // IngressClassByName defines if the Controller should watch for Ingress Classes by // .metadata.name together with .spec.Controller IngressClassByName bool } diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index 80693db5c..578d5b4e8 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "io/fs" "net" "net/http" "os" @@ -113,7 +114,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro if n.cfg.ValidationWebhook != "" { n.validationWebhookServer = &http.Server{ Addr: config.ValidationWebhook, - //G112 (CWE-400): Potential Slowloris Attack + // G112 (CWE-400): Potential Slowloris Attack ReadHeaderTimeout: 10 * time.Second, Handler: adm_controller.NewAdmissionControllerServer(&adm_controller.IngressAdmission{Checker: n}), TLSConfig: ssl.NewTLSListener(n.cfg.ValidationWebhookCertPath, n.cfg.ValidationWebhookKeyPath).TLSConfig(), @@ -180,7 +181,11 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro } filesToWatch := []string{} - err = filepath.Walk("/etc/nginx/geoip/", func(path string, info os.FileInfo, err error) error { + + if err := os.Mkdir("/etc/ingress-controller/geoip/", 0o755); err != nil && !os.IsExist(err) { + klog.Fatalf("Error creating geoip dir: %v", err) + } + err = filepath.WalkDir("/etc/ingress-controller/geoip/", func(path string, info fs.DirEntry, err error) error { if err != nil { return err } @@ -248,8 +253,7 @@ type NGINXController struct { store store.Storer - metricCollector metric.Collector - admissionCollector metric.Collector + metricCollector metric.Collector validationWebhookServer *http.Server @@ -430,7 +434,7 @@ func (n *NGINXController) start(cmd *exec.Cmd) { } // DefaultEndpoint returns the default endpoint to be use as default server that returns 404. -func (n NGINXController) DefaultEndpoint() ingress.Endpoint { +func (n *NGINXController) DefaultEndpoint() ingress.Endpoint { return ingress.Endpoint{ Address: "127.0.0.1", Port: fmt.Sprintf("%v", n.cfg.ListenPorts.Default), @@ -439,8 +443,9 @@ func (n NGINXController) DefaultEndpoint() ingress.Endpoint { } // generateTemplate returns the nginx configuration file content -func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) { - +// +//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered. +func (n *NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) { if n.cfg.EnableSSLPassthrough { servers := []*tcpproxy.TCPServer{} for _, pb := range ingressCfg.PassthroughBackends { @@ -459,6 +464,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC } } else { for _, sp := range svc.Spec.Ports { + //nolint:gosec // Ignore G109 error if sp.Port == int32(port) { port = int(sp.Port) break @@ -564,7 +570,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC if err != nil { klog.Warningf("Error reading Secret %q from local store: %v", secretName, err) } else { - nsSecName := strings.Replace(secretName, "/", "-", -1) + nsSecName := strings.ReplaceAll(secretName, "/", "-") dh, ok := secret.Data["dhparam.pem"] if ok { pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh) @@ -590,7 +596,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC } } - tc := ngx_config.TemplateConfig{ + tc := &ngx_config.TemplateConfig{ ProxySetHeaders: setHeaders, AddHeaders: addHeaders, BacklogSize: sysctlSomaxconn(), @@ -624,7 +630,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC // testTemplate checks if the NGINX configuration inside the byte array is valid // running the command "nginx -t" using a temporal file. -func (n NGINXController) testTemplate(cfg []byte) error { +func (n *NGINXController) testTemplate(cfg []byte) error { if len(cfg) == 0 { return fmt.Errorf("invalid NGINX configuration (empty)") } @@ -659,6 +665,8 @@ Error: %v // changes were detected. The received backend Configuration is merged with the // configuration ConfigMap before generating the final configuration file. // Returns nil in case the backend was successfully reloaded. +// +//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered. func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { cfg := n.store.GetBackendConfiguration() cfg.Resolver = n.resolver @@ -668,12 +676,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { return err } - err = createOpentracingCfg(cfg) - if err != nil { - return err - } - - err = createOpentelemetryCfg(cfg) + err = createOpentelemetryCfg(&cfg) if err != nil { return err } @@ -684,7 +687,10 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { } if klog.V(2).Enabled() { - src, _ := os.ReadFile(cfgPath) + src, err := os.ReadFile(cfgPath) + if err != nil { + return err + } if !bytes.Equal(src, content) { tmpfile, err := os.CreateTemp("", "new-nginx-cfg") if err != nil { @@ -695,11 +701,14 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { if err != nil { return err } - + //nolint:gosec //Ignore G204 error diffOutput, err := exec.Command("diff", "-I", "'# Configuration.*'", "-u", cfgPath, tmpfile.Name()).CombinedOutput() if err != nil { if exitError, ok := err.(*exec.ExitError); ok { - ws := exitError.Sys().(syscall.WaitStatus) + ws, ok := exitError.Sys().(syscall.WaitStatus) + if !ok { + klog.Errorf("unexpected type: %T", exitError.Sys()) + } if ws.ExitStatus() == 2 { klog.Warningf("Failed to executing diff command: %v", err) } @@ -799,45 +808,6 @@ func (n *NGINXController) setupSSLProxy() { }() } -// Helper function to clear Certificates from the ingress configuration since they should be ignored when -// checking if the new configuration changes can be applied dynamically if dynamic certificates is on -func clearCertificates(config *ingress.Configuration) { - var clearedServers []*ingress.Server - for _, server := range config.Servers { - copyOfServer := *server - copyOfServer.SSLCert = nil - clearedServers = append(clearedServers, ©OfServer) - } - config.Servers = clearedServers -} - -// Helper function to clear endpoints from the ingress configuration since they should be ignored when -// checking if the new configuration changes can be applied dynamically. -func clearL4serviceEndpoints(config *ingress.Configuration) { - var clearedTCPL4Services []ingress.L4Service - var clearedUDPL4Services []ingress.L4Service - for _, service := range config.TCPEndpoints { - copyofService := ingress.L4Service{ - Port: service.Port, - Backend: service.Backend, - Endpoints: []ingress.Endpoint{}, - Service: nil, - } - clearedTCPL4Services = append(clearedTCPL4Services, copyofService) - } - for _, service := range config.UDPEndpoints { - copyofService := ingress.L4Service{ - Port: service.Port, - Backend: service.Backend, - Endpoints: []ingress.Endpoint{}, - Service: nil, - } - clearedUDPL4Services = append(clearedUDPL4Services, copyofService) - } - config.TCPEndpoints = clearedTCPL4Services - config.UDPEndpoints = clearedUDPL4Services -} - // configureDynamically encodes new Backends in JSON format and POSTs the // payload to an internal HTTP endpoint handled by Lua. func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) error { @@ -868,9 +838,10 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro return nil } -func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []ingress.L4Service) error { +func updateStreamConfiguration(tcpEndpoints, udpEndpoints []ingress.L4Service) error { streams := make([]ingress.Backend, 0) - for _, ep := range TCPEndpoints { + for i := range tcpEndpoints { + ep := &tcpEndpoints[i] var service *apiv1.Service if ep.Service != nil { service = &apiv1.Service{Spec: ep.Service.Spec} @@ -884,7 +855,8 @@ func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints [] Service: service, }) } - for _, ep := range UDPEndpoints { + for i := range udpEndpoints { + ep := &udpEndpoints[i] var service *apiv1.Service if ep.Service != nil { service = &apiv1.Service{Spec: ep.Service.Spec} @@ -1024,43 +996,6 @@ func configureCertificates(rawServers []*ingress.Server) error { return nil } -const zipkinTmpl = `{ - "service_name": "{{ .ZipkinServiceName }}", - "collector_host": "{{ .ZipkinCollectorHost }}", - "collector_port": {{ .ZipkinCollectorPort }}, - "sample_rate": {{ .ZipkinSampleRate }} -}` - -const jaegerTmpl = `{ - "service_name": "{{ .JaegerServiceName }}", - "propagation_format": "{{ .JaegerPropagationFormat }}", - "sampler": { - "type": "{{ .JaegerSamplerType }}", - "param": {{ .JaegerSamplerParam }}, - "samplingServerURL": "{{ .JaegerSamplerHost }}:{{ .JaegerSamplerPort }}/sampling" - }, - "reporter": { - "endpoint": "{{ .JaegerEndpoint }}", - "localAgentHostPort": "{{ .JaegerCollectorHost }}:{{ .JaegerCollectorPort }}" - }, - "headers": { - "TraceContextHeaderName": "{{ .JaegerTraceContextHeaderName }}", - "jaegerDebugHeader": "{{ .JaegerDebugHeader }}", - "jaegerBaggageHeader": "{{ .JaegerBaggageHeader }}", - "traceBaggageHeaderPrefix": "{{ .JaegerTraceBaggageHeaderPrefix }}" - } -}` - -const datadogTmpl = `{ - "service": "{{ .DatadogServiceName }}", - "agent_host": "{{ .DatadogCollectorHost }}", - "agent_port": {{ .DatadogCollectorPort }}, - "environment": "{{ .DatadogEnvironment }}", - "operation_name_override": "{{ .DatadogOperationNameOverride }}", - "sample_rate": {{ .DatadogSampleRate }}, - "dd.priority.sampling": {{ .DatadogPrioritySampling }} -}` - const otelTmpl = ` exporter = "otlp" processor = "batch" @@ -1084,43 +1019,7 @@ ratio = {{ .OtelSamplerRatio }} parent_based = {{ .OtelSamplerParentBased }} ` -func createOpentracingCfg(cfg ngx_config.Configuration) error { - var tmpl *template.Template - var err error - - if cfg.ZipkinCollectorHost != "" { - tmpl, err = template.New("zipkin").Parse(zipkinTmpl) - if err != nil { - return err - } - } else if cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "" { - tmpl, err = template.New("jaeger").Parse(jaegerTmpl) - if err != nil { - return err - } - } else if cfg.DatadogCollectorHost != "" { - tmpl, err = template.New("datadog").Parse(datadogTmpl) - if err != nil { - return err - } - } else { - tmpl, _ = template.New("empty").Parse("{}") - } - - tmplBuf := bytes.NewBuffer(make([]byte, 0)) - err = tmpl.Execute(tmplBuf, cfg) - if err != nil { - return err - } - - // Expand possible environment variables before writing the configuration to file. - expanded := os.ExpandEnv(tmplBuf.String()) - - return os.WriteFile("/etc/nginx/opentracing.json", []byte(expanded), file.ReadWriteByUser) -} - -func createOpentelemetryCfg(cfg ngx_config.Configuration) error { - +func createOpentelemetryCfg(cfg *ngx_config.Configuration) error { tmpl, err := template.New("otel").Parse(otelTmpl) if err != nil { return err @@ -1145,7 +1044,10 @@ func cleanTempNginxCfg() error { return filepath.SkipDir } - dur, _ := time.ParseDuration("-5m") + dur, err := time.ParseDuration("-5m") + if err != nil { + return err + } fiveMinutesAgo := time.Now().Add(dur) if strings.HasPrefix(info.Name(), tempNginxPattern) && info.ModTime().Before(fiveMinutesAgo) { files = append(files, path) diff --git a/internal/ingress/controller/nginx_test.go b/internal/ingress/controller/nginx_test.go index 56eb0f324..c68b0b188 100644 --- a/internal/ingress/controller/nginx_test.go +++ b/internal/ingress/controller/nginx_test.go @@ -58,6 +58,7 @@ func TestConfigureDynamically(t *testing.T) { server := &httptest.Server{ Listener: listener, + //nolint:gosec // Ignore not configured ReadHeaderTimeout in testing Config: &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) @@ -76,23 +77,17 @@ func TestConfigureDynamically(t *testing.T) { switch r.URL.Path { case "/configuration/backends": - { - if strings.Contains(body, "target") { - t.Errorf("unexpected target reference in JSON content: %v", body) - } + if strings.Contains(body, "target") { + t.Errorf("unexpected target reference in JSON content: %v", body) + } - if !strings.Contains(body, "service") { - t.Errorf("service reference should be present in JSON content: %v", body) - } + if !strings.Contains(body, "service") { + t.Errorf("service reference should be present in JSON content: %v", body) } case "/configuration/general": - { - } case "/configuration/servers": - { - if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) { - t.Errorf("should be present in JSON content: %v", body) - } + if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) { + t.Errorf("should be present in JSON content: %v", body) } default: t.Errorf("unknown request to %s", r.URL.Path) @@ -218,6 +213,7 @@ func TestConfigureCertificates(t *testing.T) { server := &httptest.Server{ Listener: listener, + //nolint:gosec // Ignore not configured ReadHeaderTimeout in testing Config: &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) @@ -414,6 +410,7 @@ func TestCleanTempNginxCfg(t *testing.T) { } } +//nolint:unparam // Ingnore `network` always receives `"tcp"` error func tryListen(network, address string) (l net.Listener, err error) { condFunc := func() (bool, error) { l, err = net.Listen(network, address) diff --git a/internal/ingress/controller/process/nginx.go b/internal/ingress/controller/process/nginx.go index 70227ac2e..fdf501616 100644 --- a/internal/ingress/controller/process/nginx.go +++ b/internal/ingress/controller/process/nginx.go @@ -30,7 +30,10 @@ func IsRespawnIfRequired(err error) bool { return false } - waitStatus := exitError.Sys().(syscall.WaitStatus) + waitStatus, ok := exitError.Sys().(syscall.WaitStatus) + if !ok { + return false + } klog.Warningf(` ------------------------------------------------------------------------------- NGINX master process died (%v): %v diff --git a/internal/ingress/controller/status.go b/internal/ingress/controller/status.go index 3b0a41ab8..e061b2cb7 100644 --- a/internal/ingress/controller/status.go +++ b/internal/ingress/controller/status.go @@ -50,7 +50,7 @@ func setupLeaderElection(config *leaderElectionConfig) { var cancelContext context.CancelFunc - var newLeaderCtx = func(ctx context.Context) context.CancelFunc { + newLeaderCtx := func(ctx context.Context) context.CancelFunc { // allow to cancel the context in case we stop being the leader leaderCtx, cancel := context.WithCancel(ctx) go elector.Run(leaderCtx) @@ -86,8 +86,10 @@ func setupLeaderElection(config *leaderElectionConfig) { } broadcaster := record.NewBroadcaster() - hostname, _ := os.Hostname() - + hostname, err := os.Hostname() + if err != nil { + klog.Errorf("unexpected error getting hostname: %v", err) + } recorder := broadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{ Component: "ingress-leader-elector", Host: hostname, @@ -107,7 +109,7 @@ func setupLeaderElection(config *leaderElectionConfig) { ttl := 30 * time.Second - elector, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{ + elector, err = leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{ Lock: lock, LeaseDuration: ttl, RenewDeadline: ttl / 2, diff --git a/internal/ingress/controller/store/backend_ssl.go b/internal/ingress/controller/store/backend_ssl.go index 6f0fcf189..81b508cd2 100644 --- a/internal/ingress/controller/store/backend_ssl.go +++ b/internal/ingress/controller/store/backend_ssl.go @@ -88,10 +88,11 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error auth := secret.Data["auth"] // namespace/secretName -> namespace-secretName - nsSecName := strings.Replace(secretName, "/", "-", -1) + nsSecName := strings.ReplaceAll(secretName, "/", "-") var sslCert *ingress.SSLCert - if okcert && okkey { + switch { + case okcert && okkey: if cert == nil { return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName) } @@ -144,7 +145,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error } klog.V(3).InfoS(msg) - } else if len(ca) > 0 { + case len(ca) > 0: sslCert, err = ssl.CreateCACert(ca) if err != nil { return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err) @@ -166,7 +167,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error // makes this secret in 'syncSecret' to be used for Certificate Authentication // this does not enable Certificate Authentication klog.V(3).InfoS("Configuring Secret for TLS authentication", "secret", secretName) - } else { + default: if auth != nil { return nil, ErrSecretForAuth } diff --git a/internal/ingress/controller/store/endpointslice.go b/internal/ingress/controller/store/endpointslice.go index 78d088695..61bc63ae7 100644 --- a/internal/ingress/controller/store/endpointslice.go +++ b/internal/ingress/controller/store/endpointslice.go @@ -38,7 +38,7 @@ func (s *EndpointSliceLister) MatchByKey(key string) ([]*discoveryv1.EndpointSli keyNsLen = 0 } else { // count '/' char - keyNsLen += 1 + keyNsLen++ } // filter endpointSlices owned by svc for _, listKey := range s.ListKeys() { diff --git a/internal/ingress/controller/store/endpointslice_test.go b/internal/ingress/controller/store/endpointslice_test.go index e12a98c2f..5d423a3a6 100644 --- a/internal/ingress/controller/store/endpointslice_test.go +++ b/internal/ingress/controller/store/endpointslice_test.go @@ -59,7 +59,9 @@ func TestEndpointSliceLister(t *testing.T) { }, }, } - el.Add(endpointSlice) + if err := el.Add(endpointSlice); err != nil { + t.Errorf("unexpected error %v", err) + } endpointSlice = &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace", @@ -69,7 +71,9 @@ func TestEndpointSliceLister(t *testing.T) { }, }, } - el.Add(endpointSlice) + if err := el.Add(endpointSlice); err != nil { + t.Errorf("unexpected error %v", err) + } endpointSlice = &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace", @@ -79,9 +83,10 @@ func TestEndpointSliceLister(t *testing.T) { }, }, } - el.Add(endpointSlice) + if err := el.Add(endpointSlice); err != nil { + t.Errorf("unexpected error %v", err) + } eps, err := el.MatchByKey(key) - if err != nil { t.Errorf("unexpeted error %v", err) } @@ -108,7 +113,9 @@ func TestEndpointSliceLister(t *testing.T) { }, }, } - el.Add(endpointSlice) + if err := el.Add(endpointSlice); err != nil { + t.Errorf("unexpected error %v", err) + } endpointSlice2 := &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns2, @@ -118,7 +125,9 @@ func TestEndpointSliceLister(t *testing.T) { }, }, } - el.Add(endpointSlice2) + if err := el.Add(endpointSlice2); err != nil { + t.Errorf("unexpected error %v", err) + } eps, err := el.MatchByKey(key) if err != nil { t.Errorf("unexpeted error %v", err) diff --git a/internal/ingress/controller/store/store.go b/internal/ingress/controller/store/store.go index 7157332c3..4288785de 100644 --- a/internal/ingress/controller/store/store.go +++ b/internal/ingress/controller/store/store.go @@ -36,7 +36,6 @@ import ( "k8s.io/apimachinery/pkg/labels" k8sruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -70,6 +69,9 @@ type Storer interface { // GetBackendConfiguration returns the nginx configuration stored in a configmap GetBackendConfiguration() ngx_config.Configuration + // GetSecurityConfiguration returns the configuration options from Ingress + GetSecurityConfiguration() defaults.SecurityConfiguration + // GetConfigMap returns the ConfigMap matching key. GetConfigMap(key string) (*corev1.ConfigMap, error) @@ -103,7 +105,7 @@ type Storer interface { Run(stopCh chan struct{}) // GetIngressClass validates given ingress against ingress class configuration and returns the ingress class. - GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) + GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error) } // EventType type of event associated with an informer @@ -240,7 +242,9 @@ type k8sStore struct { defaultSSLCertificate string } -// New creates a new object store to be used in the ingress controller +// New creates a new object store to be used in the ingress controller. +// +//nolint:gocyclo // Ignore function complexity error. func New( namespace string, namespaceSelector labels.Selector, @@ -250,9 +254,9 @@ func New( updateCh *channels.RingChannel, disableCatchAll bool, deepInspector bool, - icConfig *ingressclass.IngressClassConfiguration, - disableSyncEvents bool) Storer { - + icConfig *ingressclass.Configuration, + disableSyncEvents bool, +) Storer { store := &k8sStore{ informers: &Informer{}, listers: &Lister{}, @@ -405,7 +409,10 @@ func New( return } - store.listers.IngressWithAnnotation.Delete(ing) + if err := store.listers.IngressWithAnnotation.Delete(ing); err != nil { + klog.ErrorS(err, "Error while deleting ingress from store", "ingress", klog.KObj(ing)) + return + } key := k8s.MetaNamespaceKey(ing) store.secretIngressMap.Delete(key) @@ -469,7 +476,8 @@ func New( _, errOld = store.GetIngressClass(oldIng, icConfig) classCur, errCur = store.GetIngressClass(curIng, icConfig) } - if errOld != nil && errCur == nil { + switch { + case errOld != nil && errCur == nil: if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll { klog.InfoS("ignoring update for catch-all ingress because of --disable-catch-all", "ingress", klog.KObj(curIng)) return @@ -477,11 +485,11 @@ func New( klog.InfoS("creating ingress", "ingress", klog.KObj(curIng), "ingressclass", classCur) recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync") - } else if errOld == nil && errCur != nil { + case errOld == nil && errCur != nil: klog.InfoS("removing ingress because of unknown ingressclass", "ingress", klog.KObj(curIng)) ingDeleteHandler(old) return - } else if errCur == nil && !reflect.DeepEqual(old, cur) { + case errCur == nil && !reflect.DeepEqual(old, cur): if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll { klog.InfoS("ignoring update for catch-all ingress and delete old one because of --disable-catch-all", "ingress", klog.KObj(curIng)) ingDeleteHandler(old) @@ -489,7 +497,7 @@ func New( } recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync") - } else { + default: klog.V(3).InfoS("No changes on ingress. Skipping update", "ingress", klog.KObj(curIng)) return } @@ -514,7 +522,10 @@ func New( ingressClassEventHandler := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { - ingressclass := obj.(*networkingv1.IngressClass) + ingressclass, ok := obj.(*networkingv1.IngressClass) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } foundClassByName := false if icConfig.IngressClassByName && ingressclass.Name == icConfig.AnnotationValue { klog.InfoS("adding ingressclass as ingress-class-by-name is configured", "ingressclass", klog.KObj(ingressclass)) @@ -536,7 +547,10 @@ func New( } }, DeleteFunc: func(obj interface{}) { - ingressclass := obj.(*networkingv1.IngressClass) + ingressclass, ok := obj.(*networkingv1.IngressClass) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } if ingressclass.Spec.Controller != icConfig.Controller { klog.InfoS("ignoring ingressclass as the spec.controller is not the same of this ingress", "ingressclass", klog.KObj(ingressclass)) return @@ -552,8 +566,14 @@ func New( } }, UpdateFunc: func(old, cur interface{}) { - oic := old.(*networkingv1.IngressClass) - cic := cur.(*networkingv1.IngressClass) + oic, ok := old.(*networkingv1.IngressClass) + if !ok { + klog.Errorf("unexpected type: %T", old) + } + cic, ok := cur.(*networkingv1.IngressClass) + if !ok { + klog.Errorf("unexpected type: %T", cur) + } if cic.Spec.Controller != icConfig.Controller { klog.InfoS("ignoring ingressclass as the spec.controller is not the same of this ingress", "ingressclass", klog.KObj(cic)) return @@ -576,7 +596,10 @@ func New( secrEventHandler := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { - sec := obj.(*corev1.Secret) + sec, ok := obj.(*corev1.Secret) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } key := k8s.MetaNamespaceKey(sec) if store.defaultSSLCertificate == key { @@ -603,7 +626,10 @@ func New( }, UpdateFunc: func(old, cur interface{}) { if !reflect.DeepEqual(old, cur) { - sec := cur.(*corev1.Secret) + sec, ok := cur.(*corev1.Secret) + if !ok { + klog.Errorf("unexpected type: %T", cur) + } key := k8s.MetaNamespaceKey(sec) if !watchedNamespace(sec.Namespace) { @@ -690,8 +716,14 @@ func New( } }, UpdateFunc: func(old, cur interface{}) { - oeps := old.(*discoveryv1.EndpointSlice) - ceps := cur.(*discoveryv1.EndpointSlice) + oeps, ok := old.(*discoveryv1.EndpointSlice) + if !ok { + klog.Errorf("unexpected type: %T", old) + } + ceps, ok := cur.(*discoveryv1.EndpointSlice) + if !ok { + klog.Errorf("unexpected type: %T", cur) + } if !reflect.DeepEqual(ceps.Endpoints, oeps.Endpoints) { updateCh.In() <- Event{ Type: UpdateEvent, @@ -745,7 +777,10 @@ func New( cmEventHandler := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { - cfgMap := obj.(*corev1.ConfigMap) + cfgMap, ok := obj.(*corev1.ConfigMap) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } key := k8s.MetaNamespaceKey(cfgMap) handleCfgMapEvent(key, cfgMap, "CREATE") }, @@ -754,7 +789,10 @@ func New( return } - cfgMap := cur.(*corev1.ConfigMap) + cfgMap, ok := cur.(*corev1.ConfigMap) + if !ok { + klog.Errorf("unexpected type: %T", cur) + } key := k8s.MetaNamespaceKey(cfgMap) handleCfgMapEvent(key, cfgMap, "UPDATE") }, @@ -762,7 +800,10 @@ func New( serviceHandler := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { - svc := obj.(*corev1.Service) + svc, ok := obj.(*corev1.Service) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } if svc.Spec.Type == corev1.ServiceTypeExternalName { updateCh.In() <- Event{ Type: CreateEvent, @@ -771,7 +812,10 @@ func New( } }, DeleteFunc: func(obj interface{}) { - svc := obj.(*corev1.Service) + svc, ok := obj.(*corev1.Service) + if !ok { + klog.Errorf("unexpected type: %T", obj) + } if svc.Spec.Type == corev1.ServiceTypeExternalName { updateCh.In() <- Event{ Type: DeleteEvent, @@ -780,8 +824,14 @@ func New( } }, UpdateFunc: func(old, cur interface{}) { - oldSvc := old.(*corev1.Service) - curSvc := cur.(*corev1.Service) + oldSvc, ok := old.(*corev1.Service) + if !ok { + klog.Errorf("unexpected type: %T", old) + } + curSvc, ok := cur.(*corev1.Service) + if !ok { + klog.Errorf("unexpected type: %T", cur) + } if reflect.DeepEqual(oldSvc, curSvc) { return @@ -794,17 +844,32 @@ func New( }, } - store.informers.Ingress.AddEventHandler(ingEventHandler) - if !icConfig.IgnoreIngressClass { - store.informers.IngressClass.AddEventHandler(ingressClassEventHandler) + if _, err := store.informers.Ingress.AddEventHandler(ingEventHandler); err != nil { + klog.Errorf("Error adding ingress event handler: %v", err) + } + if !icConfig.IgnoreIngressClass { + if _, err := store.informers.IngressClass.AddEventHandler(ingressClassEventHandler); err != nil { + klog.Errorf("Error adding ingress class event handler: %v", err) + } + } + if _, err := store.informers.EndpointSlice.AddEventHandler(epsEventHandler); err != nil { + klog.Errorf("Error adding endpoint slice event handler: %v", err) + } + if _, err := store.informers.Secret.AddEventHandler(secrEventHandler); err != nil { + klog.Errorf("Error adding secret event handler: %v", err) + } + if _, err := store.informers.ConfigMap.AddEventHandler(cmEventHandler); err != nil { + klog.Errorf("Error adding configmap event handler: %v", err) + } + if _, err := store.informers.Service.AddEventHandler(serviceHandler); err != nil { + klog.Errorf("Error adding service event handler: %v", err) } - store.informers.EndpointSlice.AddEventHandler(epsEventHandler) - store.informers.Secret.AddEventHandler(secrEventHandler) - store.informers.ConfigMap.AddEventHandler(cmEventHandler) - store.informers.Service.AddEventHandler(serviceHandler) // do not wait for informers to read the configmap configuration - ns, name, _ := k8s.ParseNameNS(configmap) + ns, name, err := k8s.ParseNameNS(configmap) + if err != nil { + klog.Errorf("unexpected error parsing name and ns: %v", err) + } cm, err := client.CoreV1().ConfigMaps(ns).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { klog.Warningf("Unexpected error reading configuration configmap: %v", err) @@ -820,10 +885,10 @@ func hasCatchAllIngressRule(spec networkingv1.IngressSpec) bool { return spec.DefaultBackend != nil } -func checkBadAnnotationValue(annotations map[string]string, badwords string) error { +func checkBadAnnotationValue(annotationMap map[string]string, badwords string) error { arraybadWords := strings.Split(strings.TrimSpace(badwords), ",") - for annotation, value := range annotations { + for annotation, value := range annotationMap { if strings.HasPrefix(annotation, fmt.Sprintf("%s/", parser.AnnotationsPrefix)) { for _, forbiddenvalue := range arraybadWords { if strings.Contains(value, forbiddenvalue) { @@ -868,9 +933,14 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) { k8s.SetDefaultNGINXPathType(copyIng) - err := s.listers.IngressWithAnnotation.Update(&ingress.Ingress{ + parsed, err := s.annotations.Extract(ing) + if err != nil { + klog.Error(err) + return + } + err = s.listers.IngressWithAnnotation.Update(&ingress.Ingress{ Ingress: *copyIng, - ParsedAnnotations: s.annotations.Extract(ing), + ParsedAnnotations: parsed, }) if err != nil { klog.Error(err) @@ -906,8 +976,10 @@ func (s *k8sStore) updateSecretIngressMap(ing *networkingv1.Ingress) { "proxy-ssl-secret", "secure-verify-ca-secret", } + + secConfig := s.GetSecurityConfiguration().AllowCrossNamespaceResources for _, ann := range secretAnnotations { - secrKey, err := objectRefAnnotationNsKey(ann, ing) + secrKey, err := objectRefAnnotationNsKey(ann, ing, secConfig) if err != nil && !errors.IsMissingAnnotations(err) { klog.Errorf("error reading secret reference in annotation %q: %s", ann, err) continue @@ -923,8 +995,9 @@ func (s *k8sStore) updateSecretIngressMap(ing *networkingv1.Ingress) { // objectRefAnnotationNsKey returns an object reference formatted as a // 'namespace/name' key from the given annotation name. -func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress) (string, error) { - annValue, err := parser.GetStringAnnotation(ann, ing) +func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress, allowCrossNamespace bool) (string, error) { + // We pass nil fields, as this is an internal process and we don't need to validate it. + annValue, err := parser.GetStringAnnotation(ann, ing, nil) if err != nil { return "", err } @@ -937,6 +1010,9 @@ func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress) (string, er if secrNs == "" { return fmt.Sprintf("%v/%v", ing.Namespace, secrName), nil } + if !allowCrossNamespace && secrNs != ing.Namespace { + return "", fmt.Errorf("cross namespace secret is not supported") + } return annValue, nil } @@ -971,7 +1047,7 @@ func (s *k8sStore) GetService(key string) (*corev1.Service, error) { return s.listers.Service.ByKey(key) } -func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) { +func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error) { // First we try ingressClassName if !icConfig.IgnoreIngressClass && ing.Spec.IngressClassName != nil { iclass, err := s.listers.IngressClass.ByKey(*ing.Spec.IngressClassName) @@ -982,11 +1058,11 @@ func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressc } // Then we try annotation - if ingressclass, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok { - if ingressclass != icConfig.AnnotationValue { + if class, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok { + if class != icConfig.AnnotationValue { return "", fmt.Errorf("ingress class annotation is not equal to the expected by Ingress Controller") } - return ingressclass, nil + return class, nil } // Then we accept if the WithoutClass is enabled @@ -1027,7 +1103,10 @@ func (s *k8sStore) ListIngresses() []*ingress.Ingress { // filter ingress rules ingresses := make([]*ingress.Ingress, 0) for _, item := range s.listers.IngressWithAnnotation.List() { - ing := item.(*ingress.Ingress) + ing, ok := item.(*ingress.Ingress) + if !ok { + klog.Errorf("unexpected type: %T", item) + } ingresses = append(ingresses, ing) } @@ -1111,6 +1190,17 @@ func (s *k8sStore) GetBackendConfiguration() ngx_config.Configuration { return s.backendConfig } +func (s *k8sStore) GetSecurityConfiguration() defaults.SecurityConfiguration { + s.backendConfigMu.RLock() + defer s.backendConfigMu.RUnlock() + + secConfig := defaults.SecurityConfiguration{ + AllowCrossNamespaceResources: s.backendConfig.AllowCrossNamespaceResources, + AnnotationsRiskLevel: s.backendConfig.AnnotationsRiskLevel, + } + return secConfig +} + func (s *k8sStore) setConfig(cmap *corev1.ConfigMap) { s.backendConfigMu.Lock() defer s.backendConfigMu.Unlock() @@ -1125,7 +1215,7 @@ func (s *k8sStore) setConfig(cmap *corev1.ConfigMap) { s.backendConfig.UseGeoIP2 = false } - s.writeSSLSessionTicketKey(cmap, "/etc/nginx/tickets.key") + s.writeSSLSessionTicketKey(cmap, "/etc/ingress-controller/tickets.key") } // Run initiates the synchronization of the informers and the initial @@ -1138,7 +1228,7 @@ func (s *k8sStore) Run(stopCh chan struct{}) { var runtimeScheme = k8sruntime.NewScheme() func init() { - utilruntime.Must(networkingv1.AddToScheme(runtimeScheme)) + runtime.Must(networkingv1.AddToScheme(runtimeScheme)) } func toIngress(obj interface{}) (*networkingv1.Ingress, bool) { diff --git a/internal/ingress/controller/store/store_test.go b/internal/ingress/controller/store/store_test.go index 9fe6e37bb..317c0f36c 100644 --- a/internal/ingress/controller/store/store_test.go +++ b/internal/ingress/controller/store/store_test.go @@ -44,29 +44,27 @@ import ( var pathPrefix networking.PathType = networking.PathTypePrefix -var DefaultClassConfig = &ingressclass.IngressClassConfiguration{ +var DefaultClassConfig = &ingressclass.Configuration{ Controller: ingressclass.DefaultControllerName, AnnotationValue: ingressclass.DefaultAnnotationValue, WatchWithoutClass: false, } -var ( - commonIngressSpec = networking.IngressSpec{ - Rules: []networking.IngressRule{ - { - Host: "dummy", - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: []networking.HTTPIngressPath{ - { - Path: "/", - PathType: &pathPrefix, - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: "http-svc", - Port: networking.ServiceBackendPort{ - Number: 80, - }, +var commonIngressSpec = networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + Host: "dummy", + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + Path: "/", + PathType: &pathPrefix, + Backend: networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "http-svc", + Port: networking.ServiceBackendPort{ + Number: 80, }, }, }, @@ -75,12 +73,15 @@ var ( }, }, }, - } -) + }, +} +const updateDummyHost = "update-dummy" + +//nolint:gocyclo // Ignore function complexity error func TestStore(t *testing.T) { - //TODO: move env definition to docker image? - os.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin") + // TODO: move env definition to docker image? + t.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin") pathPrefix = networking.PathTypePrefix @@ -90,9 +91,12 @@ func TestStore(t *testing.T) { t.Fatalf("error: %v", err) } - emptySelector, _ := labels.Parse("") + emptySelector, err := labels.Parse("") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } - defer te.Stop() + defer te.Stop() //nolint:errcheck // Ignore the error clientSet, err := kubernetes.NewForConfig(cfg) if err != nil { @@ -176,7 +180,11 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } + if e.Obj == nil { continue } @@ -230,7 +238,7 @@ func TestStore(t *testing.T) { time.Sleep(1 * time.Second) ni := ing.DeepCopy() - ni.Spec.Rules[0].Host = "update-dummy" + ni.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(ni, clientSet, t) if err != nil { t.Errorf("error creating ingress: %v", err) @@ -281,7 +289,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -343,7 +354,7 @@ func TestStore(t *testing.T) { defer deleteIngress(invalidIngress, clientSet, t) ni := ing.DeepCopy() - ni.Spec.Rules[0].Host = "update-dummy" + ni.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(ni, clientSet, t) if err != nil { t.Errorf("error creating ingress: %v", err) @@ -392,7 +403,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -411,7 +425,7 @@ func TestStore(t *testing.T) { } }(updateCh) - ingressClassconfig := &ingressclass.IngressClassConfiguration{ + ingressClassconfig := &ingressclass.Configuration{ Controller: ingressclass.DefaultControllerName, AnnotationValue: ingressclass.DefaultAnnotationValue, WatchWithoutClass: true, @@ -463,7 +477,7 @@ func TestStore(t *testing.T) { time.Sleep(1 * time.Second) validIngressUpdated := validIngress1.DeepCopy() - validIngressUpdated.Spec.Rules[0].Host = "update-dummy" + validIngressUpdated.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(validIngressUpdated, clientSet, t) if err != nil { t.Errorf("error updating ingress: %v", err) @@ -523,7 +537,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -542,7 +559,7 @@ func TestStore(t *testing.T) { } }(updateCh) - ingressClassconfig := &ingressclass.IngressClassConfiguration{ + ingressClassconfig := &ingressclass.Configuration{ Controller: ingressclass.DefaultControllerName, AnnotationValue: ic, IngressClassByName: true, @@ -581,7 +598,7 @@ func TestStore(t *testing.T) { time.Sleep(1 * time.Second) ingressUpdated := ing.DeepCopy() - ingressUpdated.Spec.Rules[0].Host = "update-dummy" + ingressUpdated.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(ingressUpdated, clientSet, t) if err != nil { t.Errorf("error updating ingress: %v", err) @@ -630,7 +647,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -684,7 +704,7 @@ func TestStore(t *testing.T) { time.Sleep(1 * time.Second) invalidIngressUpdated := invalidIngress.DeepCopy() - invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy" + invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(invalidIngressUpdated, clientSet, t) if err != nil { t.Errorf("error creating ingress: %v", err) @@ -725,7 +745,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -778,7 +801,7 @@ func TestStore(t *testing.T) { time.Sleep(1 * time.Second) invalidIngressUpdated := invalidIngress.DeepCopy() - invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy" + invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost _ = ensureIngress(invalidIngressUpdated, clientSet, t) if err != nil { t.Errorf("error creating ingress: %v", err) @@ -816,7 +839,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -908,7 +934,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -1008,7 +1037,6 @@ func TestStore(t *testing.T) { if atomic.LoadUint64(&del) != 1 { t.Errorf("expected 1 events of type Delete but %v occurred", del) } - }) t.Run("should create an ingress with a secret which does not exist", func(t *testing.T) { @@ -1032,7 +1060,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -1159,7 +1190,10 @@ func TestStore(t *testing.T) { return } - e := evt.(Event) + e, ok := evt.(Event) + if !ok { + return + } if e.Obj == nil { continue } @@ -1174,7 +1208,10 @@ func TestStore(t *testing.T) { } }(updateCh) - namesapceSelector, _ := labels.Parse("foo=bar") + namesapceSelector, err := labels.Parse("foo=bar") + if err != nil { + t.Errorf("unexpected error: %v", err) + } storer := New( ns, namesapceSelector, @@ -1236,7 +1273,6 @@ func TestStore(t *testing.T) { if atomic.LoadUint64(&del) != 0 { t.Errorf("expected 0 events of type Delete but %v occurred", del) } - }) // test add ingress with secret it doesn't exists and then add secret // check secret is generated on fs @@ -1274,16 +1310,16 @@ func deleteNamespace(ns string, clientSet kubernetes.Interface, t *testing.T) { func createIngressClass(clientSet kubernetes.Interface, t *testing.T, controller string) string { t.Helper() - ingressclass := &networking.IngressClass{ + class := &networking.IngressClass{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("ingress-nginx-%v", time.Now().Unix()), - //Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet + // Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet }, Spec: networking.IngressClassSpec{ Controller: controller, }, } - ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), ingressclass, metav1.CreateOptions{}) + ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), class, metav1.CreateOptions{}) if err != nil { t.Errorf("error creating ingress class: %v", err) } @@ -1299,7 +1335,7 @@ func deleteIngressClass(ic string, clientSet kubernetes.Interface, t *testing.T) } } -func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) string { +func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) { t.Helper() configMap := &v1.ConfigMap{ @@ -1308,51 +1344,47 @@ func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) st }, } - cm, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{}) + _, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{}) if err != nil { t.Errorf("error creating the configuration map: %v", err) } - - return cm.Name } -func ensureIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress { +func ensureIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress { t.Helper() - ing, err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{}) - + newIngress, err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Update(context.TODO(), ing, metav1.UpdateOptions{}) if err != nil { if k8sErrors.IsNotFound(err) { - t.Logf("Ingress %v not found, creating", ingress) + t.Logf("Ingress %v not found, creating", ing) - ing, err = clientSet.NetworkingV1().Ingresses(ingress.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{}) + newIngress, err = clientSet.NetworkingV1().Ingresses(ing.Namespace).Create(context.TODO(), ing, metav1.CreateOptions{}) if err != nil { - t.Fatalf("error creating ingress %+v: %v", ingress, err) + t.Fatalf("error creating ingress %+v: %v", ing, err) } - t.Logf("Ingress %+v created", ingress) - return ing + t.Logf("Ingress %+v created", ing) + return newIngress } - t.Fatalf("error updating ingress %+v: %v", ingress, err) + t.Fatalf("error updating ingress %+v: %v", ing, err) } - return ing + return newIngress } -func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) { +func deleteIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) { t.Helper() - err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Delete(context.TODO(), ingress.Name, metav1.DeleteOptions{}) - + err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Delete(context.TODO(), ing.Name, metav1.DeleteOptions{}) if err != nil { - t.Errorf("failed to delete ingress %+v: %v", ingress, err) + t.Errorf("failed to delete ingress %+v: %v", ing, err) } - t.Logf("Ingress %+v deleted", ingress) + t.Logf("Ingress %+v deleted", ing) } // newStore creates a new mock object store for tests which do not require the // use of Informers. -func newStore(t *testing.T) *k8sStore { +func newStore() *k8sStore { return &k8sStore{ listers: &Lister{ // add more listers if needed @@ -1369,7 +1401,7 @@ func newStore(t *testing.T) *k8sStore { } func TestUpdateSecretIngressMap(t *testing.T) { - s := newStore(t) + s := newStore() ingTpl := &networking.Ingress{ ObjectMeta: metav1.ObjectMeta{ @@ -1377,14 +1409,18 @@ func TestUpdateSecretIngressMap(t *testing.T) { Namespace: "testns", }, } - s.listers.Ingress.Add(ingTpl) + if err := s.listers.Ingress.Add(ingTpl); err != nil { + t.Errorf("error adding the Ingress template: %v", err) + } t.Run("with TLS secret", func(t *testing.T) { ing := ingTpl.DeepCopy() ing.Spec = networking.IngressSpec{ TLS: []networking.IngressTLS{{SecretName: "tls"}}, } - s.listers.Ingress.Update(ing) + if err := s.listers.Ingress.Update(ing); err != nil { + t.Errorf("error updating the Ingress: %v", err) + } s.updateSecretIngressMap(ing) if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("testns/tls")) { @@ -1397,7 +1433,9 @@ func TestUpdateSecretIngressMap(t *testing.T) { ing.ObjectMeta.SetAnnotations(map[string]string{ parser.GetAnnotationWithPrefix("auth-secret"): "auth", }) - s.listers.Ingress.Update(ing) + if err := s.listers.Ingress.Update(ing); err != nil { + t.Errorf("error updating the Ingress: %v", err) + } s.updateSecretIngressMap(ing) if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("testns/auth")) { @@ -1408,22 +1446,41 @@ func TestUpdateSecretIngressMap(t *testing.T) { t.Run("with annotation in namespace/name format", func(t *testing.T) { ing := ingTpl.DeepCopy() ing.ObjectMeta.SetAnnotations(map[string]string{ - parser.GetAnnotationWithPrefix("auth-secret"): "otherns/auth", + parser.GetAnnotationWithPrefix("auth-secret"): "testns/auth", }) - s.listers.Ingress.Update(ing) + if err := s.listers.Ingress.Update(ing); err != nil { + t.Errorf("error updating the Ingress: %v", err) + } s.updateSecretIngressMap(ing) - if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("otherns/auth")) { + if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("testns/auth")) { t.Errorf("Expected \"otherns/auth\" to be the only referenced Secret (got %d)", l) } }) + t.Run("with annotation in namespace/name format should not be supported", func(t *testing.T) { + ing := ingTpl.DeepCopy() + ing.ObjectMeta.SetAnnotations(map[string]string{ + parser.GetAnnotationWithPrefix("auth-secret"): "anotherns/auth", + }) + if err := s.listers.Ingress.Update(ing); err != nil { + t.Errorf("error updating the Ingress: %v", err) + } + s.updateSecretIngressMap(ing) + + if l := s.secretIngressMap.Len(); l != 0 { + t.Errorf("Expected \"otherns/auth\" to be denied as it contains a different namespace (got %d)", l) + } + }) + t.Run("with annotation in invalid format", func(t *testing.T) { ing := ingTpl.DeepCopy() ing.ObjectMeta.SetAnnotations(map[string]string{ parser.GetAnnotationWithPrefix("auth-secret"): "ns/name/garbage", }) - s.listers.Ingress.Update(ing) + if err := s.listers.Ingress.Update(ing); err != nil { + t.Errorf("error updating the Ingress: %v", err) + } s.updateSecretIngressMap(ing) if l := s.secretIngressMap.Len(); l != 0 { @@ -1433,7 +1490,7 @@ func TestUpdateSecretIngressMap(t *testing.T) { } func TestListIngresses(t *testing.T) { - s := newStore(t) + s := newStore() invalidIngressClass := "something" validIngressClass := ingressclass.DefaultControllerName @@ -1457,7 +1514,9 @@ func TestListIngresses(t *testing.T) { }, }, } - s.listers.IngressWithAnnotation.Add(ingressToIgnore) + if err := s.listers.IngressWithAnnotation.Add(ingressToIgnore); err != nil { + t.Errorf("error adding the Ingress: %v", err) + } ingressWithoutPath := &ingress.Ingress{ Ingress: networking.Ingress{ @@ -1492,8 +1551,9 @@ func TestListIngresses(t *testing.T) { }, }, } - s.listers.IngressWithAnnotation.Add(ingressWithoutPath) - + if err := s.listers.IngressWithAnnotation.Add(ingressWithoutPath); err != nil { + t.Errorf("error adding the Ingress: %v", err) + } ingressWithNginxClassAnnotation := &ingress.Ingress{ Ingress: networking.Ingress{ ObjectMeta: metav1.ObjectMeta{ @@ -1531,8 +1591,9 @@ func TestListIngresses(t *testing.T) { }, }, } - s.listers.IngressWithAnnotation.Add(ingressWithNginxClassAnnotation) - + if err := s.listers.IngressWithAnnotation.Add(ingressWithNginxClassAnnotation); err != nil { + t.Errorf("error adding the Ingress: %v", err) + } ingresses := s.ListIngresses() if s := len(ingresses); s != 3 { @@ -1559,7 +1620,7 @@ func TestWriteSSLSessionTicketKey(t *testing.T) { } for _, test := range tests { - s := newStore(t) + s := newStore() cmap := &v1.ConfigMap{ Data: map[string]string{ diff --git a/internal/ingress/controller/template/configmap.go b/internal/ingress/controller/template/configmap.go index c73f3b6c0..9dc019bcc 100644 --- a/internal/ingress/controller/template/configmap.go +++ b/internal/ingress/controller/template/configmap.go @@ -91,6 +91,8 @@ const ( ) // ReadConfig obtains the configuration defined by the user merged with the defaults. +// +//nolint:gocyclo // Ignore function complexity error func ReadConfig(src map[string]string) config.Configuration { conf := map[string]string{} // we need to copy the configmap data because the content is altered @@ -116,12 +118,12 @@ func ReadConfig(src map[string]string) config.Configuration { luaSharedDicts := make(map[string]int) debugConnectionsList := make([]string, 0) - //parse lua shared dict values + // parse lua shared dict values if val, ok := conf[luaSharedDictsKey]; ok { delete(conf, luaSharedDictsKey) lsd := splitAndTrimSpace(val, ",") for _, v := range lsd { - v = strings.Replace(v, " ", "", -1) + v = strings.ReplaceAll(v, " ", "") results := strings.SplitN(v, ":", 2) dictName := results[0] size := dictStrToKb(results[1]) @@ -196,7 +198,7 @@ func ReadConfig(src map[string]string) config.Configuration { if ing_net.IsIPV6(ns) { bindAddressIpv6List = append(bindAddressIpv6List, fmt.Sprintf("[%v]", ns)) } else { - bindAddressIpv4List = append(bindAddressIpv4List, fmt.Sprintf("%v", ns)) + bindAddressIpv4List = append(bindAddressIpv4List, ns.String()) } } else { klog.Warningf("%v is not a valid textual representation of an IP address", i) @@ -250,7 +252,7 @@ func ReadConfig(src map[string]string) config.Configuration { if val, ok := conf[globalAuthMethod]; ok { delete(conf, globalAuthMethod) - if len(val) != 0 && !authreq.ValidMethod(val) { + if val != "" && !authreq.ValidMethod(val) { klog.Warningf("Global auth location denied - %v.", "invalid HTTP method") } else { to.GlobalExternalAuth.Method = val @@ -261,7 +263,10 @@ func ReadConfig(src map[string]string) config.Configuration { if val, ok := conf[globalAuthSignin]; ok { delete(conf, globalAuthSignin) - signinURL, _ := parser.StringToURL(val) + signinURL, err := parser.StringToURL(val) + if err != nil { + klog.Errorf("string to URL conversion failed: %v", err) + } if signinURL == nil { klog.Warningf("Global auth location denied - %v.", "global-auth-signin setting is undefined and will not be set") } else { @@ -274,7 +279,10 @@ func ReadConfig(src map[string]string) config.Configuration { delete(conf, globalAuthSigninRedirectParam) redirectParam := strings.TrimSpace(val) - dummySigninURL, _ := parser.StringToURL(fmt.Sprintf("%s?%s=dummy", to.GlobalExternalAuth.SigninURL, redirectParam)) + dummySigninURL, err := parser.StringToURL(fmt.Sprintf("%s?%s=dummy", to.GlobalExternalAuth.SigninURL, redirectParam)) + if err != nil { + klog.Errorf("string to URL conversion failed: %v", err) + } if dummySigninURL == nil { klog.Warningf("Global auth redirect parameter denied - %v.", "global-auth-signin-redirect-param setting is invalid and will not be set") } else { @@ -286,7 +294,7 @@ func ReadConfig(src map[string]string) config.Configuration { if val, ok := conf[globalAuthResponseHeaders]; ok { delete(conf, globalAuthResponseHeaders) - if len(val) != 0 { + if val != "" { harr := splitAndTrimSpace(val, ",") for _, header := range harr { if !authreq.ValidHeader(header) { @@ -385,8 +393,8 @@ func ReadConfig(src map[string]string) config.Configuration { if val, ok := conf[debugConnections]; ok { delete(conf, debugConnections) for _, i := range splitAndTrimSpace(val, ",") { - validIp := net.ParseIP(i) - if validIp != nil { + validIP := net.ParseIP(i) + if validIP != nil { debugConnectionsList = append(debugConnectionsList, i) } else { _, _, err := net.ParseCIDR(i) @@ -415,14 +423,14 @@ func ReadConfig(src map[string]string) config.Configuration { to.DisableIpv6DNS = !ing_net.IsIPv6Enabled() to.LuaSharedDicts = luaSharedDicts - config := &mapstructure.DecoderConfig{ + decoderConfig := &mapstructure.DecoderConfig{ Metadata: nil, WeaklyTypedInput: true, Result: &to, TagName: "json", } - decoder, err := mapstructure.NewDecoder(config) + decoder, err := mapstructure.NewDecoder(decoderConfig) if err != nil { klog.Warningf("unexpected error merging defaults: %v", err) } @@ -456,6 +464,7 @@ func filterErrors(codes []int) []int { return fa } +//nolint:unparam // Ignore `sep` always receives `,` error func splitAndTrimSpace(s, sep string) []string { f := func(c rune) bool { return strings.EqualFold(string(c), sep) @@ -474,8 +483,11 @@ func dictStrToKb(sizeStr string) int { if sizeMatch == nil { return -1 } - size, _ := strconv.Atoi(sizeMatch[1]) // validated already with regex - if sizeMatch[2] == "" || strings.ToLower(sizeMatch[2]) == "m" { + size, err := strconv.Atoi(sizeMatch[1]) // validated already with regex + if err != nil { + klog.Errorf("unexpected error converting size string %s to int: %v", sizeStr, err) + } + if sizeMatch[2] == "" || strings.EqualFold(sizeMatch[2], "m") { size *= 1024 } return size diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index 6aadab48e..7410ce6e0 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -18,13 +18,14 @@ package template import ( "bytes" + "crypto/rand" "crypto/sha1" // #nosec "encoding/base64" "encoding/hex" "encoding/json" "fmt" "io" - "math/rand" // #nosec + "math/big" "net" "net/url" "os" @@ -34,7 +35,6 @@ import ( "strconv" "strings" text_template "text/template" - "time" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -52,6 +52,12 @@ const ( nonIdempotent = "non_idempotent" defBufferSize = 65535 writeIndentOnEmptyLines = true // backward-compatibility + httpProtocol = "HTTP" + autoHTTPProtocol = "AUTO_HTTP" + httpsProtocol = "HTTPS" + grpcProtocol = "GRPC" + grpcsProtocol = "GRPCS" + fcgiProtocol = "FCGI" ) const ( @@ -64,13 +70,13 @@ type Writer interface { // Write renders the template. // NOTE: Implementors must ensure that the content of the returned slice is not modified by the implementation // after the return of this function. - Write(conf config.TemplateConfig) ([]byte, error) + Write(conf *config.TemplateConfig) ([]byte, error) } -// Template ... +// Template ingress template type Template struct { tmpl *text_template.Template - //fw watch.FileWatcher + bp *BufferPool } @@ -97,7 +103,7 @@ func NewTemplate(file string) (*Template, error) { // 2. Collapses multiple empty lines to single one // 3. Re-indent // (ATW: always returns nil) -func cleanConf(in *bytes.Buffer, out *bytes.Buffer) error { +func cleanConf(in, out *bytes.Buffer) error { depth := 0 lineStarted := false emptyLineWritten := false @@ -176,7 +182,7 @@ func cleanConf(in *bytes.Buffer, out *bytes.Buffer) error { // Write populates a buffer using a template with NGINX configuration // and the servers and upstreams created by Ingress rules -func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) { +func (t *Template) Write(conf *config.TemplateConfig) ([]byte, error) { tmplBuf := t.bp.Get() defer t.bp.Put(tmplBuf) @@ -184,14 +190,14 @@ func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) { defer t.bp.Put(outCmdBuf) if klog.V(3).Enabled() { - b, err := json.Marshal(conf) + b, err := json.Marshal(*conf) if err != nil { klog.Errorf("unexpected error: %v", err) } klog.InfoS("NGINX", "configuration", string(b)) } - err := t.tmpl.Execute(tmplBuf, conf) + err := t.tmpl.Execute(tmplBuf, *conf) if err != nil { return nil, err } @@ -211,78 +217,73 @@ func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) { return res, nil } -var ( - funcMap = text_template.FuncMap{ - "empty": func(input interface{}) bool { - check, ok := input.(string) - if ok { - return len(check) == 0 - } - return true - }, - "escapeLiteralDollar": escapeLiteralDollar, - "buildLuaSharedDictionaries": buildLuaSharedDictionaries, - "luaConfigurationRequestBodySize": luaConfigurationRequestBodySize, - "buildLocation": buildLocation, - "buildAuthLocation": buildAuthLocation, - "shouldApplyGlobalAuth": shouldApplyGlobalAuth, - "buildAuthResponseHeaders": buildAuthResponseHeaders, - "buildAuthUpstreamLuaHeaders": buildAuthUpstreamLuaHeaders, - "buildAuthProxySetHeaders": buildAuthProxySetHeaders, - "buildAuthUpstreamName": buildAuthUpstreamName, - "shouldApplyAuthUpstream": shouldApplyAuthUpstream, - "extractHostPort": extractHostPort, - "changeHostPort": changeHostPort, - "buildProxyPass": buildProxyPass, - "filterRateLimits": filterRateLimits, - "buildRateLimitZones": buildRateLimitZones, - "buildRateLimit": buildRateLimit, - "configForLua": configForLua, - "locationConfigForLua": locationConfigForLua, - "buildResolvers": buildResolvers, - "buildUpstreamName": buildUpstreamName, - "isLocationInLocationList": isLocationInLocationList, - "isLocationAllowed": isLocationAllowed, - "buildDenyVariable": buildDenyVariable, - "getenv": os.Getenv, - "contains": strings.Contains, - "split": strings.Split, - "hasPrefix": strings.HasPrefix, - "hasSuffix": strings.HasSuffix, - "trimSpace": strings.TrimSpace, - "toUpper": strings.ToUpper, - "toLower": strings.ToLower, - "formatIP": formatIP, - "quote": quote, - "buildNextUpstream": buildNextUpstream, - "getIngressInformation": getIngressInformation, - "serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} { - return struct{ First, Second interface{} }{all, server} - }, - "isValidByteSize": isValidByteSize, - "buildForwardedFor": buildForwardedFor, - "buildAuthSignURL": buildAuthSignURL, - "buildAuthSignURLLocation": buildAuthSignURLLocation, - "buildOpentracing": buildOpentracing, - "buildOpentelemetry": buildOpentelemetry, - "proxySetHeader": proxySetHeader, - "enforceRegexModifier": enforceRegexModifier, - "buildCustomErrorDeps": buildCustomErrorDeps, - "buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer, - "shouldLoadModSecurityModule": shouldLoadModSecurityModule, - "buildHTTPListener": buildHTTPListener, - "buildHTTPSListener": buildHTTPSListener, - "buildOpentracingForLocation": buildOpentracingForLocation, - "buildOpentelemetryForLocation": buildOpentelemetryForLocation, - "shouldLoadOpentracingModule": shouldLoadOpentracingModule, - "shouldLoadOpentelemetryModule": shouldLoadOpentelemetryModule, - "buildModSecurityForLocation": buildModSecurityForLocation, - "buildMirrorLocations": buildMirrorLocations, - "shouldLoadAuthDigestModule": shouldLoadAuthDigestModule, - "buildServerName": buildServerName, - "buildCorsOriginRegex": buildCorsOriginRegex, - } -) +var funcMap = text_template.FuncMap{ + "empty": func(input interface{}) bool { + check, ok := input.(string) + if ok { + return check == "" + } + return true + }, + "escapeLiteralDollar": escapeLiteralDollar, + "buildLuaSharedDictionaries": buildLuaSharedDictionaries, + "luaConfigurationRequestBodySize": luaConfigurationRequestBodySize, + "buildLocation": buildLocation, + "buildAuthLocation": buildAuthLocation, + "shouldApplyGlobalAuth": shouldApplyGlobalAuth, + "buildAuthResponseHeaders": buildAuthResponseHeaders, + "buildAuthUpstreamLuaHeaders": buildAuthUpstreamLuaHeaders, + "buildAuthProxySetHeaders": buildAuthProxySetHeaders, + "buildAuthUpstreamName": buildAuthUpstreamName, + "shouldApplyAuthUpstream": shouldApplyAuthUpstream, + "extractHostPort": extractHostPort, + "changeHostPort": changeHostPort, + "buildProxyPass": buildProxyPass, + "filterRateLimits": filterRateLimits, + "buildRateLimitZones": buildRateLimitZones, + "buildRateLimit": buildRateLimit, + "configForLua": configForLua, + "locationConfigForLua": locationConfigForLua, + "buildResolvers": buildResolvers, + "buildUpstreamName": buildUpstreamName, + "isLocationInLocationList": isLocationInLocationList, + "isLocationAllowed": isLocationAllowed, + "buildDenyVariable": buildDenyVariable, + "getenv": os.Getenv, + "contains": strings.Contains, + "split": strings.Split, + "hasPrefix": strings.HasPrefix, + "hasSuffix": strings.HasSuffix, + "trimSpace": strings.TrimSpace, + "toUpper": strings.ToUpper, + "toLower": strings.ToLower, + "formatIP": formatIP, + "quote": quote, + "buildNextUpstream": buildNextUpstream, + "getIngressInformation": getIngressInformation, + "serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} { + return struct{ First, Second interface{} }{all, server} + }, + "isValidByteSize": isValidByteSize, + "buildForwardedFor": buildForwardedFor, + "buildAuthSignURL": buildAuthSignURL, + "buildAuthSignURLLocation": buildAuthSignURLLocation, + "buildOpentelemetry": buildOpentelemetry, + "proxySetHeader": proxySetHeader, + "enforceRegexModifier": enforceRegexModifier, + "buildCustomErrorDeps": buildCustomErrorDeps, + "buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer, + "shouldLoadModSecurityModule": shouldLoadModSecurityModule, + "buildHTTPListener": buildHTTPListener, + "buildHTTPSListener": buildHTTPSListener, + "buildOpentelemetryForLocation": buildOpentelemetryForLocation, + "shouldLoadOpentelemetryModule": shouldLoadOpentelemetryModule, + "buildModSecurityForLocation": buildModSecurityForLocation, + "buildMirrorLocations": buildMirrorLocations, + "shouldLoadAuthDigestModule": shouldLoadAuthDigestModule, + "buildServerName": buildServerName, + "buildCorsOriginRegex": buildCorsOriginRegex, +} // escapeLiteralDollar will replace the $ character with ${literal_dollar} // which is made to work via the following configuration in the http section of @@ -296,7 +297,7 @@ func escapeLiteralDollar(input interface{}) string { if !ok { return "" } - return strings.Replace(inputStr, `$`, `${literal_dollar}`, -1) + return strings.ReplaceAll(inputStr, `$`, `${literal_dollar}`) } // formatIP will wrap IPv6 addresses in [] and return IPv4 addresses @@ -328,9 +329,7 @@ func quote(input interface{}) string { return fmt.Sprintf("%q", inputStr) } -func buildLuaSharedDictionaries(c interface{}, s interface{}) string { - var out []string - +func buildLuaSharedDictionaries(c, s interface{}) string { cfg, ok := c.(config.Configuration) if !ok { klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) @@ -343,6 +342,7 @@ func buildLuaSharedDictionaries(c interface{}, s interface{}) string { return "" } + out := make([]string, 0, len(cfg.LuaSharedDicts)) for name, size := range cfg.LuaSharedDicts { sizeStr := dictKbToStr(size) out = append(out, fmt.Sprintf("lua_shared_dict %s %s", name, sizeStr)) @@ -364,7 +364,7 @@ func luaConfigurationRequestBodySize(c interface{}) string { if size < cfg.LuaSharedDicts["certificate_data"] { size = cfg.LuaSharedDicts["certificate_data"] } - size = size + 1024 + size += 1024 return dictKbToStr(size) } @@ -418,7 +418,7 @@ func configForLua(input interface{}) string { } // locationConfigForLua formats some location specific configuration into Lua table represented as string -func locationConfigForLua(l interface{}, a interface{}) string { +func locationConfigForLua(l, a interface{}) string { location, ok := l.(*ingress.Location) if !ok { klog.Errorf("expected an '*ingress.Location' type but %T was given", l) @@ -459,7 +459,7 @@ func locationConfigForLua(l interface{}, a interface{}) string { } // buildResolvers returns the resolvers reading the /etc/resolv.conf file -func buildResolvers(res interface{}, disableIpv6 interface{}) string { +func buildResolvers(res, disableIpv6 interface{}) string { // NGINX need IPV6 addresses to be surrounded by brackets nss, ok := res.([]net.IP) if !ok { @@ -484,7 +484,7 @@ func buildResolvers(res interface{}, disableIpv6 interface{}) string { } r = append(r, fmt.Sprintf("[%v]", ns)) } else { - r = append(r, fmt.Sprintf("%v", ns)) + r = append(r, ns.String()) } } r = append(r, "valid=30s") @@ -554,7 +554,7 @@ func buildAuthLocation(input interface{}, globalExternalAuthURL string) string { str := base64.URLEncoding.EncodeToString([]byte(location.Path)) // removes "=" after encoding - str = strings.Replace(str, "=", "", -1) + str = strings.ReplaceAll(str, "=", "") pathType := "default" if location.PathType != nil { @@ -644,7 +644,7 @@ func buildAuthUpstreamName(input interface{}, host string) string { // shouldApplyAuthUpstream returns true only in case when ExternalAuth.URL and // ExternalAuth.KeepaliveConnections are all set -func shouldApplyAuthUpstream(l interface{}, c interface{}) bool { +func shouldApplyAuthUpstream(l, c interface{}) bool { location, ok := l.(*ingress.Location) if !ok { klog.Errorf("expected an '*ingress.Location' type but %T was returned", l) @@ -672,14 +672,14 @@ func shouldApplyAuthUpstream(l interface{}, c interface{}) bool { } // extractHostPort will extract the host:port part from the URL specified by url -func extractHostPort(url string) string { - if url == "" { +func extractHostPort(newURL string) string { + if newURL == "" { return "" } - authURL, err := parser.StringToURL(url) + authURL, err := parser.StringToURL(newURL) if err != nil { - klog.Errorf("expected a valid URL but %s was returned", url) + klog.Errorf("expected a valid URL but %s was returned", newURL) return "" } @@ -687,14 +687,14 @@ func extractHostPort(url string) string { } // changeHostPort will change the host:port part of the url to value -func changeHostPort(url string, value string) string { - if url == "" { +func changeHostPort(newURL, value string) string { + if newURL == "" { return "" } - authURL, err := parser.StringToURL(url) + authURL, err := parser.StringToURL(newURL) if err != nil { - klog.Errorf("expected a valid URL but %s was returned", url) + klog.Errorf("expected a valid URL but %s was returned", newURL) return "" } @@ -707,7 +707,7 @@ func changeHostPort(url string, value string) string { // (specified through the nginx.ingress.kubernetes.io/rewrite-target annotation) // If the annotation nginx.ingress.kubernetes.io/add-base-url:"true" is specified it will // add a base tag in the head of the response from the service -func buildProxyPass(host string, b interface{}, loc interface{}) string { +func buildProxyPass(_ string, b, loc interface{}) string { backends, ok := b.([]*ingress.Backend) if !ok { klog.Errorf("expected an '[]*ingress.Backend' type but %T was returned", b) @@ -725,21 +725,18 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string { proxyPass := "proxy_pass" - switch location.BackendProtocol { - case "AUTO_HTTP": + switch strings.ToUpper(location.BackendProtocol) { + case autoHTTPProtocol: proto = "$scheme://" - case "HTTPS": + case httpsProtocol: proto = "https://" - case "GRPC": + case grpcProtocol: proto = "grpc://" proxyPass = "grpc_pass" - case "GRPCS": + case grpcsProtocol: proto = "grpcs://" proxyPass = "grpc_pass" - case "AJP": - proto = "" - proxyPass = "ajp_pass" - case "FCGI": + case fcgiProtocol: proto = "" proxyPass = "fastcgi_pass" } @@ -751,7 +748,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string { if backend.SSLPassthrough { proto = "https://" - if location.BackendProtocol == "GRPCS" { + if location.BackendProtocol == grpcsProtocol { proto = "grpcs://" } } @@ -778,7 +775,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string { var xForwardedPrefix string if len(location.XForwardedPrefix) > 0 { - xForwardedPrefix = fmt.Sprintf("%s X-Forwarded-Prefix \"%s\";\n", proxySetHeader(location), location.XForwardedPrefix) + xForwardedPrefix = fmt.Sprintf("%s X-Forwarded-Prefix %q;\n", proxySetHeader(location), location.XForwardedPrefix) } return fmt.Sprintf(` @@ -938,9 +935,7 @@ func isLocationAllowed(input interface{}) bool { return loc.Denied == nil } -var ( - denyPathSlugMap = map[string]string{} -) +var denyPathSlugMap = map[string]string{} // buildDenyVariable returns a nginx variable for a location in a // server to be used in the whitelist check @@ -980,7 +975,11 @@ func buildNextUpstream(i, r interface{}) string { return "" } - retryNonIdempotent := r.(bool) + retryNonIdempotent, ok := r.(bool) + if !ok { + klog.Errorf("expected a 'bool' type but %T was returned", i) + return "" + } parts := strings.Split(nextUpstream, " ") @@ -1005,8 +1004,10 @@ func buildNextUpstream(i, r interface{}) string { // refer to http://nginx.org/en/docs/syntax.html // Nginx differentiates between size and offset // offset directives support gigabytes in addition -var nginxSizeRegex = regexp.MustCompile("^[0-9]+[kKmM]{0,1}$") -var nginxOffsetRegex = regexp.MustCompile("^[0-9]+[kKmMgG]{0,1}$") +var ( + nginxSizeRegex = regexp.MustCompile(`^\d+[kKmM]?$`) + nginxOffsetRegex = regexp.MustCompile(`^\d+[kKmMgG]?$`) +) // isValidByteSize validates size units valid in nginx // http://nginx.org/en/docs/syntax.html @@ -1156,13 +1157,17 @@ func buildForwardedFor(input interface{}) string { return "" } - ffh := strings.Replace(s, "-", "_", -1) + ffh := strings.ReplaceAll(s, "-", "_") ffh = strings.ToLower(ffh) return fmt.Sprintf("$http_%v", ffh) } func buildAuthSignURL(authSignURL, authRedirectParam string) string { - u, _ := url.Parse(authSignURL) + u, err := url.Parse(authSignURL) + if err != nil { + klog.Errorf("error parsing authSignURL: %v", err) + return "" + } q := u.Query() if authRedirectParam == "" { authRedirectParam = defaultGlobalAuthRedirectParam @@ -1187,59 +1192,21 @@ func buildAuthSignURLLocation(location, authSignURL string) string { var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") -func init() { - rand.Seed(time.Now().UnixNano()) -} - func randomString() string { b := make([]rune, 32) for i := range b { - b[i] = letters[rand.Intn(len(letters))] // #nosec + idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) + if err != nil { + klog.Errorf("unexpected error generating random index: %v", err) + return "" + } + b[i] = letters[idx.Int64()] } return string(b) } -func buildOpentracing(c interface{}, s interface{}) string { - cfg, ok := c.(config.Configuration) - if !ok { - klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) - return "" - } - - servers, ok := s.([]*ingress.Server) - if !ok { - klog.Errorf("expected an '[]*ingress.Server' type but %T was returned", s) - return "" - } - - if !shouldLoadOpentracingModule(cfg, servers) { - return "" - } - - buf := bytes.NewBufferString("") - - if cfg.DatadogCollectorHost != "" { - buf.WriteString("opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;") - } else if cfg.ZipkinCollectorHost != "" { - buf.WriteString("opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so /etc/nginx/opentracing.json;") - } else if cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "" { - buf.WriteString("opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;") - } - - buf.WriteString("\r\n") - - if cfg.OpentracingOperationName != "" { - buf.WriteString(fmt.Sprintf("opentracing_operation_name \"%s\";\n", cfg.OpentracingOperationName)) - } - if cfg.OpentracingLocationOperationName != "" { - buf.WriteString(fmt.Sprintf("opentracing_location_operation_name \"%s\";\n", cfg.OpentracingLocationOperationName)) - } - - return buf.String() -} - -func buildOpentelemetry(c interface{}, s interface{}) string { +func buildOpentelemetry(c, s interface{}) string { cfg, ok := c.(config.Configuration) if !ok { klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) @@ -1261,7 +1228,7 @@ func buildOpentelemetry(c interface{}, s interface{}) string { buf.WriteString("\r\n") if cfg.OpentelemetryOperationName != "" { - buf.WriteString(fmt.Sprintf("opentelemetry_operation_name \"%s\";\n", cfg.OpentelemetryOperationName)) + fmt.Fprintf(buf, "opentelemetry_operation_name \"%s\";\n", cfg.OpentelemetryOperationName) } return buf.String() } @@ -1273,7 +1240,7 @@ func proxySetHeader(loc interface{}) string { return "proxy_set_header" } - if location.BackendProtocol == "GRPC" || location.BackendProtocol == "GRPCS" { + if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol { return "grpc_set_header" } @@ -1282,7 +1249,7 @@ func proxySetHeader(loc interface{}) string { // buildCustomErrorDeps is a utility function returning a struct wrapper with // the data required to build the 'CUSTOM_ERRORS' template -func buildCustomErrorDeps(upstreamName string, errorCodes []int, enableMetrics bool, modsecurityEnabled bool) interface{} { +func buildCustomErrorDeps(upstreamName string, errorCodes []int, enableMetrics, modsecurityEnabled bool) interface{} { return struct { UpstreamName string ErrorCodes []int @@ -1352,18 +1319,6 @@ func buildCustomErrorLocationsPerServer(input interface{}) []errorLocation { return errorLocations } -func opentracingPropagateContext(location *ingress.Location) string { - if location == nil { - return "" - } - - if location.BackendProtocol == "GRPC" || location.BackendProtocol == "GRPCS" { - return "opentracing_grpc_propagate_context;" - } - - return "opentracing_propagate_context;" -} - func opentelemetryPropagateContext(location *ingress.Location) string { if location == nil { return "" @@ -1374,7 +1329,7 @@ func opentelemetryPropagateContext(location *ingress.Location) string { // shouldLoadModSecurityModule determines whether or not the ModSecurity module needs to be loaded. // First, it checks if `enable-modsecurity` is set in the ConfigMap. If it is not, it iterates over all locations to // check if ModSecurity is enabled by the annotation `nginx.ingress.kubernetes.io/enable-modsecurity`. -func shouldLoadModSecurityModule(c interface{}, s interface{}) bool { +func shouldLoadModSecurityModule(c, s interface{}) bool { cfg, ok := c.(config.Configuration) if !ok { klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) @@ -1405,7 +1360,7 @@ func shouldLoadModSecurityModule(c interface{}, s interface{}) bool { return false } -func buildHTTPListener(t interface{}, s interface{}) string { +func buildHTTPListener(t, s interface{}) string { var out []string tc, ok := t.(config.TemplateConfig) @@ -1425,9 +1380,9 @@ func buildHTTPListener(t interface{}, s interface{}) string { addrV4 = tc.Cfg.BindAddressIpv4 } - co := commonListenOptions(tc, hostname) + co := commonListenOptions(&tc, hostname) - out = append(out, httpListener(addrV4, co, tc)...) + out = append(out, httpListener(addrV4, co, &tc)...) if !tc.IsIPV6Enabled { return strings.Join(out, "\n") @@ -1438,12 +1393,12 @@ func buildHTTPListener(t interface{}, s interface{}) string { addrV6 = tc.Cfg.BindAddressIpv6 } - out = append(out, httpListener(addrV6, co, tc)...) + out = append(out, httpListener(addrV6, co, &tc)...) return strings.Join(out, "\n") } -func buildHTTPSListener(t interface{}, s interface{}) string { +func buildHTTPSListener(t, s interface{}) string { var out []string tc, ok := t.(config.TemplateConfig) @@ -1458,14 +1413,14 @@ func buildHTTPSListener(t interface{}, s interface{}) string { return "" } - co := commonListenOptions(tc, hostname) + co := commonListenOptions(&tc, hostname) addrV4 := []string{""} if len(tc.Cfg.BindAddressIpv4) > 0 { addrV4 = tc.Cfg.BindAddressIpv4 } - out = append(out, httpsListener(addrV4, co, tc)...) + out = append(out, httpsListener(addrV4, co, &tc)...) if !tc.IsIPV6Enabled { return strings.Join(out, "\n") @@ -1476,12 +1431,12 @@ func buildHTTPSListener(t interface{}, s interface{}) string { addrV6 = tc.Cfg.BindAddressIpv6 } - out = append(out, httpsListener(addrV6, co, tc)...) + out = append(out, httpsListener(addrV6, co, &tc)...) return strings.Join(out, "\n") } -func commonListenOptions(template config.TemplateConfig, hostname string) string { +func commonListenOptions(template *config.TemplateConfig, hostname string) string { var out []string if template.Cfg.UseProxyProtocol { @@ -1505,7 +1460,7 @@ func commonListenOptions(template config.TemplateConfig, hostname string) string return strings.Join(out, " ") } -func httpListener(addresses []string, co string, tc config.TemplateConfig) []string { +func httpListener(addresses []string, co string, tc *config.TemplateConfig) []string { out := make([]string, 0) for _, address := range addresses { lo := []string{"listen"} @@ -1516,15 +1471,14 @@ func httpListener(addresses []string, co string, tc config.TemplateConfig) []str lo = append(lo, fmt.Sprintf("%v:%v", address, tc.ListenPorts.HTTP)) } - lo = append(lo, co) - lo = append(lo, ";") + lo = append(lo, co, ";") out = append(out, strings.Join(lo, " ")) } return out } -func httpsListener(addresses []string, co string, tc config.TemplateConfig) []string { +func httpsListener(addresses []string, co string, tc *config.TemplateConfig) []string { out := make([]string, 0) for _, address := range addresses { lo := []string{"listen"} @@ -1547,8 +1501,7 @@ func httpsListener(addresses []string, co string, tc config.TemplateConfig) []st } } - lo = append(lo, co) - lo = append(lo, "ssl") + lo = append(lo, co, "ssl") if tc.Cfg.UseHTTP2 { lo = append(lo, "http2") @@ -1561,32 +1514,7 @@ func httpsListener(addresses []string, co string, tc config.TemplateConfig) []st return out } -func buildOpentracingForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string { - isOTEnabledInLoc := location.Opentracing.Enabled - isOTSetInLoc := location.Opentracing.Set - - if isOTEnabled { - if isOTSetInLoc && !isOTEnabledInLoc { - return "opentracing off;" - } - } else if !isOTSetInLoc || !isOTEnabledInLoc { - return "" - } - - opc := opentracingPropagateContext(location) - if opc != "" { - opc = fmt.Sprintf("opentracing on;\n%v", opc) - } - - if (!isOTTrustSet && !location.Opentracing.TrustSet) || - (location.Opentracing.TrustSet && !location.Opentracing.TrustEnabled) { - opc = opc + "\nopentracing_trust_incoming_span off;" - } - - return opc -} - -func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string { +func buildOpentelemetryForLocation(isOTEnabled, isOTTrustSet bool, location *ingress.Location) string { isOTEnabledInLoc := location.Opentelemetry.Enabled isOTSetInLoc := location.Opentelemetry.Set @@ -1604,52 +1532,21 @@ func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location } if location.Opentelemetry.OperationName != "" { - opc = opc + "\nopentelemetry_operation_name " + location.Opentelemetry.OperationName + ";" + opc += "\nopentelemetry_operation_name " + location.Opentelemetry.OperationName + ";" } if (!isOTTrustSet && !location.Opentelemetry.TrustSet) || (location.Opentelemetry.TrustSet && !location.Opentelemetry.TrustEnabled) { - opc = opc + "\nopentelemetry_trust_incoming_spans off;" + opc += "\nopentelemetry_trust_incoming_spans off;" } else { - opc = opc + "\nopentelemetry_trust_incoming_spans on;" + opc += "\nopentelemetry_trust_incoming_spans on;" } return opc } -// shouldLoadOpentracingModule determines whether or not the Opentracing module needs to be loaded. -// First, it checks if `enable-opentracing` is set in the ConfigMap. If it is not, it iterates over all locations to -// check if Opentracing is enabled by the annotation `nginx.ingress.kubernetes.io/enable-opentracing`. -func shouldLoadOpentracingModule(c interface{}, s interface{}) bool { - cfg, ok := c.(config.Configuration) - if !ok { - klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) - return false - } - - servers, ok := s.([]*ingress.Server) - if !ok { - klog.Errorf("expected an '[]*ingress.Server' type but %T was returned", s) - return false - } - - if cfg.EnableOpentracing { - return true - } - - for _, server := range servers { - for _, location := range server.Locations { - if location.Opentracing.Enabled { - return true - } - } - } - - return false -} - // shouldLoadOpentelemetryModule determines whether or not the Opentelemetry module needs to be loaded. // It checks if `enable-opentelemetry` is set in the ConfigMap. -func shouldLoadOpentelemetryModule(c interface{}, s interface{}) bool { +func shouldLoadOpentelemetryModule(c, s interface{}) bool { cfg, ok := c.(config.Configuration) if !ok { klog.Errorf("expected a 'config.Configuration' type but %T was returned", c) @@ -1676,6 +1573,7 @@ func shouldLoadOpentelemetryModule(c interface{}, s interface{}) bool { return false } +//nolint:gocritic // Ignore passing cfg by pointer error func buildModSecurityForLocation(cfg config.Configuration, location *ingress.Location) string { isMSEnabledInLoc := location.ModSecurity.Enable isMSEnableSetInLoc := location.ModSecurity.EnableSet @@ -1727,7 +1625,7 @@ func buildMirrorLocations(locs []*ingress.Location) string { mapped := sets.Set[string]{} for _, loc := range locs { - if loc.Mirror.Source == "" || loc.Mirror.Target == "" { + if loc.Mirror.Source == "" || loc.Mirror.Target == "" || loc.Mirror.Host == "" { continue } @@ -1738,8 +1636,8 @@ func buildMirrorLocations(locs []*ingress.Location) string { mapped.Insert(loc.Mirror.Source) buffer.WriteString(fmt.Sprintf(`location = %v { internal; -proxy_set_header Host %v; -proxy_pass %v; +proxy_set_header Host "%v"; +proxy_pass "%v"; } `, loc.Mirror.Source, loc.Mirror.Host, loc.Mirror.Target)) @@ -1809,7 +1707,7 @@ func convertGoSliceIntoLuaTable(goSliceInterface interface{}, emptyStringAsNil b switch kind { case reflect.String: - if emptyStringAsNil && len(goSlice.Interface().(string)) == 0 { + if emptyStringAsNil && goSlice.Interface().(string) == "" { return "nil", nil } return fmt.Sprintf(`"%v"`, goSlice.Interface()), nil @@ -1842,17 +1740,17 @@ func buildCorsOriginRegex(corsOrigins []string) string { return "set $http_origin *;\nset $cors 'true';" } - var originsRegex string = "if ($http_origin ~* (" + originsRegex := "if ($http_origin ~* (" for i, origin := range corsOrigins { originTrimmed := strings.TrimSpace(origin) if len(originTrimmed) > 0 { builtOrigin := buildOriginRegex(originTrimmed) originsRegex += builtOrigin if i != len(corsOrigins)-1 { - originsRegex = originsRegex + "|" + originsRegex += "|" } } } - originsRegex = originsRegex + ")$ ) { set $cors 'true'; }" + originsRegex += ")$ ) { set $cors 'true'; }" return originsRegex } diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go index 1980d7e52..3089e3b32 100644 --- a/internal/ingress/controller/template/template_test.go +++ b/internal/ingress/controller/template/template_test.go @@ -37,7 +37,6 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/authreq" "k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity" "k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry" - "k8s.io/ingress-nginx/internal/ingress/annotations/opentracing" "k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit" "k8s.io/ingress-nginx/internal/ingress/annotations/rewrite" "k8s.io/ingress-nginx/internal/ingress/controller/config" @@ -48,9 +47,9 @@ import ( func init() { // the default value of nginx.TemplatePath assumes the template exists in // the root filesystem and not in the rootfs directory - path, err := filepath.Abs(filepath.Join("../../../../rootfs/", nginx.TemplatePath)) + absPath, err := filepath.Abs(filepath.Join("..", "..", "..", "..", "rootfs", nginx.TemplatePath)) if err == nil { - nginx.TemplatePath = path + nginx.TemplatePath = absPath } } @@ -63,7 +62,7 @@ var ( Target string Location string ProxyPass string - AutoHttpProxyPass string + AutoHTTPProxyPass string Sticky bool XForwardedPrefix string SecureBackend bool @@ -200,6 +199,12 @@ proxy_pass $scheme://upstream_balancer;`, } ) +const ( + defaultBackend = "upstream-name" + defaultHost = "example.com" + fooAuthHost = "foo.com/auth" +) + func getTestDataDir() (string, error) { pwd, err := os.Getwd() if err != nil { @@ -326,9 +331,6 @@ func TestBuildLocation(t *testing.T) { } func TestBuildProxyPass(t *testing.T) { - defaultBackend := "upstream-name" - defaultHost := "example.com" - for k, tc := range tmplFuncTestcases { loc := &ingress.Location{ Path: tc.Path, @@ -339,7 +341,7 @@ func TestBuildProxyPass(t *testing.T) { } if tc.SecureBackend { - loc.BackendProtocol = "HTTPS" + loc.BackendProtocol = httpsProtocol } backend := &ingress.Backend{ @@ -367,9 +369,6 @@ func TestBuildProxyPass(t *testing.T) { } func TestBuildProxyPassAutoHttp(t *testing.T) { - defaultBackend := "upstream-name" - defaultHost := "example.com" - for k, tc := range tmplFuncTestcases { loc := &ingress.Location{ Path: tc.Path, @@ -379,9 +378,9 @@ func TestBuildProxyPassAutoHttp(t *testing.T) { } if tc.SecureBackend { - loc.BackendProtocol = "HTTPS" + loc.BackendProtocol = httpsProtocol } else { - loc.BackendProtocol = "AUTO_HTTP" + loc.BackendProtocol = autoHTTPProtocol } backend := &ingress.Backend{ @@ -402,7 +401,7 @@ func TestBuildProxyPassAutoHttp(t *testing.T) { backends := []*ingress.Backend{backend} pp := buildProxyPass(defaultHost, backends, loc) - if !strings.EqualFold(tc.AutoHttpProxyPass, pp) { + if !strings.EqualFold(tc.AutoHTTPProxyPass, pp) { t.Errorf("%s: expected \n'%v'\nbut returned \n'%v'", k, tc.ProxyPass, pp) } } @@ -417,7 +416,7 @@ func TestBuildAuthLocation(t *testing.T) { t.Errorf("Expected '%v' but returned '%v'", expected, actual) } - authURL := "foo.com/auth" + authURL := fooAuthHost globalAuthURL := "foo.com/global-auth" loc := &ingress.Location{ @@ -428,7 +427,7 @@ func TestBuildAuthLocation(t *testing.T) { EnableGlobalAuth: true, } - encodedAuthURL := strings.Replace(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "", -1) + encodedAuthURL := strings.ReplaceAll(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "") externalAuthPath := fmt.Sprintf("/_external-auth-%v-default", encodedAuthURL) testCases := []struct { @@ -460,8 +459,7 @@ func TestBuildAuthLocation(t *testing.T) { } func TestShouldApplyGlobalAuth(t *testing.T) { - - authURL := "foo.com/auth" + authURL := fooAuthHost globalAuthURL := "foo.com/global-auth" loc := &ingress.Location{ @@ -579,12 +577,12 @@ func TestBuildAuthUpstreamName(t *testing.T) { loc := &ingress.Location{ ExternalAuth: authreq.Config{ - URL: "foo.com/auth", + URL: fooAuthHost, }, Path: "/cat", } - encodedAuthURL := strings.Replace(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "", -1) + encodedAuthURL := strings.ReplaceAll(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "") externalAuthPath := fmt.Sprintf("external-auth-%v-default", encodedAuthURL) testCases := []struct { @@ -606,7 +604,7 @@ func TestBuildAuthUpstreamName(t *testing.T) { } func TestShouldApplyAuthUpstream(t *testing.T) { - authURL := "foo.com/auth" + authURL := fooAuthHost loc := &ingress.Location{ ExternalAuth: authreq.Config{ @@ -702,7 +700,10 @@ func TestChangeHostPort(t *testing.T) { } func TestTemplateWithData(t *testing.T) { - pwd, _ := os.Getwd() + pwd, err := os.Getwd() + if err != nil { + t.Errorf("unexpected error: %v", err) + } f, err := os.Open(path.Join(pwd, "../../../../test/data/config.json")) if err != nil { t.Errorf("unexpected error reading json file: %v", err) @@ -727,7 +728,7 @@ func TestTemplateWithData(t *testing.T) { dat.Cfg.DefaultSSLCertificate = &ingress.SSLCert{} - rt, err := ngxTpl.Write(dat) + rt, err := ngxTpl.Write(&dat) if err != nil { t.Errorf("invalid NGINX template: %v", err) } @@ -746,7 +747,10 @@ func TestTemplateWithData(t *testing.T) { } func BenchmarkTemplateWithData(b *testing.B) { - pwd, _ := os.Getwd() + pwd, err := os.Getwd() + if err != nil { + b.Errorf("unexpected error: %v", err) + } f, err := os.Open(path.Join(pwd, "../../../../test/data/config.json")) if err != nil { b.Errorf("unexpected error reading json file: %v", err) @@ -767,7 +771,9 @@ func BenchmarkTemplateWithData(b *testing.B) { } for i := 0; i < b.N; i++ { - ngxTpl.Write(dat) + if _, err := ngxTpl.Write(&dat); err != nil { + b.Errorf("unexpected error writing template: %v", err) + } } } @@ -1064,9 +1070,6 @@ func TestBuildUpstreamName(t *testing.T) { t.Errorf("Expected '%v' but returned '%v'", expected, actual) } - defaultBackend := "upstream-name" - defaultHost := "example.com" - for k, tc := range tmplFuncTestcases { loc := &ingress.Location{ Path: tc.Path, @@ -1077,7 +1080,7 @@ func TestBuildUpstreamName(t *testing.T) { } if tc.SecureBackend { - loc.BackendProtocol = "HTTPS" + loc.BackendProtocol = httpsProtocol } backend := &ingress.Backend{ @@ -1130,36 +1133,15 @@ func TestEscapeLiteralDollar(t *testing.T) { } } -func TestOpentracingPropagateContext(t *testing.T) { - tests := map[*ingress.Location]string{ - {BackendProtocol: "HTTP"}: "opentracing_propagate_context;", - {BackendProtocol: "HTTPS"}: "opentracing_propagate_context;", - {BackendProtocol: "AUTO_HTTP"}: "opentracing_propagate_context;", - {BackendProtocol: "GRPC"}: "opentracing_grpc_propagate_context;", - {BackendProtocol: "GRPCS"}: "opentracing_grpc_propagate_context;", - {BackendProtocol: "AJP"}: "opentracing_propagate_context;", - {BackendProtocol: "FCGI"}: "opentracing_propagate_context;", - nil: "", - } - - for loc, expectedDirective := range tests { - actualDirective := opentracingPropagateContext(loc) - if actualDirective != expectedDirective { - t.Errorf("Expected %v but returned %v", expectedDirective, actualDirective) - } - } -} - func TestOpentelemetryPropagateContext(t *testing.T) { tests := map[*ingress.Location]string{ - {BackendProtocol: "HTTP"}: "opentelemetry_propagate;", - {BackendProtocol: "HTTPS"}: "opentelemetry_propagate;", - {BackendProtocol: "AUTO_HTTP"}: "opentelemetry_propagate;", - {BackendProtocol: "GRPC"}: "opentelemetry_propagate;", - {BackendProtocol: "GRPCS"}: "opentelemetry_propagate;", - {BackendProtocol: "AJP"}: "opentelemetry_propagate;", - {BackendProtocol: "FCGI"}: "opentelemetry_propagate;", - nil: "", + {BackendProtocol: httpProtocol}: "opentelemetry_propagate;", + {BackendProtocol: httpsProtocol}: "opentelemetry_propagate;", + {BackendProtocol: autoHTTPProtocol}: "opentelemetry_propagate;", + {BackendProtocol: grpcProtocol}: "opentelemetry_propagate;", + {BackendProtocol: grpcsProtocol}: "opentelemetry_propagate;", + {BackendProtocol: fcgiProtocol}: "opentelemetry_propagate;", + nil: "", } for loc, expectedDirective := range tests { @@ -1171,7 +1153,6 @@ func TestOpentelemetryPropagateContext(t *testing.T) { } func TestGetIngressInformation(t *testing.T) { - testcases := map[string]struct { Ingress interface{} Host string @@ -1625,7 +1606,7 @@ func TestProxySetHeader(t *testing.T) { { name: "gRPC backend", loc: &ingress.Location{ - BackendProtocol: "GRPC", + BackendProtocol: grpcProtocol, }, expected: "grpc_set_header", }, @@ -1639,86 +1620,6 @@ func TestProxySetHeader(t *testing.T) { } } -func TestBuildOpenTracing(t *testing.T) { - invalidType := &ingress.Ingress{} - expected := "" - actual := buildOpentracing(invalidType, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgNoHost := config.Configuration{ - EnableOpentracing: true, - } - expected = "\r\n" - actual = buildOpentracing(cfgNoHost, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgJaeger := config.Configuration{ - EnableOpentracing: true, - JaegerCollectorHost: "jaeger-host.com", - } - expected = "opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;\r\n" - actual = buildOpentracing(cfgJaeger, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgZipkin := config.Configuration{ - EnableOpentracing: true, - ZipkinCollectorHost: "zipkin-host.com", - } - expected = "opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so /etc/nginx/opentracing.json;\r\n" - actual = buildOpentracing(cfgZipkin, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgDatadog := config.Configuration{ - EnableOpentracing: true, - DatadogCollectorHost: "datadog-host.com", - } - expected = "opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;\r\n" - actual = buildOpentracing(cfgDatadog, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgJaegerEndpoint := config.Configuration{ - EnableOpentracing: true, - JaegerEndpoint: "http://jaeger-collector.com:14268/api/traces", - } - expected = "opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;\r\n" - actual = buildOpentracing(cfgJaegerEndpoint, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - cfgOpenTracing := config.Configuration{ - EnableOpentracing: true, - DatadogCollectorHost: "datadog-host.com", - OpentracingOperationName: "my-operation-name", - OpentracingLocationOperationName: "my-location-operation-name", - } - expected = "opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;\r\n" - expected += "opentracing_operation_name \"my-operation-name\";\n" - expected += "opentracing_location_operation_name \"my-location-operation-name\";\n" - actual = buildOpentracing(cfgOpenTracing, []*ingress.Server{}) - - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - -} - func TestBuildOpenTelemetry(t *testing.T) { invalidType := &ingress.Ingress{} expected := "" @@ -1777,6 +1678,7 @@ func TestEnforceRegexModifier(t *testing.T) { } } +//nolint:dupl // Ignore dupl errors for similar test case func TestShouldLoadModSecurityModule(t *testing.T) { // ### Invalid argument type tests ### // The first tests should return false. @@ -1830,106 +1732,6 @@ func TestShouldLoadModSecurityModule(t *testing.T) { } } -func TestOpentracingForLocation(t *testing.T) { - trueVal := true - falseVal := false - - loadOT := `opentracing on; -opentracing_propagate_context;` - loadOTUntrustedSpan := `opentracing on; -opentracing_propagate_context; -opentracing_trust_incoming_span off;` - testCases := []struct { - description string - globalOT bool - isSetInLoc bool - isOTInLoc *bool - globalTrust bool - isTrustSetInLoc bool - isTrustInLoc *bool - expected string - }{ - {"globally enabled, without annotation", true, false, nil, true, false, nil, loadOT}, - {"globally enabled and enabled in location", true, true, &trueVal, true, false, nil, loadOT}, - {"globally disabled and not enabled in location", false, false, nil, true, false, nil, ""}, - {"globally disabled but enabled in location", false, true, &trueVal, true, false, nil, loadOT}, - {"globally trusted, not trusted in location", true, false, nil, true, true, &falseVal, loadOTUntrustedSpan}, - {"not globally trusted, trust set in location", true, false, nil, false, true, &trueVal, loadOT}, - {"not globally trusted, trust not set in location", true, false, nil, false, false, nil, loadOTUntrustedSpan}, - } - - for _, testCase := range testCases { - il := &ingress.Location{ - Opentracing: opentracing.Config{Set: testCase.isSetInLoc, TrustSet: testCase.isTrustSetInLoc}, - } - if il.Opentracing.Set { - il.Opentracing.Enabled = *testCase.isOTInLoc - } - if il.Opentracing.TrustSet { - il.Opentracing.TrustEnabled = *testCase.isTrustInLoc - } - - actual := buildOpentracingForLocation(testCase.globalOT, testCase.globalTrust, il) - - if testCase.expected != actual { - t.Errorf("%v: expected '%v' but returned '%v'", testCase.description, testCase.expected, actual) - } - } -} - -func TestShouldLoadOpentracingModule(t *testing.T) { - // ### Invalid argument type tests ### - // The first tests should return false. - expected := false - - invalidType := &ingress.Ingress{} - actual := shouldLoadOpentracingModule(config.Configuration{}, invalidType) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - actual = shouldLoadOpentracingModule(invalidType, []*ingress.Server{}) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - // ### Functional tests ### - actual = shouldLoadOpentracingModule(config.Configuration{}, []*ingress.Server{}) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - // All further tests should return true. - expected = true - - configuration := config.Configuration{EnableOpentracing: true} - actual = shouldLoadOpentracingModule(configuration, []*ingress.Server{}) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - servers := []*ingress.Server{ - { - Locations: []*ingress.Location{ - { - Opentracing: opentracing.Config{ - Enabled: true, - }, - }, - }, - }, - } - actual = shouldLoadOpentracingModule(config.Configuration{}, servers) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } - - actual = shouldLoadOpentracingModule(configuration, servers) - if expected != actual { - t.Errorf("Expected '%v' but returned '%v'", expected, actual) - } -} - func TestOpentelemetryForLocation(t *testing.T) { trueVal := true falseVal := false @@ -1978,6 +1780,7 @@ opentelemetry_trust_incoming_spans off;` } } +//nolint:dupl // Ignore dupl errors for similar test case func TestShouldLoadOpentelemetryModule(t *testing.T) { // ### Invalid argument type tests ### // The first tests should return false. @@ -2104,7 +1907,6 @@ func TestModSecurityForLocation(t *testing.T) { } func TestBuildServerName(t *testing.T) { - testCases := []struct { title string hostname string diff --git a/internal/ingress/controller/util.go b/internal/ingress/controller/util.go index 6562dd17c..8851f323f 100644 --- a/internal/ingress/controller/util.go +++ b/internal/ingress/controller/util.go @@ -135,11 +135,13 @@ func (nc NginxCommand) ExecCommand(args ...string) *exec.Cmd { cmdArgs = append(cmdArgs, "-c", cfgPath) cmdArgs = append(cmdArgs, args...) + //nolint:gosec // Ignore G204 error return exec.Command(nc.Binary, cmdArgs...) } // Test checks if config file is a syntax valid nginx configuration func (nc NginxCommand) Test(cfg string) ([]byte, error) { + //nolint:gosec // Ignore G204 error return exec.Command(nc.Binary, "-c", cfg, "-t").CombinedOutput() } diff --git a/internal/ingress/defaults/main.go b/internal/ingress/defaults/main.go index 0aab2ff47..2bb58c858 100644 --- a/internal/ingress/defaults/main.go +++ b/internal/ingress/defaults/main.go @@ -34,6 +34,13 @@ type Backend struct { // toggles whether or not to remove trailing slashes during TLS redirects PreserveTrailingSlash bool `json:"preserve-trailing-slash"` + // allows usage of CustomHTTPErrors without intercepting service errors + // e.g. custom 404 and 503 when service-a does not exist or is not available + // but service-a can return 404 and 503 error codes without intercept + // http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors + // By default this is false + DisableProxyInterceptErrors bool `json:"disable-proxy-intercept-errors"` + // http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size // Sets the maximum allowed size of the client request body ProxyBodySize string `json:"proxy-body-size"` @@ -100,7 +107,7 @@ type Backend struct { // Name server/s used to resolve names of upstream servers into IP addresses. // The file /etc/resolv.conf is used as DNS resolution configuration. - Resolver []net.IP + Resolver []net.IP `json:"Resolver"` // SkipAccessLogURLs sets a list of URLs that should not appear in the NGINX access log // This is useful with urls like `/health` or `health-check` that make "complex" reading the logs @@ -170,3 +177,15 @@ type Backend struct { // It disables that behavior and instead uses a single upstream in NGINX, the service's Cluster IP and port. ServiceUpstream bool `json:"service-upstream"` } + +type SecurityConfiguration struct { + // AllowCrossNamespaceResources enables users to consume cross namespace resource on annotations + // Case disabled, attempts to use secrets or configmaps from a namespace different from Ingress will + // be denied + // This valid will default to `false` on future releases + AllowCrossNamespaceResources bool `json:"allow-cross-namespace-resources"` + + // AnnotationsRiskLevel represents the risk accepted on an annotation. If the risk is, for instance `Medium`, annotations + // with risk High and Critical will not be accepted + AnnotationsRiskLevel string `json:"annotations-risk-level"` +} diff --git a/internal/ingress/errors/errors.go b/internal/ingress/errors/errors.go index 93c9ee5e0..d70218334 100644 --- a/internal/ingress/errors/errors.go +++ b/internal/ingress/errors/errors.go @@ -33,57 +33,57 @@ var ( // NewInvalidAnnotationConfiguration returns a new InvalidConfiguration error for use when // annotations are not correctly configured -func NewInvalidAnnotationConfiguration(name string, reason string) error { - return InvalidConfiguration{ +func NewInvalidAnnotationConfiguration(name, reason string) error { + return InvalidConfigurationError{ Name: fmt.Sprintf("the annotation %v does not contain a valid configuration: %v", name, reason), } } // NewInvalidAnnotationContent returns a new InvalidContent error func NewInvalidAnnotationContent(name string, val interface{}) error { - return InvalidContent{ + return InvalidContentError{ Name: fmt.Sprintf("the annotation %v does not contain a valid value (%v)", name, val), } } // NewLocationDenied returns a new LocationDenied error func NewLocationDenied(reason string) error { - return LocationDenied{ - Reason: fmt.Errorf("Location denied, reason: %v", reason), + return LocationDeniedError{ + Reason: fmt.Errorf("location denied, reason: %v", reason), } } -// InvalidConfiguration Error -type InvalidConfiguration struct { +// InvalidConfigurationError +type InvalidConfigurationError struct { Name string } -func (e InvalidConfiguration) Error() string { +func (e InvalidConfigurationError) Error() string { return e.Name } -// InvalidContent error -type InvalidContent struct { +// InvalidContentError +type InvalidContentError struct { Name string } -func (e InvalidContent) Error() string { +func (e InvalidContentError) Error() string { return e.Name } -// LocationDenied error -type LocationDenied struct { +// LocationDeniedError +type LocationDeniedError struct { Reason error } -func (e LocationDenied) Error() string { +func (e LocationDeniedError) Error() string { return e.Reason.Error() } // IsLocationDenied checks if the err is an error which // indicates a location should return HTTP code 503 func IsLocationDenied(e error) bool { - _, ok := e.(LocationDenied) + _, ok := e.(LocationDeniedError) return ok } @@ -96,7 +96,7 @@ func IsMissingAnnotations(e error) bool { // IsInvalidContent checks if the err is an error which // indicates an annotations value is not valid func IsInvalidContent(e error) bool { - _, ok := e.(InvalidContent) + _, ok := e.(InvalidContentError) return ok } @@ -110,3 +110,47 @@ func New(m string) error { func Errorf(format string, args ...interface{}) error { return fmt.Errorf(format, args...) } + +type ValidationError struct { + Reason error +} + +type RiskyAnnotationError struct { + Reason error +} + +func (e ValidationError) Error() string { + return e.Reason.Error() +} + +// NewValidationError returns a new LocationDenied error +func NewValidationError(annotation string) error { + return ValidationError{ + Reason: fmt.Errorf("annotation %s contains invalid value", annotation), + } +} + +// IsValidationError checks if the err is an error which +// indicates that some annotation value is invalid +func IsValidationError(e error) bool { + _, ok := e.(ValidationError) + return ok +} + +// NewValidationError returns a new LocationDenied error +func NewRiskyAnnotations(name string) error { + return RiskyAnnotationError{ + Reason: fmt.Errorf("annotation group %s contains risky annotation based on ingress configuration", name), + } +} + +// IsRiskyAnnotationError checks if the err is an error which +// indicates that some annotation value is invalid +func IsRiskyAnnotationError(e error) bool { + _, ok := e.(ValidationError) + return ok +} + +func (e RiskyAnnotationError) Error() string { + return e.Reason.Error() +} diff --git a/internal/ingress/inspector/ingress_test.go b/internal/ingress/inspector/ingress_test.go index bfd9f6b93..52ad8d431 100644 --- a/internal/ingress/inspector/ingress_test.go +++ b/internal/ingress/inspector/ingress_test.go @@ -24,7 +24,6 @@ import ( ) func makeSimpleIngress(hostname string, paths ...string) *networking.Ingress { - newIngress := networking.Ingress{ ObjectMeta: v1.ObjectMeta{ Name: "test1", diff --git a/internal/ingress/inspector/inspector.go b/internal/ingress/inspector/inspector.go index e7b6c2f10..b41e18d9e 100644 --- a/internal/ingress/inspector/inspector.go +++ b/internal/ingress/inspector/inspector.go @@ -17,6 +17,9 @@ limitations under the License. package inspector import ( + "errors" + "fmt" + corev1 "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1" "k8s.io/klog/v2" @@ -36,3 +39,27 @@ func DeepInspect(obj interface{}) error { return nil } } + +var implSpecific = networking.PathTypeImplementationSpecific + +func ValidatePathType(ing *networking.Ingress) error { + if ing == nil { + return fmt.Errorf("received null ingress") + } + var err error + for _, rule := range ing.Spec.Rules { + if rule.HTTP != nil { + for _, path := range rule.HTTP.Paths { + if path.Path == "" { + continue + } + if path.PathType == nil || *path.PathType != implSpecific { + if isValid := validPathType.MatchString(path.Path); !isValid { + err = errors.Join(err, fmt.Errorf("path %s cannot be used with pathType %s", path.Path, string(*path.PathType))) + } + } + } + } + } + return err +} diff --git a/internal/ingress/inspector/inspector_test.go b/internal/ingress/inspector/inspector_test.go new file mode 100644 index 000000000..36b029cff --- /dev/null +++ b/internal/ingress/inspector/inspector_test.go @@ -0,0 +1,191 @@ +/* +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 inspector + +import ( + "errors" + "fmt" + "testing" + + networking "k8s.io/api/networking/v1" +) + +var ( + exact = networking.PathTypeExact + prefix = networking.PathTypePrefix +) + +var ( + validIngress = &networking.Ingress{ + Spec: networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + Path: "/test", + }, + { + PathType: &prefix, + Path: "/xpto/ab0/x_ss-9", + }, + { + PathType: &exact, + Path: "/bla/", + }, + }, + }, + }, + }, + }, + }, + } + + emptyIngress = &networking.Ingress{ + Spec: networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + PathType: &exact, + }, + }, + }, + }, + }, + }, + }, + } + + invalidIngress = &networking.Ingress{ + Spec: networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + PathType: &exact, + Path: "/foo.+", + }, + { + PathType: &exact, + Path: "xpto/lala", + }, + { + PathType: &exact, + Path: "/xpto/lala", + }, + { + PathType: &prefix, + Path: "/foo/bar/[a-z]{3}", + }, + { + PathType: &prefix, + Path: "/lala/xp\ntest", + }, + }, + }, + }, + }, + }, + }, + } + + validImplSpecific = &networking.Ingress{ + Spec: networking.IngressSpec{ + Rules: []networking.IngressRule{ + { + IngressRuleValue: networking.IngressRuleValue{ + HTTP: &networking.HTTPIngressRuleValue{ + Paths: []networking.HTTPIngressPath{ + { + PathType: &implSpecific, + Path: "/foo.+", + }, + { + PathType: &implSpecific, + Path: "xpto/lala", + }, + }, + }, + }, + }, + }, + }, + } +) + +var aErr = func(s, pathType string) error { + return fmt.Errorf("path %s cannot be used with pathType %s", s, pathType) +} + +func TestValidatePathType(t *testing.T) { + tests := []struct { + name string + ing *networking.Ingress + wantErr bool + err error + }{ + { + name: "nil should return an error", + ing: nil, + wantErr: true, + err: fmt.Errorf("received null ingress"), + }, + { + name: "valid should not return an error", + ing: validIngress, + wantErr: false, + }, + { + name: "empty should not return an error", + ing: emptyIngress, + wantErr: false, + }, + { + name: "empty should not return an error", + ing: validImplSpecific, + wantErr: false, + }, + { + name: "invalid should return multiple errors", + ing: invalidIngress, + wantErr: true, + err: errors.Join( + aErr("/foo.+", "Exact"), + aErr("xpto/lala", "Exact"), + aErr("/foo/bar/[a-z]{3}", "Prefix"), + aErr("/lala/xp\ntest", "Prefix"), + ), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidatePathType(tt.ing) + if (err != nil) != tt.wantErr { + t.Errorf("ValidatePathType() error = %v, wantErr %v", err, tt.wantErr) + } + if (err != nil && tt.err != nil) && tt.err.Error() != err.Error() { + t.Errorf("received invalid error: want = %v, expected %v", tt.err, err) + } + }) + } +} diff --git a/internal/ingress/inspector/rules.go b/internal/ingress/inspector/rules.go index ab573b7fe..8388efdd5 100644 --- a/internal/ingress/inspector/rules.go +++ b/internal/ingress/inspector/rules.go @@ -28,6 +28,14 @@ var ( invalidSecretsDir = regexp.MustCompile(`/var/run/secrets`) invalidByLuaDirective = regexp.MustCompile(`.*_by_lua.*`) + // validPathType enforces alphanumeric, -, _ and / characters. + // The field (?i) turns this regex case insensitive + // The remaining regex says that the string must start with a "/" (^/) + // the group [[:alnum:]\_\-\/]* says that any amount of characters (A-Za-z0-9), _, - and / + // are accepted until the end of the line + // Nothing else is accepted. + validPathType = regexp.MustCompile(`(?i)^/[[:alnum:]\_\-/]*$`) + invalidRegex = []regexp.Regexp{} ) @@ -44,8 +52,8 @@ func init() { // CheckRegex receives a value/configuration and validates if it matches with one of the // forbidden regexes. func CheckRegex(value string) error { - for _, regex := range invalidRegex { - if regex.MatchString(value) { + for i := range invalidRegex { + if invalidRegex[i].MatchString(value) { return fmt.Errorf("invalid value found: %s", value) } } diff --git a/internal/ingress/inspector/rules_test.go b/internal/ingress/inspector/rules_test.go index 6ed6dbe87..3945a3bd4 100644 --- a/internal/ingress/inspector/rules_test.go +++ b/internal/ingress/inspector/rules_test.go @@ -19,7 +19,6 @@ package inspector import "testing" func TestCheckRegex(t *testing.T) { - tests := []struct { name string value string diff --git a/internal/ingress/inspector/service.go b/internal/ingress/inspector/service.go index 27ed27a8c..8be08490b 100644 --- a/internal/ingress/inspector/service.go +++ b/internal/ingress/inspector/service.go @@ -21,6 +21,6 @@ import ( ) // InspectService will be used to inspect service objects for possible invalid configurations -func InspectService(svc *corev1.Service) error { +func InspectService(_ *corev1.Service) error { return nil } diff --git a/internal/ingress/metric/collectors/admission.go b/internal/ingress/metric/collectors/admission.go index cf42fbaa1..7b84325c0 100644 --- a/internal/ingress/metric/collectors/admission.go +++ b/internal/ingress/metric/collectors/admission.go @@ -104,7 +104,7 @@ func NewAdmissionCollector(pod, namespace, class string) *AdmissionCollector { } // Describe implements prometheus.Collector -func (am AdmissionCollector) Describe(ch chan<- *prometheus.Desc) { +func (am *AdmissionCollector) Describe(ch chan<- *prometheus.Desc) { am.testedIngressLength.Describe(ch) am.testedIngressTime.Describe(ch) am.renderingIngressLength.Describe(ch) @@ -114,7 +114,7 @@ func (am AdmissionCollector) Describe(ch chan<- *prometheus.Desc) { } // Collect implements the prometheus.Collector interface. -func (am AdmissionCollector) Collect(ch chan<- prometheus.Metric) { +func (am *AdmissionCollector) Collect(ch chan<- prometheus.Metric) { am.testedIngressLength.Collect(ch) am.testedIngressTime.Collect(ch) am.renderingIngressLength.Collect(ch) @@ -139,7 +139,7 @@ func ByteFormat(bytes int64) string { } // SetAdmissionMetrics sets the values for AdmissionMetrics that can be called externally -func (am *AdmissionCollector) SetAdmissionMetrics(testedIngressLength float64, testedIngressTime float64, renderingIngressLength float64, renderingIngressTime float64, testedConfigurationSize float64, admissionTime float64) { +func (am *AdmissionCollector) SetAdmissionMetrics(testedIngressLength, testedIngressTime, renderingIngressLength, renderingIngressTime, testedConfigurationSize, admissionTime float64) { am.testedIngressLength.Set(testedIngressLength) am.testedIngressTime.Set(testedIngressTime) am.renderingIngressLength.Set(renderingIngressLength) diff --git a/internal/ingress/metric/collectors/controller.go b/internal/ingress/metric/collectors/controller.go index 3a65a1a99..e1d6789bb 100644 --- a/internal/ingress/metric/collectors/controller.go +++ b/internal/ingress/metric/collectors/controller.go @@ -226,7 +226,7 @@ func (cm *Controller) IncCheckErrorCount(namespace, name string) { } // IncOrphanIngress sets the the orphaned ingress gauge to one -func (cm *Controller) IncOrphanIngress(namespace string, name string, orphanityType string) { +func (cm *Controller) IncOrphanIngress(namespace, name, orphanityType string) { labels := prometheus.Labels{ "namespace": namespace, "ingress": name, @@ -236,7 +236,7 @@ func (cm *Controller) IncOrphanIngress(namespace string, name string, orphanityT } // DecOrphanIngress sets the the orphaned ingress gauge to zero (all services has their endpoints) -func (cm *Controller) DecOrphanIngress(namespace string, name string, orphanityType string) { +func (cm *Controller) DecOrphanIngress(namespace, name, orphanityType string) { labels := prometheus.Labels{ "namespace": namespace, "ingress": name, @@ -261,7 +261,7 @@ func (cm *Controller) ConfigSuccess(hash uint64, success bool) { } // Describe implements prometheus.Collector -func (cm Controller) Describe(ch chan<- *prometheus.Desc) { +func (cm *Controller) Describe(ch chan<- *prometheus.Desc) { cm.configHash.Describe(ch) cm.configSuccess.Describe(ch) cm.configSuccessTime.Describe(ch) @@ -277,7 +277,7 @@ func (cm Controller) Describe(ch chan<- *prometheus.Desc) { } // Collect implements the prometheus.Collector interface. -func (cm Controller) Collect(ch chan<- prometheus.Metric) { +func (cm *Controller) Collect(ch chan<- prometheus.Metric) { cm.configHash.Collect(ch) cm.configSuccess.Collect(ch) cm.configSuccessTime.Collect(ch) @@ -295,41 +295,45 @@ func (cm Controller) Collect(ch chan<- prometheus.Metric) { // SetSSLExpireTime sets the expiration time of SSL Certificates func (cm *Controller) SetSSLExpireTime(servers []*ingress.Server) { for _, s := range servers { - if s.Hostname != "" && s.SSLCert != nil && s.SSLCert.ExpireTime.Unix() > 0 { - labels := make(prometheus.Labels, len(cm.labels)+1) - for k, v := range cm.labels { - labels[k] = v - } - labels["host"] = s.Hostname - labels["secret_name"] = s.SSLCert.Name - - cm.sslExpireTime.With(labels).Set(float64(s.SSLCert.ExpireTime.Unix())) + if !(s.Hostname != "" && s.SSLCert != nil && s.SSLCert.ExpireTime.Unix() > 0) { + continue } + + labels := make(prometheus.Labels, len(cm.labels)+1) + for k, v := range cm.labels { + labels[k] = v + } + labels["host"] = s.Hostname + labels["secret_name"] = s.SSLCert.Name + + cm.sslExpireTime.With(labels).Set(float64(s.SSLCert.ExpireTime.Unix())) } } // SetSSLInfo creates a metric with all certificates informations func (cm *Controller) SetSSLInfo(servers []*ingress.Server) { for _, s := range servers { - if s.SSLCert != nil && s.SSLCert.Certificate != nil && s.SSLCert.Certificate.SerialNumber != nil { - labels := make(prometheus.Labels, len(cm.labels)+1) - for k, v := range cm.labels { - labels[k] = v - } - labels["identifier"] = s.SSLCert.Identifier() - labels["host"] = s.Hostname - labels["secret_name"] = s.SSLCert.Name - labels["namespace"] = s.SSLCert.Namespace - labels["issuer_common_name"] = s.SSLCert.Certificate.Issuer.CommonName - labels["issuer_organization"] = "" - if len(s.SSLCert.Certificate.Issuer.Organization) > 0 { - labels["issuer_organization"] = s.SSLCert.Certificate.Issuer.Organization[0] - } - labels["serial_number"] = s.SSLCert.Certificate.SerialNumber.String() - labels["public_key_algorithm"] = s.SSLCert.Certificate.PublicKeyAlgorithm.String() - - cm.sslInfo.With(labels).Set(1) + if s.SSLCert == nil || s.SSLCert.Certificate == nil || s.SSLCert.Certificate.SerialNumber == nil { + continue } + + labels := make(prometheus.Labels, len(cm.labels)+1) + for k, v := range cm.labels { + labels[k] = v + } + labels["identifier"] = s.SSLCert.Identifier() + labels["host"] = s.Hostname + labels["secret_name"] = s.SSLCert.Name + labels["namespace"] = s.SSLCert.Namespace + labels["issuer_common_name"] = s.SSLCert.Certificate.Issuer.CommonName + labels["issuer_organization"] = "" + if len(s.SSLCert.Certificate.Issuer.Organization) > 0 { + labels["issuer_organization"] = s.SSLCert.Certificate.Issuer.Organization[0] + } + labels["serial_number"] = s.SSLCert.Certificate.SerialNumber.String() + labels["public_key_algorithm"] = s.SSLCert.Certificate.PublicKeyAlgorithm.String() + + cm.sslInfo.With(labels).Set(1) } } diff --git a/internal/ingress/metric/collectors/controller_test.go b/internal/ingress/metric/collectors/controller_test.go index ef80bc96a..15735df42 100644 --- a/internal/ingress/metric/collectors/controller_test.go +++ b/internal/ingress/metric/collectors/controller_test.go @@ -76,9 +76,12 @@ func TestControllerCounters(t *testing.T) { { name: "should set SSL certificates metrics", test: func(cm *Controller) { - t1, _ := time.Parse( + t1, err := time.Parse( time.RFC3339, "2012-11-01T22:08:41+00:00") + if err != nil { + t.Errorf("unexpected error: %v", err) + } servers := []*ingress.Server{ { @@ -106,7 +109,6 @@ func TestControllerCounters(t *testing.T) { { name: "should set SSL certificates infos metrics", test: func(cm *Controller) { - servers := []*ingress.Server{ { Hostname: "demo", @@ -143,7 +145,6 @@ func TestControllerCounters(t *testing.T) { { name: "should ignore certificates without serial number", test: func(cm *Controller) { - servers := []*ingress.Server{ { Hostname: "demo", @@ -168,7 +169,6 @@ func TestControllerCounters(t *testing.T) { { name: "should ignore certificates with nil x509 pointer", test: func(cm *Controller) { - servers := []*ingress.Server{ { Hostname: "demo", @@ -193,7 +193,6 @@ func TestControllerCounters(t *testing.T) { { name: "should ignore servers without certificates", test: func(cm *Controller) { - servers := []*ingress.Server{ { Hostname: "demo", @@ -232,9 +231,12 @@ func TestRemoveMetrics(t *testing.T) { t.Errorf("registering collector failed: %s", err) } - t1, _ := time.Parse( + t1, err := time.Parse( time.RFC3339, "2012-11-01T22:08:41+00:00") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } servers := []*ingress.Server{ { @@ -279,10 +281,12 @@ func TestRemoveAllSSLMetrics(t *testing.T) { t.Errorf("registering collector failed: %s", err) } - t1, _ := time.Parse( + t1, err := time.Parse( time.RFC3339, "2012-11-01T22:08:41+00:00") - + if err != nil { + t.Errorf("unexpected error: %v", err) + } servers := []*ingress.Server{ { Hostname: "demo", diff --git a/internal/ingress/metric/collectors/main.go b/internal/ingress/metric/collectors/main.go index 2c57ad774..b2479929b 100644 --- a/internal/ingress/metric/collectors/main.go +++ b/internal/ingress/metric/collectors/main.go @@ -17,4 +17,4 @@ limitations under the License. package collectors // PrometheusNamespace default metric namespace -var PrometheusNamespace = "nginx_ingress_controller" +var PrometheusNamespace = "nginx_ingress_controller" //#nosec G101 diff --git a/internal/ingress/metric/collectors/nginx_status.go b/internal/ingress/metric/collectors/nginx_status.go index 5aaa787de..f3afdc334 100644 --- a/internal/ingress/metric/collectors/nginx_status.go +++ b/internal/ingress/metric/collectors/nginx_status.go @@ -75,7 +75,6 @@ type NGINXStatusCollector interface { // NewNGINXStatus returns a new prometheus collector the default nginx status module func NewNGINXStatus(podName, namespace, ingressClass string) (NGINXStatusCollector, error) { - p := nginxStatusCollector{ scrapeChan: make(chan scrapeRequest), } diff --git a/internal/ingress/metric/collectors/nginx_status_test.go b/internal/ingress/metric/collectors/nginx_status_test.go index 4dc67c425..ec535745d 100644 --- a/internal/ingress/metric/collectors/nginx_status_test.go +++ b/internal/ingress/metric/collectors/nginx_status_test.go @@ -106,7 +106,7 @@ func TestStatusCollector(t *testing.T) { server := &httptest.Server{ Listener: listener, - Config: &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + Config: &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { //nolint:gosec // Ignore the gosec error in testing w.WriteHeader(http.StatusOK) if r.URL.Path == "/nginx_status" { diff --git a/internal/ingress/metric/collectors/process.go b/internal/ingress/metric/collectors/process.go index 23f533876..3803a47ae 100644 --- a/internal/ingress/metric/collectors/process.go +++ b/internal/ingress/metric/collectors/process.go @@ -53,6 +53,8 @@ type BinaryNameMatcher struct { // MatchAndName returns false if the match failed, otherwise // true and the resulting name. +// +//nolint:gocritic // nacl param cannot be a pointer since it's to implement common.MatchNamer interface func (em BinaryNameMatcher) MatchAndName(nacl common.ProcAttributes) (bool, string) { if len(nacl.Cmdline) == 0 { return false, "" @@ -94,8 +96,10 @@ type NGINXProcessCollector interface { Stop() } -var name = "nginx" -var binary = "/usr/bin/nginx" +var ( + name = "nginx" + binary = "/usr/bin/nginx" +) // NewNGINXProcess returns a new prometheus collector for the nginx process func NewNGINXProcess(pod, namespace, ingressClass string) (NGINXProcessCollector, error) { @@ -106,7 +110,7 @@ func NewNGINXProcess(pod, namespace, ingressClass string) (NGINXProcessCollector nm := newBinaryNameMatcher(name, binary) - p := namedProcess{ + p := &namedProcess{ scrapeChan: make(chan scrapeRequest), Grouper: proc.NewGrouper(nm, true, false, false, false), fs: fs, @@ -164,7 +168,7 @@ func NewNGINXProcess(pod, namespace, ingressClass string) (NGINXProcessCollector } // Describe implements prometheus.Collector. -func (p namedProcess) Describe(ch chan<- *prometheus.Desc) { +func (p *namedProcess) Describe(ch chan<- *prometheus.Desc) { ch <- p.data.cpuSecs ch <- p.data.numProcs ch <- p.data.readBytes @@ -175,13 +179,13 @@ func (p namedProcess) Describe(ch chan<- *prometheus.Desc) { } // Collect implements prometheus.Collector. -func (p namedProcess) Collect(ch chan<- prometheus.Metric) { +func (p *namedProcess) Collect(ch chan<- prometheus.Metric) { req := scrapeRequest{results: ch, done: make(chan struct{})} p.scrapeChan <- req <-req.done } -func (p namedProcess) Start() { +func (p *namedProcess) Start() { for req := range p.scrapeChan { ch := req.results p.scrape(ch) @@ -189,18 +193,19 @@ func (p namedProcess) Start() { } } -func (p namedProcess) Stop() { +func (p *namedProcess) Stop() { close(p.scrapeChan) } -func (p namedProcess) scrape(ch chan<- prometheus.Metric) { +func (p *namedProcess) scrape(ch chan<- prometheus.Metric) { _, groups, err := p.Update(p.fs.AllProcs()) if err != nil { klog.Warningf("unexpected error obtaining nginx process info: %v", err) return } - for _, gcounts := range groups { + for i := range groups { + gcounts := groups[i] ch <- prometheus.MustNewConstMetric(p.data.numProcs, prometheus.GaugeValue, float64(gcounts.Procs)) ch <- prometheus.MustNewConstMetric(p.data.memResidentbytes, diff --git a/internal/ingress/metric/collectors/process_test.go b/internal/ingress/metric/collectors/process_test.go index 45170572b..588cbafef 100644 --- a/internal/ingress/metric/collectors/process_test.go +++ b/internal/ingress/metric/collectors/process_test.go @@ -48,8 +48,11 @@ func TestProcessCollector(t *testing.T) { done := make(chan struct{}) go func() { - cmd.Wait() - status := cmd.ProcessState.Sys().(syscall.WaitStatus) + cmd.Wait() //nolint:errcheck // Ignore the error + status, ok := cmd.ProcessState.Sys().(syscall.WaitStatus) + if !ok { + t.Errorf("unexpected type: %T", cmd.ProcessState.Sys()) + } if status.Signaled() { t.Logf("Signal: %v", status.Signal()) } else { @@ -69,7 +72,7 @@ func TestProcessCollector(t *testing.T) { defer func() { cm.Stop() - cmd.Process.Kill() + cmd.Process.Kill() //nolint:errcheck // Ignore the error <-done close(done) }() diff --git a/internal/ingress/metric/collectors/socket.go b/internal/ingress/metric/collectors/socket.go index 508cc6bc8..a70024c57 100644 --- a/internal/ingress/metric/collectors/socket.go +++ b/internal/ingress/metric/collectors/socket.go @@ -44,14 +44,11 @@ type socketData struct { Latency float64 `json:"upstreamLatency"` HeaderTime float64 `json:"upstreamHeaderTime"` ResponseTime float64 `json:"upstreamResponseTime"` - //ResponseLength float64 `json:"upstreamResponseLength"` - //Status string `json:"upstreamStatus"` - - Namespace string `json:"namespace"` - Ingress string `json:"ingress"` - Service string `json:"service"` - Canary string `json:"canary"` - Path string `json:"path"` + Namespace string `json:"namespace"` + Ingress string `json:"ingress"` + Service string `json:"service"` + Canary string `json:"canary"` + Path string `json:"path"` } // HistogramBuckets allow customizing prometheus histogram buckets values @@ -89,19 +86,17 @@ type SocketCollector struct { reportStatusClasses bool } -var ( - requestTags = []string{ - "status", +var requestTags = []string{ + "status", - "method", - "path", + "method", + "path", - "namespace", - "ingress", - "service", - "canary", - } -) + "namespace", + "ingress", + "service", + "canary", +} // DefObjectives was removed in https://github.com/prometheus/client_golang/pull/262 // updating the library to latest version changed the output of the metrics @@ -112,6 +107,7 @@ var defObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001} func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStatusClasses bool, buckets HistogramBuckets, excludeMetrics []string) (*SocketCollector, error) { socket := "/tmp/nginx/prometheus-nginx.socket" // unix sockets must be unlink()ed before being used + //nolint:errcheck // Ignore unlink error _ = syscall.Unlink(socket) listener, err := net.Listen("unix", socket) @@ -119,7 +115,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat return nil, err } - err = os.Chmod(socket, 0777) // #nosec + err = os.Chmod(socket, 0o777) // #nosec if err != nil { return nil, err } @@ -152,7 +148,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat reportStatusClasses: reportStatusClasses, connectTime: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "connect_duration_seconds", Help: "The time spent on establishing a connection with the upstream server", Namespace: PrometheusNamespace, @@ -165,7 +161,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), headerTime: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "header_duration_seconds", Help: "The time spent on receiving first header from the upstream server", Namespace: PrometheusNamespace, @@ -177,7 +173,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat mm, ), responseTime: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "response_duration_seconds", Help: "The time spent on receiving the response from the upstream server", Namespace: PrometheusNamespace, @@ -190,7 +186,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), requestTime: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "request_duration_seconds", Help: "The request processing time in milliseconds", Namespace: PrometheusNamespace, @@ -203,7 +199,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), responseLength: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "response_size", Help: "The response length (including request line, header, and request body)", Namespace: PrometheusNamespace, @@ -216,7 +212,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), requestLength: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "request_size", Help: "The request length (including request line, header, and request body)", Namespace: PrometheusNamespace, @@ -229,7 +225,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), requests: counterMetric( - prometheus.CounterOpts{ + &prometheus.CounterOpts{ Name: "requests", Help: "The total number of client requests", Namespace: PrometheusNamespace, @@ -241,7 +237,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), bytesSent: histogramMetric( - prometheus.HistogramOpts{ + &prometheus.HistogramOpts{ Name: "bytes_sent", Help: "DEPRECATED The number of bytes sent to a client", Namespace: PrometheusNamespace, @@ -254,7 +250,7 @@ func NewSocketCollector(pod, namespace, class string, metricsPerHost, reportStat ), upstreamLatency: summaryMetric( - prometheus.SummaryOpts{ + &prometheus.SummaryOpts{ Name: "ingress_upstream_latency_seconds", Help: "DEPRECATED Upstream service latency per Ingress", Namespace: PrometheusNamespace, @@ -279,36 +275,36 @@ func containsMetric(excludeMetrics map[string]struct{}, name string) bool { return false } -func summaryMetric(opts prometheus.SummaryOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.SummaryVec { +func summaryMetric(opts *prometheus.SummaryOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.SummaryVec { if containsMetric(excludeMetrics, opts.Name) { return nil } m := prometheus.NewSummaryVec( - opts, + *opts, requestTags, ) metricMapping[prometheus.BuildFQName(PrometheusNamespace, "", opts.Name)] = m return m } -func counterMetric(opts prometheus.CounterOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.CounterVec { +func counterMetric(opts *prometheus.CounterOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.CounterVec { if containsMetric(excludeMetrics, opts.Name) { return nil } m := prometheus.NewCounterVec( - opts, + *opts, requestTags, ) metricMapping[prometheus.BuildFQName(PrometheusNamespace, "", opts.Name)] = m return m } -func histogramMetric(opts prometheus.HistogramOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.HistogramVec { +func histogramMetric(opts *prometheus.HistogramOpts, requestTags []string, excludeMetrics map[string]struct{}, metricMapping metricMapping) *prometheus.HistogramVec { if containsMetric(excludeMetrics, opts.Name) { return nil } m := prometheus.NewHistogramVec( - opts, + *opts, requestTags, ) metricMapping[prometheus.BuildFQName(PrometheusNamespace, "", opts.Name)] = m @@ -326,7 +322,8 @@ func (sc *SocketCollector) handleMessage(msg []byte) { return } - for _, stats := range statsBatch { + for i := range statsBatch { + stats := &statsBatch[i] if sc.metricsPerHost && !sc.hosts.Has(stats.Host) { klog.V(3).InfoS("Skipping metric for host not being served", "host", stats.Host) continue @@ -543,14 +540,14 @@ func (sc *SocketCollector) RemoveMetrics(ingresses []string, registry prometheus } // Describe implements prometheus.Collector -func (sc SocketCollector) Describe(ch chan<- *prometheus.Desc) { +func (sc *SocketCollector) Describe(ch chan<- *prometheus.Desc) { for _, metric := range sc.metricMapping { metric.Describe(ch) } } // Collect implements the prometheus.Collector interface. -func (sc SocketCollector) Collect(ch chan<- prometheus.Metric) { +func (sc *SocketCollector) Collect(ch chan<- prometheus.Metric) { for _, metric := range sc.metricMapping { metric.Collect(ch) } diff --git a/internal/ingress/metric/collectors/socket_test.go b/internal/ingress/metric/collectors/socket_test.go index fe442aba0..71e9097c9 100644 --- a/internal/ingress/metric/collectors/socket_test.go +++ b/internal/ingress/metric/collectors/socket_test.go @@ -30,6 +30,7 @@ import ( func TestNewUDPLogListener(t *testing.T) { var count uint64 + //nolint:unparam // Unused `message` param is required by the handleMessages function fn := func(message []byte) { atomic.AddUint64(&count, 1) } @@ -57,8 +58,13 @@ func TestNewUDPLogListener(t *testing.T) { } }() - conn, _ := net.Dial("unix", tmpFile) - conn.Write([]byte("message")) + conn, err := net.Dial("unix", tmpFile) + if err != nil { + t.Errorf("unexpected error connecting to unix socket: %v", err) + } + if _, err := conn.Write([]byte("message")); err != nil { + t.Errorf("unexpected error writing to unix socket: %v", err) + } conn.Close() time.Sleep(1 * time.Millisecond) @@ -68,7 +74,6 @@ func TestNewUDPLogListener(t *testing.T) { } func TestCollector(t *testing.T) { - buckets := struct { TimeBuckets []float64 LengthBuckets []float64 diff --git a/internal/ingress/metric/collectors/testutils.go b/internal/ingress/metric/collectors/testutils.go index 8c5c27b62..669e89e17 100644 --- a/internal/ingress/metric/collectors/testutils.go +++ b/internal/ingress/metric/collectors/testutils.go @@ -31,7 +31,7 @@ import ( // GatherAndCompare retrieves all metrics exposed by a collector and compares it // to an expected output in the Prometheus text exposition format. // metricNames allows only comparing the given metrics. All are compared if it's nil. -func GatherAndCompare(c prometheus.Collector, expected string, metricNames []string, reg prometheus.Gatherer) error { +func GatherAndCompare(_ prometheus.Collector, expected string, metricNames []string, reg prometheus.Gatherer) error { expected = removeUnusedWhitespace(expected) metrics, err := reg.Gather() @@ -77,9 +77,7 @@ metric output does not match expectation; want: got: -'%s' - -`, buf2.String(), buf1.String()) +'%s'`, buf2.String(), buf1.String()) } return nil } diff --git a/internal/ingress/metric/dummy.go b/internal/ingress/metric/dummy.go index d8ae0155a..a619ccbd8 100644 --- a/internal/ingress/metric/dummy.go +++ b/internal/ingress/metric/dummy.go @@ -29,50 +29,50 @@ func NewDummyCollector() Collector { // DummyCollector dummy implementation for mocks in tests type DummyCollector struct{} -// ConfigSuccess ... +// ConfigSuccess dummy implementation func (dc DummyCollector) ConfigSuccess(uint64, bool) {} -// SetAdmissionMetrics ... +// SetAdmissionMetrics dummy implementation func (dc DummyCollector) SetAdmissionMetrics(float64, float64, float64, float64, float64, float64) {} -// IncReloadCount ... +// IncReloadCount dummy implementation func (dc DummyCollector) IncReloadCount() {} -// IncReloadErrorCount ... +// IncReloadErrorCount dummy implementation func (dc DummyCollector) IncReloadErrorCount() {} -// IncOrphanIngress ... +// IncOrphanIngress dummy implementation func (dc DummyCollector) IncOrphanIngress(string, string, string) {} -// DecOrphanIngress ... +// DecOrphanIngress dummy implementation func (dc DummyCollector) DecOrphanIngress(string, string, string) {} -// IncCheckCount ... +// IncCheckCount dummy implementation func (dc DummyCollector) IncCheckCount(string, string) {} -// IncCheckErrorCount ... +// IncCheckErrorCount dummy implementation func (dc DummyCollector) IncCheckErrorCount(string, string) {} -// RemoveMetrics ... -func (dc DummyCollector) RemoveMetrics(ingresses, endpoints, certificates []string) {} +// RemoveMetrics dummy implementation +func (dc DummyCollector) RemoveMetrics(_, _, _ []string) {} -// Start ... -func (dc DummyCollector) Start(admissionStatus string) {} +// Start dummy implementation +func (dc DummyCollector) Start(_ string) {} -// Stop ... -func (dc DummyCollector) Stop(admissionStatus string) {} +// Stop dummy implementation +func (dc DummyCollector) Stop(_ string) {} -// SetSSLInfo ... +// SetSSLInfo dummy implementation func (dc DummyCollector) SetSSLInfo([]*ingress.Server) {} -// SetSSLExpireTime ... +// SetSSLExpireTime dummy implementation func (dc DummyCollector) SetSSLExpireTime([]*ingress.Server) {} -// SetHosts ... -func (dc DummyCollector) SetHosts(hosts sets.Set[string]) {} +// SetHosts dummy implementation +func (dc DummyCollector) SetHosts(_ sets.Set[string]) {} // OnStartedLeading indicates the pod is not the current leader -func (dc DummyCollector) OnStartedLeading(electionID string) {} +func (dc DummyCollector) OnStartedLeading(_ string) {} // OnStoppedLeading indicates the pod is not the current leader -func (dc DummyCollector) OnStoppedLeading(electionID string) {} +func (dc DummyCollector) OnStoppedLeading(_ string) {} diff --git a/internal/ingress/metric/main.go b/internal/ingress/metric/main.go index b2f721f62..aa35a5c51 100644 --- a/internal/ingress/metric/main.go +++ b/internal/ingress/metric/main.go @@ -115,11 +115,11 @@ func (c *collector) ConfigSuccess(hash uint64, success bool) { c.ingressController.ConfigSuccess(hash, success) } -func (c *collector) IncCheckCount(namespace string, name string) { +func (c *collector) IncCheckCount(namespace, name string) { c.ingressController.IncCheckCount(namespace, name) } -func (c *collector) IncCheckErrorCount(namespace string, name string) { +func (c *collector) IncCheckErrorCount(namespace, name string) { c.ingressController.IncCheckErrorCount(namespace, name) } @@ -183,11 +183,11 @@ func (c *collector) SetSSLInfo(servers []*ingress.Server) { c.ingressController.SetSSLInfo(servers) } -func (c *collector) IncOrphanIngress(namespace string, name string, orphanityType string) { +func (c *collector) IncOrphanIngress(namespace, name, orphanityType string) { c.ingressController.IncOrphanIngress(namespace, name, orphanityType) } -func (c *collector) DecOrphanIngress(namespace string, name string, orphanityType string) { +func (c *collector) DecOrphanIngress(namespace, name, orphanityType string) { c.ingressController.DecOrphanIngress(namespace, name, orphanityType) } @@ -195,7 +195,7 @@ func (c *collector) SetHosts(hosts sets.Set[string]) { c.socket.SetHosts(hosts) } -func (c *collector) SetAdmissionMetrics(testedIngressLength float64, testedIngressTime float64, renderingIngressLength float64, renderingIngressTime float64, testedConfigurationSize float64, admissionTime float64) { +func (c *collector) SetAdmissionMetrics(testedIngressLength, testedIngressTime, renderingIngressLength, renderingIngressTime, testedConfigurationSize, admissionTime float64) { c.admissionController.SetAdmissionMetrics( testedIngressLength, testedIngressTime, @@ -219,9 +219,7 @@ func (c *collector) OnStoppedLeading(electionID string) { c.ingressController.RemoveAllSSLMetrics(c.registry) } -var ( - currentLeader uint32 -) +var currentLeader uint32 func setLeader(leader bool) { var i uint32 diff --git a/internal/ingress/resolver/main.go b/internal/ingress/resolver/main.go index e05a2aaae..7d17f4e16 100644 --- a/internal/ingress/resolver/main.go +++ b/internal/ingress/resolver/main.go @@ -26,6 +26,9 @@ type Resolver interface { // GetDefaultBackend returns the backend that must be used as default GetDefaultBackend() defaults.Backend + // GetSecurityConfiguration returns the configuration options from Ingress + GetSecurityConfiguration() defaults.SecurityConfiguration + // GetConfigMap searches for configmap containing the namespace and name usting the character / GetConfigMap(string) (*apiv1.ConfigMap, error) diff --git a/internal/ingress/resolver/mock.go b/internal/ingress/resolver/mock.go index 62c5c6db9..679c3b13c 100644 --- a/internal/ingress/resolver/mock.go +++ b/internal/ingress/resolver/mock.go @@ -26,7 +26,9 @@ import ( // Mock implements the Resolver interface type Mock struct { - ConfigMaps map[string]*apiv1.ConfigMap + ConfigMaps map[string]*apiv1.ConfigMap + AnnotationsRiskLevel string + AllowCrossNamespace bool } // GetDefaultBackend returns the backend that must be used as default @@ -34,6 +36,17 @@ func (m Mock) GetDefaultBackend() defaults.Backend { return defaults.Backend{} } +func (m Mock) GetSecurityConfiguration() defaults.SecurityConfiguration { + defRisk := m.AnnotationsRiskLevel + if defRisk == "" { + defRisk = "Critical" + } + return defaults.SecurityConfiguration{ + AnnotationsRiskLevel: defRisk, + AllowCrossNamespaceResources: m.AllowCrossNamespace, + } +} + // GetSecret searches for secrets contenating the namespace and name using a the character / func (m Mock) GetSecret(string) (*apiv1.Secret, error) { return nil, nil diff --git a/internal/ingress/status/status.go b/internal/ingress/status/status.go index a7506705c..81fb9044a 100644 --- a/internal/ingress/status/status.go +++ b/internal/ingress/status/status.go @@ -44,7 +44,7 @@ import ( // which the status should check if an update is required. var UpdateInterval = 60 -// Syncer ... +// Syncer is an interface that implements syncer type Syncer interface { Run(chan struct{}) @@ -56,7 +56,7 @@ type ingressLister interface { ListIngresses() []*ingress.Ingress } -// Config ... +// Config is a structure that implements Client interfaces type Config struct { Client clientset.Interface @@ -87,7 +87,7 @@ type statusSync struct { } // Start starts the loop to keep the status in sync -func (s statusSync) Run(stopCh chan struct{}) { +func (s *statusSync) Run(stopCh chan struct{}) { go s.syncQueue.Run(time.Second, stopCh) // trigger initial sync @@ -95,15 +95,19 @@ func (s statusSync) Run(stopCh chan struct{}) { // when this instance is the leader we need to enqueue // an item to trigger the update of the Ingress status. - wait.PollUntil(time.Duration(UpdateInterval)*time.Second, func() (bool, error) { + //nolint:staticcheck // TODO: will replace it since wait.PollUntil is deprecated + err := wait.PollUntil(time.Duration(UpdateInterval)*time.Second, func() (bool, error) { s.syncQueue.EnqueueTask(task.GetDummyObject("sync status")) return false, nil }, stopCh) + if err != nil { + klog.ErrorS(err, "error running poll") + } } // Shutdown stops the sync. In case the instance is the leader it will remove the current IP // if there is no other instances running. -func (s statusSync) Shutdown() { +func (s *statusSync) Shutdown() { go s.syncQueue.Shutdown() if !s.UpdateStatusOnShutdown { @@ -132,7 +136,7 @@ func (s statusSync) Shutdown() { s.updateStatus([]v1.IngressLoadBalancerIngress{}) } -func (s *statusSync) sync(key interface{}) error { +func (s *statusSync) sync(_ interface{}) error { if s.syncQueue.IsShuttingDown() { klog.V(2).InfoS("skipping Ingress status update (shutting down in progress)") return nil @@ -147,13 +151,13 @@ func (s *statusSync) sync(key interface{}) error { return nil } -func (s statusSync) keyfunc(input interface{}) (interface{}, error) { +func (s *statusSync) keyfunc(input interface{}) (interface{}, error) { return input, nil } // NewStatusSyncer returns a new Syncer instance func NewStatusSyncer(config Config) Syncer { - st := statusSync{ + st := &statusSync{ Config: config, } st.syncQueue = task.NewCustomTaskQueue(st.sync, st.keyfunc) @@ -226,7 +230,6 @@ func (s *statusSync) runningAddresses() ([]v1.IngressLoadBalancerIngress, error) } func (s *statusSync) isRunningMultiplePods() bool { - // As a standard, app.kubernetes.io are "reserved well-known" labels. // In our case, we add those labels as identifiers of the Ingress // deployment in this namespace, so we can select it as a set of Ingress instances. @@ -285,7 +288,8 @@ func (s *statusSync) updateStatus(newIngressPoint []v1.IngressLoadBalancerIngres } func runUpdate(ing *ingress.Ingress, status []v1.IngressLoadBalancerIngress, - client clientset.Interface) pool.WorkFunc { + client clientset.Interface, +) pool.WorkFunc { return func(wu pool.WorkUnit) (interface{}, error) { if wu.IsCancelled() { return nil, nil @@ -338,7 +342,10 @@ func ingressSliceEqual(lhs, rhs []v1.IngressLoadBalancerIngress) bool { } func statusAddressFromService(service string, kubeClient clientset.Interface) ([]v1.IngressLoadBalancerIngress, error) { - ns, name, _ := k8s.ParseNameNS(service) + ns, name, err := k8s.ParseNameNS(service) + if err != nil { + return nil, err + } svc, err := kubeClient.CoreV1().Services(ns).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return nil, err @@ -359,15 +366,15 @@ func statusAddressFromService(service string, kubeClient clientset.Interface) ([ IP: svc.Spec.ClusterIP, }}, nil } - addrs := make([]v1.IngressLoadBalancerIngress, len(svc.Spec.ExternalIPs)) - for i, ip := range svc.Spec.ExternalIPs { - addrs[i] = v1.IngressLoadBalancerIngress{IP: ip} + addrs := make([]v1.IngressLoadBalancerIngress, 0, len(svc.Spec.ExternalIPs)) + for _, ip := range svc.Spec.ExternalIPs { + addrs = append(addrs, v1.IngressLoadBalancerIngress{IP: ip}) } return addrs, nil case apiv1.ServiceTypeLoadBalancer: - addrs := make([]v1.IngressLoadBalancerIngress, len(svc.Status.LoadBalancer.Ingress)) + addrs := make([]v1.IngressLoadBalancerIngress, 0, len(svc.Status.LoadBalancer.Ingress)) for i, ingress := range svc.Status.LoadBalancer.Ingress { - addrs[i] = v1.IngressLoadBalancerIngress{} + addrs = append(addrs, v1.IngressLoadBalancerIngress{}) if ingress.Hostname != "" { addrs[i].Hostname = ingress.Hostname } diff --git a/internal/ingress/status/status_test.go b/internal/ingress/status/status_test.go index 3dd56f37d..01419708b 100644 --- a/internal/ingress/status/status_test.go +++ b/internal/ingress/status/status_test.go @@ -18,7 +18,6 @@ package status import ( "context" - "os" "reflect" "testing" "time" @@ -34,6 +33,8 @@ import ( "k8s.io/ingress-nginx/pkg/apis/ingress" ) +const localhost = "127.0.0.1" + func buildLoadBalancerIngressByIP() []networking.IngressLoadBalancerIngress { return []networking.IngressLoadBalancerIngress{ { @@ -126,17 +127,6 @@ func buildSimpleClientSet() *testclient.Clientset { // This is commented out as the ServiceStatus.LoadBalancer field expects a LoadBalancerStatus object // which is incompatible with the current Ingress struct which expects a IngressLoadBalancerStatus object // TODO: update this service when the ServiceStatus struct gets updated - //{ - // ObjectMeta: metav1.ObjectMeta{ - // Name: "foo", - // Namespace: apiv1.NamespaceDefault, - // }, - // Status: apiv1.ServiceStatus{ - // LoadBalancer: apiv1.LoadBalancerStatus{ - // Ingress: buildLoadBalancerIngressByIP(), - // }, - // }, - //}, { ObjectMeta: metav1.ObjectMeta{ Name: "foo_non_exist", @@ -185,7 +175,8 @@ func buildSimpleClientSet() *testclient.Clientset { Name: "ingress-controller-leader", Namespace: apiv1.NamespaceDefault, }, - }}}, + }, + }}, &networking.IngressList{Items: buildExtensionsIngresses()}, ) } @@ -245,30 +236,33 @@ func buildExtensionsIngresses() []networking.Ingress { } } -type testIngressLister struct { -} +type testIngressLister struct{} func (til *testIngressLister) ListIngresses() []*ingress.Ingress { var ingresses []*ingress.Ingress - ingresses = append(ingresses, &ingress.Ingress{ - Ingress: networking.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo_ingress_non_01", - Namespace: apiv1.NamespaceDefault, - }}}) - - ingresses = append(ingresses, &ingress.Ingress{ - Ingress: networking.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo_ingress_1", - Namespace: apiv1.NamespaceDefault, - }, - Status: networking.IngressStatus{ - LoadBalancer: networking.IngressLoadBalancerStatus{ - Ingress: buildLoadBalancerIngressByIP(), + ingresses = append(ingresses, + &ingress.Ingress{ + Ingress: networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo_ingress_non_01", + Namespace: apiv1.NamespaceDefault, }, }, - }}) + }, + &ingress.Ingress{ + Ingress: networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo_ingress_1", + Namespace: apiv1.NamespaceDefault, + }, + Status: networking.IngressStatus{ + LoadBalancer: networking.IngressLoadBalancerStatus{ + Ingress: buildLoadBalancerIngressByIP(), + }, + }, + }, + }, + ) return ingresses } @@ -290,8 +284,8 @@ func buildStatusSync() statusSync { func TestStatusActions(t *testing.T) { // make sure election can be created - os.Setenv("POD_NAME", "foo1") - os.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) + t.Setenv("POD_NAME", "foo1") + t.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) c := Config{ Client: buildSimpleClientSet(), PublishService: "", @@ -315,7 +309,10 @@ func TestStatusActions(t *testing.T) { t.Fatalf("expected a valid Sync") } - fk := fkSync.(statusSync) + fk, ok := fkSync.(*statusSync) + if !ok { + t.Errorf("unexpected type: %T", fkSync) + } // start it and wait for the election and syn actions stopCh := make(chan struct{}) @@ -325,7 +322,9 @@ func TestStatusActions(t *testing.T) { // wait for the election time.Sleep(100 * time.Millisecond) // execute sync - fk.sync("just-test") + if err := fk.sync("just-test"); err != nil { + t.Errorf("unexpected error: %v", err) + } // PublishService is empty, so the running address is: ["11.0.0.2"] // after updated, the ingress's ip should only be "11.0.0.2" newIPs := []networking.IngressLoadBalancerIngress{{ @@ -364,7 +363,7 @@ func TestStatusActions(t *testing.T) { } } -func TestCallback(t *testing.T) { +func TestCallback(_ *testing.T) { buildStatusSync() } @@ -373,7 +372,6 @@ func TestKeyfunc(t *testing.T) { i := "foo_base_pod" r, err := fk.keyfunc(i) - if err != nil { t.Fatalf("unexpected error") } @@ -390,34 +388,36 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }{ "service type ClusterIP": { testclient.NewSimpleClientset( - &apiv1.PodList{Items: []apiv1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.PodSpec{ - NodeName: "foo_node", - }, - Status: apiv1.PodStatus{ - Phase: apiv1.PodRunning, + &apiv1.PodList{ + Items: []apiv1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.PodSpec{ + NodeName: "foo_node", + }, + Status: apiv1.PodStatus{ + Phase: apiv1.PodRunning, + }, }, }, }, - }, - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeClusterIP, - ClusterIP: "1.1.1.1", + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.ServiceSpec{ + Type: apiv1.ServiceTypeClusterIP, + ClusterIP: "1.1.1.1", + }, }, }, }, - }, ), []networking.IngressLoadBalancerIngress{ {IP: "1.1.1.1"}, @@ -426,19 +426,20 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }, "service type NodePort": { testclient.NewSimpleClientset( - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeNodePort, - ClusterIP: "1.1.1.1", + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.ServiceSpec{ + Type: apiv1.ServiceTypeNodePort, + ClusterIP: "1.1.1.1", + }, }, }, }, - }, ), []networking.IngressLoadBalancerIngress{ {IP: "1.1.1.1"}, @@ -447,19 +448,20 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }, "service type ExternalName": { testclient.NewSimpleClientset( - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeExternalName, - ExternalName: "foo.bar", + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.ServiceSpec{ + Type: apiv1.ServiceTypeExternalName, + ExternalName: "foo.bar", + }, }, }, }, - }, ), []networking.IngressLoadBalancerIngress{ {Hostname: "foo.bar"}, @@ -468,35 +470,36 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }, "service type LoadBalancer": { testclient.NewSimpleClientset( - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeLoadBalancer, - }, - Status: apiv1.ServiceStatus{ - LoadBalancer: apiv1.LoadBalancerStatus{ - Ingress: []apiv1.LoadBalancerIngress{ - { - IP: "10.0.0.1", - }, - { - IP: "", - Hostname: "foo", - }, - { - IP: "10.0.0.2", - Hostname: "10-0-0-2.cloudprovider.example.net", + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.ServiceSpec{ + Type: apiv1.ServiceTypeLoadBalancer, + }, + Status: apiv1.ServiceStatus{ + LoadBalancer: apiv1.LoadBalancerStatus{ + Ingress: []apiv1.LoadBalancerIngress{ + { + IP: "10.0.0.1", + }, + { + IP: "", + Hostname: "foo", + }, + { + IP: "10.0.0.2", + Hostname: "10-0-0-2.cloudprovider.example.net", + }, }, }, }, }, }, }, - }, ), []networking.IngressLoadBalancerIngress{ {IP: "10.0.0.1"}, @@ -510,28 +513,29 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }, "service type LoadBalancer with same externalIP and ingress IP": { testclient.NewSimpleClientset( - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, - }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeLoadBalancer, - ExternalIPs: []string{"10.0.0.1"}, - }, - Status: apiv1.ServiceStatus{ - LoadBalancer: apiv1.LoadBalancerStatus{ - Ingress: []apiv1.LoadBalancerIngress{ - { - IP: "10.0.0.1", + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, + Spec: apiv1.ServiceSpec{ + Type: apiv1.ServiceTypeLoadBalancer, + ExternalIPs: []string{"10.0.0.1"}, + }, + Status: apiv1.ServiceStatus{ + LoadBalancer: apiv1.LoadBalancerStatus{ + Ingress: []apiv1.LoadBalancerIngress{ + { + IP: "10.0.0.1", + }, }, }, }, }, }, }, - }, ), []networking.IngressLoadBalancerIngress{ {IP: "10.0.0.1"}, @@ -540,15 +544,16 @@ func TestRunningAddressesWithPublishService(t *testing.T) { }, "invalid service type": { testclient.NewSimpleClientset( - &apiv1.ServiceList{Items: []apiv1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: apiv1.NamespaceDefault, + &apiv1.ServiceList{ + Items: []apiv1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: apiv1.NamespaceDefault, + }, }, }, }, - }, ), nil, true, @@ -557,7 +562,6 @@ func TestRunningAddressesWithPublishService(t *testing.T) { for title, tc := range testCases { t.Run(title, func(t *testing.T) { - fk := buildStatusSync() fk.Config.Client = tc.fakeClient @@ -585,7 +589,11 @@ func TestRunningAddressesWithPods(t *testing.T) { fk := buildStatusSync() fk.PublishService = "" - r, _ := fk.runningAddresses() + r, err := fk.runningAddresses() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if r == nil { t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress") } @@ -601,9 +609,12 @@ func TestRunningAddressesWithPods(t *testing.T) { func TestRunningAddressesWithPublishStatusAddress(t *testing.T) { fk := buildStatusSync() - fk.PublishStatusAddress = "127.0.0.1" + fk.PublishStatusAddress = localhost - ra, _ := fk.runningAddresses() + ra, err := fk.runningAddresses() + if err != nil { + t.Errorf("unexpected error: %v", err) + } if ra == nil { t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress") } @@ -612,8 +623,8 @@ func TestRunningAddressesWithPublishStatusAddress(t *testing.T) { t.Errorf("returned %v but expected %v", rl, 1) } rv := ra[0] - if rv.IP != "127.0.0.1" { - t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"}) + if rv.IP != localhost { + t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: localhost}) } } @@ -621,7 +632,10 @@ func TestRunningAddressesWithPublishStatusAddresses(t *testing.T) { fk := buildStatusSync() fk.PublishStatusAddress = "127.0.0.1,1.1.1.1" - ra, _ := fk.runningAddresses() + ra, err := fk.runningAddresses() + if err != nil { + t.Errorf("unexpected error: %v", err) + } if ra == nil { t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress") } @@ -631,8 +645,8 @@ func TestRunningAddressesWithPublishStatusAddresses(t *testing.T) { } rv := ra[0] rv2 := ra[1] - if rv.IP != "127.0.0.1" { - t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"}) + if rv.IP != localhost { + t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: localhost}) } if rv2.IP != "1.1.1.1" { t.Errorf("returned %v but expected %v", rv2, networking.IngressLoadBalancerIngress{IP: "1.1.1.1"}) @@ -643,7 +657,10 @@ func TestRunningAddressesWithPublishStatusAddressesAndSpaces(t *testing.T) { fk := buildStatusSync() fk.PublishStatusAddress = "127.0.0.1, 1.1.1.1" - ra, _ := fk.runningAddresses() + ra, err := fk.runningAddresses() + if err != nil { + t.Errorf("unexpected error: %v", err) + } if ra == nil { t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngresst") } @@ -653,8 +670,8 @@ func TestRunningAddressesWithPublishStatusAddressesAndSpaces(t *testing.T) { } rv := ra[0] rv2 := ra[1] - if rv.IP != "127.0.0.1" { - t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"}) + if rv.IP != localhost { + t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: localhost}) } if rv2.IP != "1.1.1.1" { t.Errorf("returned %v but expected %v", rv2, networking.IngressLoadBalancerIngress{IP: "1.1.1.1"}) diff --git a/internal/k8s/main.go b/internal/k8s/main.go index d61013a9f..5e93e560d 100644 --- a/internal/k8s/main.go +++ b/internal/k8s/main.go @@ -33,7 +33,7 @@ import ( ) // ParseNameNS parses a string searching a namespace and name -func ParseNameNS(input string) (string, string, error) { +func ParseNameNS(input string) (ns, name string, err error) { nsName := strings.Split(input, "/") if len(nsName) != 2 { return "", "", fmt.Errorf("invalid format (namespace/name) found in '%v'", input) @@ -148,7 +148,10 @@ const IngressNGINXController = "k8s.io/ingress-nginx" // NetworkingIngressAvailable checks if the package "k8s.io/api/networking/v1" // is available or not and if Ingress V1 is supported (k8s >= v1.19.0) func NetworkingIngressAvailable(client clientset.Interface) bool { - version119, _ := version.ParseGeneric("v1.19.0") + version119, err := version.ParseGeneric("v1.19.0") + if err != nil { + return false + } serverVersion, err := client.Discovery().ServerVersion() if err != nil { diff --git a/internal/k8s/main_test.go b/internal/k8s/main_test.go index f3f8b652e..1721c1fb2 100644 --- a/internal/k8s/main_test.go +++ b/internal/k8s/main_test.go @@ -17,7 +17,6 @@ limitations under the License. package k8s import ( - "os" "testing" apiv1 "k8s.io/api/core/v1" @@ -203,7 +202,8 @@ func TestGetNodeIP(t *testing.T) { }, }, }}}), - "demo", "10.0.0.2", true}, + "demo", "10.0.0.2", true, + }, } for _, fk := range fKNodes { @@ -216,32 +216,32 @@ func TestGetNodeIP(t *testing.T) { func TestGetIngressPod(t *testing.T) { // POD_NAME & POD_NAMESPACE not exist - os.Setenv("POD_NAME", "") - os.Setenv("POD_NAMESPACE", "") + t.Setenv("POD_NAME", "") + t.Setenv("POD_NAMESPACE", "") err := GetIngressPod(testclient.NewSimpleClientset()) if err == nil { t.Errorf("expected an error but returned nil") } // POD_NAME not exist - os.Setenv("POD_NAME", "") - os.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) + t.Setenv("POD_NAME", "") + t.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) err = GetIngressPod(testclient.NewSimpleClientset()) if err == nil { t.Errorf("expected an error but returned nil") } // POD_NAMESPACE not exist - os.Setenv("POD_NAME", "testpod") - os.Setenv("POD_NAMESPACE", "") + t.Setenv("POD_NAME", "testpod") + t.Setenv("POD_NAMESPACE", "") err = GetIngressPod(testclient.NewSimpleClientset()) if err == nil { t.Errorf("expected an error but returned nil") } // POD not exist - os.Setenv("POD_NAME", "testpod") - os.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) + t.Setenv("POD_NAME", "testpod") + t.Setenv("POD_NAMESPACE", apiv1.NamespaceDefault) err = GetIngressPod(testclient.NewSimpleClientset()) if err == nil { t.Errorf("expected an error but returned nil") diff --git a/internal/net/dns/dns.go b/internal/net/dns/dns.go index 7dfbbd177..6b250e8cb 100644 --- a/internal/net/dns/dns.go +++ b/internal/net/dns/dns.go @@ -38,7 +38,7 @@ func GetSystemNameServers() ([]net.IP, error) { lines := strings.Split(string(file), "\n") for l := range lines { trimmed := strings.TrimSpace(lines[l]) - if len(trimmed) == 0 || trimmed[0] == '#' || trimmed[0] == ';' { + if trimmed == "" || trimmed[0] == '#' || trimmed[0] == ';' { continue } fields := strings.Fields(trimmed) diff --git a/internal/net/dns/dns_test.go b/internal/net/dns/dns_test.go index 708e3c6df..7e030e17a 100644 --- a/internal/net/dns/dns_test.go +++ b/internal/net/dns/dns_test.go @@ -40,13 +40,16 @@ func TestGetDNSServers(t *testing.T) { defer f.Close() defer os.Remove(f.Name()) - os.WriteFile(f.Name(), []byte(` + err = os.WriteFile(f.Name(), []byte(` # comment ; comment nameserver 2001:4860:4860::8844 nameserver 2001:4860:4860::8888 nameserver 8.8.8.8 `), file.ReadWriteByUser) + if err != nil { + t.Errorf("unexpected error: %v", err) + } defResolvConf = f.Name() s, err = GetSystemNameServers() diff --git a/internal/net/ipnet_test.go b/internal/net/ipnet_test.go index 95e6b9c32..8e460aae9 100644 --- a/internal/net/ipnet_test.go +++ b/internal/net/ipnet_test.go @@ -36,13 +36,17 @@ func TestNewIPSet(t *testing.T) { } func TestParseCIDRs(t *testing.T) { - cidr, _ := ParseCIDRs("invalid.com") + cidr, err := ParseCIDRs("invalid.com") + if err == nil { + t.Errorf("expected error but got nil") + } + if cidr != nil { t.Errorf("expected %v but got %v", nil, cidr) } expected := []string{"192.0.0.1", "192.0.1.0/24"} - cidr, err := ParseCIDRs("192.0.0.1, 192.0.1.0/24") + cidr, err = ParseCIDRs("192.0.0.1, 192.0.1.0/24") if err != nil { t.Errorf("unexpected error %v", err) } diff --git a/internal/net/net.go b/internal/net/net.go index 712262f3a..b0952d9cc 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -30,11 +30,12 @@ func IsIPV6(ip _net.IP) bool { // IsPortAvailable checks if a TCP port is available or not func IsPortAvailable(p int) bool { ln, err := _net.Listen("tcp", fmt.Sprintf(":%v", p)) - if err != nil { - return false - } - defer ln.Close() - return true + defer func() { + if ln != nil { + ln.Close() + } + }() + return err == nil } // IsIPv6Enabled checks if IPV6 is enabled or not and we have @@ -51,7 +52,10 @@ func IsIPv6Enabled() bool { } for _, addr := range addrs { - ip, _, _ := _net.ParseCIDR(addr.String()) + ip, _, err := _net.ParseCIDR(addr.String()) + if err != nil { + return false + } if IsIPV6(ip) { return true } diff --git a/internal/net/net_test.go b/internal/net/net_test.go index f2f37a838..f7a4f52e1 100644 --- a/internal/net/net_test.go +++ b/internal/net/net_test.go @@ -47,7 +47,7 @@ func TestIsPortAvailable(t *testing.T) { t.Fatal("expected port 0 to be available (random port) but returned false") } - ln, err := net.Listen("tcp", ":0") + ln, err := net.Listen("tcp", ":0") //nolint:gosec // Ignore the gosec error in testing if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/internal/net/ssl/ssl.go b/internal/net/ssl/ssl.go index 05ec10bc5..26fd706e1 100644 --- a/internal/net/ssl/ssl.go +++ b/internal/net/ssl/ssl.go @@ -52,17 +52,15 @@ import ( // certificate generated by the ingress controller var FakeSSLCertificateUID = "00000000-0000-0000-0000-000000000000" -var ( - oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} -) +var oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} const ( - fakeCertificateName = "default-fake-certificate" + fakeCertificateName = "default-fake-certificate" //#nosec G101 ) // getPemFileName returns absolute file path and file name of pem cert related to given fullSecretName -func getPemFileName(fullSecretName string) (string, string) { - pemName := fmt.Sprintf("%v.pem", fullSecretName) +func getPemFileName(fullSecretName string) (filePath, pemName string) { + pemName = fmt.Sprintf("%v.pem", fullSecretName) return fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, pemName), pemName } @@ -81,7 +79,7 @@ func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) { } } - pemCertBuffer.Write([]byte("\n")) + pemCertBuffer.WriteString("\n") pemCertBuffer.Write(key) pemBlock, _ := pem.Decode(pemCertBuffer.Bytes()) @@ -192,15 +190,15 @@ func StoreSSLCertOnDisk(name string, sslCert *ingress.SSLCert) (string, error) { // ConfigureCACertWithCertAndKey appends ca into existing PEM file consisting of cert and key // and sets relevant fields in sslCert object -func ConfigureCACertWithCertAndKey(name string, ca []byte, sslCert *ingress.SSLCert) error { +func ConfigureCACertWithCertAndKey(_ string, ca []byte, sslCert *ingress.SSLCert) error { var buffer bytes.Buffer - _, err := buffer.Write([]byte(sslCert.PemCertKey)) + _, err := buffer.WriteString(sslCert.PemCertKey) if err != nil { return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.CAFileName, err) } - _, err = buffer.Write([]byte("\n")) + _, err = buffer.WriteString("\n") if err != nil { return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.CAFileName, err) } @@ -210,12 +208,12 @@ func ConfigureCACertWithCertAndKey(name string, ca []byte, sslCert *ingress.SSLC return fmt.Errorf("could not write ca data to cert file %v: %v", sslCert.CAFileName, err) } - return os.WriteFile(sslCert.CAFileName, buffer.Bytes(), 0644) + //nolint:gosec // Not change permission to avoid possible issues + return os.WriteFile(sslCert.CAFileName, buffer.Bytes(), 0o644) } // ConfigureCRL creates a CRL file and append it into the SSLCert func ConfigureCRL(name string, crl []byte, sslCert *ingress.SSLCert) error { - crlName := fmt.Sprintf("crl-%v.pem", name) crlFileName := fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, crlName) @@ -228,12 +226,13 @@ func ConfigureCRL(name string, crl []byte, sslCert *ingress.SSLCert) error { return fmt.Errorf("CRL file %v contains invalid data, and must be created only with PEM formatted certificates", name) } - _, err := x509.ParseCRL(pemCRLBlock.Bytes) + _, err := x509.ParseRevocationList(pemCRLBlock.Bytes) if err != nil { - return fmt.Errorf(err.Error()) + return err } - err = os.WriteFile(crlFileName, crl, 0644) + //nolint:gosec // Not change permission to avoid possible issues + err = os.WriteFile(crlFileName, crl, 0o644) if err != nil { return fmt.Errorf("could not write CRL file %v: %v", crlFileName, err) } @@ -242,7 +241,6 @@ func ConfigureCRL(name string, crl []byte, sslCert *ingress.SSLCert) error { sslCert.CRLSHA = file.SHA1(crlFileName) return nil - } // ConfigureCACert is similar to ConfigureCACertWithCertAndKey but it creates a separate file @@ -251,7 +249,8 @@ func ConfigureCACert(name string, ca []byte, sslCert *ingress.SSLCert) error { caName := fmt.Sprintf("ca-%v.pem", name) fileName := fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, caName) - err := os.WriteFile(fileName, ca, 0644) + //nolint:gosec // Not change permission to avoid possible issues + err := os.WriteFile(fileName, ca, 0o644) if err != nil { return fmt.Errorf("could not write CA file %v: %v", fileName, err) } @@ -293,14 +292,14 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre var seq asn1.RawValue var rest []byte if rest, err = asn1.Unmarshal(value, &seq); err != nil { - return + return dnsNames, emailAddresses, ipAddresses, err } else if len(rest) != 0 { err = errors.New("x509: trailing data after X.509 extension") - return + return dnsNames, emailAddresses, ipAddresses, err } if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { err = asn1.StructuralError{Msg: "bad SAN sequence"} - return + return dnsNames, emailAddresses, ipAddresses, err } rest = seq.Bytes @@ -308,7 +307,7 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre var v asn1.RawValue rest, err = asn1.Unmarshal(rest, &v) if err != nil { - return + return dnsNames, emailAddresses, ipAddresses, err } switch v.Tag { case 1: @@ -321,12 +320,12 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre ipAddresses = append(ipAddresses, v.Bytes) default: err = errors.New("x509: certificate contained IP address of length " + strconv.Itoa(len(v.Bytes))) - return + return dnsNames, emailAddresses, ipAddresses, err } } } - return + return dnsNames, emailAddresses, ipAddresses, err } // AddOrUpdateDHParam creates a dh parameters file with the specified name @@ -396,7 +395,7 @@ func GetFakeSSLCert() *ingress.SSLCert { return sslCert } -func getFakeHostSSLCert(host string) ([]byte, []byte) { +func getFakeHostSSLCert(host string) (cert, key []byte) { var priv interface{} var err error @@ -412,7 +411,6 @@ func getFakeHostSSLCert(host string) ([]byte, []byte) { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { klog.Fatalf("failed to generate fake serial number: %v", err) } @@ -436,9 +434,9 @@ func getFakeHostSSLCert(host string) ([]byte, []byte) { klog.Fatalf("Failed to create fake certificate: %v", err) } - cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv.(*rsa.PrivateKey))}) + key = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv.(*rsa.PrivateKey))}) return cert, key } @@ -508,9 +506,14 @@ func NewTLSListener(certificate, key string) *TLSListener { l.load() - _, _ = file.NewFileWatcher(certificate, l.load) - _, _ = file.NewFileWatcher(key, l.load) - + _, err := file.NewFileWatcher(certificate, l.load) + if err != nil { + klog.Errorf("unexpected error: %v", err) + } + _, err = file.NewFileWatcher(key, l.load) + if err != nil { + klog.Errorf("unexpected error: %v", err) + } return &l } diff --git a/internal/net/ssl/ssl_test.go b/internal/net/ssl/ssl_test.go index e251d01d1..9f8c5eeae 100644 --- a/internal/net/ssl/ssl_test.go +++ b/internal/net/ssl/ssl_test.go @@ -20,7 +20,6 @@ import ( "bytes" "crypto" "crypto/rand" - cryptorand "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -43,7 +42,7 @@ import ( ) // generateRSACerts generates a self signed certificate using a self generated ca -func generateRSACerts(host string) (*keyPair, *keyPair, error) { +func generateRSACerts(host string) (newCert, newCa *keyPair, err error) { ca, err := newCA("self-sign-ca") if err != nil { return nil, nil, err @@ -58,7 +57,7 @@ func generateRSACerts(host string) (*keyPair, *keyPair, error) { CommonName: host, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, } - cert, err := newSignedCert(config, key, ca.Cert, ca.Key) + cert, err := newSignedCert(&config, key, ca.Cert, ca.Key) if err != nil { return nil, nil, fmt.Errorf("unable to sign the server certificate: %v", err) } @@ -140,11 +139,11 @@ func TestCACert(t *testing.T) { func TestGetFakeSSLCert(t *testing.T) { sslCert := GetFakeSSLCert() - if len(sslCert.PemCertKey) == 0 { + if sslCert.PemCertKey == "" { t.Fatalf("expected PemCertKey to not be empty") } - if len(sslCert.PemFileName) == 0 { + if sslCert.PemFileName == "" { t.Fatalf("expected PemFileName to not be empty") } @@ -196,7 +195,7 @@ func TestConfigureCRL(t *testing.T) { // Demo CRL from https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls // Converted to PEM to be tested // SHA: ef21f9c97ec2ef84ba3b2ab007c858a6f760d813 - var crl = []byte(`-----BEGIN X509 CRL----- + crl := []byte(`-----BEGIN X509 CRL----- MIIBYDCBygIBATANBgkqhkiG9w0BAQUFADBDMRMwEQYKCZImiZPyLGQBGRYDY29t MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQRcN MDUwMjA1MTIwMDAwWhcNMDUwMjA2MTIwMDAwWjAiMCACARIXDTA0MTExOTE1NTcw @@ -238,6 +237,7 @@ fUNCdMGmr8FVF6IzTNYGmCuk/C4= t.Fatalf("the expected CRL SHA wasn't found") } } + func TestCreateSSLCert(t *testing.T) { cert, _, err := generateRSACerts("echoheaders") if err != nil { @@ -336,16 +336,16 @@ const ( // newPrivateKey creates an RSA private key func newPrivateKey() (*rsa.PrivateKey, error) { - return rsa.GenerateKey(cryptorand.Reader, rsaKeySize) + return rsa.GenerateKey(rand.Reader, rsaKeySize) } // newSignedCert creates a signed certificate using the given CA certificate and key -func newSignedCert(cfg certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { +func newSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, err } - if len(cfg.CommonName) == 0 { + if cfg.CommonName == "" { return nil, errors.New("must specify a CommonName") } if len(cfg.Usages) == 0 { @@ -365,7 +365,7 @@ func newSignedCert(cfg certutil.Config, key crypto.Signer, caCert *x509.Certific KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: cfg.Usages, } - certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey) + certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey) if err != nil { return nil, err } @@ -390,7 +390,7 @@ func encodeCertPEM(cert *x509.Certificate) []byte { return pem.EncodeToMemory(&block) } -func newFakeCertificate(t *testing.T) ([]byte, string, string) { +func newFakeCertificate(t *testing.T) (sslCert []byte, certFileName, keyFileName string) { cert, key := getFakeHostSSLCert("localhost") certFile, err := os.CreateTemp("", "crt-") @@ -398,7 +398,9 @@ func newFakeCertificate(t *testing.T) ([]byte, string, string) { t.Errorf("failed to write test key: %v", err) } - certFile.Write(cert) + if _, err := certFile.Write(cert); err != nil { + t.Errorf("failed to write cert: %v", err) + } defer certFile.Close() keyFile, err := os.CreateTemp("", "key-") @@ -406,7 +408,9 @@ func newFakeCertificate(t *testing.T) ([]byte, string, string) { t.Errorf("failed to write test key: %v", err) } - keyFile.Write(key) + if _, err := keyFile.Write(key); err != nil { + t.Errorf("failed to write key: %v", err) + } defer keyFile.Close() return cert, certFile.Name(), keyFile.Name() @@ -420,10 +424,9 @@ func dialTestServer(port string, rootCertificates ...[]byte) error { return fmt.Errorf("failed to add root certificate") } } - resp, err := tls.Dial("tcp", "localhost:"+port, &tls.Config{ + resp, err := tls.Dial("tcp", "localhost:"+port, &tls.Config{ //nolint:gosec // Ignore the gosec error in testing RootCAs: roots, }) - if err != nil { return err } @@ -470,15 +473,14 @@ func TestTLSKeyReloader(t *testing.T) { } }) - //TODO: fix - /* - // simulate watch.NewFileWatcher to call the load function - watcher.load() - t.Run("when the certificate is reloaded", func(t *testing.T) { - if err := dialTestServer(port, cert); err != nil { - t.Errorf("TLS dial should succeed, got error: %v", err) - } - }) + /*TODO: fix + // simulate watch.NewFileWatcher to call the load function + watcher.load() + t.Run("when the certificate is reloaded", func(t *testing.T) { + if err := dialTestServer(port, cert); err != nil { + t.Errorf("TLS dial should succeed, got error: %v", err) + } + }) */ }) } diff --git a/internal/nginx/main.go b/internal/nginx/main.go index ae319fe1f..fc586e9e8 100644 --- a/internal/nginx/main.go +++ b/internal/nginx/main.go @@ -62,7 +62,7 @@ var StatusPath = "/nginx_status" var StreamPort = 10247 // NewGetStatusRequest creates a new GET request to the internal NGINX status server -func NewGetStatusRequest(path string) (int, []byte, error) { +func NewGetStatusRequest(path string) (statusCode int, data []byte, err error) { url := fmt.Sprintf("http://127.0.0.1:%v%v", StatusPort, path) client := http.Client{} @@ -72,7 +72,7 @@ func NewGetStatusRequest(path string) (int, []byte, error) { } defer res.Body.Close() - data, err := io.ReadAll(res.Body) + data, err = io.ReadAll(res.Body) if err != nil { return 0, nil, err } @@ -81,7 +81,7 @@ func NewGetStatusRequest(path string) (int, []byte, error) { } // NewPostStatusRequest creates a new POST request to the internal NGINX status server -func NewPostStatusRequest(path, contentType string, data interface{}) (int, []byte, error) { +func NewPostStatusRequest(path, contentType string, data interface{}) (statusCode int, body []byte, err error) { url := fmt.Sprintf("http://127.0.0.1:%v%v", StatusPort, path) buf, err := json.Marshal(data) @@ -96,7 +96,7 @@ func NewPostStatusRequest(path, contentType string, data interface{}) (int, []by } defer res.Body.Close() - body, err := io.ReadAll(res.Body) + body, err = io.ReadAll(res.Body) if err != nil { return 0, nil, err } @@ -105,7 +105,7 @@ func NewPostStatusRequest(path, contentType string, data interface{}) (int, []by } // GetServerBlock takes an nginx.conf file and a host and tries to find the server block for that host -func GetServerBlock(conf string, host string) (string, error) { +func GetServerBlock(conf, host string) (string, error) { startMsg := fmt.Sprintf("## start server %v\n", host) endMsg := fmt.Sprintf("## end server %v", host) @@ -113,7 +113,7 @@ func GetServerBlock(conf string, host string) (string, error) { if blockStart < 0 { return "", fmt.Errorf("host %v was not found in the controller's nginx.conf", host) } - blockStart = blockStart + len(startMsg) + blockStart += len(startMsg) blockEnd := strings.Index(conf, endMsg) if blockEnd < 0 { @@ -163,7 +163,10 @@ func Version() string { // IsRunning returns true if a process with the name 'nginx' is found func IsRunning() bool { - processes, _ := ps.Processes() + processes, err := ps.Processes() + if err != nil { + klog.ErrorS(err, "unexpected error obtaining process list") + } for _, p := range processes { if p.Executable() == "nginx" { return true diff --git a/internal/nginx/maxmind.go b/internal/nginx/maxmind.go index 5aee414cd..1dc2f4d4e 100644 --- a/internal/nginx/maxmind.go +++ b/internal/nginx/maxmind.go @@ -56,7 +56,7 @@ var MaxmindRetriesTimeout = time.Second * 0 const minimumRetriesCount = 1 const ( - geoIPPath = "/etc/nginx/geoip" + geoIPPath = "/etc/ingress-controller/geoip" dbExtension = ".mmdb" maxmindURL = "https://download.maxmind.com/app/geoip_download?license_key=%v&edition_id=%v&suffix=tar.gz" @@ -101,7 +101,7 @@ func DownloadGeoLite2DB(attempts int, period time.Duration) error { var lastErr error retries := 0 - _ = wait.ExponentialBackoff(defaultRetry, func() (bool, error) { + lastErr = wait.ExponentialBackoff(defaultRetry, func() (bool, error) { var dlError error for _, dbName := range strings.Split(MaxmindEditionIDs, ",") { dlError = downloadDatabase(dbName) @@ -139,8 +139,8 @@ func createURL(mirror, licenseKey, dbName string) string { } func downloadDatabase(dbName string) error { - url := createURL(MaxmindMirror, MaxmindLicenseKey, dbName) - req, err := http.NewRequest(http.MethodGet, url, nil) + newURL := createURL(MaxmindMirror, MaxmindLicenseKey, dbName) + req, err := http.NewRequest(http.MethodGet, newURL, http.NoBody) if err != nil { return err } @@ -175,24 +175,23 @@ func downloadDatabase(dbName string) error { return err } - switch header.Typeflag { - case tar.TypeReg: + if header.Typeflag == tar.TypeReg { if !strings.HasSuffix(header.Name, mmdbFile) { continue } + return func() error { + outFile, err := os.Create(path.Join(geoIPPath, mmdbFile)) + if err != nil { + return err + } - outFile, err := os.Create(path.Join(geoIPPath, mmdbFile)) - if err != nil { - return err - } + defer outFile.Close() - defer outFile.Close() - - if _, err := io.CopyN(outFile, tarReader, header.Size); err != nil { - return err - } - - return nil + if _, err := io.CopyN(outFile, tarReader, header.Size); err != nil { + return err + } + return nil + }() } } diff --git a/internal/task/queue.go b/internal/task/queue.go index ff6b20f62..f92f2a501 100644 --- a/internal/task/queue.go +++ b/internal/task/queue.go @@ -28,9 +28,7 @@ import ( "k8s.io/client-go/util/workqueue" ) -var ( - keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc -) +var keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc // Queue manages a time work queue through an independent worker that invokes the // given sync function for every work item inserted. @@ -117,7 +115,10 @@ func (t *Queue) worker() { } ts := time.Now().UnixNano() - item := key.(Element) + item, ok := key.(Element) + if !ok { + klog.ErrorS(nil, "invalid item type", "key", key) + } if item.Timestamp != 0 && t.lastSync > item.Timestamp { klog.V(3).InfoS("skipping sync", "key", item.Key, "last", t.lastSync, "now", item.Timestamp) t.queue.Forget(key) @@ -168,7 +169,7 @@ func NewTaskQueue(syncFn func(interface{}) error) *Queue { return NewCustomTaskQueue(syncFn, nil) } -// NewCustomTaskQueue ... +// NewCustomTaskQueue creates a new custom task queue with the given sync function. func NewCustomTaskQueue(syncFn func(interface{}) error, fn func(interface{}) (interface{}, error)) *Queue { q := &Queue{ queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), diff --git a/magefiles/docker.go b/magefiles/docker.go deleted file mode 100644 index d7c7f588e..000000000 --- a/magefiles/docker.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build mage - -/* -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 main diff --git a/magefiles/yaml.go b/magefiles/exec.go similarity index 84% rename from magefiles/yaml.go rename to magefiles/exec.go index d7c7f588e..0acd557c6 100644 --- a/magefiles/yaml.go +++ b/magefiles/exec.go @@ -1,4 +1,4 @@ -//go:build mage +//go:build ignore /* Copyright 2023 The Kubernetes Authors. @@ -17,3 +17,11 @@ limitations under the License. */ package main + +import ( + "os" + + "github.com/magefile/mage/mage" +) + +func main() { os.Exit(mage.Main()) } diff --git a/magefiles/go.go b/magefiles/go.go deleted file mode 100644 index 2385b089e..000000000 --- a/magefiles/go.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build mage - -/* -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 main - -//import ( -// "github.com/magefile/mage/mg" -// "github.com/mysteriumnetwork/go-ci/commands" -//) -// -//type Go mg.Namespace -// -//// Checks for issues with go imports -//func (Go) CheckGoImports() error { -// return commands.GoImports("./...") -//} diff --git a/magefiles/go.mod b/magefiles/go.mod index 40b909600..db252f7c3 100644 --- a/magefiles/go.mod +++ b/magefiles/go.mod @@ -1,32 +1,38 @@ -module github.com/kubernetes/ingress-nginx/magefiles +module k8s.io/ingress-nginx/magefiles -go 1.20 +go 1.21.3 require ( github.com/blang/semver/v4 v4.0.0 github.com/google/go-github/v48 v48.2.0 github.com/helm/helm v2.17.0+incompatible - github.com/magefile/mage v1.14.0 + github.com/magefile/mage v1.15.0 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be + golang.org/x/oauth2 v0.12.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/stretchr/testify v1.8.1 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.7.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/gomega v1.27.10 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apimachinery v0.25.4 // indirect + k8s.io/apimachinery v0.27.6 // indirect k8s.io/helm v2.17.0+incompatible // indirect ) diff --git a/magefiles/go.sum b/magefiles/go.sum index 9c0c8ee7f..ddb7475da 100644 --- a/magefiles/go.sum +++ b/magefiles/go.sum @@ -1,105 +1,162 @@ -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 h1:aRd8M7HJVZOqn/vhOzrGcQH0lNAMkqMn+pXUYkatmcA= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE= github.com/google/go-github/v48 v48.2.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/helm/helm v2.17.0+incompatible h1:0iy95yMXrfWpwaoOA9XRP+cTvitTrq+LcJV9DvR5n1Y= github.com/helm/helm v2.17.0+incompatible/go.mod h1:ahXhuvluW4YnSL6W6hDVetZsVK8Pv4BP8OwKli7aMqo= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= -github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.2 h1:uqH7bpe+ERSiDa34FDOF7RikN6RzXgduUF8yarlZp94= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= -k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.27.6 h1:mGU8jmBq5o8mWBov+mLjdTBcU+etTE19waies4AQ6NE= +k8s.io/apimachinery v0.27.6/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= diff --git a/magefiles/mage.go b/magefiles/mage.go index 605d687a9..1dfaa4dbd 100644 --- a/magefiles/mage.go +++ b/magefiles/mage.go @@ -1,8 +1,7 @@ -//go:build ignore -// +build ignore +//go:build mage /* -Copyright 2021 The Kubernetes Authors. +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. @@ -20,10 +19,6 @@ limitations under the License. package main import ( - "github.com/magefile/mage/mage" - "os" + //mage:import + _ "k8s.io/ingress-nginx/magefiles/steps" ) - -func main() { - os.Exit(mage.Main()) -} diff --git a/magefiles/release.go b/magefiles/release.go deleted file mode 100644 index c02b88cfd..000000000 --- a/magefiles/release.go +++ /dev/null @@ -1,547 +0,0 @@ -//go:build mage - -/* -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 main - -import ( - "context" - "errors" - "fmt" - "github.com/google/go-github/v48/github" - "github.com/magefile/mage/mg" - "github.com/magefile/mage/sh" - "golang.org/x/oauth2" - "gopkg.in/yaml.v3" - "io" - "net" - "net/http" - "os" - "text/template" - - "regexp" - "strings" - "time" -) - -type Release mg.Namespace - -var INGRESS_ORG = "kubernetes" // the owner so we can test from forks -var INGRESS_REPO = "ingress-nginx" // the repo to pull from -var RELEASE_BRANCH = "main" //we only release from main -var GITHUB_TOKEN string // the Google/gogithub lib needs an PAT to access the GitHub API -var K8S_IO_ORG = "kubernetes" //the owner or organization for the k8s.io repo -var K8S_IO_REPO = "k8s.io" //the repo that holds the images yaml for production promotion -var INGRESS_REGISTRY = "registry.k8s.io" //Container registry for storage Ingress-nginx images -var KUSTOMIZE_INSTALL_VERSION = "sigs.k8s.io/kustomize/kustomize/v4@v4.5.4" //static deploys needs kustomize to generate the template - -// ingress-nginx releases start with a TAG then a cloudbuild, then a promotion through a PR, this the location of that PR -var IMAGES_YAML = "https://raw.githubusercontent.com/kubernetes/k8s.io/main/registry.k8s.io/images/k8s-staging-ingress-nginx/images.yaml" -var ctx = context.Background() // Context used for GitHub Client - -const INDEX_DOCS = "docs/deploy/index.md" //index.md has a version of the controller and needs to updated -const CHANGELOG = "Changelog.md" //Name of the changelog - -// ControllerImage - struct with info about controllers -type ControllerImage struct { - Tag string - Digest string - Registry string - Name string -} - -// IngressRelease All the information about an ingress-nginx release that gets updated -type IngressRelease struct { - ControllerVersion string - ControllerImage ControllerImage - ReleaseNote ReleaseNote - Release *github.RepositoryRelease -} - -// ReleaseNote - All the pieces of information/documents that get updated during a release -type ReleaseNote struct { - Version string - NewControllerVersion string - PreviousControllerVersion string - ControllerImages []ControllerImage - DepUpdates []string - Updates []string - HelmUpdates []string - NewHelmChartVersion string - PreviousHelmChartVersion string -} - -// IMAGES_YAML returns this data structure -type ImageYamls []ImageElement - -// ImageElement - a specific image and it's data structure the dmap is a list of shas and container versions -type ImageElement struct { - Name string `json:"name"` - Dmap map[string][]string `json:"dmap"` -} - -// init will set the GitHub token from the committers/releasers env var -func init() { - GITHUB_TOKEN = os.Getenv("GITHUB_TOKEN") -} - -// PromoteImage Creates PR into the k8s.io repo for promotion of ingress from staging to production -func (Release) PromoteImage(version, sha string) { - -} - -// Release Create a new release of ingress nginx controller -func (Release) NewRelease(version string) { - //newRelease := Release{} - - //update ingress-nginx version - //This is the step that kicks all the release process - //it is already done, so it kicks off the gcloud build of the controller images - //mg.Deps(mg.F(Tag.BumpNginx, version)) - - tag, err := getIngressNGINXVersion() - CheckIfError(err, "RELEASE Retrieving the current Ingress Nginx Version") - - Info("RELEASE Checking Current Version %s to New Version %s", tag, version) - //if the version were upgrading does not match the TAG file, lets update the TAG file - if tag[1:] != version { - Warning("RELEASE Ingress Nginx TAG %s and new version %s do not match", tag, version) - mg.Deps(mg.F(Tag.BumpNginx, fmt.Sprintf("v%s", version))) - } - - //update git controller tag controller-v$version - mg.Deps(mg.F(Tag.NewControllerTag, version)) - - //make release notes - releaseNotes, err := makeReleaseNotes(version) - CheckIfError(err, "RELEASE Creating Release Notes for version %s", version) - Info("RELEASE Release Notes %s completed", releaseNotes.Version) - - //update chart values.yaml new controller tag and image digest - releaseNotes.PreviousHelmChartVersion = currentChartVersion() - - //controller tag - updateChartValue("controller.image.tag", fmt.Sprintf("v%s", releaseNotes.Version)) - //controller digest - if releaseNotes.ControllerImages[0].Name == "controller" { - updateChartValue("controller.image.digest", releaseNotes.ControllerImages[0].Digest) - } - //controller chroot digest - if releaseNotes.ControllerImages[1].Name == "controller-chroot" { - updateChartValue("controller.image.digestChroot", releaseNotes.ControllerImages[1].Digest) - } - - //update helm chart app version - mg.Deps(mg.F(Helm.UpdateVersion, version)) - - releaseNotes.NewHelmChartVersion = currentChartVersion() - - //update helm chart release notes - updateChartReleaseNotes(releaseNotes.HelmUpdates) - - //Run helm docs update - CheckIfError(runHelmDocs(), "Error Updating Helm Docs ") - - releaseNotes.helmTemplate() - - //update static manifest - CheckIfError(updateStaticManifest(), "Error Updating Static manifests") - - ////update e2e docs - updateE2EDocs() - - //update documentation with ingress-nginx version - CheckIfError(updateIndexMD(releaseNotes.PreviousControllerVersion, releaseNotes.NewControllerVersion), "Error Updating %s", INDEX_DOCS) - - //keeping these manual for now - //git commit TODO - //make Pull Request TODO - //make release TODO - //mg.Deps(mg.F(Release.CreateRelease, version)) -} - -// the index.md doc needs the controller version updated -func updateIndexMD(old, new string) error { - Info("Updating Deploy docs with new version") - data, err := os.ReadFile(INDEX_DOCS) - CheckIfError(err, "Could not read INDEX_DOCS file %s", INDEX_DOCS) - datString := string(data) - datString = strings.Replace(datString, old, new, -1) - err = os.WriteFile(INDEX_DOCS, []byte(datString), 644) - if err != nil { - ErrorF("Could not write new %s %s", INDEX_DOCS, err) - return err - } - return nil -} - -// runs the hack/generate-deploy-scripts.sh -func updateE2EDocs() { - updates, err := sh.Output("./hack/generate-e2e-suite-doc.sh") - CheckIfError(err, "Could not run update hack script") - err = os.WriteFile("docs/e2e-tests.md", []byte(updates), 644) - CheckIfError(err, "Could not write new e2e test file ") -} - -// The static deploy scripts use kustomize to generate them, this function ensures kustomize is installed -func installKustomize() error { - Info("Install Kustomize") - var g0 = sh.RunCmd("go") - // somewhere in your main code - err := g0("install", KUSTOMIZE_INSTALL_VERSION) - if err != nil { - return err - } - return nil -} - -func updateStaticManifest() error { - CheckIfError(installKustomize(), "error installing kustomize") - //hack/generate-deploy-scripts.sh - err := sh.RunV("./hack/generate-deploy-scripts.sh") - if err != nil { - return err - } - return nil -} - -//// CreateRelease Creates a new GitHub Release -//func (Release) CreateRelease(name string) { -// releaser, err := gh_release.NewReleaser(INGRESS_ORG, INGRESS_REPO, GITHUB_TOKEN) -// CheckIfError(err, "GitHub Release Client error") -// newRelease, err := releaser.Create(fmt.Sprintf("controller-%s", name)) -// CheckIfError(err, "Create release error") -// Info("New Release: Tag %v, ID: %v", newRelease.TagName, newRelease.ID) -//} - -// Returns a GitHub client ready for use -func githubClient() *github.Client { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: GITHUB_TOKEN}, - ) - oauthClient := oauth2.NewClient(ctx, ts) - return github.NewClient(oauthClient) -} - -// LatestCommitLogs Retrieves the commit log between the latest two controller versions. -func (Release) LatestCommitLogs() { - commitLog := commitsBetweenTags() - for i, s := range commitLog { - Info("#%v Version %v", i, s) - } -} - -func commitsBetweenTags() []string { - tags := getAllControllerTags() - Info("Getting Commits between %v and %v", tags[0], tags[1]) - commitLog, err := git("log", "--full-history", "--pretty", "--oneline", fmt.Sprintf("%v..%v", tags[1], tags[0])) - - if commitLog == "" { - Warning("All Controller Tags is empty") - } - CheckIfError(err, "Retrieving Commit log") - return strings.Split(commitLog, "\n") -} - -// Generate Release Notes -func (Release) ReleaseNotes(newVersion string) error { - notes, err := makeReleaseNotes(newVersion) - CheckIfError(err, "Creating Release Notes for version %s", newVersion) - Info("Release Notes %s completed", notes.Version) - return nil -} - -func makeReleaseNotes(newVersion string) (*ReleaseNote, error) { - var newReleaseNotes = ReleaseNote{} - - newReleaseNotes.Version = newVersion - allControllerTags := getAllControllerTags() - - //new version - newReleaseNotes.NewControllerVersion = allControllerTags[0] - newControllerVersion := fmt.Sprintf("controller-v%s", newVersion) - - //the newControllerVersion should match the latest tag - if newControllerVersion != allControllerTags[0] { - return nil, errors.New(fmt.Sprintf("Generating release new version %s didnt match the current latest tag %s", newControllerVersion, allControllerTags[0])) - } - //previous version - newReleaseNotes.PreviousControllerVersion = allControllerTags[1] - - Info("New Version: %s Old Version: %s", newReleaseNotes.NewControllerVersion, newReleaseNotes.PreviousControllerVersion) - - commits := commitsBetweenTags() - - //dependency_updates - //all_updates - var allUpdates []string - var depUpdates []string - var helmUpdates []string - prRegex := regexp.MustCompile("\\(#\\d+\\)") - depBot := regexp.MustCompile("^(\\w){1,10} Bump ") - helmRegex := regexp.MustCompile("helm|chart") - for i, s := range commits { - //matches on PR - if prRegex.Match([]byte(s)) { - //matches a dependant bot update - if depBot.Match([]byte(s)) { // - Debug("#%v DEPENDABOT %v", i, s) - u := strings.SplitN(s, " ", 2) - depUpdates = append(depUpdates, u[1]) - } else { // add it to the all updates slice - Debug("#%v ALL UPDATES %v", i, s) - u := strings.SplitN(s, " ", 2) - allUpdates = append(allUpdates, u[1]) - - //helm chart updates - if helmRegex.Match([]byte(s)) { - u := strings.SplitN(s, " ", 2) - helmUpdates = append(helmUpdates, u[1]) - } - } - - } - } - helmUpdates = append(helmUpdates, fmt.Sprintf("Update Ingress-Nginx version %s", newReleaseNotes.NewControllerVersion)) - - newReleaseNotes.Updates = allUpdates - newReleaseNotes.DepUpdates = depUpdates - newReleaseNotes.HelmUpdates = helmUpdates - - //controller_image_digests - imagesYaml, err := downloadFile(IMAGES_YAML) - if err != nil { - ErrorF("Could not download file %s : %s", IMAGES_YAML, err) - return nil, err - } - Debug("%s", imagesYaml) - - data := ImageYamls{} - - err = yaml.Unmarshal([]byte(imagesYaml), &data) - if err != nil { - ErrorF("Could not unmarshal images yaml %s", err) - return nil, err - } - - //controller - controllerDigest := findImageDigest(data, "controller", newVersion) - if len(controllerDigest) == 0 { - ErrorF("Controller Digest could not be found") - return nil, errors.New("Controller digest could not be found") - } - - controllerChrootDigest := findImageDigest(data, "controller-chroot", newVersion) - if len(controllerChrootDigest) == 0 { - ErrorF("Controller Chroot Digest could not be found") - return nil, errors.New("Controller Chroot digest could not be found") - } - - Debug("Latest Controller Digest %v", controllerDigest) - Debug("Latest Controller Chroot Digest %v", controllerChrootDigest) - c1 := ControllerImage{ - Digest: controllerDigest, - Registry: INGRESS_REGISTRY, - Name: "ingress-nginx/controller", - Tag: fmt.Sprintf("v%s", newReleaseNotes.Version), - } - - c2 := ControllerImage{ - Digest: controllerChrootDigest, - Registry: INGRESS_REGISTRY, - Name: "ingress-nginx/controller-chroot", - Tag: fmt.Sprintf("v%s", newReleaseNotes.Version), - } - - newReleaseNotes.ControllerImages = append(newReleaseNotes.ControllerImages, c1) - newReleaseNotes.ControllerImages = append(newReleaseNotes.ControllerImages, c2) - Debug("New Release Controller Images %s %s", newReleaseNotes.ControllerImages[0].Digest, newReleaseNotes.ControllerImages[1].Digest) - - if DEBUG { - newReleaseNotes.printRelease() - } - - //write it all out to the changelog file - newReleaseNotes.template() - - return &newReleaseNotes, nil -} - -func (i ControllerImage) print() string { - return fmt.Sprintf("%s/%s:%s@%s", i.Registry, i.Name, i.Tag, i.Digest) -} - -func (r ReleaseNote) template() { - // Files are provided as a slice of strings. - changelogTemplate, err := os.ReadFile("Changelog.md.gotmpl") - if err != nil { - ErrorF("Could not read changelog template file %s", err) - } - Debug("ChangeLog Templates %s", string(changelogTemplate)) - t := template.Must(template.New("changelog").Parse(string(changelogTemplate))) - // create a new file - file, err := os.Create(fmt.Sprintf("changelog/Changelog-%s.md", r.Version)) - if err != nil { - ErrorF("Could not create changelog file %s", err) - } - defer file.Close() - - err = t.Execute(file, r) - if err != nil { - ErrorF("executing template:", err) - } -} - -func (r ReleaseNote) helmTemplate() { - // Files are provided as a slice of strings. - changelogTemplate, err := os.ReadFile("charts/ingress-nginx/changelog.md.gotmpl") - if err != nil { - ErrorF("Could not read changelog template file %s", err) - } - Debug("ChangeLog Templates %s", string(changelogTemplate)) - t := template.Must(template.New("changelog").Parse(string(changelogTemplate))) - // create a new file - file, err := os.Create(fmt.Sprintf("charts/ingress-nginx/changelog/Changelog-%s.md", r.NewHelmChartVersion)) - if err != nil { - ErrorF("Could not create changelog file %s", err) - } - defer file.Close() - - err = t.Execute(file, r) - if err != nil { - ErrorF("executing template:", err) - } -} - -func (r ReleaseNote) printRelease() { - Info("Release Version: %v", r.NewControllerVersion) - Info("Previous Version: %v", r.PreviousControllerVersion) - Info("Controller Image: %v", r.ControllerImages[0].print()) - Info("Controller Chroot Image: %v", r.ControllerImages[1].print()) - for i := range r.Updates { - Info("Update #%v - %v", i, r.Updates[i]) - } - for j := range r.DepUpdates { - Info("Dependabot Update #%v - %v", j, r.DepUpdates[j]) - } -} - -func findImageDigest(yaml ImageYamls, image, version string) string { - version = fmt.Sprintf("v%s", version) - Info("Searching Digest for %s:%s", image, version) - for i := range yaml { - if yaml[i].Name == image { - for k, v := range yaml[i].Dmap { - if v[0] == version { - return k - } - } - return "" - } - } - return "" -} - -func downloadFile(url string) (string, error) { - client := &http.Client{ - Transport: &http.Transport{ - DialContext: (&net.Dialer{ - Timeout: 5 * time.Second, - KeepAlive: 5 * time.Second, - }).DialContext, - TLSHandshakeTimeout: 5 * time.Second, - ResponseHeaderTimeout: 5 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - MaxIdleConnsPerHost: -1, - }, - } - resp, err := client.Get(url) - if err != nil { - return "", nil - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", errors.New(fmt.Sprintf("Could not retrieve file, response from server %s for file %s", resp.StatusCode, url)) - } - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return "", nil - } - return string(bodyBytes), nil -} - -// Latest returns latest Github Release -func (Release) Latest() error { - r, _, err := latestRelease() - if err != nil { - ErrorF("Latest Release error %s", err) - return err - } - Info("Latest Release %v", r.String()) - return nil -} - -func (Release) ReleaseByTag(tag string) error { - r, _, err := releaseByTag(tag) - if err != nil { - ErrorF("Release retrieve tag error %s", tag, err) - return err - } - - Info("Latest Release %v", r.String()) - - return nil -} - -func releaseByTag(tag string) (*github.RepositoryRelease, *github.Response, error) { - ghClient := githubClient() - return ghClient.Repositories.GetReleaseByTag(ctx, INGRESS_ORG, INGRESS_REPO, tag) -} - -func latestRelease() (*github.RepositoryRelease, *github.Response, error) { - ghClient := githubClient() - return ghClient.Repositories.GetLatestRelease(ctx, INGRESS_ORG, INGRESS_REPO) -} - -// Copy Test function to copy a release -func (Release) Copy() error { - ghClient := githubClient() - kRelease, _, err := ghClient.Repositories.GetLatestRelease(ctx, "kubernetes", "ingress-nginx") - if err != nil { - ErrorF("Get Release from kubernetes %s", err) - return err - } - - sRelease := &github.RepositoryRelease{ - TagName: kRelease.TagName, - Name: kRelease.Name, - Body: kRelease.Body, - Draft: kRelease.Draft, - Prerelease: kRelease.GenerateReleaseNotes, - DiscussionCategoryName: kRelease.DiscussionCategoryName, - GenerateReleaseNotes: kRelease.GenerateReleaseNotes, - } - - sRelease, _, err = ghClient.Repositories.CreateRelease(ctx, "strongjz", "ingress-nginx", sRelease) - if err != nil { - ErrorF("Creating Strongjz release %s", err) - return err - } - Info("Copied over Kubernetes Release %v to Strongjz %v", &kRelease.Name, &sRelease.Name) - return nil -} diff --git a/magefiles/steps/helm.go b/magefiles/steps/helm.go new file mode 100644 index 000000000..b53283f78 --- /dev/null +++ b/magefiles/steps/helm.go @@ -0,0 +1,188 @@ +/* +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 steps + +import ( + "bytes" + "fmt" + "os" + "strings" + + semver "github.com/blang/semver/v4" + "github.com/helm/helm/pkg/chartutil" + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + yamlpath "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" + "gopkg.in/yaml.v3" + + utils "k8s.io/ingress-nginx/magefiles/utils" +) + +const ( + HelmChartPath = "charts/ingress-nginx/Chart.yaml" + HelmChartValues = "charts/ingress-nginx/values.yaml" +) + +type Helm mg.Namespace + +// UpdateVersion Update Helm Version of the Chart +func (Helm) UpdateVersion(version string) { + updateVersion(version) +} + +func currentChartVersion() string { + chart, err := chartutil.LoadChartfile(HelmChartPath) + utils.CheckIfError(err, "HELM Could not Load Chart") + return chart.Version +} + +func updateVersion(version string) { + utils.Info("HELM Reading File %v", HelmChartPath) + + chart, err := chartutil.LoadChartfile(HelmChartPath) + utils.CheckIfError(err, "HELM Could not Load Chart") + + // Get the current tag + // appVersionV, err := getIngressNGINXVersion() + // utils.CheckIfError(err, "HELM Issue Retrieving the Current Ingress Nginx Version") + + // remove the v from TAG + appVersion := version + + utils.Info("HELM Ingress-Nginx App Version: %s Chart AppVersion: %s", appVersion, chart.AppVersion) + if appVersion == chart.AppVersion { + utils.Warning("HELM Ingress NGINX Version didnt change Ingress-Nginx App Version: %s Chart AppVersion: %s", appVersion, chart.AppVersion) + return + } + + controllerSemVer, err := semver.Parse(version) + utils.CheckIfError(err, "error parsing semver of new app") + isPreRelease := len(controllerSemVer.Pre) > 0 + oldControllerSemVer, err := semver.Parse(chart.AppVersion) + utils.CheckIfError(err, "error parsing semver of old chart") + isBreakingChange := controllerSemVer.Major > oldControllerSemVer.Major || controllerSemVer.Minor > oldControllerSemVer.Minor + + // Update the helm chart + chart.AppVersion = appVersion + cTag, err := semver.Make(chart.Version) + utils.CheckIfError(err, "HELM Creating Chart Version: %v", err) + + incrFunc := cTag.IncrementPatch + if isBreakingChange { + cTag.Patch = 0 + incrFunc = cTag.IncrementMinor + + } + + if isPreRelease { + chart.Annotations["artifacthub.io/prerelease"] = "true" + cTag.Pre = controllerSemVer.Pre + } + + if err = incrFunc(); err != nil { + utils.ErrorF("HELM Incrementing Chart Version: %v", err) + os.Exit(1) + } + chart.Version = cTag.String() + utils.Debug("HELM Updated Chart Version: %v", chart.Version) + + err = chartutil.SaveChartfile(HelmChartPath, chart) + utils.CheckIfError(err, "HELM Saving new Chart") +} + +func updateChartReleaseNotes(releasesNotes []string) { + utils.Info("HELM Updating the Chart Release notes") + chart, err := chartutil.LoadChartfile(HelmChartPath) + utils.CheckIfError(err, "HELM Could not Load Chart to update release notes %s", HelmChartPath) + for i := range releasesNotes { + releasesNotes[i] = fmt.Sprintf("- %q", releasesNotes[i]) + } + releaseNoteString := strings.Join(releasesNotes, "\n") + utils.Info("HELM Release note string %s", releaseNoteString) + chart.Annotations["artifacthub.io/changes"] = releaseNoteString + err = chartutil.SaveChartfile(HelmChartPath, chart) + utils.CheckIfError(err, "HELM Saving updated release notes for Chart") +} + +// UpdateChartValue Updates the Helm ChartValue +func (Helm) UpdateChartValue(key, value string) { + updateChartValue(key, value) +} + +func updateChartValue(key, value string) { + utils.Info("HELM Updating Chart %s %s:%s", HelmChartValues, key, value) + + // read current values.yaml + data, err := os.ReadFile(HelmChartValues) + utils.CheckIfError(err, "HELM Could not Load Helm Chart Values files %s", HelmChartValues) + + // var valuesStruct IngressChartValue + var n yaml.Node + utils.CheckIfError(yaml.Unmarshal(data, &n), "HELM Could not Unmarshal %s", HelmChartValues) + + // update value + // keyParse := parsePath(key) + p, err := yamlpath.NewPath(key) + utils.CheckIfError(err, "HELM cannot create path") + + q, err := p.Find(&n) + utils.CheckIfError(err, "HELM unexpected error finding path") + + for _, i := range q { + utils.Info("HELM Found %s at %s", i.Value, key) + i.Value = value + utils.Info("HELM Updated %s at %s", i.Value, key) + } + + //// write to file + var b bytes.Buffer + yamlEncoder := yaml.NewEncoder(&b) + yamlEncoder.SetIndent(2) + err = yamlEncoder.Encode(&n) + utils.CheckIfError(err, "HELM Could not Marshal new Values file") + err = os.WriteFile(HelmChartValues, b.Bytes(), 0o644) + utils.CheckIfError(err, "HELM Could not write new Values file to %s", HelmChartValues) + + utils.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 { + return err + } + err = sh.RunV("helm-docs", "--chart-search-root=${PWD}/charts") + if err != nil { + return err + } + return nil +} + +func installHelmDocs() error { + utils.Info("HELM Install HelmDocs") + g0 := sh.RunCmd("go") + + err := g0("install", "github.com/norwoodj/helm-docs/cmd/helm-docs@v1.11.0") + if err != nil { + return err + } + return nil +} diff --git a/magefiles/steps/release.go b/magefiles/steps/release.go new file mode 100644 index 000000000..1ed09850f --- /dev/null +++ b/magefiles/steps/release.go @@ -0,0 +1,396 @@ +/* +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 steps + +import ( + "context" + "errors" + "fmt" + "os" + "regexp" + "strings" + + "github.com/google/go-github/v48/github" + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "golang.org/x/oauth2" + "gopkg.in/yaml.v3" + + utils "k8s.io/ingress-nginx/magefiles/utils" +) + +type Release mg.Namespace + +var ( + INGRESS_ORG = "kubernetes" // the owner so we can test from forks + INGRESS_REPO = "ingress-nginx" // the repo to pull from + RELEASE_BRANCH = "main" // we only release from main + GITHUB_TOKEN string // the Google/gogithub lib needs an PAT to access the GitHub API + K8S_IO_ORG = "kubernetes" // the owner or organization for the k8s.io repo + K8S_IO_REPO = "k8s.io" // the repo that holds the images yaml for production promotion + INGRESS_REGISTRY = "registry.k8s.io" // Container registry for storage Ingress-nginx images + KUSTOMIZE_INSTALL_VERSION = "sigs.k8s.io/kustomize/kustomize/v4@v4.5.4" // static deploys needs kustomize to generate the template +) + +// ingress-nginx releases start with a TAG then a cloudbuild, then a promotion through a PR, this the location of that PR +var ( + IMAGES_YAML = "https://raw.githubusercontent.com/kubernetes/k8s.io/main/registry.k8s.io/images/k8s-staging-ingress-nginx/images.yaml" + ctx = context.Background() // Context used for GitHub Client +) + +const ( + INDEX_DOCS = "docs/deploy/index.md" // index.md has a version of the controller and needs to updated + CHANGELOG = "Changelog.md" // Name of the changelog +) + +// init will set the GitHub token from the committers/releasers env var +func init() { + GITHUB_TOKEN = os.Getenv("GITHUB_TOKEN") +} + +// Release Create a new release of ingress nginx controller +func (Release) NewRelease(version string) { + newRelease(version, "") +} + +func (Release) NewReleaseFromOld(version, oldversion string) { + newRelease(version, oldversion) +} + +func (Release) E2EDocs() { + e2edocs, err := utils.GenerateE2EDocs() + utils.CheckIfError(err, "error on template") + err = os.WriteFile("docs/e2e-tests.md", []byte(e2edocs), 644) + utils.CheckIfError(err, "Could not write new e2e test file ") +} + +func newRelease(version, oldversion string) { + // newRelease := Release{} + + // update ingress-nginx version + // This is the step that kicks all the release process + // it is already done, so it kicks off the gcloud build of the controller images + // mg.Deps(mg.F(Tag.BumpNginx, version)) + + tag, err := getIngressNGINXVersion() + utils.CheckIfError(err, "RELEASE Retrieving the current Ingress Nginx Version") + + utils.Info("RELEASE Checking Current Version %s to New Version %s", tag, version) + // if the version were upgrading does not match the TAG file, lets update the TAG file + if tag[1:] != version { + utils.Warning("RELEASE Ingress Nginx TAG %s and new version %s do not match", tag, version) + mg.Deps(mg.F(Tag.BumpNginx, fmt.Sprintf("v%s", version))) + } + + // update git controller tag controller-v$version + mg.Deps(mg.F(Tag.NewControllerTag, version)) + + // make release notes + releaseNotes, err := makeReleaseNotes(version, oldversion) + utils.CheckIfError(err, "RELEASE Creating Release Notes for version %s", version) + utils.Info("RELEASE Release Notes %s completed", releaseNotes.Version) + + // update chart values.yaml new controller tag and image digest + releaseNotes.PreviousHelmChartVersion = currentChartVersion() + + // controller tag + updateChartValue("controller.image.tag", fmt.Sprintf("v%s", releaseNotes.Version)) + utils.Debug("releaseNotes.ControllerImages[0].Name %s", releaseNotes.ControllerImages[0].Name) + utils.Debug("releaseNotes.ControllerImages[1].Name %s", releaseNotes.ControllerImages[1].Name) + // controller digest + if releaseNotes.ControllerImages[0].Name == "ingress-nginx/controller" { + utils.Debug("Updating Chart Value %s with %s", "controller.image.digest", releaseNotes.ControllerImages[0].Digest) + updateChartValue("controller.image.digest", releaseNotes.ControllerImages[0].Digest) + } + // controller chroot digest + if releaseNotes.ControllerImages[1].Name == "ingress-nginx/controller-chroot" { + utils.Debug("Updating Chart Value %s with %s", "controller.image.digestChroot", releaseNotes.ControllerImages[1].Digest) + updateChartValue("controller.image.digestChroot", releaseNotes.ControllerImages[1].Digest) + } + + // update helm chart app version + mg.Deps(mg.F(Helm.UpdateVersion, version)) + + releaseNotes.NewHelmChartVersion = currentChartVersion() + + // update helm chart release notes + updateChartReleaseNotes(releaseNotes.HelmUpdates) + + // Run helm docs update + utils.CheckIfError(runHelmDocs(), "Error Updating Helm Docs ") + + releaseNotes.HelmTemplate() + + // update static manifest + utils.CheckIfError(updateStaticManifest(), "Error Updating Static manifests") + + ////update e2e docs + mg.Deps(mg.F(Release.E2EDocs)) + + // update documentation with ingress-nginx version + utils.CheckIfError(updateIndexMD(releaseNotes.PreviousControllerVersion, releaseNotes.NewControllerVersion), "Error Updating %s", INDEX_DOCS) + + // keeping these manual for now + // git commit TODO + // make Pull Request TODO + // make release TODO + // mg.Deps(mg.F(Release.CreateRelease, version)) +} + +// the index.md doc needs the controller version updated +func updateIndexMD(old, new string) error { + utils.Info("Updating Deploy docs with new version") + data, err := os.ReadFile(INDEX_DOCS) + utils.CheckIfError(err, "Could not read INDEX_DOCS file %s", INDEX_DOCS) + datString := string(data) + datString = strings.Replace(datString, old, new, -1) + err = os.WriteFile(INDEX_DOCS, []byte(datString), 644) + if err != nil { + utils.ErrorF("Could not write new %s %s", INDEX_DOCS, err) + return err + } + return nil +} + +// The static deploy scripts use kustomize to generate them, this function ensures kustomize is installed +func installKustomize() error { + utils.Info("Install Kustomize") + g0 := sh.RunCmd("go") + // somewhere in your main code + err := g0("install", KUSTOMIZE_INSTALL_VERSION) + if err != nil { + return err + } + return nil +} + +func updateStaticManifest() error { + utils.CheckIfError(installKustomize(), "error installing kustomize") + // hack/generate-deploy-scripts.sh + err := sh.RunV("./hack/generate-deploy-scripts.sh") + if err != nil { + return err + } + return nil +} + +//// CreateRelease Creates a new GitHub Release +//func (Release) CreateRelease(name string) { +// releaser, err := gh_release.NewReleaser(INGRESS_ORG, INGRESS_REPO, GITHUB_TOKEN) +// utils.CheckIfError(err, "GitHub Release Client error") +// newRelease, err := releaser.Create(fmt.Sprintf("controller-%s", name)) +// utils.CheckIfError(err, "Create release error") +// utils.Info("New Release: Tag %v, ID: %v", newRelease.TagName, newRelease.ID) +//} + +// Returns a GitHub client ready for use +func githubClient() *github.Client { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: GITHUB_TOKEN}, + ) + oauthClient := oauth2.NewClient(ctx, ts) + return github.NewClient(oauthClient) +} + +// LatestCommitLogs Retrieves the commit log between the latest two controller versions. +func (Release) LatestCommitLogs() { + commitLog := commitsBetweenTags("", "") + for i, s := range commitLog { + utils.Info("#%v Version %v", i, s) + } +} + +func commitsBetweenTags(newversion, oldversion string) []string { + + var newTag, oldTag string + tags := getAllControllerTags() + newTag, oldTag = tags[0], tags[1] + if newversion != "" { + newTag = newversion + } + if oldversion != "" { + oldTag = oldversion + } + + utils.Info("Getting Commits between %v and %v", newTag, oldTag) + commitLog, err := git("log", "--full-history", "--pretty", "--oneline", fmt.Sprintf("%v..%v", oldTag, newTag)) + + if commitLog == "" { + utils.Warning("All Controller Tags is empty") + } + utils.CheckIfError(err, "Retrieving Commit log") + return strings.Split(commitLog, "\n") +} + +// Generate Release Notes +func (Release) ReleaseNotes(newVersion string) error { + notes, err := makeReleaseNotes(newVersion, "") + utils.CheckIfError(err, "Creating Release Notes for version %s", newVersion) + utils.Info("Release Notes %s completed", notes.Version) + return nil +} + +func makeReleaseNotes(newVersion, oldVersion string) (*utils.ReleaseNote, error) { + newReleaseNotes := utils.ReleaseNote{} + + newReleaseNotes.Version = newVersion + allControllerTags := getAllControllerTags() + + // new version + newReleaseNotes.NewControllerVersion = allControllerTags[0] + newControllerVersion := fmt.Sprintf("controller-v%s", newVersion) + + // the newControllerVersion should match the latest tag + if newControllerVersion != allControllerTags[0] { + return nil, errors.New(fmt.Sprintf("Generating release new version %s didnt match the current latest tag %s", newControllerVersion, allControllerTags[0])) + } + // previous version + newReleaseNotes.PreviousControllerVersion = allControllerTags[1] + if oldVersion != "" { + newReleaseNotes.PreviousControllerVersion = oldVersion + } + + utils.Info("New Version: %s Old Version: %s", newReleaseNotes.NewControllerVersion, newReleaseNotes.PreviousControllerVersion) + + commits := commitsBetweenTags(newReleaseNotes.NewControllerVersion, newReleaseNotes.PreviousControllerVersion) + + // dependency_updates + // all_updates + var allUpdates []string + var depUpdates []string + var helmUpdates []string + prRegex := regexp.MustCompile("\\(#\\d+\\)") + depBot := regexp.MustCompile("^(\\w){1,10} Bump ") + helmRegex := regexp.MustCompile("helm|chart") + for i, s := range commits { + // matches on PR + if prRegex.Match([]byte(s)) { + // matches a dependant bot update + if depBot.Match([]byte(s)) { // + utils.Debug("#%v DEPENDABOT %v", i, s) + u := strings.SplitN(s, " ", 2) + depUpdates = append(depUpdates, u[1]) + } else { // add it to the all updates slice + utils.Debug("#%v ALL UPDATES %v", i, s) + u := strings.SplitN(s, " ", 2) + allUpdates = append(allUpdates, u[1]) + + // helm chart updates + if helmRegex.Match([]byte(s)) { + u := strings.SplitN(s, " ", 2) + helmUpdates = append(helmUpdates, u[1]) + } + } + } + } + helmUpdates = append(helmUpdates, fmt.Sprintf("Update Ingress-Nginx version %s", newReleaseNotes.NewControllerVersion)) + + newReleaseNotes.Updates = allUpdates + newReleaseNotes.DepUpdates = depUpdates + newReleaseNotes.HelmUpdates = helmUpdates + + // controller_image_digests + imagesYaml, err := utils.DownloadFile(IMAGES_YAML) + if err != nil { + utils.ErrorF("Could not download file %s : %s", IMAGES_YAML, err) + return nil, err + } + utils.Debug("%s", imagesYaml) + + data := utils.ImageYamls{} + + err = yaml.Unmarshal([]byte(imagesYaml), &data) + if err != nil { + utils.ErrorF("Could not unmarshal images yaml %s", err) + return nil, err + } + + // controller + controllerDigest := utils.FindImageDigest(data, "controller", newVersion) + if len(controllerDigest) == 0 { + utils.ErrorF("Controller Digest could not be found") + return nil, errors.New("Controller digest could not be found") + } + + controllerChrootDigest := utils.FindImageDigest(data, "controller-chroot", newVersion) + if len(controllerChrootDigest) == 0 { + utils.ErrorF("Controller Chroot Digest could not be found") + return nil, errors.New("Controller Chroot digest could not be found") + } + + utils.Debug("Latest Controller Digest %v", controllerDigest) + utils.Debug("Latest Controller Chroot Digest %v", controllerChrootDigest) + c1 := utils.ControllerImage{ + Digest: controllerDigest, + Registry: INGRESS_REGISTRY, + Name: "ingress-nginx/controller", + Tag: fmt.Sprintf("v%s", newReleaseNotes.Version), + } + + c2 := utils.ControllerImage{ + Digest: controllerChrootDigest, + Registry: INGRESS_REGISTRY, + Name: "ingress-nginx/controller-chroot", + Tag: fmt.Sprintf("v%s", newReleaseNotes.Version), + } + + newReleaseNotes.ControllerImages = append(newReleaseNotes.ControllerImages, c1) + newReleaseNotes.ControllerImages = append(newReleaseNotes.ControllerImages, c2) + utils.Debug("New Release Controller Images %s %s", newReleaseNotes.ControllerImages[0].Digest, newReleaseNotes.ControllerImages[1].Digest) + + if utils.DEBUG { + newReleaseNotes.PrintRelease() + } + + // write it all out to the changelog file + newReleaseNotes.Template() + + return &newReleaseNotes, nil +} + +// Latest returns latest Github Release +func (Release) Latest() error { + r, _, err := latestRelease() + if err != nil { + utils.ErrorF("Latest Release error %s", err) + return err + } + utils.Info("Latest Release %v", r.String()) + return nil +} + +func (Release) ReleaseByTag(tag string) error { + r, _, err := releaseByTag(tag) + if err != nil { + utils.ErrorF("Release retrieve tag %s error %s", tag, err) + return err + } + + utils.Info("Latest Release %v", r.String()) + + return nil +} + +func releaseByTag(tag string) (*github.RepositoryRelease, *github.Response, error) { + ghClient := githubClient() + return ghClient.Repositories.GetReleaseByTag(ctx, INGRESS_ORG, INGRESS_REPO, tag) +} + +func latestRelease() (*github.RepositoryRelease, *github.Response, error) { + ghClient := githubClient() + return ghClient.Repositories.GetLatestRelease(ctx, INGRESS_ORG, INGRESS_REPO) +} diff --git a/magefiles/tags.go b/magefiles/steps/tags.go similarity index 64% rename from magefiles/tags.go rename to magefiles/steps/tags.go index 02bbbcb37..e3c811f8c 100644 --- a/magefiles/tags.go +++ b/magefiles/steps/tags.go @@ -1,5 +1,3 @@ -//go:build mage - /* Copyright 2023 The Kubernetes Authors. @@ -16,15 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package steps import ( "fmt" + "os" + "strings" + semver "github.com/blang/semver/v4" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" - "os" - "strings" + + utils "k8s.io/ingress-nginx/magefiles/utils" ) type Tag mg.Namespace @@ -34,35 +35,35 @@ var git = sh.OutCmd("git") // Nginx returns the ingress-nginx current version func (Tag) Nginx() { tag, err := getIngressNGINXVersion() - CheckIfError(err, "") + utils.CheckIfError(err, "") fmt.Printf("%v", tag) } func getIngressNGINXVersion() (string, error) { dat, err := os.ReadFile("TAG") - CheckIfError(err, "Could not read TAG file") + utils.CheckIfError(err, "Could not read TAG file") datString := string(dat) - //remove newline + // remove newline datString = strings.Replace(datString, "\n", "", -1) return datString, nil } func checkSemVer(currentVersion, newVersion string) bool { - Info("Checking Sem Ver between current %s and new %s", currentVersion, newVersion) + utils.Info("Checking Sem Ver between current %s and new %s", currentVersion, newVersion) cVersion, err := semver.Make(currentVersion[1:]) if err != nil { - ErrorF("TAG Error Current Tag %v Making Semver : %v", currentVersion[1:], err) + utils.ErrorF("TAG Error Current Tag %v Making Semver : %v", currentVersion[1:], err) return false } nVersion, err := semver.Make(newVersion) if err != nil { - ErrorF("TAG %v Error Making Semver %v \n", newVersion, err) + utils.ErrorF("TAG %v Error Making Semver %v \n", newVersion, err) return false } err = nVersion.Validate() if err != nil { - ErrorF("TAG %v not a valid Semver %v \n", newVersion, err) + utils.ErrorF("TAG %v not a valid Semver %v \n", newVersion, err) return false } @@ -70,10 +71,10 @@ func checkSemVer(currentVersion, newVersion string) bool { //0 if newVersion == currentVersion //-1 if newVersion < currentVersion //+1 if newVersion > currentVersion. - Info("TAG Comparing Old %s to New %s", cVersion.String(), nVersion.String()) + utils.Info("TAG Comparing Old %s to New %s", cVersion.String(), nVersion.String()) comp := nVersion.Compare(cVersion) if comp <= 0 { - Warning("SemVer:%v is not an update\n", newVersion) + utils.Warning("SemVer:%v is not an update\n", newVersion) return false } return true @@ -81,29 +82,29 @@ func checkSemVer(currentVersion, newVersion string) bool { // BumpNginx will update the nginx TAG func (Tag) BumpNginx(newTag string) { - Info("TAG BumpNginx version %v", newTag) + utils.Info("TAG BumpNginx version %v", newTag) currentTag, err := getIngressNGINXVersion() - CheckIfError(err, "Getting Ingress-nginx Version") + utils.CheckIfError(err, "Getting Ingress-nginx Version") bump(currentTag, newTag) } func bump(currentTag, newTag string) { - //check if semver is valid + // check if semver is valid if !checkSemVer(currentTag, newTag) { - ErrorF("ERROR: Semver is not valid %v", newTag) + utils.ErrorF("ERROR: Semver is not valid %v", newTag) os.Exit(1) } - Info("Updating Tag %v to %v", currentTag, newTag) - err := os.WriteFile("TAG", []byte(newTag), 0666) - CheckIfError(err, "Error Writing New Tag File") + utils.Info("Updating Tag %v to %v", currentTag, newTag) + err := os.WriteFile("TAG", []byte(newTag), 0o666) + utils.CheckIfError(err, "Error Writing New Tag File") } // Git Returns the latest git tag func (Tag) Git() { tag, err := getGitTag() - CheckIfError(err, "Retrieving Git Tag") - Info("Git tag: %v", tag) + utils.CheckIfError(err, "Retrieving Git Tag") + utils.Info("Git tag: %v", tag) } func getGitTag() (string, error) { @@ -112,10 +113,10 @@ func getGitTag() (string, error) { // ControllerTag Creates a new Git Tag for the ingress controller func (Tag) NewControllerTag(version string) { - Info("Create Ingress Nginx Controller Tag v%s", version) + utils.Info("Create Ingress Nginx Controller Tag v%s", version) tag, err := controllerTag(version) - CheckIfError(err, "Creating git tag") - Debug("Git Tag: %s", tag) + utils.CheckIfError(err, "Creating git tag") + utils.Debug("Git Tag: %s", tag) } func controllerTag(version string) (string, error) { @@ -125,22 +126,22 @@ func controllerTag(version string) (string, error) { func (Tag) AllControllerTags() { tags := getAllControllerTags() for i, s := range tags { - Info("#%v Version %v", i, s) + utils.Info("#%v Version %v", i, s) } } func getAllControllerTags() []string { allControllerTags, err := git("tag", "-l", "--sort=-v:refname", "controller-v*") - CheckIfError(err, "Retrieving git tags") + utils.CheckIfError(err, "Retrieving git tags") if !sh.CmdRan(err) { - Warning("Issue Running Command") + utils.Warning("Issue Running Command") } if allControllerTags == "" { - Warning("All Controller Tags is empty") + utils.Warning("All Controller Tags is empty") } - Debug("Controller Tags: %v", allControllerTags) + utils.Debug("Controller Tags: %v", allControllerTags) temp := strings.Split(allControllerTags, "\n") - Debug("There are %v controller tags", len(temp)) + utils.Debug("There are %v controller tags", len(temp)) return temp } diff --git a/magefiles/common.go b/magefiles/utils/common.go similarity index 73% rename from magefiles/common.go rename to magefiles/utils/common.go index c5c33f989..19cad7bba 100644 --- a/magefiles/common.go +++ b/magefiles/utils/common.go @@ -1,5 +1,3 @@ -//go:build mage - /* Copyright 2023 The Kubernetes Authors. @@ -16,10 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package utils import ( + "errors" "fmt" + "io" + "net" + "net/http" "os" "strings" "time" @@ -80,3 +82,32 @@ func Debug(format string, args ...interface{}) { func ErrorF(format string, args ...interface{}) { fmt.Printf("\x1b[31;1m%s ERROR: %s\x1b[0m\n", timeStamp(), fmt.Sprintf(format, args...)) } + +func DownloadFile(url string) (string, error) { + client := &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: 5 * time.Second, + KeepAlive: 5 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConnsPerHost: -1, + }, + } + resp, err := client.Get(url) + if err != nil { + return "", nil + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", errors.New(fmt.Sprintf("could not retrieve file, response from server %d for file %s", resp.StatusCode, url)) + } + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return "", nil + } + return string(bodyBytes), nil +} diff --git a/magefiles/utils/controller.go b/magefiles/utils/controller.go new file mode 100644 index 000000000..c8e250a36 --- /dev/null +++ b/magefiles/utils/controller.go @@ -0,0 +1,68 @@ +/* +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 utils + +import ( + "fmt" + + "github.com/google/go-github/v48/github" +) + +// ControllerImage - struct with info about controllers +type ControllerImage struct { + Tag string + Digest string + Registry string + Name string +} + +// IngressRelease All the information about an ingress-nginx release that gets updated +type IngressRelease struct { + ControllerVersion string + ControllerImage ControllerImage + ReleaseNote ReleaseNote + Release *github.RepositoryRelease +} + +// IMAGES_YAML returns this data structure +type ImageYamls []ImageElement + +// ImageElement - a specific image and it's data structure the dmap is a list of shas and container versions +type ImageElement struct { + Name string `json:"name"` + Dmap map[string][]string `json:"dmap"` +} + +func (i ControllerImage) print() string { + return fmt.Sprintf("%s/%s:%s@%s", i.Registry, i.Name, i.Tag, i.Digest) +} + +func FindImageDigest(yaml ImageYamls, image, version string) string { + version = fmt.Sprintf("v%s", version) + Info("Searching Digest for %s:%s", image, version) + for i := range yaml { + if yaml[i].Name == image { + for k, v := range yaml[i].Dmap { + if v[0] == version { + return k + } + } + return "" + } + } + return "" +} diff --git a/magefiles/utils/e2edocs.go b/magefiles/utils/e2edocs.go new file mode 100644 index 000000000..2edc2ca40 --- /dev/null +++ b/magefiles/utils/e2edocs.go @@ -0,0 +1,140 @@ +/* +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 utils + +import ( + "bufio" + "bytes" + "embed" + "fmt" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" + "text/template" + + "slices" +) + +//go:embed templates/e2edocs.tpl +var tplContent embed.FS + +var ( + skipFiles = []string{ + "test/e2e/framework/framework.go", + "test/e2e/e2e.go", + "test/e2e/e2e_test.go", + } +) + +const ( + testDir = "test/e2e" + describe = "Describe" + URL = "https://github.com/kubernetes/ingress-nginx/tree/main/" +) + +var ( + betweenquotes = regexp.MustCompile(`("|\')(?P.*)("|\')`) +) + +type E2ETemplate struct { + URL string + Tests []string +} + +func getDescription(linetext, path, url string, lineN int, isDescription bool) string { + var descriptionLine string + prefix := "-" + if isDescription { + prefix = "###" + } + + matches := betweenquotes.FindStringSubmatch(linetext) + contentIndex := betweenquotes.SubexpIndex("TestDescription") + if len(matches) < 2 || contentIndex == -1 { + return "" + } + + fileName := fmt.Sprintf("%s/%s", url, path) + descriptionLine = fmt.Sprintf("%s [%s](%s#L%d)", prefix, matches[contentIndex], fileName, lineN) + + return descriptionLine +} + +func containsGinkgoTest(line string) bool { + if !strings.Contains(line, describe) && !strings.Contains(line, "It") { + return false + } + return strings.Contains(line, "func() {") +} + +func (t *E2ETemplate) walkE2eDir(path string, d fs.DirEntry, errAggregated error) error { + if errAggregated != nil { + return errAggregated + } + // Remove ignored files or non .go files + if d.IsDir() || slices.Contains(skipFiles, path) || !strings.HasSuffix(path, ".go") { + return nil + } + + content, err := os.ReadFile(path) + if err != nil { + return err + } + + fileScanner := bufio.NewScanner(bytes.NewReader(content)) + + fileScanner.Split(bufio.ScanLines) + + tests := make([]string, 0) + var lineN = 0 + for fileScanner.Scan() { + lineN = lineN + 1 + if !containsGinkgoTest(fileScanner.Text()) { + continue + } + + line := getDescription(fileScanner.Text(), path, t.URL, lineN, strings.Contains(fileScanner.Text(), describe)) + if line != "" { + tests = append(tests, line) + } + } + t.Tests = append(t.Tests, tests...) + return nil +} + +func GenerateE2EDocs() (string, error) { + e2etpl := &E2ETemplate{URL: URL} + err := filepath.WalkDir(testDir, e2etpl.walkE2eDir) + if err != nil { + return "", err + } + + tmpl, err := template.New("e2edocs.tpl").ParseFS(tplContent, "templates/e2edocs.tpl") + if err != nil { + return "", fmt.Errorf("error parsing the template file: %s", err) + } + + tplBuff := new(bytes.Buffer) + err = tmpl.Execute(tplBuff, e2etpl) + if err != nil { + return "", err + } + return tplBuff.String(), nil + +} diff --git a/magefiles/helm.go b/magefiles/utils/helm.go similarity index 50% rename from magefiles/helm.go rename to magefiles/utils/helm.go index 585a93413..dea68caab 100644 --- a/magefiles/helm.go +++ b/magefiles/utils/helm.go @@ -1,5 +1,3 @@ -//go:build mage - /* Copyright 2023 The Kubernetes Authors. @@ -16,183 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main - -import ( - "bytes" - "fmt" - "os" - "strings" - - semver "github.com/blang/semver/v4" - "github.com/helm/helm/pkg/chartutil" - "github.com/magefile/mage/mg" - "github.com/magefile/mage/sh" - yamlpath "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" - "gopkg.in/yaml.v3" -) - -const HelmChartPath = "charts/ingress-nginx/Chart.yaml" -const HelmChartValues = "charts/ingress-nginx/values.yaml" - -type Helm mg.Namespace - -// UpdateAppVersion Updates the Helm App Version of Ingress Nginx Controller -func (Helm) UpdateAppVersion() { - updateAppVersion() -} - -func updateAppVersion() { - -} - -// UpdateVersion Update Helm Version of the Chart -func (Helm) UpdateVersion(version string) { - updateVersion(version) -} - -func currentChartVersion() string { - chart, err := chartutil.LoadChartfile(HelmChartPath) - CheckIfError(err, "HELM Could not Load Chart") - return chart.Version -} - -func currentChartAppVersion() string { - chart, err := chartutil.LoadChartfile(HelmChartPath) - CheckIfError(err, "HELM Could not Load Chart") - return chart.AppVersion -} - -func updateVersion(version string) { - Info("HELM Reading File %v", HelmChartPath) - - chart, err := chartutil.LoadChartfile(HelmChartPath) - CheckIfError(err, "HELM Could not Load Chart") - - //Get the current tag - //appVersionV, err := getIngressNGINXVersion() - //CheckIfError(err, "HELM Issue Retrieving the Current Ingress Nginx Version") - - //remove the v from TAG - appVersion := version - - Info("HELM Ingress-Nginx App Version: %s Chart AppVersion: %s", appVersion, chart.AppVersion) - if appVersion == chart.AppVersion { - Warning("HELM Ingress NGINX Version didnt change Ingress-Nginx App Version: %s Chart AppVersion: %s", appVersion, chart.AppVersion) - return - } - - //Update the helm chart - chart.AppVersion = appVersion - cTag, err := semver.Make(chart.Version) - CheckIfError(err, "HELM Creating Chart Version: %v", err) - - if err = cTag.IncrementPatch(); err != nil { - ErrorF("HELM Incrementing Chart Version: %v", err) - os.Exit(1) - } - chart.Version = cTag.String() - Debug("HELM Updated Chart Version: %v", chart.Version) - - err = chartutil.SaveChartfile(HelmChartPath, chart) - CheckIfError(err, "HELM Saving new Chart") -} - -func updateChartReleaseNotes(releasesNotes []string) { - Info("HELM Updating the Chart Release notes") - chart, err := chartutil.LoadChartfile(HelmChartPath) - CheckIfError(err, "HELM Could not Load Chart to update release notes %s", HelmChartPath) - var releaseNoteString string - for i := range releasesNotes { - releaseNoteString = fmt.Sprintf("%s - \"%s\"\n", releaseNoteString, releasesNotes[i]) - } - Info("HELM Release note string %s", releaseNoteString) - chart.Annotations["artifacthub.io/changes"] = releaseNoteString - err = chartutil.SaveChartfile(HelmChartPath, chart) - CheckIfError(err, "HELM Saving updated release notes for Chart") -} - -func UpdateChartChangelog() { - -} - -// UpdateChartValue Updates the Helm ChartValue -func (Helm) UpdateChartValue(key, value string) { - updateChartValue(key, value) -} - -func updateChartValue(key, value string) { - Info("HELM Updating Chart %s %s:%s", HelmChartValues, key, value) - - //read current values.yaml - data, err := os.ReadFile(HelmChartValues) - CheckIfError(err, "HELM Could not Load Helm Chart Values files %s", HelmChartValues) - - //var valuesStruct IngressChartValue - var n yaml.Node - CheckIfError(yaml.Unmarshal(data, &n), "HELM Could not Unmarshal %s", HelmChartValues) - - //update value - //keyParse := parsePath(key) - p, err := yamlpath.NewPath(key) - CheckIfError(err, "HELM cannot create path") - - q, err := p.Find(&n) - CheckIfError(err, "HELM unexpected error finding path") - - for _, i := range q { - Info("HELM Found %s at %s", i.Value, key) - i.Value = value - Info("HELM Updated %s at %s", i.Value, key) - } - - //// write to file - var b bytes.Buffer - yamlEncoder := yaml.NewEncoder(&b) - yamlEncoder.SetIndent(2) - err = yamlEncoder.Encode(&n) - CheckIfError(err, "HELM Could not Marshal new Values file") - err = os.WriteFile(HelmChartValues, b.Bytes(), 0644) - CheckIfError(err, "HELM Could not write new Values file to %s", HelmChartValues) - - 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 { - return err - } - err = sh.RunV("helm-docs", "--chart-search-root=${PWD}/charts") - if err != nil { - return err - } - return nil -} - -func installHelmDocs() error { - Info("HELM Install HelmDocs") - var g0 = sh.RunCmd("go") - - err := g0("install", "github.com/norwoodj/helm-docs/cmd/helm-docs@v1.11.0") - if err != nil { - return err - } - return nil -} -func parsePath(key string) []string { return strings.Split(key, ".") } - -func updateHelmDocs() { - -} +package utils type IngressChartValue struct { - CommonLabels struct { - } `yaml:"commonLabels"` - Controller struct { + CommonLabels struct{} `yaml:"commonLabels"` + Controller struct { Name string `yaml:"name"` Image struct { Chroot bool `yaml:"chroot"` @@ -211,24 +37,18 @@ type IngressChartValue struct { HTTP int `yaml:"http"` HTTPS int `yaml:"https"` } `yaml:"containerPort"` - Config struct { - } `yaml:"config"` - ConfigAnnotations struct { - } `yaml:"configAnnotations"` - ProxySetHeaders struct { - } `yaml:"proxySetHeaders"` - AddHeaders struct { - } `yaml:"addHeaders"` - DNSConfig struct { - } `yaml:"dnsConfig"` - Hostname struct { - } `yaml:"hostname"` - DNSPolicy string `yaml:"dnsPolicy"` - ReportNodeInternalIP bool `yaml:"reportNodeInternalIp"` - WatchIngressWithoutClass bool `yaml:"watchIngressWithoutClass"` - IngressClassByName bool `yaml:"ingressClassByName"` - AllowSnippetAnnotations bool `yaml:"allowSnippetAnnotations"` - HostNetwork bool `yaml:"hostNetwork"` + Config struct{} `yaml:"config"` + ConfigAnnotations struct{} `yaml:"configAnnotations"` + ProxySetHeaders struct{} `yaml:"proxySetHeaders"` + AddHeaders struct{} `yaml:"addHeaders"` + DNSConfig struct{} `yaml:"dnsConfig"` + Hostname struct{} `yaml:"hostname"` + DNSPolicy string `yaml:"dnsPolicy"` + ReportNodeInternalIP bool `yaml:"reportNodeInternalIp"` + WatchIngressWithoutClass bool `yaml:"watchIngressWithoutClass"` + IngressClassByName bool `yaml:"ingressClassByName"` + AllowSnippetAnnotations bool `yaml:"allowSnippetAnnotations"` + HostNetwork bool `yaml:"hostNetwork"` HostPort struct { Enabled bool `yaml:"enabled"` Ports struct { @@ -238,21 +58,17 @@ type IngressChartValue struct { } `yaml:"hostPort"` ElectionID string `yaml:"electionID"` IngressClassResource struct { - Name string `yaml:"name"` - Enabled bool `yaml:"enabled"` - Default bool `yaml:"default"` - ControllerValue string `yaml:"controllerValue"` - Parameters struct { - } `yaml:"parameters"` + Name string `yaml:"name"` + Enabled bool `yaml:"enabled"` + Default bool `yaml:"default"` + ControllerValue string `yaml:"controllerValue"` + Parameters struct{} `yaml:"parameters"` } `yaml:"ingressClassResource"` - IngressClass string `yaml:"ingressClass"` - PodLabels struct { - } `yaml:"podLabels"` - PodSecurityContext struct { - } `yaml:"podSecurityContext"` - Sysctls struct { - } `yaml:"sysctls"` - PublishService struct { + IngressClass string `yaml:"ingressClass"` + PodLabels struct{} `yaml:"podLabels"` + PodSecurityContext struct{} `yaml:"podSecurityContext"` + Sysctls struct{} `yaml:"sysctls"` + PublishService struct { Enabled bool `yaml:"enabled"` PathOverride string `yaml:"pathOverride"` } `yaml:"publishService"` @@ -263,30 +79,23 @@ type IngressChartValue struct { } `yaml:"scope"` ConfigMapNamespace string `yaml:"configMapNamespace"` TCP struct { - ConfigMapNamespace string `yaml:"configMapNamespace"` - Annotations struct { - } `yaml:"annotations"` + ConfigMapNamespace string `yaml:"configMapNamespace"` + Annotations struct{} `yaml:"annotations"` } `yaml:"tcp"` UDP struct { - ConfigMapNamespace string `yaml:"configMapNamespace"` - Annotations struct { - } `yaml:"annotations"` + ConfigMapNamespace string `yaml:"configMapNamespace"` + Annotations struct{} `yaml:"annotations"` } `yaml:"udp"` - MaxmindLicenseKey string `yaml:"maxmindLicenseKey"` - ExtraArgs struct { - } `yaml:"extraArgs"` - ExtraEnvs []interface{} `yaml:"extraEnvs"` - Kind string `yaml:"kind"` - Annotations struct { - } `yaml:"annotations"` - Labels struct { - } `yaml:"labels"` - UpdateStrategy struct { - } `yaml:"updateStrategy"` - MinReadySeconds int `yaml:"minReadySeconds"` - Tolerations []interface{} `yaml:"tolerations"` - Affinity struct { - } `yaml:"affinity"` + MaxmindLicenseKey string `yaml:"maxmindLicenseKey"` + ExtraArgs struct{} `yaml:"extraArgs"` + ExtraEnvs []interface{} `yaml:"extraEnvs"` + Kind string `yaml:"kind"` + Annotations struct{} `yaml:"annotations"` + Labels struct{} `yaml:"labels"` + UpdateStrategy struct{} `yaml:"updateStrategy"` + MinReadySeconds int `yaml:"minReadySeconds"` + Tolerations []interface{} `yaml:"tolerations"` + Affinity struct{} `yaml:"affinity"` TopologySpreadConstraints []interface{} `yaml:"topologySpreadConstraints"` TerminationGracePeriodSeconds int `yaml:"terminationGracePeriodSeconds"` NodeSelector struct { @@ -316,29 +125,26 @@ type IngressChartValue struct { SuccessThreshold int `yaml:"successThreshold"` FailureThreshold int `yaml:"failureThreshold"` } `yaml:"readinessProbe"` - HealthCheckPath string `yaml:"healthCheckPath"` - HealthCheckHost string `yaml:"healthCheckHost"` - PodAnnotations struct { - } `yaml:"podAnnotations"` - ReplicaCount int `yaml:"replicaCount"` - MinAvailable int `yaml:"minAvailable"` - Resources struct { + HealthCheckPath string `yaml:"healthCheckPath"` + HealthCheckHost string `yaml:"healthCheckHost"` + PodAnnotations struct{} `yaml:"podAnnotations"` + ReplicaCount int `yaml:"replicaCount"` + MinAvailable int `yaml:"minAvailable"` + Resources struct { Requests struct { CPU string `yaml:"cpu"` Memory string `yaml:"memory"` } `yaml:"requests"` } `yaml:"resources"` Autoscaling struct { - APIVersion string `yaml:"apiVersion"` - Enabled bool `yaml:"enabled"` - Annotations struct { - } `yaml:"annotations"` - MinReplicas int `yaml:"minReplicas"` - MaxReplicas int `yaml:"maxReplicas"` - TargetCPUUtilizationPercentage int `yaml:"targetCPUUtilizationPercentage"` - TargetMemoryUtilizationPercentage int `yaml:"targetMemoryUtilizationPercentage"` - Behavior struct { - } `yaml:"behavior"` + APIVersion string `yaml:"apiVersion"` + Enabled bool `yaml:"enabled"` + Annotations struct{} `yaml:"annotations"` + MinReplicas int `yaml:"minReplicas"` + MaxReplicas int `yaml:"maxReplicas"` + TargetCPUUtilizationPercentage int `yaml:"targetCPUUtilizationPercentage"` + TargetMemoryUtilizationPercentage int `yaml:"targetMemoryUtilizationPercentage"` + Behavior struct{} `yaml:"behavior"` } `yaml:"autoscaling"` AutoscalingTemplate []interface{} `yaml:"autoscalingTemplate"` Keda struct { @@ -350,12 +156,10 @@ type IngressChartValue struct { CooldownPeriod int `yaml:"cooldownPeriod"` RestoreToOriginalReplicaCount bool `yaml:"restoreToOriginalReplicaCount"` ScaledObject struct { - Annotations struct { - } `yaml:"annotations"` + Annotations struct{} `yaml:"annotations"` } `yaml:"scaledObject"` Triggers []interface{} `yaml:"triggers"` - Behavior struct { - } `yaml:"behavior"` + Behavior struct{} `yaml:"behavior"` } `yaml:"keda"` EnableMimalloc bool `yaml:"enableMimalloc"` CustomTemplate struct { @@ -363,12 +167,10 @@ type IngressChartValue struct { ConfigMapKey string `yaml:"configMapKey"` } `yaml:"customTemplate"` Service struct { - Enabled bool `yaml:"enabled"` - AppProtocol bool `yaml:"appProtocol"` - Annotations struct { - } `yaml:"annotations"` - Labels struct { - } `yaml:"labels"` + Enabled bool `yaml:"enabled"` + AppProtocol bool `yaml:"appProtocol"` + Annotations struct{} `yaml:"annotations"` + Labels struct{} `yaml:"labels"` ExternalIPs []interface{} `yaml:"externalIPs"` LoadBalancerIP string `yaml:"loadBalancerIP"` LoadBalancerSourceRanges []interface{} `yaml:"loadBalancerSourceRanges"` @@ -386,20 +188,17 @@ type IngressChartValue struct { } `yaml:"targetPorts"` Type string `yaml:"type"` NodePorts struct { - HTTP string `yaml:"http"` - HTTPS string `yaml:"https"` - TCP struct { - } `yaml:"tcp"` - UDP struct { - } `yaml:"udp"` + HTTP string `yaml:"http"` + HTTPS string `yaml:"https"` + TCP struct{} `yaml:"tcp"` + UDP struct{} `yaml:"udp"` } `yaml:"nodePorts"` External struct { Enabled bool `yaml:"enabled"` } `yaml:"external"` Internal struct { - Enabled bool `yaml:"enabled"` - Annotations struct { - } `yaml:"annotations"` + Enabled bool `yaml:"enabled"` + Annotations struct{} `yaml:"annotations"` LoadBalancerSourceRanges []interface{} `yaml:"loadBalancerSourceRanges"` } `yaml:"internal"` } `yaml:"service"` @@ -417,25 +216,20 @@ type IngressChartValue struct { } `yaml:"containerSecurityContext"` } `yaml:"opentelemetry"` AdmissionWebhooks struct { - Annotations struct { - } `yaml:"annotations"` - Enabled bool `yaml:"enabled"` - ExtraEnvs []interface{} `yaml:"extraEnvs"` - FailurePolicy string `yaml:"failurePolicy"` - Port int `yaml:"port"` - Certificate string `yaml:"certificate"` - Key string `yaml:"key"` - NamespaceSelector struct { - } `yaml:"namespaceSelector"` - ObjectSelector struct { - } `yaml:"objectSelector"` - Labels struct { - } `yaml:"labels"` - ExistingPsp string `yaml:"existingPsp"` - NetworkPolicyEnabled bool `yaml:"networkPolicyEnabled"` + Annotations struct{} `yaml:"annotations"` + Enabled bool `yaml:"enabled"` + ExtraEnvs []interface{} `yaml:"extraEnvs"` + FailurePolicy string `yaml:"failurePolicy"` + Port int `yaml:"port"` + Certificate string `yaml:"certificate"` + Key string `yaml:"key"` + NamespaceSelector struct{} `yaml:"namespaceSelector"` + ObjectSelector struct{} `yaml:"objectSelector"` + Labels struct{} `yaml:"labels"` + ExistingPsp string `yaml:"existingPsp"` + NetworkPolicyEnabled bool `yaml:"networkPolicyEnabled"` Service struct { - Annotations struct { - } `yaml:"annotations"` + Annotations struct{} `yaml:"annotations"` ExternalIPs []interface{} `yaml:"externalIPs"` LoadBalancerSourceRanges []interface{} `yaml:"loadBalancerSourceRanges"` ServicePort int `yaml:"servicePort"` @@ -445,15 +239,13 @@ type IngressChartValue struct { SecurityContext struct { AllowPrivilegeEscalation bool `yaml:"allowPrivilegeEscalation"` } `yaml:"securityContext"` - Resources struct { - } `yaml:"resources"` + Resources struct{} `yaml:"resources"` } `yaml:"createSecretJob"` PatchWebhookJob struct { SecurityContext struct { AllowPrivilegeEscalation bool `yaml:"allowPrivilegeEscalation"` } `yaml:"securityContext"` - Resources struct { - } `yaml:"resources"` + Resources struct{} `yaml:"resources"` } `yaml:"patchWebhookJob"` Patch struct { Enabled bool `yaml:"enabled"` @@ -464,15 +256,13 @@ type IngressChartValue struct { Digest string `yaml:"digest"` PullPolicy string `yaml:"pullPolicy"` } `yaml:"image"` - PriorityClassName string `yaml:"priorityClassName"` - PodAnnotations struct { - } `yaml:"podAnnotations"` - NodeSelector struct { + PriorityClassName string `yaml:"priorityClassName"` + PodAnnotations struct{} `yaml:"podAnnotations"` + NodeSelector struct { KubernetesIoOs string `yaml:"kubernetes.io/os"` } `yaml:"nodeSelector"` - Tolerations []interface{} `yaml:"tolerations"` - Labels struct { - } `yaml:"labels"` + Tolerations []interface{} `yaml:"tolerations"` + Labels struct{} `yaml:"labels"` SecurityContext struct { RunAsNonRoot bool `yaml:"runAsNonRoot"` RunAsUser int `yaml:"runAsUser"` @@ -494,30 +284,26 @@ type IngressChartValue struct { PortName string `yaml:"portName"` Enabled bool `yaml:"enabled"` Service struct { - Annotations struct { - } `yaml:"annotations"` + Annotations struct{} `yaml:"annotations"` ExternalIPs []interface{} `yaml:"externalIPs"` LoadBalancerSourceRanges []interface{} `yaml:"loadBalancerSourceRanges"` ServicePort int `yaml:"servicePort"` Type string `yaml:"type"` } `yaml:"service"` ServiceMonitor struct { - Enabled bool `yaml:"enabled"` - AdditionalLabels struct { - } `yaml:"additionalLabels"` - Namespace string `yaml:"namespace"` - NamespaceSelector struct { - } `yaml:"namespaceSelector"` + Enabled bool `yaml:"enabled"` + AdditionalLabels struct{} `yaml:"additionalLabels"` + Namespace string `yaml:"namespace"` + NamespaceSelector struct{} `yaml:"namespaceSelector"` ScrapeInterval string `yaml:"scrapeInterval"` TargetLabels []interface{} `yaml:"targetLabels"` Relabelings []interface{} `yaml:"relabelings"` MetricRelabelings []interface{} `yaml:"metricRelabelings"` } `yaml:"serviceMonitor"` PrometheusRule struct { - Enabled bool `yaml:"enabled"` - AdditionalLabels struct { - } `yaml:"additionalLabels"` - Rules []interface{} `yaml:"rules"` + Enabled bool `yaml:"enabled"` + AdditionalLabels struct{} `yaml:"additionalLabels"` + Rules []interface{} `yaml:"rules"` } `yaml:"prometheusRule"` } `yaml:"metrics"` Lifecycle struct { @@ -543,9 +329,8 @@ type IngressChartValue struct { ReadOnlyRootFilesystem bool `yaml:"readOnlyRootFilesystem"` AllowPrivilegeEscalation bool `yaml:"allowPrivilegeEscalation"` } `yaml:"image"` - ExistingPsp string `yaml:"existingPsp"` - ExtraArgs struct { - } `yaml:"extraArgs"` + ExistingPsp string `yaml:"existingPsp"` + ExtraArgs struct{} `yaml:"extraArgs"` ServiceAccount struct { Create bool `yaml:"create"` Name string `yaml:"name"` @@ -567,46 +352,37 @@ type IngressChartValue struct { SuccessThreshold int `yaml:"successThreshold"` TimeoutSeconds int `yaml:"timeoutSeconds"` } `yaml:"readinessProbe"` - Tolerations []interface{} `yaml:"tolerations"` - Affinity struct { - } `yaml:"affinity"` - PodSecurityContext struct { - } `yaml:"podSecurityContext"` - ContainerSecurityContext struct { - } `yaml:"containerSecurityContext"` - PodLabels struct { - } `yaml:"podLabels"` - NodeSelector struct { + Tolerations []interface{} `yaml:"tolerations"` + Affinity struct{} `yaml:"affinity"` + PodSecurityContext struct{} `yaml:"podSecurityContext"` + ContainerSecurityContext struct{} `yaml:"containerSecurityContext"` + PodLabels struct{} `yaml:"podLabels"` + NodeSelector struct { KubernetesIoOs string `yaml:"kubernetes.io/os"` } `yaml:"nodeSelector"` - PodAnnotations struct { - } `yaml:"podAnnotations"` - ReplicaCount int `yaml:"replicaCount"` - MinAvailable int `yaml:"minAvailable"` - Resources struct { - } `yaml:"resources"` + PodAnnotations struct{} `yaml:"podAnnotations"` + ReplicaCount int `yaml:"replicaCount"` + MinAvailable int `yaml:"minAvailable"` + Resources struct{} `yaml:"resources"` ExtraVolumeMounts []interface{} `yaml:"extraVolumeMounts"` ExtraVolumes []interface{} `yaml:"extraVolumes"` Autoscaling struct { - Annotations struct { - } `yaml:"annotations"` - Enabled bool `yaml:"enabled"` - MinReplicas int `yaml:"minReplicas"` - MaxReplicas int `yaml:"maxReplicas"` - TargetCPUUtilizationPercentage int `yaml:"targetCPUUtilizationPercentage"` - TargetMemoryUtilizationPercentage int `yaml:"targetMemoryUtilizationPercentage"` + Annotations struct{} `yaml:"annotations"` + Enabled bool `yaml:"enabled"` + MinReplicas int `yaml:"minReplicas"` + MaxReplicas int `yaml:"maxReplicas"` + TargetCPUUtilizationPercentage int `yaml:"targetCPUUtilizationPercentage"` + TargetMemoryUtilizationPercentage int `yaml:"targetMemoryUtilizationPercentage"` } `yaml:"autoscaling"` Service struct { - Annotations struct { - } `yaml:"annotations"` + Annotations struct{} `yaml:"annotations"` ExternalIPs []interface{} `yaml:"externalIPs"` LoadBalancerSourceRanges []interface{} `yaml:"loadBalancerSourceRanges"` ServicePort int `yaml:"servicePort"` Type string `yaml:"type"` } `yaml:"service"` - PriorityClassName string `yaml:"priorityClassName"` - Labels struct { - } `yaml:"labels"` + PriorityClassName string `yaml:"priorityClassName"` + Labels struct{} `yaml:"labels"` } `yaml:"defaultBackend"` Rbac struct { Create bool `yaml:"create"` @@ -616,17 +392,14 @@ type IngressChartValue struct { Enabled bool `yaml:"enabled"` } `yaml:"podSecurityPolicy"` ServiceAccount struct { - Create bool `yaml:"create"` - Name string `yaml:"name"` - AutomountServiceAccountToken bool `yaml:"automountServiceAccountToken"` - Annotations struct { - } `yaml:"annotations"` + Create bool `yaml:"create"` + Name string `yaml:"name"` + AutomountServiceAccountToken bool `yaml:"automountServiceAccountToken"` + Annotations struct{} `yaml:"annotations"` } `yaml:"serviceAccount"` ImagePullSecrets []interface{} `yaml:"imagePullSecrets"` - TCP struct { - } `yaml:"tcp"` - UDP struct { - } `yaml:"udp"` - PortNamePrefix string `yaml:"portNamePrefix"` - DhParam interface{} `yaml:"dhParam"` + TCP struct{} `yaml:"tcp"` + UDP struct{} `yaml:"udp"` + PortNamePrefix string `yaml:"portNamePrefix"` + DhParam interface{} `yaml:"dhParam"` } diff --git a/magefiles/utils/releasenote.go b/magefiles/utils/releasenote.go new file mode 100644 index 000000000..fa883104f --- /dev/null +++ b/magefiles/utils/releasenote.go @@ -0,0 +1,91 @@ +/* +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 utils + +import ( + "fmt" + "os" + "text/template" +) + +// ReleaseNote - All the pieces of information/documents that get updated during a release +type ReleaseNote struct { + Version string + NewControllerVersion string + PreviousControllerVersion string + ControllerImages []ControllerImage + DepUpdates []string + Updates []string + HelmUpdates []string + NewHelmChartVersion string + PreviousHelmChartVersion string +} + +func (r ReleaseNote) Template() { + // Files are provided as a slice of strings. + changelogTemplate, err := os.ReadFile("Changelog.md.gotmpl") + if err != nil { + ErrorF("Could not read changelog template file %s", err) + } + Debug("ChangeLog Templates %s", string(changelogTemplate)) + t := template.Must(template.New("changelog").Parse(string(changelogTemplate))) + // create a new file + file, err := os.Create(fmt.Sprintf("changelog/Changelog-%s.md", r.Version)) + if err != nil { + ErrorF("Could not create changelog file %s", err) + } + defer file.Close() + + err = t.Execute(file, r) + if err != nil { + ErrorF("executing template: %s", err) + } +} + +func (r ReleaseNote) HelmTemplate() { + // Files are provided as a slice of strings. + changelogTemplate, err := os.ReadFile("charts/ingress-nginx/changelog.md.gotmpl") + if err != nil { + ErrorF("Could not read changelog template file %s", err) + } + Debug("ChangeLog Templates %s", string(changelogTemplate)) + t := template.Must(template.New("changelog").Parse(string(changelogTemplate))) + // create a new file + file, err := os.Create(fmt.Sprintf("charts/ingress-nginx/changelog/Changelog-%s.md", r.NewHelmChartVersion)) + if err != nil { + ErrorF("Could not create changelog file %s", err) + } + defer file.Close() + + err = t.Execute(file, r) + if err != nil { + ErrorF("executing template: %s", err) + } +} + +func (r ReleaseNote) PrintRelease() { + Info("Release Version: %v", r.NewControllerVersion) + Info("Previous Version: %v", r.PreviousControllerVersion) + Info("Controller Image: %v", r.ControllerImages[0].print()) + Info("Controller Chroot Image: %v", r.ControllerImages[1].print()) + for i := range r.Updates { + Info("Update #%v - %v", i, r.Updates[i]) + } + for j := range r.DepUpdates { + Info("Dependabot Update #%v - %v", j, r.DepUpdates[j]) + } +} diff --git a/magefiles/utils/templates/e2edocs.tpl b/magefiles/utils/templates/e2edocs.tpl new file mode 100644 index 000000000..8757c10bd --- /dev/null +++ b/magefiles/utils/templates/e2edocs.tpl @@ -0,0 +1,10 @@ + + +# e2e test suite for [Ingress NGINX Controller]({{.URL}}) + +{{ range $test := .Tests }} +{{ $test }} +{{- end }} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 39eab9ef0..7f10a1d53 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,7 +65,6 @@ plugins: extra_css: [extra.css] -google_analytics: ["UA-118407822-1", "kubernetes.github.io"] nav: - Welcome: @@ -100,7 +99,6 @@ nav: - TLS/HTTPS: "user-guide/tls.md" - Third party addons: - ModSecurity Web Application Firewall: "user-guide/third-party-addons/modsecurity.md" - - OpenTracing: "user-guide/third-party-addons/opentracing.md" - OpenTelemetry: "user-guide/third-party-addons/opentelemetry.md" - Examples: - Introduction: "examples/index.md" @@ -126,6 +124,9 @@ nav: - Static IPs: "examples/static-ip/README.md" - TLS termination: "examples/tls-termination/README.md" - Pod Security Policy (PSP): "examples/psp/README.md" + - Open Policy Agent rules: "examples/openpolicyagent/README.md" + - Canary Deployments: "examples/canary/README.md" - Developer Guide: - Getting Started: "developer-guide/getting-started.md" - Code Overview: "developer-guide/code-overview.md" + - FAQ: "faq.md" diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 000000000..dc4b0d1ca --- /dev/null +++ b/netlify.toml @@ -0,0 +1,11 @@ +# netlify configuration +[build] +publish = "site" +command = "make build-docs" +ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ./docs" +# available here https://github.com/netlify/build-image/blob/focal/included_software.md#languages +environment = { PYTHON_VERSION = "3.8" } + +[context.deploy-preview] + publish = "site/" + command = "make build-docs" diff --git a/pkg/apis/ingress/sslcert.go b/pkg/apis/ingress/sslcert.go index 7dee3880d..b340b2d9a 100644 --- a/pkg/apis/ingress/sslcert.go +++ b/pkg/apis/ingress/sslcert.go @@ -66,12 +66,12 @@ type SSLCert struct { } // GetObjectKind implements the ObjectKind interface as a noop -func (s SSLCert) GetObjectKind() schema.ObjectKind { +func (s *SSLCert) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } // Identifier returns a the couple issuer / serial number if they both exist, an empty string otherwise -func (s SSLCert) Identifier() string { +func (s *SSLCert) Identifier() string { if s.Certificate != nil { if s.Certificate.SerialNumber != nil { return fmt.Sprintf("%s-%s", s.Certificate.Issuer.SerialNumber, s.Certificate.SerialNumber.String()) @@ -81,7 +81,7 @@ func (s SSLCert) Identifier() string { } // HashInclude defines if a field should be used or not to calculate the hash -func (s SSLCert) HashInclude(field string, v interface{}) (bool, error) { +func (s *SSLCert) HashInclude(field string, _ interface{}) (bool, error) { switch field { case "PemSHA", "CASHA", "ExpireTime": return true, nil diff --git a/pkg/apis/ingress/types.go b/pkg/apis/ingress/types.go index e50666c18..2ad17ec3d 100644 --- a/pkg/apis/ingress/types.go +++ b/pkg/apis/ingress/types.go @@ -29,13 +29,12 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/cors" "k8s.io/ingress-nginx/internal/ingress/annotations/fastcgi" "k8s.io/ingress-nginx/internal/ingress/annotations/globalratelimit" + "k8s.io/ingress-nginx/internal/ingress/annotations/ipallowlist" "k8s.io/ingress-nginx/internal/ingress/annotations/ipdenylist" - "k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist" "k8s.io/ingress-nginx/internal/ingress/annotations/log" "k8s.io/ingress-nginx/internal/ingress/annotations/mirror" "k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity" "k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry" - "k8s.io/ingress-nginx/internal/ingress/annotations/opentracing" "k8s.io/ingress-nginx/internal/ingress/annotations/proxy" "k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl" "k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit" @@ -73,7 +72,7 @@ type Configuration struct { DefaultSSLCertificate *SSLCert `json:"-"` - StreamSnippets []string + StreamSnippets []string `json:"StreamSnippets"` } // Backend describes one or more remote server/s (endpoints) associated with a service @@ -129,7 +128,7 @@ type TrafficShapingPolicy struct { } // HashInclude defines if a field should be used or not to calculate the hash -func (s Backend) HashInclude(field string, v interface{}) (bool, error) { +func (b *Backend) HashInclude(field string, _ interface{}) (bool, error) { switch field { case "Endpoints": return false, nil @@ -224,7 +223,7 @@ type Server struct { // is required. // The chain in the execution order of annotations should be: // - Denylist -// - Whitelist +// - Allowlist // - RateLimit // - BasicDigestAuth // - ExternalAuth @@ -298,10 +297,10 @@ type Location struct { // addresses or networks are allowed. // +optional Denylist ipdenylist.SourceRange `json:"denylist,omitempty"` - // Whitelist indicates only connections from certain client + // Allowlist indicates only connections from certain client // addresses or networks are allowed. // +optional - Whitelist ipwhitelist.SourceRange `json:"whitelist,omitempty"` + Allowlist ipallowlist.SourceRange `json:"allowlist,omitempty"` // Proxy contains information about timeouts and buffer sizes // to be used in connections against endpoints // +optional @@ -346,6 +345,11 @@ type Location struct { // CustomHTTPErrors specifies the error codes that should be intercepted. // +optional CustomHTTPErrors []int `json:"custom-http-errors"` + // ProxyInterceptErrors disables error intecepting when using CustomHTTPErrors + // e.g. custom 404 and 503 when service-a does not exist or is not available + // but service-a can return 404 and 503 error codes without intercept + // +optional + DisableProxyInterceptErrors bool `json:"disable-proxy-intercept-errors"` // ModSecurity allows to enable and configure modsecurity // +optional ModSecurity modsecurity.Config `json:"modsecurity"` @@ -354,9 +358,6 @@ type Location struct { // Mirror allows you to mirror traffic to a "test" backend // +optional Mirror mirror.Config `json:"mirror,omitempty"` - // Opentracing allows the global opentracing setting to be overridden for a location - // +optional - Opentracing opentracing.Config `json:"opentracing"` // Opentelemetry allows the global opentelemetry setting to be overridden for a location // +optional Opentelemetry opentelemetry.Config `json:"opentelemetry"` @@ -410,5 +411,4 @@ type Ingress struct { } // GeneralConfig holds the definition of lua general configuration data -type GeneralConfig struct { -} +type GeneralConfig struct{} diff --git a/pkg/apis/ingress/types_equals.go b/pkg/apis/ingress/types_equals.go index 84b1a186a..eeed9a06e 100644 --- a/pkg/apis/ingress/types_equals.go +++ b/pkg/apis/ingress/types_equals.go @@ -80,58 +80,58 @@ func (c1 *Configuration) Equal(c2 *Configuration) bool { } // Equal tests for equality between two Backend types -func (b1 *Backend) Equal(b2 *Backend) bool { - if b1 == b2 { +func (b *Backend) Equal(newB *Backend) bool { + if b == newB { return true } - if b1 == nil || b2 == nil { + if b == nil || newB == nil { return false } - if b1.Name != b2.Name { + if b.Name != newB.Name { return false } - if b1.NoServer != b2.NoServer { + if b.NoServer != newB.NoServer { return false } - if b1.Service != b2.Service { - if b1.Service == nil || b2.Service == nil { + if b.Service != newB.Service { + if b.Service == nil || newB.Service == nil { return false } - if b1.Service.GetNamespace() != b2.Service.GetNamespace() { + if b.Service.GetNamespace() != newB.Service.GetNamespace() { return false } - if b1.Service.GetName() != b2.Service.GetName() { + if b.Service.GetName() != newB.Service.GetName() { return false } } - if b1.Port != b2.Port { + if b.Port != newB.Port { return false } - if b1.SSLPassthrough != b2.SSLPassthrough { + if b.SSLPassthrough != newB.SSLPassthrough { return false } - if !(&b1.SessionAffinity).Equal(&b2.SessionAffinity) { + if !(&b.SessionAffinity).Equal(&newB.SessionAffinity) { return false } - if b1.UpstreamHashBy != b2.UpstreamHashBy { + if b.UpstreamHashBy != newB.UpstreamHashBy { return false } - if b1.LoadBalancing != b2.LoadBalancing { + if b.LoadBalancing != newB.LoadBalancing { return false } - match := compareEndpoints(b1.Endpoints, b2.Endpoints) + match := compareEndpoints(b.Endpoints, newB.Endpoints) if !match { return false } - if !b1.TrafficShapingPolicy.Equal(b2.TrafficShapingPolicy) { + if !b.TrafficShapingPolicy.Equal(&newB.TrafficShapingPolicy) { return false } - return sets.StringElementsMatch(b1.AlternativeBackends, b2.AlternativeBackends) + return sets.StringElementsMatch(b.AlternativeBackends, newB.AlternativeBackends) } // Equal tests for equality between two SessionAffinityConfig types @@ -243,7 +243,7 @@ func (e1 *Endpoint) Equal(e2 *Endpoint) bool { } // Equal checks for equality between two TrafficShapingPolicies -func (tsp1 TrafficShapingPolicy) Equal(tsp2 TrafficShapingPolicy) bool { +func (tsp1 *TrafficShapingPolicy) Equal(tsp2 *TrafficShapingPolicy) bool { if tsp1.Weight != tsp2.Weight { return false } @@ -335,6 +335,8 @@ func (s1 *Server) Equal(s2 *Server) bool { } // Equal tests for equality between two Location types +// +//nolint:gocyclo // Ignore function complexity error func (l1 *Location) Equal(l2 *Location) bool { if l1 == l2 { return true @@ -400,7 +402,7 @@ func (l1 *Location) Equal(l2 *Location) bool { if !(&l1.Denylist).Equal(&l2.Denylist) { return false } - if !(&l1.Whitelist).Equal(&l2.Whitelist) { + if !(&l1.Allowlist).Equal(&l2.Allowlist) { return false } if !(&l1.Proxy).Equal(&l2.Proxy) { @@ -456,10 +458,6 @@ func (l1 *Location) Equal(l2 *Location) bool { return false } - if !l1.Opentracing.Equal(&l2.Opentracing) { - return false - } - if !l1.Opentelemetry.Equal(&l2.Opentelemetry) { return false } @@ -468,6 +466,10 @@ func (l1 *Location) Equal(l2 *Location) bool { return false } + if l1.DisableProxyInterceptErrors != l2.DisableProxyInterceptErrors { + return false + } + return true } @@ -550,39 +552,39 @@ func (l4b1 *L4Backend) Equal(l4b2 *L4Backend) bool { } // Equal tests for equality between two SSLCert types -func (s1 *SSLCert) Equal(s2 *SSLCert) bool { - if s1 == s2 { +func (s *SSLCert) Equal(newS *SSLCert) bool { + if s == newS { return true } - if s1 == nil || s2 == nil { + if s == nil || newS == nil { return false } - if s1.CASHA != s2.CASHA { + if s.CASHA != newS.CASHA { return false } - if s1.CRLSHA != s2.CRLSHA { + if s.CRLSHA != newS.CRLSHA { return false } - if s1.PemSHA != s2.PemSHA { + if s.PemSHA != newS.PemSHA { return false } - if s1.CAFileName != s2.CAFileName { + if s.CAFileName != newS.CAFileName { return false } - if s1.CRLFileName != s2.CRLFileName { + if s.CRLFileName != newS.CRLFileName { return false } - if !s1.ExpireTime.Equal(s2.ExpireTime) { + if !s.ExpireTime.Equal(newS.ExpireTime) { return false } - if s1.PemCertKey != s2.PemCertKey { + if s.PemCertKey != newS.PemCertKey { return false } - if s1.UID != s2.UID { + if s.UID != newS.UID { return false } - return sets.StringElementsMatch(s1.CN, s2.CN) + return sets.StringElementsMatch(s.CN, newS.CN) } var compareEndpointsFunc = func(e1, e2 interface{}) bool { diff --git a/pkg/apis/ingress/types_equals_test.go b/pkg/apis/ingress/types_equals_test.go index 78d29d46c..53643f912 100644 --- a/pkg/apis/ingress/types_equals_test.go +++ b/pkg/apis/ingress/types_equals_test.go @@ -25,19 +25,29 @@ import ( ) func TestEqualConfiguration(t *testing.T) { - ap, _ := filepath.Abs("../../../test/manifests/configuration-a.json") + ap, err := filepath.Abs("../../../test/manifests/configuration-a.json") + if err != nil { + t.Errorf("unexpected error: %v", err) + } a, err := readJSON(ap) if err != nil { t.Errorf("unexpected error reading JSON file: %v", err) } - bp, _ := filepath.Abs("../../../test/manifests/configuration-b.json") + bp, err := filepath.Abs("../../../test/manifests/configuration-b.json") + if err != nil { + t.Errorf("unexpected error: %v", err) + } + b, err := readJSON(bp) if err != nil { t.Errorf("unexpected error reading JSON file: %v", err) } - cp, _ := filepath.Abs("../../../test/manifests/configuration-c.json") + cp, err := filepath.Abs("../../../test/manifests/configuration-c.json") + if err != nil { + t.Errorf("unexpected error: %v", err) + } c, err := readJSON(cp) if err != nil { t.Errorf("unexpected error reading JSON file: %v", err) @@ -84,15 +94,18 @@ func TestL4ServiceElementsMatch(t *testing.T) { {[]L4Service{{Port: 80}}, []L4Service{{Port: 80}}, true}, { []L4Service{ - {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.1"}}}}, + {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.1"}}}, + }, []L4Service{{Port: 80}}, false, }, { []L4Service{ - {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.1"}, {Address: "1.1.1.2"}}}}, + {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.1"}, {Address: "1.1.1.2"}}}, + }, []L4Service{ - {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.2"}, {Address: "1.1.1.1"}}}}, + {Port: 80, Endpoints: []Endpoint{{Address: "1.1.1.2"}, {Address: "1.1.1.1"}}}, + }, true, }, { diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go index 370510380..d3bc4ee86 100644 --- a/pkg/flags/flags.go +++ b/pkg/flags/flags.go @@ -152,6 +152,9 @@ Requires the update-status parameter.`) annotationsPrefix = flags.String("annotations-prefix", parser.DefaultAnnotationsPrefix, `Prefix of the Ingress annotations specific to the NGINX controller.`) + enableAnnotationValidation = flags.Bool("enable-annotation-validation", false, + `If true, will enable the annotation validation feature. This value will be defaulted to true on a future release`) + enableSSLChainCompletion = flags.Bool("enable-ssl-chain-completion", false, `Autocomplete SSL certificate chains with missing intermediate CA certificates. Certificates uploaded to Kubernetes must have the "Authority Information Access" X.509 v3 @@ -218,7 +221,7 @@ Takes the form ":port". If not provided, no admission controller is starte disableSyncEvents = flags.Bool("disable-sync-events", false, "Disables the creation of 'Sync' event resources") - enableTopologyAwareRouting = flags.Bool("enable-topology-aware-routing", false, "Enable topology aware hints feature, needs service object annotation service.kubernetes.io/topology-aware-hints sets to auto.") + enableTopologyAwareRouting = flags.Bool("enable-topology-aware-routing", false, "Enable topology aware routing feature, needs service object annotation service.kubernetes.io/topology-mode sets to auto.") ) flags.StringVar(&nginx.MaxmindMirror, "maxmind-mirror", "", `Maxmind mirror url (example: http://geoip.local/databases.`) @@ -228,14 +231,10 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g flags.IntVar(&nginx.MaxmindRetriesCount, "maxmind-retries-count", 1, "Number of attempts to download the GeoIP DB.") flags.DurationVar(&nginx.MaxmindRetriesTimeout, "maxmind-retries-timeout", time.Second*0, "Maxmind downloading delay between 1st and 2nd attempt, 0s - do not retry to download if something went wrong.") - flag.Set("logtostderr", "true") - flags.AddGoFlagSet(flag.CommandLine) - flags.Parse(os.Args) - - // Workaround for this issue: - // https://github.com/kubernetes/kubernetes/issues/17162 - flag.CommandLine.Parse([]string{}) + if err := flags.Parse(os.Args); err != nil { + return false, nil, err + } pflag.VisitAll(func(flag *pflag.Flag) { klog.V(2).InfoS("FLAG", flag.Name, flag.Value) @@ -253,6 +252,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g } parser.AnnotationsPrefix = *annotationsPrefix + parser.EnableAnnotationValidation = *enableAnnotationValidation // check port collisions if !ing_net.IsPortAvailable(*httpPort) { @@ -298,12 +298,12 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g nginx.HealthCheckTimeout = time.Duration(*defHealthCheckTimeout) * time.Second } - if len(*watchNamespace) != 0 && len(*watchNamespaceSelector) != 0 { + if *watchNamespace != "" && *watchNamespaceSelector != "" { return false, nil, fmt.Errorf("flags --watch-namespace and --watch-namespace-selector are mutually exclusive") } var namespaceSelector labels.Selector - if len(*watchNamespaceSelector) != 0 { + if *watchNamespaceSelector != "" { var err error namespaceSelector, err = labels.Parse(*watchNamespaceSelector) if err != nil { @@ -311,7 +311,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g } } - var histogramBuckets = &collectors.HistogramBuckets{ + histogramBuckets := &collectors.HistogramBuckets{ TimeBuckets: *timeBuckets, LengthBuckets: *lengthBuckets, SizeBuckets: *sizeBuckets, @@ -360,7 +360,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g HTTPS: *httpsPort, SSLProxy: *sslProxyPort, }, - IngressClassConfiguration: &ingressclass.IngressClassConfiguration{ + IngressClassConfiguration: &ingressclass.Configuration{ Controller: *ingressClassController, AnnotationValue: *ingressClassAnnotation, WatchWithoutClass: *watchWithoutClass, @@ -380,7 +380,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g var err error if nginx.MaxmindEditionIDs != "" { - if err = nginx.ValidateGeoLite2DBEditions(); err != nil { + if err := nginx.ValidateGeoLite2DBEditions(); err != nil { return false, nil, err } if nginx.MaxmindLicenseKey != "" || nginx.MaxmindMirror != "" { diff --git a/pkg/flags/flags_test.go b/pkg/flags/flags_test.go index 2a33d73dd..bffe2b16d 100644 --- a/pkg/flags/flags_test.go +++ b/pkg/flags/flags_test.go @@ -33,7 +33,8 @@ func TestDefaults(t *testing.T) { oldArgs := os.Args defer func() { os.Args = oldArgs }() - os.Args = []string{"cmd", + os.Args = []string{ + "cmd", "--default-backend-service", "namespace/test", "--http-port", "0", "--https-port", "0", @@ -53,8 +54,8 @@ func TestDefaults(t *testing.T) { } } -func TestSetupSSLProxy(t *testing.T) { - // TODO +func TestSetupSSLProxy(_ *testing.T) { + // TODO TestSetupSSLProxy } func TestFlagConflict(t *testing.T) { diff --git a/pkg/metrics/handler.go b/pkg/metrics/handler.go index c37c1760c..73c7d328f 100644 --- a/pkg/metrics/handler.go +++ b/pkg/metrics/handler.go @@ -29,7 +29,6 @@ import ( ) func RegisterHealthz(healthPath string, mux *http.ServeMux, checks ...healthz.HealthChecker) { - healthCheck := []healthz.HealthChecker{healthz.PingHealthz} if len(checks) > 0 { healthCheck = append(healthCheck, checks...) @@ -67,7 +66,7 @@ func RegisterProfiler(host string, port int) { server := &http.Server{ Addr: fmt.Sprintf("%s:%d", host, port), - //G112 (CWE-400): Potential Slowloris Attack + // G112 (CWE-400): Potential Slowloris Attack ReadHeaderTimeout: 10 * time.Second, Handler: mux, } diff --git a/pkg/tcpproxy/tcp.go b/pkg/tcpproxy/tcp.go index 86850ad54..eda4a2746 100644 --- a/pkg/tcpproxy/tcp.go +++ b/pkg/tcpproxy/tcp.go @@ -69,7 +69,7 @@ func (p *TCPProxy) Handle(conn net.Conn) { } proxy := p.Default - hostname, err := parser.GetHostname(data[:]) + hostname, err := parser.GetHostname(data) if err == nil { klog.V(4).InfoS("TLS Client Hello", "host", hostname) proxy = p.Get(hostname) @@ -91,8 +91,14 @@ func (p *TCPProxy) Handle(conn net.Conn) { if proxy.ProxyProtocol { // write out the Proxy Protocol header - localAddr := conn.LocalAddr().(*net.TCPAddr) - remoteAddr := conn.RemoteAddr().(*net.TCPAddr) + localAddr, ok := conn.LocalAddr().(*net.TCPAddr) + if !ok { + klog.Errorf("unexpected type: %T", conn.LocalAddr()) + } + remoteAddr, ok := conn.RemoteAddr().(*net.TCPAddr) + if !ok { + klog.Errorf("unexpected type: %T", conn.RemoteAddr()) + } protocol := "UNKNOWN" if remoteAddr.IP.To4() != nil { protocol = "TCP4" @@ -119,7 +125,9 @@ func (p *TCPProxy) Handle(conn net.Conn) { func pipe(client, server net.Conn) { doCopy := func(s, c net.Conn, cancel chan<- bool) { - io.Copy(s, c) + if _, err := io.Copy(s, c); err != nil { + klog.Errorf("Error copying data: %v", err) + } cancel <- true } diff --git a/pkg/util/file/file_test.go b/pkg/util/file/file_test.go index 93ec39cf9..9a43946b3 100644 --- a/pkg/util/file/file_test.go +++ b/pkg/util/file/file_test.go @@ -35,8 +35,12 @@ func TestSHA1(t *testing.T) { if err != nil { t.Fatal(err) } - f.Write(test.content) - f.Sync() + if _, err := f.Write(test.content); err != nil { + t.Error(err) + } + if err := f.Sync(); err != nil { + t.Error(err) + } sha := SHA1(f.Name()) f.Close() diff --git a/pkg/util/file/file_watcher.go b/pkg/util/file/file_watcher.go index daf955e52..3899e41f8 100644 --- a/pkg/util/file/file_watcher.go +++ b/pkg/util/file/file_watcher.go @@ -26,8 +26,8 @@ import ( "github.com/fsnotify/fsnotify" ) -// FileWatcher is an interface we use to watch changes in files -type FileWatcher interface { +// Watcher is an interface we use to watch changes in files +type Watcher interface { Close() error } @@ -40,7 +40,7 @@ type OSFileWatcher struct { } // NewFileWatcher creates a new FileWatcher -func NewFileWatcher(file string, onEvent func()) (FileWatcher, error) { +func NewFileWatcher(file string, onEvent func()) (Watcher, error) { fw := OSFileWatcher{ file: file, onEvent: onEvent, diff --git a/pkg/util/file/file_watcher_test.go b/pkg/util/file/file_watcher_test.go index 316cb6f1e..dd2755812 100644 --- a/pkg/util/file/file_watcher_test.go +++ b/pkg/util/file/file_watcher_test.go @@ -59,7 +59,9 @@ func TestFileWatcher(t *testing.T) { t.Fatalf("expected no events before writing a file") case <-timeoutChan: } - os.WriteFile(f.Name(), []byte{}, ReadWriteByUser) + if err := os.WriteFile(f.Name(), []byte{}, ReadWriteByUser); err != nil { + t.Errorf("unexpected error: %v", err) + } select { case <-events: case <-timeoutChan: diff --git a/pkg/util/file/filesystem.go b/pkg/util/file/filesystem.go index 7c0db9f12..ccb93ed06 100644 --- a/pkg/util/file/filesystem.go +++ b/pkg/util/file/filesystem.go @@ -17,4 +17,4 @@ limitations under the License. package file // ReadWriteByUser defines linux permission to read and write files for the owner user -const ReadWriteByUser = 0700 +const ReadWriteByUser = 0o700 diff --git a/pkg/util/file/structure.go b/pkg/util/file/structure.go index d109e8c03..7d4f26da9 100644 --- a/pkg/util/file/structure.go +++ b/pkg/util/file/structure.go @@ -33,12 +33,10 @@ const ( DefaultSSLDirectory = "/etc/ingress-controller/ssl" ) -var ( - directories = []string{ - DefaultSSLDirectory, - AuthDirectory, - } -) +var directories = []string{ + DefaultSSLDirectory, + AuthDirectory, +} // CreateRequiredDirectories verifies if the required directories to // start the ingress controller exist and creates the missing ones. diff --git a/pkg/util/ingress/ingress.go b/pkg/util/ingress/ingress.go index e69ca7b29..881d5a001 100644 --- a/pkg/util/ingress/ingress.go +++ b/pkg/util/ingress/ingress.go @@ -114,7 +114,7 @@ func GetRemovedIngresses(rucfg, newcfg *ingress.Configuration) []string { // IsDynamicConfigurationEnough returns whether a Configuration can be // dynamically applied, without reloading the backend. -func IsDynamicConfigurationEnough(newcfg *ingress.Configuration, oldcfg *ingress.Configuration) bool { +func IsDynamicConfigurationEnough(newcfg, oldcfg *ingress.Configuration) bool { copyOfRunningConfig := *oldcfg copyOfPcfg := *newcfg @@ -133,21 +133,21 @@ func IsDynamicConfigurationEnough(newcfg *ingress.Configuration, oldcfg *ingress // clearL4serviceEndpoints is a helper function to clear endpoints from the ingress configuration since they should be ignored when // checking if the new configuration changes can be applied dynamically. func clearL4serviceEndpoints(config *ingress.Configuration) { - var clearedTCPL4Services []ingress.L4Service - var clearedUDPL4Services []ingress.L4Service - for _, service := range config.TCPEndpoints { + clearedTCPL4Services := make([]ingress.L4Service, 0, len(config.TCPEndpoints)) + clearedUDPL4Services := make([]ingress.L4Service, 0, len(config.UDPEndpoints)) + for i := range config.TCPEndpoints { copyofService := ingress.L4Service{ - Port: service.Port, - Backend: service.Backend, + Port: config.TCPEndpoints[i].Port, + Backend: config.TCPEndpoints[i].Backend, Endpoints: []ingress.Endpoint{}, Service: nil, } clearedTCPL4Services = append(clearedTCPL4Services, copyofService) } - for _, service := range config.UDPEndpoints { + for i := range config.UDPEndpoints { copyofService := ingress.L4Service{ - Port: service.Port, - Backend: service.Backend, + Port: config.UDPEndpoints[i].Port, + Backend: config.UDPEndpoints[i].Backend, Endpoints: []ingress.Endpoint{}, Service: nil, } @@ -160,7 +160,7 @@ func clearL4serviceEndpoints(config *ingress.Configuration) { // clearCertificates is a helper function to clear Certificates from the ingress configuration since they should be ignored when // checking if the new configuration changes can be applied dynamically if dynamic certificates is on func clearCertificates(config *ingress.Configuration) { - var clearedServers []*ingress.Server + clearedServers := make([]*ingress.Server, 0, len(config.Servers)) for _, server := range config.Servers { copyOfServer := *server copyOfServer.SSLCert = nil @@ -169,16 +169,16 @@ func clearCertificates(config *ingress.Configuration) { config.Servers = clearedServers } -type redirect struct { +type Redirect struct { From string To string SSLCert *ingress.SSLCert } // BuildRedirects build the redirects of servers based on configurations and certificates -func BuildRedirects(servers []*ingress.Server) []*redirect { +func BuildRedirects(servers []*ingress.Server) []*Redirect { names := sets.Set[string]{} - redirectServers := make([]*redirect, 0) + redirectServers := make([]*Redirect, 0) for _, srv := range servers { if !srv.RedirectFromToWWW { @@ -212,7 +212,7 @@ func BuildRedirects(servers []*ingress.Server) []*redirect { continue } - r := &redirect{ + r := &Redirect{ From: from, To: to, } diff --git a/pkg/util/process/controller.go b/pkg/util/process/controller.go index ae9bc9356..a73e81934 100644 --- a/pkg/util/process/controller.go +++ b/pkg/util/process/controller.go @@ -16,9 +16,9 @@ limitations under the License. package process -// ProcessController defines a common interface for a process to be controlled, +// Controller defines a common interface for a process to be controlled, // like the configurer, the webhook or the proper ingress controller -type ProcessController interface { +type Controller interface { Start() Stop() error } diff --git a/pkg/util/process/sigterm.go b/pkg/util/process/sigterm.go index 77c0ad58c..1c0d729c1 100644 --- a/pkg/util/process/sigterm.go +++ b/pkg/util/process/sigterm.go @@ -29,7 +29,7 @@ type exiter func(code int) // HandleSigterm receives a ProcessController interface and deals with // the graceful shutdown -func HandleSigterm(ngx ProcessController, delay int, exit exiter) { +func HandleSigterm(ngx Controller, delay int, exit exiter) { signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGTERM) <-signalChan diff --git a/pkg/util/process/sigterm_test.go b/pkg/util/process/sigterm_test.go index 2c2a6ee91..08c8275c3 100644 --- a/pkg/util/process/sigterm_test.go +++ b/pkg/util/process/sigterm_test.go @@ -42,9 +42,9 @@ func (f *FakeProcess) exiterFunc(code int) { f.exitCode = code } -func sendDelayedSignal(delay time.Duration) { - time.Sleep(delay * time.Second) - syscall.Kill(syscall.Getpid(), syscall.SIGTERM) +func sendDelayedSignal(delay time.Duration) error { + time.Sleep(delay) + return syscall.Kill(syscall.Getpid(), syscall.SIGTERM) } func TestHandleSigterm(t *testing.T) { @@ -66,7 +66,12 @@ func TestHandleSigterm(t *testing.T) { for _, tt := range tests { process := &FakeProcess{shouldError: tt.shouldError} t.Run(tt.name, func(t *testing.T) { - go sendDelayedSignal(2) // Send a signal after 2 seconds + go func() { + err := sendDelayedSignal(2 * time.Second) // Send a signal after 2 seconds + if err != nil { + t.Errorf("error sending delayed signal: %v", err) + } + }() HandleSigterm(process, tt.delay, process.exiterFunc) }) if tt.shouldError && process.exitCode != 1 { diff --git a/pkg/util/runtime/cpu_notlinux.go b/pkg/util/runtime/cpu_notlinux.go index 2a1b48252..97c72cd5a 100644 --- a/pkg/util/runtime/cpu_notlinux.go +++ b/pkg/util/runtime/cpu_notlinux.go @@ -23,7 +23,7 @@ import ( "runtime" ) -// NumCPU ... +// NumCPU returns the number of logical CPUs usable by the current process. func NumCPU() int { return runtime.NumCPU() } diff --git a/pkg/util/sets/match_test.go b/pkg/util/sets/match_test.go index e2366d2c7..a65c5a05f 100644 --- a/pkg/util/sets/match_test.go +++ b/pkg/util/sets/match_test.go @@ -20,23 +20,20 @@ import ( "testing" ) -var ( - testCasesElementMatch = []struct { - listA []string - listB []string - expected bool - }{ - {nil, nil, true}, - {[]string{"1"}, nil, false}, - {[]string{"1"}, []string{"1"}, true}, - {[]string{"1", "2", "1"}, []string{"1", "1", "2"}, true}, - {[]string{"1", "3", "1"}, []string{"1", "1", "2"}, false}, - {[]string{"1", "1"}, []string{"1", "2"}, false}, - } -) +var testCasesElementMatch = []struct { + listA []string + listB []string + expected bool +}{ + {nil, nil, true}, + {[]string{"1"}, nil, false}, + {[]string{"1"}, []string{"1"}, true}, + {[]string{"1", "2", "1"}, []string{"1", "1", "2"}, true}, + {[]string{"1", "3", "1"}, []string{"1", "1", "2"}, false}, + {[]string{"1", "1"}, []string{"1", "2"}, false}, +} func TestElementsMatch(t *testing.T) { - for _, testCase := range testCasesElementMatch { result := StringElementsMatch(testCase.listA, testCase.listB) if result != testCase.expected { diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 75cf3fc65..86517c6c1 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -49,9 +49,10 @@ COPY --chown=www-data:www-data bin/${TARGETARCH}/wait-shutdown / # with volumes (custom templates) RUN bash -xeu -c ' \ writeDirs=( \ - /etc/ingress-controller \ /etc/ingress-controller/ssl \ /etc/ingress-controller/auth \ + /etc/ingress-controller/geoip \ + /etc/ingress-controller/telemetry \ /var/log \ /var/log/nginx \ /tmp/nginx \ diff --git a/rootfs/Dockerfile-chroot b/rootfs/Dockerfile-chroot index 7ab8584a7..09e0ba7ce 100644 --- a/rootfs/Dockerfile-chroot +++ b/rootfs/Dockerfile-chroot @@ -23,7 +23,7 @@ RUN apk update \ && apk upgrade \ && /chroot.sh -FROM alpine:3.17.2 +FROM alpine:3.18.4 ARG TARGETARCH ARG VERSION @@ -50,7 +50,6 @@ RUN apk update \ && apk add -U --no-cache \ bash \ openssl \ - curl \ ca-certificates \ dumb-init \ tzdata \ diff --git a/rootfs/chroot.sh b/rootfs/chroot.sh index 9f3cbd804..38547b69c 100755 --- a/rootfs/chroot.sh +++ b/rootfs/chroot.sh @@ -21,6 +21,8 @@ writeDirs=( \ /chroot/etc/ingress-controller \ /chroot/etc/ingress-controller/ssl \ /chroot/etc/ingress-controller/auth \ + /chroot/etc/ingress-controller/telemetry \ + /chroot/etc/ingress-controller/geoip \ /chroot/opt/modsecurity/var/log \ /chroot/opt/modsecurity/var/upload \ /chroot/opt/modsecurity/var/audit \ @@ -41,7 +43,8 @@ for dir in "${writeDirs[@]}"; do done mkdir -p /chroot/lib /chroot/proc /chroot/usr /chroot/bin /chroot/dev /chroot/run -cp /etc/passwd /etc/group /chroot/etc/ +cp /etc/passwd /etc/group /etc/hosts /chroot/etc/ cp -a /usr/* /chroot/usr/ cp -a /etc/nginx/* /chroot/etc/nginx/ +cp -a /etc/ingress-controller/* /chroot/etc/ingress-controller/ cp /lib/ld-musl-* /lib/libcrypto* /lib/libssl* /lib/libz* /chroot/lib/ diff --git a/rootfs/etc/nginx/lua/tcp_udp_configuration.lua b/rootfs/etc/nginx/lua/tcp_udp_configuration.lua index 85864b45b..f191454fa 100644 --- a/rootfs/etc/nginx/lua/tcp_udp_configuration.lua +++ b/rootfs/etc/nginx/lua/tcp_udp_configuration.lua @@ -1,5 +1,6 @@ local ngx = ngx local tostring = tostring +local cjson = require("cjson.safe") -- this is the Lua representation of TCP/UDP Configuration local tcp_udp_configuration_data = ngx.shared.tcp_udp_configuration_data @@ -37,6 +38,14 @@ function _M.call() return end + local _, backends_err = cjson.decode(backends) + + if backends_err then + ngx.log(ngx.ERR, "could not parse backends data: ", backends_err) + return + end + + local success, err_conf = tcp_udp_configuration_data:set("backends", backends) if not success then ngx.log(ngx.ERR, "dynamic-configuration: error updating configuration: " .. tostring(err_conf)) diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl index 9b3a47de3..94dc12412 100644 --- a/rootfs/etc/nginx/template/nginx.tmpl +++ b/rootfs/etc/nginx/template/nginx.tmpl @@ -29,10 +29,6 @@ load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; load_module /etc/nginx/modules/ngx_http_modsecurity_module.so; {{ end }} -{{ if (shouldLoadOpentracingModule $cfg $servers) }} -load_module /etc/nginx/modules/ngx_http_opentracing_module.so; -{{ end }} - {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} load_module /modules_mount/etc/nginx/modules/otel/otel_ngx_module.so; {{ end }} @@ -140,7 +136,7 @@ http { {{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}} {{/* we use the value of the real IP for the geo_ip module */}} - {{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIp }} + {{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIP }} {{ if $cfg.UseProxyProtocol }} real_ip_header proxy_protocol; {{ else }} @@ -170,22 +166,12 @@ http { {{ end }} - {{ if $cfg.UseGeoIP }} - {{/* databases used to determine the country depending on the client IP address */}} - {{/* http://nginx.org/en/docs/http/ngx_http_geoip_module.html */}} - {{/* this is require to calculate traffic for individual country using GeoIP in the status page */}} - geoip_country /etc/nginx/geoip/GeoIP.dat; - geoip_city /etc/nginx/geoip/GeoLiteCity.dat; - geoip_org /etc/nginx/geoip/GeoIPASNum.dat; - geoip_proxy_recursive on; - {{ end }} - {{ if $cfg.UseGeoIP2 }} # https://github.com/leev/ngx_http_geoip2_module#example-usage {{ range $index, $file := $all.MaxmindEditionFiles }} {{ if eq $file "GeoLite2-Country.mmdb" }} - geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { $geoip2_country_code source=$remote_addr country iso_code; $geoip2_country_name source=$remote_addr country names en; $geoip2_country_geoname_id source=$remote_addr country geoname_id; @@ -196,7 +182,7 @@ http { {{ end }} {{ if eq $file "GeoIP2-Country.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-Country.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { $geoip2_country_code source=$remote_addr country iso_code; $geoip2_country_name source=$remote_addr country names en; $geoip2_country_geoname_id source=$remote_addr country geoname_id; @@ -207,7 +193,7 @@ http { {{ end }} {{ if eq $file "GeoLite2-City.mmdb" }} - geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { $geoip2_city_country_code source=$remote_addr country iso_code; $geoip2_city_country_name source=$remote_addr country names en; $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; @@ -224,11 +210,13 @@ http { $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; } {{ end }} {{ if eq $file "GeoIP2-City.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-City.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { $geoip2_city_country_code source=$remote_addr country iso_code; $geoip2_city_country_name source=$remote_addr country names en; $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; @@ -245,25 +233,27 @@ http { $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; } {{ end }} {{ if eq $file "GeoLite2-ASN.mmdb" }} - geoip2 /etc/nginx/geoip/GeoLite2-ASN.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { $geoip2_asn source=$remote_addr autonomous_system_number; $geoip2_org source=$remote_addr autonomous_system_organization; } {{ end }} {{ if eq $file "GeoIP2-ASN.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-ASN.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { $geoip2_asn source=$remote_addr autonomous_system_number; $geoip2_org source=$remote_addr autonomous_system_organization; } {{ end }} {{ if eq $file "GeoIP2-ISP.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-ISP.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { $geoip2_isp source=$remote_addr isp; $geoip2_isp_org source=$remote_addr organization; $geoip2_asn source=$remote_addr default=0 autonomous_system_number; @@ -271,13 +261,13 @@ http { {{ end }} {{ if eq $file "GeoIP2-Connection-Type.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-Connection-Type.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-Connection-Type.mmdb { $geoip2_connection_type connection_type; } {{ end }} {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} - geoip2 /etc/nginx/geoip/GeoIP2-Anonymous-IP.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { $geoip2_is_anon source=$remote_addr is_anonymous; $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; @@ -292,7 +282,10 @@ http { {{ end }} aio threads; + + {{ if $cfg.EnableAioWrite }} aio_write on; + {{ end }} tcp_nopush on; tcp_nodelay on; @@ -307,7 +300,6 @@ http { client_body_temp_path /tmp/nginx/client-body; fastcgi_temp_path /tmp/nginx/fastcgi-temp; proxy_temp_path /tmp/nginx/proxy-temp; - ajp_temp_path /tmp/nginx/ajp-temp; client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }}; client_header_timeout {{ $cfg.ClientHeaderTimeout }}s; @@ -315,9 +307,15 @@ http { client_body_buffer_size {{ $cfg.ClientBodyBufferSize }}; client_body_timeout {{ $cfg.ClientBodyTimeout }}s; + {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; + {{ end }} + + {{ if (gt $cfg.HTTP2MaxRequests 0) }} http2_max_requests {{ $cfg.HTTP2MaxRequests }}; + {{ end }} + http2_max_concurrent_streams {{ $cfg.HTTP2MaxConcurrentStreams }}; types_hash_max_size 2048; @@ -337,7 +335,6 @@ http { limit_req_status {{ $cfg.LimitReqStatusCode }}; limit_conn_status {{ $cfg.LimitConnStatusCode }}; - {{ buildOpentracing $cfg $servers }} {{ buildOpentelemetry $cfg $servers }} include /etc/nginx/mime.types; @@ -397,7 +394,7 @@ http { {{ if $cfg.EnableSyslog }} access_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} upstreaminfo if=$loggable; {{ else }} - access_log {{ or $cfg.HttpAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; + access_log {{ or $cfg.HTTPAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; {{ end }} {{ end }} @@ -467,7 +464,7 @@ http { ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; {{ if not (empty $cfg.SSLSessionTicketKey ) }} - ssl_session_ticket_key /etc/nginx/tickets.key; + ssl_session_ticket_key /etc/ingress-controller/tickets.key; {{ end }} # slightly reduce the time-to-first-byte @@ -490,7 +487,7 @@ http { ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; - {{ if gt (len $cfg.CustomHTTPErrors) 0 }} + {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} proxy_intercept_errors on; {{ end }} @@ -539,14 +536,14 @@ http { {{ range $rl := (filterRateLimits $servers ) }} # Ratelimit {{ $rl.Name }} - geo $remote_addr $whitelist_{{ $rl.ID }} { + geo $remote_addr $allowlist_{{ $rl.ID }} { default 0; - {{ range $ip := $rl.Whitelist }} + {{ range $ip := $rl.Allowlist }} {{ $ip }} 1;{{ end }} } # Ratelimit {{ $rl.Name }} - map $whitelist_{{ $rl.ID }} $limit_{{ $rl.ID }} { + map $allowlist_{{ $rl.ID }} $limit_{{ $rl.ID }} { 0 {{ $cfg.LimitConnZoneVariable }}; 1 ""; } @@ -700,6 +697,11 @@ http { # default server, used for NGINX healthcheck and access to nginx stats server { + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + listen 127.0.0.1:{{ .StatusPort }}; set $proxy_upstream_name "internal"; @@ -708,10 +710,6 @@ http { access_log off; - {{ if $cfg.EnableOpentracing }} - opentracing off; - {{ end }} - {{ if $cfg.EnableOpentelemetry }} opentelemetry off; {{ end }} @@ -808,7 +806,7 @@ stream { error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; - {{ if $cfg.EnableRealIp }} + {{ if $cfg.EnableRealIP }} {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} set_real_ip_from {{ $trusted_ip }}; {{ end }} @@ -1078,17 +1076,14 @@ stream { location = {{ $authPath }} { internal; - {{ if (or $all.Cfg.EnableOpentracing $location.Opentracing.Enabled) }} - opentracing on; - opentracing_propagate_context; - {{ end }} - {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} opentelemetry on; opentelemetry_propagate; {{ end }} + {{ if not $all.Cfg.EnableAuthAccessLog }} access_log off; + {{ end }} # Ensure that modsecurity will not run on an internal location as this is not accessible from outside {{ if $all.Cfg.EnableModsecurity }} @@ -1228,7 +1223,6 @@ stream { set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; set $global_rate_limit_exceeding n; - {{ buildOpentracingForLocation $all.Cfg.EnableOpentracing $all.Cfg.OpentracingTrustIncomingSpan $location }} {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} {{ if $location.Mirror.Source }} @@ -1303,8 +1297,8 @@ stream { {{ range $ip := $location.Denylist.CIDR }} deny {{ $ip }};{{ end }} {{ end }} - {{ if gt (len $location.Whitelist.CIDR) 0 }} - {{ range $ip := $location.Whitelist.CIDR }} + {{ if gt (len $location.Allowlist.CIDR) 0 }} + {{ range $ip := $location.Allowlist.CIDR }} allow {{ $ip }};{{ end }} deny all; {{ end }} @@ -1325,7 +1319,7 @@ stream { # `auth_request` module does not support HTTP keepalives in upstream block: # https://trac.nginx.org/nginx/ticket/1579 access_by_lua_block { - local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '' }) + local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) if res.status == ngx.HTTP_OK then ngx.var.auth_cookie = res.header['Set-Cookie'] {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} @@ -1382,13 +1376,11 @@ stream { {{ end }} {{/* By default use vhost as Host to upstream, but allow overrides */}} - {{ if not (eq $proxySetHeader "grpc_set_header") }} {{ if not (empty $location.UpstreamVhost) }} {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; {{ else }} {{ $proxySetHeader }} Host $best_http_host; {{ end }} - {{ end }} # Pass the extracted client certificate to the backend {{ if not (empty $server.CertificateAuth.CAFileName) }} @@ -1481,7 +1473,7 @@ stream { {{ end }} {{/* if a location-specific error override is set, add the proxy_intercept here */}} - {{ if $location.CustomHTTPErrors }} + {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} # Custom error pages per ingress proxy_intercept_errors on; {{ end }} @@ -1540,9 +1532,6 @@ stream { {{ if eq $server.Hostname "_" }} # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} location {{ $all.HealthzURI }} { - {{ if $all.Cfg.EnableOpentracing }} - opentracing off; - {{ end }} {{ if $all.Cfg.EnableOpentelemetry }} opentelemetry off; @@ -1555,9 +1544,6 @@ stream { # this is required to avoid error if nginx is being monitored # with an external software (like sysdig) location /nginx_status { - {{ if $all.Cfg.EnableOpentracing }} - opentracing off; - {{ end }} {{ if $all.Cfg.EnableOpentelemetry }} opentelemetry off; diff --git a/test/data/cleanConf.expected.conf b/test/data/cleanConf.expected.conf index 1666c19f6..7c4a16824 100644 --- a/test/data/cleanConf.expected.conf +++ b/test/data/cleanConf.expected.conf @@ -47,7 +47,7 @@ http { listen_ports = { ssl_proxy = "442", https = "443" }, hsts = true, - hsts_max_age = 15724800, + hsts_max_age = 31536000, hsts_include_subdomains = true, hsts_preload = false, }) diff --git a/test/data/cleanConf.src.conf b/test/data/cleanConf.src.conf index 0e572faa5..89954cf0d 100644 --- a/test/data/cleanConf.src.conf +++ b/test/data/cleanConf.src.conf @@ -65,7 +65,7 @@ lua_shared_dict ocsp_response_cache 5M; listen_ports = { ssl_proxy = "442", https = "443" }, hsts = true, - hsts_max_age = 15724800, + hsts_max_age = 31536000, hsts_include_subdomains = true, hsts_preload = false, }) diff --git a/test/data/config.json b/test/data/config.json index d51e1c40b..d64cfe0d2 100644 --- a/test/data/config.json +++ b/test/data/config.json @@ -25,7 +25,7 @@ "gzipTypes": "application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component", "hsts": true, "hstsIncludeSubdomains": true, - "hstsMaxAge": "15724800", + "hstsMaxAge": "31536000", "keepAlive": 75, "mapHashBucketSize": 64, "maxWorkerConnections": 16384, diff --git a/test/e2e-image/Dockerfile b/test/e2e-image/Dockerfile index 11abc26b4..cd0bcb715 100644 --- a/test/e2e-image/Dockerfile +++ b/test/e2e-image/Dockerfile @@ -1,13 +1,12 @@ ARG E2E_BASE_IMAGE FROM ${E2E_BASE_IMAGE} AS BASE -FROM alpine:3.17.2 +FROM alpine:3.18.4 RUN apk update \ && apk upgrade && apk add -U --no-cache \ ca-certificates \ bash \ - curl \ tzdata \ libc6-compat \ openssl diff --git a/test/e2e-image/Makefile b/test/e2e-image/Makefile index 011396531..ff5aa8fb6 100644 --- a/test/e2e-image/Makefile +++ b/test/e2e-image/Makefile @@ -1,6 +1,6 @@ DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -E2E_BASE_IMAGE ?= "registry.k8s.io/ingress-nginx/e2e-test-runner:v20230314-helm-chart-4.5.2-32-g520384b11@sha256:754c62f9a5efd1ee515ee908ecc16c0c4d1dda96a8cc8019667182a55f3a9035" +E2E_BASE_IMAGE ?= "registry.k8s.io/ingress-nginx/e2e-test-runner:v20231011-8b53cabe0@sha256:ed0dad805c635e66469b4ac376010eebdd0b3fe62d753f58db1632d6f12f451d" image: echo "..entered Makefile in /test/e2e-image" diff --git a/test/e2e-image/namespace-overlays/validations/values.yaml b/test/e2e-image/namespace-overlays/validations/values.yaml new file mode 100644 index 000000000..d423217db --- /dev/null +++ b/test/e2e-image/namespace-overlays/validations/values.yaml @@ -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 diff --git a/test/e2e/HTTPBUN_IMAGE b/test/e2e/HTTPBUN_IMAGE new file mode 100644 index 000000000..7e83b49fe --- /dev/null +++ b/test/e2e/HTTPBUN_IMAGE @@ -0,0 +1 @@ +registry.k8s.io/ingress-nginx/e2e-test-httpbun:v20231011-8b53cabe0 diff --git a/test/e2e/admission/admission.go b/test/e2e/admission/admission.go index 4a4e31980..c41105e2d 100644 --- a/test/e2e/admission/admission.go +++ b/test/e2e/admission/admission.go @@ -30,8 +30,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-nginx/test/e2e/framework" + + networking "k8s.io/api/networking/v1" ) +const admissionTestHost = "admission-test" + var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", func() { f := framework.NewDefaultFramework("admission") @@ -41,7 +45,7 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("reject ingress with global-rate-limit annotations when memcached is not configured", func() { - host := "admission-test" + host := admissionTestHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/global-rate-limit": "100", @@ -68,7 +72,7 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should not allow overlaps of host and paths without canary annotations", func() { - host := "admission-test" + host := admissionTestHost firstIngress := framework.NewSingleIngress("first-ingress", "/", host, f.Namespace, framework.EchoService, 80, nil) _, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{}) @@ -85,7 +89,7 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should allow overlaps of host and paths with canary annotation", func() { - host := "admission-test" + host := admissionTestHost firstIngress := framework.NewSingleIngress("first-ingress", "/", host, f.Namespace, framework.EchoService, 80, nil) _, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{}) @@ -123,7 +127,16 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if there is an error validating the ingress definition", func() { - host := "admission-test" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + host := admissionTestHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/configuration-snippet": "something invalid", @@ -134,7 +147,7 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if there is an invalid value in some annotation", func() { - host := "admission-test" + host := admissionTestHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/connection-proxy-header": "a;}", @@ -148,7 +161,7 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if there is a forbidden value in some annotation", func() { - host := "admission-test" + host := admissionTestHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/connection-proxy-header": "set_by_lua", @@ -161,6 +174,40 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid annotation value should return an error") }) + ginkgo.It("should return an error if there is an invalid path and wrong pathType is set", func() { + host := "path-validation" + var ( + exactPathType = networking.PathTypeExact + prefixPathType = networking.PathTypePrefix + implSpecific = networking.PathTypeImplementationSpecific + ) + + f.UpdateNginxConfigMapData("strict-validate-path-type", "true") + + invalidPath := framework.NewSingleIngress("first-ingress", "/foo/bar/[a-z]{3}", host, f.Namespace, framework.EchoService, 80, nil) + invalidPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType + + _, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), invalidPath, metav1.CreateOptions{}) + assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid path value should return an error") + + invalidPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &prefixPathType + _, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), invalidPath, metav1.CreateOptions{}) + assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid path value should return an error") + + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/use-regex": "true", + "nginx.ingress.kubernetes.io/rewrite-target": "/new/backend", + } + pathSpecific := framework.NewSingleIngress("pathspec-ingress", "/foo/bar/[a-z]{3}", host, f.Namespace, framework.EchoService, 80, annotations) + pathSpecific.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &implSpecific + _, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), pathSpecific, metav1.CreateOptions{}) + assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with arbitrary path and implSpecific value should not return an error") + + validPath := framework.NewSingleIngress("second-ingress", "/bloblo", host, f.Namespace, framework.EchoService, 80, nil) + _, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), validPath, metav1.CreateOptions{}) + assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with valid path should not return an error") + }) + ginkgo.It("should not return an error if the Ingress V1 definition is valid with Ingress Class", func() { out, err := createIngress(f.Namespace, validV1Ingress) assert.Equal(ginkgo.GinkgoT(), "ingress.networking.k8s.io/extensions created\n", out) @@ -194,6 +241,15 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if the Ingress V1 definition contains invalid annotations", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + out, err := createIngress(f.Namespace, invalidV1Ingress) assert.Empty(ginkgo.GinkgoT(), out) assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress using kubectl") @@ -205,6 +261,14 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should not return an error for an invalid Ingress when it has unknown class", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() out, err := createIngress(f.Namespace, invalidV1IngressWithOtherClass) assert.Equal(ginkgo.GinkgoT(), "ingress.networking.k8s.io/extensions-invalid-other created\n", out) assert.Nil(ginkgo.GinkgoT(), err, "creating an invalid ingress with unknown class using kubectl") @@ -309,7 +373,7 @@ func createIngress(namespace, ingressDefinition string) (string, error) { execOut bytes.Buffer execErr bytes.Buffer ) - + //nolint:gosec // Ignore G204 error cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%v --warnings-as-errors=false apply --namespace %s -f -", framework.KubectlPath, namespace)) cmd.Stdin = strings.NewReader(ingressDefinition) cmd.Stdout = &execOut diff --git a/test/e2e/annotations/affinity.go b/test/e2e/annotations/affinity.go index 3e1e9e969..b64581ef6 100644 --- a/test/e2e/annotations/affinity.go +++ b/test/e2e/annotations/affinity.go @@ -32,6 +32,14 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const ( + affinityAnnotation = "cookie" + cookieName = "SERVERID" + enableAnnotation = "true" + disableAnnotation = "false" + defaultHost = "foo.com" +) + var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f := framework.NewDefaultFramework("affinity") @@ -42,8 +50,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should set sticky cookie SERVERID", func() { host := "sticky.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -64,8 +72,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should change cookie name on ingress definition change", func() { host := "change.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -80,7 +88,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { WithHeader("Host", host). Expect(). Status(http.StatusOK). - Header("Set-Cookie").Contains("SERVERID") + Header("Set-Cookie").Contains(cookieName) ing.ObjectMeta.Annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "OTHERCOOKIENAME" @@ -99,8 +107,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should set the path to /something on the generated cookie", func() { host := "path.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName ing := framework.NewSingleIngress(host, "/something", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -122,8 +130,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { pathtype := networking.PathTypePrefix host := "morethanonerule.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName f.EnsureIngress(&networking.Ingress{ ObjectMeta: metav1.ObjectMeta{ @@ -194,7 +202,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should set cookie with expires", func() { host := "cookieexpires.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "ExpiresCookie" annotations["nginx.ingress.kubernetes.io/session-cookie-expires"] = "172800" annotations["nginx.ingress.kubernetes.io/session-cookie-max-age"] = "259200" @@ -211,7 +219,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { assert.Nil(ginkgo.GinkgoT(), err, "loading GMT location") assert.NotNil(ginkgo.GinkgoT(), local, "expected a location but none returned") - duration, _ := time.ParseDuration("48h") + duration, err := time.ParseDuration("48h") + assert.Nil(ginkgo.GinkgoT(), err, "parsing duration") expected := time.Now().In(local).Add(duration).Format("Mon, 02-Jan-06 15:04") f.HTTPTestClient(). @@ -225,7 +234,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should set cookie with domain", func() { host := "cookiedomain.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "DomainCookie" annotations["nginx.ingress.kubernetes.io/session-cookie-domain"] = "foo.bar" @@ -248,7 +257,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should not set cookie without domain annotation", func() { host := "cookienodomain.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "NoDomainCookie" ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) @@ -270,9 +279,9 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should work with use-regex annotation and session-cookie-path", func() { host := "useregex.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" - annotations["nginx.ingress.kubernetes.io/use-regex"] = "true" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName + annotations["nginx.ingress.kubernetes.io/use-regex"] = enableAnnotation annotations["nginx.ingress.kubernetes.io/session-cookie-path"] = "/foo/bar" ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations) @@ -294,9 +303,9 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should warn user when use-regex is true and session-cookie-path is not set", func() { host := "useregexwarn.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" - annotations["nginx.ingress.kubernetes.io/use-regex"] = "true" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName + annotations["nginx.ingress.kubernetes.io/use-regex"] = enableAnnotation ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -321,7 +330,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { host := "separate.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation ing1 := framework.NewSingleIngress("ingress1", "/foo/bar", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing1) @@ -351,8 +360,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { ginkgo.It("should set sticky cookie without host", func() { annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName ing := framework.NewSingleIngress("default-no-host", "/", "", f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -370,12 +379,12 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { }) ginkgo.It("should work with server-alias annotation", func() { - host := "foo.com" + host := defaultHost alias1 := "a1.foo.com" alias2 := "a2.foo.com" annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName annotations["nginx.ingress.kubernetes.io/server-alias"] = fmt.Sprintf("%s,%s", alias1, alias2) ing := framework.NewSingleIngress(host, "/bar", host, f.Namespace, framework.EchoService, 80, annotations) @@ -383,7 +392,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - //server alias sort by sort.Strings(), see: internal/ingress/annotations/alias/main.go:60 + // server alias sort by sort.Strings(), see: internal/ingress/annotations/alias/main.go:60 return strings.Contains(server, fmt.Sprintf("server_name %s %s %s ;", host, alias1, alias2)) }) @@ -410,11 +419,11 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { }) ginkgo.It("should set secure in cookie with provided true annotation on http", func() { - host := "foo.com" + host := defaultHost annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" - annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = "true" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName + annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = enableAnnotation ing := framework.NewSingleIngress(host, "/bar", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -433,11 +442,11 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { }) ginkgo.It("should not set secure in cookie with provided false annotation on http", func() { - host := "foo.com" + host := defaultHost annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" - annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = "false" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName + annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = disableAnnotation ing := framework.NewSingleIngress(host, "/bar", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -456,11 +465,11 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { }) ginkgo.It("should set secure in cookie with provided false annotation on https", func() { - host := "foo.com" + host := defaultHost annotations := make(map[string]string) - annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie" - annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "SERVERID" - annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = "false" + annotations["nginx.ingress.kubernetes.io/affinity"] = affinityAnnotation + annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = cookieName + annotations["nginx.ingress.kubernetes.io/session-cookie-secure"] = disableAnnotation f.EnsureIngress(framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, annotations)) @@ -470,6 +479,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { strings.Contains(server, "listen 443") }) + //nolint:gosec // Ignore the gosec error in testing f.HTTPTestClientWithTLSConfig(&tls.Config{ServerName: host, InsecureSkipVerify: true}). GET("/"). WithURL(f.GetURL(framework.HTTPS)). diff --git a/test/e2e/annotations/affinitymode.go b/test/e2e/annotations/affinitymode.go index cce2b004d..e6253b6ff 100644 --- a/test/e2e/annotations/affinitymode.go +++ b/test/e2e/annotations/affinitymode.go @@ -28,6 +28,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const sslRedirectValue = "false" + var _ = framework.DescribeAnnotation("affinitymode", func() { f := framework.NewDefaultFramework("affinity") @@ -45,7 +47,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "hello-cookie" annotations["nginx.ingress.kubernetes.io/session-cookie-expires"] = "172800" annotations["nginx.ingress.kubernetes.io/session-cookie-max-age"] = "172800" - annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false" + annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = sslRedirectValue annotations["nginx.ingress.kubernetes.io/affinity-mode"] = "balanced" annotations["nginx.ingress.kubernetes.io/session-cookie-hash"] = "sha1" @@ -78,7 +80,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "hello-cookie" annotations["nginx.ingress.kubernetes.io/session-cookie-expires"] = "172800" annotations["nginx.ingress.kubernetes.io/session-cookie-max-age"] = "172800" - annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false" + annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = sslRedirectValue annotations["nginx.ingress.kubernetes.io/affinity-mode"] = "persistent" annotations["nginx.ingress.kubernetes.io/session-cookie-hash"] = "sha1" @@ -106,7 +108,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { // Send new requests and add new backends. Check which backend responded to the sent request cookies := getCookiesFromHeader(response.Header("Set-Cookie").Raw()) for sendRequestNumber := 0; sendRequestNumber < 10; sendRequestNumber++ { - replicas = replicas + 1 + replicas++ err := framework.UpdateDeployment(f.KubeClientSet, f.Namespace, deploymentName, replicas, nil) assert.Nil(ginkgo.GinkgoT(), err) framework.Sleep() @@ -125,7 +127,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { framework.Sleep() // validate, there is no backend to serve the request - response = request.WithCookies(cookies).Expect().Status(http.StatusServiceUnavailable) + request.WithCookies(cookies).Expect().Status(http.StatusServiceUnavailable) // create brand new backends replicas = 2 diff --git a/test/e2e/annotations/alias.go b/test/e2e/annotations/alias.go index de829507d..ca4fe9c31 100644 --- a/test/e2e/annotations/alias.go +++ b/test/e2e/annotations/alias.go @@ -26,6 +26,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const fooHost = "foo" + var _ = framework.DescribeAnnotation("server-alias", func() { f := framework.NewDefaultFramework("alias") @@ -34,7 +36,7 @@ var _ = framework.DescribeAnnotation("server-alias", func() { }) ginkgo.It("should return status code 200 for host 'foo' and 404 for 'bar'", func() { - host := "foo" + host := fooHost ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -60,7 +62,7 @@ var _ = framework.DescribeAnnotation("server-alias", func() { }) ginkgo.It("should return status code 200 for host 'foo' and 'bar'", func() { - host := "foo" + host := fooHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/server-alias": "bar", } @@ -73,7 +75,7 @@ var _ = framework.DescribeAnnotation("server-alias", func() { return strings.Contains(server, fmt.Sprintf("server_name %v", host)) }) - hosts := []string{"foo", "bar"} + hosts := []string{fooHost, "bar"} for _, host := range hosts { f.HTTPTestClient(). GET("/"). @@ -85,7 +87,7 @@ var _ = framework.DescribeAnnotation("server-alias", func() { }) ginkgo.It("should return status code 200 for hosts defined in two ingresses, different path with one alias", func() { - host := "foo" + host := fooHost ing := framework.NewSingleIngress("app-a", "/app-a", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -101,7 +103,7 @@ var _ = framework.DescribeAnnotation("server-alias", func() { return strings.Contains(server, fmt.Sprintf("server_name %v bar", host)) }) - hosts := []string{"foo", "bar"} + hosts := []string{fooHost, "bar"} for _, host := range hosts { f.HTTPTestClient(). GET("/app-a"). diff --git a/test/e2e/annotations/auth.go b/test/e2e/annotations/auth.go index 4ca034825..415ffd951 100644 --- a/test/e2e/annotations/auth.go +++ b/test/e2e/annotations/auth.go @@ -23,7 +23,6 @@ import ( "net/url" "regexp" "strings" - "time" "golang.org/x/crypto/bcrypt" @@ -37,15 +36,21 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const ( + differentHost = "different" + authHost = "auth" + authURL = "http://foo.bar.baz:5000/path" +) + var _ = framework.DescribeAnnotation("auth-*", func() { - f := framework.NewDefaultFramework("auth") + f := framework.NewDefaultFramework("auth", framework.WithHTTPBunEnabled()) ginkgo.BeforeEach(func() { f.NewEchoDeployment() }) ginkgo.It("should return status code 200 when no authentication is configured", func() { - host := "auth" + host := authHost ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -64,7 +69,7 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It("should return status code 503 when authentication is configured with an invalid secret", func() { - host := "auth" + host := authHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", "nginx.ingress.kubernetes.io/auth-secret": "something", @@ -88,9 +93,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It("should return status code 401 when authentication is configured but Authorization header is not configured", func() { - host := "auth" + host := authHost - s := f.EnsureSecret(buildSecret("foo", "bar", "test", f.Namespace)) + s := f.EnsureSecret(buildSecret(fooHost, "bar", "test", f.Namespace)) annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", @@ -115,9 +120,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It("should return status code 401 when authentication is configured and Authorization header is sent with invalid credentials", func() { - host := "auth" + host := authHost - s := f.EnsureSecret(buildSecret("foo", "bar", "test", f.Namespace)) + s := f.EnsureSecret(buildSecret(fooHost, "bar", "test", f.Namespace)) annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", @@ -143,9 +148,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It("should return status code 401 and cors headers when authentication and cors is configured but Authorization header is not configured", func() { - host := "auth" + host := authHost - s := f.EnsureSecret(buildSecret("foo", "bar", "test", f.Namespace)) + s := f.EnsureSecret(buildSecret(fooHost, "bar", "test", f.Namespace)) annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", @@ -171,9 +176,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It("should return status code 200 when authentication is configured and Authorization header is sent", func() { - host := "auth" + host := authHost - s := f.EnsureSecret(buildSecret("foo", "bar", "test", f.Namespace)) + s := f.EnsureSecret(buildSecret(fooHost, "bar", "test", f.Namespace)) annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", @@ -192,15 +197,15 @@ var _ = framework.DescribeAnnotation("auth-*", func() { f.HTTPTestClient(). GET("/"). WithHeader("Host", host). - WithBasicAuth("foo", "bar"). + WithBasicAuth(fooHost, "bar"). Expect(). Status(http.StatusOK) }) ginkgo.It("should return status code 200 when authentication is configured with a map and Authorization header is sent", func() { - host := "auth" + host := authHost - s := f.EnsureSecret(buildMapSecret("foo", "bar", "test", f.Namespace)) + s := f.EnsureSecret(buildMapSecret(fooHost, "bar", "test", f.Namespace)) annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-type": "basic", @@ -220,13 +225,13 @@ var _ = framework.DescribeAnnotation("auth-*", func() { f.HTTPTestClient(). GET("/"). WithHeader("Host", host). - WithBasicAuth("foo", "bar"). + WithBasicAuth(fooHost, "bar"). Expect(). Status(http.StatusOK) }) ginkgo.It("should return status code 401 when authentication is configured with invalid content and Authorization header is sent", func() { - host := "auth" + host := authHost s := f.EnsureSecret( &corev1.Secret{ @@ -259,19 +264,27 @@ var _ = framework.DescribeAnnotation("auth-*", func() { f.HTTPTestClient(). GET("/"). WithHeader("Host", host). - WithBasicAuth("foo", "bar"). + WithBasicAuth(fooHost, "bar"). Expect(). Status(http.StatusUnauthorized) }) ginkgo.It(`should set snippet "proxy_set_header My-Custom-Header 42;" when external auth is configured`, func() { - host := "auth" + host := authHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-url": "http://foo.bar/basic-auth/user/password", "nginx.ingress.kubernetes.io/auth-snippet": ` proxy_set_header My-Custom-Header 42;`, } + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -283,7 +296,16 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should not set snippet "proxy_set_header My-Custom-Header 42;" when external auth is not configured`, func() { - host := "auth" + host := authHost + + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-snippet": ` @@ -300,7 +322,7 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should set "proxy_set_header 'My-Custom-Header' '42';" when auth-headers are set`, func() { - host := "auth" + host := authHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-url": "http://foo.bar/basic-auth/user/password", @@ -321,11 +343,11 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should set cache_key when external auth cache is configured`, func() { - host := "auth" + host := authHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/auth-url": "http://foo.bar/basic-auth/user/password", - "nginx.ingress.kubernetes.io/auth-cache-key": "foo", + "nginx.ingress.kubernetes.io/auth-cache-key": fooHost, "nginx.ingress.kubernetes.io/auth-cache-duration": "200 202 401 30m", } @@ -338,7 +360,6 @@ var _ = framework.DescribeAnnotation("auth-*", func() { func(server string) bool { return cacheRegex.MatchString(server) && strings.Contains(server, `proxy_cache_valid 200 202 401 30m;`) - }) }) @@ -390,10 +411,10 @@ http { assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - httpbunIP := e.Subsets[0].Addresses[0].IP + nginxIP := e.Subsets[0].Addresses[0].IP annotations = map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/cookies/set/alma/armud", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/cookies/set/alma/armud", nginxIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", } @@ -406,7 +427,6 @@ http { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, "server_name "+host) }) - }) ginkgo.It("user retains cookie by default", func() { @@ -432,7 +452,7 @@ http { }) ginkgo.It("user with annotated ingress retains cookie if upstream returns error status code", func() { - annotations["nginx.ingress.kubernetes.io/auth-always-set-cookie"] = "true" + annotations["nginx.ingress.kubernetes.io/auth-always-set-cookie"] = enableAnnotation f.UpdateIngress(ing1) f.UpdateIngress(ing2) @@ -452,26 +472,13 @@ http { }) ginkgo.Context("when external authentication is configured", func() { - host := "auth" + host := authHost var annotations map[string]string var ing *networking.Ingress ginkgo.BeforeEach(func() { - f.NewHttpbunDeployment() - - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - - httpbunIP := e.Subsets[0].Addresses[0].IP - annotations = map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", } @@ -509,7 +516,7 @@ http { annotations["nginx.ingress.kubernetes.io/auth-realm"] = "test auth" f.UpdateIngress(ing) - anotherHost := "different" + anotherHost := differentHost anotherAnnotations := map[string]string{} anotherIng := framework.NewSingleIngress(anotherHost, "/", anotherHost, f.Namespace, framework.EchoService, 80, anotherAnnotations) @@ -558,12 +565,12 @@ http { // Sleep a while just to guarantee that the configmap is applied framework.Sleep() - annotations["nginx.ingress.kubernetes.io/auth-url"] = "http://foo.bar.baz:5000/path" + annotations["nginx.ingress.kubernetes.io/auth-url"] = authURL f.UpdateIngress(ing) f.WaitForNginxServer("", func(server string) bool { - return strings.Contains(server, "http://foo.bar.baz:5000/path") && + return strings.Contains(server, authURL) && !strings.Contains(server, `upstream auth-external-auth`) }) }) @@ -596,19 +603,19 @@ http { // Sleep a while just to guarantee that the configmap is applied framework.Sleep() - annotations["nginx.ingress.kubernetes.io/auth-url"] = "http://foo.bar.baz:5000/path" + annotations["nginx.ingress.kubernetes.io/auth-url"] = authURL annotations["nginx.ingress.kubernetes.io/auth-keepalive"] = "-1" f.UpdateIngress(ing) f.WaitForNginxServer("", func(server string) bool { - return strings.Contains(server, "http://foo.bar.baz:5000/path") && + return strings.Contains(server, authURL) && !strings.Contains(server, `upstream auth-external-auth`) }) }) ginkgo.It(`should not create additional upstream block when auth-keepalive is set with HTTP/2`, func() { - annotations["nginx.ingress.kubernetes.io/auth-url"] = "http://foo.bar.baz:5000/path" + annotations["nginx.ingress.kubernetes.io/auth-url"] = authURL annotations["nginx.ingress.kubernetes.io/auth-keepalive"] = "123" annotations["nginx.ingress.kubernetes.io/auth-keepalive-requests"] = "456" annotations["nginx.ingress.kubernetes.io/auth-keepalive-timeout"] = "789" @@ -616,7 +623,7 @@ http { f.WaitForNginxServer("", func(server string) bool { - return strings.Contains(server, "http://foo.bar.baz:5000/path") && + return strings.Contains(server, authURL) && !strings.Contains(server, `upstream auth-external-auth`) }) }) @@ -642,28 +649,55 @@ http { strings.Contains(server, `keepalive_timeout 789s;`) }) }) + + ginkgo.It(`should disable set_all_vars when auth-keepalive-share-vars is not set`, func() { + f.UpdateNginxConfigMapData("use-http2", "false") + defer func() { + f.UpdateNginxConfigMapData("use-http2", "true") + }() + // Sleep a while just to guarantee that the configmap is applied + framework.Sleep() + + annotations["nginx.ingress.kubernetes.io/auth-keepalive"] = "10" + f.UpdateIngress(ing) + + f.WaitForNginxServer("", + func(server string) bool { + return strings.Contains(server, `upstream auth-external-auth`) && + strings.Contains(server, `keepalive 10;`) && + strings.Contains(server, `share_all_vars = false`) + }) + }) + + ginkgo.It(`should enable set_all_vars when auth-keepalive-share-vars is true`, func() { + f.UpdateNginxConfigMapData("use-http2", "false") + defer func() { + f.UpdateNginxConfigMapData("use-http2", "true") + }() + // Sleep a while just to guarantee that the configmap is applied + framework.Sleep() + + annotations["nginx.ingress.kubernetes.io/auth-keepalive"] = "10" + annotations["nginx.ingress.kubernetes.io/auth-keepalive-share-vars"] = enableAnnotation + f.UpdateIngress(ing) + + f.WaitForNginxServer("", + func(server string) bool { + return strings.Contains(server, `upstream auth-external-auth`) && + strings.Contains(server, `keepalive 10;`) && + strings.Contains(server, `share_all_vars = true`) + }) + }) }) ginkgo.Context("when external authentication is configured with a custom redirect param", func() { - host := "auth" + host := authHost var annotations map[string]string var ing *networking.Ingress ginkgo.BeforeEach(func() { - f.NewHttpbunDeployment() - - var httpbunIP string - - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - httpbunIP = e.Subsets[0].Addresses[0].IP - annotations = map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", "nginx.ingress.kubernetes.io/auth-signin-redirect-param": "orig", } @@ -702,7 +736,7 @@ http { annotations["nginx.ingress.kubernetes.io/auth-realm"] = "test auth" f.UpdateIngress(ing) - anotherHost := "different" + anotherHost := differentHost anotherAnnotations := map[string]string{} anotherIng := framework.NewSingleIngress(anotherHost, "/", anotherHost, f.Namespace, framework.EchoService, 80, anotherAnnotations) @@ -722,30 +756,15 @@ http { }) ginkgo.Context("when external authentication with caching is configured", func() { - thisHost := "auth" - thatHost := "different" + thisHost := authHost + thatHost := differentHost fooPath := "/foo" barPath := "/bar" ginkgo.BeforeEach(func() { - f.NewHttpbunDeployment() - - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - framework.Sleep(1 * time.Second) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - - httpbunIP := e.Subsets[0].Addresses[0].IP - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", "nginx.ingress.kubernetes.io/auth-cache-key": "fixed", "nginx.ingress.kubernetes.io/auth-cache-duration": "200 201 401 30m", @@ -860,7 +879,7 @@ http { }) ginkgo.Context("with invalid auth-url should deny whole location", func() { - host := "auth" + host := authHost var annotations map[string]string var ing *networking.Ingress @@ -900,7 +919,6 @@ http { // Auth error func buildSecret(username, password, name, namespace string) *corev1.Secret { - //out, err := exec.Command("openssl", "passwd", "-crypt", password).CombinedOutput() out, err := bcrypt.GenerateFromPassword([]byte(password), 14) encpass := fmt.Sprintf("%v:%s\n", username, out) assert.Nil(ginkgo.GinkgoT(), err) @@ -919,7 +937,6 @@ func buildSecret(username, password, name, namespace string) *corev1.Secret { } func buildMapSecret(username, password, name, namespace string) *corev1.Secret { - //out, err := exec.Command("openssl", "passwd", "-crypt", password).CombinedOutput() out, err := bcrypt.GenerateFromPassword([]byte(password), 14) assert.Nil(ginkgo.GinkgoT(), err) @@ -930,7 +947,7 @@ func buildMapSecret(username, password, name, namespace string) *corev1.Secret { DeletionGracePeriodSeconds: framework.NewInt64(1), }, Data: map[string][]byte{ - username: []byte(out), + username: out, }, Type: corev1.SecretTypeOpaque, } diff --git a/test/e2e/annotations/authtls.go b/test/e2e/annotations/authtls.go index e96835aa0..c7a05c053 100644 --- a/test/e2e/annotations/authtls.go +++ b/test/e2e/annotations/authtls.go @@ -26,6 +26,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const authTLSFooHost = "authtls.foo.com" + var _ = framework.DescribeAnnotation("auth-tls-*", func() { f := framework.NewDefaultFramework("authtls") @@ -34,7 +36,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should set sslClientCertificate, sslVerifyClient and sslVerifyDepth with auth-tls-secret", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -82,7 +84,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should set valid auth-tls-secret, sslVerify to off, and sslVerifyDepth to 2", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace _, err := framework.CreateIngressMASecret( @@ -112,7 +114,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should 302 redirect to error page instead of 400 when auth-tls-error-page is set", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace errorPath := "/error" @@ -159,7 +161,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should pass URL-encoded certificate to upstream", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -204,7 +206,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should validate auth-tls-verify-client", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -260,11 +262,10 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { WithHeader("Host", host). Expect(). Status(http.StatusOK) - }) ginkgo.It("should return 403 using auth-tls-match-cn with no matching CN from client", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -293,7 +294,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should return 200 using auth-tls-match-cn with matching CN from client", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -322,7 +323,7 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) ginkgo.It("should return 200 using auth-tls-match-cn where atleast one of the regex options matches CN from client", func() { - host := "authtls.foo.com" + host := authTLSFooHost nameSpace := f.Namespace clientConfig, err := framework.CreateIngressMASecret( @@ -351,7 +352,8 @@ var _ = framework.DescribeAnnotation("auth-tls-*", func() { }) }) -func assertSslClientCertificateConfig(f *framework.Framework, host string, verifyClient string, verifyDepth string) { +//nolint:unparam // Ignore the invariant param: host +func assertSslClientCertificateConfig(f *framework.Framework, host, verifyClient, verifyDepth string) { sslClientCertDirective := fmt.Sprintf("ssl_client_certificate /etc/ingress-controller/ssl/%s-%s.pem;", f.Namespace, host) sslVerify := fmt.Sprintf("ssl_verify_client %s;", verifyClient) sslVerifyDepth := fmt.Sprintf("ssl_verify_depth %s;", verifyDepth) diff --git a/test/e2e/annotations/backendprotocol.go b/test/e2e/annotations/backendprotocol.go index bccb03afb..0a181d3c7 100644 --- a/test/e2e/annotations/backendprotocol.go +++ b/test/e2e/annotations/backendprotocol.go @@ -24,6 +24,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const backendProtocolHost = "backendprotocol.foo.com" + var _ = framework.DescribeAnnotation("backend-protocol", func() { f := framework.NewDefaultFramework("backendprotocol") @@ -32,7 +34,7 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { }) ginkgo.It("should set backend protocol to https:// and use proxy_pass", func() { - host := "backendprotocol.foo.com" + host := backendProtocolHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", } @@ -46,8 +48,23 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { }) }) + ginkgo.It("should set backend protocol to https:// and use proxy_pass with lowercase annotation", func() { + host := backendProtocolHost + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/backend-protocol": "https", + } + + 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, "proxy_pass https://upstream_balancer;") + }) + }) + ginkgo.It("should set backend protocol to $scheme:// and use proxy_pass", func() { - host := "backendprotocol.foo.com" + host := backendProtocolHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "AUTO_HTTP", } @@ -62,7 +79,7 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { }) ginkgo.It("should set backend protocol to grpc:// and use grpc_pass", func() { - host := "backendprotocol.foo.com" + host := backendProtocolHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "GRPC", } @@ -77,7 +94,7 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { }) ginkgo.It("should set backend protocol to grpcs:// and use grpc_pass", func() { - host := "backendprotocol.foo.com" + host := backendProtocolHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "GRPCS", } @@ -92,7 +109,7 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { }) ginkgo.It("should set backend protocol to '' and use fastcgi_pass", func() { - host := "backendprotocol.foo.com" + host := backendProtocolHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "FCGI", } @@ -105,19 +122,4 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() { return strings.Contains(server, "fastcgi_pass upstream_balancer;") }) }) - - ginkgo.It("should set backend protocol to '' and use ajp_pass", func() { - host := "backendprotocol.foo.com" - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/backend-protocol": "AJP", - } - - 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, "ajp_pass upstream_balancer;") - }) - }) }) diff --git a/test/e2e/annotations/canary.go b/test/e2e/annotations/canary.go index 6dd81fdd8..ea733dbf4 100644 --- a/test/e2e/annotations/canary.go +++ b/test/e2e/annotations/canary.go @@ -30,28 +30,30 @@ import ( ) const ( - canaryService = "echo-canary" + canaryService = "httpbun-canary" ) var _ = framework.DescribeAnnotation("canary-*", func() { - f := framework.NewDefaultFramework("canary") + f := framework.NewDefaultFramework("canary", framework.WithHTTPBunEnabled()) ginkgo.BeforeEach(func() { - // Deployment for main backend - f.NewEchoDeployment() - // Deployment for canary backend - f.NewEchoDeployment(framework.WithDeploymentName(canaryService)) + f.NewHttpbunDeployment(framework.WithDeploymentName(canaryService)) }) ginkgo.Context("when canary is created", func() { ginkgo.It("should response with a 200 status from the mainline upstream when requests are made to the mainline ingress", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -65,20 +67,27 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) }) ginkgo.It("should return 404 status for requests to the canary if no matching ingress is found", func() { - host := "foo" + host := fooHost canaryAnnotations := map[string]string{ "nginx.ingress.kubernetes.io/canary": "true", @@ -87,13 +96,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). @@ -105,10 +118,10 @@ var _ = framework.DescribeAnnotation("canary-*", func() { TODO: This test needs improvements made to the e2e framework so that deployment updates work in order to successfully run It("should return the correct status codes when endpoints are unavailable", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) + ing := framework.NewSingleIngress(host, "/info", host, f.Namespace, framework.HTTPBunService, 80, annotations) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -123,7 +136,7 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, f.Namespace, canaryService, + canaryIng := framework.NewSingleIngress(canaryIngName, "/info", host, f.Namespace, canaryService, 80, canaryAnnotations) f.EnsureIngress(canaryIng) @@ -159,12 +172,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { */ ginkgo.It("should route requests to the correct upstream if mainline ingress is created before the canary ingress", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -178,33 +196,41 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests destined for the mainline ingress to the maineline upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests destined for the canary ingress to the canary upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) }) ginkgo.It("should route requests to the correct upstream if mainline ingress is created after the canary ingress", func() { - host := "foo" + host := fooHost canaryAnnotations := map[string]string{ "nginx.ingress.kubernetes.io/canary": "true", @@ -213,15 +239,25 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -230,30 +266,38 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.By("routing requests destined for the mainline ingress to the mainelin upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests destined for the canary ingress to the canary upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) }) ginkgo.It("should route requests to the correct upstream if the mainline ingress is modified", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -267,18 +311,27 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) modAnnotations := map[string]string{ - "foo": "bar", + fooHost: "bar", } - modIng := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, modAnnotations) - - f.UpdateIngress(modIng) + f.UpdateIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + modAnnotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -287,30 +340,38 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.By("routing requests destined fro the mainline ingress to the mainline upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests destined for the canary ingress to the canary upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) }) ginkgo.It("should route requests to the correct upstream if the canary ingress is modified", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -324,9 +385,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -338,10 +404,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-by-header": "CanaryByHeader2", } - modIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, newAnnotations) - - f.UpdateIngress(modIng) + f.UpdateIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + newAnnotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -350,31 +420,39 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.By("routing requests destined for the mainline ingress to the mainline upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader2", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests destined for the canary ingress to the canary upstream") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader2", "always"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) }) }) ginkgo.Context("when canaried by header with no value", func() { ginkgo.It("should route requests to the correct upstream", func() { - host := "foo" + host := fooHost - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, nil) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + nil)) f.WaitForNginxServer(host, func(server string) bool { @@ -388,48 +466,62 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the canary upstream when header is set to 'always'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) ginkgo.By("routing requests to the mainline upstream when header is set to 'never'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "never"). Expect(). Status(http.StatusOK). Body(). - Contains(framework.EchoService).NotContains(canaryService) + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests to the mainline upstream when header is set to anything else") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "badheadervalue"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) }) }) ginkgo.Context("when canaried by header with value", func() { ginkgo.It("should route requests to the correct upstream", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -444,55 +536,73 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the canary upstream when header is set to 'DoCanary'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "DoCanary"). Expect(). Status(http.StatusOK). - Body().Contains(canaryService) + Body(). + Contains(canaryService) ginkgo.By("routing requests to the mainline upstream when header is set to 'always'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "always"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests to the mainline upstream when header is set to 'never'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) ginkgo.By("routing requests to the mainline upstream when header is set to anything else") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "otherheadervalue"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body(). + Contains(framework.HTTPBunService). + NotContains(canaryService) }) }) ginkgo.Context("when canaried by header with value and pattern", func() { ginkgo.It("should route requests to the correct upstream", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -507,13 +617,18 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, f.Namespace, canaryService, - 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the canary upstream when header pattern is matched") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "DoCanary"). Expect(). @@ -522,19 +637,25 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.By("routing requests to the mainline upstream when header failed to match header value") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "Docanary"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body().Contains(framework.HTTPBunService).NotContains(canaryService) }) ginkgo.It("should route requests to the correct upstream", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -550,25 +671,36 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, f.Namespace, canaryService, - 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the mainline upstream when header is set to 'DoCananry' and header-value is 'DoCanary'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "DoCananry"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body().Contains(framework.HTTPBunService).NotContains(canaryService) }) ginkgo.It("should routes to mainline upstream when the given Regex causes error", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -584,30 +716,40 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, f.Namespace, canaryService, - 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the mainline upstream when the given Regex causes error") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "DoCanary"). WithCookie("CanaryByCookie", "always"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body().Contains(framework.HTTPBunService).NotContains(canaryService) }) }) ginkgo.Context("when canaried by header with value and cookie", func() { ginkgo.It("should route requests to the correct upstream", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -623,13 +765,18 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the canary upstream when header value does not match and cookie is set to 'always'") f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("CanaryByHeader", "otherheadervalue"). WithCookie("CanaryByCookie", "always"). @@ -641,12 +788,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.Context("when canaried by cookie", func() { ginkgo.It("respects always and never values", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -660,14 +812,19 @@ var _ = framework.DescribeAnnotation("canary-*", func() { canaryIngName := fmt.Sprintf("%v-canary", host) - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) ginkgo.By("routing requests to the canary upstream when cookie is set to 'always'") for i := 0; i < 50; i++ { f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithCookie("Canary-By-Cookie", "always"). Expect(). @@ -678,12 +835,12 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.By("routing requests to the mainline upstream when cookie is set to 'never'") for i := 0; i < 50; i++ { f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithCookie("Canary-By-Cookie", "never"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body().Contains(framework.HTTPBunService).NotContains(canaryService) } ginkgo.By("routing requests to the mainline upstream when cookie is set to anything else") @@ -691,24 +848,29 @@ var _ = framework.DescribeAnnotation("canary-*", func() { // This test relies on canary cookie not parsing into the valid // affinity data and canary weight not being specified at all. f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithCookie("Canary-By-Cookie", "badcookievalue"). Expect(). Status(http.StatusOK). - Body().Contains(framework.EchoService).NotContains(canaryService) + Body().Contains(framework.HTTPBunService).NotContains(canaryService) } }) }) ginkgo.Context("when canaried by weight", func() { ginkgo.It("should route requests only to mainline if canary weight is 0", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -721,9 +883,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight": "0", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -731,22 +898,27 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). Expect(). Status(http.StatusOK). Body(). - Contains(framework.EchoService). + Contains(framework.HTTPBunService). NotContains(canaryService) }) ginkgo.It("should route requests only to canary if canary weight is 100", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -759,12 +931,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight": "100", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). Expect(). Status(http.StatusOK). @@ -773,12 +950,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) ginkgo.It("should route requests only to canary if canary weight is equal to canary weight total", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -792,12 +974,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight-total": "1000", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). Expect(). Status(http.StatusOK). @@ -806,12 +993,17 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) ginkgo.It("should route requests split between mainline and canary if canary weight is 50", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -824,20 +1016,30 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight": "50", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) TestMainlineCanaryDistribution(f, host) }) ginkgo.It("should route requests split between mainline and canary if canary weight is 100 and weight total is 200", func() { - host := "foo" + host := fooHost annotations := map[string]string{} - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -851,9 +1053,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight-total": "200", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) TestMainlineCanaryDistribution(f, host) }) @@ -861,24 +1068,30 @@ var _ = framework.DescribeAnnotation("canary-*", func() { ginkgo.Context("Single canary Ingress", func() { ginkgo.It("should not use canary as a catch-all server", func() { - host := "foo" + host := fooHost canaryIngName := fmt.Sprintf("%v-canary", host) annotations := map[string]string{ "nginx.ingress.kubernetes.io/canary": "true", "nginx.ingress.kubernetes.io/canary-by-header": "CanaryByHeader", } - ing := framework.NewSingleCatchAllIngress(canaryIngName, - f.Namespace, canaryService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleCatchAllIngress( + canaryIngName, + f.Namespace, + canaryService, + 80, + annotations)) - ing = framework.NewSingleCatchAllIngress(host, f.Namespace, - framework.EchoService, 80, nil) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleCatchAllIngress( + host, + f.Namespace, + framework.HTTPBunService, + 80, + nil)) f.WaitForNginxServer("_", func(server string) bool { - upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, framework.EchoService, "80") + upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, framework.HTTPBunService, "80") canaryUpstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, canaryService, "80") return strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, host)) && @@ -889,21 +1102,31 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) ginkgo.It("should not use canary with domain as a server", func() { - host := "foo" + host := fooHost canaryIngName := fmt.Sprintf("%v-canary", host) annotations := map[string]string{ "nginx.ingress.kubernetes.io/canary": "true", "nginx.ingress.kubernetes.io/canary-by-header": "CanaryByHeader", } - ing := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + annotations)) otherHost := "bar" - ing = framework.NewSingleIngress(otherHost, "/", otherHost, - f.Namespace, framework.EchoService, 80, nil) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + otherHost, + "/info", + otherHost, + f.Namespace, + framework.HTTPBunService, + 80, + nil)) f.WaitForNginxConfiguration(func(cfg string) bool { return strings.Contains(cfg, "server_name "+otherHost) && @@ -913,7 +1136,7 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) ginkgo.It("does not crash when canary ingress has multiple paths to the same non-matching backend", func() { - host := "foo" + host := fooHost canaryIngName := fmt.Sprintf("%v-canary", host) annotations := map[string]string{ "nginx.ingress.kubernetes.io/canary": "true", @@ -921,13 +1144,22 @@ var _ = framework.DescribeAnnotation("canary-*", func() { } paths := []string{"/foo", "/bar"} - ing := framework.NewSingleIngressWithMultiplePaths(canaryIngName, paths, host, - f.Namespace, "httpy-svc-canary", 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngressWithMultiplePaths( + canaryIngName, + paths, + host, + f.Namespace, + "httpy-svc-canary", + 80, + annotations)) - ing = framework.NewSingleIngress(host, "/", host, f.Namespace, - framework.EchoService, 80, nil) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, f.Namespace, + framework.HTTPBunService, + 80, + nil)) f.WaitForNginxServer(host, func(server string) bool { @@ -936,7 +1168,7 @@ var _ = framework.DescribeAnnotation("canary-*", func() { }) ginkgo.Context("canary affinity behavior", func() { - host := "foo" + host := fooHost affinityCookieName := "aff" canaryIngName := fmt.Sprintf("%v-canary", host) @@ -946,9 +1178,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/session-cookie-name": affinityCookieName, } - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -964,14 +1201,19 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/canary-weight": "1", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) // This request will produce affinity cookie coming from the canary // backend. forcedRequestToCanary := f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("ForceCanary", "yes"). Expect(). @@ -988,7 +1230,7 @@ var _ = framework.DescribeAnnotation("canary-*", func() { // routed to a specific backend. for i := 0; i < 50; i++ { f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithCookie(affinityCookieName, affinityCookie.Raw().Value). Expect(). @@ -1003,9 +1245,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/session-cookie-name": affinityCookieName, } - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -1022,14 +1269,19 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/affinity-canary-behavior": "sticky", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) // This request will produce affinity cookie coming from the canary // backend. forcedRequestToCanary := f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("ForceCanary", "yes"). Expect(). @@ -1046,7 +1298,7 @@ var _ = framework.DescribeAnnotation("canary-*", func() { // routed to a specific backend. for i := 0; i < 50; i++ { f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithCookie(affinityCookieName, affinityCookie.Raw().Value). Expect(). @@ -1061,9 +1313,14 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/session-cookie-name": affinityCookieName, } - ing := framework.NewSingleIngress(host, "/", host, - f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/info", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -1080,14 +1337,19 @@ var _ = framework.DescribeAnnotation("canary-*", func() { "nginx.ingress.kubernetes.io/affinity-canary-behavior": "legacy", } - canaryIng := framework.NewSingleIngress(canaryIngName, "/", host, - f.Namespace, canaryService, 80, canaryAnnotations) - f.EnsureIngress(canaryIng) + f.EnsureIngress(framework.NewSingleIngress( + canaryIngName, + "/info", + host, + f.Namespace, + canaryService, + 80, + canaryAnnotations)) // This request will produce affinity cookie coming from the canary // backend. forcedRequestToCanary := f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). WithHeader("ForceCanary", "yes"). Expect(). @@ -1108,12 +1370,11 @@ var _ = framework.DescribeAnnotation("canary-*", func() { TestMainlineCanaryDistribution(f, host) }) }) - }) // This method assumes canary weight being configured at 50%. func TestMainlineCanaryDistribution(f *framework.Framework, host string) { - re := regexp.MustCompile(fmt.Sprintf(`%s.*`, framework.EchoService)) + re := regexp.MustCompile(fmt.Sprintf(`%s.*`, framework.HTTPBunService)) replicaRequestCount := map[string]int{} // The implementation of choice by weight doesn't guarantee exact @@ -1124,7 +1385,7 @@ func TestMainlineCanaryDistribution(f *framework.Framework, host string) { for i := 0; i < requestsToGet; i++ { body := f.HTTPTestClient(). - GET("/"). + GET("/info"). WithHeader("Host", host). Expect(). Status(http.StatusOK).Body().Raw() @@ -1143,6 +1404,14 @@ func TestMainlineCanaryDistribution(f *framework.Framework, host string) { assert.Equal(ginkgo.GinkgoT(), 2, len(keys)) - assert.GreaterOrEqual(ginkgo.GinkgoT(), int(replicaRequestCount[keys[0].String()]), requestsNumberToTest) - assert.GreaterOrEqual(ginkgo.GinkgoT(), int(replicaRequestCount[keys[1].String()]), requestsNumberToTest) + assert.GreaterOrEqual( + ginkgo.GinkgoT(), + replicaRequestCount[keys[0].String()], + requestsNumberToTest, + ) + assert.GreaterOrEqual( + ginkgo.GinkgoT(), + replicaRequestCount[keys[1].String()], + requestsNumberToTest, + ) } diff --git a/test/e2e/annotations/clientbodybuffersize.go b/test/e2e/annotations/clientbodybuffersize.go index 4ea1943bd..15a3530f3 100644 --- a/test/e2e/annotations/clientbodybuffersize.go +++ b/test/e2e/annotations/clientbodybuffersize.go @@ -25,6 +25,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const clientBodyBufferSizeHost = "client-body-buffer-size.com" + var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { f := framework.NewDefaultFramework("clientbodybuffersize") @@ -33,7 +35,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should set client_body_buffer_size to 1000", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1000" annotations := make(map[string]string) @@ -55,7 +57,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should set client_body_buffer_size to 1K", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1K" annotations := make(map[string]string) @@ -77,7 +79,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should set client_body_buffer_size to 1k", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1k" annotations := make(map[string]string) @@ -99,7 +101,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should set client_body_buffer_size to 1m", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1m" annotations := make(map[string]string) @@ -121,7 +123,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should set client_body_buffer_size to 1M", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1M" annotations := make(map[string]string) @@ -143,7 +145,7 @@ var _ = framework.DescribeAnnotation("client-body-buffer-size", func() { }) ginkgo.It("should not set client_body_buffer_size to invalid 1b", func() { - host := "client-body-buffer-size.com" + host := clientBodyBufferSizeHost clientBodyBufferSize := "1b" annotations := make(map[string]string) diff --git a/test/e2e/annotations/connection.go b/test/e2e/annotations/connection.go index 9cfcbacd0..428d85876 100644 --- a/test/e2e/annotations/connection.go +++ b/test/e2e/annotations/connection.go @@ -17,7 +17,6 @@ limitations under the License. package annotations import ( - "fmt" "net/http" "strings" @@ -52,6 +51,6 @@ var _ = framework.DescribeAnnotation("connection-proxy-header", func() { WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body().Contains(fmt.Sprintf("connection=keep-alive")) + Body().Contains("connection=keep-alive") }) }) diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go index f53bd8e9e..a14a5761f 100644 --- a/test/e2e/annotations/cors.go +++ b/test/e2e/annotations/cors.go @@ -25,6 +25,11 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const ( + originHost = "http://origin.com:8080" + corsHost = "cors.foo.com" +) + var _ = framework.DescribeAnnotation("cors-*", func() { f := framework.NewDefaultFramework("cors") @@ -33,7 +38,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should enable cors", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", } @@ -60,7 +65,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should set cors methods to only allow POST, GET", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-methods": "POST, GET", @@ -76,7 +81,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should set cors max-age", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-max-age": "200", @@ -92,7 +97,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should disable cors allow credentials", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-credentials": "false", @@ -108,7 +113,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow origin for cors", func() { - host := "cors.foo.com" + host := corsHost origin := "https://origin.cors.com:8080" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -135,7 +140,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow headers for cors", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-headers": "DNT, User-Agent", @@ -151,7 +156,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should expose headers for cors", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-expose-headers": "X-CustomResponseHeader, X-CustomSecondHeader", @@ -167,7 +172,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow - single origin for multiple cors values", func() { - host := "cors.foo.com" + host := corsHost origin := "https://origin.cors.com:8080" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -194,7 +199,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not allow - single origin for multiple cors values", func() { - host := "cors.foo.com" + host := corsHost origin := "http://no.origin.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -214,7 +219,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow correct origins - single origin for multiple cors values", func() { - host := "cors.foo.com" + host := corsHost badOrigin := "origin.cors.com:8080" origin1 := "https://origin2.cors.com" origin2 := "https://origin.com" @@ -265,7 +270,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not break functionality", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-origin": "*", @@ -289,7 +294,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not break functionality - without `*`", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", } @@ -312,7 +317,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not break functionality with extra domain", func() { - host := "cors.foo.com" + host := corsHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-origin": "*, foo.bar.com", @@ -336,7 +341,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not match", func() { - host := "cors.foo.com" + host := corsHost origin := "https://fooxbar.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -356,8 +361,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow - single origin with required port", func() { - host := "cors.foo.com" - origin := "http://origin.com:8080" + host := corsHost + origin := originHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-origin": "http://origin.cors.com:8080, http://origin.com:8080", @@ -384,8 +389,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not allow - single origin with port and origin without port", func() { - host := "cors.foo.com" - origin := "http://origin.com:8080" + host := corsHost + origin := originHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", "nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin2.cors.com, http://origin.com", @@ -403,7 +408,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not allow - single origin without port and origin with required port", func() { - host := "cors.foo.com" + host := corsHost origin := "http://origin.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -423,7 +428,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow - matching origin with wildcard origin (2 subdomains)", func() { - host := "cors.foo.com" + host := corsHost origin := "http://foo.origin.cors.com" origin2 := "http://bar-foo.origin.cors.com" annotations := map[string]string{ @@ -466,7 +471,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not allow - unmatching origin with wildcard origin (2 subdomains)", func() { - host := "cors.foo.com" + host := corsHost origin := "http://bar.foo.origin.cors.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -486,7 +491,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow - matching origin+port with wildcard origin", func() { - host := "cors.foo.com" + host := corsHost origin := "http://abc.origin.com:8080" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -513,7 +518,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should not allow - portless origin with wildcard origin", func() { - host := "cors.foo.com" + host := corsHost origin := "http://abc.origin.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -533,8 +538,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow correct origins - missing subdomain + origin with wildcard origin and correct origin", func() { - host := "cors.foo.com" - badOrigin := "http://origin.com:8080" + host := corsHost + badOrigin := originHost origin := "http://bar.origin.com:8080" annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-cors": "true", @@ -569,7 +574,7 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) ginkgo.It("should allow - missing origins (should allow all origins)", func() { - host := "cors.foo.com" + host := corsHost origin := "http://origin.com" origin2 := "http://book.origin.com" origin3 := "test.origin.com" @@ -627,4 +632,41 @@ var _ = framework.DescribeAnnotation("cors-*", func() { Status(http.StatusOK).Headers(). ValueEqual("Access-Control-Allow-Origin", []string{"*"}) }) + + ginkgo.It("should allow correct origin but not others - cors allow origin annotations contain trailing comma", func() { + host := corsHost + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/enable-cors": "true", + "nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin-123.cors.com:8080, ,https://origin-321.cors.com:8080,", + } + + ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) + f.EnsureIngress(ing) + + origin1 := "https://origin-123.cors.com:8080" + f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + WithHeader("Origin", origin1). + Expect(). + Headers().ContainsKey("Access-Control-Allow-Origin") + + origin2 := "https://origin-321.cors.com:8080" + f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + WithHeader("Origin", origin2). + Expect(). + Status(http.StatusOK).Headers(). + ValueEqual("Access-Control-Allow-Origin", []string{origin2}) + + origin3 := "https://unknown.cors.com:8080" + f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + WithHeader("Origin", origin3). + Expect(). + Headers(). + NotContainsKey("Access-Control-Allow-Origin") + }) }) diff --git a/test/e2e/annotations/customhttperrors.go b/test/e2e/annotations/customhttperrors.go index 3862f817b..37a3e9695 100644 --- a/test/e2e/annotations/customhttperrors.go +++ b/test/e2e/annotations/customhttperrors.go @@ -27,7 +27,7 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) -func errorBlockName(upstreamName string, errorCode string) string { +func errorBlockName(upstreamName, errorCode string) string { return fmt.Sprintf("@custom_%s_%s", upstreamName, errorCode) } diff --git a/test/e2e/annotations/default_backend.go b/test/e2e/annotations/default_backend.go index 53c98a307..72ca303b5 100644 --- a/test/e2e/annotations/default_backend.go +++ b/test/e2e/annotations/default_backend.go @@ -48,18 +48,18 @@ var _ = framework.DescribeAnnotation("default-backend", func() { return strings.Contains(server, fmt.Sprintf("server_name %v", host)) }) - requestId := "something-unique" + requestID := "something-unique" f.HTTPTestClient(). GET("/alma/armud"). WithHeader("Host", host). - WithHeader("x-request-id", requestId). + WithHeader("x-request-id", requestID). Expect(). Status(http.StatusOK). Body().Contains("x-code=503"). Contains(fmt.Sprintf("x-ingress-name=%s", host)). Contains("x-service-name=invalid"). - Contains(fmt.Sprintf("x-request-id=%s", requestId)) + Contains(fmt.Sprintf("x-request-id=%s", requestID)) }) }) }) diff --git a/test/e2e/annotations/disableproxyintercepterrors.go b/test/e2e/annotations/disableproxyintercepterrors.go new file mode 100644 index 000000000..17efa4588 --- /dev/null +++ b/test/e2e/annotations/disableproxyintercepterrors.go @@ -0,0 +1,100 @@ +/* +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 ( + "fmt" + "net/http" + "strings" + + networking "k8s.io/api/networking/v1" + + "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/assert" + "k8s.io/ingress-nginx/test/e2e/framework" +) + +var _ = framework.DescribeAnnotation("disable-proxy-intercept-errors", func() { + f := framework.NewDefaultFramework("disable-proxy-intercept-errors") + + ginkgo.BeforeEach(func() { + f.NewHttpbunDeployment() + f.NewEchoDeployment() + }) + + ginkgo.It("configures Nginx correctly", func() { + host := "pie.foo.com" + + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/custom-http-errors": "404", + "nginx.ingress.kubernetes.io/disable-proxy-intercept-errors": "true", + "nginx.ingress.kubernetes.io/default-backend": framework.EchoService, + } + + ingHTTPBunService := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, annotations) + f.EnsureIngress(ingHTTPBunService) + + var serverConfig string + f.WaitForNginxServer(host, func(sc string) bool { + serverConfig = sc + return strings.Contains(serverConfig, fmt.Sprintf("server_name %s", host)) + }) + + ginkgo.By("turning off proxy_intercept_errors directive") + assert.NotContains(ginkgo.GinkgoT(), serverConfig, "proxy_intercept_errors on;") + + // the plan for client side testing + // create ingress where we disable intercept for code 404 - that error should get to the client + // the same ingress should intercept any other error (>300 and not 404) where we will get intercepted error + ginkgo.By("client test to check response - with intercept disabled") + requestID := "proxy_intercept_errors" + + f.HTTPTestClient(). + GET("/status/404"). + WithHeader("Host", host). + WithHeader("x-request-id", requestID). + Expect(). + Status(http.StatusNotFound). + Body().Empty() + + ginkgo.By("client test to check response - with intercept enabled") + err := framework.UpdateIngress(f.KubeClientSet, f.Namespace, host, func(ingress *networking.Ingress) error { + ingress.ObjectMeta.Annotations["nginx.ingress.kubernetes.io/disable-proxy-intercept-errors"] = "false" + return nil + }) + assert.Nil(ginkgo.GinkgoT(), err) + + f.WaitForNginxServer(host, func(sc string) bool { + if serverConfig != sc { + serverConfig = sc + return true + } + return false + }) + + f.HTTPTestClient(). + GET("/status/404"). + WithHeader("Host", host). + WithHeader("x-request-id", requestID). + Expect(). + Status(http.StatusOK). + Body().Contains("x-code=404"). + Contains(fmt.Sprintf("x-ingress-name=%s", host)). + Contains(fmt.Sprintf("x-service-name=%s", framework.HTTPBunService)). + Contains(fmt.Sprintf("x-request-id=%s", requestID)) + }) +}) diff --git a/test/e2e/annotations/fastcgi.go b/test/e2e/annotations/fastcgi.go index 572eca548..bcf1c3487 100644 --- a/test/e2e/annotations/fastcgi.go +++ b/test/e2e/annotations/fastcgi.go @@ -75,14 +75,14 @@ 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", }, } f.EnsureConfigMap(configuration) - host := "fastcgi-params-configmap" + host := "fastcgi-params-configmap" //#nosec G101 annotations := map[string]string{ "nginx.ingress.kubernetes.io/backend-protocol": "FCGI", @@ -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\";") }) }) diff --git a/test/e2e/annotations/fromtowwwredirect.go b/test/e2e/annotations/fromtowwwredirect.go index 9201bedb7..b69cce93e 100644 --- a/test/e2e/annotations/fromtowwwredirect.go +++ b/test/e2e/annotations/fromtowwwredirect.go @@ -62,6 +62,15 @@ var _ = framework.DescribeAnnotation("from-to-www-redirect", func() { }) ginkgo.It("should redirect from www HTTPS to HTTPS", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + ginkgo.By("setting up server for redirect from www") fromHost := fmt.Sprintf("%s.nip.io", f.GetNginxIP()) @@ -90,7 +99,7 @@ var _ = framework.DescribeAnnotation("from-to-www-redirect", func() { ginkgo.By("sending request to www should redirect to domain") f.HTTPTestClientWithTLSConfig(&tls.Config{ - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing ServerName: toHost, }). GET("/"). @@ -102,7 +111,7 @@ var _ = framework.DescribeAnnotation("from-to-www-redirect", func() { ginkgo.By("sending request to domain should not redirect to www") f.HTTPTestClientWithTLSConfig(&tls.Config{ - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing ServerName: fromHost, }). GET("/"). diff --git a/test/e2e/annotations/globalratelimit.go b/test/e2e/annotations/globalratelimit.go index 2efcc3558..96be467fe 100644 --- a/test/e2e/annotations/globalratelimit.go +++ b/test/e2e/annotations/globalratelimit.go @@ -47,7 +47,7 @@ var _ = framework.DescribeAnnotation("annotation-global-rate-limit", func() { ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) ing = f.EnsureIngress(ing) - namespace := strings.Replace(string(ing.UID), "-", "", -1) + namespace := strings.ReplaceAll(string(ing.UID), "-", "") serverConfig := "" f.WaitForNginxServer(host, func(server string) bool { diff --git a/test/e2e/annotations/grpc.go b/test/e2e/annotations/grpc.go index 046191b21..b256b8ae4 100644 --- a/test/e2e/annotations/grpc.go +++ b/test/e2e/annotations/grpc.go @@ -17,19 +17,17 @@ limitations under the License. package annotations import ( + "context" "crypto/tls" "fmt" "strings" - "context" - pb "github.com/moul/pb/grpcbin/go-grpc" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" - core "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -37,8 +35,10 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const echoHost = "echo" + var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { - f := framework.NewDefaultFramework("grpc") + f := framework.NewDefaultFramework("grpc", framework.WithHTTPBunEnabled()) ginkgo.It("should use grpc_pass in the configuration file", func() { f.NewGRPCFortuneTellerDeployment() @@ -68,9 +68,9 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { ginkgo.It("should return OK for service with backend protocol GRPC", func() { f.NewGRPCBinDeployment() - host := "echo" + host := echoHost - svc := &core.Service{ + svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "grpcbin-test", Namespace: f.Namespace, @@ -103,14 +103,15 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { return strings.Contains(server, "grpc_pass grpc://upstream_balancer;") }) - conn, _ := grpc.Dial(f.GetNginxIP()+":443", + conn, err := grpc.Dial(f.GetNginxIP()+":443", grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ - ServerName: "echo", - InsecureSkipVerify: true, + ServerName: echoHost, + InsecureSkipVerify: true, //nolint:gosec // Ignore certificate validation in testing }), ), ) + assert.Nil(ginkgo.GinkgoT(), err, "error creating a connection") defer conn.Close() client := pb.NewGRPCBinClient(conn) @@ -121,15 +122,14 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { metadata := res.GetMetadata() assert.Equal(ginkgo.GinkgoT(), metadata["content-type"].Values[0], "application/grpc") + assert.Equal(ginkgo.GinkgoT(), metadata[":authority"].Values[0], host) }) ginkgo.It("authorization metadata should be overwritten by external auth response headers", func() { f.NewGRPCBinDeployment() - f.NewHttpbunDeployment() + host := echoHost - host := "echo" - - svc := &core.Service{ + svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "grpcbin-test", Namespace: f.Namespace, @@ -149,19 +149,8 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { } f.EnsureService(svc) - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - - httpbunIP := e.Subsets[0].Addresses[0].IP - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/response-headers?authorization=foo", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/response-headers?authorization=foo", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-response-headers": "Authorization", "nginx.ingress.kubernetes.io/backend-protocol": "GRPC", } @@ -175,14 +164,15 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { return strings.Contains(server, "grpc_pass grpc://upstream_balancer;") }) - conn, _ := grpc.Dial(f.GetNginxIP()+":443", + conn, err := grpc.Dial(f.GetNginxIP()+":443", grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ - ServerName: "echo", - InsecureSkipVerify: true, + ServerName: echoHost, + InsecureSkipVerify: true, //nolint:gosec // Ignore certificate validation in testing }), ), ) + assert.Nil(ginkgo.GinkgoT(), err) defer conn.Close() client := pb.NewGRPCBinClient(conn) @@ -193,15 +183,24 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { assert.Nil(ginkgo.GinkgoT(), err) metadata := res.GetMetadata() - assert.Equal(ginkgo.GinkgoT(), "foo", metadata["authorization"].Values[0]) + assert.Equal(ginkgo.GinkgoT(), fooHost, metadata["authorization"].Values[0]) }) ginkgo.It("should return OK for service with backend protocol GRPCS", func() { f.NewGRPCBinDeployment() - host := "echo" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() - svc := &core.Service{ + host := echoHost + + svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "grpcbin-test", Namespace: f.Namespace, @@ -239,14 +238,15 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { return strings.Contains(server, "grpc_pass grpcs://upstream_balancer;") }) - conn, _ := grpc.Dial(f.GetNginxIP()+":443", + conn, err := grpc.Dial(f.GetNginxIP()+":443", grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ - ServerName: "echo", - InsecureSkipVerify: true, + ServerName: echoHost, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing }), ), ) + assert.Nil(ginkgo.GinkgoT(), err) defer conn.Close() client := pb.NewGRPCBinClient(conn) diff --git a/test/e2e/annotations/ipwhitelist.go b/test/e2e/annotations/ipallowlist.go similarity index 81% rename from test/e2e/annotations/ipwhitelist.go rename to test/e2e/annotations/ipallowlist.go index 71f026c7f..79c77b4d0 100644 --- a/test/e2e/annotations/ipwhitelist.go +++ b/test/e2e/annotations/ipallowlist.go @@ -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) diff --git a/test/e2e/annotations/mirror.go b/test/e2e/annotations/mirror.go index ad178a947..787cbfa3b 100644 --- a/test/e2e/annotations/mirror.go +++ b/test/e2e/annotations/mirror.go @@ -60,7 +60,7 @@ var _ = framework.DescribeAnnotation("mirror-*", func() { func(server string) bool { return strings.Contains(server, fmt.Sprintf("mirror /_mirror-%v;", ing.UID)) && strings.Contains(server, "mirror_request_body on;") && - strings.Contains(server, "proxy_pass https://test.env.com/$request_uri;") + strings.Contains(server, `proxy_pass "https://test.env.com/$request_uri";`) }) }) diff --git a/test/e2e/annotations/modsecurity/modsecurity.go b/test/e2e/annotations/modsecurity/modsecurity.go index 62c9bc843..a3e7d80ba 100644 --- a/test/e2e/annotations/modsecurity/modsecurity.go +++ b/test/e2e/annotations/modsecurity/modsecurity.go @@ -25,6 +25,17 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const ( + modSecurityFooHost = "modsecurity.foo.com" + defaultSnippet = `SecRuleEngine On + 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\'\"` +) + var _ = framework.DescribeAnnotation("modsecurity owasp", func() { f := framework.NewDefaultFramework("modsecuritylocation") @@ -33,7 +44,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity", func() { - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -51,7 +62,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity with transaction ID and OWASP rules", func() { - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -72,7 +83,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should disable modsecurity", func() { - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -89,7 +100,16 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity with snippet", func() { - host := "modsecurity.foo.com" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -109,10 +129,11 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { ginkgo.It("should enable modsecurity without using 'modsecurity on;'", func() { f.SetNginxConfigMapData(map[string]string{ - "enable-modsecurity": "true"}, + "enable-modsecurity": "true", + }, ) - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -131,10 +152,11 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { ginkgo.It("should disable modsecurity using 'modsecurity off;'", func() { f.SetNginxConfigMapData(map[string]string{ - "enable-modsecurity": "true"}, + "enable-modsecurity": "true", + }, ) - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace annotations := map[string]string{ @@ -151,16 +173,19 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity with snippet and block requests", func() { - host := "modsecurity.foo.com" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + host := modSecurityFooHost nameSpace := f.Namespace - snippet := `SecRuleEngine On - 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\'\"` + snippet := defaultSnippet annotations := map[string]string{ "nginx.ingress.kubernetes.io/enable-modsecurity": "true", @@ -187,16 +212,19 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity globally and with modsecurity-snippet block requests", func() { - host := "modsecurity.foo.com" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + host := modSecurityFooHost nameSpace := f.Namespace - snippet := `SecRuleEngine On - 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\'\"` + snippet := defaultSnippet annotations := map[string]string{ "nginx.ingress.kubernetes.io/modsecurity-snippet": snippet, @@ -223,16 +251,21 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity when enable-owasp-modsecurity-crs is set to true", func() { - host := "modsecurity.foo.com" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + "enable-modsecurity": "true", + "enable-owasp-modsecurity-crs": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + host := modSecurityFooHost nameSpace := f.Namespace - snippet := `SecRuleEngine On - 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\'\"` + snippet := defaultSnippet annotations := map[string]string{ "nginx.ingress.kubernetes.io/modsecurity-snippet": snippet, @@ -243,11 +276,6 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { ing := framework.NewSingleIngress(host, "/", host, nameSpace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) - f.SetNginxConfigMapData(map[string]string{ - "enable-modsecurity": "true", - "enable-owasp-modsecurity-crs": "true", - }) - f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, "SecRuleEngine On") @@ -262,7 +290,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity through the config map", func() { - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace snippet := `SecRequestBodyAccess On @@ -282,12 +310,17 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { f.EnsureIngress(ing) expectedComment := "SecRuleEngine On" - f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", "enable-modsecurity": "true", "enable-owasp-modsecurity-crs": "true", "modsecurity-snippet": expectedComment, }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() f.WaitForNginxServer(host, func(server string) bool { @@ -303,7 +336,7 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should enable modsecurity through the config map but ignore snippet as disabled by admin", func() { - host := "modsecurity.foo.com" + host := modSecurityFooHost nameSpace := f.Namespace snippet := `SecRequestBodyAccess On @@ -345,7 +378,15 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() { }) ginkgo.It("should disable default modsecurity conf setting when modsecurity-snippet is specified", func() { - host := "modsecurity.foo.com" + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + host := modSecurityFooHost nameSpace := f.Namespace snippet := `SecRuleEngine On diff --git a/test/e2e/annotations/proxy.go b/test/e2e/annotations/proxy.go index c0c89eb43..235b828e7 100644 --- a/test/e2e/annotations/proxy.go +++ b/test/e2e/annotations/proxy.go @@ -25,6 +25,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const proxyRedirectToHost = "goodbye.com" + var _ = framework.DescribeAnnotation("proxy-*", func() { f := framework.NewDefaultFramework("proxy") host := "proxy.foo.com" @@ -38,7 +40,7 @@ var _ = framework.DescribeAnnotation("proxy-*", func() { annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-redirect-from"] = proxyRedirectFrom - annotations["nginx.ingress.kubernetes.io/proxy-redirect-to"] = "goodbye.com" + annotations["nginx.ingress.kubernetes.io/proxy-redirect-to"] = proxyRedirectToHost ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -54,7 +56,7 @@ var _ = framework.DescribeAnnotation("proxy-*", func() { annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-redirect-from"] = proxyRedirectFrom - annotations["nginx.ingress.kubernetes.io/proxy-redirect-to"] = "goodbye.com" + annotations["nginx.ingress.kubernetes.io/proxy-redirect-to"] = proxyRedirectToHost ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -67,7 +69,7 @@ var _ = framework.DescribeAnnotation("proxy-*", func() { ginkgo.It("should set proxy_redirect to hello.com goodbye.com", func() { proxyRedirectFrom := "hello.com" - proxyRedirectTo := "goodbye.com" + proxyRedirectTo := proxyRedirectToHost annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-redirect-from"] = proxyRedirectFrom @@ -244,5 +246,4 @@ var _ = framework.DescribeAnnotation("proxy-*", func() { return strings.Contains(server, fmt.Sprintf("proxy_http_version %s;", proxyHTTPVersion)) }) }) - }) diff --git a/test/e2e/annotations/proxyssl.go b/test/e2e/annotations/proxyssl.go index 7f14c3393..989d681c1 100644 --- a/test/e2e/annotations/proxyssl.go +++ b/test/e2e/annotations/proxyssl.go @@ -27,6 +27,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const proxySSLHost = "proxyssl.foo.com" + var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { f := framework.NewDefaultFramework("proxyssl") @@ -35,7 +37,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { }) ginkgo.It("should set valid proxy-ssl-secret", func() { - host := "proxyssl.foo.com" + host := proxySSLHost annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + host @@ -62,7 +64,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { }) ginkgo.It("should set valid proxy-ssl-secret, proxy-ssl-verify to on, proxy-ssl-verify-depth to 2, and proxy-ssl-server-name to on", func() { - host := "proxyssl.foo.com" + host := proxySSLHost annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + host annotations["nginx.ingress.kubernetes.io/proxy-ssl-verify"] = "on" @@ -90,9 +92,9 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { Expect(). Status(http.StatusOK) }) - + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should set valid proxy-ssl-secret, proxy-ssl-ciphers to HIGH:!AES", func() { - host := "proxyssl.foo.com" + host := proxySSLHost annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + host annotations["nginx.ingress.kubernetes.io/proxy-ssl-ciphers"] = "HIGH:!AES" @@ -118,9 +120,9 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { Expect(). Status(http.StatusOK) }) - + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should set valid proxy-ssl-secret, proxy-ssl-protocols", func() { - host := "proxyssl.foo.com" + host := proxySSLHost annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + host annotations["nginx.ingress.kubernetes.io/proxy-ssl-protocols"] = "TLSv1.2 TLSv1.3" @@ -195,7 +197,6 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { strings.Contains(server, "proxy_ssl_certificate_key")) }) }) - }) func assertProxySSL(f *framework.Framework, host, sslName, ciphers, protocols, verify string, depth int, proxySSLServerName string) { diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 79738b984..173df29f0 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -27,6 +27,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const rewriteHost = "rewrite.bar.com" + var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-log", func() { f := framework.NewDefaultFramework("rewrite") @@ -37,7 +39,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo ginkgo.It("should write rewrite logs", func() { ginkgo.By("setting enable-rewrite-log annotation") - host := "rewrite.bar.com" + host := rewriteHost annotations := map[string]string{ "nginx.ingress.kubernetes.io/rewrite-target": "/", "nginx.ingress.kubernetes.io/enable-rewrite-log": "true", @@ -64,7 +66,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo }) ginkgo.It("should use correct longest path match", func() { - host := "rewrite.bar.com" + host := rewriteHost ginkgo.By("creating a regular ingress definition") ing := framework.NewSingleIngress("kube-lego", "/.well-known/acme/challenge", host, f.Namespace, framework.EchoService, 80, nil) @@ -109,10 +111,10 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo }) ginkgo.It("should use ~* location modifier if regex annotation is present", func() { - host := "rewrite.bar.com" + host := rewriteHost ginkgo.By("creating a regular ingress definition") - ing := framework.NewSingleIngress("foo", "/foo", host, f.Namespace, framework.EchoService, 80, nil) + ing := framework.NewSingleIngress(fooHost, "/foo", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -156,10 +158,10 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo }) ginkgo.It("should fail to use longest match for documented warning", func() { - host := "rewrite.bar.com" + host := rewriteHost ginkgo.By("creating a regular ingress definition") - ing := framework.NewSingleIngress("foo", "/foo/bar/bar", host, f.Namespace, framework.EchoService, 80, nil) + ing := framework.NewSingleIngress(fooHost, "/foo/bar/bar", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) ginkgo.By(`creating an ingress definition with the use-regex annotation`) @@ -188,7 +190,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo }) ginkgo.It("should allow for custom rewrite parameters", func() { - host := "rewrite.bar.com" + host := rewriteHost ginkgo.By(`creating an ingress definition with the use-regex annotation`) annotations := map[string]string{ diff --git a/test/e2e/annotations/satisfy.go b/test/e2e/annotations/satisfy.go index 758ad21a4..6ba6db33e 100644 --- a/test/e2e/annotations/satisfy.go +++ b/test/e2e/annotations/satisfy.go @@ -17,7 +17,6 @@ limitations under the License. package annotations import ( - "context" "fmt" "net/http" "net/url" @@ -27,13 +26,12 @@ import ( "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-nginx/test/e2e/framework" ) var _ = framework.DescribeAnnotation("satisfy", func() { - f := framework.NewDefaultFramework("satisfy") + f := framework.NewDefaultFramework("satisfy", framework.WithHTTPBunEnabled()) ginkgo.BeforeEach(func() { f.NewEchoDeployment() @@ -84,17 +82,6 @@ var _ = framework.DescribeAnnotation("satisfy", func() { ginkgo.It("should allow multiple auth with satisfy any", func() { host := "auth" - // setup external auth - f.NewHttpbunDeployment() - - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - httpbunIP := e.Subsets[0].Addresses[0].IP - // create basic auth secret at ingress s := f.EnsureSecret(buildSecret("uname", "pwd", "basic-secret", f.Namespace)) @@ -105,7 +92,7 @@ var _ = framework.DescribeAnnotation("satisfy", func() { "nginx.ingress.kubernetes.io/auth-realm": "test basic auth", // annotations for external auth - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", // set satisfy any diff --git a/test/e2e/annotations/serversnippet.go b/test/e2e/annotations/serversnippet.go index df1383bdb..1195b728a 100644 --- a/test/e2e/annotations/serversnippet.go +++ b/test/e2e/annotations/serversnippet.go @@ -33,6 +33,15 @@ var _ = framework.DescribeAnnotation("server-snippet", func() { }) ginkgo.It(`add valid directives to server via server snippet`, func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + host := "serversnippet.foo.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/server-snippet": ` @@ -59,6 +68,15 @@ var _ = framework.DescribeAnnotation("server-snippet", func() { }) ginkgo.It(`drops server snippet if disabled by the administrator`, func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + host := "noserversnippet.foo.com" annotations := map[string]string{ "nginx.ingress.kubernetes.io/server-snippet": ` @@ -89,6 +107,5 @@ var _ = framework.DescribeAnnotation("server-snippet", func() { Status(http.StatusOK).Headers(). NotContainsKey("Foo"). NotContainsKey("Xpto") - }) }) diff --git a/test/e2e/annotations/serviceupstream.go b/test/e2e/annotations/serviceupstream.go index 94ad6d346..1d80f304a 100644 --- a/test/e2e/annotations/serviceupstream.go +++ b/test/e2e/annotations/serviceupstream.go @@ -25,10 +25,10 @@ import ( "github.com/stretchr/testify/assert" "k8s.io/ingress-nginx/test/e2e/framework" - - "k8s.io/ingress-nginx/internal/nginx" ) +const dbgCmd = "/dbg backends all" + var _ = framework.DescribeAnnotation("service-upstream", func() { f := framework.NewDefaultFramework("serviceupstream") host := "serviceupstream" @@ -59,10 +59,9 @@ var _ = framework.DescribeAnnotation("service-upstream", func() { ginkgo.By("checking if the Service Cluster IP and Port are used") s := f.GetService(f.Namespace, framework.EchoService) - curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort) - output, err := f.ExecIngressPod(curlCmd) + output, err := f.ExecIngressPod(dbgCmd) assert.Nil(ginkgo.GinkgoT(), err) - assert.Contains(ginkgo.GinkgoT(), output, fmt.Sprintf(`{"address":"%s"`, s.Spec.ClusterIP)) + assert.Contains(ginkgo.GinkgoT(), output, fmt.Sprintf(`"address": %q`, s.Spec.ClusterIP)) }) }) @@ -88,10 +87,9 @@ var _ = framework.DescribeAnnotation("service-upstream", func() { ginkgo.By("checking if the Service Cluster IP and Port are used") s := f.GetService(f.Namespace, framework.EchoService) - curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort) - output, err := f.ExecIngressPod(curlCmd) + output, err := f.ExecIngressPod(dbgCmd) assert.Nil(ginkgo.GinkgoT(), err) - assert.Contains(ginkgo.GinkgoT(), output, fmt.Sprintf(`{"address":"%s"`, s.Spec.ClusterIP)) + assert.Contains(ginkgo.GinkgoT(), output, fmt.Sprintf(`"address": %q`, s.Spec.ClusterIP)) }) }) @@ -119,10 +117,9 @@ var _ = framework.DescribeAnnotation("service-upstream", func() { ginkgo.By("checking if the Service Cluster IP and Port are not used") s := f.GetService(f.Namespace, framework.EchoService) - curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort) - output, err := f.ExecIngressPod(curlCmd) + output, err := f.ExecIngressPod(dbgCmd) assert.Nil(ginkgo.GinkgoT(), err) - assert.NotContains(ginkgo.GinkgoT(), output, fmt.Sprintf(`{"address":"%s"`, s.Spec.ClusterIP)) + assert.NotContains(ginkgo.GinkgoT(), output, fmt.Sprintf(`"address": %q`, s.Spec.ClusterIP)) }) }) }) diff --git a/test/e2e/annotations/snippet.go b/test/e2e/annotations/snippet.go index 367708302..0c6148a4f 100644 --- a/test/e2e/annotations/snippet.go +++ b/test/e2e/annotations/snippet.go @@ -26,21 +26,35 @@ import ( ) var _ = framework.DescribeAnnotation("configuration-snippet", func() { - f := framework.NewDefaultFramework("configurationsnippet") + f := framework.NewDefaultFramework( + "configurationsnippet", + framework.WithHTTPBunEnabled(), + ) - ginkgo.BeforeEach(func() { - f.NewEchoDeployment() - }) - - ginkgo.It(`set snippet "more_set_headers "Foo1: Bar1";" in all locations"`, func() { + ginkgo.It("set snippet more_set_headers in all locations", func() { host := "configurationsnippet.foo.com" + + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": ` - more_set_headers "Foo1: Bar1";`, + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Foo1: Bar1";`, } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) - f.EnsureIngress(ing) + f.EnsureIngress(framework.NewSingleIngress( + host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations)) f.WaitForNginxServer(host, func(server string) bool { @@ -51,23 +65,28 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() { GET("/"). WithHeader("Host", host). Expect(). - Status(http.StatusOK).Headers(). + 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() { + ginkgo.It("drops snippet more_set_header 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";`, + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Foo1: Bar1";`, } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) + ing := framework.NewSingleIngress( + host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations) + f.UpdateNginxConfigMapData("allow-snippet-annotations", "false") - defer func() { - // Return to the original value - f.UpdateNginxConfigMapData("allow-snippet-annotations", "true") - }() + // Sleep a while just to guarantee that the configmap is applied framework.Sleep() f.EnsureIngress(ing) @@ -81,7 +100,8 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() { GET("/"). WithHeader("Host", host). Expect(). - Status(http.StatusOK).Headers(). + Status(http.StatusOK). + Headers(). NotContainsKey("Foo1") }) }) diff --git a/test/e2e/annotations/streamsnippet.go b/test/e2e/annotations/streamsnippet.go index b5df50975..432537b4a 100644 --- a/test/e2e/annotations/streamsnippet.go +++ b/test/e2e/annotations/streamsnippet.go @@ -39,6 +39,15 @@ var _ = framework.DescribeSetting("stream-snippet", func() { }) ginkgo.It("should add value of stream-snippet to nginx config", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + host := "foo.com" snippet := `server {listen 8000; proxy_pass 127.0.0.1:80;}` diff --git a/test/e2e/annotations/upstreamhashby.go b/test/e2e/annotations/upstreamhashby.go index 023a094aa..1b8106662 100644 --- a/test/e2e/annotations/upstreamhashby.go +++ b/test/e2e/annotations/upstreamhashby.go @@ -39,12 +39,13 @@ func startIngress(f *framework.Framework, annotations map[string]string) map[str return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(framework.Poll, framework.DefaultTimeout, func() (bool, error) { - resp := f.HTTPTestClient(). GET("/"). WithHeader("Host", host). Expect().Raw() + defer resp.Body.Close() if resp.StatusCode == http.StatusOK { return true, nil @@ -55,7 +56,9 @@ func startIngress(f *framework.Framework, annotations map[string]string) map[str assert.Nil(ginkgo.GinkgoT(), err) - re, _ := regexp.Compile(fmt.Sprintf(`Hostname: %v.*`, framework.EchoService)) + re, err := regexp.Compile(fmt.Sprintf(`Hostname: %v.*`, framework.EchoService)) + assert.Nil(ginkgo.GinkgoT(), err, "error compiling regex") + podMap := map[string]bool{} for i := 0; i < 100; i++ { diff --git a/test/e2e/dbg/main.go b/test/e2e/dbg/main.go index 2df57469d..6c0a22f69 100644 --- a/test/e2e/dbg/main.go +++ b/test/e2e/dbg/main.go @@ -49,7 +49,7 @@ var _ = framework.IngressNginxDescribe("Debug CLI", func() { assert.Nil(ginkgo.GinkgoT(), err) // Should be 2: the default and the echo deployment - numUpstreams := len(strings.Split(strings.Trim(string(output), "\n"), "\n")) + numUpstreams := len(strings.Split(strings.Trim(output, "\n"), "\n")) assert.Equal(ginkgo.GinkgoT(), numUpstreams, 2) }) @@ -67,7 +67,7 @@ var _ = framework.IngressNginxDescribe("Debug CLI", func() { output, err := f.ExecIngressPod(cmd) assert.Nil(ginkgo.GinkgoT(), err) - backends := strings.Split(string(output), "\n") + backends := strings.Split(output, "\n") assert.Greater(ginkgo.GinkgoT(), len(backends), 0) getCmd := "/dbg backends get " + backends[0] diff --git a/test/e2e/defaultbackend/default_backend.go b/test/e2e/defaultbackend/default_backend.go index e589024b4..936878000 100644 --- a/test/e2e/defaultbackend/default_backend.go +++ b/test/e2e/defaultbackend/default_backend.go @@ -49,15 +49,15 @@ var _ = framework.IngressNginxDescribe("[Default Backend]", func() { {"basic HTTPS POST request without host to path / should return 404", "", framework.HTTPS, "POST", "/", http.StatusNotFound}, {"basic HTTPS POST request without host to path /demo should return 404", "", framework.HTTPS, "POST", "/demo", http.StatusNotFound}, - {"basic HTTP GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "GET", "/", http.StatusNotFound}, - {"basic HTTP GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "GET", "/demo", http.StatusNotFound}, - {"basic HTTPS GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "GET", "/", http.StatusNotFound}, - {"basic HTTPS GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "GET", "/demo", http.StatusNotFound}, + {"basic HTTP GET request to host foo.bar.com and path / should return 404", "foo.bar.com", framework.HTTP, "GET", "/", http.StatusNotFound}, + {"basic HTTP GET request to host foo.bar.com and path /demo should return 404", "foo.bar.com", framework.HTTP, "GET", "/demo", http.StatusNotFound}, + {"basic HTTPS GET request to host foo.bar.com and path / should return 404", "foo.bar.com", framework.HTTPS, "GET", "/", http.StatusNotFound}, + {"basic HTTPS GET request to host foo.bar.com and path /demo should return 404", "foo.bar.com", framework.HTTPS, "GET", "/demo", http.StatusNotFound}, - {"basic HTTP POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "POST", "/", http.StatusNotFound}, - {"basic HTTP POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "POST", "/demo", http.StatusNotFound}, - {"basic HTTPS POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "POST", "/", http.StatusNotFound}, - {"basic HTTPS POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "POST", "/demo", http.StatusNotFound}, + {"basic HTTP POST request to host foo.bar.com and path / should return 404", "foo.bar.com", framework.HTTP, "POST", "/", http.StatusNotFound}, + {"basic HTTP POST request to host foo.bar.com and path /demo should return 404", "foo.bar.com", framework.HTTP, "POST", "/demo", http.StatusNotFound}, + {"basic HTTPS POST request to host foo.bar.com and path / should return 404", "foo.bar.com", framework.HTTPS, "POST", "/", http.StatusNotFound}, + {"basic HTTPS POST request to host foo.bar.com and path /demo should return 404", "foo.bar.com", framework.HTTPS, "POST", "/demo", http.StatusNotFound}, } framework.Sleep() diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index 614dd166a..25d714f88 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -47,6 +47,7 @@ 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" diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 4a7a48d92..2e556cf6d 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -26,6 +26,7 @@ func init() { testing.Init() framework.RegisterParseFlags() } + func TestE2E(t *testing.T) { RunE2ETests(t) } diff --git a/test/e2e/endpointslices/longname.go b/test/e2e/endpointslices/longname.go index 0adb66767..b2242daac 100644 --- a/test/e2e/endpointslices/longname.go +++ b/test/e2e/endpointslices/longname.go @@ -36,7 +36,6 @@ var _ = framework.IngressNginxDescribe("[Endpointslices] long service name", fun }) ginkgo.It("should return 200 when service name has max allowed number of characters 63", func() { - annotations := make(map[string]string) ing := framework.NewSingleIngress(host, "/", host, f.Namespace, name, 80, annotations) f.EnsureIngress(ing) diff --git a/test/e2e/endpointslices/topology.go b/test/e2e/endpointslices/topology.go index 7cc67cedb..38c5f8b76 100644 --- a/test/e2e/endpointslices/topology.go +++ b/test/e2e/endpointslices/topology.go @@ -28,7 +28,6 @@ import ( "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" - "k8s.io/ingress-nginx/internal/nginx" "k8s.io/ingress-nginx/test/e2e/framework" ) @@ -41,7 +40,6 @@ var _ = framework.IngressNginxDescribeSerial("[TopologyHints] topology aware rou }) ginkgo.It("should return 200 when service has topology hints", func() { - annotations := make(map[string]string) ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -72,11 +70,12 @@ var _ = framework.IngressNginxDescribeSerial("[TopologyHints] topology aware rou } } - curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort) - status, err := f.ExecIngressPod(curlCmd) + dbgCmd := "/dbg backends all" + status, err := f.ExecIngressPod(dbgCmd) assert.Nil(ginkgo.GinkgoT(), err) var backends []map[string]interface{} - json.Unmarshal([]byte(status), &backends) + err = json.Unmarshal([]byte(status), &backends) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error unmarshalling backends") gotBackends := 0 for _, bck := range backends { if strings.Contains(bck["name"].(string), "topology") { @@ -85,7 +84,7 @@ var _ = framework.IngressNginxDescribeSerial("[TopologyHints] topology aware rou } if gotHints { - //we have 2 replics, if there is just one backend it means that we are routing according slices hints to same zone as controller is + // we have 2 replics, if there is just one backend it means that we are routing according slices hints to same zone as controller is assert.Equal(ginkgo.GinkgoT(), 1, gotBackends) } else { // two replicas should have two endpoints without topology hints diff --git a/test/e2e/framework/deployment.go b/test/e2e/framework/deployment.go index 06dfa0c5e..08a5353b2 100644 --- a/test/e2e/framework/deployment.go +++ b/test/e2e/framework/deployment.go @@ -43,12 +43,21 @@ const HTTPBunService = "httpbun" // NipService name of external service using nip.io const NIPService = "external-nip" +// HTTPBunImage is the default image that is used to deploy HTTPBun with the framwork +var HTTPBunImage = os.Getenv("HTTPBUN_IMAGE") + +// EchoImage is the default image to be used by the echo service +const EchoImage = "registry.k8s.io/ingress-nginx/e2e-test-echo@sha256:4938d1d91a2b7d19454460a8c1b010b89f6ff92d2987fd889ac3e8fc3b70d91a" //#nosec G101 + +// TODO: change all Deployment functions to use these options +// in order to reduce complexity and have a unified API across the +// framework type deploymentOptions struct { - namespace string name string + namespace string + image string replicas int svcAnnotations map[string]string - image string } // WithDeploymentNamespace allows configuring the deployment's namespace @@ -58,11 +67,11 @@ func WithDeploymentNamespace(n string) func(*deploymentOptions) { } } -// WithSvcTopologyAnnotations create svc with topology aware hints sets to auto +// WithSvcTopologyAnnotations create svc with topology mode sets to auto func WithSvcTopologyAnnotations() func(*deploymentOptions) { return func(o *deploymentOptions) { o.svcAnnotations = map[string]string{ - "service.kubernetes.io/topology-aware-hints": "auto", + corev1.AnnotationTopologyMode: "auto", } } } @@ -100,22 +109,25 @@ func (f *Framework) NewEchoDeployment(opts ...func(*deploymentOptions)) { namespace: f.Namespace, name: EchoService, replicas: 1, - image: "registry.k8s.io/ingress-nginx/e2e-test-echo@sha256:4938d1d91a2b7d19454460a8c1b010b89f6ff92d2987fd889ac3e8fc3b70d91a", + image: EchoImage, } for _, o := range opts { o(options) } - deployment := newDeployment(options.name, options.namespace, options.image, 80, int32(options.replicas), + f.EnsureDeployment(newDeployment( + options.name, + options.namespace, + options.image, + 80, + int32(options.replicas), nil, nil, nil, []corev1.VolumeMount{}, []corev1.Volume{}, true, - ) + )) - f.EnsureDeployment(deployment) - - service := &corev1.Service{ + f.EnsureService(&corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: options.name, Namespace: options.namespace, @@ -134,11 +146,15 @@ func (f *Framework) NewEchoDeployment(opts ...func(*deploymentOptions)) { "app": options.name, }, }, - } + }) - f.EnsureService(service) - - err := WaitForEndpoints(f.KubeClientSet, DefaultTimeout, options.name, options.namespace, options.replicas) + err := WaitForEndpoints( + f.KubeClientSet, + DefaultTimeout, + options.name, + options.namespace, + options.replicas, + ) assert.Nil(ginkgo.GinkgoT(), err, "waiting for endpoints to become ready") } @@ -147,6 +163,12 @@ func BuildNIPHost(ip string) string { return fmt.Sprintf("%s.nip.io", ip) } +// GetNipHost used to generate a nip host for external DNS resolving +// for the instance deployed by the framework +func (f *Framework) GetNIPHost() string { + return BuildNIPHost(f.HTTPBunIP) +} + // BuildNIPExternalNameService used to generate a service pointing to nip.io to // help resolve to an IP address func BuildNIPExternalNameService(f *Framework, ip, portName string) *corev1.Service { @@ -177,22 +199,34 @@ func (f *Framework) NewHttpbunDeployment(opts ...func(*deploymentOptions)) strin namespace: f.Namespace, name: HTTPBunService, replicas: 1, - image: "registry.k8s.io/ingress-nginx/e2e-test-httpbun:v20230505-v0.0.1", + image: HTTPBunImage, } for _, o := range opts { o(options) } - deployment := newDeployment(options.name, options.namespace, options.image, 80, int32(options.replicas), - nil, nil, nil, + // Create the HTTPBun Deployment + f.EnsureDeployment(newDeployment( + options.name, + options.namespace, + options.image, + 80, + int32(options.replicas), + nil, nil, + // Required to get hostname information + []corev1.EnvVar{ + { + Name: "HTTPBUN_INFO_ENABLED", + Value: "1", + }, + }, []corev1.VolumeMount{}, []corev1.Volume{}, true, - ) + )) - f.EnsureDeployment(deployment) - - service := &corev1.Service{ + // Create a service pointing to deployment + f.EnsureService(&corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: options.name, Namespace: options.namespace, @@ -211,14 +245,26 @@ func (f *Framework) NewHttpbunDeployment(opts ...func(*deploymentOptions)) strin "app": options.name, }, }, - } + }) - s := f.EnsureService(service) - - err := WaitForEndpoints(f.KubeClientSet, DefaultTimeout, options.name, options.namespace, options.replicas) + // Wait for deployment to become available + err := WaitForEndpoints( + f.KubeClientSet, + DefaultTimeout, + options.name, + options.namespace, + options.replicas, + ) assert.Nil(ginkgo.GinkgoT(), err, "waiting for endpoints to become ready") - return s.Spec.ClusterIPs[0] + // Get cluster ip for HTTPBun to be used in tests + e, err := f.KubeClientSet. + CoreV1(). + Endpoints(f.Namespace). + Get(context.TODO(), HTTPBunService, metav1.GetOptions{}) + assert.Nil(ginkgo.GinkgoT(), err, "failed to get httpbun endpoint") + + return e.Subsets[0].Addresses[0].IP } // NewSlowEchoDeployment creates a new deployment of the slow echo server image in a particular namespace. @@ -271,18 +317,21 @@ func (f *Framework) GetNginxBaseImage() string { // NGINXDeployment creates a new simple NGINX Deployment using NGINX base image // and passing the desired configuration -func (f *Framework) NGINXDeployment(name string, cfg string, waitendpoint bool) { +func (f *Framework) NGINXDeployment(name, cfg string, waitendpoint bool) { cfgMap := map[string]string{ "nginx.conf": cfg, } - _, err := f.KubeClientSet.CoreV1().ConfigMaps(f.Namespace).Create(context.TODO(), &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: f.Namespace, - }, - Data: cfgMap, - }, metav1.CreateOptions{}) + _, err := f.KubeClientSet. + CoreV1(). + ConfigMaps(f.Namespace). + Create(context.TODO(), &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: f.Namespace, + }, + Data: cfgMap, + }, metav1.CreateOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "creating configmap") deployment := newDeployment(name, f.Namespace, f.GetNginxBaseImage(), 80, 1, @@ -340,7 +389,7 @@ func (f *Framework) NGINXDeployment(name string, cfg string, waitendpoint bool) } // NGINXWithConfigDeployment creates an NGINX deployment using a configmap containing the nginx.conf configuration -func (f *Framework) NGINXWithConfigDeployment(name string, cfg string) { +func (f *Framework) NGINXWithConfigDeployment(name, cfg string) { f.NGINXDeployment(name, cfg, true) } @@ -438,7 +487,8 @@ func (f *Framework) NewGRPCBinDeployment() { } func newDeployment(name, namespace, image string, port int32, replicas int32, command []string, args []string, env []corev1.EnvVar, - volumeMounts []corev1.VolumeMount, volumes []corev1.Volume, setProbe bool) *appsv1.Deployment { + volumeMounts []corev1.VolumeMount, volumes []corev1.Volume, setProbe bool, +) *appsv1.Deployment { probe := &corev1.Probe{ InitialDelaySeconds: 2, PeriodSeconds: 1, @@ -510,12 +560,12 @@ func newDeployment(name, namespace, image string, port int32, replicas int32, co return d } -func (f *Framework) NewDeployment(name, image string, port int32, replicas int32) { +func (f *Framework) NewDeployment(name, image string, port, replicas int32) { f.NewDeploymentWithOpts(name, image, port, replicas, nil, nil, nil, nil, nil, true) } // NewDeployment creates a new deployment in a particular namespace. -func (f *Framework) NewDeploymentWithOpts(name, image string, port int32, replicas int32, command []string, args []string, env []corev1.EnvVar, volumeMounts []corev1.VolumeMount, volumes []corev1.Volume, setProbe bool) { +func (f *Framework) NewDeploymentWithOpts(name, image string, port, replicas int32, command, args []string, env []corev1.EnvVar, volumeMounts []corev1.VolumeMount, volumes []corev1.Volume, setProbe bool) { deployment := newDeployment(name, f.Namespace, image, port, replicas, command, args, env, volumeMounts, volumes, setProbe) f.EnsureDeployment(deployment) @@ -557,7 +607,7 @@ func (f *Framework) DeleteDeployment(name string) error { }) assert.Nil(ginkgo.GinkgoT(), err, "deleting deployment") - return waitForPodsDeleted(f.KubeClientSet, 2*time.Minute, f.Namespace, metav1.ListOptions{ + return waitForPodsDeleted(f.KubeClientSet, 2*time.Minute, f.Namespace, &metav1.ListOptions{ LabelSelector: labelSelectorToString(d.Spec.Selector.MatchLabels), }) } diff --git a/test/e2e/framework/exec.go b/test/e2e/framework/exec.go index d91d36551..9e4b55122 100644 --- a/test/e2e/framework/exec.go +++ b/test/e2e/framework/exec.go @@ -66,6 +66,7 @@ func (f *Framework) ExecCommand(pod *corev1.Pod, command string) (string, error) execErr bytes.Buffer ) + //nolint:gosec // Ignore G204 error cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%v exec --namespace %s %s --container controller -- %s", KubectlPath, pod.Namespace, pod.Name, command)) cmd.Stdout = &execOut cmd.Stderr = &execErr @@ -73,7 +74,6 @@ func (f *Framework) ExecCommand(pod *corev1.Pod, command string) (string, error) err := cmd.Run() if err != nil { return "", fmt.Errorf("could not execute '%s %s': %v", cmd.Path, cmd.Args, err) - } if execErr.Len() > 0 { @@ -91,6 +91,7 @@ func (f *Framework) NamespaceContent() (string, error) { execErr bytes.Buffer ) + //nolint:gosec // Ignore G204 error cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%v get pods,services,endpoints,deployments --namespace %s", KubectlPath, f.Namespace)) cmd.Stdout = &execOut cmd.Stderr = &execErr @@ -98,7 +99,6 @@ func (f *Framework) NamespaceContent() (string, error) { err := cmd.Run() if err != nil { return "", fmt.Errorf("could not execute '%s %s': %v", cmd.Path, cmd.Args, err) - } eout := strings.TrimSpace(execErr.String()) @@ -110,13 +110,18 @@ func (f *Framework) NamespaceContent() (string, error) { } // newIngressController deploys a new NGINX Ingress controller in a namespace -func (f *Framework) newIngressController(namespace string, namespaceOverlay string) error { +func (f *Framework) newIngressController(namespace, namespaceOverlay string) error { // Creates an nginx deployment isChroot, ok := os.LookupEnv("IS_CHROOT") 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)) @@ -125,12 +130,11 @@ func (f *Framework) newIngressController(namespace string, namespaceOverlay stri return nil } -var ( - proxyRegexp = regexp.MustCompile("Starting to serve on .*:([0-9]+)") -) +var proxyRegexp = regexp.MustCompile(`Starting to serve on .*:(\d+)`) // KubectlProxy creates a proxy to kubernetes apiserver func (f *Framework) KubectlProxy(port int) (int, *exec.Cmd, error) { + //nolint:gosec // Ignore G204 error cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s proxy --accept-hosts=.* --address=0.0.0.0 --port=%d", KubectlPath, port)) stdout, stderr, err := startCmdAndStreamOutput(cmd) if err != nil { @@ -158,6 +162,7 @@ func (f *Framework) KubectlProxy(port int) (int, *exec.Cmd, error) { } func (f *Framework) UninstallChart() error { + //nolint:gosec //Ignore G204 error cmd := exec.Command("helm", "uninstall", "--namespace", f.Namespace, "nginx-ingress") _, err := cmd.CombinedOutput() if err != nil { diff --git a/test/e2e/framework/fastcgi_helloserver.go b/test/e2e/framework/fastcgi_helloserver.go index 719048c06..73f9ef340 100644 --- a/test/e2e/framework/fastcgi_helloserver.go +++ b/test/e2e/framework/fastcgi_helloserver.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:dupl // Ignore dupl errors for similar test case package framework import ( @@ -75,7 +76,7 @@ func (f *Framework) NewNewFastCGIHelloServerDeploymentWithReplicas(replicas int3 d := f.EnsureDeployment(deployment) - err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, metav1.ListOptions{ + err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, &metav1.ListOptions{ LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(), }) assert.Nil(ginkgo.GinkgoT(), err, "failed to wait for to become ready") diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index cac6dfd20..b62ad691c 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -27,7 +27,6 @@ import ( "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1" apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" @@ -38,7 +37,6 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - restclient "k8s.io/client-go/rest" "k8s.io/klog/v2" ) @@ -51,10 +49,8 @@ const ( HTTPS RequestScheme = "https" ) -var ( - // KubectlPath defines the full path of the kubectl binary - KubectlPath = "/usr/local/bin/kubectl" -) +// KubectlPath defines the full path of the kubectl binary +var KubectlPath = "/usr/local/bin/kubectl" // Framework supports common operations used by e2e tests; it will keep a client & a namespace for you. type Framework struct { @@ -62,23 +58,39 @@ type Framework struct { // A Kubernetes and Service Catalog client KubeClientSet kubernetes.Interface - KubeConfig *restclient.Config + KubeConfig *rest.Config APIExtensionsClientSet apiextcs.Interface Namespace string IngressClass string - pod *corev1.Pod + pod *v1.Pod + // We use httpbun as a service that we route to in our tests through + // the ingress controller. We add it as part of the framework as it + // is used extensively + HTTPBunIP string + HTTPBunEnabled bool +} + +// WithHTTPBunEnabled deploys an instance of HTTPBun for the specific test +func WithHTTPBunEnabled() func(*Framework) { + return func(f *Framework) { + f.HTTPBunEnabled = true + } } // NewDefaultFramework makes a new framework and sets up a BeforeEach/AfterEach for // you (you can write additional before/after each functions). -func NewDefaultFramework(baseName string) *Framework { +func NewDefaultFramework(baseName string, opts ...func(*Framework)) *Framework { defer ginkgo.GinkgoRecover() f := &Framework{ BaseName: baseName, } + // set framework options + for _, o := range opts { + o(f) + } ginkgo.BeforeEach(f.BeforeEach) ginkgo.AfterEach(f.AfterEach) @@ -88,12 +100,16 @@ func NewDefaultFramework(baseName string) *Framework { // NewSimpleFramework makes a new framework that allows the usage of a namespace // for arbitraty tests. -func NewSimpleFramework(baseName string) *Framework { +func NewSimpleFramework(baseName string, opts ...func(*Framework)) *Framework { defer ginkgo.GinkgoRecover() f := &Framework{ BaseName: baseName, } + // set framework options + for _, o := range opts { + o(f) + } ginkgo.BeforeEach(f.CreateEnvironment) ginkgo.AfterEach(f.DestroyEnvironment) @@ -113,7 +129,6 @@ func (f *Framework) CreateEnvironment() { f.KubeClientSet, err = kubernetes.NewForConfig(f.KubeConfig) assert.Nil(ginkgo.GinkgoT(), err, "creating a kubernetes client") - } f.Namespace, err = CreateKubeNamespace(f.BaseName, f.KubeClientSet) @@ -142,6 +157,11 @@ func (f *Framework) BeforeEach() { assert.Nil(ginkgo.GinkgoT(), err, "updating ingress controller pod information") f.WaitForNginxListening(80) + + // If HTTPBun is enabled deploy an instance to the namespace + if f.HTTPBunEnabled { + f.HTTPBunIP = f.NewHttpbunDeployment() + } } // AfterEach deletes the namespace, after reading its events. @@ -162,7 +182,7 @@ func (f *Framework) AfterEach() { return } - cmd := fmt.Sprintf("cat /etc/nginx/nginx.conf") + cmd := "cat /etc/nginx/nginx.conf" o, err := f.ExecCommand(f.pod, cmd) if err != nil { Logf("Unexpected error obtaining nginx.conf file: %v", err) @@ -227,13 +247,13 @@ func (f *Framework) GetNginxPodIP() string { } // GetURL returns the URL should be used to make a request to NGINX -func (f *Framework) GetURL(scheme RequestScheme) string { +func (f *Framework) GetURL(requestScheme RequestScheme) string { ip := f.GetNginxIP() - return fmt.Sprintf("%v://%v", scheme, ip) + return fmt.Sprintf("%v://%v", requestScheme, ip) } // GetIngressNGINXPod returns the ingress controller running pod -func (f *Framework) GetIngressNGINXPod() *corev1.Pod { +func (f *Framework) GetIngressNGINXPod() *v1.Pod { return f.pod } @@ -247,6 +267,7 @@ func (f *Framework) updateIngressNGINXPod() error { // WaitForNginxServer waits until the nginx configuration contains a particular server section. // `cfg` passed to matcher is normalized by replacing all tabs and spaces with single space. func (f *Framework) WaitForNginxServer(name string, matcher func(cfg string) bool) { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(Poll, DefaultTimeout, f.matchNginxConditions(name, matcher)) assert.Nil(ginkgo.GinkgoT(), err, "waiting for nginx server condition/s") Sleep(1 * time.Second) @@ -255,13 +276,15 @@ func (f *Framework) WaitForNginxServer(name string, matcher func(cfg string) boo // WaitForNginxConfiguration waits until the nginx configuration contains a particular configuration // `cfg` passed to matcher is normalized by replacing all tabs and spaces with single space. func (f *Framework) WaitForNginxConfiguration(matcher func(cfg string) bool) { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(Poll, DefaultTimeout, f.matchNginxConditions("", matcher)) assert.Nil(ginkgo.GinkgoT(), err, "waiting for nginx server condition/s") Sleep(1 * time.Second) } // WaitForNginxCustomConfiguration waits until the nginx configuration given part (from, to) contains a particular configuration -func (f *Framework) WaitForNginxCustomConfiguration(from string, to string, matcher func(cfg string) bool) { +func (f *Framework) WaitForNginxCustomConfiguration(from, to string, matcher func(cfg string) bool) { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(Poll, DefaultTimeout, f.matchNginxCustomConditions(from, to, matcher)) assert.Nil(ginkgo.GinkgoT(), err, "waiting for nginx server condition/s") } @@ -279,7 +302,7 @@ func (f *Framework) matchNginxConditions(name string, matcher func(cfg string) b return func() (bool, error) { var cmd string if name == "" { - cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf") + cmd = "cat /etc/nginx/nginx.conf" } else { cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %v/,/## end server %v/'", name, name) } @@ -302,7 +325,7 @@ func (f *Framework) matchNginxConditions(name string, matcher func(cfg string) b } } -func (f *Framework) matchNginxCustomConditions(from string, to string, matcher func(cfg string) bool) wait.ConditionFunc { +func (f *Framework) matchNginxCustomConditions(from, to string, matcher func(cfg string) bool) wait.ConditionFunc { return func() (bool, error) { cmd := fmt.Sprintf("cat /etc/nginx/nginx.conf| awk '/%v/,/%v/'", from, to) @@ -372,7 +395,7 @@ func (f *Framework) CreateConfigMap(name string, data map[string]string) { } // UpdateNginxConfigMapData updates single field in ingress-nginx's nginx-ingress-controller map data -func (f *Framework) UpdateNginxConfigMapData(key string, value string) { +func (f *Framework) UpdateNginxConfigMapData(key, value string) { config, err := f.getConfigMap("nginx-ingress-controller") assert.Nil(ginkgo.GinkgoT(), err) assert.NotNil(ginkgo.GinkgoT(), config, "expected a configmap but none returned") @@ -398,6 +421,7 @@ func (f *Framework) WaitForReload(fn func()) { fn() count := 0 + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(1*time.Second, DefaultTimeout, func() (bool, error) { reloads := getReloadCount(f.pod, f.Namespace, f.KubeClientSet) // most of the cases reload the ingress controller @@ -413,13 +437,13 @@ func (f *Framework) WaitForReload(fn func()) { assert.Nil(ginkgo.GinkgoT(), err, "while waiting for ingress controller reload") } -func getReloadCount(pod *corev1.Pod, namespace string, client kubernetes.Interface) int { +func getReloadCount(pod *v1.Pod, namespace string, client kubernetes.Interface) int { events, err := client.CoreV1().Events(namespace).Search(scheme.Scheme, pod) assert.Nil(ginkgo.GinkgoT(), err, "obtaining NGINX Pod") reloadCount := 0 - for _, e := range events.Items { - if e.Reason == "RELOAD" && e.Type == corev1.EventTypeNormal { + for i := range events.Items { + if events.Items[i].Reason == "RELOAD" && events.Items[i].Type == v1.EventTypeNormal { reloadCount++ } } @@ -434,7 +458,7 @@ func (f *Framework) DeleteNGINXPod(grace int64) { err := f.KubeClientSet.CoreV1().Pods(ns).Delete(context.TODO(), f.pod.GetName(), *metav1.NewDeleteOptions(grace)) assert.Nil(ginkgo.GinkgoT(), err, "deleting ingress nginx pod") - + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err = wait.Poll(Poll, DefaultTimeout, func() (bool, error) { err := f.updateIngressNGINXPod() if err != nil || f.pod == nil { @@ -464,7 +488,7 @@ func (f *Framework) HTTPTestClientWithTLSConfig(config *tls.Config) *httpexpect. func (f *Framework) newHTTPTestClient(config *tls.Config, setIngressURL bool) *httpexpect.HTTPRequest { if config == nil { config = &tls.Config{ - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing } } var baseURL string @@ -484,12 +508,13 @@ func (f *Framework) newHTTPTestClient(config *tls.Config, setIngressURL bool) *h // WaitForNginxListening waits until NGINX starts accepting connections on a port func (f *Framework) WaitForNginxListening(port int) { - err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, 1, f.Namespace, metav1.ListOptions{ + err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, 1, f.Namespace, &metav1.ListOptions{ LabelSelector: "app.kubernetes.io/name=ingress-nginx", }) assert.Nil(ginkgo.GinkgoT(), err, "waiting for ingress pods to be ready") podIP := f.GetNginxIP() + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err = wait.Poll(500*time.Millisecond, DefaultTimeout, func() (bool, error) { hostPort := net.JoinHostPort(podIP, fmt.Sprintf("%v", port)) conn, err := net.Dial("tcp", hostPort) @@ -506,7 +531,7 @@ func (f *Framework) WaitForNginxListening(port int) { // WaitForPod waits for a specific Pod to be ready, using a label selector func (f *Framework) WaitForPod(selector string, timeout time.Duration, shouldFail bool) { - err := waitForPodsReady(f.KubeClientSet, timeout, 1, f.Namespace, metav1.ListOptions{ + err := waitForPodsReady(f.KubeClientSet, timeout, 1, f.Namespace, &metav1.ListOptions{ LabelSelector: selector, }) @@ -518,7 +543,7 @@ func (f *Framework) WaitForPod(selector string, timeout time.Duration, shouldFai } // UpdateDeployment runs the given updateFunc on the deployment and waits for it to be updated -func UpdateDeployment(kubeClientSet kubernetes.Interface, namespace string, name string, replicas int, updateFunc func(d *appsv1.Deployment) error) error { +func UpdateDeployment(kubeClientSet kubernetes.Interface, namespace, name string, replicas int, updateFunc func(d *appsv1.Deployment) error) error { deployment, err := kubeClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return err @@ -548,7 +573,7 @@ func UpdateDeployment(kubeClientSet kubernetes.Interface, namespace string, name } } - err = waitForPodsReady(kubeClientSet, DefaultTimeout, replicas, namespace, metav1.ListOptions{ + err = waitForPodsReady(kubeClientSet, DefaultTimeout, replicas, namespace, &metav1.ListOptions{ LabelSelector: fields.SelectorFromSet(fields.Set(deployment.Spec.Template.ObjectMeta.Labels)).String(), }) if err != nil { @@ -559,6 +584,7 @@ func UpdateDeployment(kubeClientSet kubernetes.Interface, namespace string, name } func waitForDeploymentRollout(kubeClientSet kubernetes.Interface, resource *appsv1.Deployment) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, 5*time.Minute, func() (bool, error) { d, err := kubeClientSet.AppsV1().Deployments(resource.Namespace).Get(context.TODO(), resource.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { @@ -582,7 +608,7 @@ func waitForDeploymentRollout(kubeClientSet kubernetes.Interface, resource *apps } // UpdateIngress runs the given updateFunc on the ingress -func UpdateIngress(kubeClientSet kubernetes.Interface, namespace string, name string, updateFunc func(d *networking.Ingress) error) error { +func UpdateIngress(kubeClientSet kubernetes.Interface, namespace, name string, updateFunc func(d *networking.Ingress) error) error { ingress, err := kubeClientSet.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return err @@ -793,7 +819,7 @@ func Sleep(duration ...time.Duration) { time.Sleep(sleepFor) } -func loadConfig() (*restclient.Config, error) { +func loadConfig() (*rest.Config, error) { config, err := rest.InClusterConfig() if err != nil { return nil, err diff --git a/test/e2e/framework/grpc_fortune_teller.go b/test/e2e/framework/grpc_fortune_teller.go index 02ff3aae2..a7be46327 100644 --- a/test/e2e/framework/grpc_fortune_teller.go +++ b/test/e2e/framework/grpc_fortune_teller.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:dupl // Ignore dupl errors for similar test case package framework import ( @@ -75,7 +76,7 @@ func (f *Framework) NewNewGRPCFortuneTellerDeploymentWithReplicas(replicas int32 d := f.EnsureDeployment(deployment) - err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, metav1.ListOptions{ + err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, &metav1.ListOptions{ LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(), }) assert.Nil(ginkgo.GinkgoT(), err, "failed to wait for to become ready") diff --git a/test/e2e/framework/healthz.go b/test/e2e/framework/healthz.go index b52c3ffde..3ee297b27 100644 --- a/test/e2e/framework/healthz.go +++ b/test/e2e/framework/healthz.go @@ -26,7 +26,7 @@ func (f *Framework) VerifyHealthz(ip string, statusCode int) error { url := fmt.Sprintf("http://%v:10254/healthz", ip) client := &http.Client{} - req, err := http.NewRequest(http.MethodGet, url, nil) + req, err := http.NewRequest(http.MethodGet, url, http.NoBody) if err != nil { return fmt.Errorf("creating GET request for URL %q failed: %v", url, err) } diff --git a/test/e2e/framework/httpexpect/chain.go b/test/e2e/framework/httpexpect/chain.go index 79956fb33..33c4d15b7 100644 --- a/test/e2e/framework/httpexpect/chain.go +++ b/test/e2e/framework/httpexpect/chain.go @@ -36,19 +36,3 @@ func (c *chain) fail(message string, args ...interface{}) { c.failbit = true c.reporter.Errorf(message, args...) } - -func (c *chain) reset() { - c.failbit = false -} - -func (c *chain) assertFailed(r Reporter) { - if !c.failbit { - r.Errorf("expected chain is failed, but it's ok") - } -} - -func (c *chain) assertOK(r Reporter) { - if c.failbit { - r.Errorf("expected chain is ok, but it's failed") - } -} diff --git a/test/e2e/framework/httpexpect/match.go b/test/e2e/framework/httpexpect/match.go index b031510c4..aad10b02e 100644 --- a/test/e2e/framework/httpexpect/match.go +++ b/test/e2e/framework/httpexpect/match.go @@ -23,7 +23,7 @@ type Match struct { names map[string]int } -func makeMatch(chain chain, submatches []string, names []string) *Match { +func makeMatch(chain chain, submatches, names []string) *Match { if submatches == nil { submatches = []string{} } diff --git a/test/e2e/framework/httpexpect/object.go b/test/e2e/framework/httpexpect/object.go index f7c27faaf..49dcf5659 100644 --- a/test/e2e/framework/httpexpect/object.go +++ b/test/e2e/framework/httpexpect/object.go @@ -23,6 +23,8 @@ import ( "github.com/yudai/gojsondiff/formatter" ) +const unavailableMsg = " (unavailable)" + // Object provides methods to inspect attached map[string]interface{} object // (Go representation of JSON object). type Object struct { @@ -81,20 +83,21 @@ func diffValues(expected, actual interface{}) string { var diff gojsondiff.Diff - if ve, ok := expected.(map[string]interface{}); ok { + switch ve := expected.(type) { + case map[string]interface{}: if va, ok := actual.(map[string]interface{}); ok { diff = differ.CompareObjects(ve, va) } else { - return " (unavailable)" + return unavailableMsg } - } else if ve, ok := expected.([]interface{}); ok { + case []interface{}: if va, ok := actual.([]interface{}); ok { diff = differ.CompareArrays(ve, va) } else { - return " (unavailable)" + return unavailableMsg } - } else { - return " (unavailable)" + default: + return unavailableMsg } config := formatter.AsciiFormatterConfig{ @@ -104,7 +107,7 @@ func diffValues(expected, actual interface{}) string { str, err := f.Format(diff) if err != nil { - return " (unavailable)" + return unavailableMsg } return "--- expected\n+++ actual\n" + str diff --git a/test/e2e/framework/httpexpect/request.go b/test/e2e/framework/httpexpect/request.go index d8edb42ce..0ae85dd79 100644 --- a/test/e2e/framework/httpexpect/request.go +++ b/test/e2e/framework/httpexpect/request.go @@ -65,7 +65,7 @@ func (h *HTTPRequest) DoRequest(method, rpath string) *HTTPRequest { var request *http.Request uri.Path = path.Join(uri.Path, rpath) - if request, err = http.NewRequest(method, uri.String(), nil); err != nil { + if request, err = http.NewRequest(method, uri.String(), http.NoBody); err != nil { h.chain.fail(err.Error()) } @@ -110,6 +110,7 @@ func (h *HTTPRequest) Expect() *HTTPResponse { if err != nil { h.chain.fail(err.Error()) } + defer response.Body.Close() h.HTTPResponse.Response = response // set the HTTP response diff --git a/test/e2e/framework/k8s.go b/test/e2e/framework/k8s.go index fc3e59b08..7c067421d 100644 --- a/test/e2e/framework/k8s.go +++ b/test/e2e/framework/k8s.go @@ -47,7 +47,7 @@ func (f *Framework) EnsureSecret(secret *core.Secret) *core.Secret { } // GetConfigMap gets a ConfigMap object from the given namespace, name and returns it, throws error if it does not exist. -func (f *Framework) GetConfigMap(namespace string, name string) *core.ConfigMap { +func (f *Framework) GetConfigMap(namespace, name string) *core.ConfigMap { cm, err := f.KubeClientSet.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metav1.GetOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "getting configmap") assert.NotNil(ginkgo.GinkgoT(), cm, "expected a configmap but none returned") @@ -73,7 +73,7 @@ func (f *Framework) EnsureConfigMap(configMap *core.ConfigMap) *core.ConfigMap { } // GetIngress gets an Ingress object from the given namespace, name and returns it, throws error if it does not exists. -func (f *Framework) GetIngress(namespace string, name string) *networking.Ingress { +func (f *Framework) GetIngress(namespace, name string) *networking.Ingress { ing, err := f.KubeClientSet.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "getting ingress") assert.NotNil(ginkgo.GinkgoT(), ing, "expected an ingress but none returned") @@ -114,7 +114,7 @@ func (f *Framework) UpdateIngress(ingress *networking.Ingress) *networking.Ingre } // GetService gets a Service object from the given namespace, name and returns it, throws error if it does not exist. -func (f *Framework) GetService(namespace string, name string) *core.Service { +func (f *Framework) GetService(namespace, name string) *core.Service { s, err := f.KubeClientSet.CoreV1().Services(namespace).Get(context.TODO(), name, metav1.GetOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "getting service") assert.NotNil(ginkgo.GinkgoT(), s, "expected a service but none returned") @@ -143,16 +143,21 @@ func (f *Framework) EnsureDeployment(deployment *appsv1.Deployment) *appsv1.Depl } // waitForPodsReady waits for a given amount of time until a group of Pods is running in the given namespace. -func waitForPodsReady(kubeClientSet kubernetes.Interface, timeout time.Duration, expectedReplicas int, namespace string, opts metav1.ListOptions) error { +func waitForPodsReady(kubeClientSet kubernetes.Interface, timeout time.Duration, expectedReplicas int, namespace string, opts *metav1.ListOptions) error { + //nolint:staticcheck // TODO: will replace it since wait.PollImmediate is deprecated return wait.PollImmediate(1*time.Second, timeout, func() (bool, error) { - pl, err := kubeClientSet.CoreV1().Pods(namespace).List(context.TODO(), opts) + pl, err := kubeClientSet.CoreV1().Pods(namespace).List(context.TODO(), *opts) if err != nil { return false, nil } r := 0 - for _, p := range pl.Items { - if isRunning, _ := podRunningReady(&p); isRunning { + for i := range pl.Items { + isRunning, err := podRunningReady(&pl.Items[i]) + if err != nil { + Logf("error checking if pod is running : %v", err) + } + if isRunning { r++ } } @@ -166,9 +171,10 @@ func waitForPodsReady(kubeClientSet kubernetes.Interface, timeout time.Duration, } // waitForPodsDeleted waits for a given amount of time until a group of Pods are deleted in the given namespace. -func waitForPodsDeleted(kubeClientSet kubernetes.Interface, timeout time.Duration, namespace string, opts metav1.ListOptions) error { +func waitForPodsDeleted(kubeClientSet kubernetes.Interface, timeout time.Duration, namespace string, opts *metav1.ListOptions) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, timeout, func() (bool, error) { - pl, err := kubeClientSet.CoreV1().Pods(namespace).List(context.TODO(), opts) + pl, err := kubeClientSet.CoreV1().Pods(namespace).List(context.TODO(), *opts) if err != nil { return false, nil } @@ -186,7 +192,7 @@ func WaitForEndpoints(kubeClientSet kubernetes.Interface, timeout time.Duration, if expectedEndpoints == 0 { return nil } - + //nolint:staticcheck // TODO: will replace it since wait.PollImmediate is deprecated return wait.PollImmediate(Poll, timeout, func() (bool, error) { endpoint, err := kubeClientSet.CoreV1().Endpoints(ns).Get(context.TODO(), name, metav1.GetOptions{}) if k8sErrors.IsNotFound(err) { @@ -248,6 +254,7 @@ func isPodReady(p *core.Pod) bool { // getIngressNGINXPod returns the ingress controller running pod func getIngressNGINXPod(ns string, kubeClientSet kubernetes.Interface) (*core.Pod, error) { var pod *core.Pod + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(1*time.Second, DefaultTimeout, func() (bool, error) { l, err := kubeClientSet.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{ LabelSelector: "app.kubernetes.io/name=ingress-nginx", @@ -256,15 +263,16 @@ func getIngressNGINXPod(ns string, kubeClientSet kubernetes.Interface) (*core.Po return false, nil } - for _, p := range l.Items { + for i := range l.Items { + p := &l.Items[i] if strings.HasPrefix(p.GetName(), "nginx-ingress-controller") { - isRunning, err := podRunningReady(&p) + isRunning, err := podRunningReady(p) if err != nil { continue } if isRunning { - pod = &p + pod = p return true, nil } } @@ -273,6 +281,7 @@ func getIngressNGINXPod(ns string, kubeClientSet kubernetes.Interface) (*core.Po return false, nil }) if err != nil { + //nolint:staticcheck // TODO: will replace it since wait.ErrWaitTimeout is deprecated if err == wait.ErrWaitTimeout { return nil, fmt.Errorf("timeout waiting at least one ingress-nginx pod running in namespace %v", ns) } @@ -285,7 +294,7 @@ func getIngressNGINXPod(ns string, kubeClientSet kubernetes.Interface) (*core.Po func createDeploymentWithRetries(c kubernetes.Interface, namespace string, obj *appsv1.Deployment) error { if obj == nil { - return fmt.Errorf("Object provided to create is empty") + return fmt.Errorf("object provided to create is empty") } createFunc := func() (bool, error) { _, err := c.AppsV1().Deployments(namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) @@ -298,7 +307,7 @@ func createDeploymentWithRetries(c kubernetes.Interface, namespace string, obj * if isRetryableAPIError(err) { return false, nil } - return false, fmt.Errorf("Failed to create object with non-retriable error: %v", err) + return false, fmt.Errorf("failed to create object with non-retriable error: %v", err) } return retryWithExponentialBackOff(createFunc) @@ -306,7 +315,7 @@ func createDeploymentWithRetries(c kubernetes.Interface, namespace string, obj * func createSecretWithRetries(c kubernetes.Interface, namespace string, obj *core.Secret) error { if obj == nil { - return fmt.Errorf("Object provided to create is empty") + return fmt.Errorf("object provided to create is empty") } createFunc := func() (bool, error) { _, err := c.CoreV1().Secrets(namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) @@ -319,14 +328,14 @@ func createSecretWithRetries(c kubernetes.Interface, namespace string, obj *core if isRetryableAPIError(err) { return false, nil } - return false, fmt.Errorf("Failed to create object with non-retriable error: %v", err) + return false, fmt.Errorf("failed to create object with non-retriable error: %v", err) } return retryWithExponentialBackOff(createFunc) } func createServiceWithRetries(c kubernetes.Interface, namespace string, obj *core.Service) error { if obj == nil { - return fmt.Errorf("Object provided to create is empty") + return fmt.Errorf("object provided to create is empty") } createFunc := func() (bool, error) { _, err := c.CoreV1().Services(namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) @@ -339,7 +348,7 @@ func createServiceWithRetries(c kubernetes.Interface, namespace string, obj *cor if isRetryableAPIError(err) { return false, nil } - return false, fmt.Errorf("Failed to create object with non-retriable error: %v", err) + return false, fmt.Errorf("failed to create object with non-retriable error: %v", err) } return retryWithExponentialBackOff(createFunc) @@ -347,7 +356,7 @@ func createServiceWithRetries(c kubernetes.Interface, namespace string, obj *cor func createIngressWithRetries(c kubernetes.Interface, namespace string, obj *networking.Ingress) error { if obj == nil { - return fmt.Errorf("Object provided to create is empty") + return fmt.Errorf("object provided to create is empty") } createFunc := func() (bool, error) { _, err := c.NetworkingV1().Ingresses(namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) @@ -360,7 +369,7 @@ func createIngressWithRetries(c kubernetes.Interface, namespace string, obj *net if isRetryableAPIError(err) { return false, nil } - return false, fmt.Errorf("Failed to create object with non-retriable error: %v", err) + return false, fmt.Errorf("failed to create object with non-retriable error: %v", err) } return retryWithExponentialBackOff(createFunc) @@ -368,7 +377,7 @@ func createIngressWithRetries(c kubernetes.Interface, namespace string, obj *net func updateIngressWithRetries(c kubernetes.Interface, namespace string, obj *networking.Ingress) error { if obj == nil { - return fmt.Errorf("Object provided to create is empty") + return fmt.Errorf("object provided to create is empty") } updateFunc := func() (bool, error) { _, err := c.NetworkingV1().Ingresses(namespace).Update(context.TODO(), obj, metav1.UpdateOptions{}) @@ -378,7 +387,7 @@ func updateIngressWithRetries(c kubernetes.Interface, namespace string, obj *net if isRetryableAPIError(err) { return false, nil } - return false, fmt.Errorf("Failed to update object with non-retriable error: %v", err) + return false, fmt.Errorf("failed to update object with non-retriable error: %v", err) } return retryWithExponentialBackOff(updateFunc) diff --git a/test/e2e/framework/metrics.go b/test/e2e/framework/metrics.go index 830237540..774f1bd7e 100644 --- a/test/e2e/framework/metrics.go +++ b/test/e2e/framework/metrics.go @@ -29,7 +29,7 @@ func (f *Framework) GetMetric(metricName, ip string) (*dto.MetricFamily, error) url := fmt.Sprintf("http://%v:10254/metrics", ip) client := &http.Client{} - req, err := http.NewRequest(http.MethodGet, url, nil) + req, err := http.NewRequest(http.MethodGet, url, http.NoBody) if err != nil { return nil, fmt.Errorf("creating GET request for URL %q failed: %v", url, err) } @@ -44,7 +44,6 @@ func (f *Framework) GetMetric(metricName, ip string) (*dto.MetricFamily, error) var parser expfmt.TextParser metrics, err := parser.TextToMetricFamilies(resp.Body) - if err != nil { return nil, fmt.Errorf("reading text format failed: %v", err) } diff --git a/test/e2e/framework/ssl.go b/test/e2e/framework/ssl.go index 52ceffc57..d6252fe47 100644 --- a/test/e2e/framework/ssl.go +++ b/test/e2e/framework/ssl.go @@ -93,8 +93,8 @@ func CreateIngressTLSSecret(client kubernetes.Interface, hosts []string, secretN // CreateIngressMASecret creates or updates a Secret containing a Mutual Auth // certificate-chain for the given Ingress and returns a TLS configuration suitable // for HTTP clients to use against that particular Ingress. -func CreateIngressMASecret(client kubernetes.Interface, host string, secretName, namespace string) (*tls.Config, error) { - if len(host) == 0 { +func CreateIngressMASecret(client kubernetes.Interface, host, secretName, namespace string) (*tls.Config, error) { + if host == "" { return nil, fmt.Errorf("requires a non-empty host") } @@ -138,12 +138,13 @@ func CreateIngressMASecret(client kubernetes.Interface, host string, secretName, return &tls.Config{ ServerName: host, Certificates: []tls.Certificate{clientPair}, - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing }, nil } // WaitForTLS waits until the TLS handshake with a given server completes successfully. func WaitForTLS(url string, tlsConfig *tls.Config) { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err := wait.Poll(Poll, DefaultTimeout, matchTLSServerName(url, tlsConfig)) assert.Nil(ginkgo.GinkgoT(), err, "waiting for TLS configuration in URL %s", url) } @@ -160,7 +161,6 @@ func generateRSACert(host string, isCA bool, keyOut, certOut io.Writer) error { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { return fmt.Errorf("failed to generate serial number: %s", err) } @@ -329,7 +329,7 @@ func tlsConfig(serverName string, pemCA []byte) (*tls.Config, error) { if !rootCAPool.AppendCertsFromPEM(pemCA) { return nil, fmt.Errorf("error creating CA certificate pool (%s)", serverName) } - return &tls.Config{ + return &tls.Config{ //nolint:gosec // Ignore the gosec error in testing ServerName: serverName, RootCAs: rootCAPool, }, nil diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go index be4cd6e5e..961357885 100644 --- a/test/e2e/framework/test_context.go +++ b/test/e2e/framework/test_context.go @@ -23,7 +23,7 @@ import ( // TestContextType describes the client context to use in communications with the Kubernetes API. type TestContextType struct { KubeHost string - //KubeConfig string + // KubeConfig string KubeContext string } @@ -33,7 +33,6 @@ var TestContext TestContextType // registerCommonFlags registers flags common to all e2e test suites. func registerCommonFlags() { flag.StringVar(&TestContext.KubeHost, "kubernetes-host", "http://127.0.0.1:8080", "The kubernetes host, or apiserver, to connect to") - //flag.StringVar(&TestContext.KubeConfig, "kubernetes-config", os.Getenv(clientcmd.RecommendedConfigPathEnvVar), "Path to config containing embedded authinfo for kubernetes. Default value is from environment variable "+clientcmd.RecommendedConfigPathEnvVar) flag.StringVar(&TestContext.KubeContext, "kubernetes-context", "", "config context to use for kubernetes. If unset, will use value from 'current-context'") } diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 90f15eb1b..abdb168e1 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -49,24 +49,24 @@ func nowStamp() string { return time.Now().Format(time.StampMilli) } -func log(level string, format string, args ...interface{}) { +func logf(level, format string, args ...interface{}) { fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...) } // Logf logs to the INFO logs. func Logf(format string, args ...interface{}) { - log("INFO", format, args...) + logf("INFO", format, args...) } // Failf logs to the INFO logs and fails the test. func Failf(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) - log("INFO", msg) + logf("INFO", msg) ginkgo.Fail(nowStamp()+": "+msg, 1) } // RestclientConfig deserializes the contents of a kubeconfig file into a Config object. -func RestclientConfig(config, context string) (*api.Config, error) { +func RestclientConfig(config, newContext string) (*api.Config, error) { Logf(">>> config: %s\n", config) if config == "" { return nil, fmt.Errorf("config file must be specified to load client config") @@ -75,9 +75,9 @@ func RestclientConfig(config, context string) (*api.Config, error) { if err != nil { return nil, fmt.Errorf("error loading config: %v", err.Error()) } - if context != "" { - Logf(">>> context: %s\n", context) - c.CurrentContext = context + if newContext != "" { + Logf(">>> context: %s\n", newContext) + c.CurrentContext = newContext } return c, nil } @@ -98,6 +98,7 @@ func createNamespace(baseName string, labels map[string]string, c kubernetes.Int var got *corev1.Namespace var err error + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err = wait.Poll(Poll, DefaultTimeout, func() (bool, error) { got, err = c.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) if err != nil { @@ -114,13 +115,11 @@ func createNamespace(baseName string, labels map[string]string, c kubernetes.Int // CreateKubeNamespace creates a new namespace in the cluster func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error) { - return createNamespace(baseName, nil, c) } // CreateKubeNamespaceWithLabel creates a new namespace with given labels in the cluster func CreateKubeNamespaceWithLabel(baseName string, labels map[string]string, c kubernetes.Interface) (string, error) { - return createNamespace(baseName, labels, c) } @@ -150,7 +149,7 @@ func CreateIngressClass(namespace string, c kubernetes.Interface) (string, error }, }, metav1.CreateOptions{}) if err != nil { - return "", fmt.Errorf("Unexpected error creating IngressClass %s: %v", icname, err) + return "", fmt.Errorf("unexpected error creating IngressClass %s: %v", icname, err) } _, err = c.RbacV1().ClusterRoles().Create(context.TODO(), &rbacv1.ClusterRole{ @@ -162,7 +161,7 @@ func CreateIngressClass(namespace string, c kubernetes.Interface) (string, error }}, }, metav1.CreateOptions{}) if err != nil { - return "", fmt.Errorf("Unexpected error creating IngressClass ClusterRole %s: %v", icname, err) + return "", fmt.Errorf("unexpected error creating IngressClass ClusterRole %s: %v", icname, err) } _, err = c.RbacV1().ClusterRoleBindings().Create(context.TODO(), &rbacv1.ClusterRoleBinding{ @@ -184,7 +183,7 @@ func CreateIngressClass(namespace string, c kubernetes.Interface) (string, error }, }, metav1.CreateOptions{}) if err != nil { - return "", fmt.Errorf("Unexpected error creating IngressClass ClusterRoleBinding %s: %v", icname, err) + return "", fmt.Errorf("unexpected error creating IngressClass ClusterRoleBinding %s: %v", icname, err) } return ic.Name, nil } @@ -200,16 +199,16 @@ func deleteIngressClass(c kubernetes.Interface, ingressclass string) error { } err = c.NetworkingV1().IngressClasses().Delete(context.TODO(), ingressclass, deleteOptions) if err != nil { - return fmt.Errorf("Unexpected error deleting IngressClass %s: %v", ingressclass, err) + return fmt.Errorf("unexpected error deleting IngressClass %s: %v", ingressclass, err) } err = c.RbacV1().ClusterRoleBindings().Delete(context.TODO(), ingressclass, deleteOptions) if err != nil { - return fmt.Errorf("Unexpected error deleting IngressClass ClusterRoleBinding %s: %v", ingressclass, err) + return fmt.Errorf("unexpected error deleting IngressClass ClusterRoleBinding %s: %v", ingressclass, err) } err = c.RbacV1().ClusterRoles().Delete(context.TODO(), ingressclass, deleteOptions) if err != nil { - return fmt.Errorf("Unexpected error deleting IngressClass ClusterRole %s: %v", ingressclass, err) + return fmt.Errorf("unexpected error deleting IngressClass ClusterRole %s: %v", ingressclass, err) } return nil @@ -223,6 +222,7 @@ func GetIngressClassName(namespace string) *string { // WaitForKubeNamespaceNotExist waits until a namespaces is not present in the cluster func WaitForKubeNamespaceNotExist(c kubernetes.Interface, namespace string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, namespaceNotExist(c, namespace)) } @@ -241,6 +241,7 @@ func namespaceNotExist(c kubernetes.Interface, namespace string) wait.ConditionF // WaitForNoPodsInNamespace waits until there are no pods running in a namespace func WaitForNoPodsInNamespace(c kubernetes.Interface, namespace string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, noPodsInNamespace(c, namespace)) } @@ -267,15 +268,17 @@ func WaitForPodRunningInNamespace(c kubernetes.Interface, pod *corev1.Pod) error if pod.Status.Phase == corev1.PodRunning { return nil } - return waitTimeoutForPodRunningInNamespace(c, pod.Name, pod.Namespace, DefaultTimeout) + return waitTimeoutForPodRunningInNamespace(c, pod.Name, pod.Namespace) } -func waitTimeoutForPodRunningInNamespace(c kubernetes.Interface, podName, namespace string, timeout time.Duration) error { +func waitTimeoutForPodRunningInNamespace(c kubernetes.Interface, podName, namespace string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, podRunning(c, podName, namespace)) } // WaitForSecretInNamespace waits a default amount of time for the specified secret is present in a particular namespace func WaitForSecretInNamespace(c kubernetes.Interface, namespace, name string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, secretInNamespace(c, namespace, name)) } @@ -298,6 +301,7 @@ func secretInNamespace(c kubernetes.Interface, namespace, name string) wait.Cond // WaitForFileInFS waits a default amount of time for the specified file is present in the filesystem func WaitForFileInFS(file string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, fileInFS(file)) } @@ -322,6 +326,7 @@ func fileInFS(file string) wait.ConditionFunc { // WaitForNoIngressInNamespace waits until there is no ingress object in a particular namespace func WaitForNoIngressInNamespace(c kubernetes.Interface, namespace, name string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, noIngressInNamespace(c, namespace, name)) } @@ -344,6 +349,7 @@ func noIngressInNamespace(c kubernetes.Interface, namespace, name string) wait.C // WaitForIngressInNamespace waits until a particular ingress object exists namespace func WaitForIngressInNamespace(c kubernetes.Interface, namespace, name string) error { + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated return wait.Poll(Poll, DefaultTimeout, ingressInNamespace(c, namespace, name)) } diff --git a/test/e2e/gracefulshutdown/grace_period.go b/test/e2e/gracefulshutdown/grace_period.go index 12e2f3d67..123892f3a 100644 --- a/test/e2e/gracefulshutdown/grace_period.go +++ b/test/e2e/gracefulshutdown/grace_period.go @@ -33,7 +33,6 @@ var _ = framework.IngressNginxDescribe("[Shutdown] Grace period shutdown", func( f := framework.NewDefaultFramework("shutdown-grace-period") ginkgo.It("/healthz should return status code 500 during shutdown grace period", func() { - f.NewSlowEchoDeployment() err := f.UpdateIngressControllerDeployment(func(deployment *appsv1.Deployment) error { @@ -83,6 +82,5 @@ var _ = framework.IngressNginxDescribe("[Shutdown] Grace period shutdown", func( for _, err := range <-result { assert.Nil(ginkgo.GinkgoT(), err) } - }) }) diff --git a/test/e2e/ingress/multiple_rules.go b/test/e2e/ingress/multiple_rules.go index c08f36374..f44b2f8dd 100644 --- a/test/e2e/ingress/multiple_rules.go +++ b/test/e2e/ingress/multiple_rules.go @@ -36,6 +36,15 @@ var _ = framework.IngressNginxDescribe("single ingress - multiple hosts", func() }) ginkgo.It("should set the correct $service_name NGINX variable", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + annotations := map[string]string{ "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "service-name: $service_name";`, } diff --git a/test/e2e/ingress/pathtype_exact.go b/test/e2e/ingress/pathtype_exact.go index ae2902e9f..d0564cbf6 100644 --- a/test/e2e/ingress/pathtype_exact.go +++ b/test/e2e/ingress/pathtype_exact.go @@ -35,6 +35,14 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { }) ginkgo.It("should choose exact location for /exact", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() host := "exact.path" @@ -42,7 +50,7 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";`, } - var exactPathType = networking.PathTypeExact + exactPathType := networking.PathTypeExact ing := framework.NewSingleIngress("exact", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) diff --git a/test/e2e/ingress/pathtype_mixed.go b/test/e2e/ingress/pathtype_mixed.go index dd183bbb4..e7bf2532e 100644 --- a/test/e2e/ingress/pathtype_mixed.go +++ b/test/e2e/ingress/pathtype_mixed.go @@ -34,9 +34,17 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi f.NewEchoDeployment() }) - var exactPathType = networking.PathTypeExact + exactPathType := networking.PathTypeExact ginkgo.It("should choose the correct location", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() host := "mixed.path" diff --git a/test/e2e/leaks/lua_ssl.go b/test/e2e/leaks/lua_ssl.go index e63a1e353..88285ba4b 100644 --- a/test/e2e/leaks/lua_ssl.go +++ b/test/e2e/leaks/lua_ssl.go @@ -87,14 +87,14 @@ func provisionIngress(hostname string, f *framework.Framework) { func checkIngress(hostname string, f *framework.Framework) { resp := f.HTTPTestClientWithTLSConfig(&tls.Config{ ServerName: hostname, - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing }). GET("/"). WithURL(f.GetURL(framework.HTTPS)). WithHeader("Host", hostname). Expect(). Raw() - + defer resp.Body.Close() assert.Equal(ginkgo.GinkgoT(), resp.StatusCode, http.StatusOK) // check the returned secret is not the fake one diff --git a/test/e2e/loadbalance/configmap.go b/test/e2e/loadbalance/configmap.go index 8cd47286b..737cd06dd 100644 --- a/test/e2e/loadbalance/configmap.go +++ b/test/e2e/loadbalance/configmap.go @@ -25,6 +25,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const loadBalanceHost = "load-balance.com" + var _ = framework.DescribeSetting("[Load Balancer] load-balance", func() { f := framework.NewDefaultFramework("lb-configmap") @@ -33,7 +35,7 @@ var _ = framework.DescribeSetting("[Load Balancer] load-balance", func() { }) ginkgo.It("should apply the configmap load-balance setting", func() { - host := "load-balance.com" + host := loadBalanceHost f.UpdateNginxConfigMapData("load-balance", "ewma") diff --git a/test/e2e/loadbalance/ewma.go b/test/e2e/loadbalance/ewma.go index e2750a09a..f457e6357 100644 --- a/test/e2e/loadbalance/ewma.go +++ b/test/e2e/loadbalance/ewma.go @@ -35,12 +35,13 @@ var _ = framework.DescribeSetting("[Load Balancer] EWMA", func() { f.NewEchoDeployment(framework.WithDeploymentReplicas(3)) f.SetNginxConfigMapData(map[string]string{ "worker-processes": "2", - "load-balance": "ewma"}, + "load-balance": "ewma", + }, ) }) ginkgo.It("does not fail requests", func() { - host := "load-balance.com" + host := loadBalanceHost f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxServer(host, @@ -52,7 +53,9 @@ var _ = framework.DescribeSetting("[Load Balancer] EWMA", func() { assert.Nil(ginkgo.GinkgoT(), err) assert.Equal(ginkgo.GinkgoT(), algorithm, "ewma") - re, _ := regexp.Compile(fmt.Sprintf(`%v.*`, framework.EchoService)) + re, err := regexp.Compile(fmt.Sprintf(`%v.*`, framework.EchoService)) + assert.Nil(ginkgo.GinkgoT(), err, "error compiling regex") + replicaRequestCount := map[string]int{} for i := 0; i < 30; i++ { diff --git a/test/e2e/loadbalance/round_robin.go b/test/e2e/loadbalance/round_robin.go index 2d0582e9a..5f6667143 100644 --- a/test/e2e/loadbalance/round_robin.go +++ b/test/e2e/loadbalance/round_robin.go @@ -37,7 +37,7 @@ var _ = framework.DescribeSetting("[Load Balancer] round-robin", func() { }) ginkgo.It("should evenly distribute requests with round-robin (default algorithm)", func() { - host := "load-balance.com" + host := loadBalanceHost f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxServer(host, @@ -45,7 +45,9 @@ var _ = framework.DescribeSetting("[Load Balancer] round-robin", func() { return strings.Contains(server, "server_name load-balance.com") }) - re, _ := regexp.Compile(fmt.Sprintf(`%v.*`, framework.EchoService)) + re, err := regexp.Compile(fmt.Sprintf(`%v.*`, framework.EchoService)) + assert.Nil(ginkgo.GinkgoT(), err, "error compiling regex") + replicaRequestCount := map[string]int{} for i := 0; i < 600; i++ { diff --git a/test/e2e/lua/dynamic_certificates.go b/test/e2e/lua/dynamic_certificates.go index 0160cc9e7..8c9df5e71 100644 --- a/test/e2e/lua/dynamic_certificates.go +++ b/test/e2e/lua/dynamic_certificates.go @@ -245,7 +245,6 @@ var _ = framework.IngressNginxDescribe("[Lua] dynamic certificates", func() { WithHeader("Host", host). Expect(). Status(http.StatusOK) - }) }) }) @@ -254,7 +253,6 @@ func extractReloadCount(mf *dto.MetricFamily) (float64, error) { vec, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{ Timestamp: model.Now(), }, mf) - if err != nil { return 0, err } diff --git a/test/e2e/lua/dynamic_configuration.go b/test/e2e/lua/dynamic_configuration.go index 8e9804545..8ec1ef839 100644 --- a/test/e2e/lua/dynamic_configuration.go +++ b/test/e2e/lua/dynamic_configuration.go @@ -26,7 +26,6 @@ import ( "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" - networking "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -199,20 +198,18 @@ var _ = framework.IngressNginxDescribe("[Lua] dynamic configuration", func() { }) }) -func ensureIngress(f *framework.Framework, host string, deploymentName string) *networking.Ingress { - ing := createIngress(f, host, deploymentName) +func ensureIngress(f *framework.Framework, host, deploymentName string) { + createIngress(f, host, deploymentName) f.HTTPTestClient(). GET("/"). WithHeader("Host", host). Expect(). Status(http.StatusOK) - - return ing } -func createIngress(f *framework.Framework, host string, deploymentName string) *networking.Ingress { - ing := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, deploymentName, 80, +func createIngress(f *framework.Framework, host, deploymentName string) { + f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, deploymentName, 80, map[string]string{ "nginx.ingress.kubernetes.io/load-balance": "ewma", }, @@ -223,21 +220,19 @@ func createIngress(f *framework.Framework, host string, deploymentName string) * return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) - - return ing } -func ensureHTTPSRequest(f *framework.Framework, url string, host string, expectedDNSName string) { +func ensureHTTPSRequest(f *framework.Framework, url, host, expectedDNSName string) { resp := f.HTTPTestClientWithTLSConfig(&tls.Config{ ServerName: host, - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // Ignore the gosec error in testing }). GET("/"). WithURL(url). WithHeader("Host", host). Expect(). Raw() - + defer resp.Body.Close() assert.Equal(ginkgo.GinkgoT(), resp.StatusCode, http.StatusOK) assert.Equal(ginkgo.GinkgoT(), len(resp.TLS.PeerCertificates), 1) assert.Equal(ginkgo.GinkgoT(), resp.TLS.PeerCertificates[0].DNSNames[0], expectedDNSName) diff --git a/test/e2e/nginx/nginx.go b/test/e2e/nginx/nginx.go index cd14a931c..7e572a976 100644 --- a/test/e2e/nginx/nginx.go +++ b/test/e2e/nginx/nginx.go @@ -100,7 +100,6 @@ var _ = framework.DescribeSetting("nginx-configuration", func() { f := framework.NewSimpleFramework("nginxconfiguration") ginkgo.It("start nginx with default configuration", func() { - f.NGINXWithConfigDeployment("default-nginx", cfgOK) f.WaitForPod("app=default-nginx", 60*time.Second, false) framework.Sleep(5 * time.Second) @@ -113,20 +112,16 @@ var _ = framework.DescribeSetting("nginx-configuration", func() { }) ginkgo.It("fails when using alias directive", func() { - f.NGINXDeployment("alias-nginx", cfgAlias, false) // This should fail with a crashloopback because our NGINX does not have // alias directive! f.WaitForPod("app=alias-nginx", 60*time.Second, true) - }) ginkgo.It("fails when using root directive", func() { - f.NGINXDeployment("root-nginx", cfgRoot, false) // This should fail with a crashloopback because our NGINX does not have // root directive! f.WaitForPod("app=root-nginx", 60*time.Second, true) - }) }) diff --git a/test/e2e/run-chart-test.sh b/test/e2e/run-chart-test.sh index 6efa63eb2..425d043e9 100755 --- a/test/e2e/run-chart-test.sh +++ b/test/e2e/run-chart-test.sh @@ -78,7 +78,7 @@ fi if [ "${SKIP_IMAGE_CREATION:-false}" = "false" ]; then if ! command -v ginkgo &> /dev/null; then - go install github.com/onsi/ginkgo/v2/ginkgo@v2.9.0 + go install github.com/onsi/ginkgo/v2/ginkgo@v2.13.0 fi echo "[dev-env] building image" make -C ${DIR}/../../ clean-image build image diff --git a/test/e2e/run-e2e-suite.sh b/test/e2e/run-e2e-suite.sh index a3bf589cd..015895e56 100755 --- a/test/e2e/run-e2e-suite.sh +++ b/test/e2e/run-e2e-suite.sh @@ -51,6 +51,7 @@ fi BASEDIR=$(dirname "$0") NGINX_BASE_IMAGE=$(cat $BASEDIR/../../NGINX_BASE) +HTTPBUN_IMAGE=$(cat $BASEDIR/HTTPBUN_IMAGE) echo -e "${BGREEN}Granting permissions to ingress-nginx e2e service account...${NC}" kubectl create serviceaccount ingress-nginx-e2e || true @@ -77,8 +78,10 @@ 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}" \ --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "ingress-nginx-e2e"}}' \ e2e --image=nginx-ingress-controller:e2e diff --git a/test/e2e/run-kind-e2e.sh b/test/e2e/run-kind-e2e.sh index fe7550360..5c4ba5c1e 100755 --- a/test/e2e/run-kind-e2e.sh +++ b/test/e2e/run-kind-e2e.sh @@ -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 @@ -95,7 +96,7 @@ fi if [ "${SKIP_E2E_IMAGE_CREATION}" = "false" ]; then if ! command -v ginkgo &> /dev/null; then - go install github.com/onsi/ginkgo/v2/ginkgo@v2.9.0 + go install github.com/onsi/ginkgo/v2/ginkgo@v2.13.0 fi echo "[dev-env] .. done building controller images" diff --git a/test/e2e/security/request_smuggling.go b/test/e2e/security/request_smuggling.go index 786a7a397..5ede02d4b 100644 --- a/test/e2e/security/request_smuggling.go +++ b/test/e2e/security/request_smuggling.go @@ -50,9 +50,9 @@ server { f.UpdateNginxConfigMapData("http-snippet", snippet) - //TODO: currently using a self hosted HTTPBun instance results in a 499, we - //should move away from using httpbun.com once we have the httpbun - //deployment as part of the framework + // TODO: currently using a self hosted HTTPBun instance results in a 499, we + // should move away from using httpbun.com once we have the httpbun + // deployment as part of the framework ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, map[string]string{ "nginx.ingress.kubernetes.io/auth-signin": "https://httpbun.com/bearer/d4bcba7a-0def-4a31-91a7-47e420adf44b", "nginx.ingress.kubernetes.io/auth-url": "https://httpbun.com/basic-auth/user/passwd", @@ -79,7 +79,9 @@ func smugglingRequest(host, addr string, port int) (string, error) { defer conn.Close() - conn.SetDeadline(time.Now().Add(time.Second * 10)) + if err := conn.SetDeadline(time.Now().Add(time.Second * 10)); err != nil { + return "", err + } _, err = fmt.Fprintf(conn, "GET /echo HTTP/1.1\r\nHost: %v\r\nContent-Length: 56\r\n\r\nGET /_hidden/index.html HTTP/1.1\r\nHost: notlocalhost\r\n\r\n", host) if err != nil { @@ -89,7 +91,7 @@ func smugglingRequest(host, addr string, port int) (string, error) { // wait for /_hidden/index.html response framework.Sleep() - var buf = make([]byte, 1024) + buf := make([]byte, 1024) r := bufio.NewReader(conn) _, err = r.Read(buf) if err != nil { diff --git a/test/e2e/servicebackend/service_backend.go b/test/e2e/servicebackend/service_backend.go index 44f3d36a5..6ab2f8ad1 100644 --- a/test/e2e/servicebackend/service_backend.go +++ b/test/e2e/servicebackend/service_backend.go @@ -29,48 +29,50 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) -var pathtype = networking.PathTypePrefix -var _ = framework.IngressNginxDescribe("[Service] backend status code 503", func() { - f := framework.NewDefaultFramework("service-backend") +var ( + pathtype = networking.PathTypePrefix + _ = framework.IngressNginxDescribe("[Service] backend status code 503", func() { + f := framework.NewDefaultFramework("service-backend") - ginkgo.It("should return 503 when backend service does not exist", func() { - host := "nonexistent.svc.com" + ginkgo.It("should return 503 when backend service does not exist", func() { + host := "nonexistent.svc.com" - bi := buildIngressWithNonexistentService(host, f.Namespace, "/") - f.EnsureIngress(bi) + bi := buildIngressWithNonexistentService(host, f.Namespace, "/") + f.EnsureIngress(bi) - f.WaitForNginxServer(host, - func(server string) bool { - return strings.Contains(server, "proxy_pass http://upstream_balancer;") - }) + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, "proxy_pass http://upstream_balancer;") + }) - f.HTTPTestClient(). - GET("/"). - WithHeader("Host", host). - Expect(). - Status(http.StatusServiceUnavailable) + f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + Expect(). + Status(http.StatusServiceUnavailable) + }) + + ginkgo.It("should return 503 when all backend service endpoints are unavailable", func() { + host := "unavailable.svc.com" + + bi, bs := buildIngressWithUnavailableServiceEndpoints(host, f.Namespace, "/") + + f.EnsureService(bs) + f.EnsureIngress(bi) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, "proxy_pass http://upstream_balancer;") + }) + + f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + Expect(). + Status(http.StatusServiceUnavailable) + }) }) - - ginkgo.It("should return 503 when all backend service endpoints are unavailable", func() { - host := "unavailable.svc.com" - - bi, bs := buildIngressWithUnavailableServiceEndpoints(host, f.Namespace, "/") - - f.EnsureService(bs) - f.EnsureIngress(bi) - - f.WaitForNginxServer(host, - func(server string) bool { - return strings.Contains(server, "proxy_pass http://upstream_balancer;") - }) - - f.HTTPTestClient(). - GET("/"). - WithHeader("Host", host). - Expect(). - Status(http.StatusServiceUnavailable) - }) -}) +) func buildIngressWithNonexistentService(host, namespace, path string) *networking.Ingress { backendService := "nonexistent-svc" @@ -146,14 +148,15 @@ func buildIngressWithUnavailableServiceEndpoints(host, namespace, path string) ( Name: backendService, Namespace: namespace, }, - Spec: corev1.ServiceSpec{Ports: []corev1.ServicePort{ - { - Name: "tcp", - Port: 80, - TargetPort: intstr.FromInt(80), - Protocol: "TCP", + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "tcp", + Port: 80, + TargetPort: intstr.FromInt(80), + Protocol: "TCP", + }, }, - }, Selector: map[string]string{ "app": backendService, }, diff --git a/test/e2e/servicebackend/service_externalname.go b/test/e2e/servicebackend/service_externalname.go index 2fd6cd080..fccd1cd19 100644 --- a/test/e2e/servicebackend/service_externalname.go +++ b/test/e2e/servicebackend/service_externalname.go @@ -30,20 +30,21 @@ import ( networking "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/ingress-nginx/internal/nginx" "k8s.io/ingress-nginx/test/e2e/framework" ) +const echoHost = "echo" + var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { - f := framework.NewDefaultFramework("type-externalname") + f := framework.NewDefaultFramework("type-externalname", framework.WithHTTPBunEnabled()) ginkgo.It("works with external name set to incomplete fqdn", func() { f.NewEchoDeployment() - host := "echo" + host := echoHost svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: framework.HTTPBunService, + Name: framework.NIPService, Namespace: f.Namespace, }, Spec: corev1.ServiceSpec{ @@ -51,10 +52,15 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Type: corev1.ServiceTypeExternalName, }, } - f.EnsureService(svc) - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, nil) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.NIPService, + 80, + nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -70,11 +76,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should return 200 for service type=ExternalName without a port defined", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() - - host := "echo" + host := echoHost svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -82,17 +84,23 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Namespace: f.Namespace, }, Spec: corev1.ServiceSpec{ - ExternalName: framework.BuildNIPHost(ip), + ExternalName: f.GetNIPHost(), Type: corev1.ServiceTypeExternalName, }, } - f.EnsureService(svc) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": framework.BuildNIPHost(ip), + "nginx.ingress.kubernetes.io/upstream-vhost": f.GetNIPHost(), } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, annotations) + + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -108,19 +116,21 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should return 200 for service type=ExternalName with a port defined", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() + host := echoHost - host := "echo" - - svc := framework.BuildNIPExternalNameService(f, ip, host) + svc := framework.BuildNIPExternalNameService(f, f.HTTPBunIP, host) f.EnsureService(svc) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": framework.BuildNIPHost(ip), + "nginx.ingress.kubernetes.io/upstream-vhost": f.GetNIPHost(), } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, annotations) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -136,11 +146,11 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should return status 502 for service type=ExternalName with an invalid host", func() { - host := "echo" + host := echoHost svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: framework.HTTPBunService, + Name: framework.NIPService, Namespace: f.Namespace, }, Spec: corev1.ServiceSpec{ @@ -148,10 +158,15 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Type: corev1.ServiceTypeExternalName, }, } - f.EnsureService(svc) - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, nil) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.NIPService, + 80, + nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -167,19 +182,22 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should return 200 for service type=ExternalName using a port name", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() + host := echoHost - host := "echo" - - svc := framework.BuildNIPExternalNameService(f, ip, host) + svc := framework.BuildNIPExternalNameService(f, f.HTTPBunIP, host) f.EnsureService(svc) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": framework.BuildNIPHost(ip), + "nginx.ingress.kubernetes.io/upstream-vhost": f.GetNIPHost(), } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, annotations) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations) + namedBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ Name: framework.NIPService, @@ -188,6 +206,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }, }, } + ing.Spec.Rules[0].HTTP.Paths[0].Backend = namedBackend f.EnsureIngress(ing) @@ -204,11 +223,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should return 200 for service type=ExternalName using FQDN with trailing dot", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() - - host := "echo" + host := echoHost svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -216,14 +231,19 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Namespace: f.Namespace, }, Spec: corev1.ServiceSpec{ - ExternalName: framework.BuildNIPHost(ip), + ExternalName: f.GetNIPHost(), Type: corev1.ServiceTypeExternalName, }, } - f.EnsureService(svc) - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, nil) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -239,20 +259,23 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { }) ginkgo.It("should update the external name after a service update", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() + host := echoHost - host := "echo" - - svc := framework.BuildNIPExternalNameService(f, ip, host) + svc := framework.BuildNIPExternalNameService(f, f.HTTPBunIP, host) f.EnsureService(svc) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": framework.BuildNIPHost(ip), + "nginx.ingress.kubernetes.io/upstream-vhost": f.GetNIPHost(), } - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBunService, 80, annotations) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.HTTPBunService, + 80, + annotations) + namedBackend := networking.IngressBackend{ Service: &networking.IngressServiceBackend{ Name: framework.NIPService, @@ -279,14 +302,20 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { assert.Contains(ginkgo.GinkgoT(), body, `"X-Forwarded-Host": "echo"`) - svc, err := f.KubeClientSet.CoreV1().Services(f.Namespace).Get(context.TODO(), framework.NIPService, metav1.GetOptions{}) + svc, err := f.KubeClientSet. + CoreV1(). + Services(f.Namespace). + Get(context.TODO(), framework.NIPService, metav1.GetOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error obtaining external service") - ip = f.NewHttpbunDeployment(framework.WithDeploymentName("eu-server")) - + // Deploy a new instance to switch routing to + ip := f.NewHttpbunDeployment(framework.WithDeploymentName("eu-server")) svc.Spec.ExternalName = framework.BuildNIPHost(ip) - _, err = f.KubeClientSet.CoreV1().Services(f.Namespace).Update(context.Background(), svc, metav1.UpdateOptions{}) + _, err = f.KubeClientSet. + CoreV1(). + Services(f.Namespace). + Update(context.Background(), svc, metav1.UpdateOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error updating external service") framework.Sleep() @@ -302,21 +331,27 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { assert.Contains(ginkgo.GinkgoT(), body, `"X-Forwarded-Host": "echo"`) ginkgo.By("checking the service is updated to use new host") - curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort) - output, err := f.ExecIngressPod(curlCmd) + dbgCmd := "/dbg backends all" + output, err := f.ExecIngressPod(dbgCmd) assert.Nil(ginkgo.GinkgoT(), err) - assert.Contains(ginkgo.GinkgoT(), output, fmt.Sprintf("{\"address\":\"%s\"", framework.BuildNIPHost(ip))) + assert.Contains( + ginkgo.GinkgoT(), + output, + fmt.Sprintf(`"address": %q`, framework.BuildNIPHost(ip)), + ) }) ginkgo.It("should sync ingress on external name service addition/deletion", func() { - // This is a workaround so we only depend on a self hosted instance of - // httpbun - ip := f.NewHttpbunDeployment() - - host := "echo" + host := echoHost // Create the Ingress first - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.NIPService, 80, nil) + ing := framework.NewSingleIngress(host, + "/", + host, + f.Namespace, + framework.NIPService, + 80, + nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, @@ -332,7 +367,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Status(http.StatusServiceUnavailable) // Now create the service - svc := framework.BuildNIPExternalNameService(f, ip, host) + svc := framework.BuildNIPExternalNameService(f, f.HTTPBunIP, host) f.EnsureService(svc) framework.Sleep() @@ -345,7 +380,10 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Status(http.StatusOK) // And back to 503 after deleting the service - err := f.KubeClientSet.CoreV1().Services(f.Namespace).Delete(context.TODO(), framework.NIPService, metav1.DeleteOptions{}) + err := f.KubeClientSet. + CoreV1(). + Services(f.Namespace). + Delete(context.TODO(), framework.NIPService, metav1.DeleteOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error deleting external service") framework.Sleep() diff --git a/test/e2e/servicebackend/service_nil_backend.go b/test/e2e/servicebackend/service_nil_backend.go index c44601c88..9b5b4c7e6 100644 --- a/test/e2e/servicebackend/service_nil_backend.go +++ b/test/e2e/servicebackend/service_nil_backend.go @@ -64,7 +64,6 @@ var _ = framework.IngressNginxDescribe("[Service] Nil Service Backend", func() { WithHeader("Host", invalidHost). Expect(). Status(http.StatusNotFound) - }) }) diff --git a/test/e2e/settings/access_log.go b/test/e2e/settings/access_log.go index 7c7e2b9e3..65b9dfda4 100644 --- a/test/e2e/settings/access_log.go +++ b/test/e2e/settings/access_log.go @@ -28,7 +28,6 @@ var _ = framework.DescribeSetting("access-log", func() { f := framework.NewDefaultFramework("access-log") ginkgo.Context("access-log-path", func() { - ginkgo.It("use the default configuration", func() { f.WaitForNginxConfiguration( func(cfg string) bool { @@ -50,7 +49,6 @@ var _ = framework.DescribeSetting("access-log", func() { }) ginkgo.Context("http-access-log-path", func() { - ginkgo.It("use the specified configuration", func() { f.UpdateNginxConfigMapData("http-access-log-path", "/tmp/nginx/http-access.log") f.WaitForNginxConfiguration( @@ -63,7 +61,6 @@ var _ = framework.DescribeSetting("access-log", func() { }) ginkgo.Context("stream-access-log-path", func() { - ginkgo.It("use the specified configuration", func() { f.UpdateNginxConfigMapData("stream-access-log-path", "/tmp/nginx/stream-access.log") f.WaitForNginxConfiguration( @@ -76,7 +73,6 @@ var _ = framework.DescribeSetting("access-log", func() { }) ginkgo.Context("http-access-log-path & stream-access-log-path", func() { - ginkgo.It("use the specified configuration", func() { f.SetNginxConfigMapData(map[string]string{ "http-access-log-path": "/tmp/nginx/http-access.log", diff --git a/test/e2e/settings/aio_write.go b/test/e2e/settings/aio_write.go new file mode 100644 index 000000000..bcfe2a4de --- /dev/null +++ b/test/e2e/settings/aio_write.go @@ -0,0 +1,54 @@ +/* +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 settings + +import ( + "strings" + + "github.com/onsi/ginkgo/v2" + + "k8s.io/ingress-nginx/test/e2e/framework" +) + +var _ = framework.DescribeSetting("aio-write", func() { + f := framework.NewDefaultFramework("aio-write") + + ginkgo.It("should be enabled by default", func() { + f.WaitForNginxConfiguration( + func(cfg string) bool { + return strings.Contains(cfg, "aio_write on") + }) + }) + + ginkgo.It("should be enabled when setting is true", func() { + f.UpdateNginxConfigMapData("enable-aio-write", "true") + + f.WaitForNginxConfiguration( + func(cfg string) bool { + return strings.Contains(cfg, "aio_write on") + }) + }) + + ginkgo.It("should be disabled when setting is false", func() { + f.UpdateNginxConfigMapData("enable-aio-write", "false") + + f.WaitForNginxConfiguration( + func(cfg string) bool { + return !strings.Contains(cfg, "aio_write on") + }) + }) +}) diff --git a/test/e2e/settings/badannotationvalues.go b/test/e2e/settings/badannotationvalues.go index b4b6def4a..f61b5bada 100644 --- a/test/e2e/settings/badannotationvalues.go +++ b/test/e2e/settings/badannotationvalues.go @@ -34,6 +34,14 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should drop an ingress if there is an invalid character in some annotation", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() host := "invalid-value-test" annotations := map[string]string{ @@ -65,6 +73,15 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should drop an ingress if there is a forbidden word in some annotation", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + host := "forbidden-value-test" annotations := map[string]string{ @@ -100,7 +117,14 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should allow an ingress if there is a default blocklist config in place", func() { - + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() hostValid := "custom-allowed-value-test" annotationsValid := map[string]string{ "nginx.ingress.kubernetes.io/configuration-snippet": ` @@ -131,6 +155,14 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should drop an ingress if there is a custom blocklist config in place and allow others to pass", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() host := "custom-forbidden-value-test" annotations := map[string]string{ @@ -159,6 +191,5 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { WithHeader("Host", host). Expect(). Status(http.StatusNotFound) - }) }) diff --git a/test/e2e/settings/brotli.go b/test/e2e/settings/brotli.go index a13678f66..aacaddec5 100644 --- a/test/e2e/settings/brotli.go +++ b/test/e2e/settings/brotli.go @@ -28,14 +28,13 @@ import ( ) var _ = framework.IngressNginxDescribe("brotli", func() { - f := framework.NewDefaultFramework("brotli") + f := framework.NewDefaultFramework( + "brotli", + framework.WithHTTPBunEnabled(), + ) host := "brotli" - ginkgo.BeforeEach(func() { - f.NewHttpbunDeployment() - }) - ginkgo.It("should only compress responses that meet the `brotli-min-length` condition", func() { brotliMinLength := 24 contentEncoding := "application/octet-stream" diff --git a/test/e2e/settings/default_ssl_certificate.go b/test/e2e/settings/default_ssl_certificate.go index ea97d4895..c48a1e87f 100644 --- a/test/e2e/settings/default_ssl_certificate.go +++ b/test/e2e/settings/default_ssl_certificate.go @@ -30,10 +30,12 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const fooHost = "foo" + var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", func() { f := framework.NewDefaultFramework("default-ssl-certificate") var tlsConfig *tls.Config - secretName := "my-custom-cert" + secretName := "my-custom-cert" //nolint:gosec // Ignore the gosec error in testing service := framework.EchoService port := 80 @@ -78,7 +80,7 @@ var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", f }) ginkgo.It("uses default ssl certificate for host based ingress when configured certificate does not match host", func() { - host := "foo" + host := fooHost ing := f.EnsureIngress(framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, service, port, nil)) _, err := framework.CreateIngressTLSSecret(f.KubeClientSet, diff --git a/test/e2e/settings/disable_catch_all.go b/test/e2e/settings/disable_catch_all.go index 0d1a14493..4e7a16f4d 100644 --- a/test/e2e/settings/disable_catch_all.go +++ b/test/e2e/settings/disable_catch_all.go @@ -48,7 +48,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { }) ginkgo.It("should ignore catch all Ingress with backend", func() { - host := "foo" + host := fooHost ing := framework.NewSingleCatchAllIngress("catch-all", f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -67,7 +67,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { }) ginkgo.It("should ignore catch all Ingress with backend and rules", func() { - host := "foo" + host := fooHost ing := framework.NewSingleIngressWithBackendAndRules(host, "/", host, f.Namespace, framework.EchoService, 80, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -79,7 +79,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { }) ginkgo.It("should delete Ingress updated to catch-all", func() { - host := "foo" + host := fooHost ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -121,7 +121,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { }) ginkgo.It("should allow Ingress with rules", func() { - host := "foo" + host := fooHost ing := framework.NewSingleIngress("not-catch-all", "/", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) diff --git a/test/e2e/settings/disable_service_external_name.go b/test/e2e/settings/disable_service_external_name.go index 7f03e5355..602828089 100644 --- a/test/e2e/settings/disable_service_external_name.go +++ b/test/e2e/settings/disable_service_external_name.go @@ -33,7 +33,10 @@ import ( ) var _ = framework.IngressNginxDescribe("[Flag] disable-service-external-name", func() { - f := framework.NewDefaultFramework("disabled-service-external-name") + f := framework.NewDefaultFramework( + "disabled-service-external-name", + framework.WithHTTPBunEnabled(), + ) ginkgo.BeforeEach(func() { f.NewEchoDeployment(framework.WithDeploymentReplicas(2)) @@ -54,21 +57,18 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-service-external-name", f externalhost := "echo-external-svc.com" - ip := f.NewHttpbunDeployment() - svc := framework.BuildNIPExternalNameService(f, ip, "echo") - f.EnsureService(svc) + f.EnsureService(framework.BuildNIPExternalNameService(f, f.HTTPBunIP, "echo")) - svcexternal := &corev1.Service{ + f.EnsureService(&corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "external", Namespace: f.Namespace, }, Spec: corev1.ServiceSpec{ - ExternalName: framework.BuildNIPHost(ip), + ExternalName: f.GetNIPHost(), Type: corev1.ServiceTypeExternalName, }, - } - f.EnsureService(svcexternal) + }) ingexternal := framework.NewSingleIngress(externalhost, "/", externalhost, f.Namespace, "external", 80, nil) f.EnsureIngress(ingexternal) @@ -95,6 +95,5 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-service-external-name", f WithHeader("Host", externalhost). Expect(). StatusRange(httpexpect.Status5xx) - }) }) diff --git a/test/e2e/settings/disable_sync_events.go b/test/e2e/settings/disable_sync_events.go index 7d1298087..033fd9194 100644 --- a/test/e2e/settings/disable_sync_events.go +++ b/test/e2e/settings/disable_sync_events.go @@ -50,6 +50,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-sync-events", func() { assert.NotEmpty(ginkgo.GinkgoT(), events.Items, "got events") }) + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should create sync events", func() { host := "disable-sync-events-false" f.NewEchoDeployment(framework.WithDeploymentReplicas(1)) @@ -77,6 +78,7 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-sync-events", func() { assert.NotEmpty(ginkgo.GinkgoT(), events.Items, "got events") }) + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should not create sync events", func() { host := "disable-sync-events-true" f.NewEchoDeployment(framework.WithDeploymentReplicas(1)) @@ -103,5 +105,4 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-sync-events", func() { assert.Empty(ginkgo.GinkgoT(), events.Items, "got events") }) - }) diff --git a/test/e2e/settings/enable_real_ip.go b/test/e2e/settings/enable_real_ip.go index 9be2e52d9..778011b9f 100644 --- a/test/e2e/settings/enable_real_ip.go +++ b/test/e2e/settings/enable_real_ip.go @@ -64,15 +64,15 @@ var _ = framework.DescribeSetting("enable-real-ip", func() { Body(). Raw() - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) + assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%s", host)) assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=%s", host)) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=80")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4")) + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=80") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-for=1.2.3.4") }) ginkgo.It("should not trust X-Forwarded-For header when setting is false", func() { @@ -101,13 +101,13 @@ var _ = framework.DescribeSetting("enable-real-ip", func() { Raw() assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%s", host)) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=80")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-original-forwarded-for=1.2.3.4")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4")) + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=80") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http") + assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-for=1.2.3.4") + assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-for=1.2.3.4") }) }) diff --git a/test/e2e/settings/forwarded_headers.go b/test/e2e/settings/forwarded_headers.go index b929e683b..44460aca6 100644 --- a/test/e2e/settings/forwarded_headers.go +++ b/test/e2e/settings/forwarded_headers.go @@ -17,7 +17,6 @@ limitations under the License. package settings import ( - "fmt" "net/http" "strings" @@ -27,6 +26,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const forwardedHeadersHost = "forwarded-headers" + var _ = framework.DescribeSetting("use-forwarded-headers", func() { f := framework.NewDefaultFramework("forwarded-headers") @@ -38,7 +39,7 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() { }) ginkgo.It("should trust X-Forwarded headers when setting is true", func() { - host := "forwarded-headers" + host := forwardedHeadersHost f.UpdateNginxConfigMapData(setting, "true") @@ -65,12 +66,12 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() { Body(). Raw() - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-scheme=myproto")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4")) + assert.Contains(ginkgo.GinkgoT(), body, "host=myhost") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=myproto") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-for=1.2.3.4") ginkgo.By("ensuring that first entry in X-Forwarded-Host is used as the best host") body = f.HTTPTestClient(). @@ -85,12 +86,12 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() { Body(). Raw() - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost.com")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost.com")) + assert.Contains(ginkgo.GinkgoT(), body, "host=myhost.com") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost.com") }) ginkgo.It("should not trust X-Forwarded headers when setting is false", func() { - host := "forwarded-headers" + host := forwardedHeadersHost f.UpdateNginxConfigMapData(setting, "false") @@ -115,16 +116,16 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() { Body(). Raw() - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=forwarded-headers")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=80")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-scheme=http")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-original-forwarded-for=1.2.3.4")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-scheme=myproto")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) - assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4")) + assert.Contains(ginkgo.GinkgoT(), body, "host=forwarded-headers") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=80") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=http") + assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-for=1.2.3.4") + assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=myproto") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") + assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-for=1.2.3.4") }) }) diff --git a/test/e2e/settings/geoip2.go b/test/e2e/settings/geoip2.go index 9b2ca8624..7da26d810 100644 --- a/test/e2e/settings/geoip2.go +++ b/test/e2e/settings/geoip2.go @@ -19,11 +19,10 @@ package settings import ( "context" "fmt" + "net/http" "path/filepath" "strings" - "net/http" - "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" @@ -55,7 +54,7 @@ var _ = framework.DescribeSetting("Geoip2", func() { }) assert.Nil(ginkgo.GinkgoT(), err, "updating ingress controller deployment flags") - filename := fmt.Sprintf("/etc/nginx/geoip/%s.mmdb", edition) + filename := fmt.Sprintf("/etc/ingress-controller/geoip/%s.mmdb", edition) exec, err := f.ExecIngressPod(fmt.Sprintf(`sh -c "mkdir -p '%s' && wget -O '%s' '%s' 2>&1"`, filepath.Dir(filename), filename, testdataURL)) framework.Logf(exec) assert.Nil(ginkgo.GinkgoT(), err, fmt.Sprintln("error downloading test geoip2 db", filename)) @@ -70,10 +69,17 @@ var _ = framework.DescribeSetting("Geoip2", func() { ginkgo.It("should only allow requests from specific countries", func() { ginkgo.Skip("GeoIP test are temporarily disabled") - f.UpdateNginxConfigMapData("use-geoip2", "true") + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + "use-geoip2": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() - httpSnippetAllowingOnlyAustralia := - `map $geoip2_city_country_code $blocked_country { + httpSnippetAllowingOnlyAustralia := `map $geoip2_city_country_code $blocked_country { default 1; AU 0; }` @@ -85,8 +91,7 @@ var _ = framework.DescribeSetting("Geoip2", func() { return strings.Contains(cfg, "map $geoip2_city_country_code $blocked_country") }) - configSnippet := - `if ($blocked_country) { + configSnippet := `if ($blocked_country) { return 403; }` diff --git a/test/e2e/settings/global_external_auth.go b/test/e2e/settings/global_external_auth.go index c5964299f..741e6f955 100644 --- a/test/e2e/settings/global_external_auth.go +++ b/test/e2e/settings/global_external_auth.go @@ -31,8 +31,16 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const ( + disable = "false" + noAuthLocaltionSetting = "no-auth-locations" +) + var _ = framework.DescribeSetting("[Security] global-auth-url", func() { - f := framework.NewDefaultFramework("global-external-auth") + f := framework.NewDefaultFramework( + "global-external-auth", + framework.WithHTTPBunEnabled(), + ) host := "global-external-auth" @@ -43,18 +51,16 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { fooPath := "/foo" barPath := "/bar" - noAuthSetting := "no-auth-locations" + noAuthSetting := noAuthLocaltionSetting noAuthLocations := barPath enableGlobalExternalAuthAnnotation := "nginx.ingress.kubernetes.io/enable-global-auth" ginkgo.BeforeEach(func() { f.NewEchoDeployment() - f.NewHttpbunDeployment() }) ginkgo.Context("when global external authentication is configured", func() { - ginkgo.BeforeEach(func() { globalExternalAuthURL := fmt.Sprintf("http://%s.%s.svc.cluster.local:80/status/401", framework.HTTPBunService, f.Namespace) @@ -83,7 +89,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It("should return status code 401 when request any protected service", func() { - ginkgo.By("Sending a request to protected service /foo") f.HTTPTestClient(). GET(fooPath). @@ -100,7 +105,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It("should return status code 200 when request whitelisted (via no-auth-locations) service and 401 when request protected service", func() { - ginkgo.By("Adding a no-auth-locations for /bar to configMap") f.UpdateNginxConfigMapData(noAuthSetting, noAuthLocations) f.WaitForNginxServer(host, @@ -124,10 +128,9 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It("should return status code 200 when request whitelisted (via ingress annotation) service and 401 when request protected service", func() { - ginkgo.By("Adding an ingress rule for /bar with annotation enable-global-auth = false") err := framework.UpdateIngress(f.KubeClientSet, f.Namespace, "bar-ingress", func(ingress *networking.Ingress) error { - ingress.ObjectMeta.Annotations[enableGlobalExternalAuthAnnotation] = "false" + ingress.ObjectMeta.Annotations[enableGlobalExternalAuthAnnotation] = disable return nil }) assert.Nil(ginkgo.GinkgoT(), err) @@ -153,9 +156,8 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It("should still return status code 200 after auth backend is deleted using cache", func() { - globalExternalAuthCacheKeySetting := "global-auth-cache-key" - globalExternalAuthCacheKey := "foo" + globalExternalAuthCacheKey := fooHost globalExternalAuthCacheDurationSetting := "global-auth-cache-duration" globalExternalAuthCacheDuration := "200 201 401 30m" globalExternalAuthURL := fmt.Sprintf("http://%s.%s.svc.cluster.local:80/status/200", framework.HTTPBunService, f.Namespace) @@ -195,7 +197,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should proxy_method method when global-auth-method is configured`, func() { - globalExternalAuthMethodSetting := "global-auth-method" globalExternalAuthMethod := "GET" @@ -208,7 +209,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should add custom error page when global-auth-signin url is configured`, func() { - globalExternalAuthSigninSetting := "global-auth-signin" globalExternalAuthSignin := "http://foo.com/global-error-page" @@ -221,7 +221,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should add auth headers when global-auth-response-headers is configured`, func() { - globalExternalAuthResponseHeadersSetting := "global-auth-response-headers" globalExternalAuthResponseHeaders := "Foo, Bar" @@ -235,7 +234,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should set request-redirect when global-auth-request-redirect is configured`, func() { - globalExternalAuthRequestRedirectSetting := "global-auth-request-redirect" globalExternalAuthRequestRedirect := "Foo-Redirect" @@ -258,7 +256,6 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { return strings.Contains(server, globalExternalAuthSnippet) }) }) - }) ginkgo.Context("cookie set by external authentication server", func() { @@ -307,9 +304,9 @@ http { assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - httpbunIP := e.Subsets[0].Addresses[0].IP + nginxIP := e.Subsets[0].Addresses[0].IP - f.UpdateNginxConfigMapData(globalExternalAuthURLSetting, fmt.Sprintf("http://%s/cookies/set/alma/armud", httpbunIP)) + f.UpdateNginxConfigMapData(globalExternalAuthURLSetting, fmt.Sprintf("http://%s/cookies/set/alma/armud", nginxIP)) ing1 = framework.NewSingleIngress(host, "/", host, f.Namespace, "http-cookie-with-error", 80, nil) f.EnsureIngress(ing1) @@ -320,7 +317,6 @@ http { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, "server_name "+host) }) - }) ginkgo.It("user retains cookie by default", func() { diff --git a/test/e2e/settings/global_options.go b/test/e2e/settings/global_options.go index 117860d59..1f84ef5d7 100644 --- a/test/e2e/settings/global_options.go +++ b/test/e2e/settings/global_options.go @@ -31,7 +31,6 @@ var _ = framework.IngressNginxDescribe("global-options", func() { ginkgo.It("should have worker_rlimit_nofile option", func() { f.WaitForNginxConfiguration(func(server string) bool { return strings.Contains(server, fmt.Sprintf("worker_rlimit_nofile %d;", rlimitMaxNumFiles()-1024)) - }) }) diff --git a/test/e2e/settings/hash-size.go b/test/e2e/settings/hash-size.go index 6e3e0480c..5aa5f7c95 100644 --- a/test/e2e/settings/hash-size.go +++ b/test/e2e/settings/hash-size.go @@ -36,7 +36,6 @@ var _ = framework.DescribeSetting("hash size", func() { }) ginkgo.Context("Check server names hash size", func() { - ginkgo.It("should set server_names_hash_bucket_size", func() { f.UpdateNginxConfigMapData("server-name-hash-bucket-size", "512") @@ -52,11 +51,9 @@ var _ = framework.DescribeSetting("hash size", func() { return strings.Contains(server, "server_names_hash_max_size 4096;") }) }) - }) ginkgo.Context("Check proxy header hash size", func() { - ginkgo.It("should set proxy-headers-hash-bucket-size", func() { f.UpdateNginxConfigMapData("proxy-headers-hash-bucket-size", "512") @@ -72,11 +69,9 @@ var _ = framework.DescribeSetting("hash size", func() { return strings.Contains(server, "proxy_headers_hash_max_size 4096;") }) }) - }) ginkgo.Context("Check the variable hash size", func() { - ginkgo.It("should set variables-hash-bucket-size", func() { f.UpdateNginxConfigMapData("variables-hash-bucket-size", "512") @@ -92,11 +87,9 @@ var _ = framework.DescribeSetting("hash size", func() { return strings.Contains(server, "variables_hash_max_size 512;") }) }) - }) ginkgo.Context("Check the map hash size", func() { - ginkgo.It("should set vmap-hash-bucket-size", func() { f.UpdateNginxConfigMapData("map-hash-bucket-size", "512") @@ -104,7 +97,5 @@ var _ = framework.DescribeSetting("hash size", func() { return strings.Contains(server, "map_hash_bucket_size 512;") }) }) - }) - }) diff --git a/test/e2e/settings/ingress_class.go b/test/e2e/settings/ingress_class.go index 232045f3a..80c09f80c 100644 --- a/test/e2e/settings/ingress_class.go +++ b/test/e2e/settings/ingress_class.go @@ -36,6 +36,8 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const barHost = "bar" + var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { f := framework.NewDefaultFramework("ingress-class") @@ -66,7 +68,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { ginkgo.Context("With default ingress class config", func() { ginkgo.It("should ignore Ingress with a different class annotation", func() { - invalidHost := "foo" + invalidHost := fooHost annotations := map[string]string{ ingressclass.IngressKey: "testclass", } @@ -75,7 +77,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { ing.Spec.IngressClassName = nil f.EnsureIngress(ing) - validHost := "bar" + validHost := barHost annotationClass := map[string]string{ ingressclass.IngressKey: ingressclass.DefaultAnnotationValue, } @@ -385,7 +387,6 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { Expect(). Status(http.StatusOK) }) - }) ginkgo.Context("With specific ingress-class flags", func() { @@ -411,13 +412,13 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { }) ginkgo.It("should ignore Ingress with no class and accept the correctly configured Ingresses", func() { - invalidHost := "bar" + invalidHost := barHost ing := framework.NewSingleIngress(invalidHost, "/", invalidHost, f.Namespace, framework.EchoService, 80, nil) ing.Spec.IngressClassName = nil f.EnsureIngress(ing) - validHost := "foo" + validHost := fooHost annotations := map[string]string{ ingressclass.IngressKey: "testclass", } @@ -455,7 +456,6 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { Expect(). Status(http.StatusNotFound) }) - }) ginkgo.Context("With watch-ingress-without-class flag", func() { @@ -480,13 +480,13 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { }) ginkgo.It("should watch Ingress with no class and ignore ingress with a different class", func() { - validHost := "bar" + validHost := barHost ing := framework.NewSingleIngress(validHost, "/", validHost, f.Namespace, framework.EchoService, 80, nil) ing.Spec.IngressClassName = nil f.EnsureIngress(ing) - invalidHost := "foo" + invalidHost := fooHost annotations := map[string]string{ ingressclass.IngressKey: "testclass123", } @@ -511,7 +511,6 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { Expect(). Status(http.StatusNotFound) }) - }) ginkgo.Context("With ingress-class-by-name flag", func() { @@ -579,11 +578,9 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { Expect(). Status(http.StatusNotFound) }) - }) ginkgo.Context("Without IngressClass Cluster scoped Permission", func() { - ginkgo.BeforeEach(func() { icname := fmt.Sprintf("ic-%s", f.Namespace) @@ -629,8 +626,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { }) ginkgo.It("should watch Ingress with correct annotation", func() { - - validHost := "foo" + validHost := fooHost annotations := map[string]string{ ingressclass.IngressKey: "testclass", } @@ -650,7 +646,6 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { }) ginkgo.It("should ignore Ingress with only IngressClassName", func() { - invalidHost := "noclassforyou" ing := framework.NewSingleIngress(invalidHost, "/", invalidHost, f.Namespace, framework.EchoService, 80, nil) @@ -666,6 +661,5 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() { Expect(). Status(http.StatusNotFound) }) - }) }) diff --git a/test/e2e/settings/keep-alive.go b/test/e2e/settings/keep-alive.go index 510a90125..167f5ac18 100644 --- a/test/e2e/settings/keep-alive.go +++ b/test/e2e/settings/keep-alive.go @@ -17,11 +17,11 @@ limitations under the License. package settings import ( - "fmt" "regexp" "strings" "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/assert" "k8s.io/ingress-nginx/test/e2e/framework" ) @@ -41,7 +41,7 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("keep-alive", "140") f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`keepalive_timeout 140s;`)) + return strings.Contains(server, `keepalive_timeout 140s;`) }) }) @@ -49,9 +49,8 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("keep-alive-requests", "200") f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`keepalive_requests 200;`)) + return strings.Contains(server, `keepalive_requests 200;`) }) - }) }) @@ -60,7 +59,8 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("upstream-keepalive-connections", "128") f.WaitForNginxConfiguration(func(server string) bool { - match, _ := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive 128;`, server) + match, err := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive 128;`, server) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error matching the upstream keepalive time") return match }) }) @@ -69,7 +69,8 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("upstream-keepalive-timeout", "120") f.WaitForNginxConfiguration(func(server string) bool { - match, _ := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_timeout\s*120s;`, server) + match, err := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_timeout\s*120s;`, server) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error matching the upstream keepalive time") return match }) }) @@ -78,7 +79,8 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("upstream-keepalive-time", "75s") f.WaitForNginxConfiguration(func(server string) bool { - match, _ := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_time\s*75s;`, server) + match, err := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_time\s*75s;`, server) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error matching the upstream keepalive time") return match }) }) @@ -87,7 +89,8 @@ var _ = framework.DescribeSetting("keep-alive keep-alive-requests", func() { f.UpdateNginxConfigMapData("upstream-keepalive-requests", "200") f.WaitForNginxConfiguration(func(server string) bool { - match, _ := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_requests\s*200;`, server) + match, err := regexp.MatchString(`upstream\supstream_balancer\s\{[\s\S]*keepalive_requests\s*200;`, server) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error matching the upstream keepalive time") return match }) }) diff --git a/test/e2e/settings/listen_nondefault_ports.go b/test/e2e/settings/listen_nondefault_ports.go index 8b5d22f6e..9d3952227 100644 --- a/test/e2e/settings/listen_nondefault_ports.go +++ b/test/e2e/settings/listen_nondefault_ports.go @@ -17,23 +17,20 @@ limitations under the License. package settings import ( - "context" "fmt" "net/http" "strings" "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.IngressNginxDescribe("[Flag] custom HTTP and HTTPS ports", func() { - host := "forwarded-headers" - f := framework.NewDefaultFramework("forwarded-port-headers") + f := framework.NewDefaultFramework("forwarded-port-headers", framework.WithHTTPBunEnabled()) ginkgo.BeforeEach(func() { f.NewEchoDeployment() @@ -46,7 +43,6 @@ var _ = framework.IngressNginxDescribe("[Flag] custom HTTP and HTTPS ports", fun ginkgo.Context("with a plain HTTP ingress", func() { ginkgo.It("should set X-Forwarded-Port headers accordingly when listening on a non-default HTTP port", func() { - ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -66,9 +62,7 @@ var _ = framework.IngressNginxDescribe("[Flag] custom HTTP and HTTPS ports", fun }) ginkgo.Context("with a TLS enabled ingress", func() { - ginkgo.It("should set X-Forwarded-Port header to 443", func() { - ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, nil) f.EnsureIngress(ing) @@ -92,27 +86,13 @@ var _ = framework.IngressNginxDescribe("[Flag] custom HTTP and HTTPS ports", fun Expect(). Status(http.StatusOK). Body(). - Contains(fmt.Sprintf("x-forwarded-port=443")) + Contains("x-forwarded-port=443") }) ginkgo.Context("when external authentication is configured", func() { - ginkgo.It("should set the X-Forwarded-Port header to 443", func() { - f.NewHttpbunDeployment() - - err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBunService, f.Namespace, 1) - assert.Nil(ginkgo.GinkgoT(), err) - - e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBunService, metav1.GetOptions{}) - assert.Nil(ginkgo.GinkgoT(), err) - - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint") - assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint") - - httpbunIP := e.Subsets[0].Addresses[0].IP - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", httpbunIP), + "nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/basic-auth/user/password", f.HTTPBunIP), "nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start", } @@ -141,7 +121,7 @@ var _ = framework.IngressNginxDescribe("[Flag] custom HTTP and HTTPS ports", fun Expect(). Status(http.StatusOK). Body(). - Contains(fmt.Sprintf("x-forwarded-port=443")) + Contains("x-forwarded-port=443") }) }) }) diff --git a/test/e2e/settings/log-format.go b/test/e2e/settings/log-format.go index 24877818d..18835cb66 100644 --- a/test/e2e/settings/log-format.go +++ b/test/e2e/settings/log-format.go @@ -36,7 +36,6 @@ var _ = framework.DescribeSetting("log-format-*", func() { }) ginkgo.Context("Check log-format-escape-json and log-format-escape-none", func() { - ginkgo.It("should not configure log-format escape by default", func() { f.WaitForNginxConfiguration( func(cfg string) bool { @@ -78,7 +77,6 @@ var _ = framework.DescribeSetting("log-format-*", func() { }) ginkgo.Context("Check log-format-upstream with log-format-escape-json and log-format-escape-none", func() { - ginkgo.It("log-format-escape-json enabled", func() { f.SetNginxConfigMapData(map[string]string{ "log-format-escape-json": "true", diff --git a/test/e2e/settings/namespace_selector.go b/test/e2e/settings/namespace_selector.go index 3bf856566..1da62ee86 100644 --- a/test/e2e/settings/namespace_selector.go +++ b/test/e2e/settings/namespace_selector.go @@ -29,12 +29,12 @@ import ( var _ = framework.IngressNginxDescribeSerial("[Flag] watch namespace selector", func() { f := framework.NewDefaultFramework("namespace-selector") - notMatchedHost, matchedHost := "bar", "foo" + notMatchedHost, matchedHost := barHost, fooHost var notMatchedNs string var matchedNs string // create a test namespace, under which create an ingress and backend deployment - prepareTestIngress := func(baseName string, host string, labels map[string]string) string { + prepareTestIngress := func(host string, labels map[string]string) string { ns, err := framework.CreateKubeNamespaceWithLabel(f.BaseName, labels, f.KubeClientSet) assert.Nil(ginkgo.GinkgoT(), err, "creating test namespace") f.NewEchoDeployment(framework.WithDeploymentNamespace(ns)) @@ -49,8 +49,8 @@ var _ = framework.IngressNginxDescribeSerial("[Flag] watch namespace selector", } ginkgo.BeforeEach(func() { - notMatchedNs = prepareTestIngress(notMatchedHost, notMatchedHost, nil) // create namespace without label "foo=bar" - matchedNs = prepareTestIngress(matchedHost, matchedHost, map[string]string{"foo": "bar"}) + notMatchedNs = prepareTestIngress(notMatchedHost, nil) // create namespace without label "foo=bar" + matchedNs = prepareTestIngress(matchedHost, map[string]string{fooHost: barHost}) }) ginkgo.AfterEach(func() { @@ -59,9 +59,7 @@ var _ = framework.IngressNginxDescribeSerial("[Flag] watch namespace selector", }) ginkgo.Context("With specific watch-namespace-selector flags", func() { - - ginkgo.It("should ingore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar", func() { - + ginkgo.It("should ignore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar", func() { f.WaitForNginxConfiguration(func(cfg string) bool { return !strings.Contains(cfg, "server_name bar") && strings.Contains(cfg, "server_name foo") @@ -86,7 +84,7 @@ var _ = framework.IngressNginxDescribeSerial("[Flag] watch namespace selector", if ns.Labels == nil { ns.Labels = make(map[string]string) } - ns.Labels["foo"] = "bar" + ns.Labels[fooHost] = barHost _, err = f.KubeClientSet.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "labeling not matched namespace") @@ -97,7 +95,7 @@ var _ = framework.IngressNginxDescribeSerial("[Flag] watch namespace selector", if ing.Labels == nil { ing.Labels = make(map[string]string) } - ing.Labels["foo"] = "bar" + ing.Labels[fooHost] = barHost _, err = f.KubeClientSet.NetworkingV1().Ingresses(notMatchedNs).Update(context.TODO(), ing, metav1.UpdateOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "updating ingress") diff --git a/test/e2e/settings/no_auth_locations.go b/test/e2e/settings/no_auth_locations.go index 2fc4b6455..103c057d7 100644 --- a/test/e2e/settings/no_auth_locations.go +++ b/test/e2e/settings/no_auth_locations.go @@ -18,12 +18,12 @@ package settings import ( "fmt" - "golang.org/x/crypto/bcrypt" "net/http" "strings" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" + "golang.org/x/crypto/bcrypt" corev1 "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -34,7 +34,7 @@ var _ = framework.DescribeSetting("[Security] no-auth-locations", func() { f := framework.NewDefaultFramework("no-auth-locations") setting := "no-auth-locations" - username := "foo" + username := fooHost password := "bar" secretName := "test-secret" host := "no-auth-locations" @@ -100,7 +100,8 @@ func buildBasicAuthIngressWithSecondPath(host, namespace, secretName, pathName s ObjectMeta: metav1.ObjectMeta{ Name: host, Namespace: namespace, - Annotations: map[string]string{"nginx.ingress.kubernetes.io/auth-type": "basic", + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/auth-type": "basic", "nginx.ingress.kubernetes.io/auth-secret": secretName, "nginx.ingress.kubernetes.io/auth-realm": "test auth", }, @@ -147,7 +148,6 @@ func buildBasicAuthIngressWithSecondPath(host, namespace, secretName, pathName s } func buildSecret(username, password, name, namespace string) *corev1.Secret { - //out, err := exec.Command("openssl", "passwd", "-crypt", password).CombinedOutput() out, err := bcrypt.GenerateFromPassword([]byte(password), 14) assert.Nil(ginkgo.GinkgoT(), err, "creating password") diff --git a/test/e2e/settings/no_tls_redirect_locations.go b/test/e2e/settings/no_tls_redirect_locations.go index 2fca545ff..8339eb23e 100644 --- a/test/e2e/settings/no_tls_redirect_locations.go +++ b/test/e2e/settings/no_tls_redirect_locations.go @@ -17,7 +17,6 @@ limitations under the License. package settings import ( - "fmt" "strings" "github.com/onsi/ginkgo/v2" @@ -34,7 +33,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.EnsureIngress(ing) f.WaitForNginxConfiguration(func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("force_no_ssl_redirect = true,")) + return !strings.Contains(server, "force_no_ssl_redirect = true,") }) wlKey := "no-tls-redirect-locations" @@ -43,8 +42,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.UpdateNginxConfigMapData(wlKey, wlValue) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf("force_no_ssl_redirect = true,")) + return strings.Contains(server, "force_no_ssl_redirect = true,") }) - }) }) diff --git a/test/e2e/settings/ocsp/ocsp.go b/test/e2e/settings/ocsp/ocsp.go index 483013f59..4d419c5d0 100644 --- a/test/e2e/settings/ocsp/ocsp.go +++ b/test/e2e/settings/ocsp/ocsp.go @@ -47,6 +47,7 @@ var _ = framework.DescribeSetting("OCSP", func() { }) ginkgo.It("should enable OCSP and contain stapling information in the connection", func() { + ginkgo.Skip("Skipped due to a bug with cfssl and Alpine") host := "www.example.com" f.UpdateNginxConfigMapData("enable-ocsp", "true") @@ -68,7 +69,7 @@ var _ = framework.DescribeSetting("OCSP", func() { var pemCertBuffer bytes.Buffer pemCertBuffer.Write(leafCert) - pemCertBuffer.Write([]byte("\n")) + pemCertBuffer.WriteString("\n") pemCertBuffer.Write(intermediateCa) f.EnsureSecret(&corev1.Secret{ @@ -112,7 +113,7 @@ var _ = framework.DescribeSetting("OCSP", func() { return strings.Contains(server, fmt.Sprintf(`server_name %v`, host)) }) - tlsConfig := &tls.Config{ServerName: host, InsecureSkipVerify: true} + tlsConfig := &tls.Config{ServerName: host, InsecureSkipVerify: true} //nolint:gosec // Ignore the gosec error in testing f.HTTPTestClientWithTLSConfig(tlsConfig). GET("/"). WithURL(f.GetURL(framework.HTTPS)). @@ -195,7 +196,8 @@ const configTemplate = ` func prepareCertificates(namespace string) error { config := fmt.Sprintf(configTemplate, namespace) - err := os.WriteFile("cfssl_config.json", []byte(config), 0644) + //nolint:gosec // Not change permission to avoid possible issues + err := os.WriteFile("cfssl_config.json", []byte(config), 0o644) if err != nil { return fmt.Errorf("creating cfssl_config.json file: %v", err) } @@ -290,7 +292,7 @@ func ocspserveDeployment(namespace string) (*appsv1.Deployment, *corev1.Service) Containers: []corev1.Container{ { Name: name, - Image: "registry.k8s.io/ingress-nginx/e2e-test-cfssl@sha256:d02c1e18f573449966999fc850f1fed3d37621bf77797562cbe77ebdb06a66ea", + Image: "registry.k8s.io/ingress-nginx/e2e-test-cfssl@sha256:adaa118c179c41cb33fb567004a1f0c71b8fce6bc13263efa63d42dddd5b4346", Command: []string{ "/bin/bash", "-c", diff --git a/test/e2e/settings/opentelemetry.go b/test/e2e/settings/opentelemetry.go index 92d202cb3..b5fc6ff4e 100644 --- a/test/e2e/settings/opentelemetry.go +++ b/test/e2e/settings/opentelemetry.go @@ -31,7 +31,9 @@ const ( opentelemetryOperationName = "opentelemetry-operation-name" opentelemetryLocationOperationName = "opentelemetry-location-operation-name" opentelemetryConfig = "opentelemetry-config" - opentelemetryConfigPath = "/etc/nginx/opentelemetry.toml" + opentelemetryConfigPath = "/etc/ingress-controller/telemetry/opentelemetry.toml" + + enable = "true" ) var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { @@ -46,7 +48,7 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { ginkgo.It("should not exists opentelemetry directive", func() { config := map[string]string{} - config[enableOpentelemetry] = "false" + config[enableOpentelemetry] = disable f.SetNginxConfigMapData(config) f.EnsureIngress(framework.NewSingleIngress(enableOpentelemetry, "/", enableOpentelemetry, f.Namespace, "http-svc", 80, nil)) @@ -59,7 +61,7 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { ginkgo.It("should exists opentelemetry directive when is enabled", func() { config := map[string]string{} - config[enableOpentelemetry] = "true" + config[enableOpentelemetry] = enable config[opentelemetryConfig] = opentelemetryConfigPath f.SetNginxConfigMapData(config) @@ -73,9 +75,9 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { ginkgo.It("should include opentelemetry_trust_incoming_spans on directive when enabled", func() { config := map[string]string{} - config[enableOpentelemetry] = "true" + config[enableOpentelemetry] = enable config[opentelemetryConfig] = opentelemetryConfigPath - config[opentelemetryTrustIncomingSpan] = "true" + config[opentelemetryTrustIncomingSpan] = enable f.SetNginxConfigMapData(config) f.EnsureIngress(framework.NewSingleIngress(enableOpentelemetry, "/", enableOpentelemetry, f.Namespace, "http-svc", 80, nil)) @@ -88,7 +90,7 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { ginkgo.It("should not exists opentelemetry_operation_name directive when is empty", func() { config := map[string]string{} - config[enableOpentelemetry] = "true" + config[enableOpentelemetry] = enable config[opentelemetryConfig] = opentelemetryConfigPath config[opentelemetryOperationName] = "" f.SetNginxConfigMapData(config) @@ -103,7 +105,7 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { ginkgo.It("should exists opentelemetry_operation_name directive when is configured", func() { config := map[string]string{} - config[enableOpentelemetry] = "true" + config[enableOpentelemetry] = enable config[opentelemetryConfig] = opentelemetryConfigPath config[opentelemetryOperationName] = "HTTP $request_method $uri" f.SetNginxConfigMapData(config) @@ -115,5 +117,4 @@ var _ = framework.IngressNginxDescribe("Configure Opentelemetry", func() { return strings.Contains(cfg, `opentelemetry_operation_name "HTTP $request_method $uri"`) }) }) - }) diff --git a/test/e2e/settings/opentracing.go b/test/e2e/settings/opentracing.go deleted file mode 100644 index aee01ea60..000000000 --- a/test/e2e/settings/opentracing.go +++ /dev/null @@ -1,252 +0,0 @@ -/* -Copyright 2020 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 ( - "fmt" - "net/http" - "strings" - "time" - - "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" - - "k8s.io/ingress-nginx/test/e2e/framework" -) - -const ( - enableOpentracing = "enable-opentracing" - opentracingTrustIncomingSpan = "opentracing-trust-incoming-span" - - zipkinCollectorHost = "zipkin-collector-host" - - jaegerCollectorHost = "jaeger-collector-host" - jaegerSamplerHost = "jaeger-sampler-host" - jaegerPropagationFormat = "jaeger-propagation-format" - // jaegerEndpoint = "jaeger-endpoint" - - datadogCollectorHost = "datadog-collector-host" - - opentracingOperationName = "opentracing-operation-name" - opentracingLocationOperationName = "opentracing-location-operation-name" -) - -var _ = framework.IngressNginxDescribe("Configure OpenTracing", func() { - f := framework.NewDefaultFramework("enable-opentracing") - - ginkgo.BeforeEach(func() { - f.NewEchoDeployment() - }) - - ginkgo.AfterEach(func() { - }) - - ginkgo.It("should not exists opentracing directive", func() { - config := map[string]string{} - config[enableOpentracing] = "false" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return !strings.Contains(cfg, "opentracing on") - }) - }) - - ginkgo.It("should exists opentracing directive when is enabled", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return strings.Contains(cfg, "opentracing on") - }) - }) - - ginkgo.It("should include opentracing_trust_incoming_span off directive when disabled", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[opentracingTrustIncomingSpan] = "false" - config[zipkinCollectorHost] = "127.0.0.1" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return strings.Contains(cfg, "opentracing_trust_incoming_span off") - }) - }) - - ginkgo.It("should not exists opentracing_operation_name directive when is empty", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - config[opentracingOperationName] = "" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return !strings.Contains(cfg, "opentracing_operation_name") - }) - }) - - ginkgo.It("should exists opentracing_operation_name directive when is configured", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - config[opentracingOperationName] = "HTTP $request_method $uri" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return strings.Contains(cfg, `opentracing_operation_name "HTTP $request_method $uri"`) - }) - }) - - ginkgo.It("should not exists opentracing_location_operation_name directive when is empty", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - config[opentracingLocationOperationName] = "" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return !strings.Contains(cfg, "opentracing_location_operation_name") - }) - }) - - ginkgo.It("should exists opentracing_location_operation_name directive when is configured", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - config[opentracingLocationOperationName] = "HTTP $request_method $uri" - f.SetNginxConfigMapData(config) - - f.EnsureIngress(framework.NewSingleIngress(enableOpentracing, "/", enableOpentracing, f.Namespace, "http-svc", 80, nil)) - - f.WaitForNginxConfiguration( - func(cfg string) bool { - return strings.Contains(cfg, "opentracing_location_operation_name \"HTTP $request_method $uri\"") - }) - }) - - ginkgo.It("should enable opentracing using zipkin", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[zipkinCollectorHost] = "127.0.0.1" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - }) - - ginkgo.It("should enable opentracing using jaeger", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[jaegerCollectorHost] = "127.0.0.1" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - }) - - ginkgo.It("should enable opentracing using jaeger with sampler host", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[jaegerCollectorHost] = "127.0.0.1" - config[jaegerSamplerHost] = "127.0.0.1" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - }) - - ginkgo.It("should propagate the w3c header when configured with jaeger", func() { - host := "jaeger-w3c" - config := map[string]string{} - config[enableOpentracing] = "true" - config[jaegerCollectorHost] = "127.0.0.1" - config[jaegerPropagationFormat] = "w3c" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - - ing := framework.NewSingleIngress(host, "/", 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("/"). - WithHeader("Host", host). - Expect(). - Status(http.StatusOK). - Body(). - Match("traceparent=[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}") - }) - - /* - ginkgo.It("should enable opentracing using jaeger with an HTTP endpoint", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[jaegerEndpoint] = "http://127.0.0.1/api/traces" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - }) - */ - - ginkgo.It("should enable opentracing using datadog", func() { - config := map[string]string{} - config[enableOpentracing] = "true" - config[datadogCollectorHost] = "http://127.0.0.1" - f.SetNginxConfigMapData(config) - - framework.Sleep(10 * time.Second) - log, err := f.NginxLogs() - assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") - assert.NotContains(ginkgo.GinkgoT(), log, "Unexpected failure reloading the backend", "reloading nginx after a configmap change") - }) -}) diff --git a/test/e2e/settings/pod_security_policy_volumes.go b/test/e2e/settings/pod_security_policy_volumes.go index dd4df3bd9..f8fc58300 100644 --- a/test/e2e/settings/pod_security_policy_volumes.go +++ b/test/e2e/settings/pod_security_policy_volumes.go @@ -38,7 +38,6 @@ var _ = framework.IngressNginxDescribe("[Security] Pod Security Policies with vo f := framework.NewDefaultFramework("pod-security-policies-volumes") ginkgo.It("should be running with a Pod Security Policy", func() { - k8sversion, err := f.KubeClientSet.Discovery().ServerVersion() if err != nil { assert.Nil(ginkgo.GinkgoT(), err, "getting version") diff --git a/test/e2e/settings/proxy_connect_timeout.go b/test/e2e/settings/proxy_connect_timeout.go index 1290775a5..185ac1dd1 100644 --- a/test/e2e/settings/proxy_connect_timeout.go +++ b/test/e2e/settings/proxy_connect_timeout.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:dupl // Ignore dupl errors for similar test case package settings import ( @@ -64,5 +65,4 @@ var _ = framework.DescribeSetting("proxy-connect-timeout", func() { return !strings.Contains(server, fmt.Sprintf("proxy_connect_timeout %ss;", proxyConnectTimeout)) }) }) - }) diff --git a/test/e2e/settings/proxy_host.go b/test/e2e/settings/proxy_host.go index 8f564414a..35aafc53d 100644 --- a/test/e2e/settings/proxy_host.go +++ b/test/e2e/settings/proxy_host.go @@ -34,6 +34,14 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) annotations := map[string]string{ "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Custom-Header: $proxy_host"`, @@ -55,6 +63,15 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host using the upstream-vhost annotation value", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) upstreamVHost := "different.host" annotations := map[string]string{ @@ -66,7 +83,7 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { f.WaitForNginxConfiguration( func(server string) bool { return strings.Contains(server, fmt.Sprintf("server_name %v", test)) && - strings.Contains(server, fmt.Sprintf("set $proxy_host $proxy_upstream_name")) + strings.Contains(server, "set $proxy_host $proxy_upstream_name") }) f.HTTPTestClient(). diff --git a/test/e2e/settings/proxy_protocol.go b/test/e2e/settings/proxy_protocol.go index 8b0e56fe4..cfce68bf8 100644 --- a/test/e2e/settings/proxy_protocol.go +++ b/test/e2e/settings/proxy_protocol.go @@ -33,8 +33,10 @@ import ( "k8s.io/ingress-nginx/test/e2e/framework" ) +const proxyProtocol = "proxy-protocol" + var _ = framework.DescribeSetting("use-proxy-protocol", func() { - f := framework.NewDefaultFramework("proxy-protocol") + f := framework.NewDefaultFramework(proxyProtocol) setting := "use-proxy-protocol" @@ -42,9 +44,9 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { f.NewEchoDeployment() f.UpdateNginxConfigMapData(setting, "false") }) - + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should respect port passed by the PROXY Protocol", func() { - host := "proxy-protocol" + host := proxyProtocol f.UpdateNginxConfigMapData(setting, "true") @@ -63,21 +65,25 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { defer conn.Close() header := "PROXY TCP4 192.168.0.1 192.168.0.11 56324 1234\r\n" - conn.Write([]byte(header)) - conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + _, err = conn.Write([]byte(header)) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing header") + + _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing request") data, err := io.ReadAll(conn) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error reading connection data") body := string(data) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", "proxy-protocol")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=192.168.0.1")) + assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", proxyProtocol)) + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-for=192.168.0.1") }) + //nolint:dupl // Ignore dupl errors for similar test case ginkgo.It("should respect proto passed by the PROXY Protocol server port", func() { - host := "proxy-protocol" + host := proxyProtocol f.UpdateNginxConfigMapData(setting, "true") @@ -96,21 +102,24 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { defer conn.Close() header := "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n" - conn.Write([]byte(header)) - conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + _, err = conn.Write([]byte(header)) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing header") + + _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing request") data, err := io.ReadAll(conn) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error reading connection data") body := string(data) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", "proxy-protocol")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=443")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=https")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=192.168.0.1")) + assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", proxyProtocol)) + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=443") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=https") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-for=192.168.0.1") }) ginkgo.It("should enable PROXY Protocol for HTTPS", func() { - host := "proxy-protocol" + host := proxyProtocol f.UpdateNginxConfigMapData(setting, "true") @@ -145,11 +154,11 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { assert.Nil(ginkgo.GinkgoT(), err, "unexpected error reading connection data") body := string(data) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", "proxy-protocol")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=https")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-scheme=https")) - assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=192.168.0.1")) + assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%v", proxyProtocol)) + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=https") + assert.Contains(ginkgo.GinkgoT(), body, "x-scheme=https") + assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-for=192.168.0.1") }) ginkgo.It("should enable PROXY Protocol for TCP", func() { @@ -205,8 +214,11 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { defer conn.Close() header := "PROXY TCP4 192.168.0.1 192.168.0.11 56324 8080\r\n" - conn.Write([]byte(header)) - conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + _, err = conn.Write([]byte(header)) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing header") + + _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: proxy-protocol\r\n\r\n")) + assert.Nil(ginkgo.GinkgoT(), err, "unexpected error writing request") _, err = io.ReadAll(conn) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error reading connection data") diff --git a/test/e2e/settings/proxy_read_timeout.go b/test/e2e/settings/proxy_read_timeout.go index c84956cc9..484b44f24 100644 --- a/test/e2e/settings/proxy_read_timeout.go +++ b/test/e2e/settings/proxy_read_timeout.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:dupl // Ignore dupl errors for similar test case package settings import ( @@ -64,5 +65,4 @@ var _ = framework.DescribeSetting("proxy-read-timeout", func() { return !strings.Contains(server, fmt.Sprintf("proxy_read_timeout %ss;", proxyReadtimeout)) }) }) - }) diff --git a/test/e2e/settings/proxy_send_timeout.go b/test/e2e/settings/proxy_send_timeout.go index 886642bc0..bdcd46f0f 100644 --- a/test/e2e/settings/proxy_send_timeout.go +++ b/test/e2e/settings/proxy_send_timeout.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:dupl // Ignore dupl errors for similar test case package settings import ( @@ -64,5 +65,4 @@ var _ = framework.DescribeSetting("proxy-send-timeout", func() { return !strings.Contains(server, fmt.Sprintf("proxy_send_timeout %ss;", proxySendTimeout)) }) }) - }) diff --git a/test/e2e/settings/server_snippet.go b/test/e2e/settings/server_snippet.go index 1577c927c..8ddf10fd9 100644 --- a/test/e2e/settings/server_snippet.go +++ b/test/e2e/settings/server_snippet.go @@ -37,10 +37,16 @@ var _ = framework.DescribeSetting("configmap server-snippet", func() { hostAnnots := "serverannotssnippet1.foo.com" f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", "server-snippet": ` more_set_headers "Globalfoo: Foooo";`, }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() annotations := map[string]string{ "nginx.ingress.kubernetes.io/server-snippet": ` more_set_headers "Foo: Bar"; @@ -99,6 +105,11 @@ var _ = framework.DescribeSetting("configmap server-snippet", func() { more_set_headers "Globalfoo: Foooo";`, }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() annotations := map[string]string{ "nginx.ingress.kubernetes.io/server-snippet": ` more_set_headers "Foo: Bar"; diff --git a/test/e2e/settings/ssl_passthrough.go b/test/e2e/settings/ssl_passthrough.go index a906a2d11..b10511bde 100644 --- a/test/e2e/settings/ssl_passthrough.go +++ b/test/e2e/settings/ssl_passthrough.go @@ -19,7 +19,7 @@ package settings import ( "context" "crypto/tls" - "fmt" + "net" "net/http" "strings" @@ -34,7 +34,7 @@ import ( ) var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { - f := framework.NewDefaultFramework("ssl-passthrough") + f := framework.NewDefaultFramework("ssl-passthrough", framework.WithHTTPBunEnabled()) ginkgo.BeforeEach(func() { err := f.UpdateIngressControllerDeployment(func(deployment *appsv1.Deployment) error { @@ -54,7 +54,6 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { ginkgo.Describe("With enable-ssl-passthrough enabled", func() { ginkgo.It("should enable ssl-passthrough-proxy-port on a different port", func() { - err := f.UpdateIngressControllerDeployment(func(deployment *appsv1.Deployment) error { args := deployment.Spec.Template.Spec.Containers[0].Args args = append(args, "--ssl-passthrough-proxy-port=1442") @@ -77,7 +76,6 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { }) ginkgo.It("should pass unknown traffic to default backend and handle known traffic", func() { - host := "testpassthrough.com" echoName := "echopass" @@ -86,7 +84,14 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { "nginx.ingress.kubernetes.io/ssl-passthrough": "true", } - ingressDef := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, echoName, 80, annotations) + ingressDef := framework.NewSingleIngressWithTLS(host, + "/", + host, + []string{host}, + f.Namespace, + echoName, + 80, + annotations) tlsConfig, err := framework.CreateIngressTLSSecret(f.KubeClientSet, ingressDef.Spec.TLS[0].Hosts, ingressDef.Spec.TLS[0].SecretName, @@ -119,7 +124,17 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { Value: "/certs/tls.key", }, } - f.NewDeploymentWithOpts("echopass", "ghcr.io/sharat87/httpbun:latest", 80, 1, nil, nil, envs, volumeMount, volume, false) + + f.NewDeploymentWithOpts("echopass", + framework.HTTPBunImage, + 80, + 1, + nil, + nil, + envs, + volumeMount, + volume, + false) f.EnsureIngress(ingressDef) @@ -133,7 +148,14 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { /* This one should not receive traffic as it does not contain passthrough annotation */ hostBad := "noannotationnopassthrough.com" - ingBad := f.EnsureIngress(framework.NewSingleIngressWithTLS(hostBad, "/", hostBad, []string{hostBad}, f.Namespace, echoName, 80, nil)) + ingBad := f.EnsureIngress(framework.NewSingleIngressWithTLS(hostBad, + "/", + hostBad, + []string{hostBad}, + f.Namespace, + echoName, + 80, + nil)) tlsConfigBad, err := framework.CreateIngressTLSSecret(f.KubeClientSet, ingBad.Spec.TLS[0].Hosts, ingBad.Spec.TLS[0].SecretName, @@ -146,20 +168,21 @@ var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { return strings.Contains(server, "listen 442") }) + //nolint:gosec // Ignore the gosec error in testing f.HTTPTestClientWithTLSConfig(&tls.Config{ServerName: host, InsecureSkipVerify: true}). GET("/"). - WithURL(fmt.Sprintf("https://%s:443", host)). + WithURL("https://"+net.JoinHostPort(host, "443")). ForceResolve(f.GetNginxIP(), 443). Expect(). Status(http.StatusOK) + //nolint:gosec // Ignore the gosec error in testing f.HTTPTestClientWithTLSConfig(&tls.Config{ServerName: hostBad, InsecureSkipVerify: true}). GET("/"). - WithURL(fmt.Sprintf("https://%s:443", hostBad)). + WithURL("https://"+net.JoinHostPort(hostBad, "443")). ForceResolve(f.GetNginxIP(), 443). Expect(). Status(http.StatusNotFound) - }) }) }) diff --git a/test/e2e/settings/tls.go b/test/e2e/settings/tls.go index a249f8bad..51f760df8 100644 --- a/test/e2e/settings/tls.go +++ b/test/e2e/settings/tls.go @@ -87,9 +87,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers)", f }) ginkgo.Context("should configure HSTS policy header", func() { - var ( - tlsConfig *tls.Config - ) + var tlsConfig *tls.Config const ( hstsMaxAge = "hsts-max-age" @@ -112,7 +110,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers)", f f.UpdateNginxConfigMapData(hstsMaxAge, "86400") f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`hsts_max_age = 86400,`)) + return strings.Contains(server, `hsts_max_age = 86400,`) }) f.HTTPTestClientWithTLSConfig(tlsConfig). @@ -131,7 +129,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers)", f }) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`hsts_include_subdomains = false,`)) + return strings.Contains(server, `hsts_include_subdomains = false,`) }) f.HTTPTestClientWithTLSConfig(tlsConfig). @@ -151,7 +149,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers)", f }) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`hsts_preload = true,`)) + return strings.Contains(server, `hsts_preload = true,`) }) f.HTTPTestClientWithTLSConfig(tlsConfig). @@ -170,17 +168,18 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers)", f hstsIncludeSubdomains: "false", }) - // we can not use gorequest here because it flattens the duplicate headers - // and specifically in case of Strict-Transport-Security it ignore extra headers - // intead of concatenating, rightfully. And I don't know of any API it provides for getting raw headers. - curlCmd := fmt.Sprintf("curl -I -k --fail --silent --resolve settings-tls:443:127.0.0.1 https://settings-tls%v", "?hsts=true") - output, err := f.ExecIngressPod(curlCmd) - assert.Nil(ginkgo.GinkgoT(), err) - assert.Contains(ginkgo.GinkgoT(), output, "strict-transport-security: max-age=86400; preload") - // this is what the upstream sets - assert.NotContains(ginkgo.GinkgoT(), output, "strict-transport-security: max-age=3600; preload") - }) + expectResponse := f.HTTPTestClientWithTLSConfig(tlsConfig). + GET("/"). + WithURL(f.GetURL(framework.HTTPS)). + WithHeader("Host", host). + WithQuery("hsts", "true"). + Expect() + expectResponse.Header("Strict-Transport-Security").Equal("max-age=86400; preload") + header := expectResponse.Raw().Header + got := header["Strict-Transport-Security"] + assert.Equal(ginkgo.GinkgoT(), 1, len(got)) + }) }) ginkgo.Context("ports or X-Forwarded-Host check during HTTP tp HTTPS redirection", func() { diff --git a/test/e2e/settings/validations/validations.go b/test/e2e/settings/validations/validations.go new file mode 100644 index 000000000..ac95a453a --- /dev/null +++ b/test/e2e/settings/validations/validations.go @@ -0,0 +1,101 @@ +/* +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") + //nolint:dupl // Ignore dupl errors for similar test case + ginkgo.It("should allow ingress based on their risk on webhooks", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + + 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") + }) + //nolint:dupl // Ignore dupl errors for similar test case + ginkgo.It("should allow ingress based on their risk on webhooks", func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "true", + }) + defer func() { + f.SetNginxConfigMapData(map[string]string{ + "allow-snippet-annotations": "false", + }) + }() + 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") + }) +}) diff --git a/test/e2e/ssl/secret_update.go b/test/e2e/ssl/secret_update.go index 77e64c6b2..8e81f09f9 100644 --- a/test/e2e/ssl/secret_update.go +++ b/test/e2e/ssl/secret_update.go @@ -73,7 +73,8 @@ var _ = framework.IngressNginxDescribe("[SSL] secret update", func() { dummySecret.Data["some-key"] = []byte("some value") - f.KubeClientSet.CoreV1().Secrets(f.Namespace).Update(context.TODO(), dummySecret, metav1.UpdateOptions{}) + _, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace).Update(context.TODO(), dummySecret, metav1.UpdateOptions{}) + assert.Nil(ginkgo.GinkgoT(), err, "updating secret") assert.NotContains(ginkgo.GinkgoT(), log, fmt.Sprintf("starting syncing of secret %v/dummy", f.Namespace)) assert.NotContains(ginkgo.GinkgoT(), log, fmt.Sprintf("error obtaining PEM from secret %v/dummy", f.Namespace)) @@ -97,7 +98,7 @@ var _ = framework.IngressNginxDescribe("[SSL] secret update", func() { return strings.Contains(server, "server_name invalid-ssl") && strings.Contains(server, "listen 443") }) - + //nolint:gosec // Ignore certificate validation in testing resp := f.HTTPTestClientWithTLSConfig(&tls.Config{ServerName: host, InsecureSkipVerify: true}). GET("/"). WithURL(f.GetURL(framework.HTTPS)). diff --git a/test/e2e/status/update.go b/test/e2e/status/update.go index 5c6ea4977..c3c48f8d2 100644 --- a/test/e2e/status/update.go +++ b/test/e2e/status/update.go @@ -71,7 +71,7 @@ var _ = framework.IngressNginxDescribe("[Status] status update", func() { f.NewEchoDeployment() - ing := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) + f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxConfiguration( func(cfg string) bool { @@ -84,7 +84,7 @@ var _ = framework.IngressNginxDescribe("[Status] status update", func() { err = cmd.Process.Kill() assert.Nil(ginkgo.GinkgoT(), err, "unexpected error terminating kubectl proxy") - ing, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Get(context.TODO(), host, metav1.GetOptions{}) + ing, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Get(context.TODO(), host, metav1.GetOptions{}) assert.Nil(ginkgo.GinkgoT(), err, "unexpected error getting %s/%v Ingress", f.Namespace, host) ing.Status.LoadBalancer.Ingress = []v1.IngressLoadBalancerIngress{} @@ -108,6 +108,7 @@ var _ = framework.IngressNginxDescribe("[Status] status update", func() { } }() + //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated err = wait.Poll(5*time.Second, 4*time.Minute, func() (done bool, err error) { ing, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Get(context.TODO(), host, metav1.GetOptions{}) if err != nil { @@ -134,7 +135,8 @@ func getHostIP() net.IP { } defer conn.Close() - localAddr := conn.LocalAddr().(*net.UDPAddr) + localAddr, ok := conn.LocalAddr().(*net.UDPAddr) + assert.True(ginkgo.GinkgoT(), ok, "unexpected type: %T", conn.LocalAddr()) return localAddr.IP } diff --git a/test/e2e/tcpudp/tcp.go b/test/e2e/tcpudp/tcp.go index 16a633b63..f06d6c9a3 100644 --- a/test/e2e/tcpudp/tcp.go +++ b/test/e2e/tcpudp/tcp.go @@ -148,18 +148,18 @@ var _ = framework.IngressNginxDescribe("[TCP] tcp-services", func() { } var ips []string - var retryErr error + var errRetry error err = wait.ExponentialBackoff(retry, func() (bool, error) { - ips, retryErr = resolver.LookupHost(context.Background(), "google-public-dns-b.google.com") - if retryErr == nil { + ips, errRetry = resolver.LookupHost(context.Background(), "google-public-dns-b.google.com") + if errRetry == nil { return true, nil } return false, nil }) - + //nolint:staticcheck // TODO: will replace it since wait.ErrWaitTimeout is deprecated if err == wait.ErrWaitTimeout { - err = retryErr + err = errRetry } assert.Nil(ginkgo.GinkgoT(), err, "unexpected error from DNS resolver") @@ -167,7 +167,6 @@ var _ = framework.IngressNginxDescribe("[TCP] tcp-services", func() { }) ginkgo.It("should reload after an update in the configuration", func() { - ginkgo.By("setting up a first deployment") f.NewEchoDeployment(framework.WithDeploymentName("first-service")) @@ -217,5 +216,4 @@ var _ = framework.IngressNginxDescribe("[TCP] tcp-services", func() { assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") assert.Contains(ginkgo.GinkgoT(), logs, "Backend successfully reloaded") }) - }) diff --git a/test/e2e/wait-for-nginx.sh b/test/e2e/wait-for-nginx.sh index 153d348c2..0726bde10 100755 --- a/test/e2e/wait-for-nginx.sh +++ b/test/e2e/wait-for-nginx.sh @@ -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}