-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
caddyhttp: Redirect HTTP requests on the HTTPS port to https:// #4313
Conversation
a82c857
to
d57ddd1
Compare
Caddyfile I'm using to test this:
Then making these requests:
First should just show |
For what its worth, I'm running into this issue that you're solving and look forward to your fix! I have just one port ( I attempted using |
One of the open questions I had was how to parse the request headers, and I have my answer by having seen the code in
textproto.NewReader(rb).ReadMIMEHeader() . That'll let us grab the Host header from the request.
Still need to figure out how to properly wrap the listener so we can actually grab the bytes when we want to without getting an error from the underlying TLS listener. Edit: The above is dumb lol, I can just use |
290eaf3
to
7b9b78c
Compare
7b9b78c
to
218007a
Compare
I figured out how to do this properly!! I scrapped the idea of overloading the Instead, I created a new listener I always wrap the listener with Steps I took to test this are essentially the same. Caddyfile:
Curl request which should show that it redirected to HTTPS, with the header
Notice that the debug logs will contain the following on any of these kinds of requests:
This message can probably be adjusted, but I return this error message because otherwise the TLS listener will try to read from the connection which is already closed. I don't think it's possible to quiet it, but either way, no biggie. |
218007a
to
c6f3c59
Compare
Thanks for this PR, looks interesting. I'm hoping to dive into this in more detail soon when I can give it some thought. My gut tells me this is something we have to be careful with, or that there will be some unintended side effects or something. |
c6f3c59
to
550958a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice overall. However, does this actually work? Like, why would a client make an HTTPS request to the same port it's already connecting to if it tried HTTP to it in the first place?
This is typically helpful for situations where a different port than And yes, this definitely does work, try it out as described above, both with curl and a browser (before and after the patch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok ok, last pass I think :)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
28ee712
to
5caa1ab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM now. Thanks!
This is very much not done yet, but I just wanted to show the proof of concept.
Background: in certain cases, users would like HTTP->HTTPS redirects in situations where they only actually serve HTTPS on one port; if an HTTP request is made to that port, Caddy currently responds with
Client sent an HTTP request to an HTTPS server.
This response comes directly from Go's stdlib (added in https://go-review.googlesource.com/c/go/+/143177/, see golang/go#23689 (comment) for context):https://github.com/golang/go/blob/56c3856d529d72e280ad6b185f7927657de86c37/src/net/http/server.go#L1817-1825
While it could be argued that HTTP responses should never be sent on the HTTPS port... Go already does it.
This is alright, but Caddy could go a step further and do an HTTP->HTTPS redirect, which would make HTTP clients happier, skipping the error and instead going straight to HTTPS. This can help avoid confusion for users making mistakes, etc.
So after hacking on this a bit, the idea I came up with is to make
tlsPlaceholderWrapper
a bit more useful by peeking the first bytes of the connection. Turns out that if we just callPeek()
on the wrapped request, then the stdlib TLS listener will return an errortls.RecordHeaderError
that we can watch for, which is the same one that gets handled for writing theClient sent an HTTP request to an HTTPS server.
message. We can do the same check on the first 5 bytes to see if it looks like HTTP, and write our own response instead.Note that
tlsPlaceholderWrapper
is now always registered, and is no longer skipped when performing the wrapping. If I didn't make this change, then the logic in the wrapper would never get called.We'll probably want to rename
tlsPlaceholderWrapper
to something more meaningful now that it has a secondary purpose 🤷♂️Still
TODO
, I need to figure out how to get the wrapper to read a bit more of the request in the error case to grab theHost
header to help us perform the redirect. I have a hunch that might not be possible with this current approach, because any attempts to read would trigger the same error since the stdlib TLS listener is still under ours. I'll have to dig into this more another day.