Changes over time

This commit is contained in:
niten 2022-02-01 10:50:01 -08:00
parent 125d2d3d57
commit 2048377c3f
23 changed files with 464 additions and 314 deletions

View File

@ -1,10 +1,11 @@
{ pkgs, ... }:
{
ip = import ./lib/lib/ip.nix { inherit pkgs; };
dns = import ./lib/lib/dns.nix { inherit pkgs; };
passwd = import ./lib/lib/passwd.nix { inherit pkgs; };
fs = import ./lib/lib/filesystem.nix { inherit pkgs; };
ip = import ./lib/lib/ip.nix { inherit pkgs; };
lisp = import ./lib/lib/lisp.nix { inherit pkgs; };
network = import ./lib/lib/network.nix { inherit pkgs; };
fs = import ./lib/lib/filesystem.nix { inherit pkgs; };
passwd = import ./lib/lib/passwd.nix { inherit pkgs; };
text = import ./lib/lib/text.nix { inherit pkgs; };
}

View File

@ -118,25 +118,48 @@ in {
# Sigh. Leave it the same as nginx default, so it works whether or not
# nginx feels like helping or not.
default = "/var/lib/acme/acme-challenge";
# default = "/run/acme-challenge";
};
};
config = {
security.acme.certs = mapAttrs (domain: domainOpts: {
email = domainOpts.admin-email;
webroot = cfg.challenge-path;
extraDomainNames = domainOpts.extra-domains;
# email = domainOpts.admin-email;
# webroot = cfg.challenge-path;
# group = "nginx";
# extraDomainNames = domainOpts.extra-domains;
}) localDomains;
# Assume that if we're acquiring SSL certs, we have a real IP for the
# host. nginx must have an acme dir for security.acme to work.
services.nginx = mkIf hasLocalDomains {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;
virtualHosts = let
server-path = "/.well-known/acme-challenge";
in (mapAttrs (domain: domainOpts: {
# THIS IS A HACK. Getting redundant paths. So if {domain} is configured
# somewhere else, assume ACME is already set.
# locations.${server-path} = mkIf (! (hasAttr domain config.services.nginx.virtualHosts)) {
# root = cfg.challenge-path;
# extraConfig = "auth_basic off;";
# };
enableACME = true;
forceSSL = true;
serverAliases = domainOpts.extra-domains;
}) localDomains) // {
"default" = {
serverName = "_";
default = true;
locations = {
${server-path} = {
root = cfg.challenge-path;
extraConfig = "auth_basic off;";
};
"/".return = "403 Forbidden";
};
};
};
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
@ -170,7 +193,17 @@ in {
if (copyOpts.group != null) then
"${copyOpts.user}:${copyOpts.group}"
else copyOpts.user;
dirs = unique [
(dirOf copyOpts.certificate)
(dirOf copyOpts.full-certificate)
(dirOf copyOpts.chain)
(dirOf copyOpts.private-key)
];
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${copy}-certs.sh" ''
${concatStringsSep "\n" (map (dir: ''
mkdir -p ${dir}
chown ${owners} ${dir}
'') dirs)}
cp ${source}/cert.pem ${copyOpts.certificate}
chmod 0444 ${copyOpts.certificate}
chown ${owners} ${copyOpts.certificate}

View File

@ -32,7 +32,6 @@ in {
domain = mkOption {
type = types.str;
description = "Domain under which this host is registered.";
default = "fudo.link";
};
server = mkOption {

View File

@ -2,9 +2,12 @@
with lib; {
imports = [
./backplane-service/dns.nix
./acme-certs.nix
./adguard-dns-proxy.nix
./authentication.nix
./backplane
./backplane.nix
./chat.nix
./client/dns.nix
./deploy.nix
@ -31,6 +34,7 @@ with lib; {
./nsd.nix
./password.nix
./postgres.nix
./powerdns.nix
./prometheus.nix
./secrets.nix
./secure-dns-proxy.nix

View File

@ -48,12 +48,54 @@ let
default = "admin@${domain}";
};
gssapi-realm = mkOption {
grafana-hosts = mkOption {
type = listOf str;
description = "List of hosts acting as Grafana metric analyzers. Requires prometheus hosts as well.";
default = [];
};
log-aggregator = mkOption {
type = nullOr str;
description = "GSSAPI (i.e. Kerberos) realm of this domain.";
description = "Host which will accept incoming log pushes.";
default = null;
};
postgresql-server = mkOption {
type = nullOr str;
description = "Hostname acting as the local PostgreSQL server.";
default = null;
};
backplane = mkOption {
type = nullOr (submodule {
options = {
nameserver = mkOption {
type = nullOr str;
description = "Host acting as backplane dynamic DNS server.";
default = null;
};
dns-service = mkOption {
type = nullOr str;
description = "DNS backplane service host.";
default = null;
};
domain = mkOption {
type = str;
description = "Domain name of the dynamic zone served by this server.";
};
};
});
description = "Backplane configuration.";
default = null;
};
gssapi-realm = mkOption {
type = str;
description = "GSSAPI (i.e. Kerberos) realm of this domain.";
};
kerberos-master = mkOption {
type = nullOr str;
description = "Hostname of the Kerberos master server for the domain, if applicable.";
@ -72,6 +114,12 @@ let
default = [];
};
prometheus-hosts = mkOption {
type = listOf str;
description = "List of hosts acting aas prometheus metric scrapers for hosts in this network.";
default = [];
};
primary-nameserver = mkOption {
type = nullOr str;
description = "Hostname of the primary nameserver for this domain.";

View File

@ -4,14 +4,14 @@
with lib;
let
cfg = config.fudo.grafana;
cfg = config.fudo.metrics.grafana;
hostname = config.instance.hostname;
domain-name = config.fudo.hosts.${hostname}.domain;
in {
options.fudo.grafana = with types; {
options.fudo.metrics.grafana = with types; {
enable = mkEnableOption "Fudo Metrics Display Service";
hostname = mkOption {
@ -88,40 +88,42 @@ in {
description = "Directory at which to store Grafana state data.";
default = "/var/lib/grafana";
};
private-network = mkEnableOption "Network is private, no SSL.";
};
config = mkIf cfg.enable {
services.nginx = {
systemd.tmpfiles.rules = let
grafana-user = config.systemd.services.grafana.serviceConfig.User;
in [
"d ${cfg.state-directory} 0700 ${grafana-user} - - -"
];
services = {
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${cfg.hostname}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
enableACME = ! cfg.private-network;
forceSSL = ! cfg.private-network;
locations."/".proxyPass = "http://127.0.0.1:3000";
};
};
};
services.grafana = {
grafana = {
enable = true;
addr = "127.0.0.1";
protocol = "http";
port = 3000;
domain = cfg.hostname;
rootUrl = "https://${cfg.hostname}/";
rootUrl = let
scheme = if cfg.private-network then "http" else "https";
in "${scheme}://${cfg.hostname}/";
dataDir = cfg.state-directory;
security = {
@ -132,9 +134,9 @@ in {
smtp = {
enable = true;
fromAddress = "metrics@fudo.org";
host = "mail.fudo.org:25";
user = cfg.smtp-username;
passwordFile = cfg.smtp-password-file;
host = "${cfg.smtp.hostname}:25";
user = cfg.smtp.username;
passwordFile = cfg.smtp.password-file;
};
database = {
@ -145,15 +147,16 @@ in {
type = "postgres";
};
provision.datasources = [
{
provision.datasources = imap0 (i: host: {
editable = false;
isDefault = true;
name = cfg.prometheus-host;
isDefault = (i == 0);
name = builtins.trace "PROMETHEUS-HOST: ${host}" host;
type = "prometheus";
url = "https://${cfg.prometheus-host}/";
}
];
url = let
scheme = if private-network then "http" else "https";
in "${scheme}://${host}/";
}) cfg.prometheus-hosts;
};
};
};
}

View File

@ -82,7 +82,7 @@ in {
packages = map (p: "${p.name}")
config.environment.systemPackages;
sorted-unique = sort lessThan (unique packages);
in concatStringsSep "\n" sorted-unique;
in "${concatStringsSep "\n" sorted-unique}\n";
};
systemPackages = with pkgs;

View File

@ -32,8 +32,9 @@ in {
config = {
boot = mkIf (initrd-cfg != null) {
kernelParams = let
site = config.fudo.sites.${config.instance.local-site};
site-gateway = site.gateway-v4;
site-name = config.instance.local-site;
site = config.fudo.sites.${site-name};
site-gateway = pkgs.lib.network.site-gateway config site-name;
netmask =
pkgs.lib.ip.maskFromV32Network site.network;
in [

View File

@ -337,11 +337,15 @@ in {
users = {
users.${cfg.user} = {
isSystemUser = true;
uid = 88;
home = state-directory;
group = cfg.group;
};
groups.${cfg.group} = { members = [ cfg.user ]; };
groups.${cfg.group} = {
gid = 88;
members = [ cfg.user ];
};
};
krb5 = {

View File

@ -218,6 +218,15 @@ in {
config = mkIf cfg.enable {
users = {
users.openldap = {
uid = 389;
};
groups.openldap = {
gid = 389;
};
};
environment = {
etc = {
"openldap/sasl2/slapd.conf" = mkIf (cfg.kerberos-keytab != null) {

View File

@ -31,7 +31,13 @@ in {
dns-listen-ips = mkOption {
type = listOf str;
description = "A list of IPs on which to server DNS queries.";
description = "A list of IPv4 addresses on which to server DNS queries.";
};
dns-listen-ipv6s = mkOption {
type = listOf str;
description = "A list of IPv6 addresses on which to server DNS queries.";
default = [ ];
};
gateway = mkOption {
@ -61,10 +67,22 @@ in {
default = false;
};
recursive-resolver = mkOption {
# recursive-resolver = mkOption {
# type = str;
# description = "DNS nameserver to use for recursive resolution.";
# default = "1.1.1.1 port 53";
# };
recursive-resolver = {
host = mkOption {
type = str;
description = "DNS nameserver to use for recursive resolution.";
default = "1.1.1.1 port 53";
description = "DNS server host or (preferably) IP.";
};
port = mkOption {
type = port;
description = "Remote host port for DNS queries.";
default = 53;
};
};
search-domains = mkOption {
@ -161,18 +179,22 @@ in {
'';
};
ipToBlock = ip:
filterRedundantIps = official-hosts: hosts: let
host-by-ip = groupBy (hostOpts: hostOpts.ipv4-address) hosts;
in filter (hostOpts:
if (length (getAttr hostOpts.ipv4-address host-by-ip) == 1) then
true
else elem hostOpts.hostname official-hosts) hosts;
ipTo24Block = ip:
concatStringsSep "." (reverseList (take 3 (splitString "." ip)));
compactHosts =
mapAttrsToList (host: data: data // { host = host; }) zone.hosts;
hostsByBlock =
groupBy (host-data: ipToBlock host-data.ipv4-address) compactHosts;
hostsByBlock = official-hosts:
groupBy (host-data: ipTo24Block host-data.ipv4-address)
(filterRedundantIps official-hosts (attrValues zone.hosts));
hostPtrRecord = host-data:
"${
last (splitString "." host-data.ipv4-address)
} IN PTR ${host-data.host}.${cfg.domain}.";
"${last (splitString "." host-data.ipv4-address)} IN PTR ${host-data.hostname}.${cfg.domain}.";
blockZones = mapAttrsToList blockHostsToZone hostsByBlock;
blockZones = official-hosts:
mapAttrsToList blockHostsToZone (hostsByBlock official-hosts);
hostARecord = host: data: "${host} IN A ${data.ipv4-address}";
hostSshFpRecords = host: data:
@ -189,13 +211,21 @@ in {
known-hosts = config.fudo.hosts;
domain-name = config.instance.local-domain;
domain-hosts =
attrNames
(filterAttrs (_: hostOpts:
hostOpts.domain == domain-name)
config.fudo.hosts);
in {
enable = true;
cacheNetworks = [ cfg.network "localhost" "localnets" ];
forwarders = [ cfg.recursive-resolver ];
forwarders = [ "${cfg.recursive-resolver.host} port ${toString cfg.recursive-resolver.port}" ];
listenOn = cfg.dns-listen-ips;
listenOnIpv6 = cfg.dns-listen-ipv6s;
extraOptions = concatStringsSep "\n" [
"dnssec-enable yes;"
"dnssec-validation yes;"
"auth-nxdomain no;"
"recursion yes;"
@ -204,36 +234,44 @@ in {
zones = [{
master = true;
name = cfg.domain;
file = pkgs.writeText "${cfg.domain}-zone" ''
@ IN SOA ns1.${cfg.domain}. hostmaster.${cfg.domain}. (
${toString config.instance.build-timestamp}
5m
2m
6w
5m)
file = let
zone-data = pkgs.lib.dns.zoneToZonefile
config.instance.build-timestamp
cfg.domain
zone;
in pkgs.writeText "zone-${cfg.domain}" zone-data;
# file = pkgs.writeText "${cfg.domain}-zone" ''
# @ IN SOA ns1.${cfg.domain}. hostmaster.${cfg.domain}. (
# ${toString config.instance.build-timestamp}
# 5m
# 2m
# 6w
# 5m)
$TTL 1h
# $TTL 1h
@ IN NS ns1.${cfg.domain}.
# @ IN NS ns1.${cfg.domain}.
$ORIGIN ${cfg.domain}.
# $ORIGIN ${cfg.domain}.
$TTL 30m
# $TTL 30m
${optionalString (zone.gssapi-realm != null)
''_kerberos IN TXT "${zone.gssapi-realm}"''}
# ${optionalString (zone.gssapi-realm != null)
# ''_kerberos IN TXT "${zone.gssapi-realm}"''}
${join-lines
(imap1 (i: server-ip: "ns${toString i} IN A ${server-ip}")
cfg.dns-servers)}
${join-lines (mapAttrsToList hostARecord zone.hosts)}
${join-lines (mapAttrsToList hostSshFpRecords zone.hosts)}
${join-lines (mapAttrsToList cnameRecord zone.aliases)}
${join-lines zone.verbatim-dns-records}
${pkgs.lib.dns.srvRecordsToBindZone zone.srv-records}
${join-lines cfg.extra-records}
'';
}] ++ blockZones;
# ${join-lines
# (imap1 (i: server-ip: "ns${toString i} IN A ${server-ip}")
# cfg.dns-servers)}
# ${join-lines (mapAttrsToList hostARecord zone.hosts)}
# ${join-lines (mapAttrsToList hostSshFpRecords zone.hosts)}
# ${join-lines (mapAttrsToList cnameRecord zone.aliases)}
# ${join-lines zone.verbatim-dns-records}
# ${pkgs.lib.dns.srvRecordsToBindZone zone.srv-records}
# ${join-lines cfg.extra-records}
# '';
}] ++ (optionals
cfg.enable-reverse-mappings
(blockZones domain-hosts));
};
};
}

View File

@ -4,14 +4,14 @@ with lib;
let
inherit (lib.strings) concatStringsSep;
cfg = config.fudo.node-exporter;
cfg = config.fudo.metrics.node-exporter;
allow-network = network: "allow ${network};";
local-networks = config.instance.local-networks;
in {
options.fudo.node-exporter = with types; {
options.fudo.metrics.node-exporter = with types; {
enable = mkEnableOption "Enable a Prometheus node exporter with some reasonable settings.";
hostname = mkOption {
@ -24,6 +24,8 @@ in {
description = "User as which to run the node exporter job.";
default = "node-exporter";
};
private-network = mkEnableOption "Network is private.";
};
config = mkIf cfg.enable {
@ -45,10 +47,12 @@ in {
# list of trusted networks, with SSL protection.
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts."${cfg.hostname}" = {
enableACME = true;
forceSSL = true;
enableACME = ! cfg.private-network;
forceSSL = ! cfg.private-network;
locations."/metrics/node" = {
extraConfig = ''
@ -57,7 +61,6 @@ in {
deny all;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
'';
proxyPass = "http://127.0.0.1:9100/metrics";

View File

@ -105,16 +105,21 @@ let
nameValuePair "DATABASE ${database}" databaseOpts.access) databases;
makeEntry = nw:
"host all all ${nw} gss include_realm=0 krb_realm=${gssapi-realm}";
"hostssl all all ${nw} gss include_realm=0 krb_realm=${gssapi-realm}";
makeNetworksEntry = networks: join-lines (map makeEntry networks);
makeLocalUserPasswordEntries = users:
join-lines (mapAttrsToList (user: opts:
makeLocalUserPasswordEntries = users: networks: let
network-entries = user: db:
join-lines
(map (network: "hostssl ${db} ${user} ${network} md5")
networks);
in join-lines (mapAttrsToList (user: opts:
join-lines (map (db: ''
local ${db} ${user} md5
host ${db} ${user} 127.0.0.1/16 md5
host ${db} ${user} ::1/128 md5
${network-entries user db}
'') (attrNames opts.databases))) (filterPasswordedUsers users));
userTableAccessSql = user: entity: access:
@ -134,18 +139,21 @@ in {
enable = mkEnableOption "Fudo PostgreSQL Server";
ssl-private-key = mkOption {
type = str;
type = nullOr str;
description = "Location of the server SSL private key.";
default = null;
};
ssl-certificate = mkOption {
type = str;
type = nullOr str;
description = "Location of the server SSL certificate.";
default = null;
};
keytab = mkOption {
type = str;
type = nullOr str;
description = "Location of the server Kerberos keytab.";
default = null;
};
local-networks = mkOption {
@ -224,32 +232,9 @@ in {
config = mkIf cfg.enable {
environment = {
systemPackages = with pkgs; [ postgresql_11_gssapi ];
networking.firewall.allowedTCPPorts = [ 5432 ];
# etc = {
# "postgresql/private/privkey.pem" = {
# mode = "0400";
# user = "postgres";
# group = "postgres";
# source = cfg.ssl-private-key;
# };
# "postgresql/cert.pem" = {
# mode = "0444";
# user = "postgres";
# group = "postgres";
# source = cfg.ssl-certificate;
# };
# "postgresql/private/postgres.keytab" = {
# mode = "0400";
# user = "postgres";
# group = "postgres";
# source = cfg.keytab;
# };
# };
};
environment.systemPackages = with pkgs; [ postgresql_11_gssapi ];
users.groups = {
${cfg.socket-group} = { members = [ "postgres" ] ++ cfg.local-users; };
@ -269,12 +254,14 @@ in {
ensurePermissions = { "DATABASE ${database}" = "ALL PRIVILEGES"; };
}) opts.users)) cfg.databases)));
settings = {
krb_server_keyfile = cfg.keytab;
settings = let
ssl-enabled = cfg.ssl-certificate != null;
in {
krb_server_keyfile = mkIf (cfg.keytab != null) cfg.keytab;
ssl = true;
ssl_cert_file = cfg.ssl-certificate;
ssl_key_file = cfg.ssl-private-key;
ssl = ssl-enabled;
ssl_cert_file = mkIf ssl-enabled cfg.ssl-certificate;
ssl_key_file = mkIf ssl-enabled cfg.ssl-private-key;
unix_socket_directories = cfg.socket-directory;
unix_socket_group = cfg.socket-group;
@ -282,12 +269,12 @@ in {
};
authentication = lib.mkForce ''
${makeLocalUserPasswordEntries cfg.users}
${makeLocalUserPasswordEntries cfg.users cfg.local-networks}
local all all ident
# host-local
host all all 127.0.0.1/32 gss include_realm=0 krb_realm=${gssapi-realm}
host all all 127.0.0.1/16 gss include_realm=0 krb_realm=${gssapi-realm}
host all all ::1/128 gss include_realm=0 krb_realm=${gssapi-realm}
# local networks
@ -308,6 +295,22 @@ in {
wantedBy = [ "multi-user.target" ];
};
paths = let
user-password-files = mapAttrsToList
(user: userOpts: userOpts.password-file)
cfg.users;
in {
postgresql-password-watcher = mkIf (length user-password-files > 0) {
wantedBy = [ "default.target" ];
description =
"Reset all user passwords if any changes occur.";
pathConfig = {
PathChanged = user-password-files;
Unit = "postgresql-password-setter.service";
};
};
};
services = {
postgresql-password-setter = let
passwords-script = passwords-setter-script cfg.users;
@ -350,7 +353,29 @@ in {
partOf = [ cfg.systemd-target ];
wants = [ "postgresql-password-setter.service" ];
postStart = let
# postStart = let
# allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
# extra-settings-sql = pkgs.writeText "settings.sql" ''
# ${concatStringsSep "\n"
# (map allow-user-login (mapAttrsToList (key: val: key) cfg.users))}
# ${usersAccessSql cfg.users}
# '';
# in ''
# ${pkgs.postgresql}/bin/psql --port ${
# toString config.services.postgresql.port
# } -d postgres -f ${extra-settings-sql}
# ${pkgs.coreutils}/bin/chgrp ${cfg.socket-group} ${cfg.socket-directory}/.s.PGSQL*
# '';
postStop = concatStringsSep "\n" cfg.cleanup-tasks;
};
postgresql-finalizer = {
requires = [ "postgresql.target" ];
after = [ "postgresql.target" "postgresql-password-setter.target" ];
partOf = [ cfg.systemd-target ];
script = let
allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
extra-settings-sql = pkgs.writeText "settings.sql" ''
@ -364,8 +389,6 @@ in {
} -d postgres -f ${extra-settings-sql}
${pkgs.coreutils}/bin/chgrp ${cfg.socket-group} ${cfg.socket-directory}/.s.PGSQL*
'';
postStop = concatStringsSep "\n" cfg.cleanup-tasks;
};
};
};

View File

@ -2,12 +2,11 @@
with lib;
let
inherit (lib.strings) concatStringsSep;
cfg = config.fudo.prometheus;
cfg = config.fudo.metrics.prometheus;
in {
options.fudo.prometheus = with types; {
options.fudo.metrics.prometheus = with types; {
enable = mkEnableOption "Fudo Prometheus Data-Gathering Server";
service-discovery-dns = mkOption {
@ -78,32 +77,33 @@ in {
description = "Directory at which to store Prometheus state.";
default = "/var/lib/prometheus";
};
private-network = mkEnableOption "Network is private.";
};
config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d ${cfg.state-directory} 0700 ${config.systemd.services.prometheus.serviceConfig.User} - - -"
];
services.nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${cfg.hostname}" = {
enableACME = true;
forceSSL = true;
enableACME = ! cfg.private-network;
forceSSL = ! cfg.private-network;
locations."/" = {
proxyPass = "http://127.0.0.1:9090";
extraConfig = let
local-networks = config.instance.local-networks;
in ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
${optionalString ((length local-networks) > 0)
(concatStringsSep "\n" (map (network: "allow ${network};") local-networks)) + "\ndeny all;"}
'';
in "${optionalString ((length local-networks) > 0)
(concatStringsSep "\n"
(map (network: "allow ${network};") local-networks)) + "\ndeny all;"}";
};
};
};
@ -118,85 +118,18 @@ in {
listenAddress = "127.0.0.1";
port = 9090;
scrapeConfigs = [
{
job_name = "docker";
scrapeConfigs = let
make-job = type: {
job_name = type;
honor_labels = false;
static_configs = [
{
targets = cfg.docker-hosts;
}
];
}
{
job_name = "node";
scheme = "https";
metrics_path = "/metrics/node";
honor_labels = false;
dns_sd_configs = [
{
names = cfg.service-discovery-dns.node;
}
];
static_configs = [
{
targets = cfg.static-targets.node;
}
];
}
{
job_name = "dovecot";
scheme = "https";
metrics_path = "/metrics/dovecot";
honor_labels = false;
dns_sd_configs = [
{
names = cfg.service-discovery-dns.dovecot;
}
];
static_configs = [
{
targets = cfg.static-targets.dovecot;
}
];
}
{
job_name = "postfix";
scheme = "https";
metrics_path = "/metrics/postfix";
honor_labels = false;
dns_sd_configs = [
{
names = cfg.service-discovery-dns.postfix;
}
];
static_configs = [
{
targets = cfg.static-targets.postfix;
}
];
}
{
job_name = "rspamd";
scheme = "https";
metrics_path = "/metrics/rspamd";
honor_labels = false;
dns_sd_configs = [
{
names = cfg.service-discovery-dns.rspamd;
}
];
static_configs = [
{
targets = cfg.static-targets.rspamd;
}
];
}
];
scheme = if cfg.private-network then "http" else "https";
metrics_path = "/metrics/${type}";
dns_sd_configs = if (hasAttr type cfg.service-discovery-dns) then
[ { names = cfg.service-discovery-dns.${type}; } ] else [];
static_configs = if (hasAttr type cfg.static-targets) then
[ { targets = cfg.static-targets.${type}; } ] else [];
};
in map make-job ["docker" "node" "dovecot" "postfix" "rspamd"];
pushgateway = {
enable = if (cfg.push-url != null) then true else false;

View File

@ -66,6 +66,7 @@ let
user = mkOption {
type = str;
description = "User (on target host) to which the file will belong.";
default = "root";
};
group = mkOption {

View File

@ -133,6 +133,12 @@ let
type = str;
description = "Hostname of the mail server to use for this site.";
};
local-gateway = mkOption {
type = nullOr str;
description = "If this is a NAT site, this should point to the host acting as network gateway.";
default = null;
};
};
};

View File

@ -166,7 +166,7 @@ let
"Level of protection to apply to the system for this service.";
};
addressFamilies = mkOption {
type = listOf (enum address-families);
type = nullOr (listOf (enum address-families));
default = [ ];
description = "List of address families which the service can use.";
};
@ -435,7 +435,8 @@ in {
WorkingDirectory =
mkIf (opts.workingDirectory != null) opts.workingDirectory;
RestrictAddressFamilies =
restrict-address-families opts.addressFamilies;
optionals (opts.addressFamilies != null)
(restrict-address-families opts.addressFamilies);
RestrictNamespaces = opts.restrictNamespaces;
User = mkIf (opts.user != null) opts.user;
Group = mkIf (opts.group != null) opts.group;

View File

@ -161,7 +161,7 @@ in {
GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive;
GEMINI_FEEDS = "${generate-feeds cfg.feeds}";
CL_SOURCE_REGISTRY = "${pkgs.lib.lisp.lisp-source-registry pkgs.cl-gemini}";
CL_SOURCE_REGISTRY = pkgs.lib.lisp.lisp-source-registry pkgs.cl-gemini;
};
path = with pkgs; [

View File

@ -62,9 +62,21 @@ in {
description = "Networks which are considered local to this host, site, or domain.";
};
service-home = mkOption {
type = str;
description = "Path to runtime home directories for services.";
default = "/run/service";
};
build-seed = mkOption {
type = str;
description = "Seed used to generate configuration.";
};
};
config = {
systemd.tmpfiles.rules = [
"d ${config.instance.service-home} 755 root root - -"
];
};
}

View File

@ -67,7 +67,7 @@ let
toString record.priority
} ${
toString record.weight
} ${record.host}.") records);
} ${toString record.port} ${record.host}.") records);
srvRecordOpts = with types; {
options = {

View File

@ -44,8 +44,14 @@ let
not-null = o: o != null;
in filter not-null [ ipv4 ipv6 ];
site-gateway = config: site-name: let
site = config.fudo.sites.${site-name};
in if (site.local-gateway != null)
then host-ipv4 config site.local-gateway
else site.gateway-v4;
in {
inherit host-ipv4 host-ipv6 host-ips;
inherit host-ipv4 host-ipv6 host-ips site-gateway;
generate-mac-address = hostname: interface: let
pkg = generate-mac-address hostname interface;

View File

@ -12,12 +12,12 @@ let
installPhase = let
passwd = removeSuffix "\n" (readFile passwd-file);
in ''
slappasswd -s ${passwd} > $out
slappasswd -s ${passwd} | tr -d '\n' > $out
'';
};
hash-ldap-passwd = name: passwd-file:
builtins.readFile "${hash-ldap-passwd-pkg name passwd-file}";
readFile "${hash-ldap-passwd-pkg name passwd-file}";
generate-random-passwd = name: length: pkgs.stdenv.mkDerivation {
name = "${name}-random-passwd";
@ -27,10 +27,28 @@ let
buildInputs = with pkgs; [ pwgen ];
installPhase = ''
pwgen --secure --num-passwords=1 ${toString length} > $out
pwgen --secure --num-passwords=1 ${toString length} | tr -d '\n' > $out
'';
};
bcrypt-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation {
name = "${name}-bcrypt";
phases = [ "installPhase" ];
buildInputs = with pkgs; [ apacheHttpd ];
installPhase = let
passwd = removeSuffix "\n" (readFile passwd-file);
in ''
htpasswd -bnBC 10 "" ${passwd} | tr -d ':\n' | sed 's/$2y/$2a/' > $out
'';
};
bcrypt-passwd = name: passwd-file:
readFile "${bcrypt-passwd-pkg name passwd-file}";
generate-stablerandom-passwd = name: { seed, length ? 20, ... }:
pkgs.stdenv.mkDerivation {
name = "${name}-stablerandom-passwd";
@ -41,13 +59,15 @@ let
installPhase = ''
echo "${name}-${seed}" > seedfile
pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out
pwgen --secure --num-passwords=1 -H seedfile ${toString length} | tr -d '\n' > $out
'';
};
in {
hash-ldap-passwd = hash-ldap-passwd;
bcrypt-passwd = bcrypt-passwd;
random-passwd-file = name: length:
builtins.toPath "${generate-random-passwd name length}";

View File

@ -1,8 +1,13 @@
{ lib, ... }:
with lib;
{
{ name, ... }: {
options = with types; {
hostname = mkOption {
type = str;
description = "Hostname.";
default = name;
};
ipv4-address = mkOption {
type = nullOr str;
description = "The V4 IP of a given host, if any.";