Secure Container Image Guidelines
These guidelines describe how to build and maintain the container images that host CIVITAS/CORE backend services. They complement the backend architecture guide and broader security practices for deployment.
1. Scope and Goals
- Provide a repeatable recipe for building backend images.
- Reduce the attack surface of every service container.
- Ensure images can pass the GitLab container scanning pipeline.
- Keep runtime behavior aligned with the operating assumptions of Kubernetes and the CIVITAS/CORE deployment inventory.
2. Base Image Strategy
- Use verified base images (e.g.,
registry.access.redhat.com/ubi-minimal,eclipse-temurin, or distro LTS images) that receive timely patches. - Pin exact digests/tags (
FROM eclipse-temurin:21.0.5_11-jre@sha256:…) so future rebuilds are reproducible and diffable. - Prefer distroless/minimal bases for final stages; keep glibc/alpine variants aligned with upstream support matrices.
- Track base image updates and proactively raise upgrade issues when GitLab scanning reports critical CVEs in the base layer.
3. Multi-Stage Build Pattern
- Builder stage
- Install compilers, package managers, and other heavy tooling.
- Run unit/integration tests where feasible before producing artifacts.
- Runtime stage
- Copy only the compiled artifacts plus minimal runtime dependencies (JRE, configuration templates, entrypoint scripts).
- Remove package managers (
apt,apk,yum) and temp directories before finalizing the layer.
- Enable Docker BuildKit to leverage layer caching and
--mount=type=cachefor dependency downloads without bloating the final image.
4. Dependency and Artifact Hygiene
- Keep dependency manifests (Maven/Gradle) inside the repo and avoid downloading unsigned binaries at build time.
- Vendor third-party binaries in a known location and verify checksums before copying them into the image.
- Clean
~/.m2,node_modules, build caches, and test reports between stages so they do not leak into the runtime layer.
5. Runtime User and File System
- Create an explicit, non-root user in the runtime stage and switch via
USER app. - Declare writable paths (
/var/lib/app,/tmp) and ensure ownership/permissions are set during build time. - Keep the root filesystem read-only at runtime where possible; write mutable data to Kubernetes volumes or emptyDir mounts.
- Never rely on shell access as root; all operational scripts should run under the dedicated user.
6. Configuration and Secrets
- Do not bake secrets (tokens, passwords, TLS material) into the image. Use environment variables, Kubernetes secrets, or Vault integrations at runtime.
- Template configuration files to read from environment variables instead of hard-coded values.
- Validate that default configuration disables debug endpoints and uses production-safe settings (e.g., debug mode disabled, verbose error messages disabled, development features disabled).
- Expose only the required ports and describe them in the Helm/inventory values to avoid accidental service exposure.
7. Hardening Steps
- Install only required OS packages; remove package repositories after installation to prevent downgrade attacks.
- Disable or remove interpreters (bash, curl, package managers) from the final image unless absolutely necessary for runtime.
- Include health/ready endpoints and run self-checks at container start to fail fast when configuration is invalid.
- Ensure logs are emitted to
stdout/stderrwith structured logging so clusters can aggregate them centrally.
8. Review Checklist
Before opening a merge request for container changes, confirm the following:
-
Dockerfileuses a pinned, supported base image and follows the multi-stage pattern. - Runtime user is non-root and only necessary files are copied.
- No secrets, sample credentials, or developer SSH keys remain in any layer.
- Image has been smoke-tested locally with representative configuration.
- Documentation/inventory updates accompany any change to ports, volumes, or environment variables.
Following this checklist keeps backend container images compliant with CIVITAS/CORE security expectations and reduces surprises during deployment rollouts.