Samstag, 7. Juli 2018

Etwas Powershell für ssh

Alle Hostverbindungen sind in lnk Dateien definiert und jetzt wäre es cool wenn man diese Definitionen vielfach weiterverwendet.
In Ergänzung zu meinem letzten Artikel will ich hier ein paar Bausteine aufzeigen und die Gedanken dahinter und den Syntax kurz erklären.

Zunächst mal ein paar grundlegende Variablen und Objekte damit der Code universell bleibt.
# Varianten für Zielpfad
#$HomePath = "$env:HOMEDRIVE$env:HOMEPATH\Desktop"
$HomePath = "$env:onedrive\ssh Hosts"
# Filter
$Filter = "*.lnk"
$FArg = "@"

# Shell Object zum Zugriff auf Eigenschaften von lnk
$shell = New-Object -COM WScript.Shell
Kurz und kompakt ist die Variante mit konsequenter Ausnutzung von Pipes.

  • GCI liefert alle Dateien mit der Endung lnk in einem Pfad.
  • Für jedes Element wird ein Shell Objekt erzeugt und im Element Arguments nochmal gefiltert. Im Beispiel auf das Zeichen @ um wirklich nur die lnk Dateien mit user@host zu behandeln. Im Element Targetpath steht der Befehl komplett mit Pfad.
  • Für alle gefilterten Elemente wird ein neuer Prozess mit cmd gestartet und einfach das Kommando aus der lnk Datei aufgeführt.

Ergo werden alle vorhandenen "ssh" Hostdefinitionen in separaten Fenstern geöffnet.
# Variante mit Pipes
Get-childItem $HomePath -filter $Filter |
  ForEach-Object {$shell.CreateShortcut($_.FullName)}|where Arguments -match $FArg|
    ForEach-Object {
       Start-Process "cmd.exe" "/c $($_.Targetpath) $($_.Arguments)"
    }
Die beiden Elemente Targetpath und Arguments liegen als eine Art Objectreferenz vor. Damit wirklich der Inhalt als String verwendet wird muss dies im Process Aufruf mit $() aufgelöst werden.
Es geht auch ohne Pipes, dann werden alle Übergänge in Variablen gespeichert, der Ablauf ist gleich.
# Variante ohne Pipes
$Files = Get-childItem $HomePath -filter $Filter
ForEach ($File in $Files) {
   $lnkfile = $shell.CreateShortcut($File.FullName) 
   if ($($lnkfile.Arguments) -match $FArg ) {
      Start-Process "cmd.exe" "/c $($lnkfile.Targetpath) $($lnkfile.Arguments)"
   }
}
Für andere Fälle kann man auch einfach ein Array mit Hosts erstellen und lässt eine ForEach Schleife über dieses Array laufen.
$Hosts = @("192.168.178.80","192.168.178.81","192.168.178.82","192.168.178.83")
ForEach ($h in $Hosts) {}
Um den Code leserlich und modular zu halten, kann man zu Beginn alles in Variablen packen. Die Unterbringung von Befehlssquenzen in Variablen ist nicht ganz trivial. Damit man gut testen kann und nicht immer den ganzen Code verändern muss, packe ich  auch das erste cmd Argument in eine String Variable $cs.
#Absoluter Pfad zum Public Key
$Keyfile = "$env:HOMEDRIVE$env:HOMEPATH\.ssh\id_rsa.pub"
$cs = "/C "  # Schließt das cmd Fenster nach Abarbeitung
#$cs = "/K "  # Lässt das cmd Fenster offen - nur für Tests wichtig!
$sshTarget = "pi@$h"
Die eigentliche Codezeile ( Ersatz ssh-copy-id) enthält zwei in sich geschachtelte Argumentelisten. Zunächst der Fenstertyp für cmd.exe in $cs.
Die Kombination aus $cs, $C1 und $sshTarget wäre für sich ausführbar, Ergebnis ist eine Shellkonsole.
Danach kommt die Argumentenliste (Befehle) die in der Shellkonsole ausgeführt werden sollen. Diese muss komplett in " " verpackt werden. Innerhalb dieser Argumentenliste werden wieder Variablen mit dem $ Zeichen im Namen gebildet. Diese Namen haben nichts mit der aufrufenden Powershell zu tun.
Damit Powershell die Zeichen $ und " nicht auflöst, muss man sie mit Backticks (`) schützen. Wenn man die Befehlsfolge an den Stellen aufteilt, wo man sie auch separat testen könnte, bleibt sie lesbar und ist gut zu dokumentieren. Der Code funktioniert, im eigentlichen Artikel gibt es eventuell eine aktuelle Variante.
$C1 = "type $Keyfile | ssh " 
$C2 = " akey='.ssh/authorized_keys'; okey='keee.tmp'; cat | tr -d '\r' >`$okey;" # Variablen erzeugen, Text aus der Pipe ohne CR speichern
$C3 = " umask 077; test -d .ssh || mkdir .ssh;" # Berechtigung setzen, Verzeichnis prüfen sonst anlegen
$C4 = " test -f `$akey || cat `$okey >`$akey;" # Datei prüfen sonst direkt anlegen
$C5 = " grep -q `"`"`$(cat `$okey)`"`" `$akey  || cat `$okey >>`$akey;rm `$okey" # Prüfen ob Key schon vorhanden sonst hinzufügen 
Der grep Befehl bekommt einen String übergeben der in sich durch " geschützt sein muss. Damit die Shell die " nicht auflöst müssen sie in der Shell durch Verdopplung geschützt werden, damit Powershell die " im String nicht auflöst müssen sie durch ` geschützt werden.
Jetzt fügen wir alles zusammen und testen das Ergebnis als Ausgabe. Hier erfolgt jetzt das oben erwähnte Einpacken der Shellbefehle in " . Man kann $C2 bis $C5 flexibel weglassen und einzeln testen.
$Argument = $($cs + $C1 + $sshTarget + " `"" + $C2 + $C3 + $C4 + $C5 + "`"")
Write-Output $Argument
Wenn alles gut aussieht kann man den Befehl testen.
Start-Process "cmd.exe" $Argument
Jetzt kann man alles zusammenpacken und auf Knopfdruck entweder alle Hosts auf einmal in getrennten ssh Fenstern öffnen oder gleich alle Hosts mit einem Vorgang mit den eigenen Public Keys versorgen.
Hier eine komplette Version mit ein paar Optionen, die durch Kommentarzeichen noch aus- oder eingeschaltet werden müssen.
# Varianten für Zielpfad
#$HomePath = "$env:HOMEDRIVE$env:HOMEPATH\Desktop"
$HomePath = "$env:onedrive\ssh Hosts"

# eigener Public Key
$Keyfile = "$env:HOMEDRIVE$env:HOMEPATH\.ssh\id_rsa.pub"
# Filter 
$Filter = "*.lnk"
$FArg = "pi@"
# cmd Fenstertyp bestimmen
$cs = "/C"  # Schließt das cmd Fenster nach Abarbeitung
#$cs = "/K"  # Lässt das cmd Fenster offen - nur für Tests wichtig!

# Kommando für cmd Aufruf in Einzelteilen
$C1 = " type $Keyfile | ssh " 
$C2 = " akey='.ssh/authorized_keys'; okey='keee.tmp'; cat | tr -d '\r' >`$okey;" # Variablen erzeugen, Text aus der Pipe ohne CR speichern
$C3 = " umask 077; test -d .ssh || mkdir .ssh;" # Berechtigung setzen, Verzeichnis prüfen sonst anlegen
$C4 = " test -f `$akey || cat `$okey >`$akey;" # Datei prüfen sonst direkt anlegen
$C5 = " grep -q `"`"`$(cat `$okey)`"`" `$akey  || cat `$okey >>`$akey;rm `$okey" # Prüfen ob Key schon vorhanden sonst hinzufügen 

# Die Alternativen Zeilen für OpenWrt Router
#$FArg = "root@"
#$C2 = " akey='/etc/dropbear/authorized_keys'; okey='keee.tmp'; cat | tr -d '\r' >`$okey;"

# Shell Object zum Zugriff auf Eigenschaften von lnk
$shell = New-Object -COM WScript.Shell
# Variante mit Pipes
Get-childItem $HomePath -filter $Filter |
  ForEach-Object {$shell.CreateShortcut($_.FullName)}|where Arguments -match $FArg|
    ForEach-Object {
       $sshTarget = $_.Arguments
       # Entweder nur die Public Keys übertragen 
       $Argument = $($cs + $C1 + $sshTarget + " `"" + $C2 + $C3 + $C4 + $C5 + "`"")
       # oder nur die Terminalsession öffnen
       #$Argument = "$cs $($_.Targetpath) $($_.Arguments)"
        
       Write-Output $Argument # Zum Test
       #Start-Process "cmd.exe" $Argument # eigentlicher Befehl 
    }

Samstag, 30. Juni 2018

Windows hat jetzt ssh

Seit vielen Jahren gibt es eine open-ssh Portierung für Windows im Entwicklungs/Beta/Schatten Dasein. Seit der Windows 10 Version 1803 (in 1709 noch als Beta) ist es jetzt offiziell als Feature enthalten und der Client sogar per Default installiert. Die interessante Frage:

Ersetzt das putty?

Wäre zumindest mein Ziel, dann muss man in Zukunft nichts nachinstallieren. Einfach mal versuchen:
Windows + r Taste zum Ausführen drücken und den Befehl eintippen:
ssh username@hostname-oder-IP-Adresse
Es kommt die Abfrage nach dem Passwort bzw. beim ersten Mal vornweg die typische Frage nach der Authentizität des Hostes. Schon ist die Terminalkonsole offen.

Verbindung mit ssh Key

Die OpenSSH Portierung stellt auch einige Tools wie ssh-keygen zur Verfügung. Wie üblich kann man sich einen Key Pärchen im Homedirectory im Pfad .ssh erzeugen lassen. Der Befehl
ssh-keygen -t rsa
schlägt als Verzeichnis vor: C:\Users\name/.ssh/id_rsa
Was natürlich bezüglich der Pfade irgendwie lustig aussieht: Windows trifft Linux.

Alle ssh Befehle mit Bezug auf die Benutzerumgebung müssen ein einem cmd Fenster in der Benutzerumgebung ausgeführt werden!
Geht ganz einfach mit Windows + r Taste + cmd
Die Verwendung einer Powershellkonsole ist wegen der Zeichenkodierung nicht zu empfehlen (siehe weiter unten).

Man kann, wie üblich, den Public Key auf andere System übertragen und sich dort ohne weitere Passworteingabe einloggen und Befehle ausführen (Blog Link zu verwandtem Thema).
Dazu unbedingt die Anmeldung am Windows entsprechend absichern!

Mit SCP Dateien kopieren

Eigentlich ist dies die Funktion von WinSCP &Co. Aber Windows bringt jetzt zumindest Zugriff über die Kommandozeile mit. Und wenn das mit dem Key ohne Passwort funktioniert - warum dann zweimal anmelden?
Kurz und knapp der Syntax um eine Datei zwischen den HomeDirs zu kopieren.
Wie immer: Quelle -> Ziel
scp Dateiname username@hostname:
scp username@hostname:Dateiname .
Während Remote als Ziel der Pfad entfallen kann (HomeDir) muss bei lokal als Ziel mindestens der "." (HomeDir) stehen.

Eigenheiten

Die Anführungszeichen!

  • Remote Befehle müssen in die "Doppelten" gekapselt werden. Bei Linux gehen dort auch einfach 'Remote Befehl'.

Das Beispiel erzeugt lokal eine Datei mit dem Inhalt der Ausgabe des Remote Befehls:
ssh username@hostname "ls -l" >InhaltUserHomeDir.txt

Tools fehlen oder sind unvollständig!

Ein paar Details und Workarounds findet man weiter hinten in diesem Artikel.

Die Antwort

Ich denke putty kann insgesamt mehr, aber wer genau das braucht, was ich hier beschrieben habe, kommt mit dem neuen ssh Client in Windows 10 (ab Version 1083) gut klar. Und die folgende Menüleiste finde ich komfortabler als putty.

Ein paar Tipps

Menüleiste mit den fertigen Verbindungen

Basis ist ein neuer Ordner mit Verknüpfungen darin.
Tipp: Wer ein MS Konto benutzt hat OneDrive, dann am Besten dort und es ist überall verfügbar!
Beispiel: Ordnername "ssh Hosts"
Rechtsklick auf Laufwerk oder Desktop / Neu / Ordner

in dem Ordner Rechtsklick / Neu /Verknüpfung

Rechtsklick Taskleiste / Neue Symbolleiste / Ordner auswählen





Zwei Angaben bei der Verknüpfung: 1 Fenster Befehl und 2. Fenster "Menüpunktname"
Nach dem Klick auf Neue Symbolleiste navigiert man zu dem gerade angelegten Ordner.
Fertiges ssh Menü
Beispiel für Verknüpfungsziele

  • ssh username@192.168.1.1
  • ssh pi@raspberrypi
  • cmd
Wie man das noch etwas mit Powershell kombinieren kann zeige ich in diesem Artikel.

Putty Keys verwenden

Wenn man schon mit einem Putty Key gearbeitet hat, kann man den weiter verwenden. Wobei es bei eigenen Maschinen auch nicht schlimm und am Ende einfacher ist, einen Neuen zu erzeugen.
Man braucht dazu puttygen. In dem grafischen Tool wird mit Load das existierende ppk File mit dem Key geladen.
Den public Key kann man direkt mit der sichtbaren Schaltfläche exportieren, er muss aber dann noch weiter bearbeitet werden, deshalb hier irgendeinen temporären Namen (c:\users\Name\ppkPub.txt) verwenden und diesen mit Pfadangabe merken!
Den private Key müssen wir exportieren: Conversions / Export OpenSSH Key
Hier gleich den richtigen Ort und Namen angeben %userprofile%\.ssh\id_rsa
Dabei eventuell vorher den Pfad %userprofile%\.ssh erzeugen.
Jetzt unbedingt ein cmd Fenster und nicht Powershell öffnen! Der Public Key hat ein falsches Text Format (mehrzeilig). Das Tool ssh-keygen kann diesen umwandeln und gleich an die richtige Stelle schreiben:
ssh-keygen -i -f c:\users\Name\ppkPub.txt >%userprofile%\.ssh\id_rsa.pub

Wie und wo installieren?

Wer jetzt Stelle sucht wo es aktiviert und installiert wird, wird feststellen: Die Sache wird auch nicht übersichtlicher! Je nach dem wie man die Windows Suche benutzt, findet man jetzt:
  • Apps & Features - dort findet man es unter dem Punkt Optionale Features verwalten.
  • Programme hinzufügen oder entfernen - führt letztlich zu Apps & Features
  • Optionale Features - führt direkt zum Punkt
  • Windows-Features aktivieren oder deaktivieren - ist etwas völlig anderes. Obwohl man hier durchaus Komponenten aus Optionale Features findet.
  • Systemsteuerung / Programm deinstallieren oder Ändern hat zwar Windows-Features als Unterpunkt aber die Programme die hier gelistet werden müssen nicht mit denen übereinstimmen die man unter Apps & Features findet.
Powershell ist immer eine gute Lösung, wenn man die Installation exakt beschreiben will. Auf docs.microsoft.com findet man unter dem Stichwort WindowsCapability ein paar cmdlets die aus der dism Verwaltung kommen. Deswegen ist der Syntax etwas "speziell" und man muss schon die Abfrage als Administrator ausführen!
Hinweis: Damit das Installationsthema nicht so einfach wird:
  • Get-WindowsOptionalFeature liefert Windows-Features, 
  • Get-Package liefert installierte Programme, 
  • Get-AppxPackage liefert "vorinstallierte Programme".
Auf dem Server gibt es ein separates Set an CMD-lets z.B. Get-WindowsFeature, Install-WindowsFeature...

Einschränkungen Windows ssh Client

Leider existiert das Tool ssh-copy-id nicht. Deswegen muss man den Key mit ein paar Linux Befehlen übertragen. Ich habe dazu einen extra Artikel verfasst.

Das Tool ssh-keygen kann die known_hosts Datei nicht verwalten.
Update: Aktuell mit der Windows Version 1809 funktioniert es wie gewohnt.
Bisher wurde ein Versuch spartanisch quittiert:
ssh-keygen -R 192.168.178.82
Updating known_hosts is not supported in Windows yet.
Die Datei .ssh\known_hosts ist UTF-8 und mit CR LF kodiert. Man kann sie mit einem Texteditor einfach editieren und die fragliche Zeile löschen. Oder mit dem cat und grep Pendant von Windows geht es mit diesem Zweizeiler:
type .ssh\known_hosts > .ssh\known_hosts.org
findstr /V "192.168.178.82" .ssh\known_hosts.org > .ssh\known_hosts

Windows OpenSSH Server installieren

Edit: Ich habe hier einen Artikel mit einer ergänzenden Anleitung zur Installation des ssh Servers erstellt.

Das machen wir einfach alles mit Powershell.
Windows 10
Der Add Befehl kann hier nicht mit Wildcards umgehen.
Add-WindowsCapability -Online -Name  OpenSSH.Server~~~~0.0.1.0
Jetzt prüfen ob erfolgreich installiert wurde:
Get-WindowsCapability -Online | where name -match 'ssh'
Man kann auch die installierten Dienste prüfen, sie laufen aber noch nicht!
Get-Service -Name *ssh*
Also Dienste auf Automatic setzen und starten:
Get-Service -Name *ssh* | Set-Service -StartupType Automatic
Get-Service -Name *ssh* | Start-Service
Jetzt kann man sofort loslegen und sich zum ssh Server verbinden, eine separate Firewallregel braucht man nicht, die ist offenbar schon eingerichtet. Ich musste nichts weiter einrichten!
Man wird über ssh zur cmd Konsole verbunden, hier kann man dann auch Powershell starten! 

Hinweis:
Die Konfiguration des Servers liegt in C:\ProgramData\ssh.
Get-ChildItem -Path 'C:\ProgramData\ssh\'
Quelle zur Installation

Windows Server
Auf dem Windows Server ist ssh erst ab der ReleaseID 1709 integriert Windows Server 2016 ist die ID 1607.
Auch auf früheren Versionen kann man die aktuelle Win32 OpenSSH Version installieren.
Achtung: für den folgenden Code (und für eine aktuelle Management Umgebung) muss Windows Management Framework 5.1 installiert sein!

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://github.com/PowerShell/Win32-OpenSSH/releases/latest/'
$request = [System.Net.WebRequest]::Create($url)
$request.AllowAutoRedirect=$false
$response=$request.GetResponse()
$url = $([String]$response.GetResponseHeader("Location")).Replace('tag','download') + '/OpenSSH-Win64.zip'
Invoke-WebRequest $url -OutFile openssh.zip
Expand-Archive .\openssh.zip 'C:\Program Files\'
cd 'C:\Program Files\OpenSSH-Win64\'
.\install-sshd.ps1
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
Start-Service -Name sshd
Set-Service -Name sshd -StartupType automatic

Tipp zur Anmeldung mit ssh an Windows 10

Ich melde mich mit einem Online Konto (Format user@domain) am Windows Desktop an. Windows legt intern zu diesem Online Konto eine Art "Kurznamen" an. Der ssh Server akzeptiert nur diesen kurzen Namen, eventuell kann man generell diesen Kurznamen verwenden, wenn man sich an anderen Diensten über das Netzwerk anmeldet.
Mit einer der folgenden Befehle kann man den Kurznamen ermitteln:
whoami
[Environment]::UserName
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name

Hintergrund zur Powershell Textcodierung

In der Powershell (offenbar der > Befehl) werden Text Dateien nicht in der Codierung UTF-8 sondern mit der Codierung UCS-2 LE BOM angelegt. Abhilfe in Powershell? Schwierig.
Mit Notepad++ kann man die Codierung gut anschauen.

Ein paar Tests:
  • Der Befehl innerhalb PS type .ssh/*.pub >catPub.txt erzeugt lokal eine Datei in UCS-2 LE BOM Codierung
  • innerhalb cmd eine Datei in UTF-8 Codierung
  • Der Befehl in CMD oder PS type .ssh/*.pub |ssh pi@192.168.178.80 "cat >picatpub.txt" erzeugt eine Datei in UTF-8 Codierung mit Windows CR LF
  • Wandelt man jetzt mit nano picatpub.txt durch Speichern mit alt+m in Unix LF um dann macht ein erneuter Befehl in cmd type .ssh/*.pub |ssh pi@192.168.178.80 "cat >>picatpub.txt" eine Datei in UTF-8 Codierung mit Unix LF

Das bedeutet:
  • die Übertragung über ssh / type ->  cat "filtert" die UCS-2 LE BOM Codierung aber nicht Windows CR LF.
  • Der cat Befehl unter linux ändert existierende Unix LF nicht!
  • Der type Befehl in PS mit Umleitung >> in eine existierende Datei mit UTF-8 führt zum Datensalat

Ein paar Ansätze:
Ansi ausgeben
UTF8-BOM erzeugen
Keys wieder löschen.
Winscp

Mittwoch, 27. Juni 2018

OpenWrt Router abfragen

Dieser Beitrag im Forum gab den Ansatz für eine Abfrage des OpenWrt Routers nach angemeldeten Wlan Clients. Für mich als Halbwissender war es mal wieder gar nicht einfach den kurzen Artikel umzusetzen. Deswegen hier etwas ausführlicher.

Vorbereitung des Routers

Für das cgi Script braucht man die Komponente bash auf dem Router, das ist schnell installiert.
System / Software im Feld "Download and install package:" einfach bash eintippen und Ok drücken.
Im Pfad /www/cgi-bin/ muss man jetzt eine Datei wifi-client mit folgendem Inhalt anlegen (z.B. mit WinsSCP).
Ich habe gegenüber dem Original Artikel noch die Umwandlung von Gross in Kleinbuchstaben eingebaut.
Die Zeile im Code unterhalb dem Hinweis  # Pfade anpassen je nach AP-Config!! muss man unbedingt anpassen!
Je nach dem welche Wlan Hardware verbaut ist:
  • radio0 -> phy0 
  • radio1 -> phy1
Und welche Wlans definiert sind (Beispiel):
  • wlan1 -> netdev:wlan1
  • wlan1-2 ->  netdev:wlan1-2
Die Benennung war hier nicht ganz konsequent ableitbar, also unbedingt im Pfad /sys/kernel/debug/ieee80211 nachschauen! Oder mit dem Befehl iwinfo die Liste der Wlan Namen ausgeben.
#!/bin/bash

# Decodes an URL-string
# an URL encoding has "+" instead of spaces
# and no special characters but "%HEX"
function urlDec() {
  local value=${*//+/%20}                   # replace +-spaces by %20 (hex)
  for part in ${value//%/ \\x}; do          # split at % prepend \x for printf
    printf "%b%s" "${part:0:4}" "${part:4}" # output decoded char
  done
}

# For all given query strings
# parse them an set shell variables
function setQueryVars() {
  local vars=${*//\*/%2A}                      # escape * as %2A
  for var in ${vars//&/ }; do                  # split at &
    local value=$(urlDec "${var#*=}")          # decode value after =
    value=${value//\\/\\\\}                    # change \ to \\ for later
    value=${value,,}                           # upper in lower case 
    eval "CGI_${var%=*}=\"${value//\"/\\\"}\"" # evaluate assignment
  done
}

# Execute the evaluation
# set all variables for both, POST and GET data
# $QUERY_STRING ist part of the environment and contain the part after ?
setQueryVars $QUERY_STRING $(</dev/stdin)

# Set formatting the Response to plain Text
echo Content-type: text/plain
echo
# for the Query ?dev=xxxxxx&test=yyyyyy
# Query dev= -> $CGI_dev contain value after dev=
# Query test= -> $CGI_test contain value after test= 
# Test the string contain only numbers,lower letters and :
case $CGI_dev in
        *[!0-9a-f:]*|"") echo "NOK MAC"; exit 0;;
esac

# Pfade anpassen je nach AP-Config!!
if [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1-1/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1-2/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/$CGI_dev" ]; then
       echo "connected"
else
       echo "no device"
fi
Diese Datei braucht jetzt nur noch das Ausführen (x) Recht für Alle, kann man auch gleich mit WinSCP machen.

Ohne Weiteres kann man sofort die Funktion testen:
http://wrt1900/cgi-bin/wifi-client?dev=11:22:33:aa:bb:cc
Bei bekannter MAC Adresse erscheint ein connected als Quittung, ansonsten eine entsprechende Ausgabe.

Funktion in 99_myUtils.pm

Um diese Abfrage in einem PRESENCE Device zu verwenden, braucht man noch eine kleine Perlfunktion.
sub wlan_det($) {
use LWP::UserAgent;
my ($client)= @_;
        my $ua = new LWP::UserAgent;
        my $response = $ua->get('http://wrt1900/cgi-bin/wifi-client?dev='.$client);

        unless ($response->is_success) {
                              return 99;
        }

        my $content = $response->decoded_content();

        if ($content =~ m/connected/) {
                              return 1;
        } else {
                              return 0;
        }
}
Ich musste gegenüber dem Originalbeitrag noch die Zeile mit "use LWP:.." ergänzen. Es ist eigentlich besser diese einmalig am Anfang der 99_myUtils.pm nach der Zeile
"# Enter you functions below _this_ line."
und vor allen eigenen Subs zu platzieren.
Die Definition in FHEM ist wie gewohnt:
defmod iPhone PRESENCE function {wlan_det("11:22:33:aa:bb:cc")}

Nacharbeit

Das war jetzt quick&dirty -  man kann noch
  • die Übergabe oder Definition der Router IP besser machen.
  • die Definition der abzufragenden Pfade im Router verbessern.
  • Prüfen was genau passiert wenn der Router nicht erreichbar ist (FHEM blockiert?)
  • Die Abfrage funktioniert so nur für Wifi, LAN Clients ermitteln? 

Dienstag, 19. Juni 2018

RaspberryPi mit OpenWrt/Lede

Auch für den Raspberry gibt es ein fertiges Image des Router Systems. Ob man den Raspberry wirklich als Router betreiben will sei mal dahin gestellt, als Testsystem oder DHCP Server taugt er aber allemal.
Das Image gibt es jeweils getrennt im Zweig brcm2708 im Unterpfad

  • bcm2708 -> B und B+
  • bcm2709 -> B2
  • bcm2710 -> B3

Für den B3+ muss man derzeit ein Image aus dem Snapshot Bereich nehmen.

Das gepackte Image (img.gz) muss zunächst entpackt werden (z.B. 7-Zip) und wird dann wie beim Pi üblich, mit einem passenden Programm (Win32DiskImager) auf die SD Card geschrieben.

Partition vergößern

Die resultierende Partition ist nur 256 MB groß. Wer etwas mehr auf dem Pi machen will als nur "Router" sollte die Partition von Beginn an vergrößern, online und innerhalb von OpenWrt geht das schwierig. Ich habe etwas herum probiert und bin zu folgendem Weg gekommen:

  • SD Card mit dem Kartenleser an einen vorhandenen Linux PC (z.B. Raspberry) anschließen.
  • der Stick wird als /dev/sda aktiviert und nicht gemounted
  • parted (oder parted /dev/sda) aufrufen
  • mit p(rint) vergewissern das richtige Laufwerk zu haben.

(parted) p
Model: SABRENT SD (scsi)
Disk /dev/sda: 15,8GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  25,2MB  21,0MB  primary  fat16        boot, lba
 2      29,4MB  298MB   268MB   primary  ext2

(parted)

  • resizepart 2 eingeben
  • Das neue Ende der Partition eingeben, z.B. 3GB
  • quit für exit
  • mit resize2fs muss das Filesystem angepasst werden
resize2fs /dev/sda2
resize2fs 1.43.4 (31-Jan-2017)
Die Größe des Dateisystems auf /dev/sda2 wird auf 725254 (4k) Blöcke geändert.
Das Dateisystem auf /dev/sda2 is nun 725254 (4k) Blöcke lang.


  • Mit e2fsck -f /dev/sda2 muss jetzt das Filesystem nochmal überprüft und repariert werden obwohl beim einfachen Test und auch beim Test vor der Vergrößerung alles "sauber" gemeldet wird.
e2fsck -f /dev/sda2
e2fsck 1.43.4 (31-Jan-2017)
Durchgang 1: Inodes, Blöcke und Größen werden geprüft
Inode 7, i_size ist 62963712, sollte 67178496 sein.  Reparieren? ja
Durchgang 2: Verzeichnisstruktur wird geprüft
Durchgang 3: Verzeichnisverknüpfungen werden geprüft
Durchgang 4: Referenzzähler werden überprüft
Durchgang 5: Zusammengefasste Gruppeninformation wird geprüft

/dev/sda2: ***** DATEISYSTEM WURDE VERÄNDERT *****
/dev/sda2: 1064/188416 Dateien (0.0% nicht zusammenhängend), 14433/725254 Blöcke

Erster Start

Der Router Pi läuft jetzt mit der Adresse 192.168.1.1 im Netzwerk. Um ihn für eine Konfiguration erreichbar zu machen, müssen wir dem PC zur Konfiguration einen IP Adresse z.B. 192.168.1.5 geben. Eine Variante Windows Powershell zeige ich hier.
Die Anmeldung erfolgt mit root ohne Passwort. Nicht vergessen: Als erstes ein Passwort vergeben.
Im Menü über / Network / Interfaces / Edit / das Protocol auf DHCP Client stellen, den Knopf "switch protocol" drücken und abschließend "Save & Apply" drücken.
 


Der Router bekommt jetzt eine Adresse aus dem lokalen Netzwerk und die lokale Adresse des Konfiguration PC kann zurück gestellt werden.
Damit später alle Zeiten im Log stimmen, sollte die Zeitzone auf Europe/Berlin gestellt werden.

Der Pi kann mit zusätzlichen Interfaces (USB LAN oder USB WLAN oder internes WLAN) arbeiten.
Achtung:! Mit der Version 18.06 hat sich das Verhalten verändert. Es wurde eine neue Sicherheit gegen Fehlkonfiguration eingebaut. Die führt leider dazu, das innerhalb 30 sec die Konfiguration zurückgerollt wird, wenn das Webinterface nicht erreichbar war.
Ich weiß noch nicht, wie man wirklich damit umgeht. Einzig mir bekannt bisher ist: warten bis dieses Fenster kommt, den roten Knopf drücken und erst dann die IP-Konfiguration des PC zurückstellen.

Zusätzliches Netzwerkinterface installieren

In der bisherigen Konfiguration ist das System nicht zu viel mehr zu gebrauchen als DHCP Server und um mit der Oberfläche zu spielen. Es empfiehlt sich das Software Paket usbutils zu installieren, damit hat man auf Kommandozeile den Befehl lsusb zur Verfügung und kann zumindest schauen ob der USB Adapter erkannt wird.

LAN Interface

Mit diesem Adapter hatte ich bisher gute Erfahrungen beim Pi:
I-Tec USB 2.0 Advance 10/100 Fast Ethernet LAN Network Adapter USB 2.0 auf RJ45
Er hat einen Asix AX88772B Chip. Durch die Installation des Packages kmod-usb-net-asix wird der Adapter problemlos in Betrieb genommen. Falls noch nicht geschehen muss als erstes der Knopf Update lists gedrückt werden!


Der USB Netzadapter wird sofort erkannt und in Betrieb genommen.
Im Menü Network/Interfaces -> New Interface steht er als eth1 zur Auswahl:

Nachdem Submit gedrückt wurde kann man die IP Adresse und die Subnet Mask eingeben. Hier wird normalerweise kein Gateway eingetragen!
Da dieser Anschluss in Zukunft der interne LAN1 Anschluss ist, wird er der Firewall Zone "grün - lan" zugeordnet.

Wir haben jetzt einen Router mit zwei LAN Segmenten ohne NAT.

Routing vs Forwarding/NAT

Damit das LAN1 Segment aus dem LAN Segment erreichbar wird, muss der vorhandene Internetrouter eine zusätzliche Route für das Netzwerk LAN1 mit dem Gateway LAN bekommen.

Will man Router ohne Veränderung des existierendes Netzwerk betreiben braucht man NAT. NAT wird einfach durch die Firewall Zone WAN aktiviert, aber Vorsicht! Damit passieren zwei Dinge:

  • NAT wird aktiviert
  • Die Firewall Regeln sehen den Anschluss LAN dann sofort als Internetanschluss. Jeglicher eingehender Verkehr wird blockiert.

Wir greifen derzeit über den Anschluss (LAN) zu, dieser wird nach dieser Konfiguration nicht mehr aus dem Netzwerk LAN erreichbar sein! Also besser jetzt gleich zum Test und zur Konfiguration "das Notebook" an den LAN1 Anschluss verbinden und neu anmelden.
Interface LAN / Edit / Firewall Settings / WAN auswählen. So sieht das Ergebnis aus:

NAT wird innerhalb weniger Sekunden aktiviert und der Zugriff ins Netzwerk LAN und auch ins Internet sollte ohne Änderungen im existierenden Netzwerk funktionieren. Allerdings hat man jetzt meist doppeltes NAT, das verkraften nicht alle Anwendungen (z.B. VPN)

WLAN Interface

Auswahl des richtigen Adapters

Das ist gar nicht so einfach! Diese beiden Adapter habe ich nicht zum Laufen bekommen:
  • TP-LINK TL-WN725N v2 mit Realtek RTL8188EUS Chip
  • "Conrad" RE150U-PA-1T1R mit Realtek RTL8188SU Chip
Offenbar verhindern inkompatible Treiber den Betrieb als Routerinterface, im normalen Raspbian werde beide Adapter als Client unterstützt.
Diesen Adapter gab es preiswert und der hat auf Anhieb funktioniert. Er verleiht dem Ganzen durch seine beiden Antennen auch einen Router-mäßigen Eindruck:
  • CSL USB 2.0 WLAN Adapter 300Mbit (2,4/5GHz) mit Ralink RT5572 Chip
Nicht zu viel erwarten: der Adapter hat aber nur ein Radiointerface und kann 2,4 oder 5 GHz und nicht beides gleichzeitig!
Den Adapter stecken und lsusb gibt uns die entsprechende Information aus:
Bus 001 Device 004: ID 148f:5572 Ralink Technology, Corp. RT5572 Wireless Adapter
Der Chipsatz braucht das Softwarepaket kmod-rt2800-usb nach der Installation ist aber zunächst nicht viel passiert. Im Systemlog sollte aber eine Meldung in der Art auftauchen
kern.info kernel: [  624.833819] ieee80211 phy0: rt2x00_set_rt: Info - RT chipset 5592, rev 0222 detected
Ich habe Wireless im Menü zum Leben erweckt, in dem ich mutig auf der Seite System /Startup den network "Restart" Knopf gedrückt habe. Nach kurzem selbsttätigem Refresh der Oberfläche war der Punkt Network / Wireless sichtbar.
Bei einem neuerlichen Versuch mit OpenWrt 22.03 reicht der restart von network nicht, man muss das System neu starten.
Jetzt kann man das Wireless Netzwerk wie gewohnt einrichten.
Allerdings ist das Bild trügerisch. Es funktioniert noch nicht richtig!
OpenWrt braucht offenbar dafür noch etwas Software. Ich habe nur dieses Paket installiert, die anderen aus der dem Link waren nicht notwendig, obwohl die im Image von meinem WRT1900ACS von Hause aus drin waren. Ich werde das noch untersuchen, siehe Zusammenfassung.
  • block-mount
Danach kann man sich zumindest im 2,4 GHz Modus verbinden, wenn man einfach eine default Konfiguration wählt und bei Device Configuration/Advanced Settings Country Code DE auswählt.

Für eine funktionierende 5 GHz Konfiguration darf Channel nicht auf auto stehen - Bug oder Feature?

Zusammenfassung meiner minimalen Konfiguration

  • Beim Download das ext4 Image nehmen, mit dem squashfs Image bin ich nicht klar gekommen.
  • primäres LAN Interface: switch Protocol to DHCP Client
  • Zeitzone: Europe/Berlin
  • Software installieren: Update List
    • usbutils (optional)
    • kmod-usb-net-asix
    • kmod-rt2800-usb
    • block-mount - offenbar mit Version 18.06 obsolet?
Zumindest mit der neuen Version 18.06 funktioniert damit alles erst einmal. Der 5GHz Modus funktioniert nur mit fester Kanalwahl!

Doku OpenWrt Wireless overview
Doku OpenWrt WiFi configuration

Samstag, 16. Juni 2018

Mal schnell die IP Adresse umstellen

Man hat ein Gerät (z.B. Router) im Netz mit statischer IP der mal schnell umkonfiguriert werden muss? Wo war jetzt im aktuellen Windows nach dem letzten Update die Adapterkonfiguration hin verschwunden?

Geht auch ganz einfach mit Powershell:
Windows + x Taste drücken und Windows Powershell (Administrator) auswählen.

Hat man nur ein LAN Netzwerkinterface per DHCP am Netzwerk verbunden, erledigt dieses kleine Script die temporäre Umschaltung auf die Adresse 192.168.1.5 (um z.B. einen OpenWrt Router nach dem Start zu konfigurieren) 
$interface = Get-NetIPInterface -AddressFamily IPv4|where {$_.InterfaceAlias -match 'Ethernet' -and $_.Dhcp -eq 'enabled' -and $_.ConnectionState -eq 'Connected'}
$interface | New-NetIPAddress 192.168.1.5 -PrefixLength 24
pause
Set-NetIPInterface -InterfaceIndex $interface.ifIndex -Dhcp enabled
Restart-NetAdapter -name $interface.InterfaceAlias

Dienstag, 24. April 2018

Smartphone aus China ...

Naja irgendwie kommt ja scheinbar unsere gesamte Technik irgendwie daher. Ich habe seit einiger Zeit ein mi A1 und ein mi 6 - und ich komme auch immer mal in die Situation für jemanden das neue Smartphone in Betrieb zu nehmen. Da sind mir in letzer Zeit ein paar Besonderheiten aufgefallen und ich habe Verschiedenes versucht zu recherchieren.
Ich muss es einfach notieren. In einem halben Jahr hat man ja wieder viel vergessen oder es ist mit der neuen Version wieder ganz anders.
Diese Dinge sind nicht unbedingt vorhersehbar, logisch oder irgendwo genauso beschrieben. Ob es Besonderheiten der Smartphones von mi sind oder dies "allgemein Android"ist, kann ich nicht sagen.

Werksreset


Das ist mit irgendeiner Android Version etwas aufwendiger geworden. Ich beziehe mit aktuell auf Version 8.
Ein vom Nutzer eingerichtetes zusätzliches Sicherheitsmerkmal (Sperrcode: Pin, Muster oder Fingerabdruck) verschlüsselt wohl übrigens das gesamte Smartphone und man kann es ohne diesen Sperrcode nicht mehr starten!
Es handelt sich neben der Funktion der Ortung um eine Sicherung gegen Missbrauch nach Diebstahl oder Verlust.

Samsung S8 

Man kann im ausgeschalteten Zustand diese Tasten drücken:
drei Tasten gleichzeitig: Lauter + Bixby + Power
Nach Erscheinen vom Samsung Logo kann man loslassen und das Smartphone bootet in einen typischen "Bootloader", dort kann man Menügeführt alles löschen und das Smartphone damit zurücksetzen.
Startet man jetzt neu wird der oben erwähnte Sicherheitscode nicht mehr abgefragt!
Man kann jetzt das Smartphone neu einrichten - aber! Man kann es nur einrichten mit dem ehemals auf diesem Smartphone verwendeten Google Account! Ohne den wird jede Einrichtung verweigert: Meines Wissen keine Chance!
(Eventuell gibt es irgendwelche Reset Funktionen mit Code vom Hersteller, ADB, 72 Stunden warten usw.)

Es ist also wichtig, den Google Account vom Smartphone sicher zu haben, obwohl man vielleicht denkt man benutzt ihn nicht! Früher waren nur die Daten weg, heute wäre das Smartphone "weg"!

mi 6

Das mi 6 kann man nicht ohne weiteres "Hart" zurücksetzen. Die Tastenkombination Power + Volume- bootet lediglich den FASTBOOT Modus. Dort kann man meines Wissen nur mit ADB weitermachen.

Werksreset geht somit nur mit der Methode aus dem angemeldeten Smartphone heraus im Menü: Einstellungen/Sichern zurücksetzen/Auf Werkszustand zurücksetzen
(bzw. Mi Oberfläche und der derzeit gemischte Sprachversion,  Version MIUI Global 9.2.3.0):
Einstellungen/System&Device/Additional settings/Backup & reset/Auf Werkszustand zurücksetzen

Achtung: dieser Vorgang geht nur, wenn man vorher die Systemsprache auf english umgestellt hat! Wahrscheinlich hat die deutsche Oberfläche einen Bug!

Laut Beschreibung könnte man danach das Smartphone völlig neu einrichten, also auch an einen Fremden geben. Doch das stimmt nicht! Wenn man jetzt das Smartphone neu einrichten will wird erstmal wieder der alte Google Account verlangt!
Den alten Account muss man eingeben und dann kann man die Einrichtung (am Besten in english) ohne jegliches Konto durchführen. Danach kann man wieder einen Werksreset machen und jetzt kann man das Smartphone wirklich mit einem neuen Konto einrichten.

Es ist offenbar ein Bug des mi 6. Ich habe den gleichen Vorgang mit einem mi A1 getestet und es klappt wie erwartet.

Aber! Um sicher zu gehen, sollte man es unbedingt immer testen, bevor man das Smartphone an Fremde weiter gibt! Ich hatte in früheren Geräten auch schon den Fall, dass Bilder nicht gelöscht wurden!

GPS Daten

Beim mi 6 und dem mi A1 ist mir noch aufgefallen: Wenn man es ohne SIM Karte startet und einrichtet, ist GPS offenbar völlig desorientiert.  Man befindet sich in Marlborough district bzw. in Blenheim in Neuseeland. Das bleibt hartnäckig so, auch mit Internet (Wlan) Verbindung, auch tagelanges warten ändert dies nicht.
Bis man einmal die SIM Karte eingesteckt hat, dann ist er bereit ordentlich zu lokalisieren. Allerdings wird dann später mal wieder ohne SIM Karte gestartet ist er wieder erstmal in Neuseeland.


Sonntag, 25. Februar 2018

Windows Server Freigabe auf dem Raspberry verbinden

Ich will hier kurz zeigen wie der Zugriff auf eine Windows (Samba) Freigabe unter Raspbian eingerichtet wird. Im Raspbian Image (Lite vom November 2018) sind alle Voraussetzungen vorhanden, man muss nichts weiter installieren. In anderen Systemen kann es sein, dass man das debian Paket cifs-utils nachinstallieren muss.  Openmediavault hat z.B. bei aktiviertem Samba nicht automatisch cifs-utils installiert.
Für den praktischen Gebrauch muss man lediglich 3 Dinge konfigurieren, für einen schnellen Test  kann man es mit einem Zweizeiler machen (siehe weiter unten):

Pfad

Unter Linux braucht man einen Ort wo man ein Laufwerk "hin" mounten kann, dabei wird
\\Servername\Sharename auf den lokalen Pfad /DirName/DirName gemappt.
Man erzeugt einen Pfad, am Besten mit einer "Wurzel", einem Verzeichnis unter dem alle Netzlaufwerke eingehängt werden:
mp='/mnt/Sicherung'
sudo mkdir -p ${mp}
Die Option -p erzeugt alle notwendigen Ordner falls sie noch nicht existieren.

Benutzerkonto

Da ein Windows Server nicht ohne weiteres einen Zugriff ohne Login ermöglicht und wir username und password nicht immer eintippen wollen, werden die Informationen entsprechend abgelegt.
Dabei gibt es drei Möglichkeiten. Ja nach Vorliebe verwendet man einen Editor (nano) oder direkte Shellbefehle
1. persönliche Datei (notwendiger Inhalt siehe nächster Codeblock):
fcred='.smbcredentials'
nano $fcred
chmod 600 $fcred
Die Datei ist damit nur vom Eigentümer lesbar!

2. allgemeine Datei:
Wenn keine Domain verwendet wird, kann der Eintrag domain entfallen oder freibleiben.
fcred='.smbcredentials'
export datei="/usr/${fcred}" 
sudo bash -c "cat >${datei}" <<'EOF'
username=UserName
password=Userpassword
domain=
EOF
Man braucht pro Konto eine credentials Datei. Diese Datei muss von allen Usern lesbar sein, die mounten wollen. Die gerade angelegte Datei ist aber auch von "Jedem" lesbar, will man das eingrenzen, kann man das so machen:
sudo -s <<EOI
  addgroup cifs
  usermod -aG cifs fhem
  usermod -aG cifs pi
  chown root:cifs ${datei}
  chmod 640 ${datei}
EOI
Hinweis: Die Änderung der Gruppenmitgliedschaft wirkt erst nach Neuanmeldung!

3. anstatt extra Datei (credentials=Datei) schreibt man die Kontodaten direkt in die fstab Zeile, dort ohne Zeilentrennung!
username=ano,password=nym

fstab

Nun fehlt noch der mount Eintrag in der fstab. In meinem Beispiel wird nicht automatisch gemountet, alle User dürfen mounten und das Laufwerk ist beschreibbar.
sudo bash -c "echo //ServerName/daten ${mp} cifs noauto,users,credentials=/usr/.smbcredentials 0 0 >> /etc/fstab"

Verwendung

Jetzt können wir das Laufwerk einbinden, den erfolgreichen mount anzeigen und den Inhalt anzeigen. Das funktioniert ohne sudo, der Benutzer braucht Leserechte auf die credentials Datei.
mount ${mp}
df
ls -l ${mp}
umount ${mp}

Hinweis

Das Windows Laufwerk wird auf diese Art nicht automatisch beim Start angebunden. Das Netzwerk ist zu diesem Zeitpunkt noch nicht verfügbar und der automount über die fstab würde nicht funktionieren. Ist für mich kein Nachteil, weil es jederzeit in einem Script per mount Befehl angebunden werden kann.

Nachtrag

Man kann auch jederzeit  mit einem simplen "Zweizeiler" ein Windows Laufwerk als sudo mounten, password= kann man weglassen, dann wird es abgefragt.

sudo mkdir -p /mnt/daten
sudo mount -t cifs -o username=UserName,password=Passwort //ServerName/Freigabe /mnt/daten
# Bei smb1 Servern und ab debian stretch
sudo mount -t cifs -o username='UserName',password='Passwort',vers=1.0 //ServerName/Freigabe /mnt/daten

Bei Problemen kann man sich die verwendeten Dateisysteme bzw. die Unterstützung des Kernels  anzeigen lassen.
cat /proc/filesystems
ls /lib/modules/$(uname -r)/kernel/fs

Zum nachlesen: Ubuntu Wiki

SMB Protokoll

smb Version 1 wird mittlerweile als extrem unsicher eingestuft. Deswegen wird es aus modernen Systemen "entfernt".
Allerdings ist es in vielen Geräten nach wie Standard und eine Unterstützung von smb 2 oder 3 nicht implementiert (Fritzbox NAS, Sonos ...)
Windows hat ab Version 1709 das Protokoll entfernt, man muss es bei Bedarf mit "Windows Apps und Features" nachinstallieren (Neustart erforderlich)
debian stretch hat es offenbar als default aus der Aushandlung herausgenommen, mann muss es mit der cifs Option "vers=1.0" beim mount Befehl explizit hinzufügen, wenn der Server nur smb1 unterstützt.