Meine Empfehlung ist ja nach wie vor das Home Assistant OS als Basis für Deine Home Assistant Installation zu nutzen. Denn dann hat man Apps (früher Add-Ons) zur Verfügung und kann weitere Anwendungen (wie evcc, zigbee2mqtt, InfluxDB, usw.) mit nur einem Klick installieren und auch ganz einfach aktuell halten. Um Backups muss man sich auch nicht mehr kümmern, da diese im Home Assistant Backup enthalten sind (wenn man dies aktiviert). Leider kommt man nicht per SSH auf das System. Ja, über Apps wie Advanced SSH & Web Terminal kann man sich per SSH verbinden. Aber dann ist man im Docker-Container der App und eben nicht auf dem Host. Das ist etwas ganz anderes. Über einen USB-Stick mit einer vorbereiteten Datei, kann man aber auf das System dennoch per SSH zugreifen. Hier möchte ich den Weg unter Proxmox zeigen, da man hier einfach mit virtuellen Datenträgern arbeiten kann.
In diesem Beitrag erfährst Du, wie man den SSH-Zugang zu einer virtuellen Maschine unter Proxmox realisiert, in welcher das Home Assistant Operating System läuft. Das ist ein sehr beliebtes Setup für viele.
Wozu brauche ich das? Als Entwickler muss ich ab und zu in die Verzeichnisse schauen, welche in die Container gemountet werden.
- Zugriff wird auf Port
22222aktiviert (da der Standard-Port für SSH22eventuell schon von einer App belegt ist) - Als SSH-Dienst kommt
dropbearzum Einsatz (läuft nur, wenn die Datei/root/.ssh/authorized_keysexistiert) - Man könnte auch
loginaufrufen, ohne SSH zu aktivieren. Dann fehlt einem allerdings Copy/Paste und das Tastatur-Layout wird nicht übernommen.
Video: Home Assistant OS: Per SSH auf den Host#
Befehle#
Die neue Partition muss CONFIG heißen. Sonst erkennt Home Assistant dieses Laufwerk nicht als Quelle für die neuen Konfigurationsdateien.
dd if=/dev/zero of=/var/lib/vz/images/haos-config-usb.img bs=1M count=64
mkfs.vfat -F 32 -n "CONFIG" /var/lib/vz/images/haos-config-usb.img
mkdir -p /mnt/temp_usb
mount -o loop /var/lib/vz/images/haos-config-usb.img /mnt/temp_usb
nano /mnt/temp_usb/authorized_keys
# --> SSH Public Key(s) einfügen
umount /mnt/temp_usb
rmdir /mnt/temp_usb
qm monitor 210
drive_add 0 if=none,id=usbkey,format=raw,file=/var/lib/vz/images/haos-config-usb.img
device_add usb-storage,id=usbkey,drive=usbkey
quit
Mit dem folgenden Befehl im Home Assistant Operating System wird die authorized_keys Datei in das ~/.ssh/ Verzeichnis kopiert.
ha os import
Test von deinem SSH-Client:
ssh -p 22222 root@homeassistant.local
Um aufzuräumen, können die vorher erstellen Dateien noch gelöscht werden:
qm monitor 210
device_del usbkey
quit
rm /var/lib/vz/images/haos-config-usb.img
Deep dive#
Auf dem Host wird /usr/sbin/haos-cli gestartet. Dieses Skript läuft als getty-Ersatz direkt auf dem Host per Dienst /usr/lib/systemd/system/ha-cli@.service.
Somit sieht man keinen Login oder ähnliches nach dem Start des Systems, sondern nur die Infos von Home Assistant mit der bekannten ha> Eingabeaufforderung, um auf grundlegende Home Assistant Funktionen zuzugreifen. Am Ende läuft das alles aber in einem Container vom Image hassio-cli weitergibt. Siehe plugin-cli. Dort läuft das Script /usr/bin/cli.sh. Tippt man nun login, wird der Prozess im Container mit dem Exit-Code 10 beendet:
login() {
echo 'Entering OS shell...'
exit 10
}
# [...]
case $line in
exit) exit 0 ;;
login|login\ *) login; return ;;
# [...]
Diesen Exit-Code wertet der Getty-Ersatz von oben auf dem Host aus und startet eine Shell:
run_shell() { exec /bin/ash -l; }
# [...]
case $? in
10)
# Jump to root login shell (login command)
run_shell
;;
# [...]
Das ist ganz smart gemacht, da so die komplette CLI-Logik im isolierten hassio-cli Container läuft und alle Befehle darin ausgeführt werden. Dadurch ist ha vor den Befehlen auch nicht nötig, da diese ja eh schon im entsprechenden Context laufen.
Aber woher kommt ha? Das liegt in einem separaten CLI Repository und ist somit vom Container getrennt. Über das Docker-File wird eine konkrete Version heruntergeladen und in /usr/bin/ha gespeichert.
Aber wie funktioniert der Import?#
Wenn der Befehl ha os import im Container ausgeführt wird, wie schafft es dieser Container dann, die Datei auf dem Host abzulegen? Dafür schauen wir den Befehl selbst an.
Eigentlich wird hier nur ein helper.GenericJSONPost("os", "config/sync", nil) aufgerufen. Also ein HTTP-Post gegen ein anderes System. Und zwar an den supervisor: POST http://supervisor/os/config/sync
Das ist wieder ein anderer Docker-Container, welcher über das Docker-Netzwerk hassio mit diesem Namen auflösbar ist. Dieser stellt einen Webservice bereit - siehe GitHub. Aber am Ende sind wir jetzt vom einen Container im anderen gelandet. Eigentlich also erstmal nur ein Signal etwas zu tun. Und zwar der Neustart eines Dienstes auf dem Host: hassos-config.service.
Dieser läuft auf dem Host als /usr/lib/systemd/system/haos-config.service und startet /usr/sbin/haos-config. Und dieser Service (vom Type=oneshot) mountet den USB-Stick und kopiert die Daten an die richtigen Stellen. Was geht alles? Hier ein Auszug:
# NetworkManager
cp -f ${CONFIG_DIR}/network/* /etc/NetworkManager/system-connections/
# Modules
cp -f ${CONFIG_DIR}/modules/* /etc/modules-load.d/
# Modules configuration
cp -f ${CONFIG_DIR}/modprobe/* /etc/modprobe.d/
# Udev
cp -f ${CONFIG_DIR}/udev/* /etc/udev/rules.d/
# SSH know hosts
cp -f ${CONFIG_DIR}/authorized_keys /root/.ssh/authorized_keys
# timesyncd
cat "${CONFIG_DIR}/timesyncd.conf" > /etc/systemd/timesyncd.conf
# Firmware update / Only USB
rauc_filename=$(ls ${USB_CONFIG}/*.raucb | head -n 1)
if rauc install "${rauc_filename}"; then
ha os import (cli-Container)
└─ HTTP POST → supervisor (internes hassio-Netz, Token)
└─ D-Bus → systemd RestartUnit (Host-Bus-Socket im Supervisor gemountet)
└─ haos-config.service → /usr/sbin/haos-config (nativ auf dem Host, root)
└─ cp von USB/CONFIG → /etc/... , /root/.ssh/...
Heißt: Auch ein Neustart des Systems würde funktionieren. Dann wird der Import ebenfalls durchgeführt.
Weiterführende Links#
Transparenz-Hinweis (Level 1: Komplett selbst finanziert)
An diesem Beitrag ist kein Hersteller beteiligt! Sämtliche Produkte habe ich selber gekauft und trage die kompletten Kosten für diesen Beitrag alleine! Die Inhalte wurden somit von niemandem gesehen oder abgestimmt. Es handelt sich zu 100 Prozent um meine persönliche Meinung und Erfahrung! Danke an die Community, dass ich solche Inhalte für die Allgemeinheit zur Verfügung stellen kann!
