Cloudflare mTLS
Breeze optionally integrates with Cloudflare API Shield to issue mTLS client certificates to agents during enrollment. This provides zero-trust authentication where both the server and agent verify each other’s identity.
How It Works
- During enrollment, the API calls Cloudflare’s Client Certificates API to issue a certificate
- The agent receives the certificate and private key, stores them alongside its config
- The agent uses the certificate for all HTTPS and WebSocket connections
- Cloudflare’s WAF enforces that only requests with valid client certificates reach your origin
- Certificates auto-renew at 2/3 lifetime via the heartbeat cycle
Setup
-
Create a Cloudflare API token
Go to Cloudflare Dashboard → My Profile → API Tokens → Create Token:
- Permission: Zone → SSL and Certificates → Edit
- Zone Resources: your domain’s zone
-
Get your Zone ID
Cloudflare Dashboard → your domain → Overview → Zone ID (right sidebar).
-
Configure environment variables
Add to
.env.prod:Terminal window CLOUDFLARE_API_TOKEN=your-cf-api-tokenCLOUDFLARE_ZONE_ID=your-zone-id -
Run the database migration
Terminal window pnpm db:migrateThis adds mTLS columns to the
devicestable:mtlsCertSerialNumbermtlsCertExpiresAtmtlsCertIssuedAtmtlsCertCfIdquarantinedAt/quarantinedReason
-
Restart the API
Terminal window docker compose -f docker/docker-compose.prod.yml restart api -
Enroll new agents
New enrollments will automatically receive mTLS certificates. The enrollment response includes an
mtlsobject with the certificate and private key. -
Configure Cloudflare WAF rules
Once all agents have certificates, add a WAF rule to enforce mTLS:
Rule name: Require mTLS for Agent APIExpression: (http.request.uri.path contains "/api/v1/agents/" and not cf.tls_client_auth.cert_verified)Action: BlockExclude the enrollment and cert renewal endpoints:
Exception: http.request.uri.path eq "/api/v1/agents/enroll"or http.request.uri.path contains "/renew-cert"
Certificate Lifecycle
| Event | Trigger | Action |
|---|---|---|
| Issuance | Agent enrollment | API calls CF API, returns cert in enrollment response |
| Renewal | Heartbeat detects 2/3 lifetime reached | API signals renewCert: true, agent calls /renew-cert |
| Revocation | Admin action or quarantine | API calls CF API to revoke, device marked quarantined |
Quarantine
When a device is quarantined:
- Its mTLS certificate is revoked via Cloudflare API
- Device status changes to
quarantined - The device cannot communicate with the API
- Admin can approve or deny the device:
# List quarantined devicescurl -H "Authorization: Bearer $TOKEN" \ https://breeze.yourdomain.com/api/v1/agents/quarantined
# Approve a quarantined device (issues new cert)curl -X POST -H "Authorization: Bearer $TOKEN" \ https://breeze.yourdomain.com/api/v1/agents/:id/approve
# Deny (permanently revoke)curl -X POST -H "Authorization: Bearer $TOKEN" \ https://breeze.yourdomain.com/api/v1/agents/:id/denyOrganization Settings
Enable or configure mTLS per organization:
curl -X PATCH -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"mtls": {"enabled": true, "quarantinePolicy": "auto"}}' \ https://breeze.yourdomain.com/api/v1/org/:orgId/settings/mtlsQuarantine policies:
auto— Automatically quarantine devices with expired or invalid certificatesmanual— Only quarantine via admin actiondisabled— mTLS tracking without enforcement