mirror of
https://github.com/portainer/portainer.git
synced 2026-08-07 11:04:49 +00:00
fix(ssrf): use the net.Dialer from http.DefaultTransport BE-13245 (#3242)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@@ -56,20 +57,28 @@ type AllowListService interface {
|
||||
ReadParsed(id portainer.AllowListKey) (*portainer.ParsedAllowList, error)
|
||||
}
|
||||
|
||||
type dialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
type safeDialer struct {
|
||||
base net.Dialer
|
||||
service AllowListService
|
||||
base dialFunc
|
||||
}
|
||||
|
||||
var globalDialer atomic.Pointer[safeDialer]
|
||||
|
||||
// Configure initializes the global SSRF policy with the allow list data service.
|
||||
// It captures http.DefaultTransport's own dial function.
|
||||
func Configure(svc AllowListService) error {
|
||||
if svc == nil {
|
||||
return errors.New("unable to configure ssrf: service must not be nil")
|
||||
}
|
||||
|
||||
globalDialer.Store(&safeDialer{service: svc})
|
||||
baseTransport, ok := http.DefaultTransport.(*http.Transport)
|
||||
if !ok {
|
||||
return errors.New("unable to configure ssrf: http.DefaultTransport is not an *http.Transport")
|
||||
}
|
||||
|
||||
globalDialer.Store(&safeDialer{service: svc, base: baseTransport.DialContext})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -117,7 +126,7 @@ func CheckURL(ctx context.Context, rawURL string) error {
|
||||
}
|
||||
|
||||
// DialContext resolves addr, validates all resolved IPs against the allowlist policy,
|
||||
// then dials using the first resolved IP to prevent DNS rebinding attacks.
|
||||
// then dials using d.base with the first resolved IP to prevent DNS rebinding attacks.
|
||||
func (d *safeDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
allowList, err := d.service.ReadParsed(portainer.AllowListSSRF)
|
||||
if err != nil {
|
||||
@@ -125,7 +134,7 @@ func (d *safeDialer) DialContext(ctx context.Context, network, addr string) (net
|
||||
}
|
||||
|
||||
if allowList.Mode == portainer.SSRFModeOff {
|
||||
return d.base.DialContext(ctx, network, addr)
|
||||
return d.base(ctx, network, addr)
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
@@ -147,7 +156,7 @@ func (d *safeDialer) DialContext(ctx context.Context, network, addr string) (net
|
||||
dialTarget := net.JoinHostPort(resolved[0].IP.String(), port)
|
||||
|
||||
if allowList.Hosts[host] || matchesWildcard(host, allowList.Wilds) {
|
||||
return d.base.DialContext(ctx, network, dialTarget)
|
||||
return d.base(ctx, network, dialTarget)
|
||||
}
|
||||
|
||||
for _, a := range resolved {
|
||||
@@ -161,7 +170,7 @@ func (d *safeDialer) DialContext(ctx context.Context, network, addr string) (net
|
||||
}
|
||||
}
|
||||
|
||||
return d.base.DialContext(ctx, network, dialTarget)
|
||||
return d.base(ctx, network, dialTarget)
|
||||
}
|
||||
|
||||
func (d *safeDialer) checkHost(ctx context.Context, host string) error {
|
||||
|
||||
Reference in New Issue
Block a user