563 lines
30 KiB
Bash
563 lines
30 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Production-boundary test for the temporary blackbox source proof. Every
|
|
# external side effect is replaced only below the Kubernetes/root boundaries;
|
|
# the validator CLI and its production control flow remain real.
|
|
set -Eeuo pipefail
|
|
set +x
|
|
umask 077
|
|
|
|
readonly ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)"
|
|
readonly VALIDATOR="$ROOT/scripts/validate/validate-blackbox-edge-source.sh"
|
|
|
|
fail() { printf 'BLACKBOX EDGE SOURCE TEST FAILURE: %s\n' "$*" >&2; exit 1; }
|
|
assert_eq() { [[ "$1" == "$2" ]] || fail "$3: expected=$1 actual=$2"; }
|
|
assert_contains() { [[ "$1" == *"$2"* ]] || fail "$3"; }
|
|
assert_not_contains() { [[ "$1" != *"$2"* ]] || fail "$3"; }
|
|
assert_no_object_create() {
|
|
[[ "$1" != *' create --dry-run=server '* && "$1" != *' create -f '* ]] || fail "$2"
|
|
}
|
|
assert_no_name_delete() {
|
|
[[ "$1" != *' delete pod '* && "$1" != *' delete networkpolicy '* ]] || fail "$2"
|
|
}
|
|
|
|
[[ -f "$VALIDATOR" && ! -L "$VALIDATOR" ]] || fail 'production validator is missing (RED: create validate-blackbox-edge-source.sh)'
|
|
|
|
# RED 8: production uses / as its rollback anchor. A canonical absolute
|
|
# descendant of that anchor must remain valid without invoking sudo or any
|
|
# Kubernetes boundary; non-root fixture-anchor checks are exercised below.
|
|
production_root_anchor_rc=0
|
|
PLATFORM_BLACKBOX_EDGE_TEST_MODE=0 bash -c '
|
|
source "$1"
|
|
root_run() { "$@"; }
|
|
validate_root_chain /var/lib /
|
|
' bash "$VALIDATOR" || production_root_anchor_rc=$?
|
|
assert_eq 0 "$production_root_anchor_rc" 'production root anchor must accept a canonical descendant'
|
|
|
|
fixture_root="$(mktemp -d /tmp/platform-blackbox-edge-test.XXXXXX)"
|
|
chmod 0700 "$fixture_root"
|
|
trap 'rm -rf -- "$fixture_root"' EXIT
|
|
mkdir -p "$fixture_root/bin" "$fixture_root/state" "$fixture_root/active" \
|
|
"$fixture_root/rollbacks/observability-20260812T000000Z"
|
|
chmod 0700 "$fixture_root/bin" "$fixture_root/state" "$fixture_root/active" \
|
|
"$fixture_root/rollbacks" "$fixture_root/rollbacks/observability-20260812T000000Z"
|
|
cp -- "$ROOT/infrastructure/networking/host-nginx/learn-services-grafana-deny-guard.conf" \
|
|
"$fixture_root/active/learn-services"
|
|
chmod 0600 "$fixture_root/active/learn-services"
|
|
|
|
cat >"$fixture_root/bin/kubectl-proxy.py" <<'PY'
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import socket
|
|
import sys
|
|
|
|
sock_path = pathlib.Path(sys.argv[1])
|
|
state = pathlib.Path(sys.argv[2])
|
|
if sock_path.exists() or sock_path.is_symlink():
|
|
raise SystemExit(91)
|
|
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
server.bind(str(sock_path))
|
|
os.chmod(sock_path, 0o600)
|
|
server.listen(1)
|
|
connection, _ = server.accept()
|
|
request = b""
|
|
while b"\r\n\r\n" not in request:
|
|
piece = connection.recv(4096)
|
|
if not piece:
|
|
break
|
|
request += piece
|
|
head, _, body = request.partition(b"\r\n\r\n")
|
|
lines = head.decode("ascii", "replace").split("\r\n")
|
|
method, path, _ = lines[0].split(" ", 2)
|
|
length = 0
|
|
for line in lines[1:]:
|
|
if line.lower().startswith("content-length:"):
|
|
length = int(line.split(":", 1)[1].strip())
|
|
while len(body) < length:
|
|
piece = connection.recv(4096)
|
|
if not piece:
|
|
break
|
|
body += piece
|
|
try:
|
|
expected = json.loads(body.decode("utf-8"))["preconditions"]["uid"]
|
|
except Exception:
|
|
connection.sendall(b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n")
|
|
connection.close(); server.close(); raise SystemExit(0)
|
|
kind = "pod" if "/pods/" in path else "networkpolicy" if "/networkpolicies/" in path else ""
|
|
item_path = state / f"{kind}.json"
|
|
log = pathlib.Path(os.environ["BB_TEST_PROXY_LOG"])
|
|
with log.open("a", encoding="utf-8") as stream:
|
|
stream.write(f"{method} {path} uid={expected}\n")
|
|
if not kind or not item_path.exists():
|
|
connection.sendall(b"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n")
|
|
elif os.environ.get("BB_TEST_DELETE_REPLACE_KIND") == kind or os.environ.get("BB_TEST_FOREIGN_UID") == "1":
|
|
item = json.loads(item_path.read_text(encoding="utf-8"))
|
|
item["metadata"]["uid"] = "uid-external-replacement"
|
|
item["metadata"]["labels"]["platform.hyeonworks.com/source-proof-run"] = "foreign-run"
|
|
item_path.write_text(json.dumps(item), encoding="utf-8")
|
|
connection.sendall(b"HTTP/1.1 409 Conflict\r\nContent-Length: 0\r\n\r\n")
|
|
elif json.loads(item_path.read_text(encoding="utf-8"))["metadata"]["uid"] != expected:
|
|
connection.sendall(b"HTTP/1.1 409 Conflict\r\nContent-Length: 0\r\n\r\n")
|
|
elif os.environ.get("BB_TEST_DELETE_AMBIGUOUS_KIND") == kind:
|
|
item_path.unlink()
|
|
connection.close(); server.close(); raise SystemExit(0)
|
|
elif os.environ.get("BB_TEST_DELETE_READ_AMBIGUOUS_KIND") == kind:
|
|
(state / f"{kind}.delete-read-ambiguous").touch()
|
|
connection.sendall(b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n{\"status\":\"Success\"}")
|
|
elif os.environ.get("BB_TEST_DELETE_NEVER_DISAPPEARS_KIND") == kind:
|
|
connection.sendall(b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n{\"status\":\"Success\"}")
|
|
elif os.environ.get("BB_TEST_DELETE_TERMINATING_KIND") == kind:
|
|
pending_polls = int(os.environ.get("BB_TEST_DELETE_PENDING_POLLS", "1"))
|
|
if pending_polls < 1:
|
|
raise SystemExit(92)
|
|
(state / f"{kind}.delete-pending").write_text(str(pending_polls), encoding="utf-8")
|
|
connection.sendall(b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n{\"status\":\"Success\"}")
|
|
else:
|
|
item_path.unlink()
|
|
connection.sendall(b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n{\"status\":\"Success\"}")
|
|
connection.close()
|
|
server.close()
|
|
PY
|
|
|
|
cat >"$fixture_root/bin/kubectl" <<'KUBECTL'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
log_command() { printf '%q ' "$@" >>"${BB_TEST_LOG:?}"; printf '\n' >>"${BB_TEST_LOG:?}"; }
|
|
state_file() { printf '%s/%s.json\n' "${BB_TEST_STATE:?}" "$1"; }
|
|
|
|
render_object() {
|
|
local kind=$1 name=$2 run=$3 uid=$4 bad=${5:-} live_defaults=${6:-0} mutate=${7:-} policy_mutate=${8:-}
|
|
/usr/bin/python3 - "$kind" "$name" "$run" "$uid" "$bad" "$live_defaults" "$mutate" "$policy_mutate" <<'PY'
|
|
import json
|
|
import sys
|
|
kind, name, run, uid, bad, live_defaults, mutate, policy_mutate = sys.argv[1:]
|
|
labels = {
|
|
"app.kubernetes.io/managed-by": "platform-blackbox-edge-source",
|
|
"platform.hyeonworks.com/source-proof-run": run,
|
|
}
|
|
metadata = {"namespace": "observability", "name": name, "uid": uid, "labels": labels}
|
|
if kind == "networkpolicy":
|
|
spec = {
|
|
"podSelector": {"matchLabels": {"platform.hyeonworks.com/source-proof-run": run}},
|
|
"policyTypes": ["Egress"],
|
|
"egress": [
|
|
{"to": [{"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "kube-system"}}, "podSelector": {"matchLabels": {"k8s-app": "kube-dns"}}}], "ports": [{"protocol": "UDP", "port": 53}, {"protocol": "TCP", "port": 53}]},
|
|
{"to": [{"ipBlock": {"cidr": "192.168.0.107/32"}}], "ports": [{"protocol": "TCP", "port": 443}]},
|
|
],
|
|
}
|
|
item = {"apiVersion": "networking.k8s.io/v1", "kind": "NetworkPolicy", "metadata": metadata, "spec": spec}
|
|
else:
|
|
spec = {
|
|
"automountServiceAccountToken": False,
|
|
"restartPolicy": "Never",
|
|
"securityContext": {"runAsNonRoot": True, "runAsUser": 65534, "runAsGroup": 65534, "seccompProfile": {"type": "RuntimeDefault"}},
|
|
"containers": [{"name": "probe", "image": "docker.io/library/busybox:1.37.0@sha256:7a3ebe5bfd1a4a19797d20b0c0bb39d44393e9a03fd852c0865b0f540d868df0", "imagePullPolicy": "IfNotPresent", "command": ["sh", "-c", "sleep 120"], "securityContext": {"allowPrivilegeEscalation": False, "readOnlyRootFilesystem": True, "capabilities": {"drop": ["ALL"]}}}],
|
|
}
|
|
item = {"apiVersion": "v1", "kind": "Pod", "metadata": metadata, "spec": spec}
|
|
if live_defaults == "1":
|
|
spec.update({
|
|
"dnsPolicy": "ClusterFirst",
|
|
"enableServiceLinks": True,
|
|
"preemptionPolicy": "PreemptLowerPriority",
|
|
"priority": 0,
|
|
"schedulerName": "default-scheduler",
|
|
"serviceAccount": "default",
|
|
"serviceAccountName": "default",
|
|
"terminationGracePeriodSeconds": 30,
|
|
"tolerations": [
|
|
{"effect": "NoExecute", "key": "node.kubernetes.io/not-ready", "operator": "Exists", "tolerationSeconds": 300},
|
|
{"effect": "NoExecute", "key": "node.kubernetes.io/unreachable", "operator": "Exists", "tolerationSeconds": 300},
|
|
],
|
|
})
|
|
spec["containers"][0].update({
|
|
"resources": {},
|
|
"terminationMessagePath": "/dev/termination-log",
|
|
"terminationMessagePolicy": "File",
|
|
})
|
|
metadata.update({"creationTimestamp": "2026-08-12T17:20:51Z", "generation": 1})
|
|
item["status"] = {"phase": "Pending", "qosClass": "BestEffort"}
|
|
if mutate == "security":
|
|
spec["containers"][0]["securityContext"]["privileged"] = True
|
|
elif mutate == "command":
|
|
spec["containers"][0]["command"] = ["sh", "-c", "sleep 120; id"]
|
|
elif mutate == "image":
|
|
spec["containers"][0]["image"] = "docker.io/library/busybox:latest"
|
|
if (kind == "pod" and mutate == "label") or (kind == "networkpolicy" and policy_mutate == "label"):
|
|
labels["platform.hyeonworks.com/unowned"] = "unexpected"
|
|
if bad == kind:
|
|
item["metadata"]["labels"]["platform.hyeonworks.com/source-proof-run"] = "foreign-run"
|
|
print(json.dumps(item))
|
|
PY
|
|
}
|
|
|
|
read_manifest() {
|
|
local body kind name run
|
|
body="$(/bin/cat)"
|
|
kind="$(printf '%s\n' "$body" | /usr/bin/awk '/^kind: / { print $2; exit }')"
|
|
name="$(printf '%s\n' "$body" | /usr/bin/awk '/^ name: / { print $2; exit }')"
|
|
run="$(printf '%s\n' "$body" | /usr/bin/awk '/source-proof-run:/ { gsub(/"/, "", $2); print $2; exit }')"
|
|
case "$kind" in NetworkPolicy) printf 'networkpolicy|%s|%s\n' "$name" "$run" ;; Pod) printf 'pod|%s|%s\n' "$name" "$run" ;; *) exit 95 ;; esac
|
|
}
|
|
|
|
log_command "$@"
|
|
args=" $* "
|
|
if [[ "$args" == *' config current-context '* ]]; then printf 'fixture-context\n'; exit 0; fi
|
|
if [[ "$args" == *' get --raw=/readyz '* ]]; then printf 'ok\n'; exit 0; fi
|
|
if [[ "$args" == *' auth can-i '* ]]; then printf '%s\n' "${BB_TEST_AUTH:-yes}"; exit 0; fi
|
|
|
|
if [[ "$args" == *' create '* ]]; then
|
|
IFS='|' read -r kind name run <<<"$(read_manifest)"
|
|
mode=create
|
|
[[ "$args" == *' --dry-run=server '* ]] && mode=dry-run
|
|
printf '%s %s\n' "$kind" "$mode" >>"${BB_TEST_CREATE_LOG:?}"
|
|
uid="uid-${kind}-${run}"
|
|
bad="${BB_TEST_BAD_SPEC_KIND:-}"
|
|
if [[ "$mode" == dry-run ]]; then render_object "$kind" "$name" "$run" dry-run "$bad" "${BB_TEST_LIVE_DEFAULTED_POD_DRY_RUN:-0}" "${BB_TEST_MUTATE_POD_FIELD:-}" "${BB_TEST_MUTATE_NETWORKPOLICY_FIELD:-}"; exit 0; fi
|
|
path="$(state_file "$kind")"
|
|
[[ ! -e "$path" && ! -L "$path" ]] || exit 1
|
|
render_object "$kind" "$name" "$run" "$uid" "$bad" >"$path"
|
|
if [[ "${BB_TEST_CREATE_AMBIGUOUS_KIND:-}" == "$kind" || "${BB_TEST_CREATE_FAILURE_KIND:-}" == "$kind" ]]; then exit 1; fi
|
|
if [[ "${BB_TEST_BAD_RESPONSE_KIND:-}" == "$kind" ]]; then
|
|
render_object "$kind" "$name" "$run" "$uid" "$kind"
|
|
exit 0
|
|
fi
|
|
/bin/cat -- "$path"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$args" == *' get pod '* || "$args" == *' get networkpolicy '* ]]; then
|
|
if [[ "$args" =~ get\ (pod|networkpolicy)\ ([^[:space:]]+) ]]; then kind=${BASH_REMATCH[1]}; name=${BASH_REMATCH[2]}; else exit 96; fi
|
|
path="$(state_file "$kind")"
|
|
[[ ! -e "${BB_TEST_STATE:?}/${kind}.delete-read-ambiguous" ]] || exit 1
|
|
if [[ -e "$path" && ! -L "$path" ]]; then
|
|
if [[ "${BB_TEST_FOREIGN_UID:-0}" == 1 ]]; then
|
|
/usr/bin/python3 - "$path" <<'PY'
|
|
import json, pathlib, sys
|
|
p = pathlib.Path(sys.argv[1]); item = json.loads(p.read_text()); item["metadata"]["uid"] = "foreign-uid"; item["metadata"]["labels"]["platform.hyeonworks.com/source-proof-run"] = "foreign-run"; p.write_text(json.dumps(item))
|
|
PY
|
|
fi
|
|
if [[ -e "${BB_TEST_STATE:?}/${kind}.delete-pending" ]]; then
|
|
remaining="$(<"${BB_TEST_STATE:?}/${kind}.delete-pending")"
|
|
[[ "$remaining" =~ ^[1-9][0-9]*$ ]] || exit 99
|
|
remaining=$((remaining - 1))
|
|
/bin/cat -- "$path"
|
|
if (( remaining == 0 )); then
|
|
/bin/rm -f -- "${BB_TEST_STATE:?}/${kind}.delete-pending" "$path"
|
|
else
|
|
printf '%s\n' "$remaining" >"${BB_TEST_STATE:?}/${kind}.delete-pending"
|
|
fi
|
|
exit 0
|
|
fi
|
|
/bin/cat -- "$path"; exit 0
|
|
fi
|
|
[[ "$args" == *'--ignore-not-found'* ]] && exit 0
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$args" == *' wait --for=condition=Ready '* && "$args" == *' pod/'* ]]; then
|
|
if [[ -n "${BB_TEST_SCHEDULED_POD_NODE:-}" && -e "$(state_file pod)" ]]; then
|
|
/usr/bin/python3 - "$(state_file pod)" "${BB_TEST_SCHEDULED_POD_NODE}" <<'PY'
|
|
import json, pathlib, sys
|
|
p = pathlib.Path(sys.argv[1]); item = json.loads(p.read_text()); item["spec"]["nodeName"] = sys.argv[2]; p.write_text(json.dumps(item))
|
|
PY
|
|
fi
|
|
[[ -e "$(state_file pod)" ]]; exit $?
|
|
fi
|
|
if [[ "$args" == *' exec '* ]]; then
|
|
[[ "${BB_TEST_EXEC_SLEEP:-0}" == 1 ]] && /usr/bin/sleep 20
|
|
case "${BB_TEST_CLIENT_STATUS:-403}" in
|
|
403) printf 'HTTP/1.1 403 Forbidden\n' >&2 ;;
|
|
200) printf 'HTTP/1.1 200 OK\n' >&2 ;;
|
|
302) printf 'HTTP/1.1 302 Found\n' >&2 ;;
|
|
*) printf 'HTTP/1.1 500 Error\n' >&2 ;;
|
|
esac
|
|
exit "${BB_TEST_CLIENT_RC:-1}"
|
|
fi
|
|
if [[ "$args" == *' proxy '* ]]; then
|
|
socket=''
|
|
for argument in "$@"; do [[ "$argument" == --unix-socket=* ]] && socket=${argument#--unix-socket=}; done
|
|
[[ "$socket" == /tmp/platform-blackbox-edge-source.*/*.sock ]] || exit 97
|
|
exec /usr/bin/python3 "${BB_TEST_PROXY_HELPER:?}" "$socket" "${BB_TEST_STATE:?}"
|
|
fi
|
|
if [[ "$args" == *' delete '* || "$args" == *' apply '* || "$args" == *' replace '* || "$args" == *' patch '* ]]; then exit 98; fi
|
|
exit 0
|
|
KUBECTL
|
|
|
|
cat >"$fixture_root/bin/sudo" <<'SUDO'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
while [[ "${1:-}" == -n || "${1:-}" == -- ]]; do shift; done
|
|
command_path=${1:-}
|
|
[[ "$command_path" == /* ]] || { printf 'non-absolute sudo child: %s\n' "$command_path" >&2; exit 97; }
|
|
printf '%q ' "$@" >>"${BB_TEST_ROOT_LOG:?}"; printf '\n' >>"${BB_TEST_ROOT_LOG:?}"
|
|
if [[ "$command_path" == /usr/bin/awk ]]; then
|
|
printf '%q ' "$@" >>"${BB_TEST_SUDO_LOG:?}"; printf '\n' >>"${BB_TEST_SUDO_LOG:?}"
|
|
case "${BB_TEST_LOG_MATCHES:-1}" in
|
|
0) exit 0 ;;
|
|
1) printf '10.42.0.55 403\n10.42.0.55 403\n10.42.0.55 403\n' ;;
|
|
2) printf '10.42.0.55 403\n10.42.0.55 403\n10.42.0.55 403\n10.42.0.55 403\n' ;;
|
|
*) exit 98 ;;
|
|
esac
|
|
exit 0
|
|
fi
|
|
if [[ "$command_path" == /usr/bin/install ]]; then
|
|
shift
|
|
filtered=()
|
|
while (( $# )); do case "$1" in -o|-g) shift 2 ;; *) filtered+=("$1"); shift ;; esac; done
|
|
exec /usr/bin/install "${filtered[@]}"
|
|
fi
|
|
if [[ "$command_path" == /bin/ln && "${BB_TEST_PUBLISH_RACE:-0}" == 1 ]]; then
|
|
/bin/mkdir -p -- "${BB_TEST_PROOF:?}.race-dir"
|
|
/bin/ln -s -- "${BB_TEST_PROOF}.race-dir" "${BB_TEST_PROOF:?}"
|
|
fi
|
|
exec "$@"
|
|
SUDO
|
|
|
|
cat >"$fixture_root/bin/date" <<'DATE'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
if [[ ( "${BB_TEST_POST_PROMPT_ACTIVE_DRIFT:-0}" == 1 || "${BB_TEST_POST_PROMPT_PROOF_APPEARS:-0}" == 1 ) && ! -e "${BB_TEST_POST_PROMPT_MARKER:?}" ]]; then
|
|
: >"${BB_TEST_POST_PROMPT_MARKER:?}"
|
|
[[ "${BB_TEST_POST_PROMPT_ACTIVE_DRIFT:-0}" == 1 ]] && printf 'active drift\n' >"${BB_TEST_ACTIVE:?}"
|
|
[[ "${BB_TEST_POST_PROMPT_PROOF_APPEARS:-0}" == 1 ]] && printf 'unsafe existing proof\n' >"${BB_TEST_PROOF:?}"
|
|
fi
|
|
if [[ "$*" == *'+%Y-%m-%dT%H:%M:%SZ'* ]]; then printf '2026-08-12T00:00:00Z\n'; else /usr/bin/date "$@"; fi
|
|
DATE
|
|
|
|
cat >"$fixture_root/bin/sleep" <<'SLEEP'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
[[ "$#" == 1 && "$1" == 0.05 ]] || exit 97
|
|
exec /usr/bin/sleep 0.001
|
|
SLEEP
|
|
|
|
chmod 0700 "$fixture_root/bin"/*
|
|
|
|
proof="$fixture_root/rollbacks/observability-20260812T000000Z/blackbox-source-proof.env"
|
|
|
|
run_validator() {
|
|
local output_file="$fixture_root/out" rc=0
|
|
/bin/rm -f -- "$fixture_root/state"/* "$fixture_root/commands.log" "$fixture_root/creates.log" \
|
|
"$fixture_root/deletes.log" "$fixture_root/sudo.log" "$fixture_root/root.log" \
|
|
"$fixture_root/proxy.log" "$fixture_root/post-prompt"
|
|
: >"$fixture_root/commands.log"; : >"$fixture_root/creates.log"; : >"$fixture_root/deletes.log"
|
|
: >"$fixture_root/sudo.log"; : >"$fixture_root/root.log"; : >"$fixture_root/proxy.log"
|
|
PLATFORM_BLACKBOX_EDGE_TEST_MODE=1 \
|
|
PLATFORM_BLACKBOX_EDGE_KUBECTL="$fixture_root/bin/kubectl" \
|
|
PLATFORM_BLACKBOX_EDGE_SUDO="$fixture_root/bin/sudo" \
|
|
PLATFORM_BLACKBOX_EDGE_DATE="$fixture_root/bin/date" \
|
|
PLATFORM_BLACKBOX_EDGE_SLEEP="$fixture_root/bin/sleep" \
|
|
PLATFORM_BLACKBOX_EDGE_ACTIVE="$fixture_root/active/learn-services" \
|
|
PLATFORM_BLACKBOX_EDGE_ROLLBACK_BASE="$fixture_root/rollbacks" \
|
|
PLATFORM_BLACKBOX_EDGE_CONFIRMATION='PROVE BLACKBOX PRIVATE EDGE fixture-context' \
|
|
PLATFORM_OBSERVABILITY_ROLLBACK_ID=20260812T000000Z \
|
|
BB_TEST_LOG="$fixture_root/commands.log" BB_TEST_CREATE_LOG="$fixture_root/creates.log" \
|
|
BB_TEST_DELETE_LOG="$fixture_root/deletes.log" BB_TEST_SUDO_LOG="$fixture_root/sudo.log" \
|
|
BB_TEST_ROOT_LOG="$fixture_root/root.log" BB_TEST_PROXY_LOG="$fixture_root/proxy.log" \
|
|
BB_TEST_PROXY_HELPER="$fixture_root/bin/kubectl-proxy.py" BB_TEST_STATE="$fixture_root/state" \
|
|
BB_TEST_TMP="$fixture_root/state" BB_TEST_ACTIVE="$fixture_root/active/learn-services" \
|
|
BB_TEST_PROOF="$proof" BB_TEST_POST_PROMPT_MARKER="$fixture_root/post-prompt" \
|
|
BB_TEST_SCHEDULED_POD_NODE="${BB_TEST_SCHEDULED_POD_NODE:-}" \
|
|
BB_TEST_DELETE_TERMINATING_KIND="${BB_TEST_DELETE_TERMINATING_KIND:-}" \
|
|
BB_TEST_DELETE_PENDING_POLLS="${BB_TEST_DELETE_PENDING_POLLS:-1}" \
|
|
BB_TEST_DELETE_READ_AMBIGUOUS_KIND="${BB_TEST_DELETE_READ_AMBIGUOUS_KIND:-}" \
|
|
BB_TEST_DELETE_NEVER_DISAPPEARS_KIND="${BB_TEST_DELETE_NEVER_DISAPPEARS_KIND:-}" \
|
|
bash "$VALIDATOR" --execute --context fixture-context >"$output_file" 2>&1 || rc=$?
|
|
RUN_OUTPUT="$(<"$output_file")"; RUN_RC=$rc
|
|
}
|
|
|
|
# Production must reject escape hatches before a dry-run can contact a fake.
|
|
override_rc=0
|
|
PLATFORM_BLACKBOX_EDGE_ACTIVE=/tmp/override bash "$VALIDATOR" >"$fixture_root/override.out" 2>&1 || override_rc=$?
|
|
assert_eq 1 "$override_rc" 'production override boundary'
|
|
|
|
# Dry-run is read-only and contains neither token nor log payload.
|
|
: >"$fixture_root/commands.log"
|
|
dry_output="$(PLATFORM_BLACKBOX_EDGE_TEST_MODE=1 PLATFORM_BLACKBOX_EDGE_KUBECTL="$fixture_root/bin/kubectl" PLATFORM_BLACKBOX_EDGE_SUDO="$fixture_root/bin/sudo" PLATFORM_BLACKBOX_EDGE_DATE="$fixture_root/bin/date" PLATFORM_BLACKBOX_EDGE_SLEEP="$fixture_root/bin/sleep" PLATFORM_BLACKBOX_EDGE_ACTIVE="$fixture_root/active/learn-services" PLATFORM_BLACKBOX_EDGE_ROLLBACK_BASE="$fixture_root/rollbacks" BB_TEST_LOG="$fixture_root/commands.log" bash "$VALIDATOR")"
|
|
assert_contains "$dry_output" 'BLACKBOX_PRIVATE_EDGE_SOURCE_DRY_RUN=PASS' 'dry-run result label'
|
|
[[ ! -s "$fixture_root/commands.log" ]] || fail 'dry-run contacted Kubernetes'
|
|
assert_not_contains "$dry_output" 'hyeonworks_probe=' 'dry-run disclosed a token'
|
|
|
|
# Bad active SHA and authorization fail before any create.
|
|
printf 'wrong active source\n' >"$fixture_root/active/learn-services"
|
|
run_validator
|
|
assert_eq 1 "$RUN_RC" 'wrong active guard must use fixed failure status'
|
|
assert_no_object_create "$(<"$fixture_root/commands.log")" 'wrong active guard created an object'
|
|
cp -- "$ROOT/infrastructure/networking/host-nginx/learn-services-grafana-deny-guard.conf" "$fixture_root/active/learn-services"
|
|
BB_TEST_AUTH=no run_validator
|
|
assert_eq 1 "$RUN_RC" 'authorization denial must use fixed failure status'
|
|
assert_no_object_create "$(<"$fixture_root/commands.log")" 'authorization denial created an object'
|
|
|
|
# RED 1: BusyBox wget returns 1 for HTTP 403. The pinned client must pass only
|
|
# for exactly that result and an exact final parsed 403—not GNU wget's rc 8.
|
|
# RED 1a: Kubernetes server-side Pod dry-run adds its default fields. They must
|
|
# not make the validator reject an otherwise exact restricted probe Pod.
|
|
BB_TEST_LIVE_DEFAULTED_POD_DRY_RUN=1 BB_TEST_CLIENT_STATUS=403 BB_TEST_CLIENT_RC=1 run_validator
|
|
assert_eq 0 "$RUN_RC" 'live-defaulted Pod server dry-run must pass'
|
|
/bin/rm -f -- "$proof"
|
|
BB_TEST_MUTATE_NETWORKPOLICY_FIELD=label run_validator
|
|
assert_eq 1 "$RUN_RC" 'NetworkPolicy extra metadata label must fail closed'
|
|
unset BB_TEST_MUTATE_NETWORKPOLICY_FIELD
|
|
for mutated_field in label security command image; do
|
|
BB_TEST_LIVE_DEFAULTED_POD_DRY_RUN=1 BB_TEST_MUTATE_POD_FIELD="$mutated_field" run_validator
|
|
assert_eq 1 "$RUN_RC" "live-defaulted Pod $mutated_field mutation must fail closed"
|
|
done
|
|
unset BB_TEST_LIVE_DEFAULTED_POD_DRY_RUN BB_TEST_MUTATE_POD_FIELD
|
|
BB_TEST_CLIENT_STATUS=403 BB_TEST_CLIENT_RC=1 run_validator
|
|
assert_eq 0 "$RUN_RC" 'BusyBox 403 exit 1 with final parsed 403 must pass'
|
|
assert_contains "$RUN_OUTPUT" 'BLACKBOX PRIVATE EDGE SOURCE PASS' 'BusyBox success label'
|
|
assert_not_contains "$RUN_OUTPUT" 'hyeonworks_probe=' 'BusyBox success disclosed token'
|
|
assert_eq 1 "$(wc -l <"$fixture_root/sudo.log")" 'exactly one sudo awk invocation'
|
|
assert_eq $'networkpolicy dry-run\nnetworkpolicy create\npod dry-run\npod create' "$(<"$fixture_root/creates.log")" 'create-only safe order'
|
|
assert_contains "$(<"$fixture_root/commands.log")" ' proxy ' 'cleanup must use bounded raw-delete proxy'
|
|
assert_no_name_delete "$(<"$fixture_root/commands.log")" 'cleanup must never name-delete'
|
|
assert_contains "$(<"$fixture_root/root.log")" '/usr/bin/mktemp' 'proof must stage under root mktemp'
|
|
assert_contains "$(<"$fixture_root/root.log")" "--tmpdir=$fixture_root/rollbacks/observability-20260812T000000Z" 'proof staging directory'
|
|
assert_contains "$(<"$fixture_root/root.log")" '/bin/ln -nT --' 'proof publication must not dereference a raced destination'
|
|
assert_eq $'schema=platform-blackbox-source-v1\nrollback_id=20260812T000000Z\nnginx_sha256='"$(sha256sum "$fixture_root/active/learn-services" | awk '{print $1}')"$'\ntested_at_utc=2026-08-12T00:00:00Z\ngrafana_remote_addr=10.42.0.55\ngrafana_status=403\nstorage_admin_remote_addr=10.42.0.55\nstorage_admin_status=403\ndb_admin_remote_addr=10.42.0.55\ndb_admin_status=403' "$(<"$proof")" 'exact proof schema and order'
|
|
[[ "$(stat -c %a "$proof")" == 600 && "$(stat -c %h "$proof")" == 1 ]] || fail 'proof must be unlinked 0600 evidence'
|
|
/bin/rm -f -- "$proof"
|
|
|
|
BB_TEST_CLIENT_STATUS=403 BB_TEST_CLIENT_RC=0 run_validator
|
|
assert_eq 1 "$RUN_RC" '403 with non-BusyBox success rc must fail'
|
|
BB_TEST_CLIENT_STATUS=403 BB_TEST_CLIENT_RC=8 run_validator
|
|
assert_eq 1 "$RUN_RC" '403 with GNU-wget rc must fail'
|
|
BB_TEST_CLIENT_STATUS=200 BB_TEST_CLIENT_RC=1 run_validator
|
|
assert_eq 1 "$RUN_RC" '200 source masquerade must fail'
|
|
assert_not_contains "$RUN_OUTPUT" 'hyeonworks_probe=' 'masquerade failure disclosed token'
|
|
|
|
# RED 2: values that drift after confirmation but before the first create are
|
|
# rejected by a repeated commit gate.
|
|
BB_TEST_POST_PROMPT_ACTIVE_DRIFT=1 run_validator
|
|
assert_eq 1 "$RUN_RC" 'post-prompt active SHA drift must fail'
|
|
assert_no_object_create "$(<"$fixture_root/commands.log")" 'active drift reached create'
|
|
cp -- "$ROOT/infrastructure/networking/host-nginx/learn-services-grafana-deny-guard.conf" "$fixture_root/active/learn-services"
|
|
/bin/rm -f -- "$proof"
|
|
BB_TEST_POST_PROMPT_PROOF_APPEARS=1 run_validator
|
|
assert_eq 1 "$RUN_RC" 'post-prompt proof appearance must fail'
|
|
assert_no_object_create "$(<"$fixture_root/commands.log")" 'proof appearance reached create'
|
|
/bin/rm -f -- "$proof"
|
|
|
|
# RED 3/4: a nonzero create may have committed. Exact owned state is cleaned
|
|
# through UID-preconditioned raw DELETE; never apply/update or name-delete.
|
|
BB_TEST_CREATE_AMBIGUOUS_KIND=networkpolicy run_validator
|
|
assert_eq 1 "$RUN_RC" 'ambiguous NetworkPolicy create must fail closed'
|
|
[[ ! -e "$fixture_root/state/networkpolicy.json" && ! -e "$fixture_root/state/pod.json" ]] || fail 'owned ambiguous NetworkPolicy was not cleaned'
|
|
assert_contains "$(<"$fixture_root/proxy.log")" '/networkpolicies/' 'NetworkPolicy raw UID delete'
|
|
BB_TEST_CREATE_AMBIGUOUS_KIND=pod run_validator
|
|
assert_eq 1 "$RUN_RC" 'ambiguous Pod create must fail closed'
|
|
[[ ! -e "$fixture_root/state/networkpolicy.json" && ! -e "$fixture_root/state/pod.json" ]] || fail 'owned partial create was not cleaned'
|
|
assert_contains "$(<"$fixture_root/proxy.log")" '/networkpolicies/' 'partial cleanup NetworkPolicy raw UID delete'
|
|
assert_contains "$(<"$fixture_root/proxy.log")" '/pods/' 'partial cleanup Pod raw UID delete'
|
|
BB_TEST_BAD_RESPONSE_KIND=pod run_validator
|
|
assert_eq 1 "$RUN_RC" 'malformed successful Pod create response must reclassify and fail closed'
|
|
[[ ! -e "$fixture_root/state/networkpolicy.json" && ! -e "$fixture_root/state/pod.json" ]] || fail 'owned malformed-response objects were not cleaned'
|
|
|
|
# RED 4a: catch a cleanup wait that gives up while a normally terminating,
|
|
# exact-owned scheduled Pod remains present beyond 640 cleanup observations,
|
|
# then disappears while still requiring two consecutive absence reads.
|
|
export BB_TEST_SCHEDULED_POD_NODE=donghyeon-system-product-name \
|
|
BB_TEST_DELETE_TERMINATING_KIND=pod BB_TEST_DELETE_PENDING_POLLS=700
|
|
run_validator
|
|
assert_eq 0 "$RUN_RC" 'expected scheduled Pod node during terminating cleanup must pass'
|
|
/bin/rm -f -- "$proof"
|
|
export BB_TEST_SCHEDULED_POD_NODE=foreign-node BB_TEST_DELETE_TERMINATING_KIND=pod \
|
|
BB_TEST_DELETE_PENDING_POLLS=2
|
|
run_validator
|
|
assert_eq 1 "$RUN_RC" 'foreign scheduled Pod node must fail closed'
|
|
assert_not_contains "$RUN_OUTPUT" 'BLACKBOX PRIVATE EDGE SOURCE PASS' 'foreign scheduled Pod node became proof-command PASS'
|
|
[[ -e "$fixture_root/state/pod.json" ]] || fail 'foreign scheduled Pod disappeared'
|
|
unset BB_TEST_SCHEDULED_POD_NODE BB_TEST_DELETE_TERMINATING_KIND BB_TEST_DELETE_PENDING_POLLS
|
|
/bin/rm -f -- "$proof"
|
|
|
|
# Cleanup GET ambiguity must fail closed with the exact-owned object retained.
|
|
BB_TEST_DELETE_READ_AMBIGUOUS_KIND=pod run_validator
|
|
assert_eq 1 "$RUN_RC" 'cleanup API/read ambiguity must fail closed'
|
|
assert_not_contains "$RUN_OUTPUT" 'BLACKBOX PRIVATE EDGE SOURCE PASS' 'cleanup API/read ambiguity became proof-command PASS'
|
|
[[ -e "$fixture_root/state/pod.json" ]] || fail 'read-ambiguous exact-owned Pod disappeared'
|
|
/bin/rm -f -- "$proof"
|
|
|
|
# An exact-owned object that never disappears must exhaust the bounded wait,
|
|
# fail cleanup, and never become a proof-command PASS.
|
|
BB_TEST_DELETE_NEVER_DISAPPEARS_KIND=pod run_validator
|
|
assert_eq 1 "$RUN_RC" 'never-disappearing exact-owned Pod must fail bounded cleanup'
|
|
assert_not_contains "$RUN_OUTPUT" 'BLACKBOX PRIVATE EDGE SOURCE PASS' 'never-disappearing exact-owned Pod became proof-command PASS'
|
|
[[ -e "$fixture_root/state/pod.json" ]] || fail 'never-disappearing exact-owned Pod disappeared'
|
|
/bin/rm -f -- "$proof"
|
|
|
|
# A transport-ambiguous raw delete is accepted only after bounded stable
|
|
# absence; a UID/label replacement remains untouched and fails safely.
|
|
BB_TEST_DELETE_AMBIGUOUS_KIND=pod run_validator
|
|
assert_eq 0 "$RUN_RC" 'ambiguous raw delete with stable absence must pass'
|
|
/bin/rm -f -- "$proof"
|
|
BB_TEST_DELETE_REPLACE_KIND=pod run_validator
|
|
assert_eq 1 "$RUN_RC" 'replacement during raw delete must fail safely'
|
|
[[ -e "$fixture_root/state/pod.json" ]] || fail 'replacement was deleted'
|
|
/bin/rm -f -- "$proof" "$fixture_root/state/pod.json" "$fixture_root/state/networkpolicy.json"
|
|
|
|
# RED 5: a proof target that becomes a directory symlink only at publication
|
|
# must fail without allowing ln to create a hard link below that foreign path.
|
|
BB_TEST_PUBLISH_RACE=1 run_validator
|
|
assert_eq 1 "$RUN_RC" 'publication symlink race must fail safely'
|
|
[[ -L "$proof" ]] || fail 'publication race did not preserve the unsafe target'
|
|
[[ ! -e "$proof.race-dir"/* && ! -L "$proof.race-dir"/* ]] || fail 'publication followed a raced destination symlink'
|
|
/bin/rm -f -- "$proof"; /bin/rm -rf -- "$proof.race-dir"
|
|
|
|
# Existing proof symlink/hardlink and any rollback-root symlink ancestor are
|
|
# unsafe. The validator must leave all temporary objects uncreated.
|
|
ln -s /dev/null "$proof"
|
|
run_validator
|
|
assert_eq 1 "$RUN_RC" 'symlink proof target must fail safely'
|
|
/bin/rm -f -- "$proof"; printf 'existing\n' >"$proof"; ln "$proof" "$proof.link"
|
|
run_validator
|
|
assert_eq 1 "$RUN_RC" 'hard-linked proof target must fail safely'
|
|
/bin/rm -f -- "$proof" "$proof.link"
|
|
/bin/mv -- "$fixture_root/rollbacks" "$fixture_root/rollbacks-real"
|
|
/bin/ln -s -- "$fixture_root/rollbacks-real" "$fixture_root/rollbacks"
|
|
run_validator
|
|
assert_eq 1 "$RUN_RC" 'rollback root symlink ancestor must fail safely'
|
|
assert_no_object_create "$(<"$fixture_root/commands.log")" 'symlink ancestor reached create'
|
|
/bin/rm -f -- "$fixture_root/rollbacks"; /bin/mv -- "$fixture_root/rollbacks-real" "$fixture_root/rollbacks"
|
|
|
|
# A foreign object is never deleted, including on a signal path.
|
|
BB_TEST_FOREIGN_UID=1 run_validator
|
|
assert_eq 1 "$RUN_RC" 'foreign UID must fail safely'
|
|
assert_not_contains "$RUN_OUTPUT" 'BLACKBOX PRIVATE EDGE SOURCE PASS' 'foreign UID became proof-command PASS'
|
|
[[ -e "$fixture_root/state/networkpolicy.json" || -e "$fixture_root/state/pod.json" ]] || fail 'foreign object disappeared'
|
|
/bin/rm -f -- "$fixture_root/state"/*.json "$proof"
|
|
|
|
: >"$fixture_root/commands.log"; : >"$fixture_root/creates.log"; : >"$fixture_root/root.log"; : >"$fixture_root/proxy.log"
|
|
setsid env \
|
|
PLATFORM_BLACKBOX_EDGE_TEST_MODE=1 \
|
|
PLATFORM_BLACKBOX_EDGE_KUBECTL="$fixture_root/bin/kubectl" \
|
|
PLATFORM_BLACKBOX_EDGE_SUDO="$fixture_root/bin/sudo" \
|
|
PLATFORM_BLACKBOX_EDGE_DATE="$fixture_root/bin/date" \
|
|
PLATFORM_BLACKBOX_EDGE_SLEEP="$fixture_root/bin/sleep" \
|
|
PLATFORM_BLACKBOX_EDGE_ACTIVE="$fixture_root/active/learn-services" \
|
|
PLATFORM_BLACKBOX_EDGE_ROLLBACK_BASE="$fixture_root/rollbacks" \
|
|
PLATFORM_BLACKBOX_EDGE_CONFIRMATION='PROVE BLACKBOX PRIVATE EDGE fixture-context' \
|
|
PLATFORM_OBSERVABILITY_ROLLBACK_ID=20260812T000000Z \
|
|
BB_TEST_LOG="$fixture_root/commands.log" BB_TEST_CREATE_LOG="$fixture_root/creates.log" \
|
|
BB_TEST_DELETE_LOG="$fixture_root/deletes.log" BB_TEST_SUDO_LOG="$fixture_root/sudo.log" \
|
|
BB_TEST_ROOT_LOG="$fixture_root/root.log" BB_TEST_PROXY_LOG="$fixture_root/proxy.log" \
|
|
BB_TEST_PROXY_HELPER="$fixture_root/bin/kubectl-proxy.py" BB_TEST_STATE="$fixture_root/state" \
|
|
BB_TEST_TMP="$fixture_root/state" BB_TEST_ACTIVE="$fixture_root/active/learn-services" \
|
|
BB_TEST_PROOF="$proof" BB_TEST_POST_PROMPT_MARKER="$fixture_root/post-prompt" \
|
|
BB_TEST_CLIENT_STATUS=403 BB_TEST_CLIENT_RC=1 BB_TEST_EXEC_SLEEP=1 \
|
|
bash "$VALIDATOR" --execute --context fixture-context >"$fixture_root/signal.out" 2>&1 &
|
|
signal_pid=$!
|
|
for ((attempt = 0; attempt < 100; attempt++)); do
|
|
[[ "$(<"$fixture_root/commands.log")" == *' exec '* ]] && break
|
|
/usr/bin/sleep 0.02
|
|
done
|
|
[[ "$(<"$fixture_root/commands.log")" == *' exec '* ]] || fail 'signal fixture did not reach client request'
|
|
/bin/kill -TERM -- "-$signal_pid"
|
|
signal_rc=0; wait "$signal_pid" || signal_rc=$?
|
|
assert_eq 143 "$signal_rc" 'SIGTERM must retain fixed status'
|
|
assert_contains "$(<"$fixture_root/proxy.log")" '/pods/' 'signal raw Pod cleanup'
|
|
assert_contains "$(<"$fixture_root/proxy.log")" '/networkpolicies/' 'signal raw NetworkPolicy cleanup'
|
|
|
|
printf 'BLACKBOX EDGE SOURCE PRODUCTION-BOUNDARY TEST PASS\n'
|