Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] HomeAssitant SSL Support #189

Open
1 task done
BugRoger opened this issue Nov 12, 2023 · 17 comments
Open
1 task done

[bug] HomeAssitant SSL Support #189

BugRoger opened this issue Nov 12, 2023 · 17 comments
Labels
bug Something isn't working integration Integration driver related issue

Comments

@BugRoger
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Description

My HA is runing behind a reverse proxy. The connection is using https/wss with signed SSL certificates provided by Let's Encrypt.

The HA integration does not support SSL. The error:
2023-11-12 10:06:51.429977 +00:00 intg-homeassistant INFO [2023-11-12T10:06:51Z WARN uc_intg_hass::controller::handler::ha_connection] Could not connect to wss://homeassistant.l48a.de/api/websocket: SendRequest(TunnelNotSupported)

How to Reproduce

Setup HA Integration using a wss:// URL.
Observe Internal Timeout Error

Expected behavior

Support SSL for HA Websocket Connection.

This might be a solution:
actix/actix-web#1069

System version

No response

What part of the system affected by the problem?

Integration

Additional context

No response

@BugRoger BugRoger added the bug Something isn't working label Nov 12, 2023
@github-project-automation github-project-automation bot moved this to Reported in Bug reports Nov 12, 2023
@github-actions github-actions bot added the integration Integration driver related issue label Nov 12, 2023
@RC-Thoughts
Copy link

I have a HA with https/wss, no http/ws used at all so I need this too.

@RC-Thoughts
Copy link

RC-Thoughts commented Nov 13, 2023

I did a quick'n'dirty workaround. I have HA as a docker on debian, installed Nginx to debian with a simple and minimal config to act as a ws to wss (http to http) proxy. Works fine.

Nginx config:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream appserver {
    server 192.168.1.6:443; # Home Assistant address
}

server {
    listen 80;

    location / {
        proxy_pass https://appserver;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

@zehnm
Copy link

zehnm commented Nov 13, 2023

@RC-Thoughts have you already tried connection with wss and get the same error?

The integration supports wss connections and it has been confirmed working. But apparently not all TLS configurations (or http2) are supported.
What kind of setup are you using?

@BugRoger thanks for the link! HTTP 1.1 only in ALPN extension is already set, but maybe I've missed something else. Since I've updated rustls which required some refactoring (unfoldedcircle/integration-home-assistant@617466d), I'd like to retest everything before a new release, but I need to find the time to setup a reverse proxy first.

@RC-Thoughts
Copy link

@zehnm Yes, and "Web Socket Client" application (Microsoft Store, Windows 11) does connect with wss just fine.

@BugRoger
Copy link
Author

I’m using Traefik as reverse proxy. The config is pretty vanilla. wss connections do work of course. I tested with a chrome plugin.

Other tools in my setup like esphome can also connect to HomeAssistant without problem.

@zehnm
Copy link

zehnm commented Nov 14, 2023

I've setup a test environment on a VPS with a public IP address. The Home-Assistant integration is working using wss, tested with the included v0.4.0 in the firmware and main branch with new rustls version.

  • using linuxserver/swag Docker image b5e8651bcc11 with Nginx reverse proxy and Let's Encrypt certbot
    • nginx 1.24.0
    • only https with http redirection
  • Home Assistant linuxserver/homeassistant Docker image running in a private network (10.89.0.0/24), only reachable over reverse proxy on a subdomain.
  • Using default configuration provided by LinuxServer
    • set my domain information as ENV vars as documented by LinuxServer
    • enabled the provided Home Assistant subdomain proxy template
    • configured Home Assistant for proxy:
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 10.89.0.0/24

homeassistant:
  external_url: "https://homeassistant.XXXXXX.ZZZ"

Please check your configuration if you see any major changes which causes wss not to work. If we find the cause and it can be fixed with a client setting, then I can fix the Home Assistant integration.

Nginx configuration files from LinuxServer as reference:

  • proxy-confs/homeassistant.subdomain.conf
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name homeassistant.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }

    location ~ ^/(api|local|media)/ {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}
  • /config/nginx/proxy.conf
## Version 2023/02/09 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/nginx/proxy.conf.sample

# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

# Proxy Connection Settings
proxy_buffers 32 4k;
proxy_connect_timeout 240;
proxy_headers_hash_bucket_size 128;
proxy_headers_hash_max_size 1024;
proxy_http_version 1.1;
proxy_read_timeout 240;
proxy_redirect http:// $scheme://;
proxy_send_timeout 240;

# Proxy Cache and Cookie Settings
proxy_cache_bypass $cookie_session;
#proxy_cookie_path / "/; Secure"; # enable at your own risk, may break certain apps
proxy_no_cache $cookie_session;

# Proxy Header Settings
proxy_set_header Connection $connection_upgrade;
proxy_set_header Early-Data $ssl_early_data;
proxy_set_header Host $host;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
  • /config/nginx/ssl.conf
## Version 2023/08/13 - Changelog: https://github.com/linuxserver/docker-baseimage-alpine-nginx/commits/master/root/defaults/nginx/ssl.conf.sample

### Mozilla Recommendations
# generated 2023-06-25, Mozilla Guideline v5.7, nginx 1.24.0, OpenSSL 3.1.1, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.24.0&config=intermediate&openssl=3.1.1&guideline=5.7

ssl_certificate /config/keys/cert.crt;
ssl_certificate_key /config/keys/cert.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;

# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dhparam /config/nginx/dhparams.pem;

# 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:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;

# HSTS (ngx_http_headers_module is required) (63072000 seconds)
#add_header Strict-Transport-Security "max-age=63072000" always;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /config/keys/cert.crt;

@Marco4223
Copy link

My HA is also using let’s encrypt and https only.
So I’m not hundred percent sure what I had chosen as connection string but my is working stable and fun so far. Only thing: I’m not using a proxy. HA is only reachable in the specific VLAN (where also the R2 life’s) internally and I’m connection to my LAN via VPN in cases I’m OutOfHome 😉

@wez
Copy link

wez commented Nov 14, 2023

FWIW, I also have my hass behind a traefik instance and was unable to connect to it from the remote using wss:// for the protocol.

Everything else on my network goes via traefik and works perfectly.

@BugRoger
Copy link
Author

BugRoger commented Nov 14, 2023

FWIW, I also have my hass behind a traefik instance and was unable to connect

@wez What did you do to fix it?

@zehnm
Copy link

zehnm commented Nov 14, 2023

Question to all for whom it does not work:

  • Is your reverse proxy in the same network as the Remote Two?
  • If on the same internal network, just out of interest: any special reason to use wss on your internal network?
  • What url are you using to connect? A public address (DNS or dynamic DNS) or an internal name?
  • If public name: are you using a NAT-loopback or split DNS (I hope I used the correct terms)?
  • Do you have IPv6 configured?

I have a suspicion if it's a public DNS name: the external IP of your router is resolved and without NAT-loopback it fails to connect.
This might be also caused by systemd-resolved with it's fallback DNS...

@BugRoger
Copy link
Author

BugRoger commented Nov 14, 2023

Is your reverse proxy in the same network as the Remote Two?

Define same network. 😄 It's in a different subnet but routable. There's a MDNS reflector between subnets.

If on the same internal network, just out of interest: any special reason to use wss on your internal network?

SNI to the reverse proxy

What url are you using to connect? A public address (DNS or dynamic DNS) or an internal name?

CNAME homeassistant.l48a.de > ingress.l48a.de. A ingress.l48a.de > 10.0.0.3, resolved by Google DNS

If public name: are you using a NAT-loopback or split DNS (I hope I used the correct terms)?

Neither. I'm just using google DNS everywhere 8.8.8.8. No NAT between remote and proxy.

Do you have IPv6 configured?

Yes

@zehnm
Copy link

zehnm commented Nov 14, 2023

@BugRoger thanks for the information!
The "Home Assistant integration" logs with priority DEBUG should show more information why the connection to the HA server doesn't work.

@BugRoger
Copy link
Author

Yes, i was too quick. Still same error with the middleware:

2023-11-14 20:40:07.751426 +00:00	core	ERROR	Error encountered while processing the incoming HTTP request: InternalServerError
2023-11-14 20:40:07.749724 +00:00	core	NOTICE	[hass.main] Waiting for available entities with request id: 119
2023-11-14 20:40:07.745470 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z WARN  uc_intg_hass::server::ws::connection] [127.0.0.1:54634] Error processing received text message: The connection is closed or closing
2023-11-14 20:40:07.745470 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z ERROR uc_intg_hass::controller::handler::r2_request] Unable to request available entities: HA client connection not available!
2023-11-14 20:40:07.745470 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z INFO  uc_intg_hass::controller] State machine transition: Running
2023-11-14 20:40:07.555404 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z INFO  uc_intg_hass::controller::handler::ha_connection] Max reconnect attempts reached (1000). Giving up!
2023-11-14 20:40:07.555404 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z WARN  uc_intg_hass::controller::handler::ha_connection] Could not connect to wss://homeassistant.l48a.de/api/websocket: SendRequest(TunnelNotSupported)
2023-11-14 20:40:07.534975 +00:00	core	NOTICE	Ok(DriverSetupChange { event_type: Stop, state: Ok, error: None, require_user_action: None })
2023-11-14 20:40:07.461902 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z INFO  uc_intg_hass::controller::handler::ha_connection] Max reconnect attempts reached (1000). Giving up!
2023-11-14 20:40:07.461902 +00:00	intg-homeassistant	INFO	[2023-11-14T20:40:07Z WARN  uc_intg_hass::controller::handler::ha_connection] Could not connect to wss://homeassistant.l48a.de/api/websocket: SendRequest(TunnelNotSupported)
2023-11-14 20:40:07.454866 +00:00	core	NOTICE	Removed standby inhibitor: Integration setup: hass
2023-11-14 20:40:07.449881 +00:00	core	NOTICE	[hass] SessionAuthenticated msg
2023-11-14 20:40:07.449179 +00:00	core	NOTICE	[hass] Session state change: Authenticated
2023-11-14 20:40:07.448125 +00:00	core	NOTICE	[hass] Session became active: sending get_driver_version, connect, subscribe_events
2023-11-14 20:40:07.446219 +00:00	core	NOTICE	[hass] SessionAuthenticated msg
2023-11-14 20:40:07.444926 +00:00	core	NOTICE	[hass] Session state change: Authenticated
2023-11-14 20:40:07.436653 +00:00	core	NOTICE	[hass] Connected to: ws://127.0.0.1:8000/ws
2023-11-14 20:40:07.431660 +00:00	core	WARN	Driver not connected to start setup flow, starting driver. State: Some(Idle)
2023-11-14 20:40:07.430359 +00:00	core	NOTICE	Added standby inhibitor: Integration setup: hass
2023-11-14 20:39:55.076549 +00:00	core	NOTICE	[hass] Session state change: Disconnected
2023-11-14 20:39:55.067017 +00:00	core	NOTICE	[hass] StopDriver msg
2023-11-14 20:39:55.053206 +00:00	core	NOTICE	Stopping driver 'hass': no more instances

@wez
Copy link

wez commented Nov 15, 2023

FWIW, I also have my hass behind a traefik instance and was unable to connect

@wez What did you do to fix it?

@BugRoger: I couldn't fix it; I had to configure the remote to use unencrypted ws:// direct to the hass instance.

@wez
Copy link

wez commented Nov 15, 2023

Question to all for whom it does not work:

* Is your reverse proxy in the same network as the Remote Two?

Yes

* If on the same internal network, just out of interest: any special reason to use wss on your internal network?

Regrettably, there are several IoT devices that I do not 100% trust not to sniff out credentials from my home network, either now or in the future, that have to run on this same network. Similarly, not everyone in my household is fully computer savvy and they are at higher risk of running with malware that may also decide to snoop on the network via their devices.

I strongly prefer to use encrypted traffic even on the local network to avoid leaking credentials.

* What url are you using to connect? A public address (DNS or dynamic DNS) or an internal name?
* If public name: are you using a NAT-loopback or split DNS (I hope I used the correct terms)?

I have split DNS for a fixed name, but whether your stack is hardcoded to use public DNS servers, or respects the local DNS configured via DHCP, the name resolves to the same IP.

* Do you have IPv6 configured?

Yes, my network is IPv6 + IPv4, but this particular name does not resolve to AAAA.

I have a suspicion if it's a public DNS name: the external IP of your router is resolved and without NAT-loopback it fails to connect. This might be also caused by systemd-resolved with it's fallback DNS...

Note that everything else on my network is capable of correctly resolving this name and talking to the traefik instance.

@zehnm
Copy link

zehnm commented Nov 24, 2023

I forgot to mention that the 1.4.8 firmware disables the fallback DNS (Cloudflare, Quad9, Google). This could resolve a few issues with split DNS setups where the Remote & HA are both in the internal network. After wakeup I've observed, that the fallback DNS were often used instead the configured ones with DHCP.

Could someone post a Traefik configuration that I could try out? I've never used it and it would speed up getting to a working setup :-)
Then I'm hopefully able to track down why it doesn't work.

@wez
Copy link

wez commented Nov 24, 2023

Could someone post a Traefik configuration that I could try out? I've never used it and it would speed up getting to a working setup

I'm by no means an expert, but here's the gist of my configuration.
It causes https://HASS.YOURDOMAIN to proxy for http://192.168.1.5:8123.
In reality, I have several other services in the config.yml file, not just the hass definition.

This is my docker-compose.yml:

version: '3'

services:
  reverse-proxy:
    container_name: traefik
    # The official v2 Traefik docker image
    image: traefik:v2.9
    network_mode: host
    restart: unless-stopped
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/config.yml:/config.yml
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
      - ./data:/config-data

Alongside it I have a data directory that contains these two files:

This is traefik.yml:

global:
  checkNewVersion: true
  sendAnonymousUsage: false

api:
  dashboard: true
  debug: false
  insecure: false

log:
  level: ERROR

entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"

serversTransport:
  insecureSkipVerify: true

# https://doc.traefik.io/traefik/observability/access-logs/
accessLog:
  fields:
    defaultMode: keep
    headers:
      defaultMode: keep
      names:
        Authorization: drop

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    # points to the `config.yml` file
    filename: /config-data/config.yml

certificatesResolvers:
  resolver:
    acme:
       # config here for Let's Encrypt.
       # See: https://doc.traefik.io/traefik/https/acme/#certificate-resolvers
       email: [email protected]
       storage: acme.json

       dnsChallenge:
           # Config here for my DNS provider. I use DNS for this because
           # I'm ok with public names, but this particular instance can only
           # respond on the LAN, so it is not possible to use HTTP or TLS
           # challenges.
           # If you don't want to or can't use DNS challenges with Let's Encrypt,
           # you can instead create and configure your own TLS certs directly.
           # I'm not familiar with how to do that, but 
           # https://doc.traefik.io/traefik/https/tls/ should tell you

This is config.yml:

http:
  routers:
    traefik:
      entryPoints:
        - "https"
      # allow seeing traefik's dashboard at `https://traefik.YOURDOMAIN`
      rule: "Host(`traefik.YOURDOMAIN`)"
      middlewares:
        - authelia
        - https-redirectscheme
      service: api@internal
      tls: &TLS
        certresolver: resolver
        domains:
          - main: "THISBOX.YOURDOMAIN"
            sans: "*.YOURDOMAIN"
    hass:
      entryPoints:
        - "https"
      rule: "Host(`hass.YOURDOMAIN`)"
      middlewares:
        - https-redirectscheme
        - default-headers
      service: hass
      tls: *TLS

  services:
    hass:
      loadBalancer:
        servers:
          - url: "http://192.168.1.5:8123"  # Your hass IP:port

  middlewares:
    https-redirectscheme:
      redirectScheme:
        scheme: https
        permanent: true
    default-headers:
      headers:
        frameDeny: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 15552000
        customFrameOptionsValue: SAMEORIGIN
        customRequestHeaders:
          X-Forwarded-Proto: https

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working integration Integration driver related issue
Projects
Status: Reported
Development

No branches or pull requests

5 participants