multiple in-housed modules and enabled gc-roots, ...

This commit is contained in:
kat witch 2021-09-08 00:40:24 +01:00
parent 8e7d43cbab
commit 7b079e36d8
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
13 changed files with 516 additions and 34 deletions

View file

@ -23,6 +23,7 @@ let
tfModule
"${toString sources.tf-nix}/modules"
];
shorthandOnlyDefinesConfig = true;
};
in
{
@ -68,10 +69,14 @@ in
};
};
config.tf = mkMerge (singleton
{
({ ... }: {
imports = [
../../targets/common.nix
../../tf-common.nix
];
deploy.gcroot = {
name = mkDefault "kw-${config.name}";
user = mkIf (builtins.getEnv "HOME_USER" != "") (mkDefault (builtins.getEnv "HOME_USER"));
};
deps = {
select.allProviders = true;
enable = true;
@ -97,7 +102,7 @@ in
};
};
continue.envVar = "TF_NIX_CONTINUE_${replaceStrings [ "-" ] [ "_" ] config.name}";
} ++ map (nodeName: mapAttrs (_: mkMerge) meta.network.nodes.${nodeName}.deploy.tf.out.set) config.nodeNames);
}) ++ map (nodeName: mapAttrs (_: mkMerge) meta.network.nodes.${nodeName}.deploy.tf.out.set) config.nodeNames);
});
in
mkOption {

View file

@ -29,7 +29,7 @@ in
};
options.deploy.tf = mkOption {
type = types.submodule {
freeformType = types.attrsOf unmergedValues;
inherit (unmerged) freeformType;
options = {
import = mkOption {

View file

@ -0,0 +1,82 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.network.firewall;
in
{
options.network.firewall = {
public.tcp.ports = mkOption {
type = types.listOf types.port;
default = [ ];
};
public.udp.ports = mkOption {
type = types.listOf types.port;
default = [ ];
};
private.tcp.ports = mkOption {
type = types.listOf types.port;
default = [ ];
};
private.udp.ports = mkOption {
type = types.listOf types.port;
default = [ ];
};
public.tcp.ranges = mkOption {
type = types.listOf (types.attrsOf types.port);
default = [ ];
};
public.udp.ranges = mkOption {
type = types.listOf (types.attrsOf types.port);
default = [ ];
};
private.tcp.ranges = mkOption {
type = types.listOf (types.attrsOf types.port);
default = [ ];
};
private.udp.ranges = mkOption {
type = types.listOf (types.attrsOf types.port);
default = [ ];
};
public.interfaces = mkOption {
type = types.listOf types.str;
description = "Public firewall interfaces";
default = [ ];
};
private.interfaces = mkOption {
type = types.listOf types.str;
description = "Private firewall interfaces";
default = [ ];
};
};
config = {
network.firewall = mkMerge (mapAttrsToList (_: user: user.network.firewall) config.home-manager.users);
networking.firewall.interfaces =
let
fwTypes = {
ports = "Ports";
ranges = "PortRanges";
};
interfaceDef = visibility:
listToAttrs (flatten (mapAttrsToList
(type: typeString:
map
(proto: {
name = "allowed${toUpper proto}${typeString}";
value = cfg.${visibility}.${proto}.${type};
}) [ "tcp" "udp" ])
fwTypes));
interfaces = visibility:
listToAttrs
(map (interface: nameValuePair interface (interfaceDef visibility))
cfg.${visibility}.interfaces);
in
mkMerge (map (visibility: interfaces visibility) [ "public" "private" ]);
};
}

View file

@ -0,0 +1,258 @@
{ config, lib, tf, pkgs, ... }:
with lib;
let
cfg = config.network;
in
{
options.network = {
enable = mkEnableOption "Use kat's network module?";
addresses = mkOption {
type = with types; attrsOf (submodule ({ name, options, config, ... }: {
options = {
enable = mkEnableOption "Is it a member of the ${name} network?";
nixos = {
ipv4 = {
enable = mkOption {
type = types.bool;
default = options.nixos.ipv4.address.isDefined;
};
address = mkOption {
type = types.str;
};
};
ipv6 = {
enable = mkOption {
type = types.bool;
default = options.nixos.ipv6.address.isDefined;
};
address = mkOption {
type = types.str;
};
};
};
tf = {
ipv4 = {
enable = mkOption {
type = types.bool;
default = options.tf.ipv4.address.isDefined;
};
address = mkOption {
type = types.str;
};
};
ipv6 = {
enable = mkOption {
type = types.bool;
default = options.tf.ipv6.address.isDefined;
};
address = mkOption {
type = types.str;
};
};
};
prefix = mkOption {
type = types.nullOr types.str;
};
subdomain = mkOption {
type = types.nullOr types.str;
};
domain = mkOption {
type = types.nullOr types.str;
default = "${config.subdomain}.${cfg.dns.domain}";
};
target = mkOption {
type = types.nullOr types.str;
default = "${config.domain}.";
};
out = {
identifierList = mkOption {
type = types.listOf types.str;
default = optionals config.enable (singleton config.domain ++ config.out.addressList);
};
addressList = mkOption {
type = types.listOf types.str;
default = optionals config.enable (concatMap (i: optional i.enable i.address) [ config.nixos.ipv4 config.nixos.ipv6 ]);
};
};
};
}));
};
extraCerts = mkOption {
type = types.attrsOf types.str;
default = { };
};
privateGateway = mkOption {
type = types.str;
default = "192.168.1.254";
};
tf = {
enable = mkEnableOption "Was the system provisioned by terraform?";
ipv4_attr = mkOption {
type = types.nullOr types.str;
default = null;
};
ipv6_attr = mkOption {
type = types.nullOr types.str;
default = null;
};
};
dns = {
enable = mkEnableOption "Do you want DNS to be semi-managed through this module?";
isRoot = mkEnableOption "Is this system supposed to be the @ for the domain?";
email = mkOption {
type = types.nullOr types.str;
};
zone = mkOption {
type = types.nullOr types.str;
};
domain = mkOption {
type = types.nullOr types.str;
};
};
};
config =
let
networks = cfg.addresses;
networksWithDomains = filterAttrs (_: v: v.enable) networks;
in
mkIf cfg.enable {
lib.kw.virtualHostGen = args: virtualHostGen ({ inherit config; } // args);
network = {
dns = {
domain = builtins.substring 0 ((builtins.stringLength cfg.dns.zone) - 1) cfg.dns.zone;
};
addresses = lib.mkMerge [
(mkIf (!cfg.tf.enable) (genAttrs [ "private" "public" "yggdrasil" "wireguard" ] (network: {
tf = {
ipv4.address = mkIf (cfg.addresses.${network}.nixos.ipv4.enable) cfg.addresses.${network}.nixos.ipv4.address;
ipv6.address = mkIf (cfg.addresses.${network}.nixos.ipv6.enable) cfg.addresses.${network}.nixos.ipv6.address;
};
})))
(mkIf cfg.tf.enable (genAttrs ["yggdrasil" "wireguard" ] (network: {
tf = {
ipv4.address = mkIf (cfg.addresses.${network}.nixos.ipv4.enable) cfg.addresses.${network}.nixos.ipv4.address;
ipv6.address = mkIf (cfg.addresses.${network}.nixos.ipv6.enable) cfg.addresses.${network}.nixos.ipv6.address;
};
})) // {
public = {
tf = {
ipv4.address = mkIf (cfg.tf.ipv4_attr != null) (tf.resources.${config.networking.hostName}.refAttr cfg.tf.ipv4_attr);
ipv6.address = mkIf (cfg.tf.ipv6_attr != null) (tf.resources.${config.networking.hostName}.refAttr cfg.tf.ipv6_attr);
};
nixos = {
ipv4.address = mkIf (tf.state.resources ? ${tf.resources.${config.networking.hostName}.out.reference} && cfg.tf.ipv4_attr != null) (tf.resources.${config.networking.hostName}.importAttr cfg.tf.ipv4_attr);
ipv6.address = mkIf (tf.state.resources ? ${tf.resources.${config.networking.hostName}.out.reference} && cfg.tf.ipv6_attr != null) (tf.resources.${config.networking.hostName}.importAttr cfg.tf.ipv6_attr);
};
};
})
({
private = {
prefix = "int";
subdomain = "${config.networking.hostName}.${cfg.addresses.private.prefix}";
};
yggdrasil = {
enable = cfg.yggdrasil.enable;
prefix = "ygg";
subdomain = "${config.networking.hostName}.${cfg.addresses.yggdrasil.prefix}";
};
public = {
subdomain = config.networking.hostName;
};
})
(mkIf cfg.yggdrasil.enable {
yggdrasil.nixos.ipv6.address = cfg.yggdrasil.address;
})
];
};
services.yggdrasil.package = pkgs.yggdrasil-held;
networking = mkIf cfg.addresses.private.enable {
domain = mkDefault (if cfg.addresses.public.enable then cfg.addresses.domain
else if cfg.addresses.private.enable then "${cfg.addresses.private.prefix}.${cfg.dns.domain}" else "");
defaultGateway = cfg.privateGateway;
};
deploy.tf.dns.records =
let
recordsV4 = mapAttrs'
(n: v:
nameValuePair "node_${n}_${config.networking.hostName}_v4" {
inherit (v.tf.ipv4) enable;
inherit (cfg.dns) zone;
domain = v.subdomain;
a = { inherit (v.tf.ipv4) address; };
})
networksWithDomains;
recordsV6 = mapAttrs'
(n: v:
nameValuePair "node_${n}_${config.networking.hostName}_v6" {
inherit (v.tf.ipv6) enable;
inherit (cfg.dns) zone;
domain = v.subdomain;
aaaa = { inherit (v.tf.ipv6) address; };
})
networksWithDomains;
in
mkMerge (map (record: mkIf cfg.dns.enable record) [
recordsV4
recordsV6
(mkIf cfg.dns.isRoot {
"node_root_${config.networking.hostName}_v4" = {
inherit (cfg.addresses.public) enable;
inherit (cfg.dns) zone;
a = { inherit (cfg.addresses.public.tf.ipv4) address; };
};
"node_root_${config.networking.hostName}_v6" = {
inherit (cfg.addresses.public) enable;
inherit (cfg.dns) zone;
aaaa = { inherit (cfg.addresses.public.tf.ipv6) address; };
};
})
]);
security.acme.certs = mkMerge (map (cert: mkIf cfg.dns.enable cert) [
(mkIf config.services.nginx.enable (mapAttrs'
(n: v:
nameValuePair "${n}_${config.networking.hostName}" {
inherit (v) domain;
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
group = mkDefault "nginx";
})
networksWithDomains))
(mapAttrs'
(n: v:
nameValuePair "${n}" {
domain = v;
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
group = mkDefault "nginx";
})
cfg.extraCerts)
]);
services.nginx.virtualHosts = mkMerge (map (host: mkIf cfg.dns.enable host) [
(mkIf config.services.nginx.enable (mapAttrs'
(n: v:
nameValuePair v.domain {
useACMEHost = "${n}_${config.networking.hostName}";
forceSSL = true;
})
networksWithDomains))
(mapAttrs'
(n: v:
nameValuePair v {
useACMEHost = "${n}";
forceSSL = true;
})
cfg.extraCerts)
]);
_module.args = { inherit (config.lib) kw; };
};
}

View file

@ -0,0 +1,131 @@
{ pkgs, lib, config, modulesPath, ... }:
let
fwcfg = config.networking.firewall;
cfg = config.networking.nftables;
doDocker = config.virtualisation.docker.enable && cfg.generateDockerRules;
mkPorts = cond: ports: ranges: action: let
portStrings = (map (range: "${toString range.from}-${toString range.to}") ranges)
++ (map toString ports);
in lib.optionalString (portStrings != []) ''
${cond} dport { ${lib.concatStringsSep ", " portStrings} } ${action}
'';
ruleset = ''
table inet filter {
chain input {
type filter hook input priority filter
policy ${cfg.inputPolicy}
icmpv6 type { echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, packet-too-big } accept
icmp type echo-request accept
ct state invalid drop
ct state established,related accept
iifname { ${
lib.concatStringsSep "," (["lo"] ++ fwcfg.trustedInterfaces)
} } accept
${mkPorts "tcp" fwcfg.allowedTCPPorts fwcfg.allowedTCPPortRanges "accept"}
${mkPorts "udp" fwcfg.allowedUDPPorts fwcfg.allowedUDPPortRanges "accept"}
${
lib.concatStringsSep "\n" (lib.mapAttrsToList (name: ifcfg:
mkPorts "iifname ${name} tcp" ifcfg.allowedTCPPorts ifcfg.allowedTCPPortRanges "accept"
+ mkPorts "iifname ${name} udp" ifcfg.allowedUDPPorts ifcfg.allowedUDPPortRanges "accept"
) fwcfg.interfaces)
}
# DHCPv6
ip6 daddr fe80::/64 udp dport 546 accept
${cfg.extraInput}
counter
}
chain output {
type filter hook output priority filter
policy ${cfg.outputPolicy}
${cfg.extraOutput}
counter
}
chain forward {
type filter hook forward priority filter
policy ${cfg.forwardPolicy}
${lib.optionalString doDocker ''
oifname docker0 ct state invalid drop
oifname docker0 ct state established,related accept
iifname docker0 accept
''}
${cfg.extraForward}
counter
}
}
${lib.optionalString doDocker ''
table ip nat {
chain docker-postrouting {
type nat hook postrouting priority 10
iifname docker0 masquerade
}
}
''}
${cfg.extraConfig}
'';
in {
options = with lib; {
networking.nftables = {
extraConfig = mkOption {
type = types.lines;
default = "";
};
extraInput = mkOption {
type = types.lines;
default = "";
};
extraOutput = mkOption {
type = types.lines;
default = "";
};
extraForward = mkOption {
type = types.lines;
default = "";
};
inputPolicy = mkOption {
type = types.str;
default = "drop";
};
outputPolicy = mkOption {
type = types.str;
default = "accept";
};
forwardPolicy = mkOption {
type = types.str;
default = "accept";
};
generateDockerRules = mkOption {
type = types.bool;
default = true;
};
};
};
config = lib.mkIf cfg.enable {
networking.firewall.enable = false;
networking.nftables = {
inherit ruleset;
};
virtualisation.docker = lib.mkIf doDocker {
extraOptions = "--iptables=false";
};
};
}

View file

@ -1,15 +0,0 @@
{ config, lib, ... }: with lib; let
cfg = config.network;
wgcfg = config.network.wireguard;
magic = toString wgcfg.magicNumber;
in {
network.addresses.wireguard = {
enable = config.network.wireguard.enable;
nixos = {
ipv4.address = "${wgcfg.prefixV4}.${magic}";
ipv6.address = "${wgcfg.prefixV6}:${magic}";
};
prefix = "wg";
subdomain = "${config.networking.hostName}.${cfg.addresses.wireguard.prefix}";
};
}

View file

@ -0,0 +1,26 @@
{ config, lib, ... }: with lib; let
cfg = config.network;
wgcfg = config.network.wireguard;
magic = toString wgcfg.magicNumber;
in {
network.addresses.wireguard = {
enable = config.network.wireguard.enable;
nixos = {
ipv4.address = "${wgcfg.prefixV4}.${magic}";
ipv6.address = "${wgcfg.prefixV6}:${magic}";
};
prefix = "wg";
subdomain = "${config.networking.hostName}.${cfg.addresses.wireguard.prefix}";
};
network.wireguard = {
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

@ -41,14 +41,6 @@ in {
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

@ -1,6 +1,8 @@
{ config, lib, ... }: with lib;
{
deploy.gcroot.enable = true;
variables.katdns-address = {
value.shellCommand = "bitw get secrets/katdns -f address";
type = "string";

View file

@ -1,16 +1,16 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, ... }: with lib;
{
users.users.kat = {
uid = 1000;
isNormalUser = true;
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCocjQqiDIvzq+Qu3jkf7FXw5piwtvZ1Mihw9cVjdVcsra3U2c9WYtYrA3rS50N3p00oUqQm9z1KUrvHzdE+03ZCrvaGdrtYVsaeoCuuvw7qxTQRbItTAEsfRcZLQ5c1v/57HNYNEsjVrt8VukMPRXWgl+lmzh37dd9w45cCY1QPi+JXQQ/4i9Vc3aWSe4X6PHOEMSBHxepnxm5VNHm4PObGcVbjBf0OkunMeztd1YYA9sEPyEK3b8IHxDl34e5t6NDLCIDz0N/UgzCxSxoz+YJ0feQuZtud/YLkuQcMxW2dSGvnJ0nYy7SA5DkW1oqcy6CGDndHl5StOlJ1IF9aGh0gGkx5SRrV7HOGvapR60RphKrR5zQbFFka99kvSQgOZqSB3CGDEQGHv8dXKXIFlzX78jjWDOBT67vA/M9BK9FS2iNnBF5x6shJ9SU5IK4ySxq8qvN7Us8emkN3pyO8yqgsSOzzJT1JmWUAx0tZWG/BwKcFBHfceAPQl6pwxx28TM3BTBRYdzPJLTkAy48y6iXW6UYdfAPlShy79IYjQtEThTuIiEzdzgYdros0x3PDniuAP0KOKMgbikr0gRa6zahPjf0qqBnHeLB6nHAfaVzI0aNbhOg2bdOueE1FX0x48sjKqjOpjlIfq4WeZp9REr2YHEsoLFOBfgId5P3BPtpBQ== cardno:000612078454"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCocjQqiDIvzq+Qu3jkf7FXw5piwtvZ1Mihw9cVjdVcsra3U2c9WYtYrA3rS50N3p00oUqQm9z1KUrvHzdE+03ZCrvaGdrtYVsaeoCuuvw7qxTQRbItTAEsfRcZLQ5c1v/57HNYNEsjVrt8VukMPRXWgl+lmzh37dd9w45cCY1QPi+JXQQ/4i9Vc3aWSe4X6PHOEMSBHxepnxm5VNHm4PObGcVbjBf0OkunMeztd1YYA9sEPyEK3b8IHxDl34e5t6NDLCIDz0N/UgzCxSxoz+YJ0feQuZtud/YLkuQcMxW2dSGvnJ0nYy7SA5DkW1oqcy6CGDndHl5StOlJ1IF9aGh0gGkx5SRrV7HOGvapR60RphKrR5zQbFFka99kvSQgOZqSB3CGDEQGHv8dXKXIFlzX78jjWDOBT67vA/M9BK9FS2iNnBF5x6shJ9SU5IK4ySxq8qvN7Us8emkN3pyO8yqgsSOzzJT1JmWUAx0tZWG/BwKcFBHfceAPQl6pwxx28TM3BTBRYdzPJLTkAy48y6iXW6UYdfAPlShy79IYjQtEThTuIiEzdzgYdros0x3PDniuAP0KOKMgbikr0gRa6zahPjf0qqBnHeLB6nHAfaVzI0aNbhOg2bdOueE1FX0x48sjKqjOpjlIfq4WeZp9REr2YHEsoLFOBfgId5P3BPtpBQ== yubikey5"
];
shell = pkgs.zsh;
extraGroups = [ "wheel" "video" "systemd-journal" "plugdev" "bird2" ];
hashedPassword =
"$6$i28yOXoo$/WokLdKds5ZHtJHcuyGrH2WaDQQk/2Pj0xRGLgS8UcmY2oMv3fw2j/85PRpsJJwCB2GBRYRK5LlvdTleHd3mB.";
removeSuffix "\n" config.kw.secrets.repo.kat-user.text;
};
systemd.tmpfiles.rules = [