An overhaul of the module system.

This commit is contained in:
kat witch 2021-09-02 03:12:04 +01:00
parent 2bf9997813
commit d2a823f5bc
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
11 changed files with 181 additions and 230 deletions

View file

@ -1,175 +0,0 @@
{ config, tf, meta, kw, pkgs, lib, sources, ... }: with lib; let
oci-root = meta.deploy.targets.oci-root.tf;
in
{
deploy.tf =
let
compartment_id = oci-root.resources.oci_kw_compartment.importAttr "id";
inherit (tf.lib.tf) terraformExpr;
in
{
deploy.systems.rinnosuke = {
lustrate = {
enable = true;
connection = tf.resources.rinnosuke.connection.set;
};
connection = {
port = 62954;
};
};
providers.oci = {
inputs = {
tenancy_ocid = oci-root.outputs.oci_tenancy.import;
user_ocid = oci-root.resources.oci_kw_user.importAttr "id";
fingerprint = oci-root.resources.oci_kw_apikey.importAttr "fingerprint";
region = oci-root.outputs.oci_region.import;
private_key_path = oci-root.resources.oci_kw_key_file.importAttr "filename";
};
};
resources = mkMerge [{
cloudinit = {
provider = "cloudinit";
type = "config";
dataSource = true;
inputs = {
part = singleton {
content_type = "text/cloud-config";
content = "#cloud-config\n" + builtins.toJSON {
disable_root = false;
};
};
};
};
availability_domain = {
provider = "oci";
type = "identity_availability_domain";
dataSource = true;
inputs = {
inherit compartment_id;
ad_number = 2;
};
};
generic_image = {
provider = "oci";
type = "core_images";
dataSource = true;
inputs = {
inherit compartment_id;
inherit (tf.resources.rinnosuke.inputs) shape;
operating_system = "Canonical Ubuntu"; # "Oracle Linux"
sort_by = "TIMECREATED";
sort_order = "DESC";
};
};
rinnosuke_vnic = {
provider = "oci";
type = "core_vnic_attachments";
dataSource = true;
inputs = {
inherit compartment_id;
instance_id = tf.resources.rinnosuke.refAttr "id";
};
};
rinnosuke_ipv6 = {
provider = "oci";
type = "core_ipv6";
inputs = {
vnic_id = tf.resources.rinnosuke_vnic.refAttr "vnic_attachments[0].vnic_id";
display_name = config.networking.hostName;
ip_address = terraformExpr ''cidrhost("${oci-root.resources.oci_kw_subnet.importAttr "ipv6cidr_block"}", 7)'';
};
};
rinnosuke = {
provider = "oci";
type = "core_instance";
inputs = {
inherit compartment_id;
extended_metadata = { };
metadata = {
ssh_authorized_keys = concatStringsSep "\n" config.users.users.root.openssh.authorizedKeys.keys;
user_data = tf.resources.cloudinit.refAttr "rendered";
};
shape = "VM.Standard.E2.1.Micro";
shape_config = {
memory_in_gbs = 1;
ocpus = 1;
};
source_details = {
source_type = "image";
source_id = tf.resources.generic_image.refAttr "images[0].id";
boot_volume_size_in_gbs = 50; # min 50GB, up to 200GB free
};
create_vnic_details = [
{
assign_public_ip = true;
subnet_id = oci-root.resources.oci_kw_subnet.importAttr "id";
private_ip = terraformExpr ''cidrhost("${oci-root.resources.oci_kw_subnet.importAttr "cidr_block"}", 3)'';
nsg_ids = [
(tf.resources.firewall_group.refAttr "id")
];
}
];
availability_domain = tf.resources.availability_domain.refAttr "name";
};
lifecycle.ignoreChanges = [
"source_details[0].source_id"
];
connection = {
type = "ssh";
user = "root";
host = tf.lib.tf.terraformSelf "public_ip";
timeout = "5m";
};
};
firewall_group = {
provider = "oci";
type = "core_network_security_group";
inputs = {
display_name = "${config.networking.hostName} firewall group";
inherit compartment_id;
vcn_id = oci-root.resources.oci_vcn.importAttr "id";
};
};
}
(
let
protoValues = {
TCP = 6;
UDP = 17;
};
inherit (config.networking) firewall;
ipv4 = "0.0.0.0/0";
ipv6 = "::/0";
mapPort = source: protocol: port: {
provider = "oci";
type = "core_network_security_group_security_rule";
inputs = {
network_security_group_id = tf.resources.firewall_group.refAttr "id";
inherit protocol source;
direction = "INGRESS";
${if protocol == protoValues.TCP then "tcp_options" else "udp_options"} = {
destination_port_range =
if isAttrs port then {
min = port.from;
max = port.to;
} else {
min = port;
max = port;
};
};
};
};
mapAll = protocol: port: [ (mapPort ipv4 protocol port) (mapPort ipv6 protocol port) ];
mapAllForInterface =
let
protos = [ "TCP" "UDP" ];
types = [ "Ports" "PortRanges" ];
in
interface: concatMap (type: concatMap (proto: (concatMap (port: (mapAll protoValues.${proto}) port) interface."allowed${proto}${type}")) protos) types;
rules = concatMap mapAllForInterface ([ firewall ] ++ map (interface: firewall.interfaces.${interface}) config.network.firewall.public.interfaces);
# TODO: use `count` and index into a fancy json or something?
in
listToAttrs (imap0 (i: rule: nameValuePair "firewall${toString i}" rule) rules)
)];
};
}

View file

@ -1,14 +1,9 @@
{ sources, ... }: { sources, ... }: {
external = [
{
imports = [
(import (sources.arcexprs + "/modules")).home-manager (import (sources.arcexprs + "/modules")).home-manager
(import (sources.katexprs + "/modules")).home (import (sources.katexprs + "/modules")).home
(import (sources.impermanence + "/home-manager.nix")) (import (sources.impermanence + "/home-manager.nix"))
(import sources.anicca).modules.home (import sources.anicca).modules.home
./deploy.nix
./theme.nix
./secrets.nix
(sources.tf-nix + "/modules/home/secrets.nix") (sources.tf-nix + "/modules/home/secrets.nix")
]; ];
} }

View file

@ -1,9 +0,0 @@
{ ... }:
{
imports = [
./imports.nix
./deploy.nix
./network.nix
];
}

View file

@ -59,8 +59,7 @@ with lib;
nixos = { nixos = {
extraModules = [ extraModules = [
"${toString sources.home-manager}/nixos" "${toString sources.home-manager}/nixos"
../../modules/nixos ] ++ singleton meta.modules.nixos;
];
specialArgs = { specialArgs = {
inherit (config.network) nodes; inherit (config.network) nodes;
inherit sources meta; inherit sources meta;

View file

@ -1,27 +0,0 @@
{ meta, sources, lib, ... }:
{
imports =
[
(import (sources.arcexprs + "/modules")).nixos
(import (sources.katexprs + "/modules")).nixos
(import (sources.impermanence + "/nixos.nix"))
(import sources.anicca).modules.nixos
./deploy.nix
./monitoring.nix
./secrets.nix
(sources.tf-nix + "/modules/nixos/secrets.nix")
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
(sources.hexchen + "/modules/network/yggdrasil")
];
options.hexchen.dns = lib.mkOption { };
options.hexchen.deploy = lib.mkOption { };
/*
This maps hosts to network.nodes from the meta config. This is required for hexchen's yggdrasil network module.
*/
config = {
_module.args.hosts = lib.mapAttrs (_: config: { inherit config; }) meta.network.nodes;
};
}

View file

@ -0,0 +1,13 @@
{ sources, ... }: {
external = [
(import (sources.arcexprs + "/modules")).nixos
(import (sources.katexprs + "/modules")).nixos
(import (sources.impermanence + "/nixos.nix"))
(import sources.anicca).modules.nixos
(sources.tf-nix + "/modules/nixos/secrets.nix")
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
];
excludes = [
"oracle"
];
}

View file

@ -0,0 +1,155 @@
{ config, meta, lib, pkgs, ... }:
with lib;
let
cfg = config.network.yggdrasil;
calcAddr = pubkey: lib.readFile (pkgs.runCommandNoCC "calcaddr-${pubkey}" {} ''
echo '{ EncryptionPublicKey: "${pubkey}" }' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -address | tr -d '\n' > $out
'').outPath;
in {
options.network.yggdrasil = {
enable = mkEnableOption "Enable the yggdrasil-based private hexnet";
pubkey = mkOption {
type = types.str;
description = "Public key of this node";
};
address = mkOption {
type = types.str;
description = "Main Yggdrasil address. Set automatically";
default = calcAddr cfg.pubkey;
};
trust = mkOption {
type = types.bool;
description = "Open Firewall completely for the network";
default = false;
};
listen.enable = mkOption {
type = types.bool;
description = "Allow other hosts in the network to connect directly";
default = false;
};
listen.endpoints = mkOption {
type = types.listOf types.str;
description = "Endpoints to listen on";
default = [];
};
dns.enable = mkOption {
type = types.bool;
description = "enable automatic dns record generation";
default = false;
};
dns.zone = mkOption {
type = types.str;
description = "Main zone to insert DNS records into";
default = "lilwit.ch";
};
dns.subdomain = mkOption {
type = types.str;
description = "subdomain to put the records into";
default = "net";
};
tunnel.localV6 = mkOption {
type = types.listOf types.str;
description = "v6 subnets to expose";
default = [];
};
tunnel.localV4 = mkOption {
type = types.listOf types.str;
description = "v4 subnets to expose";
default = [];
};
tunnel.remoteV6 = mkOption {
type = types.attrsOf types.str;
description = "Extra v6 subnets to route";
default = {};
};
tunnel.remoteV4 = mkOption {
type = types.attrsOf types.str;
description = "Extra v4 subnets to route";
default = {};
};
extra.pubkeys = mkOption {
type = types.attrsOf types.str;
description = "Additional hosts to allow into the network. Keys won't be added to definition host.";
default = {};
example = { host = "0000000000000000000000000000000000000000000000000000000000000000"; };
};
extra.addresses = mkOption {
type = types.attrsOf types.str;
internal = true;
default = mapAttrs (_: c: calcAddr c) cfg.extra.pubkeys;
};
extra.localV6 = mkOption {
type = types.listOf types.str;
description = "v6 subnets to expose, but not route";
default = [];
};
extra.localV4 = mkOption {
type = types.listOf types.str;
description = "v4 subnets to expose, but not route";
default = [];
};
};
config = mkIf cfg.enable (let
yggConfigs = filter (
c: c.enable && (cfg.pubkey != c.pubkey)
) (
mapAttrsToList (_: node: node.network.yggdrasil or { enable = false; pubkey = null; }) meta.network.nodes
);
pubkeys = flatten (map (c: [ c.pubkey ] ++ (attrValues c.extra.pubkeys)) yggConfigs);
in {
assertions = [
{
assertion = !(cfg.listen.enable && (cfg.listen.endpoints == []));
message = "Specify network.yggdrasil.listen.endpoints";
}
];
networking.firewall.trustedInterfaces = mkIf cfg.trust [ "yggdrasil" ];
services.yggdrasil = {
enable = true;
persistentKeys = true;
config = {
AllowedEncryptionPublicKeys = pubkeys;
IfName = "yggdrasil";
Listen = cfg.listen.endpoints;
Peers = lib.flatten (map (c: c.listen.endpoints) (filter (c: c.listen.enable) yggConfigs));
SessionFirewall = {
Enable = true;
AllowFromRemote = false;
WhitelistEncryptionPublicKeys = pubkeys;
};
TunnelRouting = let
subnets = v: (
listToAttrs (flatten (map (c: map (net: nameValuePair net c.pubkey) c.tunnel."localV${toString v}") yggConfigs))
) // cfg.tunnel."remoteV${toString v}";
in {
Enable = true;
IPv4LocalSubnets = cfg.tunnel.localV4 ++ cfg.extra.localV4;
IPv6LocalSubnets = cfg.tunnel.localV6 ++ cfg.extra.localV6;
IPv4RemoteSubnets = subnets 4;
IPv6RemoteSubnets = subnets 6;
};
};
};
systemd.services.yggdrasil.postStart = let
yggTun = config.services.yggdrasil.config.TunnelRouting;
addNets = v: nets: concatMapStringsSep "\n" (net: "${pkgs.iproute}/bin/ip -${toString v} route add ${net} dev yggdrasil") (attrNames nets);
in "sleep 1\n" + (concatMapStringsSep "\n" (v: addNets v yggTun."IPv${toString v}RemoteSubnets") [ 4 6 ]);
system.build.yggdrasilTemplate = let
json = builtins.toJSON {
inherit (config.services.yggdrasil.config) Peers SessionFirewall TunnelRouting;
EncryptionPublicKey = "";
EncryptionPrivateKey = "";
SigningPublicKey = "";
SigningPrivateKey = "";
};
in pkgs.runCommandNoCC "yggdrasil-template.json" {}
"echo '${json}' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -normaliseconf > $out";
});
}

View file

@ -5,7 +5,7 @@ with lib;
{ {
options.home-manager.users = mkOption { options.home-manager.users = mkOption {
type = types.attrsOf (types.submoduleWith { type = types.attrsOf (types.submoduleWith {
modules = singleton ../../modules/home; modules = singleton meta.modules.home;
specialArgs = { specialArgs = {
inherit sources tf meta; inherit sources tf meta;
nixos = config; nixos = config;

View file

@ -32,13 +32,13 @@ let
If only one exists, the path for that one is returned. If only one exists, the path for that one is returned.
Otherwise a module is generated which contains both import paths. Otherwise a module is generated which contains both import paths.
*/ */
xargNames = lib.unique (lib.folderList ./config [ "trusted" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]); xargNames = lib.unique (lib.folderList ./config [ "trusted modules" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]);
xarg = lib.mapListToAttrs xarg = (lib.mapListToAttrs
(folder: lib.nameValuePair folder (lib.domainMerge { (folder: lib.nameValuePair folder (lib.domainMerge {
inherit folder; inherit folder;
folderPaths = [ (./config + "/${folder}") (./config/trusted + "/${folder}") ]; folderPaths = [ (./config + "/${folder}") (./config/trusted + "/${folder}") ];
})) }))
xargNames; xargNames) // { modules = lib.recursiveMod { folder = ./config/modules; inherit sources; }; };
/* /*
We provide the runners with this file this way. We also provide our nix args here. We provide the runners with this file this way. We also provide our nix args here.
@ -63,6 +63,7 @@ let
# This is where the meta config is evaluated. # This is where the meta config is evaluated.
eval = lib.evalModules { eval = lib.evalModules {
modules = lib.singleton metaConfig modules = lib.singleton metaConfig
++ lib.singleton xarg.modules.meta
++ lib.attrValues (removeAttrs xarg.targets [ "common" ]) ++ lib.attrValues (removeAttrs xarg.targets [ "common" ])
++ (map ++ (map
(host: { (host: {
@ -70,8 +71,7 @@ let
imports = config.lib.kw.nodeImport host; imports = config.lib.kw.nodeImport host;
}; };
}) })
(lib.attrNames xarg.hosts)) (lib.attrNames xarg.hosts));
++ lib.singleton ./config/modules/meta/default.nix;
specialArgs = { specialArgs = {
inherit sources root; inherit sources root;

View file

@ -89,10 +89,10 @@
"homepage": null, "homepage": null,
"owner": "kittywitch", "owner": "kittywitch",
"repo": "nixexprs", "repo": "nixexprs",
"rev": "43c95a469d560b05880da5c9b83139da525f78c3", "rev": "1ba4276349007ac90a9bda6e856752945a2ba342",
"sha256": "1rx2ym7crcd8yh61cfdly9nsy5y83v9bh5rwgz2cvq3s3q0lb7k0", "sha256": "0m6v8aykjzvb5phr9kwazkyjp3bbzv6wys6jzvkpqgysl8mxyfhf",
"type": "tarball", "type": "tarball",
"url": "https://github.com/kittywitch/nixexprs/archive/43c95a469d560b05880da5c9b83139da525f78c3.tar.gz", "url": "https://github.com/kittywitch/nixexprs/archive/1ba4276349007ac90a9bda6e856752945a2ba342.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz" "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}, },
"niv": { "niv": {

@ -1 +1 @@
Subproject commit 43c95a469d560b05880da5c9b83139da525f78c3 Subproject commit 1ba4276349007ac90a9bda6e856752945a2ba342