Day 15 — Networking & firewall

Updated

July 30, 2026

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 NM

Hard-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.org

Broken 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 -ltn

Day 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 | head

Record interface name(s), IPv4, default route.


Lab 2 — Declare firewall explicitly

In flake config:

  1. networking.firewall.enable = true
  2. Allow SSH port only
  3. Rebuild (test recommended if remote)
sudo nixos-rebuild test --flake .#lab
# verify ssh still works
sudo nixos-rebuild switch --flake .#lab

Lab 3 — Port audit

ss -ltnup
# map each listener to a reason

Fill table in notes:

Port Process Declared in config? Keep?
22 sshd yes yes

Lab 4 — Controlled break (console ready)

Snapshot.

  1. Remove 22 from allowedTCPPorts while SSH is your only remote path.
  2. switch (expect lockout if no console).
  3. 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 .#lab

test 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-recover

Document: 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.