modules/{wireguard{,-tf},bird,policyrouting}: Init from hexchen

This commit is contained in:
kat witch 2021-09-07 03:51:37 +01:00
parent 1c700905c3
commit 70d95acddb
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
16 changed files with 459 additions and 6 deletions

View file

@ -7,6 +7,7 @@ with lib;
imports = with meta; [
profiles.hardware.hcloud-imperative
profiles.network
users.kat.server
users.kat.services.weechat
services.filehost

View file

@ -112,6 +112,10 @@ with lib;
};
};
};
wireguard = {
enable = true;
tf.enable = true;
};
yggdrasil = {
enable = true;
pubkey = "d3e488574367056d3ae809b678f799c29ebfd5c7151bb1f4051775b3953e5f52";

View file

@ -2,6 +2,7 @@
imports = with meta; [
profiles.hardware.aarch64
profiles.hardware.oracle.ubuntu
profiles.network
services.nginx
services.keycloak
services.glauth
@ -21,6 +22,13 @@
};
};
network = {
yggdrasil = {
enable = true;
pubkey = "89771aa2f15fce6bbc3548f95be360cf59657d299837b10adf53944b54e8f121";
};
};
services.nginx.virtualHosts =
let
splashy = pkgs.host-splash-site config.networking.hostName;

View file

@ -7,6 +7,7 @@ with lib;
imports = with meta; [
profiles.hardware.eeepc-1015pem
profiles.network
services.kattv
];

View file

@ -1,6 +1,7 @@
{ config, tf, meta, kw, pkgs, lib, sources, ... }: with lib; {
imports = with meta; [
profiles.hardware.oracle.ubuntu
profiles.network
services.knot
services.nginx
];
@ -19,6 +20,13 @@
};
};
network = {
yggdrasil = {
enable = true;
pubkey = "d3db7b089f3cb2d33e18c77b8f9a5a08185798143822b219dbc938aa37d29310";
};
};
services.nginx.virtualHosts =
let
splashy = pkgs.host-splash-site config.networking.hostName;

View file

@ -14,6 +14,7 @@ in
profiles.hardware.ms-7b86
profiles.gui
profiles.vfio
profiles.network
profiles.cross.aarch64
profiles.cross.armv6l
profiles.cross.armv7l

View file

@ -5,7 +5,7 @@
imports = with meta; [
profiles.hardware.raspi
profiles.base
profiles.network
services.dnscrypt-proxy
services.dht22-exporter
./image.nix

View file

@ -8,6 +8,7 @@ with lib;
imports = with meta; [
profiles.hardware.v330-14arr
profiles.gui
profiles.network
users.kat.guiFull
services.nginx
services.restic

View file

@ -0,0 +1,176 @@
{ config, lib, pkgs, ... }:
with lib;
let
bcfg = config.network.bird;
cfg = config.network.bird.ospf;
in
{
options.network.bird = {
routerId = mkOption {
type = types.nullOr types.str;
default = null;
description = "Router ID to use. Must be an IPv4 address.";
};
kernel4Config = mkOption {
type = types.lines;
default = ''
ipv4 {
import none;
export filter {
if source = RTS_STATIC then reject;
accept;
};
};
scan time 15;
'';
};
kernel6Config = mkOption {
type = types.lines;
default = ''
ipv6 {
import none;
export filter {
if source = RTS_STATIC then reject;
accept;
};
};
scan time 15;
'';
};
staticRoutes4 = mkOption {
type = types.listOf types.str;
default = [ ];
};
extraStatic4 = mkOption {
type = types.lines;
default = "";
};
staticRoutes6 = mkOption {
type = types.listOf types.str;
default = [ ];
};
extraStatic6 = mkOption {
type = types.lines;
default = "";
};
};
options.network.bird.ospf = {
enable = mkEnableOption "OSPF-based network routing";
protocols = mkOption {
default = { };
type = types.attrsOf (types.submodule {
options = {
version = mkOption { type = types.enum [ 2 3 ]; default = 2; };
extra = mkOption { type = types.lines; default = ""; };
areas = mkOption {
description = "areas to configure in bird";
default = { };
type = types.attrsOf (types.submodule {
options = {
extra = mkOption { type = types.lines; default = ""; };
networks = mkOption {
description = "Definition of area IP ranges. This is used in summary LSA origination.";
type = types.listOf types.str;
default = [ ];
};
external = mkOption {
description = "Definition of external area IP ranges for NSSAs. This is used for NSSA-LSA translation.";
type = types.listOf types.str;
default = [ ];
};
interfaces = mkOption {
description = "Interfaces to assign to the area";
type = types.attrsOf (types.submodule {
options = {
cost = mkOption { type = types.int; default = 10; };
poll = mkOption { type = types.int; default = 20; };
retransmit = mkOption { type = types.int; default = 5; };
priority = mkOption { type = types.int; default = 1; };
deadCount = mkOption { type = types.int; default = 4; };
type = mkOption {
type = types.enum [
null
"broadcast"
"bcast"
"pointopoint"
"ptp"
"nonbroadcast"
"nbma"
"pointomultipoint"
"ptmp"
];
default = null;
};
extra = mkOption { type = types.lines; default = ""; };
};
});
};
};
});
};
};
});
};
};
config = mkIf cfg.enable {
services.bird2 = {
enable = true;
config = ''
${optionalString (bcfg.routerId != null) "router id ${bcfg.routerId};"}
protocol device {
scan time 10;
}
protocol kernel kernel4 {
${bcfg.kernel4Config}
}
protocol kernel kernel6 {
${bcfg.kernel6Config}
}
protocol static static4 {
ipv4 { import all; export none; };
${concatMapStringsSep "\n" (x: "route ${x};") bcfg.staticRoutes4}
${bcfg.extraStatic4}
}
protocol static static6 {
ipv6 { import all; export none; };
${concatMapStringsSep "\n" (x: "route ${x};") bcfg.staticRoutes6}
${bcfg.extraStatic6}
}
${concatStringsSep "\n" (mapAttrsToList (protoName: proto: ''
protocol ospf v${toString proto.version} ${protoName} {
${concatStringsSep "\n" (mapAttrsToList (areaName: area: ''
area ${areaName} {
${optionalString
(area.networks != [])
"networks { ${concatStringsSep "\n" (map (x: "${x};") area.networks)} };"}
${optionalString
(area.external != [])
"external { ${concatStringsSep "\n" (map (x: "${x};") area.external)} };"}
${concatStringsSep "\n" (mapAttrsToList (ifacePattern: iface: ''
interface "${ifacePattern}" {
cost ${toString iface.cost};
poll ${toString iface.poll};
retransmit ${toString iface.retransmit};
priority ${toString iface.priority};
dead count ${toString iface.deadCount};
${optionalString (iface.type != null) "type ${iface.type};"}
${iface.extra}
};
'') area.interfaces)}
${area.extra}
};
'') proto.areas)}
${proto.extra}
}
'') cfg.protocols)}
'';
};
};
}

View file

@ -0,0 +1,51 @@
{ config, lib, ... }:
with lib;
let
cfg = config.networking.policyrouting;
ruleOpts = { ... }: {
options = {
prio = mkOption {
type = types.int;
};
rule = mkOption {
type = types.str;
};
};
};
in
{
options = {
networking.policyrouting = {
enable = mkEnableOption "Declarative Policy-Routing";
rules = mkOption {
type = with types; listOf (submodule ruleOpts);
default = [ ];
};
rules6 = mkOption {
type = with types; listOf (submodule ruleOpts);
default = [ ];
};
rules4 = mkOption {
type = with types; listOf (submodule ruleOpts);
default = [ ];
};
};
};
config = mkIf cfg.enable {
networking.policyrouting.rules = [
{ rule = "lookup main"; prio = 32000; }
];
networking.localCommands = ''
set -x
ip -6 rule flush
ip -4 rule flush
${concatMapStringsSep "\n" ({ prio, rule }: "ip -6 rule add ${rule} prio ${toString prio}") (cfg.rules ++ cfg.rules6)}
${concatMapStringsSep "\n" ({ prio, rule }: "ip -4 rule add ${rule} prio ${toString prio}") (cfg.rules ++ cfg.rules4)}
'';
};
}

View file

@ -0,0 +1,54 @@
{ config, pkgs, lib, tf, ... }: with lib; let
inherit (tf.lib.tf) terraformSelf;
cfg = config.network.wireguard;
dataDir = toString tf.terraform.dataDir;
in {
options.network.wireguard.tf = {
enable = mkEnableOption "using terraform for wireguard module";
};
config = mkIf config.network.wireguard.tf.enable {
deploy.tf = {
resources = {
"${config.networking.hostName}-wgmesh-gen" = {
provider = "null";
type = "resource";
provisioners = singleton {
local-exec.command = let
wg = "${pkgs.buildPackages.wireguard}/bin/wg";
in "${wg} genkey | tee ${dataDir + "/wg-private-${terraformSelf "id"}"} | ${wg} pubkey > ${dataDir + "/wg-public-${terraformSelf "id"}"}";
};
};
"${config.networking.hostName}-wgmesh-public-key" = {
provider = "local";
type = "file";
dataSource = true;
inputs.filename = dataDir + "/wg-public-${tf.resources."${config.networking.hostName}-wgmesh-gen".refAttr "id"}";
};
};
deploy.systems.${config.networking.hostName}.triggers.switch = {
wg = tf.resources."${config.networking.hostName}-wgmesh-public-key".refAttr "content";
};
};
secrets.files."${config.networking.hostName}-wgmesh-private-key" = rec {
source = dataDir + "/wg-private-${tf.resources."${config.networking.hostName}-wgmesh-gen".refAttr "id"}";
text = source;
};
network.wireguard = {
magicNumber = mkDefault (hexToInt (substring 0 2 (builtins.hashString "sha256" config.networking.hostName)));
keyPath = config.secrets.files."${config.networking.hostName}-wgmesh-private-key".path;
pubkey = let
pubKeyRes = tf.resources."${config.networking.hostName}-wgmesh-public-key";
in mkIf (tf.state.resources ? ${pubKeyRes.out.reference}) (removeSuffix "\n" (pubKeyRes.importAttr "content"));
publicAddress4 = mkDefault (if config.network.addresses.public.nixos.ipv4.enable then
config.network.addresses.public.nixos.ipv4.address
else if config.network.addresses.private.nixos.ipv4.enable then
config.network.addresses.private.nixos.ipv4.address else null);
publicAddress6 = mkDefault (if config.network.addresses.public.nixos.ipv6.enable then
config.network.addresses.public.nixos.ipv6.address
else if config.network.addresses.private.nixos.ipv6.enable then
config.network.addresses.private.nixos.ipv6.address else null);
};
};
}

View file

@ -0,0 +1,85 @@
{ config, lib, pkgs, nodes, name, ... }:
with lib;
let
cfg = config.network.wireguard;
hcfg = _: h: h.network.wireguard;
netHostsSelf = mapAttrs hcfg (filterAttrs (_: x: x.network.wireguard.enable or false) nodes);
netHosts = filterAttrs (n: x: n != name) netHostsSelf;
in
{
options.network.wireguard = {
enable = mkEnableOption "semi-automatic wireguard mesh";
magicNumber = mkOption { type = types.ints.u8; };
prefixV4 = mkOption {
type = types.str;
default = "172.23.1";
};
prefixV6 = mkOption {
type = types.str;
default = "fe80:";
};
keyPath = mkOption {
type = types.str;
default = "/etc/wireguard/mesh";
};
pubkey = mkOption {
type = with types; nullOr str;
default = null;
};
publicAddress4 = mkOption {
type = with types; nullOr str;
default = null;
};
publicAddress6 = mkOption {
type = with types; nullOr str;
default = null;
};
fwmark = mkOption {
type = with types; nullOr ints.u16;
default = null;
};
mtu = mkOption {
type = types.ints.u16;
default = 1500;
};
};
config = mkIf cfg.enable {
networking.wireguard.interfaces = mapAttrs'
(hname: hconf:
let
magicPort = 51820 + hconf.magicNumber + cfg.magicNumber;
iname = "wgmesh-${substring 0 8 hname}";
in
nameValuePair iname {
allowedIPsAsRoutes = false;
privateKeyFile = cfg.keyPath;
ips = [
"${cfg.prefixV4}.${toString cfg.magicNumber}/24"
"${cfg.prefixV6}:${toString cfg.magicNumber}/64"
];
listenPort = magicPort;
peers = optional (hconf.pubkey != null) {
publicKey = hconf.pubkey;
allowedIPs = [ "0.0.0.0/0" "::0/0" ];
endpoint = with hconf; mkIf (publicAddress4 != null || publicAddress6 != null) (
if (publicAddress4 != null)
then "${publicAddress4}:${toString magicPort}"
else "[${publicAddress6}]:${toString magicPort}"
);
persistentKeepalive = with hconf; mkIf (publicAddress4 == null && publicAddress6 == null) 25;
};
postSetup = ''
ip route add ${cfg.prefixV4}.${toString hconf.magicNumber}/32 dev ${iname}
${optionalString (cfg.fwmark != null) "wg set ${iname} fwmark ${toString cfg.fwmark}"}
ip link set ${iname} mtu ${toString cfg.mtu}
'';
}
)
netHosts;
networking.firewall.allowedUDPPorts =
mapAttrsToList (_: hconf: 51820 + hconf.magicNumber + cfg.magicNumber) netHosts;
};
}

View file

@ -1,9 +1,10 @@
{ config, lib, ... }: with lib;
{
networking.nftables.enable = true;
network = {
enable = true;
nftables.enable = true;
dns = {
enable = mkDefault true;
email = "acme@kittywit.ch";

View file

@ -0,0 +1,62 @@
{ config, lib, pkgs, ... }:
{
options.network = with lib; {
routeDefault = mkOption {
default = true;
type = types.bool;
};
};
config = {
networking.firewall.extraCommands = "ip6tables -A INPUT -p 89 -i wgmesh-+ -j ACCEPT";
networking.nftables.extraInput = ''
meta l4proto 89 iifname wgmesh-* accept
'';
networking.policyrouting = {
enable = true;
rules = [
{ rule = "lookup main suppress_prefixlength 0"; prio = 7000; }
{ rule = "lookup 89 suppress_prefixlength 0"; prio = 8000; }
{ rule = "from all fwmark 51820 lookup main"; prio = 9000; }
] ++ (lib.optional config.network.routeDefault { rule = "not from all fwmark 51820 lookup 89"; prio = 9000; });
};
network.wireguard = {
enable = true;
tf.enable = true;
fwmark = 51820;
};
network.bird =
let
mkKernel = version: ''
ipv${toString version} {
import all;
export filter {
if source = RTS_STATIC then reject;
accept;
};
};
kernel table 89;
scan time 15;
'';
mkIgp = version: {
version = 3;
extra = "ipv${toString version} { import all; export all; };";
areas."0".interfaces."wgmesh-*".cost = 100;
};
in
{
routerId = "${config.network.wireguard.prefixV4}.${toString config.network.wireguard.magicNumber}";
kernel4Config = mkKernel 4;
kernel6Config = mkKernel 6;
ospf = {
enable = true;
protocols.igp4 = mkIgp 4;
protocols.igp6 = mkIgp 6;
};
};
};
}

View file

@ -8,7 +8,7 @@
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCocjQqiDIvzq+Qu3jkf7FXw5piwtvZ1Mihw9cVjdVcsra3U2c9WYtYrA3rS50N3p00oUqQm9z1KUrvHzdE+03ZCrvaGdrtYVsaeoCuuvw7qxTQRbItTAEsfRcZLQ5c1v/57HNYNEsjVrt8VukMPRXWgl+lmzh37dd9w45cCY1QPi+JXQQ/4i9Vc3aWSe4X6PHOEMSBHxepnxm5VNHm4PObGcVbjBf0OkunMeztd1YYA9sEPyEK3b8IHxDl34e5t6NDLCIDz0N/UgzCxSxoz+YJ0feQuZtud/YLkuQcMxW2dSGvnJ0nYy7SA5DkW1oqcy6CGDndHl5StOlJ1IF9aGh0gGkx5SRrV7HOGvapR60RphKrR5zQbFFka99kvSQgOZqSB3CGDEQGHv8dXKXIFlzX78jjWDOBT67vA/M9BK9FS2iNnBF5x6shJ9SU5IK4ySxq8qvN7Us8emkN3pyO8yqgsSOzzJT1JmWUAx0tZWG/BwKcFBHfceAPQl6pwxx28TM3BTBRYdzPJLTkAy48y6iXW6UYdfAPlShy79IYjQtEThTuIiEzdzgYdros0x3PDniuAP0KOKMgbikr0gRa6zahPjf0qqBnHeLB6nHAfaVzI0aNbhOg2bdOueE1FX0x48sjKqjOpjlIfq4WeZp9REr2YHEsoLFOBfgId5P3BPtpBQ== cardno:000612078454"
];
shell = pkgs.zsh;
extraGroups = [ "wheel" "video" "systemd-journal" "plugdev" ];
extraGroups = [ "wheel" "video" "systemd-journal" "plugdev" "bird2" ];
hashedPassword =
"$6$i28yOXoo$/WokLdKds5ZHtJHcuyGrH2WaDQQk/2Pj0xRGLgS8UcmY2oMv3fw2j/85PRpsJJwCB2GBRYRK5LlvdTleHd3mB.";
};

View file

@ -171,10 +171,10 @@
"homepage": null,
"owner": "arcnmx",
"repo": "tf-nix",
"rev": "d57b4335aa35781420b7d064d3d77141004c44e9",
"sha256": "0xfl305yaz6xlgaz2jxp4qpibqsyh2hjgibvyxbqqcx0frs9bvya",
"rev": "604582c7e39c652a4e09c26849dff0fb6fed60da",
"sha256": "0a700hci5k2w6y72hnwxgkrd4vfs8y8cj85qi03n80m6r933v2wq",
"type": "tarball",
"url": "https://github.com/arcnmx/tf-nix/archive/d57b4335aa35781420b7d064d3d77141004c44e9.tar.gz",
"url": "https://github.com/arcnmx/tf-nix/archive/604582c7e39c652a4e09c26849dff0fb6fed60da.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}