OH3 with NGINX Reverse Proxy and Authentication

Currently there is also a bug in chrome that blocks the basic auth credentials to be used when openHAB is accessed via HTTPS and the service work cached the requests. Allow more flexibility in how 401s/407s are handled? · Issue #1132 · whatwg/fetch · GitHub

Posting this here has I suspect others will keep finding this post.

I just configured it to work in HAProxy on pfSense thanks to the information in this thread. You should be able to configure this on the front end or the back end. I configured it on the back end.

Under "Access control lists and actions click to create a new Action and choose “http-request header add”.

  • name: Set-Cookie
  • fmt: XOPENHAB-AUTH-HEADER=1;path=/;Secure

Add another new Action and choose “http-request header delete”

  • name: Authorization

I kept the authorization and authentication configuration on the front end. I also have basic auth turned off in openHAB’s settings. Therefore I need to enter a username and password to bring up the openHAB page at all. Then I need to do it again to log in as an administrator to change any settings. This suites me just fine.

4 Likes

I thought you were supposed to set Authorization to the token obtained from openhab?

In nginx I’ve tried

 proxy_set_header Set-Cookie "X-OPENHAB-AUTH-HEADER=true;path=/;Secure";

and either set the Authorization header to the token I got from openhab, or delete the Authorization header entirely but I still can’t get it to work. The token generated from the UI includes a label field. I’ve tried with and without that label.

What I don’t understand is why I can get the Android app to work locally direct to openab, but not through from the internet through my proxy when I completely remove the Authorization field. In both cases when I look at the network traffic to openhab it looks something like

GET /rest/ HTTP/1.0
Host: x.net/openhab
X-Real-IP: x.x.x.x
X-Forwarded-For: x.x.xx
X-Forwarded-Proto: https
Connection: Upgrade
user-agent: openHAB client for Android
cache-control: no-cache
accept-encoding: gzip

Yet it only works on my local network. When I come in from the internet and remove the Authorization header to make the request look the same as a local request it shows empty for the main /rest endpoint, but other endpoints do return data, such as /rest/items. Is openhab doing some filtering on IP? I don’t want to use the same username/password that I made for openhab to log into my main proxy, which is why I’m not just using the REST authentication option.

hi Guys, plz help… i configured nginx using openhabian-config, i got below config:

##################################
# openHABian NGINX Configuration #
##################################

## Redirection
 server {
#   listen                          80;
   server_name                     localhost;
   return 301                      https://$server_name$request_uri;
 }

## Reverse Proxy to openHAB
server {
#    listen                          80;
   listen                          443 ssl;
    server_name                     localhost;
   add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

    # Cross-Origin Resource Sharing.
    add_header 'Access-Control-Allow-Origin' '*' always; # make sure that also a 400 response works
    add_header 'Access-Control-Allow_Credentials' 'true' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always;
    add_header Set-Cookie X-OPENHAB-AUTH-HEADER=1;
    proxy_set_header Authorization "";
    
## Secure Certificate Locations
   ssl_certificate                 /etc/ssl/certs/openhab.crt;
   ssl_certificate_key             /etc/ssl/certs/openhab.key;

    location / {
        proxy_pass                              http://localhost:8080/;
        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;
        proxy_read_timeout 3600;

## Password Protection
#OH2AUTH       auth_basic                              "Username and Password Required";
#OH2AUTH       auth_basic_user_file                    /etc/nginx/.htpasswd;
    }

## Let's Encrypt webroot location
#WEBROOT   location /.well-known/acme-challenge/ {
#WEBROOT       root                                    /var/www/localhost;
#WEBROOT   }
}

# vim: filetype=conf

i enabled basic authentication from openhab UI, i configured openhab in my android using my dynamic hostname and openhab UI credentials… its working but slow… then, i enabled basic authentication in the nginx config file and stop “bundle:stop org.openhab.core.io.rest.auth”, its super fast as it was before in v. 2.5.9… but after each restart i have to do “bundle:stop org.openhab.core.io.rest.auth”… is there a way to return the old behavior?.. thanks

Performance is a well known issue

@Mohammad_Chaaban I think the proxy_set_header Authorization ""; directive should actually go in the location / block. Otherwise it will not work and the credentials that are meant for NGINX are passed to openHAB, and get validated for every request the UI makes, which is why it’s slow.

Also, if your intent is to do password protection (basic authentication) of the entire instance you need to do it solely on the NGINX level (with the .htpasswd file) and NOT enable Basic authentication in Settings > API Security in openHAB.

1 Like

Thanks for the reply… in my nginx config file, can u show me how it will be after the modification u suggested?

##################################
# openHABian NGINX Configuration #
##################################

## Redirection
 server {
#   listen                          80;
   server_name                     localhost;
   return 301                      https://$server_name$request_uri;
 }

## Reverse Proxy to openHAB
server {
#    listen                          80;
    listen                          443 ssl;
    server_name                     localhost;
    add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

    # Cross-Origin Resource Sharing.
    add_header 'Access-Control-Allow-Origin' '*' always; # make sure that also a 400 response works
    add_header 'Access-Control-Allow_Credentials' 'true' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always;
    add_header Set-Cookie X-OPENHAB-AUTH-HEADER=1;
    
## Secure Certificate Locations
   ssl_certificate                 /etc/ssl/certs/openhab.crt;
   ssl_certificate_key             /etc/ssl/certs/openhab.key;

    location / {
        proxy_pass                              http://localhost:8080/;
        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;
        proxy_set_header Authorization "";
        proxy_read_timeout 3600;

## Password Protection
        auth_basic                              "Username and Password Required";
        auth_basic_user_file                    /etc/nginx/.htpasswd;
    }

## Let's Encrypt webroot location
#WEBROOT   location /.well-known/acme-challenge/ {
#WEBROOT       root                                    /var/www/localhost;
#WEBROOT   }
}

# vim: filetype=conf

Create the user/password in the /etc/nginx/.htpasswd file with the htpasswd tools as explained in Securing Communication and Access | openHAB.

1 Like

thanks boss… it worked

My configuration had the proxy_set_header Authorization "" in the same place, up in the top section, not under the location/ block. This is where the OpenHabian-config tool put it twice and I’ve never moved it. It is also not in the documentation on the web (which I know needs to be updated).

I moved the directive down to the location/ block as suggested by @ysc and @mstormi. Everything now seems to operate with lightning efficiency. The CPU is no longer melting down when trying to login. It actually seems just as fast as when connected locally not through NGINX. So this certainly seems to have been the fix we’ve all been looking for.

This is the config for openhab showing where it was and where I moved it to.

## openhab subdomain
server {
   listen         443 ssl;
   server_name    openhab.myDomain.us;

   # Cross-Origin Resource Sharing.
   add_header                      'Access-Control-Allow-Origin' '*' always; # make sure that also a 400 response works
   add_header                      'Access-Control-Allow_Credentials' 'true' always;
   add_header                      'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always;
   add_header                      'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always;
   add_header                      Set-Cookie X-OPENHAB-AUTH-HEADER=1;
   add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";
   proxy_set_header Authorization  "";  # <<< Where OpenHabian put it by default

   ## Secure Certificate Locations
   ssl_certificate /etc/letsencrypt/live/myDomain.us/fullchain.pem; # managed by Certbot
   ssl_certificate_key /etc/letsencrypt/live/myDomain.us/privkey.pem; # managed by Certbot

   ## Access Control
   satisfy                 any;
   allow                   192.168.0.0/24;
   allow                   127.0.0.1;
   deny                    all;
   auth_basic              "Username and Password Required";
   auth_basic_user_file    /etc/nginx/.htpasswd;

   ## Root location of subdomain
   location / {
        proxy_pass                              http://192.168.0.10:8080/;
        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;
        proxy_set_header Authorization  "";  # <<< Where I just moved it to solving the problem
        proxy_read_timeout 3600;
   }

   ## Let's Encrypt webroot location
   location /.well-known/acme-challenge/ {
       root                                    /var/www/myDomain.us;
   }
}

@Spaceman_Spiff

2 Likes

@mstormi can you verify this is how your OpenHabian-config tool is (or at least should be setting it up? There are many people here who have had the configuration spit out in this incorrect manner causing this problem. I will submit updates to the NGINX documentation page this week to bring it up to OH3’s new requirements, including this directive placement.

I’ll also direct people here in the git issue and PR. Hopefully the others do this and too and it works for them and the issue(s) on it can be closed.

1 Like

thanks for this hint. Since moving the proxy_set_header to the correct block, my SSE-Abo-Error is gone, and the connecting is as fast as I´m used to it before using nginx.

Thanks!

It should, untested though.
I just fixed it there when I’ve read Yannick’s post. So it should work for new users.
Anyone who’s already using nginx, pls be aware that this is just in the main branch for now (most openHABian users will use openHAB3 so need to change branch) and in order to update your nginx config, you must purge and reinstall. It’s way easier to fix the config.

1 Like

Doc update submitted. Should bring the docs up to closely match the openhabian config and includes this authorization directive situation.

4 Likes

@ysc might be able to correct me, but the additional add_header block should not be in the location block:

From the nginx docs:

There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.

By using add_header in the location block, you remove the headers relating to CORS and strict transport security:

The openHABian template should be:

##################################
# openHABian NGINX Configuration #
##################################

## Redirection
#REDIR server {
#REDIR   listen                          80;
#REDIR   server_name                     DOMAINNAME;
#REDIR   return 301                      https://$server_name$request_uri;
#REDIR }

## Reverse Proxy to openHAB
server {
    listen                          80;
#SSL   listen                          443 ssl;
    server_name                     DOMAINNAME;
#SSL   add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

    # Cross-Origin Resource Sharing.
    add_header 'Access-Control-Allow-Origin' '*' always; # make sure that also a 400 response works
    add_header 'Access-Control-Allow_Credentials' 'true' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always;
#AUTH   add_header Set-Cookie X-OPENHAB-AUTH-HEADER=1;

## Secure Certificate Locations
#CERT   ssl_certificate                 CERTPATH;
#CERT   ssl_certificate_key             KEYPATH;

    location / {
        proxy_pass                              http://localhost:8080/;
        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;
        proxy_read_timeout 3600;
#AUTH        proxy_set_header Authorization "";

## Password Protection
#OH2AUTH       auth_basic                              "Username and Password Required";
#OH2AUTH       auth_basic_user_file                    /etc/nginx/.htpasswd;
    }

## Let's Encrypt webroot location
#WEBROOT   location /.well-known/acme-challenge/ {
#WEBROOT       root                                    /var/www/DOMAINNAME;
#WEBROOT   }
}

# vim: filetype=conf

The script also implies that the htpasswd, is used for an OH2 instance only, this isn’t necessarily the case and htpasswd could be used to provide basic auth on OH3:

@Benjy looks like you’re right - those 2 lines add_header and proxy_set_header to make the whole thing work should be added with their existing siblings.

Agreed as well.

2 Likes

PR updated to reflect that.

Thanks once again @rlkoshak, once again excactly the info I was looking for! I run opnsense, but exactly the same config works there. I prefer to have it the same way. Just wanted to leave a keyword trace here if someone is searching authorization help with oh3 via haproxy using opnsense.

Nah, too hasty. It probably had some stuff in cache. Now it’s again in the loop of asking password if I try logging in from OH3 login button.

I have this in my opnsense haproxy.cfg:

    # ACTION: require_auth
    http-request auth realm fooo unless acl_5afc4930790c77.74502605
    # ACTION: add_header_openhab_cookie
    # NOTE: actions with no ACLs/conditions will always match
    http-request add-header Set-Cookie XOPENHAB-AUTH-HEADER=1;path=/;Secure 
    # ACTION: remove_header_auth
    # NOTE: actions with no ACLs/conditions will always match
    http-request del-header Authorization 
    http-reuse never
    server OpenHAB fedora-iot:8083 check inter 2s

So https gets terminated at opnsense after haproxy does basic auth. Then I see openhab screen just nicely. I then click on left bottom corner login button, which brings the auth dialog. After inserting creds and pressing ok it just brings back the dialog. Nothing in the logs.

That add-header is actually three header entries. Maybe opnSense won’t let you combine them all on one line like that. I saw that same behavior until I got the path and Secure parts reset in the header too.