From 23a2b983d803b7a6b5f390fef0b34fc2bf3c064a Mon Sep 17 00:00:00 2001 From: ssttehrani Date: Mon, 23 Oct 2023 23:19:47 +0330 Subject: [PATCH] Initial commit --- .github/workflows/image.yaml | 34 +++ .github/workflows/pr.yaml | 21 ++ .gitignore | 25 +++ .golangci.yaml | 39 ++++ .goreleaser.yml | 50 +++++ Dockerfile | 34 +++ LICENSE | 201 ++++++++++++++++++ Makefile | 92 ++++++++ PROJECT | 3 + README.md | 168 +++++++++++++++ config/default/issuer.yaml | 6 + config/default/kustomization.yaml | 14 ++ config/default/namespace.yaml | 5 + config/htpasswd/certificate.yaml | 10 + config/htpasswd/deployment.yaml | 63 ++++++ config/htpasswd/kustomization.yaml | 14 ++ config/htpasswd/role.yaml | 15 ++ config/htpasswd/rolebinding.yaml | 11 + config/htpasswd/serviceaccount.yaml | 4 + config/oidc/certificate.yaml | 10 + config/oidc/configmap.yaml | 25 +++ config/oidc/deployment.yaml | 68 ++++++ config/oidc/kustomization.yaml | 15 ++ config/oidc/role.yaml | 15 ++ config/oidc/rolebinding.yaml | 11 + config/oidc/serviceaccount.yaml | 4 + go.mod | 85 ++++++++ go.sum | 296 ++++++++++++++++++++++++++ hack/Dockerfile.release | 3 + hack/make-release-tag.sh | 42 ++++ hack/tools.go | 17 ++ main.go | 57 +++++ pkg/auth/convert.go | 171 +++++++++++++++ pkg/auth/convert_test.go | 281 +++++++++++++++++++++++++ pkg/auth/htpasswd.go | 241 +++++++++++++++++++++ pkg/auth/htpasswd_test.go | 193 +++++++++++++++++ pkg/auth/oidc_connect.go | 315 ++++++++++++++++++++++++++++ pkg/auth/server.go | 124 +++++++++++ pkg/cli/defaults.go | 79 +++++++ pkg/cli/err.go | 61 ++++++ pkg/cli/htpasswd.go | 148 +++++++++++++ pkg/cli/oidc_connect.go | 83 ++++++++ pkg/config/config.go | 100 +++++++++ pkg/store/state.go | 91 ++++++++ pkg/store/state_test.go | 35 ++++ pkg/version/version.go | 26 +++ skaffold.yaml | 22 ++ 47 files changed, 3427 insertions(+) create mode 100644 .github/workflows/image.yaml create mode 100644 .github/workflows/pr.yaml create mode 100644 .gitignore create mode 100644 .golangci.yaml create mode 100644 .goreleaser.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 PROJECT create mode 100644 README.md create mode 100644 config/default/issuer.yaml create mode 100644 config/default/kustomization.yaml create mode 100644 config/default/namespace.yaml create mode 100644 config/htpasswd/certificate.yaml create mode 100644 config/htpasswd/deployment.yaml create mode 100644 config/htpasswd/kustomization.yaml create mode 100644 config/htpasswd/role.yaml create mode 100644 config/htpasswd/rolebinding.yaml create mode 100644 config/htpasswd/serviceaccount.yaml create mode 100644 config/oidc/certificate.yaml create mode 100644 config/oidc/configmap.yaml create mode 100644 config/oidc/deployment.yaml create mode 100644 config/oidc/kustomization.yaml create mode 100644 config/oidc/role.yaml create mode 100644 config/oidc/rolebinding.yaml create mode 100644 config/oidc/serviceaccount.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 hack/Dockerfile.release create mode 100755 hack/make-release-tag.sh create mode 100644 hack/tools.go create mode 100644 main.go create mode 100644 pkg/auth/convert.go create mode 100644 pkg/auth/convert_test.go create mode 100644 pkg/auth/htpasswd.go create mode 100644 pkg/auth/htpasswd_test.go create mode 100644 pkg/auth/oidc_connect.go create mode 100644 pkg/auth/server.go create mode 100644 pkg/cli/defaults.go create mode 100644 pkg/cli/err.go create mode 100644 pkg/cli/htpasswd.go create mode 100644 pkg/cli/oidc_connect.go create mode 100644 pkg/config/config.go create mode 100644 pkg/store/state.go create mode 100644 pkg/store/state_test.go create mode 100644 pkg/version/version.go create mode 100644 skaffold.yaml diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml new file mode 100644 index 0000000..cd2d4a6 --- /dev/null +++ b/.github/workflows/image.yaml @@ -0,0 +1,34 @@ +name: Build Container Image + +on: + push: + tags: + - 'v*' + +jobs: + docker: + name: docker + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + with: + platforms: amd64 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/${{ github.repository }} + - uses: docker/build-push-action@v5 + with: + file: "Dockerfile" + context: . + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000..e7be119 --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,21 @@ +name: Pull Request + +on: [pull_request] + +jobs: + + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 + with: + go-version: ^1.21 + - run: go mod download + - name: build + run: make build + - name: lint + run: make lint + - name: test + run: make check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8af42b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +*.vscode + +# Kubernetes Generated files - skip generated files, except for vendored files + +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +*.swp +*.swo +*~ diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..94d01e9 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,39 @@ +run: + timeout: 10m + +linters: + enable: + - bodyclose + - gocognit + - goconst + - gocyclo + - godot + - gofmt + - goimports + - golint + - goprintffuncname + - gosec + - govet + - lll + - misspell + - nakedret + - staticcheck + - unconvert + - unparam + - whitespace + - wsl + +linters-settings: + misspell: + locale: US + gofmt: + simplify: true + unparam: + check-exported: false + govet: + exclude: + +issues: + exclude: + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked + diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..ad5f945 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,50 @@ +project_name: contour-auth-multi-tenant +before: + hooks: + - go mod download + - go generate ./... +builds: +- env: + - CGO_ENABLED=0 + goarch: + - amd64 + - arm64 + goos: + - linux + - darwin + ldflags: + - -s + - -w + - -X github.com/snapp-incubator/contour-auth-multi-tenant/pkg/version.Progname={{ .ProjectName }} + - -X github.com/snapp-incubator/contour-auth-multi-tenant/pkg/version.Version={{ .Env.VERSION }} + - -X github.com/snapp-incubator/contour-auth-multi-tenant/pkg/version.Sha={{ .Env.SHA }} + - -X github.com/snapp-incubator/contour-auth-multi-tenant/pkg/version.BuildDate={{ .Date }} +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' +dockers: +- goarch: amd64 + goos: linux + dockerfile: hack/Dockerfile.release + skip_push: true + image_templates: + - "ghcr.io/projectcontour/{{ .ProjectName }}:{{ .Env.VERSION }}" + - "ghcr.io/projectcontour/{{ .ProjectName }}:latest" + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Env.VERSION}}" + - "--label=org.opencontainers.image.url=https://projectcontour.io/" + - "--label=org.opencontainers.image.documentation=https://projectcontour.io/" + - "--label=org.opencontainers.image.vendor=Project Contour" + - "--label=org.opencontainers.image.licenses=Apache-2.0" + - "--label=org.opencontainers.image.title=Contour Authserver" + - "--label=org.opencontainers.image.description=Contour Authorization Server" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..62544bb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# Build the contour-auth-multi-tenant binary. +# +# Note that this is not used for releasing, since goreleaser handles that. + +FROM golang:1.21 as base + +ENV GO111MODULE=on + +WORKDIR /workspace + +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum + +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +FROM base as builder + +# Copy the go source +COPY . . + +# Build +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on make build + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/bin/contour-auth-multi-tenant . +USER nonroot:nonroot + +ENTRYPOINT ["/contour-auth-multi-tenant"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e481249 --- /dev/null +++ b/Makefile @@ -0,0 +1,92 @@ +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +BIN := contour-auth-multi-tenant + +REPO := github.com/snapp-incubator/contour-auth-multi-tenant +SHA := $(shell git rev-parse --short=8 HEAD) +VERSION := $(shell git describe --exact-match 2>/dev/null || basename $$(git describe --all --long 2>/dev/null)) +BUILDDATE := $(shell TZ=GMT date '+%Y-%m-%dT%R:%S%z') + +GO_BUILD_LDFLAGS := \ + -s \ + -w \ + -X $(REPO)/pkg/version.Progname=$(BIN) \ + -X $(REPO)/pkg/version.Version=$(VERSION) \ + -X $(REPO)/pkg/version.Sha=$(SHA) \ + -X $(REPO)/pkg/version.BuildDate=$(BUILDDATE) + +# Image URL to use all building/pushing image targets +IMG ?= $(BIN):$(VERSION) + +all: build + +test: check + +.PHONY: check +check: ## Run tests +check: fmt vet lint + go test ./... -coverprofile cover.out + +.PHONY: build +build: ## Build controller binary +build: fmt vet + go build -mod=readonly -ldflags "$(GO_BUILD_LDFLAGS)" -o bin/$(BIN) main.go + +.PHONY: run +run: ## Run against the configured Kubernetes cluster in ~/.kube/config +run: fmt vet + go run ./main.go + +.PHONY: deploy +deploy: ## Deploy controller in the configured Kubernetes cluster in ~/.kube/config +deploy: + cd config/testserver && kustomize edit set image controller=${IMG} + kustomize build config/default | kubectl apply -f - + +.PHONY: fmt +fmt: ## Run go fmt against code + go fmt -mod=readonly ./... + +.PHONY: vet +vet: ## Run go vet against code + go vet -mod=readonly -ldflags "$(GO_BUILD_LDFLAGS)" ./... + +.PHONY: lint +lint: ## Run linters + go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2 run -v --exclude-use-default=false + +.PHONY: docker-build +docker-build: ## Build the docker image + docker build . -t ${IMG} + +.PHONY: docker-push +docker-push: ## Push the docker image + docker push ${IMG} + +.PHONY: release +release: ## Build and publish a release to Github + # Check there is a token. + [[ -n "$$GITHUB_TOKEN" ]] || [[ -r ~/.config/goreleaser/github_token ]] + # Check we are on a tag. + git describe --exact-match >/dev/null + # Do a full dry-run. + goreleaser check + SHA=$(SHA) VERSION=$(VERSION) goreleaser release --clean + +.PHONY: clean +clean: + @rm -rf cover.out + @rm -rf bin + @rm -rf dist + +.PHONY: help +help: + @echo "$(BIN)" + @echo + @echo Targets: + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9._-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort diff --git a/PROJECT b/PROJECT new file mode 100644 index 0000000..ede980f --- /dev/null +++ b/PROJECT @@ -0,0 +1,3 @@ +domain: snappcloud.io +repo: github.com/snapp-incubator/contour-auth-multi-tenant +version: "2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f5ba20e --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# contour-auth-multi-tenant + +`contour-auth-multi-tenant` is an Envoy-compatible authorization server that builds upon the foundation of [contour-authserver](https://github.com/projectcontour/contour-authserver). + +`contour-authserver` implements the Envoy [external authorization][4] +GRPC protocol (both v2 and v3). `contour-authserver` has two authorization +backends that are selected by subcommands. + +`contour-auth-multi-tenant` adds multi-tenancy feature to `contour-authserver` and improves its performance to make it production-ready. + +Key Features: +- Multi-Tenancy Support: One of the standout features of contour-auth-multi-tenant is its built-in support for multi-tenancy. This allows us to securely manage and isolate authorization scopes for different tenants within a single instance. + +- Enhanced Performance: We have meticulously optimized contour-auth-multi-tenant to deliver outstanding performance under heavy loads. This ensures that your application's authorization layer can handle requests efficiently, without introducing bottlenecks or delays. + +# htpasswd + +Usage: + +``` +Run a htpasswd basic authentication server + +Usage: + contour-authserver htpasswd [OPTIONS] + +Flags: + --address string The address the authentication endpoint binds to. (default ":9090") + --auth-realm string Basic authentication realm. (default "default") + -h, --help help for htpasswd + --metrics-address string The address the metrics endpoint binds to. (default ":8080") + --selector string Selector (label-query) to filter Secrets, supports '=', '==', and '!='. + --tls-ca-path string Path to the TLS CA certificate bundle. + --tls-cert-path string Path to the TLS server certificate. + --tls-key-path string Path to the TLS server key. + --watch-namespaces strings The list of namespaces to watch for Secrets. +``` + +## htpasswd Secrets + +The `htpasswd` backend implements [HTTP basic authentication][3] +against a set of Secrets that contain [htpasswd][1] formatted data. +The htpasswd data must be stored in the `auth` key, which is compatible +with ingress-nginx [`auth-file` Secrets][2]. + +The `htpasswd` backend only accesses Secrets that are +annotated with `auth.contour.snappcloud.io/type: basic`. + +Secrets that are annotated with the `auth.contour.snappcloud.io/realm` +will only be used if the annotation value matches the value of the +`--auth-realm` flag. +The `auth.contour.snappcloud.io/realm: *` annotation explicitly marks +a Secret as being valid for all realms. +This is equivalent to omitting the annotation. + +When it authenticates a request, the `htpasswd` backend injects the +`Auth-Username` and `Auth-Realm` headers, which contain the +authenticated user name and the basic authentication realm respectively. + +The `--watch-namespaces` flag specifies the namespaces where the +`htpasswd` backend will discover Secrets. +If this flag is empty, Secrets from all namespaces will be used. + +The `--selector` flag accepts a [label selector][5] that can be +used to further restrict which Secrets the `htpasswd` backend will consume. (Use it for lower resource consumption and better performance as it reduce reconcile loops dramatically if there are lots of secrets) + +## htpasswd Multi-Tenancy Support + +While `contour-authserver` matches the provided credential in the `Authorization` header against all the Secrets, `contour-auth-multi-tenant` offers a more refined approach. It matches the provided credential against a user-defined Secret, enabling precise control over authorization scopes. + +To leverage the multi-tenancy feature of `contour-auth-multi-tenant`, include a secret reference in your Envoy [CheckRequest](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto#service-auth-v3-checkrequest). Specify the reference in the request's context, using `secretRef` as the key and the `namespace/secretName` format for the value. + +# OIDC + +Usage: + +``` +Run a oidc authentication server + +Usage: + contour-authserver oidc [OPTIONS] + +Flags: + --config string Path to config file ( yaml format ) + -h, --help help for htpasswd + --tls-ca-path string Path to the TLS CA certificate bundle. + --tls-cert-path string Path to the TLS server certificate. + --tls-key-path string Path to the TLS server key. + +``` +Oidc configuration can be specified with configmaps. +Please visit [DexIDP](https://github.com/dexidp/dex) for more detail. + +``` +## The following entries are the variables accepted by the Contour OIDC module. +## server address and port +address: ":9443" + +## OIDC issuer URL +issuerURL: "http://" + +## App redirect path ( usually point back to app url) +redirectURL: "https://" +redirectPath: "/callback" +allowEmptyClientSecret: false +scopes: +- openid +- profile +- email +- offline_access +usernameClaim: "nickname" +emailClaim: "" +serveTLS: false +clientID: "" +clientSecret: "" +``` + + +# Request Headers + +Both authorization backends emit the `Auth-Handler` header, which +publishes the name of the backend that approved or rejected the +authorization. + +The authorization context is also reflected into HTTP headers +prefixed with `Auth-Context-`. Note that This can generate malformed +HTTP headers. The `testserver` backend always creates the context +headers, but the `htpasswd` backend only does so for authenticated +requests (i.e. the origin server gets them bu the client never +does.) + +# Deploying `contour-auth-multi-tenant` + +The recommended way to deploy `contour-auth-multi-tenant` is to use the Kustomize +[deployment YAML](./config/default). This will deploy services for `htpasswd` and `oidc` backends. For developer deployments, +[Skaffold](https://skaffold.dev/) seems to work reasonably well. + +# Releasing `contour-auth-multi-tenant` + +Maintainers who need to release a new version of `contour-auth-multi-tenant` +can follow the following steps: + +```bash +# Ensure that you have a Github token either in $GITHUB_TOKEN or in ~/.config/goreleaser/github_token. +# Ensure that goreleaser is installed. + +# Tag the release. +$ ./hack/make-release-tag.sh $OLDVERS $NEWVERS + +# Push the release tag to Github. +$ git push origin $NEWVERS + +# Build and release binaries and Docker images. +$ make release + +# Log in with your GitHub account and token to push the images. +$ docker login -u +$ docker push ghcr.io/projectcontour/contour-authserver:$NEWVERS +$ docker push ghcr.io/projectcontour/contour-authserver:latest + +# Log out. +$ docker logout +``` + +[1]: https://httpd.apache.org/docs/current/programs/htpasswd.html +[2]: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#authentication +[3]: https://tools.ietf.org/html/rfc7617 +[4]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter +[5]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors diff --git a/config/default/issuer.yaml b/config/default/issuer.yaml new file mode 100644 index 0000000..737c557 --- /dev/null +++ b/config/default/issuer.yaml @@ -0,0 +1,6 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned +spec: + selfSigned: {} diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 0000000..57e5724 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,14 @@ +# Adds namespace to all resources. +namespace: projectcontour-auth + +bases: +- ../htpasswd +- ../oidc + +resources: +- namespace.yaml +- issuer.yaml + + + + diff --git a/config/default/namespace.yaml b/config/default/namespace.yaml new file mode 100644 index 0000000..aa05cb6 --- /dev/null +++ b/config/default/namespace.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: projectcontour-auth + diff --git a/config/htpasswd/certificate.yaml b/config/htpasswd/certificate.yaml new file mode 100644 index 0000000..3728fbd --- /dev/null +++ b/config/htpasswd/certificate.yaml @@ -0,0 +1,10 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: htpasswd +spec: + dnsNames: + - htpasswd + secretName: htpasswd + issuerRef: + name: selfsigned diff --git a/config/htpasswd/deployment.yaml b/config/htpasswd/deployment.yaml new file mode 100644 index 0000000..6fbf573 --- /dev/null +++ b/config/htpasswd/deployment.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: htpasswd + labels: + app.kubernetes.io/name: htpasswd +spec: + selector: + matchLabels: + app.kubernetes.io/name: htpasswd + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: htpasswd + spec: + serviceAccountName: htpasswd + containers: + - name: htpasswd + image: htpasswd + imagePullPolicy: IfNotPresent + command: + - /contour-auth-multi-tenant + args: + - htpasswd + - --address=:9443 + - --tls-ca-path=/tls/ca.crt + - --tls-cert-path=/tls/tls.crt + - --tls-key-path=/tls/tls.key + ports: + - name: auth + containerPort: 9443 + protocol: TCP + volumeMounts: + - name: tls + mountPath: /tls + readOnly: true + resources: + limits: + cpu: 100m + memory: 90Mi + volumes: + - name: tls + secret: + secretName: htpasswd + +--- + +apiVersion: v1 +kind: Service +metadata: + name: htpasswd + labels: + app.kubernetes.io/name: htpasswd +spec: + ports: + - name: auth + protocol: TCP + port: 9443 + targetPort: 9443 + selector: + app.kubernetes.io/name: htpasswd + type: ClusterIP diff --git a/config/htpasswd/kustomization.yaml b/config/htpasswd/kustomization.yaml new file mode 100644 index 0000000..c0b892e --- /dev/null +++ b/config/htpasswd/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- deployment.yaml +- serviceaccount.yaml +- certificate.yaml +- role.yaml +- rolebinding.yaml + +images: +- name: htpasswd + newName: contour-auth-multi-tenant + newTag: latest diff --git a/config/htpasswd/role.yaml b/config/htpasswd/role.yaml new file mode 100644 index 0000000..c7c73ef --- /dev/null +++ b/config/htpasswd/role.yaml @@ -0,0 +1,15 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: contour:authserver:htpasswd +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch diff --git a/config/htpasswd/rolebinding.yaml b/config/htpasswd/rolebinding.yaml new file mode 100644 index 0000000..2fec9a6 --- /dev/null +++ b/config/htpasswd/rolebinding.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: contour:authserver:htpasswd +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: contour:authserver:htpasswd +subjects: +- kind: ServiceAccount + name: htpasswd diff --git a/config/htpasswd/serviceaccount.yaml b/config/htpasswd/serviceaccount.yaml new file mode 100644 index 0000000..acfaa7b --- /dev/null +++ b/config/htpasswd/serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: htpasswd diff --git a/config/oidc/certificate.yaml b/config/oidc/certificate.yaml new file mode 100644 index 0000000..35bbf5f --- /dev/null +++ b/config/oidc/certificate.yaml @@ -0,0 +1,10 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oidc +spec: + dnsNames: + - oidc + secretName: oidc + issuerRef: + name: selfsigned diff --git a/config/oidc/configmap.yaml b/config/oidc/configmap.yaml new file mode 100644 index 0000000..534b793 --- /dev/null +++ b/config/oidc/configmap.yaml @@ -0,0 +1,25 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: oidc-config +data: + auth-svr-config.yaml: | + address: ":9443" + issuerURL: "http://dex.auth.app.192.168.10.134.nip.io:9080" + redirectURL: "https://echo.oidc.app.local:9443" + redirectPath: "/callback" + allowEmptyClientSecret: false + scopes: + - openid + - profile + - email + - offline_access + usernameClaim: "nickname" + emailClaim: "" + serveTLS: false + clientID: "bender" + clientSecret: "googledocs" + cacheTimeout: 40 + skipIssuerCheck: true + +## ENHANCEMENT :: move clientID and Clientsecret to kubernetes secret \ No newline at end of file diff --git a/config/oidc/deployment.yaml b/config/oidc/deployment.yaml new file mode 100644 index 0000000..de6dd80 --- /dev/null +++ b/config/oidc/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: oidc + labels: + app.kubernetes.io/name: oidc +spec: + selector: + matchLabels: + app.kubernetes.io/name: oidc + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: oidc + spec: + serviceAccountName: oidc + containers: + - name: oidc + image: oidc + imagePullPolicy: IfNotPresent + command: + - /contour-auth-multi-tenant + args: + - oidc + - --config=/config/auth-svr-config.yaml + - --tls-ca-path=/tls/ca.crt + - --tls-cert-path=/tls/tls.crt + - --tls-key-path=/tls/tls.key + ports: + - name: auth + containerPort: 9443 + protocol: TCP + volumeMounts: + - name: tls + mountPath: /tls + readOnly: true + - name: oidc-config + mountPath: /config + resources: + limits: + cpu: 100m + memory: 30Mi + volumes: + - name: tls + secret: + secretName: oidc + - name: oidc-config + configMap: + name: oidc-config + + +--- +apiVersion: v1 +kind: Service +metadata: + name: oidc + labels: + app.kubernetes.io/name: oidc +spec: + ports: + - name: auth + protocol: TCP + port: 9443 + targetPort: 9443 + selector: + app.kubernetes.io/name: oidc + type: ClusterIP diff --git a/config/oidc/kustomization.yaml b/config/oidc/kustomization.yaml new file mode 100644 index 0000000..90067fc --- /dev/null +++ b/config/oidc/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- deployment.yaml +- serviceaccount.yaml +- certificate.yaml +- configmap.yaml +# - role.yaml may not be needed .... +# - rolebinding.yaml + +images: +- name: oidc + newName: contour-auth-multi-tenant + newTag: latest diff --git a/config/oidc/role.yaml b/config/oidc/role.yaml new file mode 100644 index 0000000..078a225 --- /dev/null +++ b/config/oidc/role.yaml @@ -0,0 +1,15 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: contour:authserver:oidc +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch diff --git a/config/oidc/rolebinding.yaml b/config/oidc/rolebinding.yaml new file mode 100644 index 0000000..2fec9a6 --- /dev/null +++ b/config/oidc/rolebinding.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: contour:authserver:htpasswd +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: contour:authserver:htpasswd +subjects: +- kind: ServiceAccount + name: htpasswd diff --git a/config/oidc/serviceaccount.yaml b/config/oidc/serviceaccount.yaml new file mode 100644 index 0000000..cd5718b --- /dev/null +++ b/config/oidc/serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: oidc diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1de3121 --- /dev/null +++ b/go.mod @@ -0,0 +1,85 @@ +module github.com/snapp-incubator/contour-auth-multi-tenant + +go 1.20 + +require ( + github.com/allegro/bigcache v1.2.1 + github.com/coreos/go-oidc/v3 v3.7.0 + github.com/envoyproxy/go-control-plane v0.11.1 + github.com/go-logr/logr v1.2.4 + github.com/mattn/go-isatty v0.0.19 + github.com/spf13/cobra v1.7.0 + github.com/stretchr/testify v1.8.4 + github.com/tg123/go-htpasswd v1.2.1 + golang.org/x/oauth2 v0.13.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 + google.golang.org/grpc v1.58.2 + gopkg.in/yaml.v2 v2.4.0 + k8s.io/api v0.28.2 + k8s.io/apimachinery v0.28.2 + k8s.io/client-go v0.28.2 + sigs.k8s.io/controller-runtime v0.16.2 +) + +require ( + github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/imdario/mergo v0.3.12 // 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/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/net v0.17.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 + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.28.0 // indirect + k8s.io/component-base v0.28.1 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // 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/go.sum b/go.sum new file mode 100644 index 0000000..5afe6d3 --- /dev/null +++ b/go.sum @@ -0,0 +1,296 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-oidc/v3 v3.7.0 h1:FTdj0uexT4diYIPlF4yoFVI5MRO1r5+SEcIpEw9vC0o= +github.com/coreos/go-oidc/v3 v3.7.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM= +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/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/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +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-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +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.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +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/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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.0/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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +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/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +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= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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/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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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 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-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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/tg123/go-htpasswd v1.2.1 h1:i4wfsX1KvvkyoMiHZzjS0VzbAPWfxzI8INcZAKtutoU= +github.com/tg123/go-htpasswd v1.2.1/go.mod h1:erHp1B86KXdwQf1X5ZrLb7erXZnWueEQezb2dql4q58= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +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= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-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.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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.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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +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.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +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.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-20180628173108-788fd7840127/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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/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-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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= +k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= +k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= +k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= +k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= +k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= +k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= +k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= +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-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +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/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= +sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= +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.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/hack/Dockerfile.release b/hack/Dockerfile.release new file mode 100644 index 0000000..48ceee1 --- /dev/null +++ b/hack/Dockerfile.release @@ -0,0 +1,3 @@ +FROM scratch +COPY contour-auth-multi-tenant / +ENTRYPOINT ["/contour-auth-multi-tenant"] diff --git a/hack/make-release-tag.sh b/hack/make-release-tag.sh new file mode 100755 index 0000000..aa4e66e --- /dev/null +++ b/hack/make-release-tag.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash + +# Copyright Project Contour 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. + +readonly PROGNAME=$(basename "$0") +readonly OLDVERS="$1" +readonly NEWVERS="$2" + +if [ -z "$OLDVERS" ] || [ -z "$NEWVERS" ]; then + printf "Usage: %s OLDVERS NEWVERS\n" PROGNAME + exit 1 +fi + +set -o errexit +set -o nounset +set -o pipefail + +if [ -n "$(git tag --list "$NEWVERS")" ]; then + printf "%s: tag '%s' already exists\n" "$PROGNAME" "$NEWVERS" + exit 1 +fi + +git tag -F - "$NEWVERS" < 0 { + cookies := strings.Split(cookieVal, ";") + + for _, c := range cookies { + c = strings.TrimSpace(c) + if strings.HasPrefix(c, oauthTokenName) { + cookieJSON := c[len(oauthTokenName)+1:] + if len(cookieJSON) > 0 { + state = store.ConvertToType([]byte(cookieJSON)) + return state, nil + } + } + } + } + + return nil, fmt.Errorf("no %q cookie", oauthTokenName) +} + +// initProvider initialize oidc provide with ths given issuer URL. return oidc.Provider. +func (o *OIDCConnect) initProvider(ctx context.Context) (*oidc.Provider, error) { + provider, err := oidc.NewProvider(ctx, o.OidcConfig.IssuerURL) + if err != nil { + o.Log.Error(err, "Unable to initialize provider", "issuerUrl", o.OidcConfig.IssuerURL) + return nil, err + } + + return provider, nil +} + +// oauth2Config factory method to oauth2Config. +func (o *OIDCConnect) oauth2Config() *oauth2.Config { + return &oauth2.Config{ + ClientID: o.OidcConfig.ClientID, + ClientSecret: o.OidcConfig.ClientSecret, + Endpoint: o.provider.Endpoint(), + Scopes: o.OidcConfig.Scopes, + RedirectURL: o.OidcConfig.RedirectURL + o.OidcConfig.RedirectPath, + } +} + +// createResponse helper class to create response. Accept status code and return Response. +func createResponse(status int) Response { + allow := status == http.StatusOK + + return Response{ + Response: http.Response{ + StatusCode: status, // defaulted to unauthorized + Header: http.Header{}, + }, + Allow: allow, + } +} + +func parseURL(req *Request) *url.URL { + plainURL, _ := url.QueryUnescape(req.Request.URL.String()) + u, err := url.Parse(plainURL) + + if err != nil { + return nil + } + + if s, ok := req.Request.Header["X-Forwarded-Proto"]; ok { + u.Scheme = s[0] + } else { + u.Scheme = "http" + } + + return u +} diff --git a/pkg/auth/server.go b/pkg/auth/server.go new file mode 100644 index 0000000..771987f --- /dev/null +++ b/pkg/auth/server.go @@ -0,0 +1,124 @@ +// Copyright Project Contour 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 auth + +import ( + "context" + "crypto/tls" + "crypto/x509" + "net" + "os" + + envoy_service_auth_v2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2" + envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type CheckRequestV2 = envoy_service_auth_v2.CheckRequest //nolint:golint +type CheckResponseV2 = envoy_service_auth_v2.CheckResponse //nolint:golint +type CheckRequestV3 = envoy_service_auth_v3.CheckRequest //nolint:golint +type CheckResponseV3 = envoy_service_auth_v3.CheckResponse //nolint:golint + +// Checker is an implementation of the Envoy External Auth API. +// +// https://github.com/envoyproxy/envoy/blob/release/v1.14/api/envoy/service/auth/v3/external_auth.proto +// https://github.com/envoyproxy/envoy/blob/release/v1.14/api/envoy/service/auth/v2/external_auth.proto +type Checker interface { + Check(context.Context, *Request) (*Response, error) +} + +type authV2 struct { + Checker Checker +} + +func (a *authV2) Check(ctx context.Context, check *CheckRequestV2) (*CheckResponseV2, error) { + request := Request{} + request.FromV2(check) + + response, err := a.Checker.Check(ctx, &request) + if err != nil { + return nil, err + } + + return response.AsV2(), nil +} + +type authV3 struct { + Checker Checker +} + +func (a *authV3) Check(ctx context.Context, check *CheckRequestV3) (*CheckResponseV3, error) { + request := Request{} + request.FromV3(check) + + response, err := a.Checker.Check(ctx, &request) + if err != nil { + return nil, err + } + + return response.AsV3(), nil +} + +// RegisterServer registers the Checker with the external authorization +// GRPC server. +func RegisterServer(srv *grpc.Server, c Checker) { + v2 := &authV2{Checker: c} + v3 := &authV3{Checker: c} + + envoy_service_auth_v2.RegisterAuthorizationServer(srv, v2) + envoy_service_auth_v3.RegisterAuthorizationServer(srv, v3) +} + +// RunServer runs the server until signaled by stopChan. +func RunServer(ctx context.Context, listener net.Listener, srv *grpc.Server) error { + errChan := make(chan error) + + go func() { + errChan <- srv.Serve(listener) + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + srv.Stop() + return nil + } +} + +// NewServerCredentials loads TLS transport credentials for the GRPC server. +func NewServerCredentials(certPath string, keyPath string, caPath string) (credentials.TransportCredentials, error) { + srv, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, err + } + + p := x509.NewCertPool() + + if caPath != "" { + ca, err := os.ReadFile(caPath) //nolint:gosec + if err != nil { + return nil, err + } + + p.AppendCertsFromPEM(ca) + } + + return credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{srv}, + RootCAs: p, + }), nil +} diff --git a/pkg/cli/defaults.go b/pkg/cli/defaults.go new file mode 100644 index 0000000..f9342b5 --- /dev/null +++ b/pkg/cli/defaults.go @@ -0,0 +1,79 @@ +// Copyright Project Contour 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 cli + +import ( + "fmt" + "os" + + "github.com/snapp-incubator/contour-auth-multi-tenant/pkg/auth" + "github.com/snapp-incubator/contour-auth-multi-tenant/pkg/version" + + "github.com/spf13/cobra" + "google.golang.org/grpc" +) + +func mustString(s string, err error) string { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", version.Progname, err) + os.Exit(int(EX_CONFIG)) + } + + return s +} + +func anyString(values ...string) bool { + for _, s := range values { + if s != "" { + return true + } + } + + return false +} + +// Defaults applies default settings to a cobra.Command to improve the help output. +func Defaults(c *cobra.Command) *cobra.Command { + c.SilenceUsage = true + c.SilenceErrors = true + c.DisableFlagsInUseLine = true + + return c +} + +// DefaultServer builds a gRPC server from the given flags. +func DefaultServer(cmd *cobra.Command) (*grpc.Server, error) { + opts := []grpc.ServerOption{ + grpc.MaxConcurrentStreams(1 << 20), + } + + if anyString( + mustString(cmd.Flags().GetString("tls-cert-path")), + mustString(cmd.Flags().GetString("tls-key-path")), + mustString(cmd.Flags().GetString("tls-ca-path")), + ) { + creds, err := auth.NewServerCredentials( + mustString(cmd.Flags().GetString("tls-cert-path")), + mustString(cmd.Flags().GetString("tls-key-path")), + mustString(cmd.Flags().GetString("tls-ca-path")), + ) + if err != nil { + return nil, err + } + + opts = append(opts, grpc.Creds(creds)) + } + + return grpc.NewServer(opts...), nil +} diff --git a/pkg/cli/err.go b/pkg/cli/err.go new file mode 100644 index 0000000..7d635d7 --- /dev/null +++ b/pkg/cli/err.go @@ -0,0 +1,61 @@ +// Copyright Project Contour 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 cli + +import "fmt" + +// ExitCode is a process exit code suitable for use with os.Exit. +type ExitCode int + +const ( + // EX_FAIL is an exit code indicating an unspecified error. + EX_FAIL ExitCode = 1 //nolint:golint + + // EX_USAGE is an exit code indicating invalid invocation syntax. + EX_USAGE ExitCode = 65 //nolint:golint + + // EX_NOINPUT is an exit code indicating missing input data. + EX_NOINPUT ExitCode = 66 //nolint:golint + + // EX_DATAERR means the input data was incorrect in some + // way. This should only be used for user's data and not + // system files. + EX_DATAERR ExitCode = 65 //nolint:golint + + // EX_CONFIG means that something was found in an unconfigured + // or misconfigured state. + EX_CONFIG ExitCode = 78 //nolint:golint +) + +// ExitError captures an ExitCode and its associated error message. +type ExitError struct { + Code ExitCode + Err error +} + +func (e ExitError) Error() string { + if e.Err != nil { + return e.Err.Error() + } + + return "" +} + +// ExitErrorf formats and error message along with the ExitCode. +func ExitErrorf(code ExitCode, format string, args ...interface{}) error { + return &ExitError{ + Code: code, + Err: fmt.Errorf(format, args...), + } +} diff --git a/pkg/cli/htpasswd.go b/pkg/cli/htpasswd.go new file mode 100644 index 0000000..fc3296c --- /dev/null +++ b/pkg/cli/htpasswd.go @@ -0,0 +1,148 @@ +// Copyright Project Contour 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 cli + +import ( + "net" + "sync" + + "github.com/snapp-incubator/contour-auth-multi-tenant/pkg/auth" + "github.com/tg123/go-htpasswd" + + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + ctrl_metrics_server "sigs.k8s.io/controller-runtime/pkg/metrics/server" +) + +// NewHtpasswdCommand ... +func NewHtpasswdCommand() *cobra.Command { + cmd := cobra.Command{ + Use: "htpasswd [OPTIONS]", + Short: "Run a htpasswd basic authentication server", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + log := ctrl.Log.WithName("auth.htpasswd") + s := runtime.NewScheme() + + scheme.AddToScheme(s) //nolint:gosec,errcheck + + options := ctrl.Options{ + Scheme: s, + Metrics: ctrl_metrics_server.Options{ + BindAddress: mustString(cmd.Flags().GetString("metrics-address")), + }, + } + + // if namespaces, err := cmd.Flags().GetStringSlice("watch-namespaces"); err == nil && len(namespaces) > 0 { + // // Maps namespaces to cache configs. We will set an empty config + // // so the higher level defaults are used. + // options.Cache.DefaultNamespaces = make(map[string]ctrl_cache.Config) + // for _, ns := range namespaces { + // options.Cache.DefaultNamespaces[ns] = ctrl_cache.Config{} + // } + // } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options) + if err != nil { + return ExitErrorf(EX_CONFIG, "failed to create controller manager: %s", err) + } + + secretsSelector, err := labels.Parse(mustString(cmd.Flags().GetString("selector"))) + if err != nil { + return ExitErrorf(EX_CONFIG, "failed to parse secrets selector: %s", err) + } + + creds := &auth.Creds{ + Map: make(map[string]map[string]*htpasswd.File), + Mu: &sync.RWMutex{}, + } + + htpasswd := &auth.Htpasswd{ + Log: log, + Client: mgr.GetClient(), + Realm: mustString(cmd.Flags().GetString("auth-realm")), + Creds: creds, + Mu: &sync.Mutex{}, + Selector: secretsSelector, + } + + if err := htpasswd.RegisterWithManager(mgr); err != nil { + return ExitErrorf(EX_FAIL, "htpasswd controller registration failed: %w", err) + } + + listener, err := net.Listen("tcp", mustString(cmd.Flags().GetString("address"))) + if err != nil { + return ExitError{EX_CONFIG, err} + } + + srv, err := DefaultServer(cmd) + if err != nil { + return ExitErrorf(EX_CONFIG, "invalid TLS configuration: %s", err) + } + + auth.RegisterServer(srv, htpasswd) + + errChan := make(chan error) + ctx := ctrl.SetupSignalHandler() + + go func() { + log.Info("started authorization server", + "address", mustString(cmd.Flags().GetString("address")), + "realm", htpasswd.Realm) + + if err := auth.RunServer(ctx, listener, srv); err != nil { + errChan <- ExitErrorf(EX_FAIL, "authorization server failed: %w", err) + } + + errChan <- nil + }() + + go func() { + log.Info("started controller") + + if err := mgr.Start(ctx); err != nil { + errChan <- ExitErrorf(EX_FAIL, "controller manager failed: %w", err) + } + + errChan <- nil + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + return nil + } + }, + } + + // Controller flags. + cmd.Flags().String("metrics-address", ":8080", "The address the metrics endpoint binds to.") + cmd.Flags().StringSlice("watch-namespaces", []string{}, "The list of namespaces to watch for Secrets.") + cmd.Flags().String("selector", "", "Selector (label-query) to filter Secrets, supports '=', '==', and '!='.") + + // GRPC flags. + cmd.Flags().String("address", ":9090", "The address the authentication endpoint binds to.") + cmd.Flags().String("tls-cert-path", "", "Path to the TLS server certificate.") + cmd.Flags().String("tls-ca-path", "", "Path to the TLS CA certificate bundle.") + cmd.Flags().String("tls-key-path", "", "Path to the TLS server key.") + + // Authorization flags. + cmd.Flags().String("auth-realm", "default", "Basic authentication realm.") + + return &cmd +} diff --git a/pkg/cli/oidc_connect.go b/pkg/cli/oidc_connect.go new file mode 100644 index 0000000..6966f2e --- /dev/null +++ b/pkg/cli/oidc_connect.go @@ -0,0 +1,83 @@ +// Copyright Project Contour 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 cli + +import ( + "net" + "net/http" + "time" + + "github.com/snapp-incubator/contour-auth-multi-tenant/pkg/auth" + "github.com/snapp-incubator/contour-auth-multi-tenant/pkg/config" + "github.com/spf13/cobra" + + "github.com/allegro/bigcache" + + ctrl "sigs.k8s.io/controller-runtime" +) + +// NewOIDCConnect - start server as OIDC and take in 'config' file as parameter... +func NewOIDCConnect() *cobra.Command { + cmd := cobra.Command{ + Use: "oidc Server [OPTIONS]", + Short: "Run a OIDC authentication server", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + log := ctrl.Log.WithName("auth.oidc") + + cfgFile, err := cmd.Flags().GetString("config") + if err != nil { + return ExitError{EX_CONFIG, err} + } + + cfg, err := config.NewConfig(cfgFile) + if err != nil { + return ExitError{EX_CONFIG, err} + } + + log.Info("init oidc... ") + + bigCache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Duration(cfg.CacheTimeout) * time.Minute)) + + authOidc := &auth.OIDCConnect{ + Log: log, + OidcConfig: cfg, + Cache: bigCache, + HTTPClient: http.DefaultClient, // need to handle client creation with TLS + } + + listener, err := net.Listen("tcp", authOidc.OidcConfig.Address) + if err != nil { + return ExitError{EX_CONFIG, err} + } + + srv, err := DefaultServer(cmd) + if err != nil { + return ExitErrorf(EX_CONFIG, "invalid TLS configuration: %s", err) + } + + auth.RegisterServer(srv, authOidc) + + log.Info("started serving", "address", authOidc.OidcConfig.Address) + return auth.RunServer(ctrl.SetupSignalHandler(), listener, srv) + }, + } + + cmd.Flags().String("config", "", "Path to config file ( Yaml format ).") + cmd.Flags().String("tls-cert-path", "", "Path to the TLS server certificate.") + cmd.Flags().String("tls-ca-path", "", "Path to the TLS CA certificate bundle.") + cmd.Flags().String("tls-key-path", "", "Path to the TLS server key.") + + return &cmd +} diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..f0568aa --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,100 @@ +// Copyright Project Contour 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 config + +import ( + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +// OIDCConfig defines the configuration parameters uses to configure the OIDC provider. +type OIDCConfig struct { + Address string `yaml:"address"` + + ClusterName string `yaml:"clusterName"` + IssuerURL string `yaml:"issuerURL"` + ClientID string `yaml:"clientID"` + ClientSecret string `yaml:"clientSecret"` + AllowEmptyClientSecret bool `yaml:"allowEmptyClientSecret"` + RedirectURL string `yaml:"redirectURL"` // http://gangway.auth.app.local:9080 + RedirectPath string `yaml:"redirectPath"` // /callback + Scopes []string `yaml:"scopes"` + UsernameClaim string `yaml:"usernameClaim"` + EmailClaim string `yaml:"emailClaim"` + ServeTLS bool `yaml:"serveTLS"` + Audience string `yaml:"audience"` + CacheTimeout int32 `yaml:"cacheTimeout"` + SkipIssuerCheck bool `yaml:"skipIssuerCheck"` + + // T decide wether should this be use + SessionSecurityKey string `yaml:"sessionSecurityKey" envconfig:"SESSION_SECURITY_KEY"` +} + +// NewConfig returns a Config struct from serialized config file. +func NewConfig(configFile string) (*OIDCConfig, error) { + cfg := &OIDCConfig{ + CacheTimeout: 40, + SkipIssuerCheck: false, + } + + if configFile != "" { + data, err := os.ReadFile(filepath.Clean(configFile)) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(data, cfg) + if err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("Config file path is required") + } + + err := cfg.Validate() + if err != nil { + return nil, err + } + + if cfg.Address == "" { + cfg.Address = ":9080" + } + + return cfg, nil +} + +// Validate verifies all properties of config struct are initialized. +func (cfg *OIDCConfig) Validate() error { + checks := []struct { + bad bool + errMsg string + }{ + {cfg.IssuerURL == "", "no IssuerURL specified"}, + {cfg.ClientID == "", "no clientID specified"}, + {cfg.ClientSecret == "" && !cfg.AllowEmptyClientSecret, "no clientSecret specified"}, + {cfg.RedirectURL == "", "no redirectURL specified"}, + {cfg.RedirectPath == "", "no redirectURL specified"}, + } + + for _, check := range checks { + if check.bad { + return fmt.Errorf("invalid config: %s", check.errMsg) + } + } + + return nil +} diff --git a/pkg/store/state.go b/pkg/store/state.go new file mode 100644 index 0000000..2ad194d --- /dev/null +++ b/pkg/store/state.go @@ -0,0 +1,91 @@ +// Copyright Project Contour 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 store + +import ( + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" +) + +// OIDCState defines the values kept in state. +type OIDCState struct { + Status int `json:"status"` + AccessToken string `json:"access-token"` + IDToken string `json:"id-token"` + RefreshToken string `json:"refresh-token"` + + RequestID string `json:"req-id"` + RequestPath string `json:"req-path"` + OAuthState string `json:"oauth-state"` + Scheme string `json:"scheme"` +} + +// Iota token. +const ( + StatusNeedToken = iota + StatusTokenReady +) + +// NewState create new state to store token for OIDC. +func NewState() *OIDCState { + state := &OIDCState{ + Status: StatusNeedToken, + } + + return state +} + +// ConvertToByte Convert State to Byte. +func ConvertToByte(s *OIDCState) []byte { + b, _ := json.Marshal(s) + return b +} + +// ConvertToType Convert Byte to State. +func ConvertToType(value []byte) *OIDCState { + state := &OIDCState{} + if err := json.Unmarshal(value, &state); err != nil { + fmt.Println(fmt.Errorf("could not unmarshal %v: ", err)) + } + + return state +} + +// IsNewToken check if current state is new and token from idp is needed. +func (s *OIDCState) IsNewToken() bool { + return s.Status == StatusNeedToken +} + +// IsTokenReady check if token is ready. +func (s *OIDCState) IsTokenReady() bool { + return s.Status == StatusTokenReady +} + +// GenerateOauthState generates a new Oauth State from random bytes. The state define a unique request +// from a particular user and used to identity user during callback or subsequent calls. +func (s *OIDCState) GenerateOauthState() string { + b := make([]byte, 32) + + _, err := rand.Read(b) + if err != nil { + fmt.Println(fmt.Errorf("error reading random bytes generating OauthState: %v", err)) + } + + newState := base64.URLEncoding.EncodeToString(b) + s.OAuthState = newState + + return newState +} diff --git a/pkg/store/state_test.go b/pkg/store/state_test.go new file mode 100644 index 0000000..b6b5143 --- /dev/null +++ b/pkg/store/state_test.go @@ -0,0 +1,35 @@ +// Copyright Project Contour 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 store + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHtpasswdAuth(t *testing.T) { + state := NewState() + // test binary generation + + // Make sure AuthState is empty + assert.Equal(t, false, len(state.OAuthState) > 0) + + state.GenerateOauthState() + assert.Equal(t, true, len(state.OAuthState) > 0) + assert.Equal(t, true, state.IsNewToken()) + + state.Status = StatusTokenReady + assert.Equal(t, true, state.IsTokenReady()) +} diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..43f005e --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,26 @@ +// Copyright Project Contour 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 version + +// Progname is the program name. +var Progname string + +// Version is the software version. +var Version string + +// Sha is the git SHA hash. +var Sha string + +// BuildDate is the date this binary was built. +var BuildDate string diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..938e2a1 --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,22 @@ +apiVersion: skaffold/v2beta5 +kind: Config +metadata: + name: contour-auth-multi-tenant +build: + artifacts: + - image: contour-auth-multi-tenant +deploy: + kustomize: + paths: + - config/default +profiles: +- name: testserver + deploy: + kustomize: + paths: + - config/testserver +- name: htpasswd + deploy: + kustomize: + paths: + - config/htpasswd