freman / caddy-reauth

Auth your Caddyserver requests against another server
MIT License
27 stars 15 forks source link

Using reauth with proxy #27

Open dcode opened 5 years ago

dcode commented 5 years ago

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
}
freman commented 5 years ago

Have you tried adding ,follow=true to your upstream line? that should permit it to follow the redirect.

dcode commented 5 years ago

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.

freman commented 5 years ago

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.

dcode commented 5 years ago

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!

freman commented 5 years ago

https://github.com/freman/caddy-reauth/commit/76783e70eddef7fabc2049fd56c357bab4ac6243 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 :(

dcode commented 5 years ago

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.

dcode commented 5 years ago

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.

freman commented 5 years ago

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

freman commented 5 years ago

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

dcode commented 5 years ago

@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

freman commented 5 years ago

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.

dcode commented 5 years ago

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.

macross5 commented 4 years ago

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.