From 6abe4fa771fc5a47809ad844807dcdcda9219af3 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Sat, 3 Oct 2020 00:18:34 +0200 Subject: [PATCH] bgpwtf/machines: init edge01.waw This configures our WAW edge router using NixOS. This replaces our previous Ubuntu installation. Change-Id: Ibd72bde66ec413164401da407c5b268ad83fd3af --- .../machines/edge01.waw.bgp.wtf-hardware.nix | 36 ++ bgpwtf/machines/edge01.waw.bgp.wtf.nix | 413 ++++++++++++++++ bgpwtf/machines/modules/bootstrap.nix | 77 +++ bgpwtf/machines/modules/eoip.nix | 75 +++ bgpwtf/machines/modules/prometheus.nix | 57 +++ bgpwtf/machines/modules/rename-interfaces.nix | 29 ++ bgpwtf/machines/modules/router.nix | 54 +++ bgpwtf/machines/modules/routing.nix | 440 ++++++++++++++++++ bgpwtf/machines/modules/rsh-unbound.nix | 70 +++ .../cipher/edge01.waw.bgp.wtf-private.nix | 49 ++ bgpwtf/machines/secrets/cipher/passwords.nix | 40 ++ bgpwtf/machines/secrets/plain/.gitignore | 2 + bgpwtf/machines/tests/edge01-waw.nix | 288 ++++++++++++ ops/machines.nix | 53 +++ 14 files changed, 1683 insertions(+) create mode 100644 bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix create mode 100644 bgpwtf/machines/edge01.waw.bgp.wtf.nix create mode 100644 bgpwtf/machines/modules/bootstrap.nix create mode 100644 bgpwtf/machines/modules/eoip.nix create mode 100644 bgpwtf/machines/modules/prometheus.nix create mode 100644 bgpwtf/machines/modules/rename-interfaces.nix create mode 100644 bgpwtf/machines/modules/router.nix create mode 100644 bgpwtf/machines/modules/routing.nix create mode 100644 bgpwtf/machines/modules/rsh-unbound.nix create mode 100644 bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix create mode 100644 bgpwtf/machines/secrets/cipher/passwords.nix create mode 100644 bgpwtf/machines/secrets/plain/.gitignore create mode 100644 bgpwtf/machines/tests/edge01-waw.nix create mode 100644 ops/machines.nix diff --git a/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix b/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix new file mode 100644 index 00000000..d6ed36a3 --- /dev/null +++ b/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix @@ -0,0 +1,36 @@ +# Hardware configuration of edge01. Generated by nixos-generate-config. + +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "ahci" "usb_storage" "usbhid" ]; + boot.initrd.kernelModules = [ "dm-snapshot" ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + system.stateVersion = "20.03"; + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + + fileSystems."/" = + { device = "/dev/disk/by-uuid/b1b85556-8f3b-4695-8ad5-2159c93c76de"; + fsType = "ext4"; + }; + + fileSystems."/boot" = + # Pendrive. + # Real boot is at D8BA-345D. + { device = "/dev/disk/by-uuid/D8BA-345D"; + fsType = "vfat"; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/5dadcff4-fcd4-4e8d-81f6-be68fb630396"; } + ]; + + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; +} diff --git a/bgpwtf/machines/edge01.waw.bgp.wtf.nix b/bgpwtf/machines/edge01.waw.bgp.wtf.nix new file mode 100644 index 00000000..ec459389 --- /dev/null +++ b/bgpwtf/machines/edge01.waw.bgp.wtf.nix @@ -0,0 +1,413 @@ +# Main configuration file for edge01.waw.bgp.wtf. +# This includes everything needed to run the machine, except for hardware +# configuration, which is defined in //bgpwtf/machines/ +# edge01.waw.bgp.wtf-hardware.nix. +# +# Any changes here can be tested in a local NixOS test by running the following: +# +# nix-build -A bgpwtf.machines.tests.edge01-waw +# +# To deploy changes, see //ops:machines.nix. + +{ config, pkgs, ... }: + +with builtins; + +let + passwords = import ./secrets/plain/passwords.nix; + +in rec { + networking.hostName = "edge01"; + networking.domain = "waw.bgp.wtf"; + + imports = [ + ./modules/router.nix + + # Private configuration data - notably, customer data. + ./secrets/plain/edge01.waw.bgp.wtf-private.nix + ]; + + # TODO(q3k): make this generic, move to modules/router.nix. + services.unbound = { + enable = true; + interfaces = [ + "185.236.240.1" + "2a0d:eb00:2137::1" + "127.0.0.1" + ]; + allowedAccess = [ + "185.236.240.0/22" + "2a0d:eb00::0/29" + "127.0.0.0/8" + ]; + extraConfig = '' + outgoing-interface: 185.236.240.1 + outgoing-interface: 2a0d:eb00:2137::1 + cache-max-negative-ttl: 30 + + # Disable DoH in Firefox + local-zone: "use-application-dns.net" static + + # Rejestr Stron Hazardowych. + # Populated by the rsh-unbound daemon. + include: "/var/lib/unbound/rsh.conf" + ''; + }; + hscloud.rsh = { + enable = true; + out = "/var/lib/unbound/rsh.conf"; + }; + + hscloud.renameInterfaces = { + # Link to Nitronet CPE. + e1-nnet.mac = "ac:1f:6b:1c:d7:ae"; + # Link to HSWAW Customs. + e2-customs.mac = "ac:1f:6b:1c:d7:af"; + # Link to management switch. + e3-mgmt.mac = "ac:1f:6b:1c:d7:b0"; + # Link to oob1. + e4-oob.mac = "ac:1f:6b:1c:d7:b1"; + e5.mac = "ac:1f:6b:1c:d7:b2"; + e6.mac = "ac:1f:6b:1c:d7:b3"; + # Link to dcsw01.hswaw.net + e7-dcsw.mac = "ac:1f:6b:1c:db:06"; + e8.mac = "ac:1f:6b:1c:db:07"; + }; + networking.interfaces.e7-dcsw.mtu = 9000; + + networking.vlans = { + "vl-globalmix" = { interface = "e1-nnet"; id = 466; }; + "vl-polmix" = { interface = "e1-nnet"; id = 2486; }; + "vl-openpeering" = { interface = "e1-nnet"; id = 992; }; + + "vl-dcsw-l3" = { interface = "e7-dcsw"; id = 4001; }; + "vl-dist-l3" = { interface = "e7-dcsw"; id = 3006; }; + + # Extra vlans contained in //bgpwtf/machines/secrets/plain/edge01.waw.bgp.wtf-private.nix + }; + networking.interfaces = { + lo = { + ipv4.addresses = [ { address = "185.236.240.1"; prefixLength = 32; } ]; + ipv6.addresses = [ { address = "2a0d:eb00:2137::1"; prefixLength = 64; } ]; + }; + ## EPIX links via Nitronet. + "vl-globalmix" = { + ipv4.addresses = [ { address = "185.235.70.45"; prefixLength = 31; } ]; + ipv6.addresses = [ { address = "2001:67c:778:fd40::b9eb:462d"; prefixLength = 127; } ]; + }; + "vl-polmix" = { + ipv4.addresses = [ { address = "94.246.185.175"; prefixLength = 31; } ]; + ipv6.addresses = [ { address = "2001:67c:778:fa40::5ef6:b9af"; prefixLength = 127; } ]; + }; + "vl-openpeering" = { + ipv4.addresses = [ { address = "89.46.145.61"; prefixLength = 21; } ]; + ipv6.addresses = [ { address = "2001:678:3ac::313"; prefixLength = 48; } ]; + }; + + ## L3/mgmt links.. + # To customs.hackerspace.pl. + "e2-customs" = { + ipv4.addresses = [ { address = "185.236.240.4"; prefixLength = 31; } ]; + ipv6.addresses = [ { address = "2a0d:eb00:2137:1::2"; prefixLength = 127; } ]; + }; + # To mgmt. + "e3-mgmt" = { + ipv4.addresses = [ { address = "10.10.10.1"; prefixLength = 24; } ]; + }; + # To obb1. + "e4-oob" = { + ipv4.addresses = [ { address = "185.236.240.74"; prefixLength = 29; } ]; + }; + # To dcsw01, L3 (BGP). + "vl-dcsw-l3" = { + mtu = 9000; + ipv4.addresses = [ { address = "185.236.240.6"; prefixLength = 31; } ]; + ipv6.addresses = [ { address = "2a0d:eb00:2137:1::6"; prefixLength = 127; } ]; + }; + # To dist02, L3 (BGP). + "vl-dist-l3" = { + ipv4.addresses = [ { address = "185.236.240.14"; prefixLength = 31; } ]; + ipv6.addresses = [ { address = "2a0d:eb00:2137:1::a"; prefixLength = 127; } ]; + }; + + # Extra interface configs contained in //bgpwtf/machines/secrets/plain/edge01.waw.bgp.wtf-private.nix + }; + + hscloud.routing.enable = true; + hscloud.routing.routerID = "185.236.240.1"; + hscloud.routing.asn = 204880; + # Use default master4/master6 tables so that `birdc show route` works. + hscloud.routing.tables.master.program = true; + hscloud.routing.tables.master.programSourceV4 = "185.236.240.1"; + hscloud.routing.tables.master.programSourceV6 = "2a0d:eb00:2137::1"; + + hscloud.routing.extra = '' + function net_martian_v4() { + return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+, + 127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ]; + } + function net_as204480_waw_v4() { + return net ~ [ 185.236.240.0/23+ ]; + } + function net_martian_v6() { + return net ~ [ fc00::/7+, fec0::/10+, ::/128-, ::/0{0,15}, ::/0{49,128} ]; + } + function net_as204480_waw_v6() { + return net ~ [ 2a0d:eb00::/32 ]; + } + + ''; + hscloud.routing.originate = { + # WAW prefixes, exposed into internet BGP table. + v4.waw = { table = "internet"; address = "185.236.240.0"; prefixLength = 23; }; + v6.waw = { table = "internet"; address = "2a0d:eb00::"; prefixLength = 32; }; + + # Default gateway via us, exposed into aggregated table. + v4.default = { table = "aggregate"; address = "0.0.0.0"; prefixLength = 0; }; + v6.default = { table = "aggregate"; address = "::"; prefixLength = 0; }; + }; + hscloud.routing.pipe = let + copySourcesToKernel = sources: table: { + table = "master"; + peerTable = table; + filterIn = '' + ${concatStringsSep "\n" (map (v: "if source = RTS_${v} then accept;") sources)} + reject; + ''; + }; + in { + v4."internet_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "internet"; + v4."aggregate_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "aggregate"; + v6."internet_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "internet"; + v6."aggregate_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "aggregate"; + }; + + hscloud.routing.ospf.v6.main = { + area."0.0.0.0".interfaces = { + "e2-customs" = { + type = "bcast"; + }; + "e4-oob" = { + type = "bcast"; + stub = true; + }; + }; + table = "aggregate"; + filterIn = '' + # hswaw prefix from e2-customs + if net ~ [ 2a0d:eb00:4242::/64+ ] then accept; + # e2-customs link + if net ~ [ 2a0d:eb00:2137:1::2/127+ ] then accept; + ''; + }; + hscloud.routing.ospf.v4.main = { + area."0.0.0.0".interfaces = { + "e4-oob" = { + type = "bcast"; + stub = true; + }; + }; + table = "aggregate"; + filterIn = '' + # e4-oob link + if net ~ [ 185.236.240.72/29+ ] then accept; + ''; + }; + + hscloud.routing.bgpSessions.v4 = let + filterInUpstream = '' + if net_martian_v4() then reject; + if net_as204480_waw_v4() then reject; + accept; + ''; + filterOutUpstream = '' + # Accept AS204880-announced prefixes. + if (net ~ [ 185.236.240.0/22+ ]) then accept; + reject; + ''; + in { + "waw_globalmix" = { + description = "UPSTREAM EPIX.WAR GlobalMix"; + table = "internet"; + local = "185.235.70.45"; + neighbors = [ + { address = "185.235.70.44"; asn = 62081; } + ]; + prepend = 2; pref = 100; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_polmix" = { + description = "UPSTREAM EPIX.WAR PolMix"; + table = "internet"; + local = "94.246.185.175"; + neighbors = [ + { address = "94.246.185.174"; asn = 201054; } + ]; + prepend = 1; pref = 200; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_openpeering" = { + description = "IXP EPIX.WAR OpenPeering"; + table = "internet"; + local = "89.46.145.61"; + neighbors = [ + { address = "89.46.144.11"; asn = 48850; } + { address = "89.46.144.12"; asn = 48850; } + ]; + prepend = 0; pref = 300; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_google" = { + description = "PEER Google AS15169 (EPIX)"; + table = "internet"; + local = "89.46.145.61"; + neighbors = [ + # TODO(q3k): secretify the password. + { address = "89.46.144.185"; asn = 15169; password = passwords."edge01.waw-bgp-google"; } + ]; + prepend = 0; pref = 300; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + # hscloud spine switch (dcsw01.hswaw.net). + "waw_hscloud" = { + description = "AGGREGATE CUSTOMER hscloud/dcsw01"; + table = "aggregate"; + local = "185.236.240.6"; + asn = 65000; + neighbors = [ + { address = "185.236.240.7"; asn = 65001; } + ]; + filterIn = '' + # wieloryb prefix + if net ~ [ 185.236.240.8/31+ ] then accept; + # dcsw01 l2 general purpose + if net ~ [ 185.236.240.24/29+ ] then accept; + # hscloud l2 general purpose + if net ~ [ 185.236.240.32/28+ ] then accept; + # k0 metallb pools + if net ~ [ 185.236.240.48/28+, 185.236.240.112/28+ ] then accept; + reject; + ''; + }; + # bgp.wtf internet customer router on W2A, floor 3 (dist02.bgp.wtf). + "waw_dist02" = { + description = "AGGREGATE CUSTOMER bgpwtf/dist02"; + table = "aggregate"; + local = "185.236.240.14"; + asn = 65000; + neighbors = [ + { address = "185.236.240.15"; asn = 65002; } + ]; + filterIn = '' + # dist02 customer routed + if net ~ [ 185.236.240.80/28+ ] then accept; + reject; + ''; + }; + # backup LTE link to edge01.fra + "fra_edge01" = { + description = "IBGP edge01.fra"; + table = "internet"; + local = "185.236.240.74"; + direct = true; + neighbors = [ + { address = "185.236.240.75"; asn = 204880; } + ]; + pref = 50; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + }; + hscloud.routing.bgpSessions.v6 = let + filterInUpstream = '' + if net_martian_v6() then reject; + if net_as204480_waw_v6() then reject; + accept; + ''; + filterOutUpstream = '' + # Accept AS204880-announced prefixes. + if (net ~ [ 2a0d:eb00::/29+ ]) then accept; + reject; + ''; + in { + "waw_globalmix" = { + description = "UPSTREAM EPIX.WAR GlobalMix"; + table = "internet"; + local = "2001:67c:778:fd40::b9eb:462d"; + neighbors = [ + { address = "2001:67c:778:fd40::b9eb:462c"; asn = 62081; } + ]; + prepend = 2; pref = 100; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_polmix" = { + description = "UPSTREAM EPIX.WAR PolMix"; + table = "internet"; + local = "2001:67c:778:fa40::5ef6:b9af"; + neighbors = [ + { address = "2001:67c:778:fa40::5ef6:b9ae"; asn = 201054; } + ]; + prepend = 1; pref = 200; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_openpeering" = { + description = "IXP EPIX.WAR OpenPeering"; + table = "internet"; + local = "2001:678:3ac::313"; + neighbors = [ + { address = "2001:678:3ac::11"; asn = 48850; } + { address = "2001:678:3ac::12"; asn = 48850; } + ]; + prepend = 0; pref = 300; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + "waw_google" = { + description = "PEER Google AS15169 (EPIX)"; + table = "internet"; + local = "2001:678:3ac::313"; + neighbors = [ + { address = "2001:678:3ac::185"; asn = 15169; password = passwords."edge01.waw-bgp-google"; } + ]; + prepend = 0; pref = 300; + filterIn = filterInUpstream; + filterOut = filterOutUpstream; + }; + # hscloud spine switch (dcsw01.hswaw.net). + "waw_hscloud" = { + description = "AGGREGATE CUSTOMER dcsw01.hswaw.net"; + table = "aggregate"; + local = "2a0d:eb00:2137:1::6"; + asn = 65000; + neighbors = [ + { address = "2a0d:eb00:2137:1::7"; asn = 65001; } + ]; + filterIn = '' + # dcsw01 l2 general purpose + if net ~ [ 2a0d:eb00:2137::/48+ ] then accept; + reject; + ''; + }; + # bgp.wtf internet customer router on W2A, floor 3 (dist02.bgp.wtf). + "waw_dist02" = { + description = "AGGREGATE CUSTOMER dist02.bgp.wtf"; + table = "aggregate"; + local = "2a0d:eb00:2137:1::a"; + asn = 65000; + neighbors = [ + { address = "2a0d:eb00:2137:1::b"; asn = 65002; } + ]; + filterIn = '' + # dist02 customers. + if net ~ [ 2a0d:eb00:8002::/48 ] then accept; + reject; + ''; + }; + }; +} diff --git a/bgpwtf/machines/modules/bootstrap.nix b/bgpwtf/machines/modules/bootstrap.nix new file mode 100644 index 00000000..09f2555f --- /dev/null +++ b/bgpwtf/machines/modules/bootstrap.nix @@ -0,0 +1,77 @@ +# Functionality that used to live in bootstrap.hswaw.net, a VM. +# PXE boot support has been removed and the functionality moved back to +# edge01.waw.bgp.wtf. + +{ config, pkgs, ... }: { + networking.bridges.bootstrap.interfaces = []; + networking.interfaces.bootstrap.ipv4.addresses = [ + { address = "185.236.240.18"; prefixLength = 32; } + ]; + services.dhcpd4 = { + enable = true; + interfaces = [ "bootstrap" "vl-dcsw-l3" ]; + extraConfig = '' + # ISC DHCP is trash. We only use it in relay mode, yet we have to do + # this. + subnet 185.236.240.18 netmask 255.255.255.255 {} + + subnet 185.236.240.6 netmask 255.255.255.254 {} + + subnet 185.236.240.24 netmask 255.255.255.248 { + option routers 185.236.240.25; + range 185.236.240.29 185.236.240.30; + option domain-name-servers 8.8.8.8; + } + + subnet 185.236.240.32 netmask 255.255.255.240 { + range 185.236.240.45 185.236.240.46; + option routers 185.236.240.33; + option domain-name-servers 8.8.8.8; + } + + host bc01n01 { + hardware ethernet 00:23:ae:fe:83:20; + fixed-address 185.236.240.35; + option host-name "bc01n01"; + } + host bc01n02 { + hardware ethernet 00:23:ae:fe:83:c4; + fixed-address 185.236.240.36; + option host-name "bc01n02"; + } + host bc01n03 { + hardware ethernet 00:23:ae:fe:42:80; + fixed-address 185.236.240.37; + option host-name "bc01n03"; + } + host boston-packets { + hardware ethernet 00:23:ae:fe:45:8c; + fixed-address 185.236.240.38; + option host-name "boston-packets.hackerspace.pl"; + #filename "ipxe.efi"; + } + host dcr01s22 { + hardware ethernet 90:1b:0e:08:12:b8; + fixed-address 185.236.240.39; + option host-name "dcr01s22"; + #filename "ipxe.efi"; + } + host dcr01s24 { + hardware ethernet 90:1b:0e:31:bb:6a; + fixed-address 185.236.240.40; + option host-name "dcr01s24"; + #filename "ipxe.efi"; + } + host dsctf { + hardware ethernet 00:23:ae:fe:45:50; + fixed-address 185.236.240.41; + option host-name "dsctf"; + } + host dcr03s32b1 { + hardware ethernet 02:01:87:4a:9a:b9; + fixed-address 185.236.240.26; + option host-name "dcr03s32b1"; + } + ''; + }; +} diff --git a/bgpwtf/machines/modules/eoip.nix b/bgpwtf/machines/modules/eoip.nix new file mode 100644 index 00000000..5ce04f39 --- /dev/null +++ b/bgpwtf/machines/modules/eoip.nix @@ -0,0 +1,75 @@ +# A small Ethernet-over-IP service implementation. +# Yes, that's the Mikrotik EoIP implementation. This one is somewhat sketchy +# (notably, it pumps huge zero-padded frames into tap), so doesn't use it for +# production. We currently only use it in the edge01.waw test framework to +# bring vlans across test VMs. + +{ config, pkgs, lib, ... }: + +with lib; + +let + eoip = pkgs.stdenv.mkDerivation { + pname = "eoip"; + version = "20180119"; + nativeBuildInputs = with pkgs; [ cmake ]; + src = pkgs.fetchFromGitHub { + owner = "amphineko"; + repo = "eoiptapd"; + rev = "5573a905bcbc001b503308665f098e82f451dc33"; + sha256 = "0np9dzcw5w6jarzdv2yh3mbzz0wgw10sjqyi6pxan4ipr75v1b8s"; + }; + installPhase = '' + mkdir -p $out/bin + cp eoiptapd $out/bin/eoiptapd + ''; + }; + + cfg = config.hscloud.eoip; + +in { + options.hscloud.eoip = { + interfaces = mkOption { + type = with types; attrsOf (submodule { + options = { + localV4 = mkOption { + type = types.str; + description = "Local outer IPv4 address"; + }; + remoteV4 = mkOption { + type = types.str; + description = "Remote outer IPv4 address"; + }; + id = mkOption { + type = types.int; + description = "Tunnel ID"; + }; + parent = mkOption { + type = types.str; + description = "Parent/outer device"; + }; + }; + }); + description = '' + EoIP interfaces to create. + ''; + }; + }; + + config.systemd.services = mapAttrs' (name: value: nameValuePair "${name}-eoip" { + wantedBy = [ "network.target" ]; + wants = [ + "${name}-netdev.service" + "network-addresses-${value.parent}.service" + ]; + after = [ + "network-addresses-${value.parent}.service" + ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${eoip}/bin/eoiptapd -i ${name} -l ${value.localV4} -r ${value.remoteV4} -t ${toString value.id}"; + Restart = "always"; + RestartSec = "1"; + }; + }) cfg.interfaces; +} diff --git a/bgpwtf/machines/modules/prometheus.nix b/bgpwtf/machines/modules/prometheus.nix new file mode 100644 index 00000000..704c2577 --- /dev/null +++ b/bgpwtf/machines/modules/prometheus.nix @@ -0,0 +1,57 @@ +# Prometheus configuration for a BIRD-enabled router. + +{ config, pkgs, lib, ... }: + +with lib; + +let + nodeExporterPort = 9100; + birdExporterPort = 9101; + + birdExporter = pkgs.buildGoModule rec { + pname = "bird-exporter"; + version = "1.2.5"; + src = pkgs.fetchFromGitHub { + owner = "czerwonk"; + repo = "bird_exporter"; + rev = version; + sha256 = "1qrhncy1f119f5rfgn2d1l6nvapaqkld4zb9bxzdqmmw6kicc7bs"; + }; + + vendorSha256 = null; + }; + +in { + systemd.services.bird_exporter = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${birdExporter}/bin/bird_exporter -format.new=true -bird.v2=true -web.listen-address=127.0.0.1:${toString birdExporterPort}"; + Restart = "always"; + RestartSec = "60"; + }; + }; + + services.prometheus.exporters.node = { + enable = true; + listenAddress = "127.0.0.1"; + port = nodeExporterPort; + }; + + services.nginx.enable = true; + services.nginx.virtualHosts."${config.networking.hostName}.${config.networking.domain}" = let + allowMonitoring = '' + allow 209.250.231.127; # monitoring.hackerspace.pl + deny all; + ''; + in { + locations."/metrics-node" = { + proxyPass = "http://127.0.0.1:${toString nodeExporterPort}/metrics"; + extraConfig = allowMonitoring; + }; + locations."/metrics-bird" = { + proxyPass = "http://127.0.0.1:${toString birdExporterPort}/metrics"; + extraConfig = allowMonitoring; + }; + }; +} diff --git a/bgpwtf/machines/modules/rename-interfaces.nix b/bgpwtf/machines/modules/rename-interfaces.nix new file mode 100644 index 00000000..bbb5c81b --- /dev/null +++ b/bgpwtf/machines/modules/rename-interfaces.nix @@ -0,0 +1,29 @@ +# Sketchy little module to renamei interfaces by MAC. +# This only works on startup. + +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.hscloud.renameInterfaces; +in { + options.hscloud.renameInterfaces = mkOption { + type = with types; attrsOf (submodule { + options = { + mac = mkOption { + type = types.str; + description = '' + MAC address to match by, in hexadecimal form (ie. ac:1f:6b:1c:d7:ae). + ''; + }; + }; + }); + description = '' + Interfaces to rename by property (eg. MAC address). + ''; + }; + + config.services.udev.extraRules = concatStringsSep "\n" (mapAttrsToList (n: v: '' + ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="${v.mac}", ATTR{addr_assign_type}=="0", NAME="${n}" + '') cfg); +} diff --git a/bgpwtf/machines/modules/router.nix b/bgpwtf/machines/modules/router.nix new file mode 100644 index 00000000..4999401e --- /dev/null +++ b/bgpwtf/machines/modules/router.nix @@ -0,0 +1,54 @@ +# Generic configuration for any bgpwtf router. + +{ config, pkgs, lib, ... }: + +with builtins; + +rec { + imports = [ + ./routing.nix + ./rename-interfaces.nix + ./rsh-unbound.nix + ./bootstrap.nix + ./prometheus.nix + ]; + + environment.systemPackages = with pkgs; [ + tcpdump htop dstat file strace gdb mtr + vim wget curl htop dstat whois bind + rxvt_unicode.terminfo dhcpcd efibootmgr + ]; + networking.useDHCP = false; + networking.firewall.enable = false; + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; + boot.kernel.sysctl."net.ipv4.conf.*.accept_redirects" = 0; + boot.kernel.sysctl."net.ipv4.conf.*.send_redirects" = 0; + boot.kernel.sysctl."net.ipv4.conf.*.accept_source_route" = 0; + boot.kernel.sysctl."net.ipv4.conf.*.proxy_arp" = 0; + boot.kernel.sysctl."net.ipv4.conf.*.secure_redirects" = 1; + boot.kernel.sysctl."net.ipv4.conf.*.bootp_relay" = 0; + boot.kernel.sysctl."net.ipv4.conf.*.arp_filter" = 1; + boot.kernel.sysctl."net.ipv4.conf.*.arp_ignore" = 1; + boot.kernel.sysctl."net.ipv4.conf.*.arp_announce" = 2; + boot.kernel.sysctl."net.ipv4.conf.*.rp_filter" = 0; + boot.kernel.sysctl."net.ipv6.conf.*.forwarding" = 1; + boot.kernel.sysctl."net.ipv6.conf.*.accept_ra" = 0; + boot.kernel.sysctl."net.ipv6.conf.*.autoconf" = 0; + boot.kernel.sysctl."net.ipv6.conf.*.router_solicitations" = 0; + + # Use Chrony instead of systemd-timesyncd + time.timeZone = "Europe/Warsaw"; + services.chrony.enable = true; + networking.nameservers = [ "8.8.8.8" ]; + + # Enable the OpenSSH daemon. + services.openssh.enable = true; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD4VJXAXEHEXZk2dxNwehneuJcEGkfXG/U7z4fO79vDVIENdedtXQUyLyhZJc5RTEfHhQj66FwIqzl7mzBHd9x9PuDp6QAYXrkVNMj48s6JXqZqBvF6H/weRqFMf4a2TZv+hG8D0kpvmLheCwWAVRls7Jofnp/My+yDd57GMdsbG/yFEf6WPMiOnA7hxdSJSVihCsCSw2p8PD4GhBe8CVt7xIuinhutjm9zYBjV78NT8acjDUfJh0B1ODTjs7nuW1CC4jybSe2j/OU3Yczj4AxRxBNWuFxUq+jBo9BfpbKLh+Tt7re+zBkaicM77KM/oV6943JJxgHNBBOsv9scZE7 q3k@amnesia" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG599UildOrAq+LIOQjKqtGMwjgjIxozI1jtQQRKHtCP q3k@mimeomia" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQb3YQoiYFZLKwvHYKbu1bMqzNeDCAszQhAe1+QI5SLDOotclyY/vFmOReZOsmyMFl71G2d7d+FbYNusUnNNjTxRYQ021tVc+RkMdLJaORRURmQfEFEKbai6QSFTwErXzuoIzyEPK0lbsQuGgqT9WaVnRzHJ2Q/4+qQbxAS34PuR5NqEkmn4G6LMo3OyJ5mwPkCj9lsqz4BcxRaMWFO3mNcwGDfSW+sqgc3E8N6LKrTpZq3ke7xacpQmcG5DU9VO+2QVPdltl9jWbs3gXjmF92YRNOuKPVfAOZBBsp8JOznfx8s9wDgs7RwPmDpjIAJEyoABqW5hlXfqRbTnfnMvuR informatic@InformaticPC" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGkMgEVwQM8yeuFUYL2TwlJIq9yUNBmHnwce46zeL2PK2CkMz7sxT/om7sp/K5XDiqeD05Nioe+Dr3drP6B8uI33S5NgxPIfaqQsRS+CBEgk6cqFlcdlKETU/DT+/WsdoO173n7mgGeafPInEuQuGDUID0Fl099kIxtqfAhdeZFMM6/szAZEZsElLJ8K6dp1Ni/jmnXCZhjivZH3AZUlnqrmtDG7FY1bgcOfDXAal45LItughGPtrdiigXe9DK2fW3+9DBZZduh5DMJTNlphAZ+nfSrbyHVKUg6WsgMSprur4KdU47q1QwzqqvEj75JcdP1jOWoZi4F6VJDte9Wb9lhD1jGgjxY9O6Gs4CH35bx15W7CN9hgNa0C8NbPJe/fZYIeMZmJ1m7O2xmnYwP8j+t7RNJWu7Pa3Em4mOEXvhBF07Zfq+Ye/4SluoRgADy5eII2x5fFo5EBhInxK0/X8wF6XZvysalVifoCh7T4Edejoi91oAxFgYAxbboXGlod0eEHIi2hla8SM9+IBHOChmgawKBYp2kzAJyAmHNBF+Pah9G4arVCj/axp/SJZDZbJQoI7UT/fJzEtvlb5RWrHXRq+y6IvjpUq4pzpDWW04+9UMqEEXRmhWOakHfEVM9rN8h3aJBflLUBBnh0Z/hVsKNh8bCRHaKtah8TrD9i+wMw== patryk.jakuszew@gmail.com" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC33naG1ptCvUcRWX9cj9wXM1nW1lyQC4SvMJzWlr9aMD96O8hQ2JMkuIUgUJvorAY02QRplQ2BuoVoVkdkzwjMyi1bL3OdgcKo7Z1yByClGTTocqNJYY0lcUb6EJH8+6e6F9ydrQlSxNzL1uCaA7phZr+yPcmAmWbSfioXn98yXNkE0emHxzJv/nypJY56sDCMC2IXDRd8L2goDtPwgPEW7bWfAQdIFMJ75xOidZOTxJ8eqyXLw/kxY5UlyX66jdoYz1sE5XUHuoQl1AOG9UdlMo0aMhUvP4pX5l7r7EnA9OttKMFB3oWqkVK/R6ynZ52YNOU5BZ9V+Ppaj34W0xNu+p0mbHcCtXYCTrf/OU0hcZDbDaNTjs6Vtcm2wYw9iAKX7Tex+eOMwUwlrlcyPNRV5BTot7lGNYfauHCSIuWJKN4NhCLR/NtVNh4/94eKkPTwJsY6XqDcS7q49wPAs4DAH7BJgsbHPOqygVHrY0YYEfz3Pj0HTxJHQMCP/hQX4fXEGt0BjgoVJbXPAQtPyeg0JuxiUg+b4CgVVfQ6R060MlM1BZzhmh+FY5MJH6nJppS0aHYCvSg8Z68NUlCPKy0jpcyfuAIWQWwSGG1O010WShQG2ELsvNdg5/4HVdCGNl5mmoom6JOd72FOZyQlHDFfeQUQRn9HOeCq/c51rK99SQ== bartek@IHM" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICTR292kx/2CNuWYIsZ6gykQ036aBGrmheIuZa6S1D2x implr@thonk" + ]; +} diff --git a/bgpwtf/machines/modules/routing.nix b/bgpwtf/machines/modules/routing.nix new file mode 100644 index 00000000..e87ab9d7 --- /dev/null +++ b/bgpwtf/machines/modules/routing.nix @@ -0,0 +1,440 @@ +# Declarative routing configuration. Usees BIRD2 underneath. +# +# The mapping from declarative configuration to BIRD is quite straightforward, +# however, we take a few liberties: +# - we introduce an 'originate' protocol for originating prefixes (using the +# static protocol). +# - routing tables in the configuration are referred to by a common name for +# IPv4 and IPv4 - while in BIRD, two tables are created (suffixed by '4' and +# '6', following the two default 'master4' and 'master6' tables). + +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.hscloud.routing; + + originateType = af: let + v4 = af == "ipv4"; + v6 = af == "ipv6"; + pretty = if v4 then "IPv4" else "IPv6"; + in with types; mkOption { + type = attrsOf (submodule { + options = { + table = mkOption { + type = nullOr str; + description = "BIRD table to which session should be connected."; + }; + address = mkOption { + type = str; + description = "Address part of prefix to announce."; + }; + prefixLength = mkOption { + type = int; + description = "Prefix length to announce."; + }; + }; + }); + default = {}; + description = "${pretty} prefixes to unconditionally inject into a table."; + }; + + originateRender = af: n: v: let + name = "static_originate_${af}_${n}"; + ip = if af == "ipv4" then "4" else "6"; + in '' + protocol static ${name} { + ${af} { + table ${v.table}${ip}; + import all; + export none; + }; + + route ${v.address}/${toString v.prefixLength} blackhole; + } + ''; + + ospfType = af: let + v4 = af == "ipv4"; + v6 = af == "ipv6"; + pretty = if v4 then "IPv4" else "IPv6"; + ospf = if v4 then "OSPFv2" else "OSPFv3"; + in with types; mkOption { + type = attrsOf (submodule { + options = { + table = mkOption { + type = nullOr str; + description = "BIRD table to which session should be connected."; + }; + filterIn = mkOption { + type = str; + default = "accept;"; + description = "BIRD filter definition for received routes."; + }; + filterOut = mkOption { + type = str; + default = "accept;"; + description = "BIRD filter definition for sent routes."; + }; + area = mkOption { + type = attrsOf (submodule { + options = { + interfaces = mkOption { + type = attrsOf (submodule { + options = { + cost = mkOption { + type = int; + default = 10; # 1Gbps + description = "Interface cost (10e9/iface_speed_in_bps)."; + }; + type = mkOption { + type = enum ["bcast" "nbma" "ptp" "ptmp"]; + description = "Interface type (dictates BIRD behaviour)."; + }; + stub = mkOption { + type = bool; + default = false; + description = "Interface is stub (do not HELLO)."; + }; + }; + }); + description = "Interface configuration"; + }; + }; + }); + description = "Area configuration"; + }; + }; + }); + default = {}; + description = "${ospf} configuration"; + }; + + ospfRender = af: n: v: let + v4 = af == "ipv4"; + v6 = af == "ipv6"; + ip = if v4 then "4" else "6"; + name = "ospf_${af}_${n}"; + + interfaces = mapAttrsToList (iface: ifaceConfig: '' + interface "${iface}" { + type ${ifaceConfig.type}; + cost ${toString ifaceConfig.cost}; + ${if ifaceConfig.stub then "stub yes;" else ""} + }; + ''); + areas = mapAttrsToList (area: areaConfig: '' + area ${area} { + ${concatStringsSep "\n" (interfaces areaConfig.interfaces)} + }; + '') v.area; + in '' + filter ${name}_in { + ${v.filterIn} + }; + filter ${name}_out { + ${v.filterOut} + }; + protocol ospf ${if v4 then "v2" else "v3"} ${name} { + ${af} { + table ${v.table}${ip}; + import filter ${name}_in; + export filter ${name}_out; + }; + ${concatStringsSep "\n" areas} + } + ''; + + pipeType = af: with types; mkOption { + type = attrsOf (submodule { + options = { + table = mkOption { + type = nullOr str; + description = "BIRD table to which session should be connected."; + }; + peerTable = mkOption { + type = nullOr str; + description = "BIRD 'remote' table to which session should be connected."; + }; + filterIn = mkOption { + type = str; + default = "accept"; + description = "BIRD filter definition for routes received from peerTable"; + }; + filterOut = mkOption { + type = str; + default = "reject;"; + description = "BIRD filter definition for routes sent to peerTable"; + }; + }; + }); + default = {}; + description = "${pretty} prefixes to pipe from one table to another."; + }; + + pipeRender = af: n: v: let + name = "pipe_${af}_${n}"; + v4 = af == "ipv4"; + v6 = af == "ipv6"; + ip = if v4 then "4" else "6"; + in '' + filter ${name}_in { + ${v.filterIn} + }; + filter ${name}_out { + ${v.filterOut} + }; + protocol pipe ${name} { + table ${v.table}${ip}; + peer table ${v.peerTable}${ip}; + import filter ${name}_in; + export filter ${name}_out; + } + ''; + + bgpSessionsType = af: let + v4 = af == "ipv4"; + v6 = af == "ipv6"; + pretty = if v4 then "IPv4" else "IPv6"; + in with types; mkOption { + type = attrsOf (submodule { + options = { + description = mkOption { + type = str; + description = "Session description (for BIRD)."; + }; + table = mkOption { + type = nullOr str; + description = "BIRD table to which session should be connected."; + }; + local = mkOption { + type = str; + description = "${pretty} address of this router."; + }; + asn = mkOption { + type = int; + description = "ASN of local router - will default to hscloud.routing.asn."; + default = cfg.asn; + }; + prepend = mkOption { + type = int; + default = 0; + description = "How many times to prepend this router's ASN on the link."; + }; + pref = mkOption { + type = int; + default = 100; + description = "Preference (BGP local_pref) for routes from this session."; + }; + direct = mkOption { + type = nullOr bool; + default = null; + }; + filterIn = mkOption { + type = str; + default = "accept;"; + description = "BIRD filter definition for received routes."; + }; + filterOut = mkOption { + type = str; + default = "accept;"; + description = "BIRD filter definition for sent routes."; + }; + neighbors = mkOption { + type = listOf (submodule { + options = { + address = mkOption { + type = str; + description = "${pretty} address of neighbor."; + }; + asn = mkOption { + type = int; + description = "ASN of neighbor."; + }; + password = mkOption { + type = nullOr str; + default = null; + description = "BGP TCP MD5 secret."; + }; + }; + }); + description = "BGP Neighbor configuration"; + }; + }; + }); + default = {}; + description = "BGP Sesions for ${pretty}"; + }; + + bgpSessionRender = af: n: v: let + name = "bgp_${af}_${n}"; + ip = if af == "ipv4" then "4" else "6"; + filters = '' + filter ${name}_in { + if bgp_path.len > 64 then reject; + bgp_local_pref = ${toString v.pref}; + ${v.filterIn} + } + + filter ${name}_out { + ${if v.prepend > 0 then + (concatStringsSep "\n" + (map (_: "bgp_path.prepend(${toString v.asn});") (range 0 (v.prepend - 1))) + ) + else ""} + ${v.filterOut} + } + ''; + peer = ix: peer: '' + protocol bgp ${name}_${toString ix} { + description "${v.description}"; + + ${af} { + table ${v.table}${ip}; + import filter ${name}_in; + export filter ${name}_out; + }; + + local ${v.local} as ${toString v.asn}; + neighbor ${peer.address} as ${toString peer.asn}; + ${if peer.password != null then "password \"${peer.password}\";" else ""} + ${if v.direct == true then "direct;" else ""} + } + ''; + in "${filters}\n${concatStringsSep "\n" (imap1 peer v.neighbors)}"; + + tablesFromProtoAF = + af: p: filter (el: el != null) ( + mapAttrsToList (_: v: "${af} table ${v.table}${if af == "ipv4" then "4" else "6"};") p); + tablesFromProto = p: (tablesFromProtoAF "ipv4" p.v4) ++ (tablesFromProtoAF "ipv6" p.v6); + tables = + unique ( + (tablesFromProto cfg.bgpSessions) ++ + (tablesFromProto cfg.originate) ++ + (tablesFromProto cfg.pipe) ++ + (tablesFromProto cfg.ospf) + # TODO(q3k): also slurp in peer tables from pipes. + ); + tablesRender = '' + ${concatStringsSep "\n" tables} + ''; + tablesProgram = mapAttrsToList (n: _: n) (filterAttrs (n: v: v.program == true) cfg.tables); + tableProgram = + if (length tablesProgram) != 1 then + (abort "exactly one table must be set to be programmed") + else + (head tablesProgram); + +in { + options.hscloud.routing = { + enable = mkEnableOption "declarative routing"; + routerID = mkOption { + type = types.str; + description = '' + Default Router ID for dynamic routing protocols, eg. IPv4 address from + loopback interface. + ''; + }; + asn = mkOption { + type = types.int; + description = "Default ASN for BGP."; + }; + extra = mkOption { + type = types.lines; + description = "Extra configuration lines."; + }; + bgpSessions = { + v4 = bgpSessionsType "ipv4"; + v6 = bgpSessionsType "ipv6"; + }; + originate = { + v4 = originateType "ipv4"; + v6 = originateType "ipv6"; + }; + pipe = { + v4 = pipeType "ipv4"; + v6 = pipeType "ipv6"; + }; + ospf = { + v4 = ospfType "ipv4"; + v6 = ospfType "ipv6"; + }; + tables = mkOption { + type = types.attrsOf (types.submodule { + options = { + program = mkOption { + type = types.bool; + default = false; + description = "This is the primary table programmed in to the kernel."; + }; + programSourceV4 = mkOption { + type = types.nullOr types.str; + default = null; + description = "If set, programmed routes will have source set to this address."; + }; + programSourceV6 = mkOption { + type = types.nullOr types.str; + default = null; + description = "If set, programmed routes will have source set to this address."; + }; + }; + }); + description = "Routing table configuration."; + }; + }; + + config = mkIf cfg.enable { + services.bird2.enable = true; + services.bird2.config = '' + log syslog all; + debug protocols { states, interfaces, events } + + router id ${cfg.routerID}; + + ${cfg.extra} + + ${tablesRender} + + protocol device { + scan time 10; + }; + + protocol kernel kernel_v4 { + scan time 60; + ipv4 { + table ${tableProgram}4; + import none; + export filter { + ${let src = cfg.tables."${tableProgram}".programSourceV4; in if src != null then '' + krt_prefsrc = ${src}; + '' else ""} + accept; + }; + }; + } + protocol kernel kernel_v6 { + scan time 60; + ipv6 { + table ${tableProgram}6; + import none; + export filter { + ${let src = cfg.tables."${tableProgram}".programSourceV6; in if src != null then '' + krt_prefsrc = ${src}; + '' else ""} + accept; + }; + }; + }; + + ${concatStringsSep "\n" (mapAttrsToList (bgpSessionRender "ipv4") cfg.bgpSessions.v4)} + ${concatStringsSep "\n" (mapAttrsToList (bgpSessionRender "ipv6") cfg.bgpSessions.v6)} + ${concatStringsSep "\n" (mapAttrsToList (originateRender "ipv4") cfg.originate.v4)} + ${concatStringsSep "\n" (mapAttrsToList (originateRender "ipv6") cfg.originate.v6)} + ${concatStringsSep "\n" (mapAttrsToList (pipeRender "ipv4") cfg.pipe.v4)} + ${concatStringsSep "\n" (mapAttrsToList (pipeRender "ipv6") cfg.pipe.v6)} + ${concatStringsSep "\n" (mapAttrsToList (ospfRender "ipv4") cfg.ospf.v4)} + ${concatStringsSep "\n" (mapAttrsToList (ospfRender "ipv6") cfg.ospf.v6)} + + ''; + }; +} diff --git a/bgpwtf/machines/modules/rsh-unbound.nix b/bgpwtf/machines/modules/rsh-unbound.nix new file mode 100644 index 00000000..20442fc7 --- /dev/null +++ b/bgpwtf/machines/modules/rsh-unbound.nix @@ -0,0 +1,70 @@ +# Run service that spits out an unbound-compatible blocklist of websites, +# as mandated by polish telecommunications law. +# +# https://sip.lex.pl/akty-prawne/dzu-dziennik-ustaw/gry-hazardowe-17581037/art-15-f +# Dz.U.2019.847 t.j. +# Art. 15f. [Rejestr domen służących do oferowania gier hazardowych niezgodnie z ustawą] +# 5. Przedsiębiorca telekomunikacyjny świadczący usługi dostępu do sieci +# Internet jest obowiązany do: +# 1) nieodpłatnego uniemożliwienia dostępu do stron internetowych +# wykorzystujących nazwy domen internetowych wpisanych do Rejestru +# poprzez ich usunięcie z systemów teleinformatycznych przedsiębiorców +# telekomunikacyjnych, służących do zamiany nazw domen internetowych na +# adresy IP, nie później niż w ciągu 48 godzin od dokonania wpisu do +# Rejestru; +# 2) nieodpłatnego przekierowania połączeń odwołujących się do nazw domen +# internetowych wpisanych do Rejestru do strony internetowej prowadzonej +# przez ministra właściwego do spraw finansów publicznych, zawierającej +# komunikat, skierowany do odbiorców usługi dostępu do Internetu +# obejmujący w szczególności informacje o lokalizacji Rejestru, wpisaniu +# szukanej nazwy domeny internetowej do tego Rejestru, listę podmiotów +# legalnie oferujących gry hazardowe na terytorium Rzeczypospolitej +# Polskiej, a także powiadomienie o grożącej odpowiedzialności +# karno-skarbowej uczestnika gier urządzanych wbrew przepisom ustawy; +# 3) nieodpłatnego umożliwienia dostępu do stron internetowych +# wykorzystujących nazwy domen wykreślonych z Rejestru, nie później niż w +# ciągu 48 godzin od wykreślenia nazwy domeny internetowej z Rejestru. + +{ config, pkgs, lib, ... }: + +with lib; + +let + rshUnbound = pkgs.buildGoModule { + pname = "rsh-unbound"; + version = "20200926"; + src = pkgs.fetchFromGitHub { + owner = "q3k"; + repo = "rsh-unbound"; + rev = "3d98c754adadddfae59387d033aef531f47dee5d"; + sha256 = "1ia33893m1dknw36vss97limlb1d28z5nkrkw6b4mp1igdgqsfcz"; + }; + + vendorSha256 = "1w94g2dwhf47jmds95frb26ypjmis5zhyy85rmd124v0nz3axzhf"; + }; + + cfg = config.hscloud.rsh; + +in { + options.hscloud.rsh = with types; { + enable = mkOption { + type = bool; + default = false; + description = "Enable the RSH-Unboudn service."; + }; + out = mkOption { + type = str; + description = "Output file for generated unbound config."; + }; + }; + + config.systemd.services.rsh = mkIf cfg.enable { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${rshUnbound}/bin/rsh-unbound -output ${cfg.out} -register_endpoint https://hazard.mf.gov.pl/api/Register"; + Restart = "always"; + RestartSec = "60"; + }; + }; +} diff --git a/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix b/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix new file mode 100644 index 00000000..9fe6c17b --- /dev/null +++ b/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix @@ -0,0 +1,49 @@ +-----BEGIN PGP MESSAGE----- + +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 +MZ/1vrhwOkOdDIh5spJJKXwYpBUivr/XLaQ= +=b7js +-----END PGP MESSAGE----- diff --git a/bgpwtf/machines/secrets/cipher/passwords.nix b/bgpwtf/machines/secrets/cipher/passwords.nix new file mode 100644 index 00000000..bf93736f --- /dev/null +++ b/bgpwtf/machines/secrets/cipher/passwords.nix @@ -0,0 +1,40 @@ +-----BEGIN PGP MESSAGE----- + +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 +=d5Gq +-----END PGP MESSAGE----- diff --git a/bgpwtf/machines/secrets/plain/.gitignore b/bgpwtf/machines/secrets/plain/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/bgpwtf/machines/secrets/plain/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bgpwtf/machines/tests/edge01-waw.nix b/bgpwtf/machines/tests/edge01-waw.nix new file mode 100644 index 00000000..e0298d28 --- /dev/null +++ b/bgpwtf/machines/tests/edge01-waw.nix @@ -0,0 +1,288 @@ +# Smoke test edge01.waw in a multi-VM NixOS test. +# +# This brings up three VMs: +# - dut/edge01 +# - bgpspeaker, which simulates bgp upstreams +# - customs, which simulates customs.hackerspace.pl. +# +# We use EoIP to build up virtual ethernet links between the machines, and +# to run VLANs on that. We don't just use plain 'vlans' from NixOS tests as +# we actually want to run 802.1q ourselves from the edge01 config. +# +# Everything else is pretty much straightforward. Bring up everything, ping +# stuff. We don't really test much else than internet routing. +# +# To run this: +# nix-build -A bgpwtf.machines.tests.edge01-waw +# +# To debug this: +# nix-build -A bgpwtf.machines.tests.edge01-waw.driver && result/bin/nixos-test-driver +# >>> start_all() + +{ hscloud, pkgsSrc, pkgs, lib, ... }: + +with lib; + +let + +mkBGPSpeaker = let +in { config, pkgs, ... }: { + networking.hostName = "bgpspeaker"; + virtualisation.memorySize = 1024; + virtualisation.vlans = [ 1 ]; + imports = [ + ../modules/eoip.nix + ]; + + hscloud.eoip.interfaces."nnet" = { + parent = "eth1"; + localV4 = "192.168.1.3"; + remoteV4 = "192.168.1.2"; + id = 100; + }; + networking.interfaces."nnet" = { + virtual = true; + virtualType = "tap"; + }; + networking.vlans = { + "vl-globalmix" = { interface = "nnet"; id = 466; }; + }; + networking.interfaces."vl-globalmix" = { + ipv4.addresses = [{ address = "185.235.70.44"; prefixLength = 31; }]; + ipv6.addresses = [{ address = "2001:67c:778:fd40::b9eb:462c"; prefixLength = 127; }]; + }; + + services.bird2 = { + enable = true; + config = '' + log syslog all; + debug protocols { states, interfaces, events } + router id 185.235.70.44; + + protocol device { + scan time 10; + }; + protocol kernel kernel_v4 { + ipv4 { + import none; + export all; + }; + } + protocol kernel kernel_v6 { + ipv6 { + import none; + export all; + }; + } + ipv4 table globalmix4; + ipv6 table globalmix6; + + protocol pipe pipe_globalmix4 { + table master4; + peer table globalmix4; + import all; + export none; + }; + protocol pipe pipe_globalmix6 { + table master6; + peer table globalmix6; + import all; + export none; + }; + + protocol static static_globalmix_originate_v4 { + ipv4 { + table globalmix4; + import all; + }; + route 8.8.8.0/24 blackhole; + } + protocol static static_globalmix_originate_v6 { + ipv6 { + table globalmix6; + import all; + }; + route 2a00:1450:4016::/48 blackhole; + } + protocol bgp bgp_globalmix_v4 { + ipv4 { + table globalmix4; + export all; + import all; + }; + local 185.235.70.44 as 62081; + neighbor 185.235.70.45 as 204880; + }; + protocol bgp bgp_globalmix_v6 { + ipv6 { + table globalmix6; + export all; + import all; + }; + local 2001:67c:778:fd40::b9eb:462c as 62081; + neighbor 2001:67c:778:fd40::b9eb:462d as 204880; + }; + ''; + }; + networking.firewall.enable = false; + networking.useDHCP = false; + networking.interfaces.lo.ipv4.addresses = [ { address = "8.8.8.1"; prefixLength = 32; } ]; + networking.interfaces.lo.ipv6.addresses = [ { address = "2a00:1450:4016:801::200e"; prefixLength = 128; } ]; + environment.systemPackages = with pkgs; [ + tcpdump htop dstat file + ]; + +}; + + +test = import "${pkgsSrc}/nixos/tests/make-test-python.nix" ({ pkgs, libs, ... }: { + name = "test-edge01-waw-e2e"; + + nodes = { + dut = { config, pkgs, ... }: { + imports = [ + ../edge01.waw.bgp.wtf.nix + ../modules/eoip.nix + ]; + virtualisation.memorySize = 1024; + virtualisation.vlans = [ + 1 2 + ]; + + hscloud.eoip.interfaces = { + "e1-nnet" = { parent = "eth1"; localV4 = "192.168.1.2"; remoteV4 = "192.168.1.3"; id = 100; }; + "e2-customs" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.1"; id = 200; }; + "e3-mgmt" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.111"; id = 300; }; # not connected + "e4-oob" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.112"; id = 400; }; # not connected + "e7-dcsw" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.113"; id = 500; }; # not connected + }; + networking.interfaces = { + "e1-nnet" = { virtual = true; virtualType = "tap"; }; + "e2-customs" = { virtual = true; virtualType = "tap"; }; + "e3-mgmt" = { virtual = true; virtualType = "tap"; }; + "e4-oob" = { virtual = true; virtualType = "tap"; }; + "e7-dcsw" = { virtual = true; virtualType = "tap"; }; + }; + }; + + speaker = mkBGPSpeaker; + + customs = { config, pkgs, ... }: { + imports = [ + ../modules/eoip.nix + ]; + environment.systemPackages = with pkgs; [ + tcpdump htop dstat file dhcpcd + ]; + virtualisation.memorySize = 1024; + virtualisation.vlans = [ + 2 + ]; + networking.firewall.enable = false; + networking.useDHCP = false; + networking.defaultGateway = "185.236.240.4"; + networking.defaultGateway6 = "2a0d:eb00:2137:1::2"; + networking.interfaces."edge" = { + virtual = true; + virtualType = "tap"; + ipv4.addresses = [{ address = "185.236.240.5"; prefixLength = 31; }]; + ipv6.addresses = [{ address = "2a0d:eb00:2137:1::3"; prefixLength = 127; }]; + }; + hscloud.eoip.interfaces."edge" = { + parent = "eth2"; + localV4 = "192.168.2.1"; + remoteV4 = "192.168.2.2"; + id = 200; + }; + networking.bridges."lan".interfaces = []; + networking.interfaces."lan" = { + ipv4.addresses = [{ address = "10.8.1.2"; prefixLength = 23; }]; + ipv6.addresses = [{ address = "2a0d:eb00:4242::1"; prefixLength = 64; }]; + }; + services.bird2 = { + enable = true; + config = '' + log syslog all; + debug protocols { states, interfaces, events } + router id 185.236.240.5; + + protocol device { + scan time 10; + }; + protocol kernel kernel_v4 { + ipv4 { + import none; + export all; + }; + } + protocol kernel kernel_v6 { + ipv6 { + import none; + export all; + }; + } + + protocol ospf v3 ospf_hswaw { + ipv6 { + import all; + export all; + }; + area 0.0.0.0 { + interface "edge" { + cost 10; + type bcast; + }; + interface "lan" { + cost 10; + stub yes; + type bcast; + check link no; + }; + }; + } + ''; + }; + }; + }; + + testScript = '' + start_all() + + edge01.wait_for_unit("bird2.service") + # Wait for BGP to settle. + edge01.wait_until_succeeds("ping 185.235.70.44 -c 1 -w 2") + edge01.wait_until_succeeds("birdc show route for 8.8.8.1 table all | grep via") + edge01.wait_until_succeeds( + "birdc show route for 2a00:1450:4016:801::200e table all | grep via" + ) + edge01.succeed("ping 8.8.8.1 -c 1 -w 2") + + # ping from customs to globalmix must succeed. + customs.succeed("ping 8.8.8.1 -c 1 -w 2") + customs.succeed("ping 2a00:1450:4016:801::200e -c 1 -w 2") + + # edge01 must announce exactly one v4 prefix. + bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v4 | grep unicast") + bgpspeaker.fail( + "birdc show route protocol bgp_globalmix_v4 | grep unicast | grep -v 185.236.240.0/23" + ) + + # edge01 must announce exactly one v6 prefix. + bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v6 | grep unicast") + bgpspeaker.fail( + "birdc show route protocol bgp_globalmix_v6 | grep unicast | grep -v 2a0d:eb00::/32" + ) + + # customer networks must be reachable from globalmix + bgpspeaker.succeed("ping 185.236.240.10 -c 1 -w 2") + bgpspeaker.succeed("ping 2a0d:eb00:8000::1 -c 1 -w 2") + bgpspeaker.succeed("ping 185.236.240.12 -c 1 -w 2") + bgpspeaker.succeed("ping 185.236.240.105 -c 1 -w 2") + bgpspeaker.succeed("ping 2a0d:eb00:8003::1 -c 1 -w 2") + + # dhcp agent must be reachable + customs.succeed("ping 185.236.240.18 -c 1 -w 2") + ''; +}); + +in test { inherit pkgs; inherit (pkgs) libs; } diff --git a/ops/machines.nix b/ops/machines.nix new file mode 100644 index 00000000..c341ec44 --- /dev/null +++ b/ops/machines.nix @@ -0,0 +1,53 @@ +# Top-level file aggregating all machines managed from hscloud. +# +# This allows to have a common attrset of machines that can be deployed +# in the same way. +# +# Currently building/deployment is still done in a half-assed way: +# +# machine=edge01.waw.bgp.wtf +# nix-build -A 'ops.machines."'$machine'"'.toplevel +# +# This spits out a derivation path that correponds to the built config of that +# machine. To deploy it: +# +# d=/nix/store/nkdfoobarbazl0ybhazkmeyaylmaoqcr-nixos-system-edge01-20.09pre-git +# nix-copy-closure --to root@$machine $d +# ssh root@$machine $d/bin/switch-to-configuration +# +# TODO(q3k): merge this with //cluster/clustercfg - this should be unified! + +{ hscloud, pkgs, ... }: + +let + # Stopgap measure to import //cluster/nix machine definitions into new + # //ops/machines infrastructure. + # TODO(q3k): inject defs-cluster-k0.nix / defs-machines.nix content via + # nixos options instead of having module definitions loading it themselves, + # deduplicate list of machines below with defs-machines.nix somehow. + mkClusterMachine = name: pkgs.nixos ({ config, pkgs, ... }: { + # The hostname is used by //cluster/nix machinery to load the appropriate + # config from defs-machines into defs-cluster-k0. + networking.hostName = name; + imports = [ + ../cluster/nix/modules/base.nix + ../cluster/nix/modules/kubernetes.nix + ]; + }); + + mkMachine = paths: pkgs.nixos ({ config, pkgs, ... }: { + imports = paths; + }); + +in { + "bc01n01.hswaw.net" = mkClusterMachine "bc01n01"; + "bc01n02.hswaw.net" = mkClusterMachine "bc01n02"; + "bc01n03.hswaw.net" = mkClusterMachine "bc01n03"; + "dcr01s22.hswaw.net" = mkClusterMachine "dcr01s22"; + "dcr01s24.hswaw.net" = mkClusterMachine "dcr01s24"; + + "edge01.waw.bgp.wtf" = mkMachine [ + ../bgpwtf/machines/edge01.waw.bgp.wtf.nix + ../bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix + ]; +}