Samstag, 12. Dezember 2020

Freie MQTT Instanz im Internet für Testzwecke

Es muss ja nicht immer einfach sein, einen kostenlosen Dienst zu nutzen. Deswegen meine Notiz, damit ich beim nächsten Mal noch weiß wie es geht. 

Registrierung https://myqtthub.com/en/

Hier braucht man eine funktionierende Email Adresse, gibt sich einen Usernamen und Passwort. Innerhalb kurzer Zeit bekommt man 4 Emails. Sie enthalten keine wesentlichen Informationen außer einem Link zur eigentlichen Accountverwaltung https://pas.aspl.es/ 

Montag, 30. November 2020

WSL - ein paar Notizen zur Verwendung

Es gibt eine Menge im Netz zum WSL, aber vieles ist nicht klar mit Versionshinweisen beschrieben. Ich habe etwas mit WSL herum gespielt und hier meine Notizen dazu.

Versionen

Windows Server 2019: unterstützt nur V1, Tools: wsl.exe, wslconfig.exe, keine Store - nur manuelle Installation der (älteren) Distributionen.

Windows 10: unterstützt V1 und V2, Tools: wsl.exe mit erweitertem Umfang

Version 1: keine virtuelle Netzwerkkarte, verwendet direkt das Hostnetzwerk

Version 2: eine virtuelle Netzwerkkarte (bei jedem Neustart neue IP Adresse, Bereich 172.x.x.x) für alle Distributionen, besserer Kernel

Hier der offizielle Vergleich von MS.

Zugriff mit der virtuellen Maschine

wsl.exe öffnet quasi das "Terminal" ssh braucht man für die "Bedienung" nicht. 

Im Zusammenhang mit dem Thema "Kommandozeile" lohnt sich ein Blick auf Windows Terminal, dies ist seit kurzem über Windows Store installierbar.

Alle Datenträger vom Host sind direkt erreichbar (/mnt/c/...)

Die Ports aus der virtuellen Maschine werden an den Host durchgereicht, man kann über localhost:port oder 127.0.0.1:port zugreifen. Will man aus dem Netzwerk über den Host auf Ports in der virtuellen Maschine zugreifen, ist das Vorgehen bei V1 und V2 unterschiedlich. Die virtuelle IP ist aus dem Netzwerk nicht direkt erreichbar (NAT). 

Die Ports kann man mit ss -lntu innerhalb WSL V2 anzeigen, netstat und lsof sind nicht installiert (und funktionieren wohl nicht?). In WSL V1 habe ich keine Port Ausgabe hinbekommen, die Meldung klingt so, als ob im Kernel V1 etwas nicht implementiert ist.

Interessant: 

  • Aus dem ubuntu Subsystem heraus kann man direkt Windows Programme starten.
  • Mit einer kleinen Vorbereitung geht das auch umgekehrt, siehe diese Beschreibung.
  • In Windows kann man mit bash -c "Kommando" auch direkt Linux Befehle ausführen.

Die Distributionen (Ubuntu, debian) in WSL haben offenbar generell kein init System (wie systemd) aber eventuell kann man darauf auch verzichten. Mann kann den WSL und Prozesse darin direkt über die Windows Aufgabenplanung (Taskscheduler) starten.

Eine Kommando in Windows mit wsl.exe an WSL übergeben liefert die Ausgabe an Windows zurück. Die Ermittlung der IP Adresse gibt uns dafür ein paar Beispiele.  

Die einfachste Variante, funktioniert in CMD und Powershell - hat allerdings ein Leerzeichen am Ende. Dies kann man in PS einfach abschneiden (Befehl liefert String).trim()
wsl hostname -I

Ich habe die anderen Varianten mal zur Dokumentation und als Beispiel stehen lassen:

cmd Variante (fragt die wsl Umgebung ab, funktioniert aber nicht in powershell. Anmerkung: ^ ist ein Escape Zeichen der batch Umgebung)

WSL -d debian -- ip addr show eth0 ^| grep -oP '(?^<=inet\s)\d+(\.\d+){3}'

Powershell Variante Anmerkung: ` ist das Escape Zeichen in Powershell. Der Code braucht eine zweite Zeile!

(wsl -- ip addr show eth0 `| grep 'inet ') -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
$matches[0]


Installation und Konfiguration

Das Root Dateisystem jeder Linux Distribution wird in einer dynamischen 256GB VHDX Datei installiert. Den Speicherort kann man sich in Powershell so ausgeben lassen:

& cmd "/c dir %LOCALAPPDATA%\Packages\$((Get-AppxPackage -Name "*debian*").PackageFamilyName)\LocalState"

Statt über die interaktive Installation über den Store kann man die Pakete auch manuell herunterladen. Achtung: Das praktische Ergebnis (November 2020) ist anders, als die über den MS Store. Die Versionen sind viel älter!

Und offenbar teilweise sogar unvollständig:

  • In einer Variante fehlte wget und gnupg 
  • Die öffentlichen Zertifikaten waren alt oder nicht vorhanden! Ein apt install ca-certificates behebt das Problem.

Mit der kurzen url von dieser Seite geht das auch mit Powershell. 

Get-AppxPackage *Ubuntu*|Remove-AppxPackage
Get-AppxPackage *debian*|Remove-AppxPackage
Invoke-WebRequest -Uri https://aka.ms/wsl-debian-gnulinux -OutFile Debian.appx -UseBasicParsing
Add-AppxPackage .\Debian.appx

Man kann in WSL V2 auch komplette Distributionen exportieren (einpacken) und wieder importieren.

Der Windows Server hat keinen Store, die Installation funktioniert nur so wie hier beschrieben

Der Server 2019 Version kennt anstatt Enable-WindowsOptionalFeature.

Install-WindowsFeature -Name Microsoft-Windows-Subsystem-Linux

Die Hyper-V Umgebung

Ob Hyper-V schon aktiv ist kann man mit diesem Powershell Einzeiler testen:

(gcim Win32_ComputerSystem).HypervisorPresent

Ob die Hardware Hyper-V und damit auch WSL unterstützt kann man mit einem Powershell Einzeiler testen:

$list=@('Name', 'SecondLevelAddressTranslationExtensions', 'VirtualizationFirmwareEnabled', 'VMMonitorModeExtensions');Get-CimInstance -ClassName win32_processor -Property $list|Format-List $list

Ich habe diesen Test in einer virtuellen Maschine (Hyper-V) durchgeführt. Hier muss man auf dem Hyper-V Host die Virtualisierung an die Maschine "weiterreichen":

$vm=Get-Vm -Name <MaschineName>
Stop-Vm $vm
Set-VMProcessor $vm -ExposeVirtualizationExtensions $true
Start-Vm $vm


Die Ports

Achtung: Das Verhalten der Portweiterleitung ist unmittelbar nach der Einrichtung und nach einem Neustart unter Umständen komplett anders! 

Irgendwie gelingt es relativ leicht, dass man sich das Portmapping aus WSL heraus auf 127.0.0.1 zerschießt - ein netsh interface portproxy reset bewirkt dies zuverlässig. 

Hat man einmal einen Portproxy eingerichtet, funktioniert das Ganze nach einem Neustart nicht mehr. Man muss alles löschen und neu einrichten. Will man portproxy Einträge wieder löschen, kann man dies auch einzeln mit delete v4tov4 listenport=8083 tun! Dabei bleibt offenbar das (nicht sichtbare?) default Mapping erhalten. Löscht man den Eintrag für portproxy, stellt ein Neustart die ursprüngliche Funktion wieder her. 

Die Lösung für meine beobachteten Probleme liegt darin, den portproxy nicht auf localhost einzurichten (obwohl dies zunächst funktioniert) sondern auf die WSL eth0 Adresse. Hier ein Beispiele, die ich im Weiteren auch verwendet habe: 

  1. Artikel WSL Port forwarding 
  2. Artikel auf superuser

Mit tasklist | findstr wslhost kann man nachschauen auf welcher PID wsl arbeitet, mit netstat -aon | findstr <Suchstring> kann man den Prozess oder das Port darstellen. Die Prozesse innerhalb von wsl werden aber separat geführt. Mir ist es noch nicht die gesamte Prozesskette darzustellen.

Die wsl Kommanozeile

Es gibt einen Unterschied in der Verarbeitung zwischen dem Parameter -e und -- 

  • Bei -- kann man mehrere Befehle übergeben, getrennt mit Semikolon Befehl1;Befehl2,
  • das funktioniert offenbar bei -e nicht!
  • Gibt man den Linux Account mit:  -u username - funktioniert die Übergabe mehrere Befehle aber nicht mehr.

Empfehlung: Einfach eine Scriptdatei machen, das funktioniert viel einfacher als sich mit escapen und Variablenauflösung herumzuschlagen. Das Konstrukt wsl -e bash Script.sh funktioniert in gleicher Art in CMD, Powershell.


Sonntag, 22. November 2020

WSL - Windows Linux - wie macht man das?

Ich habe mich etwas mit Windows Subsystem Linux beschäftigt und versucht einen produktiven Ablauf für Setup und Verwendung zu finden. 

Als Ergebnis gibt es auf meiner GitHub Seite ein Powershell Script, welches den automatischen Start von WSL und einer Applikation darin bewerkstelligt. Ich werde dieses Script sicher aktualisieren und erweitern, eine kurze Anleitung zur Verwendung findet sich am Ende des Artikels.

Dieser Artikel ist die Mitschrift der Installation und Einrichtung von WSL und meiner ersten Versuche. Der Code ist eher prinzipiell und nicht unbedingt fehlertolerant.

Grundeinrichtung

Update 4.8.2021

Wenn man das Update KB5004296 installiert hat, kann man im aktuellen Windows 10 wohl einfach so in der Powershell Konsole installieren - dies war bisher nur mit der Insider Preview möglich:

wsl --install

Mit dem Befehl „--install“ werden die folgenden Aktionen ausgeführt (Quelle):

  • Die optionalen Komponenten von WSL und Plattform für virtuelle Computer werden aktiviert.
  • Der aktuelle Linux-Kernel wird heruntergeladen und installiert.
  • WSL 2 wird als Standard festgelegt.
  • Eine Linux-Verteilung wird heruntergeladen und installiert (möglicherweise ist ein Neustart erforderlich) .
  • Standardmäßig ist die installierte Linux-Verteilung Ubuntu. Dies kann mit wsl --install -d <Distribution Name> geändert werden.

Unter Windows 10 kann man das Feature WSL entweder per GUI oder per Powershell installieren. Zwei optionale Feature müssen aktiviert werden:

Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform,Microsoft-Windows-Subsystem-Linux

Danach ist ein Neustart fällig.

Das Admin-Tool für Windows Subsystem Linux ist wsl.exe. Damit kann man auch testen ob die Installation erfolgreiche war und aktive Distributionen und deren Status auflisten:

wsl -l -v

Es ist jetzt noch ein Kernelupdate fällig (auch bei 20H2)! 

Bevor man eine Distribution installiert sollte man die default WSL Version 2 setzen. 

wsl --set-default-version 2

Danach installiert man über den Windows Store eine Linux Distribution (debian). Die Installation ist immer zweistufig: download des appx Packetes und anschließend automatische Installation beim ersten Start. Beide Schritte entweder über suchen und zwei Klicks im Store direkt oder über zwei Powershell Befehle. Scheinbar sind die MS Store Versionen neuer als die auf der Download Seite!

Der Windows Store verhielt sich bei mir manchmal zickig: Es passiert beim Button drücken passiert nichts!? Es gibt zahlreiche Beiträge und einen extra Link in den Windows Einstellungen "Fehlerbehebung bei Microsoft Store Apps". Es gibt das cmd Programm wsreset.exe.

Die finale Installationroutine startet das Subsystem sofort und fragt Linux User und Passwort ab. 

Jeder weitere Aufruf von wsl.exe öffnet ein "Terminalfenster", der Erste startet das Subsystem.

wsl -d debian

Die Grundlage ist geschaffen und WSL wartet auf ein praktisches Beispiel: 

Anwendung (FHEM) in WSL installieren und starten

Für die FHEM Installation braucht man vorher noch (kann sich offenbar täglich ändern)

sudo apt install wget gnupg

danach funktioniert der debian easy way, allerdings wird FHEM am Ende nicht gestartet. 

Es gibt eine Fehlermeldung am Ende, der Grund: Die debian Distribution enthält kein aktives init System (Systemd). Unschön ist, dass diese Fehlermeldung im dpkg status gespeichert bleibt und fortan jeder Befehl mit dpkg oder apt diesen Fehler anzeigen wird. 

Deswegen sollte man diesen Fehler unbedingt korrigieren:

sudo sed -i '/^Package: fhem/n;s/Status: install ok half-configured/Status: hold ok installed/' /var/lib/dpkg/status

Den Erfolg kann man leicht testen, der Fehler sollte verschwunden sein.

sudo apt install -f

Der sed Befehl 

  • patched die Datei direkt, 
  • er sucht die Zeile "Package: fhem" und 
  • ersetzt in der nächsten Zeile den Fehler. 

Der Start von FHEM benötigt zwei Befehlszeilen, mir ist das nur über einen Script Datei gelungen. Es ist wichtig diese Datei Linux konform zu erstellen. Die Windows Taste + cmd oder Powershell öffnet die Sitzung im Userpfad. Ein anschließendes wsl öffnet die Linux Sitzung im Userpfad. Jetzt einfach mit nano start.sh die Datei mit diesem Inhalt erstellen:

cd /opt/fhem
perl fhem.pl fhem.cfg

Das Script als Startbefehl übergeben und die Linux Distribution mit dem User fhem starten.

wsl -u fhem -d debian -e bash start.sh

Zugriff auf Ports innerhalb WSL

Um auf das Netzwerk in WSL zuzugreifen, kann man vom Host einfach auf localhost(127.0.0.1) zugreifen. Die Ports von WSL werden an diese Hostadresse geleitet (WSL 1 und 2).

http://localhost:8083

Will man aus dem restlichen Netzwerk auf FHEM (den WSL 2 Service) zugreifen, muss man einen PortProxy einrichten und die Ports in der Firewall freigeben. Dafür braucht man erhöhte Rechte, also Powershell im Administratormodus starten.

Anmerkung: Ich bin nicht sicher ob ich die Sache mit dem Portproxy und den Adressen richtig verstanden habe. Aber auf die Art wie ich es gelöst habe, funktioniert es zuverlässig. Im nächsten Beitrag habe ich noch meine Versuche und Erkenntnisse dokumentiert.

Da sich die IP Adresse bei jedem Neustart von WSL 2 ändert, muss man dieses Script bei jedem Start ausführen! Ich habe verschiedene Varianten zur Ermittlung der IP gefunden.

#$cAddr=(wsl -- ip addr show eth0 `| grep 'inet ') -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
#$cAddr=(bash.exe -c "ip addr show eth0 | grep 'inet '") -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
#$cAddr=$matches[0]
$cAddr=(wsl hostname -I).Trim()    # entferne Leerzeichen am Ende
$ports=8083,1883                   # Array mit allen Ports
if (netsh interface portproxy show all){netsh interface portproxy reset}
Foreach ($port in $ports){
   netsh interface portproxy add v4tov4 listenport=$port connectport=$port connectaddress=$cAddr
}

Die Firewall Rules braucht man nicht bei jedem Start neu machen

New-NetFireWallRule -DisplayName 'WSL Firewall Unlock' -Direction Outbound -LocalPort $ports -Action Allow -Protocol TCP
New-NetFireWallRule -DisplayName 'WSL Firewall Unlock' -Direction Inbound -LocalPort $ports -Action Allow -Protocol TCP

Sind die Regeln einmal vorhanden, kann man sie mit einem Befehl bez. der Ports modifizieren.

Set-NetFirewallRule -DisplayName 'WSL Firewall Unlock' -LocalPort $ports

Automatischer Start beim Windows Start

Ich habe ein Powershell Script erstellt, welches das komplette Setup erledigt. Nach dem Download in Zeile 3 kann man das Script natürlich noch anpassen.

Zur Vorbereitung braucht man vier Zeilen:

mkdir c:\scripts -force
cd c:\scripts
wget -O c:\scripts\SetupStartWsl.ps1 https://raw.githubusercontent.com/heinz-otto/Powershell/master/SetupStartWsl.ps1
Set-ExecutionPolicy RemoteSigned
.\SetupStartWsl.ps1

Das Setup Script erledigt 4 Schritte:

  1. Erzeugung des Shell Scripts für den Start innerhalb WSL. 
  2. Erzeugung des Powershell Scripts für den Taskscheduler, 
    • welches das Script aus 1. mit wsl startet, 
    • den Portproxy einrichtet und 
    • die FirewallRegeln anpasst.
  3. Firewall Regel als Grundlage erstellen.
  4. Registrierung der Aufgabe (Taskscheduler) beim Systemstart mit erhöhten Rechten das Script aus 2. auszuführen.

Man könnte noch folgendes einbauen:

  • die verwendeten Ports beim Start des wsl auslesen
  • eventuell eine Startverzögerung berücksichtigen 

Doku zu den Powershell cmdlets: Task mit Powershell


Mittwoch, 18. November 2020

Schreibe einen Patch

da ich jedes mal wieder überlegen und basteln muss - hier ein kurzes Howto - aufbauend auf das HowTo im Wiki.

Am einfachsten nimmt man sich vielleicht irgendeine virtuelle Maschine mit debian drauf. 

Man benötigt dafür keinen SVN Account!

Was man braucht ist nur ein Tool:

sudo apt update
sudo apt install subversion

Dann erstellt man eine Arbeitskopie im aktuellen User Home Directory.

svn checkout https://svn.fhem.de/fhem/trunk/fhem ~/fhem-code

Jetzt macht man seine gewünschte Änderung, am einfachsten mit nano (hier gibt es ein paar nützliche Tastenkürzel) Zum arbeiten macht es sich einfacher, wenn man im aktuellen "Working-Copy" Directory steht! Nach getaner Änderung lässt man sich den Patch anzeigen:

cd ~/fhem-code
svn diff

bzw. erstellt man gleich eine patch Datei in seinem Homeverzeichnis mit einem sprechenden Namen.

svn diff > ~/cref_motd.patch

Will man den Original Zustand der gesamten Arbeitskopie wiederherstellen:

svn revert --recursive .

Man kann die aktuellen Dateien aus dem SVN holen, treten Konflikte auf, kann man diese mit tf überschreiben.

svn update

Ein erneutes checkout holt zwar Änderungen vom Server, überschreibt aber lokale Änderungen nur wenn neue Versionen auf dem Server vorliegen!

Den eigene Patch wieder anwenden und testen:

svn patch ~/cref_motd.patch

Ist man fertig und kann von der Maschine nicht veröffentlichen, kopiert man sich das schnell auf den Arbeits PC:

scp ~/cref_motd.patch user@host:

Code Einchecken

Wie es geht steht hier im Wiki. Die Einrichtung der Umgebung steht auf der Webseite. Ich brauche nur trunk/fhem .
cd ~/fhem-ssh
svn update

Man kann den Code komfortabel mit notepad++ und dem NppFTP Plugin von Windows aus über ssh auf einer linux Umgebung editieren. 

Hat man die Änderungen fertig und getestet, erfolgt nochmal eine Überprüfung auf Konflikte mit eventuellen aktuellen Änderungen und dann commit. 

svn diff
svn commit -m "Dateiname:Beschreibung"

Da man dies selten tut, kann man die alten Befehle aus der Versenkung holen: 

history|grep 'svn'
grep "svn co " ~/.bash_history

Man kann sich vom aktuellen Pfad oder von einem anderem Pfad auch den Stand anzeigen lassen:

svn info
svn info ~/fhem-neu

...

Dienstag, 17. November 2020

Wake On Lan über "Dritte"

Zum Thema Wake On Lan habe ich ja schon ein paar Artikel geschrieben. Es gibt Situationen, da erreicht das Magic Paket nicht direkt sein Ziel. Falls also auch der "directed Broadcast" nicht funktioniert, ist vielleicht der "Agent" im Zielnetz eine Lösung!?

Auslöser für diese Notiz war dieser Beitrag im Forum.

Ein Magic Paket aus einer MAC Adresse zu bauen ist keine große Wissenschaft, dieses Paket muss man dann einfach per UDP und Port 9 ins Zielnetz schicken. Viel Code ist zur Lösung nicht nötig, das Meiste ist wie immer das User Interface und Fehlerbehandlung. 

Mir fallen drei Lösungsansätze ein, alle benötigen keine erhöhten Rechte!

Voraussetzung: ssh Verbindung mit public Key ist eingerichtet und funktioniert!

Linux Host

Auf der Suche nach einem simplen Script bin ich nur bei einer Perl Lösung fündig geworden, eine Lösung mit netcat werde ich noch bereitstellen. 

Das Setup auf dem "Remote Agenten" kann man dann auch gleich per ssh machen:

ssh user@host 'wget -qO wakeonlan https://raw.githubusercontent.com/jpoliv/wakeonlan/master/wakeonlan;chmod +x wakeonlan'

Das Script wird heruntergeladen und ausführbar gemacht. Dann ist die Verwendung simpel: 

  • mac Adresse übergeben
  • Parameter -q für quiet Modus
  • Parameter -i für eine directed Broadcast Adresse.
ssh user@host ./wakeonlan -q 12:34:56:AB:CD:EF

Windows Host

Ich hatte da mal ein PowerShellScript gemacht. Windows kann auch ssh, also kann man auch per ssh die Einrichtung und Steuerung vornehmen.

Fritzbox und TR064

Falls die Fritzbox als zentraler Router dient und die MAC Adressen dort registriert sind, kann man auch die Fritzbox benutzen um das Gerät per WOL zu starten. Per FHEM geht das simpel. Es gibt auch Powershellscripte dafür.

get <FritzBoxName> tr064Command Hosts:1 hosts X_AVM-DE_WakeOnLANByMACAddress NewMACAddress "12:34:56:78:9A:BC"

Definition in FHEM

Das WOL Modul in FHEM bietet mittlerweile den CMD Modus. Hier kann man separate Befehle zum aufwecken angeben, das Ganze auch über ssh (siehe den Link oben). 

In wolCmd steht ein FHEM Befehl. Der Platzhalter $MAC wird bei der Ausführung durch die mac Adresse in der Definition ersetzt.

Beispiel mit der Fritzbox:

Das Gerät muss in der Fritzbox registriert sein! Die Fritzbox kann Broadcast nur im eigenen Netzwerk! 

attr WolDevice wolCmd get Fritzbox tr064Command Hosts:1 hosts X_AVM-DE_WakeOnLANByMACAddress NewMACAddress $MAC

Beispiel mit Remote Host und ssh

Wir brauchen 3 Dinge - ausgeführt so als Block in der Raw Definition:

  1. Das Programm/Script auf dem Agenten,
  2. den Shell Befehl der auf dem Agenten ausgeführt werden soll,
  3. den sshHost in üblicher Form.

"ssh user@host 'wget -qO wakeonlan https://raw.githubusercontent.com/jpoliv/wakeonlan/master/wakeonlan;;chmod +x wakeonlan'"
attr WolDevice wolCmd "./wakeonlan -q $MAC"
attr WolDevice sshHost user@host


CodeSpace


Mittwoch, 9. September 2020

ssh mit public key

ich habe dazu schon mehrfach was geschrieben - aber man lernt ja immer wieder dazu. Deshalb noch ein ziemlich komprimierter Artikel: 

Ein HowTo für die Automatisierung von Remote Aufgaben in FHEM über ssh. 

Nachtrag 2023: Es gibt einen neuen Artikel, der die Einrichtung ganz kompakt mit einem Script zeigt.

Mittwoch, 26. August 2020

Crash Recovery

Das System verhält sich komisch? Raspbian bleibt beim Start hängen?

Die SD Card als Datenträger beim Raspberry soll ja häufig den Geist aufgeben. Ich hatte das noch nie, ich behaupte immer das passiert bevorzugt wenn Datenbanken im Spiel sind - aber jetzt hatte es einen Kumpel aus heiterem Himmel getroffen. Ohne Datenbank, ohne aktuellem Backup. Ich konnte ihm helfen und daraus ist dieser Artikel geworden.

Was tun?

Zutaten

  • Neue SD Card für das neue System, Größe "egal"
  • USB Stick oder zweite SD Card + Reader mit dem Speicherplatz mindestens der alten SD Card.
  • Image Tool  (win32diskimager, USB Image Tool, Etcher oder mit Linux Bordmitteln dd.)

In jedem Fall eine Kopie der SD Card anfertigen: Imagedatei ablegen.

Die Originalkarte sollte nicht weiter manipuliert werden und meist ist eine Reparatur der defekten Karte weder sinnvoll noch möglich!

Den Raspberry neu machen

An der Stelle der Hinweis, auf diesen Artikel falls man nicht mehr weiß, welche Perl Module für die persönliche Installation nötig sind.
systemctl stop fhem

Dateisystem prüfen und Zugriff herstellen

Die 1:1 Kopie auf dem USB Stick (oder SD Card in SD Reader) in den Raspberry stecken. 

Achtung: Das geht bei einem Pi ab Version 2 nach meiner Erfahrung gut. Ein 1er Exemplar würde ich zum Stecken herunterfahren, er wird beim einstecken von selbst neu starten. 

Oder die Imagedatei direkt mounten, siehe weiter unten.

Danach sollte sich folgendes Bild ergeben:

pi@raspib31:~ $ lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda           8:0    1 29,8G  0 disk
├─sda1        8:1    1 41,7M  0 part
└─sda2        8:2    1   15G  0 part
mmcblk0     179:0    0 14,9G  0 disk
├─mmcblk0p1 179:1    0  256M  0 part /boot
└─mmcblk0p2 179:2    0 14,7G  0 part /
Der USB "Stick" wird als sda erkannt, die interessante Partition ist sda2. Für die jetzt folgende Reparatur darf sda2 nicht gemountet sein/werden! 

Ein erster Check ohne Optionen erledigt offenbar schon mal alles, mir ist nicht klar in welchem Fall welche zusätzlichen Optionen (oft empfohlen -l, Hilfe mit -h) notwendig sind.

Alle weiteren Befehle erfordern erhöhte Rechte, deswegen erstmal sudo su. 

Die Ausgabe ist nur ein Beispiel und kann je nach Fehler völlig anders sein!

sudo su
fsck /dev/sda2
# fsck from util-linux 2.33.1
# e2fsck 1.44.5 (15-Dec-2018)
# /dev/sda2: recovering journal
# Setting free inodes count to 899857 (was 899863)
# Setting free blocks count to 3338902 (was 3339342)
# /dev/sda2: clean, 46703/946560 files, 577130/3916032 blocks

SD Card mounten 

Jetzt die interessante Partition mounten und hoffen dass etwas lesbar ist.

mkdir /mnt/sda2
mount /dev/sda2 /mnt/sda2

Alte Dateien kopieren

Alle Dateien von dem alten System werden kopiert und die Rechte entsprechend gesetzt, zuletzt wird das USB Gerät ausgeworfen. In der Hoffnung, dass nichts wichtiges kaputt ist! 

Idee: Man könnte die Backup Dateien ausschließen  oder rsync verwenden.

cp -R /mnt/sda2/opt/fhem/* /opt/fhem/
chown -R fhem: /opt/fhem/
umount /mnt/sda2

System prüfen

systemctl start fhem

Das kann eine Weile dauern, z.B. wenn Moduldateien fehlerhaft sind. Der Ungeduldige kann derweil ins Log schauen.

tail /opt/fhem/log/fhem-$(date +%Y)-$(date +%m).log

Wenn das System Fehler zu defekten Modulen liefert, nach dem Start in FHEM erstmal ein update versuchen.

update force

Neu starten und System wieder prüfen

systemctl restart fhem

Wenn es doch nicht läuft

  • Es fehlen System Pakete? Da hatte ich hier schon mal was geschrieben.
  • Wenigsten mit der fhem.cfg neu anfangen, notfalls eine Alte aus dem restoreDir pfad. Siehe Wikiartikel.
  • Ist eine Backupdatei in /opt/fhem/backup vorhanden? Einfach das letzte Backup versuchen, siehe hier.

Alternative - Image Datei direkt mounten

Die kopierte Image Datei wird direkt als Block Laufwerk eingebunden. Ich habe dazu hier eine gute Erklärung gefunden. Das sieht alles etwas komplizierter aus, aber eigentlich spart man die Zeit für die Kopie auf den USB Stick und man muss nichts stecken.

Zunächst den Pfad zum Image z.B. über cifs (SMB) einbinden:
sudo su
# SMB Laufwerk mounten
mp="/mnt/daten"
mkdir $mp
mount -t cifs -o username=UserName,password=Passwort //server/sicherung $mp
# Loop Device erstellen
img="$mp/fhem/raspib3/kopieSdDefekt.img"
ldev=$(losetup --show -f -P "$img")
An der Stelle hat man quasi "den USB Stick gesteckt" d.h. man hat einen /dev/ Eintrag den man wie ein angeschlossenes Laufwerk behandeln kann. Dies(e) Gerät(e) anzeigen:
losetup -l
Jetzt kann man wie oben beschrieben fsck ausführen. dafür kann man einfach die Variable $ldev verwenden:
fsck ${ldev}p2
Ist der Vorgang erfolgreich kann man die Partitionen mounten:

Achtung: Diese Schleife mounted nicht, sie gibt nur die Befehle aus!
#Alle Partitionen mounten
for part in ${ldev}p*
do 
  dst=/mnt/$(basename $part)
  echo "mkdir $dst"
  echo "mount $part $dst"
done
Nachdem man analog zu oben die Dateien kopiert hat, kann man alles wieder aufräumen:
umount /mnt/loop0*
rm -R /mnt/loop0*
losetup -D
umount $mp

Backup einrichten!

Siehe dazu auch meinem Artikel zur externen Sicherung des FHEM backups.

Sonntag, 7. Juni 2020

Worx Landroid per MQTT

Vor einem Jahr habe ich den neuen Mähroboter an FHEM angebunden. Nach einiger Zeit wurde mir irgendwie klar: Die Cloud Anbindung des Roboters läuft doch mit MQTT!?
Die Anbindung über zwei nodejs Services und ein FHEM Modul erschien mir ziemlich aufwendig:
 Cloud-MQTT - iobroker.worx - fhem-landroid-s - 74_LANDROID.pm
Ein Beitrag im Forum gab die entscheidenden Hinweise. Ich habe das ganze noch etwas weiter geführt und mich dabei auch mal etwas eingehender mit attrTemplate beschäftigt.

Vorbereitung

Zur Vorbereitung der Installation braucht man ein Tool um die Informationen für den Zugang zum Cloudserver zu bekommen.
  • Broker Server Adresse
  • Board ID z.B. bei den 2019 Modellen PRM100
  • Mac zwölf stellig
  • SSL Zertifikat AWS.p12
  • Eine neue Uuid -> kann einfach selbst erzeugt werden: 
    • cat /proc/sys/kernel/random/uuid 
    • oder im Browser hier .
Ich habe die Version V0.0.24 - Net.zip verwendet. Das Tool erfordert keine Installation. Man trägt einfach seinen Cloudzugang ein, nach der Verbindung zeigt das Tool die notwendigen Daten und das Zertifikat liegt im gleichen Pfad.
Das Zertifikat muss noch zerlegt werden, um es in FHEM mit MQTT2_CLIENT verwenden zu können.

Einrichtung

Mit scp kopiert man die Datei AWS.p12 auf den FHEM Server: 
scp "d:\Downloads\V0.0.24 - Net\AWS.p12" pi@raspib3:

Ab hier sind die Befehle für die FHEM Kommandozeile oder die Raw Definition aufbereitet!

Das Zertifikat muss zerlegt werden damit fhem die Dateien verwenden kann.
"openssl pkcs12 -in /home/pi/AWS.p12 -nokeys -passin pass: -out aws.cer -clcerts"
"openssl pkcs12 -in /home/pi/AWS.p12 -nodes -passin pass: -out aws.key -nocerts"
"chmod 0600 aws.*"
Die Template Datei kann man jederzeit aktualisieren ohne ein komplettes Update machen zu müssen. Nichtsdestotrotz muss FHEM halbwegs aktuell sein und attrTemplate funktioniert.
{ Svn_GetFile("FHEM/lib/AttrTemplate/mqtt2.template", "FHEM/lib/AttrTemplate/mqtt2.template", sub(){ AttrTemplate_Initialize() }) }
Jetzt in FHEM den Connector zum Broker herstellen und testen.
define MQTT_Worx MQTT2_CLIENT <broker-server-adresse>:8883
attr MQTT_Worx SSL 1
attr MQTT_Worx autocreate simple
{my $uuid=qx(cat /proc/sys/kernel/random/uuid);;fhem("attr MQTT_Worx clientId android-$uuid")}
attr MQTT_Worx keepaliveTimeout 600
attr MQTT_Worx mqttVersion 3.1.1
attr MQTT_Worx msgAfterConnect <BoardID>/<MAC>/commandIn {}
attr MQTT_Worx sslargs SSL_version:TLSv12 SSL_cert_file:aws.cer SSL_key_file:aws.key SSL_use_cert:1
attr MQTT_Worx subscriptions <BoardID>/<MAC>/commandOut
Hat man alles richtig gemacht, geht der state des Devices MQTT_Worx sofort auf opened.

Wechselt der Status hektisch zwischen opened und disconnected stimmt etwas mit dem Zertifikat nicht.

Die erste Übertragung sollte automatisch ein neues MQTT_DEVICE erstellen.

Auf dieses Device wird jetzt das Template angewendet und damit konfiguriert.
set MQTT2_.*:FILTER=IODev=MQTT_Worx attrTemplate worx_landroid
Die Readings und die Steuerung sind ähnlich das Moduls 74_LANDROID.pm
Problemlösung
Bleibt der Connector MQTT_Worx auf disconnected -> die Zertifikate prüfen:
{qx(ls -lha /opt/fhem/aws.*)}

Das Ergebnis sollte in etwa so aussehen:
-rw------- 1 fhem dialout 1.4K Jun  9 13:27 /opt/fhem/aws.cer
-rw------- 1 fhem dialout 1.8K Jun  9 13:27 /opt/fhem/aws.key/

Support

Es gibt dazu auch einen Beitrag im Forum.

Sonntag, 31. Mai 2020

sonos2mqtt - so weit bin ich


Ich habe derzeit die aktuellste Anleitung ins FHEM Wiki geschrieben, aufgeteilt in 2 Artikel.

sonos2mqtt ist (ein 2 Jahre altes?) Projekt welches aktiv entwickelt wird, offenbar primär für die Anbindung an Homeassistent. Aber von der Sache her ist es eine Bridge Sonos - MQTT.
Ich habe die "lokale Installation" gewählt (es gibt auch eine Docker Version).
Aus meiner Sicht ist die Sache unkritisch, man kann es in jede Umgebung einbinden.

Voraussetzungen für diese Anleitung

  • Nodejs und pm2 installiert - Anleitung
  • MQTT2_SERVER bereits in FHEM definiert - Anleitung.
    • Damit die Automatik funktioniert: autocreate simple (default) 
  • FHEM aktuell - Stand mindestens 28.7.2020
  • Alles läuft auf einer Maschine (localhost)

Setup auf Systemebene

Diese Abschnitt wird auf der Kommandoebene von Linux ausgeführt (Terminal).
Man installiert die aktuelle sonos2mqtt Release Version (siehe Projekt Webseite):
sudo npm install -g sonos2mqtt
Es wird derzeit heftig entwickelt, die Installation oder Aktualisierung auf eine neue Beta Version geht mit dem gleichen Befehl und der Zusatz Angabe zur Beta, Beispiel "sonos2mqtt@3.0.6-beta.3".

Man kann kurz überprüfen ob bisher alles gut gegangen ist und die Version abfragen:
node /usr/lib/node_modules/sonos2mqtt --version

Setup in FHEM

Die Einbindung in FHEM erfolgt wesentlich mit generischen MQTT2_DEVICEs. Eine Bridgedevice fungiert als Verteil- und Auffangstation für MQTT Nachrichten. Dadurch werden die Player als separate MQTT2_DEVICEs erzeugt. Ein paar Templates vereinfachen die Konfiguration.

Der folgende Code ist für die Raw Definiton bzw. Kommandozeile von FHEM und realisiert diese Schritte:
  • definiere die Bridge mit Template, der Topic (default: sonos) wird abgefragt!
    • Das Template definiert 2 notify welche die Player automatisch konfigurieren:
      • Das Erste wird getriggert wenn ein MQTT2_RINCON_ Device definiert wird. Es wird das Basistemplate für Player angewendet und die Abfrage der ZonenInfo gestartet.
      • Das Zweite wird beim Eintreffen der ZonenInfo gestartet und konfiguriert den Player "fein".  
  • starte die sonos2mqtt Bridge auf Systemebene im Kontext User fhem
  • nach kurzer Zeit sollten die Player fertig sein.
Nur falls es noch keinen MQTT2_SERVER gibt:
define mqtt2s MQTT2_SERVER 1883 global
attr mqtt2s room MQTT_IO
Falls ein existierender MQTT2_SERVER verwendet wird, bitte unbedingt prüfen - das autocreate auf simple steht!

Der folgende Codeschnipsel richtet die Bridge manuell ein und startet die automatische Erzeugung der Player. 
define SonosBridge MQTT2_DEVICE
attr SonosBridge IODev mqtt2s
attr SonosBridge room MQTT2_DEVICE
define n_pm2_sonos notify global:INITIALIZED "pm2 -s start sonos2mqtt"
sleep global:ATTR.SonosBridge.stateFormat.connected;trigger n_pm2_sonos start
set SonosBridge attrTemplate sonos2mqtt_bridge_comfort
Ist die Bridge schon automatisch erzeugt worden, wendet man nur das Template an. (Dann ist ein rename empfohlen!)
Nach wenigen Sekunden sollten alle Player sichtbar sein. Die haben eine schlichte Oberfläche und ein paar Dinge fehlen noch. Das ganze ist aber sowieso mehr ein Baukasten als ein fertiges Modul.

Sprachausgabe - der "speak" Befehl mit Boardmitteln

Gegenüber der Beschreibung im Wiki kann man auch die bestehende Sonos Umgebung verwenden:
  • Samba installiert, 
  • Freigabe SonosSpeak auf /var/SonosSpeak. 
Dazu wird Text2Speech etwas anders konfiguriert:
demod SonosTTS Text2Speech none
attr SonosTTS TTS_CacheFileDir /var/SonosSpeak
attr SonosTTS userReadings cifsName:lastFilename.* {my $host=ReadingsVal($name,'host','set host first');;my $lastFileName=ReadingsVal($name,'lastFilename','');;my @arr=split('/',$lastFileName);;$arr[0]='x-file-cifs:/';;$arr[1]=$host;;join('/',@arr)}
Die Weiterentwicklung des Speak Befehls im Wiki könnte der sayText Befehl sein (Für die DEF setList):
sayText:textField { my $tts=ReadingsVal('SonosBridge','tts','SonosTTS');my ($cmd,$text)=split(' ', $EVENT,2);fhem("setreading $tts text ".ReadingsVal($tts,'text',' ').' '.$text.";sleep 0.4 tts;set $tts tts [$tts:text];sleep $tts:playing:.0 ;set $NAME notify [$tts:vol] [$tts:httpName];deletereading $tts text")}
Bei diesem Befehl muss das Volume vorher als Reading gesetzt werden. Der Befehl funktioniert also bewusst anders.
setreading SonosTTS vol 15

Tipps zur Verwendung der Player

Die automatische Konfiguration setzt den alias entsprechend dem Namen im Sonos vergeben. Die langen MQTT2_RINCON_ Namen sind unhandlich und schlecht lesbar. Man kann Player mit einem devspec ansprechen, das simpelste ist:
set alias=Büro ...
Oder alle Player
set model=sonos2mqtt_speaker leaveGroup

Was passiert im Hintergrund?

Das Template "sonos2mqtt_bridge_comfort" wendet ein Template auf das Bridge Device an und definiert zwei notify für die automatische Konfiguration.
Das Erste triggert auf den autocreate Event, wendet ein Template für den Player an und fragt über MQTT weitere Informationen für den Player ab.
define n_configSonos1 notify global:DEFINED.MQTT2_RINCON_[A-Z0-9]+ sleep 1;;set $EVTPART1 attrTemplate sonos2mqtt_speaker;;set $EVTPART1 x_raw_payload {"command": "adv-command","input": {"cmd":"GetZoneInfo","reply":"ZoneInfo"}}
Das zweite notify triggert auf die Namen der Player wenn als Antwort auf den Request von notify 1 das Reading IPAdresse gesetzt wird.
defmod n_configSonos2 notify MQTT2_RINCON_[A-Z0-9]+:IPAddress:.* {\
  no warnings 'experimental::smartmatch';;\
  my @tv = ("S14","S11");;\
  my $url="http://$EVTPART1:1400";;\
  my $xmltext = GetFileFromURL("$url/xml/device_description.xml");;\
  my ($mn)=$xmltext =~ /<modelNumber>(S[0-9]+)/;;\
  my ($img)=$xmltext =~ /<url>(.*)<\/url>/;;\
  my $icon="Sonos2mqtt_icon-$mn";;\
  my $setList=AttrVal($NAME,'setList','');;\
  fhem("setreading $NAME modelNumber $mn");;\
  fhem("\"wget -qO ./www/images/default/$icon.png $url$img\"");;\
  fhem("attr $NAME icon $icon;;sleep 4 WI;;set WEB rereadicons");;\
  if ($mn ~~ @tv) {$setList=~s/input:Queue \{/input:Queue,TV \{/};;\
  $setList=~s/;;/;;;;/g;;\
  fhem("attr $NAME setList $setList")\
}
Mit diesem Befehl wird das Anlegen aller Player getriggert:
"pm2 start sonos2mqtt"

Radio starten

In der setList sind derzeit die einzelnen Steuerbefehle. setAVTUri setzt zwar eine neue Abspielquelle, stoppt aber den Player und beginnt nicht automatisch mit spielen. Eigentlich wäre es aber praktisch.

Anmerkung: Der Befehl playUri ist im aktuellen Template enthalten! 

Die Sonos Umgebung braucht offenbar dazu einen Moment, meine derzeitige Idee ist also:
  1. setAVTUri ausführen
  2. 1 sec später den play Befehl starten
Die Ergänzung in der setList sähe also so aus:
playUri:textField {fhem("set $NAME setAVTUri $EVTPART1; sleep 1; set $NAME play")}
Als vorübergehende Lösung für sowas wie Radiosender habe ich mir so etwas gebaut. Das notify schreibt den Titel und die uri beim Start (auch über die Sonos App) in den Dummy. Diesen kann man dann selbst zum Starten verwenden: [Titel:DeutschlandfunkKultur]
define Titel dummy
define n_MQTT2_RINCON notify MQTT2_RINCON_[0-9A-Z]{17}:currentTrack_Title:.* {my $title=ReadingsVal($NAME,"enqueuedMetadata_Title","");;$title=~s/\W//g;;my $uri=ReadingsVal($NAME,"currentTrack_TrackUri","");;fhem("setreading Titel $title $uri") }

Nicht einfach default

Hat man den MQTT2_SERVER anders definiert, nicht lokal bzw. mit allowed abgesichert, muss beim Start von sonos2mqtt der Parameter --mqtt übergeben werden. 
Dem pm2 Befehl folgenden durch die Angabe "--" die weiteren Parameter für sonos2mqtt.
"pm2 start sonos2mqtt -- --mqtt mqtt://myuser:the_secret_password@192.168.0.3:1800"
Mit dem Parameter --prefix könnte man den Basistopic setzen (default: sonos).

Weitere Entwicklung

Für die weitere Entwicklung ist im Player der set Punkt x_raw_payload eingebaut. Damit kann einfach json Payload "publishen". Das Original Beispiel aus der Doku, Sound abspielen:
{
  "command": "notify",
  "input": {
    "trackUri": "https://cdn.smartersoft-group.com/various/pull-bell-short.mp3",
    "onlyWhenPlaying": false,
    "timeout": 10,
    "volume": 15,
    "delayMs": 700
  }
}

Werte abfragen

Der Entwickler von sonos2mqtt hat jetzt auch die Abfrage ermöglicht. Um bei Get Befehlen etwas zurück zu bekommen, muss ein Topic übergeben werden:
{
  "command": "adv-command",
  "input": {
    "cmd": "GetFavoriteRadioStations",
    "reply": "DougRadio"
  }
}
Das Beispiel fragt die Favoriten ab. Die Ergebnisse erscheinen dann in einem Reading mit der Struktur Result_8_ItemId. Weiter Informationen findet man hier.

Anmerkung/Tipps

Anstatt FHEM komplett zu aktualisieren kann man auch nur die Templates neu laden:
{ Svn_GetFile("FHEM/lib/AttrTemplate/mqtt2.template", "FHEM/lib/AttrTemplate/mqtt2.template", sub(){ AttrTemplate_Initialize() }) }

Mir ist aufgefallen, dass offenbar System abhängig der Ordner node_modules bei dieser Installationsart an unterschiedlichen Orten angelegt wird! Ich hatte /usr/lib/node_modules und /node_modules. Der Befehl sudo find / -name "sonos2mqtt" hilft dabei den richtigen Pfad zu finden.

Will man auf Systemebene mit pm2 sonos2mqtt monitoren o.ä. muss man dies als User fhem tun. Ein anderer hat auf die Umgebung keinen Zugriff!
sudo -su fhem pm2 list

Mittwoch, 13. Mai 2020

systemd unit file

Ich habe mich etwas mit systemd, speziell dem unit file von fhem und dem Start von für FHEM notwendigen Komponenten beschäftigt.
Für detailliertere Information wird diese Seite als Start empfohlen.  Eine Befehlsübersicht zu systemctl habe ich hier gefunden.

Sonntag, 16. Februar 2020

Windows Server 2012 Foundation - Hyper-V Treiber installieren

Microsoft hat irgendwie umfangreich verhindert, dass man Windows Server 2012 Foundation in Hyper-V virtualisieren kann. Blöd wenn man eine produktiven Server hat, mit dem man ein paar Untersuchungen anstellen will. Virtualisierung / Hyper-V ist dafür nun mal eine Super Sache.
Es gibt einen guten Artikel, der mir sehr weiter geholfen hat.
Da die Situation nach einer Virtualisierung erstmal dramatisch ist, habe ich eine Weg gesucht und gefunden, es zu vereinfachen.
Das Hauptproblem ist die fehlende Installation der Hyper-V Treiber.

Das notwendige Paket windows6.2-hypervintegrationservices-x64.cab kann man bei Microsoft herunterladen. Die ursprüngliche existierende vmguest.iso wird nicht mehr ausgeliefert.
Für den Rest habe ich ein paar Powershell Scripts erstellt.
Zunächst brauchen wir die entpackten Dateien in einem temporären Pfad.
# cab Datei entpacken und in allen inf Dateien eine Zeile ergaenzen
$cabFile="E:\support\amd64\Windows6.2-HyperVIntegrationServices-x64.cab"
# Zielpfad frisch machen
$DestDir = (split-path $cabFile -leaf) -replace '.cab'
if (Test-Path -Path $DestDir) {rm $DestDir}
md $DestDir
# cab datei auspacken
cmd.exe /c "C:\Windows\System32\expand.exe -F:* $cabFile $DestDir"
Damit die Treiber später installiert werden können, muss die inf Datei um die eine Zeile ($rString) ergänzt werden. Zusätzlich muss "neben" der inf Datei die Datei update.cat liegen.
# replace in den inf Dateien
$fString=[regex]::Escape('[Version]') # escapen wegen der Klammer []
$rString="$&`nCatalogFile=update.cat" # Der String der gefunden wurde + NL + Text
gci -Recurse $DestDir |where {$_.name -like "*.inf"}|% {(Get-Content $_.fullname) -replace $fString, $rString| Set-Content $_.fullname;copy-item "$DestDir\update.cat"  (split-path $_.fullname)}
Die mit Windows Server 2012 Foundation vorbereitete virtuelle Disk (VHD Datei) kann man per Doppelklick einfach einbinden. Die Treiber werden dann ins Offline Image integriert.
# Treiber in offline Image integrieren
$offDir="F:"
$driverDir="\\omv1\Shares\Software\vmguest"
Add-WindowsDriver -Path $offDir -Driver $driverDir -Recurse -ForceUnsigned
Sollte was schiefgegangen sein, kann man auch alles wieder entfernen, oder sich nur vergewissern das die Treiber vorhanden sind.
# Alle Treiber wieder entfernen 
$dArray=gci -Recurse $driverDir |where {$_.name -like "*.inf"}|% {$_.name}
Get-WindowsDriver -path $offDir|% {if ($dArray -match (split-path $_.OriginalFileName -leaf)){Remove-WindowsDriver -path $offDir -Driver $_.driver}}
Zum Abschluss die VHD Datei auswerfen und die virtuelle Maschine starten. Es sind keine weiteren Arbeiten erforderlich.

Für die Virtualisierung einer produktiven Server 2012 Foundation Maschine hat man damit folgenden simplen Weg:
  1. Image ziehen, entweder offline mit dem USB Stick von hier, oder online mit disk2vhd.
  2. Das Image auf eine neue VHD anwenden, oder die VHD Datei von disk2vhd verwenden.
  3. VHD Datei als Laufwerk mounten und die Hyper-V Treiber integrieren. 
  4. VHD Datei Auswerfen und in Hyper-V verwenden.
Damit funktioniert:
  • Gen 2 Virtuelle Maschine
  • EFI Boot
  • SCSI Disk, Netzkarte usw.

Was leider bleibt:
  • Die Hyper-V Dienste (vmic...) laufen nicht. 
 

Mittwoch, 5. Februar 2020

Windows Core Server verwalten

Eigentlich gibt es ein simples Dokument vom Hersteller, aber das hat so viele Lücken und Fehler, ich schreibe einfach mal auf wie es kurz und knapp geht.

Verwaltung eines Core Servers (Hyper-V 2019) ohne Domäne

Um ohne lokale Konsole arbeiten zu können braucht man RDP. Das aktiviert man schnell:
  • entweder im Dialog der blauen Box von %windir%\System32\sconfig.cmd (bzw. %windir%\System32\%language%\sconfig.vbs) 
    • Dialog: 7 e 1
  • oder über ein vorhandenes Script:
cscript %WinDir%\System32\Scregedit.wsf /ar 0
REM Dieser Befehl noch für alte RDP Clients
Cscript %WinDir%\System32\Scregedit.wsf /cs 0
Man sollte den Server noch umbenennen, entweder im Dialog (2) oder
netdom renamecomputer %computername% /force /NewName:ComputerName
Danach muss man den Server neu starten!

Ab hier geht es auch Remote

Das "Netzwerkprofile" steht per default auf public, das stellt man um auf private und ermöglicht die Reaktion auf Ping. Dazu eine Powershell im "schwarzen Fenster" starten:
start Powershell
Ping funktioniert mit dem Regelset -DisplayGroup "File and Printer Sharing" (Sprachabhängig: "Datei- und Druckerfreigabe") oder "codiert" in Group.
Set-NetConnectionProfile -InterfaceIndex (Get-NetConnectionProfile).InterfaceIndex -NetworkCategory Private
Enable-NetFirewallRule -Group "@FirewallAPI.dll,-28502"

Am Client

Eine "Remote Verwaltung" funktioniert nur mit etwas Vorbereitung.

  • Der Zielcomputer muss als TrustedHost eingetragen und 
  • das Konto vom Zielcomputer muss hinterlegt werden. 

Verwirrend: Für den Hyper-V Manager muss der TrustedHost mit FQDN hinterlegt werden! Man kann sich entscheiden oder beides hinterlegen.
$server = "ComputerName"
$user = "administrator"
$pwd = Read-Host "Enter Password"
$fqdn=[System.Net.Dns]::GetHostByName($server).Hostname
Start-Service -Name winrm
Set-Item WSMan:\localhost\Client\TrustedHosts -Concatenate -Value $server
Set-Item WSMan:\localhost\Client\TrustedHosts -Concatenate -Value $fqdn
Stop-Service -Name winrm
cmdkey /add:$server /user:$user /pass:$pwd
cmdkey /add:$fqdn /user:$user /pass:$pwd

Ist alles ordentlich eingerichtet kann man eine Powershell Remotesitzung aufbauen.
Enter-PSSession -ComputerName $server

Neben Powershell und dem Hyper-V Manager funktionieren auch andere "lokale" Snap-Ins in der mmc, mir ist aber noch nicht klar nach welchem Schema was funktioniert und was nicht.
Die Hyper-V Verwaltungstools kann man besser über die grafische Schnittstelle installieren, die Powershell Schnittstelle funktioniert nur Hyper-V komplett oder gar nicht.
Mann sollte dafür die RSat Tools verwenden, oder eben alles über RDP und Powershell machen.

Informationen

Es gibt ein Tool für die Kommandozeile, welches knapp und übersichtlich die System Informationen darstellt:
systeminfo.exe
Auf dem Windows Server ist PSRemoting von Haus aus aktiviert, auf anderen Ziel Systemen kann man sie mit Enable-PSRemoting aktivieren. Ob aktiviert oder nicht lässt sich leicht von jeder Maschine aus testen:
Test-WSMan -ComputerName ComputerName
Die schon existierenden Zielsysteme kann man abfragen
Start-Service -Name winrm
(Get-Item WSMan:\localhost\Client\TrustedHosts).value
Stop-Service -Name winrm
Die existierenden Konten kann man anzeigen lassen
cmdkey /list

Auf dem Server kann man sich Profil und die aktivierten Firewall Regeln anschauen:
Get-NetConnectionProfile
Get-NetFirewallRule -DisplayGroup "Windows Remote Management"|select Name,enabled
Get-NetFirewallRule -Group "@FirewallAPI.dll,-28502"|select name,enabled
Mit der Tastenkombination Ctrl+Alt+Ende öffnet sich ein Fenster von wo aus man z.B. den Taskmanager starten kann.

ToDo Ist da noch was?
Code Block
System Volume nach der Installation auf 60GB schrumpfen und den Rest als Volume V einrichten.
$disk=Get-Partition -DriveLetter C
Resize-Partition -DiskNumber $disk.DiskNumber -PartitionNumber $disk.PartitionNumber -Size 60GB
New-Partition -DiskNumber $disk.DiskNumber -UseMaximumSize -DriveLetter v|Format-Volume

Weiteres

Insgesamt zu diesem Thema gibt es auch schon ein paar Artikel hier im Blog
Link

Sonntag, 2. Februar 2020

Altes Board von NVMe starten

M.2 SSDs sind ganz erschwinglich, einen PCIe M.2 Adapter gibt es für ein paar Euro und ein PCIe Steckplatz auf dem älteren Board ist auch noch frei. Das wäre doch was ...

Die Sache hat einen oder mehrere Haken:
  • der simple PCIe M.2 Adapter kann nur M-Key SSDs (NVMe SSDs) aufnehmen.
  • erst das EFI-BIOS der neueren Boards erkennt damit auch ein Laufwerk, die älteren kennen es bestenfalls als irgendein PCIe Gerät.
    • Die aktuellen Betriebssysteme können die NVMe SSD ohne Probleme als richtiges Laufwerk "sehen" und damit umgehen. Das geht soweit, dass man Windows installieren kann - es merkt nicht, dass das BIOS dieses Laufwerk nicht starten kann.
  • Damit kann man zwar ein System auf die neuen SSD aufspielen, aber starten kann man es nicht.
Wie so oft ist es eine Sache des Treibers: ein NVMe Treiber ist im alten BIOS nicht vorhanden.
Schade - Oder? 
Ich habe eine nicht ganz neue und simple Lösung gefunden, aber wie so oft keine aktuellen Beschreibungen. Ich musst ein paar Tage "forschen" - deswegen der Artikel.

Der "Trick" - eine kurze Umleitung


Das BIOS kann NVMe nicht direkt starten, aber Sata und USB Laufwerke. Also erstmal einen Bootloader von USB oder einer anderen EFI Partition starten, der hat den Treiber und der startet das System von NVMe. Wahrscheinlich geht das mit jedem Bootloader, wenn man weiß wie. Gefunden habe ich aber den clover efi bootloader und ein Boot Disk Utility zur einfachen Installation. Das Tool baut ohne viel Federlesen einen USB Boot Stick mit einer 200 MB EFI Bootpartition. Darin befinden sich ca. 15 MB Dateien.
Was dann nirgendwo so direkt und aktuell stand:

Die kleine Modifikation:


Es gibt in dem entstandenen Bootfähigem Laufwerk ein Verzeichnis EFI. In dem Verzeichnis <LW>:\EFI\CLOVER\drivers\UEFI müssen offenbar die notwendigen Treiber für den Start liegen.  In dem Verzeichnis <LW>:\EFI\CLOVER\drivers\off liegen offenbar die optionalen Treiber. Von dort muss man den Treiber für die NVME Disk - NvmExpressDxe.efi - in das Verzeichnis mit den notwendigen Treiber kopieren.
Das war es im Groben schon. Einfach den Stick in den PC einstecken, ihn als Boot Device konfigurieren und starten. Dann erscheint nach wenigen Sekunden ein neues Bootmenü, dort dann "Boot Microsoft EFI Boot from EFI" (oder was man starten möchte) auswählen.
Man kann den 5 sec Countdown noch verkürzen, ein Timeout 0 schaltet die GUI komplett ab und bootet sofort.
Die XML Datei kann man mit einem Texteditor bearbeiten.
<LW>\EFI\CLOVER\config.plist

Bei meinem Asrock E350M1 dauert es ca 6 sec um den Stick zu booten, die gesamte Zeit bis von der NVMe gestartet wird beträgt damit 14 sec. Ein Option Rom auf einer Sata Erweiterungskarte braucht auch ein paar Sekunden, insofern ist diese Methode kein wirklicher Nachteil.

Man braucht nicht unbedingt ein Tool um den Boot Stick zu erstellen. Man kann die gepackte ISO Datei herunterladen, entpacken und einfach wie bei einem Windows Setup Stick den Inhalt der ISO Datei auf den vorbereiten Stick kopieren.

Den Laufwerksbuchstaben kann man im laufenden Windows System mit Powershell entfernen und den Stick damit etwas verstecken.
$drive=(Get-Volume |where DriveType -eq Removable).DriveLetter
$disk=Get-Partition -DriveLetter $drive
Remove-PartitionAccessPath -DiskNumber $disk.DiskNumber -PartitionNumber $disk.PartitionNumber -AccessPath $drive":"


ToDo Ich muss noch weiter forschen....


Donnerstag, 9. Januar 2020

Passwörter in Powershell Scripts

Es gibt immer mal wieder die Notwendigkeit in automatischen Prozessen Konten (Benutzername Passwort) zu verwenden. Die sicherlich dümmste Variante ist es, sie im Script direkt zu hinterlegen. Aber wie jetzt tun?
  • Auf einem sicheren Filesystem ablegen und vor Zugriff schützen.
  • Das Passwort nur verschlüsselt ablegen. Den Key dazu im sicheren Filesystem ablegen und vor Zugriff schützen.
  • Das Script im definierten Userkontext ausführen.
  • Im Code möglichst darauf achten, dass der Speicher wieder gelöscht wird.

Das Passwort verschlüsseln

Man braucht zunächst mal einen definierten Schlüssel um zu verschlüsseln, sonst würde das Cmdlet ConvertFrom-SecureString das Benutzerkonto verwenden. das wäre nicht praktikabel.
So kann man einen zufälligen AES 256 Key (32 Byte) erzeugen:
$pwPath = ".\myPassword.txt"
$aesKeyPath = ".\aesKey.key"
$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
if (-not (Test-Path $aesKeyPath)) {Set-Content $aesKeyPath $AESKey}
Ich mag ja Einzeiler, hier eine Variante um das Passwort verschlüsselt abzulegen:
"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -Key $AESKey| Out-File $pwPath

Das Passwort entschlüsseln und verwenden

Das Passwort kann man "umgekehrt" wieder einlesen, aber dabei entsteht zunächst ein SecureString, dieser ist so nicht darstellbar und in vielen Applikationen auch nicht verwendbar..
$SecureCredential = Get-Content $pwPath | ConvertTo-SecureString -Key $AESKey
Zur Darstellung kann man das PSCredential Object verwenden (alternativ auch die .net Klasse [Runtime.InteropServices.Marshal]).
(New-Object PSCredential "username",$SecureCredential).GetNetworkCredential().Password
Dabei verlangt das PSCredential Objekt einen Benutzernamen, der mit dem Passwort aber primär gar keine Verbindung haben muss.
Entweder nimmt man hier irgendeinen "Begriff" oder man nimmt den Benutzer, den man in der weiteren Verarbeitung verwenden kann/will (z.B. Mailaccount)
$Account = (New-Object PSCredential "username",$SecureCredential).GetNetworkCredential()
$Account.Password
$Account.UserName

Das Passwort an externe Programme übergeben

Die Verwendung der Kommandozeile in Powershell kann einen manchmal verzweifeln lassen.
Einige eingebaute Alias Definitionen (Get-Alias) ersetzen bekannten Windows Kommandozeilen Befehle durch Cmdlets. Deren Aufruf Syntax ist nie 100% kompatibel!
Parameter/Argumente werden von der Powershell "analysiert" und "umgedeutet".

Im Zweifelsfall hilft nur probieren und suchen!

Leerzeichen und Sonderzeichen in den Strings machen einen das Leben zusätzlich schwer. Bestimmte Zeichen muss man mit dem backtick ` schützen (Artikel in der WinPro).
Es bieten sich ein paar Möglichkeiten zum Aufruf externer Programme an:
  • Command Shell cmd verwenden
  • Aufruf mit & Zeichen am Zeilenanfang
  • Alias setzen Set-Alias
  • Das Parsing der Argumente durch Einfügen von --% abbrechen.
Hier ein paar Beispiele für die Verwendung von Programmaufrufen in Powershell Console oder Script:
& cmd /c dir /q
set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
sz a -tzip "ArchivNameOhneEndung" "PfadName" -mem=AES256 "-p$($Account.Password)" -v2g
icacls .\Archiv1.zip.001 /grant Benutzer:`(R`)
icacls .\Archiv1.zip.001 --% /grant Benutzer:(R)
Will man die Argumente noch in eine Variable packen, wird das nochmal schwierig.
Hier hilft dann eventuell ein Array (siehe Beispiele weiter unten).

Die Dateien vor Zugriff schützen

Die Absicherung der Datei unter Windows kann mit verschiedenen Methoden erfolgen, die grafische Variante mit dem Explorer will ich nicht extra beschreiben.
Schlussendlich muss der Benutzer, der das Script ausführt die Datei lesen können!
Achtung: Der folgenden icacls Syntax funktioniert in einer Powershell Umgebung, aber besser immer icacls --% verwenden!
Beispiel: Rechte Anzeigen, Vererbung löschen, Administratoren und SYSTEM entfernen:
icacls w.txt
icacls w.txt /inheritance:d
icacls w.txt /remove Administratoren
icacls w.txt /remove SYSTEM
Mit Clear-Variable und Remove-Variable kann man sicherstellen, das Variable (mit sensiblen Inhalt) zur Laufzeit wieder gelöscht werden und nicht erst mit der "Müllabfuhr" nach Beenden des Scripts. Die Variable wird ohne $ angegeben!
Clear-Variable Account

Dinge die man nicht verstehen muss

Hier mal 3 Varianten wie man den 7Zip Befehl (Alias sz) fehlerfrei ausführen kann.
$Archiv="Archiv1"
$Pfad="c:\temp"
# Alles in einer Aufrufzeile
sz a -tzip "$Archiv" "$Pfad" -mem=AES256 "-p$($Account.Password)" -v2g
# die gesamte Aufrufzeile in eine Stringvariable verpackt
$befehl = "sz a -tzip `"$Archiv`" `"$Pfad`" -mem=AES256 -p$($Account.Password) -v2g"
Invoke-Expression $befehl
# Alle Argumente als Elemente eines Arrays
$Arguments = "a","-tzip","`"$Archiv`"","`"$Pfad`"","-mem=AES256","-p$($Account.Password)","-v2g"
sz $Arguments
So funktioniert es nicht:
$Argument="a -tzip `"$Archiv`" `"$Pfad`" -mem=AES256 -p$($Account.Password) -v2g"
sz $Argument

ToDo?

Code Block

Code Block
Und hier noch ein umfassender Beitrag zu dem Thema.