Skip to main content

Problem Summary

In a Docker-in-Docker (DinD) setup where the worker container runs inside Docker and creates containers:
  1. Volume Path Mismatch: Volume mount paths are relative to the Docker daemon’s filesystem, not the worker container
  2. Security Risk: Shared volumes in multi-tenant SaaS allow cross-tenant data access
  3. Limited stdin Approach: Using stdin for data transfer doesn’t support file-based tools

Solution: Isolated Named Volumes

Use unique Docker named volumes created per tenantId + runId + timestamp:

Architecture


Security Benefits


Implementation

Before: File Mounting (Broken in DinD)

After: Isolated Volumes (DinD Compatible)


Comparison: All Approaches


Usage Examples

Simple Input Files

Input + Output Files

Multiple Volumes


Volume Lifecycle

  1. Create: docker volume create tenant-A-run-123-...
  2. Populate: Use temporary Alpine container to write files
  3. Mount: Container uses the volume via -v volumeName:/path
  4. Read: Use temporary Alpine container to read files
  5. Cleanup: docker volume rm tenant-A-run-123-...

Automatic Cleanup

Volumes are always cleaned up via finally blocks:

Orphan Cleanup

For volumes that weren’t cleaned up (e.g., worker crash):

Security Requirements

Tenant Isolation

Every execution gets a unique volume:
Example: tenant-acme-run-wf-abc123-1732150000

Read-Only Mounts

Nonroot Container Support

Volumes automatically support containers running as nonroot users (e.g., distroless images with uid 65532). After files are written to the volume, permissions are set to 777 to allow any container user to read/write:
Why this is needed:
  • Files are written to volumes using Alpine containers (running as root)
  • Distroless nonroot images run as uid 65532
  • Without permission changes, nonroot containers can’t write output files
This is safe because:
  • Each volume is isolated per tenant + run
  • Volumes are cleaned up after execution
  • No cross-tenant access is possible

Path Validation

Filenames are automatically validated:

Security Guarantees


Performance

Volume Creation Overhead

  • Creation: ~50-100ms per volume
  • File writes: ~10-50ms per file (depends on size)
  • Cleanup: ~50-100ms per volume
Total overhead: ~100-250ms per execution This is acceptable for security tools that typically run for seconds/minutes.

Optimization Tips

  1. Batch file writes: Write all files in one initialize() call
  2. Reuse volumes: For sequential operations in same run, reuse the volume
  3. Lazy cleanup: Clean up volumes in background job if latency-sensitive

When to Use Each Approach

Use Isolated Volumes When:

  • ✅ Running in DinD environment
  • ✅ Need multi-tenant isolation
  • ✅ Tool requires file-based config
  • ✅ Tool writes output files
  • ✅ Handling binary/large files

Use stdin/stdout When:

  • ✅ Tool supports stdin input
  • ✅ Single-tenant or dev environment
  • ✅ Small text-only inputs
  • ✅ Don’t need output files

Use File Mounts When:

  • ✅ NOT running in DinD (direct Docker)
  • ✅ Development/testing only
  • ✅ Quick prototyping

Migration Checklist

To migrate a component to use isolated volumes:
  • Import IsolatedContainerVolume
  • Get tenantId from context (use fallback for now)
  • Create volume instance: new IsolatedContainerVolume(tenantId, runId)
  • Replace file writes with volume.initialize({ files })
  • Replace volume mount with volume.getVolumeConfig()
  • Add finally block with volume.cleanup()
  • If tool writes outputs, use volume.readFiles() to retrieve them
  • Test in DinD environment