Repository navigation
Contrib Manifests
contrib/ holds deployment manifests, versioned with the code. Each is hardened by default. release-please moves the image tag in Compose and kustomize to every release (# x-release-please-version markers). contrib/<name>.example.yaml documents every setting with its default.
contrib/docker/compose.yml, with no obsolete version: key:
- Pinned to the release tag, and the port bound to
127.0.0.1only (Traefik goes in front). So is each ofextra_ports: publishing one on other interfaces is a deliberate edit. -
./configmounted read-only,./statefor state, secrets as files via<PREFIX>_<KEY>_FILE. - A read-only root,
no-new-privileges, andcap_drop: [ALL]. Withpuid_pgid, only the capabilities the entrypoint needs to chown and drop privileges are added back.
contrib/k8s/, a kustomize base (no Helm), ready to apply directly or to reference from a Flux overlay with ?ref=vX.Y.Z:
- A Namespace with the restricted Pod Security Standard enforced.
- A Deployment:
runAsNonRoot, uid 1000,readOnlyRootFilesystem,capabilities: drop [ALL],seccompProfile: RuntimeDefault,automountServiceAccountToken: false, readiness on/readyz, liveness onhealth_path, requests/limits, andRecreate(state is single-writer). - A Service, and a NetworkPolicy admitting only the
traefiknamespace. Each ofextra_portsis a named port (tcp-3128) in the Deployment and the Service, and a commented-out NetworkPolicy rule: an extra listener usually serves in-cluster clients rather than the ingress controller, so nothing reaches it until you uncomment the rule and name those clients. - A ConfigMap generated from
config.yaml; its hash suffix rolls the Deployment on change. Secrets are never in the base: SOPS in the GitOps repo, mounted as files. - For
kind: web: both probes onhealth_path, the default rolling update (no state),config.jsonfrom the ConfigMap mounted bysubPathover the image's copy, and anemptyDiron/tmpfor nginx. Its Compose file mounts./config.jsonthe same way, with no state volume or environment.
contrib/systemd/<name>.service plus <name>.sysusers:
- A dedicated system user,
StateDirectory=,ConfigurationDirectory=,UMask=0077. - The full sandbox:
ProtectSystem=strict,ProtectHome, no capabilities,PrivateTmp/PrivateDevices, the kernel protections,RestrictAddressFamilies,MemoryDenyWriteExecute,SystemCallFilter=@system-service. - The install steps are in the unit's header and on the project's Deployment wiki page. The venv lives in
/opt/<name>, outside/home, because ofProtectHome.
scripts/check-manifests.sh, run by CI's manifests job (and locally, where it skips missing tools):
| Check | Tool |
|---|---|
| Dockerfile | hadolint |
| Compose | docker compose config --quiet |
| Kubernetes |
kubectl kustomize piped into kubeconform -strict
|
| systemd |
systemd-analyze verify (on a copy whose ExecStart is /bin/true) |
Project-specific needs (a privileged DaemonSet, FUSE, a sidecar) are added in the project. Loosen hardening only with a comment saying why: the review checklist asks about it.
project-template | The metalayerlab project template
GitHub β’ Releases β’ Changelog β’ Contributing
This wiki is generated from docs/wiki/ on every push to main: edits made here are overwritten. Change it with a pull request.
- π
copier updatekeeps every project in step - π·οΈ release-please: reviewed Release PRs and generated changelogs
- π A standardised, CI-checked wiki in every repo
- π€ One
AGENTS.mdfor Claude, Codex, Copilot, Gemini and Cursor - π³ Hardened Docker, Kubernetes and systemd manifests