nixos-config/config/service/nexus.nix

261 lines
8.7 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fudo.services.nexus;
hostname = config.instance.hostname;
domainName = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domainName}";
siteName = config.fudo.hosts."${hostname}".site;
site = config.fudo.sites."${siteName}";
hostNexusDomainList = host:
let
domainName = config.fudo.hosts."${host}".domain;
domain = config.fudo.domains."${domainName}";
siteName = config.fudo.hosts."${host}".site;
site = config.fudo.sites."${siteName}";
in unique (domain.nexus.domains ++ site.nexus.domains);
isEmpty = lst: lst == [ ];
localNexusDomains = getAttrs (hostNexusDomainList hostname)
(listKeys config.fudo.nexus.domains);
isServer = let
servers = concatMap (domainOpts: domainOpts.servers)
(attrValues config.fudo.nexus.domains);
in elem hostname servers;
isDnsServer = let
servers = concatMap (domainOpts: domainOpts.dns-servers)
(attrValues config.fudo.nexus.domains);
in elem hostname servers;
isDatabase = hostname == domain.postgresql-server;
enableClient = !isEmpty (hostNexusDomainList hostname);
enable = isServer || isDnsServer || isDatabase || enableClient;
servedDomains = filterAttrs (_: domainOpts:
(elem hostname domainOpts.servers)
|| (elem hostname domainOpts.dns-servers)) config.fudo.nexus.domains;
clientHosts = filter (hostname:
!isEmpty
(intersectLists (hostNexusDomainList hostname) (attrNames servedDomains)))
(attrNames config.fudo.hosts);
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
hostFqdn = hostname: "${hostname}.${domainName}";
databaseName = "nexus_dns";
serverUser = "nexus_server";
dnsServerUser = "nexus_dns";
concatMapAttrsToList = f: as: concatLists (mapAttrsToList f as);
genServerRecords = name: domain: servers:
imap0 (i: host: {
inherit host domain;
alias = "${name}${toString i}";
}) servers;
serverList = concatMapAttrsToList
(domain: domainOpts: genServerRecords "nexus-" domain domainOpts.servers)
config.fudo.nexus.domains;
dnsServerList = concatMapAttrsToList
(domain: domainOpts: genServerRecords "ns" domain domainOpts.dns-servers)
config.fudo.nexus.domains;
genSshfp = path:
pkgs.stdenv.mkDerivation {
name = "sshfp-${baseNameOf path}";
buildInputs = with pkgs; [ openssh ];
phases = [ "installPhase" ];
installPhase =
"ssh-keygen -r PLACEHOLDER -f ${path} | sed 's/PLACEHOLDER IN SSHFP //' > $out";
};
in {
options.fudo.services.nexus.dns-server = with types; {
listen-addresses = mkOption {
type = nullOr (listOf str);
description =
"Listen addresses. Defaults to 0.0.0.0 (i.e. all addresses).";
default = null;
};
};
config = mkIf enable {
nexus = {
database = {
database = databaseName;
host = pkgs.lib.getDomainPostgresqlServer domainName;
};
domains = mapAttrs (domain: domainOpts: {
admin = "admin@${domain}";
inherit (domainOpts) gssapi-realm;
trusted-networks = domainOpts.trusted-networks
++ config.instance.local-networks;
# aliases = let
# mkAlias = { host, alias, ... }:
# nameValuePair alias (pkgs.lib.getHostFqdn host);
# domainRecords = filter (record: record.domain == domain) serverList;
# in listToAttrs (map mkAlias domainRecords);
nameservers = let
domainNs = filter (record: record.domain == domain) dnsServerList;
mkNsRecord = { alias, host, ... }:
nameValuePair alias {
ipv4-address = pkgs.lib.getHostIpv4 host;
ipv6-address = pkgs.lib.getHostIpv6 host;
};
in listToAttrs (map mkNsRecord domainNs);
records = let
domainServers = filter (record: record.domain == domain) serverList;
mkHostRecords = { host, alias, ... }:
let
ipv4-address = pkgs.lib.getHostIpv4 host;
ipv6-address = pkgs.lib.getHostIpv6 host;
in (optional (ipv4-address != null) {
name = "${alias}.${domain}";
type = "A";
content = ipv4-address;
}) ++ (optional (ipv6-address != null) {
name = "${alias}.${domain}";
type = "AAAA";
content = ipv6-address;
});
in domainOpts.records ++ (concatMap mkHostRecords domainServers);
}) servedDomains;
client = {
enable = enableClient;
inherit hostname;
verbose = true;
domains = unique (domain.nexus.domains ++ site.nexus.domains);
hmac-key-file = hostSecrets.nexus-key.target-file;
servers = let localDomains = hostNexusDomainList hostname;
in map ({ domain, alias, ... }: "${alias}.${domain}")
(filter ({ domain, ... }: elem domain localDomains) serverList);
ssh-key-files = map (key: key.path) config.services.openssh.hostKeys;
};
server = {
enable = isServer;
verbose = true;
client-keys-file = hostSecrets.nexus-client-keys.target-file;
hostnames = let
hostServerRecords =
filter ({ host, ... }: host == hostname) serverList;
in map ({ domain, alias, ... }: "${alias}.${domain}") hostServerRecords;
database = {
user = serverUser;
password-file = hostSecrets.nexus-server-passwd.target-file;
};
};
dns-server = {
enable = isDnsServer;
enable-dnssec = true;
listen-addresses = mkIf (cfg.dns-server.listen-addresses != null)
cfg.dns-server.listen-addresses;
database = {
user = dnsServerUser;
password-file = hostSecrets.nexus-dns-server-passwd.target-file;
};
};
};
fudo = {
secrets.host-secrets."${hostname}" = {
nexus-client-keys = mkIf isServer {
source-file = let
clientKeyFiles =
filterAttrs (hostname: _: elem hostname clientHosts)
config.fudo.secrets.files.nexus-hmacs;
clientKeys =
mapAttrs (_: filename: readFile filename) clientKeyFiles;
in pkgs.writeText "nexus-client-keys.json"
(builtins.toJSON clientKeys);
target-file = "/run/nexus/client-keys.json";
};
nexus-key = mkIf enableClient {
source-file = config.fudo.secrets.files.nexus-hmacs."${hostname}";
target-file = "/run/nexus/client.key";
};
nexus-server-passwd = mkIf isServer {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-server-passwd"
"nexus-server-${config.instance.build-seed}";
target-file = "/run/nexus/server-db.passwd";
};
postgres-nexus-server-passwd = mkIf isDatabase {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-server-passwd"
"nexus-server-${config.instance.build-seed}";
target-file = "/run/nexus/server-db.passwd";
user = "postgres";
};
nexus-dns-server-passwd = mkIf isDnsServer {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-dns-server-passwd"
"nexus-dns-server-${config.instance.build-seed}";
target-file = "/run/nexus/dns-server-db.passwd";
};
postgres-nexus-dns-server-passwd = mkIf isDatabase {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-dns-server-passwd"
"nexus-dns-server-${config.instance.build-seed}";
target-file = "/run/nexus-db/nexus-dns.passwd";
user = "postgres";
};
};
postgresql = mkIf isDatabase {
required-services = [ "fudo-passwords.target" ];
databases."${databaseName}".users = config.instance.local-admins;
users = {
"${serverUser}" = {
password-file =
hostSecrets.postgres-nexus-server-passwd.target-file;
databases."${databaseName}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
"${dnsServerUser}" = {
password-file =
hostSecrets.postgres-nexus-dns-server-passwd.target-file;
databases."${databaseName}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
};
};
};
};
}