From 81ab67407280953fe275d4d5d0495c6bc58efb9a Mon Sep 17 00:00:00 2001 From: "mustafa.ulukaya" Date: Fri, 7 Aug 2026 09:01:35 +0300 Subject: [PATCH] docs(v1.10.0): document the GoDaddy DNS provider and upgrade notes README: add GoDaddy to the two feature bullets and to the DNS-01 provider catalog, spelling out that the API Key must be a Production key (the first key the developer dashboard issues is an OTE/test key and is rejected), that the zone must be in the same account, that the account needs a registered domain before GoDaddy permits DNS API access, and that a Personal Access Token works with the Secret left blank. Note that publishing is automatic for GoDaddy as well as Cloudflare, and add the release-notes entry. UPGRADE_GUIDE: new section stating there is no SCHEMA_VERSION bump, so the built-in-role re-seed warning from v1.9.0 does not apply, and no new environment variable, API-shape or agent change. Two limits are stated explicitly rather than glossed: the credential check is a read, so a token with read but not write scope saves successfully and only fails at the first publish; and downgrading after adopting GoDaddy is not a no-op, because an unknown provider name degrades DNS-01 orders to the manual-confirm path and leaves published TXT records marked cleaned without being removed. --- README.md | 9 +++++---- UPGRADE_GUIDE.md | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cff3032..8e80e00 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ This architecture provides better security (no inbound connections to HAProxy se ✅ **SSL Certificate Management** - Centralized SSL with expiration tracking ✅ **CSR Creation** *(v1.9.0)* - Generate a private key + CSR in-app (RSA 2048/4096, ECDSA P-256/P-384, full subject + SANs), have it signed by any external CA, then import the signed certificate — the key never leaves the server ✅ **ACME Auto SSL (Let's Encrypt)** - Automated certificate issuance, renewal, and deployment via ACME protocol -✅ **ACME DNS-01 Challenge** *(v1.8.0)* - TXT-record validation for internal/isolated clusters (no public port 80) and wildcard certificates; pluggable DNS providers (Manual + Cloudflare), opt-in, HTTP-01 unchanged +✅ **ACME DNS-01 Challenge** *(v1.8.0)* - TXT-record validation for internal/isolated clusters (no public port 80) and wildcard certificates; pluggable DNS providers (Manual + Cloudflare + GoDaddy *(v1.10.0)*), opt-in, HTTP-01 unchanged ✅ **ACME Certificate Diagnostic Panel** - Automated preflight that checks agent readiness, DNS resolution, port 80 reachability, and ACME challenge ACL before issuing certificates ✅ **WAF Rules** - Web Application Firewall management and deployment ✅ **Agent Script Versioning** - Update agents via UI (Monaco editor) with auto-upgrade @@ -256,7 +256,7 @@ This architecture provides better security (no inbound connections to HAProxy se - **Stuck Order Detection** *(v1.4.0)*: Setup wizard surfaces orders that the CA has validated but not yet downloaded, with one-click `Complete` action and automatic 60-second retry - **Multi-Provider Support**: Configurable ACME directory URL supports Let's Encrypt, ZeroSSL, Google Trust Services, Buypass, and custom CAs - **HTTP-01 Challenge**: Built-in challenge responder with automatic HAProxy routing injection; reserved backend name `_acme_challenge_backend` is auto-managed and protected from manual edits / agent sync collisions -- **DNS-01 Challenge** *(v1.8.0 — Issue #35)*: Validate via a DNS TXT record instead of HTTP on port 80, for **internal/isolated clusters with no public ingress** and for **wildcard** certificates (`*.example.com`). Pluggable per-account DNS provider (Manual + Cloudflare to start; credentials encrypted at rest and verified on save), same PENDING → APPLIED pipeline, bounded automatic retry on propagation lag, and a DNS-01 event timeline. Opt-in via a global setting; HTTP-01 behaviour is unchanged. (See the *DNS-01 Challenge* subsection under ACME Auto SSL below.) +- **DNS-01 Challenge** *(v1.8.0 — Issue #35)*: Validate via a DNS TXT record instead of HTTP on port 80, for **internal/isolated clusters with no public ingress** and for **wildcard** certificates (`*.example.com`). Pluggable per-account DNS provider (Manual + Cloudflare + GoDaddy *(v1.10.0)*; credentials encrypted at rest and verified on save), same PENDING → APPLIED pipeline, bounded automatic retry on propagation lag, and a DNS-01 event timeline. Opt-in via a global setting; HTTP-01 behaviour is unchanged. (See the *DNS-01 Challenge* subsection under ACME Auto SSL below.) - **ACME Account Management**: Register, view, and deactivate ACME accounts from the UI - **Staging Mode**: Test certificate issuance with Let's Encrypt staging environment before production - **Custom Staging Endpoint** *(v1.4.0)*: Optional `staging_url_override` setting lets you point staging mode at a private ACME test CA (e.g. Pebble) without touching the production directory URL @@ -989,9 +989,9 @@ DNS-01 is **opt-in** and fully backward compatible: it is disabled until an admi - **Enable it**: Settings → ACME / SSL Automation → **DNS-01 Challenge (advanced)** → turn on *Enable DNS-01 Challenge* and Save. While off, DNS-01 options are hidden and no DNS-01 orders can be created. - **Per-account provider**: in ACME Automation, create (or reconfigure) an ACME account with **Challenge Method = DNS-01** and a **DNS Provider**. Provider credentials are **verified before saving** and **encrypted at rest** (Fernet, mirroring the VRRP/MFA secret pattern); they are never returned by the API or written to logs. -- **Supported providers**: **Manual** (publish the TXT record yourself in any DNS — including fully internal DNS — then click *Verify*; works everywhere but cannot auto-renew unattended) and **Cloudflare** (API token with `Zone:DNS:Edit` + `Zone:Read`; the TXT record is created and cleaned up automatically and renews unattended). The provider interface is pluggable — more providers can be added without changing the issuance flow. +- **Supported providers**: **Manual** (publish the TXT record yourself in any DNS — including fully internal DNS — then click *Verify*; works everywhere but cannot auto-renew unattended), **Cloudflare** (API token with `Zone:DNS:Edit` + `Zone:Read`; the TXT record is created and cleaned up automatically and renews unattended), and **GoDaddy** *(v1.10.0)* (a **Production** API Key + Secret pair from `developer.godaddy.com/keys` — the first key that dashboard issues is an OTE/test key and is rejected; the zone must be in the same GoDaddy account, which needs at least one registered domain before GoDaddy allows DNS API access at all. A **Personal Access Token** works too: paste it as the API Key and leave the Secret blank — that is the forward path as GoDaddy retires the `sso-key` scheme. TXT records are created and cleaned up automatically and renew unattended). The provider interface is pluggable — more providers can be added without changing the issuance flow. - **Same pipeline**: after validation the certificate follows the normal PENDING → APPLIED flow (assign to clusters / Apply Management) and the agent serves it — identical to HTTP-01 from finalize onward, with **zero agent or rendered-config changes** for DNS-01. -- **Manual flow**: the order detail shows the exact `_acme-challenge.` record name + TXT value (copyable); publish it and click *I've added the records — Verify*. For Cloudflare it is automatic. +- **Manual flow**: the order detail shows the exact `_acme-challenge.` record name + TXT value (copyable); publish it and click *I've added the records — Verify*. For Cloudflare and GoDaddy it is automatic. - **Resilience**: a propagation-lag failure is recovered by a **bounded fresh-order retry chain** (1 original + 3 retries with increasing backoff, kept under Let's Encrypt's rate limits); any orphaned TXT record is cleaned up by a reconcile sweep. The order detail shows a DNS-01 event timeline (publish → validation → cleanup). - **Wildcards**: `*.example.com` is validated at `_acme-challenge.example.com`; it does **not** cover the apex — add `example.com` as a separate name if you need both (the providers handle the two coexisting TXT values automatically). - **Scope (this release)**: the Site Wizard remains HTTP-01-only; issue DNS-01 / wildcard certificates from **ACME Automation**. @@ -2474,6 +2474,7 @@ Developed with ❤️ for the HAProxy community ## Release Notes +- **v1.10.0** (2026-08-07) — **GoDaddy DNS provider for DNS-01** (Issue #35 follow-up): DNS-01 challenges can now be published and cleaned up automatically through **GoDaddy**, alongside the existing Manual and Cloudflare providers, so wildcard and internal-cluster certificates on GoDaddy-hosted zones **renew unattended**. Credentials are a **Production API Key + Secret** pair from `developer.godaddy.com/keys` (a **Personal Access Token** also works — paste it as the Key and leave the Secret blank, which is the forward path as GoDaddy retires `sso-key`); they are **verified against the GoDaddy API before being saved** and **encrypted at rest** (Fernet, the same path as Cloudflare), and are never returned by the API, logged, or written to an order event. GoDaddy's v1 API has **no per-value TXT write** — `PUT` replaces an entire RRset — so add/remove are read-modify-write with sibling values merged back, empty-`data` tombstones filtered out, and `DELETE` used for the last value (`PUT []` is rejected); this is what keeps the **apex + wildcard** case (two TXT values at one `_acme-challenge` name) working, and the record path is hard-gated so it can never collapse onto the zone-wide endpoint that would wipe SPF/DKIM/DMARC. Zone lookup probes the records API rather than the domain listing, so **delegated sub-zones** resolve and small accounts are not falsely rejected. Registry-only addition: one new provider module plus one registry line — no frontend change (the credential form is schema-driven). No schema, API-shape, agent, or rendered-config changes; Manual, Cloudflare and HTTP-01 are unaffected. - **v1.9.0** (2026-08-04) — **CSR creation** (in-app key + CSR generation and signed-certificate import): a new **CSR tab** on the SSL Certificates page generates a private key and Certificate Signing Request server-side (RSA 2048/4096 or ECDSA P-256/P-384; full subject — O/OU/L/ST/C/email — plus DNS SANs with wildcard support), for certificates signed by an **external or corporate CA**. The operator downloads/copies the CSR PEM, has it signed, then imports the signed certificate (+ optional chain): the backend verifies the certificate against the stored key (hard gate), rejects expired certs, warns on SAN drift, and creates a normal SSL certificate entry (source `CSR`) that flows through the standard **PENDING → Apply Management → agent pull** pipeline. The private key **never leaves the server** — no CSR endpoint returns it, and after import the CSR row's key copy is destroyed (the key then lives only on the certificate, like every other key). Additive schema change: one new table `ssl_csrs` (SCHEMA_VERSION 9 → 10, auto-migrated, no existing table altered); key generation runs off the event loop and is rate-limited per user; existing `ssl.*` permissions govern all new endpoints. No agent or rendered-config changes. - **v1.8.10** (2026-07-20) — **Security hardening** (GHSA-7rhv-c5pc-69r8, GHSA-3p5c-m5m4-mjpx, GHSA-3vh4): three advisory classes remediated, backend-only, no agent changes. (1) **RCE**: the agent script-template read/write endpoints now require the `agents.version` permission on top of authentication — a poisoned template is executed as root on every HAProxy node, so authentication alone was insufficient. (2) **Missing authentication**: operator/UI endpoints that were served without a JWT (dashboard stats, pool/cluster listings, agent inventory, WAF rules, config validate/optimize, SSL config-versions, health deep/agents/clusters) are now gated by a `require_authenticated_user` dependency, and agent data-plane endpoints that treated the `X-API-Key` header as *optional* (heartbeat, config, ssl-certificates, upgrade-status, pending-requests) now hard-reject a missing key. In every case the auth check was moved **ahead of** the handler's `try:` block so a 401 can no longer be rewritten into a 500 by the generic exception handler. (3) **SSRF**: a new `utils/ssrf_guard.py` (https-only, IPv4-pinned connector, all resolved addresses must be public, no redirects) protects the ACME directory fetch, the signed-request target and the ACME connection test, which accept operator- or DB-supplied URLs; the connection test also stopped reflecting arbitrary upstream JSON. Frontend dependency advisories patched in the same release. No schema, API-shape or rendered-config changes. - **v1.8.9** (2026-07-13) — **ACL `-f` pattern-file support** (Issue #38 follow-up): ACL definitions that reference a host-side pattern file (`acl … -f /etc/haproxy/lists/blocked.lst`) are accepted on import and edit instead of being rejected. The referenced file lives on the HAProxy node and cannot be validated from the manager, so the manager emits an **advisory warning** rather than a hard rejection and lets the agent's `haproxy -c` check be the fail-safe gate (a broken reference fails validation on the node and the previous config is restored). Consistent with the SPOE handling introduced in v1.8.8. diff --git a/UPGRADE_GUIDE.md b/UPGRADE_GUIDE.md index c1a5e0e..edd6b38 100644 --- a/UPGRADE_GUIDE.md +++ b/UPGRADE_GUIDE.md @@ -1,3 +1,41 @@ +# Upgrade Notes — v1.10.0 (GoDaddy DNS-01 provider) + +**Backward compatible & additive.** Nothing changes unless you select **GoDaddy** as an ACME +account's DNS provider: + +- **Schema:** **no `SCHEMA_VERSION` bump.** The GoDaddy credentials (API Key + Secret) are stored + as two keys inside the *existing* encrypted + `letsencrypt_account_dns_credentials.credentials_encrypted` blob — no new table, no new column, + no migration. +- **✅ Built-in roles are NOT re-seeded.** The re-seed warning in the v1.9.0 notes below is + triggered by a `SCHEMA_VERSION` bump. This release does not bump it, so any customization you + made to `super_admin` / `operator` / `security_admin` / `viewer` survives untouched. +- **Permissions / API shape:** unchanged. `GET /api/letsencrypt/dns-providers` simply returns one + extra entry in its `providers` array; every request and response shape is identical, and the + credential form is rendered from that schema, so there is no frontend behaviour change either. +- **Environment:** no new variable. GoDaddy credentials use the same Fernet-at-rest path as + Cloudflare (`DNS_PROVIDER_ENCRYPTION_KEY`, falling back to a key derived from `SECRET_KEY`). +- **Agents:** zero agent changes. DNS-01 is invisible to agents; an issued certificate follows the + normal PENDING → Apply Management → agent pull pipeline exactly as before. +- **Using it:** the API Key must be a **Production** key from `developer.godaddy.com/keys` (the + first key that dashboard issues is an OTE/test key and is rejected), the zone must be in the same + GoDaddy account, and that account needs at least one registered domain before GoDaddy permits DNS + API access. A Personal Access Token also works — paste it as the Key and leave the Secret blank. + Credentials are checked against the GoDaddy API before they are stored, so an invalid, OTE or + ineligible key fails at save time. Note the check is a **read**: a Personal Access Token that has + `domains.domain:read` but not `domains.dns:update` saves successfully and only fails at the first + publish, with a 403 in the order timeline. +- **Rollback:** don't select GoDaddy. Existing Manual and Cloudflare accounts and all HTTP-01 + issuance are untouched. **Downgrading after adopting GoDaddy is not a no-op**: on 1.9.0 + `godaddy` is not a known provider, so any account still set to it degrades to the manual-confirm + path (in-flight DNS-01 orders wait for a confirmation nobody can give and expire after 48h, and + renewals stop), and the cleanup sweep marks published TXT records cleaned without removing them. + Before downgrading, switch affected accounts back to Manual or Cloudflare and let the reconcile + sweep remove outstanding `_acme-challenge` records first. The stored credential row itself is + inert — an encrypted blob for an unknown provider. + +--- + # Upgrade Notes — v1.9.0 (CSR creation) **Backward compatible & additive.** Upgrading to v1.9.0 changes nothing for existing