Удаленный доступ к Home Assistant с помощью ssh туннеля

Я недавно поставил себе сервер Home Assistant (вообще, у меня даже два сервера — один в квартире, другой на даче).

Оба сервера очень похожи друг на друга — железка Raspberry Pi 3b+, на ней стоит операционная система Raspbian, а на ней крутится Home Assistant в виде Hassio.

Находясь дома или на даче я могу заходить браузером на сервер HA который там установлен и что-то на нем делать. Но мне хочется иметь возможность управлять домом не только когда я нахожусь в этом доме, но и удаленно. Для начала я собрал список всех возможных способов как можно настроить удаленный доступ к Home Assistant. И уже из этого списка я выбрал тот способ с помощью которого я буду настраивать удаленный доступ.

Лучший вариант для того чтобы настроить удаленный доступ к HA — это использовать реальный ip адрес. Это и проще всего настраивается, да и надежнее всего — нет лишних вещей, которые могут ломаться. Но у меня реального ip адреса нет. Я решил для начала не покупать реальный ip адрес у провайдера, а попробовать собрать удаленный доступ из того что у меня уже есть. У меня есть несколько виртуалок на Digital Ocean. На них есть реальный ip адрес на который можно заходить из любой точки интернета. Поэтому мне показалось логичным попробовать использовать один из этих серверов. Сначала я думал поднять VPN туннель между серверов с Home Assistant и сервером на Digital Ocean, но потом я вспомнил что туннель очень просто сделать с помощью ssh.

Как все должно работать

Как выбрать порт на виртуалке

Я написал что что на виртуалке сервер Home Assistant будет отвечать на порту :31281 Можно было какой-нибудь другой порт. Всего есть 65536 портов, но есть некоторые правила какой порт стоит использовать.

Что такое локальные порты и какие они? Чтобы это выяснить нужно запустить команду:

# cat /proc/sys/net/ipv4/ip_local_port_range
32768   60999

Вывод это команды говорит о том что на этой машине локальные порты это порты с 32768 и по 60999. Если сделать на машине "curl google.com:80", то можно увидеть что машине будет использован порт из диапазона локальных портов:

netstat --numeric
...
tcp        0      0 xxx.xxx.xxx.xxx:33780   216.58.210.46:80        TIME_WAIT
...

Если пробрасывать Home Assistant на порт из диапазона локальных портов есть вероятность что этот порт будет занят и проброска не удасться. Так что лучше всего не использовать порты из диапазона ip_local_port_range

Как поднять ssh туннель

Чтобы на виртуалке на каком-то порту появился туннель до Home Assistant нужно на сервере с Home Assistant (в моем случае это Raspbian) нужно выполнить команду:

ssh -p 27272 -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -N -R 31281:localhost:8123 login@xxx.xxx.xxx.xxx

Только нужно заменить некоторые значения:

После ввода пароля ssh туннель будет поднят.

Можно проверить что он работает с помощью команды curl на виртуалке:

curl localhost:31281
<!DOCTYPE html>...<title>Home Assistant</title>
...

Но для того чтобы все работало в автоматическом режиме нужно чтобы ssh соединение с Raspbian до виртуалки проходила без необходимости вводить пароль. (В двух словах, нужно чтобы содержимое файла "~/.ssh/id_rsa.pub" c Raspbian было в файле ~/.ssh/authorized_keys на виртуалке)

Настройка systemd

Нужно чтобы туннель создавался автоматически при перезагрузке Raspberry Pi и чтобы он пересоздавался, если он вдруг упадет. Это можно сделать с помощью настройки systemd на Raspbian. Создаем файл /etc/systemd/system/ha-tunnel.service со следующим содержимым:

[Unit]
Description=Tunnel for HA
After=network.target

[Service]
ExecStart=/usr/bin/ssh -p 27272 -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -N -R 31281:localhost:8123 login@xxx.xxx.xxx.xxx

RestartSec=5
Restart=always

[Install]
WantedBy=multi-user.target

После этого говорим:

# systemctl start ha-tunnel

Чтобы запустить туннель. И говорим:

# systemctl enable ha-tunnel

Чтобы туннель создавался при загрузке системы

Настройка nginx

Вот пример файла /etc/nginx/sites-enabled/ha.example.com

Важный момент, интерфейс Home Assistant (который называется lovelace) активно работает через websocket, поэтому нужно не забыть правильно прописать проксирование websocket в nginx. Если это не сделать, то появится страница Home Assistant с ошибкой "Unable to connect to Home Assistant." В этом примере nginx конфига настройка websocket рабочая.

server {
    listen   443;

    server_name  ha.example.com;

    ssl on;

    ssl_certificate_key /etc/nginx/ssl/ha.example.com/privkey.pem;
    ssl_certificate /etc/nginx/ssl/ha.example.com/fullchain.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_prefer_server_ciphers on;
    ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

    ssl_session_cache shared:ssl_session_cache:10m;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";

    gzip on;
    gzip_types text/css application/javascript application/x-javascript application/json;
    gzip_http_version 1.1;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_vary on;

    access_log /var/log/nginx/jsonl/https_ha_example_com.jsonl json;

    location / {
        proxy_pass http://127.0.0.1:31281;
        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /api/websocket {
        proxy_pass http://127.0.0.1:31281/api/websocket;
        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

}

Проблемы

Изначально я поднимал ssh туннель с помощью команды:

ssh -p 27272 -N -R 31281:localhost:8123 login@xxx.xxx.xxx.xxx

Это работало достаточно стабильно с одним моим сервером HA, но со вторым моим сервером HA это работало исключительно плохо. Чаще не работало, чем работало. Все стало работать весьма стабильно после того как я изменил эту команду на:

ssh -p 27272 -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -N -R 31281:localhost:8123 login@xxx.xxx.xxx.xxx

Резюме

Итак, я настроил удаленный доступ к web интерфейсу Home Assistant с помощью проброски порта через ssh туннель на виртуалку. Теперь я могу заходить откуда угодно используя адрес https://ha.example.com (не конкретно этот адрес, но что-то вроде него).

У меня такой доступ работает уже некоторое время и работает весьма стабильно. После каких-то нештатных ситуаций все автоматически само поднимается. Можно перезагрузить Raspberry Pi или виртуалку, убить ssh туннель или отключить интернет. После того как эти неприятности пропадут, все автоматически снова заработает.

В моем решении есть, как минимум, один неоптимальный момент. Я сейчас всегда захожу на адрес https://ha.example.com для того чтобы попасть в интерфейс Home Assistant. Это очень удобно что всегда нужно вводить один и тот же адрес, но плохо, так как даже когда я нахожусь дома я иду на сервер HA через виртуалку в облаке. Наверняка это влияет на скорость работы, но тут мне все равно — даже так работает исключительно быстро. Проблема в том что если пропадет интернет, то я находясь дома не смогу заходить на свой локальный сервер HA. В этом случае мне придется заходить на адрес http://hassio.local:8123 Конечно, было бы лучше если из дома адрес ha.example.com вел бы на локальный сервер HA. Я решил это не настраивать, так как как все-таки пропадание интернета дома – это авария, которая бывает редко и в такой ситуации можно и зайти на другой адрес. Зато мне не пришлось городить систему как привозить https файлы с сертификатами на Raspberry Pi с виртуалки.

Кстати, вот как настроить чтобы Home Assistant работал по адресу hassio.local

Итого. Сейчас у меня удаленный доступ к HA сделал через сервер на Digital Ocean. Пока я доволен тем насколько стабильно это работает. Что будет дальше — пока непонятно. Прямо сейчас меня устраивает такое решение, но не исключено что я все-таки куплю себе реальный ip адрес у провайдера и переделаю эту схему на более простую.

Иван Бессарабов
ivan@bessarabov.ru

2 января 2020