fix(station): Zugangsdatenpruefung entpackt die Abbilder und prueft beide Rettungssaetze
Die Pruefung des Bildsatzes "ohne-wlan" suchte den Klartext der Werte aus config.local.cmake in der Abbilddatei, wie sie auf der Platte liegt. Der Rumpf eines Abbilds ist aber gzip (ab Byte 56, die letzten 512 Byte sind Trailer bzw. Signatur) - im komprimierten Strom steht der Klartext nicht. Derselbe Pruefkoerper meldete deshalb auch fuer den Satz aus build/, der die Zugangsdaten nachweislich traegt, "sauber". Er haette nie Alarm geschlagen, egal was im Bildsatz lag. Jetzt wird jedes Abbild entpackt und erst dann durchsucht - im Rumpf wie in den Rohbytes, denn Kopf, Trailer und Beidateien wie manifest.json sind unkomprimiert. Ein Abbild, das sich nicht entpacken laesst, ist ein Fehler und bricht den Deploy ab, statt ungeprueft durchzugehen; das gilt auch fuer einen abgeschnittenen gzip-Strom, den decompressobj sonst stillschweigend annimmt. Zweitens hing der ganze Block an der Existenz von migrate-nocreds/. Der Rettungssatz rescue-nocreds/ lief ungeprueft durch, obwohl er der gefaehrlichere von beiden ist: er geht ohne Signatur an die Lampe. Geprueft werden jetzt beide Saetze "ohne-wlan"; migrate-wlan/ und rescue-wlan/ bleiben ungeprueft, die sollen die Daten enthalten. Die Meldung nennt jetzt Zahl der Abbilder und der Saetze statt der Zahl der Suchbegriffe. Gefundene Werte werden weiterhin nie ausgegeben, nur ihr Name und die Datei. Die Testattrappen haben jetzt den echten Aufbau (56 Byte Kopf, gzip-Rumpf, 512 Byte Trailer) und tragen die Zugangsdaten im komprimierten Rumpf; die Gegenproben-Hilfe tree_contains entpackt mit. Neu sind Szenarien fuer Zugangsdaten im Rettungssatz, die Abgrenzung zu rescue-wlan, ein nicht entpackbares Abbild und den Meldungstext. Szenario 33, 39, 41 und 42 sind gegen den alten Pruefkoerper rot.
This commit is contained in:
+81
-12
@@ -329,46 +329,115 @@ fi
|
||||
# Bedieners unwiderruflich in fremde Lampen wandert - der teure, nicht rueckholbare
|
||||
# Fehler. Lieber ein abgebrochener Deploy.
|
||||
#
|
||||
# Geprueft wird VOR dem Uebertragen, es ist also noch nichts beim Pi angekommen. Nur
|
||||
# dieser eine Satz: migrate-wlan/ SOLL die Zugangsdaten enthalten, das ist sein Zweck.
|
||||
# Der Pruefkoerper ist bis auf die Meldung derselbe wie in tools/release.sh.
|
||||
if [ -d "$STAGE/migrate-nocreds" ]; then
|
||||
echo "== Bildsatz 'ohne-wlan' auf Zugangsdaten pruefen =="
|
||||
# Geprueft wird VOR dem Uebertragen, es ist also noch nichts beim Pi angekommen.
|
||||
# Geprueft werden BEIDE Saetze "ohne-wlan" - der Migrationssatz migrate-nocreds/ UND der
|
||||
# Rettungssatz rescue-nocreds/. Der Rettungssatz ist dabei der gefaehrlichere von beiden:
|
||||
# er geht ohne Signatur an die Lampe. Frueher hing dieser ganze Block allein an
|
||||
# migrate-nocreds/, der Rettungssatz lief ungeprueft durch.
|
||||
# migrate-wlan/ und rescue-wlan/ bleiben ungeprueft: die SOLLEN die Zugangsdaten
|
||||
# enthalten, das ist ihr Zweck.
|
||||
#
|
||||
# Gesucht wird im ENTPACKTEN Rumpf, nicht in der Datei, wie sie auf der Platte liegt:
|
||||
# jedes Abbild (_ota.img wie die signierten _prod.img/_dev.img) ist 56 Byte Kopf, dann
|
||||
# ein gzip-Strom, dann 512 Byte Trailer bzw. Signatur. Im komprimierten Strom steht der
|
||||
# Klartext des Passworts nicht - eine Suche im Rohbyteinhalt meldete deshalb IMMER
|
||||
# "sauber", auch fuer den Satz aus build/, der die Zugangsdaten nachweislich traegt. Die
|
||||
# Pruefung hat bis hierhin also nie irgendetwas geprueft.
|
||||
#
|
||||
# Ein Abbild, das sich nicht entpacken laesst, ist ein FEHLER und bricht ab - nicht
|
||||
# "dann eben ungeprueft durchlassen". Genau diese Nachsicht waere der Weg, auf dem das
|
||||
# Passwort am Ende doch hinausgeht.
|
||||
#
|
||||
# Der Pruefkoerper in tools/release.sh ist verwandt, aber nicht mehr identisch: dort
|
||||
# wird ein fertiges Archiv aus lauter verschiedenen Dateiarten geprueft, hier ein
|
||||
# Bildsatz aus Firmware-Abbildern.
|
||||
# Als if-Zweige und nicht als "[ -d … ] && CHECK_SETS+=(…)": unter "set -e" waere eine
|
||||
# solche Zeile, wenn das Verzeichnis fehlt, ein fehlgeschlagenes letztes Kommando und
|
||||
# risse den Deploy ab.
|
||||
declare -a CHECK_SETS=()
|
||||
if [ -d "$STAGE/migrate-nocreds" ]; then CHECK_SETS+=(migrate-nocreds); fi
|
||||
if [ -d "$STAGE/rescue-nocreds" ]; then CHECK_SETS+=(rescue-nocreds); fi
|
||||
if [ ${#CHECK_SETS[@]} -gt 0 ]; then
|
||||
echo "== Bildsaetze 'ohne-wlan' auf Zugangsdaten pruefen =="
|
||||
LOCAL="$ROOT/config.local.cmake"
|
||||
if [ -f "$LOCAL" ]; then
|
||||
python3 - "$STAGE/migrate-nocreds" "$LOCAL" <<'CHECKPY' || exit 1
|
||||
python3 - "$LOCAL" "$STAGE" "${CHECK_SETS[@]}" <<'CHECKPY' || exit 1
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
stage, local = sys.argv[1], sys.argv[2]
|
||||
local, stage, sets = sys.argv[1], sys.argv[2], sys.argv[3:]
|
||||
text = open(local, encoding="utf-8", errors="replace").read()
|
||||
# Nur die drei Werte, die wirklich geheim sind. NINLIGHT_AP_PASSWORD und der
|
||||
# Vorgabetoken stehen oeffentlich im Repo (README, Bruno) - sie hier mitzupruefen
|
||||
# haette nur falsche Alarme erzeugt.
|
||||
SECRETS = ("NINLIGHT_WIFI_SSID", "NINLIGHT_WIFI_PASSWORD", "NINLIGHT_TOKEN")
|
||||
PUBLIC = {"", "ninlight", "aabbccddeeff0011223344", "ninlight-recovery", "change-me-please"}
|
||||
# Aufbau eines Abbilds: 56 Byte Kopf (vom wm_tool erzeugt), dann der gzip-Strom mit der
|
||||
# eigentlichen Firmware, dann 512 Byte ninlight-Trailer bzw. RSA-Signatur.
|
||||
HEADER_LEN = 56
|
||||
TRAILER_LEN = 512
|
||||
needles = []
|
||||
for name in SECRETS:
|
||||
m = re.search(r'^\s*set\s*\(\s*%s\s+"([^"]*)"' % name, text, re.M)
|
||||
if m and m.group(1) not in PUBLIC:
|
||||
needles.append((name, m.group(1).encode()))
|
||||
|
||||
|
||||
def unpack(raw):
|
||||
"""Entpackt den gzip-Rumpf eines Abbilds. Wirft bei allem, was nicht passt."""
|
||||
if len(raw) <= HEADER_LEN + TRAILER_LEN:
|
||||
raise ValueError("Datei ist kuerzer als Kopf + Trailer")
|
||||
dec = zlib.decompressobj(zlib.MAX_WBITS | 16)
|
||||
body = dec.decompress(raw[HEADER_LEN:-TRAILER_LEN])
|
||||
# Ohne diese Abfrage waere ein ABGESCHNITTENER gzip-Strom kein Fehler: decompressobj
|
||||
# liefert stillschweigend so viel, wie es lesen konnte. Der ungelesene Rest bliebe
|
||||
# ungeprueft - und genau dort koennte das Passwort stehen.
|
||||
if not dec.eof:
|
||||
raise ValueError("gzip-Strom ist unvollstaendig")
|
||||
return body
|
||||
|
||||
|
||||
hits = []
|
||||
for root, _dirs, files in os.walk(stage):
|
||||
broken = []
|
||||
images = 0
|
||||
for image_set in sets:
|
||||
root_dir = os.path.join(stage, image_set)
|
||||
for root, _dirs, files in os.walk(root_dir):
|
||||
for fn in sorted(files):
|
||||
path = os.path.join(root, fn)
|
||||
rel = os.path.relpath(path, stage)
|
||||
with open(path, "rb") as fh:
|
||||
data = fh.read()
|
||||
raw = fh.read()
|
||||
# Durchsucht werden beide Sichten: der entpackte Rumpf (dort steht die
|
||||
# Firmware) UND die Rohbytes (Kopf und Trailer werden nicht komprimiert,
|
||||
# und Beidateien wie manifest.json gibt es nur roh).
|
||||
views = [raw]
|
||||
if fn.endswith(".img"):
|
||||
images += 1
|
||||
try:
|
||||
views.append(unpack(raw))
|
||||
except Exception as exc:
|
||||
broken.append("%s (%s)" % (rel, exc))
|
||||
continue
|
||||
for name, needle in needles:
|
||||
if needle in data:
|
||||
hits.append("%s in %s" % (name, os.path.relpath(path, stage)))
|
||||
if any(needle in view for view in views):
|
||||
# NUR der Name des Wertes und die Datei - niemals der Wert selbst.
|
||||
hits.append("%s in %s" % (name, rel))
|
||||
if broken:
|
||||
sys.stderr.write(
|
||||
"Fehler: Abbild im Bildsatz 'ohne-wlan' laesst sich nicht entpacken, also auch "
|
||||
"nicht auf Zugangsdaten pruefen - nichts uebertragen:\n "
|
||||
+ "\n ".join(broken) + "\n")
|
||||
sys.exit(1)
|
||||
if hits:
|
||||
sys.stderr.write(
|
||||
"Fehler: Zugangsdaten aus config.local.cmake im Bildsatz 'ohne-wlan' gefunden, "
|
||||
"nichts uebertragen:\n " + "\n ".join(hits) + "\n")
|
||||
sys.exit(1)
|
||||
print(" sauber (%d geprueft)" % len(needles))
|
||||
print(" sauber (%d Abbilder in %d Saetzen geprueft: %s)"
|
||||
% (images, len(sets), ", ".join(sets)))
|
||||
CHECKPY
|
||||
else
|
||||
echo " uebersprungen - $LOCAL nicht vorhanden" >&2
|
||||
|
||||
@@ -217,18 +217,63 @@ if with_dev:
|
||||
PY
|
||||
}
|
||||
|
||||
# Erzeugt eine gueltige rohe _gz.img-Attrappe: >= 1024 Byte, durch 4 teilbar, ohne
|
||||
# NINL-Trailer - genau das, was nl_sign.py als Eingabe akzeptiert.
|
||||
make_gz_file() {
|
||||
local path=$1 fill=${2:-A}
|
||||
python3 -c "
|
||||
# Baut eine Attrappe mit dem AUFBAU EINES ECHTEN ABBILDS: 56 Byte Kopf, dann ein
|
||||
# gzip-Strom mit dem Rumpf, dann ggf. 512 Byte Trailer. Genau diesen Aufbau setzt die
|
||||
# Zugangsdatenpruefung in deploy.sh voraus - eine unkomprimierte Attrappe wuerde die
|
||||
# Pruefung an einem Abbild vorfuehren, das es so nie gibt, und damit nichts belegen.
|
||||
# $1 = Pfad, $2 = Fuellzeichen (auch die 56 Kopfbytes, damit Szenarien den Quellbaum
|
||||
# am Dateianfang wiedererkennen), $3 = "creds", wenn die Zugangsdaten in den
|
||||
# KOMPRIMIERTEN Rumpf eingebaut werden sollen, $4 = "ota" fuer den NINL-Trailer.
|
||||
# Ergebnis ohne Trailer ist eine gueltige Eingabe fuer nl_sign.py: >= 1024 Byte, durch 4
|
||||
# teilbar, kein NINL im Signaturblock.
|
||||
make_image_file() {
|
||||
local path=$1 fill=${2:-A} with_creds=${3:-} kind=${4:-gz}
|
||||
SSID="$SECRET_SSID" PASS="$SECRET_PASS" TOKEN="$SECRET_TOKEN" \
|
||||
python3 - "$path" "$fill" "$with_creds" "$kind" <<'PY'
|
||||
import gzip
|
||||
import os
|
||||
import sys
|
||||
path, fill = sys.argv[1], sys.argv[2].encode()
|
||||
data = bytearray((fill * 2048)[:2048])
|
||||
data[-512:-508] = b'\\x00\\x00\\x00\\x00' # nie zufaellig wie 'NINL' aussehen
|
||||
with open(path, 'wb') as fh:
|
||||
|
||||
path, fill, with_creds, kind = sys.argv[1], sys.argv[2].encode(), sys.argv[3], sys.argv[4]
|
||||
|
||||
body = bytearray((fill * 4096)[:4096])
|
||||
if with_creds == "creds":
|
||||
creds = (os.environ["SSID"] + "\0" + os.environ["PASS"] + "\0"
|
||||
+ os.environ["TOKEN"] + "\0").encode()
|
||||
body[64:64 + len(creds)] = creds
|
||||
|
||||
# mtime=0: sonst haengt die Dateigroesse an der Uhrzeit und Szenarien, die Groessen
|
||||
# vergleichen, waeren launisch.
|
||||
packed = gzip.compress(bytes(body), mtime=0)
|
||||
data = bytearray((fill * 56)[:56] + packed)
|
||||
# Auffuellen auf die Mindestlaenge und auf ein Vielfaches von 4 - der Rumpf eines echten
|
||||
# Abbilds ist deutlich groesser als der komprimierte Testrumpf. Die Nullen hinter dem
|
||||
# gzip-Strom stoeren das Entpacken nicht.
|
||||
while len(data) < 1024 or len(data) % 4 != 0:
|
||||
data.append(0)
|
||||
if kind == "ota":
|
||||
trailer = bytearray(b"\xff" * 512)
|
||||
trailer[0:4] = b"NINL"
|
||||
trailer[4] = 1
|
||||
data += trailer
|
||||
with open(path, "wb") as fh:
|
||||
fh.write(bytes(data))
|
||||
" "$path" "$fill"
|
||||
PY
|
||||
}
|
||||
|
||||
# Erzeugt eine gueltige rohe _gz.img-Attrappe (ohne Trailer, ohne Zugangsdaten).
|
||||
make_gz_file() {
|
||||
make_image_file "$1" "${2:-A}" "" gz
|
||||
}
|
||||
|
||||
# Erzeugt eine _ota.img-Attrappe im echten Aufbau, ohne Zugangsdaten.
|
||||
make_ota_image() {
|
||||
make_image_file "$1" "${2:-A}" "" ota
|
||||
}
|
||||
|
||||
# Dasselbe, aber mit den Zugangsdaten im komprimierten Rumpf.
|
||||
make_ota_image_with_creds() {
|
||||
make_image_file "$1" "${2:-A}" creds ota
|
||||
}
|
||||
|
||||
# Erzeugt eine _ota.img-Attrappe mit ANDEREM Inhalt als das zugehoerige _gz.img und
|
||||
@@ -270,33 +315,36 @@ EOF
|
||||
# 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"
|
||||
make_image_file "$1" "${2:-A}" creds gz
|
||||
}
|
||||
|
||||
# Sucht eine Zeichenkette BINAER in jeder Datei unterhalb eines Verzeichnisses.
|
||||
# Rueckgabe 0 = gefunden. Wie contains_secrets() in test_release.sh.
|
||||
# Sucht eine Zeichenkette in jeder Datei unterhalb eines Verzeichnisses - roh UND im
|
||||
# ENTPACKTEN Rumpf jedes .img (56 Byte Kopf, gzip, 512 Byte Trailer/Signatur).
|
||||
# Rueckgabe 0 = gefunden. Wie contains_secrets() in test_release.sh, nur dass die
|
||||
# Gegenproben seit den echt aufgebauten Attrappen sonst nichts mehr faenden: im
|
||||
# komprimierten Strom steht der Klartext nicht. Genau das war der Fehler in deploy.sh.
|
||||
tree_contains() {
|
||||
local dir=$1 needle=$2
|
||||
NEEDLE="$needle" python3 - "$dir" <<'PY'
|
||||
import os
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
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():
|
||||
raw = fh.read()
|
||||
if needle in raw:
|
||||
sys.exit(0)
|
||||
if not fn.endswith(".img") or len(raw) <= 56 + 512:
|
||||
continue
|
||||
for body in (raw[56:-512], raw[56:]):
|
||||
try:
|
||||
clear = zlib.decompressobj(zlib.MAX_WBITS | 16).decompress(body)
|
||||
except Exception:
|
||||
continue
|
||||
if needle in clear:
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
PY
|
||||
@@ -1559,6 +1607,160 @@ else
|
||||
fail "das neue Rettungsimage fehlt: $OUT"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Szenario 39: Zugangsdaten im RETTUNGSSATZ 'ohne-wlan' brechen den Deploy ab ==="
|
||||
# Der Rettungssatz ist der gefaehrlichere der beiden Saetze "ohne-wlan": er geht ohne
|
||||
# Signatur an die Lampe. Trotzdem hing die Zugangsdatenpruefung frueher allein an
|
||||
# migrate-nocreds/ - rescue-nocreds/ lief ungeprueft durch. Hier ohne Schluessel, damit
|
||||
# es NUR um den Rettungssatz geht: es entsteht gar kein migrate-nocreds/.
|
||||
SANDBOX="$TMPROOT/s39"; STUBBIN="$TMPROOT/bin39"; LOG="$TMPROOT/s39.log"; SANDBOX_STATE="$TMPROOT/s39-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
setup_stubbin "$STUBBIN" "$LOG"
|
||||
write_local_cmake "$SANDBOX"
|
||||
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
|
||||
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
|
||||
# Der undichte Rettungsabzug: die Zugangsdaten stecken im KOMPRIMIERTEN Rumpf.
|
||||
make_ota_image_with_creds "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" N
|
||||
# GEGENPROBE: ohne sie waere dieses Szenario wertlos - der "Compiler" dieser Suite muss
|
||||
# die Zugangsdaten wirklich einbauen.
|
||||
if tree_contains "$SANDBOX/build-nocreds" "$SECRET_PASS"; then
|
||||
pass "Gegenprobe: der Rettungsabzug enthaelt das WLAN-Passwort wirklich (im gzip-Rumpf)"
|
||||
else
|
||||
fail "Gegenprobe fehlgeschlagen - der Rettungsabzug enthaelt gar keine Zugangsdaten, Szenario 39 waere wertlos"
|
||||
fi
|
||||
# Und die Gegenprobe zur Gegenprobe: ROH ist nichts davon zu finden. Genau deshalb hat
|
||||
# die alte Pruefung nie etwas gemeldet.
|
||||
if grep -q "$SECRET_PASS" "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" 2>/dev/null; then
|
||||
fail "das Passwort steht im Klartext in der Datei - die Attrappe ist nicht komprimiert und belegt nichts"
|
||||
else
|
||||
pass "Gegenprobe: roh ist im Abbild nichts zu finden - nur entpackt"
|
||||
fi
|
||||
mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds"
|
||||
echo "alter-sauberer-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds/ninlight_foo_ota.img"
|
||||
: >"$LOG"
|
||||
set +e
|
||||
OUT=$(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 Rettungssatz waere ausgeliefert worden: $OUT"
|
||||
echo "$OUT" | grep -q "NINLIGHT_WIFI_PASSWORD" \
|
||||
&& pass "Fehlermeldung nennt den betroffenen Schluessel" \
|
||||
|| fail "Fehlermeldung nennt den Schluessel nicht: $OUT"
|
||||
echo "$OUT" | grep -q "ninlight_foo_ota.img" \
|
||||
&& pass "Fehlermeldung nennt die betroffene Datei" \
|
||||
|| fail "Fehlermeldung nennt die Datei nicht: $OUT"
|
||||
if echo "$OUT" | grep -q "$SECRET_PASS"; then
|
||||
fail "die Meldung gibt den WERT aus - sie darf nur den Namen nennen"
|
||||
else
|
||||
pass "die Meldung nennt nur den Namen, nie den Wert"
|
||||
fi
|
||||
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/rescue-nocreds/ninlight_foo_ota.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 40 (Abgrenzung zu 39): rescue-wlan DARF die Zugangsdaten tragen ==="
|
||||
# Dieselbe Abgrenzung wie Szenario 34 fuer die Migration: der Satz 'mit-wlan' SOLL die
|
||||
# Zugangsdaten enthalten. Eine Pruefung, die auch ihn abweist, waere unbenutzbar.
|
||||
SANDBOX="$TMPROOT/s40"; STUBBIN="$TMPROOT/bin40"; LOG="$TMPROOT/s40.log"; SANDBOX_STATE="$TMPROOT/s40-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
setup_stubbin "$STUBBIN" "$LOG"
|
||||
write_local_cmake "$SANDBOX"
|
||||
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
|
||||
make_ota_image_with_creds "$SANDBOX/build/foo/ninlight_foo_ota.img" W
|
||||
make_ota_image "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" N
|
||||
: >"$LOG"
|
||||
set +e
|
||||
OUT=$(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 Rettungssatz ab: $OUT"
|
||||
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/rescue-wlan/ninlight_foo_ota.img" ] \
|
||||
&& pass "rescue-wlan kam trotz einkompilierter Zugangsdaten an" \
|
||||
|| fail "rescue-wlan wurde faelschlich mit abgewiesen: $OUT"
|
||||
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds/ninlight_foo_ota.img" ] \
|
||||
&& pass "der saubere Rettungssatz 'ohne-wlan' kam an" \
|
||||
|| fail "der saubere Rettungssatz 'ohne-wlan' fehlt: $OUT"
|
||||
if tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/rescue-wlan" "$SECRET_PASS"; then
|
||||
pass "Gegenprobe: rescue-wlan traegt die Zugangsdaten wirklich - die Pruefung haette anschlagen KOENNEN"
|
||||
else
|
||||
fail "Gegenprobe fehlgeschlagen - rescue-wlan traegt gar keine Zugangsdaten, die Abgrenzung waere wertlos"
|
||||
fi
|
||||
if ! tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds" "$SECRET_PASS"; then
|
||||
pass "und der Rettungssatz 'ohne-wlan' auf dem Pi ist frei davon"
|
||||
else
|
||||
fail "der Rettungssatz 'ohne-wlan' auf dem Pi enthaelt Zugangsdaten"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Szenario 41: ein Abbild, das sich nicht entpacken laesst, bricht den Deploy ab ==="
|
||||
# Nachsicht an dieser Stelle ("dann eben ungeprueft durchlassen") ist der Weg, auf dem
|
||||
# das Passwort am Ende doch hinausgeht: ein Abbild in einem anderen Format ist
|
||||
# genau das, was niemand geprueft hat.
|
||||
SANDBOX="$TMPROOT/s41"; STUBBIN="$TMPROOT/bin41"; LOG="$TMPROOT/s41.log"; SANDBOX_STATE="$TMPROOT/s41-state"
|
||||
setup_sandbox "$SANDBOX"
|
||||
setup_stubbin "$STUBBIN" "$LOG"
|
||||
write_local_cmake "$SANDBOX"
|
||||
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
|
||||
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
|
||||
# Kein gzip-Rumpf: aufgebaut wie ein Abbild (56 Byte Kopf, 512 Byte Trailer), aber der
|
||||
# Rumpf dazwischen ist unkomprimiert - die alte, unkomprimierte Attrappe dieser Suite.
|
||||
make_ota_file "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img"
|
||||
: >"$LOG"
|
||||
set +e
|
||||
OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0 - ein ungepruefter Rettungssatz waere ausgeliefert worden: $OUT"
|
||||
echo "$OUT" | grep -q "nicht entpacken" \
|
||||
&& pass "Fehlermeldung nennt den Grund" \
|
||||
|| fail "keine Fehlermeldung zum Entpacken: $OUT"
|
||||
echo "$OUT" | grep -q "rescue-nocreds/ninlight_foo_ota.img" \
|
||||
&& pass "Fehlermeldung nennt die betroffene Datei" \
|
||||
|| fail "Fehlermeldung nennt die Datei 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 ungepruefbarem Abbild uebertragen: $(cat "$LOG")"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Szenario 42: die Meldung sagt, wie viele Abbilder in wie vielen Saetzen geprueft wurden ==="
|
||||
# Die alte Meldung "sauber (2 geprueft)" zaehlte die SUCHBEGRIFFE und las sich wie zwei
|
||||
# Dateien - sie haette genauso gelautet, wenn kein einziges Abbild angefasst worden waere.
|
||||
SANDBOX="$TMPROOT/s42"; STUBBIN="$TMPROOT/bin42"; LOG="$TMPROOT/s42.log"; SANDBOX_STATE="$TMPROOT/s42-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
|
||||
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
|
||||
make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N
|
||||
make_ota_image "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.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: $OUT"
|
||||
# migrate-nocreds/: foo_prod.img + foo_dev.img, rescue-nocreds/: ninlight_foo_ota.img.
|
||||
echo "$OUT" | grep -q "sauber (3 Abbilder in 2 Saetzen geprueft" \
|
||||
&& pass "die Meldung nennt Zahl der Abbilder UND der Saetze" \
|
||||
|| fail "die Meldung sagt nicht, was geprueft wurde: $OUT"
|
||||
echo "$OUT" | grep -q "migrate-nocreds, rescue-nocreds" \
|
||||
&& pass "die Meldung nennt beide geprueften Saetze beim Namen" \
|
||||
|| fail "die Meldung nennt die Saetze nicht: $OUT"
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "Alle Tests bestanden."
|
||||
|
||||
Reference in New Issue
Block a user