No description
  • Shell 96.4%
  • HCL 2.1%
  • Makefile 0.8%
  • Dockerfile 0.7%
Find a file
2026-11-13 16:06:12 +01:00
docs Initial commit 2026-11-13 16:06:12 +01:00
examples/pi Initial commit 2026-11-13 16:06:12 +01:00
library Initial commit 2026-11-13 16:06:12 +01:00
packer Initial commit 2026-11-13 16:06:12 +01:00
targets Initial commit 2026-11-13 16:06:12 +01:00
tests Initial commit 2026-11-13 16:06:12 +01:00
tools Initial commit 2026-11-13 16:06:12 +01:00
.gitignore Initial commit 2026-11-13 16:06:12 +01:00
LICENSE Initial commit 2026-11-13 16:06:12 +01:00
README.md Initial commit 2026-11-13 16:06:12 +01:00
spec.md Initial commit 2026-11-13 16:06:12 +01:00

lab-factory

Disposable network labs for people and agents: one VM running a containerlab topology, created for a session and destroyed after it. Only the golden OS image and the image library persist.

Runs on local Linux with KVM (targets/qemu) and on OVHcloud Public Cloud (targets/ovh). What has been run on each: docs/validation.md.

The production boundary

An agent can safely get write access to a network only if the environment never touches production.

  • Lab hosts are assumed compromised, and get no credentials or path to production.
  • Egress is denied by default, enforced outside the guest. --egress open is the exception.
  • Each session has its own SSH key, and its ssh_config never forwards an agent or offers another key.
  • Nothing is left running: every session has one expiry, enforced by a background reaper.
  • Configs entering a lab must already be stripped of secrets, and changes leaving one go through your normal change pipeline. Those two are rules, not code.

Details and gaps: docs/edges.md.

The contract

targets/<name>/spawn [--flavor F] [--egress deny|open] [--ttl MINUTES]   # a lab-ready host, or nothing
targets/<name>/destroy <session-id>                                      # removes everything; idempotent
targets/<name>/library-publish                                           # rebuilds the image cache
targets/<name>/image-publish                                             # registers the golden image

Full semantics: docs/target-contract.md. Why it's built this way: spec.md.

Requirements

  • Docker, for tools/devshell.sh, which has every other host tool
  • x86_64 Linux with KVM and nested virtualization, for the local target and the golden image build
  • Library images (library/BUILD.md); vendor images are never in this repo
  • For OVH: a Public Cloud project and OpenStack credentials

Quickstart

tools/devshell.sh                           # a shell with every host tool; run the rest inside it

make -C packer image verify                 # build and verify the golden image
targets/qemu/image-publish
LIBRARY_SOURCE=~/lab-library targets/qemu/library-publish

tools/session.sh ~/my-lab.clab.yaml --flavor large   # your own topology; none ship with this repo
# Ctrl+C or closing the terminal destroys the session

For OVH, copy targets/ovh/config.env.example to targets/ovh/config.env, or to ~/.config/lab-factory/ovh.env to keep it outside the checkout, and fill it in. If both exist, targets/ovh/config.env wins. Run targets/ovh/prerequisites.sh, then use the same commands with targets/ovh and --target ovh.

Without session.sh

SESSION=$(targets/qemu/spawn --ttl 120)              # logs on stderr, session ID on stdout
ssh -F sessions/$SESSION/ssh_config lab              # or scp, rsync, an agent's harness
targets/qemu/destroy "$SESSION"                      # or leave it until it expires (default 4 h)

To use it from an agent, point the harness's shell tool at that ssh command and give the agent no other shell. Treat whatever the lab returns as untrusted. For pi, see examples/pi/.

Tools and tests

tools/session.sh One session end to end: spawn, deploy, wait for nodes, destroy on exit
tools/reap.sh Destroys expired sessions; spawn starts it
tools/egress-report.sh What a QEMU host tried to reach, from a capture taken outside the guest
tools/devshell.sh Runs a command or shell with every host tool installed
make -C tests all Spawns real hosts; TARGET=ovh for OVH

License

MIT. It covers this repo's code only; vendor images are under the vendor's own license.