- Purge misplaced RFCs, scratch tokens, and node-proprietary assets - Decouple remote provisioning to ci/scripts/init-node.py - Add targeted composition orchestration in actions/compose.py - Add host-side Xray stale socket cleanup in /dev/shm - Add actions/env.py for shell exports and .env synchronization - Harden UFW, backup retention, host tempfiles, and notify dispatcher - Add pyproject.toml and Makefile with 78 passing unit tests
88 lines
3.5 KiB
Bash
Executable File
88 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Only the parser runtime may be installed before semantic input validation.
|
|
set -euo pipefail
|
|
export LC_ALL=C DEBIAN_FRONTEND=noninteractive
|
|
[[ $(id -u) == 0 ]] || { echo 'APEX seed requires root.' >&2; exit 1; }
|
|
[[ -r /etc/os-release ]] || { echo 'APEX seed requires Debian 13 amd64.' >&2; exit 1; }
|
|
. /etc/os-release
|
|
[[ ${ID:-} == debian && ${VERSION_ID:-} == 13 && $(dpkg --print-architecture) == amd64 ]] || {
|
|
echo 'APEX seed requires Debian 13 amd64.' >&2; exit 1;
|
|
}
|
|
|
|
policy=/usr/sbin/policy-rc.d
|
|
state=/usr/sbin/.apex-policy-rc.d
|
|
for path in /var /var/lib /var/lib/apex-init; do
|
|
[[ ! -L $path ]] || { echo 'Refusing redirected initialization state.' >&2; exit 1; }
|
|
done
|
|
mkdir -p -m 0700 /var/lib/apex-init
|
|
[[ $(stat -c %u /var/lib/apex-init) == 0 ]] || {
|
|
echo 'Initialization state must be root-owned.' >&2; exit 1;
|
|
}
|
|
(( (8#$(stat -c %a /var/lib/apex-init) & 022) == 0 )) || {
|
|
echo 'Initialization state must not be writable by other users.' >&2; exit 1;
|
|
}
|
|
[[ ! -L /var/lib/apex-init/init.lock ]] || { echo 'Refusing redirected initialization lock.' >&2; exit 1; }
|
|
exec 8>/var/lib/apex-init/init.lock
|
|
flock -n 8 || { echo 'Another APEX initialization is running.' >&2; exit 1; }
|
|
mkdir -p /run/lock
|
|
exec 9>/run/lock/apex-package-policy.lock
|
|
flock -n 9 || { echo 'Another APEX package operation is running.' >&2; exit 1; }
|
|
restore_policy() {
|
|
[[ -d $state ]] || return 0
|
|
[[ ! -L $state && $(stat -c %u "$state") == 0 ]] || {
|
|
echo 'Refusing untrusted service policy recovery state.' >&2; return 1;
|
|
}
|
|
if [[ -e $state/ready && ( -e $policy || -L $policy ) ]]; then
|
|
if ! cmp -s "$policy" <(printf '#!/bin/sh\nexit 101\n') &&
|
|
! cmp -s "$policy" "$state/original"; then
|
|
echo 'Service policy changed outside APEX; reconcile it explicitly.' >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
if [[ -e $state/original || -L $state/original ]]; then
|
|
rm -f -- "$state/restoring"
|
|
cp -a -- "$state/original" "$state/restoring"
|
|
mv -Tf -- "$state/restoring" "$policy"
|
|
elif [[ -e $state/absent ]]; then
|
|
rm -f -- "$policy"
|
|
elif [[ -e $state/ready ]]; then
|
|
echo 'APEX service policy recovery is incomplete; inspect /usr/sbin/.apex-policy-rc.d.' >&2
|
|
return 1
|
|
fi
|
|
rm -rf -- "$state"
|
|
}
|
|
|
|
# Release the shared host lock on exit; the target runner reacquires it later.
|
|
restore_policy
|
|
missing=()
|
|
for package in python3 python3-yaml; do
|
|
status=$(dpkg-query -W -f='${Status}' "$package" 2>/dev/null || true)
|
|
[[ $status == *' ok installed' ]] || missing+=("$package")
|
|
done
|
|
if (( ${#missing[@]} )); then
|
|
mkdir -m 0700 -- "$state"
|
|
trap restore_policy EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
if [[ -e $policy || -L $policy ]]; then
|
|
cp -a -- "$policy" "$state/original"
|
|
else
|
|
touch "$state/absent"
|
|
fi
|
|
touch "$state/ready"
|
|
printf '#!/bin/sh\nexit 101\n' > "$state/guard"
|
|
chmod 0755 "$state/guard"
|
|
mv -Tf -- "$state/guard" "$policy"
|
|
apt=(apt-get -o DPkg::Lock::Timeout=60 -o Acquire::Retries=2
|
|
-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30)
|
|
timeout 300 "${apt[@]}" update --error-on=any
|
|
install=("${apt[@]}" install -y --no-install-recommends --no-upgrade --no-remove "${missing[@]}")
|
|
plan=$(timeout 300 "${install[@]}" --simulate)
|
|
if grep -Eq '^Remv |^Inst [^ ]+ \[' <<< "$plan"; then
|
|
echo 'Parser installation requires a package upgrade/removal; reconcile it explicitly.' >&2
|
|
exit 1
|
|
fi
|
|
"${install[@]}"
|
|
fi
|
|
python3 -c 'import yaml'
|