fix(station): deploy.sh prueft Bildsatz 'ohne-wlan' auf Zugangsdaten
release.sh prueft sein fertiges Archiv binaer gegen die Werte aus config.local.cmake und bricht ab, wenn es etwas findet. deploy.sh signierte dagegen ungeprueft, was in build-nocreds/ lag - und genau dieser Satz ist die sichere Vorgabe, die im Automatikmodus an fremde Lampen in Funkreichweite geht. Der Weg dorthin ist Bedienfehler, nicht Programmfehler: ein von Hand konfiguriertes build-nocreds/<ziel>, ein gesetztes NINLIGHT_NOCREDS_BUILD_REL=build. Das Ergebnis waere, dass das Panel "ohne WLAN" anzeigt, waehrend das WLAN-Passwort des Bedieners unwiderruflich in fremde Lampen wandert. Derselbe Pruefkoerper wie in release.sh, angewandt auf $STAGE/migrate-nocreds und VOR dem Uebertragen - bei einem Treffer geht kein Byte an den Pi. Szenario 33 stellt den undichten Abzug nach (mit Gegenprobe, dass der Abzug die Zugangsdaten wirklich traegt), Szenario 34 grenzt ab, dass migrate-wlan sie weiterhin tragen darf. Mutationsproben: Pruefblock entfernt = 6 Zusagen rot (die Zugangsdaten landen beim Ziel); Pruefung auf den ganzen STAGE ausgeweitet = 4 Zusagen rot.
This commit is contained in:
@@ -247,6 +247,61 @@ with open(path, 'wb') as fh:
|
||||
" "$path"
|
||||
}
|
||||
|
||||
# Wegwerf-Zugangsdaten fuer die Pruefung des Bildsatzes "ohne-wlan" - niemals die
|
||||
# echten aus dem Repo. Dieselbe Rolle wie SECRET_* in tools/tests/test_release.sh.
|
||||
SECRET_SSID="TestWLAN-9c2e-geheim"
|
||||
SECRET_PASS="testpasswort-9c2e"
|
||||
SECRET_TOKEN="0badc0de9c2e0badc0de9c2e"
|
||||
|
||||
# Legt config.local.cmake im Sandbox-Repo an - genau die Datei, gegen die deploy.sh den
|
||||
# Bildsatz "ohne-wlan" prueft.
|
||||
write_local_cmake() {
|
||||
local sandbox=$1
|
||||
cat >"$sandbox/config.local.cmake" <<EOF
|
||||
set(NINLIGHT_WIFI_SSID "$SECRET_SSID")
|
||||
set(NINLIGHT_WIFI_PASSWORD "$SECRET_PASS")
|
||||
set(NINLIGHT_TOKEN "$SECRET_TOKEN")
|
||||
EOF
|
||||
}
|
||||
|
||||
# Wie make_gz_file, aber mit einkompilierten WLAN-Zugangsdaten - das, was ein Abzug aus
|
||||
# einem falsch konfigurierten build-nocreds/ (oder aus NINLIGHT_NOCREDS_BUILD_REL=build)
|
||||
# wirklich enthielte. Der "Compiler" dieser Suite, analog zum Docker-Stub in
|
||||
# test_release.sh: nur weil er die Werte tatsaechlich einbaut, sagt ein bestandener
|
||||
# Pruefdurchlauf ueberhaupt etwas.
|
||||
make_gz_file_with_creds() {
|
||||
local path=$1 fill=${2:-A}
|
||||
SSID="$SECRET_SSID" PASS="$SECRET_PASS" TOKEN="$SECRET_TOKEN" python3 -c "
|
||||
import os, sys
|
||||
path, fill = sys.argv[1], sys.argv[2].encode()
|
||||
creds = (os.environ['SSID'] + '\\0' + os.environ['PASS'] + '\\0'
|
||||
+ os.environ['TOKEN'] + '\\0').encode()
|
||||
data = bytearray((fill * 2048)[:2048])
|
||||
data[64:64 + len(creds)] = creds
|
||||
data[-512:-508] = b'\\x00\\x00\\x00\\x00' # nie zufaellig wie 'NINL' aussehen
|
||||
with open(path, 'wb') as fh:
|
||||
fh.write(bytes(data))
|
||||
" "$path" "$fill"
|
||||
}
|
||||
|
||||
# Sucht eine Zeichenkette BINAER in jeder Datei unterhalb eines Verzeichnisses.
|
||||
# Rueckgabe 0 = gefunden. Wie contains_secrets() in test_release.sh.
|
||||
tree_contains() {
|
||||
local dir=$1 needle=$2
|
||||
NEEDLE="$needle" python3 - "$dir" <<'PY'
|
||||
import os
|
||||
import sys
|
||||
|
||||
needle = os.environ["NEEDLE"].encode()
|
||||
for root, _dirs, files in os.walk(sys.argv[1]):
|
||||
for fn in files:
|
||||
with open(os.path.join(root, fn), "rb") as fh:
|
||||
if needle in fh.read():
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
echo "=== Szenario 1: Image besteht die Trailer-Pruefung nicht -> nichts uebertragen ==="
|
||||
SANDBOX="$TMPROOT/s1"; STUBBIN="$TMPROOT/bin1"; LOG="$TMPROOT/s1.log"; SANDBOX_STATE="$TMPROOT/s1-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
@@ -1271,6 +1326,99 @@ grep -Fq "systemctl enable ninlight-poweroff" "$LOG" \
|
||||
[ ! -e /tmp/ninlight-poweroff.service ] && [ ! -e /tmp/ninlight-reboot.service ] \
|
||||
&& pass "die Ablage wurde aufgeraeumt" || fail "Unit-Dateien blieben unter /tmp liegen"
|
||||
|
||||
echo
|
||||
echo "=== Szenario 33 (Abschlussreview, I3): Zugangsdaten im Bildsatz 'ohne-wlan' brechen den Deploy ab ==="
|
||||
# Der Satz "ohne-wlan" ist die SICHERE VORGABE - im Automatikmodus geht er an jede alte
|
||||
# Klyqa-Lampe in Funkreichweite, auch an fremde, und das Flashen ist nicht umkehrbar.
|
||||
# release.sh prueft sein Archiv binaer gegen config.local.cmake; deploy.sh signierte
|
||||
# bisher ungeprueft, was in build-nocreds/ lag. Ein von Hand konfiguriertes
|
||||
# build-nocreds/<ziel> genuegt, damit das Panel "ohne WLAN" anzeigt, waehrend das
|
||||
# WLAN-Passwort des Bedieners unwiderruflich in fremde Lampen wandert.
|
||||
SANDBOX="$TMPROOT/s33"; STUBBIN="$TMPROOT/bin33"; LOG="$TMPROOT/s33.log"; SANDBOX_STATE="$TMPROOT/s33-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
setup_stubbin "$STUBBIN" "$LOG"
|
||||
write_local_cmake "$SANDBOX"
|
||||
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
|
||||
make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" W
|
||||
echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img"
|
||||
# Der undichte Abzug: enthaelt SSID, Passwort und Token.
|
||||
make_gz_file_with_creds "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N
|
||||
# GEGENPROBE (ohne sie waere dieses Szenario wertlos): der "Compiler" dieser Suite muss
|
||||
# die Zugangsdaten wirklich einbauen. Genau diese Gegenprobe gibt es in test_release.sh
|
||||
# fuer den Docker-Stub - dort hat sie belegt, dass Fall 5 nicht nur deshalb gruen ist,
|
||||
# weil nie jemand etwas einbaut.
|
||||
if tree_contains "$SANDBOX/build-nocreds" "$SECRET_PASS"; then
|
||||
pass "Gegenprobe: der Abzug in build-nocreds/ enthaelt das WLAN-Passwort wirklich"
|
||||
else
|
||||
fail "Gegenprobe fehlgeschlagen - der Abzug enthaelt gar keine Zugangsdaten, Szenario 33 waere wertlos"
|
||||
fi
|
||||
# Der Pi hat schon einen (sauberen) Bestand aus einem frueheren Deploy - er darf durch
|
||||
# den Abbruch weder ersetzt noch geloescht werden.
|
||||
mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds"
|
||||
echo "alter-sauberer-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img"
|
||||
: >"$LOG"
|
||||
set +e
|
||||
OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0 - der undichte Bildsatz waere ausgeliefert worden: $OUT"
|
||||
echo "$OUT" | grep -q "Zugangsdaten" \
|
||||
&& pass "Fehlermeldung nennt die gefundenen Zugangsdaten" \
|
||||
|| fail "keine Fehlermeldung zu Zugangsdaten: $OUT"
|
||||
echo "$OUT" | grep -q "NINLIGHT_WIFI_PASSWORD" \
|
||||
&& pass "Fehlermeldung nennt den betroffenen Schluessel" \
|
||||
|| fail "Fehlermeldung nennt den Schluessel nicht: $OUT"
|
||||
if ! grep -q "STUB ssh\|STUB rsync\|STUB scp" "$LOG"; then
|
||||
pass "kein ssh/rsync/scp aufgerufen - nichts wurde uebertragen"
|
||||
else
|
||||
fail "es wurde trotz gefundener Zugangsdaten uebertragen: $(cat "$LOG")"
|
||||
fi
|
||||
[ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img")" = "alter-sauberer-abzug" ] \
|
||||
&& pass "der Bestand auf dem Pi blieb unangetastet" \
|
||||
|| fail "der Bestand auf dem Pi wurde veraendert"
|
||||
if ! tree_contains "$SANDBOX_STATE" "$SECRET_PASS"; then
|
||||
pass "kein Zugangsdatum hat es in die Sandbox-Zielablage geschafft"
|
||||
else
|
||||
fail "Zugangsdaten sind trotz Abbruch beim Ziel gelandet"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Szenario 34 (Abgrenzung zu 33): ein sauberer Satz 'ohne-wlan' geht raus, migrate-wlan DARF die Zugangsdaten tragen ==="
|
||||
# Die Pruefung muss genau einen Satz betreffen. migrate-wlan/ enthaelt die Zugangsdaten
|
||||
# absichtlich - das ist sein ganzer Zweck ("Lampe haengt sofort im eigenen WLAN"). Eine
|
||||
# Pruefung, die auch ihn abweist, waere unbenutzbar und wuerde nach kurzer Zeit
|
||||
# abgeschaltet.
|
||||
SANDBOX="$TMPROOT/s34"; STUBBIN="$TMPROOT/bin34"; LOG="$TMPROOT/s34.log"; SANDBOX_STATE="$TMPROOT/s34-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
setup_stubbin "$STUBBIN" "$LOG"
|
||||
write_local_cmake "$SANDBOX"
|
||||
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
|
||||
# build/ MIT Zugangsdaten (wie im echten Betrieb), build-nocreds/ ohne.
|
||||
make_gz_file_with_creds "$SANDBOX/build/foo/ninlight_foo_gz.img" W
|
||||
echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img"
|
||||
make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N
|
||||
: >"$LOG"
|
||||
set +e
|
||||
OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC - die Pruefung weist einen sauberen Satz ab: $OUT"
|
||||
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" ] \
|
||||
&& pass "der saubere Satz 'ohne-wlan' kam an" || fail "der saubere Satz 'ohne-wlan' fehlt: $OUT"
|
||||
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ] \
|
||||
&& pass "migrate-wlan kam trotz einkompilierter Zugangsdaten an" \
|
||||
|| fail "migrate-wlan wurde faelschlich mit abgewiesen: $OUT"
|
||||
if tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" "$SECRET_PASS"; then
|
||||
pass "Gegenprobe: migrate-wlan traegt die Zugangsdaten wirklich - die Pruefung haette anschlagen KOENNEN"
|
||||
else
|
||||
fail "Gegenprobe fehlgeschlagen - migrate-wlan traegt gar keine Zugangsdaten, die Abgrenzung waere wertlos"
|
||||
fi
|
||||
if ! tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds" "$SECRET_PASS"; then
|
||||
pass "und der Satz 'ohne-wlan' auf dem Pi ist frei davon"
|
||||
else
|
||||
fail "der Satz 'ohne-wlan' auf dem Pi enthaelt Zugangsdaten"
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "Alle Tests bestanden."
|
||||
|
||||
Reference in New Issue
Block a user