freifunk: format

This commit is contained in:
Sandro - 2023-09-15 22:54:36 +02:00
parent 6b84687f04
commit 29a78eae42
Signed by: sandro
GPG Key ID: 3AF5A43A3EECC2E5
1 changed files with 368 additions and 388 deletions

View File

@ -50,7 +50,8 @@ let
esac esac
''; '';
in { in
{
imports = [ imports = [
"${modulesPath}/profiles/minimal.nix" "${modulesPath}/profiles/minimal.nix"
]; ];
@ -71,47 +72,49 @@ in {
autoNetSetup = false; autoNetSetup = false;
}; };
}; };
services.collectd.plugins.protocols = "";
microvm.interfaces = [ { microvm.interfaces = [{
type = "tap"; type = "tap";
id = "core-freifunk"; id = "core-freifunk";
mac = mac.core; mac = mac.core;
} { }
type = "tap"; {
id = "bmx-freifunk"; type = "tap";
mac = mac.bmx; id = "bmx-freifunk";
} ]; mac = mac.bmx;
}];
networking.hostName = "freifunk"; networking = {
networking.useNetworkd = true; firewall.enable = false;
networking.nameservers = [ "172.20.73.8" "9.9.9.9" ]; hostName = "freifunk";
networking.firewall.enable = false; # Configure rt_table name
networking.nat = { iproute2 = {
enable = true; enable = true;
# This doesn't really work, hence the `extraCommands` rttablesExtraConfig = ''
externalInterface = meshInterface; ${toString rt_table_upstream} upstream
#internalInterfaces = [ "core" ]; ${toString rt_table_hosts} bmx_hosts
${toString rt_table_nets} bmx_nets
${toString rt_table_tuns} bmx_tuns
'';
};
nameservers = [ "172.20.73.8" "9.9.9.9" ];
nat = {
enable = true;
# This doesn't really work, hence the `extraCommands`
externalInterface = meshInterface;
#internalInterfaces = [ "core" ];
# Setup routing into Freifunk, # Setup routing into Freifunk,
# masquerading anything that isn't already their IP range # masquerading anything that isn't already their IP range
extraCommands = '' extraCommands = ''
${pkgs.iptables}/bin/iptables -t nat -F POSTROUTING ${pkgs.iptables}/bin/iptables -t nat -F POSTROUTING
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING \ ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING \
\! --source 10.200.0.0/15 -o ipip-node51001 -j SNAT --to 10.200.${ddmeshAddrPart} \! --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 ${pkgs.iptables}/bin/iptables -t nat -o bat0 -A POSTROUTING -j MASQUERADE
set -e set -e
''; '';
}; };
# Configure rt_table name useNetworkd = true;
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 ]; environment.systemPackages = with pkgs; [ tcpdump bmon wireguard-tools iperf bmxd ];
@ -131,379 +134,356 @@ in {
# unbreak wg-vpn6 ingress path # unbreak wg-vpn6 ingress path
boot.kernel.sysctl."net.ipv4.conf.core.rp_filter" = 0; boot.kernel.sysctl."net.ipv4.conf.core.rp_filter" = 0;
systemd.network = { services = {
netdevs = { # Advertise Freifunk routes to ZW core
# Dummy interface for primary (10.200) address bird2 = {
"10-bmx-prime" = { enable = true;
enable = true; # nix-build cannot access /run/secrets/
netdevConfig = { checkConfig = false;
Kind = "dummy"; config = ''
Name = meshLoopback; protocol kernel K4 {
};
};
# 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 = {
Endpoint = "vpn-it4r.freifunk-dresden.de: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 { ipv4 {
import filter { export all;
preference = preference + ${toString (200 - i)}; };
accept; }
}; # 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; table upstream4_table;
}; };
area 0 { }
interface "core" instance ${toString zentralwerk.lib.config.site.hosts.${upstream}.ospf.upstreamInstance} { protocol kernel K6 {
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 { 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 { import filter {
preference = preference + ${toString (200 - i)}; if net ~ [ 0.0.0.0/0 ] then {
accept; # 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 { area 0 {
interface "core" instance ${toString zentralwerk.lib.config.site.hosts.${upstream}.ospf.upstreamInstance} { stubnet 10.200.0.0/15;
interface "core" {
hello 10; hello 10;
wait 20; wait 20;
include "${config.sops.secrets."bird/ospf/auth".path}"; include "${config.sops.secrets."bird/ospf/auth".path}";
}; };
}; };
}; }
'') upstreams)}
router id ${coreAddress}; 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};
'';
};
collectd.plugins.protocols = "";
# HTTP Reverse Proxy to provide services into Freifunk
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/";
};
};
}; };
# HTTP Reverse Proxy to provide services into Freifunk systemd = {
services.nginx = { network = {
enable = true; netdevs = {
appendHttpConfig = '' # Dummy interface for primary (10.200) address
proxy_buffering off; "10-bmx-prime" = {
''; enable = true;
netdevConfig = {
virtualHosts = { Kind = "dummy";
"c3d2.ffdd" = { Name = meshLoopback;
default = true;
root = ./assets;
locations = let
sysinfo-json = {
alias = "/run/nginx/sysinfo.json";
extraConfig = ''
default_type "application/json;charset=UTF-8";
'';
}; };
in { };
"/" = { # Freifunk Dresden Backbone
index = "index.html"; "31-wg-vpn6" = {
extraConfig = '' enable = true;
etag off; netdevConfig = {
add_header etag "\"${builtins.substring 11 32 (./assets)}\""; 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 = {
Endpoint = "vpn-it4r.freifunk-dresden.de: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}";
}; };
"=/sysinfo-json.cgi" = sysinfo-json;
"=/sysinfo.json" = sysinfo-json;
}; };
}; };
"storage.hq.c3d2.ffdd".locations."/".proxyPass = links = {
"http://storage.hq.c3d2.de/"; # Wired mesh interface
"grafana.hq.c3d2.ffdd".locations."/" = { "10-bmx" = {
proxyPass = "https://grafana.hq.c3d2.de/"; enable = true;
extraConfig = '' matchConfig = { MACAddress = mac.bmx; };
proxy_ssl_server_name on; linkConfig.Name = meshInterface;
''; };
# Wired core interface
"10-core" = {
enable = true;
matchConfig = { MACAddress = mac.core; };
linkConfig.Name = "core";
};
}; };
"influxdb.hq.c3d2.ffdd".locations."/".proxyPass = networks = {
"http://grafana.hq.c3d2.de:8086/"; # 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;
};
}];
};
};
};
# Re-register periodically
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";
};
};
timers.ddmesh-register-node = {
partOf = [ "ddmesh-register-node.service" ];
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "daily";
};
# Refresh sysinfo.json
services.sysinfo-json = {
script = ''
${sysinfo-json}/bin/bmxddump.sh
mkdir -p /run/nginx
${sysinfo-json}/bin/sysinfo-json.cgi > /run/nginx/sysinfo.json
'';
};
timers.sysinfo-json = {
partOf = [ "sysinfo-json.service" ];
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "minutely";
}; };
}; };