diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 3bdb3840..a4551a8d 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -11,17 +11,46 @@ jobs: vet: name: vet runs-on: ubuntu-22.04 - container: golang:1.19 + container: golang:1.22 steps: + - name: "Add GitHub to the SSH known hosts file" + run: | + mkdir -p -m 0700 /root/.ssh + cat </root/.ssh/known_hosts + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + EOF + chmod 600 /root/.ssh/known_hosts + touch /root/.ssh/config + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - uses: actions/checkout@v4 - run: make vet shell: bash test: name: go test runs-on: ubuntu-22.04 - container: golang:1.19 + container: golang:1.22 steps: + - name: "Add GitHub to the SSH known hosts file" + run: | + mkdir -p -m 0700 /root/.ssh + cat </root/.ssh/known_hosts + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + EOF + chmod 600 /root/.ssh/known_hosts + touch /root/.ssh/config + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - uses: actions/checkout@v4 + - name: Adding github workspace as safe directory + # See issue https://github.com/actions/checkout/issues/760 + run: git config --global --add safe.directory $GITHUB_WORKSPACE - run: make test docker_build: name: docker_build @@ -41,6 +70,9 @@ jobs: packages: write id-token: write steps: + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - name: Install Tools # Installing 'bash' because it's required by the 'cosign-installer' action # and 'coreutils' because the 'slsa-provenance-action' requires a version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 354abfec..85d67f31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,21 +9,63 @@ on: branches: - "*" +env: + GOPRIVATE: github.com/jetstack/venafi-connection-lib + jobs: vet: name: vet runs-on: ubuntu-22.04 - container: golang:1.19 + container: golang:1.22 steps: - uses: actions/checkout@v4 + # The only reason we need to configure ~/.ssh/known_hosts is because we are + # using a container-based runner. Non-container runners already have the + # github.com fingerprints in their known_hosts file. We could use `curl + # --silent https://api.github.com/meta` to fetch it but golang:1.22 does not + # have jq installed. + # + # Remember that the container "golang:1.22.0" has two "homes": /root is the + # home returned by getent(), which is what the GitHub Action and SSH will + # use to load .ssh/config and keys under .ssh/, and $HOME is /github/home, + # which is where Git loads ~/.gitconfig from. + - name: "Add GitHub to the SSH known hosts file" + run: | + mkdir -p -m 0700 /root/.ssh + cat </root/.ssh/known_hosts + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + EOF + chmod 600 /root/.ssh/known_hosts + touch /root/.ssh/config + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - run: make vet shell: bash test: name: go test runs-on: ubuntu-22.04 - container: golang:1.19 + container: golang:1.22 steps: + - name: "Add GitHub to the SSH known hosts file" + run: | + mkdir -p -m 0700 /root/.ssh + cat </root/.ssh/known_hosts + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + EOF + chmod 600 /root/.ssh/known_hosts + touch /root/.ssh/config + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - uses: actions/checkout@v4 + - name: Adding github workspace as safe directory + # See issue https://github.com/actions/checkout/issues/760 + run: git config --global --add safe.directory $GITHUB_WORKSPACE - run: make test docker_build: name: docker_build @@ -41,6 +83,9 @@ jobs: steps: - name: Install Tools run: apk add --update make git jq rsync curl + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }} - name: Adding github workspace as safe directory # See issue https://github.com/actions/checkout/issues/760 run: git config --global --add safe.directory $GITHUB_WORKSPACE diff --git a/.gitignore b/.gitignore index 834d55b0..17966351 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ predicate.json *.pub *.tgz +_bin diff --git a/Makefile b/Makefile index 280ed9c2..0f48df1f 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ GOVERSION:=$(shell go version | awk '{print $$3 " " $$4}') GOOS:=$(shell go env GOOS) GOARCH:=$(shell go env GOARCH) +export GOPRIVATE=github.com/jetstack/venafi-connection-lib + BIN_NAME:=preflight DOCKER_IMAGE?=quay.io/jetstack/preflight @@ -48,6 +50,8 @@ build: install: cd $(ROOT_DIR) && $(GO_INSTALL) +export KUBEBUILDER_ASSETS=$(ROOT_DIR)/_bin/tools +test: _bin/tools/etcd _bin/tools/kube-apiserver test: cd $(ROOT_DIR) && go test ./... @@ -73,11 +77,13 @@ build-all-platforms-in-host: build-all-platforms-in-docker: rm -rf ./builds - docker build --rm -t preflight-bin -f ./builder.dockerfile \ + docker buildx build --load --rm -t preflight-bin -f ./builder.dockerfile \ --build-arg oauth_client_id=$(OAUTH_CLIENT_ID) \ --build-arg oauth_client_secret=$(OAUTH_CLIENT_SECRET) \ --build-arg oauth_auth_server_domain=$(OAUTH_AUTH_SERVER_DOMAIN) \ + --ssh default \ . + docker rm -f preflight-bin-container 2>/dev/null || true docker create --rm --name=preflight-bin-container preflight-bin docker cp preflight-bin-container:/go/github.com/jetstack/preflight/builds ./builds docker rm preflight-bin-container @@ -142,3 +148,54 @@ ci-build: ci-test build build-docker-image build-all-platforms bundle-all-platfo ci-publish: ci-build push-docker-image echo "ci-publish is going to be disabled. We are adopting Github actions" + +# NOTE(mael): The download targets for yq, etcd, and kube-apiserver are a lesser +# and suboptimal version of what's in venafi-enhanced-issuer. We will migrate to +# makefile-modules and klone soon, so I didn't want to work too hard on this. + +YQ_linux_amd64_SHA256SUM=bd695a6513f1196aeda17b174a15e9c351843fb1cef5f9be0af170f2dd744f08 +YQ_darwin_amd64_SHA256SUM=b2ff70e295d02695b284755b2a41bd889cfb37454e1fa71abc3a6ec13b2676cf +YQ_darwin_arm64_SHA256SUM=e9fc15db977875de982e0174ba5dc2cf5ae4a644e18432a4262c96d4439b1686 +YQ_VERSION=v4.35.1 + +_bin/downloaded/tools/yq@$(YQ_VERSION)_%: + mkdir -p _bin/downloaded/tools + curl -L https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_$* -o $@ + ./make/util/checkhash.sh $@ $(YQ_$*_SHA256SUM) + chmod +x $@ + +HOST_OS=$(shell uname | tr '[:upper:]' '[:lower:]') +HOST_ARCH=$(shell uname -m | sed 's/x86_64/amd64/') + +_bin/tools/yq: _bin/downloaded/tools/yq@$(YQ_VERSION)_$(HOST_OS)_$(HOST_ARCH) + @mkdir -p _bin/tools + @cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@) + +KUBEBUILDER_TOOLS_linux_amd64_SHA256SUM=f9699df7b021f71a1ab55329b36b48a798e6ae3a44d2132255fc7e46c6790d4d +KUBEBUILDER_TOOLS_darwin_amd64_SHA256SUM=e1913674bacaa70c067e15649237e1f67d891ba53f367c0a50786b4a274ee047 +KUBEBUILDER_TOOLS_darwin_arm64_SHA256SUM=0422632a2bbb0d4d14d7d8b0f05497a4d041c11d770a07b7a55c44bcc5e8ce66 +KUBEBUILDER_ASSETS_VERSION=1.27.1 + +_bin/downloaded/tools/etcd@$(KUBEBUILDER_ASSETS_VERSION)_%: _bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_%.tar.gz | _bin/downloaded/tools + ./make/util/checkhash.sh $< $(KUBEBUILDER_TOOLS_$*_SHA256SUM) + @# O writes the specified file to stdout + tar xfO $< kubebuilder/bin/etcd > $@ && chmod 775 $@ + +_bin/downloaded/tools/kube-apiserver@$(KUBEBUILDER_ASSETS_VERSION)_%: _bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_%.tar.gz | _bin/downloaded/tools + ./make/util/checkhash.sh $< $(KUBEBUILDER_TOOLS_$*_SHA256SUM) + @# O writes the specified file to stdout + tar xfO $< kubebuilder/bin/kube-apiserver > $@ && chmod 775 $@ + +_bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH).tar.gz: | _bin/downloaded/tools + curl -L https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-$(KUBEBUILDER_ASSETS_VERSION)-$(HOST_OS)-$(HOST_ARCH).tar.gz -o $@ + +_bin/downloaded/tools: + @mkdir -p $@ + +_bin/tools/etcd: _bin/downloaded/tools/etcd@$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH) + @mkdir -p _bin/tools + @cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@) + +_bin/tools/kube-apiserver: _bin/downloaded/tools/kube-apiserver@$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH) + @mkdir -p _bin/tools + @cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@) diff --git a/builder.dockerfile b/builder.dockerfile index 32c6ed70..f69dd000 100644 --- a/builder.dockerfile +++ b/builder.dockerfile @@ -1,13 +1,23 @@ -FROM golang:1.21.6 as builder +FROM golang:1.22.5 as builder WORKDIR /go/github.com/jetstack/preflight # Run a dependency resolve with just the go mod files present for # better caching -COPY ./go.mod . -COPY ./go.sum . +COPY go.mod go.sum . -RUN go mod download +COPY < 📖 Learn more about [Venafi Kubernetes Agent network requirements](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/), -> in the two regions. - -## Installation - -The Helm chart is available from the following Venafi OCI registries: - -- `oci://registry.venafi.cloud/charts/venafi-kubernetes-agent` (public) -- `oci://private-registry.venafi.cloud/charts/venafi-kubernetes-agent` (private, US) -- `oci://private-registry.venafi.eu/charts/venafi-kubernetes-agent` (private, EU) - -> ℹī¸ In the following steps it is assumed that you are using the **public** registry. -> -> 📖 Learn [how to access the private Venafi OCI registries](https://docs.venafi.cloud/vaas/k8s-components/th-guide-confg-access-to-tlspk-enterprise-components/). - -Familiarise yourself with the Helm chart: - -```sh -helm show readme oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -helm show values oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -helm template oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -``` - -### 1) Create a Venafi service account - -Create a new service account in the Venafi TLS Protect Cloud web UI. -The service account is used by the Venafi Kubernetes Agent to authenticate to the Venafi Control Plane. -Every Venafi Kubernetes Agent should use a unique service account. -You must create the service account **before** installing the Helm chart. - -First create an RSA key pair: - -```shell -export VENAFI_SERVICE_ACCOUNT="example-cluster" -openssl genrsa -out ${VENAFI_SERVICE_ACCOUNT}.pem -openssl rsa -in ${VENAFI_SERVICE_ACCOUNT}.pem -pubout --out ${VENAFI_SERVICE_ACCOUNT}.pub -``` - -Next create a service account in the Venafi Control Plane: - -- Click **Settings > Service Accounts**. -- Click **New**. -- Type a name for your new service account. - Must match the ${VENAFI_SERVICE_ACCOUNT} variable that you used above. -- Select an **Owning Team**, which is the team who owns the machine you want to create the service account for. -- The scope should be "Kubernetes Discovery" only. -- Set the validity period of your pubic key up to a maximum of 365 days. -- Paste in the **public key** from the pair you generated. -- Click **Save** to finish and return to the Service Account list view. -- Find the row matching the name you entered and copy the "Client ID" value, - by clicking "Copy Client ID" in the row actions menu. - You will need this when you install the Helm chart. - -### 2) Deploy the chart - -Create a Namespace and then create a Secret containing the private key of the service account: - -```shell -export VENAFI_NAMESPACE="venafi" -kubectl create namespace ${VENAFI_NAMESPACE} -kubectl create secret generic agent-credentials \ - --namespace ${VENAFI_NAMESPACE} \ - --from-file=privatekey.pem=${VENAFI_SERVICE_ACCOUNT}.pem -``` - -Install the chart: - -```shell -export VENAFI_CLIENT_ID="" -helm upgrade venafi-kubernetes-agent oci://registry.venafi.cloud/charts/venafi-kubernetes-agent \ - --install \ - --namespace ${VENAFI_NAMESPACE} \ - --set config.clientId="${VENAFI_CLIENT_ID}" -``` - -> ℹī¸ To use the [EU Venafi Control Plane](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/), -> add: `--set config.server=https://api.venafi.eu/`. - -### 3) Connect the cluster in Venafi Control Plane - -- Click **Installations > Kubernetes Clusters**. -- Click **Connect**. -- On step 1, click **Continue**. -- On step 2, select **Advanced Connection**. -- On step 3, click **Continue** to skip. -- On step 4, fill in the details as follows: - - Name: use the name of the service account that you created earlier. E.g. "example-cluster". - - Service Account: select the service account that you created earlier. - - Check "The connection command has completed." box and click **continue**. -- On step 5, either wait for validation or click **Finish** to go back to the cluster list. - -### 4) Verify the deployment - -Check the agent logs: - -```shell -kubectl logs -n ${VENAFI_NAMESPACE} -l app.kubernetes.io/instance=venafi-kubernetes-agent --tail -1 | grep -A 5 "Running Agent" -``` - -You should see: - -```console -2023/10/24 12:10:03 Running Agent... -2023/10/24 12:10:03 Posting data to: https://api.venafi.cloud/ -2023/10/24 12:10:04 Data sent successfully. -``` - -Check the cluster status by visiting the Clusters page in the Venafi Control Plane: -- Click **Installations > Kubernetes Clusters** - -You should see: -- Status: Active -- Last Check In: ...seconds ago - -Check the Event Log page: -- Click **Settings > Event Log** - -You should see the following events for your service account: -- Service Account Access Token Granted -- Login Succeeded +> 📖 Read the [Venafi Kubernetes Agent documentation](https://docs.venafi.cloud/vaas/k8s-components/c-tlspk-agent-overview/), +> to learn how install and configure this Helm chart. ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| | affinity | object | `{}` | Embed YAML for Node affinity settings, see https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/. | -| authentication | object | `{"secretKey":"privatekey.pem","secretName":"agent-credentials"}` | Authentication details for the Venafi Kubernetes Agent | +| authentication | object | `{"secretKey":"privatekey.pem","secretName":"agent-credentials","venafiConnection":{"enabled":false,"name":"venafi-components","namespace":"venafi"}}` | Authentication details for the Venafi Kubernetes Agent | | authentication.secretKey | string | `"privatekey.pem"` | Key name in the referenced secret | | authentication.secretName | string | `"agent-credentials"` | Name of the secret containing the private key | +| authentication.venafiConnection | object | `{"enabled":false,"name":"venafi-components","namespace":"venafi"}` | Configure VenafiConnection authentication | +| authentication.venafiConnection.enabled | bool | `false` | When set to true, the Venafi Kubernetes Agent will authenticate to Venafi using the configuration in a VenafiConnection resource. Use `venafiConnection.enabled=true` for [secretless authentication](https://docs.venafi.cloud/vaas/k8s-components/t-install-tlspk-agent/). When set to true, the `authentication.secret` values will be ignored and the Secret with `authentication.secretName` will _not_ be mounted into the Venafi Kubernetes Agent Pod. | +| authentication.venafiConnection.name | string | `"venafi-components"` | The name of a VenafiConnection resource which contains the configuration for authenticating to Venafi. | +| authentication.venafiConnection.namespace | string | `"venafi"` | The namespace of a VenafiConnection resource which contains the configuration for authenticating to Venafi. | | command | list | `[]` | Specify the command to run overriding default binary. | | config | object | `{"clientId":"","clusterDescription":"","clusterName":"","configmap":{"key":null,"name":null},"period":"0h1m0s","server":"https://api.venafi.cloud/"}` | Configuration section for the Venafi Kubernetes Agent itself | | config.clientId | string | `""` | The client-id returned from the Venafi Control Plane | @@ -153,11 +32,13 @@ You should see the following events for your service account: | config.configmap | object | `{"key":null,"name":null}` | Specify ConfigMap details to load config from an existing resource. This should be blank by default unless you have you own config. | | config.period | string | `"0h1m0s"` | Send data back to the platform every minute unless changed | | config.server | string | `"https://api.venafi.cloud/"` | Overrides the server if using a proxy in your environment For the EU variant use: https://api.venafi.eu/ | +| crds.forceRemoveValidationAnnotations | bool | `false` | The 'x-kubernetes-validations' annotation is not supported in Kubernetes 1.22 and below. This annotation is used by CEL, which is a feature introduced in Kubernetes 1.25 that improves how validation is performed. This option allows to force the 'x-kubernetes-validations' annotation to be excluded, even on Kubernetes 1.25+ clusters. | +| crds.venafiConnection | object | `{"include":false}` | Optionally include the VenafiConnection CRDs | +| crds.venafiConnection.include | bool | `false` | When set to false, the rendered output does not contain the VenafiConnection CRDs and RBAC. This is useful for when the Venafi Connection resources are already installed separately. | | extraArgs | list | `[]` | Specify additional arguments to pass to the agent binary. For example `["--strict", "--oneshot"]` | | fullnameOverride | string | `""` | Helm default setting, use this to shorten the full install name. | | image.pullPolicy | string | `"IfNotPresent"` | Defaults to only pull if not already present | | image.repository | string | `"registry.venafi.cloud/venafi-agent/venafi-agent"` | Default to Open Source image repository | -| image.tag | string | `"v0.1.49"` | Overrides the image tag whose default is the chart appVersion | | imagePullSecrets | list | `[]` | Specify image pull credentials if using a private registry example: - name: my-pull-secret | | metrics.enabled | bool | `true` | Enable the metrics server. If false, the metrics server will be disabled and the other metrics fields below will be ignored. | | metrics.podmonitor.annotations | object | `{}` | Additional annotations to add to the PodMonitor. | @@ -176,7 +57,7 @@ You should see the following events for your service account: | podSecurityContext | object | `{}` | Optional Pod (all containers) `SecurityContext` options, see https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod. | | replicaCount | int | `1` | default replicas, do not scale up | | resources | object | `{"limits":{"memory":"500Mi"},"requests":{"cpu":"200m","memory":"200Mi"}}` | Set resource requests and limits for the pod. Read [Venafi Kubernetes components deployment best practices](https://docs.venafi.cloud/vaas/k8s-components/c-k8s-components-best-practice/#scaling) to learn how to choose suitable CPU and memory resource requests and limits. | -| securityContext | object | `{"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000}` | Add Container specific SecurityContext settings to the container. Takes precedence over `podSecurityContext` when set. See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container | +| securityContext | object | `{"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Add Container specific SecurityContext settings to the container. Takes precedence over `podSecurityContext` when set. See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container | | serviceAccount.annotations | object | `{}` | Annotations YAML to add to the service account | | serviceAccount.create | bool | `true` | Specifies whether a service account should be created | | serviceAccount.name | string | `""` | The name of the service account to use. If blank and `serviceAccount.create` is true, a name is generated using the fullname template of the release. | diff --git a/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl b/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl index ed5e7d00..9a6edc26 100644 --- a/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl +++ b/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl @@ -3,138 +3,13 @@ {{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} -## Additional Information - The Venafi Kubernetes Agent connects your Kubernetes or OpenShift cluster to the Venafi Control Plane. You will require a Venafi Control Plane account to connect your cluster. If you do not have one, you can sign up for a free trial now at: - https://venafi.com/try-venafi/tls-protect/ -Note that there are EU and US Venafi Control Plane options. -Upon signing up you will be redirected to one of either of the following login URLs: -- https://ui.venafi.cloud/ (US) -- https://ui.venafi.eu/ (EU) - -> 📖 Learn more about [Venafi Kubernetes Agent network requirements](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/), -> in the two regions. - -## Installation - -The Helm chart is available from the following Venafi OCI registries: - -- `oci://registry.venafi.cloud/charts/venafi-kubernetes-agent` (public) -- `oci://private-registry.venafi.cloud/charts/venafi-kubernetes-agent` (private, US) -- `oci://private-registry.venafi.eu/charts/venafi-kubernetes-agent` (private, EU) - -> ℹī¸ In the following steps it is assumed that you are using the **public** registry. -> -> 📖 Learn [how to access the private Venafi OCI registries](https://docs.venafi.cloud/vaas/k8s-components/th-guide-confg-access-to-tlspk-enterprise-components/). - -Familiarise yourself with the Helm chart: - -```sh -helm show readme oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -helm show values oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -helm template oci://registry.venafi.cloud/charts/venafi-kubernetes-agent -``` - -### 1) Create a Venafi service account - -Create a new service account in the Venafi TLS Protect Cloud web UI. -The service account is used by the Venafi Kubernetes Agent to authenticate to the Venafi Control Plane. -Every Venafi Kubernetes Agent should use a unique service account. -You must create the service account **before** installing the Helm chart. - -First create an RSA key pair: - -```shell -export VENAFI_SERVICE_ACCOUNT="example-cluster" -openssl genrsa -out ${VENAFI_SERVICE_ACCOUNT}.pem -openssl rsa -in ${VENAFI_SERVICE_ACCOUNT}.pem -pubout --out ${VENAFI_SERVICE_ACCOUNT}.pub -``` - -Next create a service account in the Venafi Control Plane: - -- Click **Settings > Service Accounts**. -- Click **New**. -- Type a name for your new service account. - Must match the ${VENAFI_SERVICE_ACCOUNT} variable that you used above. -- Select an **Owning Team**, which is the team who owns the machine you want to create the service account for. -- The scope should be "Kubernetes Discovery" only. -- Set the validity period of your pubic key up to a maximum of 365 days. -- Paste in the **public key** from the pair you generated. -- Click **Save** to finish and return to the Service Account list view. -- Find the row matching the name you entered and copy the "Client ID" value, - by clicking "Copy Client ID" in the row actions menu. - You will need this when you install the Helm chart. - -### 2) Deploy the chart - -Create a Namespace and then create a Secret containing the private key of the service account: - -```shell -export VENAFI_NAMESPACE="venafi" -kubectl create namespace ${VENAFI_NAMESPACE} -kubectl create secret generic agent-credentials \ - --namespace ${VENAFI_NAMESPACE} \ - --from-file=privatekey.pem=${VENAFI_SERVICE_ACCOUNT}.pem -``` - -Install the chart: - -```shell -export VENAFI_CLIENT_ID="" -helm upgrade venafi-kubernetes-agent oci://registry.venafi.cloud/charts/venafi-kubernetes-agent \ - --install \ - --namespace ${VENAFI_NAMESPACE} \ - --set config.clientId="${VENAFI_CLIENT_ID}" -``` - -> ℹī¸ To use the [EU Venafi Control Plane](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/), -> add: `--set config.server=https://api.venafi.eu/`. - -### 3) Connect the cluster in Venafi Control Plane - -- Click **Installations > Kubernetes Clusters**. -- Click **Connect**. -- On step 1, click **Continue**. -- On step 2, select **Advanced Connection**. -- On step 3, click **Continue** to skip. -- On step 4, fill in the details as follows: - - Name: use the name of the service account that you created earlier. E.g. "example-cluster". - - Service Account: select the service account that you created earlier. - - Check "The connection command has completed." box and click **continue**. -- On step 5, either wait for validation or click **Finish** to go back to the cluster list. - -### 4) Verify the deployment - -Check the agent logs: - -```shell -kubectl logs -n ${VENAFI_NAMESPACE} -l app.kubernetes.io/instance=venafi-kubernetes-agent --tail -1 | grep -A 5 "Running Agent" -``` - -You should see: - -```console -2023/10/24 12:10:03 Running Agent... -2023/10/24 12:10:03 Posting data to: https://api.venafi.cloud/ -2023/10/24 12:10:04 Data sent successfully. -``` - -Check the cluster status by visiting the Clusters page in the Venafi Control Plane: -- Click **Installations > Kubernetes Clusters** - -You should see: -- Status: Active -- Last Check In: ...seconds ago - -Check the Event Log page: -- Click **Settings > Event Log** - -You should see the following events for your service account: -- Service Account Access Token Granted -- Login Succeeded +> 📖 Read the [Venafi Kubernetes Agent documentation](https://docs.venafi.cloud/vaas/k8s-components/c-tlspk-agent-overview/), +> to learn how install and configure this Helm chart. {{ template "chart.requirementsSection" . }} diff --git a/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml b/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml new file mode 100644 index 00000000..c4f3e814 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml @@ -0,0 +1,1199 @@ +# DO NOT EDIT. Use 'make generate-crds-venconn' to regenerate. +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: venaficonnections.jetstack.io +spec: + group: jetstack.io + names: + kind: VenafiConnection + listKind: VenafiConnectionList + plural: venaficonnections + shortNames: + - vc + singular: venaficonnection + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VenafiConnection is the Schema for the VenafiConnection API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + allowReferencesFrom: + description: |- + A namespace selector that specifies what namespaces this VenafiConnection + is allowed to be used from. + If not set/ null, the VenafiConnection can only be used within its namespace. + An empty selector ({}) matches all namespaces. + If set to a non-empty selector, the VenafiConnection can only be used from + namespaces that match the selector. This possibly excludes the namespace + the VenafiConnection is in. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + tpp: + properties: + accessToken: + description: The list of steps to retrieve a TPP access token. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: 'DEPRECATED: This field does nothing and + will be removed in the future.' + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate + with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate + with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) + ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) + ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) + ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1' + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out by + venafi-connection-lib. + type: string + required: + - url + type: object + vaas: + description: 'Deprecated: The ''vaas'' field is deprecated use the + field called ''vcp'' instead.' + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: 'DEPRECATED: This field does nothing and + will be removed in the future.' + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate + with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate + with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) + ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) + ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) + ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1' + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: 'DEPRECATED: This field does nothing and + will be removed in the future.' + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate + with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate + with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) + ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) + ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) + ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1' + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + x-kubernetes-validations: + - message: 'must have exactly ONE of the following fields set: apiKey + or accessToken' + rule: '(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : + 0) == 1' + vcp: + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: 'DEPRECATED: This field does nothing and + will be removed in the future.' + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate + with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate + with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) + ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) + ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) + ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1' + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: 'DEPRECATED: This field does nothing and + will be removed in the future.' + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault + instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate + with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate + with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) + ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) + ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) + ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1' + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + x-kubernetes-validations: + - message: 'must have exactly ONE of the following fields set: apiKey + or accessToken' + rule: '(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : + 0) == 1' + type: object + x-kubernetes-validations: + - message: 'must have exactly ONE of the following fields set: tpp or + vcp' + rule: '(has(self.tpp) ? 1 : 0) + (has(self.vaas) ? 1 : 0) + (has(self.vcp) + ? 1 : 0) == 1' + status: + properties: + conditions: + description: List of status conditions to indicate the status of a + VenafiConnection. + items: + description: ConnectionCondition contains condition information + for a VenafiConnection. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + lastUpdateTime: + description: lastUpdateTime is the time of the last update to + this condition + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Issuer. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, + `Unknown`). + type: string + tokenValidUntil: + description: |- + The ValidUntil time of the token used to authenticate with the Venafi + Control Plane server. + format: date-time + type: string + type: + description: |- + Type of the condition, should be a combination of the unique name of the + operator and the type of condition. + eg. `VenafiEnhancedIssuerReady` + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt index 5e7b015d..f74caec3 100644 --- a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt +++ b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt @@ -1,8 +1,12 @@ -1. Check the credentials Secret exists: "{{ .Values.authentication.secretName }}" +{{- if .Values.authentication.venafiConnection.enabled }} +- Check the VenafiConnection exists: "{{ .Values.authentication.venafiConnection.namespace }}/{{ .Values.authentication.venafiConnection.name }}" +> kubectl get VenafiConnection -n {{ .Values.authentication.venafiConnection.namespace }} {{ .Values.authentication.venafiConnection.name }} +{{- else }} +- Check the credentials Secret exists: "{{ .Values.authentication.secretName }}" > kubectl get secret -n {{ .Release.Namespace }} {{ .Values.authentication.secretName }} - -2. Check the application is running: +{{- end }} +- Check the application is running: > kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} -3. Check the application logs for successful connection to the platform: +- Check the application logs for successful connection to the platform: > kubectl logs -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl b/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl new file mode 100644 index 00000000..07e7fe1c --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl @@ -0,0 +1,26 @@ +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "venafi-connection.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "venafi-connection.labels" -}} +helm.sh/chart: {{ include "venafi-connection.chart" . }} +{{ include "venafi-connection.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "venafi-connection.selectorLabels" -}} +app.kubernetes.io/name: "venafi-connection" +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml b/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml index 68a1b9bb..ca3ef98a 100644 --- a/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml +++ b/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml @@ -57,8 +57,15 @@ spec: - "agent" - "-c" - "/etc/venafi/agent/config/{{ default "config.yaml" .Values.config.configmap.key }}" + {{- if .Values.authentication.venafiConnection.enabled }} + - --venafi-connection + - {{ .Values.authentication.venafiConnection.name | quote }} + - --venafi-connection-namespace + - {{ .Values.authentication.venafiConnection.namespace | quote }} + {{- else }} - "--client-id" - - "{{ .Values.config.clientId }}" + - {{ .Values.config.clientId | quote }} + {{- end }} - "-p" - "0h1m0s" - --venafi-cloud @@ -74,9 +81,11 @@ spec: - name: config mountPath: "/etc/venafi/agent/config" readOnly: true + {{- if not .Values.authentication.venafiConnection.enabled }} - name: credentials mountPath: "/etc/venafi/agent/key" readOnly: true + {{- end }} {{- with .Values.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} @@ -102,10 +111,12 @@ spec: configMap: name: {{ default "agent-config" .Values.config.configmap.name }} optional: false + {{- if not .Values.authentication.venafiConnection.enabled }} - name: credentials secret: - secretName: {{ default "agent-credentials" .Values.authentication.secretName }} + secretName: {{ .Values.authentication.secretName }} optional: false + {{- end }} {{- with .Values.volumes }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml new file mode 100644 index 00000000..e969b224 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml @@ -0,0 +1,1123 @@ +{{- if .Values.crds.venafiConnection.include }} +{{- if (or (semverCompare "<1.25" .Capabilities.KubeVersion.GitVersion) .Values.crds.forceRemoveValidationAnnotations) }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + # This annotation prevents the CRD from being pruned by Helm when this chart is deleted. + "helm.sh/resource-policy": keep + name: venaficonnections.jetstack.io + labels: + {{- include "venafi-connection.labels" $ | nindent 4 }} +spec: + group: jetstack.io + names: + kind: VenafiConnection + listKind: VenafiConnectionList + plural: venaficonnections + shortNames: + - vc + singular: venaficonnection + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VenafiConnection is the Schema for the VenafiConnection API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + allowReferencesFrom: + description: |- + A namespace selector that specifies what namespaces this VenafiConnection + is allowed to be used from. + If not set/ null, the VenafiConnection can only be used within its namespace. + An empty selector ({}) matches all namespaces. + If set to a non-empty selector, the VenafiConnection can only be used from + namespaces that match the selector. This possibly excludes the namespace + the VenafiConnection is in. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + tpp: + properties: + accessToken: + description: The list of steps to retrieve a TPP access token. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out by + venafi-connection-lib. + type: string + required: + - url + type: object + vaas: + description: "Deprecated: The 'vaas' field is deprecated use the field called 'vcp' instead." + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + vcp: + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + type: object + status: + properties: + conditions: + description: List of status conditions to indicate the status of a VenafiConnection. + items: + description: ConnectionCondition contains condition information for a VenafiConnection. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + lastUpdateTime: + description: lastUpdateTime is the time of the last update to this condition + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Issuer. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + type: string + tokenValidUntil: + description: |- + The ValidUntil time of the token used to authenticate with the Venafi + Control Plane server. + format: date-time + type: string + type: + description: |- + Type of the condition, should be a combination of the unique name of the + operator and the type of condition. + eg. `VenafiEnhancedIssuerReady` + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} +{{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml new file mode 100644 index 00000000..ff03bf68 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml @@ -0,0 +1,1147 @@ +{{- if .Values.crds.venafiConnection.include }} +{{- if not (or (semverCompare "<1.25" .Capabilities.KubeVersion.GitVersion) .Values.crds.forceRemoveValidationAnnotations) }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + # This annotation prevents the CRD from being pruned by Helm when this chart is deleted. + "helm.sh/resource-policy": keep + name: venaficonnections.jetstack.io + labels: + {{- include "venafi-connection.labels" $ | nindent 4 }} +spec: + group: jetstack.io + names: + kind: VenafiConnection + listKind: VenafiConnectionList + plural: venaficonnections + shortNames: + - vc + singular: venaficonnection + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VenafiConnection is the Schema for the VenafiConnection API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + allowReferencesFrom: + description: |- + A namespace selector that specifies what namespaces this VenafiConnection + is allowed to be used from. + If not set/ null, the VenafiConnection can only be used within its namespace. + An empty selector ({}) matches all namespaces. + If set to a non-empty selector, the VenafiConnection can only be used from + namespaces that match the selector. This possibly excludes the namespace + the VenafiConnection is in. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + tpp: + properties: + accessToken: + description: The list of steps to retrieve a TPP access token. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1" + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out by + venafi-connection-lib. + type: string + required: + - url + type: object + vaas: + description: "Deprecated: The 'vaas' field is deprecated use the field called 'vcp' instead." + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1" + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1" + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + x-kubernetes-validations: + - message: "must have exactly ONE of the following fields set: apiKey or accessToken" + rule: "(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : 0) == 1" + vcp: + properties: + accessToken: + description: |- + The list of steps to retrieve the Access Token that will be used to connect + to VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1" + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + apiKey: + description: |- + The list of steps to retrieve the API key that will be used to connect to + VCP. + items: + properties: + hashicorpVaultLDAP: + description: |- + HashicorpVaultLDAP is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + ldapPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/ldap/static-cred/:role_name + or + /v1/ldap/creds/:role_name + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - ldapPath + type: object + hashicorpVaultOAuth: + description: |- + HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource + step to provide an OAuth token, which this step uses to authenticate to + Vault. The output of this step is a Vault token. This step allows you to use + the step `HashicorpVaultSecret` afterwards. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with HashiCorp Vault. The only supported value is "OIDC". + enum: + - OIDC + type: string + authPath: + description: |- + The login URL used for obtaining the Vault token. Example: + /v1/auth/oidc/login + type: string + clientId: + description: "DEPRECATED: This field does nothing and will be removed in the future." + type: string + role: + description: |- + The role defined in Vault that we want to use when authenticating to + Vault. + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - authInputType + - authPath + - role + type: object + hashicorpVaultSecret: + description: |- + HashicorpVaultSecret is a SecretSource step that requires a Vault token in + the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It + then fetches the requested secrets from Vault for use in the next step. + properties: + fields: + description: |- + The fields are Vault keys pointing to the secrets passed to the next + SecretSource step. + + + Example 1 (TPP, username and password): imagining that you have stored + the username and password for TPP under the keys "username" and + "password", you will want to set this field to `["username", + "password"]`. The username is expected to be given first, the password + second. + items: + type: string + type: array + secretPath: + description: |- + The full HTTP path to the secret in Vault. Example: + /v1/secret/data/application-team-a/tpp-username-password + type: string + url: + description: The URL to connect to your HashiCorp Vault instance. + type: string + required: + - fields + - secretPath + type: object + secret: + description: |- + Secret is a SecretSource step meant to be the first step. It retrieves secret + values from a Kubernetes Secret, and passes them to the next step. + properties: + fields: + description: |- + The names of the fields we want to extract from the Kubernetes secret. + These fields are passed to the next step in the chain. + items: + type: string + type: array + name: + description: The name of the Kubernetes secret. + type: string + required: + - fields + - name + type: object + serviceAccountToken: + description: |- + ServiceAccountToken is a SecretSource step meant to be the first step. It + uses the Kubernetes TokenRequest API to retrieve a token for a given service + account, and passes it to the next step. + properties: + audiences: + description: |- + Audiences are the intendend audiences of the token. A recipient of a + token must identify themself with an identifier in the list of + audiences of the token, and otherwise should reject the token. A + token issued for multiple audiences may be used to authenticate + against any of the audiences listed but implies a high degree of + trust between the target audiences. + items: + type: string + type: array + expirationSeconds: + description: |- + ExpirationSeconds is the requested duration of validity of the request. The + token issuer may return a token with a different validity duration so a + client needs to check the 'expiration' field in a response. + format: int64 + type: integer + name: + description: The name of the Kubernetes service account. + type: string + required: + - audiences + - name + type: object + tppOAuth: + description: |- + TPPOAuth is a SecretSource step that authenticates to a TPP server. This + step is meant to be the last step and requires a prior step that depends + on the `authInputType`. + properties: + authInputType: + description: |- + AuthInputType is the authentication method to be used to authenticate + with TPP. The supported values are "UsernamePassword" and "JWT". + enum: + - UsernamePassword + - JWT + type: string + clientId: + default: cert-manager.io + description: ClientID is the clientId used to authenticate with TPP. + type: string + url: + description: |- + The URL to connect to the Venafi TPP instance. The two URLs + https://tpp.example.com and https://tpp.example.com/vedsdk are + equivalent. The ending `/vedsdk` is optional and is stripped out + by our client. + If not set, defaults to the URL defined at the top-level of the + TPP configuration. + type: string + required: + - authInputType + type: object + vcpOAuth: + description: |- + VCPOAuth is a SecretSource step that authenticates to the Venafi Control + Plane. This step is meant to be the last step and requires a prior step + that outputs a JWT token. + properties: + tenantID: + description: TenantID is the tenant ID used to authenticate with VCP. + type: string + type: object + type: object + x-kubernetes-validations: + - message: must have exactly one field set + rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1" + maxItems: 50 + type: array + x-kubernetes-list-type: atomic + url: + description: |- + The URL to connect to the Venafi VCP instance. If not set, the default + value https://api.venafi.cloud is used. + type: string + type: object + x-kubernetes-validations: + - message: "must have exactly ONE of the following fields set: apiKey or accessToken" + rule: "(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : 0) == 1" + type: object + x-kubernetes-validations: + - message: "must have exactly ONE of the following fields set: tpp or vcp" + rule: "(has(self.tpp) ? 1 : 0) + (has(self.vaas) ? 1 : 0) + (has(self.vcp) ? 1 : 0) == 1" + status: + properties: + conditions: + description: List of status conditions to indicate the status of a VenafiConnection. + items: + description: ConnectionCondition contains condition information for a VenafiConnection. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the timestamp corresponding to the last status + change of this condition. + format: date-time + type: string + lastUpdateTime: + description: lastUpdateTime is the time of the last update to this condition + format: date-time + type: string + message: + description: |- + Message is a human readable description of the details of the last + transition, complementing reason. + type: string + observedGeneration: + description: |- + If set, this represents the .metadata.generation that the condition was + set based upon. + For instance, if .metadata.generation is currently 12, but the + .status.condition[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the Issuer. + format: int64 + type: integer + reason: + description: |- + Reason is a brief machine readable explanation for the condition's last + transition. + type: string + status: + description: Status of the condition, one of (`True`, `False`, `Unknown`). + type: string + tokenValidUntil: + description: |- + The ValidUntil time of the token used to authenticate with the Venafi + Control Plane server. + format: date-time + type: string + type: + description: |- + Type of the condition, should be a combination of the unique name of the + operator and the type of condition. + eg. `VenafiEnhancedIssuerReady` + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} +{{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml new file mode 100644 index 00000000..620ad162 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml @@ -0,0 +1,46 @@ +{{- if .Values.crds.venafiConnection.include }} +# The 'venafi-connection' service account is used by multiple +# controllers. When configuring which resources a VenafiConnection +# can access, the RBAC rules you create manually must point to this SA. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: venafi-connection + namespace: {{ $.Release.Namespace | quote }} + labels: + {{- include "venafi-connection.labels" $ | nindent 4 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: venafi-connection-role + labels: + {{- include "venafi-connection.labels" $ | nindent 4 }} +rules: +- apiGroups: [ "" ] + resources: [ "namespaces" ] + verbs: [ "get", "list", "watch" ] + +- apiGroups: [ "jetstack.io" ] + resources: [ "venaficonnections" ] + verbs: [ "get", "list", "watch" ] + +- apiGroups: [ "jetstack.io" ] + resources: [ "venaficonnections/status" ] + verbs: [ "get", "patch" ] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: venafi-connection-rolebinding + labels: + {{- include "venafi-connection.labels" $ | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: venafi-connection-role +subjects: +- kind: ServiceAccount + name: venafi-connection + namespace: {{ $.Release.Namespace | quote }} +{{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml new file mode 100644 index 00000000..21d19f13 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml @@ -0,0 +1,30 @@ +{{- if .Values.authentication.venafiConnection.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: venafi-kubernetes-agent-impersonate-role + namespace: {{ $.Release.Namespace | quote }} + labels: + {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }} +rules: +- apiGroups: [ "" ] + resources: [ "serviceaccounts" ] + verbs: [ "impersonate" ] + resourceNames: [ "venafi-connection" ] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: venafi-kubernetes-agent-impersonate-rolebinding + namespace: {{ $.Release.Namespace | quote }} + labels: + {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: venafi-kubernetes-agent-impersonate-role +subjects: +- kind: ServiceAccount + name: {{ include "venafi-kubernetes-agent.serviceAccountName" . }} + namespace: {{ $.Release.Namespace | quote }} +{{- end }} diff --git a/deploy/charts/venafi-kubernetes-agent/values.yaml b/deploy/charts/venafi-kubernetes-agent/values.yaml index 8502a92d..7cdc22aa 100644 --- a/deploy/charts/venafi-kubernetes-agent/values.yaml +++ b/deploy/charts/venafi-kubernetes-agent/values.yaml @@ -57,7 +57,7 @@ image: # -- Defaults to only pull if not already present pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion - tag: "v0.1.49" + # tag: "v0.0.0" # -- Specify image pull credentials if using a private registry # example: - name: my-pull-secret @@ -172,6 +172,23 @@ authentication: # -- Key name in the referenced secret secretKey: "privatekey.pem" + # +docs:section=Venafi Connection + # -- Configure VenafiConnection authentication + venafiConnection: + # -- When set to true, the Venafi Kubernetes Agent will authenticate to + # Venafi using the configuration in a VenafiConnection resource. + # Use `venafiConnection.enabled=true` for [secretless authentication](https://docs.venafi.cloud/vaas/k8s-components/t-install-tlspk-agent/). + # When set to true, the `authentication.secret` values will be ignored and the + # Secret with `authentication.secretName` will _not_ be mounted into the + # Venafi Kubernetes Agent Pod. + enabled: false + # -- The name of a VenafiConnection resource which contains the configuration + # for authenticating to Venafi. + name: venafi-components + # -- The namespace of a VenafiConnection resource which contains the + # configuration for authenticating to Venafi. + namespace: venafi + # -- Configuration section for the Venafi Kubernetes Agent itself config: # -- Overrides the server if using a proxy in your environment @@ -208,3 +225,22 @@ podDisruptionBudget: # an integer (e.g. 1) or a percentage value (e.g. 25%). # Cannot be used if `minAvailable` is set. # maxUnavailable: 1 + +# +docs:section=CRDs +# The CRDs installed by this chart are annotated with "helm.sh/resource-policy: keep", this +# prevents them from being accidentally removed by Helm when this chart is deleted. After +# deleting the installed chart, the user still has to manually remove the remaining CRDs. +crds: + # -- The 'x-kubernetes-validations' annotation is not supported in Kubernetes 1.22 and below. + # This annotation is used by CEL, which is a feature introduced in Kubernetes 1.25 that + # improves how validation is performed. + # This option allows to force the 'x-kubernetes-validations' annotation to be excluded, + # even on Kubernetes 1.25+ clusters. + forceRemoveValidationAnnotations: false + + # -- Optionally include the VenafiConnection CRDs + venafiConnection: + # -- When set to false, the rendered output does not contain the + # VenafiConnection CRDs and RBAC. This is useful for when the + # Venafi Connection resources are already installed separately. + include: false diff --git a/go.mod b/go.mod index fc84bf7d..1504559f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/jetstack/preflight -go 1.21 +go 1.22.0 require ( github.com/Jeffail/gabs/v2 v2.7.0 @@ -9,6 +9,7 @@ require ( github.com/fatih/color v1.16.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 + github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a github.com/json-iterator/go v1.1.12 github.com/juju/errors v1.0.0 github.com/kylelemons/godebug v1.1.0 @@ -16,38 +17,67 @@ require ( github.com/microcosm-cc/bluemonday v1.0.26 github.com/pkg/errors v0.9.1 github.com/pmylund/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.18.0 + github.com/prometheus/client_golang v1.19.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.9.0 gopkg.in/d4l3k/messagediff.v1 v1.2.1 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.30.3 + k8s.io/apimachinery v0.30.3 + k8s.io/client-go v0.30.3 + sigs.k8s.io/controller-runtime v0.18.5 sigs.k8s.io/yaml v1.4.0 ) require ( + github.com/Khan/genqlient v0.7.0 // indirect + github.com/Venafi/vcert/v5 v5.7.1 // indirect + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/cel-go v0.17.8 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect - golang.org/x/net v0.17.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sosodev/duration v1.2.0 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/vektah/gqlparser/v2 v2.5.15 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.8.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + k8s.io/apiextensions-apiserver v0.30.3 // indirect + k8s.io/apiserver v0.30.3 // indirect + k8s.io/component-base v0.30.3 // indirect ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/evanphx/json-patch v5.9.0+incompatible // indirect + github.com/go-logr/logr v1.4.2 + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/swag v0.22.9 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -61,20 +91,19 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sys v0.15.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 - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.31.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index 4a9d1e8b..776c9978 100644 --- a/go.sum +++ b/go.sum @@ -1,62 +1,100 @@ github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= +github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w= +github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM= +github.com/Venafi/vcert/v5 v5.7.1 h1:gUDbSuP6NE4yAslWp+D+ZoJlYOSRWhQora48oExuEN4= +github.com/Venafi/vcert/v5 v5.7.1/go.mod h1:UGI1A6IdZ7Sc4E3DQU70Qzaanot6fiY0ObIupcU2O94= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= 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.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= +github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -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/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -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/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= +github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.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/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -66,6 +104,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a h1:hZZEgTTtiHZlp++rDqutTBtgsZ2fz4M+UkV65Ru2vrw= +github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a/go.mod h1:gn2domftAFcrEfrH+OhuTh85L3q0p+rw+j8hFS31Qd4= 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= @@ -74,11 +114,8 @@ github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= 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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -90,8 +127,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI= github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= @@ -103,31 +138,40 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G 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.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= 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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuDHuJ/C57Vw/XjTE= github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us= +github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= 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= @@ -135,72 +179,104 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vektah/gqlparser/v2 v2.5.15 h1:fYdnU8roQniJziV5TDiFPm/Ff7pE8xbVSOJqbsdl88A= +github.com/vektah/gqlparser/v2 v2.5.15/go.mod h1:WQQjFc+I1YIzoPvZBhUQX7waZgg3pMLi0r8KymvAE2w= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= 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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= +go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= +go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= +go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= +go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= +go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +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.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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-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.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.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/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -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= +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/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -208,27 +284,39 @@ gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3 gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.8/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= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= -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-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ= +k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04= +k8s.io/apiextensions-apiserver v0.30.3 h1:oChu5li2vsZHx2IvnGP3ah8Nj3KyqG3kRSaKmijhB9U= +k8s.io/apiextensions-apiserver v0.30.3/go.mod h1:uhXxYDkMAvl6CJw4lrDN4CPbONkF3+XL9cacCT44kV4= +k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc= +k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/apiserver v0.30.3 h1:QZJndA9k2MjFqpnyYv/PH+9PE0SHhx3hBho4X0vE65g= +k8s.io/apiserver v0.30.3/go.mod h1:6Oa88y1CZqnzetd2JdepO0UXzQX4ZnOekx2/PtEjrOg= +k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k= +k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U= +k8s.io/component-base v0.30.3 h1:Ci0UqKWf4oiwy8hr1+E3dsnliKnkMLZMVbWzeorlk7s= +k8s.io/component-base v0.30.3/go.mod h1:C1SshT3rGPCuNtBs14RmVD2xW0EhRSeLvBh7AGk1quA= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM= +k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4= +sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk= +sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= 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.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/e2e/test.sh b/hack/e2e/test.sh new file mode 100755 index 00000000..b56e4990 --- /dev/null +++ b/hack/e2e/test.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# +# Build and install venafi-kubernetes-agent for VenafiConnection based authentication. +# Wait for it to log a message indicating successful data upload. +# +# venafi-kubernetes-agent is packaged using ko and Helm and installed in a Kind cluster. +# A VenafiConnection resource is created which directly loads a bearer token +# from a Kubernetes Secret. +# This is the simplest way of testing the VenafiConnection integration, +# but it does not fully test "secretless" (workload identity federation) authentication. +# +# Prerequisites: +# * ko: https://github.com/ko-build/ko/releases/tag/v0.16.0 +# * helm: https://helm.sh/docs/intro/install/ +# * kind: https://kubernetes.io/docs/tasks/tools/#kind +# * kubectl: https://kubernetes.io/docs/tasks/tools/#kubectl +# * venctl: https://docs.venafi.cloud/vaas/venctl/t-venctl-install/ +# * jq: https://jqlang.github.io/jq/download/ +# * step: https://smallstep.com/docs/step-cli/installation/ +# * curl: https://www.man7.org/linux/man-pages/man1/curl.1.html +# * envsubst: https://www.man7.org/linux/man-pages/man1/envsubst.1.html + +set -o nounset +set -o errexit +set -o pipefail + +# Your Venafi Cloud API key. +: ${VEN_API_KEY?} + +# The Venafi Cloud team which will be the owner of the generated Venafi service +# accounts. +: ${VEN_OWNING_TEAM?} + +# The base URL of the OCI registry used for Docker images and Helm charts +# E.g. ttl.sh/63773370-0bcf-4ac0-bd42-5515616089ff +: ${OCI_BASE?} + +export VERSION=$(git describe --tags --always --match='v*' --abbrev=14 --dirty) +export KO_DOCKER_REPO=$OCI_BASE/images/venafi-agent +export TERM=dumb + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +root_dir=$(cd "${script_dir}/../.." && pwd) + +cd "${script_dir}" + +pushd "${root_dir}" +ko build --bare --tags "${VERSION}" +helm package deploy/charts/venafi-kubernetes-agent --version "${VERSION}" --app-version "${VERSION}" +helm push venafi-kubernetes-agent-${VERSION}.tgz "oci://${OCI_BASE}/charts" +popd + +kind create cluster || true + +kubectl create ns venafi || true + +# Pull secret for Venafi OCI registry +if ! kubectl get secret venafi-image-pull-secret -n venafi; then + venctl iam service-accounts registry create \ + --no-prompts \ + --owning-team "${VEN_OWNING_TEAM}" \ + --name "venafi-kubernetes-agent-e2e-registry-${RANDOM}" \ + --scopes enterprise-cert-manager,enterprise-venafi-issuer,enterprise-approver-policy \ + | jq '{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": { + "name": "venafi-image-pull-secret" + }, + "type": "kubernetes.io/dockerconfigjson", + "stringData": { + ".dockerconfigjson": { + "auths": { + "\(.oci_registry)": { + "username": .username, + "password": .password + } + } + } | tostring + } + }' \ + | kubectl create -n venafi -f - +fi + +# Cache the Service account credentials for venafi-kubernetes-agent in the cluster +# but this Secret will not be mounted by the agent. +kubectl create ns venafi-kubernetes-agent-e2e || true +if ! kubectl get secret cached-venafi-agent-service-account -n venafi-kubernetes-agent-e2e; then + venctl iam service-account agent create \ + --no-prompts \ + --owning-team "${VEN_OWNING_TEAM}" \ + --name "venafi-kubernetes-agent-e2e-agent-${RANDOM}" \ + | jq '{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": { + "name": "cached-venafi-agent-service-account" + }, + "stringData": { + "privatekey.pem": .private_key, + "client-id": .client_id + } + }' \ + | kubectl create -n venafi-kubernetes-agent-e2e -f - +fi + +export VENAFI_KUBERNETES_AGENT_CLIENT_ID="not-used-but-required-by-venctl" +venctl components kubernetes apply \ + --venafi-kubernetes-agent \ + --venafi-kubernetes-agent-version "$VERSION" \ + --venafi-kubernetes-agent-values-files "${script_dir}/values.venafi-kubernetes-agent.yaml" \ + --venafi-kubernetes-agent-custom-image-registry "${OCI_BASE}/images" \ + --venafi-kubernetes-agent-custom-chart-repository "oci://${OCI_BASE}/charts" + +privatekey=$(kubectl get secret cached-venafi-agent-service-account \ + --namespace venafi-kubernetes-agent-e2e \ + --template="{{index .data \"privatekey.pem\" | base64decode}}") +clientid=$(kubectl get secret cached-venafi-agent-service-account \ + --namespace venafi-kubernetes-agent-e2e \ + --template="{{index .data \"client-id\" | base64decode}}") +jwt=$(step crypto jwt sign \ + --key <(sed 's/ PRIVATE KEY/ EC PRIVATE KEY/g' <<<"$privatekey") \ + --aud api.venafi.cloud/v1/oauth/token/serviceaccount \ + --exp "$([ "$(uname)" = "Darwin" ] && date -v +30M +'%s' || date -d '+30 minutes' +'%s')" \ + --sub "$clientid" \ + --iss "$clientid" \ + | tee >(step crypto jwt inspect --insecure >/dev/stderr)) +accesstoken=$(curl https://api.venafi.cloud/v1/oauth/token/serviceaccount \ + -sS --fail-with-body \ + --data-urlencode assertion="$jwt" \ + --data-urlencode grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer | tee /dev/stderr | jq '.access_token' -r) +export accesstoken +envsubst < venafi-components.yaml | kubectl apply -n venafi -f - + +# Wait for log message indicating success. +# Filter out distracting data gatherer errors and warnings. +# Show other useful log messages on stderr. +kubectl logs deployments/venafi-kubernetes-agent \ + --follow \ + --namespace venafi \ + | tee >(grep -v -e "reflector\.go" -e "datagatherer" -e "data gatherer" > /dev/stderr) \ + | grep -q "Data sent successfully" diff --git a/hack/e2e/values.venafi-kubernetes-agent.yaml b/hack/e2e/values.venafi-kubernetes-agent.yaml new file mode 100644 index 00000000..a2545eca --- /dev/null +++ b/hack/e2e/values.venafi-kubernetes-agent.yaml @@ -0,0 +1,12 @@ +config: + clusterName: venafi-kubernetes-agent-e2e + clusterDescription: | + A kind cluster used for testing the venafi-kubernetes-agent. + +authentication: + venafiConnection: + enabled: true + +crds: + venafiConnection: + include: true diff --git a/hack/e2e/venafi-components.yaml b/hack/e2e/venafi-components.yaml new file mode 100644 index 00000000..5e48a08b --- /dev/null +++ b/hack/e2e/venafi-components.yaml @@ -0,0 +1,40 @@ +apiVersion: jetstack.io/v1alpha1 +kind: VenafiConnection +metadata: + name: venafi-components +spec: + vcp: + accessToken: + - secret: + name: venafi-credentials + fields: ["access-token"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: get-venafi-credentials +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get" ] + resourceNames: [ "venafi-credentials" ] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: application-team-1-secret-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: get-venafi-credentials +subjects: +- kind: ServiceAccount + name: venafi-connection + namespace: venafi +--- +apiVersion: v1 +kind: Secret +metadata: + name: venafi-credentials +stringData: + access-token: ${accesstoken} diff --git a/make/util/checkhash.sh b/make/util/checkhash.sh new file mode 100755 index 00000000..3f1907b8 --- /dev/null +++ b/make/util/checkhash.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +# This script takes the hash of its first argument and verifies it against the +# hex hash given in its second argument + +SHASUM=$(./make/util/hash.sh "$1") + +# When running 'make learn-sha-tools', we don't want this script to fail. +# Instead we log what sha values are wrong, so the make.mk file can be updated. +if [ "$SHASUM" != "$2" ] && [ "${LEARN_FILE:-}" != "" ]; then + echo "s/$2/$SHASUM/g" >> "${LEARN_FILE:-}" + exit 0 +fi + +if [ "$SHASUM" != "$2" ]; then + echo "invalid checksum for \"$1\": wanted \"$2\" but got \"$SHASUM\"" + exit 1 +fi diff --git a/make/util/hash.sh b/make/util/hash.sh new file mode 100755 index 00000000..50364102 --- /dev/null +++ b/make/util/hash.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +# This script is a wrapper for outputting purely the sha256 hash of the input file, +# ideally in a portable way. + +sha256sum "$1" | cut -d" " -f1 diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 799cb232..bf32868a 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "net/http" _ "net/http/pprof" "net/url" @@ -22,6 +23,9 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/jetstack/preflight/api" "github.com/jetstack/preflight/pkg/client" @@ -65,6 +69,24 @@ var StrictMode bool // APIToken is an authentication token used for the backend API as an alternative to oauth flows. var APIToken string +// VenConnName is the name of the VenafiConnection resource to use. Using this +// flag will enable Venafi Connection mode. +var VenConnName string + +// VenConnNS is the namespace of the VenafiConnection resource to use. It is +// only useful when the VenafiConnection isn't in the same namespace as the +// agent. +// +// May be left empty to use the same namespace as the agent. +var VenConnNS string + +// InstallNS is the namespace in which the agent is running in. Only needed when +// running the agent outside of Kubernetes. +// +// May be left empty when running in Kubernetes. In this case, the namespace is +// read from the file /var/run/secrets/kubernetes.io/serviceaccount/namespace. +var InstallNS string + // Profiling flag enabled pprof endpoints to run on the agent var Profiling bool @@ -79,6 +101,10 @@ var Prometheus bool // raw resource data of unstructuredList const schemaVersion string = "v2.0.0" +const ( + inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" +) + // Run starts the agent process func Run(cmd *cobra.Command, args []string) { ctx, cancel := context.WithCancel(context.Background()) @@ -107,6 +133,19 @@ func Run(cmd *cobra.Command, args []string) { }() } + _, isVenConn := preflightClient.(*client.VenConnClient) + if isVenConn { + go func() { + err := preflightClient.(manager.Runnable).Start(ctx) + if err != nil { + logs.Log.Fatalf("failed to start a controller-runtime component: %v", err) + } + + // The agent must stop if the controller-runtime component stops. + cancel() + }() + } + dataGatherers := map[string]datagatherer.DataGatherer{} var wg sync.WaitGroup @@ -201,7 +240,7 @@ func getConfiguration() (Config, client.Client) { } // If the ClientID of the service account is specified, then assume we are in Venafi Cloud mode. - if ClientID != "" { + if ClientID != "" || VenConnName != "" { VenafiCloudMode = true } @@ -258,6 +297,18 @@ func getConfiguration() (Config, client.Client) { } } + venConnMode := VenConnName != "" + + if venConnMode && InstallNS == "" { + InstallNS, err = getInClusterNamespace() + if err != nil { + log.Fatalf("could not guess which namespace the agent is running in: %s", err) + } + } + if venConnMode && VenConnNS == "" { + VenConnNS = InstallNS + } + agentMetadata := &api.AgentMetadata{ Version: version.PreflightVersion, ClusterID: config.ClusterID, @@ -267,6 +318,37 @@ func getConfiguration() (Config, client.Client) { switch { case credentials != nil: preflightClient, err = createCredentialClient(credentials, config, agentMetadata, baseURL) + case VenConnName != "": + // Why wasn't this added to the createCredentialClient instead? Because + // the --venafi-connection mode of authentication doesn't need any + // secrets (or any other information for that matter) to be loaded from + // disk (using --credentials-path). Everything is passed as flags. + log.Println("Venafi Connection mode was specified, using Venafi Connection authentication.") + + // The venafi-cloud.upload_path was initially meant to let users + // configure HTTP proxies, but it has never been used since HTTP proxies + // don't rewrite paths. Thus, we've disabled the ability to change this + // value with the new --venafi-connection flag, and this field is simply + // ignored. + if config.VenafiCloud != nil && config.VenafiCloud.UploadPath != "" { + log.Printf(`ignoring venafi-cloud.upload_path. In Venafi Connection mode, this field is not needed.`) + } + + // Regarding venafi-cloud.uploader_id, we found that it doesn't do + // anything in the backend. Since the backend requires it for historical + // reasons (but cannot be empty), we just ignore whatever the user has + // set in the config file, and set it to an arbitrary value in the + // client since it doesn't matter. + if config.VenafiCloud.UploaderID != "" { + log.Printf(`ignoring venafi-cloud.uploader_id. In Venafi Connection mode, this field is not needed.`) + } + + cfg, err := loadRESTConfig("") + if err != nil { + log.Fatalf("failed to load kubeconfig: %v", err) + } + + preflightClient, err = client.NewVenConnClient(cfg, agentMetadata, InstallNS, VenConnName, VenConnNS, nil) case APIToken != "": logs.Log.Println("An API token was specified, using API token authentication.") preflightClient, err = client.NewAPITokenClient(agentMetadata, APIToken, baseURL) @@ -468,3 +550,47 @@ func postData(config Config, preflightClient client.Client, readings []*api.Data return nil } + +// Inspired by the controller-runtime project. +func getInClusterNamespace() (string, error) { + // Check whether the namespace file exists. + // If not, we are not running in cluster so can't guess the namespace. + _, err := os.Stat(inClusterNamespacePath) + if os.IsNotExist(err) { + return "", fmt.Errorf("not running in cluster, please use --install-namespace to specify the namespace in which the agent is running") + } + if err != nil { + return "", fmt.Errorf("error checking namespace file: %w", err) + } + + namespace, err := os.ReadFile(inClusterNamespacePath) + if err != nil { + return "", fmt.Errorf("error reading namespace file: %w", err) + } + return string(namespace), nil +} + +func loadRESTConfig(path string) (*rest.Config, error) { + switch path { + // If the kubeconfig path is not provided, use the default loading rules + // so we read the regular KUBECONFIG variable or create a non-interactive + // client for agents running in cluster + case "": + loadingrules := clientcmd.NewDefaultClientConfigLoadingRules() + cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loadingrules, &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return nil, fmt.Errorf("failed to load kubeconfig: %w", err) + } + return cfg, nil + // Otherwise use the explicitly named kubeconfig file. + default: + cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: path}, + &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return nil, fmt.Errorf("failed to load kubeconfig from %s: %w", path, err) + } + return cfg, nil + } +} diff --git a/pkg/client/client.go b/pkg/client/client.go index 20b84818..6ddcb34d 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -13,9 +13,17 @@ type ( // Options is the struct describing additional information pertinent to an agent that isn't a data reading // These fields will then be uploaded together with data readings. Options struct { - OrgID string - ClusterID string - ClusterName string + // Only used with Jetstack Secure. + OrgID string + + // Only used with Jetstack Secure. + ClusterID string + + // Only used with Venafi Cloud. The convention is to use the agent + // config's `cluster_id` as ClusterName. + ClusterName string + + // Only used with Venafi Cloud. ClusterDescription string } diff --git a/pkg/client/client_venconn.go b/pkg/client/client_venconn.go new file mode 100644 index 00000000..eb403cd7 --- /dev/null +++ b/pkg/client/client_venconn.go @@ -0,0 +1,215 @@ +package client + +import ( + "bytes" + "context" + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/go-logr/logr" + "github.com/jetstack/preflight/api" + "github.com/jetstack/preflight/pkg/version" + venapi "github.com/jetstack/venafi-connection-lib/api/v1alpha1" + "github.com/jetstack/venafi-connection-lib/venafi_client" + "github.com/jetstack/venafi-connection-lib/venafi_client/auth" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + ctrlruntimelog "sigs.k8s.io/controller-runtime/pkg/log" +) + +type VenConnClient struct { + agentMetadata *api.AgentMetadata + connHandler venafi_client.ConnectionHandler + installNS string // Namespace in which the agent is running in. + venConnName string // Name of the VenafiConnection resource to use. + venConnNS string // Namespace of the VenafiConnection resource to use. + client *http.Client // Used to make HTTP requests to Venafi Cloud. +} + +// NewVenConnClient lets you make requests to the Venafi Cloud backend using the +// given VenafiConnection resource. +// +// You need to call Start to start watching the VenafiConnection resource. If +// you don't, the client will be unable to find the VenafiConnection that you +// are referring to as its client-go cache will remain empty. +// +// The http.Client is used for Venafi and Vault, not for Kubernetes. The +// `installNS` is the namespace in which the agent is running in. The passed +// `restcfg` is not mutated. `trustedCAs` is only used for connecting to Venafi +// Cloud and Vault and can be left nil. +func NewVenConnClient(restcfg *rest.Config, agentMetadata *api.AgentMetadata, installNS, venConnName, venConnNS string, trustedCAs *x509.CertPool) (*VenConnClient, error) { + // TODO(mael): The rest of the codebase uses the standard "log" package, + // venafi-connection-lib uses "go-logr/logr", and client-go uses "klog". We + // should standardize on one of them, probably "slog". + ctrlruntimelog.SetLogger(logr.Logger{}) + + if installNS == "" { + return nil, errors.New("programmer mistake: installNS must be provided") + } + if venConnName == "" { + return nil, errors.New("programmer mistake: venConnName must be provided") + } + if venConnNS == "" { + return nil, errors.New("programmer mistake: venConnNS must be provided") + } + + restcfg = rest.CopyConfig(restcfg) + restcfg.Impersonate = rest.ImpersonationConfig{ + UserName: fmt.Sprintf("system:serviceaccount:%s:venafi-connection", installNS), + } + + // TLS-related configuration such as root CAs and client certs are contained + // in the restcfg; let's create an http.Client that uses them. + httpCl, err := rest.HTTPClientFor(restcfg) + if err != nil { + return nil, fmt.Errorf("while turning the REST config into an HTTP client: %w", err) + } + + restMapper, err := apiutil.NewDynamicRESTMapper(restcfg, httpCl) + if err != nil { + return nil, fmt.Errorf("while creating the REST mapper: %w", err) + } + + // This Kubernetes client only needs to be able to read and write the + // VenafiConnection resources and read Secret resources. + scheme := runtime.NewScheme() + _ = venapi.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + + handler, err := venafi_client.NewConnectionHandler( + "venafi-kubernetes-agent/"+version.PreflightVersion, + "venafi-kubernetes-agent.jetstack.io", + "VenafiKubernetesAgent", + restcfg, + scheme, + restMapper, + trustedCAs, + ) + if err != nil { + return nil, err + } + + vcpClient := &http.Client{} + if trustedCAs != nil { + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig.RootCAs = trustedCAs + vcpClient.Transport = tr + } + + return &VenConnClient{ + agentMetadata: agentMetadata, + connHandler: handler, + installNS: installNS, + venConnName: venConnName, + venConnNS: venConnNS, + client: vcpClient, + }, nil +} + +// Start starts watching VenafiConnections. This function will return soon after +// the context is closed, or if an error occurs. +func (c *VenConnClient) Start(ctx context.Context) error { + return c.connHandler.CacheRunnable().Start(ctx) +} + +// `opts.ClusterName` and `opts.ClusterDescription` are the only values used +// from the Options struct. OrgID and ClusterID are not used in Venafi Cloud. +func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading, opts Options) error { + if opts.ClusterName == "" { + return fmt.Errorf("programmer mistake: the cluster name (aka `cluster_id` in the config file) cannot be left empty") + } + + _, token, err := c.connHandler.Get(context.Background(), c.installNS, auth.Scope{}, types.NamespacedName{Name: c.venConnName, Namespace: c.venConnNS}) + if err != nil { + return fmt.Errorf("while loading the VenafiConnection %s/%s: %w", c.venConnNS, c.venConnName, err) + } + if token.TPPAccessToken != "" { + return fmt.Errorf(`VenafiConnection %s/%s: the agent cannot be used with TPP`, c.venConnNS, c.venConnName) + } + if token.VCPAPIKey != "" { + // Although it is technically possible to use an API key, we have + // decided to not allow it as it isn't recommended and will eventually + // be phased out. + return fmt.Errorf(`VenafiConnection %s/%s: the agent cannot be used with an API key`, c.venConnNS, c.venConnName) + } + if token.VCPAccessToken == "" { + return fmt.Errorf(`programmer mistake: VenafiConnection %s/%s: TPPAccessToken is empty in the token returned by connHandler.Get: %v`, c.venConnNS, c.venConnName, token) + } + + payload := api.DataReadingsPost{ + AgentMetadata: c.agentMetadata, + DataGatherTime: time.Now().UTC(), + DataReadings: readings, + } + data, err := json.Marshal(payload) + if err != nil { + return err + } + + // The path parameter "no" is a dummy parameter to make the Venafi Cloud + // backend happy. This parameter, named `uploaderID` in the backend, is not + // actually used by the backend. + req, err := http.NewRequest(http.MethodPost, fullURL(token.BaseURL, "/v1/tlspk/upload/clusterdata/no"), bytes.NewBuffer(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", fmt.Sprintf("venafi-kubernetes-agent/%s", version.PreflightVersion)) + + if token.VCPAccessToken != "" { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.VCPAccessToken)) + } + if token.VCPAPIKey != "" { + req.Header.Set("tppl-api-key", token.VCPAPIKey) + } + + q := req.URL.Query() + q.Set("name", opts.ClusterName) + if opts.ClusterDescription != "" { + q.Set("description", base64.RawURLEncoding.EncodeToString([]byte(opts.ClusterDescription))) + } + req.URL.RawQuery = q.Encode() + + res, err := c.client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + if code := res.StatusCode; code < 200 || code >= 300 { + errorContent := "" + body, err := io.ReadAll(res.Body) + if err == nil { + errorContent = string(body) + } + + return fmt.Errorf("received response with status code %d. Body: [%s]", code, errorContent) + } + + return nil +} + +// PostDataReadings isn't implemented for Venafi Cloud. This is because Venafi +// Cloud needs a `clusterName` and `clusterDescription`, but this function can +// only pass `orgID` and `clusterID` which are both useless in Venafi Cloud. Use +// PostDataReadingsWithOptions instead. +func (c *VenConnClient) PostDataReadings(_orgID, _clusterID string, readings []*api.DataReading) error { + return fmt.Errorf("programmer mistake: PostDataReadings is not implemented for Venafi Cloud") +} + +// Post isn't implemented for Venafi Cloud because /v1/tlspk/upload/clusterdata +// requires using the query parameters `name` and `description` which can't be +// set using Post. Use PostDataReadingsWithOptions instead. +func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) { + return nil, fmt.Errorf("programmer mistake: Post is not implemented for Venafi Cloud") +} diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go new file mode 100644 index 00000000..0396b5e8 --- /dev/null +++ b/pkg/client/client_venconn_test.go @@ -0,0 +1,379 @@ +package client_test + +import ( + "context" + "crypto/x509" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/jetstack/preflight/api" + "github.com/jetstack/preflight/pkg/client" + + "github.com/jetstack/venafi-connection-lib/api/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/rest" + ctrlruntime "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +// These are using envtest (slow) rather than a fake clientset (fast) because +// controller-runtime's fake clientset doesn't support server-side apply [1] and +// also because we want to create serviceaccount tokens, which isn't supported +// by the fake clientset either. +// +// The goal is to test the following behaviors: +// +// - VenafiConnection's `accessToken` works as expected with a fake Venafi +// Cloud server. +// - VenafiConnection's `apiKey` and `tpp` can't be used by the user. +// - NewVenConnClient's `trustedCAs` works as expected. +// +// [1] https://github.com/kubernetes-sigs/controller-runtime/issues/2341 +func TestVenConnClient_PostDataReadingsWithOptions(t *testing.T) { + t.Parallel() + + t.Run("valid accessToken", run(testcase{ + given: undent(` + apiVersion: jetstack.io/v1alpha1 + kind: VenafiConnection + metadata: + name: venafi-components + namespace: venafi + spec: + vcp: + url: FAKE_VENAFI_CLOUD_URL + accessToken: + - secret: + name: accesstoken + fields: [accesstoken]`), + expectReadyCondMsg: "ea744d098c2c1c6044e4c4e9d3bf7c2a68ef30553db00f1714886cedf73230f1", + })) + t.Run("error when the apiKey field is used", run(testcase{ + // Why isn't it possible to use the 'apiKey' field? Although the + // Kubernetes Discovery endpoint works with an API key, we have decided + // to not support it because it isn't recommended. + given: undent(` + apiVersion: jetstack.io/v1alpha1 + kind: VenafiConnection + metadata: + name: venafi-components + namespace: venafi + spec: + vcp: + url: FAKE_VENAFI_CLOUD_URL + apiKey: + - secret: + name: apikey + fields: [apikey]`), + expectReadyCondMsg: "b099d634ccec56556da28028743475dab67f79d079b668bedc3ef544f7eed2f3", + expectErr: "VenafiConnection venafi/venafi-components: the agent cannot be used with an API key", + })) + t.Run("error when the tpp field is used", run(testcase{ + // IMPORTANT: The user may think they can use 'tpp', spend time + // debugging and making the venafi connection work, and then find out + // that it doesn't work. The reason is because as of now, we don't first + // check if the user has used the 'tpp' field before running Get. + given: undent(` + apiVersion: jetstack.io/v1alpha1 + kind: VenafiConnection + metadata: + name: venafi-components + namespace: venafi + spec: + tpp: + url: FAKE_TPP_URL + accessToken: + - secret: + name: accesstoken + fields: [accesstoken]`), + expectErr: ``, + expectReadyCondMsg: `ea744d098c2c1c6044e4c4e9d3bf7c2a68ef30553db00f1714886cedf73230f1`, + })) +} + +type testcase struct { + given string + expectErr string + expectReadyCondMsg string +} + +func run(test testcase) func(t *testing.T) { + return func(t *testing.T) { + fakeVenafiCloud, certCloud := fakeVenafiCloud(t) + fakeTPP, certTPP := fakeTPP(t) + _, restconf, kclient := startEnvtest(t) + + certPool := x509.NewCertPool() + certPool.AddCert(certCloud) + certPool.AddCert(certTPP) + + cl, err := client.NewVenConnClient( + restconf, + &api.AgentMetadata{ClusterID: "no"}, + "venafi", // Namespace in which the Agent is running. + "venafi-components", // Name of the VenafiConnection. + "venafi", // Namespace of the VenafiConnection. + certPool, + ) + require.NoError(t, err) + + // This `cancel` is important because the below func `Start(ctx)` needs + // to be stopped before the apiserver is stopped. Otherwise, the test + // fail with the message "timeout waiting for process kube-apiserver to + // stop". See: + // https://github.com/jetstack/venafi-connection-lib/pull/158#issuecomment-1949002322 + // https://github.com/kubernetes-sigs/controller-runtime/issues/1571#issuecomment-945535598 + ctx, cancel := context.WithCancel(context.Background()) + go func() { + err = cl.Start(ctx) + require.NoError(t, err) + }() + t.Cleanup(cancel) + + // Apply the same RBAC as what you would get from the Venafi + // Connection Helm chart, for example after running this: + // helm template venafi-connection oci://registry.venafi.cloud/charts/venafi-connection --version v0.1.0 -n venafi --show-only templates/venafi-connection-rbac.yaml + require.NoError(t, kclient.Create(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi"}, + })) + require.NoError(t, kclient.Create(context.Background(), &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection", Namespace: "venafi"}, + })) + require.NoError(t, kclient.Create(context.Background(), &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-role"}, + Rules: []rbacv1.PolicyRule{ + {APIGroups: []string{""}, Resources: []string{"namespaces"}, Verbs: []string{"get", "list", "watch"}}, + {APIGroups: []string{"jetstack.io"}, Resources: []string{"venaficonnections"}, Verbs: []string{"get", "list", "watch"}}, + {APIGroups: []string{"jetstack.io"}, Resources: []string{"venaficonnections/status"}, Verbs: []string{"get", "patch"}}, + }, + })) + require.NoError(t, kclient.Create(context.Background(), &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-rolebinding"}, + RoleRef: rbacv1.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: "venafi-connection-role"}, + Subjects: []rbacv1.Subject{{Kind: "ServiceAccount", Name: "venafi-connection", Namespace: "venafi"}}, + })) + require.NoError(t, kclient.Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "accesstoken", Namespace: "venafi"}, + StringData: map[string]string{"accesstoken": "VALID_ACCESS_TOKEN"}, + })) + require.NoError(t, kclient.Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "apikey", Namespace: "venafi"}, + StringData: map[string]string{"apikey": "VALID_API_KEY"}, + })) + require.NoError(t, kclient.Create(context.Background(), &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-secret-reader", Namespace: "venafi"}, + Rules: []rbacv1.PolicyRule{ + {APIGroups: []string{""}, Resources: []string{"secrets"}, Verbs: []string{"get"}, ResourceNames: []string{"accesstoken", "apikey"}}, + }, + })) + require.NoError(t, kclient.Create(context.Background(), &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-secret-reader", Namespace: "venafi"}, + RoleRef: rbacv1.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "Role", Name: "venafi-connection-secret-reader"}, + Subjects: []rbacv1.Subject{{Kind: "ServiceAccount", Name: "venafi-connection", Namespace: "venafi"}}, + })) + + test.given = strings.ReplaceAll(test.given, "FAKE_VENAFI_CLOUD_URL", fakeVenafiCloud.URL) + test.given = strings.ReplaceAll(test.given, "FAKE_TPP_URL", fakeTPP.URL) + for _, obj := range parse(test.given) { + require.NoError(t, kclient.Create(context.Background(), obj)) + } + + err = cl.PostDataReadingsWithOptions([]*api.DataReading{}, client.Options{ClusterName: "test cluster name"}) + if test.expectErr != "" { + assert.EqualError(t, err, test.expectErr) + } + + got := v1alpha1.VenafiConnection{} + err = kclient.Get(context.Background(), types.NamespacedName{Name: "venafi-components", Namespace: "venafi"}, &got) + require.NoError(t, err) + require.Len(t, got.Status.Conditions, 1) + assert.Equal(t, test.expectReadyCondMsg, got.Status.Conditions[0].Message) + } +} + +func fakeVenafiCloud(t *testing.T) (*httptest.Server, *x509.Certificate) { + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("fake api.venafi.cloud received request: %s %s", r.Method, r.URL.Path) + accessToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") + apiKey := r.Header.Get("tppl-api-key") + if accessToken != "VALID_ACCESS_TOKEN" && apiKey != "VALID_API_KEY" { + w.WriteHeader(http.StatusUnauthorized) + return + } + if r.URL.Path == "/v1/tlspk/upload/clusterdata/no" { + if r.URL.Query().Get("name") != "test cluster name" { + w.WriteHeader(http.StatusBadRequest) + return + } + _, _ = w.Write([]byte(`{"status":"ok","organization":"756db001-280e-11ee-84fb-991f3177e2d0"}`)) + } else if r.URL.Path == "/v1/useraccounts" { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"user": {"username": "user","id": "76a126f0-280e-11ee-84fb-991f3177e2d0"}}`)) + + } else if r.URL.Path == "/v1/oauth2/v2.0/756db001-280e-11ee-84fb-991f3177e2d0/token" { + _, _ = w.Write([]byte(`{"access_token":"VALID_ACCESS_TOKEN","expires_in":900,"token_type":"bearer"}`)) + } else { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(`{"error":"unexpected path in the test server","path":"` + r.URL.Path + `"}`)) + } + })) + t.Cleanup(server.Close) + + cert, err := x509.ParseCertificate(server.TLS.Certificates[0].Certificate[0]) + require.NoError(t, err) + + return server, cert +} + +func fakeTPP(t testing.TB) (*httptest.Server, *x509.Certificate) { + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("fake tpp.example.com received request: %s %s", r.Method, r.URL.Path) + + accessToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") + + if r.URL.Path == "/vedsdk/Identity/Self" { + if accessToken != "VALID_ACCESS_TOKEN" { + w.WriteHeader(http.StatusUnauthorized) + return + } + _, _ = w.Write([]byte(`{"Identities":[{"Name":"TEST"}]}`)) + } else if r.URL.Path == "/vedsdk/certificates/checkpolicy" { + _, _ = w.Write([]byte(`{"Policy":{"Subject":{"Organization":{"Value": "test-org"}}}}`)) + } else { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(`{"error":"unexpected path in the test server","path":"` + r.URL.Path + `"}`)) + } + })) + t.Cleanup(server.Close) + + cert, err := x509.ParseCertificate(server.TLS.Certificates[0].Certificate[0]) + require.NoError(t, err) + + return server, cert +} + +// To see the API server logs, set: +// +// export KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true +func startEnvtest(t testing.TB) (_ *envtest.Environment, _ *rest.Config, kclient ctrlruntime.WithWatch) { + envtest := &envtest.Environment{ + ErrorIfCRDPathMissing: true, + CRDDirectoryPaths: []string{"../../deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml"}, + } + restconf, err := envtest.Start() + require.NoError(t, err) + + t.Cleanup(func() { + t.Log("Waiting for envtest to exit") + err = envtest.Stop() + require.NoError(t, err) + }) + + sch := runtime.NewScheme() + _ = v1alpha1.AddToScheme(sch) + _ = corev1.AddToScheme(sch) + _ = rbacv1.AddToScheme(sch) + + kclient, err = ctrlruntime.NewWithWatch(restconf, ctrlruntime.Options{Scheme: sch}) + require.NoError(t, err) + + return envtest, restconf, kclient +} + +// Undent removes leading indentation/white-space from given string and returns +// it as a string. Useful for inlining YAML manifests in Go code. Inline YAML +// manifests in the Go test files makes it easier to read the test case as +// opposed to reading verbose-y Go structs. +// +// This was copied from https://github.com/jimeh/undent/blob/main/undent.go, all +// credit goes to the author, Jim Myhrberg. +func undent(s string) string { + const ( + tab = 9 + lf = 10 + spc = 32 + ) + + if len(s) == 0 { + return "" + } + + // find smallest indent relative to each line-feed + min := 99999999999 + count := 0 + + lfs := make([]int, 0, strings.Count(s, "\n")) + if s[0] != lf { + lfs = append(lfs, -1) + } + + indent := 0 + for i := 0; i < len(s); i++ { + if s[i] == lf { + lfs = append(lfs, i) + indent = 0 + } else if indent < min { + switch s[i] { + case spc, tab: + indent++ + default: + if indent > 0 { + count++ + } + if indent < min { + min = indent + } + } + } + } + + // extract each line without indentation + out := make([]byte, 0, len(s)-(min*count)) + + for i := 0; i < len(lfs); i++ { + offset := lfs[i] + 1 + end := len(s) + if i+1 < len(lfs) { + end = lfs[i+1] + 1 + } + + if offset+min < end { + out = append(out, s[offset+min:end]...) + } else if offset < end { + out = append(out, s[offset:end]...) + } + } + + return string(out) +} + +// Parses the YAML manifest. Useful for inlining YAML manifests in Go test +// files, to be used in conjunction with `undent`. +func parse(yamlmanifest string) []ctrlruntime.Object { + dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(yamlmanifest), 4096) + var objs []ctrlruntime.Object + for { + obj := &unstructured.Unstructured{} + err := dec.Decode(obj) + if err == io.EOF { + break + } + if err != nil { + panic(err) + } + + objs = append(objs, obj) + } + return objs +}