diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ad7d982d..21b25760 100755 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -224,8 +224,8 @@ COPY .devcontainer/resources/devcontainer-overlay/ / USER root # Install common tools, create user, and set up sudo RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \ - pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx - + pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \ + docker-cli-compose RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \ cp -a /usr/lib/php83/modules/. /services/php/modules/ && \ diff --git a/.devcontainer/resources/devcontainer-Dockerfile b/.devcontainer/resources/devcontainer-Dockerfile index 939de992..af17688b 100755 --- a/.devcontainer/resources/devcontainer-Dockerfile +++ b/.devcontainer/resources/devcontainer-Dockerfile @@ -18,8 +18,8 @@ COPY .devcontainer/resources/devcontainer-overlay/ / USER root # Install common tools, create user, and set up sudo RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \ - pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx - + pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \ + docker-cli-compose RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \ cp -a /usr/lib/php83/modules/. /services/php/modules/ && \ diff --git a/install/production-filesystem/services/scripts/check-app-permissions.sh b/install/production-filesystem/services/scripts/check-app-permissions.sh index 3c130ddc..595e1851 100644 --- a/install/production-filesystem/services/scripts/check-app-permissions.sh +++ b/install/production-filesystem/services/scripts/check-app-permissions.sh @@ -12,6 +12,7 @@ # --- Color Codes --- RED='\033[1;31m' YELLOW='\033[1;33m' +MAGENTA='\033[1;35m' RESET='\033[0m' # --- Main Logic --- @@ -31,19 +32,45 @@ ${NETALERTX_API} ${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN} ${NETALERTX_CONFIG} -$(dirname "${NETALERTX_DB_FILE}") +${NETALERTX_CONFIG_FILE} +${NETALERTX_DB} +${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..." + >&2 printf "%s" "${MAGENTA}" + >&2 cat <<'EOF' +══════════════════════════════════════════════════════════════════════════════ +🚨 CRITICAL SECURITY ALERT: NetAlertX is running as ROOT (UID 0)! 🚨 - # Set ownership to netalertx user and group for all read-write paths - chown -R netalertx:netalertx ${READ_WRITE_PATHS} + 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 700 {} + - find ${READ_WRITE_PATHS} -type f -exec chmod 600 {} + + 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 --- diff --git a/test/docker_tests/test_container_environment.py b/test/docker_tests/test_container_environment.py index 9b3400b2..d847000f 100644 --- a/test/docker_tests/test_container_environment.py +++ b/test/docker_tests/test_container_environment.py @@ -710,7 +710,7 @@ def test_missing_mount_app_db(tmp_path: pathlib.Path) -> None: paths = _setup_mount_tree(tmp_path, "missing_mount_app_db") volumes = _build_volume_args(paths, skip={"app_db"}) # CHANGE: Run as root (0:0) to bypass all permission checks on other mounts. - result = _run_container("missing-mount-app-db", volumes, user="0:0") + result = _run_container("missing-mount-app-db", volumes, user="20211:20211") # Acknowledge the original intent to check for permission denial (now implicit via root) # _assert_contains(result, "Write permission denied", result.args) # No longer needed, as root user is used @@ -820,7 +820,7 @@ def test_running_as_root_is_blocked(tmp_path: pathlib.Path) -> None: dedicated netalertx user. Warning about security risks, special permission fix mode. Expected: Warning about security risks, guidance to use UID 20211. - Check script: check-root.sh + Check script: check-app-permissions.sh Sample message: "⚠️ ATTENTION: NetAlertX is running as root (UID 0). This defeats every hardening..." """ paths = _setup_mount_tree(tmp_path, "run_as_root") @@ -828,10 +828,11 @@ def test_running_as_root_is_blocked(tmp_path: pathlib.Path) -> None: result = _run_container( "run-as-root", volumes, - user="0:0", + user="0", ) - _assert_contains(result, "NetAlertX is running as root", result.args) - assert result.returncode != 0 + _assert_contains(result, "NetAlertX is running as ROOT", result.args) + _assert_contains(result, "Permissions fixed for read-write paths.", result.args) + assert result.returncode == 0 # container must be forced to exit 0 by termination after warning def test_running_as_uid_1000_warns(tmp_path: pathlib.Path) -> None: @@ -852,7 +853,7 @@ def test_running_as_uid_1000_warns(tmp_path: pathlib.Path) -> None: volumes, user="1000:1000", ) - _assert_contains(result, "NetAlertX is running as UID", result.args) + _assert_contains(result, "NetAlertX is running as UID 1000:1000", result.args) assert result.returncode != 0 @@ -885,10 +886,12 @@ def test_missing_app_conf_triggers_seed(tmp_path: pathlib.Path) -> None: Container automatically regenerates default configuration on startup. Expected: Automatic regeneration of default configuration. """ - paths = _setup_mount_tree(tmp_path, "missing_app_conf") - (paths["app_config"] / "app.conf").unlink() + base = tmp_path / "missing_app_conf_base" + paths = _setup_fixed_mount_tree(base) + _chown_netalertx(paths["app_config"]) + (paths["app_config"] / "testfile.txt").write_text("test") volumes = _build_volume_args(paths) - result = _run_container("missing-app-conf", volumes, user="0:0") + result = _run_container("missing-app-conf", volumes) _assert_contains(result, "Default configuration written to", result.args) assert result.returncode != 0 @@ -900,10 +903,12 @@ def test_missing_app_db_triggers_seed(tmp_path: pathlib.Path) -> None: Container automatically creates initial database schema on startup. Expected: Automatic creation of initial database schema. """ - paths = _setup_mount_tree(tmp_path, "missing_app_db") - (paths["app_db"] / "app.db").unlink() + base = tmp_path / "missing_app_db_base" + paths = _setup_fixed_mount_tree(base) + _chown_netalertx(paths["app_db"]) + (paths["app_db"] / "testfile.txt").write_text("test") volumes = _build_volume_args(paths) - result = _run_container("missing-app-db", volumes, user="0:0") + result = _run_container("missing-app-db", volumes, user="20211:20211") _assert_contains(result, "Building initial database schema", result.args) assert result.returncode != 0