6711ac030f
Image 0.3.9 baked socktop 1.60.1 because CI's registry layer cache reused the apt-install layer from before the 1.60.2 release. 1.60.1 has no --no-kill flag, so the restricted shell's invocation parsed it as the positional websocket URL, breaking (and on overwrite, corrupting) the local profile. Pinning the package version busts the cache and ties the installed binary to the flags the restricted shell uses. Also point the manifest's webterm container at the current tag so a manual kubectl apply cannot roll the image back to 0.2.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
192 lines
6.8 KiB
Docker
192 lines
6.8 KiB
Docker
# Multi-stage Dockerfile for socktop webterm
|
|
# This reduces the final image size significantly by separating build and runtime
|
|
|
|
# ============================================================================
|
|
# Stage 1: Documentation Builder
|
|
# ============================================================================
|
|
FROM rust:1.95-slim-bookworm AS docs-builder
|
|
|
|
WORKDIR /build
|
|
|
|
# Install required tools
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends curl ca-certificates && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install mdbook first
|
|
RUN cargo install mdbook
|
|
|
|
# Copy documentation source (includes theme files)
|
|
COPY docs ./docs
|
|
|
|
# Download Catppuccin theme CSS if not already present
|
|
RUN if [ ! -f docs/theme/catppuccin.css ]; then \
|
|
curl -fsSL https://github.com/catppuccin/mdBook/releases/latest/download/catppuccin.css \
|
|
-o docs/theme/catppuccin.css && \
|
|
echo "Catppuccin CSS downloaded successfully"; \
|
|
else \
|
|
echo "Catppuccin CSS already present"; \
|
|
fi
|
|
|
|
# Build documentation
|
|
RUN cd docs && \
|
|
mdbook build && \
|
|
ls -la book/
|
|
|
|
# ============================================================================
|
|
# Stage 2: Rust Builder
|
|
# ============================================================================
|
|
FROM rust:1.95-slim-bookworm AS rust-builder
|
|
|
|
WORKDIR /build
|
|
|
|
# Install build dependencies
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends \
|
|
pkg-config \
|
|
libssl-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy only dependency files first for better caching
|
|
COPY Cargo.toml Cargo.lock ./
|
|
|
|
# Create dummy source to cache dependencies
|
|
RUN mkdir src && \
|
|
echo "fn main() {}" > src/server.rs && \
|
|
echo "pub fn lib() {}" > src/lib.rs && \
|
|
cargo build --release && \
|
|
rm -rf src target/release/webterm-server target/release/deps/webterm*
|
|
|
|
# Copy actual source code
|
|
COPY src ./src
|
|
COPY templates ./templates
|
|
COPY static ./static
|
|
COPY build.rs ./build.rs
|
|
|
|
# Copy built documentation from docs-builder stage
|
|
COPY --from=docs-builder /build/docs/book ./static/docs
|
|
|
|
# Verify documentation was copied
|
|
RUN ls -la ./static/docs/ && \
|
|
test -f ./static/docs/index.html || (echo "ERROR: Documentation index.html not found!" && exit 1)
|
|
|
|
# Build the actual application (force rebuild by touching sources)
|
|
RUN touch src/server.rs src/lib.rs && \
|
|
cargo build --release && \
|
|
strip target/release/webterm-server
|
|
|
|
# ============================================================================
|
|
# Stage 3: Node.js Builder
|
|
# ============================================================================
|
|
FROM node:20-slim AS node-builder
|
|
|
|
WORKDIR /build
|
|
|
|
# Copy package files
|
|
COPY package.json package-lock.json ./
|
|
COPY static ./static
|
|
|
|
# Install only production dependencies
|
|
RUN npm ci --only=production && \
|
|
# Copy static files to node_modules for serving
|
|
cp static/terminado-addon.js node_modules/ && \
|
|
cp static/bg.png node_modules/ && \
|
|
cp static/styles.css node_modules/ && \
|
|
cp static/terminal.js node_modules/ && \
|
|
cp static/favicon.png node_modules/
|
|
|
|
# ============================================================================
|
|
# Stage 4: Runtime Image
|
|
# ============================================================================
|
|
FROM debian:trixie-slim
|
|
|
|
# Avoid prompts from apt
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
ENV TERM=xterm-256color
|
|
|
|
# Install only runtime dependencies
|
|
RUN apt-get update && \
|
|
apt-get upgrade -y && \
|
|
apt-get install -y --no-install-recommends \
|
|
# Runtime libraries
|
|
libssl3 \
|
|
ca-certificates \
|
|
# For socktop packages
|
|
curl \
|
|
gnupg2 \
|
|
# Shell and utilities
|
|
bash \
|
|
procps \
|
|
# Health check
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Add socktop APT repository and install packages.
|
|
# The version is pinned: the restricted shell passes flags that must exist in
|
|
# the installed binary (e.g. --no-kill), and CI's registry layer cache would
|
|
# otherwise happily reuse an apt layer from before a socktop release. Bump the
|
|
# pin together with any restricted-shell.sh change that uses a new flag.
|
|
ARG SOCKTOP_VERSION=1.60.2-1
|
|
RUN curl -fsSL https://jasonwitty.github.io/socktop/KEY.gpg | \
|
|
gpg --dearmor -o /usr/share/keyrings/socktop-archive-keyring.gpg && \
|
|
echo "deb [signed-by=/usr/share/keyrings/socktop-archive-keyring.gpg] https://jasonwitty.github.io/socktop stable main" > /etc/apt/sources.list.d/socktop.list && \
|
|
apt-get update && \
|
|
apt-get install -y --no-install-recommends socktop=${SOCKTOP_VERSION} socktop-agent=${SOCKTOP_VERSION} && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create application user (if not already exists from socktop packages)
|
|
RUN id -u socktop &>/dev/null || useradd -m -s /bin/bash socktop && \
|
|
mkdir -p /home/socktop/.config/socktop && \
|
|
chown -R socktop:socktop /home/socktop
|
|
|
|
# Unprivileged user that per-session shells run as (see docker/session-shell.sh).
|
|
# Separate from socktop so a session cannot signal the server, the agent, or
|
|
# anything else that matters — the kernel refuses cross-UID signals.
|
|
RUN useradd -m -s /usr/sbin/nologin demo
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy built binary from rust-builder
|
|
COPY --from=rust-builder /build/target/release/webterm-server /usr/local/bin/webterm-server
|
|
|
|
# Copy templates and static files
|
|
COPY --from=rust-builder /build/templates ./templates
|
|
COPY --from=rust-builder /build/static ./static
|
|
|
|
# Verify documentation is present in static/docs
|
|
RUN ls -la ./static/docs/ && \
|
|
test -f ./static/docs/index.html || echo "WARNING: Documentation not found in static/docs"
|
|
|
|
# Copy node_modules from node-builder
|
|
COPY --from=node-builder /build/node_modules ./node_modules
|
|
|
|
# Copy runtime scripts
|
|
COPY docker/entrypoint.sh /entrypoint.sh
|
|
COPY docker/init-config.sh /init-config.sh
|
|
COPY docker/restricted-shell.sh /usr/local/bin/restricted-shell.sh
|
|
COPY docker/session-shell.sh /usr/local/bin/session-shell.sh
|
|
RUN chmod +x /entrypoint.sh /init-config.sh /usr/local/bin/restricted-shell.sh /usr/local/bin/session-shell.sh
|
|
|
|
# Expose ports
|
|
# 8082 - webterm HTTP server
|
|
# 3001 - socktop agent (if used)
|
|
EXPOSE 8082 3001
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
CMD curl -f http://localhost:8082/ || exit 1
|
|
|
|
# entrypoint.sh handles both cases itself: as root it prepares /home/demo,
|
|
# runs the agent as the socktop user, and keeps webterm-server as root so it
|
|
# can drop each session to the demo user (CAP_SETUID/CAP_SETGID); as non-root
|
|
# it behaves as before (single-UID, no session drop).
|
|
RUN ln -sf /entrypoint.sh /docker-entrypoint.sh
|
|
|
|
# Set entrypoint to the wrapper
|
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
|
|
# Default command - sessions enter via session-shell.sh (per-session privilege
|
|
# drop when root) which execs the restricted shell that only allows socktop
|
|
CMD ["webterm-server", "--host", "0.0.0.0", "--port", "8082", "--command", "/usr/local/bin/session-shell.sh"]
|