---
title: "ACME"
description: "Issue certificates from a private CA with cert-manager, certbot or any ACME client, using External Account Binding."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.ezghcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ACME

Every active subordinate CA has a private ACME (RFC 8555) directory:

```text
https://pki.ezghcloud.com/acme/<CA ID>/directory
```

ACME clients register with an **ACME binding**: an External Account Binding key ID (the binding's
`eab_…` slug) and HMAC key. A binding orders certificates with its owner's access, so its owner
needs `certificates.certificateAuthorities.issue` on the CA for as long as it's used. Each order
and finalization checks that again: if the owner loses the permission, leaves the organization, or
the binding is deleted, its clients stop getting certificates.

There are no challenges. EZGH can't reach your private names to validate them, so authorizations
are valid from the start: control comes from the binding's key, its allowed names, and the CA's
name constraints. Keep the HMAC key as secret as a private key.

## Create a binding

You need `certificates.acmeBindings.create` and `certificates.certificateAuthorities.issue` on the
CA. A binding has a profile (`tlsServer`, `tlsClient` or `tlsServerClient`), a longest validity (at
most the CA's longest certificate), and optionally the names it may order: exact DNS names or a
leading `*.` label, within the CA's name constraints.

```sh
ezgh private-ca acme-binding create corp-issuing --name cert-manager \
  --allowed-name '*.svc.corp.example' --max-validity-days 30
```

The directory URL, key ID and HMAC key are shown once. In the console, use **Create ACME binding**
on the CA's **ACME** tab; its last screen also has the cert-manager and certbot setups below.

Deleting a binding stops every ACME account registered with it.

## cert-manager

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: corp-issuing-eab
  namespace: cert-manager
stringData:
  secret: <HMAC key>
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: corp-issuing
spec:
  acme:
    server: https://pki.ezghcloud.com/acme/<CA ID>/directory
    privateKeySecretRef:
      name: corp-issuing-account
    externalAccountBinding:
      keyID: <key ID>
      keySecretRef:
        name: corp-issuing-eab
        key: secret
```

Certificates then name the issuer as usual (`issuerRef: {kind: ClusterIssuer, name: corp-issuing}`).
Pods that verify them need the root certificate in their trust bundle.

## certbot

```sh
certbot certonly --server https://pki.ezghcloud.com/acme/<CA ID>/directory \
  --eab-kid <key ID> --eab-hmac-key <HMAC key> \
  --manual --preferred-challenges dns --manual-auth-hook true \
  -d app.corp.example --agree-tos -m you@corp.example
```

The no-op `--manual-auth-hook` is never called, since there are no challenges; `certbot renew`
works the same way.

## Limits

Each binding may send 20 ACME requests per second, with bursts of 100. Account key rollover
(`keyChange`) is supported; ACME Renewal Information isn't.

Source: https://docs.ezghcloud.com/private-ca/acme/index.mdx
