Skip to content

Commit 48dbf06

Browse files
committed
doc: add routed-network
1 parent 3394c37 commit 48dbf06

File tree

2 files changed

+157
-2
lines changed

2 files changed

+157
-2
lines changed

doc/src/SUMMARY.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
- [MicroVM options reference ⚙️](./microvm-options.md)
1212
- [Running a MicroVM as a package](./packages.md)
1313
- [Preparing a host for declarative MicroVMs](./host.md)
14-
- [A simple network setup](./simple-network.md)
15-
- [Advanced network setup](./advanced-network.md)
14+
- [Network setup](./simple-network.md)
15+
- [A simple network setup](./simple-network.md)
16+
- [Advanced network setup](./advanced-network.md)
17+
- [Routed network setup](./routed-network.md)
1618
- [Host systemd services](./host-systemd.md)
1719
- [Host options reference ⚙️](./host-options.md)
1820
- [Declarative MicroVMs](./declarative.md)

doc/src/routed-network.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Routed network setup
2+
3+
## Motivation
4+
5+
In bridged setups the Virtual Machines share the same Ethernet
6+
segment. A compromised VM still has raw network access, allowing it to
7+
send a lot of funny packets that cause problems for other
8+
VMs. Examples:
9+
10+
- Forging MAC addresses
11+
- Running rogue DHCP servers
12+
- ARP/NDP spoofing
13+
- Meddling with link-local multicast
14+
15+
This can be avoided by unsharing the Ethernet segments, ie. removing
16+
the bridge.
17+
18+
## Addressing
19+
20+
Compared to one Ethernet where we assign a large subnet like
21+
`10.0.0.0/24`, we will now only deal with *Host Routes* where the
22+
prefix length is `/32` for IPv4 and `/128` for IPv6. Note that by
23+
doing this we no longer lose precious space to a subnet's network and
24+
broadcast addresses.
25+
26+
## Host configuration
27+
28+
Using systemd-networkd, a VM's tap interface is configured with static
29+
addresses and the corresponding host routes. We do this for up to
30+
`maxVMs`. Increasing this number will create as many `.network`
31+
configuration files, so it's relatively cheap.
32+
33+
```nix
34+
{ lib, ... }:
35+
36+
let
37+
maxVMs = 64;
38+
39+
in
40+
{
41+
networking.useNetworkd = true;
42+
43+
systemd.network.networks = builtins.listToAttrs (
44+
map (index: {
45+
name = "30-vm${toString index}";
46+
value = {
47+
matchConfig.Name = "vm${toString index}";
48+
# Host's addresses
49+
address = [
50+
"10.0.0.0/32"
51+
"fec0::/128"
52+
];
53+
# Setup routes to the VM
54+
routes = [ {
55+
Destination = "10.0.0.${toString index}/32";
56+
} {
57+
Destination = "fec0::${lib.toHexString index}/128";
58+
} ];
59+
# Enable routing
60+
networkConfig = {
61+
IPv4Forwarding = true;
62+
IPv6Forwarding = true;
63+
};
64+
};
65+
}) (lib.genList (i: i + 1) maxVMs)
66+
);
67+
}
68+
```
69+
70+
## NAT
71+
72+
For NAT configuration on the host we're not going to specify each
73+
potential tap interface. That would create a lot of firewall rules. To
74+
avoid this additional complexity, use a single subnet that matches all
75+
your VMs' addresses:
76+
77+
```nix
78+
{
79+
networking.nat = {
80+
enable = true;
81+
internalIPs = [ "10.0.0.0/24" ];
82+
# Change this to the interface with upstream Internet access
83+
externalInterface = "enp0s3";
84+
};
85+
}
86+
```
87+
88+
# Virtual Machine configuration
89+
90+
We no longer rely on DHCP for this non-standard setup. To produce IPv4
91+
and IPv6 addresses let's assign a number `index` to each MicroVM. Make
92+
sure that this number is **not reused** by two VMs!
93+
94+
We suggest creating some sort of central configuration file that
95+
contains each VM's network `index` in one place. That should make
96+
reuses obvious. If that list becomes too long, write a NixOS
97+
assertion!
98+
99+
```nix
100+
{ lib, ... }:
101+
102+
let
103+
# Change this by VM!
104+
index = 5;
105+
106+
mac = "00:00:00:00:00:01";
107+
108+
in
109+
{
110+
microvm.interfaces = [ {
111+
id = "vm${toString index}";
112+
type = "tap";
113+
inherit mac;
114+
} ];
115+
116+
networking.useNetworkd = true;
117+
118+
systemd.network.networks."10-eth" = {
119+
matchConfig.MACAddress = mac;
120+
# Static IP configuration
121+
address = [
122+
"10.0.0.${toString index}/32"
123+
"fec0::${lib.toHexString index}/128"
124+
];
125+
routes = [ {
126+
# A route to the host
127+
Destination = "10.0.0.0/32";
128+
GatewayOnLink = true;
129+
} {
130+
# Default route
131+
Destination = "0.0.0.0/0";
132+
Gateway = "10.0.0.0";
133+
GatewayOnLink = true;
134+
} {
135+
# Default route
136+
Destination = "::/0";
137+
Gateway = "fec0::";
138+
GatewayOnLink = true;
139+
} ];
140+
networkConfig = {
141+
# DNS servers no longer come from DHCP nor Router
142+
# Advertisements. Perhaps you want to change the defaults:
143+
DNS = [
144+
# Quad9.net
145+
"9.9.9.9"
146+
"149.112.112.112"
147+
"2620:fe::fe"
148+
"2620:fe::9"
149+
];
150+
};
151+
};
152+
}
153+
```

0 commit comments

Comments
 (0)