diff --git a/Dockerfile b/Dockerfile
index d775442..892c939 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.25.7 AS build
+FROM golang:1.26.0-alpine AS build
WORKDIR /workspace
diff --git a/README.md b/README.md
index 09c4d27..6dbc6b6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,157 @@
# Cert Manager DeSEC Webhook
+
+
+
+
+# Independently maintained ACME webhook for desec.io DNS API
+
+This solver can be used with [desec.io](https://desec.io) DNS API. The documentation of the API can be found [here](https://desec.readthedocs.io/en/latest/)
+
+## Requirements
+- [go](https://golang.org) >= 1.26.0
+- [helm](https://helm.sh/) >= v3.0.0
+- [kubernetes](https://kubernetes.io/) >= 1.25.0
+- [cert-manager](https://cert-manager.io/) >= 1.19.0
+
+## Installation
+
+### Using helm from local checkout
+```bash
+helm install \
+ -n cert-manager \
+ desec-webhook \
+ charts/cert-manager-desec-webhook
+```
+
+### Using public helm chart
+```bash
+helm install \
+ -n cert-manager \
+ --version \
+ desec-webhook \
+ oci://ghcr.io/pr0ton11/charts/cert-manager-desec-webhook
+```
+
+## Uninstallation
+
+## Creating an issuer
+
+Create a secret containing the credentials
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: desec-io-token
+ namespace: cert-manager
+type: Opaque
+data:
+ token: your-key-base64-encoded
+```
+
+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.
+Create a 'ClusterIssuer' or 'Issuer' resource as the following:
+
+```yaml
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+ name: letsencrypt-staging
+spec:
+ acme:
+ server: https://acme-staging-v02.api.letsencrypt.org/directory
+ email: mail@example.com
+
+ privateKeySecretRef:
+ name: letsencrypt-staging
+
+ solvers:
+ - dns01:
+ webhook:
+ config:
+ apiKeySecretRef:
+ key: token
+ name: desec-io-token
+ groupName: acme.pr0ton11.github.com
+ solverName: deSEC
+```
+
+## Create a manual certificate
+
+```yaml
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: example-cert
+ namespace: cert-manager
+spec:
+ commonName: example.com
+ dnsNames:
+ - example.com
+ issuerRef:
+ name: letsencrypt-staging
+ kind: ClusterIssuer
+ secretName: example-cert
+```
+
+## Using cert-manager with traefik ingress
+```yaml
+
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: bitwarden
+ namespace: utils
+ labels:
+ app: bitwarden
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt-staging
+ kubernetes.io/ingress.class: traefik
+ traefik.ingress.kubernetes.io/rewrite-target: /$1
+ traefik.ingress.kubernetes.io/router.entrypoints: websecure
+ traefik.ingress.kubernetes.io/router.tls: 'true'
+spec:
+ tls:
+ - hosts:
+ - bitwarden.acme.example.com
+ secretName: bitwarden-crt
+ rules:
+ - host: bitwarden.acme.example.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: bitwarden
+ port:
+ number: 80
+
+```
+
+### 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.
+
+Provide a secret.yaml in testdata/desec
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: desec-token
+data:
+ token: your-key-base64-encoded
+type: Opaque
+```
+
+Define a **TEST_ZONE_NAME** matching to your authentication credentials.
+
+```bash
+$ TEST_ZONE_NAME=example.com. make test
+```
diff --git a/go.mod b/go.mod
index 5053bbd..a834a04 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/proton11/cert-manager-desec-webhook
-go 1.25.7
+go 1.26.0
require (
github.com/cert-manager/cert-manager v1.19.3
@@ -51,13 +51,13 @@ require (
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.8 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // 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/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
- github.com/miekg/dns v1.1.68 // indirect
+ github.com/miekg/dns v1.1.72 // 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
@@ -88,7 +88,7 @@ require (
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.48.0 // indirect
- golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect
+ golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // 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
@@ -99,8 +99,8 @@ require (
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-20260209200024-4cfbd4190f57 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // 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
diff --git a/go.sum b/go.sum
index 625a161..334954e 100644
--- a/go.sum
+++ b/go.sum
@@ -114,8 +114,8 @@ 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.8 h1:NpbJl/eVbvrGE0MJ6X16X9SAifesl6Fwxg/YmCvubRI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8/go.mod h1:mi7YA+gCzVem12exXy46ZespvGtX/lZmD/RLnQhVW7U=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
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=
@@ -142,8 +142,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
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/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
+github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
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=
@@ -250,8 +250,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
+golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/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.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
@@ -296,10 +296,10 @@ 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-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/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s=
+google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
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=
diff --git a/main_test.go b/main_test.go
index 06ab7d0..9651134 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1 +1,40 @@
package main
+
+import (
+ "os"
+ "testing"
+
+ acmetest "github.com/cert-manager/cert-manager/test/acme"
+
+ "github.com/proton11/cert-manager-desec-webhook/solver"
+)
+
+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"),
+ //)
+ desecSolver := &solver.DeSECDNSProviderSolver{}
+ zoneName := os.Getenv("TEST_ZONE_NAME")
+ if zoneName == "" {
+ t.Skip("TEST_ZONE_NAME not set")
+ }
+ fixture := acmetest.NewFixture(desecSolver,
+ acmetest.SetResolvedZone(zoneName),
+ acmetest.SetManifestPath("testdata/desec"),
+ 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_test.go b/solver/solver_test.go
deleted file mode 100644
index 7434e5e..0000000
--- a/solver/solver_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package solver_test