From 08766dfeb6fa83350685a1c89ff3b2b3d062c279 Mon Sep 17 00:00:00 2001 From: niten Date: Thu, 28 Oct 2021 09:32:35 -0700 Subject: [PATCH] Partway to getting France back in the fold. Stuck on ACME certs. --- config/domains.nix | 4 +- config/hardware/zbox.nix | 70 ++++++-- config/host-config/clunk.nix | 2 +- config/host-config/france.nix | 139 ++++------------ config/host-config/france/auth.nix | 117 +++++++++++++ config/host-config/france/git.nix | 87 ++++++++++ config/host-config/france/jabber.nix | 110 ++++++++++++ config/host-config/france/mail-server.nix | 57 +++++++ config/host-config/france/monitoring.nix | 19 +++ config/host-config/france/postgres.nix | 18 ++ config/host-config/france/webmail.nix | 100 +++++++++++ config/host-config/limina.nix | 2 +- config/host-config/nostromo.nix | 26 +-- config/host-config/plato.nix | 2 +- config/host-config/procul.nix | 97 +++++++++-- config/host-config/socrates.nix | 2 +- config/host-config/spark.nix | 26 +-- config/host-config/zbox.nix | 28 ++-- config/hosts/france.nix | 24 +-- config/hosts/nostromo.nix | 23 +++ config/networks/fudo.org.nix | 2 - config/site-config/seattle.nix | 75 +++++---- config/sites.nix | 59 ++----- lib/default.nix | 2 + lib/fudo-lib.nix | 3 +- lib/fudo/acme-certs.nix | 119 +++++++++++++ lib/fudo/host-filesystems.nix | 27 +-- lib/fudo/hosts.nix | 62 ++++--- lib/fudo/include/rainloop.nix | 6 +- lib/fudo/jabber.nix | 194 ++++++++++++++++++++++ lib/fudo/kdc.nix | 29 ++-- lib/fudo/ldap.nix | 42 ++--- lib/fudo/secrets.nix | 30 +++- lib/fudo/sites.nix | 27 +-- lib/fudo/webmail.nix | 156 ++++++++++------- lib/instance.nix | 6 + lib/passwd.nix | 48 ++++++ 37 files changed, 1399 insertions(+), 441 deletions(-) create mode 100644 config/host-config/france/auth.nix create mode 100644 config/host-config/france/git.nix create mode 100644 config/host-config/france/jabber.nix create mode 100644 config/host-config/france/mail-server.nix create mode 100644 config/host-config/france/monitoring.nix create mode 100644 config/host-config/france/postgres.nix create mode 100644 config/host-config/france/webmail.nix create mode 100644 lib/fudo/acme-certs.nix create mode 100644 lib/fudo/jabber.nix create mode 100644 lib/passwd.nix diff --git a/config/domains.nix b/config/domains.nix index 3308862..ed37a4c 100644 --- a/config/domains.nix +++ b/config/domains.nix @@ -13,7 +13,7 @@ }; "sea.fudo.org" = { - local-networks = [ "10.0.0.0/24" ]; + local-networks = [ "10.0.0.0/16" ]; local-users = [ "niten" "reaper" "xiaoxuan" "ken" ]; local-groups = [ "fudo" "selby" "admin" ]; @@ -23,7 +23,7 @@ }; "rus.selby.ca" = { - local-networks = [ "10.0.0.0/24" ]; + local-networks = [ "10.0.0.0/16" ]; local-users = [ "niten" diff --git a/config/hardware/zbox.nix b/config/hardware/zbox.nix index c65b4e7..d969791 100644 --- a/config/hardware/zbox.nix +++ b/config/hardware/zbox.nix @@ -1,27 +1,59 @@ { config, lib, pkgs, ... }: -{ +{ boot = { loader = { systemd-boot.enable = true; efi.canTouchEfiVariables = true; }; initrd = { - availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "sd_mod" ]; + availableKernelModules = + [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ]; kernelModules = [ ]; }; kernelModules = [ "kvm-intel" ]; + kernelPackages = pkgs.linuxPackages_latest; extraModulePackages = [ ]; }; - fileSystems."/" = { - device = "/dev/disk/by-label/zbox-root"; - fsType = "btrfs"; - }; + system.stateVersion = "21.05"; - fileSystems."/boot" = { - device = "/dev/disk/by-label/BOOT"; - fsType = "vfat"; + fileSystems = { + "/" = { + device = "zbox-root"; + fsType = "tmpfs"; + options = [ "mode=755" ]; + }; + + "/boot" = { + device = "/dev/disk/by-label/ZBOX-BOOT"; + fsType = "vfat"; + options = [ "noexec" "noatime" "nodiratime" ]; + }; + + "/state" = { + device = "/dev/disk/by-label/zbox-data"; + fsType = "btrfs"; + options = [ "noatime" "nodiratime" "compress=zstd" "noexec" "subvol=@state" ]; + }; + + "/nix" = { + device = "/dev/disk/by-label/zbox-data"; + fsType = "btrfs"; + options = [ "noatime" "nodiratime" "compress=zstd" "subvol=@nix" ]; + }; + + "/var/log" = { + device = "/dev/disk/by-label/zbox-data"; + fsType = "btrfs"; + options = [ "noatime" "nodiratime" "compress=zstd" "noexec" "subvol=@logs" ]; + }; + + "/home" = { + device = "/dev/disk/by-label/zbox-data"; + fsType = "btrfs"; + options = [ "noatime" "nodiratime" "compress=zstd" "noexec" "subvol=@home" ]; + }; }; swapDevices = [{ device = "/dev/disk/by-label/zbox-swap"; }]; @@ -34,14 +66,23 @@ opengl = { driSupport = true; driSupport32Bit = true; - - # extraPackages32 = with pkgs.i686Linux; [ libva ]; + extraPackages = with pkgs; [ + rocm-opencl-icd + rocm-opencl-runtime + amdvlk + driversi686Linux.amdvlk + ]; + setLdLibraryPath = true; }; pulseaudio = { support32Bit = true; package = pkgs.pulseaudioFull; }; + + enableRedistributableFirmware = true; + + enableAllFirmware = true; }; networking = { @@ -64,4 +105,11 @@ nix.maxJobs = lib.mkDefault 8; powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + + systemd.targets = { + sleep.enable = false; + suspend.enable = false; + hibernate.enable = false; + hybrid-sleep.enable = false; + }; } diff --git a/config/host-config/clunk.nix b/config/host-config/clunk.nix index 386622a..61fc3ce 100644 --- a/config/host-config/clunk.nix +++ b/config/host-config/clunk.nix @@ -65,7 +65,7 @@ in { useDHCP = false; ipv4.addresses = [{ address = primary-ip; - prefixLength = 22; + prefixLength = 16; }]; }; }; diff --git a/config/host-config/france.nix b/config/host-config/france.nix index aef4184..4623d7a 100644 --- a/config/host-config/france.nix +++ b/config/host-config/france.nix @@ -1,5 +1,6 @@ { config, lib, pkgs, ... }: +with lib; let primary-ip = "208.81.3.117"; git-server-ip = "208.81.3.118"; @@ -9,131 +10,61 @@ let host-fqdn = "${hostname}.${domain-name}"; mail-hostname = "mail.fudo.org"; + france-secrets = config.fudo.secrets.host-secrets.france; + + acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem"; + acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem"; + in { - imports = [ ./france/postgresql.nix ]; + 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 nix-files ./france; config = { fudo = { - auth = { - ldap = { - enable = true; - base = "dc=fudo,dc=org"; - organization = "Fudo"; - rootpw-file = "FIXME"; - kerberos-host = host-fqdn; - kerberos-keytab = "FIXME"; - - sslCert = "FIXME"; - sslKey = "FIXME"; - sslCaCert = "FIXME"; - - listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ]; - - users = config.fudo.users; - groups = config.fudo.groups; - system-users = config.fudo.system-users; - }; - - kdc = let realm = "FUDO.ORG"; - in { - enable = true; - database-path = "FIXME"; - realm = realm; - mkey-file = "FIXME"; - acl = [ - { - principal = "pam_migrate/*.fudo.org@${realm}"; - access = "add"; - } - { - principal = "host/*.fudo.org@${realm}"; - access = "add"; - } - ] ++ (concatMap (user: [ - { - principal = "${user}@${realm}"; - access = "add,list,modify"; - } - { - principal = "${user}/root@${realm}"; - access = "all"; - } - ]) domain.admin-users); - bind-addresses = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; - }; - }; - - prometheus = { - enable = true; - hostname = "metrics.fudo.org"; - service-discovery-dns = let dns-root = "_metrics._tcp.fudo.org"; - in { - node = [ "node.${dns-root}" ]; - postfix = [ "postfix.${dns-root}" ]; - dovecot = [ "dovecot.${dns-root}" ]; - rspamd = [ "rspamd.${dns-root}" ]; - }; - }; - - postgresql = { - enable = true; - # FIXME: ssl-private-key && ssl certificate - keytab = "/srv/postgres/secure/postgres.keytab"; - local-networks = getHostLocalNetworks hostname; - admin-users = domain.admin-users; - }; + hosts.france.external-interfaces = [ "extif0" ]; client.dns = { enable = true; ipv4 = true; ipv6 = true; - user = "FIXME"; + user = "fudo-client"; external-interface = "extif0"; - password-file = "FIXME"; }; - mail-server = domain.mail-config // { - enableContainer = true; - monitoring = true; - - hostname = mail-hostname; - - state-directory = "FIXME"; - mail-directory = "FIXME"; - - dovecot.ldap = { - reader-dn = "FIXME"; - reader-password = "FIXME"; - server-urls = [ "FIXME" ]; + france = { + mail = { + mail-directory = "/state/mail-server/mail"; + state-directory = "/state/mail-server/var"; + ldap-server-urls = [ + "ldap://france.fudo.org" + ]; }; - clamav.enable = true; - dkim.signing = true; - }; - - git = { - enable = true; - hostname = "git.fudo.org"; - site-name = "Fudo Git"; - user = "FIXME"; - database = { - user = "FIXME"; - password-file = "FIXME"; - hostname = "127.0.0.1"; - name = "FIXME"; + webmail = { + # TODO: this is not using the database! + mail-server = mail-hostname; + database.hostname = "localhost"; }; - repository-dir = "FIXME"; - state-dir = "FIXME"; - ssh = { - listen-ip = git-server-ip; - listen-port = 22; + + git = { + repository-directory = "/state/gitea/repo"; + state-directory = "/state/gitea/state"; + ssh.listen-ip = git-server-ip; }; }; minecraft-server = { enable = true; package = pkgs.minecraft-current; - data-dir = "FIXME"; + data-dir = "/state/minecraft/selbyland"; world-name = "selbyland"; motd = "Welcome to the Selby Minecraft server."; }; diff --git a/config/host-config/france/auth.nix b/config/host-config/france/auth.nix new file mode 100644 index 0000000..9235c04 --- /dev/null +++ b/config/host-config/france/auth.nix @@ -0,0 +1,117 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + domain-name = config.instance.local-domain; + site-name = config.instance.local-site; + fqdn = "${hostname}.${domain-name}"; + + secrets = config.fudo.secrets.host-secrets.france; + + # same as genAttr, but takes back attrsets and merges them + concatGenAttrs = lst: f: + foldr (a0: a1: a0 // a1) {} (map f lst); + +in { + options.france = with types; { + ldap = { + ssl-certificate = mkOption { + type = path; + description = "SSL certificate to use for the LDAP server."; + }; + ssl-private-key = mkOption { + type = path; + description = "SSL private key to use for the LDAP server."; + }; + ssl-ca-certificate = mkOption { + type = path; + description = "SSL certificate authority to use for the LDAP server."; + }; + }; + + kdc = { + state-directory = mkOption { + type = str; + description = "Path at which to store kerberos state."; + }; + + master-key-file = mkOption { + type = str; + description = "Heimdal database master key file."; + }; + }; + }; + + config = { + fudo = { + secrets.host-secrets.${hostname} = { + ldap-ssl-certificate = { + source-file = cfg.ssl-certificate; + target-file = "/var/run/ldap/ssl-certificate.pem"; + user = config.services.openldap.user; + group = config.services.openldap.group; + permissions = "0444"; + }; + ldap-ssl-private-key = { + source-file = cfg.ssl-private-key; + target-file = "/var/run/ldap/ssl-private-key.pem"; + user = config.services.openldap.user; + group = config.services.openldap.group; + permissions = "0400"; + }; + ldap-ssl-ca-certificate = { + source-file = cfg.ssl-ca-certificate; + target-file = "/var/run/ldap/ssl-ca-certificate.pem"; + user = config.services.openldap.user; + group = config.services.openldap.group; + permissions = "0400"; + }; + }; + + auth = { + ldap = { + enable = true; + base = "dc=fudo,dc=org"; + organization = "Fudo"; + rootpw-file = secrets.ldap-root-passwd; + kerberos-host = fqdn; + kerberos-keytab = secrets.ldap-keytab; + + sslCert = + secrets.ldap-ssl-certificate.target-file; + sslKey = + secrets.ldap-ssl-private-key.target-file; + sslCACert = + secrets.ldap-ssl-ca-certificate.target-file; + + listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ]; + + users = config.fudo.users; + groups = config.fudo.groups; + system-users = config.fudo.system-users; + }; + + # TODO: let build hosts create keys? + kdc = { + enable = true; + realm = config.domains.${domain-name}.gssapi-realm; + state-directory = cfg.state-directory; + master-key-file = cfg.master-key-file; + acl = let + admin-entries = concatGenAttrs + config.instance.local-admins + (admin: { + "${admin}" = { perms = [ "add" "list" "change-password" ]; }; + "${admin}/root" = { perms = [ "all" ]; }; + }); + in { + "host/*.fudo.org" = { perms = [ "add" ]; }; + "pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; }; + } // admin-entries; + bind-addresses = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; + }; + }; + }; + }; +} diff --git a/config/host-config/france/git.nix b/config/host-config/france/git.nix new file mode 100644 index 0000000..7c91a00 --- /dev/null +++ b/config/host-config/france/git.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + domain-name = config.instance.local-domain; + + secrets = config.fudo.secrets.host-secrets.${hostname}; + + sshOpts = { ... }: { + options = { + listen-ip = mkOption { + type = str; + description = "IP address on which to listen for SSH connections."; + }; + listen-port = mkOption { + type = str; + description = "Port on which to listen for SSH connections."; + default = 22; + }; + }; + }; + + +in { + options.france.git = with types; { + repository-directory = mkOption { + type = str; + description = "Path to store git repositories."; + }; + state-directory = mkOption { + type = str; + description = "Path to store git server state."; + }; + database-host = mkOption { + type = str; + description = "PostGreSQL database host."; + }; + ssh = mkOption { + type = submodule sshOpts; + description = "Git SSH listen options."; + }; + }; + + config.fudo = { + postgresql = { + databases.fudo_git.users = + config.instance.local_admins; + + users.fudo_git = { + password-file = + secrets.git-database-password.target-file; + databases = { + fudo_git = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = + "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = + "SELECT, UPDATE"; + }; + }; + }; + }; + }; + + git = { + enable = true; + hostname = "git.${domain-name}"; + site-name = "Fudo Git"; + user = "git-fudo"; + repository-dir = cfg.repository-directory; + state-dir = cfg.state-directory; + database = { + user = "fudo_git"; + password-file = + secrets.git-database-password.target-file; + hostname = cfg.database-host; + name = "fudo_git"; + }; + ssh = { + listen-ip = cfg.ssh.listen-ip; + listen-port = cfg.ssh.listen-port; + }; + }; + }; +} diff --git a/config/host-config/france/jabber.nix b/config/host-config/france/jabber.nix new file mode 100644 index 0000000..005586e --- /dev/null +++ b/config/host-config/france/jabber.nix @@ -0,0 +1,110 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + secrets = config.fudo.secrets.host-secrets.${hostname}; + +in { + config = { + fudo = { + jabber = { + enable = true; + + secret-files = { + LDAP_PASSWORD = secrets.jabber-ldap-password.target-file; + }; + + sites = { + "fudo.im" = { + site-config = { + auth_method = "ldap"; + ldap_servers = [ "auth.fudo.org" ]; + ldap_port = 389; + ldap_rootdn = "cn=jabber,dc=fudo,dc=org"; + ldap_password = ''"LDAP_PASSWD"''; + ldap_base = "ou=members,dc=fudo,dc=org"; + ldap_filter = "(objectClass=posixAccount)"; + ldap_uids = { uid = "%u"; }; + + modules = { + mod_adhoc = {}; + mod_announce = {}; + mod_avatar = {}; + mod_blocking = {}; + mod_caps = {}; + mod_carboncopy = {}; + mod_client_state = {}; + mod_configure = {}; + mod_disco = {}; + mod_fail2ban = {}; + mod_last = {}; + mod_offline = { + access_max_user_messages = 5000; + }; + mod_ping = {}; + mod_privacy = {}; + mod_private = {}; + mod_pubsub = { + access_createnode = "pubsub_createnode"; + ignore_pep_from_offline = true; + last_item_cache = false; + plugins = [ + "flat" + "pep" + ]; + }; + mod_roster = {}; + mod_stream_mgmt = {}; + mod_time = {}; + mod_vcard = { + search = false; + }; + mod_vcard_xupdate = {}; + mod_version = {}; + }; + }; + }; + + "backplane.fudo.org" = { + site-config = { + auth_method = "external"; + extauth_program = "${pkgs.guile}/bin/guile -s ${backplane-auth}"; + extauth_pool_size = 3; + auth_use_cache = true; + + modules = { + mod_adhoc = {}; + mod_caps = {}; + mod_carboncopy = {}; + mod_client_state = {}; + mod_configure = {}; + mod_disco = {}; + mod_fail2ban = {}; + mod_last = {}; + mod_offline = { + access_max_user_messages = 5000; + }; + mod_ping = {}; + mod_pubsub = { + access_createnode = "pubsub_createnode"; + ignore_pep_from_offline = true; + last_item_cache = false; + plugins = [ + "flat" + "pep" + ]; + }; + mod_roster = {}; + mod_stream_mgmt = {}; + mod_time = {}; + mod_version = {}; + }; + + }; + }; + }; + }; + }; + }; +} diff --git a/config/host-config/france/mail-server.nix b/config/host-config/france/mail-server.nix new file mode 100644 index 0000000..bfaece3 --- /dev/null +++ b/config/host-config/france/mail-server.nix @@ -0,0 +1,57 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + domain-name = config.instance.local-domain; + + secrets = config.fudo.secrets.host-secrets.${hostname}; + + mail-reader-dn = "mail-auth-reader"; +in { + options.france.mail = with types; { + mail-directory = mkOption { + type = str; + description = "Directory to contain user maildirs."; + }; + + state-directory = mkOption { + type = str; + description = "Directory to contain mail-server state."; + }; + + ldap-server-urls = mkOption { + type = listOf str; + description = "List of LDAP server URLs."; + }; + }; + + config.fudo = { + system-users = { + username = mail-reader-dn; + description = "Used by the mail server to connect to LDAP for auth."; + ldap-hashed-password = + pkgs.lib.fudo.passwd.hash-ldap-passwd + secrets.mail-reader-passwd.target-file; + }; + + mail-server = { + enableContainer = true; + monitoring = true; + + hostname = "mail.${domain-name}"; + + state-directory = cfg.state-directory; + mail-directory = cfg.mail-directory; + + dovecot.ldap = { + reader-dn = "cn=mail-reader-dn,${config.fudo.auth.ldap.base}"; + reader-password-file = secrets.mail-reader-passwd.target-file; + server-urls = cfg.ldap-server-urls; + }; + + clamav.enable = true; + dkim.signing = true; + }; + }; +} diff --git a/config/host-config/france/monitoring.nix b/config/host-config/france/monitoring.nix new file mode 100644 index 0000000..b1d2fd9 --- /dev/null +++ b/config/host-config/france/monitoring.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + domain-name = config.instance.local-domain; +in { + config.fudo.prometheus = { + enable = true; + hostname = "metrics.${domain-name}"; + service-discovery-dns = let + srv-dns = "_metrics._tcp.${domain-name}"; + in { + node = [ "node.${srv-dns}" ]; + postfix = [ "postfix.${srv-dns}" ]; + dovecot = [ "dovecot.${srv-dns}" ]; + rspamd = [ "rspamd.${srv-dns}" ]; + }; + }; +} diff --git a/config/host-config/france/postgres.nix b/config/host-config/france/postgres.nix new file mode 100644 index 0000000..49cf30a --- /dev/null +++ b/config/host-config/france/postgres.nix @@ -0,0 +1,18 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + secrets = config.fudo.secrets.host-secrets.${hostname}; +in { + config.fudo.postgresql = { + enable = true; + + local-networks = config.instance.local-networks; + admin-users = config.instance.admin-users; + + ssl-private-key = secrets.postgres-ssl-key; + ssl-certificate = secrets.postgres-ssl-certificate; + keytab = secrets.postgres-keytab.target-file; + }; +} diff --git a/config/host-config/france/webmail.nix b/config/host-config/france/webmail.nix new file mode 100644 index 0000000..312d4c0 --- /dev/null +++ b/config/host-config/france/webmail.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + domain-name = config.instance.local-domain; + site-name = config.instance.local-site; + + secrets = config.fudo.secrets.host-secrets.${hostname}; + + static = config.fudo.static; + + mail-hostname = config.france.webmail.mail-server; + + db-host = config.france.webmail.database.hostname; + + db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40; + +in { + options.france.webmail = with types; { + mail-server = mkOption { + type = str; + description = "Mail server to use for webmail."; + }; + + database = { + hostname = mkOption { + type = str; + description = "PostgreSQL server."; + }; + }; + }; + + config.fudo = { + webmail = { + enable = true; + + sites = { + "webmail.fudo.link" = { + title = "Fudo Link Webmail"; + favicon = "${static}/fudo.link/favicon.ico"; + mail-server = mail-hostname; + domain = "fudo.link"; + edit-mode = "Plain"; + layout-mode = "bottom"; + database = { + hostname = db-host; + password-file = db-passwd; + }; + }; + + "webmail.test.fudo.org" = { + title = "Fudo Webmail"; + favicon = "${static}/fudo.org/favicon.ico"; + mail-server = mail-hostname; + domain = "fudo.org"; + edit-mode = "Plain"; + database = { + hostname = db-host; + password-file = db-passwd; + }; + }; + + "webmail.fudo.org" = { + title = "Fudo Webmail"; + favicon = "${static}/fudo.org/favicon.ico"; + mail-server = mail-hostname; + domain = "fudo.org"; + edit-mode = "Plain"; + database = { + hostname = db-host; + password-file = db-passwd; + }; + }; + + "webmail.test.selby.ca" = { + title = "Selby Webmail"; + favicon = "${static}/selby.ca/favicon.ico"; + mail-server = mail-hostname; + domain = "selby.ca"; + database = { + hostname = db-host; + password-file = db-passwd; + }; + }; + + "webmail.selby.ca" = { + title = "Selby Webmail"; + favicon = "${static}/selby.ca/favicon.ico"; + mail-server = mail-hostname; + domain = "selby.ca"; + database = { + hostname = db-host; + password-file = db-passwd; + }; + }; + }; + }; + }; +} diff --git a/config/host-config/limina.nix b/config/host-config/limina.nix index 6190445..7a2bae2 100644 --- a/config/host-config/limina.nix +++ b/config/host-config/limina.nix @@ -22,7 +22,7 @@ in { useDHCP = false; ipv4.addresses = [{ address = primary-ip; - prefixLength = 22; + prefixLength = 16; }]; }; intif1 = { useDHCP = false; }; diff --git a/config/host-config/nostromo.nix b/config/host-config/nostromo.nix index f4231f6..58ede2f 100644 --- a/config/host-config/nostromo.nix +++ b/config/host-config/nostromo.nix @@ -22,25 +22,6 @@ in { # Hopefully this'll help with NFS... boot.kernelModules = [ "rpcsec_gss_krb5" ]; - fudo.hosts.nostromo.encrypted-filesystems.sea-store = { - encrypted-device = "/dev/nostromo-store/locked"; - key-path = "/run/keys/sea-store"; - filesystem-type = "btrfs"; - options = [ "noatime" "nodiratime" "compress=zstd" "noexec" ]; - mountpoints = { - "/export/documents" = { - options = [ "subvol=@documents" ]; - group = "sea-documents"; - users = [ "niten" ]; - }; - "/export/downloads" = { - options = [ "subvol=@downloads" ]; - group = "sea-downloads"; - users = [ "niten" ]; - }; - }; - }; - services.nfs = { # See lib/fudo/users.nix for the user@REALM -> user mapping server = { @@ -50,6 +31,7 @@ in { exportList = [ "/export/documents 10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check,fsid=10,sec=krb5p)" "/export/downloads 10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check,fsid=11,sec=krb5i)" + "/export/projects 10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check,fsid=11,sec=krb5p)" ]; in '' ${concatStringsSep "\n" exportList} @@ -61,7 +43,11 @@ in { # Don't start on boot wantedBy = mkForce [ "sea-store.target" ]; # Only start after filesystem mounts are available - after = [ "export-documents.mount" "export-downloads.mount" ]; + after = [ + "export-documents.mount" + "export-downloads.mount" + "export-projects.mount" + ]; }; fudo.ipfs = { diff --git a/config/host-config/plato.nix b/config/host-config/plato.nix index 56409a3..0a763cf 100644 --- a/config/host-config/plato.nix +++ b/config/host-config/plato.nix @@ -16,7 +16,7 @@ in { useDHCP = false; ipv4.addresses = [{ address = primary-ip; - prefixLength = 22; + prefixLength = 16; }]; }; }; diff --git a/config/host-config/procul.nix b/config/host-config/procul.nix index 4c4c4db..436cc1f 100644 --- a/config/host-config/procul.nix +++ b/config/host-config/procul.nix @@ -5,12 +5,14 @@ let hostname = "procul"; host-ipv4 = "172.86.179.18"; git-ipv4 = "172.86.179.19"; - domain = config.fudo.hosts.${hostname}.domain; - site = config.fudo.hosts.${hostname}.site; - host-fqdn = "${hostname}.${domain}"; + 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-networks = config.fudo.domains.${domain}.local-networks - ++ config.fudo.sites.${site}.local-networks; + local-networks = + domain.local-networks ++ site.local-networks; acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem"; acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem"; @@ -18,6 +20,8 @@ let local-packages = with pkgs; [ ldns.examples ]; + secrets = config.fudo.secrets.host-secrets.procul; + in { networking = { dhcpcd.enable = false; @@ -26,9 +30,9 @@ in { enableIPv6 = true; # FIXME: this isn't the right place - search = [ domain ]; + search = [ domain-name ]; nameservers = [ "127.0.0.1" ]; - defaultGateway = "172.86.179.17"; + defaultGateway = site.gateway-v4; interfaces = { extif0 = { @@ -81,6 +85,64 @@ in { fudo = { hosts.procul.external-interfaces = [ "extif0" ]; + jabber = { + enable = true; + + secret-files = { + SECRET = secrets.jabber-ldap-password.traget-file; + }; + + sites."informis.land" = { + site-config = { + auth_method = "ldap"; + ldap_servers = [ "auth.fudo.org" ]; + ldap_port = 636; + ldap_rootdn = "cn=jabber,dc=fudo,dc=org"; + ldap_password = ''"LDAP_PASSWD"''; + ldap_base = "ou=members,dc=fudo,dc=org"; + ldap_filter = "(objectClass=posixAccount)"; + ldap_uids = { uid = "%u"; }; + + modules = { + mod_adhoc = {}; + mod_announce = {}; + mod_avatar = {}; + mod_blocking = {}; + mod_caps = {}; + mod_carboncopy = {}; + mod_client_state = {}; + mod_configure = {}; + mod_disco = {}; + mod_fail2ban = {}; + mod_last = {}; + mod_offline = { + access_max_user_messages = 5000; + }; + mod_ping = {}; + mod_privacy = {}; + mod_private = {}; + mod_pubsub = { + access_createnode = "pubsub_createnode"; + ignore_pep_from_offline = true; + last_item_cache = false; + plugins = [ + "flat" + "pep" + ]; + }; + mod_roster = {}; + mod_stream_mgmt = {}; + mod_time = {}; + mod_vcard = { + search = false; + }; + mod_vcard_xupdate = {}; + mod_version = {}; + }; + }; + }; + }; + secrets.host-secrets.procul = let secrets = config.fudo.secrets.files; in { @@ -157,7 +219,7 @@ in { enable = true; debug = true; - domain = domain; + domain = domain-name; hostname = "${host-fqdn}"; monitoring = false; mail-user = "mailuser"; @@ -167,17 +229,17 @@ in { dkim.signing = true; dovecot = { - ssl-certificate = acme-certificate "imap.${domain}"; - ssl-private-key = acme-private-key "imap.${domain}"; + ssl-certificate = acme-certificate "imap.${domain-name}"; + ssl-private-key = acme-private-key "imap.${domain-name}"; }; postfix = { - ssl-certificate = acme-certificate "smtp.${domain}"; - ssl-private-key = acme-private-key "smtp.${domain}"; + 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}" ]; + local-domains = [ host-fqdn "smtp.${domain-name}" ]; mail-directory = "/srv/mailserver/mail"; state-directory = "/srv/mailserver/state"; @@ -199,14 +261,13 @@ in { enable = true; ssl-certificate = (acme-certificate host-fqdn); ssl-private-key = (acme-private-key host-fqdn); - keytab = - config.fudo.secrets.host-secrets.procul.postgres-keytab.target-file; + keytab = secrets.postgres-keytab.target-file; local-networks = local-networks; users = { gituser = { password-file = - config.fudo.secrets.host-secrets.procul.gitea-database-password.target-file; + secrets.gitea-database-password.target-file; databases = { git = { access = "CONNECT"; @@ -232,7 +293,7 @@ in { database = { user = "gituser"; password-file = - config.fudo.secrets.host-secrets.procul.gitea-database-password.target-file; + secrets.gitea-database-password.target-file; hostname = "127.0.0.1"; name = "git"; }; @@ -244,7 +305,7 @@ in { acme = { enable = true; - admin-address = "admin@${domain}"; + admin-address = "admin@${domain-name}"; hostnames = [ "informis.land" "imap.informis.land" diff --git a/config/host-config/socrates.nix b/config/host-config/socrates.nix index 162e20d..a6893d2 100644 --- a/config/host-config/socrates.nix +++ b/config/host-config/socrates.nix @@ -17,7 +17,7 @@ in { interfaces.intif0 = { ipv4.addresses = [{ address = primary-ip; - prefixLength = 22; + prefixLength = 16; }]; }; }; diff --git a/config/host-config/spark.nix b/config/host-config/spark.nix index 1f78b85..b9881ac 100644 --- a/config/host-config/spark.nix +++ b/config/host-config/spark.nix @@ -1,19 +1,21 @@ { config, lib, pkgs, ... }: { - fudo.slynk.enable = true; + config = { + fudo.slynk.enable = true; - networking = { - interfaces = { - extif0 = { useDHCP = true; }; + networking = { + interfaces = { + extif0 = { useDHCP = true; }; + }; + }; + + i18n.inputMethod = { + enabled = "fcitx5"; + fcitx5.addons = with pkgs; [ + fcitx5-chinese-addons + fcitx5-rime + ]; }; }; - - i18n.inputMethod = { - enabled = "fcitx5"; - fcitx5.addons = with pkgs; [ - fcitx5-chinese-addons - fcitx5-rime - ]; - }; } diff --git a/config/host-config/zbox.nix b/config/host-config/zbox.nix index 2c4beb6..bfda7ac 100644 --- a/config/host-config/zbox.nix +++ b/config/host-config/zbox.nix @@ -1,22 +1,22 @@ { config, lib, pkgs, ... }: { - system.stateVersion = "20.09"; + config = { + fudo.slynk.enable = true; - # TODO: remove? - nixpkgs.config.permittedInsecurePackages = [ - "openssh-with-gssapi-8.4p1" # CVE-2021-28041 - ]; + environment.systemPackages = with pkgs; [ opencv-java ]; - fudo.slynk.enable = true; - - environment.systemPackages = with pkgs; [ opencv-java ]; - - networking = { - useDHCP = false; - interfaces = { - eno1.useDHCP = false; - intif0 = { useDHCP = true; }; + networking = { + useDHCP = false; + interfaces.intif0.useDHCP = true; + }; + + i18n.inputMethod = { + enabled = "fcitx5"; + fcitx5.addons = with pkgs; [ + fcitx5-chinese-addons + fcitx5-rime + ]; }; }; } diff --git a/config/hosts/france.nix b/config/hosts/france.nix index c0cf51a..d9ce538 100644 --- a/config/hosts/france.nix +++ b/config/hosts/france.nix @@ -1,20 +1,24 @@ { description = "Primary fudo.org server."; docker-server = true; - # ssh-fingerprints = [ - # "1 1 1b6d62dafae9ebc59169dfb4ef828582a5450d94" - # "1 2 079e7a57873542541095bf3d2f97b7350bb457d027b423a6fb56f7f6aa84ac80" - # "4 1 c95a198f504a589fc62893a95424b12f0b24732d" - # "4 2 3e7dad879d6cab7f7fb6769e156d7988d0c01281618d03b793834eea2f09bc96" - # ]; rp = "admin"; admin-email = "admin@fudo.org"; domain = "fudo.org"; site = "portage"; profile = "server"; - # ssh-pubkey = - # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1COad5NSK3mi66WK5uWf79NLMf5rk350kvJGsEdDmn"; arch = "x86_64-linux"; - # Just to stop this evaluating for now - nixos-system = false; + machine-id = "d33245603a854e48ba90002639e063f8"; + nixos-system = true; + master-key = { + public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQyRAJQpFaBanQKdu3SWCu0mjqSdF7WC1WNdKdQ1edQ"; + key-path = "/state/master-key/ed25519-key"; + }; + initrd-network = { + ip = "208.81.3.117"; + interface = "enp4s0f0"; + keypair = { + public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOAooY0j3xhs3PS5vFDXya1ljjo7fFXT98HDICVa3yBl"; + private-key-file = "/state/ssh/initrd/ssh_ed25519_key"; + }; + }; } diff --git a/config/hosts/nostromo.nix b/config/hosts/nostromo.nix index fb1cda0..29b23c3 100644 --- a/config/hosts/nostromo.nix +++ b/config/hosts/nostromo.nix @@ -22,4 +22,27 @@ key-path = "/state/master-key/key"; }; # initrd-ip = "10.0.5.10"; + encrypted-filesystems.sea-store = { + encrypted-device = "/dev/nostromo-store/locked"; + key-path = "/run/keys/sea-store"; + filesystem-type = "btrfs"; + options = [ "noatime" "nodiratime" "compress=zstd" "noexec" ]; + mountpoints = { + "/export/documents" = { + options = [ "subvol=@documents" ]; + group = "sea-documents"; + users = [ "niten" ]; + }; + "/export/downloads" = { + options = [ "subvol=@downloads" ]; + group = "sea-downloads"; + users = [ "niten" ]; + }; + "/export/projects" = { + options = [ "subvol=@projects" ]; + group = "sea-projects"; + users = [ "niten" ]; + }; + }; + }; } diff --git a/config/networks/fudo.org.nix b/config/networks/fudo.org.nix index 546a5c0..11fe827 100644 --- a/config/networks/fudo.org.nix +++ b/config/networks/fudo.org.nix @@ -1,5 +1,3 @@ -{ config, lib, ... }: - { mx = [ "mail.fudo.org" ]; diff --git a/config/site-config/seattle.nix b/config/site-config/seattle.nix index af7c287..8d66561 100644 --- a/config/site-config/seattle.nix +++ b/config/site-config/seattle.nix @@ -37,44 +37,55 @@ in { fsType = "nfs4"; options = [ "comment=systemd.automount" ]; }; + "/net/documents" = { - device = "sea-store.${local-domain}:/export/documents"; - fsType = "nfs"; - options = [ - "nfsvers=4.2" - "comment=systemd.automount" - "sec=krb5p" - # "noauto" ? - ]; + device = "sea-store.sea.fudo.org:/export/documents"; + fsType = "nfs4"; + options = [ "comment=systemd.automount" "sec=krb5p" ]; }; "/net/downloads" = { - device = "sea-store.${local-domain}:/export/downloads"; - fsType = "nfs"; - options = [ - "nfsvers=4.2" - "comment=systemd.automount" - "sec=krb5i" - # "noauto" ? - ]; + device = "sea-store.sea.fudo.org:/export/downloads"; + fsType = "nfs4"; + options = [ "comment=systemd.automount" "sec=krb5i" ]; + }; + "/net/projects" = { + device = "sea-store.sea.fudo.org:/export/projects"; + fsType = "nfs4"; + options = [ "comment=systemd.automount" "sec=krb5p" ]; }; }; - # systemd.mounts = [ - # { - # what = "sea-store.sea.fudo.org:/export/documents"; - # where = "/net/documents"; - # type = "nfs4"; - # options = "sec=krb5p"; - # description = "sea-store documents on encrypted filesysem."; - # } - # { - # what = "sea-store.sea.fudo.org:/export/downloads"; - # where = "/net/downloads"; - # type = "nfs4"; - # options = "sec=krb5i"; - # description = "sea-store downloads on encrypted filesysem."; - # } - # ]; + systemd = { + tmpfiles.rules = [ + "d /net/documents - root sea-documents - -" + "d /net/downloads - root sea-downloads - -" + "d /net/projects - root sea-projects - -" + ]; + + # mounts = [ + # { + # what = "sea-store.sea.fudo.org:/export/documents"; + # where = "/net/documents"; + # type = "nfs4"; + # options = "sec=krb5p"; + # description = "sea-store documents on encrypted filesysem."; + # } + # { + # what = "sea-store.sea.fudo.org:/export/downloads"; + # where = "/net/downloads"; + # type = "nfs4"; + # options = "sec=krb5i"; + # description = "sea-store downloads on encrypted filesysem."; + # } + # { + # what = "sea-store.sea.fudo.org:/export/projects"; + # where = "/net/projects"; + # type = "nfs4"; + # options = "sec=krb5p"; + # description = "sea-store projects on encrypted filesysem."; + # } + # ]; + }; services.printing = { enable = true; diff --git a/config/sites.nix b/config/sites.nix index c9cf998..73e1650 100644 --- a/config/sites.nix +++ b/config/sites.nix @@ -8,58 +8,34 @@ network = "10.0.0.0/16"; dynamic-network = "10.0.100.0/24"; timezone = "America/Los_Angeles"; - gateway-host = "nostromo"; 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=" ]; - build-servers = { - nostromo = { - max-jobs = 4; - speed-factor = 2; - }; - lambda = { - max-jobs = 4; - speed-factor = 2; - }; - }; - enable-distributed-builds = false; - keytab-path = "/state/secrets/kerberos"; - build-key-path = "/state/secrets/build-keys"; - # FIXME: good idea? - # network-mounts = { - # "/mnt/documents" = { - # device = "whitedwarf:/volume1/Documents"; - # fsType = "nfs4"; + # build-servers = { + # nostromo = { + # max-jobs = 4; + # speed-factor = 2; # }; - # "/mnt/downloads" = { - # device = "whitedwarf:/volume1/Downloads"; - # fsType = "nfs4"; - # }; - # "/mnt/music" = { - # device = "doraemon:/volume1/Music"; - # fsType = "nfs4"; - # }; - # "/mnt/video" = { - # device = "doraemon:/volume1/Video"; - # fsType = "nfs4"; - # }; - # "/mnt/cargo_video" = { - # device = "cargo:/volume1/video"; - # fsType = "nfs4"; - # }; - # "/mnt/photo" = { - # device = "cargo:/volume1/pictures"; - # fsType = "nfs4"; + # lambda = { + # max-jobs = 4; + # speed-factor = 2; # }; # }; + enable-distributed-builds = false; + mail-server = "mail.fudo.org"; }; portage = { gateway-v4 = "208.81.3.113"; network = "208.81.3.112/28"; - nameservers = [ "1.1.1.1" "208.81.7.14" "2606:4700:4700::1111" ]; + nameservers = [ "208.81.7.14" "1.1.1.1" ]; timezone = "America/Winnipeg"; + 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"; }; russell = { @@ -69,18 +45,19 @@ dynamic-network = "10.0.1.0/24"; timezone = "America/Winnipeg"; gateway-host = "clunk"; + mail-server = "mail.fudo.org"; }; joes-datacenter-0 = { gateway-v4 = "172.86.179.17"; + network = "172.86.179.17/29"; nameservers = [ "1.1.1.1" "2606:4700:4700::1111" ]; timezone = "America/Winnipeg"; 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=" ]; - keytab-path = "/state/secrets/kerberos"; - build-key-path = "/state/secrets/build-keys"; + mail-server = "mail.informis.land"; }; }; } diff --git a/lib/default.nix b/lib/default.nix index 7bd9165..c555faf 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -6,6 +6,7 @@ with lib; { ./instance.nix + ./fudo/acme-certs.nix ./fudo/acme-for-hostname.nix ./fudo/authentication.nix ./fudo/backplane @@ -23,6 +24,7 @@ with lib; { ./fudo/host-filesystems.nix ./fudo/initrd-network.nix ./fudo/ipfs.nix + ./fudo/jabber.nix ./fudo/kdc.nix ./fudo/ldap.nix ./fudo/local-network.nix diff --git a/lib/fudo-lib.nix b/lib/fudo-lib.nix index 040b10a..30a8e8d 100644 --- a/lib/fudo-lib.nix +++ b/lib/fudo-lib.nix @@ -3,13 +3,14 @@ let ip = import ./ip.nix { inherit lib; }; dns = import ./dns.nix { inherit lib; }; + passwd = import ./passwd.nix { inherit lib; }; in { lib.overlays = [ (final: prev: prev.lib // { fudo = { - inherit ip dns; + inherit ip dns passwd; }; }) ]; diff --git a/lib/fudo/acme-certs.nix b/lib/fudo/acme-certs.nix new file mode 100644 index 0000000..9c5e644 --- /dev/null +++ b/lib/fudo/acme-certs.nix @@ -0,0 +1,119 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + localCopyOpts = { copy, ... }: let + in { + options = with types; { + inherit domain; + 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 = { domain, ... }: { + options = with types; { + email = mkOption { + type = str; + description = "Domain administrator email."; + default = "admin@${domain}"; + }; + + extra-domains = mkOption { + type = listOf str; + description = "List of domains to add to this certificate."; + default = []; + }; + }; + }; + + head-or-null = lst: if (lst == []) then null else head lst; + rm-service-ext = filename: + head-or-null (builtins.match "^(.+)\.service$" filename); + + concatMapAttrs = f: attrs: + foldr (a: b: a // b) {} (mapAttrsToList f attrs); + + hostname = config.instance.hostname; + cfg = config.fudo.acme; + localDomains = if (hasAttr hostname cfg.host-domains) then + cfg.host-domains.${hostname} else {}; + + optionalStringOr = str: default: + if cond then str else default; + +in { + options.fudo.acme = with types; { + host-domains = mkOption { + type = attrsOf (attrsOf (submodule domainOpts)); + description = "Map of host to domains to domain options."; + default = { }; + }; + }; + + config = { + security.acme.certs = mapAttrs (domain: domainOpts: { + email = domainOpts.email; + extraDomainNames = domainOpts.extra-domains; + }) localDomains; + + systemd = { + tmpfiles.rules = let + copies = concatMapAttrs (domain: domainOpts: + domainOpts.local-copies) localDomains; + copy-paths = mapAttrsToList (copy: copyOpts: + "D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -") + copies; + in copy-paths; + + # TODO: Make this a Fudo service? + services = concatMapAttrs (domain: domainOpts: + mapAttrs' (copy: copyOpts: let + source = config.security.acme.certs.${domain}.directory; + target = copyOpts.path; + install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" '' + for cert in cert chain fullchain full key; do + cp ${source}/$cert.pem ${target}/$cert.pem + chmod 0440 ${source}/$cert.pem + done + ''; + remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${site}-certs.sh" '' + for cert in cert chain fullchain full key; do + rm -rf ${target}/$cert.pem + done + ''; + in nameValuePair + (rm-service-ext copyOpts.service) { + description = "Copy ${domain} ACME certs for ${copy}."; + after = [ "acme-${domain}.service" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = install-certs; + ExecStop = remove-certs; + RemainAfterExit = true; + StandardOutput = "journal"; + }; + }) domainOpts.local-copies) localDomains; + }; + }; +} diff --git a/lib/fudo/host-filesystems.nix b/lib/fudo/host-filesystems.nix index 6a2810d..d460d44 100644 --- a/lib/fudo/host-filesystems.nix +++ b/lib/fudo/host-filesystems.nix @@ -4,7 +4,7 @@ with lib; let hostname = config.instance.hostname; host-filesystems = config.fudo.hosts.${hostname}.encrypted-filesystems; - + optionalOrDefault = str: default: if (str != null) then str else default; filesystemsToMountpointLists = mapAttrsToList @@ -12,21 +12,24 @@ let concatMapAttrs = f: as: concatMap (i: i) (mapAttrsToList f as); + concatMapAttrsToList = f: attrs: + concatMap (i: i) (mapAttrsToList f attrs); + in { config = { users.groups = let - mountpointToGroups = mp: mpOpts: - optional (mpOpts.group != null) - (nameValuePair mpOpts.group { - members = mpOpts.users; - }); - mountpointListToGroups = - concatMapAttrs mountpointToGroups; - mountpointListsToGroups = - concatMap mountpointListToGroups; + site-name = config.instance.local-site; + site-hosts = filterAttrs + (hostname: hostOpts: hostOpts.site == site-name) + config.fudo.hosts; + site-mountpoints = concatMapAttrsToList + (host: hostOpts: concatMapAttrsToList + (fs: fsOpts: attrValues fsOpts.mountpoints) + hostOpts.encrypted-filesystems) + site-hosts; in listToAttrs - (mountpointListsToGroups - (filesystemsToMountpointLists host-filesystems)); + (map (mp: nameValuePair mp.group { members = mp.users; }) + site-mountpoints); systemd = { # Ensure the mountpoints exist diff --git a/lib/fudo/hosts.nix b/lib/fudo/hosts.nix index f642594..58dce8b 100644 --- a/lib/fudo/hosts.nix +++ b/lib/fudo/hosts.nix @@ -42,36 +42,41 @@ in { (substring 0 8 host-cfg.machine-id); }; - # NixOS generates a stupid hosts file, just force it - environment.etc = { - hosts = let - host-entries = mapAttrsToList - (ip: hostnames: "${ip} ${concatStringsSep " " hostnames}") - config.fudo.system.hostfile-entries; - in mkForce { - text = '' + environment = { + etc = { + # NixOS generates a stupid hosts file, just force it + hosts = let + host-entries = mapAttrsToList + (ip: hostnames: "${ip} ${concatStringsSep " " hostnames}") + config.fudo.system.hostfile-entries; + in mkForce { + text = '' 127.0.0.1 ${hostname}.${domain-name} ${hostname} localhost 127.0.0.2 ${hostname} localhost ::1 ${hostname}.${domain-name} ${hostname} localhost ${concatStringsSep "\n" host-entries} ''; - user = "root"; - group = "root"; - mode = "0444"; - }; + user = "root"; + group = "root"; + mode = "0444"; + }; - machine-id = mkIf (host-cfg.machine-id != null) { - text = host-cfg.machine-id; - user = "root"; - group = "root"; - mode = "0444"; - }; + machine-id = mkIf (host-cfg.machine-id != null) { + text = host-cfg.machine-id; + user = "root"; + group = "root"; + mode = "0444"; + }; - current-system-packages.text = with builtins; let - packages = map (p: "${p.name}") - config.environment.systemPackages; - sorted-unique = sort lessThan (unique packages); - in concatStringsSep "\n" sorted-unique; + current-system-packages.text = with builtins; let + packages = map (p: "${p.name}") + config.environment.systemPackages; + sorted-unique = sort lessThan (unique packages); + in concatStringsSep "\n" sorted-unique; + }; + + systemPackages = with pkgs; + mkIf (host-cfg.docker-server) [ docker nix-prefetch-docker ]; }; time.timeZone = site.timezone; @@ -80,9 +85,6 @@ in { services.cron.mailto = domain.admin-email; - environment.systemPackages = with pkgs; - mkIf (host-cfg.docker-server) [ docker nix-prefetch-docker ]; - virtualisation.docker = mkIf (host-cfg.docker-server) { enable = true; enableOnBoot = true; @@ -136,5 +138,13 @@ in { }; 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; + }; }; } diff --git a/lib/fudo/include/rainloop.nix b/lib/fudo/include/rainloop.nix index 4ab985a..3172e6e 100644 --- a/lib/fudo/include/rainloop.nix +++ b/lib/fudo/include/rainloop.nix @@ -1,14 +1,14 @@ lib: site: config: version: with lib; let - db-config = if (config.database != null) then + db-config = optionalString (config.database != null) '' type = "${config.database.type}" pdo_dsn = "${config.database.type}:host=${config.database.hostname};port=${toString config.database.port};dbname=${config.database.name}" pdo_user = "${config.database.user}" pdo_password = "${fileContents config.database.password-file}" - '' - else ""; + ''; + in '' [webmail] title = "${config.title}" diff --git a/lib/fudo/jabber.nix b/lib/fudo/jabber.nix new file mode 100644 index 0000000..e328221 --- /dev/null +++ b/lib/fudo/jabber.nix @@ -0,0 +1,194 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + + siteOpts = { ... }: with types; { + options = { + enableACME = mkOption { + type = bool; + description = "Use ACME to get SSL certificates for this site."; + default = true; + }; + + site-config = mkOption { + type = attrs; + description = "Site-specific configuration."; + }; + }; + }; + + site-copy = site: "ejabberd-${site}"; + + concatMapAttrs = f: attrs: + foldr (a: b: a // b) {} (mapAttrs f attrs); + + concatMapAttrsToList = f: attr: + attrValues (concatMapAttrs f attr); + + host-domains = config.fudo.acme.host-domains.${hostname}; + + siteCerts = site: let + certPath = config.fudo.acme.local-copies.${site-copy site}.path; + in [ + "${certPath}/fullchain.pem" + "${certPath}/privkey.pem" + "${certPath}/chain.pem" + ]; + + siteCertService = site: + config.fudo.acme.local-copies.${site-copy site}.service; + + config-file-template = let + jabber-config = { + loglevel = cfg.log-level; + + access_rules = { + c2s = { allow = "all"; }; + announce = { allow = "admin"; }; + configure = { allow = "admin"; }; + pubsub_createnode = { allow = "local"; }; + }; + + acl = { + admin = { + user = concatMap + (admin: map (site: "${admin}@${site}") + (attrNames cfg.sites)) + cfg.admins; + }; + }; + + hosts = attrNames cfg.sites; + + listen = [{ + port = cfg.port; + module = "ejabberd_c2s"; + ip = cfg.listen-ip; + starttls = true; + starttls_required = true; + }]; + + certfiles = concatMapAttrsToList + (site: siteOpts: + if (siteOpts.enableACME) then + (siteCerts site) + else []) + cfg.sites; + + host_config = + mapAttrs (site: siteOpts: siteOpts.site-config) + cfg.sites; + }; + + config-file = builtins.toJSON jabber-config; + in pkgs.writeText "ejabberd.config.yml.template" config-file; + + enter-secrets = template: secrets: target: let + secret-readers = concatStringsSep "\n" + (mapAttrsToList + (secret: file: "${secret}=$(cat ${file})") + secrets); + secret-swappers = map + (secret: "sed s/${secret}/\$${secret}/g") + (attrNames secrets); + swapper = concatStringsSep " | " secret-swappers; + in pkgs.writeShellScript "ejabberd-generate-config.sh" '' + cat ${template} | ${swapper} > ${target} + chown ${cfg.user}:${cfg.group} ${target} + ''; + + cfg = config.fudo.jabber; + +in { + options.fudo.jabber = with types; { + enable = mkEnableOption "Enable ejabberd server."; + + port = mkOption { + type = port; + description = "Port on which to listen for Jabber connections."; + default = 5222; + }; + + user = mkOption { + type = str; + description = "User as which to run the ejabberd server."; + default = "ejabberd"; + }; + + group = mkOption { + type = str; + description = "Group as which to run the ejabberd server."; + default = "ejabberd"; + }; + + admins = mkOption { + type = str; + description = "List of admin users for the server."; + default = []; + }; + + sites = mkOption { + type = attrsOf (submodule siteOpts); + description = "List of sites on which to listen for Jabber connections."; + }; + + secret-files = mkOption { + type = attrsOf str; + description = "Map of secret-name to file. File contents will be subbed for the name in the config."; + default = {}; + }; + + config-file = mkOption { + type = str; + description = "Location at which to generate the configuration file."; + default = "/var/run/ejabberd/ejabberd.yaml"; + }; + }; + + config = mkIf cfg.enable { + users = { + users.${cfg.user} = { + isSystemUser = true; + }; + + groups.${cfg.group} = { + members = [ cfg.user ]; + }; + }; + + fudo.acme.local-copies = mapAttrs' (site: siteCfg: + nameValuePair (site-copy site) + mkif siteCfg.enableACME { + domain = site; + user = cfg.user; + group = cfg.group; + }) cfg.sites; + + systemd = { + tmpfiles.rules = [ + "D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -" + ]; + + services.ejabberd = let + config-generator = enter-secrets config-file-template cfg.secret-files cfg.config-file; + in { + wants = map (site: siteCertService site) (attrNames cfg.sites); + environment = cfg.secret-files; + serviceConfig = { + ExecStartPre = mkAfter "${config-generator}"; + }; + }; + }; + + services.ejabberd = { + enable = true; + + user = cfg.user; + group = cfg.group; + + configFile = cfg.config-file; + }; + }; +} diff --git a/lib/fudo/kdc.nix b/lib/fudo/kdc.nix index 2dc86ec..6b24130 100644 --- a/lib/fudo/kdc.nix +++ b/lib/fudo/kdc.nix @@ -63,8 +63,8 @@ let } [logging] - kdc = FILE:/var/kerberos/kerberos.log - default = FILE:/var/kerberos/kerberos.log + kdc = FILE:${cfg.state-directory}/kerberos.log + default = FILE:${cfg.state-directory}/kerberos.log ''; aclEntry = { principal, ... }: { @@ -111,7 +111,9 @@ let in { - options.fudo.auth.kdc = with types; { + options.fudo.auth.kdc = with types; let + default-state-dir = "/var/kerberos"; + in { enable = mkEnableOption "Fudo KDC"; realm = mkOption { @@ -150,31 +152,31 @@ in { state-directory = mkOption { type = str; description = "Path at which to store kerberos database."; - default = "/var/kerberos"; + default = default-state-dir; }; master-key-file = mkOption { type = str; description = "File containing the master key for the realm."; - default = "/var/kerberos/master.key"; + default = "${default-state-dir}/master.key"; }; primary-keytab = mkOption { type = str; description = "Location of keytab for kadmind."; - default = "/var/kerberos/host.keytab"; + default = "${default-state-dir}/host.keytab"; }; kadmin-keytab = mkOption { type = str; description = "Location of keytab for kadmind."; - default = "/var/kerberos/kadmind.keytab"; + default = "${default-state-dir}/kadmind.keytab"; }; kpasswdd-keytab = mkOption { type = str; description = "Location of keytab for kpasswdd."; - default = "/var/kerberos/kpasswdd.keytab"; + default = "${default-state-dir}/kpasswdd.keytab"; }; kdc-internal-port = mkOption { @@ -184,13 +186,6 @@ in { default = 4088; }; - # k5login-directory = mkOption { - # type = str; - # description = - # "Directory in which k5login files are stored for local users (equivalent to ~/.k5login)."; - # default = "/var/kerberos/k5login"; - # }; - max-ticket-lifetime = mkOption { type = str; description = "Maximum lifetime of a single ticket in this realm."; @@ -208,7 +203,7 @@ in { users = { users.${cfg.user} = { isSystemUser = true; - home = "/var/kerberos"; + home = cfg.state-directory; group = cfg.group; }; @@ -224,7 +219,7 @@ in { }; realms = { ${cfg.realm} = { enable-http = false; }; }; extraConfig = '' - default = FILE:/var/kerberos/kerberos.log + default = FILE:${cfg.state-directory}/kerberos.log ''; }; diff --git a/lib/fudo/ldap.nix b/lib/fudo/ldap.nix index 2097752..ec0b57b 100644 --- a/lib/fudo/ldap.nix +++ b/lib/fudo/ldap.nix @@ -169,41 +169,41 @@ let in { - options = { + options = with types; { fudo = { auth = { ldap-server = { enable = mkEnableOption "Fudo Authentication"; kerberos-host = mkOption { - type = types.str; + type = str; description = '' The name of the host to use for Kerberos authentication. ''; }; kerberos-keytab = mkOption { - type = types.str; + type = str; description = '' The path to a keytab for the LDAP server, containing a principal for ldap/. ''; }; - sslCert = mkOption { - type = types.str; + ssl-certificate = mkOption { + type = str; description = '' The path to the SSL certificate to use for the server. ''; }; - sslKey = mkOption { - type = types.str; + ssl-private-key = mkOption { + type = str; description = '' The path to the SSL key to use for the server. ''; }; - sslCACert = mkOption { + ssl-ca-certificate = mkOption { type = with types; nullOr str; description = '' The path to the SSL CA cert used to sign the certificate. @@ -212,14 +212,14 @@ in { }; organization = mkOption { - type = types.str; + type = str; description = '' The name to use for the organization. ''; }; base = mkOption { - type = types.str; + type = str; description = '' The base dn of the LDAP server (eg. "dc=fudo,dc=org"). ''; @@ -227,24 +227,23 @@ in { rootpw-file = mkOption { default = ""; - type = types.str; + type = str; description = '' The path to a file containing the root password for this database. ''; }; listen-uris = mkOption { - default = [ ]; - type = with types; listOf str; + type = listOf str; description = '' A list of URIs on which the ldap server should listen. ''; example = [ "ldap://auth.fudo.org" "ldaps://auth.fudo.org" ]; + default = [ ]; }; users = mkOption { - default = { }; - type = with types; attrsOf (submodule ldapUserOpts); + type = attrsOf (submodule ldapUserOpts); example = { tester = { uid = 10099; @@ -255,11 +254,12 @@ in { description = '' Users to be added to the Fudo LDAP database. ''; + default = { }; }; groups = mkOption { default = { }; - type = with types; attrsOf (submodule ldapGroupOpts); + type = attrsOf (submodule ldapGroupOpts); example = { admin = { gid = 1099; @@ -273,7 +273,7 @@ in { system-users = mkOption { default = { }; - type = with types; attrsOf (submodule ldapSystemUserOpts); + type = attrsOf (submodule ldapSystemUserOpts); example = { replicator = { description = "System user for database sync"; @@ -346,10 +346,10 @@ in { extraConfig = '' - TLSCertificateFile ${cfg.sslCert} - TLSCertificateKeyFile ${cfg.sslKey} - ${optionalString (cfg.sslCACert != null) - "TLSCACertificateFile ${cfg.sslCACert}"} + TLSCertificateFile ${cfg.ssl-certificate} + TLSCertificateKeyFile ${cfg.ssl-private-key} + ${optionalString (cfg.ssl-ca-certificate != null) + "TLSCACertificateFile ${cfg.ssl-ca-certificate}"} authz-regexp "^uid=auth/([^.]+)\.fudo\.org,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org" authz-regexp "^uid=[^,/]+/root,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=admin,dc=fudo,dc=org" diff --git a/lib/fudo/secrets.nix b/lib/fudo/secrets.nix index 0ca12a9..d75effd 100644 --- a/lib/fudo/secrets.nix +++ b/lib/fudo/secrets.nix @@ -52,11 +52,12 @@ let path = [ pkgs.age ]; }; - secretOpts = { ... }: { + secretOpts = { name, ... }: { options = with types; { source-file = mkOption { type = path; # CAREFUL: this will copy the file to nixstore...keep on deploy host - description = "File from which to load the secret."; + description = "File from which to load the secret. If unspecified, a random new password will be generated."; + default = "${generate-secret name}/passwd"; }; target-file = mkOption { @@ -86,7 +87,26 @@ let nix-build-users = let usernames = attrNames config.users.users; in filter (user: (builtins.match "^nixbld[0-9]{1,2}$" user) != null) - usernames; + usernames; + + generate-secret = name: pkgs.stdenv.mkDerivation { + name = "${name}-generated-passwd"; + + phases = [ "installPhase" ]; + + buildInputs = with pkgs; [ pwgen ]; + + buildPhase = '' + echo "${name}-${config.instance.build-timestamp}" >> file.txt + pwgen --secure --symbols --num-passwords=1 --sha1=file.txt 40 > passwd + rm -f file.txt + ''; + + installPhase = '' + mkdir $out + mv passwd $out/passwd + ''; + }; in { options.fudo.secrets = with types; { @@ -139,7 +159,9 @@ in { config = mkIf cfg.enable { users.groups = { - ${cfg.secret-group} = { members = cfg.secret-users ++ nix-build-users; }; + ${cfg.secret-group} = { + members = cfg.secret-users ++ nix-build-users; + }; }; systemd = let diff --git a/lib/fudo/sites.nix b/lib/fudo/sites.nix index d9bc377..c5ad080 100644 --- a/lib/fudo/sites.nix +++ b/lib/fudo/sites.nix @@ -40,12 +40,6 @@ let default = null; }; - gateway-host = mkOption { - type = nullOr str; - description = "Identity of the host to act as a gateway."; - default = null; - }; - local-groups = mkOption { type = listOf str; description = "List of groups which should exist at this site."; @@ -135,24 +129,9 @@ let default = [ ]; }; - keytab-path = mkOption { - type = nullOr str; - description = '' - Directory containing site keytabs (files named $hostname.keytab). - - Should exist only on build host. - ''; - default = null; - }; - - build-key-path = mkOption { - type = nullOr str; - description = '' - Directory containing host build keys (files named $hostname.key). - - Should exist only on build host. - ''; - default = null; + mail-server = mkOption { + type = str; + description = "Hostname of the mail server to use for this site."; }; }; }; diff --git a/lib/fudo/webmail.nix b/lib/fudo/webmail.nix index 3ecc59a..5ce9a5b 100644 --- a/lib/fudo/webmail.nix +++ b/lib/fudo/webmail.nix @@ -2,14 +2,14 @@ with lib; let + hostname = config.instance.hostname; + cfg = config.fudo.webmail; - inherit (lib.strings) concatStringsSep; + webmail-user = cfg.user; + webmail-group = cfg.group; - webmail-user = "webmail-php"; - webmail-group = "webmail-php"; - - base-data-path = "/var/rainloop"; + base-data-path = "/var/run/rainloop"; fastcgi-conf = builtins.toFile "fastcgi.conf" '' fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; @@ -54,87 +54,81 @@ let ''; })) cfg.sites; - siteOpts = { site-host, ... }: { + siteOpts = { site-host, ... }: with types; { options = { title = mkOption { - type = types.str; + type = str; description = "Webmail site title"; example = "My Webmail"; }; debug = mkOption { - type = types.bool; + type = bool; description = "Turn debug logs on."; default = false; }; mail-server = mkOption { - type = types.str; + type = str; description = "Mail server from which to send & recieve email."; default = "mail.fudo.org"; }; favicon = mkOption { - type = types.str; + type = str; description = "URL of the site favicon"; example = "https://www.somepage.com/fav.ico"; }; messages-per-page = mkOption { - type = types.int; + type = int; description = "Default number of messages to show per page"; default = 30; }; max-upload-size = mkOption { - type = types.int; + type = int; description = "Size limit in MB for uploaded files"; default = 30; }; theme = mkOption { - type = types.str; + type = str; description = "Default theme to use for this webmail site."; default = "Default"; }; - # Ideally, don't even allow admin logins, since they'll just add state that can be clobbered - # admin-password = mkOption { - # type = types.str; - # description = "Password to use for the admin user"; - # }; - domain = mkOption { - type = types.str; + type = str; description = "Domain for which the server acts as webmail server"; }; edit-mode = mkOption { - type = types.enum [ "Plain" "Html" "PlainForced" "HtmlForced" ]; + type = enum [ "Plain" "Html" "PlainForced" "HtmlForced" ]; description = "Default text editing mode for email"; default = "Html"; }; layout-mode = mkOption { - type = types.enum [ "side" "bottom" ]; + type = enum [ "side" "bottom" ]; description = "Layout mode to use for email preview."; default = "side"; }; enable-threading = mkOption { - type = types.bool; + type = bool; description = "Whether to enable threading for email."; default = true; }; enable-mobile = mkOption { - type = types.bool; + type = bool; description = "Whether to enable a mobile site view."; default = true; }; database = mkOption { - type = with types; nullOr (submodule databaseOpts); + type = nullOr (submodule databaseOpts); description = "Database configuration for storing contact data."; example = { name = "my_db"; @@ -146,58 +140,63 @@ let }; admin-email = mkOption { - type = types.str; + type = str; description = "Email of administrator of this site."; default = "admin@fudo.org"; }; }; }; - databaseOpts = { ... }: { + databaseOpts = { ... }: with types; { options = { type = mkOption { - type = types.enum [ "pgsql" "mysql" ]; + type = enum [ "pgsql" "mysql" ]; description = "Driver to use when connecting to the database."; default = "pgsql"; }; hostname = mkOption { - type = types.str; + type = str; description = "Name of host running the database."; example = "my-db.domain.com"; }; port = mkOption { - type = types.int; + type = int; description = "Port on which the database server is listening."; default = 5432; }; name = mkOption { - type = types.str; + type = str; description = "Name of the database containing contact info. must have access."; - default = "rainloop_contacts"; + default = "rainloop_webmail"; }; user = mkOption { - type = types.str; + type = str; description = "User as which to connect to the database."; + default = "webmail"; }; password-file = mkOption { - type = types.str; - description = "Password to use when connecting to the database."; + type = nullOr str; + description = '' + Password to use when connecting to the database. + + If unset, a random password will be generated. + ''; }; }; }; in { - options.fudo.webmail = { + options.fudo.webmail = with types; { enable = mkEnableOption "Enable a RainLoop webmail server."; sites = mkOption { - type = with types; (attrsOf (submodule siteOpts)); + type = attrsOf (submodule siteOpts); description = "A map of webmail sites to site configurations."; example = { "webmail.domain.com" = { @@ -208,6 +207,18 @@ in { }; }; }; + + user = mkOption { + type = str; + description = "User as which webmail will run."; + default = "webmail-php"; + }; + + group = mkOption { + type = str; + description = "Group as which webmail will run."; + default = "webmail-php"; + }; }; config = mkIf cfg.enable { @@ -226,13 +237,12 @@ in { }; }; - security.acme.certs = mapAttrs' - (site: site-cfg: nameValuePair site { email = site-cfg.admin-email; }) + security.acme.certs = mapAttrs + (site: site-cfg: { email = site-cfg.admin-email; }) cfg.sites; services = { phpfpm = { - pools.webmail = { settings = { "pm" = "dynamic"; @@ -285,43 +295,63 @@ in { }; }; + fudo.secrets.host-secrets.${hostname} = concatMapAttrs + (site: site-cfg: let + + site-config-file = builtins.toFile "${site}-rainloop.cfg" + (import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version); + + domain-cfg-file = builtins.toFile "${site}-domain.cfg" '' + imap_host = "${site-cfg.mail-server}" + imap_port = 143 + imap_secure = "TLS" + imap_short_login = On + sieve_use = Off + sieve_allow_raw = Off + sieve_host = "" + sieve_port = 4190 + sieve_secure = "None" + smtp_host = "${site-cfg.mail-server}" + smtp_port = 587 + smtp_secure = "TLS" + smtp_short_login = On + smtp_auth = On + smtp_php_mail = Off + white_list = "" + ''; + in { + "${site}-site-config" = { + source-file = site-config-file; + target-file = "/var/run/webmail/rainloop/site-${site}-rainloop.cfg"; + user = cfg.user; + }; + + "${site}-domain-config" = { + source-file = domain-config-file; + target-file = "/var/run/webmail/rainloop/domain-${site}-rainloop.cfg"; + user = cfg.user; + }; + }) cfg.sites; + + # TODO: make this a fudo service systemd.services = { webmail-init = let link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg: let - cfg-file = builtins.toFile "${site}-rainloop.cfg" - (import ./include/rainloop.nix lib site site-cfg - site-packages.${site}.version); - domain-cfg = builtins.toFile "${site}-domain.cfg" '' - imap_host = "${site-cfg.mail-server}" - imap_port = 143 - imap_secure = "TLS" - imap_short_login = On - sieve_use = Off - sieve_allow_raw = Off - sieve_host = "" - sieve_port = 4190 - sieve_secure = "None" - smtp_host = "${site-cfg.mail-server}" - smtp_port = 587 - smtp_secure = "TLS" - smtp_short_login = On - smtp_auth = On - smtp_php_mail = Off - white_list = "" - ''; + 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; in '' ${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/configs ${pkgs.coreutils}/bin/cp ${cfg-file} ${base-data-path}/${site}/_data_/_default_/configs/application.ini ${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/domains/ - ${pkgs.coreutils}/bin/cp ${domain-cfg} ${base-data-path}/${site}/_data_/_default_/domains/${site-cfg.domain}.ini + ${pkgs.coreutils}/bin/cp ${domain-cfg-file} ${base-data-path}/${site}/_data_/_default_/domains/${site-cfg.domain}.ini '') cfg.sites); scriptPkg = (pkgs.writeScriptBin "webmail-init.sh" '' #!${pkgs.bash}/bin/bash -e ${link-configs} ${pkgs.coreutils}/bin/chown -R ${webmail-user}:${webmail-group} ${base-data-path} - ${pkgs.coreutils}/bin/chmod -R ug+w ${base-data-path} + ${pkgs.coreutils}/bin/chmod -R u+w ${base-data-path} ''); in { requiredBy = [ "nginx.service" ]; diff --git a/lib/instance.nix b/lib/instance.nix index 93eba43..6c3155a 100644 --- a/lib/instance.nix +++ b/lib/instance.nix @@ -80,6 +80,12 @@ in { local-hosts = filterAttrs (host: hostOpts: hostOpts.site == local-site) config.fudo.hosts; + + local-networks = + host.local-networks // + config.fudo.domains.${local-domain}.local-networks // + config.fudo.sites.${local-site}.local-networks; + in { instance = { local-domain = local-domain; diff --git a/lib/passwd.nix b/lib/passwd.nix new file mode 100644 index 0000000..1efb666 --- /dev/null +++ b/lib/passwd.nix @@ -0,0 +1,48 @@ +{ lib, ... }: + +with lib; +let + hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation { + name = "${name}-ldap-passwd"; + + phases = [ "buildPhase" "installPhase" ]; + + buildInputs = with pkgs; [ openldap ]; + + buildPhase = '' + slappasswd -T ${passwd-file} > ldap-passwd + ''; + + installPhase = '' + mkdir $out + mv ldap-passwd $out + ''; + }; + + hash-ldap-passwd = name: passwd-file: let + passwd-pkgs = hash-ldap-passwd-pkg name passwd-file; + in builtins.readFile "${passwd-pkgs}/ldap-passwd"; + + generate-random-passwd = name: length: pkgs.stdenv.mkDerivation { + name = "${name}-random-passwd"; + + phases = [ "buildPhase" "installPhase" ]; + + buildInputs = with pkgs; [ pwgen ]; + + buildPhase = '' + pwgen --symbols --num-passwords=1 ${length} > passwd + ''; + + installPhase = '' + mkdir $out + mv passwd $out + ''; + }; + +in { + hash-ldap-passwd = hash-ldap-passwd; + + random-passwd-file = name: length: + toPath "${generate-random-passwd name length}/passwd"; +}