#!/bin/sh set -eu cd "$(dirname "$0")/.." CONFIG_FILE="${DEPLOY_CONFIG:-deploy-routeros.env}" if [ -f "$CONFIG_FILE" ]; then # shellcheck disable=SC1090 case "$CONFIG_FILE" in /*) . "$CONFIG_FILE" ;; *) . "./$CONFIG_FILE" ;; esac fi VERSION="$(tr -d '[:space:]' < VERSION)" [ -n "$VERSION" ] || { echo "VERSION is empty" >&2; exit 2; } case "$VERSION" in *[!A-Za-z0-9._-]*) echo "VERSION contains unsupported characters: $VERSION" >&2; exit 2 ;; esac CONTAINER_NAME="suricata_${VERSION}" ROOT_DIR="/containers/${CONTAINER_NAME}/root" : "${ROUTER_HOST:=192.168.88.1}" : "${ROUTER_USER:=admin}" : "${ROUTER_PORT:=22}" : "${ROUTER_IDENTITY_FILE:=}" : "${CONTAINER_VETH:=veth-ids}" : "${CONTAINER_ENVLIST:=IDS_ENV}" : "${CONTAINER_MOUNTLIST:=IDS_MOUNTS}" : "${ROUTER_DISK:=disk1}" usage() { cat < This is an image-only container upgrade. It DOES NOT change: - bridge/IP/NAT/veth configuration, - TZSP/sniffer configuration, - firewall or REST configuration, - envlist definitions. It normalizes the mount list to one persistent /data mount and then creates: name=${CONTAINER_NAME} file= root-dir=${ROOT_DIR} and reuses: interface=${CONTAINER_VETH} envlist=${CONTAINER_ENVLIST} mountlists=${CONTAINER_MOUNTLIST} -> ${ROUTER_DISK}/containers/suricata-data:/data USAGE } IMAGE_TAR_ROS="${1:-}" if [ -z "$IMAGE_TAR_ROS" ]; then usage >&2 exit 2 fi case "$IMAGE_TAR_ROS" in *.tar) ;; *) echo "RouterOS image path must point to a .tar file: $IMAGE_TAR_ROS" >&2; exit 2 ;; esac command -v ssh >/dev/null 2>&1 || { echo "ssh is required" >&2; exit 2; } command -v scp >/dev/null 2>&1 || { echo "scp is required" >&2; exit 2; } case "$ROUTER_PORT" in *[!0-9]*|'') echo "ROUTER_PORT must be numeric" >&2; exit 2 ;; esac for value in "$IMAGE_TAR_ROS" "$CONTAINER_VETH" "$CONTAINER_ENVLIST" "$CONTAINER_MOUNTLIST" "$ROUTER_DISK"; do case "$value" in *'"'*|*'\\'*|*'$'*|*';'*|*'`'*) echo "Unsupported character in RouterOS value: $value" >&2; exit 3 ;; esac done SSH_TARGET="${ROUTER_USER}@${ROUTER_HOST}" ssh_run() { if [ -n "$ROUTER_IDENTITY_FILE" ]; then ssh -i "$ROUTER_IDENTITY_FILE" -p "$ROUTER_PORT" "$SSH_TARGET" "$1" else ssh -p "$ROUTER_PORT" "$SSH_TARGET" "$1" fi } printf '[upgrade] version: %s\n' "$VERSION" printf '[upgrade] new container: %s\n' "$CONTAINER_NAME" printf '[upgrade] image on RouterOS: %s\n' "$IMAGE_TAR_ROS" printf '[upgrade] root-dir: %s\n' "$ROOT_DIR" printf '[upgrade] reusing interface/env and normalizing mounts: %s / %s / %s\n' "$CONTAINER_VETH" "$CONTAINER_ENVLIST" "$CONTAINER_MOUNTLIST" echo '[upgrade] read-only preflight' ssh_run '/container/print' >/dev/null ssh_run "/file/print without-paging where name=\"${IMAGE_TAR_ROS}\"" | grep -F "$IMAGE_TAR_ROS" >/dev/null 2>&1 || { echo "Image TAR not found on RouterOS: $IMAGE_TAR_ROS" >&2 echo "Upload it first with scripts/upload-routeros-image.sh." >&2 exit 4 } ssh_run "/interface/veth/print without-paging where name=\"${CONTAINER_VETH}\"" | grep -F "$CONTAINER_VETH" >/dev/null 2>&1 || { echo "Existing veth not found: $CONTAINER_VETH" >&2 echo "Run the normal deploy once before using image-only upgrades." >&2 exit 5 } if ! ssh_run "/container/envs/print without-paging where list=\"${CONTAINER_ENVLIST}\"" | grep -F "$CONTAINER_ENVLIST" >/dev/null 2>&1; then echo "Existing envlist not found or empty: $CONTAINER_ENVLIST" >&2 exit 5 fi if ssh_run "/container/print without-paging where name=\"${CONTAINER_NAME}\"" | grep -F "$CONTAINER_NAME" >/dev/null 2>&1; then echo "Container already exists: $CONTAINER_NAME" >&2 echo "Bump VERSION or remove that container explicitly before retrying." >&2 exit 6 fi DEPLOY_ID="$(date -u +%Y%m%d%H%M%S)" mkdir -p build LOCAL_RSC="build/upgrade-${CONTAINER_NAME}-${DEPLOY_ID}.rsc" REMOTE_RSC_NAME="upgrade-${CONTAINER_NAME}-${DEPLOY_ID}.rsc" cat > "$LOCAL_RSC" < 0) do={ /container/stop \$running :delay 3s } /container/mounts/remove [find where list="${CONTAINER_MOUNTLIST}"] /container/mounts/add list="${CONTAINER_MOUNTLIST}" src="${ROUTER_DISK}/containers/suricata-data" dst=/data /container/add name="${CONTAINER_NAME}" file="${IMAGE_TAR_ROS}" interface="${CONTAINER_VETH}" root-dir="${ROOT_DIR}" mountlists="${CONTAINER_MOUNTLIST}" envlist="${CONTAINER_ENVLIST}" start-on-boot=yes logging=yes :local tries 0 :while (\$tries < 180) do={ :if ([:len [/container/find where name="${CONTAINER_NAME}" and status="stopped"]] > 0) do={ :set tries 999 } else={ :delay 2s :set tries (\$tries + 1) } } :if ([:len [/container/find where name="${CONTAINER_NAME}" and status="stopped"]] = 0) do={ :error "Container extraction did not reach stopped state" } /container/start [find where name="${CONTAINER_NAME}"] :delay 5s /container/print detail where name="${CONTAINER_NAME}" RSC if [ -n "$ROUTER_IDENTITY_FILE" ]; then scp -i "$ROUTER_IDENTITY_FILE" -P "$ROUTER_PORT" "$LOCAL_RSC" "${SSH_TARGET}:/${REMOTE_RSC_NAME}" else scp -P "$ROUTER_PORT" "$LOCAL_RSC" "${SSH_TARGET}:/${REMOTE_RSC_NAME}" fi echo "[upgrade] creating ${CONTAINER_NAME} without touching existing network configuration" ssh_run "/import file-name=\"${REMOTE_RSC_NAME}\"" ssh_run "/file/remove [find where name=\"${REMOTE_RSC_NAME}\"]" || true echo '[upgrade] done' echo "New container: ${CONTAINER_NAME}" echo "Root dir: ${ROOT_DIR}" echo "Older suricata_* containers were left in place, stopped and start-on-boot=no."