From 178fb54bb4c72fb5a58baf72269f5e435d57f414 Mon Sep 17 00:00:00 2001 From: Adam Outler Date: Fri, 26 Sep 2025 17:32:58 +0000 Subject: [PATCH] Python up and debuggable --- .devcontainer/Dockerfile | 38 ++++++++++++++++++++----------- .devcontainer/scripts/setup.sh | 27 +++++++++------------- Dockerfile | 41 +++++++++++++++++++++------------- 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f4ff511f..fec18e02 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,8 +1,11 @@ # DO NOT MODIFY THIS FILE DIRECTLY. IT IS AUTO-GENERATED BY .devcontainer/scripts/generate-dockerfile.sh # ---/Dockerfile--- +# Builder stage performs venv creation and installs some tools which are not needed in the final image. +# By separating the image, we are able to discard build tools and reduce the final image size. FROM alpine:3.22 AS builder + ARG INSTALL_DIR=/app ENV PYTHONUNBUFFERED=1 @@ -19,13 +22,12 @@ 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 -# 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 RUN addgroup -g 20211 netalertx && \ - adduser -u 20211 -G netalertx -D -h /app netalertx && \ - addgroup -g 20212 readonly && \ - adduser -u 20212 -G readonly -D -h /app readonly + adduser -u 20211 -G netalertx -D -h /app netalertx ARG INSTALL_DIR=/app @@ -82,7 +84,7 @@ COPY --from=builder /opt/venv /opt/venv COPY --from=builder /usr/sbin/usermod /usr/sbin/groupmod /usr/sbin/ # Simple copy of directory structure instead of individual files or complicated directory structure with RUN mkdir -COPY install/alpine-docker/ / +COPY --chown=netalertx:netalertx install/alpine-docker/ / RUN chmod -R a+x ${SYSTEM_SERVICES} /build/ /entrypoint.sh && \ sh -c "find ${NETALERTX_APP} -type d -exec chmod 750 {} \;" && \ @@ -90,7 +92,11 @@ RUN chmod -R a+x ${SYSTEM_SERVICES} /build/ /entrypoint.sh && \ sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" # Copy source -RUN mkdir ${NETALERTX_API} +COPY --chown=netalertx:netalertx --chmod=755 back ${NETALERTX_BACK} +COPY --chown=netalertx:netalertx --chmod=755 front ${NETALERTX_FRONT} +COPY --chown=netalertx:netalertx --chmod=755 server ${NETALERTX_SERVER} +# create folders with netalertx user ownership and 755 permissions +RUN install -d -o netalertx -g netalertx -m 755 ${NETALERTX_API} # Install runtime dependencies @@ -110,20 +116,26 @@ RUN echo "netalertx ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - +# Final hardened stage to improve security by setting correct permissions and removing sudo access +# When complete, if the image is compromised, there's not much that can be done with it. FROM runner AS hardened + +# create readonly user and group with no shell access +RUN addgroup -g 20212 readonly && \ + adduser -u 20212 -G readonly -D -h /app readonly + # remove netalertx from sudoers RUN sh -c "sed -i '/netalertx ALL=(ALL) NOPASSWD: ALL/d' /etc/sudoers" -RUN chown -R readonly:readonly ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} +RUN chown -R readonly:readonly ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} ${NETALERTX_SERVICES} RUN chmod -R 004 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} -RUN chmod 005 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} -RUN chmod -R 005 ${SYSTEM_SERVICES} +RUN chmod 005 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} +RUN chmod -R 005 ${SYSTEM_SERVICES} ${NETALERTX_SERVICES} -RUN chown -R netalertx:netalertx ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} ${NETALERTX_CONFIG_FILE} ${NETALERTX_DB_FILE} && \ - chmod -R 600 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_LOG} ${NETALERTX_API} && \ - chmod 700 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_LOG} ${NETALERTX_API} +RUN chown -R netalertx:netalertx ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} && \ + chmod -R 600 ${NETALERTX_CONFIG} ${NETALERTX_DB} {NETALERTX_API} ${NETALERTX_LOG} && \ + chmod 700 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} RUN chown readonly:readonly / diff --git a/.devcontainer/scripts/setup.sh b/.devcontainer/scripts/setup.sh index f6ba1d86..ee98095c 100755 --- a/.devcontainer/scripts/setup.sh +++ b/.devcontainer/scripts/setup.sh @@ -65,32 +65,27 @@ safe_link() { configure_source() { echo "[1/3] Configuring Source..." echo " -> Linking source to ${INSTALL_DIR}" - rm -Rf ${INSTALL_DIR}/* || true - - sudo ln -s -fT ${SOURCE_DIR}/back ${INSTALL_DIR}/back - sudo ln -s -fT ${SOURCE_DIR}/front ${INSTALL_DIR}/front - sudo ln -s -fT ${SOURCE_DIR}/config ${INSTALL_DIR}/config - sudo ln -s -fT ${SOURCE_DIR}/db ${INSTALL_DIR}/db - sudo ln -s -fT ${SOURCE_DIR}/server ${INSTALL_DIR}/server + sudo umount "${INSTALL_DIR}/log" 2>/dev/null + sudo umount "${INSTALL_DIR}/api" 2>/dev/null + sudo rm -Rf ${INSTALL_DIR} || true + sudo ln -s ${SOURCE_DIR} ${INSTALL_DIR} - echo " -> Mounting ramdisks for /log and /api" - - mkdir ${INSTALL_DIR}/logt ${INSTALL_DIR}/apit || true - cp -R ${SOURCE_DIR}/log/* ${INSTALL_DIR}/logt/ || true - cp ${SOURCE_DIR}/api/* ${INSTALL_DIR}/apit/ || true + sudo mkdir -p /tmp/log /tmp/api || true + sudo cp -R ${SOURCE_DIR}/log/ /tmp/log/ || true + sudo cp -R ${SOURCE_DIR}/api/ /tmp/api/ || true + sudo mkdir -p ${NETALERTX_API} ${NETALERTX_LOG} sudo mount -t tmpfs -o size=256M tmpfs "${INSTALL_DIR}/log" sudo mount -t tmpfs -o size=512M tmpfs "${INSTALL_DIR}/api" - sudo cp -R ${INSTALL_DIR}/logt/* ${INSTALL_DIR}/log/ || true - sudo cp -R ${INSTALL_DIR}/apit/* ${INSTALL_DIR}/api/ || true - rm -Rf ${INSTALL_DIR}/logt ${INSTALL_DIR}/apit || true + sudo cp -R /tmp/log/* ${NETALERTX_LOG} 2>/dev/null || true + sudo cp -R /tmp/api/* ${NETALERTX_API} 2>/dev/null || true + sudo rm -Rf /tmp/log /tmp/api || true echo "Dev">${INSTALL_DIR}/.VERSION echo " -> Setting ownership and permissions" - usermod -g netalertx nginx sudo date +%s > "${INSTALL_DIR}/front/buildtimestamp.txt" diff --git a/Dockerfile b/Dockerfile index dbbe1f9a..be20cfc4 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ +# Builder stage performs venv creation and installs some tools which are not needed in the final image. +# By separating the image, we are able to discard build tools and reduce the final image size. FROM alpine:3.22 AS builder + ARG INSTALL_DIR=/app ENV PYTHONUNBUFFERED=1 @@ -16,13 +19,12 @@ 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 -# 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 RUN addgroup -g 20211 netalertx && \ - adduser -u 20211 -G netalertx -D -h /app netalertx && \ - addgroup -g 20212 readonly && \ - adduser -u 20212 -G readonly -D -h /app readonly + adduser -u 20211 -G netalertx -D -h /app netalertx ARG INSTALL_DIR=/app @@ -79,7 +81,7 @@ COPY --from=builder /opt/venv /opt/venv COPY --from=builder /usr/sbin/usermod /usr/sbin/groupmod /usr/sbin/ # Simple copy of directory structure instead of individual files or complicated directory structure with RUN mkdir -COPY install/alpine-docker/ / +COPY --chown=netalertx:netalertx install/alpine-docker/ / RUN chmod -R a+x ${SYSTEM_SERVICES} /build/ /entrypoint.sh && \ sh -c "find ${NETALERTX_APP} -type d -exec chmod 750 {} \;" && \ @@ -87,10 +89,11 @@ RUN chmod -R a+x ${SYSTEM_SERVICES} /build/ /entrypoint.sh && \ sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" # Copy source -COPY back ${INSTALL_DIR}/back -COPY front ${INSTALL_DIR}/front -COPY server ${INSTALL_DIR}/server -RUN mkdir ${NETALERTX_API} +COPY --chown=netalertx:netalertx --chmod=755 back ${NETALERTX_BACK} +COPY --chown=netalertx:netalertx --chmod=755 front ${NETALERTX_FRONT} +COPY --chown=netalertx:netalertx --chmod=755 server ${NETALERTX_SERVER} +# create folders with netalertx user ownership and 755 permissions +RUN install -d -o netalertx -g netalertx -m 755 ${NETALERTX_API} # Install runtime dependencies @@ -111,20 +114,26 @@ RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt - +# Final hardened stage to improve security by setting correct permissions and removing sudo access +# When complete, if the image is compromised, there's not much that can be done with it. FROM runner AS hardened + +# create readonly user and group with no shell access +RUN addgroup -g 20212 readonly && \ + adduser -u 20212 -G readonly -D -h /app readonly + # remove netalertx from sudoers RUN sh -c "sed -i '/netalertx ALL=(ALL) NOPASSWD: ALL/d' /etc/sudoers" -RUN chown -R readonly:readonly ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} +RUN chown -R readonly:readonly ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} ${NETALERTX_SERVICES} RUN chmod -R 004 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} -RUN chmod 005 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} -RUN chmod -R 005 ${SYSTEM_SERVICES} +RUN chmod 005 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} +RUN chmod -R 005 ${SYSTEM_SERVICES} ${NETALERTX_SERVICES} -RUN chown -R netalertx:netalertx ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} ${NETALERTX_CONFIG_FILE} ${NETALERTX_DB_FILE} && \ - chmod -R 600 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_LOG} ${NETALERTX_API} && \ - chmod 700 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_LOG} ${NETALERTX_API} +RUN chown -R netalertx:netalertx ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} && \ + chmod -R 600 ${NETALERTX_CONFIG} ${NETALERTX_DB} {NETALERTX_API} ${NETALERTX_LOG} && \ + chmod 700 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} RUN chown readonly:readonly /