This is a webhook solver for OVH DNS. In short, if your domain has its DNS servers hosted with OVH, you can solve DNS challenges using Cert Manager and OVH Webhook for Cert Manager.
Homepage: https://github.com/aureq/cert-manager-webhook-ovh
This chart is in early development (pre-1.0). For this chart we use 0.major.minor semantic versioning, which means:
Before you install anything, 2 mains tasks need to be completed.
Obtaining API keys from your OVH account (in which your DNS zones are hosted) will allow this webhook to perform the necessary operations to help resolve Let’s Encrypt DNS01 challenges. In response to the DNS01 challenges, Let’s Encrypt will issue a valid TLS certiticate for your applications to use.
Create a new OVH API key for this webhook (This needs to be done once per OVH account)
GET /domain/zone/*PUT /domain/zone/*POST /domain/zone/*DELETE /domain/zone/*Take note of the ApplicationKey, ApplicationSecret and ConsumerKey and save them in a secure location.
In order to use OVH IAM, you need to generate a OAuth2 Client, create a policy and link the policy to the client.
The following instructions are based on the official documentation.
With the OVHcloud API:
Authentication link on the left handside to log-in using your OVH credentialsv1 and then select /me APIPOST /me/api/oauth2/client ↗️Create a new service account with the following request body and click on the Execute button.
{
"callbackUrls": [],
"description": "Service account for OVH cert-manager webhook",
"flow": "CLIENT_CREDENTIALS",
"name": "cert-manager"
}
ClientId and ClientSecret and save them in a secure location. Be carefull, you will not be able to retrieve the client secret later. You’ll need to delete and create a new service account.GET /me/api/oauth2/client/{clientId} ↗️ClientId to retrieve the details of the service account. Take note of identity.OR, with the ovhcloud CLI:
ovhcloud account api oauth2 client create --description Service account for OVH cert-manager webhook --flow CLIENT_CREDENTIALS --name cert-manager
You should obtain a response like this:
✅ OAuth2 client created successfully (client ID: xxxxxxxx, client secret: xxxxxxxx)
Take note of both ClientId and ClientSecret and save them in a secure location. Be carefull, you will not be able to retrieve the client secret later. You’ll need to delete and create a new service account.
Use the ClientId to retrieve the details of the service account:
ovhcloud account api oauth2 client get EU.590d00c34ddd55c3
You should obtain a response like this:
{
"callbackUrls": null,
"clientId": "xxxxxxxxxx",
"createdAt": "2025-11-26T14:30:04.492Z",
"description": "Service",
"flow": "CLIENT_CREDENTIALS",
"identity": "urn:v1:eu:identity:credential:xxxxxxxxxx/oauth2-xxxxxxxxxx",
"name": "cert-manager"
}
Take note of the value of the identity field.
Now, you can create the policy to grant permissions on your domain to your service account.
With the OVHcloud API:
v2 and then select /iam APIPOST /iam/policy ↗️Create a new IAM policy with the following request body. Adjust the urn to restrict the policy to one or more specifc domains and click Execute.
{
"description": "Allow cert-manager of create and manage DNS records",
"identities": [
"<--- 🔥 INSERT SERVICE ACCOUNT IDENTITY FROM STEP 8 --->"
],
"name": "cert-manager",
"permissions": {
"allow": [
{
"action": "dnsZone:apiovh:record/create"
},
{
"action": "dnsZone:apiovh:record/get"
},
{
"action": "dnsZone:apiovh:record/delete"
},
{
"action": "dnsZone:apiovh:status/get"
},
{
"action": "dnsZone:apiovh:refresh"
}
]
},
"resources": [
{
"urn": "urn:v1:eu:resource:dnsZone:*", // ⚠️ all domains
"urn": "urn:v1:eu:resource:dnsZone:example.net", // restrict the policy to "example.net"
"urn": "urn:v1:eu:resource:dnsZone:example2.net", // restrict the policy to "example2.net"
}
]
}
Add a new Helm repository
helm repo add cert-manager-webhook-ovh-charts https://aureq.github.io/cert-manager-webhook-ovh/
Refresh the repository information
helm repo update
Search for all available charts in this repository
helm search repo cert-manager-webhook-ovh-charts --versions
Or list the latest development/unstable versions
helm search repo cert-manager-webhook-ovh-charts --versions --devel
The configuration is done via values.yaml.
The documentation below is automatically generated from the values.yaml file.
| Key | Type | Default | Description |
|---|---|---|---|
| groupName | string | "acme.mycompany.example" |
The groupName here is used to identify your company or business unit that created this webhook. For example, this may be acme.mycompany.example. This 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. This group name should be unique, hence using your own company’s domain here is recommended. |
| certManager | object | {"namespace":"cert-manager","serviceAccountName":"cert-manager"} |
The values below should match your cert-manager deployment. If not, you will get permissions errors. |
| certManager.namespace | string | "cert-manager" |
namespace in which your cert-manager is deployed (default: cert-manager) |
| certManager.serviceAccountName | string | "cert-manager" |
cert-manager serverAccount name (default: cert-manager) |
| issuers | list | [{"acmeServerUrl":"https://acme-v02.api.letsencrypt.org/directory","cnameStrategy":"None","create":false,"disableAccountKeyGeneration":false,"email":"acme@example.net","externalAccountBinding":{"enabled":false,"keyID":"","keySecretRef":{"key":"","name":""}},"kind":"ClusterIssuer","name":"le-prod","namespace":"default","ovhAuthentication":{"applicationConsumerKey":"","applicationKey":"","applicationSecret":"","oauth2ClientID":"","oauth2ClientSecret":""},"ovhAuthenticationMethod":"application","ovhAuthenticationRef":{"applicationConsumerKeyRef":{"key":"applicationConsumerKey","name":""},"applicationKeyRef":{"key":"applicationKey","name":""},"applicationSecretRef":{"key":"applicationSecret","name":""},"oauth2ClientIDRef":{"key":"oauth2ClientID","name":""},"oauth2ClientSecretRef":{"key":"oauth2ClientSecret","name":""}},"ovhEndpointName":"ovh-eu","profile":"classic"}] |
One or more issuers to create |
| issuers[0] | object | {"acmeServerUrl":"https://acme-v02.api.letsencrypt.org/directory","cnameStrategy":"None","create":false,"disableAccountKeyGeneration":false,"email":"acme@example.net","externalAccountBinding":{"enabled":false,"keyID":"","keySecretRef":{"key":"","name":""}},"kind":"ClusterIssuer","name":"le-prod","namespace":"default","ovhAuthentication":{"applicationConsumerKey":"","applicationKey":"","applicationSecret":"","oauth2ClientID":"","oauth2ClientSecret":""},"ovhAuthenticationMethod":"application","ovhAuthenticationRef":{"applicationConsumerKeyRef":{"key":"applicationConsumerKey","name":""},"applicationKeyRef":{"key":"applicationKey","name":""},"applicationSecretRef":{"key":"applicationSecret","name":""},"oauth2ClientIDRef":{"key":"oauth2ClientID","name":""},"oauth2ClientSecretRef":{"key":"oauth2ClientSecret","name":""}},"ovhEndpointName":"ovh-eu","profile":"classic"} |
Name of this issuer |
| issuers[0].create | bool | false |
When true this issuer is created. |
| issuers[0].kind | string | "ClusterIssuer" |
The type of issuer to create. It can either be ClusterIssuer or Issuer. If Issuer is specified, then the namespace is also required. See https://cert-manager.io/docs/concepts/issuer/ for more information. |
| issuers[0].namespace | string | "default" |
If kind is Issuer, then indicate the namespace in which this issuer should be deployed into. |
| issuers[0].email | string | "acme@example.net" |
Email to use when registering your account with Let’s Encrypt. |
| issuers[0].disableAccountKeyGeneration | bool | false |
Enables or disables generating a new ACME account key. If true, the Issuer resource will not request a new account but will expect the account key to be supplied via an existing secret. If false, the cert-manager system will generate a new ACME account key for the Issuer. Defaults to false when unspecified. See https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEIssuer |
| issuers[0].profile | string | "classic" |
If the ACME server supports profiles, you can specify the profile name here. For more details, see https://cert-manager.io/docs/configuration/acme/#acme-certificate-profiles Possible profiles are: classic, tlsserver, shortlived. |
| issuers[0].cnameStrategy | string | "None" |
Follow CNAME records or not. 2 options: - None (default): Don’t follow CNAME records - Follow: Follow CNAME records See https://cert-manager.io/docs/configuration/acme/dns01/#delegated-domains-for-dns01 for more information. |
| issuers[0].externalAccountBinding | object | {"enabled":false,"keyID":"","keySecretRef":{"key":"","name":""}} |
Define the EAB (external account binding) key using secretRef. This is optional and only required if you want to use it. See https://cert-manager.io/docs/configuration/acme/#external-account-bindings for more information. |
| issuers[0].externalAccountBinding.enabled | bool | false |
When enabled is true, the external account binding configuration is applied. |
| issuers[0].externalAccountBinding.keyID | string | "" |
The key ID or account ID of which your external account binding is indexed by the external account manager |
| issuers[0].externalAccountBinding.keySecretRef | object | {"key":"","name":""} |
The name and key of a secret containing a base 64 encoded URL string of your external account symmetric MAC key. |
| issuers[0].externalAccountBinding.keySecretRef.name | string | "" |
Name of the Kubernetes secret containing the EAB HMAC key. |
| issuers[0].externalAccountBinding.keySecretRef.key | string | "" |
The key name in the secret above that holds the actual EAB HMAC key value. |
| issuers[0].ovhEndpointName | string | "ovh-eu" |
The endpoint name of the OVH API. It must be one of the following: ovh-eu, ovh-ca, kimsufi-eu, kimsufi-ca, soyoustart-eu, soyoustart-ca, runabove-ca. See https://docs.certifytheweb.com/docs/dns/providers/ovh/ for more information. |
| issuers[0].ovhAuthenticationMethod | string | "application" |
Define how the webhook should authenticate with the OVH API. It can be either application or oauth2. When ovhAuthenticationMethod is application, then use: - ovhAuthentication.applicationKey, ovhAuthentication.applicationSecret, and ovhAuthentication.applicationConsumerKey or - ovhAuthenticationRef.applicationKeyRef, ovhAuthenticationRef.applicationSecretRef and ovhAuthenticationRef.applicationConsumerKeyRef. When ovhAuthenticationMethod is oauth2, then use: - ovhAuthentication.oauth2ClientID and ovhAuthentication.oauth2ClientSecret or - ovhAuthenticationRef.oauth2ClientIDRef and ovhAuthenticationRef.oauth2ClientSecretRef. |
| issuers[0].ovhAuthentication | object | {"applicationConsumerKey":"","applicationKey":"","applicationSecret":"","oauth2ClientID":"","oauth2ClientSecret":""} |
Define your credentials below and the chart will create the necessary secret for you. If ovhAuthenticationMethod is application, then provide: - applicationKey, applicationSecret, and applicationConsumerKey If ovhAuthenticationMethod is oauth2, then provide: - oauth2ClientID and oauth2ClientSecret |
| issuers[0].ovhAuthentication.oauth2ClientID | string | "" |
The OVH OAuth 2 client ID. Leave empty if you are using an existing secret. |
| issuers[0].ovhAuthentication.oauth2ClientSecret | string | "" |
The OVH OAuth 2 client secret. Leave empty if you are using an existing secret. |
| issuers[0].ovhAuthentication.applicationKey | string | "" |
The OVH application key. Leave empty if you are using an existing secret. |
| issuers[0].ovhAuthentication.applicationSecret | string | "" |
The OVH application secret. Leave empty if you are using an existing secret. |
| issuers[0].ovhAuthentication.applicationConsumerKey | string | "" |
Your OVH consumer key. Leave empty if you are using an existing secret. |
| issuers[0].ovhAuthenticationRef | object | {"applicationConsumerKeyRef":{"key":"applicationConsumerKey","name":""},"applicationKeyRef":{"key":"applicationKey","name":""},"applicationSecretRef":{"key":"applicationSecret","name":""},"oauth2ClientIDRef":{"key":"oauth2ClientID","name":""},"oauth2ClientSecretRef":{"key":"oauth2ClientSecret","name":""}} |
Define the details of a secret already containing the OVH credentials. If ovhAuthenticationMethod is application, then provide: - applicationKeyRef, applicationSecretRef and applicationConsumerKeyRef. If ovhAuthenticationMethod is oauth2, then provide: - oauth2ClientIDRef and oauth2ClientSecretRef. |
| issuers[0].ovhAuthenticationRef.oauth2ClientIDRef | object | {"key":"oauth2ClientID","name":""} |
The secret reference to an existing OVH OAuth 2 client ID. |
| issuers[0].ovhAuthenticationRef.oauth2ClientIDRef.name | string | "" |
Name of the Kubernetes secret containing the OVH OAuth 2 client ID. This secret must be in the same namespace as this chart is deployed into. See https://github.com/aureq/cert-manager-webhook-ovh/issues/82 for more information. |
| issuers[0].ovhAuthenticationRef.oauth2ClientIDRef.key | string | "oauth2ClientID" |
The key name in the secret above that holds the actual OVH OAuth 2 client ID value |
| issuers[0].ovhAuthenticationRef.oauth2ClientSecretRef | object | {"key":"oauth2ClientSecret","name":""} |
The secret reference to an existing OVH Auth 2 client secret. |
| issuers[0].ovhAuthenticationRef.oauth2ClientSecretRef.name | string | "" |
Name of the Kubernetes secret containing the OVH Auth 2 client secret This secret must be in the same namespace as this chart is deployed into. See https://github.com/aureq/cert-manager-webhook-ovh/issues/82 for more information. |
| issuers[0].ovhAuthenticationRef.oauth2ClientSecretRef.key | string | "oauth2ClientSecret" |
The key name in the secret above that holds the actual OVH Auth 2 client secret value |
| issuers[0].ovhAuthenticationRef.applicationKeyRef | object | {"key":"applicationKey","name":""} |
The secret reference to an existing OVH application key. |
| issuers[0].ovhAuthenticationRef.applicationKeyRef.name | string | "" |
Name of the Kubernetes secret containing the OVH Application Key This secret must be in the same namespace as this chart is deployed into. See https://github.com/aureq/cert-manager-webhook-ovh/issues/82 for more information. |
| issuers[0].ovhAuthenticationRef.applicationKeyRef.key | string | "applicationKey" |
The key name in the secret above that holds the actual OVH application key value |
| issuers[0].ovhAuthenticationRef.applicationSecretRef | object | {"key":"applicationSecret","name":""} |
The secret reference to an existing OVH application secret. |
| issuers[0].ovhAuthenticationRef.applicationSecretRef.name | string | "" |
Name of the Kubernetes secret containing the OVH Application Secret This secret must be in the same namespace as this chart is deployed into. See https://github.com/aureq/cert-manager-webhook-ovh/issues/82 for more information. |
| issuers[0].ovhAuthenticationRef.applicationSecretRef.key | string | "applicationSecret" |
The key name in the secret above that holds the actual OVH application secret value |
| issuers[0].ovhAuthenticationRef.applicationConsumerKeyRef | object | {"key":"applicationConsumerKey","name":""} |
The secret reference to an existing OVH consumer key |
| issuers[0].ovhAuthenticationRef.applicationConsumerKeyRef.name | string | "" |
Name of the Kubernetes secret containing the OVH consumer Key This secret must be in the same namespace as this chart is deployed into. See https://github.com/aureq/cert-manager-webhook-ovh/issues/82 for more information. |
| issuers[0].ovhAuthenticationRef.applicationConsumerKeyRef.key | string | "applicationConsumerKey" |
The key name in the secret above that holds the actual OVH consumer key value |
| rbac | object | {"roleType":"Role"} |
RBAC configuration for the webhook deployment |
| rbac.roleType | string | "Role" |
If the secret of the issuer is in a different namespace, change Role (default) to ClusterRole so the webhook is able to access this secret. |
| image.registry | string | "ghcr.io" |
The registry to use. |
| image.repository | string | "aureq/cert-manager-webhook-ovh" |
The repository location on the registry. |
| image.tag | string | "" |
The tag to use from the registry. It can either be a version number like 1.0.0 or a digest like sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789. |
| image.pullPolicy | string | "IfNotPresent" |
The image pull policy as defined in the Kubernetes documentation. |
| image.pullSecrets | list | [] |
The image pull secrets to use if your registry requires it. |
| pod.annotations | object | {} |
Extra annotations for the Pod spec. See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ for more information. |
| pod.podLabels | object | {} |
Labels to add to the Pod. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ |
| pod.podAnnotations | object | {} |
Annotations to add to the Pod. See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ |
| pod.replicas | int | 1 |
Number of replicas in this deployment. |
| pod.environment | object | {} |
Use this option to add environment variables relevant to your deployment. These fields will be passed on to the container when Chart is deployed. |
| pod.securityContext | object | {"container":{"allowPrivilegeEscalation":false,"privileged":false,"readOnlyRootFilesystem":true},"pod":{"runAsGroup":1000,"runAsUser":1000}} |
Define the security context for both the Pod and the Container. See the Kubernetes API reference and the Kubernetes documentation for more information. |
| pod.securityContext.pod | object | {"runAsGroup":1000,"runAsUser":1000} |
Pod Security Context. |
| pod.securityContext.container | object | {"allowPrivilegeEscalation":false,"privileged":false,"readOnlyRootFilesystem":true} |
Container Security Context. |
| pod.port | int | 8443 |
The port used by the webhook to receive incoming traffic from the service. It may be useful to change it if the hostNetwork mode is activated to use an available port. |
| pod.hostNetwork | bool | false |
Specifies if the webhook should be started in hostNetwork mode. Required for use in some managed Kubernetes clusters (such as AWS EKS) with custom CNI (such as Calico), because control-plane managed by AWS cannot communicate with pods’ IP CIDR and admission webhooks are not working. |
| pod.priorityClassName | string | "" |
The optional priority class to be used for the cert-manager-webhook-ovh pod. |
| pod.resources | object | {"limits":{},"requests":{}} |
We usually recommend not to specify default resources and to leave this as a conscious choice for the user. This also increases chances charts run on environments with little resources, such as Minikube. If you do want to specify resources, adjust them as necessary as shown below. |
| pod.resources.requests | object | {} |
Resource Requests. |
| pod.resources.limits | object | {} |
Resource Limits. |
| pod.tolerations | list | [] |
Tolerations for the pod. See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for more information. |
| pod.selectors.nodeSelector | object | {} |
Node selector. See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ for more information. |
| pod.selectors.affinity | object | {} |
Affinity selectors. See https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/ for more information. |
| nameOverride | string | "" |
Override the name of the deployed resources |
| fullnameOverride | string | "" |
Override the full name of the deployed resources |
| service | object | {"annotations":{},"port":443,"type":"ClusterIP"} |
Service configuration for the webhook deployment. |
| service.type | string | "ClusterIP" |
The service type to use. |
| service.port | int | 443 |
The port used by the service. |
| service.annotations | object | {} |
Extra annotations for the service. See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ for more information. |
| extraObjects | list | [] |
Create dynamic manifests via values. For example: extraObjects: - \| apiVersion: v1 kind: ConfigMap metadata: name: '-extra-configmap' |
Issuers, and ClusterIssuers, are Kubernetes resources that represent certificate authorities (CAs) that are able to generate signed certificates by honoring certificate signing requests.
An Issuer is a namespaced resource, and it is not possible to issue certificates from an Issuer in a different namespace. This means you will need to create an Issuer in each namespace you wish to obtain Certificates in.
If you want to create a single Issuer that can be consumed in multiple namespaces, you should consider creating a ClusterIssuer resource. This is almost identical to the Issuer resource, however is non-namespaced so it can be used to issue Certificates across all namespaces.
If you are using cert-manager with an ingress controller, using a ClusterIssuer is recommended.
See cert-manager documentation for more details.
It is usually safe to provide your OVH API keys as part of your values.yaml file or on the command line. However, it may be needed to separate the domain of responsibility between Ops (in charge of deploying the chart) and Security (in charge of obtaining and deploying the OVH API keys).
When providing your OVH API keys directly, this chart stores the provided OVH API keys in a secret (format shown below) and then leverages secret references. The values of applicationKey, applicationSecret and applicationConsumerKey are base64 encoded.
If you decide to use secret references, simply indicate which secret name to use, and which key name in the secret holds the correct value.
If you are using a ClusterIssuer, then the secret should be stored in the same namespace as this webhook (usually cert-manager). And when using an Issuer, the secret should be stored in the same namespace as the issuer itself.
Example of a secret generated by this Helm Chart.
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: ovh-credentials
namespace: cert-manager
data:
applicationKey: YW5BcHBsaWNhdGlvbktleQ== # anApplicationKey
applicationSecret: YW5BcHBsaWNhdGlvblNlY3JldA== # anApplicationSecret
applicationConsumerKey: YUNvbnN1bWVyS2V5 # aConsumerKey
If your Kubernetes cluster requires a proxy to access the internet, you have the ability to set environment variables to help the webhook. This is done in values.yaml.
# Use this field to add environment variables relevant to this webhook.
# These fields will be passed on to the container when Chart is deployed.
environment:
# Use these variables to configure the HTTP_PROXY environment variables
HTTP_PROXY: "http://proxy:8080"
HTTPS_PROXY: "http://proxy:8080"
NO_PROXY: "10.0.0.0/8,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,*.svc,*.cluster.local,*.svc.cluster.local,169.254.169.254,127.0.0.1,localhost,localhost.localdomain"
Please note, using Split Horizon DNS is unsupported. Use at your own risks.
If you plan to use cert-manager to generate certificates for domains served via split DNS you need to ensure that your DNS server returns correct SOA record for your domain. Otherwise the certificate generation will fail with following error message:
Error presenting challenge: OVH API call failed: GET /domain/zone/com/status - HTTP Error 404: “This service does not exist”
In order to verify the record:
dig example.com soa
Example correct response:
; <<>> DiG 9.18.1-1ubuntu1.3-Ubuntu <<>> example.com soa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57237
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;example.com. IN SOA
;; ANSWER SECTION:
example.com. 86400 IN SOA dns.ovh.net. tech.ovh.net. 2023020402 86400 3600 3600000 600
;; Query time: 52 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Sat Feb 04 15:13:03 CET 2023
;; MSG SIZE rcvd: 88
Example incorrect response, notice the missing answer section:
; <<>> DiG 9.18.1-1ubuntu1.3-Ubuntu <<>> ovh.com soa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57237
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;example.com. IN SOA
;; Query time: 52 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Sat Feb 04 15:13:03 CET 2023
;; MSG SIZE rcvd: 88
To install the cert-manager-webhook-ovh chart:
helm upgrade --install --namespace cert-manager -f values.yaml cm-webhook-ovh cert-manager-webhook-ovh-charts/cert-manager-webhook-ovh
To uninstall the chart:
helm uninstall --namespace cert-manager cm-webhook-ovh
| Name | Url | |
|---|---|---|
| Aurélien Requiem | https://github.com/aureq |