Advanced Search
Search Results
190 total results found
The front door
Everything that's reachable from the outside world passes through one VM: a pfSense firewall/router (10.100.100.1 inside, a public IP outside). It wears three hats: Router/NAT — gives the private 10.100.100.0/24 VMs a way out to the internet, and is the defau...
NAT: how private VMs reach the world
The VMs live on a private range that isn't routable on the internet. They still need to fetch packages, pull container images, and so on. pfSense handles that with outbound NAT: as a VM's traffic leaves for the internet, pfSense rewrites the source to its own ...
HAProxy: one door, many rooms
All the web services share a single public IP, so something has to look at each incoming request and send it to the right backend. That's HAProxy, running on pfSense, routing by hostname: https://git.example.com -> GIT-Server 10.100.100.2:3000 htt...
TLS terminates here, once
There is exactly one place in the whole lab that deals with certificates: HAProxy on pfSense. It holds a wildcard certificate for *.example.com and terminates TLS for every service. Behind it, traffic to the backends is plain HTTP on the trusted private networ...
A gotcha that cost an afternoon
A war story, because the lessons that stick are the ones that bit you. A backend's address was changed in the HAProxy GUI, Apply was clicked, and… HAProxy kept sending traffic to the old port. The config on disk said one thing; the running proxy did another. B...
Why a jump host
None of the internal VMs accept SSH from the internet. There's exactly one machine you can SSH to from outside — the jump host (or bastion) at 10.100.100.254, which also holds a public address. From there, you hop to anything internal. you --SSH--> Jump host...
The ProxyJump pattern
You don't want to manually SSH to the bastion and then SSH again — that's clumsy and breaks tooling. SSH has a built-in feature for exactly this: ProxyJump (and its lower-level cousin, ProxyCommand). It transparently tunnels your connection to the internal hos...
Hardening, and a host-key surprise
Because the bastion is the one publicly-reachable SSH endpoint, it's the one that gets the most attention: keep it patched, restrict who can log in, prefer keys, watch its auth logs (they ship to the central log store like everything else). A surprise worth wr...
Lessons on bastion access
One way in. Internal VMs expose no SSH to the internet; the bastion is the single public SSH endpoint. ProxyJump makes it painless. Bastion-only access with none of the two-hop hassle — tooling included. Harden the one box hard. It's the door everyone uses; p...
Why golden images
There are ~15 VMs in this lab. Not one of them was installed from an ISO by hand. Every single one is a clone of a single golden image — one carefully prepared template — customised at first boot by cloud-init. The alternative (install Ubuntu by hand, fifteen ...
The template
The template is one VM (ID 9999) prepared once: a current Ubuntu cloud image, the QEMU guest agent, the logging agent, sensible defaults — and then turned into a Proxmox template so it can only be cloned, not run directly. A few things are deliberately baked i...
Injecting per-VM identity with cloud-init
Cloning gives you fifteen identical machines. Cloud-init is what makes each one itself on first boot — its hostname, its IP, its login. The flow for creating a VM here is a small, repeatable recipe: 1. clone the template (9999) -> new VM with the next free I...
Grow-on-first-boot, and a clean teardown
Two finishing touches make the recipe pleasant to live with. Disks grow to fit. The template's disk is small. When you clone and resize the new VM's disk to, say, 250 GiB, cloud-init's growpart/resize on first boot expands the filesystem to fill it automatical...
Lessons on provisioning
One golden image, cloned many times. Consistency first; it kills "works on that box" bugs. Bake the common, inject the unique. Shared tooling in the template; hostname/IP/credentials via cloud-init. Have an address convention. "IP = VM-ID minus 100" removes a...
Why a baseline at all
Every VM in the lab gets the same small set of OS-level adjustments before it does any real work. Not a heavy "hardening framework" — just a handful of settings that make a virtual machine on virtualised, SSD-backed storage behave sensibly. The value isn't any...
What's in the baseline
The recurring adjustments, and the reasoning for each: Swap — present and small on the general VMs as a safety cushion, but off on the Kubernetes nodes (the kubelet wants it gone). The point isn't "swap good" or "swap bad" — it's matching the swap policy to t...
Lessons on the baseline
Tune to the role, not dogmatically. Swap off for k8s nodes, on (small) elsewhere. BBR where it faces the internet. There's no single right answer for the whole fleet. Apply it the same way everywhere. The benefit is uniformity; a baseline that's applied to 13...
Why self-host Git
Git is distributed, but teams still need a central place to push to, open pull requests, and trigger automation. You could use a SaaS forge — or you could run your own. This lab runs Gitea (GIT-Server, 10.100.100.2), a lightweight open-source Git forge. The re...