Tenant TLS and domain automation
Direct answer
A tenant domain is not complete when a DNS record exists. The platform must prove hostname ownership, prevent duplicate claims, create the correct edge record, bind the hostname to exactly one Kubernetes route, issue a matching certificate, verify readiness, and remove access safely during suspension or deletion. OpenTenant automates that lifecycle with Cloudflare, Gateway API, and cert-manager while keeping every step tied to the tenant record.
Wildcard DNS can simplify routing for platform-owned subdomains. It must not be used as authorization for customer-owned domains.
Model domain ownership before provisioning
OpenTenant treats a hostname as a unique resource with an owner and a state machine:
requested → verification pending → verified → provisioning
→ certificate pending → route ready → active
→ suspended → released
A database-level uniqueness rule should prevent two tenants from claiming the same normalized hostname.
Normalize case and trailing dots, reject public-suffix claims, and distinguish a platform subdomain such as
acme.example.com from a customer domain such as app.customer.com.
For customer-owned domains, require a DNS challenge that contains a random, tenant-bound value. Recheck ownership before activation and periodically where the threat model requires it. A CNAME pointing at the platform proves routing intent only when the expected verification process defines it that way.
Cloudflare DNS: proxied versus DNS-only
Cloudflare documents two relevant modes for A, AAAA, and CNAME records. A proxied record returns Cloudflare anycast addresses and sends HTTP traffic through Cloudflare, enabling edge controls and hiding the origin address. A DNS-only record returns the actual origin and does not receive Cloudflare HTTP protection or analytics.
Cloudflare notes that verification, email, and some non-HTTP records should remain DNS-only. OpenTenant therefore compiles record type and proxy status from the domain purpose instead of enabling the proxy indiscriminately.
For platform subdomains, a wildcard record can reduce per-tenant DNS operations. The application layer still needs an explicit tenant hostname registry. For custom domains, OpenTenant creates or validates the required record without assuming control of the customer's entire zone.
Gateway API binds hostnames to services
Gateway API separates infrastructure-owned Gateways from application-owned Routes. An
HTTPRoute names its hostnames and attaches to a compatible Gateway listener.
Gateway API hostname rules use hostname intersection between
listeners and routes. A wildcard listener such as *.example.com can accept matching tenant
routes, while a specific hostname gets the more specific match. For TLS termination, the intersected
hostname must appear on the certificate and the client's SNI must match.
A tenant route can be rendered as:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: tenant-acme
namespace: tenant-acme
spec:
parentRefs:
- name: public-gateway
namespace: gateway-system
hostnames:
- acme.example.com
rules:
- backendRefs:
- name: web
port: 8080
Cross-namespace attachment requires deliberate Gateway allowedRoutes behavior and may require
a ReferenceGrant for some references. OpenTenant generates route ownership from trusted tenant
identity and does not allow an arbitrary values file to attach itself to another tenant's service.
cert-manager turns listeners into certificates
cert-manager's Gateway integration can create Certificate resources from annotated Gateway listeners. The listener must include a hostname, use TLS termination, and reference a Secret in the same namespace as the Gateway for the documented automatic flow.
That namespace rule affects architecture. A central Gateway may keep certificates in the gateway namespace, while tenant HTTPRoutes live in tenant namespaces. The platform should choose that topology explicitly rather than copying an Ingress pattern into Gateway API.
Gateway API also warns integrations not to infer wildcard certificate issuance merely from a wildcard hostname. Wildcard certificates should be deliberate Certificate resources, normally using DNS-01 validation, with tightly controlled access to the resulting private key.
Platform subdomains versus custom domains
Platform-owned subdomains
For tenant.platform.example, OpenTenant can validate the tenant slug, reserve the hostname,
attach an HTTPRoute, and use a platform-managed wildcard or specific certificate strategy. Wildcard
certificates reduce issuance volume but increase the impact of key compromise.
Customer-owned domains
For app.customer.com, OpenTenant records a verification challenge, waits for DNS proof,
provisions the Cloudflare/SaaS or external DNS binding supported by the deployment profile, and requests a
certificate containing that exact hostname. The route does not become active until DNS, certificate, and
backend readiness agree.
Administrative hostnames
Separate customer traffic from operator and tenant-admin traffic. admin.tenant.example should
have its own Access policy and, where practical, its own route and certificate reference. A wildcard
certificate does not imply a wildcard authorization policy.
How OpenTenant reconciles the domain lifecycle
tenant requests hostname
→ normalize and reserve unique claim
→ issue ownership challenge when external
→ verify DNS control
→ create or validate Cloudflare record
→ render Gateway listener/route relationship
→ request or attach certificate
→ verify DNS, SNI, certificate chain, and backend
→ activate tenant route
The tenant status remains pending until every required controller reports readiness. A successful DNS API response is not enough; OpenTenant probes the public hostname, validates the certificate SAN and expiry, and confirms the response belongs to the expected tenant release.
Renewal, suspension, and deletion
Certificate issuance is only the first event. cert-manager renews certificates according to issuer and Certificate configuration, but the platform must monitor Certificate conditions and public expiry. Alert before the certificate enters a risky window, and test that a renewed Secret is actually served by the Gateway implementation.
Suspension should block edge access before stopping the application. Deletion should remove routes and Access policy, then DNS, then certificates and release resources according to retention policy. Keeping a DNS record pointed at a deleted or reassigned backend creates takeover risk.
Releasing a custom-domain claim should require proof that no active route, certificate, or edge binding remains. Consider a quarantine period before another tenant can claim the same hostname.
Security guardrails
- Scope Cloudflare API tokens to the required account, zone, and operations.
- Separate hostname verification records from proxied application records.
- Store DNS and issuer credentials in Vault, not tenant chart values.
- Reserve platform labels and Route attachment fields through admission policy.
- Prevent duplicate normalized hostnames with durable uniqueness enforcement.
- Limit access to wildcard private keys and prefer specific certificates for higher-risk boundaries.
- Verify the served certificate and tenant identity from outside the cluster.
- Record every ownership, DNS, route, and certificate transition in the tenant audit trail.
Verification checklist
- Duplicate and mixed-case hostname claims are rejected.
- Customer domains cannot activate before ownership verification.
- DNS records use the intended proxied or DNS-only mode.
- Gateway and HTTPRoute hostnames intersect as expected.
- The served certificate contains the exact hostname and valid chain.
- Tenant A cannot attach a route to tenant B's service.
- Suspension removes access before workloads stop.
- Renewal updates the certificate served at the edge.
- Deletion leaves no dangling DNS or reusable route.
Sources
- Annotated Gateway resources — cert-manager
- Hostnames in Gateway API
- TLS configuration — Gateway API
- Proxied DNS records — Cloudflare
OpenTenant makes the domain, route, certificate, and tenant release one reconciled object—so “active” means the public security path is actually ready, not merely that an API call succeeded.