mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-31 07:12:23 -07:00
Basic fixes for synology
This commit is contained in:
@@ -37,7 +37,7 @@ services:
|
||||
read_only: false
|
||||
|
||||
tmpfs:
|
||||
- "/tmp:mode=1777,uid=20211,gid=20211,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
- "/tmp:mode=1755,uid=20211,gid=20211,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
|
||||
volumes:
|
||||
test_netalertx_data:
|
||||
@@ -36,7 +36,7 @@ import pytest
|
||||
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
|
||||
GRACE_SECONDS = float(os.environ.get("NETALERTX_TEST_GRACE", "2"))
|
||||
DEFAULT_CAPS = ["NET_RAW", "NET_ADMIN", "NET_BIND_SERVICE"]
|
||||
SUBPROCESS_TIMEOUT_SECONDS = float(os.environ.get("NETALERTX_TEST_SUBPROCESS_TIMEOUT", "60"))
|
||||
SUBPROCESS_TIMEOUT_SECONDS = float(os.environ.get("NETALERTX_TEST_SUBPROCESS_TIMEOUT", "30"))
|
||||
|
||||
CONTAINER_TARGETS: dict[str, str] = {
|
||||
"data": "/data",
|
||||
@@ -665,7 +665,7 @@ def _run_container(
|
||||
stdout=subprocess.PIPE, # MUST capture stdout for test assertions and debugging
|
||||
stderr=subprocess.PIPE, # MUST capture stderr for test assertions and debugging
|
||||
text=True,
|
||||
timeout=max(SUBPROCESS_TIMEOUT_SECONDS, sleep_seconds),
|
||||
timeout=max(SUBPROCESS_TIMEOUT_SECONDS, sleep_seconds), # Coderabbit - please stop trying to increase the length of timeout.
|
||||
check=False,
|
||||
)
|
||||
|
||||
@@ -1372,7 +1372,21 @@ def test_restrictive_permissions_handling(tmp_path: pathlib.Path) -> None:
|
||||
assert "permission denied" not in result_root.output.lower()
|
||||
assert "unable to write" not in result_root.output.lower()
|
||||
|
||||
_assert_contains(result_root, "NetAlertX is running as ROOT", result_root.args)
|
||||
_assert_contains(
|
||||
result_root,
|
||||
"NetAlertX startup: Running privilege check and path priming as ROOT.",
|
||||
result_root.args,
|
||||
)
|
||||
_assert_contains_any(
|
||||
result_root,
|
||||
[
|
||||
"Running as root (PUID=0). Paths will be owned by root.",
|
||||
"WARNING: Running as root (PUID=0). Prefer a non-root PUID.",
|
||||
"NetAlertX is running as ROOT",
|
||||
"NetAlertX note: current UID 0 GID 0",
|
||||
],
|
||||
result_root.args,
|
||||
)
|
||||
|
||||
check_cmd = [
|
||||
"docker", "run", "--rm",
|
||||
|
||||
@@ -76,8 +76,8 @@ CONTAINER_PATHS = {
|
||||
TMPFS_ROOT = "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
|
||||
DEFAULT_HTTP_PORT = int(os.environ.get("NETALERTX_DEFAULT_HTTP_PORT", "20211"))
|
||||
COMPOSE_PORT_WAIT_TIMEOUT = "30"
|
||||
COMPOSE_SETTLE_WAIT_SECONDS = "20"
|
||||
COMPOSE_PORT_WAIT_TIMEOUT = 30
|
||||
COMPOSE_SETTLE_WAIT_SECONDS = 20
|
||||
PREFERRED_CUSTOM_PORTS = (22111, 22112)
|
||||
HOST_ADDR_ENV = os.environ.get("NETALERTX_HOST_ADDRS", "")
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ They run in NETALERTX_CHECK_ONLY mode to avoid starting long-running services.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import os
|
||||
import subprocess
|
||||
import uuid
|
||||
@@ -28,6 +29,27 @@ def _run_root_entrypoint(
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
name = f"netalertx-test-puidpgid-{uuid.uuid4().hex[:8]}".lower()
|
||||
|
||||
env_vars = dict(env or {})
|
||||
|
||||
processed_volumes: list[str] = []
|
||||
proc_mounts_b64: str | None = None
|
||||
if volumes:
|
||||
for volume in volumes:
|
||||
parts = volume.split(":")
|
||||
if len(parts) >= 2 and os.path.normpath(parts[1]) == "/proc/mounts":
|
||||
source_path = parts[0]
|
||||
try:
|
||||
with open(source_path, "rb") as fh:
|
||||
proc_mounts_b64 = base64.b64encode(fh.read()).decode("ascii")
|
||||
except OSError as exc:
|
||||
raise RuntimeError(f"Failed to read mock /proc/mounts source: {source_path}") from exc
|
||||
continue
|
||||
else:
|
||||
processed_volumes.append(volume)
|
||||
|
||||
if proc_mounts_b64 and "NETALERTX_PROC_MOUNTS_B64" not in env_vars:
|
||||
env_vars["NETALERTX_PROC_MOUNTS_B64"] = proc_mounts_b64
|
||||
|
||||
cmd = [
|
||||
"docker",
|
||||
"run",
|
||||
@@ -66,12 +88,12 @@ def _run_root_entrypoint(
|
||||
if user:
|
||||
cmd.extend(["--user", user])
|
||||
|
||||
if volumes:
|
||||
for volume in volumes:
|
||||
if processed_volumes:
|
||||
for volume in processed_volumes:
|
||||
cmd.extend(["-v", volume])
|
||||
|
||||
if env:
|
||||
for key, value in env.items():
|
||||
if env_vars:
|
||||
for key, value in env_vars.items():
|
||||
cmd.extend(["-e", f"{key}={value}"])
|
||||
|
||||
cmd.extend(["--entrypoint", "/root-entrypoint.sh"])
|
||||
@@ -212,6 +234,95 @@ def test_synology_like_fresh_volume_is_primed() -> None:
|
||||
print(result.stderr) # DO NOT REMOVE OR MODIFY - MANDATORY LOGGING FOR DEBUGGING & CI.
|
||||
|
||||
|
||||
@pytest.mark.feature_complete
|
||||
def test_aufs_explicit_root_no_warning() -> None:
|
||||
"""Verify that explicitly setting PUID=0 on AUFS doesn't trigger the non-root warning."""
|
||||
|
||||
volume = f"nax_test_data_aufs_root_{uuid.uuid4().hex[:8]}".lower()
|
||||
|
||||
try:
|
||||
subprocess.run(["docker", "volume", "create", volume], check=True, capture_output=True, text=True, timeout=15)
|
||||
|
||||
# Mock AUFS environment
|
||||
mock_mounts_content = "none / aufs rw,relatime 0 0\n"
|
||||
mock_file_path = f"/tmp/mock_mounts_{uuid.uuid4().hex[:8]}"
|
||||
with open(mock_file_path, "w") as f:
|
||||
f.write(mock_mounts_content)
|
||||
# Run with explicit PUID=0 - should not warn about non-root
|
||||
result = _run_root_entrypoint(
|
||||
env={"PUID": "0", "PGID": "0", "SKIP_TESTS": "1"},
|
||||
volumes=[f"{volume}:/data", f"{mock_file_path}:/proc/mounts:ro"],
|
||||
)
|
||||
|
||||
combined = (result.stdout or "") + (result.stderr or "")
|
||||
assert result.returncode == 0, f"Container should start: {combined}"
|
||||
assert "Running as root (PUID=0)" in combined, f"Should confirm running as root: {combined}"
|
||||
# Should NOT have the AUFS reduced functionality warning when running as root
|
||||
assert "Reduced functionality (AUFS + non-root user)" not in combined, f"Should not warn when explicitly using root: {combined}"
|
||||
|
||||
# Clean up mock file
|
||||
os.unlink(mock_file_path)
|
||||
|
||||
finally:
|
||||
subprocess.run(["docker", "volume", "rm", "-f", volume], check=False, capture_output=True, text=True, timeout=15)
|
||||
|
||||
|
||||
@pytest.mark.feature_complete
|
||||
def test_aufs_non_root_warns() -> None:
|
||||
"""Verify that AUFS hosts warn when running as a non-root PUID."""
|
||||
|
||||
volume = f"nax_test_data_aufs_warn_{uuid.uuid4().hex[:8]}".lower()
|
||||
|
||||
try:
|
||||
subprocess.run(["docker", "volume", "create", volume], check=True, capture_output=True, text=True, timeout=15)
|
||||
|
||||
mock_mounts_content = "none / aufs rw,relatime 0 0\n"
|
||||
mock_file_path = f"/tmp/mock_mounts_{uuid.uuid4().hex[:8]}"
|
||||
with open(mock_file_path, "w") as f:
|
||||
f.write(mock_mounts_content)
|
||||
|
||||
result = _run_root_entrypoint(
|
||||
env={"PUID": "20211", "PGID": "20211"},
|
||||
volumes=[f"{volume}:/data", f"{mock_file_path}:/proc/mounts:ro"],
|
||||
)
|
||||
|
||||
combined = (result.stdout or "") + (result.stderr or "")
|
||||
assert result.returncode == 0, f"Container should continue with warnings: {combined}"
|
||||
assert "Reduced functionality (AUFS + non-root user)" in combined, f"AUFS warning missing: {combined}"
|
||||
assert "aufs-capabilities" in combined, "Warning should link to troubleshooting guide"
|
||||
|
||||
os.unlink(mock_file_path)
|
||||
|
||||
finally:
|
||||
subprocess.run(["docker", "volume", "rm", "-f", volume], check=False, capture_output=True, text=True, timeout=15)
|
||||
|
||||
|
||||
@pytest.mark.feature_complete
|
||||
def test_non_aufs_defaults_to_20211() -> None:
|
||||
"""Verify that non-AUFS storage drivers default to PUID=20211."""
|
||||
|
||||
volume = f"nax_test_data_nonaufs_{uuid.uuid4().hex[:8]}".lower()
|
||||
|
||||
try:
|
||||
subprocess.run(["docker", "volume", "create", volume], check=True, capture_output=True, text=True, timeout=15)
|
||||
|
||||
# Run with NO PUID set and normal storage driver - should default to 20211
|
||||
result = _run_root_entrypoint(
|
||||
env={"SKIP_TESTS": "1"},
|
||||
volumes=[f"{volume}:/data"],
|
||||
)
|
||||
|
||||
combined = (result.stdout or "") + (result.stderr or "")
|
||||
assert result.returncode == 0, f"Container should start: {combined}"
|
||||
# Should NOT mention AUFS
|
||||
assert "AUFS" not in combined and "aufs" not in combined, f"Should not detect AUFS: {combined}"
|
||||
# Should not auto-default to root
|
||||
assert "Auto-defaulting to PUID=0" not in combined, f"Should not auto-default to root: {combined}"
|
||||
|
||||
finally:
|
||||
subprocess.run(["docker", "volume", "rm", "-f", volume], check=False, capture_output=True, text=True, timeout=15)
|
||||
|
||||
|
||||
@pytest.mark.feature_complete
|
||||
def test_missing_cap_chown_fails_priming() -> None:
|
||||
"""Verify that priming fails when CAP_CHOWN is missing and ownership change is needed."""
|
||||
|
||||
Reference in New Issue
Block a user