Secret delivery option 2: Tmpfs-mounted secret files #134
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent epic: #129
Overview
Replace plaintext environment variables with secret files mounted into containers at
/run/secrets/. The server writes secrets to a tmpfs-backed directory, bind-mounts read-only into the container. Mirrors Docker Swarm's native convention.Docker API Support (docker-java 3.4.1)
Three Approaches
withTmpFs()withMounts(TMPFS)/dev/shmnoexec,nosuidmodeonlyro)withReadOnly)AccessMode.ro)size=Nm)sizeBytes)Key finding: You cannot combine bind mount with tmpfs properties in a single mount — they are mutually exclusive
MountTypevalues. But you can bind-mount a directory on host tmpfs (/dev/shm), achieving RAM-backed indirectly.Recommendation: Bind from
/dev/shm(Option C) — only approach that pre-populates secret files before container process reads them.Lifecycle
Race Conditions
/dev/shmcleared on reboot + startup cleanupContainer Restart Problem (Primary Weakness)
Docker's restart policy re-mounts from the original source path. If the orchestrator deleted the host-side files, restarts will fail.
Mitigations:
onFailureRestart)DockerEventMonitor)Security Analysis
Exposure Duration Comparison
/dev/shmAttack Surface Comparison
docker inspect/proc/<pid>/environ/run/secretsaccessibledocker commitTmpfs Security Caveat
Swap risk: Docker docs warn "Data may be written to swap and thereby persisted to the filesystem." Under memory pressure, tmpfs contents can be swapped to disk. Mitigate: disable swap (
swapoff -a) or use encrypted swap.vs Docker Swarm Internal Mechanism
/run/secrets/<name>/run/secrets/<name>(compatible!)emptyDir: {medium: Memory}Docker-in-Docker (SaaS Mode) — Critical
Our SaaS server runs inside Docker and creates sibling containers. This creates a path resolution problem:
/dev/shm/foo(container's tmpfs)/dev/shm/fooon the host, not the server container/dev/shm/foodoesn't exist → mount failsSolutions (same pattern as existing
JARDOCKERVOLUME):withTmpFs()+ entrypoint wrapper that reads env vars into files then unsets themPlatform Compatibility
/dev/shmemptyDir: {medium: Memory}or nativeSecretvolumesK8s note:
emptyDirwithmedium: Memorydoes NOT setnosuid,nodev,noexecby default (kubernetes/kubernetes#48912, open since 2017). Workaround:securityContext.readOnlyRootFilesystem: true.State of the Art
Who Uses This Pattern
/run/secrets/emptyDir: {medium: Memory}--secretflag (standalone, mounts to/run/secrets/)secrets:key with file sourceCIS Docker Benchmark (v1.7.0, 2025)
OWASP Secrets Management
The
_FILEconvention (e.g.,POSTGRES_PASSWORD_FILE=/run/secrets/db-password) is widely adopted by official Docker images and recommended by OWASP.Implementation Plan
Phase 1: Tmpfs + entrypoint wrapper (low effort, immediate benefit)
CAMELEER_AGENT_AUTH_TOKENis a secret;CAMELEER_AGENT_APPLICATIONis configwithTmpFs("/run/secrets", "rw,noexec,nosuid,size=1m")+ entrypoint wrapper to write secrets from env vars to files, then unset varsdocker inspectand/proc/environexposurePhase 2:
_FILEconvention in agent (medium effort)CAMELEER_AGENT_AUTH_TOKEN_FILE=/run/secrets/auth-tokenconvention_FILEsuffix setPhase 3: Platform-adaptive injection (higher effort, production-grade)
/dev/shmor Docker volume stagingSecretresources@Scheduledcleanup task every 60s for orphaned secret dirsRecommendation
Verdict: ⭐⭐⭐½ (3.5/5)
_FILEsupportThis is a solid security upgrade and aligns with industry practices. The restart resilience problem is the main caveat. Combines well with Option 6 (callback pattern) — the callback delivers secrets, the tmpfs mount stores them without env var exposure.
What NOT to Do
Sources