Class: Hermetic::Backends::Firecracker

Inherits:
Base
  • Object
show all
Defined in:
lib/hermetic/backends/firecracker.rb

Overview

Strongest self-host option: each run cold-boots a Firecracker microVM — its own guest kernel behind KVM (~125 ms boot). v1 ships the pure half (machine_config hash + jailer argv, unit-tested with zero infra) and a single-shot cold-boot path behind an injectable runner; real orchestration (jail materialization, vsock I/O, tap+nftables egress, warm pools) is deferred per spec §7, so the default runner refuses — actually booting is smoke-gated on a KVM host. trust :vm but co-located: off_host? is false. Pair with Hermetic::Remote to put the KVM host outside the app's trust domain.

Constant Summary collapse

BOOT_ARGS =
"console=ttyS0 reboot=k panic=1 pci=off"
CONFIG_FILE =

jail-relative; jailer chroots firecracker

"vm-config.json"

Constants inherited from Base

Base::OFF_HOST_TRUST, Base::TIMEOUT_EXIT

Instance Attribute Summary collapse

Attributes inherited from Base

#limits, #trust

Instance Method Summary collapse

Methods inherited from Base

#backend_name, #enabled?, #off_host?, #run

Methods included from SilasLedgerGuard

#run

Constructor Details

#initialize(kernel:, rootfs:, boot_args: BOOT_ARGS, firecracker_bin: "firecracker", jailer_bin: "jailer", uid: 65_534, gid: 65_534, chroot_base: "/srv/jailer", runner: nil, **opts) ⇒ Firecracker

Returns a new instance of Firecracker.

Raises:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/hermetic/backends/firecracker.rb', line 20

def initialize(kernel:, rootfs:, boot_args: BOOT_ARGS,
               firecracker_bin: "firecracker", jailer_bin: "jailer",
               uid: 65_534, gid: 65_534, chroot_base: "/srv/jailer",
               runner: nil, **opts)
  raise ConfigError, "kernel: (vmlinux path) is required for firecracker" if kernel.to_s.strip.empty?
  raise ConfigError, "rootfs: (ext4 image path) is required for firecracker" if rootfs.to_s.strip.empty?

  super(trust: :vm, **opts)
  @kernel = kernel
  @rootfs = rootfs
  @boot_args = boot_args
  @firecracker_bin = firecracker_bin
  @jailer_bin = jailer_bin
  @uid = uid
  @gid = gid
  @chroot_base = chroot_base
  @runner = runner
end

Instance Attribute Details

#kernelObject (readonly)

Returns the value of attribute kernel.



18
19
20
# File 'lib/hermetic/backends/firecracker.rb', line 18

def kernel
  @kernel
end

#rootfsObject (readonly)

Returns the value of attribute rootfs.



18
19
20
# File 'lib/hermetic/backends/firecracker.rb', line 18

def rootfs
  @rootfs
end

Instance Method Details

#jailer_argv(id:) ⇒ Object

Pure: jailer chroots + re-namespaces firecracker and drops to an unprivileged uid/gid before the VMM runs. Everything after "--" goes to firecracker itself; the config path is relative to the jail root.



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/hermetic/backends/firecracker.rb', line 68

def jailer_argv(id:)
  [ @jailer_bin,
    "--id", id,
    "--exec-file", @firecracker_bin,
    "--uid", @uid.to_s,
    "--gid", @gid.to_s,
    "--chroot-base-dir", @chroot_base,
    "--",
    "--no-api",
    "--config-file", CONFIG_FILE ]
end

#machine_config(request) ⇒ Object

Pure: the firecracker --config-file hash for one run. Rootfs is attached read-only (writable scratch is guest-init's job — same posture as docker --read-only + tmpfs); default-deny network attaches no NIC at all. pids has no seat in the VM config — it's enforced by guest init, deferred with the rest of orchestration.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/hermetic/backends/firecracker.rb', line 44

def machine_config(request)
  {
    "boot-source" => {
      "kernel_image_path" => @kernel,
      "boot_args" => @boot_args
    },
    "drives" => [ {
      "drive_id" => "rootfs",
      "path_on_host" => @rootfs,
      "is_root_device" => true,
      "is_read_only" => true
    } ],
    "machine-config" => {
      "vcpu_count" => vcpu_count(request.limits.cpus),
      "mem_size_mib" => mem_size_mib(request.limits.memory),
      "smt" => false
    },
    "network-interfaces" => request.network.firecracker_ifaces
  }
end