net/http Http Server - fail with Proxy Protocol header (v1/v2)

228 views
Skip to first unread message

HappyTobi

unread,
Apr 15, 2024, 12:47:24 PM4/15/24
to golang-nuts

Dear Gophers,

I would like to bring to your attention.
There is an “issue” with the Go standard implementation when handling HTTP requests that are extended by the proxy protocol v1 or v2.

While the simple HTTP server works fine with regular requests, it fails when a proxy protocol is added.


Example:

Simple http server:

package main

 

import (

    "fmt"

    "net/http"

)

 

func hello(w http.ResponseWriter, req *http.Request) {

    fmt.Fprintf(w"hello world\n")

}

 

func main() {

    http.HandleFunc("/hello"hello)

    http.ListenAndServe(":8080"nil)

}

The server is working fine when you do something like:

curl -kv http://localhost:8080/hello

 

But the implementation is failing when you add a proxy protocol (v1) to the tcp request.

curl -kv --haproxy-protocol http://localhost:8080/hello

 

The issue arises because the implementation fails to read the HTTP message, as the message starts with the proxy protocol. 
Go read request function: https://github.com/golang/go/blob/91c04826723a10f6778a935e743a34de81312489/src/net/http/request.go#L1068 

The proxy protocol is widely used, and it would be beneficial for the Go standard implementation to handle such requests.

 

I have previously posted two issues on this topic, but neither has been accepted. I would like to open a discussion on this topic and work towards implementing a solution that could be merged into the Go standard library. 
Your input and feedback is more than welcome!

Thank you all.

 

Github issue links that I posted:

net/http: Http Server (request) is not working with enabled Proxy Protocol · Issue #64365 · golang/go (github.com)

proposal: net/tspsock: filter/interceptor - concept with default implementation for proxyprotocol (v1/v2) · Issue #65078 · golang/go (github.com)

Brian Candler

unread,
Apr 15, 2024, 2:41:50 PM4/15/24
to golang-nuts
Your answer was given here:

In other words:
- net/http handles HTTP
- go-proxyprotocol handles the proxy protocol
- you combine the two to get the behaviour you want

Orthogonal pieces which handle their own responsibilities are a Good Thing™ IMO.

If you want to wrap this up in a library which has the same API as net/http but implements proxy protocol, you are free to do so (and publish it, if you wish). However, there is a very high bar for putting functionality into the core Go library, because the backwards compatibility promise means it has to be supported forever.
 
I have previously posted two issues on this topic, but neither has been accepted

I think that answers your question.

HappyTobi

unread,
Apr 15, 2024, 4:06:37 PM4/15/24
to golang-nuts
Hi,

thanks for the answer, but thats not the point.
I think it's fine to use a library or implement something to add something to the http layer to parse the proxy protocol.

My point is that a http server that is using the standard library always  reply with an "HTTP/1.1 400 Bad Request" when the proxy protocol is part of the tcp package.
So the net/http should just handle the http request like they do without the proxy protocol.
If someone needs to parse the proxy protocol, it's fine to implement or add a 3rd party lib, but I think the "default" should just work.

Brian Candler

unread,
Apr 15, 2024, 4:43:50 PM4/15/24
to golang-nuts
> My point is that a http server that is using the standard library always  reply with an "HTTP/1.1 400 Bad Request" when the proxy protocol is part of the tcp package.

That's the correct response, because the request is not compliant with HTTP.

> So the net/http should just handle the http request like they do without the proxy protocol.

Why should it do that? It would mask a configuration error - either that the upstream device is sending with proxy protocol when it should be sending plain HTTP, or that the downstream server has not been configured correctly to interpret the proxy protocol.

At worst, it would open security problems, when the upstream proxy *thinks* it is successfully passing along source IP address information, but the downstream server is ignoring it.

Can you point to any other well-known HTTP server implementation which ignores inserted PROXY headers like this? I know that Apache doesn't: you have to configure each vhost to either use or not use proxy protocol.

And whilst HTTP is a text protocol (and can distingush between PROXY and GET/POST/PUT etc), what about TLS?

HappyTobi

unread,
Apr 15, 2024, 5:19:34 PM4/15/24
to golang-nuts
Ok I got your point
Thanks for the explanation.

I also checked some other well-known HTTP servers an all of them needs to be configured to pass he proxy protocol (nginx, envoy ) so it seams the right way to explicit configure the webserver.

Thank you very much!

Eli

unread,
Apr 25, 2024, 9:30:51 PM4/25/24
to golang-nuts
> And whilst HTTP is a text protocol (and can distingush between PROXY and GET/POST/PUT etc), what about TLS?

Proxy protocol is sent as the first bytes on wire after TCP is established, not carried via HTTP headers, so it is easily distinguishable from TLS. 

I agree with all other points, but wanted to mention that. 🙂 

-eli

Brian Candler

unread,
Apr 26, 2024, 3:40:23 AM4/26/24
to golang-nuts
Really I was unsure whether you can guarantee that the first few bytes of a TLS "client hello" can never happen to be the ASCII characters P R O X Y ....  As a binary protocol I think it's unlikely to occur, but I've not attempted to prove it.

Eli Lindsey

unread,
Apr 26, 2024, 7:21:37 AM4/26/24
to Brian Candler, golang-nuts
The first few bytes on a new TLS connection will be the record layer bytes denoting a handshake and the TLS version field, so 0x160301 or 0x160303. ASCII-based proxy protocol v1 will start out 0x5052 etc, and binary-based proxy protocol v2 has its own initial 12 byte signature of 0x0D0A0D0A etc.(documented here https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). You could handle all three plus plaintext http on the same port if you felt so inclined.


-eli

-- 
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/Uvq-BlNLSOM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4e914729-9d97-4822-83e6-6bceee907b2cn%40googlegroups.com.

Reply all
Reply to author
Forward
0 new messages