forked from c3d2/nix-config
519 lines
14 KiB
Nix
519 lines
14 KiB
Nix
{ config, pkgs, lib, modulesPath, zentralwerk, ... }:
|
|
|
|
let
|
|
inherit (zentralwerk.lib.config.site.net) core;
|
|
inherit (config.networking) hostName;
|
|
coreAddress = core.hosts4.${hostName};
|
|
meshInterface = "bmx";
|
|
meshLoopback = "bmx_prime";
|
|
ddmeshRegisterUrl = "https://register.freifunk-dresden.de/bot.php";
|
|
ddmeshBroadcast = "10.255.255.255";
|
|
inherit (pkgs.c3d2-freifunk) ddmeshRegisterKey;
|
|
ddmeshNode = 51073;
|
|
ddmeshAddrPart = "200.74";
|
|
rt_table_hosts = 7;
|
|
rt_table_nets = rt_table_hosts + 1;
|
|
rt_table_tuns = rt_table_hosts + 2;
|
|
rt_table_ffgw = 244;
|
|
sysinfo-json = import ./sysinfo-json.nix { inherit pkgs ddmeshNode; };
|
|
|
|
upstreams = [ "upstream4" "upstream3" ];
|
|
upstreamMark = 3;
|
|
rt_table_upstream = 100;
|
|
|
|
node51001AddrPart = "200.2";
|
|
|
|
mac = {
|
|
core = "00:de:13:cb:9a:7b";
|
|
bmx = "00:de:13:cb:9a:7c";
|
|
};
|
|
bmxdGatewayScript = with pkgs; writeScript "bmxd-gateway.sh" ''
|
|
#! ${runtimeShell} -e
|
|
|
|
echo "bmxd-gateway.sh: $@" >&2
|
|
|
|
PATH=${lib.makeBinPath [ iproute2 ]}
|
|
case "$1" in
|
|
gateway)
|
|
;;
|
|
del|init)
|
|
;;
|
|
*)
|
|
ip tunnel del ffgw || true
|
|
|
|
ip tunnel add ffgw mode ipip local 10.200.${ddmeshAddrPart} remote "$1"
|
|
ip link set ffgw up
|
|
ip addr add 10.200.${ddmeshAddrPart}/32 dev ffgw
|
|
ip route add 0/0 dev ffgw table ${toString rt_table_ffgw}
|
|
ip rule add pref 33500 table ${toString rt_table_ffgw} || true
|
|
;;
|
|
esac
|
|
'';
|
|
|
|
in {
|
|
imports = [
|
|
"${modulesPath}/profiles/minimal.nix"
|
|
];
|
|
|
|
boot = {
|
|
postBootCommands = ''
|
|
if [ ! -c /dev/net/tun ]; then
|
|
mkdir -p /dev/net
|
|
mknod -m 666 /dev/net/tun c 10 200
|
|
fi
|
|
'';
|
|
tmp.useTmpfs = true;
|
|
};
|
|
c3d2 = {
|
|
hq.statistics.enable = true;
|
|
deployment = {
|
|
server = "server10";
|
|
autoNetSetup = false;
|
|
};
|
|
};
|
|
services.collectd.plugins.protocols = "";
|
|
|
|
microvm.interfaces = [ {
|
|
type = "tap";
|
|
id = "core-freifunk";
|
|
mac = mac.core;
|
|
} {
|
|
type = "tap";
|
|
id = "bmx-freifunk";
|
|
mac = mac.bmx;
|
|
} ];
|
|
|
|
networking.hostName = "freifunk";
|
|
networking.useNetworkd = true;
|
|
networking.nameservers = [ "172.20.73.8" "9.9.9.9" ];
|
|
networking.firewall.enable = false;
|
|
networking.nat = {
|
|
enable = true;
|
|
# This doesn't really work, hence the `extraCommands`
|
|
externalInterface = meshInterface;
|
|
#internalInterfaces = [ "core" ];
|
|
|
|
# Setup routing into Freifunk,
|
|
# masquerading anything that isn't already their IP range
|
|
extraCommands = ''
|
|
${pkgs.iptables}/bin/iptables -t nat -F POSTROUTING
|
|
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING \
|
|
\! --source 10.200.0.0/15 -o ${meshInterface} -j SNAT --to 10.200.${ddmeshAddrPart}
|
|
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING \
|
|
\! --source 10.200.0.0/15 -o ipip-node51001 -j SNAT --to 10.200.${ddmeshAddrPart}
|
|
${pkgs.iptables}/bin/iptables -t nat -o bat0 -A POSTROUTING -j MASQUERADE
|
|
set -e
|
|
'';
|
|
};
|
|
# Configure rt_table name
|
|
networking.iproute2 = {
|
|
enable = true;
|
|
rttablesExtraConfig = ''
|
|
${toString rt_table_upstream} upstream
|
|
${toString rt_table_hosts} bmx_hosts
|
|
${toString rt_table_nets} bmx_nets
|
|
${toString rt_table_tuns} bmx_tuns
|
|
'';
|
|
};
|
|
|
|
environment.systemPackages = with pkgs; [ tcpdump bmon wireguard-tools iperf bmxd ];
|
|
|
|
sops = {
|
|
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
|
defaultSopsFile = ./secrets.yaml;
|
|
secrets."wireguard/vpn6/privateKey" = {
|
|
group = "systemd-network";
|
|
mode = "0440";
|
|
};
|
|
secrets."bird/ospf/auth" = {
|
|
owner = "bird2";
|
|
};
|
|
};
|
|
|
|
# unbreak wg-vpn6 ingress path
|
|
boot.kernel.sysctl."net.ipv4.conf.core.rp_filter" = 0;
|
|
|
|
systemd.network = {
|
|
netdevs = {
|
|
# Dummy interface for primary (10.200) address
|
|
"10-bmx-prime" = {
|
|
enable = true;
|
|
netdevConfig = {
|
|
Kind = "dummy";
|
|
Name = meshLoopback;
|
|
};
|
|
};
|
|
# Freifunk Dresden Backbone
|
|
"31-wg-vpn6" = {
|
|
enable = true;
|
|
netdevConfig = {
|
|
Name = "wg-vpn6";
|
|
Kind = "wireguard";
|
|
MTUBytes = "1320";
|
|
};
|
|
wireguardConfig = {
|
|
PrivateKeyFile = config.sops.secrets."wireguard/vpn6/privateKey".path;
|
|
ListenPort = 5006;
|
|
# Mark for routing with the upstream routing table
|
|
FirewallMark = upstreamMark;
|
|
};
|
|
wireguardPeers = [ {
|
|
wireguardPeerConfig = {
|
|
# vpn6.freifunk-dresden.de
|
|
Endpoint = "85.195.253.5:5006";
|
|
PublicKey = "CIJa7xiRRIrLtEB7uyzwoyaQcpe0b8F2d16+3hk8KjU=";
|
|
AllowedIPs = "10.203.0.0/16";
|
|
};
|
|
} ];
|
|
};
|
|
"32-ipip-node51001" = {
|
|
enable = true;
|
|
netdevConfig = {
|
|
Name = "ipip-node51001";
|
|
Kind = "ipip";
|
|
};
|
|
tunnelConfig = {
|
|
Local = "10.203.${ddmeshAddrPart}";
|
|
Remote = "10.203.${node51001AddrPart}";
|
|
};
|
|
};
|
|
};
|
|
links = {
|
|
# Wired mesh interface
|
|
"10-bmx" = {
|
|
enable = true;
|
|
matchConfig = { MACAddress = mac.bmx; };
|
|
linkConfig.Name = meshInterface;
|
|
};
|
|
# Wired core interface
|
|
"10-core" = {
|
|
enable = true;
|
|
matchConfig = { MACAddress = mac.core; };
|
|
linkConfig.Name = "core";
|
|
};
|
|
};
|
|
networks = {
|
|
# Wired mesh interface
|
|
"10-bmx" = {
|
|
enable = true;
|
|
matchConfig = { MACAddress = mac.bmx; };
|
|
addresses = [{
|
|
addressConfig = {
|
|
Address = "10.201.${ddmeshAddrPart}/16";
|
|
Broadcast = ddmeshBroadcast;
|
|
};
|
|
}];
|
|
routingPolicyRules = [ {
|
|
routingPolicyRuleConfig = {
|
|
Priority = 300;
|
|
To = "10.200.0.0/16";
|
|
Table = rt_table_hosts;
|
|
};
|
|
} ];
|
|
};
|
|
# Dummy interface for primary (10.200) address
|
|
"11-bmx-prime" = {
|
|
enable = true;
|
|
matchConfig = { Name = meshLoopback; };
|
|
addresses = [{
|
|
addressConfig.Address = "10.200.${ddmeshAddrPart}/32";
|
|
}];
|
|
routingPolicyRules = [ {
|
|
routingPolicyRuleConfig = {
|
|
Priority = 33000;
|
|
Table = rt_table_tuns;
|
|
};
|
|
} ];
|
|
};
|
|
"31-wg-vpn6" = {
|
|
enable = true;
|
|
matchConfig.Name = "wg-vpn6";
|
|
addresses = [{
|
|
addressConfig.Address = "10.203.${ddmeshAddrPart}/16";
|
|
}];
|
|
# reverse dependency
|
|
networkConfig.Tunnel = [ "ipip-node51001" ];
|
|
};
|
|
"32-ipip-node51001" = {
|
|
enable = true;
|
|
matchConfig.Name = "ipip-node51001";
|
|
addresses = [{
|
|
addressConfig = {
|
|
Address = "10.201.${ddmeshAddrPart}/16";
|
|
Broadcast = ddmeshBroadcast;
|
|
};
|
|
}];
|
|
};
|
|
# ZW
|
|
"20-core" = {
|
|
enable = true;
|
|
matchConfig = { MACAddress = mac.core; };
|
|
addresses = map (Address: { addressConfig = { inherit Address; }; }) (
|
|
[
|
|
"${coreAddress}/${toString core.subnet4Len}"
|
|
] ++
|
|
map (hosts6: "${hosts6.${hostName}}/64") (
|
|
builtins.attrValues core.hosts6
|
|
)
|
|
);
|
|
routingPolicyRules = [ {
|
|
# Marked wireguard packets take the upstream routing table
|
|
routingPolicyRuleConfig = {
|
|
Table = rt_table_upstream;
|
|
FirewallMark = upstreamMark;
|
|
};
|
|
} ];
|
|
};
|
|
};
|
|
};
|
|
# Freifunk Dresden routing daemon
|
|
systemd.services.bmxd = {
|
|
after = [ "systemd-networkd.service" ];
|
|
wantedBy = [ "network.target" ];
|
|
serviceConfig = {
|
|
ExecStart = ''
|
|
${pkgs.bmxd}/sbin/bmxd \
|
|
--rt_table_offset=${toString rt_table_hosts} \
|
|
--no_fork 1 \
|
|
--throw-rules 0 \
|
|
--prio-rules 0 \
|
|
--network 10.200.0.0/16 \
|
|
--netid 0 \
|
|
--only_community_gw 1 \
|
|
--script ${bmxdGatewayScript} \
|
|
--hop_penalty 1 \
|
|
--lateness_penalty 10 \
|
|
--ogm_broadcasts 100 \
|
|
--udp_data_size 512 \
|
|
--ogm_interval 5000 \
|
|
--purge_timeout 35 \
|
|
-r 3 --gateway_hysteresis 20 \
|
|
--dev ${meshLoopback} /linklayer 0 \
|
|
--dev ${meshInterface} /linklayer 1 \
|
|
--dev ipip-node51001 /linklayer 1
|
|
'';
|
|
Restart = "always";
|
|
RestartSec = "60";
|
|
};
|
|
};
|
|
|
|
# Re-register periodically
|
|
systemd.services.ddmesh-register-node = {
|
|
script = ''
|
|
${pkgs.curl}/bin/curl -k \
|
|
-o /tmp/ddmesh-registration.json \
|
|
'${ddmeshRegisterUrl}?registerkey=${ddmeshRegisterKey}&node=${
|
|
toString ddmeshNode
|
|
}'
|
|
'';
|
|
serviceConfig = {
|
|
User = "nobody";
|
|
Group = "nogroup";
|
|
};
|
|
};
|
|
systemd.timers.ddmesh-register-node = {
|
|
partOf = [ "ddmesh-register-node.service" ];
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig.OnCalendar = "daily";
|
|
};
|
|
|
|
# Refresh sysinfo.json
|
|
systemd.services.sysinfo-json = {
|
|
script = ''
|
|
${sysinfo-json}/bin/bmxddump.sh
|
|
mkdir -p /run/nginx
|
|
${sysinfo-json}/bin/sysinfo-json.cgi > /run/nginx/sysinfo.json
|
|
'';
|
|
};
|
|
systemd.timers.sysinfo-json = {
|
|
partOf = [ "sysinfo-json.service" ];
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig.OnCalendar = "minutely";
|
|
};
|
|
|
|
# Advertise Freifunk routes to ZW core
|
|
services.bird2 = {
|
|
enable = true;
|
|
# nix-build cannot access /run/secrets/
|
|
checkConfig = false;
|
|
config = ''
|
|
protocol kernel K4 {
|
|
ipv4 {
|
|
export all;
|
|
};
|
|
}
|
|
# BIRD routing table for Wireguard transport
|
|
ipv4 table upstream4_table;
|
|
|
|
# Kernel routing table for Wireguard transport
|
|
protocol kernel upstream4 {
|
|
kernel table ${toString rt_table_upstream};
|
|
ipv4 {
|
|
export all;
|
|
table upstream4_table;
|
|
};
|
|
}
|
|
protocol kernel K6 {
|
|
ipv6 {
|
|
export all;
|
|
};
|
|
}
|
|
protocol device {
|
|
scan time 10;
|
|
}
|
|
|
|
ipv4 table bmx_gw;
|
|
protocol kernel BMX_GW {
|
|
learn;
|
|
kernel table ${toString rt_table_tuns};
|
|
ipv4 {
|
|
table bmx_gw;
|
|
import filter {
|
|
if net ~ [ 0.0.0.0/0 ] then {
|
|
# Learn Freifunk default route
|
|
accept;
|
|
}
|
|
reject;
|
|
};
|
|
};
|
|
}
|
|
protocol pipe import_bmx_gw {
|
|
table master4;
|
|
peer table bmx_gw;
|
|
import all;
|
|
}
|
|
|
|
protocol ospf v2 ZW4 {
|
|
ipv4 {
|
|
export where net != 0.0.0.0/0;
|
|
import where net != 0.0.0.0/0;
|
|
};
|
|
area 0 {
|
|
stubnet 10.200.0.0/15;
|
|
interface "core" {
|
|
hello 10;
|
|
wait 20;
|
|
include "${config.sops.secrets."bird/ospf/auth".path}";
|
|
};
|
|
};
|
|
}
|
|
|
|
protocol ospf v2 ZW4_freifunk {
|
|
ipv4 {
|
|
export where net = 0.0.0.0/0;
|
|
};
|
|
area 0 {
|
|
interface "core" instance ${toString zentralwerk.lib.config.site.hosts.freifunk.ospf.upstreamInstance} {
|
|
hello 10;
|
|
wait 20;
|
|
include "${config.sops.secrets."bird/ospf/auth".path}";
|
|
};
|
|
};
|
|
}
|
|
|
|
protocol ospf v3 ZW6 {
|
|
ipv6 {
|
|
import all;
|
|
};
|
|
area 0 {
|
|
interface "core" {
|
|
hello 10;
|
|
wait 20;
|
|
include "${config.sops.secrets."bird/ospf/auth".path}";
|
|
};
|
|
};
|
|
}
|
|
|
|
${lib.concatStrings (lib.imap0 (i: upstream: ''
|
|
# OSPFv2 to receive a default route from ${upstream}
|
|
protocol ospf v2 ZW4_${upstream} {
|
|
ipv4 {
|
|
import filter {
|
|
preference = preference + ${toString (200 - i)};
|
|
accept;
|
|
};
|
|
table upstream4_table;
|
|
};
|
|
area 0 {
|
|
interface "core" instance ${toString zentralwerk.lib.config.site.hosts.${upstream}.ospf.upstreamInstance} {
|
|
hello 10;
|
|
wait 20;
|
|
include "${config.sops.secrets."bird/ospf/auth".path}";
|
|
};
|
|
};
|
|
};
|
|
'') upstreams)}
|
|
|
|
${lib.concatStrings (lib.imap0 (i: upstream: ''
|
|
# OSPFv3 to receive a default route from ${upstream}
|
|
protocol ospf v3 ZW6_${upstream} {
|
|
ipv6 {
|
|
import filter {
|
|
preference = preference + ${toString (200 - i)};
|
|
accept;
|
|
};
|
|
};
|
|
area 0 {
|
|
interface "core" instance ${toString zentralwerk.lib.config.site.hosts.${upstream}.ospf.upstreamInstance} {
|
|
hello 10;
|
|
wait 20;
|
|
include "${config.sops.secrets."bird/ospf/auth".path}";
|
|
};
|
|
};
|
|
};
|
|
'') upstreams)}
|
|
|
|
router id ${coreAddress};
|
|
'';
|
|
};
|
|
|
|
# HTTP Reverse Proxy to provide services into Freifunk
|
|
services.nginx = {
|
|
enable = true;
|
|
appendHttpConfig = ''
|
|
proxy_buffering off;
|
|
'';
|
|
|
|
virtualHosts = {
|
|
"c3d2.ffdd" = {
|
|
default = true;
|
|
root = ./assets;
|
|
locations = let
|
|
sysinfo-json = {
|
|
alias = "/run/nginx/sysinfo.json";
|
|
extraConfig = ''
|
|
default_type application/json;charset=UTF-8;
|
|
'';
|
|
};
|
|
in {
|
|
"/" = {
|
|
index = "index.html";
|
|
extraConfig = ''
|
|
etag off;
|
|
add_header etag "\"${builtins.substring 11 32 (./assets)}\"";
|
|
'';
|
|
};
|
|
"=/sysinfo-json.cgi" = sysinfo-json;
|
|
"=/sysinfo.json" = sysinfo-json;
|
|
};
|
|
};
|
|
"storage.hq.c3d2.ffdd".locations."/".proxyPass =
|
|
"http://storage.hq.c3d2.de/";
|
|
"grafana.hq.c3d2.ffdd".locations."/" = {
|
|
proxyPass = "https://grafana.hq.c3d2.de/";
|
|
extraConfig = ''
|
|
proxy_ssl_server_name on;
|
|
'';
|
|
};
|
|
"influxdb.hq.c3d2.ffdd".locations."/".proxyPass =
|
|
"http://grafana.hq.c3d2.de:8086/";
|
|
};
|
|
};
|
|
|
|
# This value determines the NixOS release with which your system is to be
|
|
# compatible, in order to avoid breaking some software such as database
|
|
# servers. You should change this only after NixOS release notes say you
|
|
# should.
|
|
system.stateVersion = "20.03"; # Did you read the comment?
|
|
}
|