diff --git a/lib/fudo/dns.nix b/lib/fudo/dns.nix index 60e819c..f714930 100644 --- a/lib/fudo/dns.nix +++ b/lib/fudo/dns.nix @@ -6,7 +6,7 @@ let join-lines = concatStringsSep "\n"; - domainOpts = { domain, ... }: { + domainOpts = { name, ... }: { options = with types; { dnssec = mkOption { type = bool; @@ -14,77 +14,53 @@ let default = true; }; - dmarc-report-address = mkOption { - type = nullOr str; - description = "The email to use to recieve DMARC reports, if any."; - example = "admin-user@domain.com"; - default = null; - }; - zone-definition = mkOption { type = submodule (import ../types/zone-definition.nix); description = "Definition of network zone to be served by local server."; }; - - default-host = mkOption { - type = str; - description = "The host to which the domain should map by default."; - }; - - mx = mkOption { - type = listOf str; - description = "The hosts which act as the domain mail exchange."; - default = []; - }; - - gssapi-realm = mkOption { - type = nullOr str; - description = "The GSSAPI realm of this domain."; - default = null; - }; }; }; - networkHostOpts = import ../types/network-host.nix { inherit lib; }; + # networkHostOpts = import ../types/network-host.nix { inherit lib; }; - hostRecords = hostname: nethost-data: let - # FIXME: RP doesn't work. - # generic-host-records = let - # host-data = if (hasAttr hostname config.fudo.hosts) then config.fudo.hosts.${hostname} else null; - # in - # if (host-data == null) then [] else ( - # (map (sshfp: "${hostname} IN SSHFP ${sshfp}") host-data.ssh-fingerprints) ++ (optional (host-data.rp != null) "${hostname} IN RP ${host-data.rp}") - # ); - sshfp-records = if (hasAttr hostname config.fudo.hosts) then - (map (sshfp: "${hostname} IN SSHFP ${sshfp}") - config.fudo.hosts.${hostname}.ssh-fingerprints) - else []; - a-record = optional (nethost-data.ipv4-address != null) "${hostname} IN A ${nethost-data.ipv4-address}"; - aaaa-record = optional (nethost-data.ipv6-address != null) "${hostname} IN AAAA ${nethost-data.ipv6-address}"; - description-record = optional (nethost-data.description != null) "${hostname} IN TXT \"${nethost-data.description}\""; - in - join-lines (a-record ++ aaaa-record ++ description-record ++ sshfp-records); + # hostRecords = hostname: nethost-data: let + # # FIXME: RP doesn't work. + # # generic-host-records = let + # # host-data = if (hasAttr hostname config.fudo.hosts) then config.fudo.hosts.${hostname} else null; + # # in + # # if (host-data == null) then [] else ( + # # (map (sshfp: "${hostname} IN SSHFP ${sshfp}") host-data.ssh-fingerprints) ++ (optional (host-data.rp != null) "${hostname} IN RP ${host-data.rp}") + # # ); + # sshfp-records = if (hasAttr hostname config.fudo.hosts) then + # (map (sshfp: "${hostname} IN SSHFP ${sshfp}") + # config.fudo.hosts.${hostname}.ssh-fingerprints) + # else []; + # a-record = optional (nethost-data.ipv4-address != null) "${hostname} IN A ${nethost-data.ipv4-address}"; + # aaaa-record = optional (nethost-data.ipv6-address != null) "${hostname} IN AAAA ${nethost-data.ipv6-address}"; + # description-record = optional (nethost-data.description != null) "${hostname} IN TXT \"${nethost-data.description}\""; + # in + # join-lines (a-record ++ aaaa-record ++ description-record ++ sshfp-records); - makeSrvRecords = protocol: type: records: - join-lines (map (record: - "_${type}._${protocol} IN SRV ${toString record.priority} ${ - toString record.weight - } ${toString record.port} ${toString record.host}.") records); + # makeSrvRecords = protocol: type: records: + # join-lines (map (record: + # "_${type}._${protocol} IN SRV ${toString record.priority} ${ + # toString record.weight + # } ${toString record.port} ${toString record.host}.") records); - makeSrvProtocolRecords = protocol: types: - join-lines (mapAttrsToList (makeSrvRecords protocol) types); + # makeSrvProtocolRecords = protocol: types: + # join-lines (mapAttrsToList (makeSrvRecords protocol) types); - cnameRecord = alias: host: "${alias} IN CNAME ${host}"; + # cnameRecord = alias: host: "${alias} IN CNAME ${host}"; - mxRecords = mxs: concatStringsSep "\n" (map (mx: "@ IN MX 10 ${mx}.") mxs); + # mxRecords = mxs: concatStringsSep "\n" (map (mx: "@ IN MX 10 ${mx}.") mxs); - dmarcRecord = dmarc-email: - optionalString (dmarc-email != null) '' - _dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"''; + # dmarcRecord = dmarc-email: + # optionalString (dmarc-email != null) '' + # _dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"''; - nsRecords = domain: ns-hosts: - join-lines - (mapAttrsToList (host: _: "@ IN NS ${host}.${domain}.") ns-hosts); + # nsRecords = domain: ns-hosts: + # join-lines + # (mapAttrsToList (host: _: "@ IN NS ${host}.${domain}.") ns-hosts); in { @@ -92,16 +68,16 @@ in { enable = mkEnableOption "Enable master DNS services."; # FIXME: This should allow for AAAA addresses too... - nameservers = mkOption { - type = attrsOf (submodule networkHostOpts); - description = "Map of domain nameserver FQDNs to IP."; - example = { - "ns1.domain.com" = { - ipv4-address = "1.1.1.1"; - description = "my fancy dns server"; - }; - }; - }; + # nameservers = mkOption { + # type = attrsOf (submodule networkHostOpts); + # description = "Map of domain nameserver FQDNs to IP."; + # example = { + # "ns1.domain.com" = { + # ipv4-address = "1.1.1.1"; + # description = "my fancy dns server"; + # }; + # }; + # }; identity = mkOption { type = str; @@ -143,38 +119,40 @@ in { in nameValuePair "${dom}." { dnssec = dom-cfg.dnssec; - data = '' - $ORIGIN ${dom}. - $TTL 12h + data = pkgs.lib.dns.networkToZone dom dom-cfg; - @ IN SOA ns1.${dom}. hostmaster.${dom}. ( - ${toString config.instance.build-timestamp} - 30m - 2m - 3w - 5m) + # data = '' + # $ORIGIN ${dom}. + # $TTL 12h - ${optionalString (dom-cfg.default-host != null) - "@ IN A ${dom-cfg.default-host}"} + # @ IN SOA ns1.${dom}. hostmaster.${dom}. ( + # ${toString config.instance.build-timestamp} + # 30m + # 2m + # 3w + # 5m) - ${mxRecords dom-cfg.mx} + # ${optionalString (dom-cfg.default-host != null) + # "@ IN A ${dom-cfg.default-host}"} - $TTL 6h + # ${mxRecords dom-cfg.mx} - ${optionalString (dom-cfg.gssapi-realm != null) - ''_kerberos IN TXT "${dom-cfg.gssapi-realm}"''} + # $TTL 6h - ${nsRecords dom cfg.nameservers} - ${join-lines (mapAttrsToList hostRecords cfg.nameservers)} + # ${optionalString (dom-cfg.gssapi-realm != null) + # ''_kerberos IN TXT "${dom-cfg.gssapi-realm}"''} - ${dmarcRecord dom-cfg.dmarc-report-address} + # ${nsRecords dom cfg.nameservers} + # ${join-lines (mapAttrsToList hostRecords cfg.nameservers)} - ${join-lines - (mapAttrsToList makeSrvProtocolRecords net-cfg.srv-records)} - ${join-lines (mapAttrsToList hostRecords net-cfg.hosts)} - ${join-lines (mapAttrsToList cnameRecord net-cfg.aliases)} - ${join-lines net-cfg.verbatim-dns-records} - ''; + # ${dmarcRecord dom-cfg.dmarc-report-address} + + # ${join-lines + # (mapAttrsToList makeSrvProtocolRecords net-cfg.srv-records)} + # ${join-lines (mapAttrsToList hostRecords net-cfg.hosts)} + # ${join-lines (mapAttrsToList cnameRecord net-cfg.aliases)} + # ${join-lines net-cfg.verbatim-dns-records} + # ''; }) cfg.domains; }; }; diff --git a/lib/fudo/jabber.nix b/lib/fudo/jabber.nix index ba25c1b..5cc7e7d 100644 --- a/lib/fudo/jabber.nix +++ b/lib/fudo/jabber.nix @@ -67,6 +67,8 @@ let hosts = attrNames cfg.sites; + acme.auto = false; + # By default, listen on all ips listen = let common = { @@ -89,7 +91,7 @@ let cfg.sites; host_config = - mapAttrs (site: siteOpts: siteOpts.hostname) + mapAttrs (site: siteOpts: siteOpts.site-config) cfg.sites; }; @@ -208,10 +210,11 @@ in { fudo = let host-fqdn = config.instance.host-fqdn; in { - acme.host-domains.${hostname} = mapAttrs' (_: siteOpts: + acme.host-domains.${hostname} = mapAttrs' (site: siteOpts: nameValuePair siteOpts.hostname { extra-domains = - (optional (siteOpts.hostname != host-fqdn) host-fqdn); + (optional (siteOpts.hostname != host-fqdn) host-fqdn) ++ + [ "pubsub.${site}" ]; local-copies.ejabberd = { user = cfg.user; group = cfg.group; diff --git a/lib/informis/chute.nix b/lib/informis/chute.nix index 982a391..0db898b 100644 --- a/lib/informis/chute.nix +++ b/lib/informis/chute.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, ... } @ toplevel: with lib; let @@ -13,7 +13,9 @@ let }; }; - stageOpts = { ... }: { + stageOpts = { name, ... }: let + stage-name = name; + in { options = with types; { currencies = mkOption { type = attrsOf (submodule currencyOpts); @@ -39,25 +41,26 @@ let ''; }; - jabber-jid = mkOption { - type = nullOr str; - description = "Jabber JID as which to connect."; - example = "chute-user@my.domain.org"; - default = null; - }; + jabber = { + jid = mkOption { + type = str; + description = "Jabber JID as which to connect."; + example = "chute-user@my.domain.org"; + default = null; + }; - jabber-target = mkOption { - type = nullOr str; - description = "User to which logs will be sent."; - example = "target@my.domain.org"; - default = null; - }; + resource = mkOption { + type = str; + description = "Jabber resource string."; + default = "${toplevel.config.instance.hostname}-${stage-name}"; + }; - jabber-server = mkOption { - type = nullOr str; - description = "Jabber server."; - example = "my-server.domain.org"; - default = null; + target-jid = mkOption { + type = str; + description = "User to which logs will be sent."; + example = "target@my.domain.org"; + default = null; + }; }; }; }; @@ -65,7 +68,9 @@ let concatMapAttrs = f: attrs: foldr (a: b: a // b) {} (mapAttrsToList f attrs); - chute-job-definition = { stage, currency, stageOpts, currencyOpts }: { + chute-job-definition = { stage, currency, stageOpts, currencyOpts }: let + join-args = concatStringsSep " "; + in { after = [ "network-online.target" ]; wantedBy = [ "chute.target" ]; partOf = [ "chute.target" ]; @@ -73,11 +78,15 @@ let environmentFile = stageOpts.environment-file; execStart = let jabber-string = - optionalString (stageOpts.jabber-jid != null && - stageOpts.jabber-target != null && - stageOpts.jabber-server != null) - "--jabber-jid=${stageOpts.jabber-jid} --target-jid=${stageOpts.jabber-target} --jabber-server=${stageOpts.jabber-server}"; - in "${stageOpts.package}/bin/chute --currency=${currency} --stop-at-percent=${toString currencyOpts.stop-percentile} ${jabber-string}"; + optionalString (stageOpts.jabber != null) + (join-args ["--jabber-jid=${stageOpts.jabber.jid}" + "--target-jid=${stageOpts.jabber.target-jid}" + "--jabber-resource=${stageOpts.jabber.resource}-${currency}"]); + + in join-args ["${stageOpts.package}/bin/chute" + "--currency=${currency}" + "--stop-at-percent=${toString currencyOpts.stop-percentile}" + jabber-string]; privateNetwork = false; addressFamilies = [ "AF_INET" ]; memoryDenyWriteExecute = false; # Needed becuz Clojure diff --git a/lib/lib/dns.nix b/lib/lib/dns.nix index c0979f4..cc77139 100644 --- a/lib/lib/dns.nix +++ b/lib/lib/dns.nix @@ -45,6 +45,48 @@ let }; }; + hostRecords = hostname: nethost-data: let + sshfp-records = optionals (hasAttr hosttname config.fudo.hosts) + (map (sshfp: "${hostname} IN SSHFP ${sshfp}") + config.fudo.hosts.${hostname}.ssh-fingerprints); + a-record = optional (nethost-data.ipv4-address != null) + "${hostname} IN A ${nethost-data.ipv4-address}"; + aaaa-record = optional (nethost-data.ipv6-address != null) + "${hostname} IN AAAA ${nethost-data.ipv6-address}"; + description-record = optional (nethost-data.description != null) + ''${hostname} IN TXT "${nethost-data.description}"''; + in + join-lines (a-record ++ aaaa-record ++ description-record ++ sshfp-records); + + cnameRecord = alias: host: "${alias} IN CNAME ${host}"; + + dmarcRecord = dmarc-email: + optionalString (dmarc-email != null) + ''_dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"''; + + mxRecords = mxs: map (mx: "@ IN MX 10 ${mx}.") mxs; + + nsRecords = domain: ns-hosts: + mapAttrsToList (host: _: "@ IN NS ${host}.${domain}.") ns-hosts; + + flatmapAttrsToList = f: attrs: + foldr (a: b: a ++ b) [] (mapAttrsToList f attrs); + + nsARecords = _: ns-hosts: let + a-record = host: hostOpts: optional (hostOpts.ipv4-address != null) + "${host} IN A ${hostOpts.ipv4-address}"; + aaaa-record = host: hostOpts: optional (hostOpts.ipv6-address != null) + "${host} IN A ${hostOpts.ipv6-address}"; + description-record = host: hostOpts: (hostOpts.description != null) + ''${host} IN TXT "${hostOpts.description}"''; + in flatmapAttrsToList + (host: hostOpts: + (a-record host hostOpts) ++ + (aaaa-record host hostOpts) ++ + (description-record host hostOpts)) + ns-hosts; + + srvRecordPair = domain: protocol: service: record: { "_${service}._${protocol}.${domain}" = "${toString record.priority} ${toString record.weight} ${ @@ -52,6 +94,36 @@ let } ${record.host}."; }; + domain-record = dom: domCfg: '' + $ORIGIN ${dom}. + $TTL ${domCfg.default-ttl} + + ${optionalString (domCfg.default-host != null) + "@ IN A ${domCfg.default-host}"} + + ${mxRecords domCfg.mx} + + ${optionalString (domCfg.gssapi-realm != null) + ''_kerberos IN TXT "${domCfg.gssapi-realm}"''} + + $TTL ${domCfg.host-record-ttl} + + ${nsRecords dom domCfg.nameservers} + + ${nsARecords dom domCfg.nameservers} + + ${dmarcRecord domCfg.dmarc-report-address} + + ${join-lines (mapAttrsToList makeSrvProtocolRecords domCfg.srv-records)} + ${join-lines (mapAttrsToList hostRecords domCfg.hosts)} + ${join-lines (mapAttrsToList cnameRecord domCfg.aliases)} + ${join-lines domCfg.verbatim-dns-records} + + ${join-lines (mapAttrsToList + (subdom: subdomCfg: subdomain-record "${subdom}.${dom}" subdomCfg) + domCfg.subdomains)} + ''; + in rec { srvRecords = with types; attrsOf (attrsOf (listOf (submodule srvRecordOpts))); @@ -67,4 +139,18 @@ in rec { concatMapAttrs (service: records: map (srvRecordPair domain protocol service) records) services) srvRecords); + + networkToZone = dom: domCfg: pkgs.writeText "zone-${dom}" '' + $ORIGIN ${dom} + $TTL ${domCfg.default-ttl} + + @ IN SOA ns1.${dom}. hostmaster.${dom}. ( + ${toString config.instance.build-timestamp} + 30m + 2m + 3w + 5m) + + ${domain-record dom domCfg} + ''; } diff --git a/lib/types/zone-definition.nix b/lib/types/zone-definition.nix index 42f1966..949fbf0 100644 --- a/lib/types/zone-definition.nix +++ b/lib/types/zone-definition.nix @@ -33,76 +33,115 @@ let networkHostOpts = import ./network-host.nix { inherit lib; }; -in { - options = with types; { - hosts = mkOption { - type = attrsOf (submodule networkHostOpts); - description = "Hosts on the local network, with relevant settings."; - example = { - my-host = { - ipv4-address = "192.168.0.1"; - mac-address = "aa:aa:aa:aa:aa"; - }; - }; - default = { }; - }; - - srv-records = mkOption { - type = attrsOf (attrsOf (listOf (submodule srvRecordOpts))); - description = "SRV records for the network."; - example = { - tcp = { - kerberos = { - port = 88; - host = "krb-host.my-domain.com"; + zoneOpts = { ... }: with types; { + options = { + hosts = mkOption { + type = attrsOf (submodule networkHostOpts); + description = "Hosts on the local network, with relevant settings."; + example = { + my-host = { + ipv4-address = "192.168.0.1"; + mac-address = "aa:aa:aa:aa:aa"; }; }; + default = { }; }; - default = { }; - }; - aliases = mkOption { - type = attrsOf str; - default = { }; - description = - "A mapping of host-alias -> hostnames to add to the domain record."; - example = { - mail = "my-mail-host"; - music = "musicall-host.other-domain.com."; + nameservers = mkOption { + type = attrsOf (submodule networkHostOpts); + description = "Map of domain nameservers to host data."; + example = { + "ns1" = { + ipv4-address = "1.1.1.1"; + ipv6-address = "1::1"; + }; + }; + default = {}; }; - }; - verbatim-dns-records = mkOption { - type = listOf str; - description = "Records to be inserted verbatim into the DNS zone."; - example = [ "some-host IN CNAME base-host" ]; - default = [ ]; - }; + srv-records = mkOption { + type = attrsOf (attrsOf (listOf (submodule srvRecordOpts))); + description = "SRV records for the network."; + example = { + tcp = { + kerberos = { + port = 88; + host = "krb-host.my-domain.com"; + }; + }; + }; + default = { }; + }; - dmarc-report-address = mkOption { - type = nullOr str; - description = "The email to use to recieve DMARC reports, if any."; - example = "admin-user@domain.com"; - default = null; - }; + aliases = mkOption { + type = attrsOf str; + default = { }; + description = + "A mapping of host-alias -> hostnames to add to the domain record."; + example = { + mail = "my-mail-host"; + music = "musicall-host.other-domain.com."; + }; + }; - default-host = mkOption { - type = nullOr str; - description = - "IP of the host which will act as the default server for this domain, if any."; - default = null; - }; + verbatim-dns-records = mkOption { + type = listOf str; + description = "Records to be inserted verbatim into the DNS zone."; + example = [ "some-host IN CNAME base-host" ]; + default = [ ]; + }; - mx = mkOption { - type = listOf str; - description = "A list of mail servers serving this domain."; - default = [ ]; - }; + dmarc-report-address = mkOption { + type = nullOr str; + description = "The email to use to recieve DMARC reports, if any."; + example = "admin-user@domain.com"; + default = null; + }; - gssapi-realm = mkOption { - type = nullOr str; - description = "Kerberos GSSAPI realm of the zone."; - default = null; + default-host = mkOption { + type = nullOr str; + description = + "IP of the host which will act as the default server for this domain, if any."; + default = null; + }; + + mx = mkOption { + type = listOf str; + description = "A list of mail servers serving this domain."; + default = [ ]; + }; + + gssapi-realm = mkOption { + type = nullOr str; + description = "Kerberos GSSAPI realm of the zone."; + default = null; + }; + + default-ttl = mkOption { + type = str; + description = "Default time-to-live for this zone."; + default = "3h"; + }; + + host-record-ttl = mkOption { + type = str; + description = "Default time-to-live for records in this zone"; + default = "1h"; + }; + + description = mkOption { + type = str; + description = "Description of this zone."; + }; + + subdomains = mkOption { + type = attrsOf (submodule zoneOpts); + description = "Subdomains of the current zone."; + default = {}; + }; }; }; + +in { + options = zoneOpts; }