Using reauth with proxy
Opened this issue · 13 comments
I'm trying to serve an application via proxy (in this case Kibana, but similar usecase is Django, Flask, or Rails. I'm relatively new to Caddy, so I could have some basic misunderstanding of the config as well.
I have static files stored under /srv/app/static
that I would like protected by a reauth upstream authorization. To the user, it would appear that all the pages are protected by the application's login screen. Since it's all hosted on the same host endpoint, auth cookies will be passed, etcd.
Here's my current config (I'm running this in docker):
0.0.0.0 {
proxy / kibana:5601 {
except /app/static
}
reauth {
path /app/static
upstream url=http://kibana:5601/app/kibana,cookies=true
failure redirect target=/login?next={uri}
}
root /srv
log stdout
errors stdout
}
When I browse to localhost:2015, Kibana correctly picks up the request via the proxy block above. When I am not authenticated, I get redirected to the Kibana login page (/login?next=%2F
). However, when I browse to localhost:2015/app/static
, I get a status 500. I can see in the logs that Kibana is in fact receiving the auth request from Caddy.
proxy_1 | 22/May/2019:14:39:47 +0000 [ERROR 500 /app/static/] Get /login?next=%2Fapp%2Fkibana: follow redirects disabled
proxy_1 | 172.22.0.1 - - [22/May/2019:14:39:47 +0000] "GET /app/static/ HTTP/1.1" 500 26
kibana_1 | {"type":"response","@timestamp":"2019-05-22T14:39:47Z","tags":[],"pid":1,"method":"get","statusCode":302,"req":{"url":"/app/kibana","method":"get","headers":{"host":"kibana:5601","user-agent":"Go-http-client/1.1","accept-encoding":"gzip"},"remoteAddress":"172.22.0.2","userAgent":"172.22.0.2"},"res":{"statusCode":302,"responseTime":5,"contentLength":9},"message":"GET /app/kibana 302 5ms - 9.0B"}
However, if I am already authenticated to Kibana, it places a cookie called sid
(by default).
I saw the error follow redirects disabled
in the unauthenticated example, and so I thought maybe I need to apply redirects to the upstream backend. The problem here is that Kibana provides a redirect 302, and the login page /login
sends a 200.
What am I doing wrong and/or how can I get more info?
Side note, my first attempt to do this was the following. I kept getting the 500 and was getting frustrated thinking I was doing something wrong. After doing the analysis above, I think the logic is effectively the same. The config below is nicer since I don't have to create an arbitrary dir structure in the root to support the URI subpath.
0.0.0.0 {
proxy / kibana:5601 {
except /app/static
}
log stdout
errors stdout
}
0.0.0.0/app/static {
reauth {
path /app/static
upstream url=http://kibana:5601/app/kibana,cookies=true
failure redirect target=/login?next={uri}
}
root /srv
log stdout
errors stdout
}
Have you tried adding ,follow=true
to your upstream line? that should permit it to follow the redirect.
I did, but that authorizes all traffic because the redirect for auth takes you to a login form that returns a 200.
It seems to me that the failure line should generate a redirect response to the browser, not follow a redirect itself.
right right, sorry, I've had an injection of coffee now.
upstream url=http://kibana:5601/app/kibana,cookies=true
should be performing failure redirect target=/login?next={uri}
when it fails.
So the problem probably is in the detection of kibana saying user needs auth.
Because kibana does a redirect on auth failure and the code is like "nope"
Seems like I need to patch to make it smarter, perhaps allow a match. eg
something like...
upstream url=http://kibana:5601/app/kibana,cookies=true,oncode=303,match=/login/
Would allow it to decide the user needs logging in when it see's a 303 redirect to a url containing a login.
I can do this patch for you tonight.
so then, I'd be able to do ,follow=true
and the failure
action for redirect would work? I'd be happy to try a patch whenever you get it ready! Thanks for the quick effort!
76783e7 this should do it for you
So your config should look something like
upstream url=http://kibana:5601/app/kibana,cookies=true,follow=true,match=login
I've submitted the build to caddy but if you're impatient you can build it by hand :D
edit: It seems that building it by hand might be the way to go
plugin deploy failed
go vet github.com/freman/caddy-reauth@1.0.15: timed out (buildworker-enforced)
that's outside of my control :(
Yeah, that's no problem I use https://github.com/abiosoft/caddy-docker to build a container of caddy anyway, and it pulls master of all selected plugins, so I'm building it now. I'll report back my outcome.
Okay, so the auth works now, thanks! I'm still having issue on the redirect now, but just with the placeholder. Ideally, after the login, I'd like to get redirected back to the original URI.
(I think) this is the relevant part of my Caddyfile:
0.0.0.0/app/static/ {
header / {
X-Requested-URI {uri}
X-Requested-Path {path}
X-Requested-Dir {dir}
X-Requested-File {file}
}
reauth {
path /
upstream url=http://kibana:5601/app/kibana,cookies=true,follow=true,match=login
failure redirect target=/login?next={uri}
}
root /srv
log stdout
errors stdout
}
The authentication check works as desired, and will send the browser a redirect. But the {uri}
is being populated as /
.
HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Location: /login?next=%2F
Server: Caddy
X-Requested-Dir: /
X-Requested-File:
X-Requested-Path: /app/static/
X-Requested-Uri: /app/static/
Date: Fri, 24 May 2019 11:44:25 GMT
Content-Length: 38
I think this is happening because my site definition is using patch matching. I noticed that for the path
value in the reauth
block I have to specify /
, or else it doesn't trigger. It seems the http.Request
object being passed to the plugin has a context of the path, not the original request.
Here you're using the raw http.Request
URL value to populate {uri}
, but the Caddy placeholder logic references OriginalURLCtxKey
, which I don't know where that comes from. And I've reached the limit of my Golang knowledge. The {dir}
placeholder shown also is substituteded as /
, which looking at the code, Caddy doesn't retrieve the original context here. So I think that all fits together.
Is it crazy hard to use the Caddy placedholder functions to get the original context or get the original context through "magic means" like the Caddy replace function does? This is technically a different problem and I can open a different issue, if you'd prefer.
Thanks again for being so quick to fix my redirect problem. That makes my use at least functional.
No that's fine, and I suspect you've finally been able to help me figure out the issues the others were having.
I'll have a look later it actually shouldn't be that hard. I have access to your original url elsewhere in the request I can store that and reuse it if I can't find it elsewhere
Hmm, so I wrote a test case to check the reason I thought it was broken, but that doesn't seem to be it.
I can cirtainly look at populating the url with 'OriginalURLCtxKey' but can you do me a quick check?
try adding
except /login
to your reauth block so it doesn't try authenticating the /login call recursively
@freman sorry, I thought I got back to you. This didn't change anything because /login
takes another route. My full Caddyfile is here: https://gist.github.com/dcode/5b013cfc32e7482e4b6a8061203aa99d
Sorry, just been a little busy at work but I'll take another look and see if I can't replicate what's happening and get you a fix this week.
Hey, man. No problem. I maintain an open source project too, and sometimes it just takes a bit. No biggie. Thanks for your work though. Super excited about the possibilities.
I get a similar error:
[ERROR 500 /favicon.ico] Get http://heimdall/login: follow redirects disabled
My config:
failure redirect target=https://xxx:yyyy/login,code=302
upstream url=http://heimdall:80,cookies=true
When i add follow=true,match=login
to the upstream url, then all requests are allowed.
When i remove the cookies=true
with follow=true,match=login
it ends in an endless loop.