Compare commits
67 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6e1c626c0 | |||
4ec8579399 | |||
be05273ab4 | |||
988e3a5f06 | |||
90806f2f24 | |||
1d8be242a1 | |||
bcb2bdf9eb | |||
caf1a162db | |||
437fc6402f | |||
feb97b1e54 | |||
83a217777a | |||
52e5c05ebd | |||
007a88cdfa | |||
66bbe7b760 | |||
c9f66e2ed0 | |||
358859f0b7 | |||
82cd4d1db4 | |||
0eeb0eea47 | |||
217444aaf3 | |||
cc8debb111 | |||
c49b3e8259 | |||
39c9dc8376 | |||
f1f6f5e6a1 | |||
3d82323e22 | |||
d4980a2d5e | |||
4aa532bf4d | |||
763670e46a | |||
2c20446df3 | |||
233c1fb29c | |||
9eaea4c641 | |||
ef116b2b14 | |||
3c0347e734 | |||
f8a5c9d3ec | |||
340e0fcb15 | |||
46e5d85c30 | |||
101b3994a7 | |||
964974bca1 | |||
37ea31b085 | |||
730c93fc4c | |||
b0b3f03231 | |||
804eb74969 | |||
c2406ccc1e | |||
afaf723dee | |||
05fd51218c | |||
d2e648160f | |||
16c9d4b7c4 | |||
89c241cb88 | |||
e9520e66cd | |||
ff7101ce62 | |||
f45d9fa935 | |||
29405fae48 | |||
8279a28d15 | |||
9a55c90d7f | |||
8f38f6a1c1 | |||
1977bafbb5 | |||
27a6a04f67 | |||
c10160bf26 | |||
2d94cf0b1b | |||
a9f1822e6d | |||
31bd4bd157 | |||
1a3b73440a | |||
a1f2e7f28b | |||
4e071df85f | |||
1e95fbc8f1 | |||
c1eb271eda | |||
dfad95b705 | |||
113e7f3b52 |
@ -4,7 +4,7 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.adguard-dns-proxy;
|
||||
|
||||
hostname = config.instance.hostname;
|
||||
inherit (config.instance) hostname;
|
||||
|
||||
get-basename = filename:
|
||||
head (builtins.match "^[a-zA-Z0-9]+-(.+)$" (baseNameOf filename));
|
||||
@ -41,7 +41,12 @@ let
|
||||
};
|
||||
|
||||
generate-config = { dns, http, filters, verbose, upstream-dns, bootstrap-dns
|
||||
, blocked-hosts, enable-dnssec, local-domain-name, ... }: {
|
||||
, blocked-hosts, enable-dnssec, domain-upstreams, local-domain-name, ... }:
|
||||
let
|
||||
upstreamDnsEntries = mapAttrsToList (_: opts:
|
||||
let domainClause = concatStringsSep "/" opts.domains;
|
||||
in "[/${domainClause}/]${opts.upstream}") domain-upstreams;
|
||||
in {
|
||||
bind_host = http.listen-ip;
|
||||
bind_port = http.listen-port;
|
||||
users = [{
|
||||
@ -55,7 +60,7 @@ let
|
||||
dns = {
|
||||
bind_hosts = dns.listen-ips;
|
||||
port = dns.listen-port;
|
||||
upstream_dns = upstream-dns;
|
||||
upstream_dns = upstream-dns ++ upstreamDnsEntries;
|
||||
bootstrap_dns = bootstrap-dns;
|
||||
enable_dnssec = enable-dnssec;
|
||||
local_domain_name = local-domain-name;
|
||||
@ -67,16 +72,17 @@ let
|
||||
safesearch_enabled = false;
|
||||
use_private_ptr_resolvers = cfg.dns.reverse-dns != [ ];
|
||||
local_ptr_upstreams = cfg.dns.reverse-dns;
|
||||
hostsfile_enabled = false;
|
||||
};
|
||||
tls.enabled = false;
|
||||
filters = imap1 (i: filter: {
|
||||
enabled = true;
|
||||
name = filter.name;
|
||||
url = filter.url;
|
||||
}) filters;
|
||||
filters = imap1 (i:
|
||||
{ name, url, ... }: {
|
||||
enabled = true;
|
||||
inherit name url;
|
||||
}) filters;
|
||||
dhcp.enabled = false;
|
||||
clients = [ ];
|
||||
verbose = verbose;
|
||||
inherit verbose;
|
||||
schema_version = 10;
|
||||
};
|
||||
|
||||
@ -122,6 +128,25 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
domain-upstreams = mkOption {
|
||||
type = attrsOf (submodule ({ name, ... }: {
|
||||
options = {
|
||||
domains = mkOption {
|
||||
type = listOf str;
|
||||
description =
|
||||
"List of domains to route to a specific upstream DNS target.";
|
||||
default = [ name ];
|
||||
};
|
||||
|
||||
upstream = mkOption {
|
||||
type = str;
|
||||
description = "Upstream DNS target, in {ip}:{port} format.";
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = { };
|
||||
};
|
||||
|
||||
filters = mkOption {
|
||||
type = listOf (submodule filterOpts);
|
||||
description = "List of filters to apply to DNS traffic.";
|
||||
@ -183,8 +208,8 @@ in {
|
||||
# These 11 addrs send the network, so the response can prefer closer answers
|
||||
"https://9.9.9.11/dns-query"
|
||||
"https://149.112.112.11/dns-query"
|
||||
"https://2620:fe::11/dns-query"
|
||||
"https://2620:fe::fe:11/dns-query"
|
||||
# "https://2620:fe::11/dns-query"
|
||||
# "https://2620:fe::fe:11/dns-query"
|
||||
];
|
||||
};
|
||||
|
||||
@ -222,18 +247,7 @@ in {
|
||||
verbose = mkEnableOption "Keep verbose logs.";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (let
|
||||
upgrade-perms = cfg.dns.listen-port <= 1024 || cfg.http.listen-port <= 1024;
|
||||
in {
|
||||
users = mkIf upgrade-perms {
|
||||
users.${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = cfg.user;
|
||||
};
|
||||
|
||||
groups.${cfg.user} = { members = [ cfg.user ]; };
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
fudo = {
|
||||
secrets.host-secrets.${hostname} = {
|
||||
adguard-dns-proxy-admin-password = {
|
||||
@ -242,41 +256,75 @@ in {
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
system.services.adguard-dns-proxy =
|
||||
let cfg-path = "/run/adguard-dns-proxy/config.yaml";
|
||||
in {
|
||||
description =
|
||||
"DNS Proxy for ad filtering and DNS-over-HTTPS lookups.";
|
||||
wantedBy = [ "default.target" ];
|
||||
after = [ "syslog.target" ];
|
||||
requires = [ "network.target" ];
|
||||
privateNetwork = false;
|
||||
requiredCapabilities = optional upgrade-perms "CAP_NET_BIND_SERVICE";
|
||||
restartWhen = "always";
|
||||
addressFamilies = null;
|
||||
networkWhitelist = cfg.allowed-networks;
|
||||
user = mkIf upgrade-perms cfg.user;
|
||||
runtimeDirectory = "adguard-dns-proxy";
|
||||
stateDirectory = "adguard-dns-proxy";
|
||||
preStart = ''
|
||||
cp ${generate-config-file cfg} ${cfg-path};
|
||||
chown $USER ${cfg-path};
|
||||
chmod u+w ${cfg-path};
|
||||
'';
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ cfg.dns.listen-port ];
|
||||
allowedUDPPorts = [ cfg.dns.listen-port ];
|
||||
};
|
||||
|
||||
execStart = let
|
||||
args = [
|
||||
systemd.services.adguard-dns-proxy =
|
||||
let configFile = "/run/adguard-dns-proxy/config.yaml";
|
||||
in {
|
||||
description = "DNS proxy for ad filtering and DNS-over-HTTPS lookups.";
|
||||
wantedBy = [ "default.target" ];
|
||||
after = [ "network.target" ];
|
||||
requires = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
ExecStartPre = pkgs.writeShellScript "adguardsProxyPrestart.sh"
|
||||
"cp ${generate-config-file cfg} $RUNTIME_DIRECTORY/config.yaml";
|
||||
ExecStart = pkgs.writeShellScript "adguardProxyStart.sh"
|
||||
(concatStringsSep " " [
|
||||
"${pkgs.adguardhome}/bin/adguardhome"
|
||||
"--no-check-update"
|
||||
"--work-dir /var/lib/adguard-dns-proxy"
|
||||
"--pidfile /run/adguard-dns-proxy/adguard-dns-proxy.pid"
|
||||
"--pidfile /run/adguard-dns-proxy.pid"
|
||||
"--host ${cfg.http.listen-ip}"
|
||||
"--port ${toString cfg.http.listen-port}"
|
||||
"--config ${cfg-path}"
|
||||
];
|
||||
arg-string = concatStringsSep " " args;
|
||||
in "${pkgs.adguardhome}/bin/adguardhome ${arg-string}";
|
||||
"--config $RUNTIME_DIRECTORY/config.yaml"
|
||||
]);
|
||||
AmbientCapabilities = optional
|
||||
(cfg.dns.listen-port <= 1024 || cfg.http.listen-port <= 1024)
|
||||
[ "CAP_NET_BIND_SERVICE" ];
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = "adguard-dns-proxy";
|
||||
StateDirectory = "adguard-dns-proxy";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
# system.services.adguard-dns-proxy =
|
||||
# let cfg-path = "/run/adguard-dns-proxy/config.yaml";
|
||||
# in {
|
||||
# description =
|
||||
# "DNS Proxy for ad filtering and DNS-over-HTTPS lookups.";
|
||||
# wantedBy = [ "default.target" ];
|
||||
# after = [ "syslog.target" ];
|
||||
# requires = [ "network.target" ];
|
||||
# privateNetwork = false;
|
||||
# requiredCapabilities = optional upgrade-perms "CAP_NET_BIND_SERVICE";
|
||||
# restartWhen = "always";
|
||||
# addressFamilies = null;
|
||||
# networkWhitelist = cfg.allowed-networks;
|
||||
# user = mkIf upgrade-perms cfg.user;
|
||||
# runtimeDirectory = "adguard-dns-proxy";
|
||||
# stateDirectory = "adguard-dns-proxy";
|
||||
# preStart = ''
|
||||
# cp ${generate-config-file cfg} ${cfg-path};
|
||||
# chown $USER ${cfg-path};
|
||||
# chmod u+w ${cfg-path};
|
||||
# '';
|
||||
|
||||
# execStart = let
|
||||
# args = [
|
||||
# "--no-check-update"
|
||||
# "--work-dir /var/lib/adguard-dns-proxy"
|
||||
# "--pidfile /run/adguard-dns-proxy/adguard-dns-proxy.pid"
|
||||
# "--host ${cfg.http.listen-ip}"
|
||||
# "--port ${toString cfg.http.listen-port}"
|
||||
# "--config ${cfg-path}"
|
||||
# ];
|
||||
# arg-string = concatStringsSep " " args;
|
||||
# in "${pkgs.adguardhome}/bin/adguardhome ${arg-string}";
|
||||
# };
|
||||
};
|
||||
}
|
||||
|
@ -6,22 +6,24 @@ in {
|
||||
imports = [ ./kdc.nix ];
|
||||
|
||||
config = {
|
||||
krb5 = {
|
||||
security.krb5 = {
|
||||
enable = true;
|
||||
kerberos = pkgs.heimdal;
|
||||
libdefaults = {
|
||||
default_realm = realm;
|
||||
allow_weak_crypto = false;
|
||||
dns_lookup_kdc = true;
|
||||
dns_lookup_realm = true;
|
||||
forwardable = true;
|
||||
proxiable = true;
|
||||
};
|
||||
appdefaults = {
|
||||
forwardable = true;
|
||||
proxiable = true;
|
||||
encrypt = true;
|
||||
forward = true;
|
||||
package = pkgs.heimdal;
|
||||
settings = {
|
||||
libdefaults = {
|
||||
default_realm = realm;
|
||||
allow_weak_crypto = false;
|
||||
dns_lookup_kdc = true;
|
||||
dns_lookup_realm = true;
|
||||
forwardable = true;
|
||||
proxiable = true;
|
||||
};
|
||||
appdefaults = {
|
||||
forwardable = true;
|
||||
proxiable = true;
|
||||
encrypt = true;
|
||||
forward = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,6 @@ with lib; {
|
||||
./grafana.nix
|
||||
./hosts.nix
|
||||
./host-filesystems.nix
|
||||
./initrd-network.nix
|
||||
./ipfs.nix
|
||||
./jabber.nix
|
||||
# ./kdc.nix
|
||||
|
@ -159,7 +159,6 @@ in {
|
||||
state-directory = mkOption {
|
||||
type = str;
|
||||
description = "Directory at which to store Grafana state data.";
|
||||
default = "/var/lib/grafana";
|
||||
};
|
||||
|
||||
private-network = mkEnableOption "Network is private, no SSL.";
|
||||
@ -199,7 +198,7 @@ in {
|
||||
grafana = {
|
||||
enable = true;
|
||||
|
||||
dataDir = cfg.state-directory;
|
||||
dataDir = toPath cfg.state-directory;
|
||||
|
||||
settings = {
|
||||
|
||||
|
@ -93,11 +93,12 @@ in {
|
||||
|
||||
time.timeZone = site.timezone;
|
||||
|
||||
krb5.libdefaults.default_realm = domain.gssapi-realm;
|
||||
security.krb5.settings.libdefaults.default_realm = domain.gssapi-realm;
|
||||
|
||||
services = {
|
||||
cron.mailto = domain.admin-email;
|
||||
fail2ban.ignoreIP = config.instance.local-networks;
|
||||
udev.packages = optional host-cfg.android-dev pkgs.android-udev-rules;
|
||||
};
|
||||
|
||||
virtualisation.docker = mkIf (host-cfg.docker-server) {
|
||||
|
@ -1,86 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
hostname = config.instance.hostname;
|
||||
initrd-cfg = config.fudo.hosts.${hostname}.initrd-network;
|
||||
|
||||
read-lines = filename: splitString "\n" (fileContents filename);
|
||||
|
||||
concatLists = lsts: concatMap (i: i) lsts;
|
||||
|
||||
gen-sshfp-records-pkg = hostname: pubkey: let
|
||||
pubkey-file = builtins.toFile "${hostname}-initrd-ssh-pubkey" pubkey;
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = "${hostname}-initrd-ssh-firngerprint";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
buildInputs = with pkgs; [ openssh ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
ssh-keygen -r REMOVEME -f "${pubkey-file}" | sed 's/^REMOVEME IN SSHFP //' >> $out/initrd-ssh-pubkey.sshfp
|
||||
'';
|
||||
};
|
||||
|
||||
gen-sshfp-records = hostname: pubkey: let
|
||||
sshfp-record-pkg = gen-sshfp-records-pkg hostname pubkey;
|
||||
in read-lines "${sshfp-record-pkg}/initrd-ssh-pubkey.sshfp";
|
||||
|
||||
in {
|
||||
config = {
|
||||
boot = mkIf (initrd-cfg != null) {
|
||||
kernelParams = let
|
||||
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 [
|
||||
"ip=${initrd-cfg.ip}:${site-gateway}:${netmask}:${hostname}:${initrd-cfg.interface}"
|
||||
];
|
||||
initrd = {
|
||||
network = {
|
||||
enable = true;
|
||||
|
||||
ssh = let
|
||||
admin-ssh-keys =
|
||||
concatMap (admin: config.fudo.users.${admin}.ssh-authorized-keys)
|
||||
config.instance.local-admins;
|
||||
in {
|
||||
enable = true;
|
||||
port = 22;
|
||||
authorizedKeys = admin-ssh-keys;
|
||||
hostKeys = [
|
||||
initrd-cfg.keypair.private-key-file
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fudo = {
|
||||
local-network = let
|
||||
initrd-network-hosts =
|
||||
filterAttrs
|
||||
(hostname: hostOpts: hostOpts.initrd-network != null)
|
||||
config.instance.local-hosts;
|
||||
in {
|
||||
zone-definition.hosts = mapAttrs'
|
||||
(hostname: hostOpts: nameValuePair "${hostname}-recovery" {
|
||||
ipv4-address = hostOpts.initrd-network.ip;
|
||||
description = "${hostname} initrd host";
|
||||
}) initrd-network-hosts;
|
||||
|
||||
extra-records = let
|
||||
recs = (mapAttrsToList
|
||||
(hostname: hostOpts: map
|
||||
(sshfp: "${hostname} IN SSHFP ${sshfp}")
|
||||
(gen-sshfp-records hostname hostOpts.initrd-network.keypair.public-key))
|
||||
initrd-network-hosts);
|
||||
in concatLists recs;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -4,6 +4,8 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.local-network;
|
||||
|
||||
inherit (pkgs.lib) getHostIpv4 getHostFqdn;
|
||||
|
||||
join-lines = concatStringsSep "\n";
|
||||
|
||||
inherit (pkgs.lib.ip)
|
||||
@ -117,14 +119,12 @@ in {
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
fudo.system.hostfile-entries = let
|
||||
other-hosts =
|
||||
filterAttrs (hostname: hostOpts: hostname != config.instance.hostname)
|
||||
cfg.zone-definition.hosts;
|
||||
in mapAttrs' (hostname: hostOpts:
|
||||
nameValuePair hostOpts.ipv4-address [
|
||||
"${hostname}.${cfg.domain}"
|
||||
hostname
|
||||
]) other-hosts;
|
||||
siteHosts =
|
||||
(filterAttrs (_: hostOpts: hostOpts.site == config.instance.local-site)
|
||||
config.fudo.hosts);
|
||||
in mapAttrs' (hostname: _:
|
||||
nameValuePair (getHostIpv4 hostname) [ (getHostFqdn hostname) hostname ])
|
||||
(filterAttrs (hostname: _: !isNull (getHostIpv4 hostname)) siteHosts);
|
||||
|
||||
services.kea.dhcp4 = {
|
||||
enable = true;
|
||||
@ -161,6 +161,7 @@ in {
|
||||
}
|
||||
];
|
||||
subnet4 = [{
|
||||
id = 1;
|
||||
pools = [{
|
||||
pool = let
|
||||
minIp = networkMinIp cfg.dhcp-dynamic-network;
|
||||
|
@ -268,6 +268,7 @@ in {
|
||||
"${worldOpts.world-name} Minecraft Server with Clojure REPL";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
|
@ -9,14 +9,14 @@ let
|
||||
default = name;
|
||||
};
|
||||
|
||||
servers = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of servers for this Nexus domain.";
|
||||
server = mkOption {
|
||||
type = str;
|
||||
description = "Primary server for this Nexus domain.";
|
||||
};
|
||||
|
||||
dns-servers = mkOption {
|
||||
secondary-dns-servers = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of DNS servers for this Nexus domain.";
|
||||
description = "List of secondary DNS servers for this Nexus domain.";
|
||||
};
|
||||
|
||||
gssapi-realm = mkOption {
|
||||
|
@ -7,12 +7,14 @@ let
|
||||
hostname = config.instance.hostname;
|
||||
domain-name = config.instance.local-domain;
|
||||
|
||||
sslEnabled = cfg.ssl-certificate != null;
|
||||
|
||||
gssapi-realm = config.fudo.domains.${domain-name}.gssapi-realm;
|
||||
|
||||
join-lines = lib.concatStringsSep "\n";
|
||||
|
||||
strip-ext = filename: head (builtins.match "^(.+)[.][^.]+$" filename);
|
||||
|
||||
joinLines = concatStringsSep "\n";
|
||||
|
||||
userDatabaseOpts = { database, ... }: {
|
||||
options = {
|
||||
access = mkOption {
|
||||
@ -101,7 +103,7 @@ let
|
||||
exit 2
|
||||
fi
|
||||
|
||||
${join-lines (mapAttrsToList (user: opts:
|
||||
${joinLines (mapAttrsToList (user: opts:
|
||||
password-setter-script user opts.password-file "$OUTPUT_FILE")
|
||||
(filterPasswordedUsers users))}
|
||||
'';
|
||||
@ -111,17 +113,19 @@ let
|
||||
nameValuePair "DATABASE ${database}" databaseOpts.access) databases;
|
||||
|
||||
makeEntry = nw:
|
||||
"hostssl all all ${nw} gss include_realm=0 krb_realm=${gssapi-realm}";
|
||||
let hostClause = if sslEnabled then "hostssl" else "host";
|
||||
in "${hostClause} all all ${nw} gss include_realm=0 krb_realm=${gssapi-realm}";
|
||||
|
||||
makeNetworksEntry = networks: join-lines (map makeEntry networks);
|
||||
makeNetworksEntry = networks: joinLines (map makeEntry networks);
|
||||
|
||||
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: ''
|
||||
joinLines (map (network:
|
||||
let hostClause = if sslEnabled then "hostssl" else "host";
|
||||
in "${hostClause} ${db} ${user} ${network} md5") networks);
|
||||
in joinLines (mapAttrsToList (user: opts:
|
||||
joinLines (map (db: ''
|
||||
local ${db} ${user} md5
|
||||
host ${db} ${user} 127.0.0.1/16 md5
|
||||
host ${db} ${user} ::1/128 md5
|
||||
@ -132,19 +136,18 @@ let
|
||||
|
||||
enableDatabaseExtensionsSql = database: databaseOpts: ''
|
||||
\c ${database}
|
||||
${join-lines (map enableExtensionSql databaseOpts.extensions)}
|
||||
${joinLines (map enableExtensionSql databaseOpts.extensions)}
|
||||
'';
|
||||
|
||||
userTableAccessSql = user: entity: access:
|
||||
"GRANT ${access} ON ${entity} TO ${user};";
|
||||
userDatabaseAccessSql = user: database: dbOpts: ''
|
||||
\c ${database}
|
||||
${join-lines
|
||||
(mapAttrsToList (userTableAccessSql user) dbOpts.entity-access)}
|
||||
${joinLines (mapAttrsToList (userTableAccessSql user) dbOpts.entity-access)}
|
||||
'';
|
||||
userAccessSql = user: userOpts:
|
||||
join-lines (mapAttrsToList (userDatabaseAccessSql user) userOpts.databases);
|
||||
usersAccessSql = users: join-lines (mapAttrsToList userAccessSql users);
|
||||
joinLines (mapAttrsToList (userDatabaseAccessSql user) userOpts.databases);
|
||||
usersAccessSql = users: joinLines (mapAttrsToList userAccessSql users);
|
||||
|
||||
in {
|
||||
|
||||
@ -205,23 +208,23 @@ in {
|
||||
default = { };
|
||||
};
|
||||
|
||||
socket-directory = mkOption {
|
||||
type = str;
|
||||
description = "Directory in which to place unix sockets.";
|
||||
default = "/run/postgresql";
|
||||
};
|
||||
# socket-directory = mkOption {
|
||||
# type = str;
|
||||
# description = "Directory in which to place unix sockets.";
|
||||
# default = "/run/postgresql";
|
||||
# };
|
||||
|
||||
socket-group = mkOption {
|
||||
type = str;
|
||||
description = "Group for accessing sockets.";
|
||||
default = "postgres_local";
|
||||
};
|
||||
# socket-group = mkOption {
|
||||
# type = str;
|
||||
# description = "Group for accessing sockets.";
|
||||
# default = "postgres_local";
|
||||
# };
|
||||
|
||||
local-users = mkOption {
|
||||
type = listOf str;
|
||||
description = "Users able to access the server via local socket.";
|
||||
default = [ ];
|
||||
};
|
||||
# local-users = mkOption {
|
||||
# type = listOf str;
|
||||
# description = "Users able to access the server via local socket.";
|
||||
# default = [ ];
|
||||
# };
|
||||
|
||||
required-services = mkOption {
|
||||
type = listOf str;
|
||||
@ -255,23 +258,27 @@ in {
|
||||
|
||||
environment.systemPackages = with pkgs; [ cfg.package ];
|
||||
|
||||
users.groups = {
|
||||
${cfg.socket-group} = { members = [ "postgres" ] ++ cfg.local-users; };
|
||||
};
|
||||
# users.groups = {
|
||||
# ${cfg.socket-group} = { members = [ "postgres" ] ++ cfg.local-users; };
|
||||
# };
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
package = cfg.package;
|
||||
enableTCPIP = true;
|
||||
ensureDatabases = mapAttrsToList (name: value: name) cfg.databases;
|
||||
ensureUsers = ((mapAttrsToList (username: attrs: {
|
||||
name = username;
|
||||
ensurePermissions = userDatabaseAccess username attrs.databases;
|
||||
}) cfg.users) ++ (flatten (mapAttrsToList (database: opts:
|
||||
(map (username: {
|
||||
name = username;
|
||||
ensurePermissions = { "DATABASE ${database}" = "ALL PRIVILEGES"; };
|
||||
}) opts.users)) cfg.databases)));
|
||||
ensureUsers = map (user: {
|
||||
name = user;
|
||||
ensureClauses.login = true;
|
||||
}) (attrNames cfg.users);
|
||||
# ensureUsers = ((mapAttrsToList (username: attrs: {
|
||||
# name = username;
|
||||
# ensurePermissions = userDatabaseAccess username attrs.databases;
|
||||
# }) cfg.users) ++ (flatten (mapAttrsToList (database: opts:
|
||||
# (map (username: {
|
||||
# name = username;
|
||||
# ensurePermissions = { "DATABASE ${database}" = "ALL PRIVILEGES"; };
|
||||
# }) opts.users)) cfg.databases)));
|
||||
|
||||
settings = let ssl-enabled = cfg.ssl-certificate != null;
|
||||
in {
|
||||
@ -281,9 +288,11 @@ in {
|
||||
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;
|
||||
unix_socket_permissions = "0777";
|
||||
# unix_socket_directories = cfg.socket-directory;
|
||||
# unix_socket_group = cfg.socket-group;
|
||||
# unix_socket_permissions = "0777";
|
||||
|
||||
log_min_error_statement = "DEBUG3";
|
||||
};
|
||||
|
||||
authentication = lib.mkForce ''
|
||||
@ -304,9 +313,9 @@ in {
|
||||
|
||||
systemd = {
|
||||
|
||||
tmpfiles.rules = optional (cfg.state-directory != null)
|
||||
tmpfiles.rules = optionals (cfg.state-directory != null)
|
||||
(let user = config.systemd.services.postgresql.serviceConfig.User;
|
||||
in "d ${cfg.state-directory} 0700 ${user} - - -");
|
||||
in [ "d ${cfg.state-directory} 0700 ${user} - - -" ]);
|
||||
|
||||
targets.${strip-ext cfg.systemd-target} = {
|
||||
description = "Postgresql and associated systemd services.";
|
||||
@ -353,11 +362,12 @@ in {
|
||||
description =
|
||||
"A service to set postgresql user passwords after the server has started.";
|
||||
after = [ "postgresql.service" ] ++ cfg.required-services;
|
||||
requires = [ "postgresql.service" ] ++ cfg.required-services;
|
||||
wantedBy = [ "postgresql.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = config.services.postgresql.superUser;
|
||||
ExecStart = "${password-wrapper-script}";
|
||||
ExecStart = password-wrapper-script;
|
||||
};
|
||||
partOf = [ cfg.systemd-target ];
|
||||
};
|
||||
@ -372,7 +382,7 @@ in {
|
||||
# allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
|
||||
|
||||
# extra-settings-sql = pkgs.writeText "settings.sql" ''
|
||||
# ${concatStringsSep "\n"
|
||||
# ${joinLines
|
||||
# (map allow-user-login (mapAttrsToList (key: val: key) cfg.users))}
|
||||
# ${usersAccessSql cfg.users}
|
||||
# '';
|
||||
@ -384,28 +394,40 @@ in {
|
||||
# '';
|
||||
|
||||
# Wait a bit before starting dependent services, to let postgres finish initializing
|
||||
serviceConfig.ExecStartPost =
|
||||
mkAfter [ "${pkgs.coreutils}/bin/sleep 10" ];
|
||||
serviceConfig = {
|
||||
# ReadWritePaths = [ cfg.socket-directory ];
|
||||
ExecStartPost = mkAfter [ "${pkgs.coreutils}/bin/sleep 10" ];
|
||||
};
|
||||
|
||||
postStop = concatStringsSep "\n" cfg.cleanup-tasks;
|
||||
postStop = joinLines cfg.cleanup-tasks;
|
||||
};
|
||||
|
||||
postgresql-finalizer = {
|
||||
requires = [ "postgresql.service" ];
|
||||
after = [ "postgresql.service" "postgresql-password-setter.service" ];
|
||||
partOf = [ "postgresql.target" ];
|
||||
partOf = [ cfg.systemd-target ];
|
||||
wantedBy = [ "postgresql.service" ];
|
||||
serviceConfig = {
|
||||
User = config.services.postgresql.superUser;
|
||||
ExecStart = let
|
||||
allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
|
||||
enableExtensionsClause = joinLines
|
||||
(mapAttrsToList enableDatabaseExtensionsSql cfg.databases);
|
||||
|
||||
grantMasterAccessSql = db: user: ''
|
||||
GRANT ALL PRIVILEGES ON DATABASE ${db} TO ${user};
|
||||
\c ${db} postgres
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${user};
|
||||
\c postgres postgres
|
||||
'';
|
||||
|
||||
grantMasterAccess = joinLines (mapAttrsToList (database: opts:
|
||||
joinLines (map (grantMasterAccessSql database) opts.users))
|
||||
cfg.databases);
|
||||
|
||||
extra-settings-sql = pkgs.writeText "settings.sql" ''
|
||||
${join-lines
|
||||
(mapAttrsToList enableDatabaseExtensionsSql cfg.databases)}
|
||||
${enableExtensionsClause}
|
||||
|
||||
${concatStringsSep "\n" (map allow-user-login
|
||||
(mapAttrsToList (key: val: key) cfg.users))}
|
||||
${grantMasterAccess}
|
||||
|
||||
${usersAccessSql cfg.users}
|
||||
'';
|
||||
@ -413,7 +435,6 @@ in {
|
||||
${pkgs.postgresql}/bin/psql --port ${
|
||||
toString config.services.postgresql.port
|
||||
} -d postgres -f ${extra-settings-sql}
|
||||
chgrp ${cfg.socket-group} ${cfg.socket-directory}/.s.PGSQL*
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }@toplevel:
|
||||
|
||||
with lib;
|
||||
let cfg = config.fudo.metrics.prometheus;
|
||||
@ -15,35 +15,50 @@ in {
|
||||
description = "The prometheus package that should be used.";
|
||||
};
|
||||
|
||||
service-discovery-dns = mkOption {
|
||||
type = attrsOf (listOf str);
|
||||
description = ''
|
||||
A map of exporter type to a list of domains to use for service discovery.
|
||||
'';
|
||||
example = {
|
||||
node = [ "node._metrics._tcp.my-domain.com" ];
|
||||
postfix = [ "postfix._metrics._tcp.my-domain.com" ];
|
||||
};
|
||||
default = {
|
||||
dovecot = [ ];
|
||||
node = [ ];
|
||||
postfix = [ ];
|
||||
rspamd = [ ];
|
||||
};
|
||||
};
|
||||
scrapers = let
|
||||
scraperOpts.options = {
|
||||
name = mkOption {
|
||||
type = str;
|
||||
description = "Name of this exporter.";
|
||||
};
|
||||
|
||||
static-targets = mkOption {
|
||||
type = attrsOf (listOf str);
|
||||
description = ''
|
||||
A map of exporter type to a list of host:ports from which to collect metrics.
|
||||
'';
|
||||
example = { node = [ "my-host.my-domain:1111" ]; };
|
||||
default = {
|
||||
dovecot = [ ];
|
||||
node = [ ];
|
||||
postfix = [ ];
|
||||
rspamd = [ ];
|
||||
secured = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to use https instead of http.";
|
||||
default = !toplevel.config.fudo.metrics.prometheus.private-network;
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = str;
|
||||
description = "Host path at which to find exported metrics.";
|
||||
default = "/metrics";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port on which to scrape for metrics.";
|
||||
default =
|
||||
if toplevel.config.fudo.metrics.prometheus.private-network then
|
||||
80
|
||||
else
|
||||
443;
|
||||
};
|
||||
|
||||
static-targets = mkOption {
|
||||
type = listOf str;
|
||||
description = "Explicit list of hosts to scrape for metrics.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
dns-sd-records = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of DNS records to query for hosts to scrape.";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
in mkOption {
|
||||
type = listOf (submodule scraperOpts);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
docker-hosts = mkOption {
|
||||
@ -79,7 +94,6 @@ in {
|
||||
state-directory = mkOption {
|
||||
type = str;
|
||||
description = "Directory at which to store Prometheus state.";
|
||||
default = "/var/lib/prometheus";
|
||||
};
|
||||
|
||||
private-network = mkEnableOption "Network is private.";
|
||||
@ -134,21 +148,19 @@ in {
|
||||
port = 9090;
|
||||
|
||||
scrapeConfigs = let
|
||||
make-job = type: {
|
||||
job_name = type;
|
||||
honor_labels = false;
|
||||
scheme = if cfg.private-network then "http" else "https";
|
||||
metrics_path = "/metrics/${type}";
|
||||
static_configs = if (hasAttr type cfg.static-targets) then [{
|
||||
targets = cfg.static-targets.${type};
|
||||
}] else
|
||||
[ ];
|
||||
dns_sd_configs = if (hasAttr type cfg.service-discovery-dns) then [{
|
||||
names = cfg.service-discovery-dns.${type};
|
||||
}] else
|
||||
[ ];
|
||||
};
|
||||
in map make-job [ "docker" "node" "dovecot" "postfix" "rspamd" ];
|
||||
mkScraper =
|
||||
{ name, secured, path, port, static-targets, dns-sd-records }: {
|
||||
job_name = name;
|
||||
honor_labels = false;
|
||||
scheme = if secured then "https" else "http";
|
||||
metrics_path = path;
|
||||
static_configs = mkIf (static-targets != [ ])
|
||||
(let attachPort = target: "${target}:${toString port}";
|
||||
in [{ targets = map attachPort static-targets; }]);
|
||||
dns_sd_configs =
|
||||
mkIf (dns-sd-records != [ ]) [{ names = dns-sd-records; }];
|
||||
};
|
||||
in map mkScraper cfg.scrapers;
|
||||
|
||||
pushgateway = {
|
||||
enable = if (cfg.push-url != null) then true else false;
|
||||
|
@ -4,34 +4,23 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.slynk;
|
||||
|
||||
initScript = port: load-paths:
|
||||
let
|
||||
load-path-string =
|
||||
concatStringsSep " " (map (path: ''"${path}"'') load-paths);
|
||||
in pkgs.writeText "slynk.lisp" ''
|
||||
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
|
||||
(ql:quickload :slynk)
|
||||
(setf asdf:*central-registry*
|
||||
(append asdf:*central-registry*
|
||||
(list ${load-path-string})))
|
||||
initScript = port:
|
||||
pkgs.writeText "slynk.lisp" ''
|
||||
(asdf:load-system 'slynk)
|
||||
(slynk:create-server :port ${toString port} :dont-close t)
|
||||
(dolist (var '("LD_LIBRARY_PATH"))
|
||||
(format t "~S: ~S~%" var (sb-unix::posix-getenv var)))
|
||||
|
||||
(loop (sleep 60))
|
||||
'';
|
||||
|
||||
lisp-libs = with pkgs.lispPackages; [
|
||||
alexandria
|
||||
asdf-package-system
|
||||
asdf-system-connections
|
||||
cl_plus_ssl
|
||||
cl-ppcre
|
||||
quicklisp
|
||||
quri
|
||||
uiop
|
||||
usocket
|
||||
];
|
||||
sbclWithLibs = pkgs.sbcl.withPackages (ps:
|
||||
with ps; [
|
||||
alexandria
|
||||
asdf-package-system
|
||||
asdf-system-connections
|
||||
cl_plus_ssl
|
||||
cl-ppcre
|
||||
quri
|
||||
usocket
|
||||
]);
|
||||
|
||||
in {
|
||||
options.fudo.slynk = {
|
||||
@ -48,20 +37,17 @@ in {
|
||||
systemd.user.services.slynk = {
|
||||
description = "Slynk Common Lisp server.";
|
||||
|
||||
serviceConfig =
|
||||
let load-paths = (map (pkg: "${pkg}/lib/common-lisp/") lisp-libs);
|
||||
in {
|
||||
ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init";
|
||||
ExecStart =
|
||||
"${pkgs.sbcl}/bin/sbcl --load ${initScript cfg.port load-paths}";
|
||||
Restart = "on-failure";
|
||||
PIDFile = "/run/slynk.$USERNAME.pid";
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "sbcl --load ${initScript cfg.port}";
|
||||
Restart = "on-failure";
|
||||
PIDFile = "/run/slynk.$USERNAME.pid";
|
||||
};
|
||||
|
||||
path = with pkgs; [
|
||||
gcc
|
||||
glibc # for getent
|
||||
file
|
||||
sbclWithLibs
|
||||
];
|
||||
|
||||
environment = { LD_LIBRARY_PATH = "${pkgs.openssl.out}/lib"; };
|
||||
|
@ -366,6 +366,9 @@ in {
|
||||
'');
|
||||
in {
|
||||
requiredBy = [ "nginx.service" ];
|
||||
before = [ "nginx.service" ];
|
||||
requires = [ config.fudo.secrets.secret-target ];
|
||||
after = [ config.fudo.secrets.secret-target ];
|
||||
description =
|
||||
"Initialize webmail service directories prior to starting nginx.";
|
||||
script = "${scriptPkg}/bin/webmail-init.sh";
|
||||
|
@ -19,7 +19,8 @@ in {
|
||||
|
||||
build-timestamp = mkOption {
|
||||
type = int;
|
||||
description = "Timestamp associated with the build. Used for e.g. DNS serials.";
|
||||
description =
|
||||
"Timestamp associated with the build. Used for e.g. DNS serials.";
|
||||
};
|
||||
|
||||
local-domain = mkOption {
|
||||
@ -45,7 +46,8 @@ in {
|
||||
|
||||
local-admins = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of users who should have admin access to the local host.";
|
||||
description =
|
||||
"List of users who should have admin access to the local host.";
|
||||
};
|
||||
|
||||
local-groups = mkOption {
|
||||
@ -55,7 +57,8 @@ in {
|
||||
|
||||
local-hosts = mkOption {
|
||||
type = attrsOf (submodule host.hostOpts);
|
||||
description = "List of hosts that should be considered local to the current host.";
|
||||
description =
|
||||
"List of hosts that should be considered local to the current host.";
|
||||
};
|
||||
|
||||
local-users = mkOption {
|
||||
@ -65,7 +68,8 @@ in {
|
||||
|
||||
local-networks = mkOption {
|
||||
type = listOf str;
|
||||
description = "Networks which are considered local to this host, site, or domain.";
|
||||
description =
|
||||
"Networks which are considered local to this host, site, or domain.";
|
||||
};
|
||||
|
||||
service-home = mkOption {
|
||||
@ -81,8 +85,7 @@ in {
|
||||
};
|
||||
|
||||
config = {
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${config.instance.service-home} 755 root root - -"
|
||||
];
|
||||
systemd.tmpfiles.rules =
|
||||
[ "d ${config.instance.service-home} 755 root root - -" ];
|
||||
};
|
||||
}
|
||||
|
@ -153,14 +153,14 @@ let
|
||||
|
||||
${join-lines (mapAttrsToList makeMetricRecords zone.metric-records)}
|
||||
|
||||
$TTL ${zone.host-record-ttl}
|
||||
|
||||
${join-lines (mapAttrsToList hostRecords zone.hosts)}
|
||||
|
||||
${join-lines (mapAttrsToList cnameRecord zone.aliases)}
|
||||
|
||||
${join-lines zone.verbatim-dns-records}
|
||||
|
||||
$TTL ${zone.host-record-ttl}
|
||||
|
||||
${join-lines (mapAttrsToList hostRecords zone.hosts)}
|
||||
|
||||
${join-lines (mapAttrsToList
|
||||
(subdom: subdomCfg: domain-records "${subdom}.${dom}" subdomCfg)
|
||||
zone.subdomains)}
|
||||
|
@ -13,4 +13,7 @@ let
|
||||
installPhase = "python -mjson.tool ${filename} > $out";
|
||||
};
|
||||
|
||||
in { inherit format-json-file; }
|
||||
writeEnv = name: vars:
|
||||
pkgs.writeText name (mapAttrsToList (var: val: "${var}=${val}") vars);
|
||||
|
||||
in { inherit format-json-file writeEnv; }
|
||||
|
@ -92,7 +92,7 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
masterKeyOpts = { ... }: {
|
||||
masterKeyOpts = _: {
|
||||
options = with types; {
|
||||
key-path = mkOption {
|
||||
type = str;
|
||||
@ -122,7 +122,7 @@ in rec {
|
||||
type = str;
|
||||
description =
|
||||
"Primary domain to which the host belongs, in the form of a domain name.";
|
||||
default = "fudo.org";
|
||||
default = "unassigned.domain";
|
||||
};
|
||||
|
||||
extra-domains = mkOption {
|
||||
@ -290,92 +290,28 @@ in rec {
|
||||
default = false;
|
||||
};
|
||||
|
||||
wireguard = let
|
||||
clientOpts = {
|
||||
options = {
|
||||
ip = mkOption {
|
||||
type = nullOr str;
|
||||
description =
|
||||
"IP address assigned to this host in the WireGuard network.";
|
||||
};
|
||||
|
||||
bound = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to route all traffic from this host.";
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
wireguardOpts = {
|
||||
options = {
|
||||
private-key-file = mkOption {
|
||||
type = str;
|
||||
description = "WireGuard private key file of the host.";
|
||||
};
|
||||
|
||||
public-key = mkOption {
|
||||
type = str;
|
||||
description = "WireGuard public key.";
|
||||
};
|
||||
|
||||
client = mkOption {
|
||||
type = nullOr (submodule clientOpts);
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
in mkOption {
|
||||
type = nullOr (submodule wireguardOpts);
|
||||
default = null;
|
||||
};
|
||||
|
||||
initrd-network = let
|
||||
keypair-type = { ... }: {
|
||||
options = {
|
||||
public-key = mkOption {
|
||||
type = str;
|
||||
description = "SSH public key.";
|
||||
};
|
||||
|
||||
private-key-file = mkOption {
|
||||
type = str;
|
||||
description = "Path to SSH private key (on the local host!).";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
initrd-network-config = { ... }: {
|
||||
options = {
|
||||
ip = mkOption {
|
||||
type = str;
|
||||
description =
|
||||
"IP to assign to the initrd image, allowing access to host during bootup.";
|
||||
};
|
||||
keypair = mkOption {
|
||||
type = (submodule keypair-type);
|
||||
description = "SSH host key pair to use for initrd.";
|
||||
};
|
||||
interface = mkOption {
|
||||
type = str;
|
||||
description =
|
||||
"Name of interface on which to listen for connections.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in mkOption {
|
||||
type = nullOr (submodule initrd-network-config);
|
||||
description =
|
||||
"Configuration parameters to set up initrd SSH network.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
backplane-password-file = mkOption {
|
||||
type = path;
|
||||
description =
|
||||
"File containing the password used by this host to connect to the backplane.";
|
||||
};
|
||||
|
||||
initrd-ssh-key = mkOption {
|
||||
type = nullOr str;
|
||||
description =
|
||||
"Path on the local filesystem to a host SSH key for initrd, if any.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
deploy = {
|
||||
enable = mkEnableOption "Enable deploy-rs deployments.";
|
||||
|
||||
ssh-options = mkOption {
|
||||
type = listOf str;
|
||||
description = "List of SSH options to use when deploying.";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -119,6 +119,13 @@ with lib; rec {
|
||||
description = "User's surname.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
config-user = mkOption {
|
||||
type = nullOr str;
|
||||
description =
|
||||
"When generating config, consider this to be the 'real' user.";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user