Files
dockhand/Dockerfile
T

173 lines
6.3 KiB
Docker
Raw Normal View History

2026-01-02 12:24:43 +01:00
# syntax=docker/dockerfile:1.4
# =============================================================================
2026-03-02 07:59:58 +01:00
# Dockhand Docker Image - Node.js Runtime (Security-Hardened Build)
2026-01-02 12:24:43 +01:00
# =============================================================================
2026-03-02 07:59:58 +01:00
# Uses Node.js instead of Bun to eliminate BoringSSL native memory leaks
# on mTLS connections. Same Wolfi-based security-hardened OS.
2026-01-02 12:24:43 +01:00
# =============================================================================
# -----------------------------------------------------------------------------
# Stage 1: OS Generator (Alpine + apko tool)
# -----------------------------------------------------------------------------
FROM alpine:3.21 AS os-builder
2026-01-03 14:56:20 +01:00
ARG TARGETARCH
2026-01-02 12:24:43 +01:00
WORKDIR /work
2026-03-02 07:59:58 +01:00
# Install apko tool
2026-01-02 12:24:43 +01:00
ARG APKO_VERSION=0.30.34
2026-01-03 14:56:20 +01:00
RUN apk add --no-cache curl unzip \
2026-01-02 12:24:43 +01:00
&& ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \
&& curl -sL "https://github.com/chainguard-dev/apko/releases/download/v${APKO_VERSION}/apko_${APKO_VERSION}_linux_${ARCH}.tar.gz" \
| tar -xz --strip-components=1 -C /usr/local/bin \
&& chmod +x /usr/local/bin/apko
2026-03-03 10:17:41 +01:00
# Generate apko.yaml — Node.js binary comes from node:24-slim, not Wolfi
2026-01-02 12:24:43 +01:00
RUN APKO_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x86_64") \
&& printf '%s\n' \
"contents:" \
" repositories:" \
" - https://packages.wolfi.dev/os" \
" keyring:" \
" - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub" \
" packages:" \
" - wolfi-base" \
" - ca-certificates" \
" - busybox" \
" - tzdata" \
" - docker-cli" \
2026-03-13 08:22:10 +01:00
" - docker-compose=5.0.2-r1" \
2026-01-17 15:06:14 +01:00
" - docker-cli-buildx" \
2026-01-02 12:24:43 +01:00
" - sqlite" \
2026-01-28 07:33:57 +01:00
" - postgresql-client" \
2026-01-02 12:24:43 +01:00
" - git" \
" - openssh-client" \
2026-02-16 08:46:56 +01:00
" - openssh-keygen" \
2026-01-02 12:24:43 +01:00
" - curl" \
" - tini" \
" - su-exec" \
2026-03-02 07:59:58 +01:00
" - glibc" \
" - libstdc++" \
2026-01-02 12:24:43 +01:00
"entrypoint:" \
" command: /bin/sh -l" \
"archs:" \
" - ${APKO_ARCH}" \
> apko.yaml
# Build the OS tarball and extract rootfs
RUN apko build apko.yaml dockhand-base:latest output.tar \
&& mkdir -p rootfs \
&& tar -xf output.tar \
&& LAYER=$(tar -tf output.tar | grep '.tar.gz$' | head -1) \
&& tar -xzf "$LAYER" -C rootfs
# -----------------------------------------------------------------------------
2026-03-02 07:59:58 +01:00
# Stage 2: Application Builder (pure Node.js)
2026-01-02 12:24:43 +01:00
# -----------------------------------------------------------------------------
2026-03-03 10:17:41 +01:00
FROM --platform=$TARGETPLATFORM node:24-slim AS app-builder
2026-01-11 07:16:18 +01:00
WORKDIR /app
# Install build dependencies
2026-03-02 07:59:58 +01:00
RUN apt-get update && apt-get install -y --no-install-recommends \
jq git curl python3 make g++ libnss-wrapper \
&& rm -rf /var/lib/apt/lists/* \
2026-02-16 08:46:56 +01:00
&& cp "$(dpkg -L libnss-wrapper | grep 'libnss_wrapper\.so$')" /usr/local/lib/libnss_wrapper.so
2026-04-03 11:51:42 +02:00
# Copy package files and install dependencies (--ignore-scripts blocks malicious postinstall hooks)
2026-03-02 07:59:58 +01:00
COPY package.json package-lock.json ./
2026-04-03 11:51:42 +02:00
RUN npm ci --ignore-scripts \
&& npm rebuild better-sqlite3 argon2
# Copy source code and build
COPY . .
2026-03-02 07:59:58 +01:00
RUN npm run build
2026-04-03 13:53:12 +02:00
# Production dependencies only
# Preserve better-sqlite3 native addon (no prebuilds exist for Node 24 ABI 137)
RUN cp -r node_modules/better-sqlite3/build /tmp/better-sqlite3-build \
&& rm -rf node_modules \
2026-04-03 11:51:42 +02:00
&& npm ci --omit=dev --ignore-scripts \
2026-04-03 13:53:12 +02:00
&& cp -r /tmp/better-sqlite3-build node_modules/better-sqlite3/build \
&& rm -rf node_modules/@types /tmp/better-sqlite3-build
2026-03-02 07:59:58 +01:00
# Build Go collector
2026-03-13 09:29:02 +01:00
FROM --platform=$BUILDPLATFORM golang:1.25.8 AS go-builder
2026-03-02 13:10:03 +01:00
ARG TARGETARCH
2026-03-02 07:59:58 +01:00
WORKDIR /app
COPY collector/ ./collector/
2026-03-02 13:10:03 +01:00
RUN cd collector && CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /app/bin/collection-worker .
2026-01-11 07:16:18 +01:00
2026-01-02 12:24:43 +01:00
# -----------------------------------------------------------------------------
# Stage 3: Final Image (Scratch + Custom Wolfi OS)
# -----------------------------------------------------------------------------
FROM scratch
2026-03-02 07:59:58 +01:00
# Install custom Wolfi OS with Node.js
2026-01-02 12:24:43 +01:00
COPY --from=os-builder /work/rootfs/ /
2026-03-03 10:17:41 +01:00
# Copy Node.js binary from official node:24-slim (platform-correct, conservative CPU baseline)
# Wolfi's nodejs-24 targets ARMv8.1+ which causes SIGILL on Cortex-A53 (Raspberry Pi 3+)
COPY --from=app-builder /usr/local/bin/node /usr/local/bin/node
2026-03-02 07:59:58 +01:00
# Copy libnss_wrapper for git SSH with arbitrary UIDs
2026-02-16 08:46:56 +01:00
COPY --from=app-builder /usr/local/lib/libnss_wrapper.so /usr/lib/libnss_wrapper.so
2026-01-02 12:24:43 +01:00
WORKDIR /app
2026-01-02 12:24:43 +01:00
# Set up environment variables
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
NODE_ENV=production \
PORT=3000 \
HOST=0.0.0.0 \
DATA_DIR=/app/data \
HOME=/home/dockhand \
PUID=1001 \
PGID=1001
2026-03-02 07:59:58 +01:00
# Create docker compose plugin symlink
2026-01-02 12:24:43 +01:00
RUN mkdir -p /usr/libexec/docker/cli-plugins \
2026-03-02 07:59:58 +01:00
&& ln -sf /usr/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose
2026-01-02 12:24:43 +01:00
2026-03-02 07:59:58 +01:00
# Create dockhand user and group
2026-01-02 12:24:43 +01:00
RUN addgroup -g 1001 dockhand \
&& adduser -u 1001 -G dockhand -h /home/dockhand -D dockhand
2026-03-02 07:59:58 +01:00
# Copy application files with correct ownership
2026-01-02 12:24:43 +01:00
COPY --from=app-builder --chown=dockhand:dockhand /app/node_modules ./node_modules
COPY --from=app-builder --chown=dockhand:dockhand /app/package.json ./
COPY --from=app-builder --chown=dockhand:dockhand /app/build ./build
2026-03-02 07:59:58 +01:00
COPY --from=app-builder --chown=dockhand:dockhand /app/server.js ./
# Copy Go collector binary
COPY --from=go-builder --chown=dockhand:dockhand /app/bin/collection-worker ./bin/collection-worker
# Copy database migrations
2026-01-02 12:24:43 +01:00
COPY --chown=dockhand:dockhand drizzle/ ./drizzle/
COPY --chown=dockhand:dockhand drizzle-pg/ ./drizzle-pg/
# Copy legal documents
2026-01-02 12:24:43 +01:00
COPY --chown=dockhand:dockhand LICENSE.txt PRIVACY.txt ./
2026-03-02 07:59:58 +01:00
# Copy entrypoint script
COPY docker-entrypoint-node.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
2026-01-02 12:24:43 +01:00
# Copy emergency scripts
COPY --chown=dockhand:dockhand scripts/emergency/ ./scripts/
2026-01-01 16:00:34 +01:00
RUN chmod +x ./scripts/*.sh ./scripts/**/*.sh 2>/dev/null || true
2026-03-02 07:59:58 +01:00
# Create data directories
RUN mkdir -p /home/dockhand/.dockhand/stacks /app/data \
2026-01-02 12:24:43 +01:00
&& chown dockhand:dockhand /app/data /home/dockhand /home/dockhand/.dockhand /home/dockhand/.dockhand/stacks
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
2026-03-13 08:22:10 +01:00
CMD curl -f http://localhost:${PORT:-3000}/ || exit 1
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
2026-03-13 08:22:10 +01:00
CMD []