Platform Services

Public ACME Runbook

Cloudflare DNS-01 setup and Let's Encrypt issuance for OPNsense, Proxmox, PBS, and planned Kubernetes workloads.

Public certificate issuance uses one Cloudflare API token scoped to aorxi.io. OPNsense, acme.sh, and cert-manager each complete DNS-01 challenges without an inbound HTTP challenge path.

Cloudflare API Token

Create a custom token under Cloudflare Dashboard → My Profile → API Tokens.

PermissionScope
Zone → DNS → Editaorxi.io only
Zone → Zone → Readaorxi.io

Scope the Token to aorxi.io Only

Never grant DNS edit permission across all zones. Record the token when it is displayed; Cloudflare shows its value only once.

The examples below refer to the value as <CF_API_TOKEN>.

OPNsense

Install os-acme-client from System → Firmware → Plugins.

Register the ACME Account

Under Services → ACME Client → Accounts, create:

FieldValue
Nameletsencrypt
E-mail<your-email>
ACME URLhttps://acme-v02.api.letsencrypt.org/directory

Save and register the account.

Configure DNS-01

Under Services → ACME Client → Challenge Types, create:

FieldValue
Namecloudflare-dns01
Challenge TypeDNS-01
DNS ServiceCloudflare
API Token<CF_API_TOKEN>

Issue and Bind Certificates

Create one certificate entry per firewall:

CertificateAlt nameHost
sa-fw-01sa-fw-01.core.aorxi.ioSite A OPNsense WebGUI
sb-fw-01sb-fw-01.core.aorxi.ioSite B OPNsense WebGUI

Use ec-256, the letsencrypt account, cloudflare-dns01, and automatic renewal. After issuance, select the certificate under System → Settings → Administration → SSL Certificate.

The plugin renews through an OPNsense cron job. Verify renewal activity under Services → ACME Client → Log before the certificate reaches 30 days remaining.

Proxmox and PBS

Run the following on each Proxmox or Proxmox Backup Server (PBS) node.

Install and Configure acme.sh

curl https://get.acme.sh | sh -s email=<your-email>
source ~/.bashrc

export CF_Token="<CF_API_TOKEN>"
export CF_Account_ID="<CF_ACCOUNT_ID>"

echo 'export CF_Token="<CF_API_TOKEN>"' >> ~/.acme.sh/account.conf
echo 'export CF_Account_ID="<CF_ACCOUNT_ID>"' >> ~/.acme.sh/account.conf

The Cloudflare account ID appears in the dashboard URL.

Issue a Node Certificate

acme.sh --issue \
  --dns dns_cf \
  -d sa-stor-01.core.aorxi.io \
  --keylength ec-256 \
  --server letsencrypt

Substitute the target node hostname.

Install into Proxmox

acme.sh --install-cert \
  -d sa-stor-01.core.aorxi.io \
  --cert-file /etc/pve/local/pve-ssl.pem \
  --key-file /etc/pve/local/pve-ssl.key \
  --reloadcmd "systemctl restart pveproxy"

Install into PBS

acme.sh --install-cert \
  -d sa-pbs-01.core.aorxi.io \
  --cert-file /etc/proxmox-backup/proxy.pem \
  --key-file /etc/proxmox-backup/proxy.key \
  --reloadcmd "systemctl restart proxmox-backup-proxy"

acme.sh installs its renewal cron job. Verify it with:

crontab -l | grep acme
# Expected: 0 0 * * * /root/.acme.sh/acme.sh --cron --home /root/.acme.sh

Kubernetes

Tentative: Clusters Are Not Deployed

cert-manager and ClusterIssuer resources are planned for the Kubernetes phase. The manifests below describe the intended configuration, not observed state.

Install cert-manager

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

Store the Cloudflare Token

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
  namespace: cert-manager
type: Opaque
stringData:
  api-token: "<CF_API_TOKEN>"

Create the ClusterIssuer

Deploy one in each site-local cluster:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-cloudflare
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <your-email>
    privateKeySecretRef:
      name: letsencrypt-cloudflare-key
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token

Request Wildcard Certificates

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: core-wildcard
  namespace: cert-manager
spec:
  secretName: core-wildcard-tls
  issuerRef:
    name: letsencrypt-cloudflare
    kind: ClusterIssuer
  dnsNames:
  - "*.core.aorxi.io"
  - "core.aorxi.io"
  renewBefore: 360h
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: aorxi-wildcard
  namespace: cert-manager
spec:
  secretName: aorxi-wildcard-tls
  issuerRef:
    name: letsencrypt-cloudflare
    kind: ClusterIssuer
  dnsNames:
  - "*.aorxi.io"
  - "aorxi.io"
  renewBefore: 360h

Reference a shared wildcard Secret from an Ingress:

spec:
  tls:
  - hosts:
    - myapp.core.aorxi.io
    secretName: core-wildcard-tls

Alternatively, annotate an Ingress for per-service issuance:

metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-cloudflare
spec:
  tls:
  - hosts:
    - myapp.core.aorxi.io
    secretName: myapp-tls

Let's Encrypt Rate Limits

The source design records 50 certificates per domain per week and five duplicate certificates per week. Prefer a shared wildcard over repeated per-namespace issuance; validate current limits before deployment.

Operations

Rotate the Cloudflare Token

Update the token in the OPNsense challenge configuration, every node's ~/.acme.sh/account.conf, and the cloudflare-api-token Secret in each cluster. Force a cert-manager renewal when required:

cmctl renew <cert-name> -n cert-manager

Test with Staging

Validate DNS-01 Before Production Issuance

Use Let's Encrypt staging to prove propagation and authentication without consuming the production issuance allowance. Staging certificates are not browser-trusted.

  • acme.sh: add --server letsencrypt_test.
  • cert-manager: use https://acme-staging-v02.api.letsencrypt.org/directory.

Monitor Expiry

The source design calls for Prometheus blackbox_exporter TLS probes with alerts at 30 days remaining. The cert-manager Grafana dashboard covers Kubernetes-managed certificates.