From d2a823f5bca219cfe07120da6311ac8a7366ed2e Mon Sep 17 00:00:00 2001 From: kat witch Date: Thu, 2 Sep 2021 03:12:04 +0100 Subject: [PATCH] An overhaul of the module system. --- config/hosts/rinnosuke/oracle.nix | 175 ------------------ .../home/{default.nix => settings.nix} | 9 +- config/modules/meta/default.nix | 9 - config/modules/meta/network.nix | 3 +- config/modules/nixos/default.nix | 27 --- config/modules/nixos/settings.nix | 13 ++ config/modules/nixos/yggdrasil.nix | 155 ++++++++++++++++ config/profiles/base/home.nix | 2 +- default.nix | 10 +- nix/sources.json | 6 +- overlays/exprs | 2 +- 11 files changed, 181 insertions(+), 230 deletions(-) delete mode 100644 config/hosts/rinnosuke/oracle.nix rename config/modules/home/{default.nix => settings.nix} (75%) delete mode 100644 config/modules/meta/default.nix delete mode 100644 config/modules/nixos/default.nix create mode 100644 config/modules/nixos/settings.nix create mode 100644 config/modules/nixos/yggdrasil.nix diff --git a/config/hosts/rinnosuke/oracle.nix b/config/hosts/rinnosuke/oracle.nix deleted file mode 100644 index fee7672d..00000000 --- a/config/hosts/rinnosuke/oracle.nix +++ /dev/null @@ -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) - )]; - }; -} diff --git a/config/modules/home/default.nix b/config/modules/home/settings.nix similarity index 75% rename from config/modules/home/default.nix rename to config/modules/home/settings.nix index f3bcad66..cb0cb9c9 100644 --- a/config/modules/home/default.nix +++ b/config/modules/home/settings.nix @@ -1,14 +1,9 @@ -{ sources, ... }: - -{ - imports = [ +{ sources, ... }: { + external = [ (import (sources.arcexprs + "/modules")).home-manager (import (sources.katexprs + "/modules")).home (import (sources.impermanence + "/home-manager.nix")) (import sources.anicca).modules.home - ./deploy.nix - ./theme.nix - ./secrets.nix (sources.tf-nix + "/modules/home/secrets.nix") ]; } diff --git a/config/modules/meta/default.nix b/config/modules/meta/default.nix deleted file mode 100644 index 43df4768..00000000 --- a/config/modules/meta/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ ... }: - -{ - imports = [ - ./imports.nix - ./deploy.nix - ./network.nix - ]; -} diff --git a/config/modules/meta/network.nix b/config/modules/meta/network.nix index a9152d6d..b1b1358b 100644 --- a/config/modules/meta/network.nix +++ b/config/modules/meta/network.nix @@ -59,8 +59,7 @@ with lib; nixos = { extraModules = [ "${toString sources.home-manager}/nixos" - ../../modules/nixos - ]; + ] ++ singleton meta.modules.nixos; specialArgs = { inherit (config.network) nodes; inherit sources meta; diff --git a/config/modules/nixos/default.nix b/config/modules/nixos/default.nix deleted file mode 100644 index d85698ae..00000000 --- a/config/modules/nixos/default.nix +++ /dev/null @@ -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; - }; -} diff --git a/config/modules/nixos/settings.nix b/config/modules/nixos/settings.nix new file mode 100644 index 00000000..eba56e59 --- /dev/null +++ b/config/modules/nixos/settings.nix @@ -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" + ]; +} diff --git a/config/modules/nixos/yggdrasil.nix b/config/modules/nixos/yggdrasil.nix new file mode 100644 index 00000000..20402003 --- /dev/null +++ b/config/modules/nixos/yggdrasil.nix @@ -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"; + }); +} diff --git a/config/profiles/base/home.nix b/config/profiles/base/home.nix index f80a2e7b..004b8586 100644 --- a/config/profiles/base/home.nix +++ b/config/profiles/base/home.nix @@ -5,7 +5,7 @@ with lib; { options.home-manager.users = mkOption { type = types.attrsOf (types.submoduleWith { - modules = singleton ../../modules/home; + modules = singleton meta.modules.home; specialArgs = { inherit sources tf meta; nixos = config; diff --git a/default.nix b/default.nix index 897be254..d8205303 100644 --- a/default.nix +++ b/default.nix @@ -32,13 +32,13 @@ let If only one exists, the path for that one is returned. Otherwise a module is generated which contains both import paths. */ - xargNames = lib.unique (lib.folderList ./config [ "trusted" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]); - xarg = lib.mapListToAttrs + xargNames = lib.unique (lib.folderList ./config [ "trusted modules" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]); + xarg = (lib.mapListToAttrs (folder: lib.nameValuePair folder (lib.domainMerge { inherit 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. @@ -63,6 +63,7 @@ let # This is where the meta config is evaluated. eval = lib.evalModules { modules = lib.singleton metaConfig + ++ lib.singleton xarg.modules.meta ++ lib.attrValues (removeAttrs xarg.targets [ "common" ]) ++ (map (host: { @@ -70,8 +71,7 @@ let imports = config.lib.kw.nodeImport host; }; }) - (lib.attrNames xarg.hosts)) - ++ lib.singleton ./config/modules/meta/default.nix; + (lib.attrNames xarg.hosts)); specialArgs = { inherit sources root; diff --git a/nix/sources.json b/nix/sources.json index 6f72260c..df186730 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -89,10 +89,10 @@ "homepage": null, "owner": "kittywitch", "repo": "nixexprs", - "rev": "43c95a469d560b05880da5c9b83139da525f78c3", - "sha256": "1rx2ym7crcd8yh61cfdly9nsy5y83v9bh5rwgz2cvq3s3q0lb7k0", + "rev": "1ba4276349007ac90a9bda6e856752945a2ba342", + "sha256": "0m6v8aykjzvb5phr9kwazkyjp3bbzv6wys6jzvkpqgysl8mxyfhf", "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///archive/.tar.gz" }, "niv": { diff --git a/overlays/exprs b/overlays/exprs index 43c95a46..1ba42763 160000 --- a/overlays/exprs +++ b/overlays/exprs @@ -1 +1 @@ -Subproject commit 43c95a469d560b05880da5c9b83139da525f78c3 +Subproject commit 1ba4276349007ac90a9bda6e856752945a2ba342