poc2_worked

This commit is contained in:
Mateusz Gruszczyński
2026-08-15 18:29:36 +02:00
parent fc3a2944b2
commit 71b6c0d86f
62 changed files with 9112 additions and 375 deletions
+86 -9
View File
@@ -37,18 +37,44 @@ ROOT_DIR="/containers/${CONTAINER_NAME}/root"
: "${CONFIGURE_SNIFFER:=true}"
: "${START_SNIFFER:=true}"
: "${SURICATA_HOME_NET:=[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]}"
: "${MONITORED_NETWORKS:=192.168.100.0/24}"
: "${MONITORED_NETWORKS:=192.168.0.0/16,10.0.0.0/8,172.16.0.0/12}"
: "${SURICATA_LOG_MAX_MB:=512}"
: "${AUTO_BLOCK:=false}"
: "${AUTO_BLOCK_MAX_SEVERITY:=1}"
: "${BLOCK_TIMEOUT:=1h}"
: "${UPDATE_RULES_ON_START:=false}"
: "${SURICATA_PERSIST_LIB_DIR:=/data/lib/suricata}"
: "${RULE_UPDATE_INTERVAL_HOURS:=24}"
: "${ALERT_RETENTION_DAYS:=14}"
: "${REDIS_MANAGED:=true}"
: "${REDIS_DATA_DIR:=/data/redis}"
: "${REDIS_PORT:=6379}"
: "${REDIS_MAXMEMORY_MB:=0}"
: "${REDIS_SNAPSHOT_SECONDS:=1800}"
: "${REDIS_AOF:=true}"
: "${TRAFFIC_RETENTION_HOURS:=24}"
: "${TRAFFIC_MAX_EVENTS:=0}"
: "${TRAFFIC_MEMORY_EVENTS:=0}"
: "${WEBSOCKET_QUEUE_SIZE:=512}"
: "${LIVE_FLOW_UPDATE_SECONDS:=2.0}"
: "${ALERT_MAX_SEVERITY:=2}"
: "${ALERT_DEDUP_WINDOW_SECONDS:=300}"
: "${ALERT_IGNORE_SIDS:=1000001}"
: "${ALERT_IGNORE_CATEGORIES:=}"
: "${ADMIN_TOKEN:=}"
: "${ADMIN_USERNAME:=admin}"
: "${ADMIN_PASSWORD:=}"
: "${SESSION_HOURS:=168}"
: "${SESSION_COOKIE_SECURE:=false}"
: "${ANALYTICS_SNAPSHOT_INTERVAL_SECONDS:=60}"
: "${NDR_ENABLED:=true}"
: "${NDR_CORRELATION_WINDOW_SECONDS:=1800}"
: "${BEHAVIOR_MIN_OBSERVATIONS:=50}"
: "${NDR_AUTO_BLOCK:=false}"
: "${NDR_AUTO_BLOCK_RISK:=92}"
: "${ROUTEROS_INVENTORY_INTERVAL_SECONDS:=300}"
: "${NOTIFY_WEBHOOK_URL:=}"
: "${NOTIFY_MIN_RISK:=80}"
: "${NOTIFY_TIMEOUT_SECONDS:=5}"
: "${CREATE_REST_USER:=false}"
: "${ENABLE_WWW_SSL:=false}"
: "${ROUTEROS_REST_USER:=suricata-api}"
@@ -98,6 +124,28 @@ esac
case "$TZSP_PORT" in
*[!0-9]*|'') echo "TZSP_PORT must be numeric" >&2; exit 2 ;;
esac
for numeric_pair in \
"REDIS_PORT=$REDIS_PORT" \
"SURICATA_LOG_MAX_MB=$SURICATA_LOG_MAX_MB" \
"REDIS_MAXMEMORY_MB=$REDIS_MAXMEMORY_MB" \
"REDIS_SNAPSHOT_SECONDS=$REDIS_SNAPSHOT_SECONDS" \
"TRAFFIC_RETENTION_HOURS=$TRAFFIC_RETENTION_HOURS" \
"TRAFFIC_MAX_EVENTS=$TRAFFIC_MAX_EVENTS" \
"TRAFFIC_MEMORY_EVENTS=$TRAFFIC_MEMORY_EVENTS" \
"WEBSOCKET_QUEUE_SIZE=$WEBSOCKET_QUEUE_SIZE" \
"SESSION_HOURS=$SESSION_HOURS" \
"ANALYTICS_SNAPSHOT_INTERVAL_SECONDS=$ANALYTICS_SNAPSHOT_INTERVAL_SECONDS" \
"NDR_CORRELATION_WINDOW_SECONDS=$NDR_CORRELATION_WINDOW_SECONDS" \
"BEHAVIOR_MIN_OBSERVATIONS=$BEHAVIOR_MIN_OBSERVATIONS" \
"NDR_AUTO_BLOCK_RISK=$NDR_AUTO_BLOCK_RISK" \
"ROUTEROS_INVENTORY_INTERVAL_SECONDS=$ROUTEROS_INVENTORY_INTERVAL_SECONDS" \
"NOTIFY_MIN_RISK=$NOTIFY_MIN_RISK" \
"NOTIFY_TIMEOUT_SECONDS=$NOTIFY_TIMEOUT_SECONDS"
do
case "${numeric_pair#*=}" in
*[!0-9]*|'') echo "${numeric_pair%%=*} must be numeric" >&2; exit 2 ;;
esac
done
check_ros_value() {
label="$1"
@@ -129,10 +177,16 @@ for pair in \
"BLOCK_TIMEOUT=$BLOCK_TIMEOUT" \
"ALERT_IGNORE_SIDS=$ALERT_IGNORE_SIDS" \
"ALERT_IGNORE_CATEGORIES=$ALERT_IGNORE_CATEGORIES" \
"ADMIN_TOKEN=$ADMIN_TOKEN" \
"ADMIN_USERNAME=$ADMIN_USERNAME" \
"ADMIN_PASSWORD=$ADMIN_PASSWORD" \
"SESSION_COOKIE_SECURE=$SESSION_COOKIE_SECURE" \
"REDIS_DATA_DIR=$REDIS_DATA_DIR" \
"SURICATA_PERSIST_LIB_DIR=$SURICATA_PERSIST_LIB_DIR" \
"NOTIFY_WEBHOOK_URL=$NOTIFY_WEBHOOK_URL" \
"ROUTEROS_REST_USER=$ROUTEROS_REST_USER" \
"ROUTEROS_REST_PASSWORD=$ROUTEROS_REST_PASSWORD" \
"ROUTEROS_ADDRESS_LIST=$ROUTEROS_ADDRESS_LIST"
"ROUTEROS_ADDRESS_LIST=$ROUTEROS_ADDRESS_LIST" \
"LIVE_FLOW_UPDATE_SECONDS=$LIVE_FLOW_UPDATE_SECONDS"
do
check_ros_value "${pair%%=*}" "${pair#*=}"
done
@@ -191,8 +245,6 @@ REMOTE_RSC_SCP="${ROUTER_SCP_DIR%/}/${REMOTE_RSC_NAME}"
# Keep persistent application state stable between versioned containers.
DATA_DIR="${ROUTER_DISK}/containers/suricata-data"
LOG_DIR="${ROUTER_DISK}/containers/suricata-logs"
RULES_DIR="${ROUTER_DISK}/containers/suricata-rules"
REST_URL="https://${CONTAINER_GATEWAY}"
CONTAINER_IP_ONLY="${CONTAINER_IP%/*}"
@@ -225,6 +277,7 @@ cat > "$LOCAL_RSC" <<RSC
/container/envs/add list=IDS_ENV key=TAP_NAME value="suritap0"
/container/envs/add list=IDS_ENV key=TAP_MTU value="9000"
/container/envs/add list=IDS_ENV key=SURICATA_HOME_NET value="${SURICATA_HOME_NET}"
/container/envs/add list=IDS_ENV key=SURICATA_LOG_MAX_MB value="${SURICATA_LOG_MAX_MB}"
/container/envs/add list=IDS_ENV key=MONITORED_NETWORKS value="${MONITORED_NETWORKS}"
/container/envs/add list=IDS_ENV key=AUTO_BLOCK value="${AUTO_BLOCK}"
/container/envs/add list=IDS_ENV key=AUTO_BLOCK_MAX_SEVERITY value="${AUTO_BLOCK_MAX_SEVERITY}"
@@ -235,18 +288,42 @@ cat > "$LOCAL_RSC" <<RSC
/container/envs/add list=IDS_ENV key=ROUTEROS_VERIFY_TLS value="${ROUTEROS_VERIFY_TLS}"
/container/envs/add list=IDS_ENV key=ROUTEROS_ADDRESS_LIST value="${ROUTEROS_ADDRESS_LIST}"
/container/envs/add list=IDS_ENV key=UPDATE_RULES_ON_START value="${UPDATE_RULES_ON_START}"
/container/envs/add list=IDS_ENV key=SURICATA_PERSIST_LIB_DIR value="${SURICATA_PERSIST_LIB_DIR}"
/container/envs/add list=IDS_ENV key=RULE_UPDATE_INTERVAL_HOURS value="${RULE_UPDATE_INTERVAL_HOURS}"
/container/envs/add list=IDS_ENV key=ALERT_RETENTION_DAYS value="${ALERT_RETENTION_DAYS}"
/container/envs/add list=IDS_ENV key=REDIS_URL value="redis://127.0.0.1:${REDIS_PORT}/0"
/container/envs/add list=IDS_ENV key=REDIS_MANAGED value="${REDIS_MANAGED}"
/container/envs/add list=IDS_ENV key=REDIS_DATA_DIR value="${REDIS_DATA_DIR}"
/container/envs/add list=IDS_ENV key=REDIS_PORT value="${REDIS_PORT}"
/container/envs/add list=IDS_ENV key=REDIS_MAXMEMORY_MB value="${REDIS_MAXMEMORY_MB}"
/container/envs/add list=IDS_ENV key=REDIS_SNAPSHOT_SECONDS value="${REDIS_SNAPSHOT_SECONDS}"
/container/envs/add list=IDS_ENV key=REDIS_AOF value="${REDIS_AOF}"
/container/envs/add list=IDS_ENV key=TRAFFIC_RETENTION_HOURS value="${TRAFFIC_RETENTION_HOURS}"
/container/envs/add list=IDS_ENV key=TRAFFIC_MAX_EVENTS value="${TRAFFIC_MAX_EVENTS}"
/container/envs/add list=IDS_ENV key=TRAFFIC_MEMORY_EVENTS value="${TRAFFIC_MEMORY_EVENTS}"
/container/envs/add list=IDS_ENV key=WEBSOCKET_QUEUE_SIZE value="${WEBSOCKET_QUEUE_SIZE}"
/container/envs/add list=IDS_ENV key=LIVE_FLOW_UPDATE_SECONDS value="${LIVE_FLOW_UPDATE_SECONDS}"
/container/envs/add list=IDS_ENV key=ALERT_MAX_SEVERITY value="${ALERT_MAX_SEVERITY}"
/container/envs/add list=IDS_ENV key=ALERT_DEDUP_WINDOW_SECONDS value="${ALERT_DEDUP_WINDOW_SECONDS}"
/container/envs/add list=IDS_ENV key=ALERT_IGNORE_SIDS value="${ALERT_IGNORE_SIDS}"
/container/envs/add list=IDS_ENV key=ALERT_IGNORE_CATEGORIES value="${ALERT_IGNORE_CATEGORIES}"
/container/envs/add list=IDS_ENV key=ADMIN_TOKEN value="${ADMIN_TOKEN}"
/container/envs/add list=IDS_ENV key=ADMIN_USERNAME value="${ADMIN_USERNAME}"
/container/envs/add list=IDS_ENV key=ADMIN_PASSWORD value="${ADMIN_PASSWORD}"
/container/envs/add list=IDS_ENV key=SESSION_HOURS value="${SESSION_HOURS}"
/container/envs/add list=IDS_ENV key=SESSION_COOKIE_SECURE value="${SESSION_COOKIE_SECURE}"
/container/envs/add list=IDS_ENV key=ANALYTICS_SNAPSHOT_INTERVAL_SECONDS value="${ANALYTICS_SNAPSHOT_INTERVAL_SECONDS}"
/container/envs/add list=IDS_ENV key=NDR_ENABLED value="${NDR_ENABLED}"
/container/envs/add list=IDS_ENV key=NDR_CORRELATION_WINDOW_SECONDS value="${NDR_CORRELATION_WINDOW_SECONDS}"
/container/envs/add list=IDS_ENV key=BEHAVIOR_MIN_OBSERVATIONS value="${BEHAVIOR_MIN_OBSERVATIONS}"
/container/envs/add list=IDS_ENV key=NDR_AUTO_BLOCK value="${NDR_AUTO_BLOCK}"
/container/envs/add list=IDS_ENV key=NDR_AUTO_BLOCK_RISK value="${NDR_AUTO_BLOCK_RISK}"
/container/envs/add list=IDS_ENV key=ROUTEROS_INVENTORY_INTERVAL_SECONDS value="${ROUTEROS_INVENTORY_INTERVAL_SECONDS}"
/container/envs/add list=IDS_ENV key=NOTIFY_WEBHOOK_URL value="${NOTIFY_WEBHOOK_URL}"
/container/envs/add list=IDS_ENV key=NOTIFY_MIN_RISK value="${NOTIFY_MIN_RISK}"
/container/envs/add list=IDS_ENV key=NOTIFY_TIMEOUT_SECONDS value="${NOTIFY_TIMEOUT_SECONDS}"
/container/mounts/remove [find where list="IDS_MOUNTS"]
/container/mounts/add list=IDS_MOUNTS src="${DATA_DIR}" dst=/data
/container/mounts/add list=IDS_MOUNTS src="${LOG_DIR}" dst=/var/log/suricata
/container/mounts/add list=IDS_MOUNTS src="${RULES_DIR}" dst=/var/lib/suricata
RSC
if [ "$CREATE_REST_USER" = "true" ]; then
+27 -20
View File
@@ -1,13 +1,24 @@
#!/bin/sh
set -eu
mkdir -p /data /data/suricata /var/log/suricata /var/lib/suricata/rules /run/suricata
PERSIST_ROOT="${PERSIST_ROOT:-/data}"
PERSIST_LOG_DIR="${SURICATA_PERSIST_LOG_DIR:-${PERSIST_ROOT}/logs/suricata}"
PERSIST_LIB_DIR="${SURICATA_PERSIST_LIB_DIR:-${PERSIST_ROOT}/lib/suricata}"
PERSIST_STATE_DIR="${SURICATA_STATE_DIR:-${PERSIST_ROOT}/suricata}"
mkdir -p \
"$PERSIST_ROOT" \
"$PERSIST_LOG_DIR" \
"$PERSIST_LIB_DIR/rules" \
"$PERSIST_STATE_DIR" \
/run/suricata
if ! id -u suricata >/dev/null 2>&1 || ! getent group suricata >/dev/null 2>&1; then
echo "[entrypoint] FATAL: missing suricata user/group in the image; rebuild the image from the current Dockerfile" >&2
exit 70
fi
init_file() {
src="$1"
dst="$2"
@@ -17,20 +28,18 @@ init_file() {
chmod 0644 "$dst"
}
cp /opt/ids/suricata/local.rules /data/suricata/local.rules
chmod 0644 /data/suricata/local.rules
init_file /opt/ids/suricata/custom.rules.default /data/suricata/custom.rules
init_file /opt/ids/suricata/threshold.config /data/suricata/threshold.config
init_file /opt/ids/suricata/disable.conf /data/suricata/disable.conf
init_file /opt/ids/suricata/enable.conf /data/suricata/enable.conf
init_file /opt/ids/suricata/modify.conf /data/suricata/modify.conf
cp /opt/ids/suricata/local.rules "$PERSIST_STATE_DIR/local.rules"
chmod 0644 "$PERSIST_STATE_DIR/local.rules"
init_file /opt/ids/suricata/custom.rules.default "$PERSIST_STATE_DIR/custom.rules"
init_file /opt/ids/suricata/threshold.config "$PERSIST_STATE_DIR/threshold.config"
init_file /opt/ids/suricata/disable.conf "$PERSIST_STATE_DIR/disable.conf"
init_file /opt/ids/suricata/enable.conf "$PERSIST_STATE_DIR/enable.conf"
init_file /opt/ids/suricata/modify.conf "$PERSIST_STATE_DIR/modify.conf"
# RouterOS mounts /var/lib/suricata from persistent storage. On the first
# deployment that mount is empty, so seed it from the ET/Open snapshot baked
# into the image before optional online updates run.
if [ ! -s /var/lib/suricata/rules/suricata.rules ] && [ -d /opt/ids/vendor-rules-seed ]; then
echo "[entrypoint] seeding baseline vendor rules into persistent storage"
cp -a /opt/ids/vendor-rules-seed/. /var/lib/suricata/
# Seed vendor rule state into /data/lib/suricata on first start.
if [ ! -s "$PERSIST_LIB_DIR/rules/suricata.rules" ] && [ -d /opt/ids/vendor-rules-seed ]; then
echo "[entrypoint] seeding baseline vendor rules into /data"
cp -a /opt/ids/vendor-rules-seed/. "$PERSIST_LIB_DIR/"
fi
case "${UPDATE_RULES_ON_START:-false}" in
@@ -42,13 +51,11 @@ case "${UPDATE_RULES_ON_START:-false}" in
;;
esac
RULES=/var/lib/suricata/rules/suricata.rules
RULES="$PERSIST_LIB_DIR/rules/suricata.rules"
[ -f "$RULES" ] || : > "$RULES"
chown -R suricata:suricata /var/log/suricata /var/lib/suricata /run/suricata
# Rule state is edited by the root Python supervisor but must remain readable by
# the Suricata process after it drops privileges.
chmod 0755 /data /data/suricata || true
chmod 0644 /data/suricata/* 2>/dev/null || true
chown -R suricata:suricata "$PERSIST_LOG_DIR" "$PERSIST_LIB_DIR" /run/suricata
chmod 0755 "$PERSIST_ROOT" "$PERSIST_STATE_DIR" || true
chmod 0644 "$PERSIST_STATE_DIR"/* 2>/dev/null || true
exec python3 -m app.main
+15 -3
View File
@@ -15,10 +15,22 @@ if [ ! -c /dev/net/tun ]; then
echo "/dev/net/tun is missing; TAP mode requires a Linux host with TUN/TAP enabled" >&2
exit 4
fi
[ -f .env ] || cp .env.example .env
if [ ! -f .env ]; then
cp .env.example .env
ADMIN_PASSWORD_GENERATED="$(od -An -N24 -tx1 /dev/urandom | tr -d ' \n')"
sed -i "s/^ADMIN_PASSWORD=.*/ADMIN_PASSWORD=${ADMIN_PASSWORD_GENERATED}/" .env
chmod 0600 .env
echo "[first-run] generated dashboard password in .env (ADMIN_USERNAME=admin)"
fi
echo "[first-run] building and starting IDS"
docker compose up -d --build
echo "[first-run] building IDS image"
docker compose build
echo "[first-run] migrating legacy bind-mount data when needed"
./scripts/migrate-docker-volumes.sh
echo "[first-run] starting IDS"
docker compose up -d
echo "[first-run] running end-to-end TZSP test"
./scripts/selftest.sh
+81
View File
@@ -0,0 +1,81 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
if ! command -v docker >/dev/null 2>&1; then
echo "Docker Engine is required" >&2
exit 2
fi
IMAGE="${IDS_MIGRATION_IMAGE:-routeros-suricata-tzsp:local}"
TARGET_VOLUME="${IDS_DATA_VOLUME:-routeros-suricata-data}"
docker volume create "$TARGET_VOLUME" >/dev/null
target_is_empty() {
subpath="$1"
docker run --rm --entrypoint /bin/sh \
-v "${TARGET_VOLUME}:/target" \
"$IMAGE" \
-c "test ! -d '/target/${subpath}' || test -z \"\$(find '/target/${subpath}' -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)\""
}
copy_host_dir() {
source_dir="$1"
target_subpath="$2"
label="$3"
if [ ! -d "$source_dir" ] || [ -z "$(find "$source_dir" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]; then
echo "[volume-migration] no legacy ${label} data in ${source_dir}; skipping"
return 0
fi
if ! target_is_empty "$target_subpath"; then
echo "[volume-migration] /data/${target_subpath} is not empty; leaving it unchanged"
return 0
fi
source_abs="$(cd "$source_dir" && pwd)"
docker run --rm --entrypoint /bin/sh \
-v "${source_abs}:/legacy:ro" \
-v "${TARGET_VOLUME}:/target" \
"$IMAGE" \
-c "set -eu; mkdir -p '/target/${target_subpath}'; cp -a /legacy/. '/target/${target_subpath}/'"
echo "[volume-migration] migrated ${label}: ${source_dir} -> ${TARGET_VOLUME}:/${target_subpath}"
}
copy_named_volume() {
source_volume="$1"
target_subpath="$2"
label="$3"
if ! docker volume inspect "$source_volume" >/dev/null 2>&1; then
return 0
fi
if ! target_is_empty "$target_subpath"; then
echo "[volume-migration] /data/${target_subpath} is not empty; leaving legacy ${source_volume} unchanged"
return 0
fi
docker run --rm --entrypoint /bin/sh \
-v "${source_volume}:/legacy:ro" \
-v "${TARGET_VOLUME}:/target" \
"$IMAGE" \
-c "set -eu; if [ -n \"\$(find /legacy -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)\" ]; then mkdir -p '/target/${target_subpath}'; cp -a /legacy/. '/target/${target_subpath}/'; fi"
echo "[volume-migration] merged ${label}: ${source_volume} -> ${TARGET_VOLUME}:/${target_subpath}"
}
# <=0.6.1 host bind directories. Application data stays at /data root to
# preserve ids.db and redis paths; logs/rules are folded into subdirectories.
if target_is_empty ""; then
copy_host_dir "data" "" "SQLite/Redis/application"
else
echo "[volume-migration] ${TARGET_VOLUME} already contains application data; keeping it"
fi
copy_host_dir "logs" "logs/suricata" "Suricata logs"
copy_host_dir "data/vendor-rules" "lib/suricata" "vendor rules"
# 0.7.0 used three named volumes. Merge the two auxiliary volumes into the
# single data volume without overwriting newer files.
copy_named_volume "routeros-suricata-logs" "logs/suricata" "Suricata logs"
copy_named_volume "routeros-suricata-rules" "lib/suricata" "Suricata rule state"
+2 -2
View File
@@ -26,7 +26,7 @@ fi
START_SIZE="$(docker compose exec -T ids python3 - <<'PY'
import os
print(os.path.getsize('/var/log/suricata/eve.json') if os.path.exists('/var/log/suricata/eve.json') else 0)
print(os.path.getsize('/data/logs/suricata/eve.json') if os.path.exists('/data/logs/suricata/eve.json') else 0)
PY
)"
START_SIZE="$(printf '%s' "$START_SIZE" | tr -d '\r\n ')"
@@ -38,7 +38,7 @@ docker compose exec -T -e SELFTEST_START_SIZE="$START_SIZE" ids python3 - <<'PY'
import json
import os
path = '/var/log/suricata/eve.json'
path = '/data/logs/suricata/eve.json'
start = int(os.environ.get('SELFTEST_START_SIZE', '0'))
found = 0
with open(path, 'r', encoding='utf-8', errors='replace') as handle:
+11 -3
View File
@@ -16,13 +16,15 @@ if ! command -v suricata-update >/dev/null 2>&1; then
fi
STATE_DIR="${SURICATA_STATE_DIR:-/data/suricata}"
RULES="/var/lib/suricata/rules/suricata.rules"
PERSIST_LIB_DIR="${SURICATA_PERSIST_LIB_DIR:-/data/lib/suricata}"
RULES="${PERSIST_LIB_DIR}/rules/suricata.rules"
SURICATA_CONFIG="${SURICATA_CONFIG:-/etc/suricata/suricata.yaml}"
SURICATA_OUTPUT_CONFIG="${SURICATA_OUTPUT_CONFIG:-/opt/ids/suricata/ids-output.yaml}"
SURICATA_HOME_NET="${SURICATA_HOME_NET:-[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]}"
SURICATA_EXTRA_RULES_GLOB="${SURICATA_EXTRA_RULES_GLOB:-/data/suricata/*.rules}"
SURICATA_THRESHOLD_CONFIG="${SURICATA_THRESHOLD_CONFIG:-/data/suricata/threshold.config}"
mkdir -p "$STATE_DIR" /var/lib/suricata/rules
mkdir -p "$STATE_DIR" "$PERSIST_LIB_DIR/rules"
for name in disable.conf enable.conf modify.conf threshold.config; do
[ -f "$STATE_DIR/$name" ] || : > "$STATE_DIR/$name"
done
@@ -51,6 +53,7 @@ restore_previous_rules() {
echo "[rules] downloading enabled feeds with suricata-update"
if ! suricata-update \
-D "$PERSIST_LIB_DIR" \
--disable-conf="$STATE_DIR/disable.conf" \
--enable-conf="$STATE_DIR/enable.conf" \
--modify-conf="$STATE_DIR/modify.conf"; then
@@ -64,10 +67,15 @@ fi
echo "[rules] validating downloaded rules before activation"
if ! suricata -T \
-c "$SURICATA_CONFIG" \
--include "$SURICATA_OUTPUT_CONFIG" \
-l "$VALIDATE_LOG" \
-s "$SURICATA_EXTRA_RULES_GLOB" \
--set "vars.address-groups.HOME_NET=$SURICATA_HOME_NET" \
--set "threshold-file=$SURICATA_THRESHOLD_CONFIG"; then
--set "threshold-file=$SURICATA_THRESHOLD_CONFIG" \
--set "default-rule-path=$PERSIST_LIB_DIR/rules" \
--set "app-layer.protocols.tls.ja3-fingerprints=yes" \
--set "app-layer.protocols.tls.ja4-fingerprints=yes" \
--set "app-layer.protocols.ssh.hassh=yes"; then
echo "[rules] validation failed; restoring previous known-good rules" >&2
restore_previous_rules
exit 11
+10 -10
View File
@@ -28,6 +28,7 @@ ROOT_DIR="/containers/${CONTAINER_NAME}/root"
: "${CONTAINER_VETH:=veth-ids}"
: "${CONTAINER_ENVLIST:=IDS_ENV}"
: "${CONTAINER_MOUNTLIST:=IDS_MOUNTS}"
: "${ROUTER_DISK:=disk1}"
usage() {
cat <<USAGE
@@ -37,9 +38,9 @@ This is an image-only container upgrade. It DOES NOT change:
- bridge/IP/NAT/veth configuration,
- TZSP/sniffer configuration,
- firewall or REST configuration,
- envlist or mount definitions.
- envlist definitions.
It only disables/stops older suricata_* containers, creates:
It normalizes the mount list to one persistent /data mount and then creates:
name=${CONTAINER_NAME}
file=<TAR>
root-dir=${ROOT_DIR}
@@ -47,7 +48,7 @@ It only disables/stops older suricata_* containers, creates:
and reuses:
interface=${CONTAINER_VETH}
envlist=${CONTAINER_ENVLIST}
mountlists=${CONTAINER_MOUNTLIST}
mountlists=${CONTAINER_MOUNTLIST} -> ${ROUTER_DISK}/containers/suricata-data:/data
USAGE
}
@@ -67,7 +68,7 @@ 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"; do
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
@@ -86,7 +87,7 @@ 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/mounts: %s / %s / %s\n' "$CONTAINER_VETH" "$CONTAINER_ENVLIST" "$CONTAINER_MOUNTLIST"
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
@@ -104,10 +105,6 @@ if ! ssh_run "/container/envs/print without-paging where list=\"${CONTAINER_ENVL
echo "Existing envlist not found or empty: $CONTAINER_ENVLIST" >&2
exit 5
fi
if ! ssh_run "/container/mounts/print without-paging where list=\"${CONTAINER_MOUNTLIST}\"" | grep -F "$CONTAINER_MOUNTLIST" >/dev/null 2>&1; then
echo "Existing mountlist not found or empty: $CONTAINER_MOUNTLIST" >&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
@@ -122,7 +119,7 @@ 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 or mount definitions.
# 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
@@ -134,6 +131,9 @@ cat > "$LOCAL_RSC" <<RSC
: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