From fcb9b02451818be541bd4abd17dec5be727a5613 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 7 Sep 2026 11:38:53 +0200 Subject: [PATCH] [client] Require privilege to relax TLS on a resolved upload destination requirePrivilegeForUploadURL returned early on an empty URL, which was correct while an empty URL meant "do not upload": there was no destination for --upload-bundle-insecure to weaken. Now an empty URL means "use the destination the management server published", so an unprivileged local caller could send upload=true with an empty URL and uploadInsecure=true and have the root daemon PUT the bundle to that destination with TLS verification disabled. Gate the insecure flag on the empty-URL path too. A named URL keeps its existing order, so a malformed one still reports InvalidArgument rather than a privilege error. --- client/server/debug_gate.go | 9 +++++++++ client/server/debug_gate_test.go | 3 +++ 2 files changed, 12 insertions(+) diff --git a/client/server/debug_gate.go b/client/server/debug_gate.go index 983a13aaf..a75d574cf 100644 --- a/client/server/debug_gate.go +++ b/client/server/debug_gate.go @@ -53,6 +53,15 @@ func uiLogOpener(id ipcauth.Identity, identified bool) debug.LogOpener { // refused for an unprivileged caller regardless of the host. func requirePrivilegeForUploadURL(ctx context.Context, rawURL string, insecure bool) error { if rawURL == "" { + // An empty URL is not "no upload": the daemon then resolves the + // destination the management server published. Relaxing TLS on the way + // there exposes the bundle exactly as naming the host outright would, so + // it needs the same privilege. + if insecure { + return denyPrivileged(ctx, + "uploading a debug bundle without transport security (--upload-bundle-insecure)", + ipcauth.ElevatedCommand("netbird debug bundle -U --upload-bundle-insecure")) + } return nil } diff --git a/client/server/debug_gate_test.go b/client/server/debug_gate_test.go index e958fc581..c677887eb 100644 --- a/client/server/debug_gate_test.go +++ b/client/server/debug_gate_test.go @@ -116,6 +116,9 @@ func TestRequirePrivilegeForUploadURL(t *testing.T) { rootAlso bool }{ {name: "no upload", url: "", unprivOK: true}, + // An empty URL resolves to the destination management published, so + // relaxing TLS towards it needs the same privilege as naming a host. + {name: "insecure with no URL", url: "", insecure: true, rootAlso: true}, {name: "default service", url: types.DefaultBundleURL, unprivOK: true}, {name: "default service, other path", url: "https://upload.debug.netbird.io/other", unprivOK: true}, {name: "loopback exfiltration endpoint", url: "https://127.0.0.1:8080/upload-url", rootAlso: true},