Day 15 — Networking & firewall
Day 15 — Networking & firewall
Stage II · ~4h
Goal: Configure lab networking deliberately—pick networkd or NetworkManager, know your address, and run a default-deny-ish firewall that still allows SSH.
Why this day exists
A flake host that “has SSH in config” but blocked by firewall, or random DHCP with no notes, is not operable. Networking is part of the system closure story.
Theory 1 — Two common network stacks on NixOS
| Stack | Typical use | Module vibes |
|---|---|---|
| systemd-networkd | Servers, VMs, simple static | systemd.network, networking.useNetworkd |
| NetworkManager | Laptops, Wi-Fi, desktop | networking.networkmanager.enable |
Do not enable both casually without understanding conflicts. For a server-like VM, networkd or simple DHCP via networking.* is enough.
Classic DHCP simplicity
networking.useDHCP = false;
networking.interfaces.ens3.useDHCP = true; # interface name from `ip a`Or global patterns depending on release defaults—inspect generated hardware and ip a.
Theory 2 — Host identity
networking.hostName = "lab";
# networking.domain = "example.local";Hostname affects prompts, certificates later, and mental ops. Keep flake attr and hostname documented (Day 12).
Theory 3 — Firewall model
NixOS firewall (iptables/nftables backend depending on config) is commonly:
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 ];
allowedUDPPorts = [ ];
# allowedTCPPortRanges = [ { from = 8000; to = 8010; } ];
};| Idea | Practice |
|---|---|
| Default deny inbound | Enable firewall |
| Open only needed ports | List explicitly |
| Per-interface rules | Advanced; later |
| Service modules open ports | Some set firewall rules when enabled |
Always ensure SSH port is allowed before testing remote-only access.
Theory 4 — Interface names
Predictable names (enp0s3, ens18, eth0) depend on virt hardware.
ip -br a
networkctl status # if networkd
nmcli device # if NMHard-coding the wrong interface → no network after switch. Prefer DHCP-on-all or verified names.
Theory 5 — Static addressing (optional lab)
networking.interfaces.ens3.ipv4.addresses = [{
address = "192.168.56.10";
prefixLength = 24;
}];
networking.defaultGateway = "192.168.56.1";
networking.nameservers = [ "1.1.1.1" "9.9.9.9" ];Use only when you understand the hypervisor network. Wrong gateway = silence.
Theory 6 — DNS and resolution
resolvectl status # systemd-resolved often in play
getent hosts cache.nixos.orgBroken DNS looks like “Nix is broken” during rebuilds (substituters).
Theory 7 — Firewall vs service modules
Example: enabling services.openssh does not always mean you thought about firewall—on many configs you still list port 22 (or rely on module defaults). Verify:
sudo iptables -L -n 2>/dev/null | head
# or
sudo nft list ruleset | head
ss -ltnDay 37 hardens further; today: correctness + minimal surface.
Worked examples bank
Example A — Server-like DHCP + SSH
{
networking.hostName = "lab";
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
services.openssh.enable = true;
}Example B — Explicit interface DHCP
{
networking.useDHCP = false;
networking.interfaces.ens3.useDHCP = true;
}Example C — Temporary debug open (lab only)
# networking.firewall.allowedTCPPorts = [ 22 80 443 ];Remove ports you do not need.
Lab 1 — Discover ground truth
On the lab host:
ip -br a
ip route
cat /etc/hostname
resolvectl status 2>/dev/null | headRecord interface name(s), IPv4, default route.
Lab 2 — Declare firewall explicitly
In flake config:
networking.firewall.enable = true
- Allow SSH port only
- Rebuild (
testrecommended if remote)
sudo nixos-rebuild test --flake .#lab
# verify ssh still works
sudo nixos-rebuild switch --flake .#labLab 3 — Port audit
ss -ltnup
# map each listener to a reasonFill table in notes:
| Port | Process | Declared in config? | Keep? |
|---|---|---|---|
| 22 | sshd | yes | yes |
Lab 4 — Controlled break (console ready)
Snapshot.
- Remove 22 from
allowedTCPPortswhile SSH is your only remote path.
switch(expect lockout if no console).
- Fix via console; restore port.
If you are console-only already, simulate by checking nft/iptables counters and documenting the mistake.
Lab 5 — Network README snippet
Add to docs/network.md:
- Hypervisor network mode (NAT/bridge/host-only)
- How to find VM IP from host
- Firewall policy summary
- DNS notes
Theory 8 — networking.nftables awareness (26.05)
Modern NixOS often prefers nftables as the firewall backend. You rarely write raw rules by hand on day 15; you set module options and inspect:
sudo nft list ruleset | head -80
# legacy path still appears on some configs:
sudo iptables -L -n 2>/dev/null | head || true| Practice | Avoid |
|---|---|
| Module options + rebuild | Hand-editing live nft rules as SoT |
Document backend in docs/network.md |
Assuming iptables forever |
Theory 9 — Extra hosts and lab DNS
networking.extraHosts = ''
127.0.0.1 lab.local
10.0.0.5 sibling.lab
'';Useful for multi-VM labs and reverse-proxy names later (Day 38). Keep it small; real DNS arrives with ACME.
Theory 10 — networking.firewall + rebuild safety pattern
Remote-only operators should:
sudo nixos-rebuild test --flake .#lab
# new shell: ssh still works?
sudo nixos-rebuild switch --flake .#labtest activates without making the generation the boot default—slightly safer while iterating firewall. Console access remains the real safety net.
Worked example D — networkd file shape (server VM)
{
networking.useNetworkd = true;
networking.useDHCP = false;
systemd.network.networks."10-wan" = {
matchConfig.Name = "en*";
networkConfig.DHCP = "yes";
};
}Interface match patterns reduce rename pain vs hard-coding ens3 only—verify on your virt NIC names.
Lab 6 — Firewall rule ↔︎ listener map
Build a three-column table in docs/network.md:
Listener (ss) |
Firewall open? | Config source |
|---|---|---|
:22 |
yes | allowedTCPPorts / ssh module |
Any listener without a story is either localhost-only (OK) or a future Stage IV ticket.
Lab 7 — DNS failure drill (safe)
getent hosts cache.nixos.org
# temporarily wrong nameserver in a *test* generation if you can console-recoverDocument: rebuilds that need substitutes fail hard when DNS dies mid-flight—order network fixes carefully.
Common gotchas
| Symptom | Theory |
|---|---|
| Rebuild can’t fetch | DNS/route broken mid-change |
| SSH timeout after firewall | Port not allowed |
| Interface rename after hardware change | Update config |
| Both NM and networkd fighting | Pick one model |
useDHCP defaults surprise |
Read options for 26.05 |
Checkpoint
- Interface + IP documented
- Firewall enabled with explicit SSH allow
- Port audit table written
- Recovery from firewall mistake understood
- Committed network docs
Commit
cd ~/nixos-config
git add .
git commit -m "day15: networking firewall explicit ssh"Tomorrow
Day 16 — Packages & programs. environment.systemPackages vs programs.* modules—install daily tools declaratively without random nix profile drift.