// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package proxyutil import ( "errors" "fmt" "net" "sync" "time" "github.com/hashicorp/go-secure-stdlib/parseutil" sockaddr "github.com/hashicorp/go-sockaddr" proxyproto "github.com/pires/go-proxyproto" ) // ProxyProtoConfig contains configuration for the PROXY protocol type ProxyProtoConfig struct { sync.RWMutex Behavior string AuthorizedAddrs []*sockaddr.SockAddrMarshaler `json:"authorized_addrs"` } func (p *ProxyProtoConfig) SetAuthorizedAddrs(addrs interface{}) error { aa, err := parseutil.ParseAddrs(addrs) if err != nil { return err } p.AuthorizedAddrs = aa return nil } // WrapInProxyProto wraps the given listener in the PROXY protocol. If behavior // is "use_if_authorized" or "deny_if_unauthorized" it also configures a // SourceCheck based on the given ProxyProtoConfig. In an error case it returns // the original listener and the error. func WrapInProxyProto(listener net.Listener, config *ProxyProtoConfig) (net.Listener, error) { config.Lock() defer config.Unlock() var newLn *proxyproto.Listener switch config.Behavior { case "use_always": newLn = &proxyproto.Listener{ Listener: listener, ReadHeaderTimeout: 10 * time.Second, } case "allow_authorized", "deny_unauthorized": newLn = &proxyproto.Listener{ Listener: listener, ReadHeaderTimeout: 10 * time.Second, Policy: func(addr net.Addr) (proxyproto.Policy, error) { config.RLock() defer config.RUnlock() sa, err := sockaddr.NewSockAddr(addr.String()) if err != nil { return proxyproto.REJECT, fmt.Errorf("error parsing remote address: %w", err) } for _, authorizedAddr := range config.AuthorizedAddrs { if authorizedAddr.Contains(sa) { return proxyproto.USE, nil } } if config.Behavior == "allow_authorized" { return proxyproto.IGNORE, nil } return proxyproto.REJECT, errors.New(`upstream connection not trusted proxy_protocol_behavior is "deny_unauthorized"`) }, } default: return listener, fmt.Errorf("unknown behavior type for proxy proto config") } return newLn, nil }