Files
nix-config/vars/networking.nix
T
ryan4yin | 二花 e5b95affa6 feat(nixos): default-deny host inbound, trust the LAN/tailnet (#325)
Hosts used to trust the router as the only boundary (servers ran with the
firewall off; service ports were opened globally), so any LAN device could
reach every host service. Move filtering to the hosts as defence in depth
behind the router, which keeps its firewall on and uses a port mapping for
the few ports we actually need.

- base: nftables backend; deny inbound by default; trust the home LAN by
  source and the tailnet by interface (restrict the tailnet with ACLs);
  suppress per-service `openFirewall` global ports; allow node_exporter only
  from the monitoring host. The local podman bridge, the tailnet and the pod
  CIDRs are accepted only after the node_exporter drop.
- servers: stop disabling the firewall; drop redundant global port opens
  (caddy 80/443, NFS 2049, Jellyfin, localsend/test ports); delete the
  redundant desktop firewall module; aarch64/riscv64 import the firewall
  explicitly (akane was missing it).
- k3s: Cilium pods (10.0.0.0/8, fd00::/8) may reach host services; drop the
  flannel-era cni0/flannel.1 trust.
- youko: postgres listens on localhost + the LAN with pg_hba limited to the
  LAN; the NFS export stays LAN-scoped; remove the unauthenticated v2ray
  proxy.
- tests: `security-firewall` asserts every host runs the shared firewall, has
  the LAN CIDR rule and no global ports.
2026-09-26 14:02:03 +08:00

222 lines
6.1 KiB
Nix

{ lib }:
rec {
mainGateway = "192.168.5.1"; # main router
mainGateway6 = "fe80::5"; # main router's link-local address
# The trusted home LAN; the shared host firewall trusts this by source.
lanCidr = "192.168.5.0/24";
# use suzi as the default gateway
# it's a subrouter with a transparent proxy
proxyGateway = "192.168.5.178";
proxyGateway6 = "fe80::8";
nameservers = [
# IPv4
"119.29.29.29" # DNSPod https://www.dnspod.cn/Products/publicdns
"223.5.5.5" # AliDNS
"1.1.1.1" # Cloudflare https://one.one.one.one/dns/
# IPv6
"2402:4e00::" # DNSPod
"2400:3200::1" # Alidns
"2606:4700:4700::1111" # Cloudflare
];
prefixLength = 24;
# Dedicated VIP block for the k3s clusters, outside the router's DHCP pool
# (.2-.99). One /29 per cluster, handed to kube-vip: the first usable address
# is the control-plane API VIP, the rest are LoadBalancer service VIPs.
k8sVip = {
cidr = "192.168.5.192/27";
clusters = {
k3s-test-1 = {
cidr = "192.168.5.192/29";
apiVip = "192.168.5.193";
};
# Reserved for future clusters:
# .200/29 apiVip .201
# .208/29 apiVip .209
# .216/29 apiVip .217
};
};
hostsAddr = {
# ============================================
# Homelab's Physical Machines (VM hosts)
# ============================================
shoryu = {
iface = "eno1";
ipv4 = "192.168.5.181";
};
shushou = {
iface = "eno1";
ipv4 = "192.168.5.182";
};
youko = {
iface = "eno1";
ipv4 = "192.168.5.183";
};
# ============================================
# Other VMs and Physical Machines
# ============================================
ai = {
# Desktop PC
iface = "enp130s0";
ipv4 = "192.168.5.100";
ipv6 = "fe80::10"; # Link-local Address
};
# akane = {
# # VM (running in macOS's UTM App), using DHCP instead of static ip.
# iface = "enp0s1";
# ipv4 = "192.168.64.2";
# };
aquamarine = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.101";
};
ruby = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.102";
};
kana = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.103";
};
nozomi = {
# LicheePi 4A's wireless interface - RISC-V
iface = "wlan0";
ipv4 = "192.168.5.104";
};
yukina = {
# LicheePi 4A's wireless interface - RISC-V
iface = "wlan0";
ipv4 = "192.168.5.105";
};
chiaya = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.106";
};
suzu = {
# Orange Pi 5 - ARM
iface = "end1";
ipv4 = "192.168.5.107";
};
rakushun = {
# Orange Pi 5 - ARM
# RJ45 port 1 - enP4p65s0
# RJ45 port 2 - enP3p49s0
iface = "enP4p65s0";
ipv4 = "192.168.5.179";
};
suzi = {
iface = "enp2s0"; # fake iface, it's not used by the host
ipv4 = "192.168.5.178";
ipv6 = "fe80::8"; # Link-local Address, can be used as default gateway
};
mitsuha = {
iface = "enp2s0"; # fake iface, it's not used by the host
ipv4 = "192.168.5.177";
};
# ============================================
# Kubernetes Clusters
# ============================================
k3s-test-1-master-1 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.114";
};
k3s-test-1-master-2 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.115";
};
k3s-test-1-master-3 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.116";
};
k3s-test-1-worker-1 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.111";
};
k3s-test-1-worker-2 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.112";
};
k3s-test-1-worker-3 = {
# VM
iface = "enp2s0";
ipv4 = "192.168.5.113";
};
};
hostsInterface = lib.attrsets.mapAttrs (key: val: {
interfaces."${val.iface}" = {
useDHCP = false;
ipv4.addresses = [
{
inherit prefixLength;
address = val.ipv4;
}
];
};
}) hostsAddr;
ssh = {
# define the host alias for remote builders
# this config will be written to /etc/ssh/ssh_config
#
# Config format:
# Host — given the pattern used to match against the host name given on the command line.
# HostName — specify nickname or abbreviation for host
# IdentityFile — the location of your SSH key authentication file for the account.
# Format in details:
# https://www.ssh.com/academy/ssh/config
extraConfig = (
lib.attrsets.foldlAttrs (
acc: host: val:
acc
+ ''
Host ${host}
HostName ${val.ipv4}
Port 22
''
) "" hostsAddr
);
# this config will be written to /etc/ssh/ssh_known_hosts
knownHosts =
# Update only the values of the given attribute set.
#
# mapAttrs
# (name: value: ("bar-" + value))
# { x = "a"; y = "b"; }
# => { x = "bar-a"; y = "bar-b"; }
lib.attrsets.mapAttrs
(host: value: {
hostNames = [ host ] ++ (lib.optional (hostsAddr ? host) hostsAddr.${host}.ipv4);
publicKey = value.publicKey;
})
{
# Define the root user's host key for remote builders, so that nix can verify all the remote builders
aquamarine.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEOXFhFu9Duzp6ZBE288gDZ6VLrNaeWL4kDrFUh9Neic root@aquamarine";
# the restic backup server
youko.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEs8b3CmxM+utXZVKvJBssXBupBXvvzjHP40HcY7PSyZ root@youko";
# ruby.publicKey = "";
# kana.publicKey = "";
# ==================================== Other SSH Service's Public Key =======================================
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
"github.com".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
};
};
}