viaproxy

package module
v0.0.0-...-45bf782 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 13, 2018 License: MIT Imports: 6 Imported by: 0

README

Proxy Protocol support for Go net.Conn

GoDoc Go Report Card codebeat badge

Regular Go net doesn't support Proxy Protocol when being load balanced with this option enabled. This makes you loose the original remote address and will report the load balancer's address instead on net.Conn.RemoteAddr(). This package adds allows you to create net.Conn objects that know how to understand Proxy Protocol.

You can read more about this in my Proxy Protocol: what is it and how to use it with Go article.

Usage

In your server, you can do the following:

ln, err := net.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.Accept()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	pcn, err := viaproxy.Wrap(cn)
	if err != nil {
		log.Println("Wrap():", err)
		continue
	}

	log.Printf("remote address is: %v", pcn.RemoteAddr())
	log.Printf("local address is: %v", pcn.LocalAddr())
	log.Printf("proxy address is: %v", pcn.ProxyAddr())
	pcn.Close()
}

Given that one can forget about this, you can also do the following:

ln, err := viaproxy.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.Accept()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	// The connection should be safe to be converted to a *viaproxy.Conn
	// structure.
	pcn := conn.(*viaproxy.Conn)
	log.Printf("remote address is: %v", pcn.RemoteAddr())
	log.Printf("local address is: %v", pcn.LocalAddr())
	log.Printf("proxy address is: %v", pcn.ProxyAddr())
	pcn.Close()
}

In this case, Accept returns a generic net.Conn object. If you want to directly use a Conn object (which satisfies the net.Conn interface), you can use AcceptFromProxy instead:

ln, err := viaproxy.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.AcceptFromProxy()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	// The connection should be safe to be converted to a *viaproxy.Conn
	// structure.
	log.Printf("remote address is: %v", cn.RemoteAddr())
	log.Printf("local address is: %v", cn.LocalAddr())
	log.Printf("proxy address is: %v", cn.ProxyAddr())
	cn.Close()
}

Caveats

  • Only works with TCP connections.
  • Both endpoints of the connection must be compatible with proxy protocol.

License

See LICENSE.

Documentation

Overview

Package viaproxy provides the ability to manage connections that properly understand the Proxy Protocol defined by Willy Tarreau for HAProxy.

Regular net.Conn structures will return the "wrong" RemoteAddr when used behind a proxy: the remote address informed will be the one of the proxy, not the one from the client initiating the connection to the proxy. This package adds a wrapper for regular net.Conn that checks for the existence of the proxy protocol line and if present, return a net.Conn that reports the customer address when calling RemoteAddr. This wrapped connection can be casted to *viaproxy.Conn to add access to an additional ProxyAddr method that will return the proxy's address.

In order to use this extended connection type we can call Wrap on an existing connection:

ln, err := net.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.Accept()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	pcn, err := viaproxy.Wrap(cn)
	if err != nil {
		log.Println("Wrap():", err)
		continue
	}

	log.Printf("remote address is: %v", pcn.RemoteAddr())
	log.Printf("local address is: %v", pcn.LocalAddr())
	log.Printf("proxy address is: %v", pcn.ProxyAddr())
	pcn.Close()
}

Package viaproxy also provides a Listener struct that already returns viaproxy.Conn connections when calling AcceptFromProxy:

ln, err := viaproxy.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.AcceptFromProxy()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	log.Printf("remote address is: %v", cn.RemoteAddr())
	log.Printf("local address is: %v", cn.LocalAddr())
	log.Printf("proxy address is: %v", cn.ProxyAddr())
	cn.Close()
}

The Accept method in the Listener struct returns a generic net.Conn which can safely be casted to a viaproxy.Conn:

ln, err := viaproxy.Listen("tcp", *addr)
if err != nil {
	log.Fatal(err)
}

for {
	cn, err := ln.Accept()
	if err != nil {
		log.Println("ln.Accept():", err)
		continue
	}

	// The connection should be safe to be converted to a *viaproxy.Conn
	// structure.
	pcn := conn.(*viaproxy.Conn)
	log.Printf("remote address is: %v", pcn.RemoteAddr())
	log.Printf("local address is: %v", pcn.LocalAddr())
	log.Printf("proxy address is: %v", pcn.ProxyAddr())
	pcn.Close()
}

Using viaproxy.Conn objects whenever a net.Conn is expected should be safe in all cases. If you encounter an issue please send a bug report to https://github.com/inkel/viaproxy/issues

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is an implementation of net.Conn interface for TCP connections that come from a proxy that users the Proxy Protocol to communicate with the upstream servers.

func Wrap

func Wrap(cn net.Conn) (*Conn, error)

Wrap takes a net.Conn and returns a pointer to Conn that knows how to properly identify the remote address if it comes via a proxy that supports the Proxy Protocol.

Example
package main

import (
	"log"
	"net"

	"github.com/inkel/viaproxy"
)

func main() {
	// Listen on TCP port 8080 for connections coming from a proxy that sends
	// the Proxy Protocol header.
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	for {
		// Wait for a connection.
		cn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		pcn, err := viaproxy.Wrap(cn)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("remote address is: %v", pcn.RemoteAddr())
		log.Printf("local address is: %v", pcn.LocalAddr())
		log.Printf("proxy address is: %v", pcn.ProxyAddr())
		pcn.Close()
	}
}

func (*Conn) Close

func (c *Conn) Close() error

Close closes the connection.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Conn) ProxyAddr

func (c *Conn) ProxyAddr() net.Addr

ProxyAddr returns the proxy remote network address.

func (*Conn) Read

func (c *Conn) Read(b []byte) (int, error)

Read reads data from the connection.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline implements the Conn SetDeadline method.

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline implements the Conn SetReadDeadline method.

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements the Conn SetWriteDeadline method.

func (*Conn) Write

func (c *Conn) Write(b []byte) (int, error)

Write implements the Conn Write method.

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener is a wrap on net.Listener that returns wrapped Conn objects.

func Listen

func Listen(network, address string) (*Listener, error)

Listen returns a net.Listener that will wrap Accept so it returns net.Conn that know how to work with Proxy Protocol.

func (*Listener) Accept

func (l *Listener) Accept() (net.Conn, error)

Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.

Example
package main

import (
	"log"

	"github.com/inkel/viaproxy"
)

func main() {
	// Listen on TCP port 8080 for connections coming from a proxy that sends
	// the Proxy Protocol header.
	l, err := viaproxy.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	for {
		// Wait for a connection.
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		// The connection should be safe to be converted to a *viaproxy.Conn
		// structure.
		cn := conn.(*viaproxy.Conn)
		log.Printf("remote address is: %v", cn.RemoteAddr())
		log.Printf("local address is: %v", cn.LocalAddr())
		log.Printf("proxy address is: %v", cn.ProxyAddr())
		cn.Close()
	}
}

func (*Listener) AcceptFromProxy

func (l *Listener) AcceptFromProxy() (*Conn, error)

AcceptFromProxy accepts the next incoming call and returns the new connection.

Example
package main

import (
	"log"

	"github.com/inkel/viaproxy"
)

func main() {
	// Listen on TCP port 8080 for connections coming from a proxy that sends
	// the Proxy Protocol header.
	l, err := viaproxy.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	for {
		// Wait for a connection.
		cn, err := l.AcceptFromProxy()
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("remote address is: %v", cn.RemoteAddr())
		log.Printf("local address is: %v", cn.LocalAddr())
		log.Printf("proxy address is: %v", cn.ProxyAddr())
		cn.Close()
	}
}

func (*Listener) Addr

func (l *Listener) Addr() net.Addr

Addr returns the listener's network address, a *TCPAddr. The Addr returned is shared by all invocations of Addr, so do not modify it.

func (*Listener) Close

func (l *Listener) Close() error

Close stops listening on the TCP address. Already Accepted connections are not closed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL