Skip to content

[server] Add an option to disable cert verification for external client requests#5216

Closed
anudeepND wants to merge 4 commits intonetbirdio:mainfrom
anudeepND:disable-cert-verification
Closed

[server] Add an option to disable cert verification for external client requests#5216
anudeepND wants to merge 4 commits intonetbirdio:mainfrom
anudeepND:disable-cert-verification

Conversation

@anudeepND
Copy link

@anudeepND anudeepND commented Jan 30, 2026

Describe your changes

This PR creates a central HTTP client with an option to disable SSL cert validation when making external requests (for example, downloading GeoIP database, check release versions) when NB_DISABLE_CERT_VALIDATION env variable is set to true.

This is useful for self-hosted environments using internal CAs or self-signed certificates which is common when using firewalls that intercepts all the traffic.

Changes:

  1. Created util.NewTransport() and util.NewHTTPClient() to standardize HTTP transport configuration
  2. If NB_DISABLE_CERT_VALIDATION=true, the transport is configured with InsecureSkipVerify: true which will disable certificate validation
  3. If it is set to false which is by default, secure transport is used just like previous versions
  4. The new config is used in GeoIP download logic, OIDC requests, version check and IDP managers

Issue ticket number and link

Fixes #5200

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

netbirdio/docs#582

Summary by CodeRabbit

  • Infrastructure Improvements
    • Standardized HTTP client usage across the platform for more consistent and resilient network calls.
    • Added configurable transport behavior (including optional certificate-validation bypass via environment variable) for specialized deployments.
    • Improved HTTP resource handling to ensure responses are properly closed and connections are managed.

✏️ Tip: You can customize this high-level summary in your review settings.

@CLAassistant
Copy link

CLAassistant commented Jan 30, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

This PR centralizes HTTP client creation via a new util/http.go (NewTransport, NewHTTPClient), injects HTTP clients into geolocation and IDP components, and replaces direct net/http calls with the new clients across multiple modules. Function signatures for geolocation constructors and internal helpers were updated to accept *http.Client.

Changes

Cohort / File(s) Summary
HTTP Client Utility
util/http.go
Adds NewTransport() (clones default transport, optional TLS validation disable via NB_DISABLE_CERT_VALIDATION) and NewHTTPClient() (uses NewTransport, 10s timeout).
Geolocation Module
management/server/geolocation/geolocation.go, management/server/geolocation/database.go, management/server/geolocation/utils.go
Threads *http.Client through geolocation: NewGeolocation(ctx,dataDir,autoUpdate,*http.Client), getDatabaseFilename(client,...), loadGeolocationDatabases(ctx,client,...), and utility functions now accept and use the injected client (client.Head / client.Get). Added resp.Body.Close() in downloads.
Management Init & Wiring
management/cmd/management.go, management/internals/server/modules.go, management/server/instance/manager.go, management/server/identity_provider.go, version/update.go
Replace direct net/http uses with util.NewHTTPClient() or construct http.Client with util.NewTransport(); pass clients into geolocation and other initializers.
IDP Managers
management/server/idp/*.go (auth0, authentik, azure, dex, google_workspace, jumpcloud, keycloak, okta, pocketid, zitadel)
Replace transport creation from http.DefaultTransport.Clone() to util.NewTransport(); auth0 introduces separate manager and credentials clients with distinct transports/timeouts.
Shared Clients & JWT
shared/auth/jwt/validator.go, shared/management/client/rest/client.go
Switch to util.NewHTTPClient() for outbound requests; replace direct http.Get / default client with new factory.
Tests
management/server/http/handlers/policies/geolocation_handler_test.go
Updated test callsites to pass an *http.Client (http.DefaultClient) into NewGeolocation to match new signature.

Sequence Diagram(s)

sequenceDiagram
    participant Init as App Init
    participant Geo as Geolocation Service
    participant Util as util.NewHTTPClient / Transport
    participant CDN as External Host (pkgs.netbird.io)

    Init->>Util: NewTransport() / NewHTTPClient()
    Init->>Geo: NewGeolocation(ctx,dataDir,autoUpdate, *http.Client)
    Geo->>Util: client.Head/Get(database URL)
    Util->>CDN: TCP/TLS request
    CDN-->>Util: TLS response / file
    Util-->>Geo: HTTP response
    Geo-->>Init: geolocation ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • pascal-fischer
  • pappz
  • crn4

Poem

🐰 I hopped through transports, small and spry,

Built a client that won't shy or cry,
Injected deps where downloads roam,
Now certificates find a home,
A rabbit's patch—light, quick, and shy.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding an option to disable cert verification for external client requests, which is the core objective of this PR.
Description check ✅ Passed The description comprehensively covers changes made, includes the linked issue reference (#5200), specifies the refactor checkbox, indicates documentation was added, and provides the docs PR URL (netbirdio/docs#582).
Linked Issues check ✅ Passed The PR fully addresses issue #5200 by implementing environment-driven certificate validation bypass through NB_DISABLE_CERT_VALIDATION for GeoIP downloads, OIDC, version checks, and IDP managers.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the centralized HTTP client/transport with certificate validation bypass. No unrelated refactoring or feature additions are present beyond the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
management/server/geolocation/geolocation.go (1)

3-8: ⚠️ Potential issue | 🟡 Minor

Add nil guard for HTTP client to prevent panics.

If a caller passes nil, downstream calls to client.Head() and client.Get() will panic. Default to http.DefaultClient to keep the API safe.

🛡️ Suggested safeguard
 func NewGeolocation(ctx context.Context, dataDir string, autoUpdate bool, httpClient *http.Client) (Geolocation, error) {
+	if httpClient == nil {
+		httpClient = http.DefaultClient
+	}
 	mmdbGlobPattern := filepath.Join(dataDir, mmdbPattern)
🧹 Nitpick comments (4)
util/http.go (2)

19-25: Set explicit TLS MinVersion when creating tls.Config.

When TLSClientConfig is nil and you create an empty &tls.Config{}, you lose the default transport's TLS settings. While Go defaults to TLS 1.2 for clients, explicitly setting MinVersion ensures consistent security posture and satisfies security scanners.

Also consider checking case-insensitively (e.g., strings.EqualFold) for the environment variable value since users might set TRUE or True.

🔒 Proposed fix
 func NewTransport() *http.Transport {
 	tr := http.DefaultTransport.(*http.Transport).Clone()
-	if os.Getenv(disableCertValidationKey) == "true" {
+	if strings.EqualFold(os.Getenv(disableCertValidationKey), "true") {
 		log.Warnf("HTTP client certificate validation is disabled")
 		if tr.TLSClientConfig == nil {
-			tr.TLSClientConfig = &tls.Config{}
+			tr.TLSClientConfig = &tls.Config{MinVersion: tls.VersionTLS12}
 		}
 		tr.TLSClientConfig.InsecureSkipVerify = true
 	}
 	return tr
 }

You'll also need to add "strings" to the imports.


20-20: Warning log fires on every call to NewTransport().

Multiple IDP managers and other components call NewTransport(), so this warning will appear repeatedly in logs when cert validation is disabled. Consider logging once at startup (e.g., using sync.Once) or at a lower log level after the first occurrence.

management/server/geolocation/utils.go (1)

179-194: Consider adding defensive check for Content-Disposition header.

The function accesses resp.Header["Content-Disposition"][0] directly on Line 187, which will panic if the header is missing or empty. While this is pre-existing code, the function signature change provides an opportunity to add defensive handling.

🛡️ Optional: Add defensive check for missing header
 func getFilenameFromURL(client *http.Client, url string) (string, error) {
 	resp, err := client.Head(url)
 	if err != nil {
 		return "", err
 	}
 
 	defer resp.Body.Close()
 
+	cdHeader := resp.Header.Get("Content-Disposition")
+	if cdHeader == "" {
+		return "", fmt.Errorf("Content-Disposition header not found")
+	}
+
-	_, params, err := mime.ParseMediaType(resp.Header["Content-Disposition"][0])
+	_, params, err := mime.ParseMediaType(cdHeader)
 	if err != nil {
 		return "", err
 	}
 
 	filename := params["filename"]
 
 	return filename, nil
 }
management/internals/server/modules.go (1)

3-9: Prefer util.NewHTTPClient() to centralize HTTP client setup.

Keeps transport defaults in one place and avoids duplicating client wiring here.

♻️ Suggested change
 import (
 	"context"
-	"net/http"
 	"os"
 
 	"github.com/netbirdio/netbird/util"
@@
-		transport := util.NewTransport()
-		httpClient := &http.Client{
-			Transport: transport,
-		}
+		httpClient := util.NewHTTPClient()

Also applies to: 43-48

@sonarqubecloud
Copy link

@braginini
Copy link
Collaborator

braginini commented Jan 30, 2026

@anudeepND What I don't understand is how accessing websites even work in this setting? It looks like some sort of malware

@anudeepND
Copy link
Author

anudeepND commented Jan 30, 2026

@braginini this kind of traffic inspection is pretty common I guess. I have seen this in multiple places with Fortient, Cisco and Paloalto which is mentioned in the issue.

They do DPI (deep packet inspection) by performing authorised MITM. (The VM will be auto configured to trust the root CA generated in the firewall)

Basically, whatever the request I send to external clients is intercepted by the firewall, the firewall will connect to the external endpoint on my behalf with the actual CA cert, it then creates a new certificate for the external end point signed by its own internal CA.

The actual MITM attack is also done in a similar manner so that is the reason the flag is false by default. Users can set it to true for deploying netbird in this kind of strict environment.

Let me know your POV on this

@braginini
Copy link
Collaborator

@anudeepND

I understand the point but I think that it could be solved with using a proxy on a VM or docker. We can describe this case in our docs, would you mind testing this?
https://docs.docker.com/engine/cli/proxy/

P.S. Don't get me wrong, I appreciate your contribution, but I am a bit hesitant to commit code that explicitly promotes not secure behaviour. That being said, I'm excited about your use case and would love to have a chat. Could you please ping me (Misha) on slack and we set up a call: https://docs.netbird.io/slack-url

@anudeepND
Copy link
Author

@braginini I agree with your concerns, will ping you in slack to have a conversation about this. I will also be able to show my use case as well

@mlsmaycon
Copy link
Collaborator

@anudeepND can you try setting the proxy via HTTP_PROXY and HTTPS_PROXY environment variables in the management container?

@anudeepND
Copy link
Author

@mlsmaycon Docker proxy will not work in this case, the root CA is installed at the OS level. So even when Docker tries to perform SSL handshake through the tunnel, the firewall will intercept the traffic which will cause the same issue where cert verification fails in netbird

Also, is there any other communication channel other than slack?

@anudeepND anudeepND closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

failed to verify certificate when downloading geoip from pkgs.netbird.io

4 participants