mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-06 17:15:38 -08:00
Merge pull request #1235 from adamoutler/hardening-fixes
Hardening fixes
This commit is contained in:
@@ -49,25 +49,58 @@ printf '
|
||||
\033[0m
|
||||
Network intruder and presence detector.
|
||||
https://netalertx.com
|
||||
'
|
||||
|
||||
'
|
||||
set -u
|
||||
|
||||
# Run all pre-startup checks to validate container environment and dependencies
|
||||
FAILED_STATUS=""
|
||||
echo "Startup pre-checks"
|
||||
for script in ${SYSTEM_SERVICES_SCRIPTS}/check-*.sh; do
|
||||
sh "$script"
|
||||
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')
|
||||
echo " --> ${script_name}"
|
||||
|
||||
sh "$script"
|
||||
NETALERTX_DOCKER_ERROR_CHECK=$?
|
||||
|
||||
if [ ${NETALERTX_DOCKER_ERROR_CHECK} -ne 0 ]; then
|
||||
# fail but continue checks so user can see all issues
|
||||
FAILED_STATUS="${NETALERTX_DOCKER_ERROR_CHECK}"
|
||||
echo "${script_name}: FAILED with ${FAILED_STATUS}"
|
||||
echo "Failure detected in: ${script}"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [ -n "${FAILED_STATUS}" ]; then
|
||||
echo "Container startup checks failed with exit code ${FAILED_STATUS}."
|
||||
exit ${FAILED_STATUS}
|
||||
fi
|
||||
|
||||
# Set APP_CONF_OVERRIDE based on GRAPHQL_PORT if not already set
|
||||
if [ -n "${GRAPHQL_PORT:-}" ] && [ -z "${APP_CONF_OVERRIDE:-}" ]; then
|
||||
export APP_CONF_OVERRIDE='{"GRAPHQL_PORT":"'"${GRAPHQL_PORT}"'"}'
|
||||
echo "Setting APP_CONF_OVERRIDE to $APP_CONF_OVERRIDE"
|
||||
fi
|
||||
|
||||
|
||||
# Exit after checks if in check-only mode (for testing)
|
||||
if [ "${NETALERTX_CHECK_ONLY:-0}" -eq 1 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Update vendor data (MAC address OUI database) in the background
|
||||
# This happens concurrently with service startup to avoid blocking container readiness
|
||||
${SYSTEM_SERVICES_SCRIPTS}/update_vendors.sh &
|
||||
bash ${SYSTEM_SERVICES_SCRIPTS}/update_vendors.sh &
|
||||
|
||||
|
||||
|
||||
# Service management state variables
|
||||
SERVICES="" # Space-separated list of active services in format "pid:name"
|
||||
FAILED_NAME="" # Name of service that failed (used for error reporting)
|
||||
FAILED_STATUS=0 # Exit status code from failed service or signal
|
||||
|
||||
################################################################################
|
||||
# is_pid_active() - Check if a process is alive and not in zombie/dead state
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/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'
|
||||
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}
|
||||
$(dirname "${NETALERTX_DB_FILE}")
|
||||
"
|
||||
|
||||
# If running as root, fix permissions first
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
echo "Running as root. Ensuring correct ownership and permissions..."
|
||||
|
||||
# Set ownership to netalertx user and group for all read-write paths
|
||||
chown -R netalertx:netalertx ${READ_WRITE_PATHS}
|
||||
|
||||
# Set directory and file permissions for all read-write paths
|
||||
find ${READ_WRITE_PATHS} -type d -exec chmod 700 {} +
|
||||
find ${READ_WRITE_PATHS} -type f -exec chmod 600 {} +
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -27,5 +27,5 @@ then
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0 # Always exit success even after warnings
|
||||
@@ -9,11 +9,9 @@ if [ ! -f ${NETALERTX_CONFIG}/app.conf ]; then
|
||||
}
|
||||
cp /app/back/app.conf "${NETALERTX_CONFIG}/app.conf" || {
|
||||
>&2 echo "ERROR: Failed to copy default config to ${NETALERTX_CONFIG}/app.conf"
|
||||
exit 1
|
||||
exit 2
|
||||
}
|
||||
CYAN='\033[1;36m'
|
||||
RESET='\033[0m'
|
||||
>&2 printf "%s" "${CYAN}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
🆕 First run detected. Default configuration written to ${NETALERTX_CONFIG}/app.conf.
|
||||
@@ -22,6 +20,7 @@ if [ ! -f ${NETALERTX_CONFIG}/app.conf ]; then
|
||||
this instance in production.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
|
||||
>&2 printf "%s" "${RESET}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -2,8 +2,17 @@
|
||||
# This script checks if the database file exists, and if not, creates it with the initial schema.
|
||||
# It is intended to be run at the first start of the application.
|
||||
|
||||
# if the db exists, exit
|
||||
test -f "${NETALERTX_DB_FILE}" && exit 0
|
||||
# If ALWAYS_FRESH_INSTALL is true, remove the database to force a rebuild.
|
||||
if [ "${ALWAYS_FRESH_INSTALL}" = "true" ]; then
|
||||
if [ -f "${NETALERTX_DB_FILE}" ]; then
|
||||
# Provide feedback to the user.
|
||||
>&2 echo "INFO: ALWAYS_FRESH_INSTALL is true. Removing existing database to force a fresh installation."
|
||||
rm -f "${NETALERTX_DB_FILE}" "${NETALERTX_DB_FILE}-shm" "${NETALERTX_DB_FILE}-wal"
|
||||
fi
|
||||
# Otherwise, if the db exists, exit.
|
||||
elif [ -f "${NETALERTX_DB_FILE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CYAN='\033[1;36m'
|
||||
RESET='\033[0m'
|
||||
@@ -32,7 +41,6 @@ CREATE TABLE IF NOT EXISTS "Online_History" (
|
||||
"Offline_Devices" INTEGER,
|
||||
PRIMARY KEY("Index" AUTOINCREMENT)
|
||||
);
|
||||
CREATE TABLE sqlite_sequence(name,seq);
|
||||
CREATE TABLE Devices (
|
||||
devMac STRING (50) PRIMARY KEY NOT NULL COLLATE NOCASE,
|
||||
devName STRING (50) NOT NULL DEFAULT "(unknown)",
|
||||
|
||||
@@ -1,9 +1,53 @@
|
||||
#!/bin/sh
|
||||
# Initialize required directories and log files
|
||||
# These must exist before services start to avoid permission/write errors
|
||||
# TODO - improve with per-directory warning if creation fails
|
||||
[ ! -d "${NETALERTX_PLUGINS_LOG}" ] && mkdir -p "${NETALERTX_PLUGINS_LOG}"
|
||||
[ ! -d "${SYSTEM_SERVICES_RUN_LOG}" ] && mkdir -p "${SYSTEM_SERVICES_RUN_LOG}"
|
||||
[ ! -d "${SYSTEM_SERVICES_RUN_TMP}" ] && mkdir -p "${SYSTEM_SERVICES_RUN_TMP}"
|
||||
[ ! -f "${LOG_DB_IS_LOCKED}" ] && touch "${LOG_DB_IS_LOCKED}"
|
||||
[ ! -f "${LOG_EXECUTION_QUEUE}" ] && touch "${LOG_EXECUTION_QUEUE}"
|
||||
|
||||
check_mandatory_folders() {
|
||||
# Check and create plugins log directory
|
||||
if [ ! -d "${NETALERTX_PLUGINS_LOG}" ]; then
|
||||
echo " * Creating Plugins log."
|
||||
if ! mkdir -p "${NETALERTX_PLUGINS_LOG}"; then
|
||||
echo "Error: Failed to create plugins log directory: ${NETALERTX_PLUGINS_LOG}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check and create system services run log directory
|
||||
if [ ! -d "${SYSTEM_SERVICES_RUN_LOG}" ]; then
|
||||
echo " * Creating System services run log."
|
||||
if ! mkdir -p "${SYSTEM_SERVICES_RUN_LOG}"; then
|
||||
echo "Error: Failed to create system services run log directory: ${SYSTEM_SERVICES_RUN_LOG}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check and create system services run tmp directory
|
||||
if [ ! -d "${SYSTEM_SERVICES_RUN_TMP}" ]; then
|
||||
echo " * Creating System services run tmp."
|
||||
if ! mkdir -p "${SYSTEM_SERVICES_RUN_TMP}"; then
|
||||
echo "Error: Failed to create system services run tmp directory: ${SYSTEM_SERVICES_RUN_TMP}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check and create DB locked log file
|
||||
if [ ! -f "${LOG_DB_IS_LOCKED}" ]; then
|
||||
echo " * Creating DB locked log."
|
||||
if ! touch "${LOG_DB_IS_LOCKED}"; then
|
||||
echo "Error: Failed to create DB locked log file: ${LOG_DB_IS_LOCKED}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check and create execution queue log file
|
||||
if [ ! -f "${LOG_EXECUTION_QUEUE}" ]; then
|
||||
echo " * Creating Execution queue log."
|
||||
if ! touch "${LOG_EXECUTION_QUEUE}"; then
|
||||
echo "Error: Failed to create execution queue log file: ${LOG_EXECUTION_QUEUE}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Run the function
|
||||
check_mandatory_folders
|
||||
64
install/production-filesystem/services/scripts/check-network-mode.sh
Executable file
64
install/production-filesystem/services/scripts/check-network-mode.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
# check-network-mode.sh - detect when the container is not using host networking.
|
||||
|
||||
# Exit if NETALERTX_DEBUG=1
|
||||
if [ "${NETALERTX_DEBUG}" = "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get the default network interface
|
||||
DEFAULT_IF="$(ip route show default 0.0.0.0/0 2>/dev/null | awk 'NR==1 {print $5}')"
|
||||
if [ -z "${DEFAULT_IF}" ]; then
|
||||
# No default route; nothing to validate.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
IF_LINK_INFO="$(ip link show "${DEFAULT_IF}" 2>/dev/null)"
|
||||
IF_IP="$(ip -4 addr show "${DEFAULT_IF}" 2>/dev/null | awk '/inet / {print $2}' | head -n1)"
|
||||
IF_MAC=""
|
||||
if [ -r "/sys/class/net/${DEFAULT_IF}/address" ]; then
|
||||
IF_MAC="$(cat "/sys/class/net/${DEFAULT_IF}/address")"
|
||||
fi
|
||||
|
||||
looks_like_bridge="0"
|
||||
|
||||
# Check for common bridge MAC and IP patterns
|
||||
case "${IF_MAC}" in
|
||||
02:42:*) looks_like_bridge="1" ;;
|
||||
00:00:00:00:00:00) looks_like_bridge="1" ;;
|
||||
"") ;; # leave as is
|
||||
esac
|
||||
|
||||
# Check for common bridge IP ranges
|
||||
case "${IF_IP}" in
|
||||
172.1[6-9].*|172.2[0-9].*|172.3[0-1].*) looks_like_bridge="1" ;;
|
||||
192.168.65.*) looks_like_bridge="1" ;;
|
||||
esac
|
||||
|
||||
if echo "${IF_LINK_INFO}" | grep -q "@if"; then
|
||||
looks_like_bridge="1"
|
||||
fi
|
||||
|
||||
if [ "${looks_like_bridge}" -ne 1 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
YELLOW=$(printf '\033[1;33m')
|
||||
RESET=$(printf '\033[0m')
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: NetAlertX is not running with --network=host.
|
||||
|
||||
Bridge networking blocks passive discovery (ARP, NBNS, mDNS) and active
|
||||
scanning accuracy. Most plugins expect raw access to the LAN through host
|
||||
networking and CAP_NET_RAW capabilities.
|
||||
|
||||
Restart the container with:
|
||||
docker run --network=host --cap-add=NET_RAW --cap-add=NET_ADMIN --cap-add=NET_BIND_SERVICE
|
||||
or set "network_mode: host" in docker-compose.yml.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
exit 0
|
||||
50
install/production-filesystem/services/scripts/check-nginx-config.sh
Executable file
50
install/production-filesystem/services/scripts/check-nginx-config.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/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"
|
||||
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.
|
||||
if [ ! -d "${CONF_ACTIVE_DIR}" ]; then
|
||||
YELLOW=$(printf '\033[1;33m')
|
||||
RESET=$(printf '\033[0m')
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Nginx configuration mount ${CONF_ACTIVE_DIR} is missing.
|
||||
|
||||
Custom listen address or port changes require a writable nginx conf.active
|
||||
directory. Without it, the container falls back to defaults and ignores
|
||||
your overrides.
|
||||
|
||||
Create a bind mount:
|
||||
--mount type=bind,src=/path/on/host,dst=${CONF_ACTIVE_DIR}
|
||||
and ensure it is owned by the netalertx user (20211:20211) with 700 perms.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMP_FILE="${CONF_ACTIVE_DIR}/.netalertx-write-test"
|
||||
if ! ( : >"${TMP_FILE}" ) 2>/dev/null; then
|
||||
YELLOW=$(printf '\033[1;33m')
|
||||
RESET=$(printf '\033[0m')
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: Unable to write to ${TARGET_FILE}.
|
||||
|
||||
Ensure the conf.active mount is writable by the netalertx user before
|
||||
changing LISTEN_ADDR or PORT. Fix permissions:
|
||||
chown -R 20211:20211 ${CONF_ACTIVE_DIR}
|
||||
find ${CONF_ACTIVE_DIR} -type d -exec chmod 700 {} +
|
||||
find ${CONF_ACTIVE_DIR} -type f -exec chmod 600 {} +
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "${TMP_FILE}"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/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,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# TODO Add sanity checks here to ensure we can read from
|
||||
# ${NETALERTX_APP}
|
||||
# ${NETALERTX_SERVER}
|
||||
# ${NETALERTX_FRONT}
|
||||
# ${SYSTEM_SERVICES_CONFIG}
|
||||
# ${VIRTUAL_ENV}
|
||||
|
||||
# And read/write tempdirs
|
||||
# ${NETALERTX_API}
|
||||
# ${NETALERTX_LOGS}
|
||||
# ${SYSTEM_SERVICES_RUN}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/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
|
||||
@@ -41,10 +41,8 @@ failures=0
|
||||
warn_if_not_dedicated_mount "${NETALERTX_API}"
|
||||
warn_if_not_dedicated_mount "${NETALERTX_LOG}"
|
||||
|
||||
if [ "${failures}" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${SYSTEM_NGINX_CONFIG}/conf.active" ]; then
|
||||
echo "Note: Using default listen address ${LISTEN_ADDR}:${PORT} (no ${SYSTEM_NGINX_CONFIG}/conf.active override)."
|
||||
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
|
||||
@@ -20,6 +20,10 @@ if [ "${CURRENT_UID}" -eq 0 ]; then
|
||||
* 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.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# TODO Sanity checks for storage paths
|
||||
|
||||
# Ensure we can read/write to
|
||||
# ${NETALERTX_CONFIG}
|
||||
# ${NETALERTX_DB}
|
||||
41
install/production-filesystem/services/scripts/check-user-netalertx.sh
Executable file
41
install/production-filesystem/services/scripts/check-user-netalertx.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
# check-user-netalertx.sh - ensure the container is running as the hardened service user.
|
||||
|
||||
EXPECTED_USER="${NETALERTX_USER:-netalertx}"
|
||||
EXPECTED_UID="$(getent passwd "${EXPECTED_USER}" 2>/dev/null | cut -d: -f3)"
|
||||
EXPECTED_GID="$(getent passwd "${EXPECTED_USER}" 2>/dev/null | cut -d: -f4)"
|
||||
CURRENT_UID="$(id -u)"
|
||||
CURRENT_GID="$(id -g)"
|
||||
|
||||
# Fallback to known defaults when lookups fail
|
||||
if [ -z "${EXPECTED_UID}" ]; then
|
||||
EXPECTED_UID="20211"
|
||||
fi
|
||||
if [ -z "${EXPECTED_GID}" ]; then
|
||||
EXPECTED_GID="20211"
|
||||
fi
|
||||
|
||||
if [ "${CURRENT_UID}" -eq "${EXPECTED_UID}" ] && [ "${CURRENT_GID}" -eq "${EXPECTED_GID}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
YELLOW=$(printf '\033[1;33m')
|
||||
RESET=$(printf '\033[0m')
|
||||
>&2 printf "%s" "${YELLOW}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
⚠️ ATTENTION: NetAlertX is running as UID ${CURRENT_UID}:${CURRENT_GID}.
|
||||
|
||||
Hardened permissions, file ownership, and runtime isolation expect the
|
||||
dedicated service account (${EXPECTED_USER} -> ${EXPECTED_UID}:${EXPECTED_GID}).
|
||||
When you override the container user (for example, docker run --user 1000:1000
|
||||
or a Compose "user:" directive), NetAlertX loses crucial safeguards and
|
||||
future upgrades may silently fail.
|
||||
|
||||
Restore the container to the default user:
|
||||
* Remove any custom --user flag
|
||||
* Delete "user:" overrides in compose files
|
||||
* Recreate the container so volume ownership is reset
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
@@ -19,7 +19,7 @@ TEMP_FILE="/services/run/tmp/ieee-oui.txt.tmp"
|
||||
OUTPUT_FILE="/services/run/tmp/ieee-oui.txt"
|
||||
|
||||
# Download the file using wget to stdout and process it
|
||||
if ! wget --timeout=30 --tries=3 "https://standards-oui.ieee.org/oui/oui.txt" -O /dev/stdout | \
|
||||
if ! wget --timeout=30 --tries=3 "https://standards-oui.ieee.org/oui/oui.txt" -O /dev/stdout 2>/dev/null | \
|
||||
sed -E 's/ *\(base 16\)//' | \
|
||||
awk -F' ' '{printf "%s\t%s\n", $1, substr($0, index($0, $2))}' | \
|
||||
sort | \
|
||||
|
||||
@@ -11,5 +11,5 @@ done
|
||||
# Force kill if graceful shutdown failed
|
||||
killall -KILL python3 &>/dev/null
|
||||
|
||||
echo "python3 $(cat /services/config/python/backend-extra-launch-parameters 2>/dev/null) -m server > >(tee /app/log/stdout.log) 2> >(tee /app/log/stderr.log >&2)"
|
||||
exec python3 $(cat /services/config/python/backend-extra-launch-parameters 2>/dev/null) -m server > >(tee /app/log/stdout.log) 2> >(tee /app/log/stderr.log >&2)
|
||||
echo "Starting python3 $(cat /services/config/python/backend-extra-launch-parameters 2>/dev/null) -m server > /app/log/stdout.log 2> >(tee /app/log/stderr.log >&2)"
|
||||
exec python3 $(cat /services/config/python/backend-extra-launch-parameters 2>/dev/null) -m server > /app/log/stdout.log 2> >(tee /app/log/stderr.log >&2)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Starting crond..."
|
||||
|
||||
crond_pid=""
|
||||
|
||||
@@ -24,7 +23,7 @@ done
|
||||
trap cleanup EXIT
|
||||
trap forward_signal INT TERM
|
||||
|
||||
echo "/usr/sbin/crond -c \"${SYSTEM_SERVICES_CROND}\" -f -L \"${LOG_CROND}\" >>\"${LOG_CROND}\" 2>&1 &"
|
||||
echo "Starting /usr/sbin/crond -c \"${SYSTEM_SERVICES_CROND}\" -f -L \"${LOG_CROND}\" >>\"${LOG_CROND}\" 2>&1 &"
|
||||
|
||||
/usr/sbin/crond -c "${SYSTEM_SERVICES_CROND}" -f -L "${LOG_CROND}" >>"${LOG_CROND}" 2>&1 &
|
||||
crond_pid=$!
|
||||
|
||||
@@ -11,7 +11,6 @@ SYSTEM_NGINX_CONFIG_FILE="/services/config/nginx/conf.active/netalertx.conf"
|
||||
# Create directories if they don't exist
|
||||
mkdir -p "${LOG_DIR}" "${RUN_DIR}" "${TMP_DIR}"
|
||||
|
||||
echo "Starting nginx..."
|
||||
|
||||
nginx_pid=""
|
||||
|
||||
@@ -48,11 +47,11 @@ trap forward_signal INT TERM
|
||||
|
||||
# Execute nginx with overrides
|
||||
# echo the full nginx command then run it
|
||||
echo "nginx -p \"${RUN_DIR}/\" -c \"${SYSTEM_NGINX_CONFIG_FILE}\" -g \"error_log ${NETALERTX_LOG}/nginx-error.log; pid ${RUN_DIR}/nginx.pid; daemon off;\" &"
|
||||
nginx \
|
||||
echo "Starting /usr/sbin/nginx -p \"${RUN_DIR}/\" -c \"${SYSTEM_NGINX_CONFIG_FILE}\" -g \"error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; pid ${RUN_DIR}/nginx.pid; daemon off;\" &"
|
||||
/usr/sbin/nginx \
|
||||
-p "${RUN_DIR}/" \
|
||||
-c "${SYSTEM_NGINX_CONFIG_FILE}" \
|
||||
-g "error_log ${NETALERTX_LOG}/nginx-error.log; pid ${RUN_DIR}/nginx.pid; daemon off;" &
|
||||
-g "error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; pid ${RUN_DIR}/nginx.pid; daemon off;" &
|
||||
nginx_pid=$!
|
||||
|
||||
wait "${nginx_pid}"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Starting php-fpm..."
|
||||
|
||||
php_fpm_pid=""
|
||||
|
||||
cleanup() {
|
||||
@@ -24,8 +22,8 @@ done
|
||||
trap cleanup EXIT
|
||||
trap forward_signal INT TERM
|
||||
|
||||
echo "/usr/sbin/php-fpm83 -y \"${PHP_FPM_CONFIG_FILE}\" -F >>\"${LOG_APP_PHP_ERRORS}\" 2>&1 &"
|
||||
/usr/sbin/php-fpm83 -y "${PHP_FPM_CONFIG_FILE}" -F >>"${LOG_APP_PHP_ERRORS}" 2>&1 &
|
||||
echo "Starting /usr/sbin/php-fpm83 -y \"${PHP_FPM_CONFIG_FILE}\" -F >>\"${LOG_APP_PHP_ERRORS}\" 2>/dev/stderr &"
|
||||
/usr/sbin/php-fpm83 -y "${PHP_FPM_CONFIG_FILE}" -F >>"${LOG_APP_PHP_ERRORS}" 2> /dev/stderr &
|
||||
php_fpm_pid=$!
|
||||
|
||||
wait "${php_fpm_pid}"
|
||||
|
||||
Reference in New Issue
Block a user