151 lines
6.0 KiB
Bash
Executable File
151 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Read-only, loopless validation of a composed Turing RK1 image.
|
|
|
|
set -Eeuo pipefail
|
|
|
|
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
|
PROJECT_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd -P)
|
|
IMAGE=${1:-}
|
|
BASE_RAW=${2:-"$PROJECT_DIR/downloads/Armbian_community_26.11.0-trunk.1_Turing-rk1_trixie_vendor_6.1.115_minimal.img"}
|
|
[[ -n "$IMAGE" && -f "$IMAGE" ]] || {
|
|
echo "Usage: $0 IMAGE.img[.xz] [BASE.img]" >&2
|
|
exit 2
|
|
}
|
|
|
|
for tool in cmp debugfs e2fsck jq sfdisk sgdisk sha256sum xz; do
|
|
command -v "$tool" >/dev/null || { echo "Missing command: $tool" >&2; exit 1; }
|
|
done
|
|
|
|
TEST_TMP=$(mktemp -d /tmp/rk1-image-validation.XXXXXXXX)
|
|
trap 'rm -rf -- "$TEST_TMP"' EXIT
|
|
RAW_IMAGE=$IMAGE
|
|
if [[ "$IMAGE" == *.xz ]]; then
|
|
xz --test "$IMAGE"
|
|
RAW_IMAGE="$TEST_TMP/image.img"
|
|
xz --decompress --stdout "$IMAGE" >"$RAW_IMAGE"
|
|
fi
|
|
|
|
sgdisk --verify "$RAW_IMAGE" >/dev/null
|
|
partition_json=$(sfdisk --json "$RAW_IMAGE")
|
|
[[ "$(jq '.partitiontable.partitions | length' <<<"$partition_json")" == 1 ]] || {
|
|
echo 'Image must contain exactly one root partition' >&2
|
|
exit 1
|
|
}
|
|
partition_start=$(jq -r '.partitiontable.partitions[0].start' <<<"$partition_json")
|
|
partition_size=$(jq -r '.partitiontable.partitions[0].size' <<<"$partition_json")
|
|
partition_type=$(jq -r '.partitiontable.partitions[0].type' <<<"$partition_json")
|
|
[[ "$partition_start" == 32768 ]] || { echo "Unexpected partition start: $partition_start" >&2; exit 1; }
|
|
[[ "$partition_type" == B921B045-1DF0-41C3-AF44-4C6F280D3FAE ]] || {
|
|
echo "Unexpected ARM64 root partition type: $partition_type" >&2
|
|
exit 1
|
|
}
|
|
|
|
if [[ -f "$BASE_RAW" ]]; then
|
|
cmp --silent --bytes=440 "$BASE_RAW" "$RAW_IMAGE" || {
|
|
echo 'MBR bootstrap differs from the locked base image' >&2
|
|
exit 1
|
|
}
|
|
loader_offset=$((64 * 512))
|
|
loader_length=$(((32768 - 64) * 512))
|
|
cmp --silent --ignore-initial="$loader_offset:$loader_offset" \
|
|
--bytes="$loader_length" "$BASE_RAW" "$RAW_IMAGE" || {
|
|
echo 'Rockchip loader region differs from the locked base image' >&2
|
|
exit 1
|
|
}
|
|
fi
|
|
|
|
ROOTFS="$TEST_TMP/rootfs.ext4"
|
|
dd if="$RAW_IMAGE" of="$ROOTFS" bs=512 skip="$partition_start" \
|
|
count="$partition_size" status=none
|
|
e2fsck -fn "$ROOTFS" >"$TEST_TMP/e2fsck.log" 2>&1 || {
|
|
cat "$TEST_TMP/e2fsck.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
ext4_stat() { debugfs -R "stat $1" "$ROOTFS" 2>/dev/null; }
|
|
ext4_exists() { ext4_stat "$1" | grep -q '^Inode:'; }
|
|
assert_contains() {
|
|
local path=$1 pattern=$2
|
|
debugfs -R "cat $path" "$ROOTFS" 2>/dev/null | grep -Eq "$pattern" || {
|
|
echo "Missing $pattern in $path" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
assert_mode() {
|
|
local path=$1 mode=$2
|
|
ext4_stat "$path" | grep -Eq "Mode:[[:space:]]+$mode" || {
|
|
echo "Unexpected mode for $path (wanted $mode)" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
assert_contains /etc/os-release '^VERSION_ID="?13"?$'
|
|
assert_contains /etc/armbian-release '^BOARD=turing-rk1$'
|
|
assert_contains /boot/armbianEnv.txt '^fdtfile=rockchip/rk3588-turing-rk1\.dtb$'
|
|
assert_contains /boot/armbianEnv.txt '^overlays=.*panthor-gpu'
|
|
assert_contains /etc/shadow '^root:!:'
|
|
assert_contains /etc/default/armbian-firstrun '^OPENSSHD_REGENERATE_HOST_KEYS=false$'
|
|
assert_contains /etc/ssh/sshd_config.d/90-rk1-media.conf '^PermitRootLogin no$'
|
|
assert_contains /etc/ssh/sshd_config.d/90-rk1-media.conf '^PasswordAuthentication no$'
|
|
assert_contains /etc/ssh/sshd_config.d/90-rk1-media.conf '^AuthenticationMethods publickey$'
|
|
assert_contains /etc/systemd/system/rk1-media-provision.service '^Before=.*kodi-rk\.service$'
|
|
assert_contains /usr/local/sbin/rk1-media-provision \
|
|
'^seed_archives=/opt/rk1-seed/debs$'
|
|
assert_contains /usr/local/sbin/rk1-media-provision \
|
|
'Dir::Cache::archives=.*seed_archives'
|
|
assert_contains /usr/local/sbin/rk1-media-provision \
|
|
'^systemctl --no-block start .*kodi-rk\.service$'
|
|
assert_contains /usr/local/sbin/rk1-media-provision \
|
|
'^udevadm trigger --action=add --subsystem-match=misc'
|
|
assert_contains /etc/systemd/system/rk1-media-identity.service '^RuntimeDirectory=sshd$'
|
|
assert_contains /usr/local/sbin/rk1-media-identity \
|
|
'^install -d -o root -g root -m 0755 /run/sshd$'
|
|
assert_contains /usr/local/sbin/rk1-media-identity '^/usr/sbin/sshd -t$'
|
|
assert_contains /opt/rk1-seed/debs.sha256 \
|
|
'^dd0ca68696aa35b1998f9c9e131fdf8f0d1ef4ba6f58a36d0d99f0625d47f430 rk1-media-stack_'
|
|
|
|
for required_path in \
|
|
/boot/dtb/rockchip/rk3588-turing-rk1.dtb \
|
|
/boot/dtb/rockchip/overlay/rockchip-rk3588-panthor-gpu.dtbo \
|
|
/lib/modules/6.1.115-vendor-rk35xx/kernel/drivers/gpu/drm/panthor/panthor.ko \
|
|
/opt/rknn/2.3.2/lib/librknnrt.so \
|
|
/opt/rknn/2.3.2/share/models/rk3588/mobilenet_v1.rknn \
|
|
/usr/local/bin/rknn-inference-test \
|
|
/usr/local/bin/rk1-media-selftest \
|
|
/usr/local/sbin/rk1-media-migrate-root-to-nvme \
|
|
/usr/local/sbin/rk1-media-diagnostics; do
|
|
ext4_exists "$required_path" || { echo "Missing image payload: $required_path" >&2; exit 1; }
|
|
done
|
|
|
|
for critical_dir in / /etc /etc/ssh /etc/systemd /usr /opt; do
|
|
assert_mode "$critical_dir" 0755
|
|
done
|
|
assert_mode /etc/shadow 0640
|
|
ext4_stat /etc/shadow | grep -Eq 'Group:[[:space:]]+42' || {
|
|
echo '/etc/shadow is not owned by the shadow group' >&2
|
|
exit 1
|
|
}
|
|
assert_mode /usr/local/sbin/rk1-media-identity 0755
|
|
assert_mode /usr/local/sbin/rk1-media-provision 0755
|
|
assert_mode /usr/local/sbin/rk1-media-migrate-root-to-nvme 0755
|
|
|
|
ext4_stat /etc/systemd/system/sysinit.target.wants/rk1-media-identity.service \
|
|
| grep -Fq 'Fast link dest: "../rk1-media-identity.service"' || exit 1
|
|
ext4_stat /etc/systemd/system/multi-user.target.wants/rk1-media-provision.service \
|
|
| grep -Fq 'Fast link dest: "../rk1-media-provision.service"' || exit 1
|
|
|
|
for forbidden_path in /root/.not_logged_in_yet /root/.ssh/authorized_keys \
|
|
/etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_ecdsa_key \
|
|
/etc/ssh/ssh_host_ed25519_key; do
|
|
if ext4_exists "$forbidden_path"; then
|
|
echo "Image contains forbidden credential: $forbidden_path" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
deb_count=$(debugfs -R 'ls -p /opt/rk1-seed/debs' "$ROOTFS" 2>/dev/null \
|
|
| grep -c '/.*\.deb/' || true)
|
|
[[ "$deb_count" == 141 ]] || { echo "Expected 141 offline packages, found $deb_count" >&2; exit 1; }
|
|
|
|
echo "PASS: loopless RK1 image validation ($IMAGE)"
|