Adds the Blueforce Field OS provisioning stack on top of the existing fleet architecture, without rewriting the working systems (installer, diagnostics, Ansible, WireGuard/SSH/xRDP/RustDesk, Docker policies, monitoring, Docusaurus). ISO build (the previous builder was a skeleton that produced no medium): - boot layout is read from the base ISO at build time via `xorriso -report_el_torito as_mkisofs` and rebuilt with `-as mkisofs`; `-boot_image any replay` cannot work on Ubuntu live ISOs because the GRUB2 MBR lives in the source image's first 16 sectors and the EFI system partition is an appended partition, neither of which is a file in the ISO9660 tree - preflight now asserts El Torito boot metadata positively: xorriso exits 0 on a non-ISO input, which previously let an arbitrary file pass --check - artifact produced and verified: dist/Blueforce-Field-OS-1.0.0-amd64.iso, 6 481 917 952 bytes, SHA256 cb0cc56f...d431296, BIOS+UEFI entries preserved, /autoinstall.yaml at the media root, casper/minimal.squashfs byte-identical to the base - recorded baseline deviation: the verified medium on the build host is the Desktop flavour, so the manifest carries a baseline_deviation block instead of hiding it; Server remains one flag away Offline and identity: - offline APT contract with concrete package requirements, pin format and an audit table; builds fail closed while pins are UNPINNED - autoinstall keeps interactive-sections: [storage]: the operator confirms the target disk, and no automatic wipe is ever performed - firstboot terminal wizard for the 8-digit dealer id, three-state lifecycle (PROVISIONED_OFFLINE -> ENROLLED -> READY) with fail-closed gates, so a missing internet link or WireGuard handshake cannot fail provisioning but can never produce a false READY Fleet and migration: - five read-only Ansible playbooks for Field OS version, provisioning, enrollment, remote channels and offline readiness, all wave-scoped with a separate production override - Windows pre-migration inventory and data export extended to the full brief scope, read-only, never collecting credentials Tests: tests/run-all.sh -> 8/8 pass, exit 0. Boot acceptance (UEFI, Legacy BIOS, Secure Boot), air-gapped install, real enrollment endpoint and Windows rollback remain LAB work and are listed in IMPLEMENTATION-REPORT.md.
516 lines
23 KiB
Bash
Executable file
516 lines
23 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# Static and sandboxed behavior test for the first-boot terminal wizard.
|
||
#
|
||
# It never runs systemd, never touches apt, and never touches the real /var state: every
|
||
# scenario works inside a throwaway sandbox with a fake installer. Scenarios that require
|
||
# root run in a separate user namespace (unshare -r) and are reported as SKIP when the
|
||
# host does not permit that.
|
||
set -euo pipefail
|
||
|
||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
TEST_SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
||
BF="$REPO_ROOT/provisioning/firstboot/bf-firstboot"
|
||
UNIT="$REPO_ROOT/provisioning/firstboot/blueforce-firstboot.service"
|
||
README_FILE="$REPO_ROOT/provisioning/firstboot/README.md"
|
||
|
||
WORK="$(mktemp -d)"
|
||
PASSED=0
|
||
FAILED=0
|
||
SKIPPED=0
|
||
|
||
trap 'rm -rf "$WORK"' EXIT
|
||
|
||
ok() {
|
||
if [[ -n "${BF_TEST_RESULTS:-}" ]]; then
|
||
printf 'PASS\t%s\n' "$*" >>"$BF_TEST_RESULTS"
|
||
else
|
||
printf 'OK %s\n' "$*"
|
||
PASSED=$((PASSED + 1))
|
||
fi
|
||
}
|
||
bad() {
|
||
if [[ -n "${BF_TEST_RESULTS:-}" ]]; then
|
||
printf 'FAIL\t%s\n' "$*" >>"$BF_TEST_RESULTS"
|
||
else
|
||
printf 'FAIL %s\n' "$*" >&2
|
||
FAILED=$((FAILED + 1))
|
||
fi
|
||
}
|
||
skip() {
|
||
if [[ -n "${BF_TEST_RESULTS:-}" ]]; then
|
||
printf 'SKIP\t%s\n' "$*" >>"$BF_TEST_RESULTS"
|
||
else
|
||
printf 'SKIP %s\n' "$*"
|
||
SKIPPED=$((SKIPPED + 1))
|
||
fi
|
||
}
|
||
section() { [[ -n "${BF_TEST_RESULTS:-}" ]] || printf '\n== %s ==\n' "$*"; }
|
||
|
||
assert_rc() {
|
||
if [[ "$1" == "$2" ]]; then ok "$3"; else bad "$3 (expected exit $1, got $2)"; fi
|
||
}
|
||
assert_output() {
|
||
if [[ "$2" == *"$1"* ]]; then ok "$3"; else bad "$3 (output lacks: $1)"; fi
|
||
}
|
||
assert_no_output() {
|
||
if [[ "$2" != *"$1"* ]]; then ok "$3"; else bad "$3 (output unexpectedly contains: $1)"; fi
|
||
}
|
||
assert_file() {
|
||
if [[ -f "$1" ]]; then ok "$2"; else bad "$2 (missing: $1)"; fi
|
||
}
|
||
assert_no_file() {
|
||
if [[ ! -e "$1" ]]; then ok "$2"; else bad "$2 (unexpected path: $1)"; fi
|
||
}
|
||
assert_eq() {
|
||
if [[ "$1" == "$2" ]]; then ok "$3"; else bad "$3 (expected '$1', got '$2')"; fi
|
||
}
|
||
assert_grep() {
|
||
if grep -qF -- "$1" "$2" 2>/dev/null; then ok "$3"; else bad "$3 (pattern not found in $2)"; fi
|
||
}
|
||
assert_no_grep() {
|
||
if grep -qF -- "$1" "$2" 2>/dev/null; then bad "$3 (forbidden pattern found in $2)"; else ok "$3"; fi
|
||
}
|
||
assert_no_directive() {
|
||
if grep -qE "^[[:space:]]*$1=" "$2" 2>/dev/null; then bad "$3 (forbidden unit directive in $2)"; else ok "$3"; fi
|
||
}
|
||
|
||
ENV_ARGS=()
|
||
SB=''
|
||
OUT=''
|
||
RC=0
|
||
|
||
new_sandbox() {
|
||
SB="$(mktemp -d "$WORK/sb.XXXXXX")"
|
||
{
|
||
printf '#!/usr/bin/env bash\n'
|
||
printf 'printf "%%s\\n" "$*" >> "%s/calls.log"\n' "$SB"
|
||
} >"$SB/fake-install.sh"
|
||
chmod 755 "$SB/fake-install.sh"
|
||
printf 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplePublicKeyForStaticFirstbootTest field@blueforce\n' >"$SB/key.pub"
|
||
chmod 600 "$SB/key.pub"
|
||
ENV_ARGS=(
|
||
"BF_FIRSTBOOT_MARKER=$SB/marker"
|
||
"STATE_DIR=$SB/state"
|
||
"BF_INSTALLER_PATH=$SB/fake-install.sh"
|
||
"BF_ADMIN_PUBLIC_KEY_FILE=$SB/key.pub"
|
||
"BF_REIDENTITY_AUDIT_LOG=$SB/audit.log"
|
||
"BF_FIRSTBOOT_INPUT_TIMEOUT=10"
|
||
)
|
||
}
|
||
|
||
installer_calls() {
|
||
if [[ -f "$SB/calls.log" ]]; then wc -l <"$SB/calls.log" | tr -d ' '; else printf '0'; fi
|
||
}
|
||
installer_args() {
|
||
if [[ -f "$SB/calls.log" ]]; then cat "$SB/calls.log"; else printf ''; fi
|
||
}
|
||
sandbox_listing() {
|
||
(cd "$SB" && ls -1A | sort | tr '\n' ',')
|
||
}
|
||
write_marker() {
|
||
printf '%s\n' "$1" >"$SB/marker"
|
||
chmod 600 "$SB/marker"
|
||
}
|
||
marker_content() {
|
||
if [[ -f "$SB/marker" ]]; then tr -d '[:space:]' <"$SB/marker"; else printf '<no-marker>'; fi
|
||
}
|
||
file_mode() {
|
||
if [[ -e "$1" ]]; then stat -c '%a' "$1"; else printf '<missing>'; fi
|
||
}
|
||
|
||
# Non-interactive invocation: stdin is /dev/null, so the wizard can never prompt.
|
||
run_bf() {
|
||
set +e
|
||
OUT="$(timeout 60 env "${ENV_ARGS[@]}" "$@" 2>&1 </dev/null)"
|
||
RC=$?
|
||
set -e
|
||
}
|
||
|
||
# Interactive invocation behind a real pseudo terminal, with the given keystrokes.
|
||
# The command (with any VAR=value prefixes) is rebuilt inside the pty through env, so it
|
||
# is passed exactly like the plain run_bf helper.
|
||
pty_command() {
|
||
local inner='env' arg
|
||
for arg in "$@"; do inner+=" $(printf '%q' "$arg")"; done
|
||
printf '%s' "$inner"
|
||
}
|
||
|
||
run_bf_tty() {
|
||
local input="$1"
|
||
shift
|
||
local inner
|
||
inner="$(pty_command "$@")"
|
||
set +e
|
||
OUT="$(printf '%s\n' "$input" | env SHELL=/bin/bash "${ENV_ARGS[@]}" timeout 60 script -qec "$inner" /dev/null 2>&1)"
|
||
RC=$?
|
||
set -e
|
||
}
|
||
|
||
# Same, but with no bytes written to the pseudo terminal at all (timeout path).
|
||
run_bf_tty_silent() {
|
||
local inner
|
||
inner="$(pty_command "$@")"
|
||
set +e
|
||
OUT="$(printf '' | env SHELL=/bin/bash "${ENV_ARGS[@]}" timeout 60 script -qec "$inner" /dev/null 2>&1)"
|
||
RC=$?
|
||
set -e
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Source, unit and documentation checks (no execution).
|
||
# ---------------------------------------------------------------------------
|
||
check_sources() {
|
||
section 'static: script, unit, README'
|
||
|
||
[[ -x "$BF" ]] && ok 'bf-firstboot is executable' || bad 'bf-firstboot is executable'
|
||
assert_eq '#!/usr/bin/env bash' "$(head -n 1 "$BF")" 'bf-firstboot has a bash shebang'
|
||
if bash -n "$BF" 2>/dev/null; then ok 'bf-firstboot parses (bash -n)'; else bad 'bf-firstboot parses (bash -n)'; fi
|
||
assert_grep 'set -euo pipefail' "$BF" 'bf-firstboot uses strict mode'
|
||
assert_grep '^[0-9]{8}$' "$BF" 'bf-firstboot validates the dealer number with the shared regex'
|
||
assert_grep '# BLUEFORCE FIELD OS' "$BF" 'bf-firstboot prints the field console banner'
|
||
assert_grep "wizard_write '> '" "$BF" 'bf-firstboot prompts with the wizard input marker'
|
||
assert_grep '/dev/tty' "$BF" 'bf-firstboot opens /dev/tty when stdin is not a terminal'
|
||
assert_grep '/etc/blueforce/dealer-id' "$BF" 'bf-firstboot supports the seeded dealer id file'
|
||
assert_grep '--offline' "$BF" 'bf-firstboot calls the installer offline-only'
|
||
assert_grep 'blueforce-install.sh' "$BF" 'bf-firstboot targets the local installer'
|
||
assert_grep 'BF_FIRSTBOOT_TEST_MODE' "$BF" 'bf-firstboot keeps the mutation-free test mode'
|
||
assert_grep '--resume' "$BF" 'bf-firstboot resumes a partial install'
|
||
assert_grep '--force-reidentity' "$BF" 'bf-firstboot exposes the admin re-identity path'
|
||
assert_grep 'BF_ALLOW_REIDENTITY' "$BF" 'the re-identity path is gated by an operator switch'
|
||
assert_grep 'already completed' "$BF" 'bf-firstboot short-circuits on the completion marker'
|
||
|
||
assert_no_grep 'curl' "$BF" 'bf-firstboot fetches nothing from the network'
|
||
assert_no_grep 'wget' "$BF" 'bf-firstboot does not download via wget'
|
||
assert_no_grep 'set -x' "$BF" 'bf-firstboot never traces (no accidental key dump)'
|
||
if grep -nE '(^|[^A-Za-z_])(cat|tee|head|tail|awk|printf|echo|cp|scp)[[:space:]]+[^|]*\$KEY_FILE' "$BF" >/dev/null 2>&1; then
|
||
bad 'the administrator key file is never read out into a message or a copy'
|
||
else
|
||
ok 'the administrator key file is never read out into a message or a copy'
|
||
fi
|
||
|
||
assert_file "$UNIT" 'the first-boot unit is present'
|
||
assert_grep 'ConditionPathExists=/etc/blueforce/admin-authorized-key.pub' "$UNIT" 'the unit requires the administrator public key'
|
||
assert_grep 'ConditionPathExists=!/var/lib/blueforce/firstboot-complete' "$UNIT" 'the unit is skipped once the marker exists'
|
||
assert_grep 'TimeoutStartSec=' "$UNIT" 'the unit bounds its runtime'
|
||
assert_grep 'StandardInput=tty-force' "$UNIT" 'the unit attaches the wizard to the console'
|
||
assert_grep 'Type=oneshot' "$UNIT" 'the unit is a one-shot service'
|
||
assert_grep 'RemainAfterExit=yes' "$UNIT" 'the unit records its result'
|
||
assert_grep 'WantedBy=multi-user.target' "$UNIT" 'the unit runs once at boot'
|
||
assert_no_directive 'ProtectSystem' "$UNIT" 'the unit does not sandbox paths the offline installer must write'
|
||
assert_no_directive 'ProtectHome' "$UNIT" 'the unit keeps /home/blueforce writable for the user module'
|
||
|
||
assert_file "$README_FILE" 'the first-boot README is present'
|
||
assert_grep 'BLUEFORCE FIELD OS' "$README_FILE" 'the README documents the wizard screen'
|
||
assert_grep 'force-reidentity' "$README_FILE" 'the README documents the admin identity change procedure'
|
||
assert_grep 'BF_ALLOW_REIDENTITY' "$README_FILE" 'the README documents that identity change is off by default'
|
||
assert_grep 'public SSH key' "$README_FILE" 'the README states the administrator key rule'
|
||
assert_grep '/var/log/blueforce-reidentity.log' "$README_FILE" 'the README documents the identity change audit record'
|
||
assert_grep 'ConditionPathExists' "$README_FILE" 'the README explains the unit conditions'
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Command line behavior: check mode, validation, automation path, no terminal.
|
||
# ---------------------------------------------------------------------------
|
||
check_cli() {
|
||
section 'cli: validation, check mode, no-terminal fallback'
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --check --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'check mode accepts a valid dealer number'
|
||
assert_output 'no changes were made' "$OUT" 'check mode reports that it made no changes'
|
||
assert_no_file "$SB/marker" 'check mode writes no marker'
|
||
assert_no_file "$SB/state" 'check mode creates no state directory'
|
||
assert_eq '0' "$(installer_calls)" 'check mode never runs the installer'
|
||
assert_eq 'fake-install.sh,key.pub,' "$(sandbox_listing)" 'check mode leaves the sandbox untouched'
|
||
|
||
for candidate in 1234567 123456789 1234abcd '' '12 34 5678'; do
|
||
new_sandbox
|
||
run_bf bash "$BF" --check --dealer-id "$candidate"
|
||
assert_rc 2 "$RC" "invalid dealer number is rejected: '${candidate:-<empty>}'"
|
||
assert_output '8 digits' "$OUT" "rejection of '${candidate:-<empty>}' explains the expected format"
|
||
assert_no_file "$SB/marker" "rejection of '${candidate:-<empty>}' writes no marker"
|
||
done
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --check
|
||
assert_rc 2 "$RC" 'check mode without any dealer number fails with a clear error'
|
||
assert_no_file "$SB/marker" 'a failed check mode writes no marker'
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --not-a-flag
|
||
assert_rc 2 "$RC" 'an unknown argument is rejected'
|
||
assert_output 'unknown argument' "$OUT" 'an unknown argument is reported explicitly'
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --dealer-id 1234567
|
||
assert_rc 2 "$RC" 'a 7-digit dealer number never reaches the installer'
|
||
assert_no_file "$SB/marker" 'a rejected dealer number writes no marker'
|
||
assert_eq '0' "$(installer_calls)" 'a rejected dealer number never runs the installer'
|
||
|
||
# Non-interactive mode with no seeded file and no flag must fail closed, without hanging.
|
||
new_sandbox
|
||
run_bf bash "$BF" --non-interactive
|
||
assert_rc 2 "$RC" 'non-interactive mode without any dealer number fails'
|
||
if [[ "$OUT" == *'dealer id file is missing or unreadable'* || "$OUT" == *'invalid dealer number'* ]]; then
|
||
ok 'non-interactive mode names the unusable dealer id input'
|
||
else
|
||
bad 'non-interactive mode names the unusable dealer id input'
|
||
fi
|
||
assert_no_file "$SB/marker" 'the no-input path writes no marker'
|
||
|
||
# Explicit file path (automation) is honoured and never opens the wizard.
|
||
new_sandbox
|
||
printf '12010193\n' >"$SB/dealer-id"
|
||
run_bf bash "$BF" --dealer-id-file "$SB/dealer-id"
|
||
assert_no_output '# BLUEFORCE FIELD OS' "$OUT" 'the automation path never opens the wizard'
|
||
assert_no_output '8 Haneli Bayi Numarası' "$OUT" 'the automation path asks nothing at the console'
|
||
assert_eq '0' "$(installer_calls)" 'the automation path runs no installer when it cannot install'
|
||
if [[ "$(id -u)" -eq 0 ]]; then
|
||
assert_rc 0 "$RC" 'the dealer id file path completes when the test runs as root'
|
||
else
|
||
assert_rc 1 "$RC" 'the dealer id file path fails closed without root'
|
||
assert_output 'must run as root' "$OUT" 'the automation path reports the missing privilege'
|
||
assert_no_file "$SB/marker" 'the automation path writes no marker when it cannot install'
|
||
fi
|
||
|
||
new_sandbox
|
||
printf 'nope\n' >"$SB/dealer-id"
|
||
run_bf bash "$BF" --dealer-id-file "$SB/dealer-id"
|
||
assert_rc 2 "$RC" 'an invalid value in the dealer id file is rejected'
|
||
assert_output 'invalid dealer number' "$OUT" 'an invalid file value names the format rule'
|
||
|
||
new_sandbox
|
||
printf '12010193\n' >"$SB/dealer-id"
|
||
run_bf BF_FIRSTBOOT_NONINTERACTIVE=1 bash "$BF" --dealer-id-file "$SB/dealer-id"
|
||
assert_no_output '# BLUEFORCE FIELD OS' "$OUT" 'explicit non-interactive mode never opens the wizard'
|
||
assert_eq '0' "$(installer_calls)" 'explicit non-interactive mode runs no installer when it cannot install'
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Wizard behavior through a pseudo terminal.
|
||
# ---------------------------------------------------------------------------
|
||
check_wizard() {
|
||
section 'wizard: pty input, confirmation, attempt limit'
|
||
|
||
new_sandbox
|
||
run_bf_tty '1234567
|
||
abcd
|
||
nope' bash "$BF"
|
||
assert_rc 2 "$RC" 'three malformed entries abort the wizard'
|
||
assert_output '# BLUEFORCE FIELD OS' "$OUT" 'the wizard shows the field console banner'
|
||
assert_output '8 Haneli Bayi Numarası' "$OUT" 'the wizard shows the Turkish dealer number prompt'
|
||
assert_output 'deneme 3/3' "$OUT" 'the wizard counts attempts and never accepts malformed input'
|
||
assert_output 'not accepted after 3 attempts' "$OUT" 'the wizard aborts with an explicit error after the limit'
|
||
assert_no_file "$SB/marker" 'the aborted wizard writes no marker'
|
||
assert_eq '0' "$(installer_calls)" 'the aborted wizard never runs the installer'
|
||
|
||
new_sandbox
|
||
run_bf_tty '1201 0193
|
||
12010193
|
||
e' bash "$BF"
|
||
assert_output 'Geçersiz giriş' "$OUT" 'the wizard rejects a malformed entry that contains a space'
|
||
assert_output 'deneme 1/3' "$OUT" 'the wizard re-asks once a malformed entry is rejected'
|
||
assert_output 'BF-12010193' "$OUT" 'the wizard accepts the corrected entry after a rejection'
|
||
|
||
new_sandbox
|
||
run_bf_tty '12010193
|
||
e' bash "$BF"
|
||
assert_output 'BF-12010193' "$OUT" 'the confirmed wizard shows Device ID BF-<dealer-id>'
|
||
assert_output 'bf-12010193' "$OUT" 'the confirmed wizard shows hostname bf-<dealer-id>'
|
||
assert_eq '0' "$(installer_calls)" 'the wizard run does not reach the installer without root'
|
||
if [[ "$(id -u)" -eq 0 ]]; then
|
||
assert_rc 0 "$RC" 'the confirmed wizard completes when the test runs as root'
|
||
else
|
||
assert_rc 1 "$RC" 'the confirmed wizard stops at the privilege gate without root'
|
||
assert_output 'must run as root' "$OUT" 'the confirmed wizard proceeds past identity validation'
|
||
fi
|
||
|
||
new_sandbox
|
||
run_bf_tty '12010193
|
||
n
|
||
12010193
|
||
n
|
||
12010193
|
||
n' bash "$BF"
|
||
assert_rc 2 "$RC" 'refusing the confirmation three times aborts the wizard'
|
||
assert_output 'Onay verilmedi' "$OUT" 'the wizard re-asks when the identity is not confirmed'
|
||
assert_eq '0' "$(installer_calls)" 'a refused confirmation never runs the installer'
|
||
|
||
new_sandbox
|
||
run_bf_tty_silent bash "$BF"
|
||
assert_rc 2 "$RC" 'a silent console aborts the wizard instead of waiting forever'
|
||
assert_output 'timed out' "$OUT" 'the silent console path explains the timeout'
|
||
assert_eq '0' "$(installer_calls)" 'the silent console path never runs the installer'
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Root-only behavior in a separate user namespace.
|
||
# ---------------------------------------------------------------------------
|
||
check_root_paths() {
|
||
section 'root (namespace): install run, marker, key gate, re-identity'
|
||
|
||
new_sandbox
|
||
run_bf BF_FIRSTBOOT_TEST_MODE=1 bash "$BF" --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'test mode completes successfully as root'
|
||
assert_output "TEST: would run $SB/fake-install.sh --offline --dealer-id 12010193 --yes" "$OUT" 'test mode builds the exact offline installer command'
|
||
assert_no_file "$SB/marker" 'test mode is mutation-free (no marker)'
|
||
assert_eq '0' "$(installer_calls)" 'test mode never runs the installer'
|
||
|
||
new_sandbox
|
||
mkdir -p "$SB/state"
|
||
printf '01-precheck=OK\n' >"$SB/state/install-state"
|
||
run_bf BF_FIRSTBOOT_TEST_MODE=1 bash "$BF" --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'a partial install state is accepted'
|
||
assert_output '--resume' "$OUT" 'a partial install is resumed instead of restarted'
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'first boot completes with the (fake) installer'
|
||
assert_file "$SB/marker" 'the completion marker is written'
|
||
assert_eq '600' "$(file_mode "$SB/marker")" 'the marker is mode 600'
|
||
assert_eq '12010193' "$(marker_content)" 'the marker records the provisioned dealer number'
|
||
assert_eq '--offline --dealer-id 12010193 --yes' "$(installer_args)" 'the installer is called offline with the validated dealer number'
|
||
|
||
run_bf bash "$BF" --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'a second run exits successfully'
|
||
assert_output 'already completed' "$OUT" 'a provisioned device reports that first boot is done'
|
||
assert_eq '1' "$(installer_calls)" 'a provisioned device never runs the installer again'
|
||
|
||
run_bf bash "$BF" --dealer-id 87654321
|
||
assert_rc 0 "$RC" 'a different dealer number does not fail the run'
|
||
assert_output 'identity changes use the admin procedure' "$OUT" 'a different dealer number points at the admin procedure'
|
||
assert_eq '12010193' "$(marker_content)" 'the provisioned identity is unchanged'
|
||
assert_eq '1' "$(installer_calls)" 'the installer is not re-run for a different dealer number'
|
||
|
||
run_bf_tty '87654321
|
||
e' bash "$BF"
|
||
assert_eq '12010193' "$(marker_content)" 'the wizard cannot change a provisioned identity'
|
||
assert_eq '1' "$(installer_calls)" 'the wizard does not re-run the installer on a provisioned device'
|
||
|
||
new_sandbox
|
||
printf -- '-----BEGIN OPENSSH PRIVATE KEY-----\nAAAAB3NzaC1yc2EFIRSTBOOTSECRETBODY\n-----END OPENSSH PRIVATE KEY-----\n' >"$SB/priv.key"
|
||
chmod 600 "$SB/priv.key"
|
||
run_bf BF_ADMIN_PUBLIC_KEY_FILE="$SB/priv.key" bash "$BF" --dealer-id 12010193
|
||
assert_rc 1 "$RC" 'private key material is refused as an administrator key'
|
||
assert_output 'must contain only a public SSH key' "$OUT" 'the refusal states the public-key rule'
|
||
assert_no_output 'FIRSTBOOTSECRETBODY' "$OUT" 'no key material is echoed in the error output'
|
||
assert_no_output 'PRIVATE KEY' "$OUT" 'the private key header is never echoed'
|
||
assert_no_file "$SB/marker" 'a refused key writes no marker'
|
||
assert_eq '0' "$(installer_calls)" 'a refused key never runs the installer'
|
||
|
||
new_sandbox
|
||
chmod 644 "$SB/key.pub"
|
||
run_bf bash "$BF" --dealer-id 12010193
|
||
assert_rc 1 "$RC" 'a group/world readable administrator key file is refused'
|
||
assert_output 'root-owned' "$OUT" 'the refusal states the ownership and mode rule'
|
||
assert_no_file "$SB/marker" 'a refused key mode writes no marker'
|
||
|
||
new_sandbox
|
||
run_bf BF_ADMIN_PUBLIC_KEY_FILE="$SB/missing.pub" bash "$BF" --dealer-id 12010193
|
||
assert_rc 1 "$RC" 'a missing administrator key file blocks the offline install'
|
||
assert_output 'OFFLINE BLOCKED' "$OUT" 'a missing administrator key is reported as an offline block'
|
||
|
||
new_sandbox
|
||
run_bf bash "$BF" --check --dealer-id 12010193
|
||
assert_rc 0 "$RC" 'check mode succeeds as root'
|
||
assert_no_file "$SB/marker" 'check mode writes no marker as root'
|
||
assert_no_file "$SB/state" 'check mode creates no state directory as root'
|
||
assert_eq '0' "$(installer_calls)" 'check mode runs no installer as root'
|
||
assert_eq 'fake-install.sh,key.pub,' "$(sandbox_listing)" 'check mode leaves the sandbox untouched as root'
|
||
|
||
new_sandbox
|
||
write_marker 12010193
|
||
run_bf bash "$BF" --force-reidentity --dealer-id 55555555
|
||
assert_rc 1 "$RC" 're-identity is refused without the operator gate'
|
||
assert_output 'disabled by default' "$OUT" 'the refusal explains that identity change is off by default'
|
||
assert_eq '12010193' "$(marker_content)" 'a refused re-identity leaves the marker unchanged'
|
||
assert_eq '0' "$(installer_calls)" 'a refused re-identity runs no installer'
|
||
assert_no_file "$SB/audit.log" 'a refused re-identity writes no audit record'
|
||
|
||
new_sandbox
|
||
write_marker 12010193
|
||
run_bf_tty '99999999' BF_ALLOW_REIDENTITY=1 bash "$BF" --force-reidentity --dealer-id 55555555
|
||
assert_rc 2 "$RC" 'a mismatching confirmation aborts the identity change'
|
||
assert_output 'confirmation did not match' "$OUT" 'the aborted identity change says why'
|
||
assert_eq '12010193' "$(marker_content)" 'the aborted identity change leaves the marker unchanged'
|
||
assert_eq '0' "$(installer_calls)" 'the aborted identity change runs no installer'
|
||
|
||
new_sandbox
|
||
write_marker 12010193
|
||
printf 'not a directory\n' >"$SB/blocker"
|
||
run_bf_tty '55555555' BF_ALLOW_REIDENTITY=1 BF_REIDENTITY_AUDIT_LOG="$SB/blocker/audit.log" bash "$BF" --force-reidentity --dealer-id 55555555
|
||
assert_rc 1 "$RC" 'an unwritable audit log fails the identity change closed'
|
||
assert_output 'audit log is not writable' "$OUT" 'the failure names the unwritable audit log'
|
||
assert_eq '12010193' "$(marker_content)" 'a failed audit leaves the marker unchanged'
|
||
assert_eq '0' "$(installer_calls)" 'a failed audit runs no installer'
|
||
|
||
new_sandbox
|
||
write_marker 12010193
|
||
run_bf_tty '55555555' BF_ALLOW_REIDENTITY=1 bash "$BF" --force-reidentity --dealer-id 55555555
|
||
assert_rc 0 "$RC" 'an admitted identity change completes'
|
||
assert_eq '55555555' "$(marker_content)" 'the marker carries the new dealer number'
|
||
assert_eq '--offline --dealer-id 55555555 --yes' "$(installer_args)" 'the installer is re-run with the new dealer number'
|
||
if compgen -G "$SB/marker.reidentified-*" >/dev/null; then
|
||
ok 'the previous identity marker is archived'
|
||
else
|
||
bad 'the previous identity marker is archived'
|
||
fi
|
||
assert_output 'AUDIT: identity-change applied' "$OUT" 'the identity change is announced in the run log'
|
||
assert_file "$SB/audit.log" 'the identity change writes an audit record'
|
||
assert_eq '600' "$(file_mode "$SB/audit.log")" 'the audit record is mode 600'
|
||
assert_grep 'identity-change applied old=12010193 new=BF-55555555' "$SB/audit.log" 'the audit record names the old and the new identity'
|
||
|
||
new_sandbox
|
||
run_bf_tty '12010193
|
||
e' bash "$BF"
|
||
assert_rc 0 "$RC" 'the wizard completes the first boot as root'
|
||
assert_output '# BLUEFORCE FIELD OS' "$OUT" 'the interactive run shows the banner'
|
||
assert_output 'BF-12010193' "$OUT" 'the interactive run shows the Device ID'
|
||
assert_eq '12010193' "$(marker_content)" 'the interactive run writes the marker for the confirmed identity'
|
||
assert_eq '--offline --dealer-id 12010193 --yes' "$(installer_args)" 'the interactive run calls the installer offline'
|
||
}
|
||
|
||
if [[ -n "${BF_TEST_RESULTS:-}" ]]; then
|
||
# Child mode: only the root-required scenarios run; results go to the parent's file.
|
||
cd "$REPO_ROOT"
|
||
check_root_paths
|
||
exit 0
|
||
fi
|
||
|
||
check_sources
|
||
check_cli
|
||
check_wizard
|
||
|
||
section 'root (namespace): install run, marker, key gate, re-identity'
|
||
RESULTS_FILE="$WORK/root-results.tsv"
|
||
: >"$RESULTS_FILE"
|
||
if unshare -r true >/dev/null 2>&1 && [[ "$(unshare -r id -u 2>/dev/null)" == 0 ]]; then
|
||
CHILD_LOG="$WORK/child.log"
|
||
if unshare -r env BF_TEST_RESULTS="$RESULTS_FILE" bash "$TEST_SCRIPT" >"$CHILD_LOG" 2>&1; then
|
||
:
|
||
else
|
||
printf 'FAIL the privileged (namespace) section did not complete; see the log below\n' >&2
|
||
sed -n '1,20p' "$CHILD_LOG" >&2
|
||
FAILED=$((FAILED + 1))
|
||
fi
|
||
if [[ -s "$RESULTS_FILE" ]]; then
|
||
while IFS=$'\t' read -r status name; do
|
||
case "$status" in
|
||
PASS) ok "$name";;
|
||
SKIP) skip "$name";;
|
||
*) bad "$name";;
|
||
esac
|
||
done <"$RESULTS_FILE"
|
||
else
|
||
skip 'the privileged (namespace) section produced no results'
|
||
fi
|
||
else
|
||
skip 'unshare -r is unavailable here: root-only first-boot scenarios were not run'
|
||
fi
|
||
|
||
printf '\ntest-firstboot-static: %s passed, %s failed, %s skipped\n' "$PASSED" "$FAILED" "$SKIPPED"
|
||
if [[ "$FAILED" -ne 0 ]]; then
|
||
printf 'test-firstboot-static: FAILED\n' >&2
|
||
exit 1
|
||
fi
|
||
printf 'test-firstboot-static: all passed\n'
|