mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-06 17:15:38 -08:00
Move all check- scripts to /entrypoint.d/ for better organization
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
242
install/production-filesystem/entrypoint.d/10-mounts.py
Executable file
242
install/production-filesystem/entrypoint.d/10-mounts.py
Executable file
@@ -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()
|
||||
@@ -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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
❌ CRITICAL: Path does not exist.
|
||||
|
||||
The required path "${path}" could not be found. The application
|
||||
cannot start without its complete directory structure.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
elif [ ! -r "$path" ]; then
|
||||
failures=1
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Read permission denied.
|
||||
|
||||
The application cannot read from "${path}". This will cause
|
||||
unpredictable errors. Please correct the file system permissions.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
elif [ ! -w "$path" ]; then
|
||||
failures=1
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Write permission denied.
|
||||
|
||||
The application cannot write to "${path}". This will prevent it from
|
||||
saving data, logs, or configuration.
|
||||
|
||||
To fix this automatically, restart the container with root privileges
|
||||
(e.g., remove the "user:" directive in your Docker Compose file).
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
fi
|
||||
done
|
||||
|
||||
# If there were any failures, exit
|
||||
if [ "$failures" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
❌ CRITICAL: Path does not exist.
|
||||
|
||||
The required path "${path}" could not be found. The application
|
||||
cannot start without its complete directory structure.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
elif [ ! -r "$path" ]; then
|
||||
failures=1
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Read permission denied.
|
||||
|
||||
The application cannot read from "${path}". This will cause
|
||||
unpredictable errors. Please correct the file system permissions.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Write permission denied.
|
||||
|
||||
The application cannot write to "${path}". This will prevent it from
|
||||
saving data, logs, or configuration.
|
||||
|
||||
To fix this automatically, restart the container with root privileges
|
||||
(e.g., remove the "user:" directive in your Docker Compose file).
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
fi
|
||||
done
|
||||
|
||||
# If there were any failures, exit
|
||||
if [ "$failures" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@@ -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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: ${path} is not a persistent mount.
|
||||
|
||||
${label} relies on host storage to persist data across container restarts.
|
||||
Mount this directory from the host or a named volume before trusting the
|
||||
container's output.
|
||||
|
||||
Example:
|
||||
--mount type=bind,src=/path/on/host,dst=${path}
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&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
|
||||
@@ -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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: ${path} is not a persistent mount.
|
||||
|
||||
Your data in this directory may not persist across container restarts or
|
||||
upgrades. The filesystem type for this path is identified as non-persistent.
|
||||
|
||||
Fix: mount ${path} explicitly as a bind mount or a named volume:
|
||||
# Bind mount
|
||||
--mount type=bind,src=/path/on/host,dst=${path}
|
||||
|
||||
# Named volume
|
||||
--mount type=volume,src=netalertx-data,dst=${path}
|
||||
|
||||
Apply one of these mount options and restart the container.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&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
|
||||
@@ -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 <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: ${path} is not mounted separately inside this container.
|
||||
|
||||
NetAlertX runs as a single unprivileged process and pounds this directory
|
||||
with writes. Leaving it on the container overlay will thrash storage and
|
||||
slow the stack.
|
||||
|
||||
Fix: mount ${path} explicitly — tmpfs for ephemeral data, or bind/volume if
|
||||
you want to preserve history:
|
||||
--mount type=tmpfs,destination=${path}
|
||||
# or
|
||||
--mount type=bind,src=/path/on/host,dst=${path}
|
||||
|
||||
Apply the mount and restart the container.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user