Merge pull request #117126 from Izorkin/update-mastodon-init-db

nixos/mastodon: fix init db on remote postgresql

authored by Kerstin and committed by GitHub c28d4da8 757b8221

+383 -143
+17
nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
··· 146 146 </listitem> 147 147 <listitem> 148 148 <para> 149 + In <literal>mastodon</literal> it is now necessary to specify 150 + location of file with <literal>PostgreSQL</literal> database 151 + password. In 152 + <literal>services.mastodon.database.passwordFile</literal> 153 + parameter default value 154 + <literal>/var/lib/mastodon/secrets/db-password</literal> has 155 + been changed to <literal>null</literal>. 156 + </para> 157 + </listitem> 158 + <listitem> 159 + <para> 149 160 The <literal>nix.readOnlyStore</literal> option has been 150 161 renamed to <literal>boot.readOnlyNixStore</literal> to clarify 151 162 that it configures the NixOS boot process, not the Nix daemon. ··· 206 217 <para> 207 218 The minimal ISO image now uses the 208 219 <literal>nixos/modules/profiles/minimal.nix</literal> profile. 220 + </para> 221 + </listitem> 222 + <listitem> 223 + <para> 224 + <literal>mastodon</literal> now supports connection to a 225 + remote <literal>PostgreSQL</literal> database. 209 226 </para> 210 227 </listitem> 211 228 <listitem>
+4
nixos/doc/manual/release-notes/rl-2305.section.md
··· 43 43 44 44 - Qt 5.12 and 5.14 have been removed, as the corresponding branches have been EOL upstream for a long time. This affected under 10 packages in nixpkgs, largely unmaintained upstream as well, however, out-of-tree package expressions may need to be updated manually. 45 45 46 + - In `mastodon` it is now necessary to specify location of file with `PostgreSQL` database password. In `services.mastodon.database.passwordFile` parameter default value `/var/lib/mastodon/secrets/db-password` has been changed to `null`. 47 + 46 48 - The `nix.readOnlyStore` option has been renamed to `boot.readOnlyNixStore` to clarify that it configures the NixOS boot process, not the Nix daemon. 47 49 48 50 ## Other Notable Changes {#sec-release-23.05-notable-changes} ··· 63 65 - To reduce closure size in `nixos/modules/profiles/minimal.nix` profile disabled installation documentations and manuals. Also disabled `logrotate` and `udisks2` services. 64 66 65 67 - The minimal ISO image now uses the `nixos/modules/profiles/minimal.nix` profile. 68 + 69 + - `mastodon` now supports connection to a remote `PostgreSQL` database. 66 70 67 71 - A new `virtualisation.rosetta` module was added to allow running `x86_64` binaries through [Rosetta](https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment) inside virtualised NixOS guests on Apple silicon. This feature works by default with the [UTM](https://docs.getutm.app/) virtualisation [package](https://search.nixos.org/packages?channel=unstable&show=utm&from=0&size=1&sort=relevance&type=packages&query=utm). 68 72
+46 -12
nixos/modules/services/web-apps/mastodon.nix
··· 1 - { config, lib, pkgs, ... }: 1 + { lib, pkgs, config, options, ... }: 2 2 3 3 let 4 4 cfg = config.services.mastodon; 5 + opt = options.services.mastodon; 6 + 5 7 # We only want to create a database if we're actually going to connect to it. 6 8 databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "/run/postgresql"; 7 9 ··· 23 25 REDIS_HOST = cfg.redis.host; 24 26 REDIS_PORT = toString(cfg.redis.port); 25 27 DB_HOST = cfg.database.host; 26 - DB_PORT = toString(cfg.database.port); 27 28 DB_NAME = cfg.database.name; 28 29 LOCAL_DOMAIN = cfg.localDomain; 29 30 SMTP_SERVER = cfg.smtp.host; ··· 37 38 38 39 TRUSTED_PROXY_IP = cfg.trustedProxy; 39 40 } 40 - // (if cfg.smtp.authenticate then { SMTP_LOGIN = cfg.smtp.user; } else {}) 41 + // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; } 42 + // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN = cfg.smtp.user; } 41 43 // cfg.extraConfig; 42 44 43 45 systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@mount" "@obsolete" "@privileged" "@setuid" ]; ··· 314 316 }; 315 317 316 318 port = lib.mkOption { 317 - type = lib.types.port; 318 - default = 5432; 319 + type = lib.types.nullOr lib.types.port; 320 + default = if cfg.database.createLocally then null else 5432; 321 + defaultText = lib.literalExpression '' 322 + if config.${opt.database.createLocally} 323 + then null 324 + else 5432 325 + ''; 319 326 description = lib.mdDoc "Database host port."; 320 327 }; 321 328 ··· 333 340 334 341 passwordFile = lib.mkOption { 335 342 type = lib.types.nullOr lib.types.path; 336 - default = "/var/lib/mastodon/secrets/db-password"; 337 - example = "/run/keys/mastodon-db-password"; 343 + default = null; 344 + example = "/var/lib/mastodon/secrets/db-password"; 338 345 description = lib.mdDoc '' 339 346 A file containing the password corresponding to 340 347 {option}`database.user`. ··· 468 475 assertions = [ 469 476 { 470 477 assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user); 471 - message = ''For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user and services.mastodon.database.user must be identical.''; 478 + message = '' 479 + For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer 480 + authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user 481 + and services.mastodon.database.user must be identical. 482 + ''; 483 + } 484 + { 485 + assertion = !databaseActuallyCreateLocally -> (cfg.database.host != "/run/postgresql"); 486 + message = '' 487 + <option>services.mastodon.database.host</option> needs to be set if 488 + <option>services.mastodon.database.createLocally</option> is not enabled. 489 + ''; 472 490 } 473 491 { 474 492 assertion = cfg.smtp.authenticate -> (cfg.smtp.user != null); ··· 512 530 OTP_SECRET="$(cat ${cfg.otpSecretFile})" 513 531 VAPID_PRIVATE_KEY="$(cat ${cfg.vapidPrivateKeyFile})" 514 532 VAPID_PUBLIC_KEY="$(cat ${cfg.vapidPublicKeyFile})" 533 + '' + lib.optionalString (cfg.database.passwordFile != null) '' 515 534 DB_PASS="$(cat ${cfg.database.passwordFile})" 516 - '' + (if cfg.smtp.authenticate then '' 535 + '' + lib.optionalString cfg.smtp.authenticate '' 517 536 SMTP_PASSWORD="$(cat ${cfg.smtp.passwordFile})" 518 - '' else "") + '' 537 + '' + '' 519 538 EOF 520 539 ''; 521 540 environment = env; ··· 530 549 }; 531 550 532 551 systemd.services.mastodon-init-db = lib.mkIf cfg.automaticMigrations { 533 - script = '' 552 + script = lib.optionalString (!databaseActuallyCreateLocally) '' 553 + umask 077 554 + 555 + export PGPASSFILE 556 + PGPASSFILE=$(mktemp) 557 + cat > $PGPASSFILE <<EOF 558 + ${cfg.database.host}:${toString cfg.database.port}:${cfg.database.name}:${cfg.database.user}:$(cat ${cfg.database.passwordFile}) 559 + EOF 560 + 561 + '' + '' 534 562 if [ `psql ${cfg.database.name} -c \ 535 563 "select count(*) from pg_class c \ 536 564 join pg_namespace s on s.oid = c.relnamespace \ ··· 541 569 else 542 570 rails db:migrate 543 571 fi 572 + '' + lib.optionalString (!databaseActuallyCreateLocally) '' 573 + rm $PGPASSFILE 574 + unset PGPASSFILE 544 575 ''; 545 576 path = [ cfg.package pkgs.postgresql ]; 546 - environment = env; 577 + environment = env // lib.optionalAttrs (!databaseActuallyCreateLocally) { 578 + PGHOST = cfg.database.host; 579 + PGUSER = cfg.database.user; 580 + }; 547 581 serviceConfig = { 548 582 Type = "oneshot"; 549 583 EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
+1 -1
nixos/tests/all-tests.nix
··· 365 365 mailhog = handleTest ./mailhog.nix {}; 366 366 man = handleTest ./man.nix {}; 367 367 mariadb-galera = handleTest ./mysql/mariadb-galera.nix {}; 368 - mastodon = handleTestOn ["x86_64-linux" "i686-linux" "aarch64-linux"] ./web-apps/mastodon.nix {}; 368 + mastodon = discoverTests (import ./web-apps/mastodon { inherit handleTestOn; }); 369 369 matomo = handleTest ./matomo.nix {}; 370 370 matrix-appservice-irc = handleTest ./matrix/appservice-irc.nix {}; 371 371 matrix-conduit = handleTest ./matrix/conduit.nix {};
-130
nixos/tests/web-apps/mastodon.nix
··· 1 - import ../make-test-python.nix ({pkgs, ...}: 2 - let 3 - cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' 4 - openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500 5 - mkdir -p $out 6 - cp key.pem cert.pem $out 7 - ''; 8 - 9 - hosts = '' 10 - 192.168.2.101 mastodon.local 11 - ''; 12 - 13 - in 14 - { 15 - name = "mastodon"; 16 - meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ]; 17 - 18 - nodes = { 19 - server = { pkgs, ... }: { 20 - 21 - virtualisation.memorySize = 2048; 22 - 23 - networking = { 24 - interfaces.eth1 = { 25 - ipv4.addresses = [ 26 - { address = "192.168.2.101"; prefixLength = 24; } 27 - ]; 28 - }; 29 - extraHosts = hosts; 30 - firewall.allowedTCPPorts = [ 80 443 ]; 31 - }; 32 - 33 - security = { 34 - pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 35 - }; 36 - 37 - services.redis.servers.mastodon = { 38 - enable = true; 39 - bind = "127.0.0.1"; 40 - port = 31637; 41 - }; 42 - 43 - services.mastodon = { 44 - enable = true; 45 - configureNginx = true; 46 - localDomain = "mastodon.local"; 47 - enableUnixSocket = false; 48 - smtp = { 49 - createLocally = false; 50 - fromAddress = "mastodon@mastodon.local"; 51 - }; 52 - extraConfig = { 53 - EMAIL_DOMAIN_ALLOWLIST = "example.com"; 54 - }; 55 - }; 56 - 57 - services.nginx = { 58 - virtualHosts."mastodon.local" = { 59 - enableACME = pkgs.lib.mkForce false; 60 - sslCertificate = "${cert pkgs}/cert.pem"; 61 - sslCertificateKey = "${cert pkgs}/key.pem"; 62 - }; 63 - }; 64 - }; 65 - 66 - client = { pkgs, ... }: { 67 - environment.systemPackages = [ pkgs.jq ]; 68 - networking = { 69 - interfaces.eth1 = { 70 - ipv4.addresses = [ 71 - { address = "192.168.2.102"; prefixLength = 24; } 72 - ]; 73 - }; 74 - extraHosts = hosts; 75 - }; 76 - 77 - security = { 78 - pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 79 - }; 80 - }; 81 - }; 82 - 83 - testScript = '' 84 - start_all() 85 - 86 - server.wait_for_unit("nginx.service") 87 - server.wait_for_unit("redis-mastodon.service") 88 - server.wait_for_unit("postgresql.service") 89 - server.wait_for_unit("mastodon-sidekiq.service") 90 - server.wait_for_unit("mastodon-streaming.service") 91 - server.wait_for_unit("mastodon-web.service") 92 - server.wait_for_open_port(55000) 93 - server.wait_for_open_port(55001) 94 - 95 - # Check that mastodon-media-auto-remove is scheduled 96 - server.succeed("systemctl status mastodon-media-auto-remove.timer") 97 - 98 - # Check Mastodon version from remote client 99 - client.succeed("curl --fail https://mastodon.local/api/v1/instance | jq -r '.version' | grep '${pkgs.mastodon.version}'") 100 - 101 - # Check access from remote client 102 - client.succeed("curl --fail https://mastodon.local/about | grep 'Mastodon hosted on mastodon.local'") 103 - client.succeed("curl --fail $(curl https://mastodon.local/api/v1/instance 2> /dev/null | jq -r .thumbnail) --output /dev/null") 104 - 105 - # Simple check tootctl commands 106 - # Check Mastodon version 107 - server.succeed("mastodon-tootctl version | grep '${pkgs.mastodon.version}'") 108 - 109 - # Manage accounts 110 - server.succeed("mastodon-tootctl email_domain_blocks add example.com") 111 - server.succeed("mastodon-tootctl email_domain_blocks list | grep example.com") 112 - server.fail("mastodon-tootctl email_domain_blocks list | grep mastodon.local") 113 - server.fail("mastodon-tootctl accounts create alice --email=alice@example.com") 114 - server.succeed("mastodon-tootctl email_domain_blocks remove example.com") 115 - server.succeed("mastodon-tootctl accounts create bob --email=bob@example.com") 116 - server.succeed("mastodon-tootctl accounts approve bob") 117 - server.succeed("mastodon-tootctl accounts delete bob") 118 - 119 - # Manage IP access 120 - server.succeed("mastodon-tootctl ip_blocks add 192.168.0.0/16 --severity=no_access") 121 - server.succeed("mastodon-tootctl ip_blocks export | grep 192.168.0.0/16") 122 - server.fail("mastodon-tootctl ip_blocks export | grep 172.16.0.0/16") 123 - client.fail("curl --fail https://mastodon.local/about") 124 - server.succeed("mastodon-tootctl ip_blocks remove 192.168.0.0/16") 125 - client.succeed("curl --fail https://mastodon.local/about") 126 - 127 - server.shutdown() 128 - client.shutdown() 129 - ''; 130 - })
+9
nixos/tests/web-apps/mastodon/default.nix
··· 1 + { system ? builtins.currentSystem, handleTestOn }: 2 + let 3 + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; 4 + 5 + in 6 + { 7 + standard = handleTestOn supportedSystems ./standard.nix { inherit system; }; 8 + remote-postgresql = handleTestOn supportedSystems ./remote-postgresql.nix { inherit system; }; 9 + }
+160
nixos/tests/web-apps/mastodon/remote-postgresql.nix
··· 1 + import ../../make-test-python.nix ({pkgs, ...}: 2 + let 3 + cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' 4 + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500 5 + mkdir -p $out 6 + cp key.pem cert.pem $out 7 + ''; 8 + 9 + hosts = '' 10 + 192.168.2.103 mastodon.local 11 + ''; 12 + 13 + in 14 + { 15 + name = "mastodon-remote-postgresql"; 16 + meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ]; 17 + 18 + nodes = { 19 + database = { 20 + networking = { 21 + interfaces.eth1 = { 22 + ipv4.addresses = [ 23 + { address = "192.168.2.102"; prefixLength = 24; } 24 + ]; 25 + }; 26 + extraHosts = hosts; 27 + firewall.allowedTCPPorts = [ 5432 ]; 28 + }; 29 + 30 + services.postgresql = { 31 + enable = true; 32 + enableTCPIP = true; 33 + authentication = '' 34 + hostnossl mastodon_local mastodon_test 192.168.2.201/32 md5 35 + ''; 36 + initialScript = pkgs.writeText "postgresql_init.sql" '' 37 + CREATE ROLE mastodon_test LOGIN PASSWORD 'SoDTZcISc3f1M1LJsRLT'; 38 + CREATE DATABASE mastodon_local TEMPLATE template0 ENCODING UTF8; 39 + GRANT ALL PRIVILEGES ON DATABASE mastodon_local TO mastodon_test; 40 + ''; 41 + }; 42 + }; 43 + 44 + nginx = { 45 + networking = { 46 + interfaces.eth1 = { 47 + ipv4.addresses = [ 48 + { address = "192.168.2.103"; prefixLength = 24; } 49 + ]; 50 + }; 51 + extraHosts = hosts; 52 + firewall.allowedTCPPorts = [ 80 443 ]; 53 + }; 54 + 55 + security = { 56 + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 57 + }; 58 + 59 + services.nginx = { 60 + enable = true; 61 + recommendedProxySettings = true; 62 + virtualHosts."mastodon.local" = { 63 + root = "/var/empty"; 64 + forceSSL = true; 65 + enableACME = pkgs.lib.mkForce false; 66 + sslCertificate = "${cert pkgs}/cert.pem"; 67 + sslCertificateKey = "${cert pkgs}/key.pem"; 68 + locations."/" = { 69 + tryFiles = "$uri @proxy"; 70 + }; 71 + locations."@proxy" = { 72 + proxyPass = "http://192.168.2.201:55001"; 73 + proxyWebsockets = true; 74 + }; 75 + locations."/api/v1/streaming/" = { 76 + proxyPass = "http://192.168.2.201:55002"; 77 + proxyWebsockets = true; 78 + }; 79 + }; 80 + }; 81 + }; 82 + 83 + server = { pkgs, ... }: { 84 + virtualisation.memorySize = 2048; 85 + 86 + environment = { 87 + etc = { 88 + "mastodon/password-posgressql-db".text = '' 89 + SoDTZcISc3f1M1LJsRLT 90 + ''; 91 + }; 92 + }; 93 + 94 + networking = { 95 + interfaces.eth1 = { 96 + ipv4.addresses = [ 97 + { address = "192.168.2.201"; prefixLength = 24; } 98 + ]; 99 + }; 100 + extraHosts = hosts; 101 + firewall.allowedTCPPorts = [ 55001 55002 ]; 102 + }; 103 + 104 + services.mastodon = { 105 + enable = true; 106 + configureNginx = false; 107 + localDomain = "mastodon.local"; 108 + enableUnixSocket = false; 109 + database = { 110 + createLocally = false; 111 + host = "192.168.2.102"; 112 + port = 5432; 113 + name = "mastodon_local"; 114 + user = "mastodon_test"; 115 + passwordFile = "/etc/mastodon/password-posgressql-db"; 116 + }; 117 + smtp = { 118 + createLocally = false; 119 + fromAddress = "mastodon@mastodon.local"; 120 + }; 121 + extraConfig = { 122 + BIND = "0.0.0.0"; 123 + EMAIL_DOMAIN_ALLOWLIST = "example.com"; 124 + RAILS_SERVE_STATIC_FILES = "true"; 125 + TRUSTED_PROXY_IP = "192.168.2.103"; 126 + }; 127 + }; 128 + }; 129 + 130 + client = { pkgs, ... }: { 131 + environment.systemPackages = [ pkgs.jq ]; 132 + networking = { 133 + interfaces.eth1 = { 134 + ipv4.addresses = [ 135 + { address = "192.168.2.202"; prefixLength = 24; } 136 + ]; 137 + }; 138 + extraHosts = hosts; 139 + }; 140 + 141 + security = { 142 + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 143 + }; 144 + }; 145 + }; 146 + 147 + testScript = import ./script.nix { 148 + inherit pkgs; 149 + extraInit = '' 150 + nginx.wait_for_unit("nginx.service") 151 + nginx.wait_for_open_port(443) 152 + database.wait_for_unit("postgresql.service") 153 + database.wait_for_open_port(5432) 154 + ''; 155 + extraShutdown = '' 156 + nginx.shutdown() 157 + database.shutdown() 158 + ''; 159 + }; 160 + })
+54
nixos/tests/web-apps/mastodon/script.nix
··· 1 + { pkgs 2 + , extraInit ? "" 3 + , extraShutdown ? "" 4 + }: 5 + 6 + '' 7 + start_all() 8 + 9 + ${extraInit} 10 + 11 + server.wait_for_unit("redis-mastodon.service") 12 + server.wait_for_unit("mastodon-sidekiq.service") 13 + server.wait_for_unit("mastodon-streaming.service") 14 + server.wait_for_unit("mastodon-web.service") 15 + server.wait_for_open_port(55000) 16 + server.wait_for_open_port(55001) 17 + 18 + # Check that mastodon-media-auto-remove is scheduled 19 + server.succeed("systemctl status mastodon-media-auto-remove.timer") 20 + 21 + # Check Mastodon version from remote client 22 + client.succeed("curl --fail https://mastodon.local/api/v1/instance | jq -r '.version' | grep '${pkgs.mastodon.version}'") 23 + 24 + # Check access from remote client 25 + client.succeed("curl --fail https://mastodon.local/about | grep 'Mastodon hosted on mastodon.local'") 26 + client.succeed("curl --fail $(curl https://mastodon.local/api/v1/instance 2> /dev/null | jq -r .thumbnail) --output /dev/null") 27 + 28 + # Simple check tootctl commands 29 + # Check Mastodon version 30 + server.succeed("mastodon-tootctl version | grep '${pkgs.mastodon.version}'") 31 + 32 + # Manage accounts 33 + server.succeed("mastodon-tootctl email_domain_blocks add example.com") 34 + server.succeed("mastodon-tootctl email_domain_blocks list | grep example.com") 35 + server.fail("mastodon-tootctl email_domain_blocks list | grep mastodon.local") 36 + server.fail("mastodon-tootctl accounts create alice --email=alice@example.com") 37 + server.succeed("mastodon-tootctl email_domain_blocks remove example.com") 38 + server.succeed("mastodon-tootctl accounts create bob --email=bob@example.com") 39 + server.succeed("mastodon-tootctl accounts approve bob") 40 + server.succeed("mastodon-tootctl accounts delete bob") 41 + 42 + # Manage IP access 43 + server.succeed("mastodon-tootctl ip_blocks add 192.168.0.0/16 --severity=no_access") 44 + server.succeed("mastodon-tootctl ip_blocks export | grep 192.168.0.0/16") 45 + server.fail("mastodon-tootctl ip_blocks export | grep 172.16.0.0/16") 46 + client.fail("curl --fail https://mastodon.local/about") 47 + server.succeed("mastodon-tootctl ip_blocks remove 192.168.0.0/16") 48 + client.succeed("curl --fail https://mastodon.local/about") 49 + 50 + server.shutdown() 51 + client.shutdown() 52 + 53 + ${extraShutdown} 54 + ''
+92
nixos/tests/web-apps/mastodon/standard.nix
··· 1 + import ../../make-test-python.nix ({pkgs, ...}: 2 + let 3 + cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' 4 + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500 5 + mkdir -p $out 6 + cp key.pem cert.pem $out 7 + ''; 8 + 9 + hosts = '' 10 + 192.168.2.101 mastodon.local 11 + ''; 12 + 13 + in 14 + { 15 + name = "mastodon-standard"; 16 + meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ]; 17 + 18 + nodes = { 19 + server = { pkgs, ... }: { 20 + 21 + virtualisation.memorySize = 2048; 22 + 23 + networking = { 24 + interfaces.eth1 = { 25 + ipv4.addresses = [ 26 + { address = "192.168.2.101"; prefixLength = 24; } 27 + ]; 28 + }; 29 + extraHosts = hosts; 30 + firewall.allowedTCPPorts = [ 80 443 ]; 31 + }; 32 + 33 + security = { 34 + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 35 + }; 36 + 37 + services.redis.servers.mastodon = { 38 + enable = true; 39 + bind = "127.0.0.1"; 40 + port = 31637; 41 + }; 42 + 43 + services.mastodon = { 44 + enable = true; 45 + configureNginx = true; 46 + localDomain = "mastodon.local"; 47 + enableUnixSocket = false; 48 + smtp = { 49 + createLocally = false; 50 + fromAddress = "mastodon@mastodon.local"; 51 + }; 52 + extraConfig = { 53 + EMAIL_DOMAIN_ALLOWLIST = "example.com"; 54 + }; 55 + }; 56 + 57 + services.nginx = { 58 + virtualHosts."mastodon.local" = { 59 + enableACME = pkgs.lib.mkForce false; 60 + sslCertificate = "${cert pkgs}/cert.pem"; 61 + sslCertificateKey = "${cert pkgs}/key.pem"; 62 + }; 63 + }; 64 + }; 65 + 66 + client = { pkgs, ... }: { 67 + environment.systemPackages = [ pkgs.jq ]; 68 + networking = { 69 + interfaces.eth1 = { 70 + ipv4.addresses = [ 71 + { address = "192.168.2.102"; prefixLength = 24; } 72 + ]; 73 + }; 74 + extraHosts = hosts; 75 + }; 76 + 77 + security = { 78 + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; 79 + }; 80 + }; 81 + }; 82 + 83 + testScript = import ./script.nix { 84 + inherit pkgs; 85 + extraInit = '' 86 + server.wait_for_unit("nginx.service") 87 + server.wait_for_open_port(443) 88 + server.wait_for_unit("postgresql.service") 89 + server.wait_for_open_port(5432) 90 + ''; 91 + }; 92 + })