166 lines
5.9 KiB
Shell
Executable file
166 lines
5.9 KiB
Shell
Executable file
#!/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|docker\.m\.daocloud\.io/library/ubuntu)@sha256:[0-9a-f]{64}$ ]] || die "image must use an immutable Ubuntu digest from the direct or approved proxy repository"
|
|
[[ "${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
|