caddy: v2&v1: HTTP/3 breaks support of getting remote IP and port
The version of caddy is 1.0.4 and 2.0beta14.
Config of caddy 2:
{
"admin": {
"disabled": true,
"listen": "",
"enforce_origin": false,
"origins": [""],
"config": {
"persist": false
}
},
"apps": {
"http": {
"servers": {
"h3-proxy": {
"listen": [":2"],
"routes": [{
"match": [{
"host": ["qwq.ren", "*.qwq.ren", "imvictor.tech", "*.imvictor.tech"]
}],
"handle": [{
"handler": "subroute",
"routes": [{
"handle": [{
"encodings": {
"brotli": {},
"gzip": {}
},
"handler": "encode"
}, {
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"Host": ["{http.request.host}"],
"X-Forwarded-For": ["{http.request.remote.host}"],
"X-Forwarded-Proto": ["{http.request.scheme}"],
"X-Real-IP": ["{http.request.remote.host}"],
"X-Victors-Test1": ["passed"]
}
}
},
"upstreams": [{
"dial": "127.0.0.1:80"
}]
}]
}]
}],
"terminal": true
}],
"tls_connection_policies": [{
"match": {
"sni": ["*.imvictor.tech", "qwq.ren", "*.qwq.ren", "imvictor.tech"]
},
"certificate_selection": {
"policy": "custom",
"tag": "main-cert"
}
}, {}],
"experimental_http3": true,
"automatic_https": {
"disable": true
}
}
}
},
"tls": {
"certificates": {
"load_files": [{
"certificate": "[HIDDEN].cer",
"key": "[HIDDEN].key",
"tags": ["main-cert"]
}]
}
}
}
}
Config of caddy 1.0.4:
https://qwq.ren:2 https://*.qwq.ren:2 https://imvictor.tech:2 https://*.imvictor.tech:2 {
gzip
proxy / http://127.0.0.1:80 {
header_upstream Host {host}
header_upstream X-Forwarded-Proto {scheme}
header_upstream X-Forwarded-Port {port}
header_upstream X-Forwarded-For {remote}
header_upstream X-Real-IP {remote}
header_upstream X-Victors-Test1 "passed"
insecure_skip_verify
}
tls [HIDDEN].cer [HIDDEN].key
}
Test HTTP/3 with curl 7.69.0-dev, it shows that some headers like X-Forwarded-For or X-Real-IP cannot be passed to the backend,
Victor@Victor-Mac:/tmp » curl-h3 -6 --http3 https://dns-main.imvictor.tech:2/__test/dump -H 'X-Victors-Test0: test'
Port: 80
Remote Addr: 127.0.0.1
Proxy Protocol Addr:
X-Forwarded-For Header:
X-Real-IP Header:
X-Forwarded-Proto Header: https
X-Forwarded-Port Header:
X-Victors-Test0 Header: test
X-Victors-Test1 Header: passed
while HTTP/2 and HTTP/1.1 both work:
Victor@Victor-Mac:/tmp » curl-h3 -6 --http2 https://dns-main.imvictor.tech:2/__test/dump -H 'X-Victors-Test0: test'
Port: 80
Remote Addr: 2409:8a3c:5b7c:2300:[HIDDEN]
Proxy Protocol Addr:
X-Forwarded-For Header: 2409:8a3c:5b7c:2300:[HIDDEN]
X-Real-IP Header: 2409:8a3c:5b7c:2300:[HIDDEN]
X-Forwarded-Proto Header: https
X-Forwarded-Port Header: 54881
X-Victors-Test0 Header: test
X-Victors-Test1 Header: passed
Victor@Victor-Mac:/tmp » curl-h3 -6 --http1.1 https://dns-main.imvictor.tech:2/__test/dump -H 'X-Victors-Test0: test'
Port: 80
Remote Addr: 2409:8a3c:5b7c:2300:[HIDDEN]
Proxy Protocol Addr:
X-Forwarded-For Header: 2409:8a3c:5b7c:2300:[HIDDEN]
X-Real-IP Header: 2409:8a3c:5b7c:2300:[HIDDEN]
X-Forwarded-Proto Header: https
X-Forwarded-Port Header: 55544
X-Victors-Test0 Header: test
X-Victors-Test1 Header: passed
Now I have no idea to make a workaround.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 24 (16 by maintainers)
I’m very glad to see the problem fixed. Thank you all for your hard work.
@mholt Sure, let me see what’s going on.
@mholt It doesn’t. The request gets passed to the handler just a few lines after the remote address is set: https://github.com/lucas-clemente/quic-go/blob/9899be3a06a168f36768cc4a89cc9df6077b3ac7/http3/server.go#L270
I’m going to confirm this. But as version goes, the structure of config seems to have changed. I have to find out the right way to configure it before I can confirm it.
FWIW, I’m currently convinced that the bug is upstream. Can’t prove it either way yet, so I might be wrong, but from what I know of the Caddy code base, that’s my current suspicion. I don’t have the time or funding to spend more time on this right now (especially since the HTTP/3 integration is still experimental), but feel free to drill down more if you want a quicker fix! It will help a lot.
I think
bugandv2labels can be added to this issue. Looks like that it’s a strange bug, which needs more attention.@marten-seemann That’s what I figured too. Just spitballing here.
@qwqVictor Thanks for checking!
Hmm. This is odd…
As a matter of fact, it’s also broken with IPv4.
SetQuicHeaders()just takes thehttp.Header, and adds some values to it. It doesn’t even have access to thehttp.Requeststruct.@qwqVictor Does this happen with an IPv4 address as well?
@marten-seemann
Seems to be the case… there’s gotta be some other factor we aren’t considering yet then.
We do call
SetQuicHeaders(), but that couldn’t possibly affect the request, right? https://github.com/caddyserver/caddy/blob/57c6f22684e74191814a30f9de83a05c11ac4b78/modules/caddyhttp/server.go#L120-L125I’m out of ideas for right now…
To make sure if it’s related to HTTP/3, there is a piece of log dumped from a HTTP/1.1 request. All parameters except HTTP version are the same.
Hope it could help.
@qwqVictor Awesome, thanks!
@marten-seemann For reasons I can’t explain, the
req.RemoteAddrfield is empty on HTTP/3 requests. According to the logs, it is empty even at the very entry point of Caddy’s ServeHTTP. Do you think you could help us investigate when you have a chance?Edit: I just noticed that the tls information is all empty, too. (Not sure if that’s related.)
I have got the log and formatted it. It suggests that Caddy is unable to get a remote_addr value. @mholt
Cool, thanks for the link.
Nope, that should be all: https://github.com/caddyserver/caddy/blob/57c6f22684e74191814a30f9de83a05c11ac4b78/modules/caddyhttp/replacer.go#L84-L89
@qwqVictor Let’s try debugging this with Caddy 2. Would you mind enabling access logging in your JSON config? All you have to do is set
"logs": {}in yourserverstruct: https://caddyserver.com/docs/json/apps/http/servers/logs/That should output details of HTTP requests to the console when you make requests, including the request header values.
You can also try replacing your reverse_proxy handler with a static_response, like:
Then can you report back with a log and HTTP response?
@mholt Yes, quic-go does that: https://github.com/lucas-clemente/quic-go/blob/9899be3a06a168f36768cc4a89cc9df6077b3ac7/http3/server.go#L242
Is there anything else that needs to be set for this to work?