Categorygithub.com/ruscalworld/go-haproxy
repositorypackage
0.0.0-20220708064143-2f1fc38d9308
Repository: https://github.com/ruscalworld/go-haproxy.git
Documentation: pkg.go.dev

# README

go-haproxy

A simple library that allows you to add support for HAProxy protocol to your project. It allows you to get real IP addresses of clients connected via HAProxy or forward IP addresses to other servers that support HAProxy protocol. This library supports only Version 2 of protocol as most other apps that ever supported HAProxy.

Installation

You can install go-haproxy using Go modules:

go get github.com/ruscalworld/go-haproxy

A few words on protocol

HAProxy's protocol is quite simple. All that HAProxy does to forward client address to the target server is sends some data after the connection is being established and before any other data is sent. This data forms a so-called PROXY protocol header. It contains such information as used protocol, source and destination IP address and ports, etc.

Usage

You can use this library for both: parsing and serializing HAProxy headers.

Serializing

Let's assume that you are building a TCP proxy server that should forward client address to a backend server. In this case you should just initialize a new instance of haproxy.Header structure, fill it with data and send it to your backend server. Error handling is omitted in this example for simplicity.

package main

import (
	"net"

	"github.com/ruscalworld/go-haproxy"
)

func main() {
	// You should obtain this data from client's connection
	clientAddress := &net.TCPAddr{
		IP:   net.ParseIP("123.123.123.123"),
		Port: 55555,
	}

	serverAddress := &net.TCPAddr{
		IP:   net.ParseIP("127.0.0.1"),
		Port: 8080,
	}

	// WrapAddress is a convenience function that makes a ProxyAddress that you
	// can use in Header struct
	address, _ := haproxy.WrapAddress(clientAddress, serverAddress)

	header := haproxy.Header{
		// In most cases you should use PROXY command which tells the target server
		// that your header contains a client address, and you are going to forward
		// the entire connection
		Command: haproxy.CommandPROXY,

		// Address data that should be forwarded to the backend server, includes
		// source and destination address
		ProxyAddress: address,
	}

	// Connect to your backend
	conn, _ := net.Dial("tcp", serverAddress.String())

	// And write the PROXY protocol header
	header.WriteTo(conn)

	// Then handle connection as you wish
}

Parsing

In cases when you want to add support for PROXY protocol to your backend service which is running behind proxy, you should just read the PROXY header before handling the connection. Make sure that only trusted proxies can send you a header. Error handling is omitted in this example for simplicity.

package main

import (
	"net"

	"github.com/ruscalworld/go-haproxy"
)

func main() {
	server, _ := net.Listen("tcp", ":8080")

	for {
		conn, _ := server.Accept()
		remoteAddr := conn.RemoteAddr().String()

		// If you are not going to configure the firewall of some kind that will allow connections to your service only 
		// from trusted addresses, you must make check that will prevent your service from reading PROXY headers sent 
		// by untrusted proxies.
		if isTrusted(remoteAddr) {
			// Read the PROXY header
			var header haproxy.Header
			header.ReadFrom(conn)

			// This returns a struct which contains forwarded addresses
			// You can also use IPv6Address and UnixAddress if you are using corresponding listener
			addr := header.ProxyAddress.(*haproxy.IPv4Address)
			remoteAddr = addr.SourceAddr.String()
		}
	}
}

func isTrusted(addr string) bool {
	// Add your logic if necessary
	return true
}