mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-31 12:01:29 +02:00
[management,proxy] Add per-provider skip_tls_verification for agent-network (#6630)
* [management,proxy] Add per-provider skip_tls_verification for agent-network Let agent-network providers opt into skipping upstream TLS verification for self-hosted / internal gateways behind a private or self-signed cert. - provider: add SkipTLSVerification (persisted via AutoMigrate) with request/response mapping (nil on update preserves, explicit false clears). - openapi: skip_tls_verification on the provider request + response; types regenerated. - synthesizer: carry the flag into the llm_router route config so it reaches the proxy. - proxy: llm_router sets it on the UpstreamRewrite mutation, and the reverse proxy applies roundtrip.WithSkipTLSVerify per selected route when forwarding upstream (the router dials per provider, so a per-target flag alone wouldn't cover it). - tests: synthesizer route config carries the flag, router rewrite propagates it, and the request/response round-trip incl. update semantics. * [e2e] Validate per-provider skip_tls_verification end to end Add a self-signed HTTPS upstream (nginx) to the harness and a test that provisions two providers on that same upstream — one with skip_tls_verification=true, one false — behind one proxy + client. The skip=true provider's chat reaches the upstream (200); the skip=false provider's fails the TLS handshake (5xx). Same upstream, opposite outcome, which proves the flag is honoured per provider (a single target-level flag could not, since all of an account's providers share one synthesised target). * [e2e] WaitProxyPeer: require >=1 connected peer, not exact 1/1 Each proxy container registers a fresh WireGuard key and its peer is not removed on teardown, so proxy peers from earlier tests linger in the account as disconnected. WaitProxyPeer matched the exact string "1/1 Connected", which failed once a second proxy-using test ran in the same package (status "1/2"). Parse the "Peers count: X/Y Connected" line and wait for X>=1 instead: only the live proxy can be connected, and the caller's subsequent chat is the real end-to-end assertion. Fixes the CI failure of TestProviderSkipTLSVerification (runs after TestProvidersMatrix).
This commit is contained in:
@@ -59,6 +59,10 @@ type ProviderRoute struct {
|
||||
// (instead of the static AuthHeaderValue) — so the gateway holds a durable
|
||||
// Vertex credential rather than a 1-hour token.
|
||||
GCPServiceAccountKeyB64 string `json:"gcp_sa_key_b64,omitempty"`
|
||||
// SkipTLSVerify disables upstream TLS certificate verification when dialing
|
||||
// this route's upstream. For self-hosted / internal gateways behind a
|
||||
// private or self-signed certificate.
|
||||
SkipTLSVerify bool `json:"skip_tls_verify,omitempty"`
|
||||
}
|
||||
|
||||
// Config is the on-wire configuration accepted by the factory. An
|
||||
|
||||
@@ -615,8 +615,9 @@ func (m *Middleware) allowWithRoute(route ProviderRoute, userGroups []string) *m
|
||||
// path is silently dropped and the gateway returns a 4xx for
|
||||
// the malformed URL. Empty value leaves the original
|
||||
// target's path untouched.
|
||||
Path: route.UpstreamPath,
|
||||
StripHeaders: append([]string(nil), strippedAuthHeaders...),
|
||||
Path: route.UpstreamPath,
|
||||
StripHeaders: append([]string(nil), strippedAuthHeaders...),
|
||||
SkipTLSVerify: route.SkipTLSVerify,
|
||||
}
|
||||
authValue := route.AuthHeaderValue
|
||||
if route.GCPServiceAccountKeyB64 != "" {
|
||||
|
||||
@@ -107,6 +107,41 @@ func TestRouter_HappyPath(t *testing.T) {
|
||||
assert.Equal(t, "allow", dec, "decision metadata must be allow on a match")
|
||||
}
|
||||
|
||||
func TestRouter_SkipTLSVerifyPropagates(t *testing.T) {
|
||||
base := ProviderRoute{
|
||||
ID: "internal-gw",
|
||||
Models: []string{"gpt-4o"},
|
||||
AllowedGroupIDs: []string{defaultTestGroup},
|
||||
UpstreamScheme: "https",
|
||||
UpstreamHost: "gateway.internal",
|
||||
AuthHeaderName: "Authorization",
|
||||
AuthHeaderValue: "Bearer sk-test-123",
|
||||
}
|
||||
|
||||
t.Run("enabled", func(t *testing.T) {
|
||||
route := base
|
||||
route.SkipTLSVerify = true
|
||||
mw := New(Config{Providers: []ProviderRoute{route}})
|
||||
|
||||
out, err := mw.Invoke(context.Background(), newInputWithModel("gpt-4o"))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out.Mutations, "matched route must emit mutations")
|
||||
require.NotNil(t, out.Mutations.RewriteUpstream, "matched route must emit upstream rewrite")
|
||||
assert.True(t, out.Mutations.RewriteUpstream.SkipTLSVerify,
|
||||
"skip_tls_verify on the route must ride on the upstream rewrite")
|
||||
})
|
||||
|
||||
t.Run("default off", func(t *testing.T) {
|
||||
mw := New(Config{Providers: []ProviderRoute{base}})
|
||||
|
||||
out, err := mw.Invoke(context.Background(), newInputWithModel("gpt-4o"))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out.Mutations.RewriteUpstream, "matched route must emit upstream rewrite")
|
||||
assert.False(t, out.Mutations.RewriteUpstream.SkipTLSVerify,
|
||||
"skip_tls_verify must default to false when the route does not set it")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouter_MissingModel(t *testing.T) {
|
||||
mw := New(Config{Providers: []ProviderRoute{{
|
||||
ID: "openai-prod",
|
||||
|
||||
@@ -243,6 +243,10 @@ type UpstreamRewrite struct {
|
||||
StripPathPrefix string
|
||||
AuthHeader *AuthHeader
|
||||
StripHeaders []string
|
||||
// SkipTLSVerify, when true, makes the proxy dial the rewritten upstream
|
||||
// without verifying its TLS certificate. Set by llm_router from the
|
||||
// provider's skip_tls_verification for self-hosted / internal gateways.
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
// AuthHeader is a single name/value pair the proxy injects on the
|
||||
|
||||
@@ -346,6 +346,11 @@ func (p *ReverseProxy) forwardUpstream(respWriter http.ResponseWriter, r *http.R
|
||||
r.Host = effectiveURL.Host
|
||||
applyUpstreamHeaders(r, upstreamRewrite)
|
||||
stripUpstreamPathPrefix(r, upstreamRewrite.StripPathPrefix)
|
||||
// A router-selected route (e.g. agent-network provider) can opt into
|
||||
// skipping upstream TLS verification per its provider config.
|
||||
if upstreamRewrite.SkipTLSVerify {
|
||||
ctx = roundtrip.WithSkipTLSVerify(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
rp := &httputil.ReverseProxy{
|
||||
|
||||
Reference in New Issue
Block a user