diff options
Diffstat (limited to '')
| -rw-r--r-- | modules/vhack/ma/mail/module.nix | 374 | ||||
| -rw-r--r-- | modules/vhack/ma/mastodon/module.nix (renamed from modules/by-name/ma/mastodon/module.nix) | 27 | ||||
| -rw-r--r-- | modules/vhack/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch (renamed from modules/by-name/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch) | 28 | ||||
| -rw-r--r-- | modules/vhack/ma/matrix/module.nix (renamed from modules/by-name/ma/matrix/module.nix) | 80 |
4 files changed, 449 insertions, 60 deletions
diff --git a/modules/vhack/ma/mail/module.nix b/modules/vhack/ma/mail/module.nix new file mode 100644 index 0000000..034924a --- /dev/null +++ b/modules/vhack/ma/mail/module.nix @@ -0,0 +1,374 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.mail; +in { + options.vhack.mail = { + enable = lib.mkEnableOption "sophisticated mail setup with simple-nixos-mailserver"; + fqdn = lib.mkOption { + type = lib.types.str; + description = "The fqdn mailserver should be served on."; + }; + + domains = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = ["example.com"]; + default = []; + description = "The domains that this mail server serves."; + }; + + accounts = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ( + {name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + example = "user1@example.com"; + readOnly = true; + internal = true; + description = '' + The login username for this account. + ''; + }; + + hashedPassword = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "$y$j9T$vfGrwkAaXCjCEWtVNMQck1$383uIXQmn2z0hnmVAA8kwFQmjNj78.nYbvWeyNLIaP1"; + description = '' + The hashed login password for this account. + + Use `mkpasswd` to create password hashes: + ``` + nix-shell -p mkpasswd --run 'mkpasswd -s' + ``` + + :::{note} + This is a convenience option, when your threat model allows + storing hashed secrets in the world-readable Nix store. + + Passing the hash through + {option}`mailserver.accounts.<name>.hashedPasswordFile` + allows relying on filesystem discretionary access control as + another security boundary. + ::: + ''; + }; + + hashedPasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/user1-pw-hash"; + description = '' + The hashed login password for this account read from a file. + + Use `mkpasswd to create password hashes: + ``` + nix-shell -p mkpasswd --run 'mkpasswd -s' + ``` + ''; + }; + + passwordFile = lib.mkOption { + type = with lib.types; + nullOr (pathWith { + inStore = false; + }); + default = null; + example = "/run/keys/user1-pw"; + description = '' + The plaintext login password for this account read from a file. + + :::{note} + The password is hashed before it is passed on to Dovecot. + ::: + ''; + }; + + aliases = lib.mkOption { + type = with lib.types; listOf types.str; + example = [ + "abuse@example.com" + "postmaster@example.com" + ]; + default = []; + description = '' + List of additional mail addresses (aliases) that get routed to this account. + + :::{admonition} Catch-all with sending permissions + :class: tip + Configure `@example.com` to create a catch-all for this domain + that also allows sending from all addresses. + ::: + ''; + }; + + aliasesRegexp = lib.mkOption { + type = with lib.types; listOf types.str; + example = [''/^tom\..*@domain\.com$/'']; + default = []; + description = '' + Same as {option}`mailserver.accounts.<name>.aliases` but + using PCRE (Perl compatible regex). + ''; + }; + + catchAll = lib.mkOption { + type = with lib.types; listOf (enum cfg.domains); + example = [ + "example.com" + "example2.com" + ]; + default = []; + description = '' + For which domains should this account act as a catch all? + + :::{warning} + Does not allow sending from all addresses of these domains. + Use {option}`mailserver.accounts.<name>.aliases` if that + is required. + ::: + ''; + }; + + quota = lib.mkOption { + type = with lib.types; nullOr types.str; + default = null; + example = "2G"; + description = '' + The quota limit for this user. + + The value is must use a size format like `500M`, `2G`, `10G`. + + If unset, will fall back to {option}`mailserver.quota.defaults.perUser` if set. + ''; + }; + + sieveScript = lib.mkOption { + type = with lib.types; nullOr lines; + default = null; + example = '' + require ["fileinto", "mailbox"]; + + if address :is "from" "gitlab@mg.gitlab.com" { + fileinto :create "GitLab"; + stop; + } + + # This must be the last rule, it will check if list-id is set, and + # file the message into the Lists folder for further investigation + elsif header :matches "list-id" "<?*>" { + fileinto :create "Lists"; + stop; + } + ''; + description = '' + Per-user sieve script. + ''; + }; + + sendOnly = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Specifies if the account should be a send-only account. + + Emails sent to send-only accounts will + be rejected with the reason configured in + {option}`mailserver.accounts.<name>.sendOnlyRejectMessage`. + ''; + }; + + sendOnlyRejectMessage = lib.mkOption { + type = lib.types.str; + default = "This account cannot receive emails."; + description = '' + The message returned to the sender for a send-only account. + + See {option}`mailserver.accounts.<name>.sendOnly`. + ''; + }; + }; + } + ) + ); + example = lib.literalExpression '' + { + user1 = { + # This password hash leaks into the Nix store + hashedPassword = "$y$j9T$y6eZ1o.IvVNfdGMAsUEvh1$6K/llP52uw2iDh4iSwtAn54/JYy7FzCcoCHmjmx00H5"; + }; + user2 = { + # Hashed password passed as a file + hashedPasswordFile = "/run/keys/user2-pw-hash"; + }; + user3 = { + # Plaintext password file + passwordFile = "/run/keys/user3-pw"; + }; + } + ''; + description = '' + Attribute set of mail accounts. + + Each entry defines a mailbox and login credentials, where the attribute + name is used as the login username and optionally routed mail address. + + Use `mkpasswd` to generate password hashes. + ''; + default = {}; + }; + + security = lib.mkOption { + type = lib.types.submodule { + options = { + dkimKeys = lib.mkOption { + type = lib.types.nullOr (lib.types.attrsOf (lib.types.submodule { + options = { + dkimPublicKey = lib.mkOption { + type = lib.types.str; + description = '' + The base 64 encoded representation of the public dkim key. + ''; + }; + dkimPrivateKeyPath = lib.mkOption { + type = lib.types.path; + description = '' + Path to the file for the pre-generated dkim key file. + Should not point to the nixos store. + ''; + }; + keyAlgorithm = lib.mkOption { + type = lib.types.enum ["ed25519-sha256" "rsa-sha-256" "rsa-sha-1"]; + description = "The algorithm of the used key"; + }; + }; + })); + description = '' + Which key to use for which domain. The attr keys are the domains + ''; + default = null; + }; + }; + }; + description = '' + Security options. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + vhack.persist.directories = [ + { + directory = "/var/lib/mail/backup"; + user = "virtualMail"; + group = "virtualMail"; + mode = "0700"; + } + { + directory = "/var/lib/mail/vmail"; + user = "virtualMail"; + group = "virtualMail"; + mode = "0700"; + } + { + directory = "/var/lib/mail/dkim"; + user = "opendkim"; + group = "opendkim"; + mode = "0700"; + } + { + directory = "/var/lib/postfix/data"; + user = "postfix"; + group = "postfix"; + mode = "0700"; + } + { + directory = "/var/lib/postfix/queue"; + user = "postfix"; + group = "postfix"; + mode = "0700"; + } + { + directory = "/var/lib/rspamd"; + user = "rspamd"; + group = "rspamd"; + mode = "0700"; + } + ]; + + vhack.nginx.enable = true; + + services.nginx.virtualHosts = { + "${cfg.fqdn}" = { + enableACME = true; + forceSSL = true; + }; + }; + + mailserver = { + enable = true; + inherit (cfg) fqdn; + + stateVersion = 5; + + storage = { + directoryLayout = "fs"; + path = "/var/lib/mail/vmail"; + }; + + dkim = let + dkimKeys = + builtins.mapAttrs (name: value: { + selectors."mail".keyFile = + value.dkimPrivateKeyPath; + }) + cfg.security.dkimKeys; + in { + # domains."${cfg.fqdn}".selectors."mail" = lib.mkIf (cfg.dkimKey != null) { + # keyFile = cfg.dkimKey; + # }; + domains = lib.mkIf (cfg.security.dkimKeys != null) dkimKeys; + keyDirectory = lib.mkIf (cfg.security.dkimKeys == null) "/var/lib/mail/dkim"; + }; + + backup.snapshotRoot = "/var/lib/mail/backup"; + + enableImap = false; + enableImapSsl = true; + enablePop3 = false; + enablePop3Ssl = true; + # SMTP + enableSubmission = false; + enableSubmissionSsl = true; + openFirewall = true; + + # Reference the existing ACME configuration created by nginx + x509.useACMEHost = cfg.fqdn; + + inherit (cfg) domains accounts; + }; + + users = { + users = { + knot-resolver = { + uid = config.vhack.constants.ids.uids.knot-resolver; + group = "knot-resolver"; + }; + redis-rspamd.uid = config.vhack.constants.ids.uids.redis-rspamd; + rspamd.uid = config.vhack.constants.ids.uids.rspamd; + postfix-tlspol.uid = config.vhack.constants.ids.uids.postfix-tlspol; + }; + groups = { + knot-resolver.gid = lib.mkForce config.vhack.constants.ids.gids.knot-resolver; + redis-rspamd.gid = config.vhack.constants.ids.gids.redis-rspamd; + rspamd.gid = config.vhack.constants.ids.gids.rspamd; + postfix-tlspol.gid = config.vhack.constants.ids.gids.postfix-tlspol; + }; + }; + }; +} diff --git a/modules/by-name/ma/mastodon/module.nix b/modules/vhack/ma/mastodon/module.nix index 895428d..84f3ec8 100644 --- a/modules/by-name/ma/mastodon/module.nix +++ b/modules/vhack/ma/mastodon/module.nix @@ -37,16 +37,22 @@ in { owner = "mastodon"; group = "mastodon"; }; - vhack.persist.directories = [ - { - directory = "/var/lib/mastodon"; - user = "mastodon"; - group = "mastodon"; - mode = "0700"; - } - ]; - vhack.postgresql.enable = true; + vhack = { + persist.directories = [ + { + directory = "/var/lib/mastodon"; + user = "mastodon"; + group = "mastodon"; + mode = "0700"; + } + ]; + + postgresql.enable = true; + + nginx.enable = true; + }; + services.mastodon = { enable = true; @@ -54,7 +60,7 @@ in { # Unstable Mastodon package, used if # security updates aren't backported. - #package = applyPatches pkgs-unstable.mastodon; + #package = applyPatches pkgsUnstable.mastodon; localDomain = if cfg.enableTLD @@ -75,7 +81,6 @@ in { }; }; - vhack.nginx.enable = true; services.nginx = { enable = true; recommendedProxySettings = true; # required for redirections to work diff --git a/modules/by-name/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch b/modules/vhack/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch index 35dc809..870492a 100644 --- a/modules/by-name/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch +++ b/modules/vhack/ma/mastodon/patches/0001-feat-treewide-Increase-character-limit-to-5000-in-me.patch @@ -1,6 +1,6 @@ -From ab67426c53d343eee349de501767ecbbf5d211ad Mon Sep 17 00:00:00 2001 +From 265bc7b12ab8229225b13565a361051b9e120792 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz <benedikt.peetz@b-peetz.de> -Date: Sat, 21 Dec 2024 20:07:11 +0100 +Date: Tue, 7 Jul 2026 11:34:50 +0200 Subject: [PATCH] feat(treewide): Increase character limit to 5000 in messages The default of 500 was just not enough. @@ -10,31 +10,31 @@ The default of 500 was just not enough. 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js -index bda2edba6..76ac65bf3 100644 +index c5cffff10d..2172a0ad8f 100644 --- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js +++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js -@@ -28,7 +28,7 @@ const mapStateToProps = state => ({ - anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, +@@ -58,7 +58,7 @@ const mapStateToProps = state => ({ + && !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]), isInReply: state.getIn(['compose', 'in_reply_to']) !== null, lang: state.getIn(['compose', 'language']), -- maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500), -+ maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 5000), +- maxChars: state.getIn(['server', 'server', 'item', 'configuration', 'statuses', 'max_characters'], 500), ++ maxChars: state.getIn(['server', 'server', 'item', 'configuration', 'statuses', 'max_characters'], 5000), }); - - const mapDispatchToProps = (dispatch) => ({ + + const mapDispatchToProps = (dispatch, props) => ({ diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb -index dc841ded3..9cb1ec94b 100644 +index 575aaf1869..ecefca6fe9 100644 --- a/app/validators/status_length_validator.rb +++ b/app/validators/status_length_validator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true - + class StatusLengthValidator < ActiveModel::Validator - MAX_CHARS = 500 + MAX_CHARS = 5000 URL_PLACEHOLDER_CHARS = 23 URL_PLACEHOLDER = 'x' * 23 - --- -2.47.0 + +-- +2.54.0 diff --git a/modules/by-name/ma/matrix/module.nix b/modules/vhack/ma/matrix/module.nix index 4b730da..39631ef 100644 --- a/modules/by-name/ma/matrix/module.nix +++ b/modules/vhack/ma/matrix/module.nix @@ -1,6 +1,5 @@ { config, - pkgs, lib, ... }: let @@ -29,6 +28,7 @@ in { description = "The age encrypted shared secret file for synapse, passed to agenix"; }; }; + config = lib.mkIf cfg.enable { age.secrets.matrix-synapse_registration_shared_secret = { file = cfg.sharedSecretFile; @@ -38,45 +38,55 @@ in { }; networking.firewall.allowedTCPPorts = [80 443]; - vhack.persist.directories = [ - { - directory = "/var/lib/matrix"; - user = "matrix-synapse"; - group = "matrix-synapse"; - mode = "0700"; - } - { - directory = "/var/lib/mautrix-whatsapp"; - user = "mautrix-whatsapp"; - group = "matrix-synapse"; - mode = "0750"; - } - ]; - systemd.tmpfiles.rules = [ - "d /etc/matrix 0755 matrix-synapse matrix-synapse" - ]; + vhack = { + persist.directories = [ + { + directory = "/var/lib/matrix"; + user = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0700"; + } + { + directory = "/var/lib/mautrix-whatsapp"; + user = "mautrix-whatsapp"; + group = "matrix-synapse"; + mode = "0750"; + } + ]; - vhack.postgresql.enable = true; - vhack.nginx.enable = true; + postgresql.enable = true; + nginx.enable = true; + }; + + systemd = { + tmpfiles.rules = [ + "d /etc/matrix 0755 matrix-synapse matrix-synapse" + ]; + # TODO: Do we still need this? <2025-12-18> + # The `$PSQL` env var seemed to go away between the 25.05 -> 25.11 update + # services.postgresql.postStart = '' + # $PSQL -tAc "ALTER ROLE \"matrix-synapse\" WITH PASSWORD 'synapse';" + # $PSQL -tAc "ALTER ROLE \"mautrix-whatsapp\" WITH PASSWORD 'whatsapp';" + # ''; + }; services = { postgresql = { enable = true; - initialScript = pkgs.writeText "synapse-init.sql" '' - --Matrix: - CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; - CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" - TEMPLATE template0 - LC_COLLATE = "C" - LC_CTYPE = "C"; - - --Whatsapp-bridge: - CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp'; - CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp" - TEMPLATE template0 - LC_COLLATE = "C" - LC_CTYPE = "C"; - ''; + ensureUsers = [ + { + name = "matrix-synapse"; + ensureDBOwnership = true; + } + { + name = "mautrix-whatsapp"; + ensureDBOwnership = true; + } + ]; + ensureDatabases = [ + "matrix-synapse" + "mautrix-whatsapp" + ]; }; nginx = { |
