Graylog2 / graylog-plugin-auth-sso

SSO support for Graylog through trusted HTTP headers set by load balancers or authentication proxies
Other
50 stars 13 forks source link

Kerberos Single Sign-On does not work #16

Open acudovs opened 8 years ago

acudovs commented 8 years ago

Kerberos Single Sign-On does not work

I've tried to setup Kerberos Single Sign-On to Graylog 2.1 on my Apache HTTP Server proxy.

My current Apache HTTP Server proxy configuration:

    <Location />
        SSLRequireSSL
        RequestHeader set X-Graylog-Server-URL "https://graylog.example.com/api/"
        ProxyPass http://127.0.0.1:9000/
        ProxyPassReverse http://127.0.0.1:9000/
    </Location>

First of all I've created user admin@EXAMPLE.COM via Graylog WEB UI /system/authentication/users and configured SSO Plugin /system/authentication/config/sso to trust X-Remote-User HTTP header.

To test SSO plugin works as expected I've added static header to my configuration:

    <Location />
        SSLRequireSSL
        RequestHeader set X-Graylog-Server-URL "https://graylog.example.com/api/"
        RequestHeader set X-Remote-User "admin@EXAMPLE.COM"
        ProxyPass http://127.0.0.1:9000/
        ProxyPassReverse http://127.0.0.1:9000/
    </Location>

With the above configuration I always login as admin@EXAMPLE.COM without prompting for password.

So, the Kerberos part uses mod_auth_gssapi https://github.com/modauthgssapi/mod_auth_gssapi

    <Location />
        SSLRequireSSL

        AuthType GSSAPI
        AuthName "Kerberos Login"
        GssapiCredStore keytab:/etc/httpd/conf/krb5.keytab
        GssapiUseSessions On
        Require valid-user

        RequestHeader set X-Graylog-Server-URL "https://graylog.example.com/api/"
        RequestHeader set X-Remote-User %{REMOTE_USER}s

        Session On
        SessionCookieName gssapi_session path=/;httponly;secure;

        ProxyPass http://127.0.0.1:9000/
        ProxyPassReverse http://127.0.0.1:9000/
    </Location>

With the above configuration Apache HTTP Server authenticates me as admin@EXAMPLE.COM but Graylog API session is not authorized

192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:19 +0300] "GET / HTTP/1.1" 200 500 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:19 +0300] "GET /config.js HTTP/1.1" 200 136 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:19 +0300] "GET /assets/polyfill.6469f06d961e83d45607.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:20 +0300] "GET /assets/plugin/org.graylog.plugins.pipelineprocessor.ProcessorPlugin/plugin.org.graylog.plugins.pipelineprocessor.PipelineProcessorPlugin.052c725323b2a784f7b0.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - - [08/Sep/2016:14:05:20 +0300] "GET /api/system/sessions HTTP/1.1" 401 381 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - - [08/Sep/2016:14:05:20 +0300] "GET /api/system/sessions HTTP/1.1" 401 381 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:21 +0300] "GET /assets/plugin/org.graylog.plugins.enterprise_integration.EnterpriseIntegrationPlugin/plugin.org.graylog.plugins.enterprise_integration.EnterpriseIntegrationPlugin.cac9c48526f92b69f0dc.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:21 +0300] "GET /assets/plugin/org.graylog.plugins.map.MapWidgetPlugin/plugin.org.graylog.plugins.map.MapWidgetPlugin.2d9b16670c4a97bedae2.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:22 +0300] "GET /api/system/cluster/node HTTP/1.1" 200 223 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - - [08/Sep/2016:14:05:22 +0300] "GET /api/system/sessions HTTP/1.1" 401 381 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - - [08/Sep/2016:14:05:22 +0300] "GET /api/system/sessions HTTP/1.1" 401 381 "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:22 +0300] "GET /assets/f9a25466e5ac752f14dfa013fad9730a.jpg HTTP/1.1" 304 - "https://graylog.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:23 +0300] "GET /assets/plugin/org.graylog.plugins.auth.sso.SsoAuthPlugin/plugin.org.graylog.plugins.auth.sso.SsoAuthPlugin.2b841b0e8c062b58a186.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:23 +0300] "GET /assets/2.LoginPage.6469f06d961e83d45607.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:23 +0300] "GET /assets/32.32.6469f06d961e83d45607.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:23 +0300] "GET /assets/plugin/org.graylog.plugins.collector.CollectorPlugin/plugin.org.graylog.plugins.collector.CollectorPlugin.2d7e15af839c3b19942b.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"
192.168.0.133 - admin@EXAMPLE.COM [08/Sep/2016:14:05:23 +0300] "GET /assets/app.6469f06d961e83d45607.js.map HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36"

Request headers:

GET /api/system/sessions HTTP/1.1
Host: graylog.example.com
Connection: keep-alive
Authorization: Basic dW5kZWZpbmVkOnNlc3Npb24=
Accept: application/json
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.92 Safari/537.36
Content-Type: application/json
Referer: https://graylog.example.com/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8,ru;q=0.6
Cookie: gssapi_session=MagBearerToken=XXXYYY

Response headers:

HTTP/1.1 401 Unauthorized
Date: Thu, 08 Sep 2016 11:05:20 GMT
Server: Apache
Set-Cookie: gssapi_session=MagBearerToken=XXXYYY;path=/;httponly;secure;
WWW-Authenticate: Negotiate
Cache-Control: no-cache
Content-Length: 381
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1

Seems like request to /api/system/sessions breaks Kerberos auth (header WWW-Authenticate: Negotiate) by adding HTTP header "Authorization: Basic dW5kZWZpbmVkOnNlc3Npb24=".

Environment

hc4 commented 8 years ago

I had similar problem (but I'm using squid for kerberos auth) And my solution was to not authorize with kerberos requests containing session auth (authorization header contains session id as login and "session" as password)

hc4 commented 8 years ago

Also be sure that XHR requests generated for server name (not ip). For this web_endpoint_uri should be set to relative (empty string) or http://servername

acudovs commented 8 years ago

Could you share your Squid configuration?

hc4 commented 8 years ago

The key part is that:

acl with_session_or_token req_header Authorization ^Basic\s.+(OnNlc3Npb24=|p0b2tlbg==)$
acl authenticated proxy_auth REQUIRED

http_access allow with_session_or_token
http_access deny !authenticated
http_access allow all
acudovs commented 8 years ago

I'm not sure how to mimic this in the Apache configuration.

For Single Sign-On to works properly user session needs to be authenticated using X-Remote-User header. But if I disable Kerberos for some requests to /api/system/sessions the Apache doesn't add X-Remote-User header, authentication fails and Graylog shows standard login form.

hc4 commented 8 years ago

graylog will work with session auth header only

hc4 commented 8 years ago

check order of auth providers: image

acudovs commented 8 years ago

It's true only if you already got the session somehow. I've spent hours trying different configurations. But maybe I just can't catch your point. Do you have spare Apache server to test the above configuration yourself? And don't forget to open a browser in incognito mode or you may share some previous session.

hc4 commented 8 years ago

Actualy I don't want to run Apache :) But my point is that you need to pass X-Remote-User header only until session will be created. After that you will have session auth and you no longer need Kerberos auth. Also it is important to have graylog WEB and graylog REST on same address.

acudovs commented 8 years ago

You are just right! I need to create initial session by passing the X-Remote-User header with correct Kerberos user principal in this header but I can't because only Kerberos knows the principal name and it needs to be disabled for the same request. The "chicken or the egg" :) Maybe I should try Squid if it works for you...

But the main problem is that Graylog breaks standard proxy authentication algorithm (including but not limited to Kerberos) by its own unexpected Authorization header.

hc4 commented 8 years ago

Yep. Sure graylog auth header is a root problem. But I still can't get a problem. Send X-Remote-User header using Kerberos until graylog will generate auth header. After that X-Remote-User is not needed anymore.

This is what I did with squid config. It checks auth header with session and passes request if header found. Otherwise it makes Kerberos auth and sends X-Remote-User

hc4 commented 8 years ago

Myabe the problem is that you sending auth header as undefined:session?

kroepke commented 8 years ago

The undefined:session is from the Graylog frontend code, when it tries to find out whether it has a valid session or not. I'm not sure what you mean by the auth header being the root problem, Graylog is using Basic auth, and has always done that. The SSO plugin only creates a Graylog session based on the externally set trusted headers and does not change anything else about the authentication mechanism.

From my limited Kerberos knowledge, the client needs to follow a few steps to get a valid Kerberos ticket, in which case the client is the Graylog web interface code running in the browser. We assume Basic auth and this is unlikely to change, to be honest.

acudovs commented 8 years ago

Kerberos works as expected, there is no problem with tickets. But Graylog's Basic auth breaks Kerberos auth as both rely on Authorization header. So, technically SSO plugin can't be used for SSO with Apache + Kerberos. At least, until some workaround is found.

kroepke commented 8 years ago

Ah, then I misunderstood. So Kerberos is relying on being able to use the Authentication header for itself?

acudovs commented 8 years ago

Sure! But not only Kerberos. As soon as "Require valid-user" added to Apache configuration it starts using Authentication header depending on AuthType. And of course everything breaks with unexpected (for Apache) Graylog's "Authorization: Basic undefined:session" header.

kroepke commented 8 years ago

Ok, I see. As this is something that needs to changed in Graylog server, I don't think we will be able to do anything about it before 3.0.

hc4 commented 8 years ago

Squid could be used as workaround. At least it works for me :)

hc4 commented 8 years ago

Full working squid3 config:

http_port pub_ip:80 accel
cache_peer 127.0.0.1 parent 80 0 no-query proxy-only

auth_param negotiate program /usr/lib/squid3/negotiate_kerberos_auth -r -s GSS_C_NO_NAME #-d
auth_param negotiate children 10
auth_param negotiate keep_alive on

acl with_session_or_token req_header Authorization ^Basic\s.+(OnNlc3Npb24=|p0b2tlbg==)$
acl authenticated proxy_auth REQUIRED

http_access allow with_session_or_token
http_access deny !authenticated
http_access allow all

request_header_access X-Remote-User deny all
request_header_add X-Remote-User "%ul" authenticated
request_header_add X-Graylog-Server-URL "http://%{Host}>h" all
saidst commented 8 years ago

@kroepke, I see you have added this to the 2.2.0 milestone. How do you plan to cope with this issue? Are you changing the process of session creation, e.g. by stopping to use the Authorization header?

sigmaris commented 6 years ago

It is possible to use Kerberos SSO with nginx with some trickery in the config file, and the same may be possible with Apache. This snippet of nginx config works for me:

# Set a variable $passthru_authorization to the original value of any Basic Authorization
# header. We do this because the ngx_http_auth_spnego_module may later overwrite the value!
map $http_authorization $passthru_authorization {
    default                             "";
    "~(?<auth_header_capture>Basic .*)" $auth_header_capture;
}

server {
    # put your server directives here
    # ...

     location / {
        # a trick using fake error codes above 418 to perform internal redirects
        # to named locations
        error_page 463 = @graylog_no_auth;
        error_page 464 = @graylog_with_auth;

        # Check if the request has Basic auth with username="undefined",
        # password="session". this is the first auth-related request from a
        # logged-out client, with an undefined session token
        if ($http_authorization = "Basic dW5kZWZpbmVkOnNlc3Npb24=") {
            # We want to apply Kerberos authentication to this request,
            # so that the request that reaches Graylog has the Remote-User
            # header and the client obtains a proper session token to use
            # in future requests
            return 464;
        }

        # Check if the client sent a session or API token using Basic auth.
        # If they have, we should pass it to Graylog without triggering
        # Kerberos auth, as Graylog should accept it.
        # These possible matches are the different possible base64
        # representations of ":session" or ":token" at the end of the
        # Authorization header
        if ($http_authorization ~ ".+(6c2Vzc2lvbg==|OnNlc3Npb24=|pzZXNzaW9u|p0b2tlbg==|6dG9rZW4=|OnRva2Vu)$") {
            return 463;
        }

        # If there is no session or API token, trigger Kerberos auth.
        if ($http_authorization !~ ".+(6c2Vzc2lvbg==|OnNlc3Npb24=|pzZXNzaW9u|p0b2tlbg==|6dG9rZW4=|OnRva2Vu)$") {
            return 464;
        }
    }

    location @graylog_with_auth {
        # These are the directives which require Kerberos authentication
        auth_gss on;
        auth_gss_keytab /etc/nginx/krb5.keytab;

        # Don't try and use Basic auth credentials to log in to Kerberos
        auth_gss_allow_basic_fallback off;

        # Pass the real IP to Graylog server
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Since in this location we require Kerberos auth, we should have
        # a valid remote user to pass to the Graylog server, which should
        # trust it implicitly and log the user in.
        proxy_set_header Remote-User $remote_user;

        # The ngx_http_auth_spnego_module sets a fake Authorization header,
        # remove it as it'll probably confuse the Graylog server
        proxy_set_header Authorization "";

        # Proxy to Graylog server, assuming it's running on localhost port 9000
        proxy_pass http://127.0.0.1:9000;
    }

    location @graylog_no_auth {
        # Pass the real IP to Graylog server
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Clear the Remote-User header; we aren't doing any SSO in this location
        proxy_set_header Remote-User "";

        # Pass through Basic Authorization header from the client,
        # as it contains Graylog session or API token
        proxy_set_header Authorization $passthru_authorization;

        # Proxy to Graylog server, assuming it's running on localhost port 9000
        proxy_pass http://127.0.0.1:9000;
    }
}

The key thing is to enable or disable Kerberos auth based on the type of Basic auth presented by the client. If the client presents Basic auth with a password of "session" or "token", that means the client is trying to use Graylog session or token-based authentication, with the token in the username, and Kerberos authentication shouldn't be enabled/required. However an exception to that rule is if the username is "undefined" - that's a sign that the client is logged out and so Kerberos authentication should be enabled for that request, to sign them in. After that request, the client should obtain a valid session token, use that for future requests, and no longer require Kerberos auth until their session expires.

toddlindner commented 4 years ago

This is still a bug. Any chance graylog can move away from misusing the Authorization header?

kroepke commented 4 years ago

We will deprecate and remove this plugin in the near future, it was never a good idea in the first place, and as you note its implementation is flawed. For a replacement we need to rework some intervals though and that process is not finished yet. Sorry for the inconvenience.

audunmg commented 4 years ago

The idea was good, and with the workarounds it works great. I hope it returns without the need for workarounds.

toddlindner commented 4 years ago

@kroepke I'm not sure you are understanding the issue. There is nothing inherently wrong with the graylog-plugin-auth-sso plugin.

The issue is that Graylog itself (even when the graylog-plugin-auth-sso plugin is disabled) is mis-using the Authorization header for its own session management. If that internal session handling was moved to a header such as "AuthorizationGraylogSession" instead of "Authorization" there would be no issue.

This misuse of the Authorization header interferes with Kerberos implementations that are outside of both graylog and the graylog-plugin-auth-sso plugin.