Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
29aa884
architectural change 1
adamoutler Sep 24, 2025
2e694a7
using 4 startup scripts instead of RC6
adamoutler Sep 24, 2025
8ed21a8
monolithic alpine container
adamoutler Sep 25, 2025
dfcc375
Non-root launch
adamoutler Sep 25, 2025
c228d45
Devcontainer operational, services all down
adamoutler Sep 25, 2025
798d246
expand initial filesystem
adamoutler Sep 26, 2025
b0a6f88
Update gitignore
adamoutler Sep 26, 2025
178fb54
Python up and debuggable
adamoutler Sep 26, 2025
bad67b2
fix dockerfile error
adamoutler Sep 26, 2025
2419a26
updated devcontainer dockerfile
adamoutler Sep 26, 2025
2def3f1
Validated launch on runner & hardend
adamoutler Sep 27, 2025
c5d7480
Merge branch 'jokob-sk:main' into hardening
adamoutler Sep 27, 2025
46097bb
solid hardened config
adamoutler Sep 27, 2025
b47df7b
capcheck
adamoutler Sep 27, 2025
d182a55
Move filesystem to more generic name & add perms
adamoutler Sep 28, 2025
c6efe5a
All services moved to deployed filesystem
adamoutler Sep 28, 2025
dc4848a
Information on default config and entrypoints for debug
adamoutler Sep 29, 2025
044035e
Devcontainer overlay
adamoutler Sep 30, 2025
0cd1dc8
Scanning Operational with monitoring
adamoutler Oct 1, 2025
33aa849
Debugging operational in vscode
adamoutler Oct 2, 2025
c81a054
Coderabit
adamoutler Oct 3, 2025
1e04e9f
Remove .git-placeholder, add dockerignore
adamoutler Oct 3, 2025
ada9271
all debugging online.
adamoutler Oct 3, 2025
290b6c6
Remove nohup.out
adamoutler Oct 4, 2025
558ab44
Minimize differences between devcontainer and production
adamoutler Oct 6, 2025
016a6ad
Dockerfile.debian building and running
adamoutler Oct 8, 2025
be73e3a
debian dockerfile completed properly.
adamoutler Oct 10, 2025
1be9155
Set container parameters
adamoutler Oct 12, 2025
5109a08
Additional hardening
adamoutler Oct 13, 2025
de92c95
break apart services, fix startup
adamoutler Oct 15, 2025
f57ec74
Minor alterations to ddevcontainer.
adamoutler Oct 16, 2025
e733f8a
Relay failed status to docker.
adamoutler Oct 16, 2025
a3dae08
Fix debian docker start
adamoutler Oct 16, 2025
dc44411
Improve mount permissions
adamoutler Oct 17, 2025
d11c9d7
Improve warnings.
adamoutler Oct 17, 2025
c1b573f
Add some todos
adamoutler Oct 18, 2025
7483e46
Merge remote-tracking branch 'origin/main' into hardening
adamoutler Oct 18, 2025
028335c
Coderabit suggestions
adamoutler Oct 18, 2025
62536e4
Coderabit suggestions
adamoutler Oct 18, 2025
80c1459
Final touches on devcontainer
adamoutler Oct 19, 2025
1f46f20
Generate devcontainer configs
adamoutler Oct 19, 2025
9b3ddda
Fix persistent environment issues
adamoutler Oct 19, 2025
14be7a2
Missing Slash
adamoutler Oct 19, 2025
a58b3e3
Coderabbit suggestions
adamoutler Oct 19, 2025
131c0c0
Fix fish terminal. Smarter code completion and other nicities.
adamoutler Oct 19, 2025
dcf250d
Coderabbit nitpicks.
adamoutler Oct 19, 2025
84f1283
Add novel coderabit no-write database creation
adamoutler Oct 19, 2025
8eab7ee
Update .devcontainer/scripts/setup.sh
adamoutler Oct 19, 2025
86bf0a3
Update install/production-filesystem/services/scripts/check-first-run…
adamoutler Oct 19, 2025
999feb2
Update install/production-filesystem/services/scripts/update_vendors.sh
adamoutler Oct 19, 2025
660f0c2
Update install/production-filesystem/services/scripts/update_vendors.sh
adamoutler Oct 19, 2025
066fecf
add caps to python instead of scapy.
adamoutler Oct 19, 2025
628f35c
Remove unused pythonpathpath variable
adamoutler Oct 19, 2025
5ed46da
Set caps on actual python3.12
adamoutler Oct 19, 2025
ea4c70e
Update install/production-filesystem/services/scripts/check-first-run…
adamoutler Oct 19, 2025
6f2e556
Remove duplicate file replacement logic in update_vendors.sh
adamoutler Oct 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 205 additions & 81 deletions .devcontainer/Dockerfile
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,112 +1,236 @@
# DO NOT MODIFY THIS FILE DIRECTLY. IT IS AUTO-GENERATED BY .devcontainer/scripts/generate-dockerfile.sh

# ---/Dockerfile---
# The NetAlertX Dockerfile has 3 stages:
#
# Stage 1. Builder - NetAlertX Requires special tools and packages to build our virtual environment, but
# which are not needed in future stages. We build the builder and extract the venv for runner to use as
# a base.
#
# Stage 2. Runner builds the bare minimum requirements to create an operational NetAlertX. The primary
# reason for breaking at this stage is it leaves the system in a proper state for devcontainer operation
# This image also provides a break-out point for uses who wish to execute the anti-pattern of using a
# docker container as a VM for experimentation and various development patterns.
#
# Stage 3. Hardened removes root, sudoers, folders, permissions, and locks the system down into a read-only
# compatible image. While NetAlertX does require some read-write operations, this image can guarantee the
# code pushed out by the project is the only code which will run on the system after each container restart.
# It reduces the chance of system hijacking and operates with all modern security protocols in place as is
# expected from a security appliance.
#
# This file can be built with `docker compose -f docker-compose.yml up --build --force-recreate`

FROM alpine:3.22 AS builder

ARG INSTALL_DIR=/app

ENV PYTHONUNBUFFERED=1
ENV PATH="/opt/venv/bin:$PATH"

# Install build dependencies
COPY requirements.txt /tmp/requirements.txt
RUN apk add --no-cache bash shadow python3 python3-dev gcc musl-dev libffi-dev openssl-dev git \
&& python -m venv /opt/venv

# Enable venv
ENV PATH="/opt/venv/bin:$PATH"


RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf git+https://github.com/foreign-sub/aiofreepybox.git
# Create virtual environment owned by root, but readable by everyone else. This makes it easy to copy
# into hardened stage without worrying about permissions and keeps image size small. Keeping the commands
# together makes for a slightly smaller image size.
RUN pip install -r /tmp/requirements.txt && \
chmod -R u-rwx,g-rwx /opt

# Append Iliadbox certificate to aiofreepybox

# second stage
# second stage is the main runtime stage with just the minimum required to run the application
# The runner is used for both devcontainer, and as a base for the hardened stage.
FROM alpine:3.22 AS runner

ARG INSTALL_DIR=/app

COPY --from=builder /opt/venv /opt/venv
COPY --from=builder /usr/sbin/usermod /usr/sbin/groupmod /usr/sbin/

# Enable venv
ENV PATH="/opt/venv/bin:$PATH"

# default port and listen address
ENV PORT=20211 LISTEN_ADDR=0.0.0.0

# needed for s6-overlay
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
# NetAlertX app directories
ENV NETALERTX_APP=${INSTALL_DIR}
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config
ENV NETALERTX_FRONT=${NETALERTX_APP}/front
ENV NETALERTX_SERVER=${NETALERTX_APP}/server
ENV NETALERTX_API=${NETALERTX_APP}/api
ENV NETALERTX_DB=${NETALERTX_APP}/db
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
ENV NETALERTX_BACK=${NETALERTX_APP}/back
ENV NETALERTX_LOG=${NETALERTX_APP}/log
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf

# NetAlertX log files
ENV LOG_IP_CHANGES=${NETALERTX_LOG}/IP_changes.log
ENV LOG_APP=${NETALERTX_LOG}/app.log
ENV LOG_APP_FRONT=${NETALERTX_LOG}/app_front.log
ENV LOG_REPORT_OUTPUT_TXT=${NETALERTX_LOG}/report_output.txt
ENV LOG_DB_IS_LOCKED=${NETALERTX_LOG}/db_is_locked.log
ENV LOG_REPORT_OUTPUT_HTML=${NETALERTX_LOG}/report_output.html
ENV LOG_STDERR=${NETALERTX_LOG}/stderr.log
ENV LOG_APP_PHP_ERRORS=${NETALERTX_LOG}/app.php_errors.log
ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
ENV LOG_CROND=${NETALERTX_LOG}/crond.log

# System Services configuration files
ENV SYSTEM_SERVICES=/services
ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \
${SYSTEM_SERVICES_CONFIG}"
ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \
${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \
${SYSTEM_SERVICES_RUN_LOG}"

#Python environment
ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv
ENV VIRTUAL_ENV_BIN=/opt/venv/bin
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages
ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH"

# App Environment
ENV LISTEN_ADDR=0.0.0.0
ENV PORT=20211
ENV NETALERTX_DEBUG=0
ENV VENDORSPATH=/app/back/ieee-oui.txt
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt
ENV PYTHONPATHPATH="${NETALERTX_APP}:${VIRTUAL_ENV}/bin:${PATH}"
ENV ENVIRONMENT=alpine
ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly
ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx
ENV LANG=C.UTF-8


RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap \
nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \
nginx shadow && \
rm -Rf /var/cache/apk/* && \
rm -Rf /etc/nginx && \
addgroup -g 20211 ${NETALERTX_GROUP} && \
adduser -u 20211 -D -h ${NETALERTX_APP} -G ${NETALERTX_GROUP} ${NETALERTX_USER} && \
apk del shadow



# Install application, copy files, set permissions
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} install/production-filesystem/ /
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER}
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 755 ${NETALERTX_API} \
${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
-exec chmod 750 {} \;"

# Copy the virtualenv from the builder stage
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}


# Initialize each service with the dockerfiles/init-*.sh scripts, once.
# This is done after the copy of the venv to ensure the venv is in place
# although it may be quicker to do it before the copy, it keeps the image
# layers smaller to do it after.
RUN apk add libcap && \
setcap cap_net_raw+ep /bin/busybox && \
setcap cap_net_raw,cap_net_admin+eip /usr/bin/nmap && \
setcap cap_net_raw,cap_net_admin+eip /usr/bin/arp-scan && \
setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /usr/bin/nbtscan && \
setcap cap_net_raw,cap_net_admin+eip /usr/bin/traceroute && \
setcap cap_net_raw,cap_net_admin+eip ${VIRTUAL_ENV_BIN}/scapy && \
/bin/sh /build/init-nginx.sh && \
/bin/sh /build/init-php-fpm.sh && \
/bin/sh /build/init-crond.sh && \
/bin/sh /build/init-backend.sh && \
rm -rf /build && \
apk del libcap


ENTRYPOINT ["/bin/sh","/entrypoint.sh"]

# Final hardened stage to improve security by setting least possible permissions and removing sudo access.
# When complete, if the image is compromised, there's not much that can be done with it.
# This stage is separate from Runner stage so that devcontainer can use the Runner stage.
FROM runner AS hardened

ENV UMASK=0077

# Create readonly user and group with no shell access.
# Readonly user marks folders that are created by NetAlertX, but should not be modified.
# AI may claim this is stupid, but it's actually least possible permissions as
# read-only user cannot login, cannot sudo, has no write permission, and cannot even
# read the files it owns. The read-only user is ownership-as-a-lock hardening pattern.
RUN addgroup -g 20212 ${READ_ONLY_GROUP} && \
adduser -u 20212 -G ${READ_ONLY_GROUP} -D -h /app ${READ_ONLY_USER}


# reduce permissions to minimum necessary for all NetAlertX files and folders
# Permissions 005 and 004 are not typos, they enable read-only. Everyone can
# read the read-only files, and nobody can write to them, even the readonly user.
RUN chown -R ${READ_ONLY_USER}:${READ_ONLY_GROUP} ${READ_ONLY_FOLDERS} && \
chmod -R 004 ${READ_ONLY_FOLDERS} && \
find ${READ_ONLY_FOLDERS} -type d -exec chmod 005 {} + && \
install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \
chown -R ${NETALERTX_USER}:${NETALERTX_GROUP} ${READ_WRITE_FOLDERS} && \
chmod -R 600 ${READ_WRITE_FOLDERS} && \
find ${READ_WRITE_FOLDERS} -type d -exec chmod 700 {} + && \
chown ${READ_ONLY_USER}:${READ_ONLY_GROUP} /entrypoint.sh /opt /opt/venv && \
chmod 005 /entrypoint.sh ${SYSTEM_SERVICES}/*.sh /app /opt /opt/venv && \
apk del apk-tools && \
rm -Rf /var /etc/sudoers.d/* /etc/shadow /etc/gshadow /etc/sudoers \
/lib/apk /lib/firmware /lib/modules-load.d /lib/sysctl.d /mnt /home/ /root \
/srv /media && \
sed -i "/^\(${READ_ONLY_USER}\|${NETALERTX_USER}\):/!d" /etc/passwd && \
sed -i "/^\(${READ_ONLY_GROUP}\|${NETALERTX_GROUP}\):/!d" /etc/group && \
echo -ne '#!/bin/sh\n"$@"\n' > /usr/bin/sudo && chmod +x /usr/bin/sudo

USER netalertx

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD /services/healthcheck.sh

# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.sh file as well ❗

RUN apk update --no-cache \
&& apk add --no-cache bash libbsd zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
&& apk add --no-cache python3 nginx \
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
&& rm -f /etc/nginx/http.d/default.conf


# Add crontab file
COPY --chmod=600 --chown=root:root install/crontab /etc/crontabs/root

# Start all required services

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \
CMD curl -sf -o /dev/null ${LISTEN_ADDR}:${PORT}/php/server/query_json.php?file=app_state.json

ENTRYPOINT ["/init"]

# ---/resources/devcontainer-Dockerfile---

# Devcontainer build stage (do not build directly)
# This file is combined with the root /Dockerfile by
# .devcontainer/scripts/generate-dockerfile.sh
# .devcontainer/scripts/generate-configs.sh
# The generator appends this stage to produce .devcontainer/Dockerfile.
# Prefer to place dev-only setup here; use setup.sh only for runtime fixes.

FROM runner AS devcontainer
ENV INSTALL_DIR=/app
FROM runner AS netalertx-devcontainer
ENV INSTALL_DIR=/app

ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages
ENV PATH=/services:${PATH}
ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d
ENV LISTEN_ADDR=0.0.0.0
ENV PORT=20211
ENV NETALERTX_DEBUG=1
ENV PYDEVD_DISABLE_FILE_VALIDATION=1
COPY .devcontainer/resources/devcontainer-overlay/ /

# 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 && \
adduser -D -s /bin/sh netalertx && \
addgroup netalertx nginx && \
addgroup netalertx www-data && \
echo "netalertx ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-netalertx && \
chmod 440 /etc/sudoers.d/90-netalertx
RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest pytest-cov fish shfmt sudo

RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \
cp -a /usr/lib/php83/modules/. /services/php/modules/ && \
echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# Install debugpy in the virtualenv if present, otherwise into system python3
RUN /bin/sh -c '(/opt/venv/bin/python3 -m pip install --no-cache-dir debugpy) || (python3 -m pip install --no-cache-dir debugpy) || true'
# setup nginx
COPY .devcontainer/resources/netalertx-devcontainer.conf /etc/nginx/http.d/netalert-frontend.conf
RUN set -e; \
chown netalertx:nginx /etc/nginx/http.d/netalert-frontend.conf; \
install -d -o netalertx -g www-data -m 775 /app; \
install -d -o netalertx -g www-data -m 755 /run/nginx; \
install -d -o netalertx -g www-data -m 755 /var/lib/nginx/logs; \
rm -f /var/lib/nginx/logs/* || true; \
for f in error access; do : > /var/lib/nginx/logs/$f.log; done; \
install -d -o netalertx -g www-data -m 777 /run/php; \
install -d -o netalertx -g www-data -m 775 /var/log/php; \
chown -R netalertx:www-data /etc/nginx/http.d; \
chmod -R 775 /etc/nginx/http.d; \
chown -R netalertx:www-data /var/lib/nginx; \
chmod -R 755 /var/lib/nginx && \
chown -R netalertx:www-data /var/log/nginx/ && \
sed -i '/^user /d' /etc/nginx/nginx.conf; \
sed -i 's|^error_log .*|error_log /dev/stderr warn;|' /etc/nginx/nginx.conf; \
sed -i 's|^access_log .*|access_log /dev/stdout main;|' /etc/nginx/nginx.conf; \
sed -i 's|error_log .*|error_log /dev/stderr warn;|g' /etc/nginx/http.d/*.conf 2>/dev/null || true; \
sed -i 's|access_log .*|access_log /dev/stdout main;|g' /etc/nginx/http.d/*.conf 2>/dev/null || true; \
mkdir -p /run/openrc; \
chown netalertx:nginx /run/openrc/; \
rm -Rf /run/openrc/*;

# setup pytest
RUN sudo /opt/venv/bin/python -m pip install -U pytest pytest-cov

WORKDIR /workspaces/NetAlertX


ENTRYPOINT ["/bin/sh","-c","sleep infinity"]
RUN /bin/sh -c '(/opt/venv/bin/python3 -m pip install --no-cache-dir debugpy) || (python3 -m pip install --no-cache-dir debugpy) || true' && \
mkdir /workspaces && \
install -d -o netalertx -g netalertx -m 777 /services/run/logs && \
install -d -o netalertx -g netalertx -m 777 /app/run/tmp/client_body && \
sed -i -e 's|:/app:|:/workspaces:|' /etc/passwd && \
python -m pip install -U pytest pytest-cov

ENTRYPOINT ["/bin/sh","-c","sleep infinity"]
Loading