# Let's Encrypt wildcard certificates with Gyro DNS: acme.sh, Certbot, Caddy, Traefik and Kubernetes

https://www.gyrohosting.com/en/knowledgebase/article/gyro-dns-lets-encrypt-wildcard-certificate

- **What you will learn**: how to get and renew Let's Encrypt certificates fully automatically with DNS validation (DNS-01), `*.example.com` wildcards included, when your domain's DNS is on Gyro DNS; and how to create a restricted **ACME key** that can only write validation records.
- **Time**: 10 minutes

## 1. Why DNS validation, and why a separate key?

Let's Encrypt checks that you control a domain before it issues a certificate. A **wildcard certificate** (`*.example.com`) can only be obtained with the **DNS-01** method: your certificate tool writes a temporary TXT record at `_acme-challenge.example.com`, Let's Encrypt reads it, and the record is removed afterwards. DNS validation has other advantages too: the server does not need port 80 open to the internet, and internal or not-yet-public servers can get certificates.

To write that record the tool needs access to your DNS. Putting a full API key on every server is risky, so Gyro DNS has an **ACME key** for exactly this job:

| An ACME key can | An ACME key cannot |
| --- | --- |
| Add and remove TXT values at names starting with `_acme-challenge` (TTL 60 s) | Read your zones, or change or delete any other record (A, MX, CNAME ...) |
| Work only in the zones you select (optional) | Reach your other services, invoices or account data |
| Be used only from IP addresses you list (optional) | Create new keys |

Even if a key leaks, the worst case is a validation record; delete the key in the panel. We recommend one key per server or cluster.

> **Note**
> 
> ACME keys do not work on the read-only zones copied from your hosting (cPanel) account; those domains use cPanel's own free SSL (AutoSSL). ACME keys are for the zones you add to Gyro DNS.

## 2. Create an ACME key

1. In the client area open **My Services → Gyro DNS** and go to the **Management** tab.
2. In the left menu choose **Advanced → ACME / Let's Encrypt**.
3. Enter a **Name** you will recognise (for example `web-01` or `k8s-prod`).
4. Select the **Zones** the key may use. If you select none, the key works in every zone of this service.
5. Optionally enter the IP address of the server that will request certificates in **Allowed IPs** (comma separated).
6. Click **Create key**. The key (`wck_...`) is shown only once: copy it and store it safely.

The same from the command line or Terraform:

```
gyro dns acme-key create <service-id> --name web-01 --zones example.com
gyro dns acme-key list <service-id>
```

```
resource "gyro_dns_acme_key" "web" {
  service_id = 12345
  name       = "web-01"
  zones      = ["example.com"]
}
```

To revoke a key click the bin icon in the table or run `gyro dns acme-key delete <service-id> <key-id>`; it stops working at once.

## 3. acme.sh

With [acme.sh](https://github.com/acmesh-official/acme.sh) installed, download the Gyro DNS hook and pass the key:

```
curl -fsSL -o ~/.acme.sh/dnsapi/dns_gyro.sh https://raw.githubusercontent.com/Gyro-Hosting/gyro-acme/main/acme.sh/dns_gyro.sh
export GYRO_Token="wck_..."
acme.sh --issue --dns dns_gyro -d example.com -d '*.example.com'
```

After adding the record the hook waits until both of our name servers publish it (usually under a minute). The key is saved in the acme.sh configuration, and renewals run with acme.sh's own scheduler. Use `acme.sh --install-cert` to copy the certificate to your web server.

## 4. Certbot

Install the plugin into the Python environment of Certbot:

```
pip install "certbot-dns-gyro @ git+https://github.com/Gyro-Hosting/gyro-acme@v0.1.0#subdirectory=certbot-dns-gyro"
```

Store the key in a file only root can read:

```
printf 'dns_gyro_token = wck_...\n' > /etc/letsencrypt/gyro.ini
chmod 600 /etc/letsencrypt/gyro.ini
certbot certonly --authenticator dns-gyro --dns-gyro-credentials /etc/letsencrypt/gyro.ini -d example.com -d '*.example.com'
```

The snap package of Certbot cannot load this plugin; use a pip-installed Certbot or acme.sh.

## 5. Caddy

Build Caddy with the Gyro DNS module and use `dns gyro` in your Caddyfile:

```
xcaddy build --with github.com/Gyro-Hosting/caddy-dns-gyro
```

```
*.example.com, example.com {
	tls {
		dns gyro {env.GYRO_TOKEN}
	}
	reverse_proxy 127.0.0.1:8080
}
```

Put your ACME key in the `GYRO_TOKEN` environment variable. Caddy obtains, renews and replaces the certificate before it expires.

## 6. Traefik and lego

Traefik runs the small `gyro-acme-exec` program through its `exec` DNS provider. Download it from the [libdns-gyro releases](https://github.com/Gyro-Hosting/libdns-gyro/releases) and mount it into the container:

```
# traefik.yml
certificatesResolvers:
  le:
    acme:
      email: you@example.com
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: exec
```

Environment of the Traefik container:

```
EXEC_PATH=/usr/local/bin/gyro-acme-exec
GYRO_TOKEN=wck_...
EXEC_PROPAGATION_TIMEOUT=300
EXEC_POLLING_INTERVAL=10
```

On the router set `tls.certresolver=le`, `tls.domains[0].main=example.com` and `tls.domains[0].sans=*.example.com`. The same program works with the lego CLI: `EXEC_PATH=./gyro-acme-exec GYRO_TOKEN=wck_... lego --dns exec -d example.com -d '*.example.com' --email you@example.com run`.

## 7. Kubernetes (cert-manager)

With cert-manager installed, add the Gyro DNS webhook with Helm, store the key as a Secret and define a ClusterIssuer:

```
helm install cert-manager-webhook-gyro oci://ghcr.io/gyro-hosting/charts/cert-manager-webhook-gyro --namespace cert-manager
kubectl -n cert-manager create secret generic gyro-acme --from-literal=token='wck_...'
```

```
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-gyro
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: you@example.com
    privateKeySecretRef:
      name: letsencrypt-gyro-account
    solvers:
      - dns01:
          webhook:
            groupName: acme.gyrohosting.com
            solverName: gyro
            config:
              tokenSecretRef:
                name: gyro-acme
                key: token
```

Then use `dnsNames: ["example.com", "*.example.com"]` and `issuerRef: {name: letsencrypt-gyro, kind: ClusterIssuer}` in a `Certificate`. The webhook can read Secrets only in the namespaces you allow in Helm and runs as a non-root user.

## 8. Any other tool: the API

If your tool is not listed, three simple calls are enough. The service and the zone are found from the record name; only the ACME key is needed:

```
API=https://www.gyrohosting.com/api/v1/client
# add
curl -X POST $API/cloud/dns/acme-challenge -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" -d '{"name":"_acme-challenge.example.com","value":"<VALUE>"}'
# ready? ("ready": true once both name servers publish it)
curl "$API/cloud/dns/acme-challenge?name=_acme-challenge.example.com&value=<VALUE>" -H "Authorization: Bearer $GYRO_TOKEN"
# remove
curl -X DELETE "$API/cloud/dns/acme-challenge?name=_acme-challenge.example.com&value=<VALUE>" -H "Authorization: Bearer $GYRO_TOKEN"
```

Adding keeps the other values at the same name, so `example.com` and `*.example.com` can be validated together. CLI equivalents: `gyro dns acme-challenge add|status|remove`. Full reference: [developer documentation](https://www.gyrohosting.com/developers/).

## 9. Frequently asked questions

**Which names should I request for a wildcard certificate?**

Request both `example.com` and `*.example.com`. A wildcard covers one level only: it is valid for `www.example.com` and `api.example.com`, not for `example.com` or `a.b.example.com`.

**How long does validation take?**

Both of our name servers usually publish the record in under a minute after it is written. The plugins wait for this themselves; the whole process mostly finishes in 1-2 minutes.

**What if my ACME key is stolen?**

It can only write `_acme-challenge` TXT records; it cannot touch your website or e-mail records or your account. Still, delete it in the panel and create a new one. A key limited to IP addresses cannot be used from anywhere else.

**I get "zone_not_allowed" or "zone_not_found".** The domain is not a live zone of this service (it is pending, in another service, or a hosting zone), or the key is limited to other zones. Check the key's zone list in the panel.

**Are certificates renewed before they expire?**

Yes. acme.sh, Certbot, Caddy, Traefik and cert-manager renew in the last weeks of validity automatically, with the same ACME key.

**Does it work with certificate authorities other than Let's Encrypt?**

Yes. DNS-01 is a standard; the same key works with any ACME CA such as ZeroSSL, Google Trust Services or your own ACME server.

> **Did you know?**
> 
> You can create ACME keys with Terraform and pass them straight into a Kubernetes Secret: the `token` of `gyro_dns_acme_key` is read only at creation and kept as a sensitive value in state.

## Next step

- Plugin source code and releases: [github.com/Gyro-Hosting/gyro-acme](https://github.com/Gyro-Hosting/gyro-acme)
- Plans: [Gyro DNS](https://www.gyrohosting.com/en/managed-dns)
