From df53c13bb7f15336a2b0f110cef0a57af7089737 Mon Sep 17 00:00:00 2001 From: niten Date: Wed, 1 Jun 2022 13:57:36 -0700 Subject: [PATCH] Many changes --- lib/fudo/chat.nix | 144 +++++++++++--------- lib/fudo/domains.nix | 306 ++++++++++++++++++++++-------------------- lib/fudo/postgres.nix | 18 +++ lib/lib/network.nix | 67 +++++---- 4 files changed, 293 insertions(+), 242 deletions(-) diff --git a/lib/fudo/chat.nix b/lib/fudo/chat.nix index 8e19f70..9ec9e14 100644 --- a/lib/fudo/chat.nix +++ b/lib/fudo/chat.nix @@ -102,7 +102,7 @@ in { EmailSettings = { RequireEmailVerification = true; SMTPServer = cfg.smtp.server; - SMTPPort = 587; + SMTPPort = "587"; EnableSMTPAuth = true; SMTPUsername = cfg.smtp.user; SMTPPassword = "__SMTP_PASSWD__"; @@ -113,22 +113,20 @@ in { }; EnableEmailInvitations = true; SqlSettings.DriverName = "postgres"; - SqlSettings.DataSource = "postgres://${ - cfg.database.user - }:__DATABASE_PASSWORD__@${ - cfg.database.hostname - }:5432/${ - cfg.database.name - }"; + SqlSettings.DataSource = + "postgres://${cfg.database.user}:__DATABASE_PASSWORD__@${cfg.database.hostname}:5432/${cfg.database.name}"; }; mattermost-config-file-template = - pkgs.writeText "mattermost-config.json.template" (builtins.toJSON modified-config); + pkgs.writeText "mattermost-config.json.template" + (builtins.toJSON modified-config); - generate-mattermost-config = target: template: smtp-passwd-file: db-passwd-file: + generate-mattermost-config = + target: template: smtp-passwd-file: db-passwd-file: pkgs.writeScript "mattermost-config-generator.sh" '' + rm ${target} SMTP_PASSWD=$( cat ${smtp-passwd-file} ) DATABASE_PASSWORD=$( cat ${db-passwd-file} ) - sed -e 's/__SMTP_PASSWD__/"$SMTP_PASSWD"/' -e 's/__DATABASE_PASSWORD__/"$DATABASE_PASSWORD"/' ${template} > ${target} + sed -e "s/__SMTP_PASSWD__/$SMTP_PASSWD/" -e "s/__DATABASE_PASSWORD__/$DATABASE_PASSWORD/" ${template} > ${target} ''; in { @@ -136,10 +134,10 @@ in { users = { ${cfg.user} = { isSystemUser = true; - group = mattermost-group; + group = cfg.group; }; }; - groups.${cfg.group}.members = [ cfg.user ]; + groups = { ${cfg.group} = { members = [ cfg.user ]; }; }; }; fudo.system.services.mattermost = { @@ -147,17 +145,25 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - preStart = '' - ${generate-mattermost-config - mattermost-config-target - mattermost-config-file-template - cfg.smtp.password-file - cfg.database.password-file} - cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json - cp -uRL ${pkg}/client ${cfg.state-directory} - chown ${cfg.user}:${cfg.group} ${cfg.state-directory}/client - chmod 0750 ${cfg.state-directory}/client - ''; + preStart = + let config-target = "${cfg.state-directory}/config/config.json"; + in '' + if [ ! -f ${config-target} ]; then + ${ + generate-mattermost-config mattermost-config-target + mattermost-config-file-template cfg.smtp.password-file + cfg.database.password-file + } + cp ${mattermost-config-target} ${config-target} + chown ${cfg.user}:${cfg.group} ${config-target} + chmod 640 ${config-target} + fi + if [ ! -e ${cfg.state-directory} ]; then + cp -uRL ${pkg}/client ${cfg.state-directory} + chown ${cfg.user}:${cfg.group} ${cfg.state-directory}/client + chmod 0750 ${cfg.state-directory}/client + fi + ''; execStart = "${pkg}/bin/mattermost"; workingDirectory = cfg.state-directory; user = cfg.user; @@ -167,8 +173,9 @@ in { systemd = { tmpfiles.rules = [ - "d ${cfg.state-directory} 0750 ${cfg.user} ${cfg.group} - -" - "d ${cfg.state-directory}/config 0750 ${cfg.user} ${cfg.group} - -" + "d ${cfg.state-directory} 0750 ${cfg.user} - - -" + "d ${cfg.state-directory}/config 0750 ${cfg.user} - - -" + "d ${dirOf mattermost-config-target} 0750 ${cfg.user} - - -" "L ${cfg.state-directory}/bin - - - - ${pkg}/bin" "L ${cfg.state-directory}/fonts - - - - ${pkg}/fonts" "L ${cfg.state-directory}/i18n - - - - ${pkg}/i18n" @@ -183,6 +190,8 @@ in { proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off; ''; + recommendedProxySettings = true; + virtualHosts = { "${cfg.hostname}" = { enableACME = true; @@ -190,51 +199,52 @@ in { locations."/" = { proxyPass = "http://127.0.0.1:8065"; + proxyWebsockets = true; - extraConfig = '' - client_max_body_size 50M; - proxy_set_header Connection ""; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-By $server_addr:$server_port; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - proxy_buffers 256 16k; - proxy_buffer_size 16k; - proxy_read_timeout 600s; - proxy_cache mattermost_cache; - proxy_cache_revalidate on; - proxy_cache_min_uses 2; - proxy_cache_use_stale timeout; - proxy_cache_lock on; - proxy_http_version 1.1; - ''; + # extraConfig = '' + # client_max_body_size 50M; + # proxy_set_header Connection ""; + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-By $server_addr:$server_port; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto $scheme; + # proxy_set_header X-Frame-Options SAMEORIGIN; + # proxy_buffers 256 16k; + # proxy_buffer_size 16k; + # proxy_read_timeout 600s; + # proxy_cache mattermost_cache; + # proxy_cache_revalidate on; + # proxy_cache_min_uses 2; + # proxy_cache_use_stale timeout; + # proxy_cache_lock on; + # proxy_http_version 1.1; + # ''; }; - locations."~ /api/v[0-9]+/(users/)?websocket$" = { - proxyPass = "http://127.0.0.1:8065"; + # locations."~ /api/v[0-9]+/(users/)?websocket$" = { + # proxyPass = "http://127.0.0.1:8065"; - extraConfig = '' - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - client_max_body_size 50M; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-By $server_addr:$server_port; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - proxy_buffers 256 16k; - proxy_buffer_size 16k; - client_body_timeout 60; - send_timeout 300; - lingering_timeout 5; - proxy_connect_timeout 90; - proxy_send_timeout 300; - proxy_read_timeout 90s; - ''; - }; + # extraConfig = '' + # proxy_set_header Upgrade $http_upgrade; + # proxy_set_header Connection "upgrade"; + # client_max_body_size 50M; + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-By $server_addr:$server_port; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto $scheme; + # proxy_set_header X-Frame-Options SAMEORIGIN; + # proxy_buffers 256 16k; + # proxy_buffer_size 16k; + # client_body_timeout 60; + # send_timeout 300; + # lingering_timeout 5; + # proxy_connect_timeout 90; + # proxy_send_timeout 300; + # proxy_read_timeout 90s; + # ''; + # }; }; }; }; diff --git a/lib/fudo/domains.nix b/lib/fudo/domains.nix index 7548a44..a915e7a 100644 --- a/lib/fudo/domains.nix +++ b/lib/fudo/domains.nix @@ -5,171 +5,185 @@ let hostname = config.instance.hostname; domain = config.instance.local-domain; - domainOpts = { name, ... }: let - domain = name; - in { - options = with types; { - domain = mkOption { - type = str; - description = "Domain name."; - default = domain; - }; - - local-networks = mkOption { - type = listOf str; - description = - "A list of networks to be considered trusted on this network."; - default = [ ]; - }; - - local-users = mkOption { - type = listOf str; - description = - "A list of users who should have local (i.e. login) access to _all_ hosts in this domain."; - default = [ ]; - }; - - local-admins = mkOption { - type = listOf str; - description = - "A list of users who should have admin access to _all_ hosts in this domain."; - default = [ ]; - }; - - local-groups = mkOption { - type = listOf str; - description = "List of groups which should exist within this domain."; - default = [ ]; - }; - - admin-email = mkOption { - type = str; - description = "Email for the administrator of this domain."; - default = "admin@${domain}"; - }; - - grafana-hosts = mkOption { - type = listOf str; - description = "List of hosts acting as Grafana metric analyzers. Requires prometheus hosts as well."; - default = []; - }; - - log-aggregator = mkOption { - type = nullOr str; - description = "Host which will accept incoming log pushes."; - default = null; - }; - - postgresql-server = mkOption { - type = nullOr str; - description = "Hostname acting as the local PostgreSQL server."; - default = null; - }; - - backplane = mkOption { - type = nullOr (submodule { - options = { - nameserver = mkOption { - type = nullOr str; - description = "Host acting as backplane dynamic DNS server."; - default = null; - }; - - dns-service = mkOption { - type = nullOr str; - description = "DNS backplane service host."; - default = null; - }; - - domain = mkOption { - type = str; - description = "Domain name of the dynamic zone served by this server."; - }; - }; - }); - description = "Backplane configuration."; - default = null; - }; - - wireguard = { - gateway = mkOption { + domainOpts = { name, ... }: + let domain = name; + in { + options = with types; { + domain = mkOption { type = str; - description = "Host serving as WireGuard gateway for this domain."; + description = "Domain name."; + default = domain; }; - network = mkOption { - type = str; - description = "IP subnet used for WireGuard clients."; - default = "172.16.0.0/16"; + local-networks = mkOption { + type = listOf str; + description = + "A list of networks to be considered trusted on this network."; + default = [ ]; }; - routed-network = mkOption { + local-users = mkOption { + type = listOf str; + description = + "A list of users who should have local (i.e. login) access to _all_ hosts in this domain."; + default = [ ]; + }; + + local-admins = mkOption { + type = listOf str; + description = + "A list of users who should have admin access to _all_ hosts in this domain."; + default = [ ]; + }; + + local-groups = mkOption { + type = listOf str; + description = "List of groups which should exist within this domain."; + default = [ ]; + }; + + admin-email = mkOption { + type = str; + description = "Email for the administrator of this domain."; + default = "admin@${domain}"; + }; + + grafana-hosts = mkOption { + type = listOf str; + description = + "List of hosts acting as Grafana metric analyzers. Requires prometheus hosts as well."; + default = [ ]; + }; + + log-aggregator = mkOption { type = nullOr str; - description = "Subnet of larger network for which we NAT traffic."; - default = "172.16.16.0/20"; + description = "Host which will accept incoming log pushes."; + default = null; }; - }; - gssapi-realm = mkOption { - type = str; - description = "GSSAPI (i.e. Kerberos) realm of this domain."; - }; + postgresql-server = mkOption { + type = nullOr str; + description = "Hostname acting as the local PostgreSQL server."; + default = null; + }; - kerberos-master = mkOption { - type = nullOr str; - description = "Hostname of the Kerberos master server for the domain, if applicable."; - default = null; - }; + chat-server = mkOption { + type = nullOr str; + description = + "Hostname acting as the domain chat server (using Mattermost)."; + default = null; + }; - kerberos-slaves = mkOption { - type = listOf str; - description = "List of hosts acting as Kerberos slaves for the domain."; - default = []; - }; + backplane = mkOption { + type = nullOr (submodule { + options = { + nameserver = mkOption { + type = nullOr str; + description = "Host acting as backplane dynamic DNS server."; + default = null; + }; - ldap-servers = mkOption { - type = listOf str; - description = "List of hosts acting as LDAP authentication servers for the domain."; - default = []; - }; + dns-service = mkOption { + type = nullOr str; + description = "DNS backplane service host."; + default = null; + }; - prometheus-hosts = mkOption { - type = listOf str; - description = "List of hosts acting aas prometheus metric scrapers for hosts in this network."; - default = []; - }; + domain = mkOption { + type = str; + description = + "Domain name of the dynamic zone served by this server."; + }; + }; + }); + description = "Backplane configuration."; + default = null; + }; - primary-nameserver = mkOption { - type = nullOr str; - description = "Hostname of the primary nameserver for this domain."; - default = null; - }; + wireguard = { + gateway = mkOption { + type = str; + description = "Host serving as WireGuard gateway for this domain."; + }; - secondary-nameservers = mkOption { - type = listOf str; - description = "List of hostnames of slave nameservers for this domain."; - default = []; - }; + network = mkOption { + type = str; + description = "IP subnet used for WireGuard clients."; + default = "172.16.0.0/16"; + }; - primary-mailserver = mkOption { - type = nullOr str; - description = "Hostname of the primary mail server for this domain."; - default = null; - }; + routed-network = mkOption { + type = nullOr str; + description = "Subnet of larger network for which we NAT traffic."; + default = "172.16.16.0/20"; + }; + }; - xmpp-servers = mkOption { - type = listOf str; - description = "Hostnames of the domain XMPP servers."; - default = []; - }; + gssapi-realm = mkOption { + type = str; + description = "GSSAPI (i.e. Kerberos) realm of this domain."; + }; - zone = mkOption { - type = nullOr str; - description = "Name of the DNS zone associated with domain."; - default = null; + kerberos-master = mkOption { + type = nullOr str; + description = + "Hostname of the Kerberos master server for the domain, if applicable."; + default = null; + }; + + kerberos-slaves = mkOption { + type = listOf str; + description = + "List of hosts acting as Kerberos slaves for the domain."; + default = [ ]; + }; + + ldap-servers = mkOption { + type = listOf str; + description = + "List of hosts acting as LDAP authentication servers for the domain."; + default = [ ]; + }; + + prometheus-hosts = mkOption { + type = listOf str; + description = + "List of hosts acting aas prometheus metric scrapers for hosts in this network."; + default = [ ]; + }; + + primary-nameserver = mkOption { + type = nullOr str; + description = "Hostname of the primary nameserver for this domain."; + default = null; + }; + + secondary-nameservers = mkOption { + type = listOf str; + description = + "List of hostnames of slave nameservers for this domain."; + default = [ ]; + }; + + primary-mailserver = mkOption { + type = nullOr str; + description = "Hostname of the primary mail server for this domain."; + default = null; + }; + + xmpp-servers = mkOption { + type = listOf str; + description = "Hostnames of the domain XMPP servers."; + default = [ ]; + }; + + zone = mkOption { + type = nullOr str; + description = "Name of the DNS zone associated with domain."; + default = null; + }; }; }; - }; in { options.fudo.domains = mkOption { diff --git a/lib/fudo/postgres.nix b/lib/fudo/postgres.nix index cabc0bc..a3d8595 100644 --- a/lib/fudo/postgres.nix +++ b/lib/fudo/postgres.nix @@ -64,6 +64,13 @@ let "A list of users who should have full access to this database."; default = [ ]; }; + + extensions = mkOption { + type = listOf str; + description = + "A list of extensions which should be created for this database."; + default = [ ]; + }; }; }; @@ -121,6 +128,13 @@ let ${network-entries user db} '') (attrNames opts.databases))) (filterPasswordedUsers users)); + enableExtensionSql = ext: ''CREATE EXTENSION IF NOT EXISTS "${ext}";''; + + enableDatabaseExtensionsSql = database: databaseOpts: '' + \c ${database} + ${join-lines (map enableExtensionSql databaseOpts.extensions)} + ''; + userTableAccessSql = user: entity: access: "GRANT ${access} ON ${entity} TO ${user};"; userDatabaseAccessSql = user: database: dbOpts: '' @@ -382,8 +396,12 @@ in { allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;"; extra-settings-sql = pkgs.writeText "settings.sql" '' + ${join-lines + (mapAttrsToList enableDatabaseExtensionsSql cfg.databases)} + ${concatStringsSep "\n" (map allow-user-login (mapAttrsToList (key: val: key) cfg.users))} + ${usersAccessSql cfg.users} ''; in pkgs.writeShellScript "postgresql-finalizer.sh" '' diff --git a/lib/lib/network.nix b/lib/lib/network.nix index 960eae3..25e45ba 100644 --- a/lib/lib/network.nix +++ b/lib/lib/network.nix @@ -2,13 +2,14 @@ with pkgs.lib; let - generate-mac-address = hostname: interface: pkgs.stdenv.mkDerivation { - name = "mk-mac-${hostname}-${interface}"; - phases = [ "installPhase" ]; - installPhase = '' - echo ${hostname}-${interface} | sha1sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/' > $out - ''; - }; + generate-mac-address = hostname: interface: + pkgs.stdenv.mkDerivation { + name = "mk-mac-${hostname}-${interface}"; + phases = [ "installPhase" ]; + installPhase = '' + echo ${hostname}-${interface} | sha1sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/' > $out + ''; + }; # dropUntil = pred: lst: let # drop-until-helper = pred: lst: @@ -28,32 +29,40 @@ let # (lib.reverseList lines-front-stripped)); # in concatStringsSep "\n" lines-rear-stripped; - host-ipv4 = config: hostname: let - domain = config.fudo.hosts.${hostname}.domain; - host-network = config.fudo.zones.${domain}; - in host-network.hosts.${hostname}.ipv4-address; + host-ipv4 = config: hostname: + let + domain = config.fudo.hosts.${hostname}.domain; + host-network = config.fudo.zones.${domain}; + in host-network.hosts.${hostname}.ipv4-address; - host-ipv6 = config: hostname: let - domain = config.fudo.hosts.${hostname}.domain; - host-network = config.fudo.zones.${domain}; - in host-network.hosts.${hostname}.ipv6-address; + host-ipv6 = config: hostname: + let + domain = config.fudo.hosts.${hostname}.domain; + host-network = config.fudo.zones.${domain}; + in host-network.hosts.${hostname}.ipv6-address; - host-ips = config: hostname: let - ipv4 = host-ipv4 config hostname; - ipv6 = host-ipv6 config hostname; - not-null = o: o != null; - in filter not-null [ ipv4 ipv6 ]; + host-ips = config: hostname: + let + ipv4 = host-ipv4 config hostname; + ipv6 = host-ipv6 config hostname; + not-null = o: o != null; + in filter not-null [ ipv4 ipv6 ]; - site-gateway = config: site-name: let - site = config.fudo.sites.${site-name}; - in if (site.local-gateway != null) - then host-ipv4 config site.local-gateway - else site.gateway-v4; + site-gateway = config: site-name: + let site = config.fudo.sites.${site-name}; + in if (site.local-gateway != null) then + host-ipv4 config site.local-gateway + else + site.gateway-v4; + + host-fqdn = config: hostname: + let domain-name = config.fudo.hosts.${hostname}.domain; + in "${hostname}.${domain-name}"; in { - inherit host-ipv4 host-ipv6 host-ips site-gateway; + inherit host-ipv4 host-ipv6 host-ips site-gateway host-fqdn; - generate-mac-address = hostname: interface: let - pkg = generate-mac-address hostname interface; - in removeSuffix "\n" (builtins.readFile "${pkg}"); + generate-mac-address = hostname: interface: + let pkg = generate-mac-address hostname interface; + in removeSuffix "\n" (builtins.readFile "${pkg}"); }