diff options
Diffstat (limited to '')
| -rw-r--r-- | flake.lock | 48 | ||||
| -rw-r--r-- | hosts/by-name/server2/configuration.nix | 20 | ||||
| -rw-r--r-- | hosts/by-name/server3/configuration.nix | 15 | ||||
| -rw-r--r-- | modules/by-name/co/constants/module.nix | 16 | ||||
| -rw-r--r-- | modules/by-name/ng/nginx/module.nix | 5 | ||||
| -rw-r--r-- | modules/by-name/sh/sharkey/module.nix | 297 | ||||
| -rw-r--r-- | modules/by-name/st/stalwart-mail/module.nix | 106 | ||||
| -rw-r--r-- | modules/by-name/st/stalwart-mail/settings.nix | 34 | ||||
| -rw-r--r-- | modules/by-name/ta/taskchampion-sync/module.nix | 10 | ||||
| -rw-r--r-- | pkgs/by-name/sh/sharkey/package.nix | 4 | ||||
| -rw-r--r-- | pkgs/by-name/sh/sharkey/unstable_package.nix | 176 | ||||
| -rw-r--r-- | pkgs/by-name/st/stalwart-mail-free/package.nix | 4 | ||||
| -rw-r--r-- | tests/by-name/em/email-dns/nodes/mail_server.nix | 14 | ||||
| -rw-r--r-- | tests/by-name/em/email-dns/nodes/name_server.nix | 234 | ||||
| -rw-r--r-- | tests/by-name/em/email-dns/nodes/user.nix | 8 | ||||
| -rw-r--r-- | tests/by-name/em/email-dns/test.nix | 38 | ||||
| -rw-r--r-- | tests/by-name/em/email-http/nodes/mail_server.nix | 57 | ||||
| -rw-r--r-- | tests/by-name/em/email-http/nodes/user.nix | 26 | ||||
| -rw-r--r-- | tests/by-name/em/email-http/test.nix | 115 | ||||
| -rw-r--r-- | tests/by-name/em/email-ip/test.nix | 4 | ||||
| -rw-r--r-- | tests/by-name/sh/sharkey/test.nix | 118 | ||||
| -rwxr-xr-x | tests/common/acme/certs/generate (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate) | 0 | ||||
| -rwxr-xr-x | tests/common/acme/certs/generate.ca (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate.ca) | 0 | ||||
| -rwxr-xr-x | tests/common/acme/certs/generate.client (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate.client) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/acme.test.cert.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/acme.test.key.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/acme.test.template (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/ca.cert.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/ca.key.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/output/ca.template (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template) | 0 | ||||
| -rw-r--r-- | tests/common/acme/certs/snakeoil-certs.nix (renamed from tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix) | 0 | ||||
| -rw-r--r-- | tests/common/acme/client.nix (renamed from tests/by-name/em/email-dns/nodes/acme/client.nix) | 0 | ||||
| -rw-r--r-- | tests/common/acme/scripts.nix | 30 | ||||
| -rw-r--r-- | tests/common/acme/server.nix (renamed from tests/by-name/em/email-dns/nodes/acme/default.nix) | 27 | ||||
| -rw-r--r-- | tests/common/dns/client.nix | 10 | ||||
| -rw-r--r-- | tests/common/dns/server.nix | 43 | ||||
| -rw-r--r-- | tests/common/email/dkim/alice.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/alice.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/alice.com/public) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/bob.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/bob.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/bob.com/public) | 0 | ||||
| -rwxr-xr-x | tests/common/email/dkim/gen_key.sh (renamed from tests/by-name/em/email-dns/secrets/dkim/gen_key.sh) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/mail1.server.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/mail1.server.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/mail2.server.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age) | 0 | ||||
| -rw-r--r-- | tests/common/email/dkim/mail2.server.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public) | 0 | ||||
| -rw-r--r-- | tests/common/email/hostKey (renamed from tests/by-name/em/email-dns/secrets/hostKey) | 0 | ||||
| -rwxr-xr-x | update.sh | 3 | ||||
| -rw-r--r-- | zones/vhack.eu/zone.nix | 3 |
48 files changed, 1164 insertions, 301 deletions
diff --git a/flake.lock b/flake.lock index a23f22a..75b1bc4 100644 --- a/flake.lock +++ b/flake.lock @@ -43,11 +43,11 @@ }, "crane": { "locked": { - "lastModified": 1742394900, - "narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=", + "lastModified": 1745022865, + "narHash": "sha256-tXL4qUlyYZEGOHUKUWjmmcvJjjLQ+4U38lPWSc8Cgdo=", "owner": "ipetkov", "repo": "crane", - "rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd", + "rev": "25ca4c50039d91ad88cc0b8feacb9ad7f748dedf", "type": "github" }, "original": { @@ -111,11 +111,11 @@ ] }, "locked": { - "lastModified": 1741786315, - "narHash": "sha256-VT65AE2syHVj6v/DGB496bqBnu1PXrrzwlw07/Zpllc=", + "lastModified": 1745224732, + "narHash": "sha256-0OWgbEKhpMLpk3WQi3ugOwxWW4Y6JVpKiQ+o0nuNzus=", "owner": "nix-community", "repo": "disko", - "rev": "0d8c6ad4a43906d14abd5c60e0ffe7b587b213de", + "rev": "1770bf1ae5da05564f86b969ef21c7228cc1a70b", "type": "github" }, "original": { @@ -214,11 +214,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1743036386, - "narHash": "sha256-W1Qap/jwnpvWPXC+cUp0PlaZsJO05sfQfzffRrYW7YY=", + "lastModified": 1745186762, + "narHash": "sha256-vn1ixtFWtellgcZpdIoxCVu9agdK3647hi7lcc/58eQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1751c9cb80247edc5fed79b90211a92c56bf91e6", + "rev": "c16961fda203155a314b0c75c13961c29e9ea7b0", "type": "github" }, "original": { @@ -245,11 +245,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1743039536, - "narHash": "sha256-O3GFPU0Uyv80LKVMMukVqrfxSzWKkBwQIHN2UnRSCZk=", + "lastModified": 1745215074, + "narHash": "sha256-JjkdlVI9BImDV5RrCiJk17cMSIqbefUXBM9trHRif+c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0ab4a506520ea9247e673e9849ecf84c72f88be2", + "rev": "78e2cd1a1590f8c70b329cbc7d13bb2ab5b5a16c", "type": "github" }, "original": { @@ -278,11 +278,11 @@ ] }, "locked": { - "lastModified": 1741508717, - "narHash": "sha256-iQf1WdNxaApOFHIx4RLMRZ4f8g+8Xp0Z1/E/Mz2rLxY=", + "lastModified": 1744897914, + "narHash": "sha256-GIVU92o2TZBnKQXTb76zpQbWR4zjU2rFqWKNIIpXnqA=", "owner": "yaxitech", "repo": "ragenix", - "rev": "2a2bea99d74927e54adf53cbf113219def67d5c9", + "rev": "40f2e17ecaeab4d78ec323e96a04548c0aaa5223", "type": "github" }, "original": { @@ -317,11 +317,11 @@ ] }, "locked": { - "lastModified": 1743042789, - "narHash": "sha256-yPlxN0r3pQjUIwyX/qeWSTdpHjWy/AfmM0PK1bYkO18=", + "lastModified": 1745289264, + "narHash": "sha256-7nt+UJ7qaIUe2J7BdnEEph9n2eKEwxUwKS/QIr091uA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "b4d2dee9d16e7725b71969f28862ded3a94a7934", + "rev": "3b7171858c20d5293360042936058fb0c4cb93a9", "type": "github" }, "original": { @@ -342,11 +342,11 @@ "nixpkgs-24_11": "nixpkgs-24_11" }, "locked": { - "lastModified": 1742413977, - "narHash": "sha256-NkhM9GVu3HL+MiXtGD0TjuPCQ4GFVJPBZ8KyI2cFDGU=", + "lastModified": 1745164839, + "narHash": "sha256-+0T3pBcl5BD3qHB2nGfwwnRhb4EeNlJEI0BUm5fXPmE=", "owner": "simple-nixos-mailserver", "repo": "nixos-mailserver", - "rev": "b4fbffe79c00f19be94b86b4144ff67541613659", + "rev": "42651ce2d337921c99ae0c293ed9af49f7a89c6a", "type": "gitlab" }, "original": { @@ -378,11 +378,11 @@ ] }, "locked": { - "lastModified": 1742982148, - "narHash": "sha256-aRA6LSxjlbMI6MmMzi/M5WH/ynd8pK+vACD9za3MKLQ=", + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "61c88349bf6dff49fa52d7dfc39b21026c2a8881", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", "type": "github" }, "original": { diff --git a/hosts/by-name/server2/configuration.nix b/hosts/by-name/server2/configuration.nix index ba0de14..24513f4 100644 --- a/hosts/by-name/server2/configuration.nix +++ b/hosts/by-name/server2/configuration.nix @@ -83,6 +83,26 @@ }; redlib.enable = true; rust-motd.enable = true; + sharkey = { + enable = true; + fqdn = "sharkey.vhack.eu"; + settings = { + id = "aidx"; + + maxNoteLength = 8192; + maxFileSize = 1024 * 1024 * 1024; + proxyRemoteFiles = true; + + # > At the suggestion of Sharkey maintainers, + # > this allows the server to run multiple workers + # > and without this (and postgres tuning), the instance runs slowly. + # Copied from: https://github.com/sodiboo/system/blob/b63c7b27f49043e8701b3ff5e1441cd27d5a2fff/sharkey.mod.nix#L21-L23 + clusterLimit = 3; + + signToActivityPubGet = true; + CheckActivityPubGetSigned = false; + }; + }; taskchampion-sync.enable = true; users.enable = true; }; diff --git a/hosts/by-name/server3/configuration.nix b/hosts/by-name/server3/configuration.nix index 7f5bce5..17085e8 100644 --- a/hosts/by-name/server3/configuration.nix +++ b/hosts/by-name/server3/configuration.nix @@ -71,6 +71,7 @@ enable = true; fqdn = "mail.vhack.eu"; admin = "admin@vhack.eu"; + initialAdminPassword = "$6$k/JGlODSgRyb6dG2$KV78QYipkS423WQQoQIcEWNFZdq4uuS5uIpJUNL1WLmXsD3b6KuLtt18TNU24Hnpup5TbMM5vtui/I.vGAybS/"; security = { dkimKeys = let loadKey = name: { @@ -84,19 +85,7 @@ verificationMode = "strict"; }; openFirewall = true; - principals = [ - { - class = "individual"; - name = "soispha"; - secret = "$2b$05$XX36sJuHNbTFvi8DFldscOeQBHahluSkiUqD9QGzQaET7NJusSuQW"; - email = [ - "soispha@vhack.eu" - "abuse@vhack.eu" - "postmaster@vhack.eu" - "admin@vhack.eu" - ]; - } - ]; + principals = null; }; postgresql.enable = true; rust-motd.enable = true; diff --git a/modules/by-name/co/constants/module.nix b/modules/by-name/co/constants/module.nix index d6674bb..2115a37 100644 --- a/modules/by-name/co/constants/module.nix +++ b/modules/by-name/co/constants/module.nix @@ -25,6 +25,7 @@ config.vhack.constants = { ids.uids = { # Keep this sorted with `!sort --numeric-sort --key=2 --field-separator="="` + systemd-coredump = 151; # GROUP opendkim = 221; mautrix-whatsapp = 222; etebase-server = 223; @@ -43,16 +44,22 @@ nscd = 330; sshd = 331; systemd-oom = 332; + resolvconf = 333; # GROUP nix-sync = 334; nextcloud = 335; redis-nextcloud = 336; taskchampion = 337; + stalwart-mail-certificates = 338; # GROUP + sharkey = 339; + redis-sharkey = 340; # As per the NixOS file, the uids should not be greater or equal to 400; }; ids.gids = let inherit (config.vhack.constants.ids) uids; in { + # Please add your groups to the users and inherit them here. + # This avoids having an user/group id mismatch. inherit (uids) acme @@ -76,12 +83,13 @@ sshd stalwart-mail systemd-oom + sharkey + redis-sharkey + systemd-coredump # matches systemd-coredump user + resolvconf # This group is not matched to an user? + stalwart-mail-certificates # This group is used to connect nginx and stalwart-mail ; - # Keep this sorted with `!sort --numeric-sort --key=2 --field-separator="="` - systemd-coredump = 151; # matches systemd-coredump user - resolvconf = 333; # This group is not matched to an user? - # The gid should match the uid. Thus should not be >= 400; }; }; diff --git a/modules/by-name/ng/nginx/module.nix b/modules/by-name/ng/nginx/module.nix index 1cb4e46..fa3337d 100644 --- a/modules/by-name/ng/nginx/module.nix +++ b/modules/by-name/ng/nginx/module.nix @@ -44,7 +44,10 @@ in { ]; users = { - users.acme.uid = config.vhack.constants.ids.uids.acme; + users.acme = { + uid = config.vhack.constants.ids.uids.acme; + group = "acme"; + }; groups.acme.gid = config.vhack.constants.ids.gids.acme; }; diff --git a/modules/by-name/sh/sharkey/module.nix b/modules/by-name/sh/sharkey/module.nix new file mode 100644 index 0000000..a2f5445 --- /dev/null +++ b/modules/by-name/sh/sharkey/module.nix @@ -0,0 +1,297 @@ +# Source: https://github.com/sodiboo/system/blob/b63c7b27f49043e8701b3ff5e1441cd27d5a2fff/sharkey/module.nix +{ + config, + lib, + pkgs, + vhackPackages, + ... +}: let + cfg = config.vhack.sharkey; + + createDB = cfg.database.host == "127.0.0.1" && cfg.database.createLocally; + + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "sharkey-config.yml" cfg.settings; +in { + options.vhack.sharkey = { + enable = lib.mkEnableOption "sharkey"; + + fqdn = lib.mkOption { + description = "The fully qualified domain name of this instance."; + type = lib.types.str; + example = "sharkey.shonk.social"; + }; + + package = lib.mkOption { + type = lib.types.package; + default = vhackPackages.sharkey; + defaultText = lib.literalExpression "vhackPackages.sharkey"; + description = "Sharkey package to use."; + }; + + dataDirectory = lib.mkOption { + type = lib.types.path; + default = "/var/lib/sharkey"; + description = "The directory where sharkey stores it's data."; + + # This is already set in the package. + readOnly = true; + }; + + database = { + createLocally = lib.mkOption { + description = "Whether to enable local db creation."; + type = lib.types.bool; + default = true; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "The database host."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 5432; + description = "The database port."; + }; + + name = lib.mkOption { + type = lib.types.str; + default = "sharkey"; + description = "The database name in postgresql."; + }; + }; + + settings = lib.mkOption { + inherit (settingsFormat) type; + default = {}; + description = '' + Configuration for Sharkey, see + <link xlink:href="https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/.config/example.yml"/> + for supported settings. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + vhack = { + nginx.enable = true; + + sharkey.settings = { + id = "aidx"; + + url = "https://${cfg.fqdn}/"; + port = 5312; + + db = { + inherit (cfg.database) host port; + db = cfg.database.name; + user = cfg.database.name; + pass = "sharkey-password"; + }; + redis = { + path = config.services.redis.servers."sharkey".unixSocket; + }; + }; + + persist.directories = [ + { + directory = "${config.services.redis.servers."sharkey".settings.dir}"; + user = "sharkey"; + group = "redis-sharey"; + mode = "0770"; + } + { + directory = "${cfg.dataDirectory}"; + user = "sharkey"; + group = "sharkey"; + mode = "0770"; + } + ]; + }; + + services = { + nginx.virtualHosts."${cfg.fqdn}" = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.settings.port}"; + proxyWebsockets = true; + }; + + # proxy_set_header Host $host; + # proxy_http_version 1.1; + # proxy_redirect off; + # + # # If it's behind another reverse proxy or CDN, remove the following. + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto https; + # + # # For WebSocket + # proxy_set_header Upgrade $http_upgrade; + # proxy_set_header Connection $connection_upgrade; + # + # # Cache settings + # proxy_cache cache1; + # proxy_cache_lock on; + # proxy_cache_use_stale updating; + # proxy_force_ranges on; + # add_header X-Cache $upstream_cache_status; + + enableACME = true; + forceSSL = true; + }; + + postgresql = lib.mkIf createDB { + enable = true; + settings.port = cfg.database.port; + ensureUsers = [ + { + inherit (cfg.database) name; + ensureDBOwnership = true; + } + ]; + ensureDatabases = [cfg.database.name]; + }; + + redis = { + servers."sharkey" = { + enable = true; + + user = "sharkey"; + + # Disable TCP listening. (We have a UNIX socket) + port = 0; + bind = null; + + settings = { + protected-mode = true; + enable-protected-configs = false; + enable-debug-command = false; + enable-module-command = false; + + supervised = "systemd"; + stop-writes-on-bgsave-error = true; + sanitize-dump-payload = "clients"; + }; + }; + }; + }; + + systemd.services.postgresql.postStart = '' + $PSQL -tAc "ALTER ROLE ${cfg.database.name} WITH ENCRYPTED PASSWORD 'sharkey-password';" + ''; + + systemd.services.sharkey = { + requires = + [ + "redis-sharkey.service" + "network-online.target" + ] + ++ lib.optionals createDB ["postgresql.service"]; + + after = + [ + "redis-sharkey.service" + "network-online.target" + ] + ++ lib.optionals createDB ["postgresql.service"]; + + wantedBy = ["multi-user.target"]; + + environment = { + MISSKEY_CONFIG_YML = "${configFile}"; + NODE_ENV = "production"; + }; + + serviceConfig = { + Type = "simple"; + + StateDirectory = "sharkey"; + StateDirectoryMode = "0700"; + CacheDirectory = "sharkey"; + RuntimeDirectory = "sharkey"; + RuntimeDirectoryMode = "0700"; + ExecStart = "${lib.getExe cfg.package} migrateandstart"; + + TimeoutSec = 60; + Restart = "no"; + + StandardOutput = "journal"; + StandardError = "journal"; + SyslogIdentifier = "sharkey"; + + User = "sharkey"; + Group = "sharkey"; + + # Bind standard privileged ports + AmbientCapabilities = []; + CapabilityBoundingSet = []; + + ReadWritePaths = [ + "${cfg.dataDirectory}" + ]; + + # Hardening + DeviceAllow = [""]; + LockPersonality = true; + # Probably needed for v8's JIT (crashes with it on). + MemoryDenyWriteExecute = false; + PrivateDevices = true; + PrivateUsers = true; + ProcSubset = "pid"; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_UNIX" # Local communication unix(7) + "AF_INET" # IPv4 Internet protocols ip(7) + "AF_INET6" # IPv6 Internet protocols ipv6(7) + # Needed for nodes `os.networkInterfaces()` function. + "AF_NETLINK" # Kernel user interface device netlink(7) + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@mount" + ]; + UMask = "0077"; + }; + }; + + users = { + groups.sharkey = { + gid = config.vhack.constants.ids.gids.sharkey; + }; + users.sharkey = { + isSystemUser = true; + group = "sharkey"; + uid = config.vhack.constants.ids.uids.sharkey; + home = cfg.package; + packages = [cfg.package]; + }; + + groups.redis-sharkey = { + gid = config.vhack.constants.ids.gids.redis-sharkey; + }; + users.redis-sharkey = { + group = "redis-sharkey"; + uid = config.vhack.constants.ids.uids.redis-sharkey; + }; + }; + }; +} diff --git a/modules/by-name/st/stalwart-mail/module.nix b/modules/by-name/st/stalwart-mail/module.nix index 1e39e81..396116d 100644 --- a/modules/by-name/st/stalwart-mail/module.nix +++ b/modules/by-name/st/stalwart-mail/module.nix @@ -10,6 +10,33 @@ configFormat = pkgs.formats.toml {}; configFile = configFormat.generate "stalwart-mail.toml" topCfg.settings; + + hashedPassword = let + prefix = pre: lib.types.strMatching "^${lib.strings.escapeRegex pre}.*"; + in + lib.types.oneOf [ + (prefix "$argon2") + (prefix "$pbkdf2") + (prefix "$scrypt") + (prefix "$2") # bcrypt + (prefix "$6$") # sha-512 + (prefix "$5$") # sha-256 + (prefix "$sha1") + (prefix "$1") # md5 + (prefix "_") # BSDi crypt + (prefix "{SHA}") # base64 sha + (prefix "{SSHA}") # base64 salted sha + + # unix crypt + (prefix "{CRYPT}") + (prefix "{crypt}") + + # Plain text + (prefix "{PLAIN}") + (prefix "{plain}") + (prefix "{CLEAR}") + (prefix "{clear}") + ]; in { imports = [ ./settings.nix @@ -24,14 +51,20 @@ in { description = '' Email address to advertise as administrator. This is the address, where dkim, spv etc. refusal reports are sent to. - - The format should be: `mailto:<name>@<domain>` ''; type = lib.types.str; - example = "mailto:dmarc+rua@example.com"; + example = "dmarc+rua@example.com"; default = ""; }; + initialAdminPassword = lib.mkOption { + type = hashedPassword; + description = '' + The hash of the password for the admin account, used to bootstrap account + creation. + ''; + }; + fqdn = lib.mkOption { type = lib.types.str; example = "mail.foss-syndicate.org"; @@ -61,7 +94,7 @@ in { }; secret = lib.mkOption { - type = lib.types.str; + type = hashedPassword; description = '' Sets the password for the user account. Passwords can be stored hashed or in plain text (not recommended). @@ -160,25 +193,16 @@ in { # However, this decision could obviously be reversed in the future. <2025-02-08> enable = false; inherit (cfg) package; - # dataDir = cfg.dataDirectory; }; - # FIXME(@bpeetz): This is currently needed for a successful acme http-01 challenge. - # We could also use the DNS challenge. <2025-03-01> nginx.virtualHosts."${cfg.fqdn}" = { - enableACME = false; - extraConfig = - # This is copied directly from the nixos nginx module. - # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) - # We use ^~ here, so that we don't check any regexes (which could - # otherwise easily override this intended match accidentally). - '' - location ^~ /.well-known/acme-challenge/ { - root ${config.security.acme.certs.${cfg.fqdn}.webroot}; - auth_basic off; - auth_request off; - } - ''; + locations."/" = { + proxyPass = "http://${builtins.elemAt config.services.stalwart-mail.settings.server.listener.http.bind 0}"; + recommendedProxySettings = true; + }; + + useACMEHost = "${cfg.fqdn}"; + forceSSL = true; }; redis = { @@ -209,7 +233,7 @@ in { security.acme.certs = { "${cfg.fqdn}" = { domain = cfg.fqdn; - group = "stalwart-mail"; + group = "stalwart-mail-certificates"; }; }; @@ -249,20 +273,31 @@ in { # service is restarted on a potentially large number of files. # That would cause unnecessary and unwanted delays. users = { - groups.stalwart-mail = { - gid = config.vhack.constants.ids.gids.stalwart-mail; - }; - users.stalwart-mail = { - isSystemUser = true; - group = "stalwart-mail"; - uid = config.vhack.constants.ids.uids.stalwart-mail; - }; - groups.redis-stalwart-mail = { - gid = config.vhack.constants.ids.gids.redis-stalwart-mail; + groups = { + stalwart-mail = { + gid = config.vhack.constants.ids.gids.stalwart-mail; + }; + stalwart-mail-certificates = { + gid = config.vhack.constants.ids.gids.stalwart-mail-certificates; + }; + redis-stalwart-mail = { + gid = config.vhack.constants.ids.gids.redis-stalwart-mail; + }; }; - users.redis-stalwart-mail = { - group = "redis-stalwart-mail"; - uid = config.vhack.constants.ids.uids.redis-stalwart-mail; + users = { + nginx = { + extraGroups = ["stalwart-mail-certificates"]; + }; + stalwart-mail = { + isSystemUser = true; + group = "stalwart-mail"; + uid = config.vhack.constants.ids.uids.stalwart-mail; + extraGroups = ["stalwart-mail-certificates"]; + }; + redis-stalwart-mail = { + group = "redis-stalwart-mail"; + uid = config.vhack.constants.ids.uids.redis-stalwart-mail; + }; }; }; @@ -321,8 +356,7 @@ in { ${lib.getExe cfg.package} --config="$CACHE_DIRECTORY/mutable_config_file.toml" ''; - Restart = "on-failure"; - RestartSec = 5; + Restart = "no"; KillMode = "process"; KillSignal = "SIGINT"; diff --git a/modules/by-name/st/stalwart-mail/settings.nix b/modules/by-name/st/stalwart-mail/settings.nix index 17f045d..765d8db 100644 --- a/modules/by-name/st/stalwart-mail/settings.nix +++ b/modules/by-name/st/stalwart-mail/settings.nix @@ -100,7 +100,7 @@ in { from-name = "'TLS Report'"; from-address = "'noreply-tls@${cfg.fqdn}'"; org-name = "'Foss Syndicate Mail Handling'"; - contact-info = "'${cfg.admin}'"; + contact-info = "'mailto:${cfg.admin}'"; send = "daily"; max-size = 26214400; # 25 MiB sign = lib.mkIf (cfg.security != null) "'${cfg.fqdn}'"; @@ -110,7 +110,7 @@ in { from-name = "'DMARC Report'"; from-address = "'noreply-dmarc@${cfg.fqdn}'"; org-name = "'Foss Syndicate Mail Handling'"; - contact-info = "'${cfg.admin}'"; + contact-info = "'mailto:${cfg.admin}'"; send = "weekly"; max-size = 26214400; # 25MiB sign = lib.mkIf (cfg.security != null) "'${cfg.fqdn}'"; @@ -344,13 +344,13 @@ in { hostname = cfg.fqdn; listener = { - # TODO(@bpeetz): Add this <2025-02-08> - # # HTTP (used for jmap) - # "http" = { - # bind = ["[::]:8080"]; - # protocol = "http"; - # tls.implicit = true; - # }; + # HTTP (used for jmap) + "http" = { + bind = ["127.0.0.1:8112"]; + protocol = "http"; + # handled by ngnix + tls.implicit = false; + }; # IMAP "imap" = { @@ -406,11 +406,12 @@ in { certificate = "default"; }; - # TODO(@bpeetz): Configure that <2025-02-07> - # http = { - # url = ""; - # allowed-endpoint = ["404"]; - # }; + http = { + url = "protocol + '://' + config_get('server.hostname') + ':' + local_port"; + + # We are behind a nginx proxy, and can thus trust this header. + use-x-forwarded = true; + }; auto-ban = { # Ban if the same IP fails to login 10 times in a day @@ -540,6 +541,11 @@ in { }; }; + authentication.fallback-admin = { + user = cfg.admin; + secret = cfg.initialAdminPassword; + }; + certificate = { "default" = { cert = "%{file:${config.security.acme.certs.${cfg.fqdn}.directory}/fullchain.pem}%"; diff --git a/modules/by-name/ta/taskchampion-sync/module.nix b/modules/by-name/ta/taskchampion-sync/module.nix index 2fa0a3f..1870186 100644 --- a/modules/by-name/ta/taskchampion-sync/module.nix +++ b/modules/by-name/ta/taskchampion-sync/module.nix @@ -4,6 +4,7 @@ ... }: let cfg = config.vhack.taskchampion-sync; + dataDirectory = "/var/lib/taskchampion-sync-server"; in { options.vhack.taskchampion-sync = { enable = lib.mkEnableOption "taskchampion-sync"; @@ -16,13 +17,18 @@ in { }; vhack.persist.directories = [ - "/var/lib/taskchampion-sync-server" + { + directory = dataDirectory; + user = "taskchampion"; + group = "taskchampion"; + mode = "0700"; + } ]; services.taskchampion-sync-server = { enable = true; openFirewall = true; - dataDir = "/var/lib/taskchampion-sync-server"; + dataDir = dataDirectory; }; }; } diff --git a/pkgs/by-name/sh/sharkey/package.nix b/pkgs/by-name/sh/sharkey/package.nix new file mode 100644 index 0000000..22261fa --- /dev/null +++ b/pkgs/by-name/sh/sharkey/package.nix @@ -0,0 +1,4 @@ +{pkgsUnstable}: +# NOTE(@bpeetz): The package was written for unstable, and I don't see a reason to migrate +# it back, considering that 25.05 is right around the corner. <2025-04-22> +pkgsUnstable.callPackage ./unstable_package.nix {} diff --git a/pkgs/by-name/sh/sharkey/unstable_package.nix b/pkgs/by-name/sh/sharkey/unstable_package.nix new file mode 100644 index 0000000..3922ca0 --- /dev/null +++ b/pkgs/by-name/sh/sharkey/unstable_package.nix @@ -0,0 +1,176 @@ +# Source: https://github.com/sodiboo/system/blob/b63c7b27f49043e8701b3ff5e1441cd27d5a2fff/sharkey/package.nix +{ + lib, + stdenv, + fetchFromGitLab, + # Build time + makeWrapper, + copyDesktopItems, + jq, + moreutils, + cacert, + python3, + pkg-config, + # Run time + bash, + jemalloc, + ffmpeg-headless, + nodejs, + pnpm_9, + glib, + vips, + pixman, + pango, + cairo, +}: +stdenv.mkDerivation (finalAttrs: { + pname = "sharkey"; + version = "2025.2.2"; + + src = fetchFromGitLab { + owner = "TransFem-org"; + repo = "Sharkey"; + domain = "activitypub.software"; + rev = finalAttrs.version; + hash = "sha256-KVr4KLtJ22LEk94GuxeTk8/GcFs7oU/gkoVTvrgbYBg="; + fetchSubmodules = true; + }; + + pnpmDeps = stdenv.mkDerivation { + pname = "${finalAttrs.pname}-pnpm-deps"; + inherit (finalAttrs) src version; + + nativeBuildInputs = [ + jq + moreutils + pnpm_9 + cacert + ]; + + # https://github.com/NixOS/nixpkgs/blob/763e59ffedb5c25774387bf99bc725df5df82d10/pkgs/applications/misc/pot/default.nix#L56 + installPhase = '' + export HOME=$(mktemp --directory) + + pnpm config set store-dir $out + pnpm config set side-effects-cache false + pnpm install --force --frozen-lockfile --ignore-scripts + ''; + + fixupPhase = '' + rm --recursive --force $out/v3/tmp + for f in $(find $out -name "*.json"); do + sed --in-place --regexp-extended --expression='s/"checkedAt":[0-9]+,//g' "$f" + jq --sort-keys . "$f" | sponge "$f" + done + ''; + + dontBuild = true; + outputHashMode = "recursive"; + outputHash = "sha256-XWcDchvrYSJr0s/DMb8FIEK7MdE6aC2bAbrW88Ig4ug="; + }; + + nativeBuildInputs = [ + copyDesktopItems + pnpm_9 + nodejs + makeWrapper + python3 + pkg-config + ]; + + buildInputs = [ + glib + vips + + pixman + pango + cairo + ]; + + configurePhase = '' + runHook preConfigure + + export HOME=$(mktemp --directory) + export STORE_PATH=$(mktemp --directory) + + export npm_config_nodedir=${nodejs} + + cp --no-target-directory --recursive "$pnpmDeps" "$STORE_PATH" + chmod --recursive +w "$STORE_PATH" + + pnpm config set store-dir "$STORE_PATH" + pnpm install --offline --frozen-lockfile --ignore-scripts + + ( + cd node_modules/.pnpm/node_modules/v-code-diff + pnpm run postinstall + ) + ( + cd node_modules/.pnpm/node_modules/re2 + pnpm run rebuild + ) + ( + cd node_modules/.pnpm/node_modules/sharp + pnpm run install + ) + ( + cd node_modules/.pnpm/node_modules/canvas + pnpm run install + ) + + runHook postConfigure + ''; + + buildPhase = '' + runHook preBuild + + pnpm build + + runHook postBuild + ''; + + installPhase = let + libPath = lib.makeLibraryPath [ + jemalloc + ffmpeg-headless + stdenv.cc.cc.lib + ]; + + binPath = lib.makeBinPath [ + bash + pnpm_9 + nodejs + ]; + in + # bash + '' + runHook preInstall + + mkdir --parents $out/Sharkey + + ln --symbolic /var/lib/sharkey $out/Sharkey/files + ln --symbolic /run/sharkey $out/Sharkey/.config + cp --recursive * $out/Sharkey + + # We cannot `--set` the PATH, because sharkey runs shellscripts at start (and maybe + # at other times), which need these things. + makeWrapper ${lib.getExe pnpm_9} $out/bin/sharkey \ + --chdir $out/Sharkey \ + --prefix PATH : ${binPath} \ + --prefix LD_LIBRARY_PATH : ${libPath} + + runHook postInstall + ''; + + passthru = { + inherit (finalAttrs) pnpmDeps; + }; + + meta = { + description = "🌎 A Sharkish microblogging platform 🚀"; + homepage = "https://joinsharkey.org"; + license = lib.licenses.gpl3Only; + platforms = ["x86_64-linux" "aarch64-linux"]; + mainProgram = "sharkey"; + }; +}) diff --git a/pkgs/by-name/st/stalwart-mail-free/package.nix b/pkgs/by-name/st/stalwart-mail-free/package.nix index bb2c1db..19405c7 100644 --- a/pkgs/by-name/st/stalwart-mail-free/package.nix +++ b/pkgs/by-name/st/stalwart-mail-free/package.nix @@ -24,7 +24,7 @@ in } "stalwart-mail passthru"; useFetchCargoVendor = true; - cargoHash = "sha256-Qg01QXP/ImRCUw3aXcZbnM1hysHUwozCdQ7LecjUa0o="; + cargoHash = "sha256-FCRsmH9ZGYaf9ss3ECjZQkknIxAaDj9cedW32dWmBGY="; # The tests should check if this works. # And this shaves of around 50% of the build time. @@ -40,7 +40,7 @@ in (prev.postUnpack or "") + '' cp --recursive "${mail-send}" ./source/crates/mail-send - chmod -R +w "./source/crates/mail-send" + chmod --recursive +w "./source/crates/mail-send" ''; cargoPatches = diff --git a/tests/by-name/em/email-dns/nodes/mail_server.nix b/tests/by-name/em/email-dns/nodes/mail_server.nix index a8c528a..279d289 100644 --- a/tests/by-name/em/email-dns/nodes/mail_server.nix +++ b/tests/by-name/em/email-dns/nodes/mail_server.nix @@ -13,7 +13,8 @@ extraModules ++ [ ../../../../../modules - ./acme/client.nix + ../../../../common/acme/client.nix + ../../../../common/dns/client.nix ]; environment.systemPackages = [ @@ -21,12 +22,7 @@ pkgs.openssl ]; - networking.nameservers = lib.mkForce [ - nodes.name_server.networking.primaryIPAddress - nodes.name_server.networking.primaryIPv6Address - ]; - - age.identityPaths = ["${../secrets/hostKey}"]; + age.identityPaths = ["${../../../../common/email/hostKey}"]; vhack = { stalwart-mail = { @@ -36,8 +32,8 @@ security = { dkimKeys = let loadKey = name: { - dkimPublicKey = builtins.readFile (../secrets/dkim + "/${name}/public"); - dkimPrivateKeyPath = ../secrets/dkim + "/${name}/private.age"; + dkimPublicKey = builtins.readFile (../../../../common/email/dkim + "/${name}/public"); + dkimPrivateKeyPath = ../../../../common/email/dkim + "/${name}/private.age"; keyAlgorithm = "ed25519-sha256"; }; in { diff --git a/tests/by-name/em/email-dns/nodes/name_server.nix b/tests/by-name/em/email-dns/nodes/name_server.nix index ef657f4..d9d3617 100644 --- a/tests/by-name/em/email-dns/nodes/name_server.nix +++ b/tests/by-name/em/email-dns/nodes/name_server.nix @@ -139,14 +139,10 @@ in { extraModules ++ [ ../../../../../modules - ./acme/client.nix + ../../../../common/acme/client.nix + ../../../../common/dns/server.nix ]; - networking.nameservers = lib.mkForce [ - nodes.name_server.networking.primaryIPAddress - nodes.name_server.networking.primaryIPv6Address - ]; - services.nginx = { logError = "stderr debug"; virtualHosts = let @@ -175,145 +171,121 @@ in { nginx = { enable = true; }; - dns = { - enable = true; - openFirewall = true; - interfaces = [ - nodes.name_server.networking.primaryIPAddress - nodes.name_server.networking.primaryIPv6Address - ]; - - zones = let - stsZone = { - SOA = { - nameServer = "ns"; - adminEmail = "admin@server.com"; - serial = 2025012301; - }; + dns.zones = let + stsZone = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; + }; - useOrigin = false; + useOrigin = false; - A = [ - nodes.name_server.networking.primaryIPAddress - ]; - AAAA = [ - nodes.name_server.networking.primaryIPv6Address - ]; + A = [ + nodes.name_server.networking.primaryIPAddress + ]; + AAAA = [ + nodes.name_server.networking.primaryIPv6Address + ]; + }; + in { + "arpa" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; }; - in { - "arpa" = { - SOA = { - nameServer = "ns"; - adminEmail = "admin@server.com"; - serial = 2025012301; - }; - useOrigin = false; + useOrigin = false; - PTR = [ - { - name = "acme.test"; - ip.v4 = nodes.acme.networking.primaryIPAddress; - } - { - name = "acme.test"; - ip.v6 = nodes.acme.networking.primaryIPv6Address; - } + PTR = [ + { + name = "acme.test"; + ip.v4 = nodes.acme.networking.primaryIPAddress; + } + { + name = "acme.test"; + ip.v6 = nodes.acme.networking.primaryIPv6Address; + } - { - name = "alice.com"; - ip.v4 = nodes.alice.networking.primaryIPAddress; - } - { - name = "alice.com"; - ip.v6 = nodes.alice.networking.primaryIPv6Address; - } + { + name = "alice.com"; + ip.v4 = nodes.alice.networking.primaryIPAddress; + } + { + name = "alice.com"; + ip.v6 = nodes.alice.networking.primaryIPv6Address; + } - { - name = "bob"; - ip.v4 = nodes.bob.networking.primaryIPAddress; - } - { - name = "bob"; - ip.v6 = nodes.bob.networking.primaryIPv6Address; - } + { + name = "bob"; + ip.v4 = nodes.bob.networking.primaryIPAddress; + } + { + name = "bob"; + ip.v6 = nodes.bob.networking.primaryIPv6Address; + } - { - name = "mail1.server.com"; - ip.v4 = nodes.mail1_server.networking.primaryIPAddress; - } - { - name = "mail1.server.com"; - ip.v6 = nodes.mail1_server.networking.primaryIPv6Address; - } + { + name = "mail1.server.com"; + ip.v4 = nodes.mail1_server.networking.primaryIPAddress; + } + { + name = "mail1.server.com"; + ip.v6 = nodes.mail1_server.networking.primaryIPv6Address; + } - { - name = "mail2.server.com"; - ip.v4 = nodes.mail2_server.networking.primaryIPAddress; - } - { - name = "mail2.server.com"; - ip.v6 = nodes.mail2_server.networking.primaryIPv6Address; - } + { + name = "mail2.server.com"; + ip.v4 = nodes.mail2_server.networking.primaryIPAddress; + } + { + name = "mail2.server.com"; + ip.v6 = nodes.mail2_server.networking.primaryIPv6Address; + } - { - name = "ns.server.com"; - ip.v4 = nodes.name_server.networking.primaryIPAddress; - } - { - name = "ns.server.com"; - ip.v6 = nodes.name_server.networking.primaryIPv6Address; - } - ]; - }; - - "alice.com" = mkZone "alice" nodes lib nodes.mail2_server.vhack.stalwart-mail; - "mta-sts.alice.com" = stsZone; - "bob.com" = mkZone "bob" nodes lib nodes.mail1_server.vhack.stalwart-mail; - "mta-sts.bob.com" = stsZone; - "mail1.server.com" = mkServerZone "mail1" nodes lib; - "mail2.server.com" = mkServerZone "mail2" nodes lib; - "ns.server.com" = { - SOA = { - nameServer = "ns"; - adminEmail = "admin@server.com"; - serial = 2025012301; - }; - useOrigin = false; + { + name = "ns.server.com"; + ip.v4 = nodes.name_server.networking.primaryIPAddress; + } + { + name = "ns.server.com"; + ip.v6 = nodes.name_server.networking.primaryIPv6Address; + } + ]; + }; - A = [ - nodes.name_server.networking.primaryIPAddress - ]; - AAAA = [ - nodes.name_server.networking.primaryIPv6Address - ]; + "alice.com" = mkZone "alice" nodes lib nodes.mail2_server.vhack.stalwart-mail; + "mta-sts.alice.com" = stsZone; + "bob.com" = mkZone "bob" nodes lib nodes.mail1_server.vhack.stalwart-mail; + "mta-sts.bob.com" = stsZone; + "mail1.server.com" = mkServerZone "mail1" nodes lib; + "mail2.server.com" = mkServerZone "mail2" nodes lib; + "ns.server.com" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; }; - "acme.test" = { - SOA = { - nameServer = "ns"; - adminEmail = "admin@server.com"; - serial = 2025012301; - }; - useOrigin = false; + useOrigin = false; - A = [ - nodes.acme.networking.primaryIPAddress - ]; - AAAA = [ - nodes.acme.networking.primaryIPv6Address - ]; + A = [ + nodes.name_server.networking.primaryIPAddress + ]; + AAAA = [ + nodes.name_server.networking.primaryIPv6Address + ]; + }; + "server.com" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; }; - "server.com" = { - SOA = { - nameServer = "ns"; - adminEmail = "admin@server.com"; - serial = 2025012301; - }; - useOrigin = false; - NS = [ - "ns.server.com." - ]; - }; + useOrigin = false; + NS = [ + "ns.server.com." + ]; }; }; }; diff --git a/tests/by-name/em/email-dns/nodes/user.nix b/tests/by-name/em/email-dns/nodes/user.nix index e4db347..fba02ce 100644 --- a/tests/by-name/em/email-dns/nodes/user.nix +++ b/tests/by-name/em/email-dns/nodes/user.nix @@ -8,7 +8,8 @@ ... }: { imports = [ - ./acme/client.nix + ../../../../common/acme/client.nix + ../../../../common/dns/client.nix ]; environment.systemPackages = [ @@ -20,11 +21,6 @@ pkgs.openssl ]; - networking.nameservers = lib.mkForce [ - nodes.name_server.networking.primaryIPAddress - nodes.name_server.networking.primaryIPv6Address - ]; - users.users."${user}" = {isNormalUser = true;}; systemd.tmpfiles.rules = [ diff --git a/tests/by-name/em/email-dns/test.nix b/tests/by-name/em/email-dns/test.nix index 32447ae..f0399a5 100644 --- a/tests/by-name/em/email-dns/test.nix +++ b/tests/by-name/em/email-dns/test.nix @@ -31,9 +31,9 @@ in lib, ... }: { - imports = [./nodes/acme]; - networking.nameservers = lib.mkForce [ - nodes.name_server.networking.primaryIPAddress + imports = [ + ../../../common/acme/server.nix + ../../../common/dns/client.nix ]; }; @@ -44,7 +44,7 @@ in { class = "individual"; name = "bob"; - secret = "bob-password"; + secret = "{PLAIN}bob-password"; email = ["bob@bob.com"]; }; @@ -53,7 +53,7 @@ in { class = "individual"; name = "alice"; - secret = "alice-password"; + secret = "{PLAIN}alice-password"; email = ["alice@alice.com"]; }; @@ -89,7 +89,8 @@ in exit 1 } ''; - inherit (pkgs) lib; + + acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;}; in /* python @@ -121,30 +122,7 @@ in with subtest("Add pebble ca key to all services"): for node in [name_server, mail1_server, mail2_server, alice, bob]: - node.succeed("${pkgs.writeShellScript "fetch-and-set-ca" '' - set -xe - - # Fetch the randomly generated ca certificate - curl https://acme.test:15000/roots/0 > /tmp/ca.crt - curl https://acme.test:15000/intermediates/0 >> /tmp/ca.crt - - # Append it to the various system stores - # The file paths are from <nixpgks>/modules/security/ca.nix - for cert_path in "ssl/certs/ca-certificates.crt" "ssl/certs/ca-bundle.crt" "pki/tls/certs/ca-bundle.crt"; do - cert_path="/etc/$cert_path" - - mv "$cert_path" "$cert_path.old" - cat "$cert_path.old" > "$cert_path" - cat /tmp/ca.crt >> "$cert_path" - done - - export NIX_SSL_CERT_FILE=/tmp/ca.crt - export SSL_CERT_FILE=/tmp/ca.crt - - # TODO - # # P11-Kit trust source. - # environment.etc."ssl/trust-source".source = "$${cacertPackage.p11kit}/etc/ssl/trust-source"; - ''}") + node.succeed("${acme_scripts.add_pebble_acme_ca}") with subtest("Both mailserver successfully started all services"): import json diff --git a/tests/by-name/em/email-http/nodes/mail_server.nix b/tests/by-name/em/email-http/nodes/mail_server.nix new file mode 100644 index 0000000..e94c4e9 --- /dev/null +++ b/tests/by-name/em/email-http/nodes/mail_server.nix @@ -0,0 +1,57 @@ +{ + extraModules, + pkgs, + vhackPackages, +}: { + mkMailServer = serverName: principal: { + config, + lib, + nodes, + ... + }: { + imports = + extraModules + ++ [ + ../../../../../modules + ../../../../common/acme/client.nix + ]; + + environment.systemPackages = [ + pkgs.bind + pkgs.openssl + ]; + + networking.nameservers = lib.mkForce [ + nodes.name_server.networking.primaryIPAddress + nodes.name_server.networking.primaryIPv6Address + ]; + + age.identityPaths = ["${../../../../common/email/hostKey}"]; + + vhack = { + stalwart-mail = { + enable = true; + fqdn = "${serverName}.server.com"; + admin = "admin@${serverName}.server.com"; + security = { + dkimKeys = let + loadKey = name: { + dkimPublicKey = builtins.readFile (../../../../common/email/dkim + "/${name}/public"); + dkimPrivateKeyPath = ../../../../common/email/dkim + "/${name}/private.age"; + keyAlgorithm = "ed25519-sha256"; + }; + in { + "mail.server.com" = loadKey "mail1.server.com"; + "bob.com" = loadKey "bob.com"; + }; + verificationMode = "strict"; + }; + openFirewall = true; + principals = + if principal == null + then null + else [principal]; + }; + }; + }; +} diff --git a/tests/by-name/em/email-http/nodes/user.nix b/tests/by-name/em/email-http/nodes/user.nix new file mode 100644 index 0000000..73b9ff7 --- /dev/null +++ b/tests/by-name/em/email-http/nodes/user.nix @@ -0,0 +1,26 @@ +{ + pkgs, + vhackPackages, +}: { + mkUser = user: serverName: { + nodes, + lib, + ... + }: { + imports = [ + ../../../../common/acme/client.nix + ]; + + environment.systemPackages = [ + pkgs.bind + pkgs.openssl + ]; + + networking.nameservers = lib.mkForce [ + nodes.name_server.networking.primaryIPAddress + nodes.name_server.networking.primaryIPv6Address + ]; + + users.users."${user}" = {isNormalUser = true;}; + }; +} diff --git a/tests/by-name/em/email-http/test.nix b/tests/by-name/em/email-http/test.nix new file mode 100644 index 0000000..f508b9f --- /dev/null +++ b/tests/by-name/em/email-http/test.nix @@ -0,0 +1,115 @@ +{ + nixos-lib, + pkgsUnstable, + nixpkgs-unstable, + vhackPackages, + pkgs, + extraModules, + nixLib, + ... +}: let + mail_server = import ./nodes/mail_server.nix {inherit extraModules pkgs vhackPackages;}; + inherit (mail_server) mkMailServer; + user = import ./nodes/user.nix {inherit pkgs vhackPackages;}; + inherit (user) mkUser; +in + nixos-lib.runTest { + hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs + + name = "email-http"; + + node = { + specialArgs = {inherit pkgsUnstable vhackPackages nixpkgs-unstable nixLib;}; + + # Use the nixpkgs as constructed by the `nixpkgs.*` options + pkgs = null; + }; + + nodes = { + acme = { + nodes, + lib, + ... + }: { + imports = [ + ../../../common/acme/server.nix + ../../../common/dns/client.nix + ]; + }; + + name_server = {nodes, ...}: { + imports = + extraModules + ++ [ + ../../../common/acme/client.nix + ../../../common/dns/server.nix + ]; + + vhack.dns.zones = { + "mail.server.com" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; + }; + useOrigin = false; + + A = [ + nodes.mail_server.networking.primaryIPAddress + ]; + AAAA = [ + nodes.mail_server.networking.primaryIPv6Address + ]; + }; + }; + }; + + mail_server = mkMailServer "mail" null; + + bob = mkUser "bob" "mail"; + }; + + # TODO(@bpeetz): This test should also test the http JMAP features of stalwart-mail. <2025-04-12> + testScript = _: let + acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;}; + in + /* + python + */ + '' + # Start dependencies for the other services + acme.start() + acme.wait_for_unit("pebble.service") + name_server.start() + name_server.wait_for_unit("nsd.service") + + # Start the actual testing machines + start_all() + + mail_server.wait_for_unit("stalwart-mail.service") + mail_server.wait_for_open_port(993) # imap + mail_server.wait_for_open_port(465) # smtp + + bob.wait_for_unit("multi-user.target") + + with subtest("Add pebble ca key to all services"): + for node in [name_server, mail_server, bob]: + node.wait_for_unit("network-online.target") + node.succeed("${acme_scripts.add_pebble_acme_ca}") + + with subtest("The mailserver successfully started all services"): + import json + def all_services_running(host): + (status, output) = host.systemctl("list-units --state=failed --plain --no-pager --output=json") + host_failed = json.loads(output) + assert len(host_failed) == 0, f"Expected zero failing services, but found: {json.dumps(host_failed, indent=4)}" + all_services_running(mail_server) + + with subtest("Bob can use the self-service interface"): + bob.succeed("${pkgs.writeShellScript "check-self-service" '' + curl mail.server.com --location --output /home/bob/output.html; + ''}") + + bob.copy_from_vm("/home/bob", "") + ''; + } diff --git a/tests/by-name/em/email-ip/test.nix b/tests/by-name/em/email-ip/test.nix index 688cd8f..dabc404 100644 --- a/tests/by-name/em/email-ip/test.nix +++ b/tests/by-name/em/email-ip/test.nix @@ -113,13 +113,13 @@ in { class = "individual"; name = "alice"; - secret = "alice-password"; + secret = "{PLAIN}alice-password"; email = ["alice@${domain}"]; } { class = "individual"; name = "bob"; - secret = "bob-password"; + secret = "{PLAIN}bob-password"; email = ["bob@${domain}"]; } ]; diff --git a/tests/by-name/sh/sharkey/test.nix b/tests/by-name/sh/sharkey/test.nix new file mode 100644 index 0000000..40efe17 --- /dev/null +++ b/tests/by-name/sh/sharkey/test.nix @@ -0,0 +1,118 @@ +{ + nixos-lib, + pkgsUnstable, + nixpkgs-unstable, + vhackPackages, + pkgs, + extraModules, + nixLib, + ... +}: +nixos-lib.runTest { + hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs + + name = "sharkey"; + + node = { + specialArgs = {inherit pkgsUnstable extraModules vhackPackages nixpkgs-unstable nixLib;}; + + # Use the nixpkgs as constructed by the `nixpkgs.*` options + pkgs = null; + }; + + nodes = { + acme = {...}: { + imports = [ + ../../../common/acme/server.nix + ../../../common/dns/client.nix + ]; + }; + name_server = {nodes, ...}: { + imports = + extraModules + ++ [ + ../../../common/acme/client.nix + ../../../common/dns/server.nix + ]; + + vhack.dns.zones = { + "sharkey.server" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; + }; + useOrigin = false; + + A = [ + nodes.server.networking.primaryIPAddress + ]; + AAAA = [ + nodes.server.networking.primaryIPv6Address + ]; + }; + }; + }; + + server = {config, ...}: { + imports = + extraModules + ++ [ + ../../../../modules + ../../../common/acme/client.nix + ../../../common/dns/client.nix + ]; + + vhack = { + persist.enable = true; + nginx.enable = true; + sharkey = { + enable = true; + fqdn = "sharkey.server"; + }; + }; + }; + + client = {...}: { + imports = [ + ../../../common/acme/client.nix + ../../../common/dns/client.nix + ]; + }; + }; + + testScript = {nodes, ...}: let + acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;}; + in + /* + python + */ + '' + # Start dependencies for the other services + acme.start() + acme.wait_for_unit("pebble.service") + name_server.start() + name_server.wait_for_unit("nsd.service") + + # Start the actual testing machines + start_all() + + + with subtest("Add pebble ca key to all services"): + for node in [name_server, server, client]: + node.wait_for_unit("network-online.target") + node.succeed("${acme_scripts.add_pebble_acme_ca}") + + server.wait_for_unit("sharkey.service") + + with subtest("All services running"): + import json + def all_services_running(host): + (status, output) = host.systemctl("list-units --state=failed --plain --no-pager --output=json") + host_failed = json.loads(output) + assert len(host_failed) == 0, f"Expected zero failing services, but found: {json.dumps(host_failed, indent=4)}" + all_services_running(server) + + client.wait_until_succeeds("curl --silent https://sharkey.server | grep 'Thank you for using Sharkey!'") + ''; +} diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate b/tests/common/acme/certs/generate index 0d6258e..0d6258e 100755 --- a/tests/by-name/em/email-dns/nodes/acme/certs/generate +++ b/tests/common/acme/certs/generate diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate.ca b/tests/common/acme/certs/generate.ca index 92832c5..92832c5 100755 --- a/tests/by-name/em/email-dns/nodes/acme/certs/generate.ca +++ b/tests/common/acme/certs/generate.ca diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate.client b/tests/common/acme/certs/generate.client index 5930298..5930298 100755 --- a/tests/by-name/em/email-dns/nodes/acme/certs/generate.client +++ b/tests/common/acme/certs/generate.client diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem b/tests/common/acme/certs/output/acme.test.cert.pem index 687101d..687101d 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem +++ b/tests/common/acme/certs/output/acme.test.cert.pem diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem b/tests/common/acme/certs/output/acme.test.key.pem index 06195b8..06195b8 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem +++ b/tests/common/acme/certs/output/acme.test.key.pem diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template b/tests/common/acme/certs/output/acme.test.template index 320a170..320a170 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template +++ b/tests/common/acme/certs/output/acme.test.template diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem b/tests/common/acme/certs/output/ca.cert.pem index 0fa9d14..0fa9d14 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem +++ b/tests/common/acme/certs/output/ca.cert.pem diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem b/tests/common/acme/certs/output/ca.key.pem index 64263bc..64263bc 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem +++ b/tests/common/acme/certs/output/ca.key.pem diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template b/tests/common/acme/certs/output/ca.template index a2295d8..a2295d8 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template +++ b/tests/common/acme/certs/output/ca.template diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix b/tests/common/acme/certs/snakeoil-certs.nix index aeb6dfc..aeb6dfc 100644 --- a/tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix +++ b/tests/common/acme/certs/snakeoil-certs.nix diff --git a/tests/by-name/em/email-dns/nodes/acme/client.nix b/tests/common/acme/client.nix index 2b870e8..2b870e8 100644 --- a/tests/by-name/em/email-dns/nodes/acme/client.nix +++ b/tests/common/acme/client.nix diff --git a/tests/common/acme/scripts.nix b/tests/common/acme/scripts.nix new file mode 100644 index 0000000..2228823 --- /dev/null +++ b/tests/common/acme/scripts.nix @@ -0,0 +1,30 @@ +{pkgs}: +/* +* Extra functions useful for the test script. +*/ +{ + add_pebble_acme_ca = pkgs.writeShellScript "fetch-and-set-ca" '' + set -xe + + # Fetch the randomly generated ca certificate + curl https://acme.test:15000/roots/0 > /tmp/ca.crt + curl https://acme.test:15000/intermediates/0 >> /tmp/ca.crt + + # Append it to the various system stores + # The file paths are from <nixpgks>/modules/security/ca.nix + for cert_path in "ssl/certs/ca-certificates.crt" "ssl/certs/ca-bundle.crt" "pki/tls/certs/ca-bundle.crt"; do + cert_path="/etc/$cert_path" + + mv "$cert_path" "$cert_path.old" + cat "$cert_path.old" > "$cert_path" + cat /tmp/ca.crt >> "$cert_path" + done + + export NIX_SSL_CERT_FILE=/tmp/ca.crt + export SSL_CERT_FILE=/tmp/ca.crt + + # TODO + # # P11-Kit trust source. + # environment.etc."ssl/trust-source".source = "$${cacertPackage.p11kit}/etc/ssl/trust-source"; + ''; +} diff --git a/tests/by-name/em/email-dns/nodes/acme/default.nix b/tests/common/acme/server.nix index 236ba6a..997c944 100644 --- a/tests/by-name/em/email-dns/nodes/acme/default.nix +++ b/tests/common/acme/server.nix @@ -1,28 +1,5 @@ -# The certificate for the ACME service is exported as: -# -# config.test-support.acme.caCert -# -# This value can be used inside the configuration of other test nodes to inject -# the test certificate into security.pki.certificateFiles or into package -# overlays. -# -# { -# acme = { nodes, lib, ... }: { -# imports = [ ./common/acme/server ]; -# networking.nameservers = lib.mkForce [ -# nodes.mydnsresolver.networking.primaryIPAddress -# ]; -# }; -# -# dnsmyresolver = ...; -# } -# -# Keep in mind, that currently only _one_ resolver is supported, if you have -# more than one resolver in networking.nameservers only the first one will be -# used. -# -# Also make sure that whenever you use a resolver from a different test node -# that it has to be started _before_ the ACME service. +# Add this node as acme server. +# This also needs a DNS server. { config, pkgs, diff --git a/tests/common/dns/client.nix b/tests/common/dns/client.nix new file mode 100644 index 0000000..52f3267 --- /dev/null +++ b/tests/common/dns/client.nix @@ -0,0 +1,10 @@ +{ + lib, + nodes, + ... +}: { + networking.nameservers = lib.mkForce [ + nodes.name_server.networking.primaryIPAddress + nodes.name_server.networking.primaryIPv6Address + ]; +} diff --git a/tests/common/dns/server.nix b/tests/common/dns/server.nix new file mode 100644 index 0000000..0c8d72c --- /dev/null +++ b/tests/common/dns/server.nix @@ -0,0 +1,43 @@ +{ + lib, + nodes, + ... +}: { + imports = [ + ../../../modules + ]; + + networking.nameservers = lib.mkForce [ + nodes.name_server.networking.primaryIPAddress + nodes.name_server.networking.primaryIPv6Address + ]; + + vhack = { + dns = { + enable = true; + openFirewall = true; + interfaces = [ + nodes.name_server.networking.primaryIPAddress + nodes.name_server.networking.primaryIPv6Address + ]; + + zones = { + "acme.test" = { + SOA = { + nameServer = "ns"; + adminEmail = "admin@server.com"; + serial = 2025012301; + }; + useOrigin = false; + + A = [ + nodes.acme.networking.primaryIPAddress + ]; + AAAA = [ + nodes.acme.networking.primaryIPv6Address + ]; + }; + }; + }; + }; +} diff --git a/tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age b/tests/common/email/dkim/alice.com/private.age index 5415fdc..5415fdc 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age +++ b/tests/common/email/dkim/alice.com/private.age diff --git a/tests/by-name/em/email-dns/secrets/dkim/alice.com/public b/tests/common/email/dkim/alice.com/public index 0f3c3b2..0f3c3b2 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/alice.com/public +++ b/tests/common/email/dkim/alice.com/public diff --git a/tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age b/tests/common/email/dkim/bob.com/private.age index c07c997..c07c997 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age +++ b/tests/common/email/dkim/bob.com/private.age diff --git a/tests/by-name/em/email-dns/secrets/dkim/bob.com/public b/tests/common/email/dkim/bob.com/public index ddea670..ddea670 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/bob.com/public +++ b/tests/common/email/dkim/bob.com/public diff --git a/tests/by-name/em/email-dns/secrets/dkim/gen_key.sh b/tests/common/email/dkim/gen_key.sh index 48b4434..48b4434 100755 --- a/tests/by-name/em/email-dns/secrets/dkim/gen_key.sh +++ b/tests/common/email/dkim/gen_key.sh diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age b/tests/common/email/dkim/mail1.server.com/private.age index 8c5d3c3..8c5d3c3 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age +++ b/tests/common/email/dkim/mail1.server.com/private.age diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public b/tests/common/email/dkim/mail1.server.com/public index 4941b85..4941b85 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public +++ b/tests/common/email/dkim/mail1.server.com/public diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age b/tests/common/email/dkim/mail2.server.com/private.age index d39631a..d39631a 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age +++ b/tests/common/email/dkim/mail2.server.com/private.age diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public b/tests/common/email/dkim/mail2.server.com/public index 5c4406d..5c4406d 100644 --- a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public +++ b/tests/common/email/dkim/mail2.server.com/public diff --git a/tests/by-name/em/email-dns/secrets/hostKey b/tests/common/email/hostKey index 79c9d6c..79c9d6c 100644 --- a/tests/by-name/em/email-dns/secrets/hostKey +++ b/tests/common/email/hostKey diff --git a/update.sh b/update.sh index c43b80b..e22f4ea 100755 --- a/update.sh +++ b/update.sh @@ -3,12 +3,13 @@ __update_sh_run() { __update_sh_command="$1" shift 1 - printf "\033[35;1m> \033[0m\033[35;1m%s\033[0m\n" "Running '$(basename "$__update_sh_command")' .." + printf "\033[35;1m> \033[0m\033[35;1m%s\033[0m\n" "Running '$(basename "$__update_sh_command") $*' .." "$__update_sh_command" "$@" unset __update_sh_command } +__update_sh_run nix flake update __update_sh_run ./pkgs/update_pkgs.sh "$@" # vim: ft=sh diff --git a/zones/vhack.eu/zone.nix b/zones/vhack.eu/zone.nix index d647174..696d1df 100644 --- a/zones/vhack.eu/zone.nix +++ b/zones/vhack.eu/zone.nix @@ -38,7 +38,7 @@ in { adminEmail = "dns-admin@foss-syndicate.org"; # NOTE(@bpeetz): ALWAYS change the serial number, when you change something in the # zone file! <2025-04-01> - serial = mkSerial 2025 04 11 01; + serial = mkSerial 2025 04 23 01; }; useOrigin = false; @@ -157,6 +157,7 @@ in { redlib.CNAME = ["server2.vhack.eu."]; nextcloud.CNAME = ["server2.vhack.eu."]; + sharkey.CNAME = ["server2.vhack.eu."]; mastodon.CNAME = ["server3.vhack.eu."]; matrix.CNAME = ["server3.vhack.eu."]; |
