Tons of changes, I guess?
This commit is contained in:
+3
-31
@@ -2,34 +2,6 @@
|
||||
|
||||
with lib;
|
||||
let
|
||||
# localCopyOpts = { copy, ... }: let
|
||||
# in {
|
||||
# options = with types; {
|
||||
# user = mkOption {
|
||||
# type = str;
|
||||
# description = "User to which this copy belongs.";
|
||||
# };
|
||||
|
||||
# group = mkOption {
|
||||
# type = nullOr str;
|
||||
# description = "Group to which this copy belongs.";
|
||||
# default = null;
|
||||
# };
|
||||
|
||||
# path = mkOption {
|
||||
# type = str;
|
||||
# description = "Path at which to store the local copy.";
|
||||
# #default = "/var/run/${toplevel.config.domain}/${copy}";
|
||||
# };
|
||||
|
||||
# service = mkOption {
|
||||
# type = str;
|
||||
# description = "systemd job to copy certs.";
|
||||
# default = "fudo-${toplevel.config.domain}-${copy}-certs.service";
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
domainOpts = { name, ... }: let
|
||||
domain = name;
|
||||
in {
|
||||
@@ -140,14 +112,14 @@ in {
|
||||
perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500";
|
||||
copy-paths = mapAttrsToList (copy: copyOpts:
|
||||
let
|
||||
dir-entry = copyOpts: file: "D '${dirOf file}' ${perms copyOpts} ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -";
|
||||
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;
|
||||
in unique (concatMap (i: unique i) copy-paths);
|
||||
|
||||
services = concatMapAttrs (domain: domainOpts:
|
||||
mapAttrs' (copy: copyOpts: let
|
||||
@@ -169,7 +141,7 @@ in {
|
||||
'';
|
||||
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" ''
|
||||
rm -f ${copyOpts.private-key}
|
||||
rm -f ${copyOpts.chainy}
|
||||
rm -f ${copyOpts.chain}
|
||||
rm -f ${copyOpts.full-certificate}
|
||||
rm -f ${copyOpts.certificate}
|
||||
'';
|
||||
|
||||
+46
-33
@@ -4,7 +4,7 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.backplane.dns;
|
||||
|
||||
powerdns-conf-dir = "${cfg.powerdns-home}/conf.d";
|
||||
powerdns-conf-dir = "${cfg.powerdns.home}/conf.d";
|
||||
|
||||
backplaneOpts = { ... }: {
|
||||
options = {
|
||||
@@ -90,6 +90,7 @@ in {
|
||||
type = listOf str;
|
||||
description =
|
||||
"A list of services required before the DNS server can start.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
@@ -109,16 +110,24 @@ in {
|
||||
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 = submodule backplaneOpts;
|
||||
description = "Backplane Jabber 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";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = str;
|
||||
description = "Username as which to run PowerDNS.";
|
||||
default = "backplane-powerdns";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
@@ -130,16 +139,16 @@ in {
|
||||
createHome = true;
|
||||
home = "/var/home/${cfg.user}";
|
||||
};
|
||||
backplane-powerdns = {
|
||||
${cfg.powerdns.user} = {
|
||||
isSystemUser = true;
|
||||
home = cfg.powerdns-home;
|
||||
home = cfg.powerdns.home;
|
||||
createHome = true;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
"${cfg.group}" = { members = [ cfg.user ]; };
|
||||
backplane-powerdns = { members = [ "backplane-powerdns" ]; };
|
||||
${cfg.powerdns.user} = { members = [ cfg.powerdns.user ]; };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,7 +165,7 @@ in {
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${powerdns-conf-dir}
|
||||
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
|
||||
chown ${cfg.powerdns.user}:${cfg.powerdns.user} ${powerdns-conf-dir}
|
||||
'';
|
||||
|
||||
# This builds the config in a bash script, to avoid storing the password
|
||||
@@ -175,7 +184,7 @@ in {
|
||||
fi
|
||||
|
||||
touch $TMPCONF
|
||||
chown backplane-powerdns:backplane-powerdns $TMPCONF
|
||||
chown ${cfg.powerdns.user}:${cfg.powerdns.user} $TMPCONF
|
||||
chmod go-rwx $TMPCONF
|
||||
PASSWORD=$(cat ${cfg.database.password-file})
|
||||
echo "launch+=gpgsql" >> $TMPCONF
|
||||
@@ -192,25 +201,6 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -220,7 +210,7 @@ in {
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
requires = cfg.required-services ++ [ "postgresql.service" ];
|
||||
environment = {
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
|
||||
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
|
||||
@@ -243,7 +233,30 @@ in {
|
||||
backplane-dns = {
|
||||
description = "Fudo DNS backplane services.";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requries = cfg.required-services ++ [ "postgresql.service" ];
|
||||
after = cfg.required-services ++ [ "postgresql.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
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 ];
|
||||
serviceConfig = {
|
||||
ExecStart = "pdns_server --setuid=${cfg.powerdns.user} --setgid=${cfg.powerdns.user} --chroot=${cfg.powerdns.home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${pdns-config-dir}";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+137
-95
@@ -1,67 +1,71 @@
|
||||
{ pkgs, lib, config, ... }:
|
||||
|
||||
with lib;
|
||||
let cfg = config.fudo.chat;
|
||||
let
|
||||
cfg = config.fudo.chat;
|
||||
mattermost-config-target = "/run/chat/mattermost/mattermost-config.json";
|
||||
|
||||
in {
|
||||
options.fudo.chat = {
|
||||
options.fudo.chat = with types; {
|
||||
enable = mkEnableOption "Enable chat server";
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Hostname at which this chat server is accessible.";
|
||||
example = "chat.mydomain.com";
|
||||
};
|
||||
|
||||
site-name = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "The name of this chat server.";
|
||||
example = "My Fancy Chat Site";
|
||||
};
|
||||
|
||||
smtp-server = mkOption {
|
||||
type = types.str;
|
||||
description = "SMTP server to use for sending notification emails.";
|
||||
example = "mail.my-site.com";
|
||||
};
|
||||
smtp = {
|
||||
server = mkOption {
|
||||
type = str;
|
||||
description = "SMTP server to use for sending notification emails.";
|
||||
example = "mail.my-site.com";
|
||||
};
|
||||
|
||||
smtp-user = mkOption {
|
||||
type = types.str;
|
||||
description = "Username with which to connect to the SMTP server.";
|
||||
};
|
||||
user = mkOption {
|
||||
type = str;
|
||||
description = "Username with which to connect to the SMTP server.";
|
||||
};
|
||||
|
||||
smtp-password-file = mkOption {
|
||||
type = types.str;
|
||||
description =
|
||||
"Path to a file containing the password to use while connecting to the SMTP server.";
|
||||
password-file = mkOption {
|
||||
type = str;
|
||||
description =
|
||||
"Path to a file containing the password to use while connecting to the SMTP server.";
|
||||
};
|
||||
};
|
||||
|
||||
state-directory = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Path at which to store server state data.";
|
||||
default = "/var/lib/mattermost";
|
||||
};
|
||||
|
||||
database = mkOption {
|
||||
type = (types.submodule {
|
||||
type = (submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Database name.";
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Database host.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Database user.";
|
||||
};
|
||||
|
||||
password-file = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Path to file containing database password.";
|
||||
};
|
||||
};
|
||||
@@ -85,11 +89,11 @@ in {
|
||||
TeamSettings.SiteName = cfg.site-name;
|
||||
EmailSettings = {
|
||||
RequireEmailVerification = true;
|
||||
SMTPServer = cfg.smtp-server;
|
||||
SMTPServer = cfg.smtp.server;
|
||||
SMTPPort = 587;
|
||||
EnableSMTPAuth = true;
|
||||
SMTPUsername = cfg.smtp-user;
|
||||
SMTPPassword = (fileContents cfg.smtp-password-file);
|
||||
SMTPUsername = cfg.smtp.user;
|
||||
SMTPPassword = "__SMTP_PASSWD__";
|
||||
SendEmailNotifications = true;
|
||||
ConnectionSecurity = "STARTTLS";
|
||||
FeedbackEmail = "chat@fudo.org";
|
||||
@@ -97,15 +101,26 @@ in {
|
||||
};
|
||||
EnableEmailInvitations = true;
|
||||
SqlSettings.DriverName = "postgres";
|
||||
SqlSettings.DataSource = "postgres://${cfg.database.user}:${
|
||||
fileContents cfg.database.password-file
|
||||
}@${cfg.database.hostname}:5432/${cfg.database.name}";
|
||||
SqlSettings.DataSource = "postgres://${
|
||||
cfg.database.user
|
||||
}:__DATABASE_PASSWORD__@${
|
||||
cfg.database.hostname
|
||||
}:5432/${
|
||||
cfg.database.name
|
||||
}";
|
||||
};
|
||||
mattermost-config-file =
|
||||
pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config);
|
||||
mattermost-config-file-template =
|
||||
pkgs.writeText "mattermost-config.json.template" (builtins.toJSON modified-config);
|
||||
mattermost-user = "mattermost";
|
||||
mattermost-group = "mattermost";
|
||||
|
||||
generate-mattermost-config = target: template: smtp-passwd-file: db-passwd-file:
|
||||
pkgs.writeScript "mattermost-config-generator.sh" ''
|
||||
SMTP_PASSWD=$( cat ${smtp-passwd-file} )
|
||||
DATABASE_PASSWORD=$( cat ${db-passwd-file} )
|
||||
sed -e 's/__SMTP_PASSWD__/"$SMTP_PASSWD"/' -e 's/__DATABASE_PASSWORD__/"$DATABASE_PASSWORD"/' ${template} > ${target}
|
||||
'';
|
||||
|
||||
in {
|
||||
users = {
|
||||
users = {
|
||||
@@ -118,48 +133,75 @@ in {
|
||||
groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; };
|
||||
};
|
||||
|
||||
system.activationScripts.mattermost = ''
|
||||
mkdir -p ${cfg.state-directory}
|
||||
'';
|
||||
|
||||
systemd.services.mattermost = {
|
||||
fudo.system.services.mattermost = {
|
||||
description = "Mattermost Chat Server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${cfg.state-directory}/config
|
||||
cp ${mattermost-config-file} ${cfg.state-directory}/config/config.json
|
||||
ln -sf ${pkg}/bin ${cfg.state-directory}
|
||||
ln -sf ${pkg}/fonts ${cfg.state-directory}
|
||||
ln -sf ${pkg}/i18n ${cfg.state-directory}
|
||||
ln -sf ${pkg}/templates ${cfg.state-directory}
|
||||
${generate-mattermost-config
|
||||
mattermost-config-target
|
||||
mattermost-config-file-template
|
||||
cfg.smtp.password-file
|
||||
cfg.database.password-file}
|
||||
cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json
|
||||
cp -uRL ${pkg}/client ${cfg.state-directory}
|
||||
chown -R ${mattermost-user}:${mattermost-group} ${cfg.state-directory}
|
||||
chmod u+w -R ${cfg.state-directory}/client
|
||||
chmod o-rwx -R ${cfg.state-directory}
|
||||
chown ${mattermost-user}:${mattermost-group} ${cfg.state-directory}/client
|
||||
chmod 0750 ${cfg.state-directory}/client
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
PermissionsStartOnly = true;
|
||||
ExecStart = "${pkg}/bin/mattermost";
|
||||
WorkingDirectory = cfg.state-directory;
|
||||
Restart = "always";
|
||||
RestartSec = "10";
|
||||
LimitNOFILE = "49152";
|
||||
User = mattermost-user;
|
||||
Group = mattermost-group;
|
||||
};
|
||||
execStart = "${pkg}/bin/mattermost";
|
||||
workingDirectory = cfg.state-directory;
|
||||
user = mattermost-user;
|
||||
group = mattermost-group;
|
||||
};
|
||||
|
||||
security.acme.certs.${cfg.hostname}.email = config.fudo.common.admin-email;
|
||||
systemd = {
|
||||
|
||||
tmpfiles.rules = [
|
||||
"d ${cfg.state-directory} 0750 ${mattermost-user} ${mattermost-group} - -"
|
||||
"d ${cfg.state-directory}/config 0750 ${mattermost-user} ${mattermost-group} - -"
|
||||
"L ${cfg.state-directory}/bin - - - - ${pkg}/bin"
|
||||
"L ${cfg.state-directory}/fonts - - - - ${pkg}/fonts"
|
||||
"L ${cfg.state-directory}/i18n - - - - ${pkg}/i18n"
|
||||
"L ${cfg.state-directory}/templates - - - - ${pkg}/templates"
|
||||
];
|
||||
|
||||
# services.mattermost = {
|
||||
# description = "Mattermost Chat Server";
|
||||
# wantedBy = [ "multi-user.target" ];
|
||||
# after = [ "network.target" ];
|
||||
|
||||
# preStart = ''
|
||||
# ${generate-mattermost-config
|
||||
# mattermost-config-target
|
||||
# mattermost-config-file-template
|
||||
# cfg.smtp.password-file
|
||||
# cfg.database.password-file}
|
||||
# cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json
|
||||
# cp -uRL ${pkg}/client ${cfg.state-directory}
|
||||
# chown ${mattermost-user}:${mattermost-group} ${cfg.state-directory}/client
|
||||
# chmod 0750 ${cfg.state-directory}/client
|
||||
# '';
|
||||
|
||||
# serviceConfig = {
|
||||
# PermissionsStartOnly = true;
|
||||
# ExecStart = "${pkg}/bin/mattermost";
|
||||
# WorkingDirectory = cfg.state-directory;
|
||||
# Restart = "always";
|
||||
# RestartSec = "10";
|
||||
# LimitNOFILE = "49152";
|
||||
# User = mattermost-user;
|
||||
# Group = mattermost-group;
|
||||
# };
|
||||
# };
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
appendHttpConfig = ''
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off;
|
||||
'';
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off;
|
||||
'';
|
||||
|
||||
virtualHosts = {
|
||||
"${cfg.hostname}" = {
|
||||
@@ -170,48 +212,48 @@ in {
|
||||
proxyPass = "http://127.0.0.1:8065";
|
||||
|
||||
extraConfig = ''
|
||||
client_max_body_size 50M;
|
||||
proxy_set_header Connection "";
|
||||
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 $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_cache mattermost_cache;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_min_uses 2;
|
||||
proxy_cache_use_stale timeout;
|
||||
proxy_cache_lock on;
|
||||
proxy_http_version 1.1;
|
||||
'';
|
||||
client_max_body_size 50M;
|
||||
proxy_set_header Connection "";
|
||||
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 $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_cache mattermost_cache;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_min_uses 2;
|
||||
proxy_cache_use_stale timeout;
|
||||
proxy_cache_lock on;
|
||||
proxy_http_version 1.1;
|
||||
'';
|
||||
};
|
||||
|
||||
locations."~ /api/v[0-9]+/(users/)?websocket$" = {
|
||||
proxyPass = "http://127.0.0.1:8065";
|
||||
|
||||
extraConfig = ''
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
client_max_body_size 50M;
|
||||
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 $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
client_body_timeout 60;
|
||||
send_timeout 300;
|
||||
lingering_timeout 5;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 90s;
|
||||
'';
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
client_max_body_size 50M;
|
||||
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 $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
client_body_timeout 60;
|
||||
send_timeout 300;
|
||||
lingering_timeout 5;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 90s;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+20
-50
@@ -8,7 +8,14 @@ let
|
||||
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||
generate-string-hash = name: str: let
|
||||
string-hash-pkg = pkgs.stdenv.mkDerivation {
|
||||
name = "${name}-string-hash";
|
||||
phases = "installPhase";
|
||||
buildInputs = [ pkgs.openssl ];
|
||||
installPhase = "openssl passwd -6 ${str} > $out";
|
||||
};
|
||||
in string-hash-pkg;
|
||||
|
||||
in {
|
||||
options.fudo.hosts = with types;
|
||||
@@ -37,9 +44,12 @@ in {
|
||||
#defaultGateway = site.gateway-v4;
|
||||
#defaultGateway6 = site.gateway-v6;
|
||||
|
||||
firewall = {
|
||||
enable = (length host-cfg.external-interfaces) > 0;
|
||||
allowedTCPPorts = [ 22 ];
|
||||
firewall = mkIf ((length host-cfg.external-interfaces) > 0) {
|
||||
enable = true;
|
||||
allowedTCPPorts = [ 22 2112 ]; # Make sure _at least_ SSH is allowed
|
||||
trustedInterfaces = let
|
||||
all-interfaces = attrNames config.networking.interfaces;
|
||||
in subtractLists host-cfg.external-interfaces all-interfaces;
|
||||
};
|
||||
|
||||
hostId = mkIf (host-cfg.machine-id != null)
|
||||
@@ -79,6 +89,8 @@ in {
|
||||
in concatStringsSep "\n" sorted-unique;
|
||||
|
||||
build-timestamp.text = toString config.instance.build-timestamp;
|
||||
build-seed-hash.source =
|
||||
generate-string-hash "build-seed" config.instance.build-seed;
|
||||
};
|
||||
|
||||
systemPackages = with pkgs;
|
||||
@@ -89,7 +101,10 @@ in {
|
||||
|
||||
krb5.libdefaults.default_realm = domain.gssapi-realm;
|
||||
|
||||
services.cron.mailto = domain.admin-email;
|
||||
services = {
|
||||
cron.mailto = domain.admin-email;
|
||||
fail2ban.ignoreIP = config.instance.local-networks;
|
||||
};
|
||||
|
||||
virtualisation.docker = mkIf (host-cfg.docker-server) {
|
||||
enable = true;
|
||||
@@ -97,56 +112,11 @@ in {
|
||||
autoPrune.enable = true;
|
||||
};
|
||||
|
||||
fudo = let
|
||||
try-attr = attr: set: if (hasAttr attr set) then set.${attr} else null;
|
||||
|
||||
files = config.fudo.secrets.files;
|
||||
|
||||
keytab-file = try-attr hostname files.host-keytabs;
|
||||
|
||||
build-private-key-file =
|
||||
mapOptional
|
||||
(keypair: keypair.private-key)
|
||||
(try-attr hostname files.build-keypairs);
|
||||
|
||||
in {
|
||||
secrets.host-secrets.${hostname} = {
|
||||
host-keytab = mkIf (keytab-file != null) {
|
||||
source-file = keytab-file;
|
||||
target-file = "/etc/krb5.keytab";
|
||||
user = "root";
|
||||
};
|
||||
|
||||
build-private-key = mkIf (build-private-key-file != null) {
|
||||
source-file = build-private-key-file;
|
||||
target-file = "/var/run/nix-build/host.key";
|
||||
user = "root";
|
||||
};
|
||||
|
||||
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 =
|
||||
host-secrets.backplane-passwd.target-file;
|
||||
};
|
||||
|
||||
programs.adb.enable = host-cfg.android-dev;
|
||||
users.groups.adbusers = mkIf host-cfg.android-dev {
|
||||
members = config.instance.local-admins;
|
||||
};
|
||||
|
||||
boot.tmpOnTmpfs = host-cfg.tmp-on-tmpfs;
|
||||
|
||||
home-manager.users.root.home.file = {
|
||||
".k5login".text = let
|
||||
realm = domain.gssapi-realm;
|
||||
entries =
|
||||
map (admin: "${admin}/root@${realm}") config.instance.local-admins;
|
||||
in concatStringsSep "\n" entries;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+2
-7
@@ -7,9 +7,6 @@ let
|
||||
user-group-entry = group: user:
|
||||
nameValuePair user { extraGroups = [ group ]; };
|
||||
|
||||
user-home-entry = ipfs-path: user:
|
||||
nameValuePair user { home.sessionVariables = { IPFS_PATH = ipfs-path; }; };
|
||||
|
||||
in {
|
||||
options.fudo.ipfs = with types; {
|
||||
enable = mkEnableOption "Fudo IPFS";
|
||||
@@ -53,7 +50,8 @@ in {
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.users = listToAttrs (map (user-group-entry cfg.group) cfg.users);
|
||||
users.users =
|
||||
mapAttrs user-group-entry config.instance.local-users;
|
||||
|
||||
services.ipfs = {
|
||||
enable = true;
|
||||
@@ -64,8 +62,5 @@ in {
|
||||
group = cfg.group;
|
||||
dataDir = cfg.data-dir;
|
||||
};
|
||||
|
||||
home-manager.users =
|
||||
listToAttrs (map (user-home-entry cfg.data-dir) cfg.users);
|
||||
};
|
||||
}
|
||||
|
||||
+49
-139
@@ -5,100 +5,7 @@ let
|
||||
|
||||
cfg = config.fudo.auth.ldap-server;
|
||||
|
||||
ldapSystemUserOpts = { name, ... }: {
|
||||
options = {
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The description of this system user.
|
||||
'';
|
||||
};
|
||||
|
||||
hashed-password = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The password for this user, hashed with ldappasswd.
|
||||
'';
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ldapGroupOpts = { name, ... }: {
|
||||
options = {
|
||||
gid = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
The GID number of this group.
|
||||
'';
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The description of this group.
|
||||
'';
|
||||
};
|
||||
|
||||
members = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
A list of usernames representing the members of this group.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ldapUserOpts = { name, ... }: {
|
||||
options = {
|
||||
|
||||
uid = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
The UID number of this user.
|
||||
'';
|
||||
};
|
||||
|
||||
common-name = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The given name of this user.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The name of the user's primary group.
|
||||
'';
|
||||
};
|
||||
|
||||
login-shell = mkOption {
|
||||
type = types.str;
|
||||
default = "/bin/bash";
|
||||
description = ''
|
||||
The user's preferred shell. Default is /bin/bash.
|
||||
'';
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Fudo Member";
|
||||
description = ''
|
||||
The description of this user.
|
||||
'';
|
||||
};
|
||||
|
||||
hashed-password = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The password for this user, hashed with ldappasswd.
|
||||
'';
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
};
|
||||
user-type = import ../types/user.nix { inherit lib; };
|
||||
|
||||
stringJoin = joiner: attrList:
|
||||
if (length attrList) == 0 then
|
||||
@@ -107,15 +14,15 @@ let
|
||||
foldr (lAttr: rAttr: "${lAttr}${joiner}${rAttr}") (last attrList)
|
||||
(init attrList);
|
||||
|
||||
getUserGidNumber = user: group-map: group-map.${user.group}.gid;
|
||||
getUserGidNumber = user: group-map: group-map.${user.primary-group}.gid;
|
||||
|
||||
attrOr = attrs: attr: value: if attrs ? ${attr} then attrs.${attr} else value;
|
||||
|
||||
mkHomeDir = username: user-opts:
|
||||
if (user-opts.group == "admin") then
|
||||
if (user-opts.primary-group == "admin") then
|
||||
"/home/${username}"
|
||||
else
|
||||
"/home/${user-opts.group}/${username}";
|
||||
"/home/${user-opts.primary-group}/${username}";
|
||||
|
||||
userLdif = base: name: group-map: opts: ''
|
||||
dn: uid=${name},ou=members,${base}
|
||||
@@ -131,7 +38,7 @@ let
|
||||
shadowLastChange: 12230
|
||||
shadowMax: 99999
|
||||
shadowWarning: 7
|
||||
userPassword: ${opts.hashed-password}
|
||||
userPassword: ${opts.ldap-hashed-passwd}
|
||||
'';
|
||||
|
||||
systemUserLdif = base: name: opts: ''
|
||||
@@ -140,7 +47,7 @@ let
|
||||
objectClass: simpleSecurityObject
|
||||
cn: ${name}
|
||||
description: ${opts.description}
|
||||
userPassword: ${opts.hashed-password}
|
||||
userPassword: ${opts.ldap-hashed-password}
|
||||
'';
|
||||
|
||||
toMemberList = userList:
|
||||
@@ -242,7 +149,7 @@ in {
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
type = attrsOf (submodule ldapUserOpts);
|
||||
type = attrsOf (submodule user-type.userOpts);
|
||||
example = {
|
||||
tester = {
|
||||
uid = 10099;
|
||||
@@ -258,7 +165,7 @@ in {
|
||||
|
||||
groups = mkOption {
|
||||
default = { };
|
||||
type = attrsOf (submodule ldapGroupOpts);
|
||||
type = attrsOf (submodule user-type.groupOpts);
|
||||
example = {
|
||||
admin = {
|
||||
gid = 1099;
|
||||
@@ -272,16 +179,19 @@ in {
|
||||
|
||||
system-users = mkOption {
|
||||
default = { };
|
||||
type = attrsOf (submodule ldapSystemUserOpts);
|
||||
type = attrsOf (submodule user-type.systemUserOpts);
|
||||
example = {
|
||||
replicator = {
|
||||
description = "System user for database sync";
|
||||
hashed-password = "<insert password hash>";
|
||||
ldap-hashed-password = "<insert password hash>";
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
System users to be added to the Fudo LDAP database.
|
||||
'';
|
||||
description = "System users to be added to the Fudo LDAP database.";
|
||||
};
|
||||
|
||||
database-directory = mkOption {
|
||||
type = str;
|
||||
description = "Path at which to store the database.";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -337,21 +247,19 @@ in {
|
||||
|
||||
services.openldap = {
|
||||
enable = true;
|
||||
suffix = cfg.base;
|
||||
rootdn = "cn=admin,${cfg.base}";
|
||||
rootpwFile = "${cfg.rootpw-file}";
|
||||
urlList = cfg.listen-uris;
|
||||
database = "mdb";
|
||||
|
||||
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}";
|
||||
makePermEntry = dn: perm: "by ${dn} ${perm}";
|
||||
|
||||
makeAccessLine = target: perm-map: let
|
||||
perm-entries = mapAttrsToList makePermEntry perm-map;
|
||||
in "to ${target} ${concatStringsSep " " perm-entries} done";
|
||||
|
||||
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;
|
||||
access-lines = mapAttrsToList makeAccessLine;
|
||||
numbered-access-lines = imap0 (i: line: "{${toString i}}${line}");
|
||||
in numbered-access-lines (access-lines access-map);
|
||||
|
||||
in {
|
||||
attrs = {
|
||||
@@ -363,8 +271,8 @@ in {
|
||||
olcTLSCACertificateFile = cfg.ssl-ca-certificate;
|
||||
olcSaslSecProps = "noplain,noanonymous";
|
||||
olcAuthzRegexp = let
|
||||
authz-regex-entry = i: { regex, target}:
|
||||
"{${i}}\"${rx}\" \"${target}\"";
|
||||
authz-regex-entry = i: { regex, target }:
|
||||
"{${toString i}}\"${regex}\" \"${target}\"";
|
||||
in imap0 authz-regex-entry [
|
||||
{
|
||||
regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$";
|
||||
@@ -396,7 +304,7 @@ in {
|
||||
olcAccess = makeAccess {
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=admin,dc=fudo,dc=org" = "manage";
|
||||
"dn.exact=cn=admin,${cfg.base}" = "manage";
|
||||
"*" = "none";
|
||||
};
|
||||
};
|
||||
@@ -462,30 +370,32 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
declarativeContents = ''
|
||||
dn: ${cfg.base}
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: ${cfg.organization}
|
||||
declarativeContents = {
|
||||
"dc=fudo,dc=org" = ''
|
||||
dn: ${cfg.base}
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: ${cfg.organization}
|
||||
|
||||
dn: ou=groups,${cfg.base}
|
||||
objectClass: organizationalUnit
|
||||
description: ${cfg.organization} groups
|
||||
dn: ou=groups,${cfg.base}
|
||||
objectClass: organizationalUnit
|
||||
description: ${cfg.organization} groups
|
||||
|
||||
dn: ou=members,${cfg.base}
|
||||
objectClass: organizationalUnit
|
||||
description: ${cfg.organization} members
|
||||
dn: ou=members,${cfg.base}
|
||||
objectClass: organizationalUnit
|
||||
description: ${cfg.organization} members
|
||||
|
||||
dn: cn=admin,${cfg.base}
|
||||
objectClass: organizationalRole
|
||||
cn: admin
|
||||
description: "Admin User"
|
||||
dn: cn=admin,${cfg.base}
|
||||
objectClass: organizationalRole
|
||||
cn: admin
|
||||
description: "Admin User"
|
||||
|
||||
${systemUsersLdif cfg.base cfg.system-users}
|
||||
${groupsLdif cfg.base cfg.groups}
|
||||
${usersLdif cfg.base cfg.groups cfg.users}
|
||||
'';
|
||||
${systemUsersLdif cfg.base cfg.system-users}
|
||||
${groupsLdif cfg.base cfg.groups}
|
||||
${usersLdif cfg.base cfg.groups cfg.users}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+87
-84
@@ -1,4 +1,4 @@
|
||||
{ lib, config, ... }:
|
||||
{ pkgs, lib, config, ... }:
|
||||
with lib;
|
||||
let
|
||||
hostname = config.instance.hostname;
|
||||
@@ -11,22 +11,18 @@ let
|
||||
container-mail-user-id = 542;
|
||||
container-mail-group = "mailer";
|
||||
|
||||
build-timestamp = config.instance.build-timestamp;
|
||||
build-seed = config.instance.build-seed;
|
||||
site = config.instance.local-site;
|
||||
domain = cfg.domain;
|
||||
|
||||
local-networks = config.instance.local-networks;
|
||||
|
||||
in rec {
|
||||
config = mkIf (cfg.enableContainer) {
|
||||
# Disable postfix on this host--it'll be run in the container instead
|
||||
services.postfix.enable = false;
|
||||
|
||||
fudo.acme.host-domains.${hostname}.${cfg.mail-hostname} = {
|
||||
local-copies = {
|
||||
postfix = {
|
||||
user = "root";
|
||||
};
|
||||
dovecot-cert = {
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = mkIf cfg.monitoring {
|
||||
enable = true;
|
||||
|
||||
@@ -36,10 +32,10 @@ in rec {
|
||||
proxy_set_header Host $host;
|
||||
'';
|
||||
trusted-network-string =
|
||||
optionalString ((length config.instance.local-networks) > 0)
|
||||
optionalString ((length local-networks) > 0)
|
||||
(concatStringsSep "\n"
|
||||
(map (network: "allow ${network};")
|
||||
config.instance.local-networks)) + ''
|
||||
local-networks)) + ''
|
||||
|
||||
deny all;'';
|
||||
|
||||
@@ -85,9 +81,7 @@ in rec {
|
||||
|
||||
autoStart = true;
|
||||
|
||||
bindMounts = let
|
||||
cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies;
|
||||
in {
|
||||
bindMounts = {
|
||||
"${container-maildir}" = {
|
||||
hostPath = cfg.mail-directory;
|
||||
isReadOnly = false;
|
||||
@@ -98,53 +92,54 @@ in rec {
|
||||
isReadOnly = false;
|
||||
};
|
||||
|
||||
"/etc/${container-shared}" = {
|
||||
hostPath = "/etc/${container-shared}";
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/postfix/cert.pem" = {
|
||||
hostPath = cert-copies.postfix.certificate;
|
||||
hostPath = cfg.ssl.certificate;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/postfix/key.pem" = {
|
||||
hostPath = cert-copies.postfix.private-key;
|
||||
hostPath = cfg.ssl.private-key;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/dovecot/cert.pem" = {
|
||||
hostPath = cert-copies.dovecot.certificate;
|
||||
hostPath = cfg.ssl.certificate;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/certs/dovecot/key.pem" = {
|
||||
hostPath = cert-copies.dovecot.private-key;
|
||||
hostPath = cfg.ssl.private-key;
|
||||
isReadOnly = true;
|
||||
};
|
||||
|
||||
"/run/mail/passwords/dovecot/ldap-reader.passwd" = {
|
||||
hostPath = cfg.dovecot.ldap.reader-password-file;
|
||||
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, ... }: {
|
||||
|
||||
imports = let
|
||||
initialize-host = import ../../initialize.nix;
|
||||
profile = "container";
|
||||
in [
|
||||
./mail.nix
|
||||
|
||||
(initialize-host {
|
||||
inherit
|
||||
lib
|
||||
pkgs
|
||||
build-timestamp
|
||||
site
|
||||
domain
|
||||
profile;
|
||||
hostname = "mail-container";
|
||||
})
|
||||
];
|
||||
|
||||
instance.build-seed = build-seed;
|
||||
|
||||
environment.etc = {
|
||||
"mail-server/postfix/cert.pem" = {
|
||||
source = "/run/mail/certs/postfix/cert.pem";
|
||||
@@ -158,59 +153,67 @@ in rec {
|
||||
};
|
||||
"mail-server/dovecot/cert.pem" = {
|
||||
source = "/run/mail/certs/dovecot/cert.pem";
|
||||
user = config.services.dovecot.user;
|
||||
user = config.services.dovecot2.user;
|
||||
mode = "0444";
|
||||
};
|
||||
"mail-server/dovecot/key.pem" = {
|
||||
source = "/run/mail/certs/dovecot/key.pem";
|
||||
user = config.services.dovecot.user;
|
||||
user = config.services.dovecot2.user;
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
## The pre-script runs as root anyway...
|
||||
# "mail-server/dovecot/ldap-reader.passwd" = {
|
||||
# source = "/run/mail/passwords/dovecot/ldap-reader.passwd";
|
||||
# user = config.services.dovecot2.user;
|
||||
# mode = "0400";
|
||||
# };
|
||||
};
|
||||
|
||||
imports = [ ./mail.nix ];
|
||||
fudo = {
|
||||
|
||||
fudo.mail-server = {
|
||||
enable = true;
|
||||
hostname = cfg.hostname;
|
||||
domain = cfg.domain;
|
||||
mail-server = {
|
||||
enable = true;
|
||||
mail-hostname = cfg.mail-hostname;
|
||||
domain = cfg.domain;
|
||||
|
||||
debug = cfg.debug;
|
||||
monitoring = cfg.monitoring;
|
||||
debug = cfg.debug;
|
||||
monitoring = cfg.monitoring;
|
||||
|
||||
state-directory = container-statedir;
|
||||
mail-directory = container-maildir;
|
||||
state-directory = container-statedir;
|
||||
mail-directory = container-maildir;
|
||||
|
||||
postfix = {
|
||||
ssl-certificate = "/etc/mail-server/postfix/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/postfix/key.pem";
|
||||
};
|
||||
|
||||
dovecot = {
|
||||
ssl-certificate = "/etc/mail-server/dovecot/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/dovecot/key.pem";
|
||||
ldap = {
|
||||
server-urls = cfg.dovecot.ldap.server-urls;
|
||||
reader-dn = cfg.dovecot.ldap.reader-dn;
|
||||
reader-passwd = cfg.dovecot.ldap.reader-passwd;
|
||||
postfix = {
|
||||
ssl-certificate = "/etc/mail-server/postfix/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/postfix/key.pem";
|
||||
};
|
||||
|
||||
dovecot = {
|
||||
ssl-certificate = "/etc/mail-server/dovecot/cert.pem";
|
||||
ssl-private-key = "/etc/mail-server/dovecot/key.pem";
|
||||
ldap = {
|
||||
server-urls = cfg.dovecot.ldap.server-urls;
|
||||
reader-dn = cfg.dovecot.ldap.reader-dn;
|
||||
reader-password-file = "/run/mail/passwords/dovecot/ldap-reader.passwd";
|
||||
};
|
||||
};
|
||||
|
||||
local-domains = cfg.local-domains;
|
||||
|
||||
alias-users = cfg.alias-users;
|
||||
user-aliases = cfg.user-aliases;
|
||||
sender-blacklist = cfg.sender-blacklist;
|
||||
recipient-blacklist = cfg.recipient-blacklist;
|
||||
trusted-networks = cfg.trusted-networks;
|
||||
|
||||
mail-user = container-mail-user;
|
||||
mail-user-id = container-mail-user-id;
|
||||
mail-group = container-mail-group;
|
||||
|
||||
clamav.enable = cfg.clamav.enable;
|
||||
|
||||
dkim.signing = cfg.dkim.signing;
|
||||
};
|
||||
|
||||
local-domains = cfg.local-domains;
|
||||
|
||||
alias-users = cfg.alias-users;
|
||||
user-aliases = cfg.user-aliases;
|
||||
sender-blacklist = cfg.sender-blacklist;
|
||||
recipient-blacklist = cfg.recipient-blacklist;
|
||||
trusted-networks = cfg.trusted-networks;
|
||||
|
||||
mail-user = container-mail-user;
|
||||
mail-user-id = container-mail-user-id;
|
||||
mail-group = container-mail-group;
|
||||
|
||||
clamav.enable = cfg.clamav.enable;
|
||||
|
||||
dkim.signing = cfg.dkim.signing;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+41
-24
@@ -7,7 +7,7 @@ let
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.mail-server = {
|
||||
options.fudo.mail-server = with types; {
|
||||
enable = mkEnableOption "Fudo Email Server";
|
||||
|
||||
enableContainer = mkEnableOption ''
|
||||
@@ -17,18 +17,17 @@ in {
|
||||
'';
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "The main and default domain name for this email server.";
|
||||
};
|
||||
|
||||
mail-hostname = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "The domain name to use for the mail server.";
|
||||
};
|
||||
|
||||
|
||||
ldap-url = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "URL of the LDAP server to use for authentication.";
|
||||
example = "ldaps://auth.fudo.org/";
|
||||
};
|
||||
@@ -36,23 +35,25 @@ in {
|
||||
monitoring = mkEnableOption "Enable monitoring for the mail server.";
|
||||
|
||||
mail-user = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "User to use for mail delivery.";
|
||||
default = "mailuser";
|
||||
};
|
||||
|
||||
# No group id, because NixOS doesn't seem to use it
|
||||
mail-group = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Group to use for mail delivery.";
|
||||
default = "mailgroup";
|
||||
};
|
||||
|
||||
mail-user-id = mkOption {
|
||||
type = types.int;
|
||||
type = int;
|
||||
description = "UID of mail-user.";
|
||||
};
|
||||
|
||||
local-domains = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "A list of domains for which we accept mail.";
|
||||
default = ["localhost" "localhost.localdomain"];
|
||||
example = [
|
||||
@@ -64,17 +65,17 @@ in {
|
||||
};
|
||||
|
||||
mail-directory = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Path to use for mail storage.";
|
||||
};
|
||||
|
||||
state-directory = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Path to use for state data.";
|
||||
};
|
||||
|
||||
trusted-networks = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "A list of trusted networks, for which we will happily relay without auth.";
|
||||
example = [
|
||||
"10.0.0.0/16"
|
||||
@@ -83,7 +84,7 @@ in {
|
||||
};
|
||||
|
||||
sender-blacklist = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "A list of email addresses for whom we will not send email.";
|
||||
default = [];
|
||||
example = [
|
||||
@@ -93,7 +94,7 @@ in {
|
||||
};
|
||||
|
||||
recipient-blacklist = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description = "A list of email addresses for whom we will not accept email.";
|
||||
default = [];
|
||||
example = [
|
||||
@@ -103,14 +104,14 @@ in {
|
||||
};
|
||||
|
||||
message-size-limit = mkOption {
|
||||
type = types.int;
|
||||
type = int;
|
||||
description = "Size of max email in megabytes.";
|
||||
default = 30;
|
||||
};
|
||||
|
||||
user-aliases = mkOption {
|
||||
type = with types; attrsOf(listOf str);
|
||||
description = "A map of real user to list of aliases.";
|
||||
type = attrsOf (listOf str);
|
||||
description = "A map of real user to list of alias emails.";
|
||||
default = {};
|
||||
example = {
|
||||
someuser = ["alias0" "alias1"];
|
||||
@@ -118,7 +119,7 @@ in {
|
||||
};
|
||||
|
||||
alias-users = mkOption {
|
||||
type = with types; attrsOf(listOf str);
|
||||
type = attrsOf (listOf str);
|
||||
description = "A map of email alias to a list of users.";
|
||||
example = {
|
||||
alias = ["realuser0" "realuser1"];
|
||||
@@ -164,15 +165,27 @@ in {
|
||||
|
||||
debug = mkOption {
|
||||
description = "Enable debugging on mailservers.";
|
||||
type = types.bool;
|
||||
type = bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
max-user-connections = mkOption {
|
||||
description = "Max simultaneous connections per user.";
|
||||
type = types.int;
|
||||
type = int;
|
||||
default = 20;
|
||||
};
|
||||
|
||||
ssl = {
|
||||
certificate = mkOption {
|
||||
type = str;
|
||||
description = "Path to the ssl certificate for the mail server to use.";
|
||||
};
|
||||
|
||||
private-key = mkOption {
|
||||
type = str;
|
||||
description = "Path to the ssl private key for the mail server to use.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
@@ -184,22 +197,26 @@ in {
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.mail-directory} 770 ${cfg.mail-user} ${cfg.mail-group} - -"
|
||||
];
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 25 110 143 587 993 995 ];
|
||||
};
|
||||
|
||||
users = {
|
||||
users = {
|
||||
mailuser = {
|
||||
${cfg.mail-user} = {
|
||||
isSystemUser = true;
|
||||
uid = cfg.mail-user-id;
|
||||
group = "mailgroup";
|
||||
group = cfg.mail-group;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
mailgroup = {
|
||||
members = ["mailuser"];
|
||||
${cfg.mail-group} = {
|
||||
members = [ cfg.mail-user ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+72
-44
@@ -4,7 +4,7 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.mail-server;
|
||||
|
||||
state-directory = "${cfg.state-directory}/dovecot";
|
||||
sieve-path = "${cfg.state-directory}/dovecot/imap_sieve";
|
||||
|
||||
pipe-bin = pkgs.stdenv.mkDerivation {
|
||||
name = "pipe_bin";
|
||||
@@ -23,35 +23,47 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
ldap-conf = filename: config:
|
||||
ldap-conf-template = ldap-cfg:
|
||||
let
|
||||
ssl-config = if config.ca == null then ''
|
||||
ssl-config = if (ldap-cfg.ca == null) then ''
|
||||
tls = no
|
||||
tls_require_cert = try
|
||||
'' else ''
|
||||
tls_ca_cert_file = ${config.ca}
|
||||
tls_ca_cert_file = ${ldap-cfg.ca}
|
||||
tls = yes
|
||||
tls_require_cert = try
|
||||
'';
|
||||
|
||||
in
|
||||
pkgs.writeText filename ''
|
||||
uris = ${concatStringsSep " " config.server-urls}
|
||||
ldap_version = 3
|
||||
dn = ${config.reader-dn}
|
||||
dnpass = ${config.reader-passwd}
|
||||
auth_bind = yes
|
||||
auth_bind_userdn = uid=%u,ou=members,dc=fudo,dc=org
|
||||
base = dc=fudo,dc=org
|
||||
${ssl-config}
|
||||
'';
|
||||
pkgs.writeText "dovecot2-ldap-config.conf.template" ''
|
||||
uris = ${concatStringsSep " " ldap-cfg.server-urls}
|
||||
ldap_version = 3
|
||||
dn = ${ldap-cfg.reader-dn}
|
||||
dnpass = __LDAP_READER_PASSWORD__
|
||||
auth_bind = yes
|
||||
auth_bind_userdn = uid=%u,ou=members,dc=fudo,dc=org
|
||||
base = dc=fudo,dc=org
|
||||
${ssl-config}
|
||||
'';
|
||||
|
||||
ldap-conf-generator = ldap-cfg: let
|
||||
template = ldap-conf-template ldap-cfg;
|
||||
target-dir = dirOf ldap-cfg.generated-ldap-config;
|
||||
target = ldap-cfg.generated-ldap-config;
|
||||
in pkgs.writeScript "dovecot2-ldap-password-swapper.sh" ''
|
||||
mkdir -p ${target-dir}
|
||||
touch ${target}
|
||||
chmod 600 ${target}
|
||||
chown ${config.services.dovecot2.user} ${target}
|
||||
LDAP_READER_PASSWORD=$( cat "${ldap-cfg.reader-password-file}" )
|
||||
sed 's/__LDAP_READER_PASSWORD__/$LDAP_READER_PASSWORD/' '${template}' > ${target}
|
||||
'';
|
||||
|
||||
ldap-passwd-entry = ldap-config: ''
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${ldap-conf "ldap-passdb.conf" ldap-config}
|
||||
}
|
||||
'';
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${ldap-conf "ldap-passdb.conf" ldap-config}
|
||||
}
|
||||
'';
|
||||
|
||||
ldapOpts = {
|
||||
options = with types; {
|
||||
@@ -61,6 +73,12 @@ let
|
||||
default = null;
|
||||
};
|
||||
|
||||
base = mkOption {
|
||||
type = str;
|
||||
description = "Base of the LDAP server database.";
|
||||
example = "dc=fudo,dc=org";
|
||||
};
|
||||
|
||||
server-urls = mkOption {
|
||||
type = listOf str;
|
||||
description = "A list of LDAP server URLs used for authentication.";
|
||||
@@ -69,16 +87,20 @@ let
|
||||
reader-dn = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
DN to use for reading user information. Needs access to homeDirectory,
|
||||
uidNumber, gidNumber, and uid, but not password attributes.
|
||||
'';
|
||||
DN to use for reading user information. Needs access to homeDirectory,
|
||||
uidNumber, gidNumber, and uid, but not password attributes.
|
||||
'';
|
||||
};
|
||||
|
||||
reader-passwd = mkOption {
|
||||
reader-password-file = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
Password for the user specified in ldap-reader-dn.
|
||||
'';
|
||||
description = "Password for the user specified in ldap-reader-dn.";
|
||||
};
|
||||
|
||||
generated-ldap-config = mkOption {
|
||||
type = str;
|
||||
description = "Path at which to store the generated LDAP config file, including password.";
|
||||
default = "/run/dovecot2/config/ldap.conf";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -206,8 +228,12 @@ in {
|
||||
|
||||
auth_mechanisms = login plain
|
||||
|
||||
${optionalString (cfg.dovecot.ldap != null)
|
||||
(ldap-passwd-entry cfg.dovecot.ldap)}
|
||||
${optionalString (cfg.dovecot.ldap != null) ''
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${cfg.dovecot.ldap.generated-ldap-config}
|
||||
}
|
||||
''}
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=${toString cfg.mail-user-id} home=${cfg.mail-directory}/%u
|
||||
@@ -249,12 +275,12 @@ in {
|
||||
# From elsewhere to Spam folder
|
||||
imapsieve_mailbox1_name = Junk
|
||||
imapsieve_mailbox1_causes = COPY
|
||||
imapsieve_mailbox1_before = file:${state-directory}/imap_sieve/report-spam.sieve
|
||||
imapsieve_mailbox1_before = file:${sieve-path}/report-spam.sieve
|
||||
# From Spam folder to elsewhere
|
||||
imapsieve_mailbox2_name = *
|
||||
imapsieve_mailbox2_from = Junk
|
||||
imapsieve_mailbox2_causes = COPY
|
||||
imapsieve_mailbox2_before = file:${state-directory}/imap_sieve/report-ham.sieve
|
||||
imapsieve_mailbox2_before = file:${sieve-path}/report-ham.sieve
|
||||
sieve_pipe_bin_dir = ${pipe-bin}/pipe/bin
|
||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
|
||||
}
|
||||
@@ -268,19 +294,21 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.dovecot2.preStart = ''
|
||||
mkdir -p '${state-directory}'
|
||||
chown ${dovecot-user}:${cfg.mail-group} '${state-directory}'
|
||||
rm -rf '${state-directory}/imap_sieve'
|
||||
mkdir '${state-directory}/imap_sieve'
|
||||
cp -p "${./dovecot/imap_sieve}"/*.sieve '${state-directory}/imap_sieve/'
|
||||
for k in "${state-directory}/imap_sieve"/*.sieve ; do
|
||||
${pkgs.dovecot_pigeonhole}/bin/sievec "$k"
|
||||
done
|
||||
chown -R '${dovecot-user}:${cfg.mail-group}' '${state-directory}/imap_sieve'
|
||||
systemd = {
|
||||
tmpfiles.rules = [
|
||||
"d ${sieve-path} 750 ${dovecot-user} ${cfg.mail-group} - -"
|
||||
];
|
||||
|
||||
chown '${cfg.mail-user}:${cfg.mail-group}' ${cfg.mail-directory}
|
||||
chmod g+w ${cfg.mail-directory}
|
||||
'';
|
||||
services.dovecot2.preStart = ''
|
||||
rm -f ${sieve-path}/*
|
||||
cp -p ${./dovecot/imap_sieve}/*.sieve ${sieve-path}
|
||||
for k in ${sieve-path}/*.sieve ; do
|
||||
${pkgs.dovecot_pigeonhole}/bin/sievec "$k"
|
||||
done
|
||||
|
||||
${optionalString (cfg.dovecot.ldap != null)
|
||||
(ldap-conf-generator cfg.dovecot.ldap)}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ in {
|
||||
enable = true;
|
||||
domain = cfg.domain;
|
||||
origin = cfg.domain;
|
||||
hostname = cfg.hostname;
|
||||
hostname = cfg.mail-hostname;
|
||||
destination = ["localhost" "localhost.localdomain"];
|
||||
# destination = ["localhost" "localhost.localdomain" cfg.hostname] ++
|
||||
# cfg.local-domains;;
|
||||
@@ -150,7 +150,7 @@ in {
|
||||
# mail_spool_directory = "${cfg.mail-directory}/";
|
||||
message_size_limit = toString(cfg.message-size-limit * 1024 * 1024);
|
||||
|
||||
smtpd_banner = "${cfg.hostname} ESMTP NO UCE";
|
||||
smtpd_banner = "${cfg.mail-hostname} ESMTP NO UCE";
|
||||
|
||||
tls_eecdh_strong_curve = "prime256v1";
|
||||
tls_eecdh_ultra_curve = "secp384r1";
|
||||
|
||||
+17
-15
@@ -4,6 +4,11 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.postgresql;
|
||||
|
||||
hostname = config.instance.hostname;
|
||||
domain-name = config.instance.local-domain;
|
||||
|
||||
gssapi-realm = config.fudo.domains.${domain-name}.gssapi-realm;
|
||||
|
||||
join-lines = lib.concatStringsSep "\n";
|
||||
|
||||
userDatabaseOpts = { database, ... }: {
|
||||
@@ -28,15 +33,15 @@ let
|
||||
};
|
||||
|
||||
userOpts = { username, ... }: {
|
||||
options = {
|
||||
options = with types; {
|
||||
password-file = mkOption {
|
||||
type = with types; nullOr str;
|
||||
type = nullOr str;
|
||||
description = "A file containing the user's (plaintext) password.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = with types; attrsOf (submodule userDatabaseOpts);
|
||||
type = attrsOf (submodule userDatabaseOpts);
|
||||
description = "Map of databases to required database/table perms.";
|
||||
default = { };
|
||||
example = {
|
||||
@@ -50,9 +55,9 @@ let
|
||||
};
|
||||
|
||||
databaseOpts = { dbname, ... }: {
|
||||
options = {
|
||||
options = with types; {
|
||||
users = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = listOf str;
|
||||
description =
|
||||
"A list of users who should have full access to this database.";
|
||||
default = [ ];
|
||||
@@ -74,9 +79,7 @@ let
|
||||
'';
|
||||
|
||||
passwords-setter-script = users:
|
||||
pkgs.writeScriptBin "postgres-set-passwords.sh" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
|
||||
pkgs.writeScript "postgres-set-passwords.sh" ''
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "usage: $0 output-file.sql"
|
||||
exit 1
|
||||
@@ -99,7 +102,7 @@ let
|
||||
nameValuePair "DATABASE ${database}" databaseOpts.access) databases;
|
||||
|
||||
makeEntry = nw:
|
||||
"host all all ${nw} gss include_realm=0 krb_realm=FUDO.ORG";
|
||||
"host all all ${nw} gss include_realm=0 krb_realm=${gssapi-realm}";
|
||||
|
||||
makeNetworksEntry = networks: join-lines (map makeEntry networks);
|
||||
|
||||
@@ -263,8 +266,8 @@ in {
|
||||
local all all ident
|
||||
|
||||
# host-local
|
||||
host all all 127.0.0.1/32 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
host all all ::1/128 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
host all all 127.0.0.1/32 gss include_realm=0 krb_realm=${gssapi-realm}
|
||||
host all all ::1/128 gss include_realm=0 krb_realm=${gssapi-realm}
|
||||
|
||||
# local networks
|
||||
${makeNetworksEntry cfg.local-networks}
|
||||
@@ -278,8 +281,7 @@ in {
|
||||
postgresql-password-setter = let
|
||||
passwords-script = passwords-setter-script cfg.users;
|
||||
password-wrapper-script =
|
||||
pkgs.writeScriptBin "password-script-wrapper.sh" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
pkgs.writeScript "password-script-wrapper.sh" ''
|
||||
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX)
|
||||
echo "using temp dir $TMPDIR"
|
||||
PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql
|
||||
@@ -287,7 +289,7 @@ in {
|
||||
touch $PASSWORD_SQL_FILE
|
||||
chown ${config.services.postgresql.superUser} $PASSWORD_SQL_FILE
|
||||
chmod go-rwx $PASSWORD_SQL_FILE
|
||||
${passwords-script}/bin/postgres-set-passwords.sh $PASSWORD_SQL_FILE
|
||||
${passwords-script} $PASSWORD_SQL_FILE
|
||||
echo "executing $PASSWORD_SQL_FILE"
|
||||
${pkgs.postgresql}/bin/psql --port ${
|
||||
toString config.services.postgresql.port
|
||||
@@ -306,7 +308,7 @@ in {
|
||||
Type = "oneshot";
|
||||
User = config.services.postgresql.superUser;
|
||||
};
|
||||
script = "${password-wrapper-script}/bin/password-script-wrapper.sh";
|
||||
script = "${password-wrapper-script}";
|
||||
};
|
||||
|
||||
postgresql.postStart = let
|
||||
|
||||
@@ -86,15 +86,17 @@ in {
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:9090";
|
||||
|
||||
extraConfig = ''
|
||||
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 fudo-cfg.local-networks) > 0)
|
||||
(concatStringsSep "\n" (map (network: "allow ${network};") fudo-cfg.local-networks)) + "\ndeny all;"}
|
||||
${optionalString ((length local-networks) > 0)
|
||||
(concatStringsSep "\n" (map (network: "allow ${network};") local-networks)) + "\ndeny all;"}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -185,18 +185,6 @@ in {
|
||||
};
|
||||
|
||||
config = {
|
||||
# users.users = {
|
||||
# ${site-cfg.build-user} = mkIf
|
||||
# (any (build-host: build-host == config.instance.hostname)
|
||||
# (attrNames site-cfg.build-servers)) {
|
||||
# isSystemUser = true;
|
||||
# openssh.authorizedKeys.keys =
|
||||
# concatMap (hostOpts: hostOpts.build-pubkeys)
|
||||
# (attrValues site-hosts);
|
||||
# shell = pkgs.bash;
|
||||
# };
|
||||
# };
|
||||
|
||||
networking.firewall.allowedTCPPorts =
|
||||
mkIf site-cfg.enable-ssh-backdoor [ site-cfg.dropbear-ssh-port ];
|
||||
|
||||
|
||||
+2
-58
@@ -1,65 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
hostname = config.instance.hostname;
|
||||
has-attrs = set: length (attrNames set) > 0;
|
||||
host-keypairs =
|
||||
if (hasAttr hostname config.fudo.secrets.files.host-ssh-keypairs) then
|
||||
config.fudo.secrets.files.host-ssh-keypairs.${hostname}
|
||||
else [];
|
||||
|
||||
|
||||
sshfp-filename = host: keypair: "ssh-${host}-${keypair.key-type}.sshfp-record";
|
||||
|
||||
dns-sshfp-records = host: keypair: let
|
||||
filename = sshfp-filename host keypair;
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = "${host}-sshfp-record";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
buildInputs = with pkgs; [ openssh ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
ssh-keygen -r REMOVEME -f "${keypair.public-key}" | sed 's/^REMOVEME IN SSHFP //' > $out/${filename}
|
||||
'';
|
||||
};
|
||||
|
||||
read-lines = filename: splitString "\n" (fileContents filename);
|
||||
|
||||
host-cfg = config.fudo.hosts.${hostname};
|
||||
|
||||
in {
|
||||
{
|
||||
config = {
|
||||
fudo = {
|
||||
secrets.host-secrets.${hostname} = listToAttrs
|
||||
(map
|
||||
(keypair: nameValuePair "host-${keypair.key-type}-private-key" {
|
||||
source-file = keypair.private-key;
|
||||
target-file = "/var/run/ssh/private/host-${keypair.key-type}-private-key";
|
||||
user = "root";
|
||||
})
|
||||
host-keypairs);
|
||||
|
||||
hosts = mapAttrs (hostname: keypairs: {
|
||||
ssh-pubkeys = map (keypair: keypair.public-key) keypairs;
|
||||
ssh-fingerprints = concatMap (keypair:
|
||||
let
|
||||
fingerprint-derivation = dns-sshfp-records hostname keypair;
|
||||
filename = sshfp-filename hostname keypair;
|
||||
in read-lines "${fingerprint-derivation}/${filename}") keypairs;
|
||||
}) config.fudo.secrets.files.host-ssh-keypairs;
|
||||
};
|
||||
|
||||
services.openssh.hostKeys = map (keypair: {
|
||||
path = "/var/run/ssh/private/host-${keypair.key-type}-private-key";
|
||||
type = keypair.key-type;
|
||||
}) host-keypairs;
|
||||
|
||||
programs.ssh.knownHosts = let
|
||||
|
||||
keyed-hosts =
|
||||
filterAttrs (h: o: o.ssh-pubkeys != [])
|
||||
config.fudo.hosts;
|
||||
@@ -75,7 +19,7 @@ in {
|
||||
|
||||
in mapAttrs (hostname: hostOpts: {
|
||||
publicKeyFile = builtins.head hostOpts.ssh-pubkeys;
|
||||
hostNames = all-hostnames hostname host-cfg;
|
||||
hostNames = all-hostnames hostname hostOpts;
|
||||
}) keyed-hosts;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Common home-manager config
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
list-contains = lst: item: any (i: i == item) lst;
|
||||
|
||||
domain-realm = domain: domainOpts: domainOpts.gssapi-realm;
|
||||
|
||||
user-realms = username:
|
||||
mapAttrsToList domain-realm
|
||||
(filterAttrs (domain: domainOpts: list-contains domainOpts.local-users username)
|
||||
config.fudo.domains);
|
||||
|
||||
user-principals = username:
|
||||
map (realm: "${username}@${realm}") (user-realms username);
|
||||
|
||||
user-k5login = username: userOpts: let
|
||||
principals = userOpts.k5login ++ (user-principals username);
|
||||
in ''
|
||||
${concatStringsSep "\n" principals}
|
||||
'';
|
||||
|
||||
user-config = username: userOpts: {
|
||||
home.file.".k5login" = {
|
||||
source = pkgs.writeText "${username}-k5login" (user-k5login username userOpts);
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
config.home-manager.users = mapAttrs user-config config.instance.local-users;
|
||||
}
|
||||
+5
-25
@@ -65,34 +65,17 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
imports = [ ./users-common.nix ];
|
||||
|
||||
config = let
|
||||
sys = config.instance;
|
||||
in {
|
||||
fudo.auth.ldap-server = let
|
||||
ldapUsers = (filterAttrs
|
||||
(username: userOpts: userOpts.ldap-hashed-password != null))
|
||||
fudo.auth.ldap-server = {
|
||||
users = filterAttrs
|
||||
(username: userOpts: userOpts.ldap-hashed-passwd != null)
|
||||
config.fudo.users;
|
||||
|
||||
in {
|
||||
users = mapAttrs (username: userOpts: {
|
||||
uid = userOpts.uid;
|
||||
group = userOpts.primary-group;
|
||||
common-name = userOpts.common-name;
|
||||
hashed-password = userOpts.ldap-hashed-password;
|
||||
}) ldapUsers;
|
||||
groups = config.fudo.groups;
|
||||
|
||||
groups = mapAttrs (groupname: groupOpts: {
|
||||
gid = groupOpts.gid-number;
|
||||
description = groupOpts.description;
|
||||
members = filterExistingUsers ldapUsers groupOpts.members;
|
||||
}) config.fudo.groups;
|
||||
|
||||
system-users = mapAttrs (username: userOpts: {
|
||||
description = userOpts.description;
|
||||
hashed-password = userOpts.ldap-hashed-passwd;
|
||||
}) config.fudo.system-users;
|
||||
system-users = config.fudo.system-users;
|
||||
};
|
||||
|
||||
programs.ssh.extraConfig = mkAfter ''
|
||||
@@ -161,9 +144,6 @@ in {
|
||||
in admin-entries // user-entries;
|
||||
};
|
||||
|
||||
# TODO: This is NOT where this should be...
|
||||
home-manager.useGlobalPkgs = true;
|
||||
|
||||
# Group home directories have to exist, otherwise users can't log in
|
||||
systemd.services = let
|
||||
ensure-group-directories = group:
|
||||
|
||||
@@ -302,7 +302,7 @@ in {
|
||||
(site: site-cfg: let
|
||||
|
||||
site-config-file = builtins.toFile "${site}-rainloop.cfg"
|
||||
(import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version);
|
||||
(import ./include/rainloop.nix lib site site-cfg site-packages.${site}.version);
|
||||
|
||||
domain-config-file = builtins.toFile "${site}-domain.cfg" ''
|
||||
imap_host = "${site-cfg.mail-server}"
|
||||
@@ -342,7 +342,7 @@ in {
|
||||
link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg:
|
||||
let
|
||||
cfg-file = config.fudo.secrets.host-secrets.${hostname}."${site}-site-config".target-file;
|
||||
domain-cfg-file = config.fudo.secrets.host-secrets.${hostname}."${site}-doomain-config".target-file;
|
||||
domain-cfg-file = config.fudo.secrets.host-secrets.${hostname}."${site}-domain-config".target-file;
|
||||
in ''
|
||||
${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/configs
|
||||
${pkgs.coreutils}/bin/cp ${cfg-file} ${base-data-path}/${site}/_data_/_default_/configs/application.ini
|
||||
|
||||
Reference in New Issue
Block a user