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."


Dienstag, 31. Juli 2018

Import static Leases in OpenWrt

OpenWrt kann mit speziellen Kommandos (uci) in der Shell konfiguriert werden.
Ich habe mir immer gewünscht, statische DHCP Reservierungen einfach durch Textdateien zu konfigurieren. OpenWrt kann zwar wenigsten in der Weboberfläche die MAC Adressen per Copy&Paste einfügen, aber ich hätte es gern komfortabler.
Wer spielen will, der macht zunächst nichts kaputt. Die Konfigurationsänderungen werden zunächst nur temporär ausgeführt.
Die dhcp Konfiguration anzeigen:
uci show dhcp
uci show dhcp | grep '@host\[0\]'
uci get dhcp.@host[0].ip
Die Host Einträge in der /etc/config/dhcp stehen dort am Ende und sind nicht indiziert. Das uci indiziert sie "fließend" für uns und einen direkten Zugriff:
  • [0] ist der erste, 
  • [-1] der letzte Eintrag.

Vorbemerkung

Ich habe ein paar Scripts als heredoc erstellt, die kann man einfach per copy&paste ins Terminal fallen lassen. Damit die $ Zeichen nicht als Variablen interpretiert werden, muss EOF in einfache ' ' gesetzt werden!
Die uci arbeitet temporär, man kann sich mit uci changes den Erfolg anzeigen lassen. Mit uci revert dhcp kann man alles immer wieder rückgängig machen.
Erst ein uci commit schafft Tatsachen.

Hosts hinzufügen

Will man einen neuen Host eintragen geht das mit folgendem Beispiel. Ein neuer Eintrag wird angefügt und ist damit der Letzte, dieser wird um die Werte ip und mac ergänzt:
script="addhost.sh"
cat <<'EOF' >$script
#!/bin/sh
if [ $# -eq 3 ]
  then
    echo "name=$1 ip=$2 mac=$3"
  else
    echo "Fehler: Bitte name ip und mac angeben."
    exit
fi
uci add dhcp host
uci set dhcp.@host[-1].name=$1
uci set dhcp.@host[-1].ip=$2
uci set dhcp.@host[-1].mac=$3
EOF
chmod +x $script
Jetzt kann man eine existierende Lease Zeile aus dem Browser kopieren und als Reservierung einfügen
./addhost.sh HPF43909586B94 192.168.56.111 F4:39:09:58:6B:94
Mit einer Schleife kann man Zeilenweise den stdin lesen und und pro Zeile einen neuen Hosteintrag erstellen. Damit die static Leases lesbar bleiben sollte ein Hostname mit in die Tabelle.
Read Host per Line -> rhpl.sh
script="rhpl.sh"
cat <<'EOF' >$script
#!/bin/sh
while read line; do
./addhost.sh $line
done
EOF
chmod +x $script
Dazu eine Datei mit Wertepaaren -> wp.txt
host1 192.168.1.1 aa:bb:cc:dd:ee:d1
host2 192.168.1.2 aa:bb:cc:dd:ee:d2
host3 192.168.1.3 aa:bb:cc:dd:ee:d3
Das Ganze mit der Pipe verknüpft und fertig ist der Import!
./rhpl.sh <wp.txt
Nachtrag 2021: Hat man mehrere Dateien, kann man diese gemeinsam lesen, die MAC Adressen in Großbuchstaben wandeln, sortieren, Doubletten entfernen und einlesen
cat leases.txt wpo.txt |awk '{ print( $1,$2,toupper($3) ) }'|sort|uniq|./rhpl.sh
Noch ist nichts aktiv!

Hosts löschen

Will man vor dem Import aufräumen, kann man mit einem (nicht perfekten) Einzeiler erst einmal alle Hosts löschen:
while uci delete dhcp.@host[0]; do :;done
Die Schleife läuft einmal zu viel, deswegen gibt es einen Fehler. Aber alle Hosts sind gelöscht.

Host auslesen

Damit man die Liste der Werte Paar nicht komplette per Hand erstellen muss, kann man existierende Daten auslesen. Die entstandenen Dateien kann man bei Bedarf bearbeiten und anschließend importieren.

Static Leases auslesen

Um alle Hosts in eine Datei zu exportieren, braucht man eine formatierte Ausgabe
printf "$(uci get dhcp.@host[-1].name) $(uci get dhcp.@host[-1].ip) $(uci get dhcp.@host[-1].mac)\n"
Und ein Array mit den vorhandenen Indizes. Ein doppelter "Eierkuchen" liefert uns dies aus dem show Befehl.
echo $(uci show dhcp | grep -oE "host\[\d+\].ip"|grep -oE '\d+')
Eine simple Schleife liefert uns die Wertepaare als Ausgabe.
script="wrlp.sh"
cat <<'EOF' > $script
#!/bin/sh
for i in $(uci show dhcp | grep -oE "host\[\d+\].ip"|grep -oE '\d+'); do
  printf "$(uci get dhcp.@host[$i].name) $(uci get dhcp.@host[$i].ip) $(uci get dhcp.@host[$i].mac)\n"
done
EOF
chmod +x $script
Die schreibt man einfach in eine Datei.
./wrlp.sh >wpo.txt

Aktuelle Leases auslesen

Nachtrag 2021: Ich habe noch eine bessere Variante zum Auslesen der aktiven Leases gefunden, lasse aber den alten Code bewusst noch stehen.
cat /tmp/dhcp.leases| awk '$4 != "*" { print( $4,$3,$2 ) }'
Um einfach mal alle existierenden Leases auszulesen genügt ein Einzeiler. Allerdings ist das Ausgabeformat anders als im Beispiel oben: mac IP Host (Darstellung im Browser Host IP mac)
cat /tmp/dhcp.leases |grep -oE '([a-f0-9]{2}:){5}[a-f0-9]{2}\s[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}\s[A-Z,a-z,0-9,-,_]*'
Das Script macht ein match auf mac Adresse, IP Adresse und String (Hostname)

Ich habe noch eine vereinfachte Form "experimentell" gefunden (\s.*\s passt auf IP Adresse und Hostname - warum? Der Rest wird abgeschnitten).
Aus der Ausgabe kann man noch per Pipe mit grep die Zeilen mit unbekannten Hostnamen (*) entfernen und mit awk formatieren.

grep -oE '([a-f0-9]{2}:){5}[a-f0-9]{2}\s.*\s' /tmp/dhcp.leases|grep -v "*"|awk '{print $3,$2,$1}'

Finale

Das uci System arbeitet temporär, will man seine Änderungen fest machen, muss man diese speichern und das jeweilige System(dhcp) neu starten. 
Achtung: Scheinbar wird seit einiger Zeit nicht mehr dnsmasq sondern odhcpd für den DHCP Service verwendet. Offenbar genügt aber ein commit Befehl den restart & reload kann man sparen?
uci commit dhcp
/etc/init.d/dnsmasq restart
luci-reload

Die uci Schnittstelle ist auch über JSON erreichbar.
wiki.teltonika.lt

Ersatz für ssh-copy-id

Manchmal kommt es vor, das auf einem System das Tool ssh-copy-id nicht vorhanden ist. Dann gibt es zwar einfache Alternativen, aber wenn man nicht genau weiß worauf es ankommt, wird es schnell kompliziert. Ich mache hier raus mal einen kurzen Artikel, weil ich auch ein paar Besonderheiten der Shell gelernt habe. 
Für die Verwendung unter Windows habe ich ein Powershellscript auf GitHub abgelegt.

Die einzelnen Schritte

Was macht ssh-copy-id bzw. was muss man berücksichtigen?
Alles in allem geht es darum, den Public Key von System 1 (lokal) nach System 2 (remote) zu übertragen um sich zukünftig vom System 1 am System 2 vereinfachten anzumelden und Befehle auszuführen.
  • lokalen Public Key an das Remotesystem senden.
  • im Remotesystem schauen ob der Pfad .ssh schon existiert, 
    • wenn nicht: Pfad anlegen.
  • in diesem Pfad schauen ob schon eine Datei .ssh/id_rsa.pub existiert, 
    • wenn nicht: mit dem Public Key erzeugen.
    • wenn schon vorhanden: prüfen ob der public Key schon enthalten ist,
      • wenn nicht: Den Public Key hinzufügen.
Wer Hemmungen hat den Programmcode zu verwenden, kann die oben genannten Schritte auch manuell mit ls und nano (Public Key einfach mit copy&paste übertragen) abarbeiten.
 Schon die erste Aufgabe ist nicht trivial, hier wird mit der Pipe gearbeitet und in Abhängigkeit der Shell stehen dort unterschiedliche Subsysteme zur Verfügung. Variablen können nicht einfach transportiert werden.
Hinweise:

  • Ich habe die Codestücke zur Erklärung ohne Maskierung geschrieben. So funktionieren sie aus der Windows cmd Konsole bzw. direkt in der Shell. Wie weiter unten erläutert muss $ und " maskiert werden!
  • der letzte Befehl (cat bzw. echo) ist nur zum Test ob es funktioniert hat.
Am Einfachsten geht es in eine Datei.
cat .ssh/id_rsa.pub | ssh user@host "cat >kee.tmp;cat kee.tmp"
In Windows sind für Linux oft schädliche Zeilumbrüche (CR/LF) enthalten, dies Variante filtert die CR aus.
type .ssh\id_rsa.pub | ssh user@host "cat | tr -d '\r' >kee.tmp;cat kee.tmp"
Nach einigen Versuchen glaube ich mittlerweile, dass es mit diesem Konstrukt auch zuverlässig in eine Variable ($pub) kommt:
cat .ssh/id_rsa.pub | ssh user@host "pub=$(cat );echo $pub"
type .ssh\id_rsa.pub | ssh user@host "pub=$(cat | tr -d '\r');echo $pub"
Ob das Verzeichnis existiert ist schnell getestet, das ODER führt den Befehl danach nur bei false aus. Vorher wird mit dem umask Befehl die richtige Berechtigung für den neuen Pfad sichergestellt.
umask 077;test -d .ssh || mkdir .ssh
Auf ähnliche Art wird die Datei getestet und bei false einfach erzeugt:
test -f .ssh/authorized_keys || cat kee.tmp >.ssh/authorized_keys
test -f .ssh/authorized_keys || echo $pub >.ssh/authorized_keys
Danach wird der Inhalt der Datei mit grep auf Vorhandensein des Key geprüft und bei false wird der public Key angehängt. Bei der ersten Variante wird die tmp datei gelöscht.
Da Leerzeichen im Public Key sind, braucht grep den Suchstring in "ein zwei drei"!
grep -q "$(cat kee.tmp)" .ssh/authorized_keys  || cat kee.tmp >>.ssh/authorized_keys;rm kee.tmp
grep -q "$(echo $pub)" .ssh/authorized_keys  || echo $pub >>.ssh/authorized_keys

Die Einzeiler

Da die Dateinamen im Code öfters gebraucht werden, definiere ich sie am Anfang als Variable. Jetzt steht noch zusätzlich die Aufgabe den Einzeiler so zu schreiben, dass alle Sonderzeichen von der Shell richtig behandelt werden. Die folgenden Varianten haben als Ziel ein Linux System! Startet man den Befehl aus der Linux Shell muss mit dem Backslash \ maskiert werden:
cat .ssh/id_rsa.pub | ssh user@host "p='.ssh';akey=\$p'/authorized_keys'; okey='kee.tmp'; cat >\$okey; umask 077; test -d \$p || mkdir \$p; test -f \$akey || cat \$okey >\$akey; grep -q \"\$(cat \$okey)\" \$akey  || cat \$okey >>\$akey;rm \$okey"
cat .ssh/id_rsa.pub | ssh user@host "p='.ssh';akey=\$p'/authorized_keys'; pub=\$(cat ); umask 077; test -d \$p || mkdir \$p; test -f \$akey || echo \$pub >\$akey; grep -q \"\$(echo \$pub)\" \$akey || echo \$pub >>\$akey"
Startet man man den Einzeiler aus der cmd von Windows, müssen lediglich die " verdoppelt werden:
type .ssh\id_rsa.pub | ssh user@host "p='.ssh';akey=$p'/authorized_keys'; pub=$(cat | tr -d '\r'); umask 077; test -d $p || mkdir $p; test -f $akey || echo $pub >$akey; grep -q ""$(echo $pub)"" $akey || echo $pub >>$akey"
Baut man den Befehl in Powershell zusammen muss mit dem Backtick ` maskiert werden.

Der Pfad .ssh gilt für die meistens Standard Systeme mit Benutzeranmeldung. Bei vielen Spezialsystemen (Zielsystem) muss der Pfad angepasst werden!
z.B. /etc/dropbear bei OpenWrt.
cat .ssh/id_rsa.pub | ssh user@host "p='/etc/dropbear';akey=\$p'/authorized_keys'; pub=\$(cat ); umask 077; test -d \$p || mkdir \$p; test -f \$akey || echo \$pub >\$akey; grep -q \"\$(echo \$pub)\" \$akey || echo \$pub >>\$akey"
Die nächste Variante hat als Ziel ein Windows System!
ziel='user@host';pkey=$(cat ~/.ssh/id_rsa.pub);ssh $ziel "findstr /c:\"$pkey\" .ssh\authorized_keys ||mkdir .ssh ||echo $pkey >>.ssh\authorized_keys"

Maskierung von Sonderzeichen
Windows: Nur " muss verdoppelt werden -> ""
Linux: $ und " müssen mit \ maskiert werden -> \$ \"
Powershell: $ und " müssen mit ` maskiert werden -> `$ `"