feat: add isolated Linux shadow backend
This commit is contained in:
parent
36996be39a
commit
f5217fef0f
6 changed files with 283 additions and 2 deletions
|
|
@ -78,6 +78,19 @@ at 100% of declared source lines and functions. No production Linux
|
||||||
subcontrol backend, independent Guanghu boot supervisor, JD boot change, or
|
subcontrol backend, independent Guanghu boot supervisor, JD boot change, or
|
||||||
physical cutover is implied by that source result.
|
physical cutover is implied by that source result.
|
||||||
|
|
||||||
|
`scripts/linux-subcontrol-docker-backend.sh` is the first executable shadow
|
||||||
|
backend for the Linux-hosted transition. It binds the exact JD instance,
|
||||||
|
backend id, immutable Ubuntu image digest, and preserved GRUB rescue slot. A
|
||||||
|
wake starts a networkless, read-only, capability-free container with explicit
|
||||||
|
CPU, memory, and PID limits. Only the registered runtime-identity readback is
|
||||||
|
accepted; reclaim removes the container and verifies `DORMANT`. Environment
|
||||||
|
path overrides are rejected outside the isolated test harness.
|
||||||
|
|
||||||
|
This backend can prove the bounded Linux lifecycle while Ubuntu is still the
|
||||||
|
host. It cannot prove Guanghu-first boot control or the final topology. Those
|
||||||
|
remain `0` until the same lifecycle is owned by an independently booted
|
||||||
|
Guanghu supervisor and has a current JD server receipt.
|
||||||
|
|
||||||
## Language-primary boot target
|
## Language-primary boot target
|
||||||
|
|
||||||
`guanghu-language-primary.target` makes the accepted cognitive-control model
|
`guanghu-language-primary.target` makes the accepted cognitive-control model
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -88,6 +88,8 @@ run_gate world_and_protocol_validation \
|
||||||
run_gate shell_syntax bash -c \
|
run_gate shell_syntax bash -c \
|
||||||
'for script in "$1"/scripts/*.sh "$1"/world-seed/scripts/*.sh; do bash -n "$script"; done' \
|
'for script in "$1"/scripts/*.sh "$1"/world-seed/scripts/*.sh; do bash -n "$script"; done' \
|
||||||
_ "${source_root}"
|
_ "${source_root}"
|
||||||
|
run_gate linux_subcontrol_docker_backend \
|
||||||
|
"${source_root}/scripts/test-linux-subcontrol-docker-backend.sh"
|
||||||
run_gate auditable_line_coverage_100_percent \
|
run_gate auditable_line_coverage_100_percent \
|
||||||
bash -c '
|
bash -c '
|
||||||
cargo llvm-cov clean --workspace --manifest-path "$1/Cargo.toml"
|
cargo llvm-cov clean --workspace --manifest-path "$1/Cargo.toml"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "guanghu.os-control-architecture/v1",
|
"schema": "guanghu.os-control-architecture/v1",
|
||||||
"record_id": "HLP-GUANGHU-OS-CONTROL-001",
|
"record_id": "HLP-GUANGHU-OS-CONTROL-001",
|
||||||
"version": "2026-08-15.1",
|
"version": "2026-08-15.2",
|
||||||
"state": "CURRENT_CANONICAL",
|
"state": "CURRENT_CANONICAL",
|
||||||
"final_topology": "GUANGHU_MASTER_WITH_ON_DEMAND_LINUX_SUBCONTROL_AND_RESCUE",
|
"final_topology": "GUANGHU_MASTER_WITH_ON_DEMAND_LINUX_SUBCONTROL_AND_RESCUE",
|
||||||
"control_ownership": {
|
"control_ownership": {
|
||||||
|
|
@ -46,7 +46,9 @@
|
||||||
"typed_policy_authorization_and_rollback_binding": 100,
|
"typed_policy_authorization_and_rollback_binding": 100,
|
||||||
"target_readback_and_mandatory_reclaim_state_machine": 100,
|
"target_readback_and_mandatory_reclaim_state_machine": 100,
|
||||||
"declared_supervisor_core_line_and_function_coverage": 100,
|
"declared_supervisor_core_line_and_function_coverage": 100,
|
||||||
|
"docker_shadow_backend_source_and_isolation_contract": 100,
|
||||||
"real_isolated_linux_backend": 0,
|
"real_isolated_linux_backend": 0,
|
||||||
|
"jd_shadow_backend_physical_cycle": 0,
|
||||||
"independent_guanghu_first_boot_supervisor": 0,
|
"independent_guanghu_first_boot_supervisor": 0,
|
||||||
"jd_physical_deployment": 0
|
"jd_physical_deployment": 0
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,13 @@ test("current JD state stays transitional and cannot impersonate final master co
|
||||||
});
|
});
|
||||||
|
|
||||||
test("source lifecycle progress does not impersonate backend, boot, or JD deployment", () => {
|
test("source lifecycle progress does not impersonate backend, boot, or JD deployment", () => {
|
||||||
assert.equal(contract.version, "2026-08-15.1");
|
assert.equal(contract.version, "2026-08-15.2");
|
||||||
assert.equal(contract.implementation.guanghu_supervisor_lifecycle_contract_source, 100);
|
assert.equal(contract.implementation.guanghu_supervisor_lifecycle_contract_source, 100);
|
||||||
assert.equal(contract.implementation.target_readback_and_mandatory_reclaim_state_machine, 100);
|
assert.equal(contract.implementation.target_readback_and_mandatory_reclaim_state_machine, 100);
|
||||||
assert.equal(contract.implementation.declared_supervisor_core_line_and_function_coverage, 100);
|
assert.equal(contract.implementation.declared_supervisor_core_line_and_function_coverage, 100);
|
||||||
|
assert.equal(contract.implementation.docker_shadow_backend_source_and_isolation_contract, 100);
|
||||||
assert.equal(contract.implementation.real_isolated_linux_backend, 0);
|
assert.equal(contract.implementation.real_isolated_linux_backend, 0);
|
||||||
|
assert.equal(contract.implementation.jd_shadow_backend_physical_cycle, 0);
|
||||||
assert.equal(contract.implementation.independent_guanghu_first_boot_supervisor, 0);
|
assert.equal(contract.implementation.independent_guanghu_first_boot_supervisor, 0);
|
||||||
assert.equal(contract.implementation.jd_physical_deployment, 0);
|
assert.equal(contract.implementation.jd_physical_deployment, 0);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue