feat(network): add configurable VM egress firewall policies and WireGuard-based host routing

This commit is contained in:
2026-05-01 18:32:08 +00:00
parent 69e66cba00
commit 8c8b692fc1
7 changed files with 998 additions and 57 deletions
+104 -6
View File
@@ -16,9 +16,11 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
- Creates and controls microVMs through Firecracker's HTTP-over-Unix-socket API.
- Converts TypeScript camelCase config into Firecracker's snake_case payloads.
- Creates TAP devices, a Linux bridge, static guest IP assignments, and NAT rules.
- Applies optional global VM egress firewall rules for the managed subnet.
- Routes VM egress through host-side WireGuard when configured.
- Defaults VM runtime artifacts to tmpfs via `/dev/shm/.smartvm/runtime` when available.
- Stages writable drives into per-VM ephemeral storage by default so guest writes do not touch cached rootfs files.
- Cleans up Firecracker processes, sockets, TAPs, bridges, NAT rules, and staged drive copies.
- Cleans up Firecracker processes, sockets, TAPs, bridges, NAT/firewall/WireGuard rules, and staged drive copies.
The design goal is close to a Cloudflare Workers/Deno-style filesystem model, adapted to Firecracker: immutable root image, explicit writable scratch, no accidental persistent state, and persistence only when you opt in.
@@ -37,7 +39,7 @@ Firecracker is a Linux/KVM technology. The package is TypeScript, but the runtim
| Linux with `/dev/kvm` | Firecracker needs KVM acceleration. |
| Firecracker binary | Downloaded by `ensureBinary()` or supplied through `firecrackerBinaryPath`. |
| Root privileges for networking | TAP devices, bridges, IP forwarding, and iptables NAT require elevated privileges. |
| Host tools: `curl`, `tar`, `ip`, `sysctl`, `iptables` | Used for binary/image downloads and network setup. |
| Host tools: `curl`, `tar`, `ip`, `sysctl`, `iptables`, `wg` when WireGuard is used | Used for binary/image downloads and network setup. |
| Enough tmpfs memory | Writable VM drives are copied into `/dev/shm` by default when available. |
## Quick Start
@@ -160,7 +162,7 @@ Best practice for high-volume VM starts:
SmartVM
ImageManager downloads/caches Firecracker binaries and manual images
BaseImageManager resolves known-good base-image bundles
NetworkManager creates TAP devices, bridge, NAT, and static guest network data
NetworkManager creates TAP devices, bridge, NAT, firewall/WireGuard egress, and static guest network data
MicroVM
FirecrackerProcess starts/stops the VMM process
SocketClient talks HTTP over the Firecracker Unix socket
@@ -186,6 +188,16 @@ const options: ISmartVMOptions = {
firecrackerBinaryPath: '/usr/bin/firecracker',
bridgeName: 'svbr0',
subnet: '172.30.0.0/24',
firewall: {
egress: {
defaultAction: 'allow',
rules: [],
},
},
wireguard: {
existingInterface: 'wg0',
failClosed: true,
},
baseImageCacheDir: '/tmp/.smartvm/base-images',
maxStoredBaseImages: 2,
baseImageManifestUrl: 'https://assets.example.com/smartvm/manifest.json',
@@ -426,9 +438,84 @@ Networking behavior:
- Allocation is sequential and not reused within the same `NetworkManager` instance
- MAC addresses are deterministic and locally administered (`02:xx:xx:xx:xx:xx`)
- TAP names are capped to Linux's 15-character IFNAMSIZ limit
- NAT masquerade uses the host default route interface
- NAT masquerade uses the host default route interface unless WireGuard egress is configured
- Use a dedicated bridge name; `cleanup()` tears down the bridge configured by this manager
### Egress Firewall
Configure `firewall.egress` on `SmartVM` to apply one ordered policy to all VMs using that manager's subnet. The default action is `allow`, so existing behavior is preserved unless you opt into a stricter policy.
```typescript
const smartvm = new SmartVM({
firewall: {
egress: {
defaultAction: 'deny',
rules: [
{ action: 'allow', to: '1.1.1.1', protocol: 'udp', ports: 53, comment: 'DNS' },
{ action: 'allow', to: '203.0.113.0/24', protocol: 'tcp', ports: [443] },
],
},
},
});
```
Firewall behavior:
- Rules are evaluated in order before the final `defaultAction`.
- `to` accepts IPv4 addresses or CIDR ranges only.
- `protocol` can be `all`, `tcp`, `udp`, or `icmp`.
- `ports` are destination ports and require `protocol: 'tcp'` or `protocol: 'udp'`.
- The implementation uses an owned `iptables` chain jumped from `FORWARD` for traffic from the VM subnet.
### WireGuard Egress
WireGuard routing is host-side. The guest does not need WireGuard installed; `smartvm` policy-routes packets from the VM subnet through a WireGuard interface and leaves normal host traffic on the host default route.
Managed interface mode creates and removes the WireGuard interface:
```typescript
const smartvm = new SmartVM({
wireguard: {
interfaceName: 'svwg0',
routeTable: 51820,
failClosed: true,
config: `
[Interface]
PrivateKey = <private-key>
Address = 10.70.0.2/32
MTU = 1420
[Peer]
PublicKey = <public-key>
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.example.com:51820
PersistentKeepalive = 25
`,
},
});
```
Existing interface mode uses an interface you manage outside `smartvm`:
```typescript
const smartvm = new SmartVM({
wireguard: {
existingInterface: 'wg0',
routeTable: 51820,
failClosed: true,
},
});
```
WireGuard behavior:
- `routeAllVmTraffic` defaults to `true`; set it to `false` to keep normal default-route NAT.
- `failClosed` defaults to `true`; VM forwarding to non-WireGuard egress interfaces is dropped when WireGuard routing is active.
- Managed configs accept wg-quick-style `Address` and `MTU`, but reject `PreUp`, `PostUp`, `PreDown`, `PostDown`, and `SaveConfig`.
- `DNS` and `Table` in managed configs are ignored; use host DNS and the `routeTable` option instead.
- IPv4 addresses and IPv4 `AllowedIPs` are supported in this release.
- `cleanup()` removes owned policy routes, iptables rules, NAT rules, and managed WireGuard interfaces. Existing WireGuard interfaces are not deleted.
## ImageManager
`ImageManager` is the lower-level helper for Firecracker binaries and manually managed kernel/rootfs files.
@@ -578,8 +665,11 @@ try {
| `INVALID_BASE_IMAGE_CACHE_LIMIT` | Base-image retention limit is invalid. |
| `INVALID_SUBNET` | Subnet is not a supported IPv4 CIDR. |
| `INVALID_INTERFACE_NAME` | Bridge or TAP name is invalid. |
| `INVALID_FIREWALL_CONFIG` | VM egress firewall config is invalid. |
| `INVALID_WIREGUARD_CONFIG` | WireGuard egress config is invalid. |
| `IP_EXHAUSTED` | No guest IPs remain in the configured subnet. |
| `BRIDGE_SETUP_FAILED` | Bridge/NAT setup failed. |
| `WIREGUARD_SETUP_FAILED` | WireGuard interface or policy-route setup failed. |
| `TAP_CREATE_FAILED` | TAP creation failed. |
| `ROOTFS_CREATE_FAILED` | Blank rootfs creation failed. |
| `ROOTFS_CLONE_FAILED` | Rootfs clone failed. |
@@ -595,7 +685,7 @@ pnpm test
pnpm run build
```
The default suite covers config validation, payload generation, lifecycle guards, base-image cache behavior, hosted manifest validation, VM tracking, ephemeral drive staging, and subnet/IP behavior.
The default suite covers config validation, payload generation, lifecycle guards, base-image cache behavior, hosted manifest validation, VM tracking, ephemeral drive staging, subnet/IP behavior, and firewall/WireGuard option validation.
Opt into real Firecracker boot tests on a Linux/KVM host:
@@ -661,6 +751,14 @@ import type {
IRateLimiter,
INetworkManagerOptions,
ITapDevice,
IFirewallConfig,
IFirewallEgressConfig,
IFirewallRule,
TFirewallAction,
TFirewallProtocol,
TWireGuardConfig,
IWireGuardManagedConfig,
IWireGuardExistingInterfaceConfig,
ISocketClientOptions,
IApiResponse,
TVMState,
@@ -675,7 +773,7 @@ import type {
## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.