dbeaver / cloudbeaver

Cloud Database Manager
https://dbeaver.com/
Apache License 2.0
3.52k stars 382 forks source link

Issue with enableReverseProxyAuth #979

Closed dinu111 closed 2 years ago

dinu111 commented 2 years ago

Hi team,

We would like to make a few adjustments to our cloudBeaver instances and run it with enableReverseProxyAuth: true so we can bypass the login screen for authorised users.

From what I see in your documentation this can be achieved if you run an nginx that forwards the user and role headers to cloudBeaver and authenticates everything in an oauth-proxy backed (please correct me if this is wrong).

I found and followed two comprehensive guides that helped me make this possible:

  1. This guid from your documentation
  2. This guid from okta

After configuring everything and passing the enableReverseProxyAuth to /opt/cloudbeaver/workspace/.data/.cloudbeaver.runtime.conf it is still asking me for a login screen. At first I thought that maybe the headers are not passed but I checked them by going to httpbin: https://httpbin.org/#/Request_inspection/get_headers and they are sent.

image

I would also like to mention that we are running everything in an AKS cluster.

I would also like to pass you the current configuration we are using for nginx and couldBeaver:

Nginx config: `
server { listen 8080; server_name db-ui;

 location /oauth2/ {
   proxy_pass       http://dcp-db-ui-proxy:4180;
   # proxy_set_header Host                    $host;
   proxy_set_header X-Real-IP               $remote_addr;
   proxy_set_header X-Scheme                $scheme;
   proxy_set_header X-Auth-Request-Redirect $request_uri;
   # or, if you are handling multiple domains:
   # proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
 }
 location = /oauth2/auth {
   proxy_pass       http://dcp-db-ui-proxy:4180;
   # proxy_set_header Host             $host;
   proxy_set_header X-Real-IP        $remote_addr;
   proxy_set_header X-Scheme         $scheme;
   # nginx auth_request includes headers but not body
   proxy_set_header Content-Length   "";
   proxy_pass_request_body           off;
 }

 location ^~ / {
   auth_request /oauth2/auth;
   error_page 401 = /oauth2/sign_in;

   # pass information via X-User and X-Email headers to backend,
   # requires running with --set-xauthrequest flag
   auth_request_set $user   $upstream_http_x_auth_request_user;
   proxy_set_header X-User  cbadmin;

   auth_request_set $role  $upstream_http_x_auth_request_role;
   proxy_set_header X-Role admin|user;

   # if you enabled --pass-access-token, this will pass the token to the backend
   auth_request_set $token  $upstream_http_x_auth_request_access_token;
   proxy_set_header X-Access-Token $token;

   # if you enabled --cookie-refresh, this is needed for it to work with auth_request
   auth_request_set $auth_cookie $upstream_http_set_cookie;
   add_header Set-Cookie $auth_cookie;

   # When using the --set-authorization-header flag, some provider's cookies can exceed the 4kb
   # limit and so the OAuth2 Proxy splits these into multiple parts.
   # Nginx normally only copies the first `Set-Cookie` header from the auth_request to the response,
   # so if your cookies are larger than 4kb, you will need to extract additional cookies manually.
   auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;

   # Extract the Cookie attributes from the first Set-Cookie header and append them
   # to the second part ($upstream_cookie_* variables only contain the raw cookie content)
   if ($auth_cookie ~* "(; .*)") {
       set $auth_cookie_name_0 $auth_cookie;
       set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
   }

   # Send both Set-Cookie headers now if there was a second part
   if ($auth_cookie_name_upstream_1) {
       add_header Set-Cookie $auth_cookie_name_0;
       add_header Set-Cookie $auth_cookie_name_1;
   }

  #  proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   # proxy_set_header X-Forwarded-Host $host:80;
   # proxy_set_header X-Forwarded-Port 80;
  # proxy_set_header X-Forwarded-Server $host;
   # proxy_set_header X-Forwarded-Proto http;

   proxy_http_version 1.1;
   proxy_pass http://dcp-db-ui:8978;
   # or "root /path/to/site;" or "fastcgi_pass ..." etc
 }

} `

Yes I know I have deactivated the "# proxy_set_header Host $host;" but it does not work if I add it, I get an upstream error and I guess it is not really required.

Now this is the config for cloudBeaver from "/opt/cloudbeaver/workspace/.data/.cloudbeaver.runtime.conf":

{ "server": { "serverName": "DEV-PLAYGROUND DB UI", "serverURL": "https://db-ui", "expireSessionAfterPeriod": 1800000 }, "app": { "anonymousAccessEnabled": false, "enableReverseProxyAuth": true, "supportsCustomConnections": false, "publicCredentialsSaveEnabled": true, "adminCredentialsSaveEnabled": true, "defaultNavigatorSettings": {}, "enableReverseProxyAuth": true } }

Thank you for your time, please let me know if I can give you any more information

dinu111 commented 2 years ago

I have upgraded to the latest dbeaver image and now I am getting some activity in the logs, to be more precise a nullpointer is popping up: 08:52:17.094 [qtp508323203-32] ERROR i.c.service.core.impl.WebServiceCore - Error calling session handler 'RPSessionHandler' io.cloudbeaver.DBWebException: Error: null at io.cloudbeaver.service.auth.RPSessionHandler.reverseProxyAuthentication(RPSessionHandler.java:116) ~[na:na] at io.cloudbeaver.service.auth.RPSessionHandler.handleSessionOpen(RPSessionHandler.java:58) ~[na:na] at io.cloudbeaver.service.core.impl.WebServiceCore.openSession(WebServiceCore.java:179) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na] at io.cloudbeaver.service.WebServiceBindingBase$ServiceInvocationHandler.invoke(WebServiceBindingBase.java:154) ~[na:na] at com.sun.proxy.$Proxy9.openSession(Unknown Source) ~[na:na] at io.cloudbeaver.service.core.WebServiceBindingCore.lambda$13(WebServiceBindingCore.java:89) ~[na:na] at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:86) ~[na:na] at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:258) ~[na:na] at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:197) ~[na:na] at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:72) ~[na:na] at graphql.execution.Execution.executeOperation(Execution.java:160) ~[na:na] at graphql.execution.Execution.execute(Execution.java:101) ~[na:na] at graphql.GraphQL.execute(GraphQL.java:591) ~[na:na] at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:521) ~[na:na] at graphql.GraphQL.executeAsync(GraphQL.java:495) ~[na:na] at graphql.GraphQL.execute(GraphQL.java:426) ~[na:na] at io.cloudbeaver.server.graphql.GraphQLEndpoint.executeQuery(GraphQLEndpoint.java:258) ~[na:na] at io.cloudbeaver.server.graphql.GraphQLEndpoint.executeSingleQuery(GraphQLEndpoint.java:202) ~[na:na] at io.cloudbeaver.server.graphql.GraphQLEndpoint.doPost(GraphQLEndpoint.java:185) ~[na:na] at javax.servlet.http.HttpServlet.service(HttpServlet.java:517) ~[na:na] at javax.servlet.http.HttpServlet.service(HttpServlet.java:584) ~[na:na] at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764) ~[na:na] at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:508) ~[na:na] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221) ~[na:na] at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571) ~[na:na] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221) ~[na:na] at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1378) ~[na:na] at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176) ~[na:na] at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463) ~[na:na] at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544) ~[na:na] at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174) ~[na:na] at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1300) ~[na:na] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129) ~[na:na] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122) ~[na:na] at org.eclipse.jetty.server.Server.handle(Server.java:562) ~[na:na] at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505) ~[na:na] at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762) ~[na:na] at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497) ~[na:na] at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282) ~[na:na] at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319) ~[na:na] at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100) ~[na:na] at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53) ~[na:na] at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:407) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038) ~[org.eclipse.jetty.util_10.0.9.jar:10.0.9] at java.base/java.lang.Thread.run(Unknown Source) ~[na:na] Caused by: java.lang.NullPointerException: null at io.cloudbeaver.service.auth.RPSessionHandler.reverseProxyAuthentication(RPSessionHandler.java:97) ~[na:na]

kseniiaguzeeva commented 2 years ago

Thank you for the report. We are going to investigate and fix the issue.

dinu111 commented 2 years ago

Hi, After upgrading to the "prod" version it works :) thank you

kseniiaguzeeva commented 2 years ago

Could you please tell me the CloudBeaver version in which you encountered the bug? I want to try to reproduce the issue and find its reason.

kseniaguzeeva commented 2 years ago

Another bug with Proxy users is fixed and available in the latest 22.2.0 release.