Move profiles from ./profile-config to ./profile
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
{ lib, ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
with pkgs.lib;
|
||||
let
|
||||
join-lines = concatStringsSep "\n";
|
||||
|
||||
|
||||
+9
-7
@@ -1,16 +1,18 @@
|
||||
{ lib, ... }:
|
||||
|
||||
let
|
||||
ip = import ./ip.nix { inherit lib; };
|
||||
dns = import ./dns.nix { inherit lib; };
|
||||
passwd = import ./passwd.nix { inherit lib; };
|
||||
in
|
||||
# NOTE: OBSOLETE! See overlay.nix
|
||||
|
||||
{
|
||||
lib.overlays = [
|
||||
(final: prev:
|
||||
prev.lib // {
|
||||
fudo = {
|
||||
inherit ip dns passwd;
|
||||
fudo = let
|
||||
lib = prev.lib;
|
||||
in {
|
||||
ip = import ./ip.nix { inherit lib; };
|
||||
dns = import ./dns.nix { inherit lib; };
|
||||
passwd = import ./passwd.nix { inherit lib; };
|
||||
lisp = import ./lisp.nix { inherit lib; };
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
+62
-23
@@ -30,7 +30,9 @@ let
|
||||
# };
|
||||
# };
|
||||
|
||||
domainOpts = { domain, ... }: {
|
||||
domainOpts = { name, ... }: let
|
||||
domain = name;
|
||||
in {
|
||||
options = with types; {
|
||||
email = mkOption {
|
||||
type = str;
|
||||
@@ -45,8 +47,12 @@ let
|
||||
};
|
||||
|
||||
local-copies = let
|
||||
localCopyOpts = { copy, ... }: {
|
||||
options = with types; {
|
||||
localCopyOpts = { name, ... }: let
|
||||
copy = name;
|
||||
in {
|
||||
options = with types; let
|
||||
target-path = "/var/run/${domain}/${copy}";
|
||||
in {
|
||||
user = mkOption {
|
||||
type = str;
|
||||
description = "User to which this copy belongs.";
|
||||
@@ -58,17 +64,35 @@ let
|
||||
default = null;
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = str;
|
||||
description = "Path at which to store the local copy.";
|
||||
default = "/var/run/${domain}/${copy}";
|
||||
};
|
||||
|
||||
service = mkOption {
|
||||
type = str;
|
||||
description = "systemd job to copy certs.";
|
||||
default = "fudo-${domain}-${copy}-certs.service";
|
||||
};
|
||||
|
||||
certificate = mkOption {
|
||||
type = str;
|
||||
description = "Full path to the local copy certificate.";
|
||||
default = "${target-path}/cert.pem";
|
||||
};
|
||||
|
||||
full-certificate = mkOption {
|
||||
type = str;
|
||||
description = "Full path to the local copy certificate.";
|
||||
default = "${target-path}/fullchain.pem";
|
||||
};
|
||||
|
||||
chain = mkOption {
|
||||
type = str;
|
||||
description = "Full path to the local copy certificate.";
|
||||
default = "${target-path}/chain.pem";
|
||||
};
|
||||
|
||||
private-key = mkOption {
|
||||
type = str;
|
||||
description = "Full path to the local copy certificate.";
|
||||
default = "${target-path}/key.pem";
|
||||
};
|
||||
};
|
||||
};
|
||||
in mkOption {
|
||||
@@ -92,7 +116,7 @@ let
|
||||
cfg.host-domains.${hostname} else {};
|
||||
|
||||
optionalStringOr = str: default:
|
||||
if cond then str else default;
|
||||
if (str != null) then str else default;
|
||||
|
||||
in {
|
||||
options.fudo.acme = with types; {
|
||||
@@ -113,26 +137,41 @@ in {
|
||||
tmpfiles.rules = let
|
||||
copies = concatMapAttrs (domain: domainOpts:
|
||||
domainOpts.local-copies) localDomains;
|
||||
perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500";
|
||||
copy-paths = mapAttrsToList (copy: copyOpts:
|
||||
"D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -")
|
||||
copies;
|
||||
in copy-paths;
|
||||
let
|
||||
dir-entry = copyOpts: file: "D '${dirOf file}' ${perms copyOpts} ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -";
|
||||
in map (dir-entry copyOpts) [
|
||||
copyOpts.certificate
|
||||
copyOpts.full-certificate
|
||||
copyOpts.chain
|
||||
copyOpts.private-key
|
||||
]) copies;
|
||||
in unique copy-paths;
|
||||
|
||||
# TODO: Make this a Fudo service?
|
||||
services = concatMapAttrs (domain: domainOpts:
|
||||
mapAttrs' (copy: copyOpts: let
|
||||
key-perms = copyOpts: if (copyOpts.group != null) then "0440" else "0400";
|
||||
source = config.security.acme.certs.${domain}.directory;
|
||||
target = copyOpts.path;
|
||||
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" ''
|
||||
for cert in cert chain fullchain full key; do
|
||||
cp ${source}/$cert.pem ${target}/$cert.pem
|
||||
chmod 0440 ${source}/$cert.pem
|
||||
done
|
||||
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${copy}-certs.sh" ''
|
||||
cp cert.pem ${copyOpts.certificate}
|
||||
chmod 0444 ${copyOpts.certificate}
|
||||
|
||||
cp full.pem ${copyOpts.full-certificate}
|
||||
chmod 0444 ${copyOpts.full-certificate}
|
||||
|
||||
cp chain.pem ${copyOpts.chain}
|
||||
chmod 0444 ${copyOpts.chain}
|
||||
|
||||
cp key.pem ${copyOpts.private-key}
|
||||
chmod ${key-perms copyOpts} ${copyOpts.private-key}
|
||||
'';
|
||||
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${site}-certs.sh" ''
|
||||
for cert in cert chain fullchain full key; do
|
||||
rm -rf ${target}/$cert.pem
|
||||
done
|
||||
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" ''
|
||||
rm -f ${copyOpts.private-key}
|
||||
rm -f ${copyOpts.chainy}
|
||||
rm -f ${copyOpts.full-certificate}
|
||||
rm -f ${copyOpts.certificate}
|
||||
'';
|
||||
in nameValuePair
|
||||
(rm-service-ext copyOpts.service) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{ ... }:
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
imports = [
|
||||
./dns.nix
|
||||
|
||||
+117
-193
@@ -1,63 +1,10 @@
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.backplane.dns;
|
||||
|
||||
lisp-pkgs = with pkgs.localLispPackages; [
|
||||
arrows
|
||||
backplane-dns
|
||||
backplane-server
|
||||
cl-sasl
|
||||
cl-xmpp
|
||||
ip-utils
|
||||
|
||||
alexandria
|
||||
babel
|
||||
bordeaux-threads
|
||||
cffi
|
||||
cl-base64
|
||||
cl-json
|
||||
cl-postgres
|
||||
cl-ppcre
|
||||
cl-unicode
|
||||
cl_plus_ssl
|
||||
closer-mop
|
||||
closure-common
|
||||
cxml
|
||||
flexi-streams
|
||||
global-vars
|
||||
introspect-environment
|
||||
ironclad
|
||||
iterate
|
||||
lisp-namespace
|
||||
md5
|
||||
nibbles
|
||||
postmodern
|
||||
puri
|
||||
s-sql
|
||||
split-sequence
|
||||
trivia
|
||||
trivia_dot_balland2006
|
||||
trivia_dot_level0
|
||||
trivia_dot_level1
|
||||
trivia_dot_level2
|
||||
trivia_dot_trivial
|
||||
trivial-cltl2
|
||||
trivial-features
|
||||
trivial-garbage
|
||||
trivial-gray-streams
|
||||
type-i
|
||||
uax-15
|
||||
usocket
|
||||
];
|
||||
|
||||
backup-directory = "/var/lib/fudo/backplane/dns";
|
||||
|
||||
powerdns-home = "/var/lib/powerdns";
|
||||
|
||||
powerdns-conf-dir = "${powerdns-home}/conf.d";
|
||||
powerdns-conf-dir = "${cfg.powerdns-home}/conf.d";
|
||||
|
||||
backplaneOpts = { ... }: {
|
||||
options = {
|
||||
@@ -117,53 +64,59 @@ let
|
||||
};
|
||||
|
||||
in {
|
||||
options.fudo.backplane.dns = {
|
||||
options.fudo.backplane.dns = with types; {
|
||||
enable = mkEnableOption "Enable backplane dynamic DNS server.";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
type = port;
|
||||
description = "Port on which to serve authoritative DNS requests.";
|
||||
default = 53;
|
||||
};
|
||||
|
||||
listen-v4-addresses = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "IPv4 addresses on which to listen for dns requests.";
|
||||
default = [ "0.0.0.0" ];
|
||||
};
|
||||
|
||||
listen-v6-addresses = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "IPv6 addresses on which to listen for dns requests.";
|
||||
example = [ "[abcd::1]" ];
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
required-services = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description =
|
||||
"A list of services required before the DNS server can start.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "User as which to run DNS backplane listener service.";
|
||||
default = "backplane-dns";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Group as which to run DNS backplane listener service.";
|
||||
default = "backplane-dns";
|
||||
};
|
||||
|
||||
database = mkOption {
|
||||
type = with types; submodule databaseOpts;
|
||||
type = submodule databaseOpts;
|
||||
description = "Database settings for the DNS server.";
|
||||
};
|
||||
|
||||
powerdns-home = mkOption {
|
||||
type = str;
|
||||
description = "Directory at which to store powerdns configuration and state.";
|
||||
default = "/run/backplane-dns/powerdns";
|
||||
};
|
||||
|
||||
backplane = mkOption {
|
||||
type = with types; submodule backplaneOpts;
|
||||
type = submodule backplaneOpts;
|
||||
description = "Backplane Jabber settings for the DNS server.";
|
||||
};
|
||||
};
|
||||
@@ -177,7 +130,11 @@ in {
|
||||
createHome = true;
|
||||
home = "/var/home/${cfg.user}";
|
||||
};
|
||||
backplane-powerdns = { isSystemUser = true; };
|
||||
backplane-powerdns = {
|
||||
isSystemUser = true;
|
||||
home = cfg.powerdns-home;
|
||||
createHome = true;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
@@ -186,140 +143,107 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
fudo.system.services = {
|
||||
backplane-powerdns-config-generator = {
|
||||
description =
|
||||
"Generate postgres configuration for backplane DNS server.";
|
||||
requires = cfg.required-services;
|
||||
type = "oneshot";
|
||||
restartIfChanged = true;
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
|
||||
readWritePaths = [ powerdns-conf-dir ];
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${powerdns-conf-dir}
|
||||
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
|
||||
'';
|
||||
|
||||
# This builds the config in a bash script, to avoid storing the password
|
||||
# in the nix store at any point
|
||||
script = ''
|
||||
if [ ! -d ${powerdns-conf-dir} ]; then
|
||||
mkdir ${powerdns-conf-dir}
|
||||
fi
|
||||
|
||||
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
|
||||
TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
|
||||
|
||||
if [ ! -f ${cfg.database.password-file} ]; then
|
||||
echo "${cfg.database.password-file} does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch $TMPCONF
|
||||
chown backplane-powerdns:backplane-powerdns $TMPCONF
|
||||
chmod go-rwx $TMPCONF
|
||||
PASSWORD=$(cat ${cfg.database.password-file})
|
||||
echo "launch+=gpgsql" >> $TMPCONF
|
||||
echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
|
||||
echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
|
||||
echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
|
||||
echo "gpgsql-password=$PASSWORD" >> $TMPCONF
|
||||
echo "gpgsql-dnssec=yes" >> $TMPCONF
|
||||
|
||||
mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
|
||||
rm -rf $TMPDIR
|
||||
|
||||
exit 0
|
||||
'';
|
||||
};
|
||||
|
||||
backplane-powerdns = let
|
||||
pdns-config-dir = pkgs.writeTextDir "pdns.conf" ''
|
||||
local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses}
|
||||
local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses}
|
||||
local-port=${toString cfg.port}
|
||||
launch=
|
||||
include-dir=${powerdns-conf-dir}/
|
||||
'';
|
||||
in {
|
||||
description = "Backplane PowerDNS name server";
|
||||
requires = [
|
||||
"postgresql.service"
|
||||
"backplane-powerdns-config-generator.service"
|
||||
];
|
||||
after = [ "network.target" ];
|
||||
path = with pkgs; [ powerdns postgresql ];
|
||||
execStart = "pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${cfg.powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${pdns-config-dir}";
|
||||
};
|
||||
|
||||
backplane-dns = {
|
||||
description = "Fudo DNS Backplane Server";
|
||||
restartIfChanged = true;
|
||||
path = with pkgs; [ backplane-dns-server ];
|
||||
execStart = "launch-backplane-dns.sh";
|
||||
pidFile = "/run/backplane-dns.$USERNAME.pid";
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
environment = {
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
|
||||
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
|
||||
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
|
||||
cfg.backplane.database.username;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
|
||||
cfg.backplane.database.password-file;
|
||||
|
||||
CL_SOURCE_REGISTRY =
|
||||
pkgs.lib.fudo.lisp.lisp-source-registry pkgs.backplane-dns-server;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
targets = {
|
||||
backplane-dns = {
|
||||
description = "Fudo DNS backplane services.";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
|
||||
backplane-powerdns = let
|
||||
configDir = pkgs.writeTextDir "pdns.conf" ''
|
||||
local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses}
|
||||
local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses}
|
||||
local-port=${toString cfg.port}
|
||||
launch=
|
||||
include-dir=${powerdns-conf-dir}/
|
||||
'';
|
||||
|
||||
psql-user = config.services.postgresql.superUser;
|
||||
|
||||
in {
|
||||
unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)";
|
||||
description = "Backplane PowerDNS name server";
|
||||
requires = [
|
||||
"postgresql.service"
|
||||
"backplane-dns-config-generator.service"
|
||||
"backplane-dns.target"
|
||||
];
|
||||
after = [ "network.target" "postgresql.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
path = with pkgs; [ postgresql ];
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10";
|
||||
StartLimitInterval = "0";
|
||||
PrivateDevices = true;
|
||||
# CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT";
|
||||
# NoNewPrivileges=true;
|
||||
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${powerdns-home}";
|
||||
ExecStart =
|
||||
"${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
|
||||
ProtectSystem = "full";
|
||||
# ProtectHome=true;
|
||||
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
};
|
||||
};
|
||||
|
||||
backplane-dns-config-generator = {
|
||||
description =
|
||||
"Generate postgres configuration for backplane DNS server.";
|
||||
requiredBy = [ "backplane-powerdns.service" ];
|
||||
requires = cfg.required-services;
|
||||
serviceConfig.Type = "oneshot";
|
||||
restartIfChanged = true;
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${powerdns-conf-dir}
|
||||
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
|
||||
'';
|
||||
|
||||
# This builds the config in a bash script, to avoid storing the password
|
||||
# in the nix store at any point
|
||||
script = ''
|
||||
if [ ! -d ${powerdns-conf-dir} ]; then
|
||||
mkdir ${powerdns-conf-dir}
|
||||
fi
|
||||
|
||||
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
|
||||
TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
|
||||
|
||||
if [ ! -f ${cfg.database.password-file} ]; then
|
||||
echo "${cfg.database.password-file} does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch $TMPCONF
|
||||
chown backplane-powerdns:backplane-powerdns $TMPCONF
|
||||
chmod go-rwx $TMPCONF
|
||||
PASSWORD=$(cat ${cfg.database.password-file})
|
||||
echo "launch+=gpgsql" >> $TMPCONF
|
||||
echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
|
||||
echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
|
||||
echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
|
||||
echo "gpgsql-password=$PASSWORD" >> $TMPCONF
|
||||
echo "gpgsql-dnssec=yes" >> $TMPCONF
|
||||
|
||||
mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
|
||||
|
||||
rm -rf $TMPDIR
|
||||
|
||||
exit 0
|
||||
'';
|
||||
};
|
||||
|
||||
backplane-dns = {
|
||||
description = "Fudo DNS Backplane Server";
|
||||
restartIfChanged = true;
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
"${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh";
|
||||
Restart = "on-failure";
|
||||
PIDFile = "/run/backplane-dns.$USERNAME.pid";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StandardOutput = "journal";
|
||||
};
|
||||
|
||||
environment = {
|
||||
# LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
|
||||
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
|
||||
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
|
||||
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
|
||||
cfg.backplane.database.username;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
|
||||
cfg.backplane.database.password-file;
|
||||
|
||||
# CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//";
|
||||
|
||||
CL_SOURCE_REGISTRY =
|
||||
lib.concatStringsSep ":" (map (pkg: "${pkg}//") lisp-pkgs);
|
||||
};
|
||||
|
||||
requires = cfg.required-services;
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requries = cfg.required-services ++ [ "postgresql.service" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ in {
|
||||
enable = true;
|
||||
appName = cfg.site-name;
|
||||
database = {
|
||||
createDatabase = true;
|
||||
createDatabase = false;
|
||||
host = cfg.database.hostname;
|
||||
name = cfg.database.name;
|
||||
user = cfg.database.user;
|
||||
|
||||
@@ -34,8 +34,9 @@ in {
|
||||
systemd = {
|
||||
# Ensure the mountpoints exist
|
||||
tmpfiles.rules = let
|
||||
mpPerms = mpOpts: if mpOpts.world-readable then "755" else "750";
|
||||
mountpointToPath = mp: mpOpts:
|
||||
"d '${mp}' 750 root ${optionalOrDefault mpOpts.group "-"} - -";
|
||||
"d '${mp}' ${mpPerms mpOpts} root ${optionalOrDefault mpOpts.group "-"} - -";
|
||||
filesystemsToMountpointLists = mapAttrsToList
|
||||
(fs: fsOpts: fsOpts.mountpoints);
|
||||
mountpointListsToPaths = concatMap
|
||||
|
||||
+11
-9
@@ -6,6 +6,10 @@ let
|
||||
|
||||
host = import ../types/host.nix { inherit lib; };
|
||||
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||
|
||||
in {
|
||||
options.fudo.hosts = with types;
|
||||
mkOption {
|
||||
@@ -73,6 +77,8 @@ in {
|
||||
config.environment.systemPackages;
|
||||
sorted-unique = sort lessThan (unique packages);
|
||||
in concatStringsSep "\n" sorted-unique;
|
||||
|
||||
build-timestamp.text = toString config.instance.build-timestamp;
|
||||
};
|
||||
|
||||
systemPackages = with pkgs;
|
||||
@@ -103,10 +109,6 @@ in {
|
||||
(keypair: keypair.private-key)
|
||||
(try-attr hostname files.build-keypairs);
|
||||
|
||||
backplane-passwd-source = try-attr hostname files.backplane-passwords;
|
||||
|
||||
backplane-passwd-target = "/var/run/backplane/passwd";
|
||||
|
||||
in {
|
||||
secrets.host-secrets.${hostname} = {
|
||||
host-keytab = mkIf (keytab-file != null) {
|
||||
@@ -121,15 +123,15 @@ in {
|
||||
user = "root";
|
||||
};
|
||||
|
||||
backplane-passwd = mkIf (backplane-passwd-source != null) {
|
||||
source-file = backplane-passwd-source;
|
||||
target-file = backplane-passwd-target;
|
||||
backplane-passwd = {
|
||||
source-file = host-cfg.backplane-password-file;
|
||||
target-file = "/run/backplane/client/passwd";
|
||||
user = config.fudo.client.dns.user;
|
||||
};
|
||||
};
|
||||
|
||||
client.dns.password-file = mkIf (backplane-passwd-source != null)
|
||||
backplane-passwd-target;
|
||||
client.dns.password-file =
|
||||
host-secrets.backplane-passwd.target-file;
|
||||
};
|
||||
|
||||
programs.adb.enable = host-cfg.android-dev;
|
||||
|
||||
+67
-23
@@ -23,16 +23,16 @@ let
|
||||
foldr (a: b: a // b) {} (mapAttrs f attrs);
|
||||
|
||||
concatMapAttrsToList = f: attr:
|
||||
attrValues (concatMapAttrs f attr);
|
||||
concatMap (i: i) (attrValues (mapAttrs f attr));
|
||||
|
||||
host-domains = config.fudo.acme.host-domains.${hostname};
|
||||
|
||||
siteCerts = site: let
|
||||
certPath = host-domains.${site}.local-copies.ejabberd.path;
|
||||
cert-copy = host-domains.${site}.local-copies.ejabberd;
|
||||
in [
|
||||
"${certPath}/fullchain.pem"
|
||||
"${certPath}/privkey.pem"
|
||||
"${certPath}/chain.pem"
|
||||
cert-copy.certificate
|
||||
cert-copy.private-key
|
||||
cert-copy.chain
|
||||
];
|
||||
|
||||
siteCertService = site:
|
||||
@@ -60,13 +60,13 @@ let
|
||||
|
||||
hosts = attrNames cfg.sites;
|
||||
|
||||
listen = [{
|
||||
listen = map (ip: {
|
||||
port = cfg.port;
|
||||
module = "ejabberd_c2s";
|
||||
ip = cfg.listen-ip;
|
||||
ip = ip;
|
||||
starttls = true;
|
||||
starttls_required = true;
|
||||
}];
|
||||
}) cfg.listen-ips;
|
||||
|
||||
certfiles = concatMapAttrsToList
|
||||
(site: siteOpts:
|
||||
@@ -94,7 +94,6 @@ let
|
||||
swapper = concatStringsSep " | " secret-swappers;
|
||||
in pkgs.writeShellScript "ejabberd-generate-config.sh" ''
|
||||
cat ${template} | ${swapper} > ${target}
|
||||
chown ${cfg.user}:${cfg.group} ${target}
|
||||
'';
|
||||
|
||||
cfg = config.fudo.jabber;
|
||||
@@ -102,6 +101,11 @@ let
|
||||
in {
|
||||
options.fudo.jabber = with types; {
|
||||
enable = mkEnableOption "Enable ejabberd server.";
|
||||
|
||||
listen-ips = mkOption {
|
||||
type = listOf str;
|
||||
description = "IPs on which to listen for Jabber connections.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
@@ -122,7 +126,7 @@ in {
|
||||
};
|
||||
|
||||
admins = mkOption {
|
||||
type = str;
|
||||
type = listOf str;
|
||||
description = "List of admin users for the server.";
|
||||
default = [];
|
||||
};
|
||||
@@ -141,7 +145,23 @@ in {
|
||||
config-file = mkOption {
|
||||
type = str;
|
||||
description = "Location at which to generate the configuration file.";
|
||||
default = "/var/run/ejabberd/ejabberd.yaml";
|
||||
default = "/run/ejabberd/ejabberd.yaml";
|
||||
};
|
||||
|
||||
log-level = mkOption {
|
||||
type = int;
|
||||
description = ''
|
||||
Log level at which to run the server.
|
||||
|
||||
See: https://docs.ejabberd.im/admin/guide/troubleshooting/
|
||||
'';
|
||||
default = 3;
|
||||
};
|
||||
|
||||
environment = mkOption {
|
||||
type = attrsOf str;
|
||||
description = "Environment variables to set for the ejabberd daemon.";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,26 +176,50 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
fudo.acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
|
||||
mkIf siteCfg.enableACME {
|
||||
local-copies.ejabberd = {
|
||||
fudo = {
|
||||
acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
|
||||
mkIf siteCfg.enableACME {
|
||||
local-copies.ejabberd = {
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
};
|
||||
}) cfg.sites;
|
||||
|
||||
system = let
|
||||
config-dir = dirOf cfg.config-file;
|
||||
in {
|
||||
ensure-directories.${config-dir} = {
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
perms = "0700";
|
||||
};
|
||||
}) cfg.sites;
|
||||
|
||||
services.ejabberd-config-generator = let
|
||||
config-generator =
|
||||
enter-secrets config-file-template cfg.secret-files cfg.config-file;
|
||||
in {
|
||||
script = "${config-generator}";
|
||||
readWritePaths = [ config-dir ];
|
||||
workingDirectory = config-dir;
|
||||
user = cfg.user;
|
||||
description = "Generate ejabberd config file with necessary passwords.";
|
||||
postStart = ''
|
||||
chown ${cfg.user} ${cfg.config-file}
|
||||
chmod 0400 ${cfg.config-file}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
tmpfiles.rules = [
|
||||
"D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
services.ejabberd = let
|
||||
config-generator = enter-secrets config-file-template cfg.secret-files cfg.config-file;
|
||||
in {
|
||||
wants = map (site: siteCertService site) (attrNames cfg.sites);
|
||||
environment = cfg.secret-files;
|
||||
serviceConfig = {
|
||||
ExecStartPre = mkAfter "${config-generator}";
|
||||
services = {
|
||||
ejabberd = {
|
||||
wants = map (site: siteCertService site) (attrNames cfg.sites);
|
||||
requires = [ "ejabberd-config-generator.service" ];
|
||||
environment = cfg.environment;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+123
-71
@@ -205,6 +205,7 @@ in {
|
||||
|
||||
ssl-ca-certificate = mkOption {
|
||||
type = with types; nullOr str;
|
||||
|
||||
description = ''
|
||||
The path to the SSL CA cert used to sign the certificate.
|
||||
'';
|
||||
@@ -220,9 +221,8 @@ in {
|
||||
|
||||
base = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
The base dn of the LDAP server (eg. "dc=fudo,dc=org").
|
||||
'';
|
||||
description = "The base dn of the LDAP server.";
|
||||
example = "dc=fudo,dc=org";
|
||||
};
|
||||
|
||||
rootpw-file = mkOption {
|
||||
@@ -239,7 +239,6 @@ in {
|
||||
A list of URIs on which the ldap server should listen.
|
||||
'';
|
||||
example = [ "ldap://auth.fudo.org" "ldaps://auth.fudo.org" ];
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
@@ -295,12 +294,11 @@ in {
|
||||
etc = {
|
||||
"openldap/sasl2/slapd.conf" = {
|
||||
mode = "0400";
|
||||
user = "openldap";
|
||||
group = "openldap";
|
||||
# FIXME: take arguments!
|
||||
user = config.services.openldap.user;
|
||||
group = config.services.openldap.group;
|
||||
text = ''
|
||||
mech_list: gssapi external
|
||||
keytab: /etc/ldap/ldap.keytab
|
||||
keytab: ${cfg.kerberos-keytab}
|
||||
'';
|
||||
};
|
||||
};
|
||||
@@ -343,72 +341,126 @@ in {
|
||||
rootdn = "cn=admin,${cfg.base}";
|
||||
rootpwFile = "${cfg.rootpw-file}";
|
||||
urlList = cfg.listen-uris;
|
||||
database = "mdb";
|
||||
|
||||
extraConfig = ''
|
||||
settings = let
|
||||
makeAccessLine = i: attrs: perm-map: let
|
||||
perm-strings = mapAttrs (dn: perm: "by ${dn} ${perm}") perm-map;
|
||||
perm-string = concatStringsSep " " perm-strings;
|
||||
in "${i}to ${attrs} ${perm-string}";
|
||||
|
||||
TLSCertificateFile ${cfg.ssl-certificate}
|
||||
TLSCertificateKeyFile ${cfg.ssl-private-key}
|
||||
${optionalString (cfg.ssl-ca-certificate != null)
|
||||
"TLSCACertificateFile ${cfg.ssl-ca-certificate}"}
|
||||
makeAccess = access-map: let
|
||||
pairs = mapAttrsToList (target: perm-map: [target perm-map]) access-map;
|
||||
in imap0 (i: pair: makeAccessLine i pair[0] pair[1]) pairs;
|
||||
|
||||
authz-regexp "^uid=auth/([^.]+)\.fudo\.org,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org"
|
||||
authz-regexp "^uid=[^,/]+/root,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=admin,dc=fudo,dc=org"
|
||||
authz-regexp "^uid=([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "uid=$1,ou=members,dc=fudo,dc=org"
|
||||
authz-regexp "^uid=host/([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org"
|
||||
authz-regexp "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$" "cn=admin,dc=fudo,dc=org"
|
||||
|
||||
'';
|
||||
|
||||
extraDatabaseConfig = ''
|
||||
# access to dn=base=""
|
||||
# by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
||||
# by * read
|
||||
|
||||
access to attrs=userPassword,shadowLastChange
|
||||
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
||||
by group.exact="cn=admin,ou=members,${cfg.base}" write
|
||||
by dn.exact="cn=auth_reader,${cfg.base}" read
|
||||
by dn.exact="cn=replicator,${cfg.base}" read
|
||||
by self write
|
||||
by * auth
|
||||
|
||||
access to dn.exact="cn=admin,ou=groups,${cfg.base}"
|
||||
by dn.exact="cn=admin,${cfg.base}" write
|
||||
by users read
|
||||
by * none
|
||||
|
||||
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid
|
||||
by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write
|
||||
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
||||
by users read
|
||||
by * none
|
||||
|
||||
access to dn.subtree="ou=members,${cfg.base}" attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber
|
||||
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
||||
by dn.exact="cn=user_db_reader,${cfg.base}" read
|
||||
by users read
|
||||
by * none
|
||||
|
||||
access to dn.exact="cn=admin,ou=groups,${cfg.base}"
|
||||
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
||||
by users read
|
||||
by * none
|
||||
|
||||
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid
|
||||
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
||||
by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write
|
||||
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
||||
by users read
|
||||
by * none
|
||||
|
||||
access to *
|
||||
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
||||
by users read
|
||||
by * none
|
||||
|
||||
|
||||
index objectClass,uid eq
|
||||
'';
|
||||
in {
|
||||
attrs = {
|
||||
cn = "config";
|
||||
objectClass = "olcGlobal";
|
||||
olcPidFile = "/run/slapd/slapd.pid";
|
||||
olcTLSCertificateFile = cfg.ssl-certificate;
|
||||
olcTLSCertificateKeyFile = cfg.ssl-private-key;
|
||||
olcTLSCACertificateFile = cfg.ssl-ca-certificate;
|
||||
olcSaslSecProps = "noplain,noanonymous";
|
||||
olcAuthzRegexp = let
|
||||
authz-regex-entry = i: { regex, target}:
|
||||
"{${i}}\"${rx}\" \"${target}\"";
|
||||
in imap0 authz-regex-entry [
|
||||
{
|
||||
regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$";
|
||||
target = "cn=$1,ou=hosts,dc=fudo,dc=org";
|
||||
}
|
||||
{
|
||||
regex = "^uid=[^,/]+/root,cn=fudo.org,cn=gssapi,cn=auth$";
|
||||
target = "cn=admin,dc=fudo,dc=org";
|
||||
}
|
||||
{
|
||||
regex = "^uid=([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$";
|
||||
target = "uid=$1,ou=members,dc=fudo,dc=org";
|
||||
}
|
||||
{
|
||||
regex = "^uid=host/([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$";
|
||||
target = "cn=$1,ou=hosts,dc=fudo,dc=org";
|
||||
}
|
||||
{
|
||||
regex = "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$";
|
||||
target = "cn=admin,dc=fudo,dc=org";
|
||||
}
|
||||
];
|
||||
};
|
||||
children = {
|
||||
"olcDatabase{-1}frontend" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ];
|
||||
olcDatabase = "{-1}frontend";
|
||||
olcAccess = makeAccess {
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,dc=fudo,dc=org" = "manage";
|
||||
"*" = "none";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
"olcDatabase{0}config" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" ];
|
||||
olcDatabase = "{0}config";
|
||||
olcAccess = [ "by * none" ];
|
||||
};
|
||||
};
|
||||
"olcDatabase{1}mdb" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
||||
olcDatabase = "{1}mdb";
|
||||
olcSuffix = cfg.base;
|
||||
# olcRootDN = "cn=admin,${cfg.base}";
|
||||
# olcRootPW = FIXME; # NOTE: this should be hashed...
|
||||
olcDbDirectory = cfg.database-directory;
|
||||
olcDbIndex = [ "objectClass eq" "uid eq" ];
|
||||
olcAccess = makeAccess {
|
||||
"attrs=userPassword,shadowLastChange" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"dn.exact=cn=auth_reader,${cfg.base}" = "read";
|
||||
"dn.exact=cn=replicator,${cfg.base}" = "read";
|
||||
"self" = "write";
|
||||
"*" = "auth";
|
||||
};
|
||||
"dn.base=cn=admin,ou=groups,${cfg.base}" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
};
|
||||
"dn.subtree=ou=groups,${cfg.base} attrs=memberUid" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write";
|
||||
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
};
|
||||
"dn.subtree=ou=members,${cfg.base} attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"dn.exact=cn=user_db_reader,${cfg.base}" = "read";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
};
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
|
||||
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "read";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
declarativeContents = ''
|
||||
dn: ${cfg.base}
|
||||
|
||||
+85
-72
@@ -1,67 +1,32 @@
|
||||
{ lib, config, ... }:
|
||||
with lib;
|
||||
let
|
||||
hostname = config.instance.hostname;
|
||||
cfg = config.fudo.mail-server;
|
||||
container-maildir = "/var/lib/mail";
|
||||
container-statedir = "/var/lib/mail-state";
|
||||
container-shared = "container/mail-server";
|
||||
container-postfix-cert = "${container-shared}/postfix/cert.pem";
|
||||
container-postfix-key = "${container-shared}/postfix/key.pem";
|
||||
container-dovecot-cert = "${container-shared}/dovecot/cert.pem";
|
||||
container-dovecot-key = "${container-shared}/dovecot/key.pem";
|
||||
container-fudo-ca-cert = "${container-shared}/fudo-ca.pem";
|
||||
|
||||
# Don't bother with group-id, nixos doesn't seem to use it anyway
|
||||
container-mail-user = "mailer";
|
||||
container-mail-user-id = 542;
|
||||
container-mail-group = "mailer";
|
||||
fudo-cfg = config.fudo.common;
|
||||
|
||||
in rec {
|
||||
options.fudo.mail-server.container = {
|
||||
ldap-url = mkOption {
|
||||
type = types.str;
|
||||
description = "URL of the LDAP server to use for authentication.";
|
||||
example = "ldaps://auth.fudo.org/";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.enableContainer && !cfg.enable) {
|
||||
|
||||
# Disable postfix on thi host--it'll be run in the container instead
|
||||
config = mkIf (cfg.enableContainer) {
|
||||
# Disable postfix on this host--it'll be run in the container instead
|
||||
services.postfix.enable = false;
|
||||
|
||||
# Copy data intended for the container to a path in /etc which can be
|
||||
# bind-mounted.
|
||||
environment.etc = {
|
||||
"${container-postfix-cert}" = {
|
||||
mode = "0444";
|
||||
source = cfg.postfix.ssl-certificate;
|
||||
};
|
||||
|
||||
"${container-postfix-key}" = {
|
||||
mode = "0400";
|
||||
source = cfg.postfix.ssl-private-key;
|
||||
};
|
||||
|
||||
"${container-dovecot-cert}" = {
|
||||
mode = "0444";
|
||||
source = cfg.dovecot.ssl-certificate;
|
||||
};
|
||||
|
||||
"${container-dovecot-key}" = {
|
||||
mode = "0400";
|
||||
source = cfg.dovecot.ssl-private-key;
|
||||
};
|
||||
|
||||
"${container-fudo-ca-cert}" = {
|
||||
mode = "0444";
|
||||
source = "/etc/nixos/static/fudo_ca.pem";
|
||||
fudo.acme.host-domains.${hostname}.${cfg.mail-hostname} = {
|
||||
local-copies = {
|
||||
postfix = {
|
||||
user = "root";
|
||||
};
|
||||
dovecot-cert = {
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email;
|
||||
|
||||
services.nginx = mkIf cfg.monitoring {
|
||||
enable = true;
|
||||
|
||||
@@ -71,14 +36,15 @@ in rec {
|
||||
proxy_set_header Host $host;
|
||||
'';
|
||||
trusted-network-string =
|
||||
optionalString ((length fudo-cfg.local-networks) > 0)
|
||||
optionalString ((length config.instance.local-networks) > 0)
|
||||
(concatStringsSep "\n"
|
||||
(map (network: "allow ${network};") fudo-cfg.local-networks)) + ''
|
||||
(map (network: "allow ${network};")
|
||||
config.instance.local-networks)) + ''
|
||||
|
||||
deny all;'';
|
||||
|
||||
in {
|
||||
"${cfg.hostname}" = {
|
||||
"${cfg.mail-hostname}" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
@@ -119,7 +85,9 @@ in rec {
|
||||
|
||||
autoStart = true;
|
||||
|
||||
bindMounts = {
|
||||
bindMounts = let
|
||||
cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies;
|
||||
in {
|
||||
"${container-maildir}" = {
|
||||
hostPath = cfg.mail-directory;
|
||||
isReadOnly = false;
|
||||
@@ -134,30 +102,74 @@ in rec {
|
||||
hostPath = "/etc/${container-shared}";
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/postfix/cert.pem" = {
|
||||
hostPath = cert-copies.postfix.certificate;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/postfix/key.pem" = {
|
||||
hostPath = cert-copies.postfix.private-key;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/dovecot/cert.pem" = {
|
||||
hostPath = cert-copies.dovecot.certificate;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/dovecot/key.pem" = {
|
||||
hostPath = cert-copies.dovecot.private-key;
|
||||
isReadOnly = true;
|
||||
};
|
||||
};
|
||||
|
||||
imports = let
|
||||
initialize-host = import ../../initialize-host.nix;
|
||||
build-timestamp = config.instance.build-timestamp;
|
||||
site = config.instance.site;
|
||||
domain = config.instance.domain;
|
||||
profile = "container";
|
||||
in [
|
||||
(initialize-host {
|
||||
inherit
|
||||
lib
|
||||
pkgs
|
||||
build-timestamp
|
||||
site
|
||||
domain
|
||||
profile;
|
||||
hostname = "mail-container";
|
||||
})
|
||||
];
|
||||
|
||||
config = { config, pkgs, ... }: {
|
||||
|
||||
environment.systemPackages = with pkgs; [ nmap ];
|
||||
|
||||
imports = [ ./mail.nix ];
|
||||
|
||||
environment = {
|
||||
etc = {
|
||||
"postfix-certs/key.pem" = {
|
||||
source = "/etc/${container-postfix-key}";
|
||||
user = config.services.postfix.user;
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
"dovecot-certs/key.pem" = {
|
||||
source = "/etc/${container-dovecot-key}";
|
||||
user = config.services.dovecot2.user;
|
||||
mode = "0400";
|
||||
};
|
||||
environment.etc = {
|
||||
"mail-server/postfix/cert.pem" = {
|
||||
source = "/run/mail/certs/postfix/cert.pem";
|
||||
user = config.services.postfix.user;
|
||||
mode = "0444";
|
||||
};
|
||||
"mail-server/postfix/key.pem" = {
|
||||
source = "/run/mail/certs/postfix/key.pem";
|
||||
user = config.services.postfix.user;
|
||||
mode = "0400";
|
||||
};
|
||||
"mail-server/dovecot/cert.pem" = {
|
||||
source = "/run/mail/certs/dovecot/cert.pem";
|
||||
user = config.services.dovecot.user;
|
||||
mode = "0444";
|
||||
};
|
||||
"mail-server/dovecot/key.pem" = {
|
||||
source = "/run/mail/certs/dovecot/key.pem";
|
||||
user = config.services.dovecot.user;
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
|
||||
imports = [ ./mail.nix ];
|
||||
|
||||
fudo.mail-server = {
|
||||
enable = true;
|
||||
hostname = cfg.hostname;
|
||||
@@ -169,14 +181,15 @@ in rec {
|
||||
state-directory = container-statedir;
|
||||
mail-directory = container-maildir;
|
||||
|
||||
postfix.ssl-certificate = "/etc/${container-postfix-cert}";
|
||||
postfix.ssl-private-key = "/etc/postfix-certs/key.pem";
|
||||
postfix = {
|
||||
ssl-certificate = "/etc/mail-server/postfix/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/postfix/key.pem";
|
||||
};
|
||||
|
||||
dovecot = {
|
||||
ssl-certificate = "/etc/${container-dovecot-cert}";
|
||||
ssl-private-key = "/etc/dovecot-certs/key.pem";
|
||||
ssl-certificate = "/etc/mail-server/dovecot/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/dovecot/key.pem";
|
||||
ldap = {
|
||||
# ca = "/etc/${container-fudo-ca-cert}";
|
||||
server-urls = cfg.dovecot.ldap.server-urls;
|
||||
reader-dn = cfg.dovecot.ldap.reader-dn;
|
||||
reader-passwd = cfg.dovecot.ldap.reader-passwd;
|
||||
|
||||
+9
-2
@@ -21,11 +21,18 @@ in {
|
||||
description = "The main and default domain name for this email server.";
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
mail-hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "The domain name to use for the mail server.";
|
||||
};
|
||||
|
||||
|
||||
ldap-url = mkOption {
|
||||
type = types.str;
|
||||
description = "URL of the LDAP server to use for authentication.";
|
||||
example = "ldaps://auth.fudo.org/";
|
||||
};
|
||||
|
||||
monitoring = mkEnableOption "Enable monitoring for the mail server.";
|
||||
|
||||
mail-user = mkOption {
|
||||
@@ -176,7 +183,7 @@ in {
|
||||
./mail/clamav.nix
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
config = mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 25 110 143 587 993 995 ];
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ with lib;
|
||||
let
|
||||
inherit (lib.strings) concatStringsSep;
|
||||
cfg = config.fudo.prometheus;
|
||||
fudo-cfg = config.fudo.common;
|
||||
|
||||
in {
|
||||
|
||||
@@ -76,9 +75,6 @@ in {
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email;
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
@@ -111,7 +107,8 @@ in {
|
||||
|
||||
webExternalUrl = "https://${cfg.hostname}";
|
||||
|
||||
listenAddress = "127.0.0.1:9090";
|
||||
listenAddress = "127.0.0.1";
|
||||
port = 9090;
|
||||
|
||||
scrapeConfigs = [
|
||||
{
|
||||
|
||||
@@ -9,7 +9,10 @@ let
|
||||
webmail-user = cfg.user;
|
||||
webmail-group = cfg.group;
|
||||
|
||||
base-data-path = "/var/run/rainloop";
|
||||
base-data-path = "/run/rainloop";
|
||||
|
||||
concatMapAttrs = f: attrs:
|
||||
foldr (a: b: a // b) {} (mapAttrsToList f attrs);
|
||||
|
||||
fastcgi-conf = builtins.toFile "fastcgi.conf" ''
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
@@ -301,7 +304,7 @@ in {
|
||||
site-config-file = builtins.toFile "${site}-rainloop.cfg"
|
||||
(import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version);
|
||||
|
||||
domain-cfg-file = builtins.toFile "${site}-domain.cfg" ''
|
||||
domain-config-file = builtins.toFile "${site}-domain.cfg" ''
|
||||
imap_host = "${site-cfg.mail-server}"
|
||||
imap_port = 143
|
||||
imap_secure = "TLS"
|
||||
|
||||
+18
-10
@@ -1,16 +1,24 @@
|
||||
{ lib, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
let
|
||||
hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename;
|
||||
|
||||
is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null;
|
||||
is-regular-file = filename: type: type == "regular" || type == "link";
|
||||
|
||||
host-files = host-path:
|
||||
attrNames
|
||||
(filterAttrs is-nix-file
|
||||
(filterAttrs is-regular-file
|
||||
(builtins.readDir host-path)));
|
||||
|
||||
hosts = host-path:
|
||||
map hostname-from-file (host-files host-path);
|
||||
in {
|
||||
base-host-config = host-path: let
|
||||
hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename;
|
||||
|
||||
is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null;
|
||||
is-regular-file = filename: type: type == "regular" || type == "link";
|
||||
|
||||
host-files = attrNames (filterAttrs is-nix-file (filterAttrs is-regular-file (builtins.readDir host-path)));
|
||||
hosts = map hostname-from-file host-files;
|
||||
|
||||
load-host-file = hostname: import (host-path + "/${hostname}.nix");
|
||||
in genAttrs hosts (hostname: load-host-file hostname);
|
||||
in genAttrs (hosts host-path) (hostname: load-host-file hostname);
|
||||
|
||||
host-list = host-path: hosts host-path;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ with lib;
|
||||
let
|
||||
cfg = config.informis.cl-gemini;
|
||||
|
||||
lisp-helper = import ../lisp.nix { inherit pkgs; };
|
||||
|
||||
feedOpts = { ... }: with types; {
|
||||
options = {
|
||||
url = mkOption {
|
||||
@@ -163,7 +161,7 @@ in {
|
||||
GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive;
|
||||
GEMINI_FEEDS = "${generate-feeds cfg.feeds}";
|
||||
|
||||
CL_SOURCE_REGISTRY = "${lisp-helper.lisp-source-registry pkgs.cl-gemini}";
|
||||
CL_SOURCE_REGISTRY = "${pkgs.lib.fudo.lisp.lisp-source-registry pkgs.cl-gemini}";
|
||||
};
|
||||
|
||||
path = with pkgs; [
|
||||
|
||||
+18
-7
@@ -51,6 +51,11 @@ in {
|
||||
type = attrsOf (submodule user.userOpts);
|
||||
description = "List of users who should have access to the local host";
|
||||
};
|
||||
|
||||
build-seed = mkOption {
|
||||
type = str;
|
||||
description = "Seed used to generate configuration.";
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
@@ -86,15 +91,21 @@ in {
|
||||
config.fudo.domains.${local-domain}.local-networks //
|
||||
config.fudo.sites.${local-site}.local-networks;
|
||||
|
||||
local-profile = host.profile;
|
||||
|
||||
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
|
||||
|
||||
in {
|
||||
instance = {
|
||||
local-domain = local-domain;
|
||||
local-site = local-site;
|
||||
local-users = local-users;
|
||||
local-admins = local-admins;
|
||||
local-groups = local-groups;
|
||||
local-hosts = local-hosts;
|
||||
local-profile = host.profile;
|
||||
inherit
|
||||
build-seed
|
||||
local-domain
|
||||
local-site
|
||||
local-users
|
||||
local-admins
|
||||
local-groups
|
||||
local-hosts
|
||||
local-profile;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{ lib, ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
with pkgs.lib;
|
||||
let
|
||||
pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1));
|
||||
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
let
|
||||
in rec {
|
||||
rec {
|
||||
gather-dependencies = pkg: unique (pkg.propagatedBuildInputs ++ (concatMap gather-dependencies pkg.propagatedBuildInputs));
|
||||
|
||||
lisp-source-registry = pkg: concatStringsSep ":" (map (p: "${p}//") (gather-dependencies pkg));
|
||||
|
||||
+8
-6
@@ -1,10 +1,12 @@
|
||||
(final: prev: let
|
||||
ip = import ./ip.nix { lib = prev.lib; };
|
||||
dns = import ./dns.nix { lib = prev.lib; };
|
||||
in {
|
||||
(final: prev: {
|
||||
lib = prev.lib // {
|
||||
fudo = {
|
||||
inherit ip dns;
|
||||
fudo = let
|
||||
lib = prev.lib;
|
||||
in {
|
||||
ip = import ./ip.nix { pkgs = prev; };
|
||||
dns = import ./dns.nix { pkgs = prev;};
|
||||
passwd = import ./passwd.nix { pkgs = prev;};
|
||||
lisp = import ./lisp.nix { pkgs = prev;};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
+23
-11
@@ -1,6 +1,6 @@
|
||||
{ lib, ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
with pkgs.lib;
|
||||
let
|
||||
hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation {
|
||||
name = "${name}-ldap-passwd";
|
||||
@@ -14,7 +14,7 @@ let
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
mkdir -p $out
|
||||
mv ldap-passwd $out
|
||||
'';
|
||||
};
|
||||
@@ -26,23 +26,35 @@ let
|
||||
generate-random-passwd = name: length: pkgs.stdenv.mkDerivation {
|
||||
name = "${name}-random-passwd";
|
||||
|
||||
phases = [ "buildPhase" "installPhase" ];
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
buildInputs = with pkgs; [ pwgen ];
|
||||
|
||||
buildPhase = ''
|
||||
pwgen --symbols --num-passwords=1 ${length} > passwd
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
mv passwd $out
|
||||
pwgen --secure --num-passwords=1 ${length} > $out
|
||||
'';
|
||||
};
|
||||
|
||||
generate-stablerandom-passwd = name: { seed, length ? 20, ... }:
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "${name}-stablerandom-passwd";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
buildInputs = with pkgs; [ pwgen ];
|
||||
|
||||
installPhase = ''
|
||||
echo "${name}-${seed}" > seedfile
|
||||
pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out
|
||||
'';
|
||||
};
|
||||
|
||||
in {
|
||||
hash-ldap-passwd = hash-ldap-passwd;
|
||||
|
||||
random-passwd-file = name: length:
|
||||
toPath "${generate-random-passwd name length}/passwd";
|
||||
builtins.toPath "${generate-random-passwd name length}";
|
||||
|
||||
stablerandom-passwd-file = name: seed:
|
||||
builtins.toPath "${generate-stablerandom-passwd name { seed = seed; }}";
|
||||
}
|
||||
|
||||
+18
-2
@@ -1,7 +1,10 @@
|
||||
{ lib, ... }:
|
||||
|
||||
with lib;
|
||||
rec {
|
||||
let
|
||||
passwd = import ../passwd.nix { inherit lib; };
|
||||
|
||||
in rec {
|
||||
encryptedFSOpts = { ... }: let
|
||||
mountpoint = { mp, ... }: {
|
||||
options = with types; {
|
||||
@@ -31,6 +34,12 @@ rec {
|
||||
'';
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
world-readable = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to leave the top level world-readable.";
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
@@ -81,7 +90,9 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
hostOpts = { hostname, ... }: {
|
||||
hostOpts = { name, ... }: let
|
||||
hostname = name;
|
||||
in {
|
||||
options = with types; {
|
||||
master-key = mkOption {
|
||||
type = nullOr (submodule masterKeyOpts);
|
||||
@@ -284,6 +295,11 @@ rec {
|
||||
description = "Configuration parameters to set up initrd SSH network.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
backplane-password-file = mkOption {
|
||||
options = path;
|
||||
description = "File containing the password used by this host to connect to the backplane.";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+5
-3
@@ -2,12 +2,12 @@
|
||||
|
||||
with lib;
|
||||
rec {
|
||||
systemUserOpts = { username, ... }: {
|
||||
systemUserOpts = { name, ... }: {
|
||||
options = with lib.types; {
|
||||
username = mkOption {
|
||||
type = str;
|
||||
description = "The system user's login name.";
|
||||
default = username;
|
||||
default = name;
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
@@ -23,7 +23,9 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
userOpts = { username, ... }: {
|
||||
userOpts = { name, ... }: let
|
||||
username = name;
|
||||
in {
|
||||
options = with lib.types; {
|
||||
username = mkOption {
|
||||
type = str;
|
||||
|
||||
Reference in New Issue
Block a user