Merge branch 'B2B'
This commit is contained in:
@@ -83,27 +83,84 @@ Push to `main`, or run the workflow manually with a ref. The workflow refuses to
|
||||
|
||||
---
|
||||
|
||||
## 4. TLS
|
||||
## 4. Domains and TLS — dynamic by default
|
||||
|
||||
No certificate is issued during provisioning, because certbot cannot validate a domain that does not yet point at the server.
|
||||
Domains arrive continuously: one today, five tomorrow. Nothing here requires a person per domain.
|
||||
|
||||
Per domain, once its A record resolves here:
|
||||
**HTTP already needs zero configuration.** The nginx catch-all serves *any* `Host`, and the SPA resolves its tenant from that header. Point a domain's A record at the server and it works over port 80 immediately. Only TLS needs a certificate per name — that is the whole problem this section solves.
|
||||
|
||||
Two mechanisms, used together:
|
||||
|
||||
### 4.1 Wildcard — tenants on our own apex
|
||||
|
||||
One certificate covers every `<slug>.<apex>`. A new tenant subdomain is then live over HTTPS the moment DNS resolves, with **no certificate work at all**.
|
||||
|
||||
```bash
|
||||
sudo bash setup-wildcard-tls.sh \
|
||||
--apex marketplaces.example.com \
|
||||
--email ops@example.com \
|
||||
--dns cloudflare --creds /root/cloudflare.ini
|
||||
```
|
||||
|
||||
Wildcards require DNS-01 validation, so certbot must write a `_acme-challenge` TXT record. With a provider plugin (`cloudflare`, `route53`) renewal is unattended. `--dns manual` works but prompts for a TXT record at **every** renewal — fine to prove the setup out, not acceptable as a steady state.
|
||||
|
||||
**Hostinger has no certbot plugin.** If DNS lives there: either move DNS to a provider that has one (Cloudflare is free, minutes of work), or drive issuance from the [Phase 9](backend/PHASE-9-TENANT-REGISTRY-DOMAINS-CONTRACT.md) domain-automation API once it exists.
|
||||
|
||||
### 4.2 Reconciler — tenants on their own domains
|
||||
|
||||
A wildcard cannot cover a customer's own domain. `sync-domains.sh` runs on a 10-minute timer and reconciles the live set against a desired list:
|
||||
|
||||
- issues certificates for domains that lack one
|
||||
- skips domains whose certificate has more than 30 days left
|
||||
- skips subdomains already covered by `WILDCARD_APEX`
|
||||
- leaves domains alone while their DNS has not propagated yet, and retries next tick
|
||||
- disables server blocks for domains removed from the source — **without deleting the certificate**, so re-adding one later is instant
|
||||
- caps issuance per run, so a misconfigured source cannot burn the weekly ACME budget in a single pass
|
||||
|
||||
Configure `/etc/marketplaces/domains.env`:
|
||||
|
||||
```bash
|
||||
DOMAINS_SOURCE=file:/etc/marketplaces/domains.txt
|
||||
CERTBOT_EMAIL=ops@example.com
|
||||
MAX_ISSUE_PER_RUN=10
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now marketplaces-domains.timer
|
||||
sudo /srv/marketplaces/bin/sync-domains.sh --dry-run # see the plan, change nothing
|
||||
```
|
||||
|
||||
Adding a domain becomes: append a line to `/etc/marketplaces/domains.txt` (or add the row in the backend registry), point DNS, wait one tick.
|
||||
|
||||
### 4.3 Backend-driven, once Phase 9 ships
|
||||
|
||||
Point the reconciler at the registry instead of a file and the loop closes — `MarketplaceDomain` already carries exactly the statuses this needs (`planned → dns_pending → ssl_pending → active → failed`):
|
||||
|
||||
```bash
|
||||
DOMAINS_SOURCE=https://api.example.com/api/admin/v2/domains
|
||||
DOMAINS_API_TOKEN=...
|
||||
```
|
||||
|
||||
The script accepts a bare JSON array of hostnames, or objects with `domain` + `status`, in which case it acts only on `active` rows. **A fetch failure aborts the run rather than reading as "remove every domain."**
|
||||
|
||||
### 4.4 One-off
|
||||
|
||||
For a single domain, outside the reconciler:
|
||||
|
||||
```bash
|
||||
sudo bash add-domain.sh shop.example.com --email ops@example.com --with-www
|
||||
```
|
||||
|
||||
This writes a server block for that domain (same root — it exists only to give certbot a concrete `server_name`), issues the certificate, enables the HTTP→HTTPS redirect, and enables `certbot.timer` for renewal.
|
||||
|
||||
Verify:
|
||||
### 4.5 Verify
|
||||
|
||||
```bash
|
||||
curl -I https://shop.example.com/health
|
||||
sudo certbot certificates
|
||||
journalctl -u marketplaces-domains.service --since "1 hour ago"
|
||||
```
|
||||
|
||||
**On wildcards:** `add-domain.sh` uses HTTP-01, which cannot issue wildcards. If tenants all live under one apex (`*.marketplaces.example.com`), a DNS-01 wildcard is fewer moving parts — but it requires API credentials for the DNS provider and a different certbot plugin. Not set up here; raise it when the tenant count makes per-domain issuance annoying.
|
||||
|
||||
---
|
||||
|
||||
## 5. Rollback
|
||||
|
||||
Reference in New Issue
Block a user