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, ... }:
{
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")
];
}

View file

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

View file

@ -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;

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 {
type = types.attrsOf (types.submoduleWith {
modules = singleton ../../modules/home;
modules = singleton meta.modules.home;
specialArgs = {
inherit sources tf meta;
nixos = config;