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

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

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/6009
Author: @TechHutTV
Created: 4/27/2026
Status: Closed

Base: mainHead: spike/dns01-cloudflare


📝 Commits (1)

  • a32a8ea spike: prove Lego + Cloudflare

📊 Changes

8 files changed (+740 additions, -113 deletions)

View changed files

📝 go.mod (+36 -36)
📝 go.sum (+74 -77)
proxy/cmd/dns01-spike/README.md (+107 -0)
proxy/cmd/dns01-spike/cmd/root.go (+119 -0)
proxy/cmd/dns01-spike/main.go (+17 -0)
proxy/internal/acme/backend.go (+29 -0)
proxy/internal/acme/lego_backend.go (+125 -0)
proxy/internal/acme/legoclient/client.go (+233 -0)

📄 Description

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.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/6009 **Author:** [@TechHutTV](https://github.com/TechHutTV) **Created:** 4/27/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `spike/dns01-cloudflare` --- ### 📝 Commits (1) - [`a32a8ea`](https://github.com/netbirdio/netbird/commit/a32a8ea19248aa77dfea62ef0fc58191823a9034) spike: prove Lego + Cloudflare ### 📊 Changes **8 files changed** (+740 additions, -113 deletions) <details> <summary>View changed files</summary> 📝 `go.mod` (+36 -36) 📝 `go.sum` (+74 -77) ➕ `proxy/cmd/dns01-spike/README.md` (+107 -0) ➕ `proxy/cmd/dns01-spike/cmd/root.go` (+119 -0) ➕ `proxy/cmd/dns01-spike/main.go` (+17 -0) ➕ `proxy/internal/acme/backend.go` (+29 -0) ➕ `proxy/internal/acme/lego_backend.go` (+125 -0) ➕ `proxy/internal/acme/legoclient/client.go` (+233 -0) </details> ### 📄 Description ## 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. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#27311