Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cbc516fb0
|
||
|
|
7351194fee
|
||
|
|
6aa799c05d
|
||
|
|
ddb6d43b0c
|
||
|
|
8af0ecbf39
|
@@ -14,12 +14,12 @@
|
|||||||
boot.extraModulePackages = [ ];
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
fileSystems."/" =
|
fileSystems."/" =
|
||||||
{ device = "/dev/disk/by-uuid/f222513b-ded1-49fa-b591-20ce86a2fe7f";
|
{ device = "/dev/disk/by-uuid/0dec41bf-aba0-4b7b-9f61-bbb572e5bd1e";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems."/boot" =
|
fileSystems."/boot" =
|
||||||
{ device = "/dev/disk/by-uuid/12CE-A600";
|
{ device = "/dev/disk/by-uuid/9F5E-672F";
|
||||||
fsType = "vfat";
|
fsType = "vfat";
|
||||||
options = [ "fmask=0022" "dmask=0022" ];
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ in
|
|||||||
./opts.nix
|
./opts.nix
|
||||||
./ifconfig.nix
|
./ifconfig.nix
|
||||||
./wireguard.nix
|
./wireguard.nix
|
||||||
|
./tailscale.nix
|
||||||
./firewall.nix
|
./firewall.nix
|
||||||
./dns.nix
|
./dns.nix
|
||||||
./kea.nix
|
./kea.nix
|
||||||
|
|||||||
@@ -54,17 +54,17 @@ in
|
|||||||
enable_dnssec = true;
|
enable_dnssec = true;
|
||||||
bootstrap_dns = [ "1.1.1.1" "9.9.9.9" ];
|
bootstrap_dns = [ "1.1.1.1" "9.9.9.9" ];
|
||||||
upstream_dns = [
|
upstream_dns = [
|
||||||
# Default upstreams
|
|
||||||
"quic://p0.freedns.controld.com"
|
|
||||||
"tls://one.one.one.one"
|
|
||||||
"tls://dns.quad9.net"
|
|
||||||
|
|
||||||
# Adguard uses upstream and not rewrite rules to resolve cname rewrites,
|
# Adguard uses upstream and not rewrite rules to resolve cname rewrites,
|
||||||
# and obviously my sysdomain entries don't exist in cloudflare.
|
# and obviously my sysdomain entries don't exist in cloudflare.
|
||||||
"[/${sysdomain}/][::1]" # Sys domains to self (for cname rewrites)
|
"[/${sysdomain}/][::1]" # Sys domains to self (for cname rewrites)
|
||||||
|
|
||||||
"[/${ldomain}/][::1]:1053" # Local domains to Knot (ddns)
|
"[/${ldomain}/][::1]:1053" # Local domains to Knot (ddns)
|
||||||
"[/home/][${ifs.lan.ulaPrefix}::250]" # .home domains to opnsense (temporary)
|
"[/home/][${ifs.lan.ulaPrefix}::250]" # .home domains to opnsense (temporary)
|
||||||
|
|
||||||
|
# Default upstreams
|
||||||
|
"quic://p0.freedns.controld.com"
|
||||||
|
"tls://one.one.one.one"
|
||||||
|
"tls://dns.quad9.net"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
# https://adguard-dns.io/kb/general/dns-filtering-syntax/
|
# https://adguard-dns.io/kb/general/dns-filtering-syntax/
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ let
|
|||||||
${ifs.lan50.name},
|
${ifs.lan50.name},
|
||||||
${ifs.wg0.name},
|
${ifs.wg0.name},
|
||||||
}
|
}
|
||||||
|
# Tailscale is its own zone, since tailnet traffic is already filtered by tailscale ACLs
|
||||||
|
# and needs some special handling (exit node NAT with CGNAT/ULA addrs)
|
||||||
|
define ZONE_TAILSCALE_IFS = { ${ifs.ts0.name} }
|
||||||
|
define TAILSCALE_NET4 = ${ifs.ts0.net4}
|
||||||
|
define TAILSCALE_NET6 = ${ifs.ts0.net6}
|
||||||
define OPNSENSE_NET6 = ${vars.extra.opnsense.net6}
|
define OPNSENSE_NET6 = ${vars.extra.opnsense.net6}
|
||||||
define ZONE_LAN_EXTRA_NET6 = {
|
define ZONE_LAN_EXTRA_NET6 = {
|
||||||
# TODO: reevaluate this statement
|
# TODO: reevaluate this statement
|
||||||
@@ -58,6 +63,7 @@ in
|
|||||||
udp . 48425 : ${vars.extra.opnsense.addr4} . 48425,
|
udp . 48425 : ${vars.extra.opnsense.addr4} . 48425,
|
||||||
# Amnezia VPN server
|
# Amnezia VPN server
|
||||||
udp . 37138 : ${vars.extra.amnezia.addr4} . 37138,
|
udp . 37138 : ${vars.extra.amnezia.addr4} . 37138,
|
||||||
|
tcp . 38247 : ${vars.extra.amnezia.addr4} . 22,
|
||||||
# Minecruft server
|
# Minecruft server
|
||||||
tcp . 25565 : ${vars.extra.minecruft.addr4} . 25565,
|
tcp . 25565 : ${vars.extra.minecruft.addr4} . 25565,
|
||||||
udp . 25565 : ${vars.extra.minecruft.addr4} . 25565,
|
udp . 25565 : ${vars.extra.minecruft.addr4} . 25565,
|
||||||
@@ -79,6 +85,8 @@ in
|
|||||||
|
|
||||||
# Masquerade LAN addrs
|
# Masquerade LAN addrs
|
||||||
oifname $ZONE_WAN_IFS ip saddr $RFC1918 masquerade
|
oifname $ZONE_WAN_IFS ip saddr $RFC1918 masquerade
|
||||||
|
# Masquerade tailnet addrs (tailscale exit node)
|
||||||
|
oifname $ZONE_WAN_IFS ip saddr $TAILSCALE_NET4 masquerade
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -96,12 +104,28 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# IPv6 masquerading for tailnet addrs only (tailscale exit node)
|
||||||
|
# Tailscale only assigns ULAs from $TAILSCALE_NET6, so NAT is unavoidable here
|
||||||
|
networking.nftables.tables.nat6_tailscale = {
|
||||||
|
family = "ip6";
|
||||||
|
content = ''
|
||||||
|
${nftIdentifiers}
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority srcnat; policy accept;
|
||||||
|
oifname $ZONE_WAN_IFS ip6 saddr $TAILSCALE_NET6 masquerade
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
networking.nftables.tables.firewall = {
|
networking.nftables.tables.firewall = {
|
||||||
family = "inet";
|
family = "inet";
|
||||||
content = ''
|
content = ''
|
||||||
${nftIdentifiers}
|
${nftIdentifiers}
|
||||||
define ALLOWED_TCP_PORTS = { ssh }
|
define ALLOWED_TCP_PORTS = { ssh }
|
||||||
define ALLOWED_UDP_PORTS = { ${toString vars.ifs.wg0.listenPort} }
|
define ALLOWED_UDP_PORTS = {
|
||||||
|
${toString vars.ifs.wg0.listenPort},
|
||||||
|
${toString vars.ifs.ts0.listenPort}, # tailscale direct connections
|
||||||
|
}
|
||||||
define ALLOWED_TCP_LAN_PORTS = { ssh, https }
|
define ALLOWED_TCP_LAN_PORTS = { ssh, https }
|
||||||
define ALLOWED_UDP_LAN_PORTS = { bootps, dhcpv6-server, domain, https }
|
define ALLOWED_UDP_LAN_PORTS = { bootps, dhcpv6-server, domain, https }
|
||||||
set port_forward_v6 {
|
set port_forward_v6 {
|
||||||
@@ -160,6 +184,8 @@ in
|
|||||||
# iifname $ZONE_LAN_IFS accept
|
# iifname $ZONE_LAN_IFS accept
|
||||||
iifname $ZONE_LAN_IFS jump zone_lan_input
|
iifname $ZONE_LAN_IFS jump zone_lan_input
|
||||||
ip6 saddr $ZONE_LAN_EXTRA_NET6 jump zone_lan_input
|
ip6 saddr $ZONE_LAN_EXTRA_NET6 jump zone_lan_input
|
||||||
|
# Tailscale zone input rules
|
||||||
|
iifname $ZONE_TAILSCALE_IFS jump zone_tailscale_input
|
||||||
|
|
||||||
# log
|
# log
|
||||||
}
|
}
|
||||||
@@ -175,6 +201,8 @@ in
|
|||||||
# LAN zone forward rules
|
# LAN zone forward rules
|
||||||
iifname $ZONE_LAN_IFS jump zone_lan_forward
|
iifname $ZONE_LAN_IFS jump zone_lan_forward
|
||||||
ip6 saddr $ZONE_LAN_EXTRA_NET6 jump zone_lan_forward
|
ip6 saddr $ZONE_LAN_EXTRA_NET6 jump zone_lan_forward
|
||||||
|
# Tailscale zone forward rules
|
||||||
|
iifname $ZONE_TAILSCALE_IFS jump zone_tailscale_forward
|
||||||
}
|
}
|
||||||
|
|
||||||
chain zone_wan_input {
|
chain zone_wan_input {
|
||||||
@@ -214,6 +242,23 @@ in
|
|||||||
|
|
||||||
# Allow traffic between LANs
|
# Allow traffic between LANs
|
||||||
oifname $ZONE_LAN_IFS accept
|
oifname $ZONE_LAN_IFS accept
|
||||||
|
|
||||||
|
# Allow LAN to reach tailnet (no SNAT, tailnet peers see real LAN addrs)
|
||||||
|
oifname $ZONE_TAILSCALE_IFS accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain zone_tailscale_input {
|
||||||
|
# Tailnet access is restricted by tailscale ACLs, treat like LAN
|
||||||
|
jump zone_lan_input
|
||||||
|
}
|
||||||
|
|
||||||
|
chain zone_tailscale_forward {
|
||||||
|
# Subnet router: allow tailnet to LANs (advertised routes)
|
||||||
|
oifname $ZONE_LAN_IFS accept
|
||||||
|
|
||||||
|
# Exit node: allow tailnet to WAN (masqueraded in nat4 / nat6_tailscale)
|
||||||
|
oifname $ZONE_WAN_IFS ip saddr $TAILSCALE_NET4 accept
|
||||||
|
oifname $ZONE_WAN_IFS ip6 saddr $TAILSCALE_NET6 accept
|
||||||
}
|
}
|
||||||
|
|
||||||
chain output {
|
chain output {
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ in
|
|||||||
# since my WAN and LAN20 are technically bridged.
|
# since my WAN and LAN20 are technically bridged.
|
||||||
# https://networkengineering.stackexchange.com/questions/83071/why-linux-answers-arp-requests-for-ips-that-belong-to-different-network-interfac
|
# https://networkengineering.stackexchange.com/questions/83071/why-linux-answers-arp-requests-for-ips-that-belong-to-different-network-interfac
|
||||||
boot.kernel.sysctl."net.ipv4.conf.default.arp_filter" = 1;
|
boot.kernel.sysctl."net.ipv4.conf.default.arp_filter" = 1;
|
||||||
|
# Later I discovered that rp_filter might also be good to set
|
||||||
|
# Strict mode (1) drops packets if the src ip isn't a local route on the interface
|
||||||
|
boot.kernel.sysctl."net.ipv4.conf.default.rp_filter" = 1;
|
||||||
|
|
||||||
# It is impossible to do multiple prefix requests with networkd,
|
# It is impossible to do multiple prefix requests with networkd,
|
||||||
# so I use dhcpcd for this
|
# so I use dhcpcd for this
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 D2MY/A txjDSEsRr2CJtC9rDIiY9H8aX0us53vhnqJStt+XvlA
|
||||||
|
vjmrx5ByEhbHE/hBEQq3gRqY8b5/mjHLs+He3D/27LM
|
||||||
|
--- rp66vDf9qnM7f9S5YD/X/99YsufPWmvz+V8z8htB2Ow
|
||||||
|
9‡Ài¨*¡-oš·}Ć(Š5[¬[ÕyKr)¾-cùäóŽù¼R0?Ë8`Èô×{
|
||||||
@@ -29,10 +29,12 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# https://wiki.nixos.org/wiki/Grafana#Declarative_configuration
|
# https://wiki.nixos.org/wiki/Grafana#Declarative_configuration
|
||||||
|
secrix.services.grafana.secrets.security-key.encrypted.file = ./secrets/grafana-secret-key.age;
|
||||||
services.grafana = {
|
services.grafana = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
security.allow_embedding = true;
|
security.allow_embedding = true;
|
||||||
|
security.secret_key = "$__file{${config.secrix.services.grafana.secrets.security-key.decrypted.path}}";
|
||||||
server = {
|
server = {
|
||||||
http_port = 3001;
|
http_port = 3001;
|
||||||
domain = "grouter.${domain}";
|
domain = "grouter.${domain}";
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
vars = import ./vars.nix config;
|
||||||
|
ifs = vars.ifs;
|
||||||
|
ts0 = ifs.ts0;
|
||||||
|
|
||||||
|
# Subnets advertised to the tailnet (still need to be approved in the admin console)
|
||||||
|
advertisedRoutes = [
|
||||||
|
"${vars.p4}.0.0/16" # all LAN v4 subnets
|
||||||
|
"${vars.ulaPrefix}::/48" # all LAN ULA subnets
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# One-time setup after deploying: `sudo tailscale up`
|
||||||
|
# (state is persisted in /var/lib/tailscale)
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
port = ts0.listenPort;
|
||||||
|
interfaceName = ts0.name;
|
||||||
|
# Enables ip forwarding (already enabled globally by networkd anyway).
|
||||||
|
# Not using "both", since that sets rp_filter to loose on all interfaces.
|
||||||
|
useRoutingFeatures = "server";
|
||||||
|
# Firewall is managed manually in firewall.nix (networking.firewall is disabled)
|
||||||
|
openFirewall = false;
|
||||||
|
# Applied with `tailscale set` on every start, so these stay declarative
|
||||||
|
extraSetFlags = [
|
||||||
|
# Don't let tailscale touch nftables/iptables, all rules live in firewall.nix
|
||||||
|
"--netfilter-mode=off"
|
||||||
|
# Don't SNAT tailnet -> LAN traffic, LAN hosts route back via the router anyway
|
||||||
|
# (preserves real source addrs for logging/ACLs on LAN hosts)
|
||||||
|
"--snat-subnet-routes=false"
|
||||||
|
# The router is its own DNS server, don't let tailscale mess with resolv.conf
|
||||||
|
"--accept-dns=false"
|
||||||
|
"--advertise-routes=${lib.strings.concatStringsSep "," advertisedRoutes}"
|
||||||
|
"--advertise-exit-node"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# networkd removes "foreign" ip rules when it (re)starts, which would wipe tailscale's
|
||||||
|
# policy routing rules (table 52). This is extra important since networkd is
|
||||||
|
# restarted every minute (see ifconfig.nix).
|
||||||
|
# The nixpkgs tailscale module already marks tailscale0 as unmanaged in networkd.
|
||||||
|
systemd.network.config.networkConfig.ManageForeignRoutingPolicyRules = false;
|
||||||
|
}
|
||||||
@@ -111,6 +111,14 @@ rec {
|
|||||||
} // {
|
} // {
|
||||||
listenPort = 51944;
|
listenPort = 51944;
|
||||||
};
|
};
|
||||||
|
# Tailscale addresses are assigned by the coordination server,
|
||||||
|
# these are the fixed ranges tailscale allocates node addrs from.
|
||||||
|
ts0 = {
|
||||||
|
name = "tailscale0";
|
||||||
|
listenPort = 41641;
|
||||||
|
net4 = "100.64.0.0/10";
|
||||||
|
net6 = "fd7a:115c:a1e0::/48";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extra = {
|
extra = {
|
||||||
|
|||||||
Reference in New Issue
Block a user