deploy stack
This commit is contained in:
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== 1. Verifying rootless podman ==="
|
||||
PODMAN_USER=$(whoami)
|
||||
if ! loginctl show-user ${PODMAN_USER} | grep -q "Linger=yes"; then
|
||||
echo "Error: Linger is not enabled for user ${PODMAN_USER}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== 2. Ansible: WireGuard ==="
|
||||
ansible-playbook -i ansible/inventory.yml ansible/wireguard.yml
|
||||
|
||||
echo "Client configs generated at: $(realpath ./ansible/../generated/clients)"
|
||||
echo "Please securely copy these to your client devices."
|
||||
|
||||
echo "=== 3. Verify wg0 ==="
|
||||
if ! wg show wg0 >/dev/null 2>&1; then
|
||||
echo "Error: wg0 interface is not up"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== 4. Ansible: Networks ==="
|
||||
ansible-playbook -i ansible/inventory.yml ansible/networks.yml
|
||||
|
||||
echo "=== 5. Materialize .env ==="
|
||||
gcloud secrets versions access latest --secret=admin-env > .env
|
||||
chmod 600 .env
|
||||
|
||||
echo "=== 6. Materialize Caddy SA Key ==="
|
||||
mkdir -p ./secrets
|
||||
gcloud secrets versions access latest --secret=admin-caddy-dns-sa-key > ./secrets/caddy-dns-sa.json
|
||||
chmod 600 ./secrets/caddy-dns-sa.json
|
||||
|
||||
echo "=== 7. Podman Compose Build ==="
|
||||
podman compose build
|
||||
|
||||
echo "=== 8. Podman Compose Pull ==="
|
||||
podman compose pull
|
||||
|
||||
echo "=== 9. Podman Compose Up ==="
|
||||
podman compose up -d
|
||||
|
||||
echo "=== 10. Wait for services ==="
|
||||
echo "Waiting up to 120s for services to become healthy..."
|
||||
sleep 10 # Let them start
|
||||
|
||||
echo "=== 11. Verify ==="
|
||||
./scripts/verify.sh
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SRC_VOLUME="admin-sqlite"
|
||||
DEST_DIR="/var/backups/admin-sqlite"
|
||||
TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
DEST_FILE="${DEST_DIR}/admin-${TIMESTAMP}.sqlite"
|
||||
|
||||
mkdir -p "${DEST_DIR}"
|
||||
|
||||
# Use sqlite3 .backup for an atomic snapshot
|
||||
podman run --rm \
|
||||
-v "${SRC_VOLUME}:/data:ro" \
|
||||
-v "${DEST_DIR}:/out" \
|
||||
docker.io/keinos/sqlite3:3.42.0 \
|
||||
sqlite3 /data/admin.db ".backup '/out/admin-${TIMESTAMP}.sqlite'"
|
||||
|
||||
# Retain last 7 snapshots locally
|
||||
ls -1t "${DEST_DIR}"/admin-*.sqlite | tail -n +8 | xargs -r rm
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
WG_SERVER_IP="${WG_SERVER_IP:-10.42.0.1}"
|
||||
HOST_PUBLIC_IP=$(curl -s ifconfig.me || echo "127.0.0.1")
|
||||
|
||||
echo "Running verification checks..."
|
||||
|
||||
# 1. WG interface up
|
||||
if wg show wg0 >/dev/null 2>&1 && wg show wg0 peers | grep -q .; then
|
||||
echo "[ok] WG interface wg0 is up and has peers"
|
||||
else
|
||||
echo "[fail] WG interface wg0 is down or has no peers"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. WG IP bound
|
||||
if ip -o addr show wg0 | grep -q "${WG_SERVER_IP}"; then
|
||||
echo "[ok] WG interface wg0 bound to ${WG_SERVER_IP}"
|
||||
else
|
||||
echo "[fail] WG interface wg0 is not bound to ${WG_SERVER_IP}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Caddy listening on WG IP, NOT public IP
|
||||
if ss -tlnp | grep -E ':443\b' | grep -q "${WG_SERVER_IP}"; then
|
||||
if ss -tlnp | grep -E ':443\b' | grep -q -E "0\.0\.0\.0|::|\*"; then
|
||||
echo "[fail] Caddy is bound to public IP"
|
||||
exit 1
|
||||
else
|
||||
echo "[ok] Caddy is bound only to WG IP"
|
||||
fi
|
||||
else
|
||||
echo "[fail] Caddy is not bound to ${WG_SERVER_IP}:443"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. Admin endpoint NOT reachable from public
|
||||
if curl --max-time 3 -k https://${HOST_PUBLIC_IP}/ >/dev/null 2>&1; then
|
||||
echo "[fail] Admin endpoint is reachable from public IP"
|
||||
exit 1
|
||||
else
|
||||
echo "[ok] Admin endpoint is not reachable from public IP"
|
||||
fi
|
||||
|
||||
# 5. Networks present
|
||||
if podman network inspect admin-edge >/dev/null 2>&1 && podman network inspect stoat-shared >/dev/null 2>&1; then
|
||||
echo "[ok] Podman networks admin-edge and stoat-shared exist"
|
||||
else
|
||||
echo "[fail] Required podman networks are missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. All expected services healthy
|
||||
SERVICES=("admin-stack-caddy-1" "admin-stack-admin-frontend-1" "admin-stack-admin-api-1")
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if podman ps --format "{{.Names}}" | grep -q "${service}"; then
|
||||
echo "[ok] Service ${service} is running"
|
||||
else
|
||||
echo "[fail] Service ${service} is not running"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 7. admin-api can reach MongoDB
|
||||
API_CONTAINER=$(podman ps -q -f name=admin-stack-admin-api-1)
|
||||
if podman exec "${API_CONTAINER}" curl -s http://localhost:3000/health >/dev/null 2>&1; then
|
||||
echo "[ok] admin-api healthcheck passed"
|
||||
else
|
||||
echo "[fail] admin-api healthcheck failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 8. No unexpected host ports bound
|
||||
if podman ps --format '{{.Ports}}' | grep -v "${WG_SERVER_IP}" | grep -q ":"; then
|
||||
echo "[fail] Unexpected ports bound"
|
||||
podman ps --format '{{.Names}}: {{.Ports}}'
|
||||
exit 1
|
||||
else
|
||||
echo "[ok] No unexpected host ports bound"
|
||||
fi
|
||||
|
||||
echo "All checks passed!"
|
||||
Reference in New Issue
Block a user