unable to refresh entities
BGOtura opened this issue · 15 comments
Hi,
I have managed to connect to my HA using a long-lived access token.
My configuration in Connect IQ HA settings is:
Scenes:
Group: group.garmin
My config in HA helpers is as below:
When I do a "Refresh entities" I get on the watch:
Failed
Unknown error
code -300
It also seems to trigger this on HA´s log:
2023-03-18 11:25:43.732 ERROR (MainThread) [aiohttp.server] Error handling request Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/web_protocol.py", line 334, in data_received messages, upgraded, tail = self._request_parser.feed_data(data) File "aiohttp/_http_parser.pyx", line 551, in aiohttp._http_parser.HttpParser.feed_data aiohttp.http_exceptions.BadStatusLine: 400, message="Bad status line 'Invalid method encountered'"
Any ideas?
hi
which HA version are you using?
i ran into the same issue a few days ago.
it disappeared after the update to 2023.3.5
I've had the same problem as author (Unknown error -300). I did a little bit of troubleshooting and it turns out it was caused by my self signed certificate even though cert is installed and working in my android (Using Caddy as reverse proxy with self signed cert). When I exposed HA instance to internet (using duckdns with LetsEncrypt certificate) everything started to work.
@hatl is there a way to make self signed cert working? I would prefer using VPN to access my HA instance rather than exposing it to the internet
I can now also confirm that. When I use my self signed certificate, that I imported to my android device the widget refuses to refresh the entities with error code 300.
I use my self signed certs with Nginx Proxy Manager.
When I expose my HA instance to the web with an let's encrypt certificate, it works.
It would be really great if self signed certificates would also work, because I don't want to expose my home assistant to the internet just to use the widget.
I'm using caddy to get the certificates and home assistant is only available in the LAN and the following Caddyfile:
home-assistant.mydomain.com {
@internal {
remote_ip 192.168.0.0/24
}
handle @internal {
reverse_proxy 192.168.0.1:8123 {
header_up X-Forwarded-Host {host}
}
}
respond 403
}
I don't understand this resolution...I get a similar error using an internal-only TLS certificate. Is there any way to allow more general TLS connectivity? The connectivity works totally fine from my Android device.
@hatl : agree with @F13 . Just received my Venu 3 and wanted to your application but I'm getting the same error with a self-signed certificate. The API token works fine. I'm using the buit-in Nginx module.
According to your configuration file, the nginx default one seems pretty much the same:
core-nginx-proxy:/# cat /etc/nginx.conf
daemon off;
error_log stderr;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server_tokens off;
server_names_hash_bucket_size 128;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
#include /data/cloudflare.conf;
server {
server_name _;
listen 80 default_server;
listen 443 ssl http2 default_server;
ssl_reject_handshake on;
return 444;
}
server {
server_name ha.hysteresis.lan;
# These shouldn't need to be changed
listen 80;
return 301 https://$host$request_uri;
}
server {
server_name ha.local.lan;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_certificate /ssl/local.lan.crt;
ssl_certificate_key /ssl/local.lan.key;
# dhparams file
ssl_dhparam /data/dhparams.pem;
listen 443 ssl http2;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
proxy_buffering off;
#include /share/nginx_proxy_default*.conf;
location / {
proxy_pass http://homeassistant.local.hass.io:8123;
proxy_set_header Host $http_host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-Host $http_host;
}
}
#include /share/nginx_proxy/*.conf;
}
BTW, a couple of comments regarding the logging process:
To login, simply open the widget and trigger any scene. Shortly after, you will see a sign in request on your smartphone. Complete the sign in process on your phone and return to the watch.
I did create a scene and triggered it (using my laptop) after opening the widget (and click on login
, but all I got is a black screen.
If you don't have any scenes, you can login by hold the menu button on your watch and logging in from them widget menu.
This is the process I followed and I get a blackscreen.
and FYI, I've added my self-signed certificate to my phone and the application (Companion) works well, as well as with Chrome.
Thanks!
the Garmin API doesn't allow the usage of self-signed certificates - for good reasons (security)
importing them on the phone only makes them available on the phone
they would have to be added on the watch (which is - as far as i know - currently not possible)
so having an "official" SSL certificate is currently a must-have
you can get them either by using the caddy config i provided or by buying a certificate - those have become quite cheap
That is very unfortunate. There's no inherent security risk to allowing the user to trust certificates outside of the "globally trusted" certificate issuers; in fact, for advanced users, it can be just the opposite. In order to get an "official" SSL certificate, I would have to setup DNS resolution for a globally accessible domain that points to my infrastructure.
I understand most people are fine with this, but I disagree with device manufacturers' insistence in locking down SSL trust chains :)
Is there any option in the Garmin API to simply skip certificate validation? Exposing that option to users would allow the user to use whatever certificate they wanted.
Does the watch communicate directly with the services over HTTPS? It doesn't go through your connected mobile (Android/Apple)?
Is there any option in the Garmin API to simply skip certificate validation?
Unfortunately not - the documentation of the corresponding method (makeWebRequest) can be found here: https://developer.garmin.com/connect-iq/api-docs/Toybox/Communications.html#makeWebRequest-instance_function
I would have to setup DNS resolution for a globally accessible domain that points to my infrastructure
yes.
you could use Duck DNS and Dnsmasq
there are Home Assistant add-ons for both
Duck DNS can be configured to use Let's Encrypt SSL certificates
Does the watch communicate directly with the services over HTTPS?
Its being "tunneled" trhough the bluetooth connection to the phone, but the actual connection is established between the Garmin device and Home Assistant. This is why adding the SSL certificates on the phone doesn't help.
@F13 agree for the security, and yeah, it's unfortunate.
@hatl thanks for the doc.
As a workaround, it could be possible to retrieve a genuine LE certificate and use a local DNS to fake the IP address.
EDIT: someone apparently did it by installing a root certificate on his iPhone: https://forums.garmin.com/developer/connect-iq/f/discussion/291012/makewebrequest-for-internal-networks
My CA cert is fully installed and trusted on my Android already. It doesn't appear that Garmin Connect uses the Android certificates.
I finally managed to find a way using the following steps:
- request a certificate from let's encrypt (e.g ha.example.com)
- add a
A
entry on your DNS provider to point to your reverse proxy internal IP - copy the certificate to a server handling the reverse proxy
- add this proxy to the
trusted_proxies
in HA - configure the app to point to your domain
- (optional) add a
list rebind_domain
in case you get aDNS-rebind attack detected
with OpenWRT
It's probably possible to bypass the reverse to point to HA directly (Nginx) but the certificate provided by LE doesn't seem to be recognize by Nginx (most probably a format issue)
EDIT: I found a way to add another nginx conf file to handle the request for the new domain following this guide https://community.home-assistant.io/t/using-nginx-ssl-proxy-to-forward-different-domains-to-different-services/347342/4
Now HA handles the request coming from ha.example.com
instead of using another server. Last step would be to automate the key copy between the 2 servers but it's pretty easy to deal with.
Anyway, thanks for your app @hatl !