diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000..df618a6
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,43 @@
+name: 'Build docker image'
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ build:
+ name: "Build"
+ runs-on: ubuntu-latest
+ steps:
+ - name: "Checkout sources"
+ uses: actions/checkout@v6
+ - name: "Login to docker registry"
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: "Set version container labels"
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: |
+ ghcr.io/pr0ton11/cert-manager-desec-webhook
+ tags: |
+ type=raw,value=latest,enable={{is_default_branch}}
+ type=sha
+ labels: |
+ maintainer=proton11@github.com
+ org.opencontainers.image.title=cert-manager-deSEC-webhook
+ org.opencontainers.image.description=An independently maintained deSEC DNS validation webhook for cert-manager
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: "Build docker image"
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ push: true
+ platforms: linux/amd64
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a4be81c..d220510 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,8 +12,11 @@
*.out
# Ignore the built binary
-cert-manager-webhook-example
+cert-manager-desec-webhook
# Make artifacts
_out
_test
+
+# Ensure the test secrets are not accidentally pushed
+testdata/secret.yaml
diff --git a/Dockerfile b/Dockerfile
index 826e31c..8f826c9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,16 @@
-FROM golang:1.23-alpine3.19@sha256:5f3336882ad15d10ac1b59fbaba7cb84c35d4623774198b36ae60edeba45fd84 AS build_deps
+FROM golang:1.25.7 AS build
RUN apk add --no-cache git
WORKDIR /workspace
-COPY go.mod .
-COPY go.sum .
+COPY . .
RUN go mod download
-FROM build_deps AS build
-
-COPY . .
-
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
-FROM alpine:3.23@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
+FROM alpine:latest
RUN apk add --no-cache ca-certificates
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 9d3dc55..0000000
--- a/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-GO ?= $(shell which go)
-OS ?= $(shell $(GO) env GOOS)
-ARCH ?= $(shell $(GO) env GOARCH)
-
-IMAGE_NAME := "webhook"
-IMAGE_TAG := "latest"
-
-OUT := $(shell pwd)/_out
-
-KUBEBUILDER_VERSION=1.28.0
-
-HELM_FILES := $(shell find deploy/example-webhook)
-
-test: _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl
- TEST_ASSET_ETCD=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd \
- TEST_ASSET_KUBE_APISERVER=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver \
- TEST_ASSET_KUBECTL=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl \
- $(GO) test -v .
-
-_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH).tar.gz: | _test
- curl -fsSL https://go.kubebuilder.io/test-tools/$(KUBEBUILDER_VERSION)/$(OS)/$(ARCH) -o $@
-
-_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl: _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH).tar.gz | _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)
- tar xfO $< kubebuilder/bin/$(notdir $@) > $@ && chmod +x $@
-
-.PHONY: clean
-clean:
- rm -r _test $(OUT)
-
-.PHONY: build
-build:
- docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
-
-.PHONY: rendered-manifest.yaml
-rendered-manifest.yaml: $(OUT)/rendered-manifest.yaml
-
-$(OUT)/rendered-manifest.yaml: $(HELM_FILES) | $(OUT)
- helm template \
- --name example-webhook \
- --set image.repository=$(IMAGE_NAME) \
- --set image.tag=$(IMAGE_TAG) \
- deploy/example-webhook > $@
-
-_test $(OUT) _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH):
- mkdir -p $@
diff --git a/OWNERS b/OWNERS
deleted file mode 100644
index b72219c..0000000
--- a/OWNERS
+++ /dev/null
@@ -1,20 +0,0 @@
-approvers:
-- munnerz
-- joshvanl
-- wallrj
-- jakexks
-- maelvls
-- inteon
-- sgtcodfish
-- erikgb
-- thatsmrtalbot
-reviewers:
-- munnerz
-- joshvanl
-- wallrj
-- jakexks
-- maelvls
-- inteon
-- sgtcodfish
-- erikgb
-- thatsmrtalbot
diff --git a/README.md b/README.md
index 24ec32c..09c4d27 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,2 @@
-
-
-
+# Cert Manager DeSEC Webhook
-# ACME webhook example
-
-The ACME issuer type supports an optional 'webhook' solver, which can be used
-to implement custom DNS01 challenge solving logic.
-
-This is useful if you need to use cert-manager with a DNS provider that is not
-officially supported in cert-manager core.
-
-## Why not in core?
-
-As the project & adoption has grown, there has been an influx of DNS provider
-pull requests to our core codebase. As this number has grown, the test matrix
-has become un-maintainable and so, it's not possible for us to certify that
-providers work to a sufficient level.
-
-By creating this 'interface' between cert-manager and DNS providers, we allow
-users to quickly iterate and test out new integrations, and then packaging
-those up themselves as 'extensions' to cert-manager.
-
-We can also then provide a standardised 'testing framework', or set of
-conformance tests, which allow us to validate that a DNS provider works as
-expected.
-
-## Creating your own webhook
-
-Webhook's themselves are deployed as Kubernetes API services, in order to allow
-administrators to restrict access to webhooks with Kubernetes RBAC.
-
-This is important, as otherwise it'd be possible for anyone with access to your
-webhook to complete ACME challenge validations and obtain certificates.
-
-To make the set up of these webhook's easier, we provide a template repository
-that can be used to get started quickly.
-
-When implementing your webhook, you should set the `groupName` in the
-[values.yml](deploy/example-webhook/values.yaml) of your chart to a domain name that
-you - as the webhook-author - own. It should not need to be adjusted by the users of
-your chart.
-
-### Creating your own repository
-
-### Running the test suite
-
-All DNS providers **must** run the DNS01 provider conformance testing suite,
-else they will have undetermined behaviour when used with cert-manager.
-
-**It is essential that you configure and run the test suite when creating a
-DNS01 webhook.**
-
-An example Go test file has been provided in [main_test.go](https://github.com/cert-manager/webhook-example/blob/master/main_test.go).
-
-You can run the test suite with:
-
-```bash
-$ TEST_ZONE_NAME=example.com. make test
-```
-
-The example file has a number of areas you must fill in and replace with your
-own options in order for tests to pass.
diff --git a/deploy/example-webhook/.helmignore b/deploy/cert-manager-desec-webhook/.helmignore
similarity index 100%
rename from deploy/example-webhook/.helmignore
rename to deploy/cert-manager-desec-webhook/.helmignore
diff --git a/deploy/cert-manager-desec-webhook/Chart.yaml b/deploy/cert-manager-desec-webhook/Chart.yaml
new file mode 100644
index 0000000..321a6db
--- /dev/null
+++ b/deploy/cert-manager-desec-webhook/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1
+appVersion: "1.0"
+description: An independently maintained deSEC DNS validation webhook for cert-manager
+name: cert-manager-desec-webhook
+version: 0.1.0
diff --git a/deploy/example-webhook/templates/_helpers.tpl b/deploy/cert-manager-desec-webhook/templates/_helpers.tpl
similarity index 59%
rename from deploy/example-webhook/templates/_helpers.tpl
rename to deploy/cert-manager-desec-webhook/templates/_helpers.tpl
index d3c474b..2da07e5 100644
--- a/deploy/example-webhook/templates/_helpers.tpl
+++ b/deploy/cert-manager-desec-webhook/templates/_helpers.tpl
@@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
-{{- define "example-webhook.name" -}}
+{{- define "cert-manager-desec-webhook.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -11,7 +11,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
-{{- define "example-webhook.fullname" -}}
+{{- define "cert-manager-desec-webhook.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
@@ -27,22 +27,22 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
-{{- define "example-webhook.chart" -}}
+{{- define "cert-manager-desec-webhook.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
-{{- define "example-webhook.selfSignedIssuer" -}}
-{{ printf "%s-selfsign" (include "example-webhook.fullname" .) }}
+{{- define "cert-manager-desec-webhook.selfSignedIssuer" -}}
+{{ printf "%s-selfsign" (include "cert-manager-desec-webhook.fullname" .) }}
{{- end -}}
-{{- define "example-webhook.rootCAIssuer" -}}
-{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
+{{- define "cert-manager-desec-webhook.rootCAIssuer" -}}
+{{ printf "%s-ca" (include "cert-manager-desec-webhook.fullname" .) }}
{{- end -}}
-{{- define "example-webhook.rootCACertificate" -}}
-{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
+{{- define "cert-manager-desec-webhook.rootCACertificate" -}}
+{{ printf "%s-ca" (include "cert-manager-desec-webhook.fullname" .) }}
{{- end -}}
-{{- define "example-webhook.servingCertificate" -}}
-{{ printf "%s-webhook-tls" (include "example-webhook.fullname" .) }}
+{{- define "cert-manager-desec-webhook.servingCertificate" -}}
+{{ printf "%s-webhook-tls" (include "cert-manager-desec-webhook.fullname" .) }}
{{- end -}}
diff --git a/deploy/example-webhook/templates/apiservice.yaml b/deploy/cert-manager-desec-webhook/templates/apiservice.yaml
similarity index 63%
rename from deploy/example-webhook/templates/apiservice.yaml
rename to deploy/cert-manager-desec-webhook/templates/apiservice.yaml
index 4f6d5ce..85a0b33 100644
--- a/deploy/example-webhook/templates/apiservice.yaml
+++ b/deploy/cert-manager-desec-webhook/templates/apiservice.yaml
@@ -3,17 +3,17 @@ kind: APIService
metadata:
name: v1alpha1.{{ .Values.groupName }}
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
- cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "example-webhook.servingCertificate" . }}"
+ cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "cert-manager-desec-webhook.servingCertificate" . }}"
spec:
group: {{ .Values.groupName }}
groupPriorityMinimum: 1000
versionPriority: 15
service:
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
version: v1alpha1
diff --git a/deploy/example-webhook/templates/deployment.yaml b/deploy/cert-manager-desec-webhook/templates/deployment.yaml
similarity index 77%
rename from deploy/example-webhook/templates/deployment.yaml
rename to deploy/cert-manager-desec-webhook/templates/deployment.yaml
index 057cc42..5e97429 100644
--- a/deploy/example-webhook/templates/deployment.yaml
+++ b/deploy/cert-manager-desec-webhook/templates/deployment.yaml
@@ -1,26 +1,26 @@
apiVersion: apps/v1
kind: Deployment
metadata:
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
- app: {{ include "example-webhook.name" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
- app: {{ include "example-webhook.name" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
release: {{ .Release.Name }}
spec:
- serviceAccountName: {{ include "example-webhook.fullname" . }}
+ serviceAccountName: {{ include "cert-manager-desec-webhook.fullname" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@@ -54,7 +54,7 @@ spec:
volumes:
- name: certs
secret:
- secretName: {{ include "example-webhook.servingCertificate" . }}
+ secretName: {{ include "cert-manager-desec-webhook.servingCertificate" . }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
diff --git a/deploy/cert-manager-desec-webhook/templates/pki.yaml b/deploy/cert-manager-desec-webhook/templates/pki.yaml
new file mode 100644
index 0000000..8814352
--- /dev/null
+++ b/deploy/cert-manager-desec-webhook/templates/pki.yaml
@@ -0,0 +1,76 @@
+---
+# Create a selfsigned Issuer, in order to create a root CA certificate for
+# signing webhook serving certificates
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+ name: {{ include "cert-manager-desec-webhook.selfSignedIssuer" . }}
+ namespace: {{ .Release.Namespace | quote }}
+ labels:
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ selfSigned: {}
+
+---
+
+# Generate a CA Certificate used to sign certificates for the webhook
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: {{ include "cert-manager-desec-webhook.rootCACertificate" . }}
+ namespace: {{ .Release.Namespace | quote }}
+ labels:
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ secretName: {{ include "cert-manager-desec-webhook.rootCACertificate" . }}
+ duration: 43800h # 5y
+ issuerRef:
+ name: {{ include "cert-manager-desec-webhook.selfSignedIssuer" . }}
+ commonName: "ca.cert-manager-desec-webhook.cert-manager"
+ isCA: true
+
+---
+
+# Create an Issuer that uses the above generated CA certificate to issue certs
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+ name: {{ include "cert-manager-desec-webhook.rootCAIssuer" . }}
+ namespace: {{ .Release.Namespace | quote }}
+ labels:
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ ca:
+ secretName: {{ include "cert-manager-desec-webhook.rootCACertificate" . }}
+
+---
+
+# Finally, generate a serving certificate for the webhook to use
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: {{ include "cert-manager-desec-webhook.servingCertificate" . }}
+ namespace: {{ .Release.Namespace | quote }}
+ labels:
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ secretName: {{ include "cert-manager-desec-webhook.servingCertificate" . }}
+ duration: 8760h # 1y
+ issuerRef:
+ name: {{ include "cert-manager-desec-webhook.rootCAIssuer" . }}
+ dnsNames:
+ - {{ include "cert-manager-desec-webhook.fullname" . }}
+ - {{ include "cert-manager-desec-webhook.fullname" . }}.{{ .Release.Namespace }}
+ - {{ include "cert-manager-desec-webhook.fullname" . }}.{{ .Release.Namespace }}.svc
diff --git a/deploy/example-webhook/templates/rbac.yaml b/deploy/cert-manager-desec-webhook/templates/rbac.yaml
similarity index 61%
rename from deploy/example-webhook/templates/rbac.yaml
rename to deploy/cert-manager-desec-webhook/templates/rbac.yaml
index 605fcf5..564bf0c 100644
--- a/deploy/example-webhook/templates/rbac.yaml
+++ b/deploy/cert-manager-desec-webhook/templates/rbac.yaml
@@ -1,11 +1,11 @@
apiVersion: v1
kind: ServiceAccount
metadata:
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
---
@@ -15,11 +15,11 @@ metadata:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
- name: {{ include "example-webhook.fullname" . }}:webhook-authentication-reader
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}:webhook-authentication-reader
namespace: kube-system
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
@@ -29,7 +29,7 @@ roleRef:
subjects:
- apiGroup: ""
kind: ServiceAccount
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
---
# apiserver gets the auth-delegator role to delegate auth decisions to
@@ -37,10 +37,10 @@ subjects:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
- name: {{ include "example-webhook.fullname" . }}:auth-delegator
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}:auth-delegator
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
@@ -50,17 +50,17 @@ roleRef:
subjects:
- apiGroup: ""
kind: ServiceAccount
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
---
# Grant cert-manager permission to validate using our apiserver
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
- name: {{ include "example-webhook.fullname" . }}:domain-solver
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}:domain-solver
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
rules:
@@ -74,16 +74,16 @@ rules:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
- name: {{ include "example-webhook.fullname" . }}:domain-solver
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}:domain-solver
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
- name: {{ include "example-webhook.fullname" . }}:domain-solver
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}:domain-solver
subjects:
- apiGroup: ""
kind: ServiceAccount
diff --git a/deploy/example-webhook/templates/service.yaml b/deploy/cert-manager-desec-webhook/templates/service.yaml
similarity index 59%
rename from deploy/example-webhook/templates/service.yaml
rename to deploy/cert-manager-desec-webhook/templates/service.yaml
index a76ddc7..5e5afe0 100644
--- a/deploy/example-webhook/templates/service.yaml
+++ b/deploy/cert-manager-desec-webhook/templates/service.yaml
@@ -1,11 +1,11 @@
apiVersion: v1
kind: Service
metadata:
- name: {{ include "example-webhook.fullname" . }}
+ name: {{ include "cert-manager-desec-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
+ chart: {{ include "cert-manager-desec-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
@@ -16,5 +16,5 @@ spec:
protocol: TCP
name: https
selector:
- app: {{ include "example-webhook.name" . }}
+ app: {{ include "cert-manager-desec-webhook.name" . }}
release: {{ .Release.Name }}
diff --git a/deploy/example-webhook/values.yaml b/deploy/cert-manager-desec-webhook/values.yaml
similarity index 51%
rename from deploy/example-webhook/values.yaml
rename to deploy/cert-manager-desec-webhook/values.yaml
index 9423bd4..4eccf01 100644
--- a/deploy/example-webhook/values.yaml
+++ b/deploy/cert-manager-desec-webhook/values.yaml
@@ -1,21 +1,11 @@
-# The groupName avoids naming conflicts on the Kubernetes API, it should be set by the
-# author of the webhook a unique domain that the author owns. For
-# example: some-provider-webhook.mycompany.tld , or
-# some-provider-webhook.mypersonalsite.tld if your webhook is a personal open-source
-# project.
-# Once set to a unique domain name by the webhook author, it does not need to be further
-# adjusted by the users of a webhook chart!
-# The name will need to be referenced in each Issuer's `webhook` stanza to
-# inform cert-manager of where to send ChallengePayload resources in order to
-# solve the DNS01 challenge.
-groupName: acme.mycompany.com
+groupName: acme.pr0ton11.github.com
certManager:
namespace: cert-manager
serviceAccountName: cert-manager
image:
- repository: mycompany/webhook-image
+ repository: ghcr.io/pr0ton11/cert-manager-desec-webhook
tag: latest
pullPolicy: IfNotPresent
diff --git a/deploy/example-webhook/Chart.yaml b/deploy/example-webhook/Chart.yaml
deleted file mode 100644
index 77c6ead..0000000
--- a/deploy/example-webhook/Chart.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-appVersion: "1.0"
-description: A Helm chart for Kubernetes
-name: example-webhook
-version: 0.1.0
diff --git a/deploy/example-webhook/templates/NOTES.txt b/deploy/example-webhook/templates/NOTES.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/deploy/example-webhook/templates/pki.yaml b/deploy/example-webhook/templates/pki.yaml
deleted file mode 100644
index b4b4c23..0000000
--- a/deploy/example-webhook/templates/pki.yaml
+++ /dev/null
@@ -1,76 +0,0 @@
----
-# Create a selfsigned Issuer, in order to create a root CA certificate for
-# signing webhook serving certificates
-apiVersion: cert-manager.io/v1
-kind: Issuer
-metadata:
- name: {{ include "example-webhook.selfSignedIssuer" . }}
- namespace: {{ .Release.Namespace | quote }}
- labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
- release: {{ .Release.Name }}
- heritage: {{ .Release.Service }}
-spec:
- selfSigned: {}
-
----
-
-# Generate a CA Certificate used to sign certificates for the webhook
-apiVersion: cert-manager.io/v1
-kind: Certificate
-metadata:
- name: {{ include "example-webhook.rootCACertificate" . }}
- namespace: {{ .Release.Namespace | quote }}
- labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
- release: {{ .Release.Name }}
- heritage: {{ .Release.Service }}
-spec:
- secretName: {{ include "example-webhook.rootCACertificate" . }}
- duration: 43800h # 5y
- issuerRef:
- name: {{ include "example-webhook.selfSignedIssuer" . }}
- commonName: "ca.example-webhook.cert-manager"
- isCA: true
-
----
-
-# Create an Issuer that uses the above generated CA certificate to issue certs
-apiVersion: cert-manager.io/v1
-kind: Issuer
-metadata:
- name: {{ include "example-webhook.rootCAIssuer" . }}
- namespace: {{ .Release.Namespace | quote }}
- labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
- release: {{ .Release.Name }}
- heritage: {{ .Release.Service }}
-spec:
- ca:
- secretName: {{ include "example-webhook.rootCACertificate" . }}
-
----
-
-# Finally, generate a serving certificate for the webhook to use
-apiVersion: cert-manager.io/v1
-kind: Certificate
-metadata:
- name: {{ include "example-webhook.servingCertificate" . }}
- namespace: {{ .Release.Namespace | quote }}
- labels:
- app: {{ include "example-webhook.name" . }}
- chart: {{ include "example-webhook.chart" . }}
- release: {{ .Release.Name }}
- heritage: {{ .Release.Service }}
-spec:
- secretName: {{ include "example-webhook.servingCertificate" . }}
- duration: 8760h # 1y
- issuerRef:
- name: {{ include "example-webhook.rootCAIssuer" . }}
- dnsNames:
- - {{ include "example-webhook.fullname" . }}
- - {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }}
- - {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }}.svc
diff --git a/example/dns.go b/example/dns.go
deleted file mode 100644
index e29597e..0000000
--- a/example/dns.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package example
-
-import (
- "fmt"
-
- "github.com/miekg/dns"
-)
-
-func (e *exampleSolver) handleDNSRequest(w dns.ResponseWriter, req *dns.Msg) {
- msg := new(dns.Msg)
- msg.SetReply(req)
- switch req.Opcode {
- case dns.OpcodeQuery:
- for _, q := range msg.Question {
- if err := e.addDNSAnswer(q, msg, req); err != nil {
- msg.SetRcode(req, dns.RcodeServerFailure)
- break
- }
- }
- }
- w.WriteMsg(msg)
-}
-
-func (e *exampleSolver) addDNSAnswer(q dns.Question, msg *dns.Msg, req *dns.Msg) error {
- switch q.Qtype {
- // Always return loopback for any A query
- case dns.TypeA:
- rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN A 127.0.0.1", q.Name))
- if err != nil {
- return err
- }
- msg.Answer = append(msg.Answer, rr)
- return nil
-
- // TXT records are the only important record for ACME dns-01 challenges
- case dns.TypeTXT:
- e.RLock()
- record, found := e.txtRecords[q.Name]
- e.RUnlock()
- if !found {
- msg.SetRcode(req, dns.RcodeNameError)
- return nil
- }
- rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN TXT %s", q.Name, record))
- if err != nil {
- return err
- }
- msg.Answer = append(msg.Answer, rr)
- return nil
-
- // NS and SOA are for authoritative lookups, return obviously invalid data
- case dns.TypeNS:
- rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN NS ns.example-acme-webook.invalid.", q.Name))
- if err != nil {
- return err
- }
- msg.Answer = append(msg.Answer, rr)
- return nil
- case dns.TypeSOA:
- rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN SOA %s 20 5 5 5 5", "ns.example-acme-webook.invalid.", "ns.example-acme-webook.invalid."))
- if err != nil {
- return err
- }
- msg.Answer = append(msg.Answer, rr)
- return nil
- default:
- return fmt.Errorf("unimplemented record type %v", q.Qtype)
- }
-}
diff --git a/example/example.go b/example/example.go
deleted file mode 100644
index 8cfe59e..0000000
--- a/example/example.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// package example contains a self-contained example of a webhook that passes the cert-manager
-// DNS conformance tests
-package example
-
-import (
- "fmt"
- "os"
- "sync"
-
- "github.com/cert-manager/cert-manager/pkg/acme/webhook"
- acme "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
- "github.com/miekg/dns"
- "k8s.io/client-go/rest"
-)
-
-type exampleSolver struct {
- name string
- server *dns.Server
- txtRecords map[string]string
- sync.RWMutex
-}
-
-func (e *exampleSolver) Name() string {
- return e.name
-}
-
-func (e *exampleSolver) Present(ch *acme.ChallengeRequest) error {
- e.Lock()
- e.txtRecords[ch.ResolvedFQDN] = ch.Key
- e.Unlock()
- return nil
-}
-
-func (e *exampleSolver) CleanUp(ch *acme.ChallengeRequest) error {
- e.Lock()
- delete(e.txtRecords, ch.ResolvedFQDN)
- e.Unlock()
- return nil
-}
-
-func (e *exampleSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
- go func(done <-chan struct{}) {
- <-done
- if err := e.server.Shutdown(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err.Error())
- }
- }(stopCh)
- go func() {
- if err := e.server.ListenAndServe(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err.Error())
- os.Exit(1)
- }
- }()
- return nil
-}
-
-func New(port string) webhook.Solver {
- e := &exampleSolver{
- name: "example",
- txtRecords: make(map[string]string),
- }
- e.server = &dns.Server{
- Addr: ":" + port,
- Net: "udp",
- Handler: dns.HandlerFunc(e.handleDNSRequest),
- }
- return e
-}
diff --git a/example/example_test.go b/example/example_test.go
deleted file mode 100644
index ef4dde3..0000000
--- a/example/example_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package example
-
-import (
- "crypto/rand"
- "math/big"
- "testing"
-
- acme "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
- "github.com/miekg/dns"
- "github.com/stretchr/testify/assert"
-)
-
-func TestExampleSolver_Name(t *testing.T) {
- port, _ := rand.Int(rand.Reader, big.NewInt(50000))
- port = port.Add(port, big.NewInt(15534))
- solver := New(port.String())
- assert.Equal(t, "example", solver.Name())
-}
-
-func TestExampleSolver_Initialize(t *testing.T) {
- port, _ := rand.Int(rand.Reader, big.NewInt(50000))
- port = port.Add(port, big.NewInt(15534))
- solver := New(port.String())
- done := make(chan struct{})
- err := solver.Initialize(nil, done)
- assert.NoError(t, err, "Expected Initialize not to error")
- close(done)
-}
-
-func TestExampleSolver_Present_Cleanup(t *testing.T) {
- port, _ := rand.Int(rand.Reader, big.NewInt(50000))
- port = port.Add(port, big.NewInt(15534))
- solver := New(port.String())
- done := make(chan struct{})
- err := solver.Initialize(nil, done)
- assert.NoError(t, err, "Expected Initialize not to error")
-
- validTestData := []struct {
- hostname string
- record string
- }{
- {"test1.example.com.", "testkey1"},
- {"test2.example.com.", "testkey2"},
- {"test3.example.com.", "testkey3"},
- }
- for _, test := range validTestData {
- err := solver.Present(&acme.ChallengeRequest{
- Action: acme.ChallengeActionPresent,
- Type: "dns-01",
- ResolvedFQDN: test.hostname,
- Key: test.record,
- })
- assert.NoError(t, err, "Unexpected error while presenting %v", t)
- }
-
- // Resolve test data
- for _, test := range validTestData {
- msg := new(dns.Msg)
- msg.Id = dns.Id()
- msg.RecursionDesired = true
- msg.Question = make([]dns.Question, 1)
- msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
- in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
-
- assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
- assert.Len(t, in.Answer, 1, "RR response is of incorrect length")
- assert.Equal(t, []string{test.record}, in.Answer[0].(*dns.TXT).Txt, "TXT record returned did not match presented record")
- }
-
- // Cleanup test data
- for _, test := range validTestData {
- err := solver.CleanUp(&acme.ChallengeRequest{
- Action: acme.ChallengeActionCleanUp,
- Type: "dns-01",
- ResolvedFQDN: test.hostname,
- Key: test.record,
- })
- assert.NoError(t, err, "Unexpected error while cleaning up %v", t)
- }
-
- // Resolve test data
- for _, test := range validTestData {
- msg := new(dns.Msg)
- msg.Id = dns.Id()
- msg.RecursionDesired = true
- msg.Question = make([]dns.Question, 1)
- msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
- in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
-
- assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
- assert.Len(t, in.Answer, 0, "RR response is of incorrect length")
- assert.Equal(t, dns.RcodeNameError, in.Rcode, "Expexted NXDOMAIN")
- }
-
- close(done)
-}
diff --git a/go.mod b/go.mod
index 07cbcc3..5053bbd 100644
--- a/go.mod
+++ b/go.mod
@@ -1,17 +1,17 @@
-module github.com/cert-manager/webhook-example
+module github.com/proton11/cert-manager-desec-webhook
-go 1.25.0
+go 1.25.7
require (
github.com/cert-manager/cert-manager v1.19.3
- github.com/miekg/dns v1.1.72
- github.com/stretchr/testify v1.11.1
- k8s.io/apiextensions-apiserver v0.35.0
- k8s.io/client-go v0.35.0
+ github.com/nrdcg/desec v0.11.1
+ k8s.io/apiextensions-apiserver v0.35.1
+ k8s.io/apimachinery v0.35.1
+ k8s.io/client-go v0.35.1
)
require (
- cel.dev/expr v0.24.0 // indirect
+ cel.dev/expr v0.25.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -19,7 +19,7 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
@@ -29,86 +29,95 @@ require (
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
- github.com/go-openapi/jsonpointer v0.22.1 // indirect
- github.com/go-openapi/jsonreference v0.21.2 // indirect
- github.com/go-openapi/swag v0.23.1 // indirect
- github.com/go-openapi/swag/jsonname v0.25.1 // indirect
+ github.com/go-openapi/jsonpointer v0.22.4 // indirect
+ github.com/go-openapi/jsonreference v0.21.4 // indirect
+ github.com/go-openapi/swag v0.25.4 // indirect
+ github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
+ github.com/go-openapi/swag/conv v0.25.4 // indirect
+ github.com/go-openapi/swag/fileutils v0.25.4 // indirect
+ github.com/go-openapi/swag/jsonname v0.25.4 // indirect
+ github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
+ github.com/go-openapi/swag/loading v0.25.4 // indirect
+ github.com/go-openapi/swag/mangling v0.25.4 // indirect
+ github.com/go-openapi/swag/netutils v0.25.4 // indirect
+ github.com/go-openapi/swag/stringutils v0.25.4 // indirect
+ github.com/go-openapi/swag/typeutils v0.25.4 // indirect
+ github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
- github.com/google/cel-go v0.26.0 // indirect
- github.com/google/gnostic-models v0.7.0 // indirect
+ github.com/google/cel-go v0.27.0 // indirect
+ github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
- github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/miekg/dns v1.1.68 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/peterhellberg/link v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
- github.com/prometheus/common v0.66.1 // indirect
- github.com/prometheus/procfs v0.17.0 // indirect
- github.com/spf13/cobra v1.10.1 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.19.2 // indirect
+ github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
- go.etcd.io/etcd/api/v3 v3.6.5 // indirect
- go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect
- go.etcd.io/etcd/client/v3 v3.6.5 // indirect
- go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
- go.opentelemetry.io/otel v1.37.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
- go.opentelemetry.io/otel/metric v1.37.0 // indirect
- go.opentelemetry.io/otel/sdk v1.37.0 // indirect
- go.opentelemetry.io/otel/trace v1.37.0 // indirect
- go.opentelemetry.io/proto/otlp v1.7.0 // indirect
+ go.etcd.io/etcd/api/v3 v3.6.8 // indirect
+ go.etcd.io/etcd/client/pkg/v3 v3.6.8 // indirect
+ go.etcd.io/etcd/client/v3 v3.6.8 // indirect
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
+ go.opentelemetry.io/otel v1.40.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
+ go.opentelemetry.io/otel/metric v1.40.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.40.0 // indirect
+ go.opentelemetry.io/otel/trace v1.40.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- go.uber.org/zap v1.27.0 // indirect
+ go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
- golang.org/x/crypto v0.46.0 // indirect
- golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
- golang.org/x/mod v0.31.0 // indirect
- golang.org/x/net v0.48.0 // indirect
- golang.org/x/oauth2 v0.31.0 // indirect
+ golang.org/x/crypto v0.48.0 // indirect
+ golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect
+ golang.org/x/mod v0.33.0 // indirect
+ golang.org/x/net v0.50.0 // indirect
+ golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.19.0 // indirect
- golang.org/x/sys v0.39.0 // indirect
- golang.org/x/term v0.38.0 // indirect
- golang.org/x/text v0.32.0 // indirect
- golang.org/x/time v0.13.0 // indirect
- golang.org/x/tools v0.40.0 // indirect
+ golang.org/x/sys v0.41.0 // indirect
+ golang.org/x/term v0.40.0 // indirect
+ golang.org/x/text v0.34.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+ golang.org/x/tools v0.42.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
- google.golang.org/grpc v1.75.1 // indirect
- google.golang.org/protobuf v1.36.9 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
+ google.golang.org/grpc v1.79.1 // indirect
+ google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/api v0.35.0 // indirect
- k8s.io/apimachinery v0.35.0 // indirect
- k8s.io/apiserver v0.35.0 // indirect
- k8s.io/component-base v0.35.0 // indirect
+ k8s.io/api v0.35.1 // indirect
+ k8s.io/apiserver v0.35.1 // indirect
+ k8s.io/component-base v0.35.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
- k8s.io/kms v0.35.0 // indirect
- k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
- k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
- sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
- sigs.k8s.io/controller-runtime v0.22.3 // indirect
- sigs.k8s.io/gateway-api v1.4.0 // indirect
+ k8s.io/kms v0.35.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
+ k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect
+ sigs.k8s.io/controller-runtime v0.23.1 // indirect
+ sigs.k8s.io/gateway-api v1.4.1 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
- sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/go.sum b/go.sum
index afc5eb8..625a161 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
-cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
+cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
@@ -19,8 +19,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.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/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -34,6 +34,8 @@ github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lSh
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@@ -47,17 +49,42 @@ 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.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
-github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
-github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
-github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
-github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
-github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
-github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU=
-github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo=
+github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
+github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
+github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
+github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
+github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
+github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
+github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
+github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
+github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
+github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
+github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
+github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
+github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
+github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
+github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
+github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
+github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
+github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
+github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
+github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
+github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
+github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
+github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
+github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
+github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
+github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
+github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
+github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
+github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
+github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
+github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
+github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
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/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
@@ -66,10 +93,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
-github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
-github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
-github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
+github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
+github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
+github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -87,14 +114,18 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/E
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
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/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8 h1:NpbJl/eVbvrGE0MJ6X16X9SAifesl6Fwxg/YmCvubRI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8/go.mod h1:mi7YA+gCzVem12exXy46ZespvGtX/lZmD/RLnQhVW7U=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
+github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -107,10 +138,12 @@ 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=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
-github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
-github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
-github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
+github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -119,10 +152,14 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/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/nrdcg/desec v0.11.1 h1:ilpKmCr4gGsLcyq3RHfHNmlRzm9fzT2XbWxoVaUCS0s=
+github.com/nrdcg/desec v0.11.1/go.mod h1:2LuxHlOcwML/7cntu0eimONmA1U+ZxFDAonoSXr4igQ=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
+github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
+github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -132,10 +169,10 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
-github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
-github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
-github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
-github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
+github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -143,22 +180,15 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
-github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
-github.com/stoewer/go-strcase v1.3.1/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=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
@@ -171,46 +201,46 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
-go.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA=
-go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ=
-go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8=
-go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk=
-go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U=
-go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo=
+go.etcd.io/etcd/api/v3 v3.6.8 h1:gqb1VN92TAI6G2FiBvWcqKtHiIjr4SU2GdXxTwyexbM=
+go.etcd.io/etcd/api/v3 v3.6.8/go.mod h1:qyQj1HZPUV3B5cbAL8scG62+fyz5dSxxu0w8pn28N6Q=
+go.etcd.io/etcd/client/pkg/v3 v3.6.8 h1:Qs/5C0LNFiqXxYf2GU8MVjYUEXJ6sZaYOz0zEqQgy50=
+go.etcd.io/etcd/client/pkg/v3 v3.6.8/go.mod h1:GsiTRUZE2318PggZkAo6sWb6l8JLVrnckTNfbG8PWtw=
+go.etcd.io/etcd/client/v3 v3.6.8 h1:B3G76t1UykqAOrbio7s/EPatixQDkQBevN8/mwiplrY=
+go.etcd.io/etcd/client/v3 v3.6.8/go.mod h1:MVG4BpSIuumPi+ELF7wYtySETmoTWBHVcDoHdVupwt8=
go.etcd.io/etcd/pkg/v3 v3.6.5 h1:byxWB4AqIKI4SBmquZUG1WGtvMfMaorXFoCcFbVeoxM=
go.etcd.io/etcd/pkg/v3 v3.6.5/go.mod h1:uqrXrzmMIJDEy5j00bCqhVLzR5jEJIwDp5wTlLwPGOU=
go.etcd.io/etcd/server/v3 v3.6.5 h1:4RbUb1Bd4y1WkBHmuF+cZII83JNQMuNXzyjwigQ06y0=
go.etcd.io/etcd/server/v3 v3.6.5/go.mod h1:PLuhyVXz8WWRhzXDsl3A3zv/+aK9e4A9lpQkqawIaH0=
go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
-go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
-go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
-go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
-go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
-go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
-go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
-go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
-go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
-go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
-go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
-go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
-go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
-go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
+go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
+go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
+go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
+go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
+go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
+go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
+go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
+go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
+go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
+go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
+go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
+go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@@ -218,22 +248,22 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
-golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
-golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
-golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
+golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
+golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
+golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o=
+golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
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.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
-golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
+golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
+golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
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.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
-golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
-golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
-golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
+golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
+golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
+golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
+golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
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=
@@ -242,22 +272,22 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
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.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
-golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
-golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
+golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
+golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
+golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
-golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
-golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
-golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
+golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
+golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
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.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
-golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
+golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
+golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
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=
@@ -266,14 +296,14 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
-google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
-google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
-google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
-google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
-google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
+google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
+google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
+google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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=
@@ -283,40 +313,39 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-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.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
-k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
-k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
-k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
-k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
-k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
-k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4=
-k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds=
-k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
-k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
-k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94=
-k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0=
+k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q=
+k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM=
+k8s.io/apiextensions-apiserver v0.35.1 h1:p5vvALkknlOcAqARwjS20kJffgzHqwyQRM8vHLwgU7w=
+k8s.io/apiextensions-apiserver v0.35.1/go.mod h1:2CN4fe1GZ3HMe4wBr25qXyJnJyZaquy4nNlNmb3R7AQ=
+k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU=
+k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
+k8s.io/apiserver v0.35.1 h1:potxdhhTL4i6AYAa2QCwtlhtB1eCdWQFvJV6fXgJzxs=
+k8s.io/apiserver v0.35.1/go.mod h1:BiL6Dd3A2I/0lBnteXfWmCFobHM39vt5+hJQd7Lbpi4=
+k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM=
+k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA=
+k8s.io/component-base v0.35.1 h1:XgvpRf4srp037QWfGBLFsYMUQJkE5yMa94UsJU7pmcE=
+k8s.io/component-base v0.35.1/go.mod h1:HI/6jXlwkiOL5zL9bqA3en1Ygv60F03oEpnuU1G56Bs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kms v0.35.0 h1:/x87FED2kDSo66csKtcYCEHsxF/DBlNl7LfJ1fVQs1o=
-k8s.io/kms v0.35.0/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ=
-k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
-k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
-k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
-k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 h1:qPrZsv1cwQiFeieFlRqT627fVZ+tyfou/+S5S0H5ua0=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
-sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y=
-sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
-sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ=
-sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk=
+k8s.io/kms v0.35.1 h1:kjv2r9g1mY7uL+l1RhyAZvWVZIA/4qIfBHXyjFGLRhU=
+k8s.io/kms v0.35.1/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ=
+k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=
+k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE=
+sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
+sigs.k8s.io/gateway-api v1.4.1 h1:NPxFutNkKNa8UfLd2CMlEuhIPMQgDQ6DXNKG9sHbJU8=
+sigs.k8s.io/gateway-api v1.4.1/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
-sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
-sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/main.go b/main.go
index 969e6d2..8d954be 100644
--- a/main.go
+++ b/main.go
@@ -1,146 +1,21 @@
package main
import (
- "encoding/json"
- "fmt"
"os"
- extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
- "k8s.io/client-go/rest"
-
- "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
+ "github.com/proton11/cert-manager-desec-webhook/solver"
)
-var GroupName = os.Getenv("GROUP_NAME")
-
+// Entrypoint of the application
func main() {
- if GroupName == "" {
- panic("GROUP_NAME must be specified")
+
+ // Read the custom group name from environment variables
+ groupName, ok := os.LookupEnv("GROUP_NAME")
+ // Without a custom group name, return the default (also defined in the Helm chart)
+ if !ok || groupName == "" {
+ groupName = "acme.pr0ton11.github.com"
}
-
- // This will register our custom DNS provider with the webhook serving
- // library, making it available as an API under the provided GroupName.
- // You can register multiple DNS provider implementations with a single
- // webhook, where the Name() method will be used to disambiguate between
- // the different implementations.
- cmd.RunWebhookServer(GroupName,
- &customDNSProviderSolver{},
- )
-}
-
-// customDNSProviderSolver implements the provider-specific logic needed to
-// 'present' an ACME challenge TXT record for your own DNS provider.
-// To do so, it must implement the `github.com/cert-manager/cert-manager/pkg/acme/webhook.Solver`
-// interface.
-type customDNSProviderSolver struct {
- // If a Kubernetes 'clientset' is needed, you must:
- // 1. uncomment the additional `client` field in this structure below
- // 2. uncomment the "k8s.io/client-go/kubernetes" import at the top of the file
- // 3. uncomment the relevant code in the Initialize method below
- // 4. ensure your webhook's service account has the required RBAC role
- // assigned to it for interacting with the Kubernetes APIs you need.
- //client kubernetes.Clientset
-}
-
-// customDNSProviderConfig is a structure that is used to decode into when
-// solving a DNS01 challenge.
-// This information is provided by cert-manager, and may be a reference to
-// additional configuration that's needed to solve the challenge for this
-// particular certificate or issuer.
-// This typically includes references to Secret resources containing DNS
-// provider credentials, in cases where a 'multi-tenant' DNS solver is being
-// created.
-// If you do *not* require per-issuer or per-certificate configuration to be
-// provided to your webhook, you can skip decoding altogether in favour of
-// using CLI flags or similar to provide configuration.
-// You should not include sensitive information here. If credentials need to
-// be used by your provider here, you should reference a Kubernetes Secret
-// resource and fetch these credentials using a Kubernetes clientset.
-type customDNSProviderConfig struct {
- // Change the two fields below according to the format of the configuration
- // to be decoded.
- // These fields will be set by users in the
- // `issuer.spec.acme.dns01.providers.webhook.config` field.
-
- //Email string `json:"email"`
- //APIKeySecretRef v1alpha1.SecretKeySelector `json:"apiKeySecretRef"`
-}
-
-// Name is used as the name for this DNS solver when referencing it on the ACME
-// Issuer resource.
-// This should be unique **within the group name**, i.e. you can have two
-// solvers configured with the same Name() **so long as they do not co-exist
-// within a single webhook deployment**.
-// For example, `cloudflare` may be used as the name of a solver.
-func (c *customDNSProviderSolver) Name() string {
- return "my-custom-solver"
-}
-
-// Present is responsible for actually presenting the DNS record with the
-// DNS provider.
-// This method should tolerate being called multiple times with the same value.
-// cert-manager itself will later perform a self check to ensure that the
-// solver has correctly configured the DNS provider.
-func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
- cfg, err := loadConfig(ch.Config)
- if err != nil {
- return err
- }
-
- // TODO: do something more useful with the decoded configuration
- fmt.Printf("Decoded configuration %v", cfg)
-
- // TODO: add code that sets a record in the DNS provider's console
- return nil
-}
-
-// CleanUp should delete the relevant TXT record from the DNS provider console.
-// If multiple TXT records exist with the same record name (e.g.
-// _acme-challenge.example.com) then **only** the record with the same `key`
-// value provided on the ChallengeRequest should be cleaned up.
-// This is in order to facilitate multiple DNS validations for the same domain
-// concurrently.
-func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
- // TODO: add code that deletes a record from the DNS provider's console
- return nil
-}
-
-// Initialize will be called when the webhook first starts.
-// This method can be used to instantiate the webhook, i.e. initialising
-// connections or warming up caches.
-// Typically, the kubeClientConfig parameter is used to build a Kubernetes
-// client that can be used to fetch resources from the Kubernetes API, e.g.
-// Secret resources containing credentials used to authenticate with DNS
-// provider accounts.
-// The stopCh can be used to handle early termination of the webhook, in cases
-// where a SIGTERM or similar signal is sent to the webhook process.
-func (c *customDNSProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
- ///// UNCOMMENT THE BELOW CODE TO MAKE A KUBERNETES CLIENTSET AVAILABLE TO
- ///// YOUR CUSTOM DNS PROVIDER
-
- //cl, err := kubernetes.NewForConfig(kubeClientConfig)
- //if err != nil {
- // return err
- //}
- //
- //c.client = cl
-
- ///// END OF CODE TO MAKE KUBERNETES CLIENTSET AVAILABLE
- return nil
-}
-
-// loadConfig is a small helper function that decodes JSON configuration into
-// the typed config struct.
-func loadConfig(cfgJSON *extapi.JSON) (customDNSProviderConfig, error) {
- cfg := customDNSProviderConfig{}
- // handle the 'base case' where no configuration has been provided
- if cfgJSON == nil {
- return cfg, nil
- }
- if err := json.Unmarshal(cfgJSON.Raw, &cfg); err != nil {
- return cfg, fmt.Errorf("error decoding solver config: %v", err)
- }
-
- return cfg, nil
+ // Start the webhook server with our solver
+ cmd.RunWebhookServer(groupName, &solver.DeSECDNSProviderSolver{})
}
diff --git a/main_test.go b/main_test.go
index 1d7d5ff..06ab7d0 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1,41 +1 @@
package main
-
-import (
- "os"
- "testing"
-
- acmetest "github.com/cert-manager/cert-manager/test/acme"
-
- "github.com/cert-manager/webhook-example/example"
-)
-
-var (
- zone = os.Getenv("TEST_ZONE_NAME")
-)
-
-func TestRunsSuite(t *testing.T) {
- // The manifest path should contain a file named config.json that is a
- // snippet of valid configuration that should be included on the
- // ChallengeRequest passed as part of the test cases.
- //
-
- // Uncomment the below fixture when implementing your custom DNS provider
- //fixture := acmetest.NewFixture(&customDNSProviderSolver{},
- // acmetest.SetResolvedZone(zone),
- // acmetest.SetAllowAmbientCredentials(false),
- // acmetest.SetManifestPath("testdata/my-custom-solver"),
- // acmetest.SetBinariesPath("_test/kubebuilder/bin"),
- //)
- solver := example.New("59351")
- fixture := acmetest.NewFixture(solver,
- acmetest.SetResolvedZone("example.com."),
- acmetest.SetManifestPath("testdata/my-custom-solver"),
- acmetest.SetDNSServer("127.0.0.1:59351"),
- acmetest.SetUseAuthoritative(false),
- )
- //need to uncomment and RunConformance delete runBasic and runExtended once https://github.com/cert-manager/cert-manager/pull/4835 is merged
- //fixture.RunConformance(t)
- fixture.RunBasic(t)
- fixture.RunExtended(t)
-
-}
diff --git a/solver/solver.go b/solver/solver.go
new file mode 100644
index 0000000..f8febd3
--- /dev/null
+++ b/solver/solver.go
@@ -0,0 +1,138 @@
+package solver
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ acme "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
+ v1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
+ "github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/util"
+ "github.com/nrdcg/desec"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+)
+
+// Configuration for the DeSEC DNS-01 challenge solver
+type DeSECDNSProviderSolverConfig struct {
+ // Reference to the kubernetes secret containing the API token for deSEC
+ APIKeySecretRef v1.SecretKeySelector `json:"apiKeySecretRef"`
+ // A global namespace (e.g APIKeySecretRefNamespace is not required, because ClusterIssuer provides the cert-manager namespace as default value for global issuers)
+}
+
+// A DNS-01 challenge solver for the DeSEC DNS Provider
+type DeSECDNSProviderSolver struct {
+ // Client to communicate with the kubernetes API
+ k8s *kubernetes.Clientset
+}
+
+// Returns the name of the DNS solver
+func (s *DeSECDNSProviderSolver) Name() string {
+ return "deSEC"
+}
+
+// Initializes a new client
+func (s *DeSECDNSProviderSolver) getClient(config *apiextensionsv1.JSON, namespace string) (*desec.Client, error) {
+ // Check if configuration is empty or was not parsed
+ if config == nil {
+ return nil, fmt.Errorf("missing configuration in issuer found; webhook configuration requires apiKeySecretRef containing deSEC API token")
+ }
+ // Initialize the configuration object and unmarshal json
+ solverConfig := DeSECDNSProviderSolverConfig{}
+ if err := json.Unmarshal(config.Raw, &solverConfig); err != nil {
+ return nil, fmt.Errorf("invalid configuration in issuer found; webhook configuration requires apiKeySecretRef containing deSEC API token")
+ }
+ // Check if the k8s client has been initialized
+ // This should never happen as cert-manager calls s.Initialize() which assigns the k8s client
+ if s.k8s == nil {
+ return nil, fmt.Errorf("k8s client has not been initialized by cert-manager; this should never happen")
+ }
+ // Read the secret from k8s
+ secret, err := s.k8s.CoreV1().Secrets(namespace).Get(context.Background(), solverConfig.APIKeySecretRef.Name, metav1.GetOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("k8s secret %s not found in namespace %s", solverConfig.APIKeySecretRef.Name, namespace)
+ }
+ token, ok := secret.Data[solverConfig.APIKeySecretRef.Key]
+ if !ok {
+ return nil, fmt.Errorf("k8s secret key %s not found in secret %s in namespace %s", solverConfig.APIKeySecretRef.Key, solverConfig.APIKeySecretRef.Name, namespace)
+ }
+ // Finally assign the client
+ client := desec.New(string(token), desec.NewDefaultClientOptions())
+
+ // Return the client (reuse if initialized)
+ return client, nil
+}
+
+// Present presents the TXT DNS entry after completion of the ACME DNS-01 challenge
+func (s *DeSECDNSProviderSolver) Present(req *acme.ChallengeRequest) error {
+ // Create or reuse the API client
+ apiClient, err := s.getClient(req.Config, req.ResourceNamespace)
+ if err != nil {
+ return err
+ }
+ zone := util.UnFqdn(req.ResolvedZone)
+ fqdn := util.UnFqdn(req.ResolvedFQDN)
+ // Cut the zone from the fqdn to retrieve the subdomain
+ subdomain := util.UnFqdn(strings.Replace(fqdn, zone, "", 0))
+ // Check if zone is managed in deSEC
+ domain, err := apiClient.Domains.Get(context.Background(), zone)
+ if err != nil {
+ return fmt.Errorf("domain %s could not be retrieved from deSEC API: %w", zone, err)
+ }
+ // Create the TXT record to be created
+ recordSet := desec.RRSet{
+ Domain: domain.Name,
+ SubName: subdomain,
+ Records: []string{fmt.Sprintf("\"%s\"", req.Key)},
+ Type: "TXT",
+ TTL: 3600,
+ }
+ // Create the TXT record
+ _, err = apiClient.Records.Create(context.Background(), recordSet)
+ if err != nil {
+ return fmt.Errorf("DNS record %s creation failed: %w", fqdn, err)
+ }
+ // Return no error
+ return nil
+}
+
+// Cleanup removes the TXT DNS entry after completion of the ACME DNS-01 challenge
+func (s *DeSECDNSProviderSolver) CleanUp(req *acme.ChallengeRequest) error {
+ // Create or reuse the API client
+ apiClient, err := s.getClient(req.Config, req.ResourceNamespace)
+ if err != nil {
+ return err
+ }
+ zone := util.UnFqdn(req.ResolvedZone)
+ fqdn := util.UnFqdn(req.ResolvedFQDN)
+ // Cut the zone from the fqdn to retrieve the subdomain
+ subdomain := util.UnFqdn(strings.Replace(fqdn, zone, "", 0))
+ // Check if zone is managed in deSEC
+ domain, err := apiClient.Domains.Get(context.Background(), zone)
+ if err != nil {
+ return fmt.Errorf("domain %s could not be retrieved from deSEC API: %w", zone, err)
+ }
+ // Delete the TXT record
+ err = apiClient.Records.Delete(context.Background(), domain.Name, subdomain, "TXT")
+ if err != nil {
+ return fmt.Errorf("DNS record %s deletion failed: %w", fqdn, err)
+ }
+ // Return no error
+ return nil
+}
+
+// Initializes the solver
+func (s *DeSECDNSProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
+ // Create the k8s client
+ k8s, err := kubernetes.NewForConfig(kubeClientConfig)
+ if err != nil {
+ return err
+ }
+ // Assign the k8s client to the solver
+ s.k8s = k8s
+ // Return no error
+ return nil
+}
diff --git a/solver/solver_test.go b/solver/solver_test.go
new file mode 100644
index 0000000..7434e5e
--- /dev/null
+++ b/solver/solver_test.go
@@ -0,0 +1 @@
+package solver_test
diff --git a/testdata/desec/config.json b/testdata/desec/config.json
new file mode 100644
index 0000000..ad9fcf4
--- /dev/null
+++ b/testdata/desec/config.json
@@ -0,0 +1,6 @@
+{
+ "apiKeySecretRef": {
+ "name": "desec-token",
+ "key": "token"
+ }
+}
\ No newline at end of file
diff --git a/testdata/desec/secret.yaml b/testdata/desec/secret.yaml
new file mode 100644
index 0000000..965eaaa
--- /dev/null
+++ b/testdata/desec/secret.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: desec-token
+data:
+ token: changeme
+type: Opaque
\ No newline at end of file
diff --git a/testdata/my-custom-solver/README.md b/testdata/my-custom-solver/README.md
deleted file mode 100644
index feb4cbd..0000000
--- a/testdata/my-custom-solver/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Solver testdata directory
-
-TODO
diff --git a/testdata/my-custom-solver/config.json b/testdata/my-custom-solver/config.json
deleted file mode 100644
index 0967ef4..0000000
--- a/testdata/my-custom-solver/config.json
+++ /dev/null
@@ -1 +0,0 @@
-{}