feat: add guest network autoconfiguration via Firecracker MMDS

Introduces optional per-clone IP assignment using the Firecracker Microvm
Metadata Service (MMDS). A background daemon (fc-net-init) is baked into
the rootfs during init and captured in the golden snapshot — on clone
resume it polls 169.254.169.254 and applies the IP/GW/DNS config injected
by the orchestrator immediately after snapshot restore.

- config.go: add AutoNetConfig bool (FC_AUTO_NET_CONFIG=1)
- orchestrator.go: embed fc-net-init daemon + MMDS link-local route in
  init script; set AllowMMDS: true on golden NIC; spawnOne/SpawnSingle
  accept net bool and propagate it via FC_AUTO_NET_CONFIG in proxy env
- console.go: set AllowMMDS: true on clone NIC; call configureMmds()
  after m.Start() when AutoNetConfig is enabled
- network.go: add configureMmds() — PUT /mmds with ip/gw/dns over the
  clone's Firecracker Unix socket
- serve.go: POST /clones accepts optional {"net": bool} body to override
  the global AutoNetConfig default per-request
- web/terminal.html: spawn button always sends {"net": true}
- docs/commands.md: document manual config + MMDS autoconfiguration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 11:58:59 +00:00
parent 82c11dd2f8
commit 5e23e0ab4e
7 changed files with 197 additions and 19 deletions

View File

@@ -14,11 +14,12 @@ type Config struct {
Rootfs string // path to base rootfs.ext4
VCPUs int64
MemMiB int64
Bridge string // host bridge name, or "none" to skip networking
BridgeCIDR string // e.g. "172.30.0.1/24"
GuestPrefix string // e.g. "172.30.0" — clones get .10, .11, ...
GuestGW string
BootArgs string
Bridge string // host bridge name, or "none" to skip networking
BridgeCIDR string // e.g. "172.30.0.1/24"
GuestPrefix string // e.g. "172.30.0" — clones get .11, .12, ...
GuestGW string // default gateway for guest VMs
AutoNetConfig bool // inject guest IP/GW/DNS via MMDS on clone start
BootArgs string
}
func DefaultConfig() Config {
@@ -29,9 +30,10 @@ func DefaultConfig() Config {
MemMiB: envOrInt("FC_MEM_MIB", 128),
Bridge: envOr("FC_BRIDGE", "fcbr0"),
BridgeCIDR: envOr("FC_BRIDGE_CIDR", "172.30.0.1/24"),
GuestPrefix: envOr("FC_GUEST_PREFIX", "172.30.0"),
GuestGW: envOr("FC_GUEST_GW", "172.30.0.1"),
BootArgs: "console=ttyS0 reboot=k panic=1 pci=off i8042.noaux quiet loglevel=0",
GuestPrefix: envOr("FC_GUEST_PREFIX", "172.30.0"),
GuestGW: envOr("FC_GUEST_GW", "172.30.0.1"),
AutoNetConfig: envOr("FC_AUTO_NET_CONFIG", "") == "1",
BootArgs: "console=ttyS0 reboot=k panic=1 pci=off i8042.noaux quiet loglevel=0",
}
c.Kernel = envOr("FC_KERNEL", c.BaseDir+"/vmlinux")
c.KernelURL = envOr("FC_KERNEL_URL",