Reduce what a container can do and what it contains, so that code execution inside it yields as little as possible.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add snoodleboot-io/prompticorn --skill minimal --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Minimal?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/snoodleboot-io-minimal-d6b3bda1)More formats (shields.io, HTML) on the badges page.
# Container Security Hardening (Minimal)
## Purpose
Reduce what a container can do and what it contains, so that code execution inside it yields as little as possible.
## Core Techniques
### 1. Shrink the Image and Drop Root
```dockerfile
# ❌ Before — full OS, root, build tooling baked in
FROM python:3.12
COPY . /app
RUN pip install -r /app/requirements.txt
CMD ["python", "/app/main.py"]
```
```dockerfile
# ✅ After — multi-stage, non-root, minimal runtime
FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
RUN useradd --uid 10001 --no-create-home --shell /usr/sbin/nologin app
COPY --from=build /install /usr/local
COPY --chown=10001:10001 . /app
USER 10001
WORKDIR /app
CMD ["python", "main.py"]
```
The build stage keeps compilers, headers, and package caches out of the shipped image. A numeric `USER` matters: Kubernetes `runAsNonRoot` validates the numeric UID and cannot resolve a username at admission time.
### 2. Never Put Secrets in a Layer
```dockerfile
# ❌ The token is in the layer forever — ENV and later RUN rm do not remove it
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && npm ci
# ✅ BuildKit secret mount — never written to any layer
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
```
Build with `docker build --secret id=npmrc,src=$HOME/.npmrc .`, and audit what actually shipped with `docker history --no-trunc myapp:1.4.2`.
### 3. Pin Base Images by Digest
`FROM python:3.12-slim` resolves to a different image over time; `FROM node:latest` is unreproducible by construction. You cannot say what shipped, cannot reproduce a build, and a rollback may not restore the bits you had.
```dockerfile
FROM python:3.12-slim@sha256:<digest> # immutable; renovate bots can bump it
```
### 4. Constrain the Runtime
```yaml
securityContext: # pod level
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
seccompProfile: {type: RuntimeDefault}
containers:
- name: api
image: registry.example.com/api@sha256:<digest>
securityContext: # container level
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: ["ALL"]}
volumeMounts:
- {name: tmp, mountPath: /tmp}
volumes:
- name: tmp
emptyDir: {}
```
`allowPrivilegeEscalation: false` sets `no_new_privs`, blocking setuid binaries from gaining privilege. `drop: ["ALL"]` removes even the default set — most apps need none; a listener on a port below 1024 needs `NET_BIND_SERVICE`, or better, just listen on 8080.
### 5. Scan in CI and Again on a Schedule
```bash
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 myapp:$SHA
```
Gate the build on reachable Critical/High, and re-scan running images nightly — a CVE published after your last deploy is invisible to a build-time-only scan. Triage the output with the vulnerability-assessment skill; do not fail builds on unfixable findings.
## Warning Signs
- No `USER` in the Dockerfile, or a username instead of a numeric UID
- `FROM ...:latest` or any unpinned tag in a production image
- `docker history` shows tokens, `.git`, `.env`, or SSH keys in a layer
- Containers running with `privileged: true` or the default capability set
- `readOnlyRootFilesystem` unset, so an attacker can drop a binary anywhere
- Compilers, `curl`, and a package manager present in the runtime image
- Secrets injected as env vars readable via `/proc/1/environ` or a crash dump
- Namespaces without a Pod Security Admission label, so anything can be scheduled
- Image scanning only at build time, never against what is actually running
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!