Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images. - Added symbols.json detailing function entry and sink points in the WordPress code. - Included runtime traces for function calls in both reachable and unreachable scenarios. - Developed OpenVEX files indicating vulnerability status and justification for both cases. - Updated README for evaluator harness to guide integration with scanner output.
170 lines
5.1 KiB
Bash
170 lines
5.1 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
cd "$SCRIPT_DIR"
|
|
|
|
COMPOSE_FILE=${COMPOSE_FILE:-"$SCRIPT_DIR/sealed-mode-compose.yml"}
|
|
PROJECT_NAME=${COMPOSE_PROJECT_NAME:-sealedmode}
|
|
NETWORK_NAME="${PROJECT_NAME}_sealed-ci"
|
|
ARTIFACT_ROOT=${ARTIFACT_ROOT:-"$SCRIPT_DIR/artifacts/sealed-mode-ci"}
|
|
STAMP=$(date -u +"%Y%m%dT%H%M%SZ")
|
|
OUT_DIR="$ARTIFACT_ROOT/$STAMP"
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
log() {
|
|
printf '[%s] %s\n' "$(date -u +%H:%M:%S)" "$*"
|
|
}
|
|
|
|
EXIT_CODE=0
|
|
IPTABLES_SNAPSHOT=""
|
|
|
|
cleanup() {
|
|
local exit_code=$?
|
|
log "Collecting docker compose logs"
|
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" logs >"$OUT_DIR/compose.log" 2>&1 || true
|
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" ps -a >"$OUT_DIR/compose.ps" 2>&1 || true
|
|
log "Tearing down sealed-mode stack"
|
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" down -v >"$OUT_DIR/docker-down.log" 2>&1 || true
|
|
if [[ -n "$IPTABLES_SNAPSHOT" && -f "$IPTABLES_SNAPSHOT" ]]; then
|
|
log "Restoring iptables snapshot"
|
|
sudo iptables-restore <"$IPTABLES_SNAPSHOT" || true
|
|
rm -f "$IPTABLES_SNAPSHOT"
|
|
fi
|
|
log "Artifacts stored at $OUT_DIR"
|
|
exit $exit_code
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
log "Pulling compose images (best effort)"
|
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" pull --ignore-pull-failures || true
|
|
|
|
log "Starting sealed-mode stack"
|
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" up -d --remove-orphans
|
|
|
|
wait_for_port() {
|
|
local port=$1
|
|
local label=$2
|
|
for attempt in $(seq 1 30); do
|
|
if curl -fsS --max-time 5 "http://127.0.0.1:${port}/healthz" >/dev/null 2>&1; then
|
|
log "$label responded on port $port"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
log "$label failed to respond on port $port"
|
|
return 1
|
|
}
|
|
|
|
wait_for_port 5088 "Authority" || EXIT_CODE=1
|
|
wait_for_port 6088 "Signer" || EXIT_CODE=1
|
|
wait_for_port 7088 "Attestor" || EXIT_CODE=1
|
|
|
|
log "Fetching probe helper image"
|
|
docker pull curlimages/curl:8.6.0 >/dev/null 2>&1 || true
|
|
|
|
log "Snapshotting iptables state"
|
|
IPTABLES_SNAPSHOT=$(mktemp)
|
|
sudo iptables-save >"$IPTABLES_SNAPSHOT"
|
|
|
|
log "Applying sealed-mode egress policy"
|
|
CHAIN="STELLAOPS_SEALED"
|
|
sudo iptables -N "$CHAIN" 2>/dev/null || sudo iptables -F "$CHAIN"
|
|
for cidr in 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16; do
|
|
sudo iptables -A "$CHAIN" -d "$cidr" -j RETURN
|
|
done
|
|
sudo iptables -A "$CHAIN" -j LOG --log-prefix "stellaops-sealed-deny " --log-level 4
|
|
sudo iptables -A "$CHAIN" -j DROP
|
|
sudo iptables -I DOCKER-USER 1 -j "$CHAIN"
|
|
sudo iptables -I OUTPUT 1 -j "$CHAIN"
|
|
|
|
check_health() {
|
|
local name=$1
|
|
local port=$2
|
|
local url="http://127.0.0.1:${port}/healthz"
|
|
local log_file="$OUT_DIR/${name}.health.log"
|
|
local status="fail"
|
|
for attempt in $(seq 1 20); do
|
|
if curl -fsS --max-time 5 "$url" >"$log_file" 2>&1; then
|
|
status="pass"
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
if [[ "$status" == "pass" ]]; then
|
|
log "$name health check succeeded"
|
|
else
|
|
log "$name health check failed"
|
|
EXIT_CODE=1
|
|
fi
|
|
local upper
|
|
upper=$(echo "$name" | tr '[:lower:]' '[:upper:]')
|
|
eval "${upper}_HEALTH_STATUS=$status"
|
|
eval "${upper}_HEALTH_URL=$url"
|
|
}
|
|
|
|
check_health authority 5088
|
|
check_health signer 6088
|
|
check_health attestor 7088
|
|
|
|
log "Running egress probe via docker network $NETWORK_NAME"
|
|
EGRESS_JSON="$OUT_DIR/egress-probe.json"
|
|
if python3 "$SCRIPT_DIR/egress_probe.py" --network "$NETWORK_NAME" --image curlimages/curl:8.6.0 --timeout 8 --output "$EGRESS_JSON"; then
|
|
EGRESS_STATUS="pass"
|
|
else
|
|
EGRESS_STATUS="fail"
|
|
EXIT_CODE=1
|
|
fi
|
|
|
|
log "Dumping iptables counters"
|
|
sudo iptables -v -x -L DOCKER-USER >"$OUT_DIR/iptables-docker-user.txt"
|
|
sudo iptables -v -x -L OUTPUT >"$OUT_DIR/iptables-output.txt"
|
|
|
|
log "Recording summary JSON"
|
|
export PROJECT_NAME NETWORK_NAME EGRESS_STATUS EGRESS_JSON
|
|
export AUTHORITY_HEALTH_STATUS SIGNER_HEALTH_STATUS ATTESTOR_HEALTH_STATUS
|
|
export AUTHORITY_HEALTH_URL SIGNER_HEALTH_URL ATTESTOR_HEALTH_URL
|
|
python3 - <<'PY' >"$OUT_DIR/authority-sealed-ci.json"
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
summary = {
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
"project": os.environ.get("PROJECT_NAME"),
|
|
"network": os.environ.get("NETWORK_NAME"),
|
|
"health": {
|
|
"authority": {
|
|
"status": os.environ.get("AUTHORITY_HEALTH_STATUS", "unknown"),
|
|
"url": os.environ.get("AUTHORITY_HEALTH_URL"),
|
|
"log": "authority.health.log",
|
|
},
|
|
"signer": {
|
|
"status": os.environ.get("SIGNER_HEALTH_STATUS", "unknown"),
|
|
"url": os.environ.get("SIGNER_HEALTH_URL"),
|
|
"log": "signer.health.log",
|
|
},
|
|
"attestor": {
|
|
"status": os.environ.get("ATTESTOR_HEALTH_STATUS", "unknown"),
|
|
"url": os.environ.get("ATTESTOR_HEALTH_URL"),
|
|
"log": "attestor.health.log",
|
|
},
|
|
},
|
|
"egressProbe": {
|
|
"status": os.environ.get("EGRESS_STATUS", "unknown"),
|
|
"report": os.path.basename(os.environ.get("EGRESS_JSON", "egress-probe.json")),
|
|
},
|
|
}
|
|
json.dump(summary, sys.stdout, indent=2)
|
|
print()
|
|
PY
|
|
|
|
if [[ $EXIT_CODE -eq 0 ]]; then
|
|
log "Sealed-mode CI run completed successfully"
|
|
else
|
|
log "Sealed-mode CI run completed with failures"
|
|
fi
|
|
|
|
exit $EXIT_CODE
|