[PR #6009] [proxy] spike: prove Lego + Cloudflare DNS-01 integration (NOT FOR MERGE) #27318

Open
opened 2026-08-05 07:08:32 -04:00 by saavagebueno · 0 comments
Owner

Original Pull Request: https://github.com/netbirdio/netbird/pull/6009

State: closed
Merged: No


Summary

This branch is a vertical-slice spike demonstrating that DNS-01 ACME via Lego + Cloudflare integrates cleanly with NetBird's reverse proxy. It proves the integration shape works; it is not a production implementation.

The motivation: NetBird's reverse proxy currently supports only tls-alpn-01 and http-01 ACME challenges, both of which require the proxy to be publicly reachable. DNS-01 closes that gap, enabling Let's Encrypt certs for services that should never be on the public internet. This spike confirms the integration is tractable before committing engineering capacity to a production rollout.

This branch is for review of the integration shape only. Do not merge.

Status

Verified end-to-end against Let's Encrypt staging on 2026-04-27. Real cert issued for spike-test.hopki.net via Cloudflare DNS-01 in ~15 seconds. Idempotent reruns confirmed (no duplicate ACME order, no rate-limit waste).

What's in the diff

New code:

  • proxy/cmd/dns01-spike/ — runnable cobra CLI that issues a real Let's Encrypt cert via Cloudflare DNS-01
    • main.go, cmd/root.go, README.md
  • proxy/internal/acme/backend.goCertBackend interface with a compile-time assertion that the existing *Manager (autocert-backed) already satisfies it. This is the architectural proof: the abstraction fits without forcing a method-signature change on the existing code.
  • proxy/internal/acme/lego_backend.goLegoBackend struct implementing CertBackend (compile-only — not wired into the running Server)
  • proxy/internal/acme/legoclient/client.go — shared Lego helper used by both the CLI and LegoBackend

Modified:

  • go.mod, go.sum — adds github.com/go-acme/lego/v4 v4.35.2 and ~20 transitive upgrades (mostly minor bumps to AWS SDK, miekg/dns, golang.org/x/crypto). Diff is non-trivial; review carefully.

Untouched (deliberately):

  • proxy/internal/acme/manager.go, proxy/server.go, proxy/cmd/proxy/cmd/root.go. The existing autocert path continues to work exactly as before. Layer is purely additive.

What this spike deliberately does NOT do

  • No per-service config plumbing (challenge type stays a global env var)
  • No encrypted credential storage (Cloudflare token via env var only)
  • No distributed locking integration (single-process)
  • No LegoBackend wiring into the running Server (compile-only)
  • No tests beyond compile + smoke
  • No providers other than Cloudflare
  • No production ACME directory (Let's Encrypt staging only)
  • No renewal lifecycle or multi-replica coordination

All of the above are real engineering work for the production rollout, not the spike.

How to run it locally

From the repo root:

export CF_DNS_API_TOKEN="<scoped Cloudflare token, Zone:DNS:Edit>"
export SPIKE_DOMAIN="<test FQDN under that zone>"
export SPIKE_EMAIL="<acme account email>"

go run ./proxy/cmd/dns01-spike

Full setup walkthrough in proxy/cmd/dns01-spike/README.md. The CLI defaults to Let's Encrypt staging to avoid burning real-world rate limits.

Verified output

subject=CN=spike-test.hopki.net
issuer=C=US, O=(STAGING) Let's Encrypt, CN=(STAGING) Tenuous Tomato R13
notBefore=Apr 27 21:48:41 2026 GMT
notAfter=Jul 26 21:48:40 2026 GMT
X509v3 Subject Alternative Name: DNS:spike-test.hopki.net

Real chain (leaf + intermediate), valid 90 days, issued by Let's Encrypt staging. The intentional (STAGING) issuer is correct — the spike pins staging on purpose.

Disposition

Do not merge. When the production rollout starts:

  • The CertBackend interface in backend.go is reusable as-is.
  • The legoclient helper is reusable but will be substantially rewritten to use encrypted credential storage, the existing distributed locker, and per-account / per-service provider selection.
  • The LegoBackend in lego_backend.go is a sketch. Production work replaces nearly all of it.
  • The dns01-spike CLI is throwaway.

After review, this branch can either stay open as a reference or be deleted — it serves no purpose merged.

**Original Pull Request:** https://github.com/netbirdio/netbird/pull/6009 **State:** closed **Merged:** No --- ## Summary This branch is a **vertical-slice spike** demonstrating that DNS-01 ACME via Lego + Cloudflare integrates cleanly with NetBird's reverse proxy. It proves the integration shape works; it is **not** a production implementation. The motivation: NetBird's reverse proxy currently supports only `tls-alpn-01` and `http-01` ACME challenges, both of which require the proxy to be publicly reachable. DNS-01 closes that gap, enabling Let's Encrypt certs for services that should never be on the public internet. This spike confirms the integration is tractable before committing engineering capacity to a production rollout. **This branch is for review of the integration shape only. Do not merge.** ## Status **Verified end-to-end against Let's Encrypt staging on 2026-04-27.** Real cert issued for `spike-test.hopki.net` via Cloudflare DNS-01 in ~15 seconds. Idempotent reruns confirmed (no duplicate ACME order, no rate-limit waste). ## What's in the diff **New code:** - `proxy/cmd/dns01-spike/` — runnable cobra CLI that issues a real Let's Encrypt cert via Cloudflare DNS-01 - `main.go`, `cmd/root.go`, `README.md` - `proxy/internal/acme/backend.go` — `CertBackend` interface with a **compile-time assertion that the existing `*Manager` (autocert-backed) already satisfies it.** This is the architectural proof: the abstraction fits without forcing a method-signature change on the existing code. - `proxy/internal/acme/lego_backend.go` — `LegoBackend` struct implementing `CertBackend` (compile-only — not wired into the running `Server`) - `proxy/internal/acme/legoclient/client.go` — shared Lego helper used by both the CLI and `LegoBackend` **Modified:** - `go.mod`, `go.sum` — adds `github.com/go-acme/lego/v4 v4.35.2` and ~20 transitive upgrades (mostly minor bumps to AWS SDK, miekg/dns, golang.org/x/crypto). Diff is non-trivial; review carefully. **Untouched (deliberately):** - `proxy/internal/acme/manager.go`, `proxy/server.go`, `proxy/cmd/proxy/cmd/root.go`. The existing autocert path continues to work exactly as before. Layer is purely additive. ## What this spike deliberately does NOT do - No per-service config plumbing (challenge type stays a global env var) - No encrypted credential storage (Cloudflare token via env var only) - No distributed locking integration (single-process) - No `LegoBackend` wiring into the running `Server` (compile-only) - No tests beyond compile + smoke - No providers other than Cloudflare - No production ACME directory (Let's Encrypt staging only) - No renewal lifecycle or multi-replica coordination All of the above are real engineering work for the production rollout, not the spike. ## How to run it locally From the repo root: ```sh export CF_DNS_API_TOKEN="<scoped Cloudflare token, Zone:DNS:Edit>" export SPIKE_DOMAIN="<test FQDN under that zone>" export SPIKE_EMAIL="<acme account email>" go run ./proxy/cmd/dns01-spike ``` Full setup walkthrough in `proxy/cmd/dns01-spike/README.md`. The CLI defaults to Let's Encrypt staging to avoid burning real-world rate limits. ## Verified output ``` subject=CN=spike-test.hopki.net issuer=C=US, O=(STAGING) Let's Encrypt, CN=(STAGING) Tenuous Tomato R13 notBefore=Apr 27 21:48:41 2026 GMT notAfter=Jul 26 21:48:40 2026 GMT X509v3 Subject Alternative Name: DNS:spike-test.hopki.net ``` Real chain (leaf + intermediate), valid 90 days, issued by Let's Encrypt staging. The intentional `(STAGING)` issuer is correct — the spike pins staging on purpose. ## Disposition **Do not merge.** When the production rollout starts: - The `CertBackend` interface in `backend.go` is reusable as-is. - The `legoclient` helper is reusable but will be substantially rewritten to use encrypted credential storage, the existing distributed locker, and per-account / per-service provider selection. - The `LegoBackend` in `lego_backend.go` is a sketch. Production work replaces nearly all of it. - The `dns01-spike` CLI is throwaway. After review, this branch can either stay open as a reference or be deleted — it serves no purpose merged.
saavagebueno added the pull-request label 2026-08-05 07:08:32 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#27318