# This module is for use by all C3D2 machines. # That includes physical servers, VMs, containers, and personal machines. # { config, lib, ... }: let hqPrefix64 = "fd23:42:c3d2:523"; # TODO: Is this stable? Is there a better place to specifiy this? # Generate a deterministic IPv6 address for a 64 bit prefix # and seed string. Prefix must not contain trailing ':'. toIpv6Address = prefix64: seed: with builtins; let digest = builtins.hashString "sha256" seed; hextets = map (i: substring (4 * i) 4 digest) [ 0 1 2 3 ]; in concatStringsSep ":" ([ prefix64 ] ++ hextets); # Generate a deterministic public IPv6 addresses # for the HQ networking using a seed string. toHqPrivateAddress = toIpv6Address hqPrefix64; /* # Generate a deterministic public IPv6 addresses # for the HQ networking using a seed string. toHqPublicAddress = toIpv6Address publicPrefix64; # Generate a deterministic public IPv6 addresses # for the HQ networking using a seed string. toserver7YggdrasilAddress = toIpv6Address server7YggrasilPrefix64; */ cfg = config.c3d2; in { options.c3d2 = with lib; with lib.types; { isInHq = mkEnableOption "HQ presence"; enableMotd = mkOption { type = bool; default = cfg.isInHq; defaultText = literalExample "config.c3d2.isInHq"; }; mapPublicHosts = mkOption { type = bool; default = false; description = '' Whether to add all external HQ host mappings to /etc/hosts. ''; }; mapHqHosts = mkOption { type = bool; default = cfg.isInHq; description = '' Whether to add all internal HQ host mappings to /etc/hosts. ''; }; enableHail = mkOption { type = bool; default = false; description = "Whether to enable Hail continuous deployment from the local Hydra."; }; hq = { /* externalInterface = mkOption { type = nullOr str; default = null; example = "eth0"; description = '' Configure the given interface name with an external IP address. ''; }; */ interface = mkOption { type = nullOr str; default = null; example = "eth0"; description = '' Configure the given interface name with an internal IP address. ''; }; statistics = { enable = mkEnableOption "statistics collection"; }; }; }; config = let cfg = config.c3d2; hostRegistry = import ../host-registry.nix; mkIfIsInHq = lib.mkIf cfg.isInHq; in { assertions = [{ assertion = let check = hostName: hostName == config.networking.hostName; checkRegistry = list: builtins.any check list; in cfg.isInHq -> checkRegistry hostRegistry.hqLocal; message = "${config.networking.hostName} is not registered in ${ toString ../host-registry.nix }"; }]; networking.defaultGateway = mkIfIsInHq "172.22.99.4"; networking.domain = mkIfIsInHq "hq.c3d2.de"; users.motd = lib.mkIf cfg.enableMotd (builtins.readFile ./motd); networking.hosts = let getHost = hostName: builtins.getAttr hostName hostRegistry.hosts; mapHostsNamesToAttrs = f: list: builtins.listToAttrs (map f list); /* hqPublicHosts = mapHostsNamesToAttrs (hostName: { name = toHqPublicAddress hostName; value = [ "${hostName}.hq.c3d2.de" hostName ]; }) hostRegistry.hqPublic; */ hqLocalHosts = with builtins; let f = hostName: let host = getHost hostName; ip6 = if hasAttr "ip6" host then host.ip6 else toHqPrivateAddress hostName; in [{ name = ip6; value = [ "${hostName}.hq" hostName ]; }] ++ lib.optional (hasAttr "ip4" host) { name = host.ip4; value = [ "${hostName}.hq" hostName ]; }; in listToAttrs (concatLists (map f (attrNames hostRegistry.hosts))); in if cfg.mapHqHosts then hqLocalHosts else { }; networking.interfaces = /* (if cfg.hq.externalInterface == null then { } else { "${cfg.hq.externalInterface}" = { ipv6.addresses = [{ address = toHqPublicAddress config.networking.hostName; prefixLength = 64; }]; }; }) // */ (if cfg.hq.interface == null then { } else { "${cfg.hq.interface}" = { ipv6.addresses = [{ address = toHqPrivateAddress config.networking.hostName; prefixLength = 64; }]; }; }); programs.ssh.knownHosts = with builtins; let hostNames = hostRegistry.hqLocal; intersectKeys = intersectAttrs { publicKey = null; publicKeyFile = null; }; list = map (name: let host = getAttr name hostRegistry.hosts; sshAttrs = intersectKeys host; in if sshAttrs == { } then null else { inherit name; value = let ip6 = if hasAttr "ip6" host then host.ip6 else toHqPrivateAddress name; in { publicKey = null; publicKeyFile = null; hostNames = [ ip6 "${name}.hq.c3d2.de" "${name}.hq" name ]; } // sshAttrs; }) hostNames; keyedHosts = filter (x: x != null) list; in listToAttrs keyedHosts; services.collectd = lib.mkIf cfg.hq.statistics.enable { enable = true; autoLoadPlugin = true; extraConfig = '' HostName "${config.networking.hostName}" FQDNLookup false Interval 10 LoadPlugin network Server "grafana.hq" "25826" ''; }; services.hail = lib.mkIf cfg.enableHail { enable = true; hydraJobUri = let hydra = if cfg.mapHqHosts then "hydra.hq" else "hydra.hq.c3d2.de"; in "https:///${hydra}/job/c3d2/hail/${config.networking.hostName}-activator "; # pad the end so the URL doesn't break when systemshit puts a ; on the end package = (import { }).haskellPackages.hail; # Only builds > 19.09 }; }; meta.maintainers = with lib.maintainers; [ ehmry ]; }