Networking and firewall

Updated

July 30, 2026

Networking and firewall

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 chapter matters

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.

Ops motivation: most “NixOS is down” tickets are network, DNS, or firewall mistakes. Declare them, test them, document them.


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.

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.


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 (flake hosts chapter).

Extra hosts

networking.extraHosts = ''
  127.0.0.1 lab.local
  10.0.0.5  sibling.lab
'';

Useful for multi-VM labs; real DNS arrives with TLS/ACME later.


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.

nftables awareness (26.05)

Modern NixOS often prefers nftables as the firewall backend. You rarely write raw rules by hand; you set module options and inspect:

sudo nft list ruleset | head -80
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 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).

Failure Symptom
No route Timeouts
Bad DNS NXDOMAIN / resolve fail mid-rebuild
Captive / filtered net Partial substituter failures

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 nft list ruleset | head
ss -ltn

Hardening deep-dives continue in the Services and security part; this chapter is correctness + minimal surface.


Theory 8 — Rebuild safety pattern for remote operators

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.

Lockout drill ethics

Only lock yourself out on a VM with console + snapshot. Production requires out-of-band access plans.


Theory 9 — Port audit as continuous practice

Listener Firewall Config source Keep?
:22 open allowedTCPPorts / ssh yes
localhost-only n/a bind address ok
mystery ? ? investigate

Any public listener without a story is a future ticket.


Theory 10 — Hypervisor port forwards

NAT mode often needs host port → guest 22 forward. Document:

host localhost:2222 → guest :22
ssh -p 2222 alice@127.0.0.1

Firewall on guest still applies to forwarded traffic.


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.

Example D — networkd DHCP match

See Theory 1 networkd snippet.

Example E — DNS pin

networking.nameservers = [ "1.1.1.1" "9.9.9.9" ];

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 docs/network.md.


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 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
  • Backend (nftables/iptables observation)

Lab 6 — Firewall rule ↔︎ listener map

Build a three-column table:

Listener (ss) Firewall open? Config source
:22 yes allowedTCPPorts / ssh module

Lab 7 — DNS failure drill (safe)

getent hosts cache.nixos.org

Document: rebuilds that need substitutes fail hard when DNS dies mid-flight—order network fixes carefully.


Exercises

  1. Draw your hypervisor network path (host → NAT → guest).
  2. Switch from interface-hardcoded DHCP to a match pattern (if networkd).
  3. Add a second allowed port for a localhost service only if intentional.
  4. Compare test vs switch after a firewall edit.
  5. Capture nft list ruleset before/after enabling firewall.
  6. Break DNS with a bad nameserver in a snapshotted VM; recover.
  7. Document IPv6 status on your lab (on/off/partial).
  8. Map each ss listener to a Nix option or “unknown.”
  9. Write a “no inbound except SSH” policy in one paragraph.
  10. Practice reconnect scripts from the workstation.
  11. Note whether openssh module opened ports for you automatically.
  12. Prepare for TLS chapter: pick a future lab hostname.

Common pitfalls

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
Live nft edits as SoT Lost on rebuild
NAT without port forward “VM has no SSH” false alarm

Checkpoint

  • Interface + IP documented
  • Firewall enabled with explicit SSH allow
  • Port audit table written
  • Recovery from firewall mistake understood
  • Committed network docs
  • Prefer test for risky net changes

Further depth (this book)

Topic Chapter / part
SSH users Users and access
Service ports systemd via Nix
Hardening / TLS Services and security part
Firewall discipline deep Services and security part