DNS and certificates¶
Direct TLS does not require Advanced Certificate Manager. Client Traefik obtains and renews Let's Encrypt certificates; Cloudflare provides authoritative DNS. Configure an ACME contact for the installation.
Routing¶
| Purpose | Name | DNS and TLS |
|---|---|---|
| Control host | headscale.example.com |
A → 192.0.2.10, DNS-only, direct TLS |
| Client production | <company>.oduflow.sh |
A → client VM, DNS-only, Traefik HTTPS |
| Client Oduflow | oduflow.<company>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
| Client Paseo | paseo.<company>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
| Branch/service route | <name>.<company>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
Create <company>.oduflow.sh and *.<company>.oduflow.sh with
proxied=false. Publish AAAA only when IPv6 actually works. TCP 80 supports
HTTP-01 issuance and HTTPS redirects; TCP 443 serves Traefik. Administrative Salt
ports remain inside the VPN. Reserve the Oduflow/Paseo names against branch use.
DNS-only is deliberate: browsers see Traefik's certificate directly. With Cloudflare proxy enabled, Cloudflare presents its own edge certificate. Universal SSL on a full zone normally covers the zone apex and one subdomain level, not arbitrary deeper client hosts. An origin Let's Encrypt certificate does not extend Cloudflare edge coverage. Universal SSL limits.
Cloudflare Tunnel is not the default client publication path: it introduces a separate browser-to-Cloudflare TLS connection. Total TLS does not issue certificates for Tunnel hostnames. Total TLS, Tunnel routing.
Current Let's Encrypt configuration¶
The implemented client uses HTTP-01 for concrete hostnames, including the production apex and both panels. Wildcard DNS routing does not mean the TLS certificate must itself be a wildcard. No broad Cloudflare API token is delivered to the client. Oduflow 1.75 manages Traefik and its persistent ACME volume.
A conceptual static resolver configuration is:
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /var/lib/traefik/acme.json
httpChallenge:
entryPoint: web
The actual paths and container routing are generated by client Oduflow. ACME state
must persist across restarts, remain private (0600) and never be copied into a
golden image with client certificates or keys. Readiness verifies certificate
trust/hostname and application authentication, not merely a running Traefik process.
See production publication for the temporary host ingress
guard and post-hardening ACME retry.
Traefik ACME.
Optional future wildcard certificates¶
A certificate for <company>.oduflow.sh and *.<company>.oduflow.sh would
require DNS-01. Wildcards cover one additional label; *.*.oduflow.sh is invalid.
Traefik/Lego can create _acme-challenge TXT records using its cloudflare
provider. Such a design is not enabled by the current client role.
A dedicated DNS-01 token would need Zone / Zone / Read and Zone / DNS / Edit
scoped to the intended zone, supplied through a private file such as
CF_DNS_API_TOKEN_FILE. A separate CF_ZONE_API_TOKEN can supply zone lookup.
Zone scope does not isolate one client's subdomain, so distributing the control
plane's zone-wide token is inappropriate. A delegated challenge zone/CNAME or
central certificate issuer would need a separate design.
Lego Cloudflare provider,
Traefik DNS challenge.
Odoo contract and migration¶
New instances use oduflow.sh, naming_version=nested_v2 and
ingress_mode=direct_tls. Production uses <slug>.<domain>, Oduflow uses
oduflow.<slug>.<domain> and Paseo uses paseo.<slug>.<domain>. The client owns
*.<slug>.<domain>; its Oduflow chooses names within that namespace. The control
plane does not reserve individual devN/svcN slots.
Preparation freezes naming version, ingress, ACME contact, client domain and panel
addresses. Pillar remains schema=1; nested_v2 environment/service lists are
empty and wildcard identifies the owned subtree. Numeric quotas do not assign
DNS names. Direct TLS needs neither a tunnel token nor cloudflared.
Migration 19.0.1.2.0 assigns legacy rows flat_v1 and tunnel before model
initialization. Existing <slug>-devN.<domain> / <slug>-paseo.<domain> names and
base_domain remain unchanged. Prepared snapshots receive their historical DNS
set; new defaults must not rewrite old addresses.
Wildcard namespace reservations cover the whole client subtree, including its
apex, even though a TLS wildcard covers only one label. Conflict checks include
exact names and nested subtrees across companies. Every Prepare transaction updates
one private oduflow.dns.namespace.guard row before checking conflicts. Under
REPEATABLE READ, PostgreSQL rejects a stale concurrent snapshot and Odoo retries.
An advisory lock alone would not make that snapshot fresh. Reservations survive
infrastructure deletion; an infrastructure hostname cannot be reused as a client slug.
Migration 19.0.3.1.0 removes separate fourth-level reservations only when the same
instance owns the apex and wildcard. Legacy flat_v1 records remain. The client
form no longer exposes a separate Reserved Hostnames page.