89 lines
3.6 KiB
Markdown
89 lines
3.6 KiB
Markdown
|
|
# labsim — libvirt replica of the lab network
|
||
|
|
|
||
|
|
A throwaway copy of the production VLAN topology for testing routing, firewall
|
||
|
|
rules and failover **without touching the real network**. Same VLAN IDs and
|
||
|
|
roles as UniFi, deliberately different IP ranges so nothing can be confused for
|
||
|
|
production.
|
||
|
|
|
||
|
|
## Topology
|
||
|
|
|
||
|
|
Each VLAN is its own isolated libvirt network with one tiny Alpine VM on it.
|
||
|
|
|
||
|
|
| VLAN | Name | Sim subnet | VM address | Mirrors production |
|
||
|
|
|-----:|------|------------|-----------|--------------------|
|
||
|
|
| 1 | management | 172.31.1.0/24 | 172.31.1.10 | 192.168.1.0/24 |
|
||
|
|
| 2 | k8s | 172.31.2.0/24 | 172.31.2.10 | 192.168.8.0/23 |
|
||
|
|
| 3 | kvm | 172.31.3.0/24 | 172.31.3.10 | 192.168.3.0/24 |
|
||
|
|
| 9 | private | 172.31.9.0/24 | 172.31.9.10 | 10.0.9.0/23 |
|
||
|
|
| 10 | lot | 172.31.10.0/24 | 172.31.10.10 | 10.0.0.0/23 |
|
||
|
|
| 200 | roomates | 172.31.200.0/24 | 172.31.200.10 | 192.168.2.0/24 |
|
||
|
|
|
||
|
|
The sim subnet always encodes the VLAN id: `172.31.<vlan>.0/24`.
|
||
|
|
|
||
|
|
Address plan, identical on every VLAN:
|
||
|
|
|
||
|
|
| Address | Role |
|
||
|
|
|---------|------|
|
||
|
|
| `.1` | gateway under test — a router VM you add (not created by default) |
|
||
|
|
| `.2` | host bridge — how you reach the VMs from this workstation |
|
||
|
|
| `.10` | the VLAN's micro VM |
|
||
|
|
| `.254` | reserved for a VRRP VIP, mirroring production |
|
||
|
|
|
||
|
|
The host sits at `.2` purely so you can SSH in. It is deliberately **not** the
|
||
|
|
VMs' default route — that is `.1` — so inter-VLAN tests fail loudly when no
|
||
|
|
router is present instead of being silently served by the host's own routing
|
||
|
|
table. libvirt also installs reject rules that stop these networks forwarding
|
||
|
|
to each other, so traffic between VLANs only works once a router VM bridges
|
||
|
|
them.
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
```bash
|
||
|
|
./labsim-up.sh # bring up every VLAN (idempotent)
|
||
|
|
./labsim-up.sh 2 3 # only VLANs 2 and 3
|
||
|
|
./labsim-down.sh # destroy VMs + networks, keep the base image
|
||
|
|
./labsim-down.sh --purge # also delete the downloaded Alpine image
|
||
|
|
```
|
||
|
|
|
||
|
|
Each VM: 256 MB, 1 vCPU, a copy-on-write overlay on one shared 176 MB Alpine
|
||
|
|
image (so six VMs cost a few MB of disk, not 1 GB).
|
||
|
|
|
||
|
|
## Access
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh alpine@172.31.2.10 # normal user (password: labsim)
|
||
|
|
ssh root@172.31.2.10 # privileged — this image has no sudo
|
||
|
|
curl http://172.31.2.10/ # hello-world page naming the VLAN
|
||
|
|
```
|
||
|
|
|
||
|
|
Console, when the network is the thing that is broken:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo virsh console labsim-2-k8s # root / labsim
|
||
|
|
```
|
||
|
|
|
||
|
|
## Notes for whoever extends this
|
||
|
|
|
||
|
|
Things that cost time the first time round, all verified on this image:
|
||
|
|
|
||
|
|
- **No `sudo`.** Alpine ships `doas`; cloud-init's `sudo:` directive is inert
|
||
|
|
here. Use `root@` for privileged work.
|
||
|
|
- **cloud-init leaves users locked** (`!*` in `/etc/shadow`) unless
|
||
|
|
`lock_passwd: false`, and sshd then refuses key auth for that user.
|
||
|
|
- **One failing `runcmd` aborts every command after it.** Each entry is
|
||
|
|
`|| true` for that reason.
|
||
|
|
- **busybox here has no `httpd` applet**, and the VMs have no internet to
|
||
|
|
`apk add` one — so the hello-world server is `python3 -m http.server`
|
||
|
|
(python3 is already present because cloud-init depends on it).
|
||
|
|
- **`start-stop-daemon --exec /usr/bin/python3` matches cloud-init's own
|
||
|
|
python3** at boot and refuses to start anything.
|
||
|
|
- **busybox `pgrep -f PATTERN` matches its own argv**, so a "skip if already
|
||
|
|
running" guard always fires. Verified: `guard_exit=0` with nothing listening.
|
||
|
|
|
||
|
|
## Not modelled (yet)
|
||
|
|
|
||
|
|
VLANs are separate L2 segments rather than one 802.1Q trunk, so this exercises
|
||
|
|
inter-VLAN routing but not a `bond0.<vif>` trunk config specifically. A router
|
||
|
|
VM would attach one NIC per VLAN. Adding a tagged-trunk variant is the obvious
|
||
|
|
next step if the bond/vif config itself needs testing.
|