unikernel: isolated host-only bridge networking (Phase 2)

When the mcp-br0 bridge exists, the agent runs unikernels on it instead
of QEMU user-mode networking: each VM gets a TAP device on the bridge
and a static 10.99.0.0/24 IP (baked into the Nanos image via ops
RunConfig). With the host firewall dropping off-bridge VM traffic and no
NAT, a VM can reach only the gateway -- making mc-proxy mediation
mandatory by topology rather than convention.

- runtime/qemu.go: bridge mode (createTAP/destroyTAP, IP allocator,
  deterministic MAC, static-IP ops config, VMAddr for proxy backends).
- agent auto-enables bridge mode when /sys/class/net/mcp-br0 exists.

Verified on straylight: uktest unikernel boots on mcp-br0 at 10.99.0.2,
serves via the gateway, TAP enslaved to the bridge; bridge has no uplink
and off-bridge forwarding is dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kyle Isom
2026-06-11 01:07:49 -07:00
parent d56f224359
commit 47ec4e60ad
3 changed files with 249 additions and 29 deletions

View File

@@ -59,12 +59,23 @@ func Run(cfg *config.AgentConfig, version string) error {
// runtime = "unikernel" are placed by the master on KVM-capable nodes.
var uk runtime.Runtime
if unikernelSupported() {
uk = &runtime.QEMU{
qemu := &runtime.QEMU{
ImageDir: filepath.Join(homeDir(cfg), "images"),
StateDir: filepath.Join(homeDir(cfg), "vm"),
HomeDir: homeDir(cfg),
}
logger.Info("unikernel runtime enabled (KVM detected)")
// If the isolated host-only bridge exists, switch unikernels to
// bridge networking (Phase 2: mandatory mediation). Otherwise they
// use QEMU user-mode port forwards (Phase 1).
if _, err := os.Stat("/sys/class/net/" + unikernelBridge); err == nil {
qemu.Bridge = unikernelBridge
qemu.Gateway = unikernelGateway
qemu.SubnetPrefix = unikernelSubnetPrefix
logger.Info("unikernel runtime enabled (KVM + isolated bridge)", "bridge", unikernelBridge)
} else {
logger.Info("unikernel runtime enabled (KVM, user-mode networking)")
}
uk = qemu
}
mon := monitor.New(db, rt, cfg.Monitor, cfg.Agent.NodeName, logger)