This document describes the runtime isolation cleanup system that transforms builder chroot runtime installations into secure, isolated runtime environments for production jobs. The cleanup process ensures that production jobs using runtimes cannot access the host OS filesystem through runtime mounts.
When runtimes are built in the builder chroot environment, they have access to the full host OS filesystem. Runtime
setup scripts create runtime.yml
configurations that reference host OS paths:
# INSECURE: Points to actual host OS paths
mounts:
- source: "usr/lib/jvm/java-21-openjdk-amd64"
target: "/usr/lib/jvm/java-21-openjdk-amd64"
readonly: true
- source: "usr/bin/java"
target: "/usr/bin/java"
readonly: true
Production jobs using these runtimes would mount the actual host OS directories, potentially exposing:
The cleanup system transforms runtime installations into isolated, self-contained packages:
flowchart TD
A[Runtime Built in Builder Chroot] --> B[Cleanup Phase Initiated]
B --> C[Parse runtime.yml]
C --> D[Create isolated/ Directory]
D --> E[Copy Runtime Files from Host Paths]
E --> F[Update runtime.yml with Isolated Paths]
F --> G[Backup Original Configuration]
G --> H[Secure Runtime Ready for Production]
Before Cleanup (INSECURE):
/opt/joblet/runtimes/java/openjdk-21/
├── runtime.yml # Points to host OS paths
└── setup.sh # Setup script
After Cleanup (SECURE):
/opt/joblet/runtimes/java/openjdk-21/
├── isolated/ # NEW: Self-contained runtime files
│ ├── usr/
│ │ ├── lib/jvm/ # Copied Java installation
│ │ └── bin/ # Copied Java binaries
│ └── etc/ssl/certs/ # Copied certificates
├── runtime.yml # Updated with isolated paths
├── runtime.yml.original # Backup of original
└── setup.sh # Setup script
The cleanup process is integrated directly into runtime setup scripts:
#!/bin/bash
# In setup-ubuntu-amd64.sh
# Install runtime using host package manager
install_packages "apt" "openjdk-21-jdk-headless ca-certificates-java"
# Create initial runtime.yml with host paths
create_initial_runtime_config
# CLEANUP PHASE - Create isolated structure
create_isolated_runtime_structure
# Update runtime.yml with isolated paths
update_runtime_config_with_isolated_paths
The cleanup process copies essential runtime files to the isolated structure:
# Java Runtime Cleanup Example
mkdir -p "/opt/joblet/runtimes/java/openjdk-21/isolated/usr/lib/jvm"
cp -r "/usr/lib/jvm/java-21-openjdk-amd64" \
"/opt/joblet/runtimes/java/openjdk-21/isolated/usr/lib/jvm/"
mkdir -p "/opt/joblet/runtimes/java/openjdk-21/isolated/usr/bin"
cp "/usr/bin/java" "/opt/joblet/runtimes/java/openjdk-21/isolated/usr/bin/"
cp "/usr/bin/javac" "/opt/joblet/runtimes/java/openjdk-21/isolated/usr/bin/"
mkdir -p "/opt/joblet/runtimes/java/openjdk-21/isolated/etc/ssl/certs"
cp -r "/etc/ssl/certs/java" \
"/opt/joblet/runtimes/java/openjdk-21/isolated/etc/ssl/certs/"
The runtime.yml
is rewritten to use isolated paths:
# BEFORE (INSECURE)
mounts:
- source: "usr/lib/jvm/java-21-openjdk-amd64" # Host path
target: "/usr/lib/jvm/java-21-openjdk-amd64"
readonly: true
# AFTER (SECURE)
mounts:
- source: "isolated/usr/lib/jvm/java-21-openjdk-amd64" # Isolated path
target: "/usr/lib/jvm/java-21-openjdk-amd64"
readonly: true
Different runtime types require different cleanup strategies:
/usr/lib/jvm/
)/usr/bin/java
, /usr/bin/javac
)/etc/ssl/certs/java
)Before Cleanup:
# Production job using INSECURE runtime
mounts:
- source: "usr/bin/java" # → /usr/bin/java (HOST BINARY!)
target: "/usr/bin/java"
Risk: Production job can access host /usr/bin/java
and potentially explore host filesystem
After Cleanup:
# Production job using SECURE runtime
mounts:
- source: "isolated/usr/bin/java" # → isolated copy within runtime dir
target: "/usr/bin/java"
Secure: Production job only accesses isolated copy within /opt/joblet/runtimes/
/opt/joblet/runtimes/{runtime}/isolated/
internal/joblet/core/runtime_cleanup.go
)runtimes/openjdk-21/setup-ubuntu-amd64.sh
)runtime.yml
)runtimes/python-*/setup-*.sh
)runtimes/node-*/setup-*.sh
)# Test that production job cannot access host filesystem
rnx job run --runtime=java:21 find /usr -name "*.so" | head -10
# Should only see isolated runtime files, not host libraries
# Test that runtime mounts cannot escape isolation
rnx job run --runtime=java:21 ls -la /usr/../../../
# Should be contained within runtime isolation
# Verify runtime files are functional copies
rnx job run --runtime=java:21 java -version
# Should work correctly with isolated Java installation
isolated/
directories are created during buildsruntime.yml
files for isolated pathsFuture versions could implement fully automated cleanup:
// Automatic post-build cleanup
func (rs *RuntimeService) BuildRuntime(ctx context.Context, req *pb.BuildRuntimeRequest) (*pb.BuildRuntimeResponse, error) {
// Execute build job
job, err := rs.joblet.StartJob(ctx, buildReq)
// Monitor job completion
go rs.monitorBuildJobAndCleanup(job.Uuid, req.RuntimeSpec)
return response, nil
}
func (rs *RuntimeService) monitorBuildJobAndCleanup(jobUuid, runtimeSpec string) {
// Wait for job completion
for job.Status == "RUNNING" { /* wait */ }
if job.Status == "COMPLETED" {
// Trigger automated cleanup
cleaner := core.NewRuntimeCleanup(rs.platform)
err := cleaner.CleanupRuntime(runtimeDir)
}
}
// Validate that runtime.yml only uses isolated paths
func (rc *RuntimeCleanup) ValidateRuntimeConfig(configPath string) error {
config := parseRuntimeConfig(configPath)
for _, mount := range config.Mounts {
if !strings.HasPrefix(mount.Source, "isolated/") {
return fmt.Errorf("insecure mount source: %s", mount.Source)
}
}
return nil
}
The runtime isolation cleanup system successfully resolves the critical security gap in runtime installations. By copying runtime files to isolated structures and updating mount configurations, production jobs are completely isolated from the host OS filesystem while maintaining full runtime functionality.
This approach provides:
The cleanup system is a critical security component that ensures the dual chroot architecture maintains its security guarantees while providing full runtime functionality to production workloads.