304 lines
8.5 KiB
Nix
304 lines
8.5 KiB
Nix
# This module is for use by all C3D2 machines.
|
|
# That includes physical servers, VMs, containers, and personal machines.
|
|
#
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
hqPrefix64 = "fd23:42:c3d2:523";
|
|
# TODO: Is this stable? Is there a better place to specifiy this?
|
|
|
|
server7Ygg = import ../hosts/server7/yggaddr.nix;
|
|
|
|
# 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;
|
|
|
|
toServer7Address = toIpv6Address server7Ygg.prefix64;
|
|
|
|
# toHqPublicAddress = toIpv6Address publicPrefix64;
|
|
|
|
cfg = config.c3d2;
|
|
|
|
in {
|
|
|
|
imports = [ ./users ];
|
|
|
|
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"; };
|
|
|
|
enableBinaryCache = mkOption {
|
|
type = bool;
|
|
default = cfg.isInHq;
|
|
defaultText = literalExample "config.c3d2.isInHq";
|
|
description = "Whether to enable the local Nix binary cache";
|
|
};
|
|
|
|
enableMpdProxy = mkOption {
|
|
type = bool;
|
|
default = false;
|
|
description = "Whether to proxy the local MPD database";
|
|
};
|
|
|
|
yggdrasil.enableGateway = mkEnableOption
|
|
"Whether to join the host to the Yggdrasil network via a gateway";
|
|
};
|
|
|
|
};
|
|
|
|
config = let
|
|
cfg = config.c3d2;
|
|
hostRegistry = import ../host-registry.nix;
|
|
mkIfIsInHq = lib.mkIf cfg.isInHq;
|
|
in {
|
|
# Configuration specific to this machine
|
|
|
|
assertions = [
|
|
{
|
|
assertion = cfg.isInHq -> (config.users.users.root.password == null);
|
|
message = "Root passwords not allowed in HQ";
|
|
}
|
|
{
|
|
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
|
|
}";
|
|
}
|
|
{
|
|
assertion = cfg.hq.enableBinaryCache -> cfg.mapHqHosts;
|
|
message = "mapHqHosts must be enabled for enableBinaryCache";
|
|
}
|
|
{
|
|
assertion = cfg.hq.enableMpdProxy -> cfg.mapHqHosts;
|
|
message = "mapHqHosts must be enabled for enableMpdProxy";
|
|
}
|
|
];
|
|
|
|
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 ];
|
|
}
|
|
{
|
|
name = toServer7Address hostName;
|
|
value = [ "${hostName}.y.c3d2.de" "${hostName}.y" ];
|
|
}
|
|
] ++ 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;
|
|
}] ++ lib.optional (cfg.hq.yggdrasil.enableGateway) {
|
|
address = toServer7Address config.networking.hostName;
|
|
prefixLength = 64;
|
|
};
|
|
ipv6.routes = lib.optional (cfg.hq.yggdrasil.enableGateway) {
|
|
address = "200::";
|
|
options.pref = "low";
|
|
prefixLength = 7;
|
|
via = server7Ygg.prefix64 + "::1";
|
|
};
|
|
};
|
|
});
|
|
|
|
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;
|
|
extraConfig = ''
|
|
FQDNLookup false
|
|
Interval 10
|
|
'';
|
|
buildMinimalPackage = true;
|
|
plugins = {
|
|
logfile = ''
|
|
LogLevel info
|
|
File STDOUT
|
|
'';
|
|
network = ''
|
|
Server "grafana.serv.zentralwerk.dn42" "25826"
|
|
'';
|
|
memory = "";
|
|
processes = "";
|
|
disk = "";
|
|
df = "";
|
|
cpu = "";
|
|
entropy = "";
|
|
load = "";
|
|
swap = "";
|
|
cgroups = "";
|
|
vmem = "";
|
|
interface = "";
|
|
};
|
|
};
|
|
|
|
services.hail = lib.mkIf cfg.enableHail {
|
|
enable = true;
|
|
hydraJobUri =
|
|
"https://hydra.hq.c3d2.de/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 <nixpkgs-unstable> { }).haskellPackages.hail;
|
|
# Only builds > 19.09
|
|
};
|
|
|
|
nix = lib.mkIf
|
|
(cfg.hq.enableBinaryCache && config.networking.hostName != "server7") {
|
|
binaryCaches = [ "https://cache.server7.hq.c3d2.de" ];
|
|
binaryCachePublicKeys = [
|
|
"cache.server7.hq.c3d2.de:x8JLRG26zRZ8ysYZLEkPxuAYuK1VSJi/aMAEIs2Lv+U="
|
|
];
|
|
};
|
|
|
|
services.mpd.extraConfig = lib.mkIf cfg.hq.enableMpdProxy ''
|
|
database {
|
|
plugin "proxy"
|
|
host "mpd-index.hq"
|
|
}
|
|
'';
|
|
|
|
};
|
|
|
|
meta.maintainers = with lib.maintainers; [ ehmry ];
|
|
}
|