poc2_worked
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Executable
+81
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user