Initial commit: Velocity-OS migration
This commit is contained in:
118
infrastructure/ingress-box/poll_and_transfer.sh
Normal file
118
infrastructure/ingress-box/poll_and_transfer.sh
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# Velocity-OS — Ingress Box: Air-Gap Transfer Agent
|
||||
# Runs on a LAN-connected node (Raspberry Pi / VM).
|
||||
# Polls ECR every 5 minutes for new signed images.
|
||||
# Verifies cosign signature. Transfers to air-gapped workstation.
|
||||
# Triggers K3s rolling restart on new image.
|
||||
#
|
||||
# Install as systemd service:
|
||||
# sudo cp poll_and_transfer.service /etc/systemd/system/
|
||||
# sudo systemctl enable --now poll_and_transfer
|
||||
# ============================================================
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────
|
||||
AWS_REGION="${AWS_REGION:-ap-south-1}"
|
||||
AWS_ACCOUNT_ID="${AWS_ACCOUNT_ID:?Must set AWS_ACCOUNT_ID}"
|
||||
ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
|
||||
REGISTRY_PREFIX="velocity-os"
|
||||
SERVICES=("core" "webos" "media-engine" "agents")
|
||||
|
||||
# Air-gapped workstation (LAN only — no internet)
|
||||
WORKSTATION_IP="${WORKSTATION_IP:-192.168.1.100}"
|
||||
WORKSTATION_USER="${WORKSTATION_USER:-ubuntu}"
|
||||
WORKSTATION_SSH_KEY="${WORKSTATION_SSH_KEY:-/home/ingress/.ssh/velocity_workstation_ed25519}"
|
||||
|
||||
# State file: tracks last-transferred digest per service
|
||||
STATE_DIR="/var/lib/velocity-ingress"
|
||||
mkdir -p "${STATE_DIR}"
|
||||
|
||||
# Temp dir for image tarballs
|
||||
TRANSFER_DIR="/tmp/velocity-transfer"
|
||||
mkdir -p "${TRANSFER_DIR}"
|
||||
|
||||
# ── Functions ─────────────────────────────────────────────────
|
||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"; }
|
||||
|
||||
get_latest_digest() {
|
||||
local repo="${REGISTRY_PREFIX}/$1"
|
||||
aws ecr describe-images \
|
||||
--repository-name "${repo}" \
|
||||
--image-ids imageTag=latest \
|
||||
--region "${AWS_REGION}" \
|
||||
--query 'imageDetails[0].imageDigest' \
|
||||
--output text 2>/dev/null || echo "NONE"
|
||||
}
|
||||
|
||||
transfer_image() {
|
||||
local svc="$1"
|
||||
local digest="$2"
|
||||
local full_image="${ECR_REGISTRY}/${REGISTRY_PREFIX}/${svc}@${digest}"
|
||||
local tar_file="${TRANSFER_DIR}/${svc}.tar"
|
||||
|
||||
log " [${svc}] Pulling from ECR..."
|
||||
docker pull "${ECR_REGISTRY}/${REGISTRY_PREFIX}/${svc}:latest"
|
||||
|
||||
log " [${svc}] Verifying cosign signature..."
|
||||
cosign verify \
|
||||
--certificate-identity-regexp ".*" \
|
||||
--certificate-oidc-issuer-regexp ".*" \
|
||||
"${full_image}" || {
|
||||
log " [${svc}] ERROR: Signature verification FAILED. Refusing transfer."
|
||||
return 1
|
||||
}
|
||||
|
||||
log " [${svc}] Saving image to tarball..."
|
||||
docker save "${ECR_REGISTRY}/${REGISTRY_PREFIX}/${svc}:latest" \
|
||||
-o "${tar_file}"
|
||||
|
||||
log " [${svc}] Transferring to workstation via SCP..."
|
||||
scp -i "${WORKSTATION_SSH_KEY}" \
|
||||
-o StrictHostKeyChecking=yes \
|
||||
"${tar_file}" \
|
||||
"${WORKSTATION_USER}@${WORKSTATION_IP}:/tmp/${svc}.tar"
|
||||
|
||||
log " [${svc}] Importing into K3s containerd + rolling restart..."
|
||||
ssh -i "${WORKSTATION_SSH_KEY}" \
|
||||
-o StrictHostKeyChecking=yes \
|
||||
"${WORKSTATION_USER}@${WORKSTATION_IP}" \
|
||||
"sudo k3s ctr images import /tmp/${svc}.tar && \
|
||||
sudo kubectl rollout restart deployment/${svc} -n velocity-os && \
|
||||
rm /tmp/${svc}.tar"
|
||||
|
||||
# Record transferred digest
|
||||
echo "${digest}" > "${STATE_DIR}/${svc}.last_digest"
|
||||
log " [${svc}] ✓ Transfer complete. Digest: ${digest}"
|
||||
rm -f "${tar_file}"
|
||||
}
|
||||
|
||||
# ── Main poll loop ────────────────────────────────────────────
|
||||
log "=== Velocity-OS Ingress Box polling ECR ==="
|
||||
|
||||
# Login to ECR (token expires every 12h; cron re-runs this)
|
||||
aws ecr get-login-password --region "${AWS_REGION}" | \
|
||||
docker login --username AWS --password-stdin "${ECR_REGISTRY}"
|
||||
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
log "[${svc}] Checking for updates..."
|
||||
|
||||
CURRENT_DIGEST=$(get_latest_digest "${svc}")
|
||||
LAST_DIGEST=$(cat "${STATE_DIR}/${svc}.last_digest" 2>/dev/null || echo "NONE")
|
||||
|
||||
if [[ "${CURRENT_DIGEST}" == "NONE" ]]; then
|
||||
log " [${svc}] No image found in ECR. Skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${CURRENT_DIGEST}" == "${LAST_DIGEST}" ]]; then
|
||||
log " [${svc}] Up to date. No transfer needed."
|
||||
continue
|
||||
fi
|
||||
|
||||
log " [${svc}] New digest detected: ${CURRENT_DIGEST}"
|
||||
transfer_image "${svc}" "${CURRENT_DIGEST}" || \
|
||||
log " [${svc}] Transfer FAILED. Will retry next cycle."
|
||||
done
|
||||
|
||||
log "=== Poll cycle complete ==="
|
||||
Reference in New Issue
Block a user