diff --git a/Dockerfile b/Dockerfile index 154068c1..46d94d11 100755 --- a/Dockerfile +++ b/Dockerfile @@ -69,11 +69,13 @@ ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log ENV LOG_CROND=${NETALERTX_LOG}/crond.log # System Services configuration files +ENV ENTRYPOINT_CHECKS=/entrypoint.d ENV SYSTEM_SERVICES=/services ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf +ENV SYSTEM_SERVICES_ACTIVE_CONFIG=${SYSTEM_NGINX_CONFIG}/conf.active ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond @@ -82,7 +84,7 @@ ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \ - ${SYSTEM_SERVICES_CONFIG}" + ${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}" ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \ ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \ ${SYSTEM_SERVICES_RUN_LOG}" @@ -181,7 +183,7 @@ RUN chown -R ${READ_ONLY_USER}:${READ_ONLY_GROUP} ${READ_ONLY_FOLDERS} && \ chmod -R 600 ${READ_WRITE_FOLDERS} && \ find ${READ_WRITE_FOLDERS} -type d -exec chmod 700 {} + && \ chown ${READ_ONLY_USER}:${READ_ONLY_GROUP} /entrypoint.sh /opt /opt/venv && \ - chmod 005 /entrypoint.sh ${SYSTEM_SERVICES}/*.sh /app /opt /opt/venv && \ + chmod 005 /entrypoint.sh ${SYSTEM_SERVICES}/*.sh ${ENTRYPOINT_CHECKS}/* /app /opt /opt/venv && \ for dir in ${READ_WRITE_FOLDERS}; do \ install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 "$dir"; \ done && \ diff --git a/install/production-filesystem/entrypoint.d/0-storage-permission.sh b/install/production-filesystem/entrypoint.d/0-storage-permission.sh new file mode 100644 index 00000000..b83defff --- /dev/null +++ b/install/production-filesystem/entrypoint.d/0-storage-permission.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# 0-storage-permission.sh: Fix permissions if running as root. +# +# This script checks if running as root and fixes ownership and permissions +# for read-write paths to ensure proper operation. + +# --- Color Codes --- +MAGENTA='\033[1;35m' +RESET='\033[0m' + +# --- Main Logic --- + +# Define paths that need read-write access +READ_WRITE_PATHS=" +${NETALERTX_API} +${NETALERTX_LOG} +${SYSTEM_SERVICES_RUN} +${NETALERTX_CONFIG} +${NETALERTX_CONFIG_FILE} +${NETALERTX_DB} +${NETALERTX_DB_FILE} +" + +# If running as root, fix permissions first +if [ "$(id -u)" -eq 0 ]; then + >&2 printf "%s" "${MAGENTA}" + >&2 cat <<'EOF' +══════════════════════════════════════════════════════════════════════════════ +🚨 CRITICAL SECURITY ALERT: NetAlertX is running as ROOT (UID 0)! 🚨 + + This configuration bypasses all built-in security hardening measures. + You've granted a network monitoring application unrestricted access to + your host system. A successful compromise here could jeopardize your + entire infrastructure. + + IMMEDIATE ACTION REQUIRED: Switch to the dedicated 'netalertx' user: + * Remove any 'user:' directive specifying UID 0 from docker-compose.yml or + * switch to the default USER in the image (20211:20211) + + IMPORTANT: This corrective mode automatically adjusts ownership of + /app/db and /app/config directories to the netalertx user, ensuring + proper operation in subsequent runs. + + Remember: Never operate security-critical tools as root unless you're + actively trying to get pwned. +══════════════════════════════════════════════════════════════════════════════ +EOF + >&2 printf "%s" "${RESET}" + + # Set ownership to netalertx user for all read-write paths + chown -R netalertx ${READ_WRITE_PATHS} + + # Set directory and file permissions for all read-write paths + find ${READ_WRITE_PATHS} -type d -exec chmod u+rwx {} + 2>/dev/null + find ${READ_WRITE_PATHS} -type f -exec chmod u+rw {} + 2>/dev/null + echo Permissions fixed for read-write paths. Please restart the container as user 20211. + sleep infinity & wait $!; exit 211 +fi + + + diff --git a/install/production-filesystem/entrypoint.d/10-mounts.py b/install/production-filesystem/entrypoint.d/10-mounts.py new file mode 100755 index 00000000..1df680e3 --- /dev/null +++ b/install/production-filesystem/entrypoint.d/10-mounts.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 + +import os +import sys +from dataclasses import dataclass + +@dataclass +class MountCheckResult: + """Object to track mount status and potential issues.""" + var_name: str + path: str = "" + is_writeable: bool = False + is_mounted: bool = False + is_ramdisk: bool = False + underlying_fs_is_ramdisk: bool = False # Track this separately + fstype: str = "N/A" + error: bool = False + write_error: bool = False + performance_issue: bool = False + dataloss_risk: bool = False + +def get_mount_info(): + """Parses /proc/mounts to get a dict of {mount_point: fstype}.""" + mounts = {} + try: + with open('/proc/mounts', 'r') as f: + for line in f: + parts = line.strip().split() + if len(parts) >= 3: + mount_point = parts[1].replace('\\040', ' ') + fstype = parts[2] + mounts[mount_point] = fstype + except FileNotFoundError: + print("Error: /proc/mounts not found. Not a Linux system?", file=sys.stderr) + return None + return mounts + +def analyze_path(var_name, is_persistent, mounted_filesystems, non_persistent_fstypes, read_only_vars): + """ + Analyzes a single path, checking for errors, performance, and dataloss. + """ + result = MountCheckResult(var_name=var_name) + target_path = os.environ.get(var_name) + + if target_path is None: + result.path = f"({var_name} unset)" + result.error = True + return result + + result.path = target_path + + # --- 1. Check Write Permissions --- + is_writeable = os.access(target_path, os.W_OK) + + if not is_writeable and not os.path.exists(target_path): + parent_dir = os.path.dirname(target_path) + if os.access(parent_dir, os.W_OK): + is_writeable = True + + result.is_writeable = is_writeable + + if var_name not in read_only_vars and not result.is_writeable: + result.error = True + result.write_error = True + + # --- 2. Check Filesystem Type (Parent and Self) --- + parent_mount_fstype = "" + longest_mount = "" + + for mount_point, fstype in mounted_filesystems.items(): + if target_path.startswith(mount_point): + if len(mount_point) > len(longest_mount): + longest_mount = mount_point + parent_mount_fstype = fstype + + result.underlying_fs_is_ramdisk = parent_mount_fstype in non_persistent_fstypes + + if parent_mount_fstype: + result.fstype = parent_mount_fstype + + # --- 3. Check if path IS a mount point --- + if target_path in mounted_filesystems: + result.is_mounted = True + result.fstype = mounted_filesystems[target_path] + result.is_ramdisk = result.fstype in non_persistent_fstypes + else: + result.is_mounted = False + result.is_ramdisk = False + + # --- 4. Apply Risk Logic --- + if is_persistent: + if result.underlying_fs_is_ramdisk: + result.dataloss_risk = True + + if not result.is_mounted: + result.dataloss_risk = True + + else: + # Performance issue if it's not a ramdisk mount + if not result.is_mounted or not result.is_ramdisk: + result.performance_issue = True + + return result + +def print_warning_message(): + """Prints a formatted warning to stderr.""" + YELLOW = '\033[1;33m' + RESET = '\033[0m' + + message = ( + "══════════════════════════════════════════════════════════════════════════════\n" + "⚠️ ATTENTION: Configuration issues detected (marked with ❌).\n\n" + " Your configuration has write permission, dataloss, or performance issues\n" + " as shown in the table above.\n\n" + " We recommend starting with the default docker-compose.yml as the\n" + " configuration can be quite complex.\n\n" + " Review the documentation for a correct setup:\n" + " https://github.com/jokob-sk/NetAlertX/blob/main/docs/DOCKER_COMPOSE.md\n" + "══════════════════════════════════════════════════════════════════════════════\n" + ) + + print(f"{YELLOW}{message}{RESET}", file=sys.stderr) + +def main(): + NON_PERSISTENT_FSTYPES = {'tmpfs', 'ramfs'} + PERSISTENT_VARS = {'NETALERTX_DB', 'NETALERTX_CONFIG'} + # Define all possible read-only vars + READ_ONLY_VARS = {'SYSTEM_NGINX_CONFIG', 'SYSTEM_SERVICES_ACTIVE_CONFIG'} + + # Base paths to check + PATHS_TO_CHECK = { + 'NETALERTX_DB': True, + 'NETALERTX_CONFIG': True, + 'NETALERTX_API': False, + 'NETALERTX_LOG': False, + 'SYSTEM_SERVICES_RUN': False, + } + + # *** KEY CHANGE: Conditionally add path based on PORT *** + port_val = os.environ.get("PORT") + if port_val is not None and port_val != "20211": + PATHS_TO_CHECK['SYSTEM_SERVICES_ACTIVE_CONFIG'] = False + # *** END KEY CHANGE *** + + mounted_filesystems = get_mount_info() + if mounted_filesystems is None: + sys.exit(1) + + results = [] + has_issues = False + for var_name, is_persistent in PATHS_TO_CHECK.items(): + result = analyze_path( + var_name, is_persistent, + mounted_filesystems, NON_PERSISTENT_FSTYPES, READ_ONLY_VARS + ) + if result.performance_issue or result.dataloss_risk or result.error: + has_issues = True + results.append(result) + + if has_issues: + # --- Print Table --- + headers = ["Path", "Writeable", "Mount", "RAMDisk", "Performance", "DataLoss"] + + CHECK_SYMBOL = "✅" + CROSS_SYMBOL = "❌" + BLANK_SYMBOL = "➖" + + bool_to_check = lambda is_good: CHECK_SYMBOL if is_good else CROSS_SYMBOL + + col_widths = [len(h) for h in headers] + for r in results: + col_widths[0] = max(col_widths[0], len(str(r.path))) + + header_fmt = ( + f" {{:<{col_widths[0]}}} |" + f" {{:^{col_widths[1]}}} |" + f" {{:^{col_widths[2]}}} |" + f" {{:^{col_widths[3]}}} |" + f" {{:^{col_widths[4]}}} |" + f" {{:^{col_widths[5]}}} " + ) + + row_fmt = ( + f" {{:<{col_widths[0]}}} |" + f" {{:^{col_widths[1]}}}|" # No space + f" {{:^{col_widths[2]}}}|" # No space + f" {{:^{col_widths[3]}}}|" # No space + f" {{:^{col_widths[4]}}}|" # No space + f" {{:^{col_widths[5]}}} " # DataLoss is last, needs space + ) + + separator = ( + "-" * (col_widths[0] + 2) + "+" + + "-" * (col_widths[1] + 2) + "+" + + "-" * (col_widths[2] + 2) + "+" + + "-" * (col_widths[3] + 2) + "+" + + "-" * (col_widths[4] + 2) + "+" + + "-" * (col_widths[5] + 2) + ) + + print(header_fmt.format(*headers)) + print(separator) + for r in results: + is_persistent = r.var_name in PERSISTENT_VARS + + # --- Symbol Logic --- + write_symbol = bool_to_check(r.is_writeable) + # Special case for read-only vars + if r.var_name in READ_ONLY_VARS: + write_symbol = CHECK_SYMBOL + + mount_symbol = CHECK_SYMBOL if r.is_mounted else CROSS_SYMBOL + + ramdisk_symbol = "" + if is_persistent: + ramdisk_symbol = CROSS_SYMBOL if r.underlying_fs_is_ramdisk else BLANK_SYMBOL + else: + ramdisk_symbol = CHECK_SYMBOL if r.is_ramdisk else CROSS_SYMBOL + + if is_persistent: + perf_symbol = BLANK_SYMBOL + else: + perf_symbol = bool_to_check(not r.performance_issue) + + dataloss_symbol = bool_to_check(not r.dataloss_risk) + + print(row_fmt.format( + r.path, + write_symbol, + mount_symbol, + ramdisk_symbol, + perf_symbol, + dataloss_symbol + )) + + # --- Print Warning --- + print("\n", file=sys.stderr) + print_warning_message() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/install/production-filesystem/services/scripts/check-first-run-config.sh b/install/production-filesystem/entrypoint.d/15-first-run-config.sh similarity index 100% rename from install/production-filesystem/services/scripts/check-first-run-config.sh rename to install/production-filesystem/entrypoint.d/15-first-run-config.sh diff --git a/install/production-filesystem/services/scripts/check-first-run-db.sh b/install/production-filesystem/entrypoint.d/20-first-run-db.sh similarity index 100% rename from install/production-filesystem/services/scripts/check-first-run-db.sh rename to install/production-filesystem/entrypoint.d/20-first-run-db.sh diff --git a/install/production-filesystem/services/scripts/check-mandatory-folders.sh b/install/production-filesystem/entrypoint.d/25-mandatory-folders.sh similarity index 100% rename from install/production-filesystem/services/scripts/check-mandatory-folders.sh rename to install/production-filesystem/entrypoint.d/25-mandatory-folders.sh diff --git a/install/production-filesystem/entrypoint.d/30-writable-config.sh b/install/production-filesystem/entrypoint.d/30-writable-config.sh new file mode 100644 index 00000000..ac30a65a --- /dev/null +++ b/install/production-filesystem/entrypoint.d/30-writable-config.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# 30-writable-config.sh: Verify read/write permissions for config and database files. +# +# This script ensures that the application can read from and write to the +# critical configuration and database files after startup. + +# --- Color Codes --- +RED='\033[1;31m' +YELLOW='\033[1;33m' +RESET='\033[0m' + +# --- Main Logic --- + +# Define paths that need read-write access +READ_WRITE_PATHS=" +${NETALERTX_CONFIG_FILE} +${NETALERTX_DB_FILE} +" + +# --- Permission Validation --- + +failures=0 + +# Check read-write paths for existence, read, and write access +for path in $READ_WRITE_PATHS; do + if [ ! -e "$path" ]; then + failures=1 + >&2 printf "%s" "${RED}" + >&2 cat <&2 printf "%s" "${RESET}" + elif [ ! -r "$path" ]; then + failures=1 + >&2 printf "%s" "${YELLOW}" + >&2 cat <&2 printf "%s" "${RESET}" + elif [ ! -w "$path" ]; then + failures=1 + >&2 printf "%s" "${YELLOW}" + >&2 cat <&2 printf "%s" "${RESET}" + fi +done + +# If there were any failures, exit +if [ "$failures" -ne 0 ]; then + exit 1 +fi \ No newline at end of file diff --git a/install/production-filesystem/services/scripts/check-nginx-config.sh b/install/production-filesystem/entrypoint.d/35-nginx-config.sh similarity index 97% rename from install/production-filesystem/services/scripts/check-nginx-config.sh rename to install/production-filesystem/entrypoint.d/35-nginx-config.sh index 6b2e6e9e..aa4706b3 100755 --- a/install/production-filesystem/services/scripts/check-nginx-config.sh +++ b/install/production-filesystem/entrypoint.d/35-nginx-config.sh @@ -1,7 +1,7 @@ #!/bin/sh # check-nginx-config.sh - verify nginx conf.active mount is writable when startup needs to render config. -CONF_ACTIVE_DIR="${SYSTEM_NGINX_CONFIG}/conf.active" +CONF_ACTIVE_DIR="${SYSTEM_SERVICES_ACTIVE_CONFIG}" TARGET_FILE="${CONF_ACTIVE_DIR}/netalertx.conf" # If the directory is missing entirely we warn and exit failure so the caller can see the message. diff --git a/install/production-filesystem/services/scripts/check-user-netalertx.sh b/install/production-filesystem/entrypoint.d/60-user-netalertx.sh similarity index 100% rename from install/production-filesystem/services/scripts/check-user-netalertx.sh rename to install/production-filesystem/entrypoint.d/60-user-netalertx.sh diff --git a/install/production-filesystem/services/scripts/check-network-mode.sh b/install/production-filesystem/entrypoint.d/80-host-mode-network.sh similarity index 96% rename from install/production-filesystem/services/scripts/check-network-mode.sh rename to install/production-filesystem/entrypoint.d/80-host-mode-network.sh index 85948aef..ce83cc2b 100755 --- a/install/production-filesystem/services/scripts/check-network-mode.sh +++ b/install/production-filesystem/entrypoint.d/80-host-mode-network.sh @@ -1,5 +1,5 @@ #!/bin/sh -# check-network-mode.sh - detect when the container is not using host networking. +# detect when the container is not using host networking. # Exit if NETALERTX_DEBUG=1 if [ "${NETALERTX_DEBUG}" = "1" ]; then diff --git a/install/production-filesystem/services/scripts/check-capabilities.sh b/install/production-filesystem/entrypoint.d/85-layer-2-capabilities.sh similarity index 95% rename from install/production-filesystem/services/scripts/check-capabilities.sh rename to install/production-filesystem/entrypoint.d/85-layer-2-capabilities.sh index a14e76ab..855c34bd 100755 --- a/install/production-filesystem/services/scripts/check-capabilities.sh +++ b/install/production-filesystem/entrypoint.d/85-layer-2-capabilities.sh @@ -1,5 +1,5 @@ #!/bin/sh -# check-cap.sh - Uses a real nmap command to detect missing container +# layer-2-network.sh - Uses a real nmap command to detect missing container # privileges and warns the user. It is silent on success. # Run a fast nmap command that requires raw sockets, capturing only stderr. diff --git a/install/production-filesystem/entrypoint.sh b/install/production-filesystem/entrypoint.sh index 1ccfbd48..7083d70c 100755 --- a/install/production-filesystem/entrypoint.sh +++ b/install/production-filesystem/entrypoint.sh @@ -55,15 +55,15 @@ set -u FAILED_STATUS="" echo "Startup pre-checks" -for script in ${SYSTEM_SERVICES_SCRIPTS}/check-*.sh; do +for script in ${ENTRYPOINT_CHECKS}/*; do if [ -n "${SKIP_TESTS:-}" ]; then echo "Skipping startup checks as SKIP_TESTS is set." break fi - script_name=$(basename "$script" | sed 's/^check-//;s/\.sh$//;s/-/ /g') + script_name=$(basename "$script" | sed 's/^[0-9]*-//;s/\.sh$//;s/-/ /g') echo " --> ${script_name}" - sh "$script" + "$script" NETALERTX_DOCKER_ERROR_CHECK=$? if [ ${NETALERTX_DOCKER_ERROR_CHECK} -ne 0 ]; then diff --git a/install/production-filesystem/services/scripts/check-app-permissions.sh b/install/production-filesystem/services/scripts/check-app-permissions.sh deleted file mode 100644 index 595e1851..00000000 --- a/install/production-filesystem/services/scripts/check-app-permissions.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/sh - -# check-0-permissions.sh: Verify file system permissions for critical paths. -# -# This script ensures that the application has the necessary read and write -# permissions for its operational directories. It distinguishes between running -# as root (user 0) and a non-privileged user. -# -# As root, it will proactively fix ownership and permissions. -# As a non-root user, it will only warn about issues. - -# --- Color Codes --- -RED='\033[1;31m' -YELLOW='\033[1;33m' -MAGENTA='\033[1;35m' -RESET='\033[0m' - -# --- Main Logic --- - -# Define paths that need read-only access -READ_ONLY_PATHS=" -${NETALERTX_APP} -${NETALERTX_SERVER} -${NETALERTX_FRONT} -${SYSTEM_SERVICES_CONFIG} -${VIRTUAL_ENV} -" - -# Define paths that need read-write access -READ_WRITE_PATHS=" -${NETALERTX_API} -${NETALERTX_LOG} -${SYSTEM_SERVICES_RUN} -${NETALERTX_CONFIG} -${NETALERTX_CONFIG_FILE} -${NETALERTX_DB} -${NETALERTX_DB_FILE} -" - -# If running as root, fix permissions first -if [ "$(id -u)" -eq 0 ]; then - >&2 printf "%s" "${MAGENTA}" - >&2 cat <<'EOF' -══════════════════════════════════════════════════════════════════════════════ -🚨 CRITICAL SECURITY ALERT: NetAlertX is running as ROOT (UID 0)! 🚨 - - This configuration bypasses all built-in security hardening measures. - You've granted a network monitoring application unrestricted access to - your host system. A successful compromise here could jeopardize your - entire infrastructure. - - IMMEDIATE ACTION REQUIRED: Switch to the dedicated 'netalertx' user: - * Remove any 'user:' directive specifying UID 0 from docker-compose.yml or - * switch to the default USER in the image (20211:20211) - - IMPORTANT: This corrective mode automatically adjusts ownership of - /app/db and /app/config directories to the netalertx user, ensuring - proper operation in subsequent runs. - - Remember: Never operate security-critical tools as root unless you're - actively trying to get pwned. -══════════════════════════════════════════════════════════════════════════════ -EOF - >&2 printf "%s" "${RESET}" - - # Set ownership to netalertx user for all read-write paths - chown -R netalertx ${READ_WRITE_PATHS} - - # Set directory and file permissions for all read-write paths - find ${READ_WRITE_PATHS} -type d -exec chmod u+rwx {} + 2>/dev/null - find ${READ_WRITE_PATHS} -type f -exec chmod u+rw {} + 2>/dev/null - echo Permissions fixed for read-write paths. Please restart the container as user 20211. - sleep infinity & wait $!; exit 211 -fi - -# --- Permission Validation --- - -failures=0 - -# Check all paths -ALL_PATHS="${READ_ONLY_PATHS} ${READ_WRITE_PATHS}" -echo "${READ_ONLY_PATHS}" | while IFS= read -r path; do - [ -z "$path" ] && continue - if [ ! -e "$path" ]; then - failures=1 - >&2 printf "%s" "${RED}" - >&2 cat <&2 printf "%s" "${RESET}" - elif [ ! -r "$path" ]; then - failures=1 - >&2 printf "%s" "${YELLOW}" - >&2 cat <&2 printf "%s" "${RESET}" - fi -done - -# Check read-write paths specifically for write access -for path in $READ_WRITE_PATHS; do - if [ -e "$path" ] && [ ! -w "$path" ]; then - failures=1 - >&2 printf "%s" "${YELLOW}" - >&2 cat <&2 printf "%s" "${RESET}" - fi -done - -# If there were any failures, exit -if [ "$failures" -ne 0 ]; then - exit 1 -fi - - - diff --git a/install/production-filesystem/services/scripts/check-nonpersistent-storage.sh b/install/production-filesystem/services/scripts/check-nonpersistent-storage.sh deleted file mode 100644 index 2e59e20d..00000000 --- a/install/production-filesystem/services/scripts/check-nonpersistent-storage.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -# check-storage-extra.sh - ensure additional NetAlertX directories are persistent mounts. - - -if [ "${NETALERTX_DEBUG}" == "1" ]; then - exit 0 -fi - -warn_if_not_persistent_mount() { - path="$1" - label="$2" - if awk -v target="${path}" '$5 == target {found=1} END {exit found ? 0 : 1}' /proc/self/mountinfo; then - return 0 - fi - - failures=1 - YELLOW=$(printf '\033[1;33m') - RESET=$(printf '\033[0m') - >&2 printf "%s" "${YELLOW}" - >&2 cat <&2 printf "%s" "${RESET}" - return 1 -} - -failures=0 -warn_if_not_persistent_mount "${NETALERTX_LOG}" "Logs" || failures=$((failures + 1)) -warn_if_not_persistent_mount "${NETALERTX_API}" "API JSON cache" || failures=$((failures + 1)) -warn_if_not_persistent_mount "${SYSTEM_SERVICES_RUN}" "Runtime work directory" || failures=$((failures + 1)) - -if [ "${failures}" -ne 0 ]; then - exit 1 -fi - -exit 0 diff --git a/install/production-filesystem/services/scripts/check-persistent-storage.sh b/install/production-filesystem/services/scripts/check-persistent-storage.sh deleted file mode 100644 index 13933fc5..00000000 --- a/install/production-filesystem/services/scripts/check-persistent-storage.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/sh -# check-storage.sh - Verify critical paths are persistent mounts. - -# Define non-persistent filesystem types to check against -# NOTE: 'overlay' and 'aufs' are the primary non-persistent types for container roots. -# 'tmpfs' and 'ramfs' are for specific non-persistent mounts. -NON_PERSISTENT_FSTYPES="tmpfs|ramfs|overlay|aufs" -MANDATORY_PERSISTENT_PATHS="/app/db /app/config" - -# This function is now the robust persistence checker. -is_persistent_mount() { - target_path="$1" - - mount_entry=$(awk -v path="${target_path}" '$2 == path { print $0 }' /proc/mounts) - - if [ -z "${mount_entry}" ]; then - # CRITICAL FIX: If the mount entry is empty, check if it's one of the mandatory paths. - if echo "${MANDATORY_PERSISTENT_PATHS}" | grep -w -q "${target_path}"; then - # The path is mandatory but not mounted: FAIL (Not persistent) - return 1 - else - # Not mandatory and not a mount point: Assume persistence is inherited from parent (pass) - return 0 - fi - fi - - # ... (rest of the original logic remains the same for explicit mounts) - fs_type=$(echo "${mount_entry}" | awk '{print $3}') - - # Check if the filesystem type matches any non-persistent types - if echo "${fs_type}" | grep -E -q "^(${NON_PERSISTENT_FSTYPES})$"; then - return 1 # Not persistent (matched a non-persistent type) - else - return 0 # Persistent - fi -} - -warn_if_not_persistent_mount() { - path="$1" - - if is_persistent_mount "${path}"; then - return 0 - fi - - failures=1 - YELLOW=$(printf '\033[1;33m') - RESET=$(printf '\033[0m') - >&2 printf "%s" "${YELLOW}" - >&2 cat <&2 printf "%s" "${RESET}" -} - -# If NETALERTX_DEBUG=1 then we will exit -if [ "${NETALERTX_DEBUG}" = "1" ]; then - exit 0 -fi - -failures=0 -# NETALERTX_DB is a file, so we check its directory -warn_if_not_persistent_mount "$(dirname "${NETALERTX_DB_FILE}")" -warn_if_not_persistent_mount "${NETALERTX_CONFIG}" - - -if [ "${failures}" -ne 0 ]; then - # We only warn, not exit, as this is not a critical failure - # but the user should be aware of the potential data loss. - sleep 1 # Give user time to read the message -fi \ No newline at end of file diff --git a/install/production-filesystem/services/scripts/check-ramdisk.sh b/install/production-filesystem/services/scripts/check-ramdisk.sh deleted file mode 100755 index a71a9893..00000000 --- a/install/production-filesystem/services/scripts/check-ramdisk.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh -# storage-check.sh - Verify critical paths use dedicated mounts. - -warn_if_not_dedicated_mount() { - path="$1" - if awk -v target="${path}" '$5 == target {found=1} END {exit found ? 0 : 1}' /proc/self/mountinfo; then - return 0 - fi - - failures=1 - YELLOW=$(printf '\033[1;33m') - RESET=$(printf '\033[0m') - >&2 printf "%s" "${YELLOW}" - >&2 cat <&2 printf "%s" "${RESET}" -} - - -# If NETALERTX_DEBUG=1 then we will exit -if [ "${NETALERTX_DEBUG}" = "1" ]; then - exit 0 -fi - -failures=0 -warn_if_not_dedicated_mount "${NETALERTX_API}" -warn_if_not_dedicated_mount "${NETALERTX_LOG}" - - -if [ ! -w "${SYSTEM_NGINX_CONFIG}/conf.active" ]; then - echo "Note: Using default listen address 0.0.0.0:20211 instead of ${LISTEN_ADDR}:${PORT} (no ${SYSTEM_NGINX_CONFIG}/conf.active override)." -fi -exit 0 \ No newline at end of file diff --git a/install/production-filesystem/services/scripts/check-root.sh b/install/production-filesystem/services/scripts/check-root.sh deleted file mode 100755 index 8c292872..00000000 --- a/install/production-filesystem/services/scripts/check-root.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# check-root.sh - ensure the container is not running as root. - -CURRENT_UID="$(id -u)" - -if [ "${CURRENT_UID}" -eq 0 ]; then - YELLOW=$(printf '\033[1;33m') - RESET=$(printf '\033[0m') - >&2 printf "%s" "${YELLOW}" - >&2 cat <<'EOF' -══════════════════════════════════════════════════════════════════════════════ -⚠️ ATTENTION: NetAlertX is running as root (UID 0). - - This defeats every hardening safeguard built into the image. You just - handed a high-value network monitoring appliance full control over your - host. If an attacker compromises NetAlertX now, the entire machine goes - with it. - - Run the container as the dedicated 'netalertx' user instead: - * Keep the default USER in the image (20211:20211), or - * In docker-compose.yml, remove any 'user:' override that sets UID 0. - - Note: As a courtesy, this special mode is only used to set the permissions - of /app/db and /app/config to be owned by the netalertx user so future - runs work correctly. - - Bottom line: never run security tooling as root unless you are actively - trying to get pwned. -══════════════════════════════════════════════════════════════════════════════ -EOF - >&2 printf "%s" "${RESET}" - exit 1 -fi - -exit 0