[GH-ISSUE #5479] [Feature Proposal] Internal HTTPS Certificates for Peer Resources #11544

Open
opened 2026-08-05 01:29:59 -04:00 by saavagebueno · 3 comments
Owner

Originally created by @zgv163 on GitHub (Mar 1, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5479

TL;DR: There's no way to get valid TLS certificates for NetBird peer hostnames (e.g., crm.netbird.cloud). Tailscale solved this years ago with tailscale cert. Two viable paths exist: an internal CA (potentially via Smallstep), or the brand-new Let's Encrypt DNS-PERSIST-01 challenge type arriving Q2 2026. Is this on the roadmap? Would a community PR be welcome?


The Gap

When you access services over NetBird peer domains, browsers throw security warnings because there's no valid TLS cert for the hostname. WireGuard encrypts the tunnel, but browsers don't know that.

The workarounds today aren't great:

  • Ignore the warnings (not acceptable when working in teams or production)
  • Set up a self-signed CA manually and install it on every device
  • Use the new Reverse Proxy, which is cool but always exposes things publicly, often the opposite of what you want

This keeps coming up and there hasn't been an official response yet:

Two Possible Approaches

Approach 1: Internal CA

The management server generates a root CA and signs certificates for peers on request. A netbird cert CLI command handles the flow. The peer's private key never leaves the device, only a CSR is sent.

This could be built from scratch using Go's crypto/x509 stdlib, or by integrating [Smallstep's step-ca](https://github.com/smallstep/certificates) which brings ACME support, auto-renewal, and certificate policies out of the box.

Pro: Works today, works for any domain including .selfhosted, .local, custom ones.
Con: Users need to trust the CA on their devices.

Approach 2: Let's Encrypt via DNS-PERSIST-01

Let's Encrypt announced DNS-PERSIST-01 in February 2026. It's a new ACME challenge where a single persistent DNS record authorizes an ACME account to issue certs for a domain and all its subdomains. No per-peer DNS updates needed, ever.

_validation-persist.netbird.cloud. IN TXT (
  "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/XXX; policy=wildcard"
)

Set it once, issue certs for any *.netbird.cloud subdomain indefinitely. Production rollout is expected Q2 2026.

Pro: Real publicly-trusted certs, zero trust store setup on client devices.
Con: Only works for NetBird-controlled domains (cloud users) or self-hosters with a public domain. Depends on LE timeline.

Where Should This Live?

Should this be part of the management server (which already knows all peer identities and has gRPC channels to every peer), or within the reverse proxy service as an extension? Management feels like the natural home since this is about internal resources, but curious what the team thinks.

The Ask

  1. Is this planned? Even a rough timeframe helps the community plan around it.
  2. Would a community PR for Approach 1 (internal CA) be welcome as a starting point?
  3. For cloud users: could DNS-PERSIST-01 work with netbird.cloud once LE ships it?

Both approaches aren't mutually exclusive. Approach 1 could ship now, Approach 2 layered on when LE is ready.

Originally created by @zgv163 on GitHub (Mar 1, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5479 **TL;DR:** There's no way to get valid TLS certificates for NetBird peer hostnames (e.g., `crm.netbird.cloud`). Tailscale solved this years ago with `tailscale cert`. Two viable paths exist: an internal CA (potentially via Smallstep), or the brand-new Let's Encrypt DNS-PERSIST-01 challenge type arriving Q2 2026. Is this on the roadmap? Would a community PR be welcome? --- ## The Gap When you access services over NetBird peer domains, browsers throw security warnings because there's no valid TLS cert for the hostname. WireGuard encrypts the tunnel, but browsers don't know that. The workarounds today aren't great: - Ignore the warnings (not acceptable when working in teams or production) - Set up a self-signed CA manually and install it on every device - Use the new Reverse Proxy, which is cool but always exposes things publicly, often the opposite of what you want This keeps coming up and there hasn't been an official response yet: - [[#2080](https://github.com/netbirdio/netbird/issues/2080)](https://github.com/netbirdio/netbird/issues/2080) - 22+ 👍, open since May 2024 - [[#4903](https://github.com/netbirdio/netbird/issues/4903)](https://github.com/netbirdio/netbird/issues/4903) - Dec 2025 - [[#5029](https://github.com/netbirdio/netbird/issues/5029)](https://github.com/netbirdio/netbird/issues/5029) - Jan 2026, directly references Tailscale ## Two Possible Approaches ### Approach 1: Internal CA The management server generates a root CA and signs certificates for peers on request. A `netbird cert` CLI command handles the flow. The peer's private key never leaves the device, only a CSR is sent. This could be built from scratch using Go's `crypto/x509` stdlib, or by integrating [[Smallstep's step-ca](https://github.com/smallstep/certificates)](https://github.com/smallstep/certificates) which brings ACME support, auto-renewal, and certificate policies out of the box. **Pro:** Works today, works for any domain including `.selfhosted`, `.local`, custom ones. **Con:** Users need to trust the CA on their devices. ### Approach 2: Let's Encrypt via [DNS-PERSIST-01](https://letsencrypt.org/2026/02/18/dns-persist-01) Let's Encrypt announced [DNS-PERSIST-01](https://letsencrypt.org/2026/02/18/dns-persist-01) in February 2026. It's a new ACME challenge where a single persistent DNS record authorizes an ACME account to issue certs for a domain and all its subdomains. No per-peer DNS updates needed, ever. ``` _validation-persist.netbird.cloud. IN TXT ( "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/XXX; policy=wildcard" ) ``` Set it once, issue certs for any `*.netbird.cloud` subdomain indefinitely. Production rollout is expected Q2 2026. **Pro:** Real publicly-trusted certs, zero trust store setup on client devices. **Con:** Only works for NetBird-controlled domains (cloud users) or self-hosters with a public domain. Depends on LE timeline. ## Where Should This Live? Should this be part of the **management server** (which already knows all peer identities and has gRPC channels to every peer), or within the **reverse proxy service** as an extension? Management feels like the natural home since this is about internal resources, but curious what the team thinks. ## The Ask 1. Is this planned? Even a rough timeframe helps the community plan around it. 2. Would a community PR for Approach 1 (internal CA) be welcome as a starting point? 3. For cloud users: could DNS-PERSIST-01 work with `netbird.cloud` once LE ships it? Both approaches aren't mutually exclusive. Approach 1 could ship now, Approach 2 layered on when LE is ready.
saavagebueno added the feature-request label 2026-08-05 01:29:59 -04:00
Author
Owner

@zgv163 commented on GitHub (Mar 6, 2026):

Revised direction: Let's Encrypt DNS-PERSIST-01

cc @mlsmaycon @pascal-fischer @braginini @lixmal

After prototyping the Internal CA approach (PR #5491, now closed), I concluded it's not viable cross-platform. iOS and Android can't programmatically trust a custom root CA. Tailscale reached the same conclusion (tailscale/tailscale#4454) and went with publicly-trusted Let's Encrypt certs instead.

What's changed: Let's Encrypt announced DNS-PERSIST-01 (staging late Q1, production Q2 2026). One persistent TXT record at _validation-persist.<domain> authorizes all future cert issuances, no per-request DNS changes. With policy=wildcard, a single record covers all peer FQDNs.

Looking at the codebase, there are two separate ACME implementations that don't share code: autocert for management and proxy, certmagic for Route53. The Go team has explicitly ruled out DNS challenge support for autocert (golang/go#23198), so dns-persist-01 can't be added there. On the other hand, lego already has a working dns-persist-01 implementation in v5 (#2871), written by an LE engineer.

A few questions:

  1. Would consolidating ACME into a shared module backed by lego be of interest? It would unify cert handling for both management and proxy.
  2. Would centralizing cert issuance on the management server make sense for proxied services too? Proxies wouldn't need internet-facing ACME ports anymore.
  3. With dns-persist-01, the management server could issue publicly-trusted certs for peer FQDNs. Is this something the project would consider?

Happy to contribute a proof-of-concept if there's interest.

<!-- gh-comment-id:4012360208 --> @zgv163 commented on GitHub (Mar 6, 2026): ## Revised direction: Let's Encrypt DNS-PERSIST-01 cc @mlsmaycon @pascal-fischer @braginini @lixmal After prototyping the Internal CA approach (PR #5491, now closed), I concluded it's not viable cross-platform. iOS and Android can't programmatically trust a custom root CA. Tailscale reached the same conclusion (tailscale/tailscale#4454) and went with publicly-trusted Let's Encrypt certs instead. **What's changed:** Let's Encrypt [announced DNS-PERSIST-01](https://letsencrypt.org/2026/02/18/dns-persist-01) (staging late Q1, production Q2 2026). One persistent TXT record at `_validation-persist.<domain>` authorizes all future cert issuances, no per-request DNS changes. With `policy=wildcard`, a single record covers all peer FQDNs. Looking at the codebase, there are two separate ACME implementations that don't share code: `autocert` for management and proxy, `certmagic` for Route53. The Go team has explicitly ruled out DNS challenge support for autocert ([golang/go#23198](https://github.com/golang/go/issues/23198)), so dns-persist-01 can't be added there. On the other hand, [lego](https://github.com/go-acme/lego) already has a working dns-persist-01 implementation in v5 ([#2871](https://github.com/go-acme/lego/pull/2871)), written by an LE engineer. A few questions: 1. Would consolidating ACME into a shared module backed by lego be of interest? It would unify cert handling for both management and proxy. 2. Would centralizing cert issuance on the management server make sense for proxied services too? Proxies wouldn't need internet-facing ACME ports anymore. 3. With dns-persist-01, the management server could issue publicly-trusted certs for peer FQDNs. Is this something the project would consider? Happy to contribute a proof-of-concept if there's interest.
Author
Owner

@renne commented on GitHub (Apr 11, 2026):

Suggested workflow:

  1. Netbird peer creates Certificate-Sign-Request with own private key.
  2. Netbird peer sends CSR to Netbird managment server
  3. Netbird management server queries the DNS-PERSIST-01 record
  4. Netbird management server requests the certificate from ACME-CA
  5. Netbird management server sends the public certificate chain to Netbird peer.

The Netbird reverse proxy would just be a Netbird peer.

<!-- gh-comment-id:4229352452 --> @renne commented on GitHub (Apr 11, 2026): Suggested workflow: 1. Netbird peer creates Certificate-Sign-Request with own private key. 2. Netbird peer sends CSR to Netbird managment server 3. Netbird management server queries the DNS-PERSIST-01 record 4. Netbird management server requests the certificate from ACME-CA 5. Netbird management server sends the public certificate chain to Netbird peer. The Netbird reverse proxy would just be a Netbird peer.
Author
Owner

@renne commented on GitHub (Apr 12, 2026):

Building on my previous comment: the workflow I described implies a broader architectural change worth naming explicitly — merging the Reverse Proxy into the Netbird client.

Current state:

  • netbird-proxy is a separate binary with its own lightweight WireGuard tunnel management
  • It is not a full Netbird peer; it needs a separate deployment (container, config, etc.)
  • netbird expose already added ephemeral per-peer proxying to the client itself

Proposed: Make the reverse proxy an optional capability of the regular Netbird client, activated via configuration or a flag (similar to how a peer opts into being a routing peer today). This means:

  1. Any enrolled peer can act as a proxy node — no separate binary to deploy.
  2. The peer already holds WireGuard tunnels to all other peers, so no duplicate tunnel infrastructure is needed.
  3. Certificate delivery uses the DNS-PERSIST-01 workflow from my previous comment: the management server issues a publicly-trusted cert for the peer's FQDN and pushes it to the peer via the existing gRPC channel.
  4. The peer serves HTTPS locally with that cert — without requiring internet exposure. Private services get trusted certificates purely within the overlay network.

This would directly solve the core problem in this issue: browsers warn because there's no trusted cert for peer.netbird.domain. With the client acting as proxy and receiving a cert from management, internal HTTPS "just works" on all platforms, including iOS and Android (publicly-trusted cert, no custom CA to install).

The netbird expose command is already a step in this direction (ephemeral, local ports). Making it permanent, cert-aware, and targeting remote peers/resources would complete the picture.

<!-- gh-comment-id:4231319043 --> @renne commented on GitHub (Apr 12, 2026): Building on my previous comment: the workflow I described implies a broader architectural change worth naming explicitly — **merging the Reverse Proxy into the Netbird client**. **Current state:** - `netbird-proxy` is a separate binary with its own lightweight WireGuard tunnel management - It is not a full Netbird peer; it needs a separate deployment (container, config, etc.) - `netbird expose` already added ephemeral per-peer proxying to the client itself **Proposed:** Make the reverse proxy an optional capability of the regular Netbird client, activated via configuration or a flag (similar to how a peer opts into being a routing peer today). This means: 1. Any enrolled peer can act as a proxy node — no separate binary to deploy. 2. The peer already holds WireGuard tunnels to all other peers, so no duplicate tunnel infrastructure is needed. 3. Certificate delivery uses the DNS-PERSIST-01 workflow from my previous comment: the management server issues a publicly-trusted cert for the peer's FQDN and pushes it to the peer via the existing gRPC channel. 4. The peer serves HTTPS locally with that cert — **without requiring internet exposure**. Private services get trusted certificates purely within the overlay network. This would directly solve the core problem in this issue: browsers warn because there's no trusted cert for `peer.netbird.domain`. With the client acting as proxy and receiving a cert from management, internal HTTPS "just works" on all platforms, including iOS and Android (publicly-trusted cert, no custom CA to install). The `netbird expose` command is already a step in this direction (ephemeral, local ports). Making it permanent, cert-aware, and targeting remote peers/resources would complete the picture.
Sign in to join this conversation.
No Label feature-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#11544