Skip to main content
Version: v0.9.0a2
Operator

Container hardening

3 min readOperatorSpec-10-Hardening

What this page is

Stigmem's reference node image ships with a hardened container posture as of pre-reset hardening (Spec-10-Hardening). This page describes the controls baked into the image and how to verify them.

What's includedโ€‹

Control
Implementation
Notes
Non-root runtime
UID/GID 65532
Baked into node/Dockerfile.
Minimal runtime image
multi-stage
Only Python 3.11 slim + installed packages reach the final layer.
No build tools at runtime
stripped
uv, compiler, and pip are absent from the shipped image.
Read-only root filesystem
enforced via Compose/Helm
/tmp and /run are tmpfs.
All capabilities dropped
CAP_DROP ALL
No capabilities re-added in Compose/Helm.
no-new-privileges
set in all recipes
Universal.
seccomp denylist
deploy/seccomp/stigmem-node.json
Extends Docker default by blocking additional container-escape syscalls.
SBOM
syft-generated SPDX JSON
Attached to every published image as an OCI referrer.
Image signing
cosign keyless (Sigstore)
Signature recorded in the Rekor transparency log.

Image referenceโ€‹

ghcr.io/eidetic-labs/stigmem-node:<tag>

The hardened image is published for linux/amd64 and linux/arm64.

For hardened production, pin to a digest

The <tag> placeholder above stands in for the tag you choose. For supply-chain-conscious deployments use @sha256:<digest> instead โ€” a digest pin is tamper-evident and immune to tag reassignment. See the tag-selection guide for the full breakdown.

Verifying the image signatureโ€‹

# Requires cosign โ‰ฅ 2.x
cosign verify \
--certificate-identity-regexp "https://github.com/eidetic-labs/stigmem/.github/workflows/publish.yml" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/eidetic-labs/stigmem-node:<tag>

A successful verification prints the signing certificate and Rekor log entry.

Verifying the SBOMโ€‹

# Download the SBOM attached to a specific digest
cosign download sbom ghcr.io/eidetic-labs/stigmem-node@<digest> \
| python3 -m json.tool | head -40

# Or pull it with oras (OCI referrers API)
oras discover --platform linux/amd64 \
ghcr.io/eidetic-labs/stigmem-node:<tag>

Running with the seccomp profileโ€‹

Docker / Composeโ€‹

The profile is shipped at deploy/seccomp/stigmem-node.json. The deploy Compose recipe loads it automatically:

docker compose -f deploy/compose/docker-compose.yml up -d

For a manual docker run:

docker run -d \
--user 65532:65532 \
--read-only \
--tmpfs /tmp:mode=1777,size=64m \
--tmpfs /run:mode=755,size=16m \
--cap-drop ALL \
--security-opt no-new-privileges \
--security-opt seccomp=deploy/seccomp/stigmem-node.json \
-v stigmem-data:/data \
-e STIGMEM_DB_PATH=/data/stigmem.db \
-p 8765:8765 \
ghcr.io/eidetic-labs/stigmem-node:0.9.0a1

For audit-traceable production, replace :0.9.0a1 with @sha256:<digest> โ€” see the tag-selection guide.

Kubernetes / Helm and Fly.io

Helm chart hardening defaults and Fly.io micro-VM guidance lived alongside the v1.0 deploy recipes. Those recipes are deferred to experimental/deploy-helm/ and experimental/deploy-fly/ in v0.9.0a1 and are unsupported until they pass the ADR-008 reintroduction gates. The Docker and Docker Compose hardening guidance above is the supported v0.9.0a1 surface.

Build reproducibilityโ€‹

The Dockerfile pins the uv version using Docker's --from copy syntax:

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

For fully reproducible builds, pin the digest:

COPY --from=ghcr.io/astral-sh/uv:0.6.14@sha256:<digest> /uv /usr/local/bin/uv

And pin the base image tag in node/Dockerfile to a specific digest as well.

What the seccomp profile blocksโ€‹

deploy/seccomp/stigmem-node.json uses a denylist strategy (default ALLOW) and explicitly blocks the following syscall classes.

Process injection

ptrace, process_vm_readv, process_vm_writev.

Kernel module loading

init_module, finit_module, delete_module.

kexec

kexec_load, kexec_file_load.

Namespace manipulation

Container escape vectors: pivot_root, mount, umount2, unshare, setns, open_tree, move_mount, fsopen, fsconfig, fsmount, fspick.

eBPF

bpf, perf_event_open.

Kernel time adjustment

adjtimex, clock_adjtime, settimeofday, clock_settime.

Privileged I/O

iopl, ioperm.

Key ring

add_key, keyctl, request_key.

Misc dangerous

reboot, syslog, chroot, acct, swapon, swapoff, userfaultfd, seccomp, quotactl.

io_uring

CVE-2022-29582, CVE-2023-2598, CVE-2022-2586: io_uring_setup, io_uring_enter, io_uring_register โ€” blocked because Python/uvicorn does not use io_uring and the interface has accumulated significant kernel exploit history.

Shocker-class escape

Host inode access via bind-mount: open_by_handle_at, name_to_handle_at.

Cross-process disclosure

kcmp.

The clone/clone3 syscalls are NOT denied.

Python's threading model requires them. Namespace-related misuse is blocked by the missing CAP_SYS_ADMIN capability, providing defense-in-depth.

Known limitationsโ€‹

Per-distro AppArmor profiles

Out of scope for pre-reset hardening; community contributions welcome.

Multi-arch matrix

Covers linux/amd64 and linux/arm64 only.

Helm RuntimeDefault

seccompProfile.type: RuntimeDefault Helm default does not apply the stigmem-specific profile; operators who want the full denylist must load the JSON file as a Localhost profile.