Dear community,
when I began to deal with openhab the lack of access control was really annoying me, but the open architecture made me stay with it.
Access control to give certain users only restricted access was mandatory for my use case. I acquired an appropriate solution for my requirements that I want to share with you.
As a new user here I “can only put 2 links in a post”. As a workaround links are placed at the bottom for bold items.
Summary
- Access control by reverse proxy
- Restricting access to the REST API (e. g. used by mobile apps)
- Not working with Basic UI, Paper UI and other frontends not using the REST API
- High effort for an individual rule set
- It has a taste of hack
A reverse proxy that openhab works behind is definetly a good idea for security reasons like SSL/TLS as described in Securing access to openHAB. Furthermore it can be configured to restrict access per user. Effectively that is done by removing unwanted parts from an openhab json response before it is sent to the client.
I use the openhab app and watched its requests sent to openhab and their responses. For example at first it requests the available sitemaps by calling /rest/sitemaps
. By removing any sitemaps from the response that are not designated for the user he only sees what he should see.
The following is related to Apache used as reverse proxy (for nginx it might be similar).
Any user must have its own domain for access like alice.mydomain.com
and bob.mydomain.com
. (More users can adress a single domain if they are supposed to have the same access restrictions.) This is necessary because Apache first decides what is requested and after that it gives access (and modifies the response) or not. It is not possible to configure something like if user = alice then this and if user = bob then that. So every “entrance” (domain) defines what a user can see and what he is allowed to do here. (Without authentication for this entrace he will directly be thrown out.)
If a DynDNS service is used it should offer a few sub domains (such as noip.com
offers 3 for free) and your router or whatever DynDNS client is used must also be able to feed more than one DynDNS name.
(This should also work: Get an account for one or more DynDNS services. Setup a cron job for calling regularly the update URL for each subdomain like https://youruser:yourpassword@dynupdate.any-dyndns-service.com/update?hostname=alice.yourdomain.com
. Stick to the terms of usage to not get blocked.)
The (sub) domains are mapped to virtual hosts in apache.
<IfModule ssl_module>
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerName alice.mydomain.com
ErrorLog ${APACHE_LOG_DIR}/alice.mydomain.com_error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/alice.mydomain.com_access.log combined
# SSL config goes here
<IfModule proxy_module>
# proxy config goes here
The core work ist done by jq, a neat json processor which is included in many linux distribution repositories. It is called by the mod_ext_filter apache module passing the openhab response and a filter to it. It returns the result appropriate to the given filter.
Each api function = url location has its own process definition (deny, grant at all, grant and let jq modify the response).
# here begins the rule set
# globally deny access to openhab, in particular to "open" frontends like Basic UI, Paper UI...
<Location "/">
Require all denied
</Location>
# give access for a user to the REST API
<Location "/rest">
AuthType Basic
AuthName "Access for Alice"
AuthBasicProvider file
AuthUserFile "/etc/openhab2/openhab.htpasswd"
Require user alice
</Location>
# give access to request available sitemaps and ...
<LocationMatch "^/rest/sitemaps/?$">
AuthType Basic
AuthName "Access for Alice"
AuthBasicProvider file
AuthUserFile "/etc/openhab2/openhab.htpasswd"
Require user alice
# ... apply the filter to respond only those sitemaps designated for the user
SetOutputFilter filter-alice-sitemaps
</LocationMatch>
# give full access (no response modification) to a particular sitemap
<Location "/rest/sitemaps/alice_sitemap">
AuthType Basic
AuthName "Access for Alice"
AuthBasicProvider file
AuthUserFile "/etc/openhab2/openhab.htpasswd"
Require user alice
</Location>
# more location rules needed here for the other functions
# see http://demo.openhab.org:8080/doc/index.html and the log for this virtual host when accessed by an openhab client
</IfModule>
</IfModule>
</VirtualHost>
</IfModule>
What is missing so far is the filter definition. (Just place it above all the other stuff!)
ExtFilterDefine filter-alice-sitemaps mode=output cmd="/usr/bin/jq -c '[.[]|select(.name==\"alice_sitemap\")]'"
# Explanation of this filter
# filter-alice-sitemaps: the filter name
# mode=output: apply the filter to the response which will be delivered to the client
# cmd="/usr/bin/jq -c...: call jq
# [ return all matches as an array ]
# .[] take each element of the original array
# |select(.name==\"alice_sitemap\") retain only those of them containing a key "name" with the value "alice_sitemap"
Example: If this is the openhab response for a sitemaps request rest/sitemaps
(notice all links with alice.mydomain.com
)
[
{
"name": "admin_sitemap",
"label": "Admin",
"link": "https://alice.mydomain.com/rest/sitemaps/admin_sitemap",
"homepage": {
"link": "https://alice.mydomain.com/rest/sitemaps/admin_sitemap/admin_sitemap",
"leaf": false,
"timeout": false,
"widgets": []
}
},
{
"name": "alice_sitemap",
"label": "Alice",
"link": "https://alice.mydomain.com/rest/sitemaps/alice_sitemap",
"homepage": {
"link": "https://alice.mydomain.com/rest/sitemaps/alice_sitemap/alice_sitemap",
"leaf": false,
"timeout": false,
"widgets": []
}
},
{
"name": "bob_sitemap",
"label": "Bob",
"link": "https://alice.mydomain.com/rest/sitemaps/bob_sitemap",
"homepage": {
"link": "https://alice.mydomain.com/rest/sitemaps/bob_sitemap/bob_sitemap",
"leaf": false,
"timeout": false,
"widgets": []
}
}
]
the result is
[
{
"name": "alice_sitemap",
"label": "Alice",
"link": "https://alice.mydomain.com/rest/sitemaps/alice_sitemap",
"homepage": {
"link": "https://alice.mydomain.com/rest/sitemaps/alice_sitemap/alice_sitemap",
"leaf": false,
"timeout": false,
"widgets": []
}
}
]
Just try it on jqplay.org without the backslahes [.[]|select(.name=="alice_sitemap")]
Filters for Things, Items and others are omitted here but they are necessary for denying access to them. I can supply my rules on demand.
Much stuff, maybe this will help someone.
At last the workaround link list
Securing access to openHAB
https://www.openhab.org/docs/installation/security.html
openhab app
https://www.openhab.org/docs/apps/android.html
Sitemaps
https://www.openhab.org/docs/configuration/sitemaps.html
jq
https://stedolan.github.io/jq/
mod_ext_filter
https://httpd.apache.org/docs/2.4/mod/mod_ext_filter.html