A simple Go library that detects protocol automatically:
- Support for PROXY and PROXYv2 allows detecting the real user's IP when, for example, using AWS elastic load balancers. The fact the protocol is detected automatically allows the daemon to work even before ELB is properly configured, and avoid rejecting requests by mistake.
- Automatic TLS support allows using a single port for SSL and non-SSL traffic, and simplifies configuration.
- Allows creating listener for specific TLS negociated protocols, allowing a single port to be used for many things easily.
This library was used in some of my projects, I've cleaned it up and licensed it under the MIT License since it's small and useful. Pull requests welcome.
It is written to work with protocols where the client sends the first data, and it expects the client to send at least 16 bytes. This works nicely with HTTP (GET / HTTP/1.0\r\n
is exactly 16 bytes), SSL, etc. but may not work with protocols such as POP3, IMAP or SMTP where the server is expected to send the first bytes unless TLS is required. In this case using the ForceTLS
filter only allows to still benefit from the TLS NextProto routing.
Use magictls.Listen()
to create sockets the same way you would use tls.Listen()
.
socket, err := magictls.Listen("tcp", ":8080", tlsConfig)
if err != nil {
...
}
log.Fatal(http.Serve(socket, handler))
The created listener can receive various configurations. For example if you need to force all connections to be TLS and only want to use PROXY protocol detection:
socket, err := magictls.Listen("tcp", ":8443", tlsConfig)
if err != nil {
...
}
socket.Filters = []magictls.Filter{magictls.DetectProxy, magictls.ForceTLS}
log.Fatal(http.Serve(socket, handler))
It is also possible to implement your own filters.
Depending on your provider, you may need to allow more than the local IPs for PROXY protocol.
For example Google Cloud's global load balancer uses a wider range of IPs that may come with PROXY requests and need this to be called:
magictls.AddAllowedProxies("35.191.0.0/16", "130.211.0.0/22", "2600:2d00:1:b029::/64", "2600:2d00:1:1::/64")
magictls.AddAllowedProxiesSpf("_cloud-eoips.googleusercontent.com")
This can be used with autocert too for automatic TLS certificates. Note that in this case you are required to have a listener on port 443.
// initialize autocert structure
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("domain", "domain2"),
Cache: autocert.DirCache("/tmp"), // use os.UserCacheDir() to find where to put that
}
// grab autocert TLS config
cfg := m.TLSConfig()
// you may want to add to cfg.NextProtos any protocol you want to handle with ProtoListener. Be careful to not overwrite it.
cfg.NextProtos = append(cfg.NextProtos, "my-proto")
// standard listen
socket, err := magictls.Listen("tcp", ":443", cfg)
if err != nil {
...
}
...