Just got into Home Assistant, and although I’m excited about the possibilities, I’d like to lock down remote access and am very close to making it all work perfectly. Any help would be very appreciated
I’m currently trying to set up remote access from my Android phone and a few Windows machines on Chrome/Chromium-based browsers, where each device has its own SSL client certificate while the server uses a SSL cert from Let’sEncrypt. I can actually remotely access HA perfectly after installing a client cert on any device, but whenever I completely close/kill the Chrome app on either platform, I’m unable to reconnect to HA. More details below to help triage the issue, but does anybody have this setup working, and could they share their setup and config?
Some details about my setup:
- Home Assistant 0.57.3 on Raspbian on a Pi 3 with a password
- NGINX reverse proxy, config file supplied below
- ssl_certificate and ssl_certificate_key fields are from LetsEncrypt
- ssl_client_certificate field (the CA, as I understand it) properly generated, mostly based on this guide.
- Using client certs (.p12 extensions) on each device, generated by mostly following that same guide
- Running the latest version of Chrome and the Samsung Browser on Android and on various Win10 machines
- Installed client certificate through the system settings
- Upon accessing the site for the first time, I am prompted to select a cert to use, and upon selecting my client cert, I gain access to HA
- Killing the browser from the multi-tasking view, reopening Chrome, and refreshing the site results in either the HA “Connecting” screen if I logged into HA previously or a general “ERR_FAILED” page from the browser if I installed the cert but didn’t log in. Not sure this actually matters.
- The app must be killed. Simply pressing back to the home screen on Android is not enough to reproduce the issue.
Long story short, I used Chrome dev tools to see an error come from WebSockets while I reproduced the issue: “Websocket connection to ‘wss:///api/websocket’ failed: Websocket opening handshake was canceled” from a script called core-.js. I’m pretty sure this error comes from something called a service worker when it tries to create a WebSocket object, but I could be wrong. This same error comes up for both Windows and Android in Chrome when I reproduce the issue.
Clearing all cookies and site data for works around the issue, as I’m prompted for a certificate when I try again. Firefox and Edge on Windows also get around the issue by just prompting for a cert every time I restart the browser and load the site. I really want this to work in Chrome though so I can use html5 notifications on my desktop and phone.
My suspicion is that the problem is with WebSockets themselves rather than a NGINX misconfiguration, but I’ll admit, I don’t fully understand exactly what each line in the config file does. More specifically, I’m wondering if maybe WebSockets is not properly closing the SSL connection because of the surprise kill? I don’t know, I’m just speculating at this point.
NGINX config below, for reference. Things in are from me.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# http->https
server {
server_name <my-site>;
listen [::]:80 default_server ipv6only=off;
return 301 https://$host$request_uri;
}
# Home Assistant with client certs
server {
server_name <my-site>;
ssl_certificate /etc/letsencrypt/live/<my-site>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<my-site>/privkey.pem;
ssl_dhparam <path-to-dhparams>/dhparams.pem;
ssl_client_certificate <path-to-ca>/ca.crt;
ssl_crl <path-to-ca>/ca.crl;
ssl_verify_client optional;
#ssl_session_timeout 5m; # Note: this doesn't have any effect on the issue
listen [::]:443 default_server ipv6only=off; # if your nginx version is >= 1.9.5 you can also add the "http2" fl\
ag here
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
ssl on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
proxy_buffering off;
location / {
# SSL client verification is optional in general but not for the front end, so I can use other apps without client certificates.
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_pass http://localhost:<ha_port>;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
location /api/websocket {
proxy_pass http://localhost:<ha_port>/api/websocket;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}