Montag, 27. August 2018

Hyper-V Server verwenden

Der Plan: Hyper-V Server 2016 (download) als Grundlage für einen kleinen Server verwenden und darauf u.a. einen bisherigen Windows Server 2012R als virtuelle Maschine zu betreiben. Nebenbei sollen dann eventuell noch andere kleinere System laufen.
  • Hardware Unabhängigkeit (es gibt einige Hardwaretreiber nicht als Serverversion, auch von namhaften Herstellern wie Intel) 
  • Kein Leistungsverlust
  • Headless Betrieb, komplett von Windows 10 remote administrierbar
Ich hatte dazu schon einmal einen Artikel, den will ich hiermit konkretisieren.

Installation und Einrichtung des Servers


  • Ganz klassisch mit USB Stick, Monitor und Tastatur. Standardeinstellungen, die System Partition auf der SSD soll später verkleinert werden. 
  • System english aber Spracheinstellung deutsch, Zeitzone Berlin. 
  • Beim ersten Boot vom USB Stick auf UEFI oder BIOS Modus achten!

Mit dem Programm sconfig (nach der Anmeldung im Vordergrund und jederzeit wieder mit sconfig aufrufbar) wird folgendes eingestellt:

2) Computer Name
4) Configure Remote Man. (4-3)Enabled + ...Response to Ping
7) Remote Desktop (7-e-2)     Enabled (all Clients) ...less secure

Für den Computer Namen will er neu starten, deswegen kann man den Punkt zuletzt machen und anschließen braucht man die lokale Anmeldung nicht  mehr. Es geht alles über RDP.

Die weiteren Einstellungen erfolgen in Powershell, dazu kann man einfach das cmd Fenster hinter dem sconfig Fenster in den Vordergrund holen und Start Powershell starten.

Remote Desktop

Funktioniert sofort nach der obigen Grundkonfiguration, aber eben nur wenn nicht die Option 1) more secure eingestellt wurde. Das liegt offenbar an einem, nach der Installation noch nicht vorhandenem Patch des CredSSP. Nach dem Windows Update des Systems funktioniert auch die Option 1).

Netzwerk und Firewall

Windows typisch befindet sich das Netzwerk zunächst in der Category Public. Damit sind aber viele Firewall Regeln schärfer und viele Zugriff nicht zugelassen. Das ist im internen Netzwerk nicht sinnvoll, also ändern auf private. Der erste Befehl liefert uns die Informationen zum Netzwerk:
Get-NetConnectionProfile


Name             : Network
InterfaceAlias   : Ethernet
InterfaceIndex   : 3
NetworkCategory  : Public
IPv4Connectivity : Internet
IPv6Connectivity : NoTraffic
Der folgende Befehl setzt den Anschluss auf Private:
Set-NetConnectionProfile -InterfaceIndex 3 -NetworkCategory Private
Den Erfolg kann man leicht mit dem vorhergehenden Befehl überprüfen.
Die IP Adresse sollte fest sein, der DHCP Server und wenn vorhanden der DNS Server sollten so konfiguriert werden, dass der die IP Adresse fest zugeordnet ist und der Computer Name als FQDN aufgelöst werden kann.

Remote Management

Dafür sind Powershell Remoting und der Credential Security Support Provider zu aktivieren.
Enable-PSRemoting
Enable-WSManCredSSP -Role server
Damit ist die Server Konfiguration abgeschlossen

Einrichtung am Client Windows 10

Das Netzwerk und die Firewall sind zunächst auch so einzurichten, dass die NetworkCategory  : Private ist (siehe oben).
Der Computer Name des Hyper-V Servers muss als FQDN auflösbar sein, bitte so prüfen.
nslookup name.domain
Funktioniert das nicht, kann/muss ein Eintrag in der Datei C:\Windows\System32\drivers\etc\hosts gemacht werden.

Hyper-V-Verwaltungstools

Man kann sich anzeigen lassen, welche Hyper-V Features schon installiert sind
Get-WindowsOptionalFeature -Online -FeatureName *hyper-v*  | where state -eq enabled| select DisplayName, FeatureName
Will man nur die Management Tools installieren, geht prinzipiell mit Powershell, ist jedoch noch nichts von Hyper-V installiert kommt leider ein Fehler. Es funktioniert nur im Dialog so wie gewünscht.
Windows Features aktivieren oder deaktivieren (nicht Apps & Features und nicht optionale Features)
Eine Krücke mit Powershell wäre: Alles installieren und die Hyper-V-Plattform wieder deinstallieren.
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell -all
disable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V
Allerdings ist dafür wegen der Installation von Hyper-V-Plattform ein Neustart fällig.

Vertrauen in einer Workgroup 

Eigentlich ist die Empfehlung von Microsoft auf dem Client als Erstes ebenfalls den Befehl Enable-PSRemoting auszuführen. Allerdings aktiviert das primär auch die Remote Verwaltung des Clients.
Es geht auch etwas sparsamer:

$server = "name.domain"
$user = "administrator"
Start-Service -Name winrm
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $server
Stop-Service -Name winrm
cmdkey /add:$server /user:$user /pass

Jetzt öffnet man den Hyper-V Manager und fügt unter dem Punkt "Verbindung mit Server herstellen" den Server mit dem Namen "name.domain" (also exakt so wie bisher verwendet) ohne Angabe eines Benutzers hinzu.

Danke, dass ich dafür diesen Artikel lesen durfte.

Remote Shutdown für den Server einrichten

Die Konfiguration wird auf dem Hyper-V Server in der Powershellkonsole durchgeführt.

Benutzer anlegen

Das Passwort wird abgefragt.
New-LocalUser "UserShutdown"

Will man verhindern, dass das Passwort altert:
Set-LocalUser -Name "UserShutdown" -PasswordNeverExpires 1

Benutzerrechte festlegen

Dazu muss zunächst ein PS Script Modul heruntergeladen und eingebunden werden.
$url = "https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0/file/198800/1/UserRights.psm1"
$output = "UserRights.psm1"
# $start_time = Get-Date
Invoke-WebRequest -Uri $url -OutFile $output
# Write-Output "Time taken: $((Get-Date).Subtract($start_time).Seconds) second(s)"
Import-Module .\UserRights.psm1

User Rechte erteilen 
Grant-UserRight -Account "UserShutdown" -Right SeRemoteShutdownPrivilege

Firewall

Regeln für "File and Print" aktivieren, entweder mit dem netsh Tool, was mit dem dargestellten Syntax nur auf einem englischen System funktioniert.
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=Yes
Oder mit dem Powershell cmdlet und der "codierten" Variante, die sprachunabhängig funktioniert.
Enable-NetFirewallRule -Group "@FirewallAPI.dll,-28502"

Volume verkleinern

Ich würde gern noch das Volume C auf dem Hyper-V Server minimieren. 48 GB sollten dafür ausreichen, derzeit ist das Volume 127 GB groß. Die Aufgabe erledigt man einfach mit:
diskpart

Am Prompt von diskpart lässt man sich die Plattenkonfiguration zeigen, wählt die gewünschte Partition und verkleinert sie um den Betrag 80000 MB. (1024 MB = 1 GB)
list disk
sel disk 0
list part
sel part 4
shrink desired=80000
exit

Probleme

Scheinbar ziemlich aktuell taucht ein Problem auf, wenn die CredSSP der beiden Maschinen einen unterschiedlichen Patchlevel haben. Ich konnte mich zwar mit dem Hyper-V Manager zu meinem Hyper-V Server verbinden und kann den komplett administrieren, eine Verbindung zu den Virtuellen Gastmaschinen schlug aber wieder mit der Meldung "CredSSP ... Oracle ... Remediation ... linkid=866660" fehl.
Abhilfe schafft in dem Fall auf die Schnelle:
gpedit (Editor für Lokale Gruppenrichtlinien) -> Computerkonfiguration -> Administrative Vorlagen -> System -> Delegieren von Anmeldeinformationen
Dann diese Einstellung am Client
Letzendlich wird damit dieser Schlüssel in der Registry gesetzt:
REG_DWORD
AllowEncryptionOracle : 2
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP
Original existiert dieser Wert nicht und entspricht damit 0

Quelle1
Quelle2

Dies und das

Firewall aus und einschalten
NetSh Advfirewall set allprofiles state off
NetSh Advfirewall set allprofiles state on

Firewall Rules abfragen
Get-NetFirewallRule -name *

Donnerstag, 23. August 2018

FritzBox mit TR064 abfragen

TR064 oder TR-064 ist ein Schnittstellen Protokoll für DSL Router. Hier findet man nähere Information und ein paar Dokumente.
Das Modul FRITZBOX von FHEM kennt zwei Kommandos für TR064
get FritzBoxName tr064ServiceList
gibt einen strukturierte List mit Funktionen und Kommandos.
get FritzBoxName tr064Command Kommando
lässt TR064 Kommandos an die Fritzbox senden und man bekommt etwas zurück. Aber wie kommt man zu dem Syntax für "Kommando"?
Hier habe ich im Forum mal einen Beispielbefehl gefunden, von dem habe ich mich versucht weiter zu hangeln.
get FB7490 tr064Command WANPPPConnection:1 wanpppconn1 GetInfo
Gibt eine Liste zurück aus der man Werte zu DSL Verbindung auslesen könnte.
  • Aber was gibt es denn noch so? 
  • Wie kommt man von der Service Liste zum Befehl?
Beispiel aus der Service Liste
 Spec: http://192.168.90.1:49000/wancommonifconfigSCPD.xml    Version: 1.0
 Service: WANCommonInterfaceConfig:1     Control: wancommonifconfig1
----------------------------------------------------------------------------------------------------------------------------------
  GetCommonLinkProperties ( ) = ( NewWANAccessType NewLayer1UpstreamMaxBitRate NewLayer1DownstreamMaxBitRate NewPhysicalLinkStatus )
  GetTotalBytesSent ( ) = ( NewTotalBytesSent )
  GetTotalBytesReceived ( ) = ( NewTotalBytesReceived )
  GetTotalPacketsSent ( ) = ( NewTotalPacketsSent )
  GetTotalPacketsReceived ( ) = ( NewTotalPacketsReceived )
  X_AVM-DE_SetWANAccessType ( NewAccessType )
  X_AVM-DE_GetOnlineMonitor ( NewSyncGroupIndex ) = ( NewTotalNumberSyncGroups NewSyncGroupName NewSyncGroupMode Newmax_ds
                                    Newmax_us Newds_current_bps Newmc_current_bps Newus_current_bps Newprio_realtime_bps
                                    Newprio_high_bps Newprio_default_bps Newprio_low_bps )
Für ein funktionierendes tr064Command braucht man:
  1. Aus der Überschrift: 
    • den Wert hinter Service: WANCommonInterfaceConfig:1
    • den Wert hinter Control: wancommonifconfig1
  2. Die Werte für Action stehen in der Tabelle darunter links: X_AVM-DE_GetOnlineMonitor
  3. dahinter steht in der Klammer das Argument: NewSyncGroupIndex
  4. auf der rechten Seite (hinter dem Gleichheitszeichen) stehen die zu erwartenden Werte.

Das Kommando wird im Prinzip so zusammen gesetzt:
get FritzboxName tr064Command Service Control Action Argument Value
Beispiel 1: ohne Argument (weil die Klammer leer ist)
get FB7490 tr064Command WANCommonInterfaceConfig:1 wancommonifconfig1 GetCommonLinkProperties
Beispiel 2: Argument und Value
get FB7490 tr064Command WANCommonInterfaceConfig:1 wancommonifconfig1 X_AVM-DE_GetOnlineMonitor NewSyncGroupIndex 0

Während man ja bis zum Argument noch über die Service Liste kommt, wird es dann beim Value irgendwie schwierig. Zumindest gab es in dem Dokument  dann Anhaltspunkte, das habe ich gefunden über Google mit dem Begriff aus der Überschrift des Service Abschnittes "wancommonifconfigSCPD"

Ansonsten hilft probieren, suchen und raten.

Noch ein Beispiel für den Fritzbox Neustart.
Und es gibt auch was für Powershell.

Mittwoch, 22. August 2018

QR Code für WiFi Zugang

Ohne viel zu tippen und eventuell falsche Zahlen und Buchstaben kommt man mit modernen Android und IOS Systemen per Scan ins Wlan. WiFi Zugang (WLAN) teilen heisst der Menüpunkt im Smartphone wenn man mit dem Wlan verbunden ist. Und das Smartphone zeigt in dem Moment genau einen QR Code, den man mit dem anderen Smartphone scannen kann und mit einem Klick kann man sich verbinden.

Also einfach einen QR Code fürs Wlan basteln!

Es gibt dazu einen standardisierten String der bereitgestellt werden muss. Die einzelnen Textelemente sind durch ; getrennt. Die "" können normalerweise entfallen.

WIFI:T:WPA;P:"password";S:"SSID Name";;

Unter debian Linux

sudo apt-get install qrencode
Unter OpenWrt gibt es auch das Paket qrencode allerdings mit Einschränkungen.

Diese Befehlszeile erzeugt die PNG Datei:

qrencode -t png -o ~/SSID_wifi_config.png 'WIFI:T:WPA;P:"password";S:"SSID Name";;'


Oder unter OpenWrt erzeugt man eine SVG Datei, da ist kein PNG verfügbar!

qrencode -t svg -o ~/SSID_wifi_config.svg 'WIFI:T:WPA;P:"password";S:"SSID Name";;'

Die fertige Datei drucken, oder anderweitig zur Verfügung stellen.

Mit dem "QR Code Reader - Ohne Werbung" von Sustainable App-Developer hatte ich guten Erfolg unter Android. Er will nur Bilder aufnehmen und braucht keine weiteren Rechte.

Freitag, 17. August 2018

Mit OpenWrt Presence Informationen ermitteln

Der zentrale Router weiß ja eigentlich am Besten was los ist im Netzwerk. Ich habe mal die Idee verfolgt auf dem OpenWrt einen Prozess zu starten, der aktuelle Information von Netzwerkgeräten nach "aussen" übermittelt. Als erstes Beispiel will ich mal einen bestimmten Wlan Client ermitteln.
Da die finale kurze Abfrage Schleife leicht unübersichtlich ist, erkläre ich erstmal die einzelnen Komponenten.

Ein paar Grundlagen

Je nach Wlan Hardware hat man unterschiedliche Tools um das Wlan abzufragen. Bei mir war iw und iwinfo vorhanden. Ich muss noch prüfen ob der Befehl ip auch (und ganz universell) zu gebrauchen wäre.
Die Ausgabe wird dann für die weitere Verarbeitung gefiltert. Diese beiden Befehle liefern das gleiche Ergebnis:
iwinfo |grep -oE "wlan\d-\d|wlan\d"
iw dev | grep Interface | cut -f 2 -s -d" "
Eine Liste der im Router definierten Wlan Netzwerke.
wlan0
wlan1
wlan1-1
wlan1-2
Eine List der angemeldeten Clients bekommt man mit einem dieser beiden Befehle:
iwinfo wlan1-1 assoclist
iw dev wlan1-1 station dump
In einer Schleife über alle Wlan Netzwerke und einem Ausgabe Filter bekommt man eine komplette Liste der MAC Adressen der aktiven Wlan Clients:
for w in $(iwinfo |grep -oE "wlan\d-\d|wlan\d"); do
   iwinfo $w assoclist | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'
done

Abfrage einer bestimmten MAC Adresse

Jetzt alles zusammengefasst mit einer Abfrage eine bestimmten MAC Adresse und als Resultat die Übergabe an einen Dummy in FHEM (API WEB ohne csrf Token!).
Das Script /root/loop.sh
#Die Verzögerungszeit der Abfrageschleife kann auch übergeben werden
WATCHDOG_SLEEP_SEC=${1:-2}
#Url zu FHEM
u=http://192.168.56.80:8088/fhem?cmd=set%20WL_Mi6%20
MAC_ADDRESS_1="80:AD:xx:xx:xx:xx"

while sleep $WATCHDOG_SLEEP_SEC; do
 if (
      for m in $(
                 for w in $(iwinfo |grep -oE "wlan\d-\d|wlan\d")
                 do
                   iwinfo $w assoclist | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'
                 done
                )
      do
        [ "$m" = "$MAC_ADDRESS_1" ] && exit 0
      done
      exit 1
    )
 then
   c=on
 else
   c=off
 fi

 # tue nur etwas wenn sich c geändert hat
 if [ "$c1" != "$c" ] ; then wget -qs $u$c ; c1=$c ; fi

done

Automatischer Start

Jetzt fehlt noch der automatische Start in OpenWrt. Dafür ist procd zuständig. Ich habe dazu ein init Script mit minimaler Funktion erstellt (siehe auch Erklärung unter dem Text):
cat <<EOF > /etc/init.d/loop
#!/bin/sh /etc/rc.common

USE_PROCD=1

START=99
STOP=01

start_service() {
    procd_open_instance
    procd_set_param command /bin/sh "/root/loop.sh"
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
}
EOF 
Achtung! ich lerne ja selbst immer wieder dazu und habe diesen Code mal bewusst als Here Doc  dargestellt. Wenn man den Text, inklusive der ersten und letzten Zeile, einfach in die Eingabe des Terminals wirft, wird automatisch die Datei am richten Ort erzeugt, ohne das man einen Editor bemühen muss! 
Wer das nicht will, der kopiert den Text ohne die erste und letzte Zeile in den Editor seiner Wahl.

Das Ganze muss noch ausführbar gemacht und aktiviert werden:
chmod +x /etc/init.d/loop
/etc/init.d/loop enable
/etc/init.d/loop start 
Mit dem Befehl service kann man überprüfen ob der Dienst angekommen ist und ihn auch steuern.
root@wrt1900:~# service
service "" not found, the following services are available:
boot              dropbear          linksys_recovery  network           sysfixtime        uhttpd
cron              firewall          lm-sensors        odhcpd            sysntpd           umount
dnsmasq           gpio_switch       log               rpcd              system            urandom_seed
done              led               loop              sysctl            ucitrack

War es das schon?

Es gibt natürlich weitere Ideen:

  • Umstieg auf Eventbasierte Abarbeitung ähnlich wie im verlinkten Beispiel.
  • Konfigurierbare Abfrage mehrerer Clients
  • Auslesen aller Host und Übergabe an FHEM 
Aber jetzt will ich erstmal diesen Prozess eine Weile Probe laufen lassen und ein Monitoring des Routers einrichten.

Mittwoch, 8. August 2018

Lokale Namen OpenWrt

Mit der Fritzbox war es immer etwas mystisch: Wann klappt die Namens Auflösung von lokalen Geräten? Am Ende war nur noch Konfiguration mit IP Adressen anstatt Namen angesagt. Blöd wenn man mal etwas umstellen will. Mit OpenWrt sollte es doch jetzt alles klar beherrschbar sein!?

Erkenntnisse

Scheinbar werden von dnsmasq die lokalen Namen erst geliefert, wenn die DHCP Clients die Lease aktualisiert haben. Theoretisch überleben die Leases auch einen Neustart des Routers. Ich hatte aber oft die Situation, da war nach dem Start alles erstmal weg. Die Leasetime steht per Standard auf 12 h. Da dauert es dann ganz schön lange, wenn ein Client mal die Lease erneuern will. Irgendwo hatte ich mal gelesen, dass passiert nach der halben Leasetime (6 h). Dafür muss eine Lösung her.
Frage: Wo packt man jetzt Hostnamen hin, damit sie sofort zur Verfügung stehen?
Antwort: /etc/hosts

Code

Die static Leases sind schlecht lesbar wenn sie nur die IP und Mac enthalten, der Hostname sollte da schon mit in die Liste. Damit man nicht alles doppelt halten und konfigurieren muss, wäre es jetzt sinnvoll die static Leases einfach auszulesen und in die hosts Datei zu schreiben.
Teile des Codes habe ich aus dem Ubuntu Wiki.
Die Abschnitte im Code sind entsprechend kommentiert.

#!/bin/ash

# Perform work in temporary files
temphosts1=`mktemp`
temphosts2=`mktemp`
dom=$(uci get dhcp.@dnsmasq[0].domain)

# If this is our first run, save a copy of the system's original hosts file and set to read-only for safety
if [ ! -f ~/hosts-system ]
then
 echo "Saving copy of system's original hosts file..."
 cp /etc/hosts ~/hosts-system
 chmod 444 ~/hosts-system
fi

# Read Hostnames from DHCP
for i in $(uci show dhcp | grep -oE "host\[\d+\].ip"|grep -oE '\d+'); do
  ip=$(uci get dhcp.@host[$i].ip)
  name=$(uci get dhcp.@host[$i].name)
   if [ ! -z $name ]
   then
    echo $ip $name.$dom $name >>$temphosts2
   fi
done

echo -e "\n# Ad hosts from dhcp "`date` | cat ~/hosts-system - $temphosts2 > $temphosts1

cp $temphosts1 /etc/hosts

# Clean up temp files and remind user to copy new file
echo "Cleaning up..."
rm $temphosts1 $temphosts2

# The File must be readable for everyone
chmod 644 /etc/hosts

#Restart Service
/etc/init.d/dnsmasq restart
echo "Done."