Tons of changes, I guess?

This commit is contained in:
niten 2021-11-05 07:06:08 -07:00
parent c31af09ede
commit 2dd5407129
62 changed files with 1789 additions and 958 deletions

29
config/aliases.nix Normal file
View File

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
let
hostname = config.instance.hostname;
admins = config.instance.local-admins;
domain = config.instance.local-domain;
gen-addrs = names: domain:
map (name: "${name}@${domain}") names;
admin-addrs = gen-addrs admins domain;
in {
config.fudo.mail-server.alias-users = {
root = admin-addrs;
postmaster = admin-addrs;
www-data = admin-addrs;
hostmaster = admin-addrs;
webmaster = admin-addrs;
ftp = admin-addrs;
irc = admin-addrs;
admin = admin-addrs;
system = admin-addrs;
asdf = [ "mswaffer@gmail.com" "bouncetest@fudo.org" ];
network-info = [ "niten@fudo.org" ];
};
}

View File

@ -2,13 +2,13 @@
{ {
imports = [ imports = [
./aliases.nix
./bash.nix ./bash.nix
./common.nix ./common.nix
./domains.nix ./domains.nix
./groups.nix ./groups.nix
./hosts.nix ./hosts.nix
./networks.nix ./networks.nix
./profiles.nix
./sites.nix ./sites.nix
./users.nix ./users.nix
./wireless-networks.nix ./wireless-networks.nix

View File

@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
}

View File

@ -3,7 +3,11 @@
{ {
config.fudo.domains = { config.fudo.domains = {
"fudo.org" = { "fudo.org" = {
local-networks = [ "208.81.1.128/28" "208.81.3.112/28" ]; local-networks = [
"208.81.1.128/28"
"208.81.3.112/28"
"91.229.23.204/31"
];
local-users = [ "niten" "reaper" ]; local-users = [ "niten" "reaper" ];
local-groups = [ "fudo" "selby" "admin" ]; local-groups = [ "fudo" "selby" "admin" ];
@ -13,7 +17,12 @@
}; };
"sea.fudo.org" = { "sea.fudo.org" = {
local-networks = [ "10.0.0.0/16" ]; local-networks = [
"10.0.0.0/16"
"208.81.1.128/28"
"208.81.3.112/28"
];
local-users = [ "niten" "reaper" "xiaoxuan" "ken" ]; local-users = [ "niten" "reaper" "xiaoxuan" "ken" ];
local-groups = [ "fudo" "selby" "admin" ]; local-groups = [ "fudo" "selby" "admin" ];
@ -44,7 +53,9 @@
}; };
"informis.land" = { "informis.land" = {
local-networks = [ ]; local-networks = [
"172.86.179.17/29"
];
local-users = [ "niten" "viator" ]; local-users = [ "niten" "viator" ];
local-groups = [ "admin" "informis" ]; local-groups = [ "admin" "informis" ];
@ -52,5 +63,19 @@
admin-email = "viator@informis.land"; admin-email = "viator@informis.land";
gssapi-realm = "INFORMIS.LAND"; gssapi-realm = "INFORMIS.LAND";
}; };
eur.fudo.org = {
local-networks = [
"208.81.1.128/28"
"208.81.3.112/28"
"91.229.23.204/31"
];
local-users = [ "niten"];
local-groups = [ "admin" ];
local-admins = [ "niten" ];
admin-email = "nitenn@fudo.org";
gssapi-realm = "FUDO.ORG";
};
}; };
} }

View File

@ -3,8 +3,15 @@
{ {
boot = { boot = {
initrd = { initrd = {
availableKernelModules = availableKernelModules = [
[ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "floppy" "sd_mod" "sr_mod" ]; "uhci_hcd"
"ehci_pci"
"ata_piix"
"ahci"
"floppy"
"sd_mod"
"sr_mod"
];
kernelModules = [ "dm-snapshot" ]; kernelModules = [ "dm-snapshot" ];
}; };
@ -23,23 +30,27 @@
"/boot" = { "/boot" = {
device = "/dev/disk/by-label/france-boot"; device = "/dev/disk/by-label/france-boot";
fsType = "ext4"; fsType = "ext4";
options = [ "noatime" "nodiratime" ];
}; };
"/" = { "/" = {
device = "/dev/disk/by-label/france-root"; device = "/dev/disk/by-label/france-root";
fsType = "ext4"; fsType = "ext4";
options = [ "noatime" "nodiratime" ];
}; };
"/var/lib/lxd/storage-pools/pool0" = { "/var/lib/lxd/storage-pools/pool0" = {
device = "/dev/disk/by-label/pool0"; device = "/dev/disk/by-label/pool0";
fsType = "btrfs"; fsType = "btrfs";
label = "pool0"; label = "pool0";
options = [ "noatime" "nodiratime" "noexec" ];
}; };
"/var/lib/lxd/storage-pools/pool1" = { "/var/lib/lxd/storage-pools/pool1" = {
device = "/dev/france-user/fudo-user"; device = "/dev/france-user/fudo-user";
fsType = "btrfs"; fsType = "btrfs";
label = "pool1"; label = "pool1";
options = [ "noatime" "nodiratime" "noexec" ];
}; };
}; };
@ -50,6 +61,8 @@
hardware.bluetooth.enable = false; hardware.bluetooth.enable = false;
networking = { networking = {
useDHCP = false;
macvlans = { macvlans = {
intif0 = { intif0 = {
interface = "enp4s0f1"; interface = "enp4s0f1";
@ -60,6 +73,11 @@
interface = "enp4s0f0"; interface = "enp4s0f0";
mode = "bridge"; mode = "bridge";
}; };
# extif1 = {
# interface = "enp4s0f0";
# mode = "bridge";
# };
}; };
interfaces = { interfaces = {
@ -72,6 +90,11 @@
# output of: echo france-extif0|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/' # output of: echo france-extif0|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
macAddress = "02:5e:ff:e4:83:e4"; macAddress = "02:5e:ff:e4:83:e4";
}; };
# extif1 = {
# # output of: echo france-extif1|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
# macAddress = "02:30:91:97:40:1f";
# };
}; };
}; };
} }

View File

@ -0,0 +1,75 @@
{ config, lib, pkgs, ... }:
with lib; {
boot = {
initrd = {
availableKernelModules = [
"ahci"
"usbhid"
];
kernelModules = [ "dm-snapshot" ];
};
kernelModules = [ ];
extraModulePackages = [ ];
loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
supportedFilesystems = [ "btrfs" ];
};
fileSystems = {
"/" = {
device = "root-tmpfs";
fsType = "tmpfs";
options = [ "mode=755" "noexec" ];
};
"/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "ext4";
options = [ "noexec" "noatime" "nodiratime" ];
};
"/nix" = {
device = "/dev/disk/by-label/system";
fsType = "btrfs";
options = [ "subvol=@nix" "compress=zstd" "noatime" "nodiratime" ];
};
"/var/log" = {
device = "/dev/disk/by-label/system";
fsType = "btrfs";
options = [ "subvol=@logs" "compress=zstd" "noatime" "nodiratime" "noexec" ];
neededForBoot = true;
};
"/state" = {
device = "/dev/disk/by-label/system";
fsType = "btrfs";
options = [ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/swap"; }];
networking = {
macvlans = {
extif0 = {
interface = "eno2";
mode = "bridge";
};
};
useDHCP = false;
interfaces = {
extif0 = {
# output of: echo legatus-extif0|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
macAddress = pkgs.lib.fudo.network.generate-mac-address "legatus" "extif0";
};
};
};
}

View File

@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
}

View File

@ -71,6 +71,7 @@
device = "/dev/disk/by-label/socrates-data"; device = "/dev/disk/by-label/socrates-data";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=@log" "compress=zstd" "noatime" "nodiratime" "noexec" ]; options = [ "subvol=@log" "compress=zstd" "noatime" "nodiratime" "noexec" ];
neededForBoot = true;
}; };
"/state" = { "/state" = {

View File

@ -9,6 +9,7 @@ let
domain = config.fudo.domains.${domain-name}; domain = config.fudo.domains.${domain-name};
host-fqdn = "${hostname}.${domain-name}"; host-fqdn = "${hostname}.${domain-name}";
mail-hostname = "mail.fudo.org"; mail-hostname = "mail.fudo.org";
mail-directory = "/srv/mail";
secrets = config.fudo.secrets.host-secrets.france; secrets = config.fudo.secrets.host-secrets.france;
secret-files = config.fudo.secrets.files; secret-files = config.fudo.secrets.files;
@ -37,14 +38,73 @@ in {
config = { config = {
security.acme.email = "admin@fudo.org"; security.acme.email = "admin@fudo.org";
fudo = { fileSystems = {
"/srv/archiva" = {
fsType = "btrfs";
label = "pool0";
options = [ "noatime" "nodiratime" "noexec" "subvol=archiva" ];
};
"/srv/grafana" = {
fsType = "btrfs";
label = "pool0";
options = [ "noatime" "nodiratime" "noexec" "subvol=grafana" ];
};
"/srv/gitlab" = {
fsType = "btrfs";
label = "pool0";
options = [ "noatime" "nodiratime" "noexec" "subvol=grafana" ];
};
${mail-directory} = {
fsType = "btrfs";
label = "pool0";
options = [ "noatime" "nodiratime" "noexec" "subvol=mail" ];
};
};
users.users.archiva = {
isSystemUser = true;
group = "nogroup";
};
virtualisation = {
lxd.enable = true;
oci-containers = {
backend = "docker";
containers = {
archiva = {
image = "xetusoss/archiva";
autoStart = true;
ports = [ "8001:8080/tcp" ];
# Ugly: name-to-uid lookup fails.
user = toString config.users.users.archiva.uid;
volumes = [ "/srv/archiva:/archiva-data" ];
environment = {
# Not directly connected to the world anyway
SSL_ENABLED = "false";
PROXY_BASE_URL = "https://archiva.fudo.org/";
};
};
};
};
};
fudo = let
backplane-dns-password-file = pkgs.lib.fudo.passwd.stablerandom-passwd-file
"dns-service-backplane-passwd"
"dns-service-backplane-passwd-${config.instance.build-seed}";
in {
hosts.france.external-interfaces = [ "extif0" ]; hosts.france.external-interfaces = [ "extif0" ];
acme.host-domains.france."france.fudo.org" = { acme.host-domains.france."france.fudo.org" = {
email = "admin@fudo.org"; email = "admin@fudo.org";
local-copies = { local-copies = {
postgres = { postgres = {
user = config.services.postgresql.user; user = "postgres";
}; };
openldap = { openldap = {
user = config.services.openldap.user; user = config.services.openldap.user;
@ -56,26 +116,6 @@ in {
ldap-user = config.services.openldap.user; ldap-user = config.services.openldap.user;
ldap-group = config.services.openldap.group; ldap-group = config.services.openldap.group;
in { in {
ldap-ssl-certificate = {
source-file = cfg.ssl-certificate;
target-file = "/run/openldap/ssl-certificate.pem";
user = ldap-user;
group = ldap-group;
permissions = "0444";
};
ldap-ssl-private-key = {
source-file = cfg.ssl-private-key;
target-file = "/run/openldap/ssl-private-key.pem";
user = ldap-user;
group = ldap-group;
};
ldap-ssl-ca-certificate = {
source-file = cfg.ssl-ca-certificate;
target-file = "/run/openldap/ssl-ca-certificate.pem";
user = ldap-user;
group = ldap-group;
permissions = "0444";
};
ldap-keytab = { ldap-keytab = {
source-file = secret-files.service-keytabs.france.ldap; source-file = secret-files.service-keytabs.france.ldap;
target-file = "/run/openldap/ldap.keytab"; target-file = "/run/openldap/ldap.keytab";
@ -83,7 +123,8 @@ in {
group = ldap-group; group = ldap-group;
}; };
ldap-root-passwd = { ldap-root-passwd = {
source-file = passwd.random-passwd-file; source-file =
pkgs.lib.fudo.passwd.random-passwd-file "ldap-root-passwd" 20;
target-file = "/run/openldap/root.passwd"; target-file = "/run/openldap/root.passwd";
user = ldap-user; user = ldap-user;
group = ldap-group; group = ldap-group;
@ -91,7 +132,12 @@ in {
postgres-keytab = { postgres-keytab = {
source-file = secret-files.service-keytabs.france.postgres; source-file = secret-files.service-keytabs.france.postgres;
target-file = "/run/postgres/postgres.keytab"; target-file = "/run/postgres/postgres.keytab";
user = config.services.postgresql.user; user = "postgres"; # This is just plain hard-coded...
};
backplane-dns-password = {
source-file = backplane-dns-password-file;
target-file = "/run/backplane/dns/xmpp.passwd";
user = config.fudo.backplane.dns.user;
}; };
}; };
@ -117,34 +163,34 @@ in {
kdc = { kdc = {
state-directory = "/state/kerberos"; state-directory = "/state/kerberos";
master-key-file = ""; master-key-file = secret-files.realm-master-keys."FUDO.ORG";
listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ];
}; };
jabber = { jabber = {
ldap-servers = [ "france.fudo.org" ]; ldap-servers = [ "france.fudo.org" ];
listen-ips = [ primary-ip ]; listen-ips = [ primary-ip ];
};
backplane = { backplane = {
host-passwd-files = let host-passwd-files = let
hosts = attrNames config.fudo.hosts; hosts = attrNames config.fudo.hosts;
in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file) in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file)
config.fudo.hosts; config.fudo.hosts;
service-passwd-files = genAttrs [ "dns" ] service-passwd-files = {
(service-name: dns = backplane-dns-password-file;
lib.fudo.passwd.stablerandom-passwd-file };
"${service-name}-service-backplane-passwd" };
"${service-name}-service-backplane-passwd-${config.instance.build-seed}");
}; };
backplane-server = { backplane-server = {
listen-ips = [ primary-ip ]; listen-ips = [ primary-ip ];
backplane-dns-password-file =
secrets.backplane-dns-password.target-file;
}; };
mail = { mail = {
mail-directory = "/srv/mail/mailboxes"; mail-directory = "${mail-directory}/mailboxes";
state-directory = "/srv/mail/var"; state-directory = "${mail-directory}/var";
ldap-server-urls = [ ldap-server-urls = [
"ldap://france.fudo.org" "ldap://france.fudo.org"
]; ];
@ -169,6 +215,18 @@ in {
ssl-certificate = cert-copy.certificate; ssl-certificate = cert-copy.certificate;
ssl-private-key = cert-copy.private-key; ssl-private-key = cert-copy.private-key;
}; };
dns = {
default-host = primary-ip;
listen-ip = primary-ip;
mail-hosts = [ "mail.fudo.org" ];
};
chat = {
chat-hostname = "chat.fudo.org";
mail-server = "mail.fudo.org";
database-host = "localhost";
};
}; };
minecraft-server = { minecraft-server = {
@ -181,8 +239,6 @@ in {
}; };
networking = { networking = {
useDHCP = false;
interfaces = { interfaces = {
intif0 = { intif0 = {
ipv4.addresses = [{ ipv4.addresses = [{
@ -196,10 +252,6 @@ in {
address = primary-ip; address = primary-ip;
prefixLength = 28; prefixLength = 28;
} }
{
address = git-server-ip;
prefixLength = 32;
}
]; ];
}; };
}; };
@ -218,6 +270,22 @@ in {
enableACME = true; enableACME = true;
locations."/".return = "301 https://webmail.fudo.org$request_uri"; locations."/".return = "301 https://webmail.fudo.org$request_uri";
}; };
"archiva.fudo.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8001";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
}; };
}; };
}; };

View File

@ -11,8 +11,6 @@ let
concatGenAttrs = lst: f: concatGenAttrs = lst: f:
foldr (a0: a1: a0 // a1) {} (map f lst); foldr (a0: a1: a0 // a1) {} (map f lst);
passwd = import ../../../lib/passwd.nix { inherit lib; };
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france; cfg = config.fudo.france;
@ -69,45 +67,47 @@ in {
user = config.fudo.auth.kdc.user; user = config.fudo.auth.kdc.user;
}; };
auth = { # auth = {
ldap-server = { # ldap-server = {
enable = true; # enable = true;
base = "dc=fudo,dc=org"; # base = "dc=fudo,dc=org";
organization = "Fudo"; # organization = "Fudo";
rootpw-file = cfg.ldap.root-password-file; # rootpw-file = cfg.ldap.root-password-file;
kerberos-host = fqdn; # kerberos-host = fqdn;
kerberos-keytab = cfg.ldap.keytab; # kerberos-keytab = cfg.ldap.keytab;
ssl-certificate = cfg.ldap.ssl-certificate; # ssl-certificate = cfg.ldap.ssl-certificate;
ssl-private-key = cfg.ldap.ssl-private-key; # ssl-private-key = cfg.ldap.ssl-private-key;
ssl-ca-certificate = cfg.ldap.ssl-ca-certificate; # ssl-ca-certificate = cfg.ldap.ssl-ca-certificate;
listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ]; # listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ];
users = config.fudo.users; # users = config.fudo.users;
groups = config.fudo.groups; # groups = config.fudo.groups;
system-users = config.fudo.system-users; # system-users = config.fudo.system-users;
};
# TODO: let build hosts create keys? # database-directory = "/state/openldap";
kdc = { # };
enable = true;
realm = config.fudo.domains.${domain-name}.gssapi-realm; # # TODO: let build hosts create keys?
state-directory = cfg.kdc.state-directory; # kdc = {
master-key-file = secrets.kdc-master-key.target-file; # enable = true;
acl = let # realm = config.fudo.domains.${domain-name}.gssapi-realm;
admin-entries = concatGenAttrs # state-directory = cfg.kdc.state-directory;
config.instance.local-admins # master-key-file = secrets.kdc-master-key.target-file;
(admin: { # acl = let
"${admin}" = { perms = [ "add" "list" "change-password" ]; }; # admin-entries = concatGenAttrs
"${admin}/root" = { perms = [ "all" ]; }; # config.instance.local-admins
}); # (admin: {
in { # "${admin}" = { perms = [ "add" "list" "change-password" ]; };
"host/*.fudo.org" = { perms = [ "add" ]; }; # "${admin}/root" = { perms = [ "all" ]; };
"pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; }; # });
} // admin-entries; # in {
bind-addresses = cfg.kdc.listen-ips; # "host/*.fudo.org" = { perms = [ "add" ]; };
}; # "pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; };
}; # } // admin-entries;
# bind-addresses = cfg.kdc.listen-ips;
# };
# };o
}; };
}; };
} }

View File

@ -10,14 +10,12 @@ let
backplane-dns-user = "backplane-dns"; backplane-dns-user = "backplane-dns";
generate-role-passwd = role: generate-role-passwd = role:
lib.fudo.passwd.stablerandom-password-file pkgs.lib.fudo.passwd.stablerandom-passwd-file
"backplane-${role}-password" "backplane-${role}-password"
"${hostname}-${domain}-${role}-password-${config.instance.build-timestamp}"; "${hostname}-${domain}-${role}-password-${config.instance.build-seed}";
powerdns-password = generate-role-passwd "powerdns-db"; powerdns-password = generate-role-passwd "powerdns-db";
backplane-dns-xmpp-password = generate-role-passwd "backplane-dns-xmpp";
backplane-dns-db-password = generate-role-passwd "backplane-dns-db"; backplane-dns-db-password = generate-role-passwd "backplane-dns-db";
secrets = config.fudo.secrets.host-secrets.france; secrets = config.fudo.secrets.host-secrets.france;
@ -36,6 +34,11 @@ in {
description = "List of IPv6s on which to listen for incoming backplane connections."; description = "List of IPv6s on which to listen for incoming backplane connections.";
default = []; default = [];
}; };
backplane-dns-password-file = mkOption {
type = str;
description = "Path to file containing the password for connecting to the XMPP backplane.";
};
}; };
config = { config = {
@ -64,19 +67,13 @@ in {
powerdns-password = { powerdns-password = {
source-file = powerdns-password; source-file = powerdns-password;
target-file = "/run/backplane/dns/powerdns/db.passwd"; target-file = "/run/backplane/dns/powerdns/db.passwd";
user = config.fudo.backplane.dns.database.user; user = config.fudo.backplane.dns.powerdns.user;
}; };
backplane-dns-db-password = { backplane-dns-db-password = {
source-file = backplane-dns-db-password; source-file = backplane-dns-db-password;
target-file = "/run/backplane/dns/db.passwd"; target-file = "/run/backplane/dns/db.passwd";
user = config.fudo.backplane.dns.backplane.user; user = config.fudo.backplane.dns.user;
};
backplane-dns-xmpp-password = {
source-file = backplane-dns-db-password;
target-file = "/run/backplane/dns/xmpp.passwd";
user = config.fudo.backplane.dns.backplane.user;
}; };
}; };
@ -98,7 +95,7 @@ in {
}; };
}; };
${backplane-dns-user} = { ${backplane-dns-user} = {
password-file = secrets.backplane-dns-db-password; password-file = secrets.backplane-dns-db-password.target-file;
databases = { databases = {
backplane_dns = { backplane_dns = {
access = "CONNECT"; access = "CONNECT";
@ -134,7 +131,7 @@ in {
backplane = { backplane = {
host = "backplane.fudo.org"; host = "backplane.fudo.org";
role = "service-dns"; role = "service-dns";
password-file = secrets.backplane-dns-xmpp-password.target-file; password-file = cfg.backplane-dns-password-file;
database = { database = {
username = backplane-dns-user; username = backplane-dns-user;
database = backplane-dns-user; database = backplane-dns-user;

View File

@ -0,0 +1,98 @@
{ config, lib, pkgs, ... }:
with lib;
{
options.fudo.france.chat = with types; {
chat-hostname = mkOption {
type = str;
description = "Hostname of the chat server.";
};
mail-server = mkOption {
type = str;
description = "Email server to use for communication.";
};
database-host = mkOption {
type = str;
description = "Hostname of the database server.";
};
};
config = let
hostname = config.instance.hostname;
cfg = config.fudo.france.chat;
secrets = config.fudo.secrets.host-secrets.${hostname};
in {
fudo = {
secrets.host-secrets.${hostname} = {
mattermost-mail-password = {
source-file = pkgs.lib.fudo.passwd.stablerandom-passwd-file
"mattermost-mail-password"
"${hostname}-mattermost-mail-password-${config.instance.build-seed}";
target-file = "/run/chat/mattermost/mail.passwd";
user = config.services.mattermost.user;
};
mattermost-db-password = {
source-file = pkgs.lib.fudo.passwd.stablerandom-passwd-file
"mattermost-db-password"
"${hostname}-mattermost-db-password-${config.instance.build-seed}";
target-file = "/run/chat/mattermost/database.passwd";
user = config.services.mattermost.user;
};
};
users.fudo-chat = {
uid = 20001;
primary-group = "fudo";
common-name = "Fudo Chat";
ldap-hashed-passwd =
pkgs.lib.fudo.passwd.hash-ldap-passwd "mattermost-chat"
secrets.mattermost-mail-password.source-file;
};
postgresql = {
databases.mattermost.users =
config.instance.local-admins;
users.mattermost = {
password-file =
secrets.mattermost-db-password.target-file;
databases = {
mattermost = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" =
"SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" =
"SELECT,UPDATE";
};
};
};
};
};
chat = {
enable = true;
hostname = cfg.chat-hostname;
site-name = "Fudo Chat";
smtp = {
server = cfg.mail-server;
user = "fudo-chat";
password-file = secrets.mattermost-mail-password.target-file;
};
database = {
name = "mattermost";
hostname = cfg.database-host;
user = "mattermost";
password-file = secrets.mattermost-db-password.target-file;
};
};
};
};
}

View File

@ -0,0 +1,88 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
cfg = config.fudo.france.dns;
in {
options.fudo.france.dns = with types; {
default-host = mkOption {
type = str;
description = "IP address to which the domain will map.";
};
listen-ip = mkOption {
type = str;
description = "IP addresses on which to listen";
};
listen-ipv6 = mkOption {
type = nullOr str;
description = "IPv6 addresses on which to listen";
default = null;
};
mail-hosts = mkOption {
type = listOf str;
description = "List of mail hosts for the MX records.";
};
};
config = let
dom = config.instance.local-domain;
dom-cfg = config.fudo.domains.${dom};
in {
fudo = {
mail-server.alias-users.dmarc-report =
map (admin: "${admin}@${dom}") dom-cfg.local-admins;
dns = {
enable = true;
identity = "${hostname}.fudo.org";
listen-ips =
[ cfg.listen-ip ] ++
(optional (cfg.listen-ipv6 != null) cfg.listen-ipv6);
nameservers = {
ns1 = {
ipv4-address = cfg.listen-ip;
ipv6-address = mkIf (cfg.listen-ipv6 != null) cfg.listen-ipv6;
description = "Nameserver 1, france, in Winnipeg, MB, CA";
};
ns2 = {
ipv4-address = "209.117.102.102";
ipv6-address = "2001:470:1f16:40::2";
description = "Nameserver 2, musashi, in Winnipeg, MB, CA";
};
ns3 = {
ipv4-address = "104.131.53.95";
ipv6-address = "2604:a880:800:10::8:7001";
description =
"Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
};
ns4 = {
ipv4-address = "204.42.254.5";
ipv6-address = "2001:418:3f4::5";
description = "Nameserver 4, puck.nether.net, in Chicago, IL, US";
};
};
domains = let
in {
${dom} = {
dnssec = true;
default-host = cfg.default-host;
gssapi-realm = dom-cfg.gssapi-realm;
mx = cfg.mail-hosts;
dmarc-report-address = "dmarc-report@${dom}";
network-definition = import ../../networks/fudo.org.nix;
};
};
};
};
};
}

View File

@ -46,7 +46,7 @@ in {
config.fudo = { config.fudo = {
secrets.host-secrets.${hostname}.git-database-password = { secrets.host-secrets.${hostname}.git-database-password = {
source-file = lib.fudo.passwd.stablerandom-passwd-file source-file = pkgs.lib.fudo.passwd.stablerandom-passwd-file
"gitea-database-passwd" "gitea-database-passwd"
"${hostname}-gitea-database-passwd-${config.instance.build-seed}"; "${hostname}-gitea-database-passwd-${config.instance.build-seed}";
target-file = "/var/gitea/database.passwd"; target-file = "/var/gitea/database.passwd";
@ -55,7 +55,7 @@ in {
postgresql = { postgresql = {
databases.fudo_git.users = databases.fudo_git.users =
config.instance.local_admins; config.instance.local-admins;
users.fudo_git = { users.fudo_git = {
password-file = password-file =

View File

@ -5,23 +5,23 @@ let
hostname = config.instance.hostname; hostname = config.instance.hostname;
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france; cfg = config.fudo.france.jabber;
generate-auth-file = name: files: let generate-auth-file = name: files: let
make-entry = name: passwd-file: make-entry = name: passwd-file:
''("${name}" . "${readFile passwd-file}")''; ''("${name}" . "${readFile passwd-file}")'';
entries = mapAttrsToList make-entry files; entries = mapAttrsToList make-entry files;
content = concatStringsSep "\n" entries; content = concatStringsSep "\n" entries;
in writeText "${name}-backplane-auth.scm" "'(${content})'"; in pkgs.writeText "${name}-backplane-auth.scm" "'(${content})";
host-auth-file = generate-auth-file "host" cfg.host-passwd-files; host-auth-file = generate-auth-file "host" cfg.backplane.host-passwd-files;
service-auth-file = generate-auth-filre "service" cfg.service-passwd-files; service-auth-file = generate-auth-file "service" cfg.backplane.service-passwd-files;
ldap-password-file = ldap-password-file =
lib.fudo.passwd.random-passwd-file "ejabberd-ldap-auth-user"; pkgs.lib.fudo.passwd.random-passwd-file "ejabberd-ldap-auth-user" 30;
ldap-hashed-password = ldap-hashed-password =
hash-ldap-passwd "ejabberd-ldap-hashed-passwd" ldap-password-file; pkgs.lib.fudo.passwd.hash-ldap-passwd "ejabberd-ldap-hashed-passwd" ldap-password-file;
in { in {
options.fudo.france = with types; { options.fudo.france = with types; {
@ -41,7 +41,6 @@ in {
type = listOf str; type = listOf str;
description = "IPs on which to listen for incoming connections."; description = "IPs on which to listen for incoming connections.";
}; };
};
backplane = { backplane = {
host-passwd-files = mkOption { host-passwd-files = mkOption {
@ -57,12 +56,13 @@ in {
}; };
}; };
}; };
};
config = { config = {
fudo = { fudo = {
system-users.${cfg.jabber.ldap-user} = { system-users.${cfg.ldap-user} = {
description = "ejabberd authentication user."; description = "ejabberd authentication user.";
hashed-password = ldap-hashed-password; ldap-hashed-password = ldap-hashed-password;
}; };
secrets.host-secrets.${hostname} = let secrets.host-secrets.${hostname} = let
@ -88,7 +88,7 @@ in {
jabber = { jabber = {
enable = true; enable = true;
listen-ips = cfg.jabber.listen-ips; listen-ips = cfg.listen-ips;
environment = { environment = {
FUDO_HOST_PASSWD_FILE = secrets.host-auth.target-file; FUDO_HOST_PASSWD_FILE = secrets.host-auth.target-file;
@ -103,9 +103,9 @@ in {
"fudo.im" = { "fudo.im" = {
site-config = { site-config = {
auth_method = "ldap"; auth_method = "ldap";
ldap_servers = cfg.jabber.ldap-servers; ldap_servers = cfg.ldap-servers;
ldap_port = 389; ldap_port = 389;
ldap_rootdn = "cn=${cfg.jabber.ldap-user},dc=fudo,dc=org"; ldap_rootdn = "cn=${cfg.ldap-user},dc=fudo,dc=org";
ldap_password = ''"LDAP_PASSWD"''; ldap_password = ''"LDAP_PASSWD"'';
ldap_base = "ou=members,dc=fudo,dc=org"; ldap_base = "ou=members,dc=fudo,dc=org";
ldap_filter = "(objectClass=posixAccount)"; ldap_filter = "(objectClass=posixAccount)";

View File

@ -5,6 +5,8 @@ let
hostname = config.instance.hostname; hostname = config.instance.hostname;
domain-name = config.instance.local-domain; domain-name = config.instance.local-domain;
cfg = config.fudo.france.mail;
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
mail-reader-dn = "mail-auth-reader"; mail-reader-dn = "mail-auth-reader";
@ -26,35 +28,63 @@ in {
}; };
}; };
config.fudo = { config.fudo = let
system-users = { mail-reader-password =
username = mail-reader-dn; pkgs.lib.fudo.passwd.random-passwd-file "${mail-reader-dn}-ldap-password" 30;
in {
# This is used at build time...
# secrets.host-secrets.${hostname}.mail-reader-passwd = {
# source-file = ldap-password;
# target-file = "/run/mail/${mail-reader-dn}-ldap.passwd";
# user = config.services.dovecot2.user;
# };
system-users.${mail-reader-dn} = {
description = "Used by the mail server to connect to LDAP for auth."; description = "Used by the mail server to connect to LDAP for auth.";
ldap-hashed-password = ldap-hashed-password =
pkgs.lib.fudo.passwd.hash-ldap-passwd pkgs.lib.fudo.passwd.hash-ldap-passwd
secrets.mail-reader-passwd.target-file; "${mail-reader-dn}-hashed"
mail-reader-password;
}; };
mail-server = { mail-server = let
mail-hostname = "mail.${domain-name}";
mail-ssl-dir = config.security.acme.certs.${mail-hostname}.directory;
ssl-certificate = "${mail-ssl-dir}/cert.pem";
ssl-private-key = "${mail-ssl-dir}/key.pem";
in {
enableContainer = true; enableContainer = true;
monitoring = true; monitoring = true;
domain = domain-name; domain = domain-name;
mail-hostname = "mail.${domain-name}"; mail-hostname = "mail.${domain-name}";
trusted-networks = config.instance.local-networks;
dovecot = { dovecot = {
ldap = { ldap = {
reader-dn = "cn=${mail-reader-dn},${config.fudo.auth.ldap.base}"; reader-dn = "cn=${mail-reader-dn},${config.fudo.authentication.base}";
reader-password-file = secrets.mail-reader-passwd.target-file; reader-password-file = mail-reader-password;
server-urls = cfg.ldap-server-urls; server-urls = cfg.ldap-server-urls;
}; };
}; };
user-aliases = let
aliased-users = filterAttrs
(username: userOpts: length userOpts.email-aliases > 0)
config.fudo.users;
in mapAttrs (username: userOpts: userOpts.email-aliases) aliased-users;
state-directory = cfg.state-directory; state-directory = cfg.state-directory;
mail-directory = cfg.mail-directory; mail-directory = cfg.mail-directory;
clamav.enable = true; clamav.enable = true;
dkim.signing = true; dkim.signing = true;
ssl = {
certificate = ssl-certificate;
private-key = ssl-private-key;
};
}; };
}; };
} }

View File

@ -4,6 +4,7 @@ with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france.postgresql;
in { in {
options.fudo.france.postgresql = with types; { options.fudo.france.postgresql = with types; {
ssl-certificate = mkOption { ssl-certificate = mkOption {

View File

@ -8,11 +8,10 @@ let
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
static = config.fudo.static; # TODO: what should go here?
static = ../../../static;
mail-hostname = config.france.webmail.mail-server; cfg = config.fudo.france.webmail;
db-host = config.france.webmail.database.hostname;
db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40; db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40;
@ -39,12 +38,12 @@ in {
"webmail.fudo.link" = { "webmail.fudo.link" = {
title = "Fudo Link Webmail"; title = "Fudo Link Webmail";
favicon = "${static}/fudo.link/favicon.ico"; favicon = "${static}/fudo.link/favicon.ico";
mail-server = mail-hostname; mail-server = cfg.mail-server;
domain = "fudo.link"; domain = "fudo.link";
edit-mode = "Plain"; edit-mode = "Plain";
layout-mode = "bottom"; layout-mode = "bottom";
database = { database = {
hostname = db-host; hostname = cfg.database.hostname;
password-file = db-passwd; password-file = db-passwd;
}; };
}; };
@ -52,11 +51,11 @@ in {
"webmail.test.fudo.org" = { "webmail.test.fudo.org" = {
title = "Fudo Webmail"; title = "Fudo Webmail";
favicon = "${static}/fudo.org/favicon.ico"; favicon = "${static}/fudo.org/favicon.ico";
mail-server = mail-hostname; mail-server = cfg.mail-server;
domain = "fudo.org"; domain = "fudo.org";
edit-mode = "Plain"; edit-mode = "Plain";
database = { database = {
hostname = db-host; hostname = cfg.database.hostname;
password-file = db-passwd; password-file = db-passwd;
}; };
}; };
@ -64,11 +63,11 @@ in {
"webmail.fudo.org" = { "webmail.fudo.org" = {
title = "Fudo Webmail"; title = "Fudo Webmail";
favicon = "${static}/fudo.org/favicon.ico"; favicon = "${static}/fudo.org/favicon.ico";
mail-server = mail-hostname; mail-server = cfg.mail-server;
domain = "fudo.org"; domain = "fudo.org";
edit-mode = "Plain"; edit-mode = "Plain";
database = { database = {
hostname = db-host; hostname = cfg.database.hostname;
password-file = db-passwd; password-file = db-passwd;
}; };
}; };
@ -76,10 +75,10 @@ in {
"webmail.test.selby.ca" = { "webmail.test.selby.ca" = {
title = "Selby Webmail"; title = "Selby Webmail";
favicon = "${static}/selby.ca/favicon.ico"; favicon = "${static}/selby.ca/favicon.ico";
mail-server = mail-hostname; mail-server = cfg.mail-server;
domain = "selby.ca"; domain = "selby.ca";
database = { database = {
hostname = db-host; hostname = cfg.database.hostname;
password-file = db-passwd; password-file = db-passwd;
}; };
}; };
@ -87,10 +86,10 @@ in {
"webmail.selby.ca" = { "webmail.selby.ca" = {
title = "Selby Webmail"; title = "Selby Webmail";
favicon = "${static}/selby.ca/favicon.ico"; favicon = "${static}/selby.ca/favicon.ico";
mail-server = mail-hostname; mail-server = cfg.mail-server;
domain = "selby.ca"; domain = "selby.ca";
database = { database = {
hostname = db-host; hostname = cfg.database.hostname;
password-file = db-passwd; password-file = db-passwd;
}; };
}; };

View File

@ -0,0 +1,233 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = "legatus";
host-ipv4 = "91.229.23.204";
domain-name = config.fudo.hosts.${hostname}.domain;
domain = config.fudo.domains.${domain-name};
site-name = config.fudo.hosts.${hostname}.site;
site = config.fudo.sites.${site-name};
host-fqdn = "${hostname}.${domain-name}";
local-packages = with pkgs; [ ldns.examples ];
secrets = config.fudo.secrets.host-secrets.${hostname};
in {
networking = {
enableIPv6 = true;
nameservers = [ "1.1.1.1" ];
defaultGateway = {
address = site.gateway-v4;
interface = "extif0";
};
interfaces.extif0.ipv4.addresses = [{
address = host-ipv4;
prefixLength = 24;
}];
};
systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime"
];
environment.systemPackages = local-packages;
# networking.firewall.allowedTCPPorts = [ 80 443 ];
# informis.cl-gemini = {
# enable = true;
# hostname = "gemini.informis.land";
# server-ip = host-ipv4;
# document-root = "/srv/gemini/root";
# textfiles-archive = "${pkgs.textfiles}";
# slynk-port = 4005;
# feeds = {
# viator = {
# title = "viator's phlog";
# path = "/home/viator/gemini-public/feed/";
# url = "gemini://informis.land/user/viator/feed/";
# };
# };
# };
fudo = {
hosts.legatus.external-interfaces = [ "extif0" ];
# secrets.host-secrets.procul = let
# files = config.fudo.secrets.files;
# in {
# postgres-keytab = {
# source-file = files.service-keytabs.procul.postgres;
# target-file = "/srv/postgres/secure/postgres.keytab";
# user = "root";
# };
# gitea-database-password = {
# source-file = files.service-passwords.procul.gitea-database;
# target-file = "/srv/gitea/secure/database.passwd";
# user = config.fudo.git.user;
# };
# };
# client.dns = {
# enable = true;
# ipv4 = true;
# ipv6 = true;
# user = "fudo-client";
# external-interface = "extif0";
# };
# auth.kdc = {
# enable = true;
# realm = "INFORMIS.LAND";
# bind-addresses = [ host-ipv4 "127.0.0.1" ];
# acl = {
# "niten" = { perms = [ "add" "change-password" "list" ]; };
# "*/root" = { perms = [ "all" ]; };
# };
# };
# secure-dns-proxy = {
# enable = true;
# upstream-dns =
# [ "https://1.1.1.1/dns-query" "https://1.0.0.1/dns-query" ];
# bootstrap-dns = "1.1.1.1";
# listen-ips = [ "127.0.0.1" ];
# listen-port = 53;
# allowed-networks = [ "1.1.1.1/32" "1.0.0.1/32" "localhost" "link-local" ];
# };
# dns = {
# enable = true;
# identity = "procul.informis.land";
# nameservers = {
# ns1 = {
# ipv4-address = host-ipv4;
# description = "Primary Informis Nameserver";
# };
# ns2 = {
# ipv4-address = host-ipv4;
# description = "Secondary Informis Nameserver";
# };
# };
# listen-ips = [ host-ipv4 ];
# domains = {
# "informis.land" = {
# dnssec = true;
# default-host = host-ipv4;
# gssapi-realm = "INFORMIS.LAND";
# mx = [ "smtp.informis.land" ];
# network-definition = config.fudo.networks."informis.land";
# dmarc-report-address = "dmarc-report@informis.land";
# };
# };
# };
# mail-server = {
# enable = true;
# debug = true;
# domain = domain-name;
# mail-hostname = "${host-fqdn}";
# monitoring = false;
# mail-user = "mailuser";
# mail-user-id = 525;
# mail-group = "mailgroup";
# clamav.enable = true;
# dkim.signing = true;
# dovecot = {
# ssl-certificate = acme-certificate "imap.${domain-name}";
# ssl-private-key = acme-private-key "imap.${domain-name}";
# };
# postfix = {
# ssl-certificate = acme-certificate "smtp.${domain-name}";
# ssl-private-key = acme-private-key "smtp.${domain-name}";
# };
# # This should NOT include the primary domain
# local-domains = [ host-fqdn "smtp.${domain-name}" ];
# mail-directory = "/srv/mailserver/mail";
# state-directory = "/srv/mailserver/state";
# trusted-networks = [ "172.86.179.16/29" "127.0.0.0/16" ];
# alias-users = {
# root = [ "niten" ];
# postmaster = [ "niten" ];
# hostmaster = [ "niten" ];
# webmaster = [ "niten" ];
# system = [ "niten" ];
# admin = [ "niten" ];
# dmarc-report = [ "niten" ];
# };
# };
# postgresql = {
# enable = true;
# ssl-certificate = (acme-certificate host-fqdn);
# ssl-private-key = (acme-private-key host-fqdn);
# keytab = secrets.postgres-keytab.target-file;
# local-networks = local-networks;
# users = {
# gituser = {
# password-file =
# secrets.gitea-database-password.target-file;
# databases = {
# git = {
# access = "CONNECT";
# entity-access = {
# "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
# "ALL SEQUENCES IN SCHEMA public" = "SELECT, UPDATE";
# };
# };
# };
# };
# };
# databases = { git = { users = [ "niten" ]; }; };
# };
# git = {
# enable = true;
# hostname = "git.informis.land";
# site-name = "informis git";
# user = "gituser";
# repository-dir = /srv/git/repo;
# state-dir = /srv/git/state;
# database = {
# user = "gituser";
# password-file =
# secrets.gitea-database-password.target-file;
# hostname = "127.0.0.1";
# name = "git";
# };
# ssh = {
# listen-ip = host-ipv4;
# listen-port = 2222;
# };
# };
# acme = {
# enable = true;
# admin-address = "admin@${domain-name}";
# hostnames = [
# "informis.land"
# "imap.informis.land"
# "smtp.informis.land"
# "gemini.informis.land"
# ];
# };
};
}

View File

@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
}

View File

@ -50,11 +50,12 @@ in {
]; ];
}; };
fudo.ipfs = { ## Until I can figure out how to use one common host API, forget this
enable = true; # fudo.ipfs = {
users = [ "niten" ]; # enable = true;
api-address = "/ip4/0.0.0.0/tcp/5001"; # users = [ "niten" ];
}; # api-address = "/ip4/0.0.0.0/tcp/5001";
# };
virtualisation = { virtualisation = {
libvirtd = { libvirtd = {

View File

@ -164,7 +164,7 @@ in {
debug = true; debug = true;
domain = domain-name; domain = domain-name;
hostname = "${host-fqdn}"; mail-hostname = "${host-fqdn}";
monitoring = false; monitoring = false;
mail-user = "mailuser"; mail-user = "mailuser";
mail-user-id = 525; mail-user-id = 525;

25
config/hosts/legatus.nix Normal file
View File

@ -0,0 +1,25 @@
{
description = "informis.land server.";
rp = "niten";
admin-email = "niten@fudo.org";
domain = "eur.fudo.org";
site = "worldstream";
profile = "server";
tmp-on-tmpfs = false;
enable-gui = false;
arch = "x86_64-linux";
nixos-system = true;
machine-id = "749bbf411088411b8784b76bb44bd617";
master-key = {
public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIqUnzf8bfPyoJX6XjFqD6v5MZQnV8STP0152VS3uwM7";
key-path = "/state/master-key/ed25519_key";
};
# initrd-network = {
# ip = "172.86.179.18";
# interface = "enp0s25";
# keypair = {
# public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIgvl/pxPGN5XuUFsEywHV/PJMI+wPHA6NKTtE8SZC04";
# private-key-file = "/state/ssh/initrd/ssh_ed25519_key";
# };
# };
}

View File

@ -0,0 +1,11 @@
{
description = "Fudo mailserver container";
rp = "admin";
admin-email = "admin@fudo.org";
domain = "fudo.org";
site = "portage";
profile = "container";
arch = "x86_64-linux";
machine-id = "5a907f8cf2644b0ba5a786fd45b758b3";
nixos-system = false;
}

View File

@ -1,8 +1,8 @@
{ {
description = "informis.land server."; description = "informis.land server.";
docker-server = true; docker-server = true;
rp = "niten"; rp = "viator";
admin-email = "niten@fudo.org"; admin-email = "viator@fudo.org";
domain = "informis.land"; domain = "informis.land";
site = "joes-datacenter-0"; site = "joes-datacenter-0";
profile = "server"; profile = "server";

View File

@ -2,8 +2,10 @@
{ {
config.fudo.networks = { config.fudo.networks = {
"fudo.org" = import ./networks/fudo.org.nix;
"rus.selby.ca" = import ./networks/rus.selby.ca.nix; "rus.selby.ca" = import ./networks/rus.selby.ca.nix;
"sea.fudo.org" = import ./networks/sea.fudo.org.nix; "sea.fudo.org" = import ./networks/sea.fudo.org.nix;
"informis.land" = import ./networks/informis.land.nix; "informis.land" = import ./networks/informis.land.nix;
"eur.fudo.org" = import ./networks/eur.fudo.org.nix;
}; };
} }

View File

@ -0,0 +1,7 @@
{
mx = [ "mail.fudo.org" ];
hosts = {
legatus.ipv4-address = "91.229.23.204";
};
}

View File

@ -1,8 +1,4 @@
{ {
mx = [ "mail.fudo.org" ];
default-host = "208.81.3.117";
aliases = { aliases = {
pop = "mail.fudo.org."; pop = "mail.fudo.org.";
smtp = "mail.fudo.org."; smtp = "mail.fudo.org.";
@ -27,13 +23,11 @@
wiki = "hanover.fudo.org."; wiki = "hanover.fudo.org.";
}; };
extra-dns-records = [ verbatim-dns-records = [
''@ IN TXT "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"'' ''@ IN TXT "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"''
''@ IN SPF "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"'' ''@ IN SPF "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"''
]; ];
dmarc-report-address = "dmarc-report@fudo.org";
srv-records = { srv-records = {
tcp = { tcp = {
domain = [ domain = [

View File

@ -0,0 +1,10 @@
{ config, lib, pkgs, ... }:
with lib;
let
has-secret-files = hasAttr "files" config.fudo.secrets;
in {
config.instance = mkIf has-secret-files {
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
};
}

View File

@ -14,7 +14,26 @@ let
wget wget
]; ];
import-paths = [
./build
./host
./user
];
in { in {
imports = let
is-regular-file = filename: type: type == "regular" || type == "link";
regular-files = path:
attrNames (filterAttrs is-regular-file (builtins.readDir path));
is-nix-file = filename: (builtins.match "^(.+)\.nix$" filename) != null;
nix-files = path:
map
(file: path + "/${file}")
(filter is-nix-file (regular-files path));
in concatMap nix-files import-paths;
config = {
environment = { environment = {
etc.nixos-live.source = ../../.; etc.nixos-live.source = ../../.;
@ -79,7 +98,6 @@ in {
in { in {
enable = config.networking.firewall.enable; enable = config.networking.firewall.enable;
bantime-increment.enable = true; bantime-increment.enable = true;
ignoreIP = config.instance.local-networks;
}; };
xserver = { xserver = {
@ -141,4 +159,9 @@ in {
}; };
}; };
}; };
home-manager = {
useGlobalPkgs = true;
};
};
} }

View File

@ -0,0 +1,7 @@
{ config, lib, pkgs, ... }:
{
config = {
};
}

View File

@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
let
hostname = config.instance.hostname;
host-cfg = config.fudo.hosts.${hostname};
secrets = config.fudo.secrets.host-secrets.${hostname};
in {
config.fudo = {
secrets.host-secrets.${hostname} = {
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 =
secrets.backplane-passwd.target-file;
};
}

View File

@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
has-secret-files = hasAttr "files" config.fudo.secrets;
try-attr = attr: set: if (hasAttr attr set) then set.${attr} else null;
in {
config = mkIf has-secret-files {
fudo.secrets.host-secrets.${hostname} = let
keytab-file = try-attr hostname config.fudo.secrets.files.host-keytabs;
in mkIf (keytab-file != null) {
host-keytab = {
source-file = keytab-file;
target-file = "/etc/krb5.keytab";
user = "root";
};
};
};
}

View File

@ -0,0 +1,60 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
has-attrs = set: length (attrNames set) > 0;
read-lines = filename: splitString "\n" (fileContents filename);
has-secret-files = hasAttr "files" config.fudo.secrets;
in {
config = mkIf has-secret-files
(let
host-keypairs =
if (hasAttr hostname config.fudo.secrets.files.host-ssh-keypairs) then
config.fudo.secrets.files.host-ssh-keypairs.${hostname}
else [];
in {
fudo = let
sshfp-filename = host: keypair: "ssh-${host}-${keypair.key-type}.sshfp-record";
dns-sshfp-records = host: keypair:
pkgs.stdenv.mkDerivation {
name = "${host}-sshfp-records";
phases = [ "installPhase" ];
buildInputs = with pkgs; [ openssh ];
installPhase =
"ssh-keygen -r REMOVEME -f \"${keypair.public-key}\" | sed 's/^REMOVEME IN SSHFP //' > $out";
};
host-cfg = config.fudo.hosts.${hostname};
in {
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 = mkIf (hasAttr "files" config.fudo.secrets)
(mapAttrs (hostname: keypairs: {
ssh-pubkeys = map (keypair: keypair.public-key) keypairs;
ssh-fingerprints = concatMap (keypair:
let
fingerprint-derivation = dns-sshfp-records hostname keypair;
in read-lines "${fingerprint-derivation}") 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;
});
}

View File

@ -45,7 +45,7 @@ in {
config = { config = {
environment = { environment = {
serverPackages = with pkgs; systemPackages = with pkgs;
[ emacs-nox reboot-if-necessary test-config ]; [ emacs-nox reboot-if-necessary test-config ];
}; };

View File

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
with lib;
let
ipfs-cfg = config.fudo.ipfs;
site-name = config.instance.site;
site = config.site.${site-name};
in {
config = {
home-manager.users = mapAttrs
(user: userOpts: {
home.sessionVariables.IPFS_PATH = ipfs-cfg.data-dir;
}) config.instance.local-users;
};
}

View File

@ -0,0 +1,52 @@
{ 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);
admin-realms = username:
mapAttrsToList domain-realm
(filterAttrs
(domain: domainOpts:
list-contains domainOpts.local-admins username)
config.fudo.domains);
user-principals = username: let
user-princs = map (realm: "${username}@${realm}") (user-realms username);
admin-princs = map (realm: "${username}/root@${realm}") (admin-realms username);
in user-princs ++ admin-princs;
user-k5login = principals: concatStringsSep "\n" principals;
user-config = username: userOpts: {
home.file.".k5login".text =
user-k5login (user-principals username);
};
in {
config = {
home-manager.users = let
user-configs =
mapAttrs user-config config.instance.local-users;
root-config = {
root = let
domain-name = config.instance.local-domain;
realm = config.fudo.domains.${domain-name}.gssapi-realm;
principals = map (admin: "${admin}/root@${realm}")
config.instance.local-admins;
in {
home.file.".k5login".text =
user-k5login principals;
};
};
in user-configs // root-config;
};
}

View File

@ -3,16 +3,16 @@
let local-domain = "sea.fudo.org"; let local-domain = "sea.fudo.org";
in { in {
fileSystems = { fileSystems = {
"/mnt/documents" = { # "/mnt/documents" = {
device = "whitedwarf.${local-domain}:/volume1/Documents"; # device = "whitedwarf.${local-domain}:/volume1/Documents";
fsType = "nfs4"; # fsType = "nfs4";
options = [ "comment=systemd.automount" ]; # options = [ "comment=systemd.automount" ];
}; # };
"/mnt/downloads" = { # "/mnt/downloads" = {
device = "whitedwarf.${local-domain}:/volume1/Downloads"; # device = "whitedwarf.${local-domain}:/volume1/Downloads";
fsType = "nfs4"; # fsType = "nfs4";
options = [ "comment=systemd.automount" ]; # options = [ "comment=systemd.automount" ];
}; # };
"/mnt/music" = { "/mnt/music" = {
device = "doraemon.${local-domain}:/volume1/Music"; device = "doraemon.${local-domain}:/volume1/Music";
fsType = "nfs4"; fsType = "nfs4";

View File

@ -59,5 +59,17 @@
]; ];
mail-server = "mail.informis.land"; mail-server = "mail.informis.land";
}; };
worldstream = {
gateway-v4 = "91.229.23.204";
network = "91.229.23.0/24";
nameservers = [ "1.1.1.1" "2606:4700:4700::1111" ];
timezone = "Europe/Amsterdam";
deploy-pubkeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDPwh522lvafTJYA0X2uFdP7Ws+Um1f8gZsARK1Y5nMzf6ZcWBF1jplTOKUVSOl4isMWni0Tu0TnX4zqCcgocWUVbwIwXSIRYqdiCPvVOH+/Ibc97n1/dYxk5JPMtbrsEw6/gWZxVg0qwe0J3dQWldEMiDY7iWhlrmIr7YL+Y3PUd7DOwp3PbfWfNyzTfE1kXcz5YvTeN+txFhbbXT0oS2R2wtc1vYXFZ/KbNstjqd+i8jszAq3ZkbbwL3aNR0RO4n8+GoIILGw8Ya4eP7D6+mYk608IhAoxpGyMrUch2TC2uvOK3rd/rw1hsTxf4AKjAZbrfd/FJaYru9ZeoLjD4bRGMdVp56F1m7pLvRiWRK62pV2Q/fjx+4KjHUrgyPd601eUIP0ayS/Rfuq8ijLpBJgO5/Y/6mFus/kjZIfRR9dXfLM67IMpyEzEITYrc/R2sedWf+YHxSh6eguAZ/kLzioar1nHLR7Wzgeu0tgWkD78WQGjpXGoefAz3xHeBg3Et0="
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGVez4of30f+j0cWKj5kYCKeFjyNsYvG9UbOMxF5hImD2lP5MSbFBv31gFgHjx3yCG4zQRZlpuyU5uWo0qIwe9N84/LcZcB9WrWKZXDmuof7zPFy0J+Hj+LVLDQI/mVXHNwkMhBMHpPrdwA05EYDAYCYklWT4cSByu10pHtST+olF8i+A+UQgUzgNZzdJVeiYZv6MBDTYsJWptGeDUkl2B0Es3gtbGYcCCfnyS3RC7DIXlDo3NBbAr7WaHY2MBbT+R/+jicn9E3IY3NCM5jENxqmvHy9MDsxEEYgFNm7IDwq4V1VRUWy277YsvRbmEaHb+osOA5u1VNN4z3UftOZcSZgR5C/vR71cENXoPt1YQpCzu7i38ojtvL+tDVEKT7sIovrQw8q1sszNlW2nXh8RSPiIq5TMnrV73MP0egKcr9n3tfxwi1BIkLjvfom/02BkTK9R9v+VMNhYU1YwROhORCiMIgoxUGiUvtH8u38JGr7E0hhMoAjCE5k80WPUivl0="
];
mail-server = "mail.fudo.org";
};
}; };
} }

View File

@ -27,6 +27,20 @@ in {
"niten/root@RUS.SELBY.CA" "niten/root@RUS.SELBY.CA"
]; ];
email = "niten@fudo.org"; email = "niten@fudo.org";
email-aliases = [
"ertian@fudo.org"
"peter@fudo.org"
"peter@fudo.link"
"pselby@fudo.org"
"yiliu@fudo.org"
"forum@selby.ca"
"peter@selby.ca"
# Used to create spotify accounts for Google Home & Tesla
"tesla@fudo.org"
"seattle-home@fudo.org"
];
}; };
andrew = { andrew = {
@ -99,6 +113,7 @@ in {
ldap-hashed-passwd = "{SSHA}YvtkEpqsReXcMdrzlui/ZmhIUKN42YO1"; ldap-hashed-passwd = "{SSHA}YvtkEpqsReXcMdrzlui/ZmhIUKN42YO1";
login-hashed-passwd = login-hashed-passwd =
"$6$EwK9fpbH8$gYVzYY1IYw2/G0wCeUxXrZZqvjWCkCZbBqCOhxowbMuYtC5G0vp.AoYhVKWOJcHJM2c7TdPmAdnhLIe2KYStf."; "$6$EwK9fpbH8$gYVzYY1IYw2/G0wCeUxXrZZqvjWCkCZbBqCOhxowbMuYtC5G0vp.AoYhVKWOJcHJM2c7TdPmAdnhLIe2KYStf.";
email-aliases = [ "kselby@selby.ca" ];
}; };
reaper = { reaper = {
@ -112,6 +127,12 @@ in {
k5login = k5login =
[ "reaper@FUDO.ORG" "reaper/root@FUDO.ORG" "reaper/admin@FUDO.ORG" ]; [ "reaper@FUDO.ORG" "reaper/root@FUDO.ORG" "reaper/admin@FUDO.ORG" ];
email = "reaper@fudo.org"; email = "reaper@fudo.org";
email-aliases = [
"cricket@fudo.org"
"jstewart@fudo.org"
"jonathan@fudo.org"
"reaper@fudo.link"
];
}; };
slickoil = { slickoil = {
@ -133,6 +154,7 @@ in {
primary-group = "fudo"; primary-group = "fudo";
common-name = "Mark Swaffer"; common-name = "Mark Swaffer";
ldap-hashed-passwd = "{MD5}C5gIsLsaKSvIPydu4uzhNg=="; ldap-hashed-passwd = "{MD5}C5gIsLsaKSvIPydu4uzhNg==";
email-aliases = [ "mark@fudo.org" ];
}; };
brian = { brian = {
@ -192,6 +214,13 @@ in {
login-hashed-passwd = login-hashed-passwd =
"$6$C8lYHrK7KvdKm/RE$cHZ2hg5gEOEjTV8Zoayik8sz5h.Vh0.ClCgOlQn8l/2Qx/qdxqZ7xCsAZ1GZ.IEyESfhJeJbjLpykXDwPpfVF0"; "$6$C8lYHrK7KvdKm/RE$cHZ2hg5gEOEjTV8Zoayik8sz5h.Vh0.ClCgOlQn8l/2Qx/qdxqZ7xCsAZ1GZ.IEyESfhJeJbjLpykXDwPpfVF0";
email = "xiaoxuan@fudo.org"; email = "xiaoxuan@fudo.org";
email-aliases = [
"xixi@fudo.org"
"claire@fudo.org"
"xixi@selby.ca"
"claire@selby.ca"
];
}; };
thibor = { thibor = {
@ -311,6 +340,9 @@ in {
primary-group = "selby"; primary-group = "selby";
common-name = "Vee Selby"; common-name = "Vee Selby";
ldap-hashed-passwd = "snoinuer"; ldap-hashed-passwd = "snoinuer";
email-aliases = [
"virginia@selby.ca"
];
}; };
dabar = { dabar = {

View File

@ -2,34 +2,6 @@
with lib; with lib;
let 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 domainOpts = { name, ... }: let
domain = name; domain = name;
in { in {
@ -140,14 +112,14 @@ in {
perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500"; perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500";
copy-paths = mapAttrsToList (copy: copyOpts: copy-paths = mapAttrsToList (copy: copyOpts:
let 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) [ in map (dir-entry copyOpts) [
copyOpts.certificate copyOpts.certificate
copyOpts.full-certificate copyOpts.full-certificate
copyOpts.chain copyOpts.chain
copyOpts.private-key copyOpts.private-key
]) copies; ]) copies;
in unique copy-paths; in unique (concatMap (i: unique i) copy-paths);
services = concatMapAttrs (domain: domainOpts: services = concatMapAttrs (domain: domainOpts:
mapAttrs' (copy: copyOpts: let mapAttrs' (copy: copyOpts: let
@ -169,7 +141,7 @@ in {
''; '';
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" '' remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" ''
rm -f ${copyOpts.private-key} rm -f ${copyOpts.private-key}
rm -f ${copyOpts.chainy} rm -f ${copyOpts.chain}
rm -f ${copyOpts.full-certificate} rm -f ${copyOpts.full-certificate}
rm -f ${copyOpts.certificate} rm -f ${copyOpts.certificate}
''; '';

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.fudo.backplane.dns; cfg = config.fudo.backplane.dns;
powerdns-conf-dir = "${cfg.powerdns-home}/conf.d"; powerdns-conf-dir = "${cfg.powerdns.home}/conf.d";
backplaneOpts = { ... }: { backplaneOpts = { ... }: {
options = { options = {
@ -90,6 +90,7 @@ in {
type = listOf str; type = listOf str;
description = description =
"A list of services required before the DNS server can start."; "A list of services required before the DNS server can start.";
default = [ ];
}; };
user = mkOption { user = mkOption {
@ -109,15 +110,23 @@ in {
description = "Database settings for the DNS server."; description = "Database settings for the DNS server.";
}; };
powerdns-home = mkOption { backplane = mkOption {
type = submodule backplaneOpts;
description = "Backplane Jabber settings for the DNS server.";
};
powerdns = {
home = mkOption {
type = str; type = str;
description = "Directory at which to store powerdns configuration and state."; description = "Directory at which to store powerdns configuration and state.";
default = "/run/backplane-dns/powerdns"; default = "/run/backplane-dns/powerdns";
}; };
backplane = mkOption { user = mkOption {
type = submodule backplaneOpts; type = str;
description = "Backplane Jabber settings for the DNS server."; description = "Username as which to run PowerDNS.";
default = "backplane-powerdns";
};
}; };
}; };
@ -130,16 +139,16 @@ in {
createHome = true; createHome = true;
home = "/var/home/${cfg.user}"; home = "/var/home/${cfg.user}";
}; };
backplane-powerdns = { ${cfg.powerdns.user} = {
isSystemUser = true; isSystemUser = true;
home = cfg.powerdns-home; home = cfg.powerdns.home;
createHome = true; createHome = true;
}; };
}; };
groups = { groups = {
"${cfg.group}" = { members = [ cfg.user ]; }; "${cfg.group}" = { members = [ cfg.user ]; };
backplane-powerdns = { members = [ "backplane-powerdns" ]; }; ${cfg.powerdns.user} = { members = [ cfg.powerdns.user ]; };
}; };
}; };
@ -156,7 +165,7 @@ in {
preStart = '' preStart = ''
mkdir -p ${powerdns-conf-dir} 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 # This builds the config in a bash script, to avoid storing the password
@ -175,7 +184,7 @@ in {
fi fi
touch $TMPCONF touch $TMPCONF
chown backplane-powerdns:backplane-powerdns $TMPCONF chown ${cfg.powerdns.user}:${cfg.powerdns.user} $TMPCONF
chmod go-rwx $TMPCONF chmod go-rwx $TMPCONF
PASSWORD=$(cat ${cfg.database.password-file}) PASSWORD=$(cat ${cfg.database.password-file})
echo "launch+=gpgsql" >> $TMPCONF 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 = { backplane-dns = {
description = "Fudo DNS Backplane Server"; description = "Fudo DNS Backplane Server";
restartIfChanged = true; restartIfChanged = true;
@ -220,7 +210,7 @@ in {
user = cfg.user; user = cfg.user;
group = cfg.group; group = cfg.group;
partOf = [ "backplane-dns.target" ]; partOf = [ "backplane-dns.target" ];
requires = [ "postgresql.service" ]; requires = cfg.required-services ++ [ "postgresql.service" ];
environment = { environment = {
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
@ -243,7 +233,30 @@ in {
backplane-dns = { backplane-dns = {
description = "Fudo DNS backplane services."; description = "Fudo DNS backplane services.";
wantedBy = [ "multi-user.target" ]; 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}";
};
}; };
}; };
}; };

View File

@ -1,67 +1,71 @@
{ pkgs, lib, config, ... }: { pkgs, lib, config, ... }:
with lib; with lib;
let cfg = config.fudo.chat; let
cfg = config.fudo.chat;
mattermost-config-target = "/run/chat/mattermost/mattermost-config.json";
in { in {
options.fudo.chat = { options.fudo.chat = with types; {
enable = mkEnableOption "Enable chat server"; enable = mkEnableOption "Enable chat server";
hostname = mkOption { hostname = mkOption {
type = types.str; type = str;
description = "Hostname at which this chat server is accessible."; description = "Hostname at which this chat server is accessible.";
example = "chat.mydomain.com"; example = "chat.mydomain.com";
}; };
site-name = mkOption { site-name = mkOption {
type = types.str; type = str;
description = "The name of this chat server."; description = "The name of this chat server.";
example = "My Fancy Chat Site"; example = "My Fancy Chat Site";
}; };
smtp-server = mkOption { smtp = {
type = types.str; server = mkOption {
type = str;
description = "SMTP server to use for sending notification emails."; description = "SMTP server to use for sending notification emails.";
example = "mail.my-site.com"; example = "mail.my-site.com";
}; };
smtp-user = mkOption { user = mkOption {
type = types.str; type = str;
description = "Username with which to connect to the SMTP server."; description = "Username with which to connect to the SMTP server.";
}; };
smtp-password-file = mkOption { password-file = mkOption {
type = types.str; type = str;
description = description =
"Path to a file containing the password to use while connecting to the SMTP server."; "Path to a file containing the password to use while connecting to the SMTP server.";
}; };
};
state-directory = mkOption { state-directory = mkOption {
type = types.str; type = str;
description = "Path at which to store server state data."; description = "Path at which to store server state data.";
default = "/var/lib/mattermost"; default = "/var/lib/mattermost";
}; };
database = mkOption { database = mkOption {
type = (types.submodule { type = (submodule {
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = str;
description = "Database name."; description = "Database name.";
}; };
hostname = mkOption { hostname = mkOption {
type = types.str; type = str;
description = "Database host."; description = "Database host.";
}; };
user = mkOption { user = mkOption {
type = types.str; type = str;
description = "Database user."; description = "Database user.";
}; };
password-file = mkOption { password-file = mkOption {
type = types.str; type = str;
description = "Path to file containing database password."; description = "Path to file containing database password.";
}; };
}; };
@ -85,11 +89,11 @@ in {
TeamSettings.SiteName = cfg.site-name; TeamSettings.SiteName = cfg.site-name;
EmailSettings = { EmailSettings = {
RequireEmailVerification = true; RequireEmailVerification = true;
SMTPServer = cfg.smtp-server; SMTPServer = cfg.smtp.server;
SMTPPort = 587; SMTPPort = 587;
EnableSMTPAuth = true; EnableSMTPAuth = true;
SMTPUsername = cfg.smtp-user; SMTPUsername = cfg.smtp.user;
SMTPPassword = (fileContents cfg.smtp-password-file); SMTPPassword = "__SMTP_PASSWD__";
SendEmailNotifications = true; SendEmailNotifications = true;
ConnectionSecurity = "STARTTLS"; ConnectionSecurity = "STARTTLS";
FeedbackEmail = "chat@fudo.org"; FeedbackEmail = "chat@fudo.org";
@ -97,15 +101,26 @@ in {
}; };
EnableEmailInvitations = true; EnableEmailInvitations = true;
SqlSettings.DriverName = "postgres"; SqlSettings.DriverName = "postgres";
SqlSettings.DataSource = "postgres://${cfg.database.user}:${ SqlSettings.DataSource = "postgres://${
fileContents cfg.database.password-file cfg.database.user
}@${cfg.database.hostname}:5432/${cfg.database.name}"; }:__DATABASE_PASSWORD__@${
cfg.database.hostname
}:5432/${
cfg.database.name
}";
}; };
mattermost-config-file = mattermost-config-file-template =
pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config); pkgs.writeText "mattermost-config.json.template" (builtins.toJSON modified-config);
mattermost-user = "mattermost"; mattermost-user = "mattermost";
mattermost-group = "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 { in {
users = { users = {
users = { users = {
@ -118,41 +133,68 @@ in {
groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; }; groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; };
}; };
system.activationScripts.mattermost = '' fudo.system.services.mattermost = {
mkdir -p ${cfg.state-directory}
'';
systemd.services.mattermost = {
description = "Mattermost Chat Server"; description = "Mattermost Chat Server";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network.target" ]; after = [ "network.target" ];
preStart = '' preStart = ''
mkdir -p ${cfg.state-directory}/config ${generate-mattermost-config
cp ${mattermost-config-file} ${cfg.state-directory}/config/config.json mattermost-config-target
ln -sf ${pkg}/bin ${cfg.state-directory} mattermost-config-file-template
ln -sf ${pkg}/fonts ${cfg.state-directory} cfg.smtp.password-file
ln -sf ${pkg}/i18n ${cfg.state-directory} cfg.database.password-file}
ln -sf ${pkg}/templates ${cfg.state-directory} cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json
cp -uRL ${pkg}/client ${cfg.state-directory} cp -uRL ${pkg}/client ${cfg.state-directory}
chown -R ${mattermost-user}:${mattermost-group} ${cfg.state-directory} chown ${mattermost-user}:${mattermost-group} ${cfg.state-directory}/client
chmod u+w -R ${cfg.state-directory}/client chmod 0750 ${cfg.state-directory}/client
chmod o-rwx -R ${cfg.state-directory}
''; '';
execStart = "${pkg}/bin/mattermost";
serviceConfig = { workingDirectory = cfg.state-directory;
PermissionsStartOnly = true; user = mattermost-user;
ExecStart = "${pkg}/bin/mattermost"; group = mattermost-group;
WorkingDirectory = cfg.state-directory;
Restart = "always";
RestartSec = "10";
LimitNOFILE = "49152";
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 = { services.nginx = {
enable = true; enable = true;

View File

@ -8,7 +8,14 @@ let
hostname = config.instance.hostname; 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 { in {
options.fudo.hosts = with types; options.fudo.hosts = with types;
@ -37,9 +44,12 @@ in {
#defaultGateway = site.gateway-v4; #defaultGateway = site.gateway-v4;
#defaultGateway6 = site.gateway-v6; #defaultGateway6 = site.gateway-v6;
firewall = { firewall = mkIf ((length host-cfg.external-interfaces) > 0) {
enable = (length host-cfg.external-interfaces) > 0; enable = true;
allowedTCPPorts = [ 22 ]; 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) hostId = mkIf (host-cfg.machine-id != null)
@ -79,6 +89,8 @@ in {
in concatStringsSep "\n" sorted-unique; in concatStringsSep "\n" sorted-unique;
build-timestamp.text = toString config.instance.build-timestamp; build-timestamp.text = toString config.instance.build-timestamp;
build-seed-hash.source =
generate-string-hash "build-seed" config.instance.build-seed;
}; };
systemPackages = with pkgs; systemPackages = with pkgs;
@ -89,7 +101,10 @@ in {
krb5.libdefaults.default_realm = domain.gssapi-realm; 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) { virtualisation.docker = mkIf (host-cfg.docker-server) {
enable = true; enable = true;
@ -97,56 +112,11 @@ in {
autoPrune.enable = true; 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; programs.adb.enable = host-cfg.android-dev;
users.groups.adbusers = mkIf host-cfg.android-dev { users.groups.adbusers = mkIf host-cfg.android-dev {
members = config.instance.local-admins; members = config.instance.local-admins;
}; };
boot.tmpOnTmpfs = host-cfg.tmp-on-tmpfs; 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;
};
}; };
} }

View File

@ -7,9 +7,6 @@ let
user-group-entry = group: user: user-group-entry = group: user:
nameValuePair user { extraGroups = [ group ]; }; nameValuePair user { extraGroups = [ group ]; };
user-home-entry = ipfs-path: user:
nameValuePair user { home.sessionVariables = { IPFS_PATH = ipfs-path; }; };
in { in {
options.fudo.ipfs = with types; { options.fudo.ipfs = with types; {
enable = mkEnableOption "Fudo IPFS"; enable = mkEnableOption "Fudo IPFS";
@ -53,7 +50,8 @@ in {
config = mkIf cfg.enable { 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 = { services.ipfs = {
enable = true; enable = true;
@ -64,8 +62,5 @@ in {
group = cfg.group; group = cfg.group;
dataDir = cfg.data-dir; dataDir = cfg.data-dir;
}; };
home-manager.users =
listToAttrs (map (user-home-entry cfg.data-dir) cfg.users);
}; };
} }

View File

@ -5,100 +5,7 @@ let
cfg = config.fudo.auth.ldap-server; cfg = config.fudo.auth.ldap-server;
ldapSystemUserOpts = { name, ... }: { user-type = import ../types/user.nix { inherit lib; };
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 = "";
};
};
};
stringJoin = joiner: attrList: stringJoin = joiner: attrList:
if (length attrList) == 0 then if (length attrList) == 0 then
@ -107,15 +14,15 @@ let
foldr (lAttr: rAttr: "${lAttr}${joiner}${rAttr}") (last attrList) foldr (lAttr: rAttr: "${lAttr}${joiner}${rAttr}") (last attrList)
(init 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; attrOr = attrs: attr: value: if attrs ? ${attr} then attrs.${attr} else value;
mkHomeDir = username: user-opts: mkHomeDir = username: user-opts:
if (user-opts.group == "admin") then if (user-opts.primary-group == "admin") then
"/home/${username}" "/home/${username}"
else else
"/home/${user-opts.group}/${username}"; "/home/${user-opts.primary-group}/${username}";
userLdif = base: name: group-map: opts: '' userLdif = base: name: group-map: opts: ''
dn: uid=${name},ou=members,${base} dn: uid=${name},ou=members,${base}
@ -131,7 +38,7 @@ let
shadowLastChange: 12230 shadowLastChange: 12230
shadowMax: 99999 shadowMax: 99999
shadowWarning: 7 shadowWarning: 7
userPassword: ${opts.hashed-password} userPassword: ${opts.ldap-hashed-passwd}
''; '';
systemUserLdif = base: name: opts: '' systemUserLdif = base: name: opts: ''
@ -140,7 +47,7 @@ let
objectClass: simpleSecurityObject objectClass: simpleSecurityObject
cn: ${name} cn: ${name}
description: ${opts.description} description: ${opts.description}
userPassword: ${opts.hashed-password} userPassword: ${opts.ldap-hashed-password}
''; '';
toMemberList = userList: toMemberList = userList:
@ -242,7 +149,7 @@ in {
}; };
users = mkOption { users = mkOption {
type = attrsOf (submodule ldapUserOpts); type = attrsOf (submodule user-type.userOpts);
example = { example = {
tester = { tester = {
uid = 10099; uid = 10099;
@ -258,7 +165,7 @@ in {
groups = mkOption { groups = mkOption {
default = { }; default = { };
type = attrsOf (submodule ldapGroupOpts); type = attrsOf (submodule user-type.groupOpts);
example = { example = {
admin = { admin = {
gid = 1099; gid = 1099;
@ -272,16 +179,19 @@ in {
system-users = mkOption { system-users = mkOption {
default = { }; default = { };
type = attrsOf (submodule ldapSystemUserOpts); type = attrsOf (submodule user-type.systemUserOpts);
example = { example = {
replicator = { replicator = {
description = "System user for database sync"; description = "System user for database sync";
hashed-password = "<insert password hash>"; ldap-hashed-password = "<insert password hash>";
}; };
}; };
description = '' description = "System users to be added to the Fudo LDAP database.";
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 = { services.openldap = {
enable = true; enable = true;
suffix = cfg.base;
rootdn = "cn=admin,${cfg.base}";
rootpwFile = "${cfg.rootpw-file}";
urlList = cfg.listen-uris; urlList = cfg.listen-uris;
database = "mdb";
settings = let settings = let
makeAccessLine = i: attrs: perm-map: let makePermEntry = dn: perm: "by ${dn} ${perm}";
perm-strings = mapAttrs (dn: perm: "by ${dn} ${perm}") perm-map;
perm-string = concatStringsSep " " perm-strings; makeAccessLine = target: perm-map: let
in "${i}to ${attrs} ${perm-string}"; perm-entries = mapAttrsToList makePermEntry perm-map;
in "to ${target} ${concatStringsSep " " perm-entries} done";
makeAccess = access-map: let makeAccess = access-map: let
pairs = mapAttrsToList (target: perm-map: [target perm-map]) access-map; access-lines = mapAttrsToList makeAccessLine;
in imap0 (i: pair: makeAccessLine i pair[0] pair[1]) pairs; numbered-access-lines = imap0 (i: line: "{${toString i}}${line}");
in numbered-access-lines (access-lines access-map);
in { in {
attrs = { attrs = {
@ -363,8 +271,8 @@ in {
olcTLSCACertificateFile = cfg.ssl-ca-certificate; olcTLSCACertificateFile = cfg.ssl-ca-certificate;
olcSaslSecProps = "noplain,noanonymous"; olcSaslSecProps = "noplain,noanonymous";
olcAuthzRegexp = let olcAuthzRegexp = let
authz-regex-entry = i: { regex, target}: authz-regex-entry = i: { regex, target }:
"{${i}}\"${rx}\" \"${target}\""; "{${toString i}}\"${regex}\" \"${target}\"";
in imap0 authz-regex-entry [ in imap0 authz-regex-entry [
{ {
regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$"; regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$";
@ -396,7 +304,7 @@ in {
olcAccess = makeAccess { olcAccess = makeAccess {
"*" = { "*" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; "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"; "*" = "none";
}; };
}; };
@ -462,7 +370,8 @@ in {
}; };
}; };
declarativeContents = '' declarativeContents = {
"dc=fudo,dc=org" = ''
dn: ${cfg.base} dn: ${cfg.base}
objectClass: top objectClass: top
objectClass: dcObject objectClass: dcObject
@ -488,4 +397,5 @@ in {
''; '';
}; };
}; };
};
} }

View File

@ -1,4 +1,4 @@
{ lib, config, ... }: { pkgs, lib, config, ... }:
with lib; with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
@ -11,22 +11,18 @@ let
container-mail-user-id = 542; container-mail-user-id = 542;
container-mail-group = "mailer"; 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 { in rec {
config = mkIf (cfg.enableContainer) { config = mkIf (cfg.enableContainer) {
# Disable postfix on this host--it'll be run in the container instead # Disable postfix on this host--it'll be run in the container instead
services.postfix.enable = false; 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 { services.nginx = mkIf cfg.monitoring {
enable = true; enable = true;
@ -36,10 +32,10 @@ in rec {
proxy_set_header Host $host; proxy_set_header Host $host;
''; '';
trusted-network-string = trusted-network-string =
optionalString ((length config.instance.local-networks) > 0) optionalString ((length local-networks) > 0)
(concatStringsSep "\n" (concatStringsSep "\n"
(map (network: "allow ${network};") (map (network: "allow ${network};")
config.instance.local-networks)) + '' local-networks)) + ''
deny all;''; deny all;'';
@ -85,9 +81,7 @@ in rec {
autoStart = true; autoStart = true;
bindMounts = let bindMounts = {
cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies;
in {
"${container-maildir}" = { "${container-maildir}" = {
hostPath = cfg.mail-directory; hostPath = cfg.mail-directory;
isReadOnly = false; isReadOnly = false;
@ -98,39 +92,40 @@ in rec {
isReadOnly = false; isReadOnly = false;
}; };
"/etc/${container-shared}" = {
hostPath = "/etc/${container-shared}";
isReadOnly = true;
};
"/run/mail/certs/postfix/cert.pem" = { "/run/mail/certs/postfix/cert.pem" = {
hostPath = cert-copies.postfix.certificate; hostPath = cfg.ssl.certificate;
isReadOnly = true; isReadOnly = true;
}; };
"/run/mail/certs/postfix/key.pem" = { "/run/mail/certs/postfix/key.pem" = {
hostPath = cert-copies.postfix.private-key; hostPath = cfg.ssl.private-key;
isReadOnly = true; isReadOnly = true;
}; };
"/run/mail/certs/dovecot/cert.pem" = { "/run/mail/certs/dovecot/cert.pem" = {
hostPath = cert-copies.dovecot.certificate; hostPath = cfg.ssl.certificate;
isReadOnly = true; isReadOnly = true;
}; };
"/run/mail/certs/dovecot/key.pem" = { "/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; isReadOnly = true;
}; };
}; };
config = { config, pkgs, ... }: {
imports = let imports = let
initialize-host = import ../../initialize-host.nix; initialize-host = import ../../initialize.nix;
build-timestamp = config.instance.build-timestamp;
site = config.instance.site;
domain = config.instance.domain;
profile = "container"; profile = "container";
in [ in [
./mail.nix
(initialize-host { (initialize-host {
inherit inherit
lib lib
@ -143,7 +138,7 @@ in rec {
}) })
]; ];
config = { config, pkgs, ... }: { instance.build-seed = build-seed;
environment.etc = { environment.etc = {
"mail-server/postfix/cert.pem" = { "mail-server/postfix/cert.pem" = {
@ -158,21 +153,28 @@ in rec {
}; };
"mail-server/dovecot/cert.pem" = { "mail-server/dovecot/cert.pem" = {
source = "/run/mail/certs/dovecot/cert.pem"; source = "/run/mail/certs/dovecot/cert.pem";
user = config.services.dovecot.user; user = config.services.dovecot2.user;
mode = "0444"; mode = "0444";
}; };
"mail-server/dovecot/key.pem" = { "mail-server/dovecot/key.pem" = {
source = "/run/mail/certs/dovecot/key.pem"; source = "/run/mail/certs/dovecot/key.pem";
user = config.services.dovecot.user; user = config.services.dovecot2.user;
mode = "0400"; 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 = { mail-server = {
enable = true; enable = true;
hostname = cfg.hostname; mail-hostname = cfg.mail-hostname;
domain = cfg.domain; domain = cfg.domain;
debug = cfg.debug; debug = cfg.debug;
@ -192,7 +194,7 @@ in rec {
ldap = { ldap = {
server-urls = cfg.dovecot.ldap.server-urls; server-urls = cfg.dovecot.ldap.server-urls;
reader-dn = cfg.dovecot.ldap.reader-dn; reader-dn = cfg.dovecot.ldap.reader-dn;
reader-passwd = cfg.dovecot.ldap.reader-passwd; reader-password-file = "/run/mail/passwords/dovecot/ldap-reader.passwd";
}; };
}; };
@ -215,4 +217,5 @@ in rec {
}; };
}; };
}; };
};
} }

View File

@ -7,7 +7,7 @@ let
in { in {
options.fudo.mail-server = { options.fudo.mail-server = with types; {
enable = mkEnableOption "Fudo Email Server"; enable = mkEnableOption "Fudo Email Server";
enableContainer = mkEnableOption '' enableContainer = mkEnableOption ''
@ -17,18 +17,17 @@ in {
''; '';
domain = mkOption { domain = mkOption {
type = types.str; type = str;
description = "The main and default domain name for this email server."; description = "The main and default domain name for this email server.";
}; };
mail-hostname = mkOption { mail-hostname = mkOption {
type = types.str; type = str;
description = "The domain name to use for the mail server."; description = "The domain name to use for the mail server.";
}; };
ldap-url = mkOption { ldap-url = mkOption {
type = types.str; type = str;
description = "URL of the LDAP server to use for authentication."; description = "URL of the LDAP server to use for authentication.";
example = "ldaps://auth.fudo.org/"; example = "ldaps://auth.fudo.org/";
}; };
@ -36,23 +35,25 @@ in {
monitoring = mkEnableOption "Enable monitoring for the mail server."; monitoring = mkEnableOption "Enable monitoring for the mail server.";
mail-user = mkOption { mail-user = mkOption {
type = types.str; type = str;
description = "User to use for mail delivery."; description = "User to use for mail delivery.";
default = "mailuser";
}; };
# No group id, because NixOS doesn't seem to use it # No group id, because NixOS doesn't seem to use it
mail-group = mkOption { mail-group = mkOption {
type = types.str; type = str;
description = "Group to use for mail delivery."; description = "Group to use for mail delivery.";
default = "mailgroup";
}; };
mail-user-id = mkOption { mail-user-id = mkOption {
type = types.int; type = int;
description = "UID of mail-user."; description = "UID of mail-user.";
}; };
local-domains = mkOption { local-domains = mkOption {
type = with types; listOf str; type = listOf str;
description = "A list of domains for which we accept mail."; description = "A list of domains for which we accept mail.";
default = ["localhost" "localhost.localdomain"]; default = ["localhost" "localhost.localdomain"];
example = [ example = [
@ -64,17 +65,17 @@ in {
}; };
mail-directory = mkOption { mail-directory = mkOption {
type = types.str; type = str;
description = "Path to use for mail storage."; description = "Path to use for mail storage.";
}; };
state-directory = mkOption { state-directory = mkOption {
type = types.str; type = str;
description = "Path to use for state data."; description = "Path to use for state data.";
}; };
trusted-networks = mkOption { 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."; description = "A list of trusted networks, for which we will happily relay without auth.";
example = [ example = [
"10.0.0.0/16" "10.0.0.0/16"
@ -83,7 +84,7 @@ in {
}; };
sender-blacklist = mkOption { sender-blacklist = mkOption {
type = with types; listOf str; type = listOf str;
description = "A list of email addresses for whom we will not send email."; description = "A list of email addresses for whom we will not send email.";
default = []; default = [];
example = [ example = [
@ -93,7 +94,7 @@ in {
}; };
recipient-blacklist = mkOption { recipient-blacklist = mkOption {
type = with types; listOf str; type = listOf str;
description = "A list of email addresses for whom we will not accept email."; description = "A list of email addresses for whom we will not accept email.";
default = []; default = [];
example = [ example = [
@ -103,14 +104,14 @@ in {
}; };
message-size-limit = mkOption { message-size-limit = mkOption {
type = types.int; type = int;
description = "Size of max email in megabytes."; description = "Size of max email in megabytes.";
default = 30; default = 30;
}; };
user-aliases = mkOption { user-aliases = mkOption {
type = with types; attrsOf(listOf str); type = attrsOf (listOf str);
description = "A map of real user to list of aliases."; description = "A map of real user to list of alias emails.";
default = {}; default = {};
example = { example = {
someuser = ["alias0" "alias1"]; someuser = ["alias0" "alias1"];
@ -118,7 +119,7 @@ in {
}; };
alias-users = mkOption { alias-users = mkOption {
type = with types; attrsOf(listOf str); type = attrsOf (listOf str);
description = "A map of email alias to a list of users."; description = "A map of email alias to a list of users.";
example = { example = {
alias = ["realuser0" "realuser1"]; alias = ["realuser0" "realuser1"];
@ -164,15 +165,27 @@ in {
debug = mkOption { debug = mkOption {
description = "Enable debugging on mailservers."; description = "Enable debugging on mailservers.";
type = types.bool; type = bool;
default = false; default = false;
}; };
max-user-connections = mkOption { max-user-connections = mkOption {
description = "Max simultaneous connections per user."; description = "Max simultaneous connections per user.";
type = types.int; type = int;
default = 20; 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 = [ imports = [
@ -184,22 +197,26 @@ in {
]; ];
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d ${cfg.mail-directory} 770 ${cfg.mail-user} ${cfg.mail-group} - -"
];
networking.firewall = { networking.firewall = {
allowedTCPPorts = [ 25 110 143 587 993 995 ]; allowedTCPPorts = [ 25 110 143 587 993 995 ];
}; };
users = { users = {
users = { users = {
mailuser = { ${cfg.mail-user} = {
isSystemUser = true; isSystemUser = true;
uid = cfg.mail-user-id; uid = cfg.mail-user-id;
group = "mailgroup"; group = cfg.mail-group;
}; };
}; };
groups = { groups = {
mailgroup = { ${cfg.mail-group} = {
members = ["mailuser"]; members = [ cfg.mail-user ];
}; };
}; };
}; };

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.fudo.mail-server; cfg = config.fudo.mail-server;
state-directory = "${cfg.state-directory}/dovecot"; sieve-path = "${cfg.state-directory}/dovecot/imap_sieve";
pipe-bin = pkgs.stdenv.mkDerivation { pipe-bin = pkgs.stdenv.mkDerivation {
name = "pipe_bin"; name = "pipe_bin";
@ -23,29 +23,41 @@ let
''; '';
}; };
ldap-conf = filename: config: ldap-conf-template = ldap-cfg:
let let
ssl-config = if config.ca == null then '' ssl-config = if (ldap-cfg.ca == null) then ''
tls = no tls = no
tls_require_cert = try tls_require_cert = try
'' else '' '' else ''
tls_ca_cert_file = ${config.ca} tls_ca_cert_file = ${ldap-cfg.ca}
tls = yes tls = yes
tls_require_cert = try tls_require_cert = try
''; '';
in in
pkgs.writeText filename '' pkgs.writeText "dovecot2-ldap-config.conf.template" ''
uris = ${concatStringsSep " " config.server-urls} uris = ${concatStringsSep " " ldap-cfg.server-urls}
ldap_version = 3 ldap_version = 3
dn = ${config.reader-dn} dn = ${ldap-cfg.reader-dn}
dnpass = ${config.reader-passwd} dnpass = __LDAP_READER_PASSWORD__
auth_bind = yes auth_bind = yes
auth_bind_userdn = uid=%u,ou=members,dc=fudo,dc=org auth_bind_userdn = uid=%u,ou=members,dc=fudo,dc=org
base = dc=fudo,dc=org base = dc=fudo,dc=org
${ssl-config} ${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: '' ldap-passwd-entry = ldap-config: ''
passdb { passdb {
driver = ldap driver = ldap
@ -61,6 +73,12 @@ let
default = null; default = null;
}; };
base = mkOption {
type = str;
description = "Base of the LDAP server database.";
example = "dc=fudo,dc=org";
};
server-urls = mkOption { server-urls = mkOption {
type = listOf str; type = listOf str;
description = "A list of LDAP server URLs used for authentication."; description = "A list of LDAP server URLs used for authentication.";
@ -74,11 +92,15 @@ let
''; '';
}; };
reader-passwd = mkOption { reader-password-file = mkOption {
type = str; type = str;
description = '' description = "Password for the user specified in ldap-reader-dn.";
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 auth_mechanisms = login plain
${optionalString (cfg.dovecot.ldap != null) ${optionalString (cfg.dovecot.ldap != null) ''
(ldap-passwd-entry cfg.dovecot.ldap)} passdb {
driver = ldap
args = ${cfg.dovecot.ldap.generated-ldap-config}
}
''}
userdb { userdb {
driver = static driver = static
args = uid=${toString cfg.mail-user-id} home=${cfg.mail-directory}/%u args = uid=${toString cfg.mail-user-id} home=${cfg.mail-directory}/%u
@ -249,12 +275,12 @@ in {
# From elsewhere to Spam folder # From elsewhere to Spam folder
imapsieve_mailbox1_name = Junk imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY 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 # From Spam folder to elsewhere
imapsieve_mailbox2_name = * imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY 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_pipe_bin_dir = ${pipe-bin}/pipe/bin
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
} }
@ -268,19 +294,21 @@ in {
''; '';
}; };
systemd.services.dovecot2.preStart = '' systemd = {
mkdir -p '${state-directory}' tmpfiles.rules = [
chown ${dovecot-user}:${cfg.mail-group} '${state-directory}' "d ${sieve-path} 750 ${dovecot-user} ${cfg.mail-group} - -"
rm -rf '${state-directory}/imap_sieve' ];
mkdir '${state-directory}/imap_sieve'
cp -p "${./dovecot/imap_sieve}"/*.sieve '${state-directory}/imap_sieve/' services.dovecot2.preStart = ''
for k in "${state-directory}/imap_sieve"/*.sieve ; do 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" ${pkgs.dovecot_pigeonhole}/bin/sievec "$k"
done done
chown -R '${dovecot-user}:${cfg.mail-group}' '${state-directory}/imap_sieve'
chown '${cfg.mail-user}:${cfg.mail-group}' ${cfg.mail-directory} ${optionalString (cfg.dovecot.ldap != null)
chmod g+w ${cfg.mail-directory} (ldap-conf-generator cfg.dovecot.ldap)}
''; '';
}; };
};
} }

View File

@ -109,7 +109,7 @@ in {
enable = true; enable = true;
domain = cfg.domain; domain = cfg.domain;
origin = cfg.domain; origin = cfg.domain;
hostname = cfg.hostname; hostname = cfg.mail-hostname;
destination = ["localhost" "localhost.localdomain"]; destination = ["localhost" "localhost.localdomain"];
# destination = ["localhost" "localhost.localdomain" cfg.hostname] ++ # destination = ["localhost" "localhost.localdomain" cfg.hostname] ++
# cfg.local-domains;; # cfg.local-domains;;
@ -150,7 +150,7 @@ in {
# mail_spool_directory = "${cfg.mail-directory}/"; # mail_spool_directory = "${cfg.mail-directory}/";
message_size_limit = toString(cfg.message-size-limit * 1024 * 1024); 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_strong_curve = "prime256v1";
tls_eecdh_ultra_curve = "secp384r1"; tls_eecdh_ultra_curve = "secp384r1";

View File

@ -4,6 +4,11 @@ with lib;
let let
cfg = config.fudo.postgresql; 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"; join-lines = lib.concatStringsSep "\n";
userDatabaseOpts = { database, ... }: { userDatabaseOpts = { database, ... }: {
@ -28,15 +33,15 @@ let
}; };
userOpts = { username, ... }: { userOpts = { username, ... }: {
options = { options = with types; {
password-file = mkOption { password-file = mkOption {
type = with types; nullOr str; type = nullOr str;
description = "A file containing the user's (plaintext) password."; description = "A file containing the user's (plaintext) password.";
default = null; default = null;
}; };
databases = mkOption { databases = mkOption {
type = with types; attrsOf (submodule userDatabaseOpts); type = attrsOf (submodule userDatabaseOpts);
description = "Map of databases to required database/table perms."; description = "Map of databases to required database/table perms.";
default = { }; default = { };
example = { example = {
@ -50,9 +55,9 @@ let
}; };
databaseOpts = { dbname, ... }: { databaseOpts = { dbname, ... }: {
options = { options = with types; {
users = mkOption { users = mkOption {
type = with types; listOf str; type = listOf str;
description = description =
"A list of users who should have full access to this database."; "A list of users who should have full access to this database.";
default = [ ]; default = [ ];
@ -74,9 +79,7 @@ let
''; '';
passwords-setter-script = users: passwords-setter-script = users:
pkgs.writeScriptBin "postgres-set-passwords.sh" '' pkgs.writeScript "postgres-set-passwords.sh" ''
#!${pkgs.bash}/bin/bash
if [ $# -ne 1 ]; then if [ $# -ne 1 ]; then
echo "usage: $0 output-file.sql" echo "usage: $0 output-file.sql"
exit 1 exit 1
@ -99,7 +102,7 @@ let
nameValuePair "DATABASE ${database}" databaseOpts.access) databases; nameValuePair "DATABASE ${database}" databaseOpts.access) databases;
makeEntry = nw: 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); makeNetworksEntry = networks: join-lines (map makeEntry networks);
@ -263,8 +266,8 @@ in {
local all all ident local all all ident
# host-local # host-local
host all all 127.0.0.1/32 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=FUDO.ORG host all all ::1/128 gss include_realm=0 krb_realm=${gssapi-realm}
# local networks # local networks
${makeNetworksEntry cfg.local-networks} ${makeNetworksEntry cfg.local-networks}
@ -278,8 +281,7 @@ in {
postgresql-password-setter = let postgresql-password-setter = let
passwords-script = passwords-setter-script cfg.users; passwords-script = passwords-setter-script cfg.users;
password-wrapper-script = password-wrapper-script =
pkgs.writeScriptBin "password-script-wrapper.sh" '' pkgs.writeScript "password-script-wrapper.sh" ''
#!${pkgs.bash}/bin/bash
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX) TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX)
echo "using temp dir $TMPDIR" echo "using temp dir $TMPDIR"
PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql
@ -287,7 +289,7 @@ in {
touch $PASSWORD_SQL_FILE touch $PASSWORD_SQL_FILE
chown ${config.services.postgresql.superUser} $PASSWORD_SQL_FILE chown ${config.services.postgresql.superUser} $PASSWORD_SQL_FILE
chmod go-rwx $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" echo "executing $PASSWORD_SQL_FILE"
${pkgs.postgresql}/bin/psql --port ${ ${pkgs.postgresql}/bin/psql --port ${
toString config.services.postgresql.port toString config.services.postgresql.port
@ -306,7 +308,7 @@ in {
Type = "oneshot"; Type = "oneshot";
User = config.services.postgresql.superUser; User = config.services.postgresql.superUser;
}; };
script = "${password-wrapper-script}/bin/password-script-wrapper.sh"; script = "${password-wrapper-script}";
}; };
postgresql.postStart = let postgresql.postStart = let

View File

@ -86,15 +86,17 @@ in {
locations."/" = { locations."/" = {
proxyPass = "http://127.0.0.1:9090"; 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port; proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
${optionalString ((length fudo-cfg.local-networks) > 0) ${optionalString ((length local-networks) > 0)
(concatStringsSep "\n" (map (network: "allow ${network};") fudo-cfg.local-networks)) + "\ndeny all;"} (concatStringsSep "\n" (map (network: "allow ${network};") local-networks)) + "\ndeny all;"}
''; '';
}; };
}; };

View File

@ -185,18 +185,6 @@ in {
}; };
config = { 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 = networking.firewall.allowedTCPPorts =
mkIf site-cfg.enable-ssh-backdoor [ site-cfg.dropbear-ssh-port ]; mkIf site-cfg.enable-ssh-backdoor [ site-cfg.dropbear-ssh-port ];

View File

@ -1,65 +1,9 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; 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 = { 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 programs.ssh.knownHosts = let
keyed-hosts = keyed-hosts =
filterAttrs (h: o: o.ssh-pubkeys != []) filterAttrs (h: o: o.ssh-pubkeys != [])
config.fudo.hosts; config.fudo.hosts;
@ -75,7 +19,7 @@ in {
in mapAttrs (hostname: hostOpts: { in mapAttrs (hostname: hostOpts: {
publicKeyFile = builtins.head hostOpts.ssh-pubkeys; publicKeyFile = builtins.head hostOpts.ssh-pubkeys;
hostNames = all-hostnames hostname host-cfg; hostNames = all-hostnames hostname hostOpts;
}) keyed-hosts; }) keyed-hosts;
}; };
} }

View File

@ -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;
}

View File

@ -65,34 +65,17 @@ in {
}; };
}; };
imports = [ ./users-common.nix ];
config = let config = let
sys = config.instance; sys = config.instance;
in { in {
fudo.auth.ldap-server = let fudo.auth.ldap-server = {
ldapUsers = (filterAttrs users = filterAttrs
(username: userOpts: userOpts.ldap-hashed-password != null)) (username: userOpts: userOpts.ldap-hashed-passwd != null)
config.fudo.users; config.fudo.users;
in { groups = config.fudo.groups;
users = mapAttrs (username: userOpts: {
uid = userOpts.uid;
group = userOpts.primary-group;
common-name = userOpts.common-name;
hashed-password = userOpts.ldap-hashed-password;
}) ldapUsers;
groups = mapAttrs (groupname: groupOpts: { system-users = config.fudo.system-users;
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;
}; };
programs.ssh.extraConfig = mkAfter '' programs.ssh.extraConfig = mkAfter ''
@ -161,9 +144,6 @@ in {
in admin-entries // user-entries; 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 # Group home directories have to exist, otherwise users can't log in
systemd.services = let systemd.services = let
ensure-group-directories = group: ensure-group-directories = group:

View File

@ -302,7 +302,7 @@ in {
(site: site-cfg: let (site: site-cfg: let
site-config-file = builtins.toFile "${site}-rainloop.cfg" 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" '' domain-config-file = builtins.toFile "${site}-domain.cfg" ''
imap_host = "${site-cfg.mail-server}" imap_host = "${site-cfg.mail-server}"
@ -342,7 +342,7 @@ in {
link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg: link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg:
let let
cfg-file = config.fudo.secrets.host-secrets.${hostname}."${site}-site-config".target-file; 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 '' in ''
${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/configs ${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 ${pkgs.coreutils}/bin/cp ${cfg-file} ${base-data-path}/${site}/_data_/_default_/configs/application.ini

View File

@ -52,6 +52,11 @@ in {
description = "List of users who should have access to the local host"; description = "List of users who should have access to the local host";
}; };
local-networks = mkOption {
type = listOf str;
description = "Networks which are considered local to this host, site, or domain.";
};
build-seed = mkOption { build-seed = mkOption {
type = str; type = str;
description = "Seed used to generate configuration."; description = "Seed used to generate configuration.";
@ -87,25 +92,23 @@ in {
filterAttrs (host: hostOpts: hostOpts.site == local-site) config.fudo.hosts; filterAttrs (host: hostOpts: hostOpts.site == local-site) config.fudo.hosts;
local-networks = local-networks =
host.local-networks // host.local-networks ++
config.fudo.domains.${local-domain}.local-networks // config.fudo.domains.${local-domain}.local-networks ++
config.fudo.sites.${local-site}.local-networks; config.fudo.sites.${local-site}.local-networks;
local-profile = host.profile; local-profile = host.profile;
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
in { in {
instance = { instance = {
inherit inherit
build-seed
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; local-profile
local-networks;
}; };
}; };
} }

17
lib/network.nix Normal file
View File

@ -0,0 +1,17 @@
{ pkgs, ... }:
with pkgs.lib;
let
generate-mac-address = hostname: interface: pkgs.stdenv.mkDerivation {
name = "mk-mac-${hostname}-${interface}";
phases = [ "installPhase" ];
installPhase = ''
echo ${hostname}-${interface} | sha1sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/' > $out
'';
};
in {
generate-mac-address = hostname: interface: let
pkg = generate-mac-address hostname interface;
in builtins.readFile "${pkg}";
}

View File

@ -4,9 +4,10 @@
lib = prev.lib; lib = prev.lib;
in { in {
ip = import ./ip.nix { pkgs = prev; }; ip = import ./ip.nix { pkgs = prev; };
dns = import ./dns.nix { pkgs = prev;}; dns = import ./dns.nix { pkgs = prev; };
passwd = import ./passwd.nix { pkgs = prev;}; passwd = import ./passwd.nix { pkgs = prev; };
lisp = import ./lisp.nix { pkgs = prev;}; lisp = import ./lisp.nix { pkgs = prev; };
network = import ./network.nix { pkgs = prev; };
}; };
}; };
}) })

View File

@ -5,23 +5,17 @@ let
hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation { hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation {
name = "${name}-ldap-passwd"; name = "${name}-ldap-passwd";
phases = [ "buildPhase" "installPhase" ]; phases = [ "installPhase" ];
buildInputs = with pkgs; [ openldap ]; buildInputs = with pkgs; [ openldap ];
buildPhase = ''
slappasswd -T ${passwd-file} > ldap-passwd
'';
installPhase = '' installPhase = ''
mkdir -p $out slappasswd -T ${passwd-file} > $out
mv ldap-passwd $out
''; '';
}; };
hash-ldap-passwd = name: passwd-file: let hash-ldap-passwd = name: passwd-file:
passwd-pkgs = hash-ldap-passwd-pkg name passwd-file; builtins.readFile "${hash-ldap-passwd-pkg name passwd-file}";
in builtins.readFile "${passwd-pkgs}/ldap-passwd";
generate-random-passwd = name: length: pkgs.stdenv.mkDerivation { generate-random-passwd = name: length: pkgs.stdenv.mkDerivation {
name = "${name}-random-passwd"; name = "${name}-random-passwd";
@ -31,7 +25,7 @@ let
buildInputs = with pkgs; [ pwgen ]; buildInputs = with pkgs; [ pwgen ];
installPhase = '' installPhase = ''
pwgen --secure --num-passwords=1 ${length} > $out pwgen --secure --num-passwords=1 ${toString length} > $out
''; '';
}; };

View File

@ -108,15 +108,21 @@ rec {
description = "User's primary email address."; description = "User's primary email address.";
default = null; default = null;
}; };
email-aliases = mkOption {
type = listOf str;
description = "Email aliases that should map to this user.";
default = [];
};
}; };
}; };
groupOpts = { group-name, ... }: { groupOpts = { name, ... }: {
options = with lib.types; { options = with lib.types; {
# group-name = mkOption { group-name = mkOption {
# description = "Group name."; description = "Group name.";
# default = group-name; default = name;
# }; };
description = mkOption { description = mkOption {
type = str; type = str;