@@ -0,0 +1,265 @@
1+/*
2+ Pleroma E2E VM test.
3+
4+ Abstract:
5+ =========
6+ Using pleroma, postgresql, a local CA cert, a nginx reverse proxy
7+ and a toot-based client, we're going to:
8+
9+ 1. Provision a pleroma service from scratch (pleroma config + postgres db).
10+ 2. Create a "jamy" admin user.
11+ 3. Send a toot from this user.
12+ 4. Send a upload from this user.
13+ 5. Check the toot is part of the server public timeline
14+
15+ Notes:
16+ - We need a fully functional TLS setup without having any access to
17+ the internet. We do that by issuing a self-signed cert, add this
18+ self-cert to the hosts pki trust store and finally spoof the
19+ hostnames using /etc/hosts.
20+ - For this NixOS test, we *had* to store some DB-related and
21+ pleroma-related secrets to the store. Keep in mind the store is
22+ world-readable, it's the worst place possible to store *any*
23+ secret. **DO NOT DO THIS IN A REAL WORLD DEPLOYMENT**.
24+*/
25+
26+import ./make-test-python.nix ({ pkgs, ... }:
27+ let
28+ send-toot = pkgs.writeScriptBin "send-toot" ''
29+ set -eux
30+ # toot is using the requests library internally. This library
31+ # sadly embed its own certificate store instead of relying on the
32+ # system one. Overriding this pretty bad default behaviour.
33+ export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
34+
35+ export TOOT_LOGIN_CLI_PASSWORD="jamy-password"
36+ toot login_cli -i "pleroma.nixos.test" -e "jamy@nixos.test"
37+ echo "Login OK"
38+
39+ # Send a toot then verify it's part of the public timeline
40+ echo "y" | toot post "hello world Jamy here"
41+ echo "Send toot OK"
42+ echo "y" | toot timeline | grep -c "hello world Jamy here"
43+ echo "Get toot from timeline OK"
44+
45+ # Test file upload
46+ echo "y" | toot upload ${db-seed} | grep -c "https://pleroma.nixos.test/media"
47+ echo "File upload OK"
48+
49+ echo "====================================================="
50+ echo "= SUCCESS ="
51+ echo "= ="
52+ echo "= We were able to sent a toot + a upload and ="
53+ echo "= retrieve both of them in the public timeline. ="
54+ echo "====================================================="
55+ '';
56+
57+ provision-db = pkgs.writeScriptBin "provision-db" ''
58+ set -eux
59+ sudo -u postgres psql -f ${db-seed}
60+ '';
61+
62+ test-db-passwd = "SccZOvTGM//BMrpoQj68JJkjDkMGb4pHv2cECWiI+XhVe3uGJTLI0vFV/gDlZ5jJ";
63+
64+ /* For this NixOS test, we *had* to store this secret to the store.
65+ Keep in mind the store is world-readable, it's the worst place
66+ possible to store *any* secret. **DO NOT DO THIS IN A REAL WORLD
67+ DEPLOYMENT**.*/
68+ db-seed = pkgs.writeText "provision.psql" ''
69+ CREATE USER pleroma WITH ENCRYPTED PASSWORD '${test-db-passwd}';
70+ CREATE DATABASE pleroma OWNER pleroma;
71+ \c pleroma;
72+ --Extensions made by ecto.migrate that need superuser access
73+ CREATE EXTENSION IF NOT EXISTS citext;
74+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
75+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
76+ '';
77+
78+ pleroma-conf = ''
79+ import Config
80+
81+ config :pleroma, Pleroma.Web.Endpoint,
82+ url: [host: "pleroma.nixos.test", scheme: "https", port: 443],
83+ http: [ip: {127, 0, 0, 1}, port: 4000]
84+
85+ config :pleroma, :instance,
86+ name: "NixOS test pleroma server",
87+ email: "pleroma@nixos.test",
88+ notify_email: "pleroma@nixos.test",
89+ limit: 5000,
90+ registrations_open: true
91+
92+ config :pleroma, :media_proxy,
93+ enabled: false,
94+ redirect_on_failure: true
95+ #base_url: "https://cache.pleroma.social"
96+
97+ config :pleroma, Pleroma.Repo,
98+ adapter: Ecto.Adapters.Postgres,
99+ username: "pleroma",
100+ password: "${test-db-passwd}",
101+ database: "pleroma",
102+ hostname: "localhost",
103+ pool_size: 10,
104+ prepare: :named,
105+ parameters: [
106+ plan_cache_mode: "force_custom_plan"
107+ ]
108+
109+ config :pleroma, :database, rum_enabled: false
110+ config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
111+ config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
112+ config :pleroma, configurable_from_database: false
113+ '';
114+
115+ /* For this NixOS test, we *had* to store this secret to the store.
116+ Keep in mind the store is world-readable, it's the worst place
117+ possible to store *any* secret. **DO NOT DO THIS IN A REAL WORLD
118+ DEPLOYMENT**.
119+ In a real-word deployment, you'd handle this either by:
120+ - manually upload your pleroma secrets to /var/lib/pleroma/secrets.exs
121+ - use a deployment tool such as morph or NixOps to deploy your secrets.
122+ */
123+ pleroma-conf-secret = pkgs.writeText "secrets.exs" ''
124+ import Config
125+
126+ config :joken, default_signer: "PS69/wMW7X6FIQPABt9lwvlZvgrJIncfiAMrK9J5mjVus/7/NJJi1DsDA1OghBE5"
127+
128+ config :pleroma, Pleroma.Web.Endpoint,
129+ secret_key_base: "NvfmU7lYaQrmmxt4NACm0AaAfN9t6WxsrX0NCB4awkGHvr1S7jyshlEmrjaPFhhq",
130+ signing_salt: "3L41+BuJ"
131+
132+ config :web_push_encryption, :vapid_details,
133+ subject: "mailto:pleroma@nixos.test",
134+ public_key: "BKjfNX9-UqAcncaNqERQtF7n9pKrB0-MO-juv6U5E5XQr_Tg5D-f8AlRjduAguDpyAngeDzG8MdrTejMSL4VF30",
135+ private_key: "k7o9onKMQrgMjMb6l4fsxSaXO0BTNAer5MVSje3q60k"
136+ '';
137+
138+ /* For this NixOS test, we *had* to store this secret to the store.
139+ Keep in mind the store is world-readable, it's the worst place
140+ possible to store *any* secret. **DO NOT DO THIS IN A REAL WORLD
141+ DEPLOYMENT**.
142+ In a real-word deployment, you'd handle this either by:
143+ - manually upload your pleroma secrets to /var/lib/pleroma/secrets.exs
144+ - use a deployment tool such as morph or NixOps to deploy your secrets.
145+ */
146+ provision-secrets = pkgs.writeScriptBin "provision-secrets" ''
147+ set -eux
148+ cp "${pleroma-conf-secret}" "/var/lib/pleroma/secrets.exs"
149+ chown pleroma:pleroma /var/lib/pleroma/secrets.exs
150+ '';
151+
152+ /* For this NixOS test, we *had* to store this secret to the store.
153+ Keep in mind the store is world-readable, it's the worst place
154+ possible to store *any* secret. **DO NOT DO THIS IN A REAL WORLD
155+ DEPLOYMENT**.
156+ */
157+ provision-user = pkgs.writeScriptBin "provision-user" ''
158+ set -eux
159+
160+ # Waiting for pleroma to be up.
161+ timeout 5m bash -c 'while [[ "$(curl -s -o /dev/null -w '%{http_code}' https://pleroma.nixos.test/api/v1/instance)" != "200" ]]; do sleep 2; done'
162+ pleroma_ctl user new jamy jamy@nixos.test --password 'jamy-password' --moderator --admin -y
163+ '';
164+
165+ tls-cert = pkgs.runCommandNoCC "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
166+ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=pleroma.nixos.test'
167+ mkdir -p $out
168+ cp key.pem cert.pem $out
169+ '';
170+
171+ /* Toot is preventing users from feeding login_cli a password non
172+ interactively. While it makes sense most of the times, it's
173+ preventing us to login in this non-interactive test. This patch
174+ introduce a TOOT_LOGIN_CLI_PASSWORD env variable allowing us to
175+ provide a password to toot login_cli
176+
177+ If https://github.com/ihabunek/toot/pull/180 gets merged at some
178+ point, feel free to remove this patch. */
179+ custom-toot = pkgs.toot.overrideAttrs(old:{
180+ patches = [ (pkgs.fetchpatch {
181+ url = "https://github.com/NinjaTrappeur/toot/commit/b4a4c30f41c0cb7e336714c2c4af9bc9bfa0c9f2.patch";
182+ sha256 = "sha256-0xxNwjR/fStLjjUUhwzCCfrghRVts+fc+fvVJqVcaFg=";
183+ }) ];
184+ });
185+
186+ hosts = nodes: ''
187+ ${nodes.pleroma.config.networking.primaryIPAddress} pleroma.nixos.test
188+ ${nodes.client.config.networking.primaryIPAddress} client.nixos.test
189+ '';
190+ in {
191+ name = "pleroma";
192+ nodes = {
193+ client = { nodes, pkgs, config, ... }: {
194+ security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
195+ networking.extraHosts = hosts nodes;
196+ environment.systemPackages = with pkgs; [
197+ custom-toot
198+ send-toot
199+ ];
200+ };
201+ pleroma = { nodes, pkgs, config, ... }: {
202+ security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
203+ networking.extraHosts = hosts nodes;
204+ networking.firewall.enable = false;
205+ environment.systemPackages = with pkgs; [
206+ provision-db
207+ provision-secrets
208+ provision-user
209+ ];
210+ services = {
211+ pleroma = {
212+ enable = true;
213+ configs = [
214+ pleroma-conf
215+ ];
216+ };
217+ postgresql = {
218+ enable = true;
219+ package = pkgs.postgresql_12;
220+ };
221+ nginx = {
222+ enable = true;
223+ virtualHosts."pleroma.nixos.test" = {
224+ addSSL = true;
225+ sslCertificate = "${tls-cert}/cert.pem";
226+ sslCertificateKey = "${tls-cert}/key.pem";
227+ locations."/" = {
228+ proxyPass = "http://127.0.0.1:4000";
229+ extraConfig = ''
230+ add_header 'Access-Control-Allow-Origin' '*' always;
231+ add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
232+ add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
233+ add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
234+ if ($request_method = OPTIONS) {
235+ return 204;
236+ }
237+ add_header X-XSS-Protection "1; mode=block";
238+ add_header X-Permitted-Cross-Domain-Policies none;
239+ add_header X-Frame-Options DENY;
240+ add_header X-Content-Type-Options nosniff;
241+ add_header Referrer-Policy same-origin;
242+ add_header X-Download-Options noopen;
243+ proxy_http_version 1.1;
244+ proxy_set_header Upgrade $http_upgrade;
245+ proxy_set_header Connection "upgrade";
246+ proxy_set_header Host $host;
247+ client_max_body_size 16m;
248+ '';
249+ };
250+ };
251+ };
252+ };
253+ };
254+ };
255+
256+ testScript = { nodes, ... }: ''
257+ pleroma.wait_for_unit("postgresql.service")
258+ pleroma.succeed("provision-db")
259+ pleroma.succeed("provision-secrets")
260+ pleroma.systemctl("restart pleroma.service")
261+ pleroma.wait_for_unit("pleroma.service")
262+ pleroma.succeed("provision-user")
263+ client.succeed("send-toot")
264+ '';
265+})