Skip to content

Conversation

@adamoutler
Copy link
Collaborator

@adamoutler adamoutler commented Jan 21, 2026

Refactor Dockerfile.debian to match alpine security, structure, compatibility, and startup.

Summary by CodeRabbit

  • Chores
    • Reworked container image build into a multi-stage builder/runner/hardened pipeline.
    • Hardened runtime with explicit read-only/read-write directories, refined permission model, and reduced attack surface.
    • Improved initialization, lifecycle scripts and health checks for more reliable startup and monitoring.

✏️ Tip: You can customize this high-level summary in your review settings.

Refactor Dockerfile to improve structure and security.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

Replaces a single-stage Debian image with a multi-stage Dockerfile (builder, runner, hardened), adding build/runtime scaffolding, explicit read-only/read-write folder delineation, environment variables for NetAlertX paths, lifecycle bootstrap scripts, and a healthcheck; applies permission hardening and runtime pruning.

Changes

Cohort / File(s) Summary
Dockerfile multi-stage
Dockerfile.debian
Replaced single-stage image with three stages: builder (build deps, venv, preinstall), runner (ARG/ENV scaffolding for apps/data/config/server/logs/plugins, PHP/nginx setup, copy filesystem, venv init), and hardened (prune, create readonly user, remove package managers/sensitive files, adjust permissions, dummy sudo shim, entrypoint and healthcheck). Added READ_ONLY/READ_WRITE folder sets, expanded PATH/PYTHONPATH, and lifecycle/bootstrap scripts. Lines: +191/-128.

Possibly related PRs

  • PR #1268: Changes READ_WRITE/READ_ONLY folder permission handling and nginx/service config ownership in the Dockerfile.
  • PR #1304: Overlaps on PHP install, apt flags, and hadolint/cleanup adjustments to Dockerfile.debian.

Poem

🐰 In layers I dig, then stitch and mend,
Builder, runner, hardened—each a friend.
Permissions set, the paths align,
A safer image, byte by byte I dine.
Hop! The container hums, secure and fine. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor Dockerfile.debian for multi-stage build and hardening' directly and accurately captures the main changes: converting to multi-stage build architecture and adding hardening measures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@adamoutler adamoutler mentioned this pull request Jan 21, 2026
4 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@Dockerfile.debian`:
- Around line 42-49: There is a duplicate environment variable declaration for
NETALERTX_BACK; remove the redundant ENV NETALERTX_BACK line so the variable is
only defined once (keep the original intended definition and delete the
duplicate), and verify NETALERTX_BACK is referenced consistently with
NETALERTX_APP elsewhere (no other ENV entries rely on the removed line).
- Line 235: Replace the dummy sudo script content written by the current printf
invocation that creates /usr/bin/sudo so it uses exec and correct argument
quoting; instead of writing '#!/bin/sh\n"$@"\n' produce a script that starts
with the shebang and calls exec "$@" to forward and execute the original argv
list (then keep the chmod +x). Locate the printf that writes /usr/bin/sudo and
change the string it writes to include exec "$@" with proper quoting.
🧹 Nitpick comments (5)
Dockerfile.debian (5)

94-94: Hardcoded Python version in PYTHONPATH.

The path python3.11 is hardcoded. While Debian bookworm ships with Python 3.11, consider making this dynamic for maintainability:

Alternative approach
-ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.11/site-packages
+ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}

The virtual environment's site-packages is typically already on the path when the venv is activated via PATH.


138-139: Consider verifying the GPG key fingerprint for the PHP repository.

The GPG key is downloaded over HTTPS but not verified against a known fingerprint. For supply-chain security, consider adding fingerprint verification:

Proposed fix
-    && wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
+    && wget -qO /tmp/php.gpg https://packages.sury.org/php/apt.gpg \
+    && gpg --dearmor -o /etc/apt/trusted.gpg.d/php.gpg /tmp/php.gpg \
+    && rm /tmp/php.gpg \

Alternatively, document the expected fingerprint in a comment for auditability.


191-196: Commented-out code should be addressed or removed.

There are several commented-out lines:

  • Lines 191-192: Python setcap (with a note about dynamic path)
  • Lines 195-196: Cron init (with a note about Debian differences)

If these are intentionally disabled, consider adding TODO/FIXME comments with issue references, or remove if not needed.


110-110: tini is installed but not used in ENTRYPOINT.

tini is installed as an init system to handle zombie processes and signal forwarding, but the entrypoints use /bin/bash and /root-entrypoint.sh directly without tini.

Consider using tini in entrypoint
-ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
+ENTRYPOINT ["/usr/bin/tini", "--", "/bin/bash", "/entrypoint.sh"]

And for the hardened stage:

-ENTRYPOINT ["/root-entrypoint.sh"]
+ENTRYPOINT ["/usr/bin/tini", "--", "/root-entrypoint.sh"]

237-238: Add a clarifying comment about the permission-priming rationale for running as root initially.

The /root-entrypoint.sh script does properly drop privileges to the netalertx user (UID 20211 by default) via su-exec after performing permission-priming operations on mounted volumes. This is intentional for handling host volume ownership issues on appliances like Synology where Docker volume copy-up is limited. However, this design is not obvious from the Dockerfile alone and would benefit from a brief inline comment before USER 0:

# Run as root initially for best-effort permission priming on host mounts
# (privileges are dropped by root-entrypoint.sh via su-exec after initialization)
USER 0
ENTRYPOINT ["/root-entrypoint.sh"]

The hardening efforts remain effective since the application runs as the unprivileged netalertx user after the entrypoint completes.

Co-authored-by: Jokob @netalertx <96159884+jokob-sk@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@Dockerfile.debian`:
- Around line 106-149: The Dockerfile RUN installation block is missing
libcap2-bin, causing subsequent setcap calls (used to grant raw socket
capabilities to nmap, arp-scan, nbtscan, traceroute) to fail; update the apt-get
install list inside the RUN that installs system packages (the multi-line RUN
where packages like nmap, arp-scan, nbtscan, traceroute, php8.3-*, gosu are
installed) to include libcap2-bin so setcap is available during the build and
the later setcap invocations succeed.
♻️ Duplicate comments (1)
Dockerfile.debian (1)

216-234: Dummy sudo script doesn’t exec argv correctly.
"$@" alone doesn’t execute the command and collapses args. This was raised previously and still appears unchanged.

🐛 Proposed fix
-    printf '#!/bin/sh\n"$@"\n' > /usr/bin/sudo && chmod +x /usr/bin/sudo
+    printf '#!/bin/sh\nexec "$@"\n' > /usr/bin/sudo && chmod +x /usr/bin/sudo
🧹 Nitpick comments (1)
Dockerfile.debian (1)

90-104: Avoid hard-coding the Python minor version in PYTHONPATH.
python3.11 can drift if the base image changes; the venv already injects site-packages. Consider only app paths here (or derive dynamically).

♻️ Proposed adjustment
-ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.11/site-packages
+ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}

Comment on lines +106 to +149
# Install dependencies
# Using sury.org for PHP 8.3 to match Alpine version
RUN apt-get update && apt-get install -y --no-install-recommends \
tini snmp ca-certificates curl libwww-perl arp-scan sudo gettext-base \
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \
python3 python3-dev iproute2 nmap fping python3-pip zip git systemctl usbutils traceroute nbtscan openrc \
busybox nginx nginx-core mtr python3-venv && \
rm -rf /var/lib/apt/lists/*

# While php8.3 is in debian bookworm repos, php-fpm is not included so we need to add sury.org repo
# (Ondřej Surý maintains php packages for debian. This is temp until debian includes php-fpm in their
# repos. Likely it will be in Debian Trixie.). This keeps the image up-to-date with the alpine version.
# hadolint ignore=DL3008
RUN apt-get install -y --no-install-recommends \
apt-transport-https \
tini \
snmp \
ca-certificates \
curl \
libwww-perl \
arp-scan \
sudo \
gettext-base \
nginx-light \
sqlite3 \
dnsutils \
net-tools \
python3 \
iproute2 \
nmap \
fping \
zip \
git \
usbutils \
traceroute \
nbtscan \
lsb-release \
wget && \
wget -q -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg && \
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list && \
apt-get update && \
apt-get install -y --no-install-recommends php8.3-fpm php8.3-cli php8.3-sqlite3 php8.3-common php8.3-curl php8.3-cgi && \
ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm83 && \
rm -rf /var/lib/apt/lists/* # make it compatible with alpine version

# Setup virtual python environment and use pip3 to install packages
RUN python3 -m venv ${VIRTUAL_ENV} && \
/bin/bash -c "source ${VIRTUAL_ENV_BIN}/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install -r ${INSTALL_DIR}/requirements.txt"

# Configure php-fpm
RUN chmod -R 755 /services && \
chown -R ${USER}:${USER_GID} /services && \
sed -i 's/^;listen.mode = .*/listen.mode = 0666/' ${SYSTEM_SERVICES_PHP_FPM_D}/www.conf && \
printf "user = %s\ngroup = %s\n" "${USER}" "${USER_GID}" >> /services/config/php/php-fpm.d/www.conf



# Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt
USER netalertx:netalertx
ENTRYPOINT ["/bin/bash","/entrypoint.sh"]


wget \
apt-transport-https \
gnupg2 \
mtr \
procps \
gosu \
&& wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
php8.3-fpm \
php8.3-cli \
php8.3-sqlite3 \
php8.3-common \
php8.3-curl \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm83 \
&& ln -s /usr/sbin/gosu /usr/sbin/su-exec \
&& rm -rf /var/lib/apt/lists/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for setcap usage in Dockerfile.debian
rg "setcap" Dockerfile.debian

Repository: netalertx/NetAlertX

Length of output: 429


🏁 Script executed:

# Also check the full context of the Dockerfile
wc -l Dockerfile.debian

Repository: netalertx/NetAlertX

Length of output: 84


🏁 Script executed:

# Get the full Dockerfile to understand the context
cat Dockerfile.debian

Repository: netalertx/NetAlertX

Length of output: 9102


Install libcap2-bin for setcap usage.
setcap is invoked later in the RUN block to set capabilities for raw socket access on nmap, arp-scan, nbtscan, and traceroute; without libcap2-bin the build will fail.

Proposed fix
     mtr \
     procps \
+    libcap2-bin \
     gosu \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Install dependencies
# Using sury.org for PHP 8.3 to match Alpine version
RUN apt-get update && apt-get install -y --no-install-recommends \
tini snmp ca-certificates curl libwww-perl arp-scan sudo gettext-base \
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \
python3 python3-dev iproute2 nmap fping python3-pip zip git systemctl usbutils traceroute nbtscan openrc \
busybox nginx nginx-core mtr python3-venv && \
rm -rf /var/lib/apt/lists/*
# While php8.3 is in debian bookworm repos, php-fpm is not included so we need to add sury.org repo
# (Ondřej Surý maintains php packages for debian. This is temp until debian includes php-fpm in their
# repos. Likely it will be in Debian Trixie.). This keeps the image up-to-date with the alpine version.
# hadolint ignore=DL3008
RUN apt-get install -y --no-install-recommends \
apt-transport-https \
tini \
snmp \
ca-certificates \
curl \
libwww-perl \
arp-scan \
sudo \
gettext-base \
nginx-light \
sqlite3 \
dnsutils \
net-tools \
python3 \
iproute2 \
nmap \
fping \
zip \
git \
usbutils \
traceroute \
nbtscan \
lsb-release \
wget && \
wget -q -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg && \
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list && \
apt-get update && \
apt-get install -y --no-install-recommends php8.3-fpm php8.3-cli php8.3-sqlite3 php8.3-common php8.3-curl php8.3-cgi && \
ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm83 && \
rm -rf /var/lib/apt/lists/* # make it compatible with alpine version
# Setup virtual python environment and use pip3 to install packages
RUN python3 -m venv ${VIRTUAL_ENV} && \
/bin/bash -c "source ${VIRTUAL_ENV_BIN}/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install -r ${INSTALL_DIR}/requirements.txt"
# Configure php-fpm
RUN chmod -R 755 /services && \
chown -R ${USER}:${USER_GID} /services && \
sed -i 's/^;listen.mode = .*/listen.mode = 0666/' ${SYSTEM_SERVICES_PHP_FPM_D}/www.conf && \
printf "user = %s\ngroup = %s\n" "${USER}" "${USER_GID}" >> /services/config/php/php-fpm.d/www.conf
# Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt
USER netalertx:netalertx
ENTRYPOINT ["/bin/bash","/entrypoint.sh"]
wget \
apt-transport-https \
gnupg2 \
mtr \
procps \
gosu \
&& wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
php8.3-fpm \
php8.3-cli \
php8.3-sqlite3 \
php8.3-common \
php8.3-curl \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm83 \
&& ln -s /usr/sbin/gosu /usr/sbin/su-exec \
&& rm -rf /var/lib/apt/lists/*
# Install dependencies
# Using sury.org for PHP 8.3 to match Alpine version
RUN apt-get update && apt-get install -y --no-install-recommends \
tini \
snmp \
ca-certificates \
curl \
libwww-perl \
arp-scan \
sudo \
gettext-base \
nginx-light \
sqlite3 \
dnsutils \
net-tools \
python3 \
iproute2 \
nmap \
fping \
zip \
git \
usbutils \
traceroute \
nbtscan \
lsb-release \
wget \
apt-transport-https \
gnupg2 \
mtr \
procps \
libcap2-bin \
gosu \
&& wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
php8.3-fpm \
php8.3-cli \
php8.3-sqlite3 \
php8.3-common \
php8.3-curl \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm \
&& ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm83 \
&& ln -s /usr/sbin/gosu /usr/sbin/su-exec \
&& rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
In `@Dockerfile.debian` around lines 106 - 149, The Dockerfile RUN installation
block is missing libcap2-bin, causing subsequent setcap calls (used to grant raw
socket capabilities to nmap, arp-scan, nbtscan, traceroute) to fail; update the
apt-get install list inside the RUN that installs system packages (the
multi-line RUN where packages like nmap, arp-scan, nbtscan, traceroute,
php8.3-*, gosu are installed) to include libcap2-bin so setcap is available
during the build and the later setcap invocations succeed.

@jokob-sk jokob-sk merged commit 97bc220 into netalertx:main Jan 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants