From a6370808aee06cfcdac60ba20b7cbddedf60f5f7 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:13:43 -0300 Subject: [PATCH] fix(ssrf): disable HTTP/2 for some specific cases BE-13121 (#2996) --- api/http/proxy/factory/agent.go | 1 + api/http/proxy/factory/kubernetes/agent_transport.go | 5 ++++- pkg/libhttp/ssrf/builder.go | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/api/http/proxy/factory/agent.go b/api/http/proxy/factory/agent.go index fc252536df..46e00333e8 100644 --- a/api/http/proxy/factory/agent.go +++ b/api/http/proxy/factory/agent.go @@ -55,6 +55,7 @@ func (factory *ProxyFactory) NewAgentProxy(endpoint *portainer.Endpoint) (*Proxy innerTransport = ssrf.NewInternalTransport(tlsConfig) } else { innerTransport = ssrf.NewTransport(tlsConfig) + innerTransport.Protocols = ssrf.HTTP1Only() } } else if endpointutils.IsEdgeEndpoint(endpoint) { innerTransport = ssrf.NewInternalTransport(nil) diff --git a/api/http/proxy/factory/kubernetes/agent_transport.go b/api/http/proxy/factory/kubernetes/agent_transport.go index 1cdfd126c5..42266c080e 100644 --- a/api/http/proxy/factory/kubernetes/agent_transport.go +++ b/api/http/proxy/factory/kubernetes/agent_transport.go @@ -23,9 +23,12 @@ func NewAgentTransport(signatureService portainer.DigitalSignatureService, token return nil, err } + httpTransport := ssrf.NewTransport(tlsConfig) + httpTransport.Protocols = ssrf.HTTP1Only() + transport := &agentTransport{ baseTransport: newBaseTransport( - ssrf.NewTransport(tlsConfig), + httpTransport, tokenManager, endpoint, k8sClientFactory, diff --git a/pkg/libhttp/ssrf/builder.go b/pkg/libhttp/ssrf/builder.go index efcd1fb544..697047b11c 100644 --- a/pkg/libhttp/ssrf/builder.go +++ b/pkg/libhttp/ssrf/builder.go @@ -44,6 +44,15 @@ func WrapDefaultTransport() bool { return true } +// HTTP1Only returns a Protocols value that enables HTTP/1.x and disables HTTP/2. +// Use this to assign to Transport.Protocols — a nil Protocols defaults to both +// HTTP/1 and HTTP/2, so the field must be non-nil to restrict to HTTP/1 only. +func HTTP1Only() *http.Protocols { + p := new(http.Protocols) + p.SetHTTP1(true) + return p +} + // applySSRF sets the SSRF-filtering DialContext on t when the global dialer is active. func applySSRF(t *http.Transport) { d := globalDialer.Load()