171 lines
5.8 KiB
Bash
Executable File
171 lines
5.8 KiB
Bash
Executable File
#!/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 <<USAGE
|
|
Usage: $0 <TAR already uploaded to RouterOS>
|
|
|
|
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=<TAR>
|
|
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" <<RSC
|
|
# Image-only Suricata container upgrade.
|
|
# This script intentionally does not modify networking, sniffer, firewall,
|
|
# envlist definitions. Mounts are normalized to the single persistent /data volume.
|
|
|
|
:foreach c in=[/container/find where name~"^suricata_"] do={
|
|
/container/set \$c start-on-boot=no
|
|
}
|
|
|
|
:local running [/container/find where name~"^suricata_" and status="running"]
|
|
:if ([:len \$running] > 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."
|