Skip to content

Security & Jailer

SwarmCracker runs every workload as a Firecracker microVM: KVM hardware virtualisation gives the guest its own kernel, so a container escape is a hypervisor escape, not a shared-kernel privilege escalation. On the host side, the optional Firecracker jailer adds a second ring of defence around the VMM process itself.

This guide covers the host-side isolation model, how to enable the jailer, and a hardening checklist.

┌─ Host ──────────────────────────────────────────────────────────────┐
│ swarmd-firecracker (root, orchestrates VM lifecycle) │
│ │
│ ┌─ Jailer sandbox (unprivileged uid 1000) ──────────────────────┐ │
│ │ chroot: /var/lib/swarmcracker/jailer/<task-id>/ │ │
│ │ ├─ root/ VM root filesystem │ │
│ │ ├─ run/ Firecracker API socket │ │
│ │ └─ log/ logging FIFO │ │
│ │ │ │
│ │ ┌─ Firecracker process ──────────────────────────────────┐ │ │
│ │ │ • pid + net namespace isolated │ │ │
│ │ │ • cgroup cpu/memory limits │ │ │
│ │ │ • Firecracker seccomp-bpf policy │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ KVM │ /dev/kvm │
│ ▼ │
│ ┌─ Guest microVM (own kernel, hardware-isolated) ───────────────┐ │
│ │ workload container(s) │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

The jailer provides:

  • Filesystem isolation — Firecracker is chrooted; it cannot see the host filesystem.
  • Privilege dropping — the VMM runs as an unprivileged user, not root.
  • Resource limits — cgroup v1/v2 caps CPU, memory, and I/O per VM.
  • Namespace isolation — dedicated PID and network namespaces and TAP device.
  • Syscall filtering — Firecracker applies its own seccomp-bpf policy.

For production paths and privilege model of the daemon itself, see Contributing → Security.

Jailer settings live under executor.jailer in /etc/swarmcracker/config.yaml:

executor:
enable_jailer: true
jailer:
uid: 1000 # unprivileged uid to run Firecracker as
gid: 1000 # matching gid
chroot_base_dir: "/srv/jailer" # base directory for per-VM chroots
netns: "" # optional network namespace
Option Default Description
executor.enable_jailer false Master switch for the jailer sandbox
executor.jailer.uid 1000 UID the jailed Firecracker process runs as
executor.jailer.gid 1000 GID the jailed Firecracker process runs as
executor.jailer.chroot_base_dir /srv/jailer Base directory for per-VM chroots
executor.jailer.netns "" Optional network namespace name
Terminal window
sudo groupadd -r -g 1000 firecracker
sudo useradd -r -u 1000 -g 1000 -s /usr/sbin/nologin firecracker
sudo usermod -aG kvm firecracker

The jailer ships with Firecracker. If you installed Firecracker via swarmcracker setup install, the jailer is already on the host; otherwise:

Terminal window
curl -fsSL https://github.com/firecracker-microvm/firecracker/releases/download/v1.15.1/firecracker-v1.15.1-x86_64.tgz | tar xz
sudo cp release-v1.15.1-x86_64/jailer /usr/local/bin/
sudo chmod +x /usr/local/bin/jailer

swarmcracker cluster init / cluster join generate the swarmcracker-manager.service / swarmcracker-worker.service units and pass VM settings to swarmd-firecracker as CLI flags. The jailer is not yet wired into those generated flags, so enable it by adding the flags below to the unit’s ExecStart (or run the daemon directly).

Terminal window
sudo swarmd-firecracker \
--join-addr 192.168.1.10:4242 \
--join-token SWMTKN-1-... \
--enable-jailer \
--jailer-path /usr/local/bin/jailer \
--jailer-uid 1000 \
--jailer-gid 1000 \
--jailer-chroot-dir /var/lib/swarmcracker/jailer \
--parent-cgroup firecracker \
--cgroup-version v2 \
--enable-cgroups
Flag Default Purpose
--enable-jailer false Enable the jailer
--jailer-path /usr/local/bin/jailer Jailer binary path
--jailer-uid / --jailer-gid 1000 UID/GID for jailed processes
--jailer-chroot-dir /var/lib/swarmcracker/jailer Chroot base directory
--parent-cgroup firecracker Parent cgroup for VM limits
--cgroup-version auto-detect v1 or v2
--enable-cgroups true Enforce cgroup resource limits

To persist the change:

Terminal window
sudo systemctl daemon-reload
sudo systemctl restart swarmcracker-worker

Process ownership — Firecracker should run as the unprivileged user, not root:

Terminal window
ps -o user,pid,cmd -C firecracker
# firecr+ 12345 /usr/local/bin/jailer --id vm-xxx ...

Chroot layout — one directory per task:

Terminal window
ls -la /var/lib/swarmcracker/jailer/<task-id>/
# root/ VM root filesystem
# run/ Firecracker API socket
# log/ logging FIFO

Cgroup limits — under the parent cgroup:

Terminal window
cat /sys/fs/cgroup/firecracker/<task-id>/cpu.max # quota period
cat /sys/fs/cgroup/firecracker/<task-id>/memory.max # limit in bytes
cat /sys/fs/cgroup/firecracker/<task-id>/memory.current # live usage

Syscall filtering — Firecracker’s own seccomp policy:

Terminal window
PID=$(pgrep -f 'firecracker.*--id')
grep Seccomp /proc/$PID/status
# Seccomp: 2 (filter mode active)
Item Check
KVM access limited to the daemon + firecracker user ls -la /dev/kvm
Dedicated unprivileged user exists id firecracker
Jailer enabled and validated swarmcracker config validate
cgroup limits enforced per VM cat /sys/fs/cgroup/firecracker/<task-id>/memory.max
Chroot base directory locked down ls -la /srv/jailer
One network namespace + TAP per VM ip netns list
Workloads never run as root inside the guest image USER directive
Resource requests set on services --memory, --cpu on service create

The firecracker user must own the chroot base and have KVM access:

Terminal window
sudo chown -R firecracker:firecracker /var/lib/swarmcracker/jailer
sudo usermod -aG kvm firecracker

failed to create cgroup: operation not permitted

Section titled “failed to create cgroup: operation not permitted”

Verify cgroup v2 is mounted, or force v1:

Terminal window
cat /sys/fs/cgroup/cgroup.controllers # should list controllers
# then either fix the mount, or set --cgroup-version v1

The seccomp policy blocked a syscall. Firecracker fails closed rather than run with a weakened policy — check the kernel log for the blocked call and update the policy rather than disabling filtering:

Terminal window
sudo dmesg | grep -i seccomp

socket not created: context deadline exceeded

Section titled “socket not created: context deadline exceeded”

Check the worker logs and the common causes:

Terminal window
sudo journalctl -u swarmcracker-worker -f
  • Firecracker or jailer binary not found at the configured path
  • kernel/rootfs paths incorrect or unreadable
  • chroot permissions wrong

For local debugging only:

executor:
enable_jailer: false