Security Model for iii Docker Containers: Distroless Runtime and Defence-in-Depth
The iii engine implements a defence-in-depth security model using distroless base images, non-root execution, read-only filesystems, and minimal Linux capabilities to eliminate shell access and reduce attack surface.
The iii project from iii-hq/iii packages its workflow execution engine as a hardened Docker container designed for production security. Understanding the security model for iii Docker containers is essential for deploying this open-source engine in enterprise environments. The architecture leverages distroless runtime images and multiple hardening layers to prevent privilege escalation and container escapes.
Distroless Base Image and Non-Root Execution
The foundation of the security model starts with the base image selection. According to the source code in engine/Dockerfile, the iii engine uses gcr.io/distroless/cc-debian12:nonroot as its base image. This distroless image contains only the runtime libraries necessary to execute the compiled binary and explicitly excludes shells, package managers, or standard Linux utilities.
This choice eliminates an entire class of attacks where an attacker attempts to spawn an interactive shell or install additional tooling inside a compromised container. The image runs as a built-in non-root user (nonroot), and the Dockerfile never switches to the root user during the build process.
FROM gcr.io/distroless/cc-debian12:nonroot
ARG TARGETARCH
COPY iii-${TARGETARCH} /app/iii
ENV III_EXECUTION_CONTEXT=docker \
III_ENV=development
EXPOSE 49134 3111 3112 9464
ENTRYPOINT ["/app/iii"]
CMD ["--config", "/app/config.yaml"]
Runtime Filesystem and Capability Hardening
Beyond the base image, the production deployment configuration implements additional hardening measures documented in engine/README.md. These settings create an immutable runtime environment that restricts what a compromised process can modify or execute.
Read-Only Filesystem and Temporary Storage
Production containers launch with the --read-only flag, making the root filesystem immutable. A temporary in-memory filesystem (--tmpfs /tmp) provides writable space for ephemeral data without persisting changes to the container layers.
Linux Capability Reduction
The runtime drops all Linux capabilities using --cap-drop=ALL, then selectively re-adds only NET_BIND_SERVICE to allow binding to privileged ports if required. This eliminates dangerous capabilities such as SYS_ADMIN or NET_ADMIN that could enable container escapes.
No-New-Privileges Protection
The --security-opt=no-new-privileges:true flag ensures that processes cannot gain additional privileges through mechanisms like setuid binaries, even if they attempt to execute privileged operations.
docker run \
--read-only \
--tmpfs /tmp \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt=no-new-privileges:true \
-v ./iii-config.yaml:/app/iii-config.yaml:ro \
-p 3111:3111 -p 49134:49134 -p 3112:3112 -p 9464:9464 \
iiidev/iii:latest
Network Surface Minimization
The security model restricts network exposure by publishing only the specific ports required for engine operation. As documented in the README, these include:
- 3111: Primary engine port
- 3112: Secondary engine port
- 49134: Internal communication
- 9464: Metrics endpoint
This selective port binding reduces the network attack surface exposed to other services or the internet.
CI/CD Security Scanning and SBOM Generation
The continuous integration pipeline enforces security through automated vulnerability scanning. According to .github/workflows/docker-engine.yml, every image build undergoes scanning with Trivy to detect critical and high-severity CVEs before publication. The workflow also generates a Software Bill of Materials (SBOM) to provide provenance and transparency for downstream consumers.
- name: Run Trivy vulnerability scanner
continue-on-error: true
uses: aquasecurity/trivy-action@0.35.0
with:
image-ref: ${{ env.DOCKERHUB_REPO }}:${{ needs.setup.outputs.version }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '0'
Worker Container Isolation
The security model extends beyond the main engine to user-provided worker functions. Separate Docker images for Python and Node workers enforce the same non-root execution principles. The Python worker image, defined in crates/iii-worker/images/python/Dockerfile, creates a dedicated python-user (UID 1000) and installs only minimal build tools required for the language runtime. Similarly, the Node worker uses a dedicated node user.
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
LANG=C.UTF-8 \
DEBIAN_FRONTEND=noninteractive
ARG USER_NAME="python-user"
ARG USER_UID="1000"
ARG USER_GID="100"
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential curl git wget libssl-dev ca-certificates \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
&& useradd -m -s /bin/bash -N -u $USER_UID $USER_NAME \
&& mkdir -p /home/$USER_NAME/work \
&& chown -R $USER_NAME:$USER_GID /home/$USER_NAME \
&& pip install --no-cache-dir --upgrade pip setuptools wheel \
&& pip install --no-cache-dir black flake8 mypy pytest pytest-cov requests ipython
USER $USER_NAME
WORKDIR /home/$USER_NAME/work
CMD ["tail", "-f", "/dev/null"]
This approach ensures that arbitrary user code executes within the same restricted environment as the main engine, preventing privilege escalation from worker processes.
Summary
The security model for iii Docker containers creates a minimal, auditable runtime through multiple defence-in-depth layers:
- Distroless foundation: The
gcr.io/distroless/cc-debian12:nonrootbase image eliminates shells and package managers from the engine container. - Non-root execution: Both the engine and worker containers run as unprivileged users without UID 0 access.
- Immutable filesystems: Production deployments use
--read-onlyroot filesystems with ephemeral/tmpstorage. - Capability minimization: All Linux capabilities drop except
NET_BIND_SERVICE, with--no-new-privilegespreventing escalation. - Supply chain verification: CI pipelines scan images with Trivy and generate SBOMs for vulnerability tracking.
- Network restriction: Only ports 3111, 3112, 49134, and 9464 are exposed, limiting the attack surface.
Frequently Asked Questions
What makes distroless images more secure than standard container images?
Distroless images contain only the application and its runtime dependencies, excluding package managers, shells, and standard Linux utilities. In engine/Dockerfile, the iii project uses gcr.io/distroless/cc-debian12:nonroot, which prevents attackers from spawning interactive shells or installing additional tooling if they compromise the container. This significantly reduces the attack surface compared to Debian or Alpine base images that include full operating system utilities.
How does iii prevent privilege escalation from worker functions?
The iii project isolates user-provided code in separate worker containers defined in crates/iii-worker/images/python/Dockerfile and crates/iii-worker/images/node/Dockerfile. These images create dedicated non-root users (python-user and node) with UID 1000, install only minimal language runtimes, and never execute as root. This containment ensures that arbitrary user code runs with the same privilege restrictions as the main engine, preventing container escapes through setuid binaries or kernel exploitation.
Which Linux capabilities does the iii container retain?
The production runtime configuration drops all Linux capabilities using --cap-drop=ALL and selectively re-adds only NET_BIND_SERVICE if the engine needs to bind to privileged ports below 1024. This configuration, documented in engine/README.md, removes dangerous capabilities such as SYS_ADMIN, NET_ADMIN, and SYS_PTRACE that attackers commonly exploit for container escapes or network sniffing.
How does the iii project detect vulnerabilities in container images?
The continuous integration pipeline defined in .github/workflows/docker-engine.yml automatically scans every build with Trivy, a vulnerability scanner that detects CVEs in OS packages and application dependencies. The workflow specifically flags critical and high-severity vulnerabilities and generates a Software Bill of Materials (SBOM) to provide provenance tracking. This ensures that known security issues are identified before images publish to Docker Hub.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →