mirror of
https://github.com/netbootxyz/docker-netbootxyz.git
synced 2025-08-06 22:27:41 +02:00
Implement rootless Docker container with enhanced security and preserved TFTP logging (#87)
* Implement rootless Docker container with preserved TFTP logging - Convert all processes to run as non-root user (nbxyz) for enhanced security - Add customizable PUID/PGID environment variables for volume permission management - Implement privilege dropping using gosu for secure initialization - Optimize Dockerfile with multi-stage build and better caching - Create dnsmasq wrapper script to ensure TFTP logs appear in docker logs - Configure supervisord to properly forward dnsmasq output to container stdout - Maintain full TFTP/PXE boot debugging functionality for users Security improvements: - All application processes (nginx, webapp, dnsmasq) run as nbxyz user - Only supervisord initialization runs as root, then drops privileges - Proper permission management for all service directories - Configurable user/group IDs via PUID/PGID environment variables Performance optimizations: - Multi-stage Docker build reduces final image size - Better layer caching for faster rebuilds - Optimized package installation and cleanup 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Update Dockerfile Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update root/init.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
994dc608a9
commit
ca7a3390c2
59
.dockerignore
Normal file
59
.dockerignore
Normal file
@ -0,0 +1,59 @@
|
||||
# Git and version control
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
*.md
|
||||
docs/
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
.gitlab-ci.yml
|
||||
.travis.yml
|
||||
|
||||
# Docker files
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
.tmp
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
test-results/
|
||||
|
||||
# Build artifacts
|
||||
dist/
|
||||
build/
|
97
Dockerfile
97
Dockerfile
@ -1,69 +1,106 @@
|
||||
# Build stage - Download and prepare webapp
|
||||
FROM alpine:3.22.0 AS build
|
||||
|
||||
# set version label
|
||||
# Set version label
|
||||
ARG WEBAPP_VERSION
|
||||
|
||||
RUN apk add --no-cache \
|
||||
# Install build dependencies with virtual package for easy cleanup
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
bash \
|
||||
busybox \
|
||||
curl \
|
||||
git \
|
||||
jq \
|
||||
npm && \
|
||||
mkdir /app && \
|
||||
if [ -z ${WEBAPP_VERSION+x} ]; then \
|
||||
npm \
|
||||
&& mkdir /app \
|
||||
# Determine webapp version if not provided
|
||||
&& if [ -z "${WEBAPP_VERSION+x}" ]; then \
|
||||
WEBAPP_VERSION=$(curl -sX GET "https://api.github.com/repos/netbootxyz/webapp/releases/latest" \
|
||||
| awk '/tag_name/{print $4;exit}' FS='[""]'); \
|
||||
fi && \
|
||||
curl -o /tmp/webapp.tar.gz -L \
|
||||
"https://github.com/netbootxyz/webapp/archive/${WEBAPP_VERSION}.tar.gz" && \
|
||||
tar xf /tmp/webapp.tar.gz -C /app/ --strip-components=1 && \
|
||||
npm install --prefix /app && \
|
||||
rm -rf /tmp/*
|
||||
fi \
|
||||
# Download and extract webapp
|
||||
&& curl -o /tmp/webapp.tar.gz -L \
|
||||
"https://github.com/netbootxyz/webapp/archive/${WEBAPP_VERSION}.tar.gz" \
|
||||
&& tar xf /tmp/webapp.tar.gz -C /app/ --strip-components=1 \
|
||||
# Install only production dependencies
|
||||
&& cd /app \
|
||||
&& npm install --omit=dev --no-audit --no-fund \
|
||||
# Clean up build artifacts and cache
|
||||
&& npm cache clean --force \
|
||||
&& rm -rf /tmp/* \
|
||||
&& apk del .build-deps
|
||||
|
||||
# Production stage - Final container
|
||||
FROM alpine:3.22.0
|
||||
|
||||
# set version label
|
||||
# Build arguments for labels
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
ARG VCS_REF
|
||||
|
||||
LABEL build_version="netboot.xyz version: ${VERSION} Build-date: ${BUILD_DATE}"
|
||||
LABEL maintainer="antonym"
|
||||
LABEL org.opencontainers.image.description="netboot.xyz official docker container - Your favorite operating systems in one place. A network-based bootable operating system installer based on iPXE."
|
||||
# Enhanced container labels following OCI spec
|
||||
LABEL org.opencontainers.image.title="netboot.xyz" \
|
||||
org.opencontainers.image.description="Your favorite operating systems in one place. A network-based bootable operating system installer based on iPXE." \
|
||||
org.opencontainers.image.version="${VERSION}" \
|
||||
org.opencontainers.image.created="${BUILD_DATE}" \
|
||||
org.opencontainers.image.revision="${VCS_REF}" \
|
||||
org.opencontainers.image.vendor="netboot.xyz" \
|
||||
org.opencontainers.image.url="https://netboot.xyz" \
|
||||
org.opencontainers.image.source="https://github.com/netbootxyz/docker-netbootxyz" \
|
||||
org.opencontainers.image.licenses="Apache-2.0" \
|
||||
maintainer="antonym"
|
||||
|
||||
# Install runtime dependencies and configure system in a single layer
|
||||
RUN apk add --no-cache \
|
||||
# Core utilities
|
||||
bash \
|
||||
busybox \
|
||||
curl \
|
||||
dnsmasq \
|
||||
envsubst \
|
||||
git \
|
||||
jq \
|
||||
nghttp2-dev \
|
||||
tar \
|
||||
# Network services
|
||||
dnsmasq \
|
||||
nginx \
|
||||
nodejs \
|
||||
# System services
|
||||
shadow \
|
||||
sudo \
|
||||
supervisor \
|
||||
syslog-ng \
|
||||
tar && \
|
||||
groupmod -g 1000 users && \
|
||||
useradd -u 911 -U -d /config -s /bin/false nbxyz && \
|
||||
usermod -G users nbxyz && \
|
||||
mkdir /app /config /defaults
|
||||
# Security tools
|
||||
gosu \
|
||||
# Runtime libraries
|
||||
nghttp2-dev \
|
||||
# Create required directories
|
||||
&& mkdir -p /app /config /defaults \
|
||||
# Remove unnecessary packages to reduce size
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Copy webapp from build stage
|
||||
COPY --from=build /app /app
|
||||
|
||||
ENV TFTPD_OPTS=''
|
||||
ENV NGINX_PORT='80'
|
||||
ENV WEB_APP_PORT='3000'
|
||||
# Environment variables with defaults
|
||||
ENV TFTPD_OPTS='' \
|
||||
NGINX_PORT='80' \
|
||||
WEB_APP_PORT='3000' \
|
||||
NODE_ENV='production' \
|
||||
NPM_CONFIG_CACHE='/tmp/.npm' \
|
||||
PUID='1000' \
|
||||
PGID='1000'
|
||||
|
||||
EXPOSE 69/udp
|
||||
EXPOSE 80
|
||||
EXPOSE 3000
|
||||
|
||||
COPY root/ /
|
||||
# Copy configuration files and scripts
|
||||
COPY --chown=root:root root/ /
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 CMD /healthcheck.sh
|
||||
# Make scripts executable
|
||||
RUN chmod +x /start.sh /init.sh /healthcheck.sh /usr/local/bin/dnsmasq-wrapper.sh
|
||||
|
||||
CMD ["sh","/start.sh"]
|
||||
# Enhanced health check with better timing for slow systems
|
||||
HEALTHCHECK --interval=30s --timeout=15s --start-period=60s --retries=3 \
|
||||
CMD /healthcheck.sh
|
||||
|
||||
# Use exec form for better signal handling
|
||||
CMD ["/start.sh"]
|
||||
|
21
README.md
21
README.md
@ -57,6 +57,8 @@ The following snippets are examples of starting up the container.
|
||||
```shell
|
||||
docker run -d \
|
||||
--name=netbootxyz \
|
||||
-e PUID=1000 `# optional, UserID for volume permissions` \
|
||||
-e PGID=1000 `# optional, GroupID for volume permissions` \
|
||||
-e MENU_VERSION=2.0.84 `# optional` \
|
||||
-e NGINX_PORT=80 `# optional` \
|
||||
-e WEB_APP_PORT=3000 `# optional` \
|
||||
@ -114,6 +116,8 @@ Container images are configured using parameters passed at runtime (such as thos
|
||||
| `-p 3000` | Web configuration interface. |
|
||||
| `-p 69/udp` | TFTP Port. |
|
||||
| `-p 80` | NGINX server for hosting assets. |
|
||||
| `-e PUID=1000` | UserID for volume permissions - see below for explanation |
|
||||
| `-e PGID=1000` | GroupID for volume permissions - see below for explanation |
|
||||
| `-e WEB_APP_PORT=3000` | Specify a different port for the web configuration interface to listen on. |
|
||||
| `-e NGINX_PORT=80` | Specify a different port for NGINX service to listen on. |
|
||||
| `-e MENU_VERSION=2.0.76` | Specify a specific version of boot files you want to use from netboot.xyz (unset pulls latest) |
|
||||
@ -121,6 +125,23 @@ Container images are configured using parameters passed at runtime (such as thos
|
||||
| `-v /config` | Storage for boot menu files and web application config |
|
||||
| `-v /assets` | Storage for netboot.xyz bootable assets (live CDs and other files) |
|
||||
|
||||
## User / Group Identifiers
|
||||
|
||||
When using volumes (`-v` flags), permissions issues can arise between the host OS and the container. We avoid this issue by allowing you to specify the user `PUID` and group `PGID`.
|
||||
|
||||
Ensure any volume directories on the host are owned by the same user you specify and any permissions issues will vanish like magic.
|
||||
|
||||
In this instance `PUID=1000` and `PGID=1000`, to find yours use `id your_user` as below:
|
||||
|
||||
```bash
|
||||
id your_user
|
||||
```
|
||||
|
||||
Example output:
|
||||
```bash
|
||||
uid=1000(your_user) gid=1000(your_user) groups=1000(your_user)
|
||||
```
|
||||
|
||||
## DHCP Configurations
|
||||
|
||||
The netboot.xyz Docker image requires the usage of a DHCP server in order to function properly. If you have an existing DHCP server, usually you will need to make some small adjustments to make your DHCP server forward requests to the netboot.xyz container. The main settings in your DHCP or router that you will typically need to set are:
|
||||
|
@ -1,33 +1,29 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
|
||||
[program:syslog-ng]
|
||||
command=/usr/sbin/syslog-ng --foreground --no-caps
|
||||
stdout_syslog=true
|
||||
stdout_capture_maxbytes=1MB
|
||||
priority = 1
|
||||
silent=false
|
||||
logfile=/tmp/supervisord.log
|
||||
pidfile=/run/supervisord.pid
|
||||
|
||||
[program:nginx]
|
||||
command = /usr/sbin/nginx -c /config/nginx/nginx.conf
|
||||
command = gosu nbxyz /usr/sbin/nginx -c /config/nginx/nginx.conf
|
||||
startretries = 2
|
||||
daemon=off
|
||||
priority = 2
|
||||
stdout_logfile=/dev/null
|
||||
stderr_logfile=/dev/null
|
||||
|
||||
[program:webapp]
|
||||
environment=NODE_ENV="production",PORT=%(ENV_WEB_APP_PORT)s
|
||||
command=/usr/bin/node app.js
|
||||
user=nbxyz
|
||||
command=gosu nbxyz /usr/bin/node app.js
|
||||
directory=/app
|
||||
priority = 3
|
||||
stdout_logfile=/dev/null
|
||||
stderr_logfile=/dev/null
|
||||
|
||||
[program:dnsmasq]
|
||||
command=/usr/sbin/dnsmasq --port=0 --keep-in-foreground --enable-tftp --user=nbxyz --tftp-secure --tftp-root=/config/menus %(ENV_TFTPD_OPTS)s
|
||||
stdout_logfile=/config/tftpd.log
|
||||
command=/usr/local/bin/dnsmasq-wrapper.sh %(ENV_TFTPD_OPTS)s
|
||||
priority = 3
|
||||
redirect_stderr=true
|
||||
priority = 4
|
||||
|
||||
[program:messages-log]
|
||||
command=tail -f /var/log/messages
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
|
32
root/init.sh
32
root/init.sh
@ -1,5 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configure user and group IDs
|
||||
PUID=${PUID:-1000}
|
||||
PGID=${PGID:-1000}
|
||||
|
||||
echo "[init] Setting up user nbxyz with PUID=${PUID} and PGID=${PGID}"
|
||||
|
||||
# Create group with specified GID if it doesn't exist
|
||||
if ! getent group ${PGID} > /dev/null 2>&1; then
|
||||
groupadd -g ${PGID} nbxyz
|
||||
else
|
||||
echo "[init] Group with GID ${PGID} already exists"
|
||||
fi
|
||||
|
||||
# Create user with specified UID if it doesn't exist
|
||||
if ! getent passwd ${PUID} > /dev/null 2>&1; then
|
||||
useradd -u ${PUID} -g ${PGID} -d /config -s /bin/false nbxyz
|
||||
else
|
||||
echo "[init] User with UID ${PUID} already exists"
|
||||
fi
|
||||
|
||||
# Add to users group for compatibility
|
||||
usermod -a -G users nbxyz 2>/dev/null || true
|
||||
|
||||
# make our folders
|
||||
mkdir -p \
|
||||
/assets \
|
||||
@ -7,7 +30,8 @@ mkdir -p \
|
||||
/config/log/nginx \
|
||||
/run \
|
||||
/var/lib/nginx/tmp/client_body \
|
||||
/var/tmp/nginx
|
||||
/var/tmp/nginx \
|
||||
/var/log
|
||||
|
||||
# copy config files
|
||||
[[ ! -f /config/nginx/nginx.conf ]] && \
|
||||
@ -15,10 +39,12 @@ mkdir -p \
|
||||
[[ ! -f /config/nginx/site-confs/default ]] && \
|
||||
envsubst '${NGINX_PORT}' < /defaults/default > /config/nginx/site-confs/default
|
||||
|
||||
# Ownership
|
||||
# Set up permissions for all directories that services need to write to
|
||||
chown -R nbxyz:nbxyz /assets
|
||||
chown -R nbxyz:nbxyz /var/lib/nginx
|
||||
chown -R nbxyz:nbxyz /var/log/nginx
|
||||
chown -R nbxyz:nbxyz /config/log/nginx
|
||||
chown -R nbxyz:nbxyz /run
|
||||
chown -R nbxyz:nbxyz /var/tmp/nginx
|
||||
|
||||
# create local logs dir
|
||||
mkdir -p \
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Perform the initial configuration
|
||||
# Perform the initial configuration as root
|
||||
/init.sh
|
||||
|
||||
echo " _ _ _ "
|
||||
@ -15,4 +15,7 @@ echo
|
||||
echo "https://opencollective.com/netbootxyz"
|
||||
echo "https://github.com/sponsors/netbootxyz"
|
||||
echo
|
||||
supervisord -c /etc/supervisor.conf
|
||||
|
||||
# Run supervisord as root (it will use gosu for individual programs)
|
||||
echo "[start] Starting supervisord (programs will run as nbxyz)"
|
||||
exec supervisord -c /etc/supervisor.conf
|
||||
|
10
root/usr/local/bin/dnsmasq-wrapper.sh
Normal file
10
root/usr/local/bin/dnsmasq-wrapper.sh
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Wrapper script for dnsmasq to ensure TFTP logs are visible in docker logs
|
||||
echo "[dnsmasq] Starting TFTP server on port 69"
|
||||
echo "[dnsmasq] TFTP root: /config/menus"
|
||||
echo "[dnsmasq] TFTP security: enabled"
|
||||
echo "[dnsmasq] Logging: enabled (dhcp and queries)"
|
||||
|
||||
# Start dnsmasq via gosu with logging to stderr (which supervisord can capture)
|
||||
exec gosu nbxyz /usr/sbin/dnsmasq --port=0 --keep-in-foreground --enable-tftp --user=nbxyz --tftp-secure --tftp-root=/config/menus --log-facility=- --log-dhcp --log-queries "$@"
|
Loading…
Reference in New Issue
Block a user