Files
Velocity-OS/infrastructure/ingress-box/poll_and_transfer.sh

119 lines
4.3 KiB
Bash

#!/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 ==="