diff --git a/config/default.nix b/config/default.nix index 030ced4..e39b02f 100644 --- a/config/default.nix +++ b/config/default.nix @@ -8,6 +8,7 @@ ./groups.nix ./hosts.nix ./networks.nix + ./profile.nix ./sites.nix ./users.nix ./wireless-networks.nix diff --git a/config/hardware/france.nix b/config/hardware/france.nix index db0d7bb..320db4d 100644 --- a/config/hardware/france.nix +++ b/config/hardware/france.nix @@ -49,7 +49,7 @@ hardware.bluetooth.enable = false; - network = { + networking = { macvlans = { intif0 = { interface = "enp4s0f1"; diff --git a/config/host-config/france.nix b/config/host-config/france.nix index 4623d7a..1054e35 100644 --- a/config/host-config/france.nix +++ b/config/host-config/france.nix @@ -10,10 +10,17 @@ let host-fqdn = "${hostname}.${domain-name}"; mail-hostname = "mail.fudo.org"; - france-secrets = config.fudo.secrets.host-secrets.france; + secrets = config.fudo.secrets.host-secrets.france; + secret-files = config.fudo.secrets.files; - acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem"; - acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem"; + letsencrypt-full-chain = name: chain: pkgs.stdenv.mkDerivation { + name = "${name}-letsencrypt-full-chain.pem"; + phases = "installPhase"; + installPhase = '' + cat ${chain} > $out + cat ${pkgs.letsencrypt-ca}/ca.pem >> $out + ''; + }; in { imports = let @@ -28,9 +35,66 @@ in { in nix-files ./france; config = { + security.acme.email = "admin@fudo.org"; + fudo = { hosts.france.external-interfaces = [ "extif0" ]; + acme.host-domains.france."france.fudo.org" = { + email = "admin@fudo.org"; + local-copies = { + postgres = { + user = config.services.postgresql.user; + }; + openldap = { + user = config.services.openldap.user; + }; + }; + }; + + secrets.host-secrets.${hostname} = let + ldap-user = config.services.openldap.user; + ldap-group = config.services.openldap.group; + 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 = { + source-file = secret-files.service-keytabs.france.ldap; + target-file = "/run/openldap/ldap.keytab"; + user = ldap-user; + group = ldap-group; + }; + ldap-root-passwd = { + source-file = passwd.random-passwd-file; + target-file = "/run/openldap/root.passwd"; + user = ldap-user; + group = ldap-group; + }; + postgres-keytab = { + source-file = secret-files.service-keytabs.france.postgres; + target-file = "/run/postgres/postgres.keytab"; + user = config.services.postgresql.user; + }; + }; + client.dns = { enable = true; ipv4 = true; @@ -40,16 +104,53 @@ in { }; france = { + ldap = let + cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.openldap; + chain = "${letsencrypt-full-chain "openldap-france" cert-copy.chain}"; + in { + ssl-certificate = cert-copy.certificate; + ssl-private-key = cert-copy.private-key; + ssl-ca-certificate = chain; + keytab = secrets.ldap-keytab.target-file; + root-password-file = secrets.ldap-root-passwd.target-file; + }; + + kdc = { + state-directory = "/state/kerberos"; + master-key-file = ""; + listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; + }; + + jabber = { + ldap-servers = [ "france.fudo.org" ]; + listen-ips = [ primary-ip ]; + }; + + backplane = { + host-passwd-files = let + hosts = attrNames config.fudo.hosts; + in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file) + config.fudo.hosts; + service-passwd-files = genAttrs [ "dns" ] + (service-name: + lib.fudo.passwd.stablerandom-passwd-file + "${service-name}-service-backplane-passwd" + "${service-name}-service-backplane-passwd-${config.instance.build-seed}"); + }; + + backplane-server = { + listen-ips = [ primary-ip ]; + }; + mail = { - mail-directory = "/state/mail-server/mail"; - state-directory = "/state/mail-server/var"; + mail-directory = "/srv/mail/mailboxes"; + state-directory = "/srv/mail/var"; ldap-server-urls = [ "ldap://france.fudo.org" ]; }; webmail = { - # TODO: this is not using the database! mail-server = mail-hostname; database.hostname = "localhost"; }; @@ -58,6 +159,15 @@ in { repository-directory = "/state/gitea/repo"; state-directory = "/state/gitea/state"; ssh.listen-ip = git-server-ip; + database-host = "localhost"; + }; + + postgresql = let + cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.postgres; + in { + keytab = secrets.postgres-keytab.target-file; + ssl-certificate = cert-copy.certificate; + ssl-private-key = cert-copy.private-key; }; }; @@ -71,23 +181,27 @@ in { }; networking = { - intif0 = { - ipv4.addresses = [{ - address = "192.168.11.1"; - prefixLength = 24; - }]; - }; - extif0 = { - ipv4.addresses = [ - { - address = primary-ip; - prefixLength = 28; - } - { - address = git-server-ip; - prefixLength = 32; - } - ]; + useDHCP = false; + + interfaces = { + intif0 = { + ipv4.addresses = [{ + address = "192.168.11.1"; + prefixLength = 24; + }]; + }; + extif0 = { + ipv4.addresses = [ + { + address = primary-ip; + prefixLength = 28; + } + { + address = git-server-ip; + prefixLength = 32; + } + ]; + }; }; }; @@ -95,7 +209,7 @@ in { nginx = { enable = true; recommendedGzipSettings = true; - recommendedOptimisations = true; + recommendedOptimisation = true; recommendedTlsSettings = true; recommendedProxySettings = true; diff --git a/config/host-config/france/auth.nix b/config/host-config/france/auth.nix index 9235c04..648ee20 100644 --- a/config/host-config/france/auth.nix +++ b/config/host-config/france/auth.nix @@ -7,83 +7,79 @@ let 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); + passwd = import ../../../lib/passwd.nix { inherit lib; }; + + secrets = config.fudo.secrets.host-secrets.${hostname}; + + cfg = config.fudo.france; + in { - options.france = with types; { + options.fudo.france = with types; { ldap = { ssl-certificate = mkOption { - type = path; + type = str; description = "SSL certificate to use for the LDAP server."; }; ssl-private-key = mkOption { - type = path; + type = str; description = "SSL private key to use for the LDAP server."; }; ssl-ca-certificate = mkOption { - type = path; + type = str; description = "SSL certificate authority to use for the LDAP server."; }; + keytab = mkOption { + type = str; + description = "Path to the LDAP service keytab."; + }; + root-password-file = mkOption { + type = str; + description = "Path to the file containing the LDAP root password."; + }; }; kdc = { state-directory = mkOption { type = str; description = "Path at which to store kerberos state."; + default = "/state/kerberos"; }; master-key-file = mkOption { type = str; description = "Heimdal database master key file."; }; + + listen-ips = mkOption { + type = listOf str; + description = "IP addresses on which to listen for connections."; + }; }; }; 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"; - }; + secrets.host-secrets.${hostname}.kdc-master-key = { + source-file = cfg.kdc.master-key-file; + target-file = "/run/kerberos/kdc/master.key"; + user = config.fudo.auth.kdc.user; }; auth = { - ldap = { + ldap-server = { enable = true; base = "dc=fudo,dc=org"; organization = "Fudo"; - rootpw-file = secrets.ldap-root-passwd; + rootpw-file = cfg.ldap.root-password-file; 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; + kerberos-keytab = cfg.ldap.keytab; + ssl-certificate = cfg.ldap.ssl-certificate; + ssl-private-key = cfg.ldap.ssl-private-key; + ssl-ca-certificate = cfg.ldap.ssl-ca-certificate; listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ]; @@ -95,9 +91,9 @@ in { # 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; + realm = config.fudo.domains.${domain-name}.gssapi-realm; + state-directory = cfg.kdc.state-directory; + master-key-file = secrets.kdc-master-key.target-file; acl = let admin-entries = concatGenAttrs config.instance.local-admins @@ -109,7 +105,7 @@ 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" ]; + bind-addresses = cfg.kdc.listen-ips; }; }; }; diff --git a/config/host-config/france/backplane.nix b/config/host-config/france/backplane.nix new file mode 100644 index 0000000..3106570 --- /dev/null +++ b/config/host-config/france/backplane.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + timestamp = config.instance.build-timestamp; + domain = config.instance.local-domain; + + powerdns-user = "backplane-powerdns"; + backplane-dns-user = "backplane-dns"; + + generate-role-passwd = role: + lib.fudo.passwd.stablerandom-password-file + "backplane-${role}-password" + "${hostname}-${domain}-${role}-password-${config.instance.build-timestamp}"; + + 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"; + + secrets = config.fudo.secrets.host-secrets.france; + + cfg = config.fudo.france.backplane-server; + +in { + options.fudo.france.backplane-server = with types; { + listen-ips = mkOption { + type = listOf str; + description = "List of IPs on which to listen for incoming backplane connections."; + }; + + listen-ipv6s = mkOption { + type = listOf str; + description = "List of IPv6s on which to listen for incoming backplane connections."; + default = []; + }; + }; + + config = { + users = { + users = { + ${powerdns-user} = { + isSystemUser = true; + }; + ${backplane-dns-user} = { + isSystemUser = true; + }; + }; + + groups = { + ${powerdns-user} = { + members = [ powerdns-user ]; + }; + ${backplane-dns-user} = { + members = [ backplane-dns-user ]; + }; + }; + }; + + fudo = { + secrets.host-secrets.france = { + powerdns-password = { + source-file = powerdns-password; + target-file = "/run/backplane/dns/powerdns/db.passwd"; + user = config.fudo.backplane.dns.database.user; + }; + + backplane-dns-db-password = { + source-file = backplane-dns-db-password; + target-file = "/run/backplane/dns/db.passwd"; + user = config.fudo.backplane.dns.backplane.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; + }; + }; + + postgresql = { + enable = true; + required-services = [ "fudo-passwords.target" ]; + + users = { + ${powerdns-user} = { + password-file = secrets.powerdns-password.target-file; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; + }; + }; + ${backplane-dns-user} = { + password-file = secrets.backplane-dns-db-password; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; + }; + }; + }; + + databases = { + backplane_dns = { + users = ["niten"]; + }; + }; + }; + + backplane.dns = { + enable = true; + listen-v4-addresses = cfg.listen-ips; + listen-v6-addresses = cfg.listen-ipv6s; + user = backplane-dns-user; + group = backplane-dns-user; + database = { + username = powerdns-user; + database = "backplane_dns"; + # Uses an IP to avoid cyclical dependency... + host = "127.0.0.1"; + password-file = secrets.powerdns-password.target-file; + }; + backplane = { + host = "backplane.fudo.org"; + role = "service-dns"; + password-file = secrets.backplane-dns-xmpp-password.target-file; + database = { + username = backplane-dns-user; + database = backplane-dns-user; + host = "127.0.0.1"; + password-file = secrets.backplane-dns-db-password.target-file; + }; + }; + }; + }; + }; +} diff --git a/config/host-config/france/git.nix b/config/host-config/france/git.nix index 7c91a00..0e8bcb8 100644 --- a/config/host-config/france/git.nix +++ b/config/host-config/france/git.nix @@ -7,14 +7,16 @@ let secrets = config.fudo.secrets.host-secrets.${hostname}; + cfg = config.fudo.france.git; + sshOpts = { ... }: { - options = { + options = with types; { listen-ip = mkOption { type = str; description = "IP address on which to listen for SSH connections."; }; listen-port = mkOption { - type = str; + type = port; description = "Port on which to listen for SSH connections."; default = 22; }; @@ -23,7 +25,7 @@ let in { - options.france.git = with types; { + options.fudo.france.git = with types; { repository-directory = mkOption { type = str; description = "Path to store git repositories."; @@ -43,6 +45,14 @@ in { }; config.fudo = { + secrets.host-secrets.${hostname}.git-database-password = { + source-file = lib.fudo.passwd.stablerandom-passwd-file + "gitea-database-passwd" + "${hostname}-gitea-database-passwd-${config.instance.build-seed}"; + target-file = "/var/gitea/database.passwd"; + user = config.services.gitea.user; + }; + postgresql = { databases.fudo_git.users = config.instance.local_admins; diff --git a/config/host-config/france/jabber.nix b/config/host-config/france/jabber.nix index 005586e..fbd15a3 100644 --- a/config/host-config/france/jabber.nix +++ b/config/host-config/france/jabber.nix @@ -4,24 +4,108 @@ with lib; let hostname = config.instance.hostname; secrets = config.fudo.secrets.host-secrets.${hostname}; - + + cfg = config.fudo.france; + + generate-auth-file = name: files: let + make-entry = name: passwd-file: + ''("${name}" . "${readFile passwd-file}")''; + entries = mapAttrsToList make-entry files; + content = concatStringsSep "\n" entries; + in writeText "${name}-backplane-auth.scm" "'(${content})'"; + + host-auth-file = generate-auth-file "host" cfg.host-passwd-files; + service-auth-file = generate-auth-filre "service" cfg.service-passwd-files; + + ldap-password-file = + lib.fudo.passwd.random-passwd-file "ejabberd-ldap-auth-user"; + + ldap-hashed-password = + hash-ldap-passwd "ejabberd-ldap-hashed-passwd" ldap-password-file; + in { + options.fudo.france = with types; { + jabber = { + ldap-user = mkOption { + type = str; + description = "System user as which to authenticate to LDAP."; + default = "ejabberd"; + }; + + ldap-servers = mkOption { + type = listOf str; + description = "LDAP servers to use for user authentication."; + }; + + listen-ips = mkOption { + type = listOf str; + description = "IPs on which to listen for incoming connections."; + }; + }; + + backplane = { + host-passwd-files = mkOption { + type = attrsOf str; + description = "Map of hostname to password file, for backplane host authentication."; + default = {}; + }; + + service-passwd-files = mkOption { + type = attrsOf str; + description = "Map of service to password file, for backplane service authentication."; + default = {}; + }; + }; + }; + config = { fudo = { + system-users.${cfg.jabber.ldap-user} = { + description = "ejabberd authentication user."; + hashed-password = ldap-hashed-password; + }; + + secrets.host-secrets.${hostname} = let + user = config.services.ejabberd.user; + in { + host-auth = { + source-file = host-auth-file; + target-file = "/run/backplane/host-auth-file.scm"; + user = user; + }; + service-auth = { + source-file = service-auth-file; + target-file = "/run/backplane/service-auth-file.scm"; + user = user; + }; + ldap-password = { + source-file = ldap-password-file; + target-file = "/run/ejabberd/ldap.passwd"; + user = user; + }; + }; + jabber = { enable = true; + listen-ips = cfg.jabber.listen-ips; + + environment = { + FUDO_HOST_PASSWD_FILE = secrets.host-auth.target-file; + FUDO_SERVICE_PASSWD_FILE = secrets.service-auth.target-file; + }; + secret-files = { - LDAP_PASSWORD = secrets.jabber-ldap-password.target-file; + LDAP_PASSWORD = secrets.ldap-password.target-file; }; sites = { "fudo.im" = { site-config = { auth_method = "ldap"; - ldap_servers = [ "auth.fudo.org" ]; + ldap_servers = cfg.jabber.ldap-servers; ldap_port = 389; - ldap_rootdn = "cn=jabber,dc=fudo,dc=org"; + ldap_rootdn = "cn=${cfg.jabber.ldap-user},dc=fudo,dc=org"; ldap_password = ''"LDAP_PASSWD"''; ldap_base = "ou=members,dc=fudo,dc=org"; ldap_filter = "(objectClass=posixAccount)"; @@ -69,7 +153,7 @@ in { "backplane.fudo.org" = { site-config = { auth_method = "external"; - extauth_program = "${pkgs.guile}/bin/guile -s ${backplane-auth}"; + extauth_program = "${pkgs.guile}/bin/guile -s ${pkgs.backplane-auth}/backplane-auth.scm"; extauth_pool_size = 3; auth_use_cache = true; @@ -100,7 +184,6 @@ in { mod_time = {}; mod_version = {}; }; - }; }; }; diff --git a/config/host-config/france/mail-server.nix b/config/host-config/france/mail-server.nix index bfaece3..52e2202 100644 --- a/config/host-config/france/mail-server.nix +++ b/config/host-config/france/mail-server.nix @@ -9,7 +9,7 @@ let mail-reader-dn = "mail-auth-reader"; in { - options.france.mail = with types; { + options.fudo.france.mail = with types; { mail-directory = mkOption { type = str; description = "Directory to contain user maildirs."; @@ -39,17 +39,20 @@ in { enableContainer = true; monitoring = true; - hostname = "mail.${domain-name}"; + domain = domain-name; + mail-hostname = "mail.${domain-name}"; + + 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; + }; + }; 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/postgres.nix b/config/host-config/france/postgres.nix index 49cf30a..05a6009 100644 --- a/config/host-config/france/postgres.nix +++ b/config/host-config/france/postgres.nix @@ -5,14 +5,28 @@ let hostname = config.instance.hostname; secrets = config.fudo.secrets.host-secrets.${hostname}; in { + options.fudo.france.postgresql = with types; { + ssl-certificate = mkOption { + type = str; + description = "SSL certificate to use for the LDAP server."; + }; + ssl-private-key = mkOption { + type = str; + description = "SSL private key to use for the LDAP server."; + }; + keytab = mkOption { + type = path; + description = "Postgres service keytab."; + }; + }; + 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; + ssl-private-key = cfg.ssl-private-key; + ssl-certificate = cfg.ssl-certificate; + keytab = cfg.keytab; }; } diff --git a/config/host-config/france/webmail.nix b/config/host-config/france/webmail.nix index 312d4c0..7f283f1 100644 --- a/config/host-config/france/webmail.nix +++ b/config/host-config/france/webmail.nix @@ -17,7 +17,7 @@ let db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40; in { - options.france.webmail = with types; { + options.fudo.france.webmail = with types; { mail-server = mkOption { type = str; description = "Mail server to use for webmail."; diff --git a/config/host-config/procul.nix b/config/host-config/procul.nix index 436cc1f..2906aa2 100644 --- a/config/host-config/procul.nix +++ b/config/host-config/procul.nix @@ -22,6 +22,8 @@ let secrets = config.fudo.secrets.host-secrets.procul; + passwd = pkgs.lib.fudo.passwd; + in { networking = { dhcpcd.enable = false; @@ -85,75 +87,17 @@ 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; + files = config.fudo.secrets.files; in { postgres-keytab = { - source-file = secrets.service-keytabs.procul.postgres; + source-file = files.service-keytabs.procul.postgres; target-file = "/srv/postgres/secure/postgres.keytab"; user = "root"; }; gitea-database-password = { - source-file = secrets.service-passwords.procul.gitea-database; + source-file = files.service-passwords.procul.gitea-database; target-file = "/srv/gitea/secure/database.passwd"; user = config.fudo.git.user; }; diff --git a/config/hosts.nix b/config/hosts.nix index ea7997a..d2e2d5e 100644 --- a/config/hosts.nix +++ b/config/hosts.nix @@ -1,8 +1,18 @@ { config, lib, pkgs, ... }: +with lib; let syslib = pkgs.callPackage ../lib/hosts.nix {}; in { - config.fudo.hosts = syslib.base-host-config ./hosts; + config.fudo.hosts = let + build-seed = config.instance.build-seed; + base-config = syslib.base-host-config ./hosts; + in mapAttrs (hostname: base-config: + base-config // { + backplane-password-file = + pkgs.lib.fudo.passwd.stablerandom-passwd-file + "${hostname}-host-backplane-passwd" + "${hostname}-host-backplane-passwd-${build-seed}"; + }) base-config; } diff --git a/config/profile-config/common-ui.nix b/config/profile-config/common-ui.nix index 5a512f5..a8a721d 100644 --- a/config/profile-config/common-ui.nix +++ b/config/profile-config/common-ui.nix @@ -1,140 +1,4 @@ { config, lib, pkgs, ... }: -with lib; -let - hostname = config.instance.hostname; - enable-gui = config.fudo.hosts.${hostname}.enable-gui; - -in { - imports = [ ./common.nix ]; - - boot.plymouth.enable = false; - - boot.tmpOnTmpfs = true; - - environment = mkIf enable-gui { - systemPackages = [ - #libva - ]; - }; - - # We're deploying via nixops, this is just annoying - system = { autoUpgrade.enable = false; }; - - services = { - xserver = mkIf enable-gui { - enable = true; - - desktopManager.gnome.enable = true; - - displayManager.gdm = { - enable = true; - wayland = false; - }; - - windowManager.stumpwm.enable = true; - - # windowManager.session = pkgs.lib.singleton { - # name = "stumpwm"; - # start = '' - # ${pkgs.lispPackages.stumpwm}/bin/stumpwm & - # waidPID=$! - # ''; - # }; - }; - - trezord.enable = true; - }; - - hardware = { - bluetooth.enable = true; - - opengl = mkIf enable-gui { - enable = true; - driSupport = true; - driSupport32Bit = true; - }; - }; - - sound.enable = true; - hardware.pulseaudio = { - enable = true; - support32Bit = config.hardware.pulseaudio.enable; - }; - - console.font = - lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-g18n.psf.gz"; - - services.gnome = mkIf enable-gui { - evolution-data-server.enable = mkForce false; - gnome-user-share.enable = mkForce false; - }; - - services.flatpak.enable = enable-gui; - - # programs.steam.enable = enable-gui; - - fonts = mkIf enable-gui { - fontDir.enable = true; - fontconfig.enable = true; - #fontconfig.antialias = true; - #fontconfig.penultimate.enable = true; - #fontconfig.subpixel.lcdfilter = "default"; - - fonts = with pkgs; [ - cantarell_fonts - dejavu_fonts - dina-font - dosemu_fonts - fira-code - fira-code-symbols - freefont_ttf - liberation_ttf - mplus-outline-fonts - nerdfonts - noto-fonts - noto-fonts-cjk - noto-fonts-emoji - proggyfonts - terminus_font - ubuntu_font_family - ucsFonts - ultimate-oldschool-pc-font-pack - unifont - xorg.fontadobe100dpi - xorg.fontadobe75dpi - xorg.fontadobeutopia100dpi - xorg.fontadobeutopia75dpi - xorg.fontadobeutopiatype1 - xorg.fontarabicmisc - xorg.fontbh100dpi - xorg.fontbh75dpi - xorg.fontbhlucidatypewriter100dpi - xorg.fontbhlucidatypewriter75dpi - xorg.fontbhttf - xorg.fontbhtype1 - xorg.fontbitstream100dpi - xorg.fontbitstream75dpi - xorg.fontbitstreamtype1 - xorg.fontcronyxcyrillic - xorg.fontcursormisc - xorg.fontdaewoomisc - xorg.fontdecmisc - xorg.fontibmtype1 - xorg.fontisasmisc - xorg.fontjismisc - xorg.fontmicromisc - xorg.fontmisccyrillic - xorg.fontmiscethiopic - xorg.fontmiscmeltho - xorg.fontmiscmisc - xorg.fontmuttmisc - xorg.fontschumachermisc - xorg.fontscreencyrillic - xorg.fontsonymisc - xorg.fontsunmisc - xorg.fontwinitzkicyrillic - xorg.fontxfree86type1 - ]; - }; +{ } diff --git a/config/profile-config/common.nix b/config/profile-config/common.nix index fca97ee..ae437af 100644 --- a/config/profile-config/common.nix +++ b/config/profile-config/common.nix @@ -1,142 +1,5 @@ { config, lib, pkgs, ... }: with lib; -let - # Available to all users on the system. Keep it minimal. - global-packages = with pkgs; [ - bind - cryptsetup - git - heimdal - openssh_gssapi - tldr - vim - wget - ]; - -in { - environment = { - etc.nixos-live.source = ../../.; - - systemPackages = global-packages; - - # shellInit = '' - # ${pkgs.gnupg}/bin/gpg-connect-agent /bye - # export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket) - # ''; - }; - - nix = { - package = pkgs.nixFlakes; - extraOptions = '' - experimental-features = nix-command flakes - ''; - }; - - nixpkgs.config.allowUnfree = true; - security.acme.acceptTerms = true; - hardware.enableRedistributableFirmware = true; - - krb5 = { - enable = true; - - appdefaults = { - forwardable = true; - proxiable = true; - encrypt = true; - forward = true; - }; - - libdefaults = { - allow_weak_crypto = true; - dns_lookup_kdc = true; - dns_lookup_realm = true; - forwardable = true; - proxiable = true; - }; - - kerberos = pkgs.heimdalFull; - }; - - services = { - openssh = { - enable = true; - startWhenNeeded = true; - useDns = true; - permitRootLogin = "prohibit-password"; - extraConfig = '' - GSSAPIAuthentication yes - GSSAPICleanupCredentials yes - GSSAPIKeyExchange yes - GSSAPIStoreCredentialsOnRekey yes - ''; - }; - - fail2ban = - let domain-name = config.fudo.hosts.${config.instance.hostname}.domain; - in { - enable = config.networking.firewall.enable; - bantime-increment.enable = true; - ignoreIP = config.fudo.domains.${domain-name}.local-networks; - }; - - xserver = { - layout = "us"; - xkbVariant = "dvp"; - xkbOptions = "ctrl:nocaps"; - }; - - # pcscd.enable = true; - # udev.packages = with pkgs; [ yubikey-personalization ]; - }; - - networking.firewall = { - # Allow mosh connections if the firewall is enabled - allowedUDPPortRanges = [{ - from = 60000; - to = 60100; - }]; - }; - - console.useXkbConfig = true; - - i18n.defaultLocale = "en_US.UTF-8"; - - programs = { - mosh.enable = true; - - bash.enableCompletion = true; - - fish.enable = true; - - gnupg.agent = { - enable = true; - # enableSSHSupport = true; - # pinentryFlavor = if cfg.enable-gui then "gnome3" else "curses"; - }; - - ssh = { - startAgent = true; - - package = pkgs.openssh_gssapi; - - extraConfig = '' - GSSAPIAuthentication yes - GSSAPIDelegateCredentials yes - ''; - }; - }; - - security.pam = { - enableSSHAgentAuth = true; - - services = { - sshd = { - makeHomeDir = true; - sshAgentAuth = true; - # This isn't supposed to ask for a code unless ~/.google_authenticator exists...but it does - # googleAuthenticator.enable = true; - }; - }; - }; +{ } diff --git a/config/profile-config/desktop.nix b/config/profile-config/desktop.nix index 5b46b5c..231995a 100644 --- a/config/profile-config/desktop.nix +++ b/config/profile-config/desktop.nix @@ -1,7 +1,4 @@ { config, lib, pkgs, ... }: with lib; { - imports = [ ./common-ui.nix ]; - - config = { networking = { networkmanager.enable = mkForce false; }; }; } diff --git a/config/profile-config/laptop.nix b/config/profile-config/laptop.nix index dca0312..ae437af 100644 --- a/config/profile-config/laptop.nix +++ b/config/profile-config/laptop.nix @@ -2,31 +2,4 @@ with lib; { - imports = [ ./common-ui.nix ]; - - options.fudo.profile.laptop = { - use-network-manager = - mkEnableOption "Use NetworkManager instead of wpa_supplicant."; - }; - - config = { - - environment.systemPackages = with pkgs; [ acpi upower wpa_supplicant ]; - - networking = if (config.fudo.profile.laptop.use-network-manager) then { - networkmanager.enable = true; - } else { - networkmanager.enable = false; - wireless = { - enable = true; - userControlled = { - enable = true; - group = "wheel"; - }; - networks = mapAttrs (network: networkOpts: { - psk = networkOpts.key; - }) config.fudo.wireless-networks; - }; - }; - }; } diff --git a/config/profile-config/server.nix b/config/profile-config/server.nix index fca8406..ae437af 100644 --- a/config/profile-config/server.nix +++ b/config/profile-config/server.nix @@ -1,75 +1,5 @@ { config, lib, pkgs, ... }: with lib; -let - serverPackages = with pkgs; [ emacs-nox reboot-if-necessary test-config ]; - - reboot-if-necessary = pkgs.writeShellScriptBin "reboot-if-necessary" '' - if [ $# -ne 1 ]; then - echo "FAILED: no sync file provided." - exit 1 - fi - - WALL=${pkgs.utillinux}/bin/wall - - if [ -f $1 ]; then - $WALL "$1 exists, rebooting system" - ${pkgs.systemd}/bin/reboot - else - $WALL "$1 does not exist, switching config." - nixos-rebuild switch - fi - - exit 0 - ''; - - test-config = pkgs.writeShellScriptBin "fudo-test-config" '' - if [ $# -gt 1 ]; then - echo "usage: $0 [timeout]" - exit 1 - elif [ $# -eq 1 ]; then - TIMEOUT=$1 - else - TIMEOUT=15m - fi - - SYNCFILE=$TMP/sync-$(date +"%Y%m%d-%H%M%N") - touch $SYNCFILE - ${pkgs.utillinux}/bin/wall "Launching config. System will restart in $TIMEOUT if $SYNCFILE still exists." - systemd-run --on-active=$TIMEOUT ${reboot-if-necessary} $SYNCFILE - nixos-rebuild test - - exit 0 - ''; - -in { - imports = [ ./common.nix ]; - - config = { - environment = { systemPackages = serverPackages; }; - - system.autoUpgrade.enable = false; - - networking.networkmanager.enable = mkForce false; - - services = { xserver.enable = false; }; - - sound.enable = false; - hardware.pulseaudio.enable = false; - - powerManagement = - if config.fudo.hosts.${config.instance.hostname}.keep-cool then { - enable = true; - cpuFreqGovernor = "ondemand"; - } else { - enable = false; - }; - - systemd.targets = { - sleep.enable = false; - suspend.enable = false; - hibernate.enable = false; - hybrid-sleep.enable = false; - }; - }; +{ } diff --git a/config/profiles/common-ui.nix b/config/profiles/common-ui.nix new file mode 100644 index 0000000..bf8a511 --- /dev/null +++ b/config/profiles/common-ui.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + enable-gui = config.fudo.hosts.${hostname}.enable-gui; + +in { + imports = [ ./common.nix ]; + + boot = { + plymouth.enable = false; + tmpOnTmpfs = true; + }; + + services = { + xserver = mkIf enable-gui { + enable = true; + + desktopManager.gnome.enable = true; + + displayManager.gdm = { + enable = true; + wayland = false; + autoSuspend = false; + }; + + windowManager.stumpwm.enable = true; + + # windowManager.session = pkgs.lib.singleton { + # name = "stumpwm"; + # start = '' + # ${pkgs.lispPackages.stumpwm}/bin/stumpwm & + # waidPID=$! + # ''; + # }; + }; + + trezord.enable = true; + }; + + hardware = { + bluetooth.enable = true; + + opengl = mkIf enable-gui { + enable = true; + driSupport = true; + driSupport32Bit = true; + }; + }; + + sound.enable = true; + hardware.pulseaudio = { + enable = true; + support32Bit = config.hardware.pulseaudio.enable; + }; + + # console.font = + # lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-g18n.psf.gz"; + + services.gnome = mkIf enable-gui { + evolution-data-server.enable = mkForce false; + gnome-user-share.enable = mkForce false; + }; + + services.flatpak.enable = enable-gui; + + fonts = mkIf enable-gui { + fontDir.enable = true; + fontconfig.enable = true; + #fontconfig.antialias = true; + #fontconfig.penultimate.enable = true; + #fontconfig.subpixel.lcdfilter = "default"; + + fonts = with pkgs; [ + cantarell_fonts + dejavu_fonts + dina-font + dosemu_fonts + fira-code + fira-code-symbols + freefont_ttf + liberation_ttf + mplus-outline-fonts + nerdfonts + noto-fonts + noto-fonts-cjk + noto-fonts-emoji + proggyfonts + terminus_font + ubuntu_font_family + ucsFonts + ultimate-oldschool-pc-font-pack + unifont + xorg.fontadobe100dpi + xorg.fontadobe75dpi + xorg.fontadobeutopia100dpi + xorg.fontadobeutopia75dpi + xorg.fontadobeutopiatype1 + xorg.fontarabicmisc + xorg.fontbh100dpi + xorg.fontbh75dpi + xorg.fontbhlucidatypewriter100dpi + xorg.fontbhlucidatypewriter75dpi + xorg.fontbhttf + xorg.fontbhtype1 + xorg.fontbitstream100dpi + xorg.fontbitstream75dpi + xorg.fontbitstreamtype1 + xorg.fontcronyxcyrillic + xorg.fontcursormisc + xorg.fontdaewoomisc + xorg.fontdecmisc + xorg.fontibmtype1 + xorg.fontisasmisc + xorg.fontjismisc + xorg.fontmicromisc + xorg.fontmisccyrillic + xorg.fontmiscethiopic + xorg.fontmiscmeltho + xorg.fontmiscmisc + xorg.fontmuttmisc + xorg.fontschumachermisc + xorg.fontscreencyrillic + xorg.fontsonymisc + xorg.fontsunmisc + xorg.fontwinitzkicyrillic + xorg.fontxfree86type1 + ]; + }; +} diff --git a/config/profiles/common.nix b/config/profiles/common.nix new file mode 100644 index 0000000..1c486f8 --- /dev/null +++ b/config/profiles/common.nix @@ -0,0 +1,144 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + # Available to all users on the system. Keep it minimal. + global-packages = with pkgs; [ + bind + cryptsetup + git + heimdal + openssh_gssapi + tldr + vim + wget + ]; + +in { + environment = { + etc.nixos-live.source = ../../.; + + systemPackages = global-packages; + + # shellInit = '' + # ${pkgs.gnupg}/bin/gpg-connect-agent /bye + # export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket) + # ''; + }; + + system.autoUpgrade.enable = false; + + nix = { + package = pkgs.nixFlakes; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; + + nixpkgs.config.allowUnfree = true; + security.acme.acceptTerms = true; + hardware.enableRedistributableFirmware = true; + + krb5 = { + enable = true; + + appdefaults = { + forwardable = true; + proxiable = true; + encrypt = true; + forward = true; + }; + + libdefaults = { + allow_weak_crypto = true; + dns_lookup_kdc = true; + dns_lookup_realm = true; + forwardable = true; + proxiable = true; + }; + + kerberos = pkgs.heimdalFull; + }; + + services = { + openssh = { + enable = true; + startWhenNeeded = true; + useDns = true; + permitRootLogin = "prohibit-password"; + extraConfig = '' + GSSAPIAuthentication yes + GSSAPICleanupCredentials yes + GSSAPIKeyExchange yes + GSSAPIStoreCredentialsOnRekey yes + ''; + }; + + fail2ban = let + domain-name = config.fudo.hosts.${config.instance.hostname}.domain; + in { + enable = config.networking.firewall.enable; + bantime-increment.enable = true; + ignoreIP = config.instance.local-networks; + }; + + xserver = { + layout = "us"; + xkbVariant = "dvp"; + xkbOptions = "ctrl:nocaps"; + }; + + # pcscd.enable = true; + # udev.packages = with pkgs; [ yubikey-personalization ]; + }; + + networking.firewall = { + # Allow mosh connections if the firewall is enabled + allowedUDPPortRanges = [{ + from = 60000; + to = 60100; + }]; + }; + + console.useXkbConfig = true; + + i18n.defaultLocale = "en_US.UTF-8"; + + programs = { + mosh.enable = true; + + bash.enableCompletion = true; + + fish.enable = true; + + gnupg.agent = { + enable = true; + # enableSSHSupport = true; + # pinentryFlavor = if cfg.enable-gui then "gnome3" else "curses"; + }; + + ssh = { + startAgent = true; + + package = pkgs.openssh_gssapi; + + extraConfig = '' + GSSAPIAuthentication yes + GSSAPIDelegateCredentials yes + ''; + }; + }; + + security.pam = { + enableSSHAgentAuth = true; + + services = { + sshd = { + makeHomeDir = true; + sshAgentAuth = true; + # This isn't supposed to ask for a code unless ~/.google_authenticator exists...but it does + # googleAuthenticator.enable = true; + }; + }; + }; +} diff --git a/config/profiles/desktop.nix b/config/profiles/desktop.nix new file mode 100644 index 0000000..ca0d5d8 --- /dev/null +++ b/config/profiles/desktop.nix @@ -0,0 +1,10 @@ +{ config, lib, pkgs, ... }: + +with lib; +{ + imports = [ ./common-ui.nix ]; + + config = { + networking.networkmanager.enable = mkForce false; + }; +} diff --git a/config/profiles/laptop.nix b/config/profiles/laptop.nix new file mode 100644 index 0000000..dca0312 --- /dev/null +++ b/config/profiles/laptop.nix @@ -0,0 +1,32 @@ +{ config, lib, pkgs, ... }: + +with lib; +{ + imports = [ ./common-ui.nix ]; + + options.fudo.profile.laptop = { + use-network-manager = + mkEnableOption "Use NetworkManager instead of wpa_supplicant."; + }; + + config = { + + environment.systemPackages = with pkgs; [ acpi upower wpa_supplicant ]; + + networking = if (config.fudo.profile.laptop.use-network-manager) then { + networkmanager.enable = true; + } else { + networkmanager.enable = false; + wireless = { + enable = true; + userControlled = { + enable = true; + group = "wheel"; + }; + networks = mapAttrs (network: networkOpts: { + psk = networkOpts.key; + }) config.fudo.wireless-networks; + }; + }; + }; +} diff --git a/config/profiles/server.nix b/config/profiles/server.nix new file mode 100644 index 0000000..4183d7c --- /dev/null +++ b/config/profiles/server.nix @@ -0,0 +1,74 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + reboot-if-necessary = pkgs.writeShellScriptBin "reboot-if-necessary" '' + if [ $# -ne 1 ]; then + echo "FAILED: no sync file provided." + exit 1 + fi + + WALL=${pkgs.utillinux}/bin/wall + + if [ -f $1 ]; then + $WALL "$1 exists, rebooting system" + ${pkgs.systemd}/bin/reboot + else + $WALL "$1 does not exist, switching config." + nixos-rebuild switch + fi + + exit 0 + ''; + + test-config = pkgs.writeShellScriptBin "fudo-test-config" '' + if [ $# -gt 1 ]; then + echo "usage: $0 [timeout]" + exit 1 + elif [ $# -eq 1 ]; then + TIMEOUT=$1 + else + TIMEOUT=15m + fi + + SYNCFILE=$TMP/sync-$(date +"%Y%m%d-%H%M%N") + touch $SYNCFILE + ${pkgs.utillinux}/bin/wall "Launching config. System will restart in $TIMEOUT if $SYNCFILE still exists." + systemd-run --on-active=$TIMEOUT ${reboot-if-necessary} $SYNCFILE + nixos-rebuild test + + exit 0 + ''; + +in { + imports = [ ./common.nix ]; + + config = { + environment = { + serverPackages = with pkgs; + [ emacs-nox reboot-if-necessary test-config ]; + }; + + networking.networkmanager.enable = mkForce false; + + services.xserver.enable = false; + + sound.enable = false; + hardware.pulseaudio.enable = false; + + powerManagement = + if config.fudo.hosts.${config.instance.hostname}.keep-cool then { + enable = true; + cpuFreqGovernor = "ondemand"; + } else { + enable = false; + }; + + systemd.targets = { + sleep.enable = false; + suspend.enable = false; + hibernate.enable = false; + hybrid-sleep.enable = false; + }; + }; +} diff --git a/config/site-config/seattle.nix b/config/site-config/seattle.nix index 8d66561..2bd9fc9 100644 --- a/config/site-config/seattle.nix +++ b/config/site-config/seattle.nix @@ -38,29 +38,31 @@ in { options = [ "comment=systemd.automount" ]; }; + # NOTE: these are pointing directly to nostromo so the krb lookup works "/net/documents" = { - device = "sea-store.sea.fudo.org:/export/documents"; + device = "nostromo.sea.fudo.org:/export/documents"; fsType = "nfs4"; - options = [ "comment=systemd.automount" "sec=krb5p" ]; + options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ]; }; "/net/downloads" = { - device = "sea-store.sea.fudo.org:/export/downloads"; + device = "nostromo.sea.fudo.org:/export/downloads"; fsType = "nfs4"; - options = [ "comment=systemd.automount" "sec=krb5i" ]; + options = [ "comment=systemd.automount" "sec=krb5i" "proto=tcp" ]; }; "/net/projects" = { - device = "sea-store.sea.fudo.org:/export/projects"; + device = "nostromo.sea.fudo.org:/export/projects"; fsType = "nfs4"; - options = [ "comment=systemd.automount" "sec=krb5p" ]; + options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ]; }; }; systemd = { - tmpfiles.rules = [ - "d /net/documents - root sea-documents - -" - "d /net/downloads - root sea-downloads - -" - "d /net/projects - root sea-projects - -" - ]; + ## This fails if the filesystems already exist + # tmpfiles.rules = [ + # "d /net/documents - root sea-documents - -" + # "d /net/downloads - root sea-downloads - -" + # "d /net/projects - root sea-projects - -" + # ]; # mounts = [ # { diff --git a/config/system-users.nix b/config/system-users.nix new file mode 100644 index 0000000..6e22c51 --- /dev/null +++ b/config/system-users.nix @@ -0,0 +1,17 @@ + +{ + replicator = { + description = "Database Replicator"; + hashed-password = "{SHA}HpiRMyxLR+0ZFHz/COvG9lcNYyQ="; + }; + + auth_reader = { + description = "System Authenticator"; + hashed-password = "{MD5}N36/kQ64mev1HARddvVk7Q=="; + }; + + user_db_reader = { + description = "User Database Reader"; + hashed-password = "{SSHA}IVKhrB+wMOCI/CCzbJW8sNDbH67ZTMBv"; + }; +} diff --git a/initialize.nix b/initialize.nix index 5161ba3..3b00085 100644 --- a/initialize.nix +++ b/initialize.nix @@ -1,4 +1,4 @@ -{ hostname, site, domain, profile, build-timestamp, ... }: +{ lib, pkgs, hostname, site, domain, profile, build-timestamp, ... }: let # Get info on this host so we know what to load diff --git a/lib/dns.nix b/lib/dns.nix index 3223d62..c0979f4 100644 --- a/lib/dns.nix +++ b/lib/dns.nix @@ -1,6 +1,6 @@ -{ lib, ... }: +{ pkgs, ... }: -with lib; +with pkgs.lib; let join-lines = concatStringsSep "\n"; diff --git a/lib/fudo-lib.nix b/lib/fudo-lib.nix index 30a8e8d..b23cfd0 100644 --- a/lib/fudo-lib.nix +++ b/lib/fudo-lib.nix @@ -1,16 +1,18 @@ { lib, ... }: -let - ip = import ./ip.nix { inherit lib; }; - dns = import ./dns.nix { inherit lib; }; - passwd = import ./passwd.nix { inherit lib; }; -in +# NOTE: OBSOLETE! See overlay.nix + { lib.overlays = [ (final: prev: prev.lib // { - fudo = { - inherit ip dns passwd; + fudo = let + lib = prev.lib; + in { + ip = import ./ip.nix { inherit lib; }; + dns = import ./dns.nix { inherit lib; }; + passwd = import ./passwd.nix { inherit lib; }; + lisp = import ./lisp.nix { inherit lib; }; }; }) ]; diff --git a/lib/fudo/acme-certs.nix b/lib/fudo/acme-certs.nix index a4f0ddc..6af407d 100644 --- a/lib/fudo/acme-certs.nix +++ b/lib/fudo/acme-certs.nix @@ -30,7 +30,9 @@ let # }; # }; - domainOpts = { domain, ... }: { + domainOpts = { name, ... }: let + domain = name; + in { options = with types; { email = mkOption { type = str; @@ -45,8 +47,12 @@ let }; local-copies = let - localCopyOpts = { copy, ... }: { - options = with types; { + localCopyOpts = { name, ... }: let + copy = name; + in { + options = with types; let + target-path = "/var/run/${domain}/${copy}"; + in { user = mkOption { type = str; description = "User to which this copy belongs."; @@ -58,17 +64,35 @@ let default = null; }; - path = mkOption { - type = str; - description = "Path at which to store the local copy."; - default = "/var/run/${domain}/${copy}"; - }; - service = mkOption { type = str; description = "systemd job to copy certs."; default = "fudo-${domain}-${copy}-certs.service"; }; + + certificate = mkOption { + type = str; + description = "Full path to the local copy certificate."; + default = "${target-path}/cert.pem"; + }; + + full-certificate = mkOption { + type = str; + description = "Full path to the local copy certificate."; + default = "${target-path}/fullchain.pem"; + }; + + chain = mkOption { + type = str; + description = "Full path to the local copy certificate."; + default = "${target-path}/chain.pem"; + }; + + private-key = mkOption { + type = str; + description = "Full path to the local copy certificate."; + default = "${target-path}/key.pem"; + }; }; }; in mkOption { @@ -92,7 +116,7 @@ let cfg.host-domains.${hostname} else {}; optionalStringOr = str: default: - if cond then str else default; + if (str != null) then str else default; in { options.fudo.acme = with types; { @@ -113,26 +137,41 @@ in { tmpfiles.rules = let copies = concatMapAttrs (domain: domainOpts: domainOpts.local-copies) localDomains; + perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500"; copy-paths = mapAttrsToList (copy: copyOpts: - "D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -") - copies; - in copy-paths; + let + dir-entry = copyOpts: file: "D '${dirOf file}' ${perms copyOpts} ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -"; + in map (dir-entry copyOpts) [ + copyOpts.certificate + copyOpts.full-certificate + copyOpts.chain + copyOpts.private-key + ]) copies; + in unique copy-paths; - # TODO: Make this a Fudo service? services = concatMapAttrs (domain: domainOpts: mapAttrs' (copy: copyOpts: let + key-perms = copyOpts: if (copyOpts.group != null) then "0440" else "0400"; source = config.security.acme.certs.${domain}.directory; target = copyOpts.path; - install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" '' - for cert in cert chain fullchain full key; do - cp ${source}/$cert.pem ${target}/$cert.pem - chmod 0440 ${source}/$cert.pem - done + install-certs = pkgs.writeShellScript "fudo-install-${domain}-${copy}-certs.sh" '' + cp cert.pem ${copyOpts.certificate} + chmod 0444 ${copyOpts.certificate} + + cp full.pem ${copyOpts.full-certificate} + chmod 0444 ${copyOpts.full-certificate} + + cp chain.pem ${copyOpts.chain} + chmod 0444 ${copyOpts.chain} + + cp key.pem ${copyOpts.private-key} + chmod ${key-perms copyOpts} ${copyOpts.private-key} ''; - remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${site}-certs.sh" '' - for cert in cert chain fullchain full key; do - rm -rf ${target}/$cert.pem - done + remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" '' + rm -f ${copyOpts.private-key} + rm -f ${copyOpts.chainy} + rm -f ${copyOpts.full-certificate} + rm -f ${copyOpts.certificate} ''; in nameValuePair (rm-service-ext copyOpts.service) { diff --git a/lib/fudo/backplane/default.nix b/lib/fudo/backplane/default.nix index 028c784..c27c2df 100644 --- a/lib/fudo/backplane/default.nix +++ b/lib/fudo/backplane/default.nix @@ -1,5 +1,6 @@ -{ ... }: +{ config, pkgs, lib, ... }: +with lib; { imports = [ ./dns.nix diff --git a/lib/fudo/backplane/dns.nix b/lib/fudo/backplane/dns.nix index 009f6e8..759f630 100644 --- a/lib/fudo/backplane/dns.nix +++ b/lib/fudo/backplane/dns.nix @@ -1,63 +1,10 @@ - { config, pkgs, lib, ... }: with lib; let cfg = config.fudo.backplane.dns; - lisp-pkgs = with pkgs.localLispPackages; [ - arrows - backplane-dns - backplane-server - cl-sasl - cl-xmpp - ip-utils - - alexandria - babel - bordeaux-threads - cffi - cl-base64 - cl-json - cl-postgres - cl-ppcre - cl-unicode - cl_plus_ssl - closer-mop - closure-common - cxml - flexi-streams - global-vars - introspect-environment - ironclad - iterate - lisp-namespace - md5 - nibbles - postmodern - puri - s-sql - split-sequence - trivia - trivia_dot_balland2006 - trivia_dot_level0 - trivia_dot_level1 - trivia_dot_level2 - trivia_dot_trivial - trivial-cltl2 - trivial-features - trivial-garbage - trivial-gray-streams - type-i - uax-15 - usocket - ]; - - backup-directory = "/var/lib/fudo/backplane/dns"; - - powerdns-home = "/var/lib/powerdns"; - - powerdns-conf-dir = "${powerdns-home}/conf.d"; + powerdns-conf-dir = "${cfg.powerdns-home}/conf.d"; backplaneOpts = { ... }: { options = { @@ -117,53 +64,59 @@ let }; in { - options.fudo.backplane.dns = { + options.fudo.backplane.dns = with types; { enable = mkEnableOption "Enable backplane dynamic DNS server."; port = mkOption { - type = types.port; + type = port; description = "Port on which to serve authoritative DNS requests."; default = 53; }; listen-v4-addresses = mkOption { - type = with types; listOf str; + type = listOf str; description = "IPv4 addresses on which to listen for dns requests."; default = [ "0.0.0.0" ]; }; listen-v6-addresses = mkOption { - type = with types; listOf str; + type = listOf str; description = "IPv6 addresses on which to listen for dns requests."; example = [ "[abcd::1]" ]; default = [ ]; }; required-services = mkOption { - type = with types; listOf str; + type = listOf str; description = "A list of services required before the DNS server can start."; }; user = mkOption { - type = types.str; + type = str; description = "User as which to run DNS backplane listener service."; default = "backplane-dns"; }; group = mkOption { - type = types.str; + type = str; description = "Group as which to run DNS backplane listener service."; default = "backplane-dns"; }; database = mkOption { - type = with types; submodule databaseOpts; + type = submodule databaseOpts; description = "Database settings for the DNS server."; }; + powerdns-home = mkOption { + type = str; + description = "Directory at which to store powerdns configuration and state."; + default = "/run/backplane-dns/powerdns"; + }; + backplane = mkOption { - type = with types; submodule backplaneOpts; + type = submodule backplaneOpts; description = "Backplane Jabber settings for the DNS server."; }; }; @@ -177,7 +130,11 @@ in { createHome = true; home = "/var/home/${cfg.user}"; }; - backplane-powerdns = { isSystemUser = true; }; + backplane-powerdns = { + isSystemUser = true; + home = cfg.powerdns-home; + createHome = true; + }; }; groups = { @@ -186,140 +143,107 @@ in { }; }; + fudo.system.services = { + backplane-powerdns-config-generator = { + description = + "Generate postgres configuration for backplane DNS server."; + requires = cfg.required-services; + type = "oneshot"; + restartIfChanged = true; + partOf = [ "backplane-dns.target" ]; + + readWritePaths = [ powerdns-conf-dir ]; + + preStart = '' + mkdir -p ${powerdns-conf-dir} + chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir} + ''; + + # This builds the config in a bash script, to avoid storing the password + # in the nix store at any point + script = '' + if [ ! -d ${powerdns-conf-dir} ]; then + mkdir ${powerdns-conf-dir} + fi + + TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX) + TMPCONF=$TMPDIR/pdns.local.gpgsql.conf + + if [ ! -f ${cfg.database.password-file} ]; then + echo "${cfg.database.password-file} does not exist!" + exit 1 + fi + + touch $TMPCONF + chown backplane-powerdns:backplane-powerdns $TMPCONF + chmod go-rwx $TMPCONF + PASSWORD=$(cat ${cfg.database.password-file}) + echo "launch+=gpgsql" >> $TMPCONF + echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF + echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF + echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF + echo "gpgsql-password=$PASSWORD" >> $TMPCONF + echo "gpgsql-dnssec=yes" >> $TMPCONF + + mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf + rm -rf $TMPDIR + + exit 0 + ''; + }; + + backplane-powerdns = let + pdns-config-dir = pkgs.writeTextDir "pdns.conf" '' + local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses} + local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses} + local-port=${toString cfg.port} + launch= + include-dir=${powerdns-conf-dir}/ + ''; + in { + description = "Backplane PowerDNS name server"; + requires = [ + "postgresql.service" + "backplane-powerdns-config-generator.service" + ]; + after = [ "network.target" ]; + path = with pkgs; [ powerdns postgresql ]; + execStart = "pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${cfg.powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${pdns-config-dir}"; + }; + + backplane-dns = { + description = "Fudo DNS Backplane Server"; + restartIfChanged = true; + path = with pkgs; [ backplane-dns-server ]; + execStart = "launch-backplane-dns.sh"; + pidFile = "/run/backplane-dns.$USERNAME.pid"; + user = cfg.user; + group = cfg.group; + partOf = [ "backplane-dns.target" ]; + requires = [ "postgresql.service" ]; + environment = { + FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; + FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; + FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; + FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host; + FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database; + FUDO_DNS_BACKPLANE_DATABASE_USERNAME = + cfg.backplane.database.username; + FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = + cfg.backplane.database.password-file; + + CL_SOURCE_REGISTRY = + pkgs.lib.fudo.lisp.lisp-source-registry pkgs.backplane-dns-server; + }; + }; + }; + systemd = { targets = { backplane-dns = { description = "Fudo DNS backplane services."; wantedBy = [ "multi-user.target" ]; - }; - }; - - services = { - - backplane-powerdns = let - configDir = pkgs.writeTextDir "pdns.conf" '' - local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses} - local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses} - local-port=${toString cfg.port} - launch= - include-dir=${powerdns-conf-dir}/ - ''; - - psql-user = config.services.postgresql.superUser; - - in { - unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)"; - description = "Backplane PowerDNS name server"; - requires = [ - "postgresql.service" - "backplane-dns-config-generator.service" - "backplane-dns.target" - ]; - after = [ "network.target" "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; - - path = with pkgs; [ postgresql ]; - - serviceConfig = { - Restart = "on-failure"; - RestartSec = "10"; - StartLimitInterval = "0"; - PrivateDevices = true; - # CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT"; - # NoNewPrivileges=true; - ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${powerdns-home}"; - ExecStart = - "${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}"; - ProtectSystem = "full"; - # ProtectHome=true; - RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; - }; - }; - - backplane-dns-config-generator = { - description = - "Generate postgres configuration for backplane DNS server."; - requiredBy = [ "backplane-powerdns.service" ]; - requires = cfg.required-services; - serviceConfig.Type = "oneshot"; - restartIfChanged = true; - partOf = [ "backplane-dns.target" ]; - - preStart = '' - mkdir -p ${powerdns-conf-dir} - chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir} - ''; - - # This builds the config in a bash script, to avoid storing the password - # in the nix store at any point - script = '' - if [ ! -d ${powerdns-conf-dir} ]; then - mkdir ${powerdns-conf-dir} - fi - - TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX) - TMPCONF=$TMPDIR/pdns.local.gpgsql.conf - - if [ ! -f ${cfg.database.password-file} ]; then - echo "${cfg.database.password-file} does not exist!" - exit 1 - fi - - touch $TMPCONF - chown backplane-powerdns:backplane-powerdns $TMPCONF - chmod go-rwx $TMPCONF - PASSWORD=$(cat ${cfg.database.password-file}) - echo "launch+=gpgsql" >> $TMPCONF - echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF - echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF - echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF - echo "gpgsql-password=$PASSWORD" >> $TMPCONF - echo "gpgsql-dnssec=yes" >> $TMPCONF - - mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf - - rm -rf $TMPDIR - - exit 0 - ''; - }; - - backplane-dns = { - description = "Fudo DNS Backplane Server"; - restartIfChanged = true; - - serviceConfig = { - ExecStart = - "${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh"; - Restart = "on-failure"; - PIDFile = "/run/backplane-dns.$USERNAME.pid"; - User = cfg.user; - Group = cfg.group; - StandardOutput = "journal"; - }; - - environment = { - # LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; - - FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; - FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; - FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; - FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host; - FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database; - FUDO_DNS_BACKPLANE_DATABASE_USERNAME = - cfg.backplane.database.username; - FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = - cfg.backplane.database.password-file; - - # CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//"; - - CL_SOURCE_REGISTRY = - lib.concatStringsSep ":" (map (pkg: "${pkg}//") lisp-pkgs); - }; - - requires = cfg.required-services; - partOf = [ "backplane-dns.target" ]; - wantedBy = [ "multi-user.target" ]; + requries = cfg.required-services ++ [ "postgresql.service" ]; }; }; }; diff --git a/lib/fudo/git.nix b/lib/fudo/git.nix index 6893da8..7d904c4 100644 --- a/lib/fudo/git.nix +++ b/lib/fudo/git.nix @@ -108,7 +108,7 @@ in { enable = true; appName = cfg.site-name; database = { - createDatabase = true; + createDatabase = false; host = cfg.database.hostname; name = cfg.database.name; user = cfg.database.user; diff --git a/lib/fudo/host-filesystems.nix b/lib/fudo/host-filesystems.nix index d460d44..e3c3214 100644 --- a/lib/fudo/host-filesystems.nix +++ b/lib/fudo/host-filesystems.nix @@ -34,8 +34,9 @@ in { systemd = { # Ensure the mountpoints exist tmpfiles.rules = let + mpPerms = mpOpts: if mpOpts.world-readable then "755" else "750"; mountpointToPath = mp: mpOpts: - "d '${mp}' 750 root ${optionalOrDefault mpOpts.group "-"} - -"; + "d '${mp}' ${mpPerms mpOpts} root ${optionalOrDefault mpOpts.group "-"} - -"; filesystemsToMountpointLists = mapAttrsToList (fs: fsOpts: fsOpts.mountpoints); mountpointListsToPaths = concatMap diff --git a/lib/fudo/hosts.nix b/lib/fudo/hosts.nix index 58dce8b..f9ce3a5 100644 --- a/lib/fudo/hosts.nix +++ b/lib/fudo/hosts.nix @@ -6,6 +6,10 @@ let host = import ../types/host.nix { inherit lib; }; + hostname = config.instance.hostname; + + host-secrets = config.fudo.secrets.host-secrets.${hostname}; + in { options.fudo.hosts = with types; mkOption { @@ -73,6 +77,8 @@ in { config.environment.systemPackages; sorted-unique = sort lessThan (unique packages); in concatStringsSep "\n" sorted-unique; + + build-timestamp.text = toString config.instance.build-timestamp; }; systemPackages = with pkgs; @@ -103,10 +109,6 @@ in { (keypair: keypair.private-key) (try-attr hostname files.build-keypairs); - backplane-passwd-source = try-attr hostname files.backplane-passwords; - - backplane-passwd-target = "/var/run/backplane/passwd"; - in { secrets.host-secrets.${hostname} = { host-keytab = mkIf (keytab-file != null) { @@ -121,15 +123,15 @@ in { user = "root"; }; - backplane-passwd = mkIf (backplane-passwd-source != null) { - source-file = backplane-passwd-source; - target-file = backplane-passwd-target; + backplane-passwd = { + source-file = host-cfg.backplane-password-file; + target-file = "/run/backplane/client/passwd"; user = config.fudo.client.dns.user; }; }; - client.dns.password-file = mkIf (backplane-passwd-source != null) - backplane-passwd-target; + client.dns.password-file = + host-secrets.backplane-passwd.target-file; }; programs.adb.enable = host-cfg.android-dev; diff --git a/lib/fudo/jabber.nix b/lib/fudo/jabber.nix index 9d10fa9..a048063 100644 --- a/lib/fudo/jabber.nix +++ b/lib/fudo/jabber.nix @@ -23,16 +23,16 @@ let foldr (a: b: a // b) {} (mapAttrs f attrs); concatMapAttrsToList = f: attr: - attrValues (concatMapAttrs f attr); + concatMap (i: i) (attrValues (mapAttrs f attr)); host-domains = config.fudo.acme.host-domains.${hostname}; siteCerts = site: let - certPath = host-domains.${site}.local-copies.ejabberd.path; + cert-copy = host-domains.${site}.local-copies.ejabberd; in [ - "${certPath}/fullchain.pem" - "${certPath}/privkey.pem" - "${certPath}/chain.pem" + cert-copy.certificate + cert-copy.private-key + cert-copy.chain ]; siteCertService = site: @@ -60,13 +60,13 @@ let hosts = attrNames cfg.sites; - listen = [{ + listen = map (ip: { port = cfg.port; module = "ejabberd_c2s"; - ip = cfg.listen-ip; + ip = ip; starttls = true; starttls_required = true; - }]; + }) cfg.listen-ips; certfiles = concatMapAttrsToList (site: siteOpts: @@ -94,7 +94,6 @@ let swapper = concatStringsSep " | " secret-swappers; in pkgs.writeShellScript "ejabberd-generate-config.sh" '' cat ${template} | ${swapper} > ${target} - chown ${cfg.user}:${cfg.group} ${target} ''; cfg = config.fudo.jabber; @@ -102,6 +101,11 @@ let in { options.fudo.jabber = with types; { enable = mkEnableOption "Enable ejabberd server."; + + listen-ips = mkOption { + type = listOf str; + description = "IPs on which to listen for Jabber connections."; + }; port = mkOption { type = port; @@ -122,7 +126,7 @@ in { }; admins = mkOption { - type = str; + type = listOf str; description = "List of admin users for the server."; default = []; }; @@ -141,7 +145,23 @@ in { config-file = mkOption { type = str; description = "Location at which to generate the configuration file."; - default = "/var/run/ejabberd/ejabberd.yaml"; + default = "/run/ejabberd/ejabberd.yaml"; + }; + + log-level = mkOption { + type = int; + description = '' + Log level at which to run the server. + + See: https://docs.ejabberd.im/admin/guide/troubleshooting/ + ''; + default = 3; + }; + + environment = mkOption { + type = attrsOf str; + description = "Environment variables to set for the ejabberd daemon."; + default = {}; }; }; @@ -156,26 +176,50 @@ in { }; }; - fudo.acme.host-domains.${hostname} = mapAttrs (site: siteCfg: - mkIf siteCfg.enableACME { - local-copies.ejabberd = { + fudo = { + acme.host-domains.${hostname} = mapAttrs (site: siteCfg: + mkIf siteCfg.enableACME { + local-copies.ejabberd = { + user = cfg.user; + group = cfg.group; + }; + }) cfg.sites; + + system = let + config-dir = dirOf cfg.config-file; + in { + ensure-directories.${config-dir} = { user = cfg.user; - group = cfg.group; + perms = "0700"; }; - }) cfg.sites; + + services.ejabberd-config-generator = let + config-generator = + enter-secrets config-file-template cfg.secret-files cfg.config-file; + in { + script = "${config-generator}"; + readWritePaths = [ config-dir ]; + workingDirectory = config-dir; + user = cfg.user; + description = "Generate ejabberd config file with necessary passwords."; + postStart = '' + chown ${cfg.user} ${cfg.config-file} + chmod 0400 ${cfg.config-file} + ''; + }; + }; + }; systemd = { tmpfiles.rules = [ "D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -" ]; - services.ejabberd = let - config-generator = enter-secrets config-file-template cfg.secret-files cfg.config-file; - in { - wants = map (site: siteCertService site) (attrNames cfg.sites); - environment = cfg.secret-files; - serviceConfig = { - ExecStartPre = mkAfter "${config-generator}"; + services = { + ejabberd = { + wants = map (site: siteCertService site) (attrNames cfg.sites); + requires = [ "ejabberd-config-generator.service" ]; + environment = cfg.environment; }; }; }; diff --git a/lib/fudo/ldap.nix b/lib/fudo/ldap.nix index ec0b57b..1ca3dd4 100644 --- a/lib/fudo/ldap.nix +++ b/lib/fudo/ldap.nix @@ -205,6 +205,7 @@ in { ssl-ca-certificate = mkOption { type = with types; nullOr str; + description = '' The path to the SSL CA cert used to sign the certificate. ''; @@ -220,9 +221,8 @@ in { base = mkOption { type = str; - description = '' - The base dn of the LDAP server (eg. "dc=fudo,dc=org"). - ''; + description = "The base dn of the LDAP server."; + example = "dc=fudo,dc=org"; }; rootpw-file = mkOption { @@ -239,7 +239,6 @@ in { A list of URIs on which the ldap server should listen. ''; example = [ "ldap://auth.fudo.org" "ldaps://auth.fudo.org" ]; - default = [ ]; }; users = mkOption { @@ -295,12 +294,11 @@ in { etc = { "openldap/sasl2/slapd.conf" = { mode = "0400"; - user = "openldap"; - group = "openldap"; - # FIXME: take arguments! + user = config.services.openldap.user; + group = config.services.openldap.group; text = '' mech_list: gssapi external - keytab: /etc/ldap/ldap.keytab + keytab: ${cfg.kerberos-keytab} ''; }; }; @@ -343,72 +341,126 @@ in { rootdn = "cn=admin,${cfg.base}"; rootpwFile = "${cfg.rootpw-file}"; urlList = cfg.listen-uris; + database = "mdb"; - extraConfig = '' + settings = let + makeAccessLine = i: attrs: perm-map: let + perm-strings = mapAttrs (dn: perm: "by ${dn} ${perm}") perm-map; + perm-string = concatStringsSep " " perm-strings; + in "${i}to ${attrs} ${perm-string}"; - TLSCertificateFile ${cfg.ssl-certificate} - TLSCertificateKeyFile ${cfg.ssl-private-key} - ${optionalString (cfg.ssl-ca-certificate != null) - "TLSCACertificateFile ${cfg.ssl-ca-certificate}"} + makeAccess = access-map: let + pairs = mapAttrsToList (target: perm-map: [target perm-map]) access-map; + in imap0 (i: pair: makeAccessLine i pair[0] pair[1]) pairs; - authz-regexp "^uid=auth/([^.]+)\.fudo\.org,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org" - authz-regexp "^uid=[^,/]+/root,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=admin,dc=fudo,dc=org" - authz-regexp "^uid=([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "uid=$1,ou=members,dc=fudo,dc=org" - authz-regexp "^uid=host/([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org" - authz-regexp "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$" "cn=admin,dc=fudo,dc=org" - - ''; - - extraDatabaseConfig = '' - # access to dn=base="" - # by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - # by * read - - access to attrs=userPassword,shadowLastChange - by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by group.exact="cn=admin,ou=members,${cfg.base}" write - by dn.exact="cn=auth_reader,${cfg.base}" read - by dn.exact="cn=replicator,${cfg.base}" read - by self write - by * auth - - access to dn.exact="cn=admin,ou=groups,${cfg.base}" - by dn.exact="cn=admin,${cfg.base}" write - by users read - by * none - - access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid - by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write - by group.exact="cn=admin,ou=groups,${cfg.base}" write - by users read - by * none - - access to dn.subtree="ou=members,${cfg.base}" attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber - by group.exact="cn=admin,ou=groups,${cfg.base}" write - by dn.exact="cn=user_db_reader,${cfg.base}" read - by users read - by * none - - access to dn.exact="cn=admin,ou=groups,${cfg.base}" - by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by users read - by * none - - access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid - by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write - by group.exact="cn=admin,ou=groups,${cfg.base}" write - by users read - by * none - - access to * - by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by users read - by * none - - - index objectClass,uid eq - ''; + in { + attrs = { + cn = "config"; + objectClass = "olcGlobal"; + olcPidFile = "/run/slapd/slapd.pid"; + olcTLSCertificateFile = cfg.ssl-certificate; + olcTLSCertificateKeyFile = cfg.ssl-private-key; + olcTLSCACertificateFile = cfg.ssl-ca-certificate; + olcSaslSecProps = "noplain,noanonymous"; + olcAuthzRegexp = let + authz-regex-entry = i: { regex, target}: + "{${i}}\"${rx}\" \"${target}\""; + in imap0 authz-regex-entry [ + { + regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$"; + target = "cn=$1,ou=hosts,dc=fudo,dc=org"; + } + { + regex = "^uid=[^,/]+/root,cn=fudo.org,cn=gssapi,cn=auth$"; + target = "cn=admin,dc=fudo,dc=org"; + } + { + regex = "^uid=([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$"; + target = "uid=$1,ou=members,dc=fudo,dc=org"; + } + { + regex = "^uid=host/([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$"; + target = "cn=$1,ou=hosts,dc=fudo,dc=org"; + } + { + regex = "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$"; + target = "cn=admin,dc=fudo,dc=org"; + } + ]; + }; + children = { + "olcDatabase{-1}frontend" = { + attrs = { + objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ]; + olcDatabase = "{-1}frontend"; + olcAccess = makeAccess { + "*" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "dn.exact=cn=admin,dc=fudo,dc=org" = "manage"; + "*" = "none"; + }; + }; + }; + }; + "olcDatabase{0}config" = { + attrs = { + objectClass = [ "olcDatabaseConfig" ]; + olcDatabase = "{0}config"; + olcAccess = [ "by * none" ]; + }; + }; + "olcDatabase{1}mdb" = { + attrs = { + objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ]; + olcDatabase = "{1}mdb"; + olcSuffix = cfg.base; + # olcRootDN = "cn=admin,${cfg.base}"; + # olcRootPW = FIXME; # NOTE: this should be hashed... + olcDbDirectory = cfg.database-directory; + olcDbIndex = [ "objectClass eq" "uid eq" ]; + olcAccess = makeAccess { + "attrs=userPassword,shadowLastChange" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "dn.exact=cn=auth_reader,${cfg.base}" = "read"; + "dn.exact=cn=replicator,${cfg.base}" = "read"; + "self" = "write"; + "*" = "auth"; + }; + "dn.base=cn=admin,ou=groups,${cfg.base}" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "dn.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "users" = "read"; + "*" = "none"; + }; + "dn.subtree=ou=groups,${cfg.base} attrs=memberUid" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "dn.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write"; + "group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "users" = "read"; + "*" = "none"; + }; + "dn.subtree=ou=members,${cfg.base} attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "dn.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "dn.exact=cn=user_db_reader,${cfg.base}" = "read"; + "users" = "read"; + "*" = "none"; + }; + "*" = { + "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage"; + "dn.exact=cn=admin,ou=groups,${cfg.base}" = "write"; + "group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "read"; + "users" = "read"; + "*" = "none"; + }; + }; + }; + }; + }; + }; declarativeContents = '' dn: ${cfg.base} diff --git a/lib/fudo/mail-container.nix b/lib/fudo/mail-container.nix index 5cae00e..1b7ed5a 100644 --- a/lib/fudo/mail-container.nix +++ b/lib/fudo/mail-container.nix @@ -1,67 +1,32 @@ { lib, config, ... }: with lib; let + hostname = config.instance.hostname; cfg = config.fudo.mail-server; container-maildir = "/var/lib/mail"; container-statedir = "/var/lib/mail-state"; - container-shared = "container/mail-server"; - container-postfix-cert = "${container-shared}/postfix/cert.pem"; - container-postfix-key = "${container-shared}/postfix/key.pem"; - container-dovecot-cert = "${container-shared}/dovecot/cert.pem"; - container-dovecot-key = "${container-shared}/dovecot/key.pem"; - container-fudo-ca-cert = "${container-shared}/fudo-ca.pem"; # Don't bother with group-id, nixos doesn't seem to use it anyway container-mail-user = "mailer"; container-mail-user-id = 542; container-mail-group = "mailer"; - fudo-cfg = config.fudo.common; in rec { - options.fudo.mail-server.container = { - ldap-url = mkOption { - type = types.str; - description = "URL of the LDAP server to use for authentication."; - example = "ldaps://auth.fudo.org/"; - }; - }; - - config = mkIf (cfg.enableContainer && !cfg.enable) { - - # Disable postfix on thi host--it'll be run in the container instead + config = mkIf (cfg.enableContainer) { + # Disable postfix on this host--it'll be run in the container instead services.postfix.enable = false; - # Copy data intended for the container to a path in /etc which can be - # bind-mounted. - environment.etc = { - "${container-postfix-cert}" = { - mode = "0444"; - source = cfg.postfix.ssl-certificate; - }; - - "${container-postfix-key}" = { - mode = "0400"; - source = cfg.postfix.ssl-private-key; - }; - - "${container-dovecot-cert}" = { - mode = "0444"; - source = cfg.dovecot.ssl-certificate; - }; - - "${container-dovecot-key}" = { - mode = "0400"; - source = cfg.dovecot.ssl-private-key; - }; - - "${container-fudo-ca-cert}" = { - mode = "0444"; - source = "/etc/nixos/static/fudo_ca.pem"; + fudo.acme.host-domains.${hostname}.${cfg.mail-hostname} = { + local-copies = { + postfix = { + user = "root"; + }; + dovecot-cert = { + user = "root"; + }; }; }; - security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email; - services.nginx = mkIf cfg.monitoring { enable = true; @@ -71,14 +36,15 @@ in rec { proxy_set_header Host $host; ''; trusted-network-string = - optionalString ((length fudo-cfg.local-networks) > 0) + optionalString ((length config.instance.local-networks) > 0) (concatStringsSep "\n" - (map (network: "allow ${network};") fudo-cfg.local-networks)) + '' + (map (network: "allow ${network};") + config.instance.local-networks)) + '' deny all;''; in { - "${cfg.hostname}" = { + "${cfg.mail-hostname}" = { enableACME = true; forceSSL = true; @@ -119,7 +85,9 @@ in rec { autoStart = true; - bindMounts = { + bindMounts = let + cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies; + in { "${container-maildir}" = { hostPath = cfg.mail-directory; isReadOnly = false; @@ -134,30 +102,74 @@ in rec { hostPath = "/etc/${container-shared}"; isReadOnly = true; }; + + "/run/mail/certs/postfix/cert.pem" = { + hostPath = cert-copies.postfix.certificate; + isReadOnly = true; + }; + + "/run/mail/certs/postfix/key.pem" = { + hostPath = cert-copies.postfix.private-key; + isReadOnly = true; + }; + + "/run/mail/certs/dovecot/cert.pem" = { + hostPath = cert-copies.dovecot.certificate; + isReadOnly = true; + }; + + "/run/mail/certs/dovecot/key.pem" = { + hostPath = cert-copies.dovecot.private-key; + isReadOnly = true; + }; }; + imports = let + initialize-host = import ../../initialize-host.nix; + build-timestamp = config.instance.build-timestamp; + site = config.instance.site; + domain = config.instance.domain; + profile = "container"; + in [ + (initialize-host { + inherit + lib + pkgs + build-timestamp + site + domain + profile; + hostname = "mail-container"; + }) + ]; + config = { config, pkgs, ... }: { - environment.systemPackages = with pkgs; [ nmap ]; - - imports = [ ./mail.nix ]; - - environment = { - etc = { - "postfix-certs/key.pem" = { - source = "/etc/${container-postfix-key}"; - user = config.services.postfix.user; - mode = "0400"; - }; - - "dovecot-certs/key.pem" = { - source = "/etc/${container-dovecot-key}"; - user = config.services.dovecot2.user; - mode = "0400"; - }; + environment.etc = { + "mail-server/postfix/cert.pem" = { + source = "/run/mail/certs/postfix/cert.pem"; + user = config.services.postfix.user; + mode = "0444"; + }; + "mail-server/postfix/key.pem" = { + source = "/run/mail/certs/postfix/key.pem"; + user = config.services.postfix.user; + mode = "0400"; + }; + "mail-server/dovecot/cert.pem" = { + source = "/run/mail/certs/dovecot/cert.pem"; + user = config.services.dovecot.user; + mode = "0444"; + }; + "mail-server/dovecot/key.pem" = { + source = "/run/mail/certs/dovecot/key.pem"; + user = config.services.dovecot.user; + mode = "0400"; }; }; + imports = [ ./mail.nix ]; + fudo.mail-server = { enable = true; hostname = cfg.hostname; @@ -169,14 +181,15 @@ in rec { state-directory = container-statedir; mail-directory = container-maildir; - postfix.ssl-certificate = "/etc/${container-postfix-cert}"; - postfix.ssl-private-key = "/etc/postfix-certs/key.pem"; + postfix = { + ssl-certificate = "/etc/mail-server/postfix/cert.pem"; + ssl-private-key = "/etc/mail-server/postfix/key.pem"; + }; dovecot = { - ssl-certificate = "/etc/${container-dovecot-cert}"; - ssl-private-key = "/etc/dovecot-certs/key.pem"; + ssl-certificate = "/etc/mail-server/dovecot/cert.pem"; + ssl-private-key = "/etc/mail-server/dovecot/key.pem"; ldap = { - # ca = "/etc/${container-fudo-ca-cert}"; server-urls = cfg.dovecot.ldap.server-urls; reader-dn = cfg.dovecot.ldap.reader-dn; reader-passwd = cfg.dovecot.ldap.reader-passwd; diff --git a/lib/fudo/mail.nix b/lib/fudo/mail.nix index d59d626..e48472a 100644 --- a/lib/fudo/mail.nix +++ b/lib/fudo/mail.nix @@ -21,11 +21,18 @@ in { description = "The main and default domain name for this email server."; }; - hostname = mkOption { + mail-hostname = mkOption { type = types.str; description = "The domain name to use for the mail server."; }; + + ldap-url = mkOption { + type = types.str; + description = "URL of the LDAP server to use for authentication."; + example = "ldaps://auth.fudo.org/"; + }; + monitoring = mkEnableOption "Enable monitoring for the mail server."; mail-user = mkOption { @@ -176,7 +183,7 @@ in { ./mail/clamav.nix ]; - config = mkIf cfg.enable { + config = mkIf cfg.enable { networking.firewall = { allowedTCPPorts = [ 25 110 143 587 993 995 ]; }; diff --git a/lib/fudo/prometheus.nix b/lib/fudo/prometheus.nix index f8c38d4..3a98d06 100644 --- a/lib/fudo/prometheus.nix +++ b/lib/fudo/prometheus.nix @@ -4,7 +4,6 @@ with lib; let inherit (lib.strings) concatStringsSep; cfg = config.fudo.prometheus; - fudo-cfg = config.fudo.common; in { @@ -76,9 +75,6 @@ in { }; config = mkIf cfg.enable { - - security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email; - services.nginx = { enable = true; @@ -111,7 +107,8 @@ in { webExternalUrl = "https://${cfg.hostname}"; - listenAddress = "127.0.0.1:9090"; + listenAddress = "127.0.0.1"; + port = 9090; scrapeConfigs = [ { diff --git a/lib/fudo/webmail.nix b/lib/fudo/webmail.nix index 5ce9a5b..ae04523 100644 --- a/lib/fudo/webmail.nix +++ b/lib/fudo/webmail.nix @@ -9,7 +9,10 @@ let webmail-user = cfg.user; webmail-group = cfg.group; - base-data-path = "/var/run/rainloop"; + base-data-path = "/run/rainloop"; + + concatMapAttrs = f: attrs: + foldr (a: b: a // b) {} (mapAttrsToList f attrs); fastcgi-conf = builtins.toFile "fastcgi.conf" '' fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; @@ -301,7 +304,7 @@ in { site-config-file = builtins.toFile "${site}-rainloop.cfg" (import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version); - domain-cfg-file = builtins.toFile "${site}-domain.cfg" '' + domain-config-file = builtins.toFile "${site}-domain.cfg" '' imap_host = "${site-cfg.mail-server}" imap_port = 143 imap_secure = "TLS" diff --git a/lib/hosts.nix b/lib/hosts.nix index da4ca03..ba83187 100644 --- a/lib/hosts.nix +++ b/lib/hosts.nix @@ -1,16 +1,24 @@ { lib, ... }: with lib; -{ +let + hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename; + + is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null; + is-regular-file = filename: type: type == "regular" || type == "link"; + + host-files = host-path: + attrNames + (filterAttrs is-nix-file + (filterAttrs is-regular-file + (builtins.readDir host-path))); + + hosts = host-path: + map hostname-from-file (host-files host-path); +in { base-host-config = host-path: let - hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename; - - is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null; - is-regular-file = filename: type: type == "regular" || type == "link"; - - host-files = attrNames (filterAttrs is-nix-file (filterAttrs is-regular-file (builtins.readDir host-path))); - hosts = map hostname-from-file host-files; - load-host-file = hostname: import (host-path + "/${hostname}.nix"); - in genAttrs hosts (hostname: load-host-file hostname); + in genAttrs (hosts host-path) (hostname: load-host-file hostname); + + host-list = host-path: hosts host-path; } diff --git a/lib/informis/cl-gemini.nix b/lib/informis/cl-gemini.nix index 8b903dc..71c0a09 100644 --- a/lib/informis/cl-gemini.nix +++ b/lib/informis/cl-gemini.nix @@ -4,8 +4,6 @@ with lib; let cfg = config.informis.cl-gemini; - lisp-helper = import ../lisp.nix { inherit pkgs; }; - feedOpts = { ... }: with types; { options = { url = mkOption { @@ -163,7 +161,7 @@ in { GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive; GEMINI_FEEDS = "${generate-feeds cfg.feeds}"; - CL_SOURCE_REGISTRY = "${lisp-helper.lisp-source-registry pkgs.cl-gemini}"; + CL_SOURCE_REGISTRY = "${pkgs.lib.fudo.lisp.lisp-source-registry pkgs.cl-gemini}"; }; path = with pkgs; [ diff --git a/lib/instance.nix b/lib/instance.nix index 6c3155a..4536992 100644 --- a/lib/instance.nix +++ b/lib/instance.nix @@ -51,6 +51,11 @@ in { type = attrsOf (submodule user.userOpts); description = "List of users who should have access to the local host"; }; + + build-seed = mkOption { + type = str; + description = "Seed used to generate configuration."; + }; }; config = let @@ -86,15 +91,21 @@ in { config.fudo.domains.${local-domain}.local-networks // config.fudo.sites.${local-site}.local-networks; + local-profile = host.profile; + + build-seed = builtins.readFile config.fudo.secrets.files.build-seed; + in { instance = { - local-domain = local-domain; - local-site = local-site; - local-users = local-users; - local-admins = local-admins; - local-groups = local-groups; - local-hosts = local-hosts; - local-profile = host.profile; + inherit + build-seed + local-domain + local-site + local-users + local-admins + local-groups + local-hosts + local-profile; }; }; } diff --git a/lib/ip.nix b/lib/ip.nix index 0b7667b..33d477a 100644 --- a/lib/ip.nix +++ b/lib/ip.nix @@ -1,6 +1,6 @@ -{ lib, ... }: +{ pkgs, ... }: -with lib; +with pkgs.lib; let pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1)); diff --git a/lib/lisp.nix b/lib/lisp.nix index 4b6d63a..1eea537 100644 --- a/lib/lisp.nix +++ b/lib/lisp.nix @@ -1,8 +1,7 @@ { pkgs, ... }: with pkgs.lib; -let -in rec { +rec { gather-dependencies = pkg: unique (pkg.propagatedBuildInputs ++ (concatMap gather-dependencies pkg.propagatedBuildInputs)); lisp-source-registry = pkg: concatStringsSep ":" (map (p: "${p}//") (gather-dependencies pkg)); diff --git a/lib/overlay.nix b/lib/overlay.nix index ab20609..6c613a9 100644 --- a/lib/overlay.nix +++ b/lib/overlay.nix @@ -1,10 +1,12 @@ -(final: prev: let - ip = import ./ip.nix { lib = prev.lib; }; - dns = import ./dns.nix { lib = prev.lib; }; -in { +(final: prev: { lib = prev.lib // { - fudo = { - inherit ip dns; + fudo = let + lib = prev.lib; + in { + ip = import ./ip.nix { pkgs = prev; }; + dns = import ./dns.nix { pkgs = prev;}; + passwd = import ./passwd.nix { pkgs = prev;}; + lisp = import ./lisp.nix { pkgs = prev;}; }; }; }) diff --git a/lib/passwd.nix b/lib/passwd.nix index 1efb666..720ed18 100644 --- a/lib/passwd.nix +++ b/lib/passwd.nix @@ -1,6 +1,6 @@ -{ lib, ... }: +{ pkgs, ... }: -with lib; +with pkgs.lib; let hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation { name = "${name}-ldap-passwd"; @@ -14,7 +14,7 @@ let ''; installPhase = '' - mkdir $out + mkdir -p $out mv ldap-passwd $out ''; }; @@ -26,23 +26,35 @@ let generate-random-passwd = name: length: pkgs.stdenv.mkDerivation { name = "${name}-random-passwd"; - phases = [ "buildPhase" "installPhase" ]; + phases = [ "installPhase" ]; buildInputs = with pkgs; [ pwgen ]; - buildPhase = '' - pwgen --symbols --num-passwords=1 ${length} > passwd - ''; - installPhase = '' - mkdir $out - mv passwd $out + pwgen --secure --num-passwords=1 ${length} > $out ''; }; + generate-stablerandom-passwd = name: { seed, length ? 20, ... }: + pkgs.stdenv.mkDerivation { + name = "${name}-stablerandom-passwd"; + + phases = [ "installPhase" ]; + + buildInputs = with pkgs; [ pwgen ]; + + installPhase = '' + echo "${name}-${seed}" > seedfile + pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out + ''; + }; + in { hash-ldap-passwd = hash-ldap-passwd; random-passwd-file = name: length: - toPath "${generate-random-passwd name length}/passwd"; + builtins.toPath "${generate-random-passwd name length}"; + + stablerandom-passwd-file = name: seed: + builtins.toPath "${generate-stablerandom-passwd name { seed = seed; }}"; } diff --git a/lib/types/host.nix b/lib/types/host.nix index 6bcb76c..c01d0a4 100644 --- a/lib/types/host.nix +++ b/lib/types/host.nix @@ -1,7 +1,10 @@ { lib, ... }: with lib; -rec { +let + passwd = import ../passwd.nix { inherit lib; }; + +in rec { encryptedFSOpts = { ... }: let mountpoint = { mp, ... }: { options = with types; { @@ -31,6 +34,12 @@ rec { ''; default = [ ]; }; + + world-readable = mkOption { + type = bool; + description = "Whether to leave the top level world-readable."; + default = true; + }; }; }; in { @@ -81,7 +90,9 @@ rec { }; }; - hostOpts = { hostname, ... }: { + hostOpts = { name, ... }: let + hostname = name; + in { options = with types; { master-key = mkOption { type = nullOr (submodule masterKeyOpts); @@ -284,6 +295,11 @@ rec { description = "Configuration parameters to set up initrd SSH network."; default = null; }; + + backplane-password-file = mkOption { + options = path; + description = "File containing the password used by this host to connect to the backplane."; + }; }; }; } diff --git a/lib/types/user.nix b/lib/types/user.nix index 49e26a5..b85cab5 100644 --- a/lib/types/user.nix +++ b/lib/types/user.nix @@ -2,12 +2,12 @@ with lib; rec { - systemUserOpts = { username, ... }: { + systemUserOpts = { name, ... }: { options = with lib.types; { username = mkOption { type = str; description = "The system user's login name."; - default = username; + default = name; }; description = mkOption { @@ -23,7 +23,9 @@ rec { }; }; - userOpts = { username, ... }: { + userOpts = { name, ... }: let + username = name; + in { options = with lib.types; { username = mkOption { type = str;