openhab / org.openhab.ui.habmin

HABmin - a graphical user interface for openHAB 2
231 stars 92 forks source link

Using HABmin behind nginx proxy #220

Closed ploef closed 7 years ago

ploef commented 7 years ago

Hi,

I was using Openhab behind an Nginx proxy without problems. But now I'm trying HABmin and I'm getting problems. After logging in with my (nginx) credentials I can use the Paper UI without problems, but starting HABmin just keeps asking me for the credentials over and over again.

Is there a solution for this?

My Nginx config looks like this:

server {
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and

        listen 9443 ssl;
        listen [::]:9443 ssl;
        server_name xxx.xxxxx.xxx;

        ssl_certificate /etc/ssl/certs/xxxxxx.crt.crt;
        ssl_certificate_key /etc/ssl/private/xxxxxxxxxx.key;
        include snippets/ssl-params.conf;

        location / {
          auth_basic "Restricted";
          auth_basic_user_file /etc/nginx/.htpasswd;
          proxy_pass http://localhost:8080/;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Host $http_host;
          proxy_set_header X-Forwarded-Proto https;
          proxy_redirect http:// $scheme://;

        }

}

Regards, Peter

mdeneen commented 7 years ago

This also happens behind haproxy.

cdjackson commented 7 years ago

Can anyone provide debug information so we can see what's happening (or better still, propose a solution ;) ).

BClark09 commented 7 years ago

If it helps, this is the chrome log when trying to connect to Habmin, "removing saved authentication data" seems a little suspicious?

                    Navigated to https://x.x.x.x/start/index
                    Navigated to https://x.x.x.x/habmin/index.html
userModel.js:42     Removing saved authentication data!
restModel.js:47     REST start get Mon Sep 12 2016 13:09:22 GMT+0100 (GMT Daylight Time)
app.js:339          App logged in at startup
restModel.js:47     REST start get Mon Sep 12 2016 13:09:22 GMT+0100 (GMT Daylight Time)
itemModel.js:49     Updated to  undefined
favicon.ico:1       GET https://x.x.x.x/habmin/assets/favicon.ico 401 (Unauthorized)
restModel.js:47     REST start get Mon Sep 12 2016 13:09:52 GMT+0100 (GMT Daylight Time)
restModel.js:47     REST start get Mon Sep 12 2016 13:10:22 GMT+0100 (GMT Daylight Time)
angular.js:12011    GET https://x.x.x.x/rest 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790bootstrapApply @ angular.js:1761invoke @ angular.js:4718doBootstrap @ angular.js:1759bootstrap @ angular.js:1779(anonymous function) @ index.html:375fire @ jquery.js:3099fireWith @ jquery.js:3211ready @ jquery.js:3417completed @ jquery.js:3433
angular.js:12011    GET https://x.x.x.x/habmin/languages/en-GB/discovery.lang.json 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790bootstrapApply @ angular.js:1761invoke @ angular.js:4718doBootstrap @ angular.js:1759bootstrap @ angular.js:1779(anonymous function) @ index.html:375fire @ jquery.js:3099fireWith @ jquery.js:3211ready @ jquery.js:3417completed @ jquery.js:3433
angular.js:12011    GET https://x.x.x.x/habmin/languages/en-GB/habmin.lang.json 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790bootstrapApply @ angular.js:1761invoke @ angular.js:4718doBootstrap @ angular.js:1759bootstrap @ angular.js:1779(anonymous function) @ index.html:375fire @ jquery.js:3099fireWith @ jquery.js:3211ready @ jquery.js:3417completed @ jquery.js:3433
angular.js:12011    GET https://x.x.x.x/rest 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790bootstrapApply @ angular.js:1761invoke @ angular.js:4718doBootstrap @ angular.js:1759bootstrap @ angular.js:1779(anonymous function) @ index.html:375fire @ jquery.js:3099fireWith @ jquery.js:3211ready @ jquery.js:3417completed @ jquery.js:3433
angular.js:12011    GET https://x.x.x.x/habmin/languages/en-GB/common.lang.json 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790bootstrapApply @ angular.js:1761invoke @ angular.js:4718doBootstrap @ angular.js:1759bootstrap @ angular.js:1779(anonymous function) @ index.html:375fire @ jquery.js:3099fireWith @ jquery.js:3211ready @ jquery.js:3417completed @ jquery.js:3433
angular.js:12011    GET https://x.x.x.x/rest 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790tick @ angular.js:12637
angular.js:12011    GET https://x.x.x.x/rest 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790tick @ angular.js:12637
restModel.js:47     REST start get Mon Sep 12 2016 13:10:52 GMT+0100 (GMT Daylight Time)
angular.js:12011    GET https://x.x.x.x/rest 401 (Unauthorized)(anonymous function) @ angular.js:12011sendReq @ angular.js:11776serverRequest @ angular.js:11571processQueue @ angular.js:16383(anonymous function) @ angular.js:16399$eval @ angular.js:17682$digest @ angular.js:17495$apply @ angular.js:17790tick @ angular.js:12637
BClark09 commented 7 years ago

@cdjackson I have a debug openhab.log extract that you can have, unfortunately clicking HABmin dumps 2.86MB of logs before it calms down again. Doing a simple search for "authentication" only brings up "DisableAuthenticationHttpContext" for a large number of files for example:

2016-09-13 15:27:09.122 [DEBUG] [.jetty.server.handler.ContextHandler] - scope null||/habmin/app/configuration/configParameter.js @ HttpServiceContext{httpContext=org.jupnp.transport.impl.osgi.DisableAuthenticationHttpContext@1cf2e9}

I don't know how much help this is...

cdjackson commented 7 years ago

Hmmm - not so much help. I was assuming that the problem was in the web app, and not in the server side since I think someone said that the REST interfaces work ok behind the proxy? If so, I was thinking (maybe incorrectly) that there might be some errors in the browser debugger - I don’t know if they would be useful or not though, but I’m not sure how else to proceed at the moment.

BClark09 commented 7 years ago

Yeah, the REST interface works fine for me. I think you would be right in thinking it's the web app. The browser debug I posted above suggests that the authentication information is removed on starting habmin, and then never correctly restored; at least that's my interpretation of it.

I'd need to log back in if I went to another UI after attempting habmin.

cdjackson commented 7 years ago

So do you think this is an authentication issue? Can yo provide the debugger information?

BClark09 commented 7 years ago

I posted what my browser gave me above, let me know if there's any way of getting verbose information out of that.

On my proxies side I only get this information, there's nothing reported when I open any other UI:

2016/09/13 16:44:43 [info] 2521#0: *5 epoll_wait() reported that client prematurely closed connection, so upstream connection is closed too while sending request to upstream, client: x.x.x.x, server: y.y.y, request: "GET /rest/thing-types HTTP/1.1", upstream: "http://127.0.0.1:8080/rest/thing-types", host: "y.y.y", referrer: "https://y.y.y/ui/index.html"
2016/09/13 16:45:06 [info] 2518#0: *173 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /rest HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:06 [info] 2518#0: *260 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/common.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:06 [info] 2518#0: *174 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/discovery.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:06 [info] 2521#0: *6 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/habmin.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:06 [info] 2518#0: *176 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /rest/events?topics=smarthome/* HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:08 [info] 2521#0: *6 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/habmin.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:08 [info] 2518#0: *174 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/discovery.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:08 [info] 2518#0: *173 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /rest HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"
2016/09/13 16:45:08 [info] 2518#0: *260 no user/password was provided for basic authentication, client: x.x.x.x, server: y.y.y, request: "GET /habmin/languages/en-GB/common.lang.json HTTP/1.1", host: "y.y.y", referrer: "https://y.y.y/habmin/index.html"

In the order of things:

  1. I try to go into habmin
  2. habmin/app/models/userModel.js reports "Removing saved authentication data!"
  3. My nginx proxy server reports no user/password was provided as above.
  4. I input credentials on the new popup
  5. Any file on the server via habmin is unauthorised still.
  6. More of the no user/password log from nginx.
BClark09 commented 7 years ago

Hi @cdjackson did you get any further with this? On any subsequent requests in Habmin only, the Authorization header is dropped.

I am told that habmin works fine with safari. Do you do anything with with this header that would cause a proxy to drop it?

csowada commented 7 years ago

+1

fracarvic commented 7 years ago

I have the same problem when using auth with nginx proxy.

It seems that first http request denied by nginx triggers browser auth, and for the subsequent requests, the browser add Authorization header. This works for the static resources, but seems that for the rest api requests made with javascript, the Authorization header gets overwrited by and empty value.

I think that if we are using nginx auth, and not habmin auth, this header need not to be overwriten by javascript code.

I don't know about the habmin internals, but this line of code may cause the problems, I'm not sure.

roben commented 7 years ago

I don't know about the habmin internals, but this line of code may cause the problems, I'm not sure.

This line alone is not the cause. But I temporarily commented out every line setting $http.defaults.headers.common['Authorization'] in this file using the chrome developer tools and the issue disappeared.

Why is this header touched anyways?

jvolkman commented 7 years ago

This does appear to be related to Habmin's javascript messing with the Authorization header.

For the time being I've been able to work around the issue with pure nginx config. I'm using a mixture of an auth token cookie and basic auth.

Here's my config with embedded Lua:

#################################
# openHABian NGINX Confiuration #
#################################

init_by_lua '
  KEY = "<some secret key>"
  TTL = 21600  -- 6 hours
';

## Redirection
server {
   listen                          80;
   server_name                     openhab.myserver.com;
   return 301                      https://$server_name$request_uri;
}

## Reverse Proxy to openHAB
server {
#    listen                          80;
   listen                          443 ssl;
   listen                          [::]:443 ssl;
   server_name                     openhab.myserver.com;
   add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

## Secure Certificate Locations
   ssl_certificate                 /etc/ssl/certs/openhab.crt;
   ssl_certificate_key             /etc/ssl/certs/openhab.key;

   location / {
      content_by_lua '
         local cookie = ngx.var.cookie_HabAuthToken
         if cookie ~= nil and cookie:find(":") ~= nil then
            local divider = cookie:find(":")
            local hmac = ngx.decode_base64(cookie:sub(divider+1))
            local timestamp = cookie:sub(0, divider-1)

            if ngx.hmac_sha1(KEY, timestamp) == hmac and tonumber(timestamp) >= os.time() then
               ngx.exec("@main")
            end
         end

         ngx.exec("@auth")
      '; 
    }

    location @auth {
       auth_basic                              "Username and Password Required";
       auth_basic_user_file                    /etc/nginx/.htpasswd;

       content_by_lua '
          local exp = os.time() + TTL
          local hmac = ngx.hmac_sha1(KEY, exp)
          cookie_string = exp .. ":" .. ngx.encode_base64(hmac)

          ngx.header["Content-Type"] = {"text/html"}
          ngx.header["Set-Cookie"] = {"HabAuthToken=" .. cookie_string .. "; Path=/; Secure; Max-Age=" .. TTL}  
          ngx.exec("@main")
       ';
    }

    location @main {
      proxy_pass                              http://localhost:8080;
#        proxy_buffering                         off;  # openHAB supports non-buffering specifically for SSEs now
      proxy_set_header Host                   $http_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-Proto      $scheme;
    }
}

Instead of using "auth_basic" on /, I use "access_by_lua". The Lua script checks for a valid auth token. If one does not exist, it internally redirects to @auth which invokes the basic auth flow and writes the token if successful. The token expires after 6 hours.

This requires the nginx Lua bindings, which can be installed with sudo apt-get install nginx-extras. If you use this in your own config, make sure to change the key to some random string.

DanielMalmgren commented 7 years ago

Just for the record: I'm using Apache as proxy and have got the same problem.

Flole998 commented 7 years ago

If I am not mistaken the Problem is indeed, that the requests are made using JavaScript and the script clears the logon data. It should not do that, unless Habmin authentication is used.

sipvoip commented 7 years ago

Any fix for this? Just started securing openHAB and ran into this same issue.

cdjackson commented 7 years ago

Not that I've seen.

Sent from my iPhone

On 8 May 2017, at 18:18, Nathan Stratton notifications@github.com wrote:

Any fix for this? Just started securing openHAB and ran into this same issue.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

jvolkman commented 7 years ago

I think this line is the problem, but I'm not sure what removing it will break (if anything). @cdjackson, do you recall why you needed to clear the authentication header?

cdjackson commented 7 years ago

I’m not sure - this looks like it just removes the authentication data after a timeout. If someone can provide a PR to fix this then please feel free - I’m unable to look at this at the moment.