feat: add isolated Linux shadow backend
This commit is contained in:
parent
36996be39a
commit
f5217fef0f
6 changed files with 283 additions and 2 deletions
|
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
die() {
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_FAIL_0: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
test_mode=${GH_SUBCONTROL_TEST_MODE:-0}
|
||||
if [[ "${test_mode}" == 1 ]]; then
|
||||
config_path=${GH_SUBCONTROL_CONFIG:?test config is required}
|
||||
docker_bin=${GH_SUBCONTROL_DOCKER:?test docker is required}
|
||||
dmi_path=${GH_SUBCONTROL_DMI_PATH:?test DMI path is required}
|
||||
grub_cfg=${GH_SUBCONTROL_GRUB_CFG:?test GRUB path is required}
|
||||
state_root=${GH_SUBCONTROL_STATE_ROOT:?test state root is required}
|
||||
receipt_root=${GH_SUBCONTROL_RECEIPT_ROOT:?test receipt root is required}
|
||||
else
|
||||
[[ "${EUID}" == 0 ]] || die "root execution is required"
|
||||
for override in \
|
||||
GH_SUBCONTROL_CONFIG GH_SUBCONTROL_DOCKER GH_SUBCONTROL_DMI_PATH \
|
||||
GH_SUBCONTROL_GRUB_CFG GH_SUBCONTROL_STATE_ROOT GH_SUBCONTROL_RECEIPT_ROOT; do
|
||||
[[ -z "${!override:-}" ]] || die "production path override is forbidden: ${override}"
|
||||
done
|
||||
config_path=/etc/guanghu/linux-subcontrol.conf
|
||||
docker_bin=/usr/bin/docker
|
||||
dmi_path=/sys/class/dmi/id/product_uuid
|
||||
grub_cfg=/boot/grub/grub.cfg
|
||||
state_root=/run/guanghu/linux-subcontrol
|
||||
receipt_root=/guanghu/receipts/linux-subcontrol
|
||||
[[ -f "${config_path}" && ! -L "${config_path}" ]] || die "root-owned backend config is missing"
|
||||
[[ "$(stat -c '%u:%a' "${config_path}")" =~ ^0:(600|640|644)$ ]] ||
|
||||
die "backend config owner or mode is unsafe"
|
||||
fi
|
||||
|
||||
schema=
|
||||
node_id=
|
||||
instance_id=
|
||||
backend_id=
|
||||
container_name=
|
||||
image=
|
||||
rescue_slot_id=
|
||||
while IFS='=' read -r key value; do
|
||||
[[ -n "${key}" ]] || continue
|
||||
case "${key}" in
|
||||
schema|node_id|instance_id|backend_id|container_name|image|rescue_slot_id)
|
||||
printf -v "${key}" '%s' "${value}"
|
||||
;;
|
||||
*) die "unknown config field: ${key}" ;;
|
||||
esac
|
||||
done <"${config_path}"
|
||||
|
||||
[[ "${schema}" == "guanghu.linux-subcontrol-docker-backend/v1" ]] || die "config schema mismatch"
|
||||
[[ "${node_id}" == "JD-FD-PRIMARY" ]] || die "target node mismatch"
|
||||
[[ "${instance_id}" =~ ^[0-9a-f-]{36}$ ]] || die "instance id is invalid"
|
||||
[[ "${backend_id}" =~ ^[a-z0-9-]+$ ]] || die "backend id is invalid"
|
||||
[[ "${container_name}" =~ ^[a-z0-9-]+$ ]] || die "container name is invalid"
|
||||
[[ "${image}" =~ ^ubuntu@sha256:[0-9a-f]{64}$ ]] || die "image must use an immutable Ubuntu digest"
|
||||
[[ "${rescue_slot_id}" =~ ^[A-Za-z0-9._-]+$ ]] || die "rescue slot id is invalid"
|
||||
[[ -x "${docker_bin}" ]] || die "docker executable is unavailable"
|
||||
|
||||
requested_backend=${2:-}
|
||||
[[ -n "${requested_backend}" && "${requested_backend}" == "${backend_id}" ]] ||
|
||||
die "backend binding mismatch"
|
||||
|
||||
verify_machine() {
|
||||
local observed
|
||||
observed=$(tr '[:upper:]' '[:lower:]' <"${dmi_path}")
|
||||
[[ "${observed}" == "${instance_id}" ]] || die "machine identity mismatch"
|
||||
}
|
||||
|
||||
verify_rescue() {
|
||||
grep -Fq -- "--id '${rescue_slot_id}'" "${grub_cfg}" ||
|
||||
die "independent Linux rescue slot is unavailable"
|
||||
}
|
||||
|
||||
container_running() {
|
||||
[[ "$("${docker_bin}" inspect --format '{{.State.Running}}' "${container_name}" 2>/dev/null || true)" == true ]]
|
||||
}
|
||||
|
||||
observe() {
|
||||
if container_running; then
|
||||
echo ready
|
||||
else
|
||||
echo dormant
|
||||
fi
|
||||
}
|
||||
|
||||
action=${1:-}
|
||||
case "${action}" in
|
||||
preflight)
|
||||
verify_machine
|
||||
verify_rescue
|
||||
"${docker_bin}" image inspect "${image}" >/dev/null
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_PREFLIGHT_OK"
|
||||
;;
|
||||
observe)
|
||||
observe
|
||||
;;
|
||||
wake)
|
||||
verify_machine
|
||||
verify_rescue
|
||||
[[ "$(observe)" == dormant ]] || die "backend is not dormant before wake"
|
||||
"${docker_bin}" image inspect "${image}" >/dev/null
|
||||
mkdir -p "${state_root}" "${receipt_root}"
|
||||
"${docker_bin}" run \
|
||||
--detach \
|
||||
--pull never \
|
||||
--name "${container_name}" \
|
||||
--hostname jd-linux-subcontrol \
|
||||
--label guanghu.owner=GUANGHU_OS \
|
||||
--label "guanghu.node=${node_id}" \
|
||||
--label "guanghu.backend=${backend_id}" \
|
||||
--network none \
|
||||
--read-only \
|
||||
--tmpfs /run:rw,nosuid,nodev,noexec,size=16m \
|
||||
--tmpfs /tmp:rw,nosuid,nodev,noexec,size=16m \
|
||||
--cap-drop ALL \
|
||||
--security-opt no-new-privileges \
|
||||
--pids-limit 64 \
|
||||
--memory 256m \
|
||||
--cpus 0.50 \
|
||||
"${image}" \
|
||||
/bin/sh -ceu 'trap "exit 0" TERM INT; while :; do sleep 3600 & wait $!; done' \
|
||||
>/dev/null
|
||||
container_running || die "backend failed readiness readback"
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_READY"
|
||||
;;
|
||||
execute|verify)
|
||||
capability=${3:-}
|
||||
request_id=${4:-}
|
||||
[[ "${capability}" == runtime-identity-readback ]] || die "capability is not allowlisted"
|
||||
[[ "${request_id}" =~ ^[A-Za-z0-9._-]+$ ]] || die "request id is invalid"
|
||||
container_running || die "backend is not ready"
|
||||
mkdir -p "${state_root}" "${receipt_root}"
|
||||
target_file="${state_root}/${request_id}.target"
|
||||
if [[ "${action}" == execute ]]; then
|
||||
temporary="${target_file}.tmp"
|
||||
"${docker_bin}" exec "${container_name}" /bin/sh -ceu \
|
||||
'. /etc/os-release; printf "ID=%s\nVERSION_ID=%s\n" "$ID" "$VERSION_ID"' \
|
||||
>"${temporary}"
|
||||
chmod 600 "${temporary}"
|
||||
mv "${temporary}" "${target_file}"
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_EXECUTED"
|
||||
else
|
||||
[[ -f "${target_file}" ]] || die "target readback is missing"
|
||||
expected=$'ID=ubuntu\nVERSION_ID=22.04'
|
||||
stored=$(cat "${target_file}")
|
||||
live=$("${docker_bin}" exec "${container_name}" /bin/sh -ceu \
|
||||
'. /etc/os-release; printf "ID=%s\nVERSION_ID=%s\n" "$ID" "$VERSION_ID"')
|
||||
[[ "${stored}" == "${expected}" && "${live}" == "${expected}" ]] ||
|
||||
die "target readback mismatch"
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_TARGET_VERIFIED"
|
||||
fi
|
||||
;;
|
||||
reclaim)
|
||||
if container_running; then
|
||||
"${docker_bin}" rm --force "${container_name}" >/dev/null
|
||||
fi
|
||||
[[ "$(observe)" == dormant ]] || die "backend reclaim failed"
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_DORMANT"
|
||||
;;
|
||||
*)
|
||||
die "usage: $0 <preflight|observe|wake|execute|verify|reclaim> <backend-id> [capability] [request-id]"
|
||||
;;
|
||||
esac
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
adapter="${source_root}/scripts/linux-subcontrol-docker-backend.sh"
|
||||
fixture=$(mktemp -d)
|
||||
trap 'rm -rf "${fixture}"' EXIT
|
||||
|
||||
mkdir -p "${fixture}/bin" "${fixture}/state" "${fixture}/receipts"
|
||||
printf '%s\n' 'f3d4b730-7f02-452f-975b-7091a4800431' >"${fixture}/dmi"
|
||||
printf '%s\n' "menuentry 'Ubuntu' --id 'gnulinux-simple-9e4550a0-452b-4f28-b5a5-d5364aa450f6' {" >"${fixture}/grub.cfg"
|
||||
cat >"${fixture}/backend.conf" <<'EOF'
|
||||
schema=guanghu.linux-subcontrol-docker-backend/v1
|
||||
node_id=JD-FD-PRIMARY
|
||||
instance_id=f3d4b730-7f02-452f-975b-7091a4800431
|
||||
backend_id=jd-linux-on-demand
|
||||
container_name=guanghu-linux-subcontrol-jd
|
||||
image=ubuntu@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
rescue_slot_id=gnulinux-simple-9e4550a0-452b-4f28-b5a5-d5364aa450f6
|
||||
EOF
|
||||
|
||||
cat >"${fixture}/bin/docker" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
printf '%s\n' "$*" >>"${FAKE_DOCKER_LOG}"
|
||||
case "${1:-}" in
|
||||
image)
|
||||
[[ "${2:-}" == inspect ]]
|
||||
;;
|
||||
inspect)
|
||||
[[ -f "${FAKE_DOCKER_STATE}" ]] || exit 1
|
||||
printf '%s\n' true
|
||||
;;
|
||||
run)
|
||||
touch "${FAKE_DOCKER_STATE}"
|
||||
printf '%s\n' fixture-container-id
|
||||
;;
|
||||
exec)
|
||||
[[ -f "${FAKE_DOCKER_STATE}" ]]
|
||||
printf '%s\n' 'ID=ubuntu' 'VERSION_ID=22.04'
|
||||
;;
|
||||
rm)
|
||||
rm -f "${FAKE_DOCKER_STATE}"
|
||||
;;
|
||||
*)
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "${fixture}/bin/docker"
|
||||
|
||||
export GH_SUBCONTROL_TEST_MODE=1
|
||||
export GH_SUBCONTROL_CONFIG="${fixture}/backend.conf"
|
||||
export GH_SUBCONTROL_DOCKER="${fixture}/bin/docker"
|
||||
export GH_SUBCONTROL_DMI_PATH="${fixture}/dmi"
|
||||
export GH_SUBCONTROL_GRUB_CFG="${fixture}/grub.cfg"
|
||||
export GH_SUBCONTROL_STATE_ROOT="${fixture}/state"
|
||||
export GH_SUBCONTROL_RECEIPT_ROOT="${fixture}/receipts"
|
||||
export FAKE_DOCKER_LOG="${fixture}/docker.log"
|
||||
export FAKE_DOCKER_STATE="${fixture}/container.running"
|
||||
|
||||
[[ "$("${adapter}" observe jd-linux-on-demand)" == dormant ]]
|
||||
"${adapter}" preflight jd-linux-on-demand
|
||||
"${adapter}" wake jd-linux-on-demand
|
||||
[[ "$("${adapter}" observe jd-linux-on-demand)" == ready ]]
|
||||
"${adapter}" execute jd-linux-on-demand runtime-identity-readback REQ-001
|
||||
"${adapter}" verify jd-linux-on-demand runtime-identity-readback REQ-001
|
||||
"${adapter}" reclaim jd-linux-on-demand
|
||||
[[ "$("${adapter}" observe jd-linux-on-demand)" == dormant ]]
|
||||
|
||||
grep -Fq -- '--network none' "${fixture}/docker.log"
|
||||
grep -Fq -- '--read-only' "${fixture}/docker.log"
|
||||
grep -Fq -- '--cap-drop ALL' "${fixture}/docker.log"
|
||||
grep -Fq -- '--security-opt no-new-privileges' "${fixture}/docker.log"
|
||||
grep -Fq -- '--pids-limit 64' "${fixture}/docker.log"
|
||||
grep -Fq -- 'ubuntu@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' "${fixture}/docker.log"
|
||||
|
||||
if "${adapter}" execute jd-linux-on-demand arbitrary-shell REQ-002; then
|
||||
echo "unregistered capability was accepted" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' 'wrong-instance' >"${fixture}/dmi"
|
||||
if "${adapter}" wake jd-linux-on-demand; then
|
||||
echo "wrong machine identity was accepted" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' 'f3d4b730-7f02-452f-975b-7091a4800431' >"${fixture}/dmi"
|
||||
: >"${fixture}/grub.cfg"
|
||||
if "${adapter}" wake jd-linux-on-demand; then
|
||||
echo "missing rescue slot was accepted" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "GUANGHU_LINUX_SUBCONTROL_DOCKER_BACKEND_OK"
|
||||
Loading…
Reference in a new issue