Securing/Running openHAB Behind a Reverse Proxy and Using Sonos Binding

Hello together,

Goal
my goal is to secure my local openhab installation also against the local network. So I followed the guide provided Running openHAB Behind a Reverse Proxy which is quite straight forward. Additionally I disabled openhab listening on the lan on port 8080 by changing

OPENHAB_HTTP_ADDRESS=127.0.0.1

So far so good - everything works perfect - and all request are going through the nginx Proxy on port 80 which is forwarded to 127.0.0.1:8080 (yes I know it might be better using ssl - but at the moment I don’t care on my local network).

Problem
After this changes my sonos binding stoped partly working. Partly in the sense, that commands sending to the sonos working fine, but updates from the sonos system aren’t transmitted to the corresponding openhab items (e.g. Volume or state when changing at the speaker)

Findings so far
The state from the sonos has to be transfared from the sonos to openhab. Looking at the following link Sonos Firewall different ports are used. I tried to open the different UDP Ports - without any success. Looking at the network traffic with disabled firewall showed me that the state ist transmitted via http requests on port 8080:

192.168.x.104.38049 > 192.168…20.8080: Flags [.], cksum 0x22c2 (correct), seq 1812005778:1812007226, ack 4060895772, win 1460, options [nop,nop,TS val 83766754 ecr 74613054], length 1448: HTTP, le
ngth: 1448
       NOTIFY /upnpcallback/dev/RINCON_5CAAFD46885E01400_MR/svc/upnp-org/AVTransport/event/cb HTTP/1.1
       HOST: 192.168.x.20:8080
       CONNECTION: close
       CONTENT-TYPE: text/xml
       CONTENT-LENGTH: 2090
       NT: upnp:event
       NTS: upnp:propchange
       SID: uuid:RINCON_5CAAFD46885E01400_sub0000000501
       SEQ: 274
       <e:propertyset xmlns:e=“urn:schemas-upnp-org:event-1-0”><e:property><LastChange><Event xmlns=“urn:schemas-upnp-org:metadata-1-0/AVT/” xmlns:r=“urn:schemas-rinconnetworks-com:me
tadata-1-0/”><InstanceID val=“0”><TransportState val=“PAUSED_PLAYBACK”/><CurrentPlayMode val=“NORMAL”/><CurrentCrossfadeMode val=“0”/
><NumberOfTracks val=“2”/><CurrentTrack val=“2”/><CurrentSection val=“0”/><CurrentTrackURI val=“x-file-cifs://192.168.x.60/Media/music/rxxxx.mp3”/><CurrentTrackDuration val=“1:00:03”/><CurrentTrackMetaData val="&lt;DIDL-Lite xmlns:dc=&quot;http://purl.org/dc/el
ements/1.1/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot; xmlns:r=&quot;urn:schemas-rinconnetworks-com:metadata-1-0/&quot; xmlns=&quot;urn:schemas-upnp-org
:metadata-1-0/DIDL-Lite/&quot;&gt;&lt;item id=&quot;-1&quot; parentID=&quot;-1&quot; restricted=&quot;true&quot;&gt;&am[!http]
10:23:01.312717 IP (tos 0x0, ttl 64, id 11890, offset 0, flags [DF], proto TCP (6), length 981)
   192.168.x.104.38049 > 192.168.x.20.8080: Flags [P.], cksum 0xcf4a (correct), seq 1448:2377, ack 1, win 1460, options [nop,nop,TS val 83766754 ecr 74613054], length 929: HTTP
10:23:01.374963 IP (tos 0x0, ttl 64, id 11891, offset 0, flags [DF], proto TCP (6), length 52)
   192.168.x.104.38049 > 192.168.x.20.8080: Flags [R.], cksum 0xb8db (correct), seq 2377, ack 134, win 1460, options [nop,nop,TS val 83766766 ecr 74613058], length 0
10:23:01.374967 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
   192.168.x.104.38049 > 192.168.x.20.8080: Flags [R], cksum 0xfd05 (correct), seq 1812008155, win 0, length 0
10:23:01.463829 IP (tos 0x0, ttl 64, id 63534, offset 0, flags [DF], proto TCP (6), length 60)
   192.168.x.104.38052 > 192.168.x.20.8080: Flags [S], cksum 0x81d7 (correct), seq 1815074342, win 5840, options [mss 1460,sackOK,TS val 83766792 ecr 0,nop,wscale 2], length 0
10:23:01.466815 IP (tos 0x0, ttl 64, id 63535, offset 0, flags [DF], proto TCP (6), length 1500)
   192.168.x.104.38052 > 192.168.x.20.8080: Flags [.], cksum 0xacee (correct), seq 1815074343:1815075791, ack 2224430947, win 1460, options [nop,nop,TS val 83766793 ecr 74613070], length 1448: HTTP, le
ngth: 1448
       NOTIFY /upnpcallback/dev/RINCON_5CAAFD46885E01400/svc/upnp-org/DeviceProperties/event/cb HTTP/1.1
       HOST: 192.168.x.20:8080
       CONNECTION: close
       CONTENT-TYPE: text/xml
       CONTENT-LENGTH: 1760
       NT: upnp:event
       NTS: upnp:propchange
       SID: uuid:RINCON_5CAAFD46885E01400_sub0000007406
       SEQ: 19

So it seems, that the sonos binding is somehow registering at the sonos device and then gets state updates via normal http calls…
My idea is, to somehow configure the port on which the sonos binding will be listening and only allow traffic to this port.
The second best method I already tried is only allowing some hosts (based on IP) to allow access on port 8080. But I think in terms of security this isn’t the best solution…
Any ideas on this ?

P.S.: I didn’t have a lool at the hue binding - but perhaps there it is the same problem ?

@schogit Any luck with your Problem?

@Stone @schogit Hi! Do you have any updates on that one?

Unfortunately not. I gave up back then and didn’t try again since, due to timely restrictions.

Your issue is the Sonos binding is using UPnP for communication, like some other bindings.
For events, it registers a listener on Port 1900.

Thank you guys for responses.
@hmerk Is then any possible solution to loosen access to the 1900 port and keep the 8080 and 8443 still restricted just to localhost access? If not, is there any way to change the listeners port from 1900 to something other (let’s say 1899)? I’d be able then to listen on 1900 in my reverse proxy server and forward the incoming packages directly to 1899 hiding completely the openHAB behind the proxy. If it is still not possible, do you have any ideas and suggestions how to enable the access to the openHAB instance only via proxy and still get it working with Sonos?

Thank you

Sorry, I have no experience with reverse proxies. Port 1900 is per UPnP definition and cannot be changed.

Sorry, I mixed something up. Port 1900 is used for discovery. Need to check which port is used for GENA events, but cannot tell when to find time.

@hmerk Thank you for response. I have run a tcpdump on my machine that hosts an openHAB instance and I see that the Sonos device is still trying to connect to the 8080 port as a callback:

20:31:23.111612 IP 192.168.88.240.53059 > 192.168.88.27.8080: Flags [.], seq 3557700879:3557702327, ack 2755601836, win 1460, options [nop,nop,TS val 28673434 ecr 3512792541], length 1448: HTTP: NOTIFY /upnpcallback/dev/RINCON_5CAAFD25F40201400_MR/svc/upnp-org/AVTransport/event/cb HTTP/1.1

The 192.168.88.240 is the Sonos device addresss and 192.168.88.27 is address of my openHAB hosting machine. I came to the same conclusion as @schogit - the Sonos binding is somehow providing a callback URL to the Sonos device with hardcoded 8080 port. The communication with Sonos device works fine in one-way: I’m able to send command to Sonos like PLAY, PAUSE etc., but I don’t get any state updates from the Sonos devices, because the Sonos binding is listening right on the 8080 port and this port is configurable via OPENHAB_HTTP_PORT (I’ve checked that). And this is good, since my reverse proxy forwards all HTTP(S) (80/443) requests right to the port 8080, and the port 8080 is blocked on the system firewall. The solution would be just to somehow override the callback URL’s port for the Sonos device from 8080 to 80 or 443, so that it’ll be properly forwarded right to the openHAB instance. Is it possible to achieve that somehow?

No, sorry for being so direct, but you are misunderstanding the UPnP implementation.

UPnP bindings like Sonos or Wemo use the JUPnP stack included in openHAB. JUPnP reuses openHABs jetty server and standard HTTP port for callback urls. Bindings register a device specific calback url at JUPnP/jetty and announce this to the device.

Your reverse proxy blocks communication to the standard port, that‘s why control still works, but incoming state events cannot be received by the binding.

As you cannot mock the outgoing callback announcement to use the reverse proxy port, it is not possible to use UPnP behind a proxy.

Hope this explanation is understandable…

It’s ok, indeed I haven’t worked with UPnP yet, especially with JUPnP stack, but I understand why the connection is only one-way. I just hoped that there is a way to mock/override somehow callback announcement, but I didn’t realize that it is related to how JUPnP reuses the Jetty server underneath the openHAB and there is nothing more we can do about that. Anyway thank you for comprehensive explanation, I need to give up with this reverse proxy and find some other way to secure my openHAB instance then.
Thank you!

It is not only UPnP. Let‘s think of Shelly or Ikea TRADFRI, which use CoAP. This protokoll registers callback urls at jetty as well and announce them to the devices. So any binding using callback urls will be problematic for reverse proxies.

In fact, I never understood why it is neccesary to use a reverse proxy for internal communication in the same subnet. I don‘t see a use case for that, so never tried such thing.

Sure, I get it. I’m totally new to openHAB and I’m just following documentation. Once I got the basic concepts and played a little with openHAB configuration I naturally got down to look for some security aspects of openHAB and encountered this doc page. Since it stands right in the official docs, I just assumed it is recommended way for now to achieve some kind of authentication, at least until the native openHAB authentication mechanism is under developement (as far as I learned). Maybe I need to give it a try…

What you have read is mostly for securing from outside the network. As I wrote, never saw a use case to secure my openHAB in my home network.

Only internal security I use is the admin role within openHAB 3 to restrict configuration access.

For remote access, I use myopenHAB or a VPN tunnel into my network.

Hello togehther,

thanks for freshing up my old thread. Also I learned a lot of how the UPnP functionallity is implemented in openhab.
For me I found a quick workarround and adjusted my local firewall on the openhab instance to allow incoming traffic from the sonos to the openhab port and disallow for all other ip adresses.
But the discousing led me perhabs to a better solution - also proxying the original openhab port through nginx. But I am not sure if it is possible to listen on the same port (openhab and nginx) on different IPs on the same host.
But for my understanding I definitely need the nginx for authorisation. With openhab I can open my frontdoor, garage, turn of the heating etc. definitely nothing without authorisation, even on my local network. For remote I use a VPN.

Best regards

Hello again,

just a short update - my previous idea is really working, at least at the first look…
So openhab is only listening to the local interface on port 8080 (and unused at 8443)
/etc/default/openhab2

...
OPENHAB_HTTP_ADDRESS=127.0.0.1
...

And in the nginx configuration I enabled the authorization for openhab in general and disable it for the upnpcallback:
/etc/nginx/sites-available/openhab

server {
        listen 80 default_server;
        listen 192.168.x.y:8080 default_server;
        listen [::]:80 default_server;

        ...
        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;
                satisfy                         any;
                allow                           127.0.0.1;
                deny                            all;
                auth_basic                      "Foo baar";
                auth_basic_user_file            /etc/nginx/.htpasswd;
        }
        ....
       

        #special rule for sonos callback
        location /upnpcallback/ {
                proxy_pass                              http://localhost:8080/upnpcallback/;
                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;
                auth_basic "off";
                satisfy                         any;
                allow                           192.168.x.z; #the ip of the sonos system
                allow                           127.0.0.1;
        }
...
}

For completness - I am still running openhab 2.5.x because I am not miigrated all my v1 bindings - bit I am looking forward to do so…

@schogit Thank you for your answer! In fact your solution seems to work perfectly. I just created another server instance in my nginx configuration file and I put all the location /upnpcallback/ {...} right there. Thank you for that :slight_smile: