From 6942d1dcf2941604207404b416a524d5aad179ae Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 22 Feb 2024 04:56:31 -0500 Subject: [PATCH 01/87] mastodon vm --- .envrc | 1 + .gitignore | 4 +++ README.md | 29 +++++++++++++++++++ configuration.nix | 71 +++++++++++++++++++++++++++++++++++++++++++++++ flake.lock | 27 ++++++++++++++++++ flake.nix | 27 ++++++++++++++++++ 6 files changed, 159 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 README.md create mode 100644 configuration.nix create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83e248 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +nixos.qcow2 +result* +.direnv + diff --git a/README.md b/README.md new file mode 100644 index 0000000..60a31ec --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# How to start up a mastodon VM + +```bash +nixos-rebuild build-vm --flake .#mastodon +./result/bin/run-nixos-vm +``` + +Now you can access mastodon at + +You will have to "accept the security risk". + +# TODOs + +- [ ] set up a domain name and a DNS service so we can do deploy this to an actual machine +- [ ] set up an email service +- [ ] add logging + - [ ] errors / logs + - [ ] performance +- [ ] switch to garage / s3 storage + - SEE: https://docs.joinmastodon.org/admin/optional/object-storage/ +- [ ] decouple the postgres database from this machine +- [ ] test with high use / throughput +- [ ] configure scaling behaviour + - SEE: https://docs.joinmastodon.org/admin/scaling/ +- [ ] remove the need for "accept security risk" dialogue if possible + +# resources + +- Tutorial for setting up better logging: https://krisztianfekete.org/self-hosting-mastodon-on-nixos-a-proof-of-concept/ diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..33ee5ff --- /dev/null +++ b/configuration.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, ... }: { + + # open up access to the mastodon web interface + networking.firewall.allowedTCPPorts = [ 443 ]; + + services.mastodon = { + enable = true; + + # TODO: set up a domain name, and a DNS service so that this can run not in a vm + # localDomain = "domain.social"; + configureNginx = true; + + # TODO: configure a mailserver so this works + smtp.fromAddress = "mastodon_vm"; + + # TODO: this is hardware-dependent. let's figure it out when we have hardware + # streamingProcesses = 1; + }; + + security.acme = { + acceptTerms = true; + preliminarySelfsigned = true; + # TODO: configure a mailserver so we can set up acme + # defaults.email = "test@example.com"; + }; + + # let us log in + users.mutableUsers = false; + users.users.root.password = " "; + + # access to convenient things + environment.systemPackages = with pkgs; [ w3m python3 ]; + nix.extraOptions = '' + extra-experimental-features = nix-command flakes + ''; + + # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) + virtualisation.vmVariant = { config, ... }: { + services.mastodon = { + # redirects to localhost, but allows it to have a proper domain name + # SEE: local.gd + localDomain = "social.local.gd"; + + # from the documentation: recommended is the amount of your CPU cores minus one. + # but it also must be a positive integer + streamingProcesses = let + ncores = config.virtualistation.cores; + max = x: y: if x > y then x else y; + in + max 1 (ncores - 1); + }; + + security.acme = { + defaults = { + # invalid server; the systemd service will fail, and we won't get properly signed certificates + # but let's not spam the letsencrypt servers (and we don't own this domain anyways) + server = "https://127.0.0.1"; + email = "none"; + }; + }; + + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 44443; + guest.port = 443; + } + ]; + }; +} + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..419cf1d --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1708475490, + "narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e74ca98a74bc7270d28838369593635a5db3260", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..71b9ce3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,27 @@ +{ + description = "Testing mastodon configurations"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + + nixosConfigurations = { + mastodon = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ ./configuration.nix ]; + }; + }; + + devShells.${system}.default = pkgs.mkShell { + inputs = with pkgs; [ + nil + ]; + }; + }; +} From ecf89fc0d0c99af5c92409f36b61ec56c71e1bf9 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 28 Feb 2024 16:49:16 -0500 Subject: [PATCH 02/87] tweaks --- README.md | 3 ++- configuration.nix | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60a31ec..35a396e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ nixos-rebuild build-vm --flake .#mastodon Now you can access mastodon at -You will have to "accept the security risk". +- You will have to "accept the security risk". +- It may take a minute for the webpage to come online. Until then you will see 502 Bad Gateway # TODOs diff --git a/configuration.nix b/configuration.nix index 33ee5ff..4fb4d41 100644 --- a/configuration.nix +++ b/configuration.nix @@ -11,7 +11,7 @@ configureNginx = true; # TODO: configure a mailserver so this works - smtp.fromAddress = "mastodon_vm"; + # smtp.fromAddress = "mastodon@social.local.gd"; # TODO: this is hardware-dependent. let's figure it out when we have hardware # streamingProcesses = 1; @@ -41,10 +41,14 @@ # SEE: local.gd localDomain = "social.local.gd"; + smtp = { + fromAddress = "mastodon@social.local.gd"; + createLocally = false; + }; # from the documentation: recommended is the amount of your CPU cores minus one. # but it also must be a positive integer streamingProcesses = let - ncores = config.virtualistation.cores; + ncores = config.virtualisation.cores; max = x: y: if x > y then x else y; in max 1 (ncores - 1); @@ -59,6 +63,7 @@ }; }; + virtualisation.memorySize = 2048; virtualisation.forwardPorts = [ { from = "host"; From a4cb05d8a10eb0bc1562f8c609ce706cbb4ca016 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 6 Mar 2024 04:40:22 -0500 Subject: [PATCH 03/87] account creation --- README.md | 28 ++++++++++++++++++ configuration.nix | 74 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35a396e..5e9c8de 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,29 @@ Now you can access mastodon at - You will have to "accept the security risk". - It may take a minute for the webpage to come online. Until then you will see 502 Bad Gateway +Remember that if you want to clear the state from one launch to the next, you should delete the `nixos.qcow2` file that is created. + +# Account creation / access + +Mastodon throws a hissyfit when trying to create accounts / login if it's not being **accessed** on port 443. This is a problem with the way we've set up port forwarding. + +My current (terrible) solution is to run +``` +nixos-rebuild build-vm --flake .#mastodon + +# start a proxy server to the server on port 1234 (you can pick your favourite port) +ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=Error -D 1234 root@localhost -p 2222 + +# optional. create a new firefox profile so we don't have to undo the settings when we're done +mkdir /tmp/profile && firefox --profile /tmp/profile +``` + +Then configure Firefox by going to `about:config` and setting `network.proxy.allow_hijacking_localhost` to `true`, and in `about:preferences` set the proxy to manual `localhost` port `1234`, and enable `Proxy DNS` at the bottom. + +Navigate to , and click "create account" + +- email verification is WIP, but should be accessible at + # TODOs - [ ] set up a domain name and a DNS service so we can do deploy this to an actual machine @@ -24,7 +47,12 @@ Now you can access mastodon at - [ ] configure scaling behaviour - SEE: https://docs.joinmastodon.org/admin/scaling/ - [ ] remove the need for "accept security risk" dialogue if possible +- [ ] development environment does not work seamlessly. +- [ ] don't require proxy server + - either forward 443 directly, or get mastodon to accept connections on a different port (maybe 3000? see development environment documentation) +- [ ] get letter_opener working # resources - Tutorial for setting up better logging: https://krisztianfekete.org/self-hosting-mastodon-on-nixos-a-proof-of-concept/ +- Setting up development environment: https://docs.joinmastodon.org/dev/setup/ diff --git a/configuration.nix b/configuration.nix index 4fb4d41..9cdcede 100644 --- a/configuration.nix +++ b/configuration.nix @@ -26,7 +26,15 @@ # let us log in users.mutableUsers = false; - users.users.root.password = " "; + users.users.root.hashedPassword = ""; + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "yes"; + PermitEmptyPasswords = "yes"; + UsePAM = "no"; + }; + }; # access to convenient things environment.systemPackages = with pkgs; [ w3m python3 ]; @@ -45,6 +53,24 @@ fromAddress = "mastodon@social.local.gd"; createLocally = false; }; + + extraConfig = { + EMAIL_DOMAIN_ALLOWLIST = "example.com"; + RAILS_ENV = "development"; + # for letter_opener + REMOTE_DEV = "true"; + }; + # database = { + # # createLocally = false; + # # host = "/run/postgresql"; + # # port = null; + # name = "mastodon_development"; + # user = "mastodon_development"; + # }; + # user = "mastodon_development"; + + # database.createLocally = false; + # from the documentation: recommended is the amount of your CPU cores minus one. # but it also must be a positive integer streamingProcesses = let @@ -54,6 +80,41 @@ max 1 (ncores - 1); }; + # users.users.mastodon_development = { + # isSystemUser = true; + # home = config.services.mastodon.package; + # group = "mastodon"; + # packages = [ config.services.mastodon.package pkgs.imagemagick ]; + # }; + + services.postgresql = { + enable = true; + ensureUsers = [ + { + name = config.services.mastodon.database.user; + ensureClauses.createdb = true; + # ensurePermissions."mastodon_development_test.*" = "ALL PRIVILEGES"; + } + ]; + # ensureDatabases = [ "mastodon_development_test" ]; + }; + + systemd.services.mastodon-init-db.script = lib.mkForce '' + if [ `psql -c \ + "select count(*) from pg_class c \ + join pg_namespace s on s.oid = c.relnamespace \ + where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ + and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then + echo "Seeding database" + rails db:setup + # SAFETY_ASSURED=1 rails db:schema:load + rails db:seed + else + echo "Migrating database (this might be a noop)" + rails db:migrate + fi + ''; + security.acme = { defaults = { # invalid server; the systemd service will fail, and we won't get properly signed certificates @@ -63,6 +124,12 @@ }; }; + services.nginx.virtualHosts.${config.services.mastodon.localDomain} = { + # extraConfig = '' + # add_header Referrer-Policy "same-origin"; + # ''; + }; + virtualisation.memorySize = 2048; virtualisation.forwardPorts = [ { @@ -70,6 +137,11 @@ host.port = 44443; guest.port = 443; } + { + from = "host"; + host.port = 2222; + guest.port = 22; + } ]; }; } From 230810bf6ff24471a3d89ff6531b2707a313950f Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 6 Mar 2024 04:48:01 -0500 Subject: [PATCH 04/87] refactor & cleanup --- configuration.nix | 276 +++++++++++++++++++++++----------------------- 1 file changed, 137 insertions(+), 139 deletions(-) diff --git a/configuration.nix b/configuration.nix index 9cdcede..85ca54d 100644 --- a/configuration.nix +++ b/configuration.nix @@ -1,148 +1,146 @@ -{ config, lib, pkgs, ... }: { - - # open up access to the mastodon web interface - networking.firewall.allowedTCPPorts = [ 443 ]; - - services.mastodon = { - enable = true; - - # TODO: set up a domain name, and a DNS service so that this can run not in a vm - # localDomain = "domain.social"; - configureNginx = true; - - # TODO: configure a mailserver so this works - # smtp.fromAddress = "mastodon@social.local.gd"; - - # TODO: this is hardware-dependent. let's figure it out when we have hardware - # streamingProcesses = 1; - }; - - security.acme = { - acceptTerms = true; - preliminarySelfsigned = true; - # TODO: configure a mailserver so we can set up acme - # defaults.email = "test@example.com"; - }; - - # let us log in - users.mutableUsers = false; - users.users.root.hashedPassword = ""; - services.openssh = { - enable = true; - settings = { - PermitRootLogin = "yes"; - PermitEmptyPasswords = "yes"; - UsePAM = "no"; - }; - }; - - # access to convenient things - environment.systemPackages = with pkgs; [ w3m python3 ]; - nix.extraOptions = '' - extra-experimental-features = nix-command flakes - ''; - - # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) - virtualisation.vmVariant = { config, ... }: { - services.mastodon = { - # redirects to localhost, but allows it to have a proper domain name - # SEE: local.gd - localDomain = "social.local.gd"; - - smtp = { - fromAddress = "mastodon@social.local.gd"; - createLocally = false; - }; - - extraConfig = { - EMAIL_DOMAIN_ALLOWLIST = "example.com"; - RAILS_ENV = "development"; - # for letter_opener - REMOTE_DEV = "true"; - }; - # database = { - # # createLocally = false; - # # host = "/run/postgresql"; - # # port = null; - # name = "mastodon_development"; - # user = "mastodon_development"; - # }; - # user = "mastodon_development"; - - # database.createLocally = false; - - # from the documentation: recommended is the amount of your CPU cores minus one. - # but it also must be a positive integer - streamingProcesses = let - ncores = config.virtualisation.cores; - max = x: y: if x > y then x else y; - in - max 1 (ncores - 1); - }; - - # users.users.mastodon_development = { - # isSystemUser = true; - # home = config.services.mastodon.package; - # group = "mastodon"; - # packages = [ config.services.mastodon.package pkgs.imagemagick ]; - # }; - - services.postgresql = { +{ config, lib, pkgs, ... }: lib.mkMerge [ + # not mastodon related + { + # let us log in + users.mutableUsers = false; + users.users.root.hashedPassword = ""; + services.openssh = { enable = true; - ensureUsers = [ - { - name = config.services.mastodon.database.user; - ensureClauses.createdb = true; - # ensurePermissions."mastodon_development_test.*" = "ALL PRIVILEGES"; - } - ]; - # ensureDatabases = [ "mastodon_development_test" ]; + settings = { + PermitRootLogin = "yes"; + PermitEmptyPasswords = "yes"; + UsePAM = "no"; + }; }; - systemd.services.mastodon-init-db.script = lib.mkForce '' - if [ `psql -c \ - "select count(*) from pg_class c \ - join pg_namespace s on s.oid = c.relnamespace \ - where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ - and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then - echo "Seeding database" - rails db:setup - # SAFETY_ASSURED=1 rails db:schema:load - rails db:seed - else - echo "Migrating database (this might be a noop)" - rails db:migrate - fi + # access to convenient things + environment.systemPackages = with pkgs; [ w3m python3 ]; + nix.extraOptions = '' + extra-experimental-features = nix-command flakes ''; + } + + # mastodon setup + { + # open up access to the mastodon web interface + networking.firewall.allowedTCPPorts = [ 443 ]; + + services.mastodon = { + enable = true; + + # TODO: set up a domain name, and a DNS service so that this can run not in a vm + # localDomain = "domain.social"; + configureNginx = true; + + # TODO: configure a mailserver so this works + # smtp.fromAddress = "mastodon@mastodon.localhost"; + + # TODO: this is hardware-dependent. let's figure it out when we have hardware + # streamingProcesses = 1; + }; security.acme = { - defaults = { - # invalid server; the systemd service will fail, and we won't get properly signed certificates - # but let's not spam the letsencrypt servers (and we don't own this domain anyways) - server = "https://127.0.0.1"; - email = "none"; + acceptTerms = true; + preliminarySelfsigned = true; + # TODO: configure a mailserver so we can set up acme + # defaults.email = "test@example.com"; + }; + } + + # VM setup + { + # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) + virtualisation.vmVariant = { config, ... }: { + services.mastodon = { + # redirects to localhost, but allows it to have a proper domain name + localDomain = "mastodon.localhost"; + + smtp = { + fromAddress = "mastodon@mastodon.localhost"; + createLocally = false; + }; + + extraConfig = { + EMAIL_DOMAIN_ALLOWLIST = "example.com"; + }; + + # from the documentation: recommended is the amount of your CPU cores minus one. + # but it also must be a positive integer + streamingProcesses = let + ncores = config.virtualisation.cores; + max = x: y: if x > y then x else y; + in + max 1 (ncores - 1); }; - }; - services.nginx.virtualHosts.${config.services.mastodon.localDomain} = { - # extraConfig = '' - # add_header Referrer-Policy "same-origin"; - # ''; - }; + security.acme = { + defaults = { + # invalid server; the systemd service will fail, and we won't get properly signed certificates + # but let's not spam the letsencrypt servers (and we don't own this domain anyways) + server = "https://127.0.0.1"; + email = "none"; + }; + }; - virtualisation.memorySize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 44443; - guest.port = 443; - } - { - from = "host"; - host.port = 2222; - guest.port = 22; - } - ]; - }; -} - + virtualisation.memorySize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 44443; + guest.port = 443; + } + { + from = "host"; + host.port = 2222; + guest.port = 22; + } + ]; + }; + } + + # mastodon development environment + { + virtualisation.vmVariant = { config, ... }: { + services.mastodon = { + extraConfig = { + RAILS_ENV = "development"; + # for letter_opener + REMOTE_DEV = "true"; + }; + }; + + services.postgresql = { + enable = true; + ensureUsers = [ + { + name = config.services.mastodon.database.user; + ensureClauses.createdb = true; + # ensurePermissions doesn't work anymore + # ensurePermissions = { + # "mastodon_development.*" = "ALL PRIVILEGES"; + # "mastodon_test.*" = "ALL PRIVILEGES"; + # } + } + ]; + # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; + }; + + # run rails db:seed so that mastodon sets up the databases for us + systemd.services.mastodon-init-db.script = lib.mkForce '' + if [ `psql -c \ + "select count(*) from pg_class c \ + join pg_namespace s on s.oid = c.relnamespace \ + where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ + and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then + echo "Seeding database" + rails db:setup + # SAFETY_ASSURED=1 rails db:schema:load + rails db:seed + else + echo "Migrating database (this might be a noop)" + rails db:migrate + fi + ''; + }; + } +] From dc6e4936ed8200ad6e15f7e97ad066939846c23a Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 6 Mar 2024 09:16:35 -0500 Subject: [PATCH 05/87] don't require proxy server --- README.md | 25 +++---------------------- configuration.nix | 13 ++++++++++++- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5e9c8de..4b35f76 100644 --- a/README.md +++ b/README.md @@ -5,33 +5,14 @@ nixos-rebuild build-vm --flake .#mastodon ./result/bin/run-nixos-vm ``` -Now you can access mastodon at +Now you can access mastodon at - You will have to "accept the security risk". - It may take a minute for the webpage to come online. Until then you will see 502 Bad Gateway Remember that if you want to clear the state from one launch to the next, you should delete the `nixos.qcow2` file that is created. -# Account creation / access - -Mastodon throws a hissyfit when trying to create accounts / login if it's not being **accessed** on port 443. This is a problem with the way we've set up port forwarding. - -My current (terrible) solution is to run -``` -nixos-rebuild build-vm --flake .#mastodon - -# start a proxy server to the server on port 1234 (you can pick your favourite port) -ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=Error -D 1234 root@localhost -p 2222 - -# optional. create a new firefox profile so we don't have to undo the settings when we're done -mkdir /tmp/profile && firefox --profile /tmp/profile -``` - -Then configure Firefox by going to `about:config` and setting `network.proxy.allow_hijacking_localhost` to `true`, and in `about:preferences` set the proxy to manual `localhost` port `1234`, and enable `Proxy DNS` at the bottom. - -Navigate to , and click "create account" - -- email verification is WIP, but should be accessible at +- email, when it works, will be accessible at # TODOs @@ -48,7 +29,7 @@ Navigate to , and click "create account" - SEE: https://docs.joinmastodon.org/admin/scaling/ - [ ] remove the need for "accept security risk" dialogue if possible - [ ] development environment does not work seamlessly. -- [ ] don't require proxy server +- [x] don't require proxy server - either forward 443 directly, or get mastodon to accept connections on a different port (maybe 3000? see development environment documentation) - [ ] get letter_opener working diff --git a/configuration.nix b/configuration.nix index 85ca54d..b38c355 100644 --- a/configuration.nix +++ b/configuration.nix @@ -102,6 +102,9 @@ { virtualisation.vmVariant = { config, ... }: { services.mastodon = { + # needed so we can directly access mastodon at port 55001 + # otherwise, mastodon has to be accessed *from* port 443, which we can't do via port forwarding + enableUnixSocket = false; extraConfig = { RAILS_ENV = "development"; # for letter_opener @@ -138,9 +141,17 @@ rails db:seed else echo "Migrating database (this might be a noop)" - rails db:migrate + # TODO: this breaks for some reason + # rails db:migrate fi ''; + virtualisation.forwardPorts = lib.mkForce [ + { + from = "host"; + host.port = 55001; + guest.port = 55001; + } + ]; }; } ] From 8c40168532d664325d752818832bd42a2870f525 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 19 Mar 2024 19:43:20 -0400 Subject: [PATCH 06/87] minimal peertube VM --- README.md | 27 +++++++++++++++++++++ common.nix | 37 +++++++++++++++++++++++++++++ flake.nix | 7 +++++- configuration.nix => mastodon.nix | 22 ----------------- peertube.nix | 39 +++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 common.nix rename configuration.nix => mastodon.nix (89%) create mode 100644 peertube.nix diff --git a/README.md b/README.md index 4b35f76..9a33301 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,31 @@ Remember that if you want to clear the state from one launch to the next, you sh - email, when it works, will be accessible at +## peertube + +```bash +nixos-rebuild build-vm --flake .#peertube +./result/bin/run-nixos-vm +``` + +Now you can access peertube at + +The root account can be logged in with username "root". The password can be obtained with the command +```bash +journalctl -u peertube | perl -ne '/password: (.*)/ && print $1' +``` + +or just + +```bash +journalctl -u peertube | grep password +``` + +and look at the end of the line. + +Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account. + + # TODOs - [ ] set up a domain name and a DNS service so we can do deploy this to an actual machine @@ -37,3 +62,5 @@ Remember that if you want to clear the state from one launch to the next, you sh - Tutorial for setting up better logging: https://krisztianfekete.org/self-hosting-mastodon-on-nixos-a-proof-of-concept/ - Setting up development environment: https://docs.joinmastodon.org/dev/setup/ + +- Tutorial for PeerTube that doesn't use `createLocally`: https://nixos.wiki/wiki/PeerTube diff --git a/common.nix b/common.nix new file mode 100644 index 0000000..1ec8c20 --- /dev/null +++ b/common.nix @@ -0,0 +1,37 @@ +{ pkgs, ... }: { + virtualisation.vmVariant = { + # let us log in + users.mutableUsers = false; + users.users.root.hashedPassword = ""; + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "yes"; + PermitEmptyPasswords = "yes"; + UsePAM = "no"; + }; + }; + + # automatically log in + services.getty.autologinUser = "root"; + + # access to convenient things + environment.systemPackages = with pkgs; [ w3m python3 ]; + nix.extraOptions = '' + extra-experimental-features = nix-command flakes + ''; + + # no graphics. see nixos-shell + virtualisation = { + graphics = false; + qemu.consoles = [ "tty0" "hvc0" ]; + qemu.options = [ + "-serial null" + "-device virtio-serial" + "-chardev stdio,mux=on,id=char0,signal=off" + "-mon chardev=char0,mode=readline" + "-device virtconsole,chardev=char0,nr=0" + ]; + }; + }; +} diff --git a/flake.nix b/flake.nix index 71b9ce3..a002450 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,12 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./configuration.nix ]; + modules = [ ./common.nix ./mastodon.nix ]; + }; + + peertube = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ ./common.nix ./peertube.nix ]; }; }; diff --git a/configuration.nix b/mastodon.nix similarity index 89% rename from configuration.nix rename to mastodon.nix index b38c355..6a80420 100644 --- a/configuration.nix +++ b/mastodon.nix @@ -1,25 +1,4 @@ { config, lib, pkgs, ... }: lib.mkMerge [ - # not mastodon related - { - # let us log in - users.mutableUsers = false; - users.users.root.hashedPassword = ""; - services.openssh = { - enable = true; - settings = { - PermitRootLogin = "yes"; - PermitEmptyPasswords = "yes"; - UsePAM = "no"; - }; - }; - - # access to convenient things - environment.systemPackages = with pkgs; [ w3m python3 ]; - nix.extraOptions = '' - extra-experimental-features = nix-command flakes - ''; - } - # mastodon setup { # open up access to the mastodon web interface @@ -46,7 +25,6 @@ # defaults.email = "test@example.com"; }; } - # VM setup { # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) diff --git a/peertube.nix b/peertube.nix new file mode 100644 index 0000000..195832c --- /dev/null +++ b/peertube.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 9000 ]; + + # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) + virtualisation.vmVariant = { config, ... }: { + services.peertube = { + enable = true; + # redirects to localhost, but allows it to have a proper domain name + localDomain = "peertube.localhost"; + enableWebHttps = false; + settings = { + listen.hostname = "0.0.0.0"; + instance.name = "PeerTube Test VM"; + }; + # TODO: use agenix + secrets.secretsFile = pkgs.runCommand "secret-gen" { + nativeBuildInputs = [ pkgs.openssl ]; + } '' + openssl rand -hex 32 > $out + ''; + redis.createLocally = true; + database.createLocally = true; + configureNginx = true; + }; + + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 9000; + guest.port = 9000; + } + { + from = "host"; + host.port = 2222; + guest.port = 22; + } + ]; + }; +} From 3e4ab1ecf6b39a9fb4c4bec844e5b7524cffbd7b Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 19 Mar 2024 20:39:59 -0400 Subject: [PATCH 07/87] simple pixelfed & redo readme --- README.md | 65 +++++++++++++++++++++++++++------------------------- common.nix | 7 ++++++ flake.nix | 10 ++++++++ mastodon.nix | 10 +++----- peertube.nix | 8 +++---- pixelfed.nix | 25 ++++++++++++++++++++ 6 files changed, 83 insertions(+), 42 deletions(-) create mode 100644 pixelfed.nix diff --git a/README.md b/README.md index 9a33301..7fecd12 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,45 @@ -# How to start up a mastodon VM +# Fediverse VMs + +This repo is, for now, an attempt to familiarize myself with NixOS options for Fediverse applications, and build up a configuration layer that will set most of the relevant options for you (in a semi-opinionated way) given some high-level configuration. This is in the same vein as [nixos-mailserver](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver). + +Eventually, this will be tailored to high-throughput multi-machine setups. For now, it's just a small configuration to run in VMs. + +## Running the VMs + +you can build a VM using ```bash -nixos-rebuild build-vm --flake .#mastodon +nixos-rebuild build-vm --flake .# +``` + +where `` is one of `mastodon`, `peertube`, `pixelfed`, or `all` + +and then run it with +```bash ./result/bin/run-nixos-vm ``` -Now you can access mastodon at +You can then access the apps on your local machine (using the magic of port forwarding) at the following addresses -- You will have to "accept the security risk". -- It may take a minute for the webpage to come online. Until then you will see 502 Bad Gateway +- Mastodon: + - You will have to "accept the security risk" + - It may take a minute for the webpage to come online. Until then you will see "502 Bad Gateway" + - (NOTE: currently broken) email sent from the mastodon instance (e.g. for setting up an account) will be accessible at -Remember that if you want to clear the state from one launch to the next, you should delete the `nixos.qcow2` file that is created. +- PeerTube: + - The root account can be accessed with username "root". The password can be obtained by running the following command on the VM: + ```bash + journalctl -u peertube | perl -ne '/password: (.*)/ && print $1' + ``` + - Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account. -- email, when it works, will be accessible at - -## peertube - -```bash -nixos-rebuild build-vm --flake .#peertube -./result/bin/run-nixos-vm -``` - -Now you can access peertube at - -The root account can be logged in with username "root". The password can be obtained with the command -```bash -journalctl -u peertube | perl -ne '/password: (.*)/ && print $1' -``` - -or just - -```bash -journalctl -u peertube | grep password -``` - -and look at the end of the line. - -Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account. +## debugging notes +- it is sometimes useful to `cat result/bin/run-nixos-vm` to see what's really going on (e.g. which ports are getting forwarded) +- relevant systemd services: + - mastodon-web.service + - peertube.service + - unclear yet which pixelfed services are useful # TODOs @@ -57,6 +59,7 @@ Creating other accounts has to be enabled via the admin interface. `Administrati - [x] don't require proxy server - either forward 443 directly, or get mastodon to accept connections on a different port (maybe 3000? see development environment documentation) - [ ] get letter_opener working +- [ ] share resources (e.g. s3 storage) between the services # resources diff --git a/common.nix b/common.nix index 1ec8c20..e3ac132 100644 --- a/common.nix +++ b/common.nix @@ -32,6 +32,13 @@ "-mon chardev=char0,mode=readline" "-device virtconsole,chardev=char0,nr=0" ]; + # forwardPorts = [ + # { + # from = "host"; + # host.port = 2222; + # guest.port = 22; + # } + # ]; }; }; } diff --git a/flake.nix b/flake.nix index a002450..d381e12 100644 --- a/flake.nix +++ b/flake.nix @@ -21,6 +21,16 @@ inherit system; modules = [ ./common.nix ./peertube.nix ]; }; + + pixelfed = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ ./common.nix ./pixelfed.nix ]; + }; + + all = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ ./common.nix ./mastodon.nix ./peertube.nix ./pixelfed.nix ]; + }; }; devShells.${system}.default = pkgs.mkShell { diff --git a/mastodon.nix b/mastodon.nix index 6a80420..77546d8 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -67,17 +67,13 @@ host.port = 44443; guest.port = 443; } - { - from = "host"; - host.port = 2222; - guest.port = 22; - } ]; }; } # mastodon development environment { + networking.firewall.allowedTCPPorts = [ 55001 ]; virtualisation.vmVariant = { config, ... }: { services.mastodon = { # needed so we can directly access mastodon at port 55001 @@ -85,7 +81,7 @@ enableUnixSocket = false; extraConfig = { RAILS_ENV = "development"; - # for letter_opener + # for letter_opener (still doesn't work though) REMOTE_DEV = "true"; }; }; @@ -123,7 +119,7 @@ # rails db:migrate fi ''; - virtualisation.forwardPorts = lib.mkForce [ + virtualisation.forwardPorts = [ { from = "host"; host.port = 55001; diff --git a/peertube.nix b/peertube.nix index 195832c..3eb6d45 100644 --- a/peertube.nix +++ b/peertube.nix @@ -13,11 +13,11 @@ instance.name = "PeerTube Test VM"; }; # TODO: use agenix - secrets.secretsFile = pkgs.runCommand "secret-gen" { - nativeBuildInputs = [ pkgs.openssl ]; - } '' - openssl rand -hex 32 > $out + secrets.secretsFile = pkgs.writeText "secret" '' + 574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24 ''; + + # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. redis.createLocally = true; database.createLocally = true; configureNginx = true; diff --git a/pixelfed.nix b/pixelfed.nix new file mode 100644 index 0000000..186fc8b --- /dev/null +++ b/pixelfed.nix @@ -0,0 +1,25 @@ +{ config, lib, pkgs, ... }: { + virtualisation.vmVariant = { + networking.firewall.allowedTCPPorts = [ 80 ]; + services.pixelfed = { + enable = true; + domain = "pixelfed.localhost"; + secretFile = pkgs.writeText "secrets.env" '' + APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA + ''; + settings = { + OPEN_REGISTRATION = true; + FORCE_HTTPS_URLS = false; + }; + # TODO: I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options here + nginx = {}; + }; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 8000; + guest.port = 80; + } + ]; + }; +} From 1b0fcff9fba8b53f8c174a2cd39808fcfe0a29d7 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 20 Mar 2024 05:23:57 -0400 Subject: [PATCH 08/87] fix mastodon (why was it broken??) --- README.md | 3 ++- common.nix | 14 +++++++------- mastodon.nix | 2 ++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7fecd12..b111967 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ and then run it with You can then access the apps on your local machine (using the magic of port forwarding) at the following addresses -- Mastodon: +- Mastodon: - You will have to "accept the security risk" - It may take a minute for the webpage to come online. Until then you will see "502 Bad Gateway" - (NOTE: currently broken) email sent from the mastodon instance (e.g. for setting up an account) will be accessible at @@ -40,6 +40,7 @@ You can then access the apps on your local machine (using the magic of port forw - mastodon-web.service - peertube.service - unclear yet which pixelfed services are useful +- you can ssh to the machine using `ssh -p 2222 root@localhost` # TODOs diff --git a/common.nix b/common.nix index e3ac132..d9674e8 100644 --- a/common.nix +++ b/common.nix @@ -32,13 +32,13 @@ "-mon chardev=char0,mode=readline" "-device virtconsole,chardev=char0,nr=0" ]; - # forwardPorts = [ - # { - # from = "host"; - # host.port = 2222; - # guest.port = 22; - # } - # ]; + forwardPorts = [ + { + from = "host"; + host.port = 2222; + guest.port = 22; + } + ]; }; }; } diff --git a/mastodon.nix b/mastodon.nix index 77546d8..7fb1fe4 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -81,6 +81,8 @@ enableUnixSocket = false; extraConfig = { RAILS_ENV = "development"; + # to be accessible from outside the VM + BIND = "0.0.0.0"; # for letter_opener (still doesn't work though) REMOTE_DEV = "true"; }; From 907a9c94945836955a0b5f17b30074003004df71 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 20 Mar 2024 05:24:24 -0400 Subject: [PATCH 09/87] get all the services working together --- peertube.nix | 5 ----- 1 file changed, 5 deletions(-) diff --git a/peertube.nix b/peertube.nix index 3eb6d45..7de11c8 100644 --- a/peertube.nix +++ b/peertube.nix @@ -29,11 +29,6 @@ host.port = 9000; guest.port = 9000; } - { - from = "host"; - host.port = 2222; - guest.port = 22; - } ]; }; } From 5fd1e115a009bc1b5844db07c547e56a69ef5935 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 27 Mar 2024 05:42:11 -0400 Subject: [PATCH 10/87] basic s3 garage setup for mastodon it's still having trouble fetching stored images for some reason --- README.md | 8 ++++++ flake.nix | 2 +- garage.nix | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mastodon.nix | 28 ++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 garage.nix diff --git a/README.md b/README.md index b111967..acf1ba9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ You can then access the apps on your local machine (using the magic of port forw - You will have to "accept the security risk" - It may take a minute for the webpage to come online. Until then you will see "502 Bad Gateway" - (NOTE: currently broken) email sent from the mastodon instance (e.g. for setting up an account) will be accessible at + - You can also create accounts on the machine itself by running `mastodon-tootctl accounts create --email --confirmed --approve` - PeerTube: - The root account can be accessed with username "root". The password can be obtained by running the following command on the VM: @@ -61,6 +62,13 @@ You can then access the apps on your local machine (using the magic of port forw - either forward 443 directly, or get mastodon to accept connections on a different port (maybe 3000? see development environment documentation) - [ ] get letter_opener working - [ ] share resources (e.g. s3 storage) between the services +- [ ] get garage running on another machine + - [ ] get garage replication running (multiple machines) + +# questions + +- what is meant to be shared between instances? + - this is relevant to the security model. If garage is being shared between instances, we have to be careful having configurations depend on each other. # resources diff --git a/flake.nix b/flake.nix index d381e12..88c269e 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./mastodon.nix ]; + modules = [ ./common.nix ./mastodon.nix ./garage.nix ]; }; peertube = nixpkgs.lib.nixosSystem { diff --git a/garage.nix b/garage.nix new file mode 100644 index 0000000..16e2adb --- /dev/null +++ b/garage.nix @@ -0,0 +1,74 @@ +let + # generate one using openssl (somehow) + # XXX: when importing, garage tells you importing is only meant for keys previously generated by garage. is it okay to generate them using openssl? + snakeoil_key = { + id = "GK3515373e4c851ebaad366558"; + secret = "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; + }; +in +# TODO: expand to a multi-machine setup +{ config, lib, pkgs, ... }: { + # add in options to ensure creation of buckets and keys + + config = { + virtualisation.vmVariant = { + virtualisation.diskSize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 3901; + guest.port = 3901; + } + { + from = "host"; + host.port = 3902; + guest.port = 3902; + } + ]; + }; + environment.systemPackages = [ pkgs.minio-client ]; + + networking.firewall.allowedTCPPorts = [ 3901 3902 ]; + services.garage = { + enable = true; + package = pkgs.garage_0_9; + settings = { + replication_mode = "none"; + # TODO: use a secret file + rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; + # TODO: why does this have to be set? is there not a sensible default? + rpc_bind_addr = "[::]:3901"; + rpc_public_addr = "[::1]:3901"; + s3_api.api_bind_addr = "[::]:3900"; + s3_web.bind_addr = "[::]:3902"; + s3_web.root_domain = ".web.garage.localhost"; + index = "index.html"; + + s3_api.s3_region = "garage"; + s3_api.root_domain = ".s3.garage.localhost"; + }; + }; + systemd.services.ensure-garage = { + after = [ "garage.service" ]; + wantedBy = [ "garage.service" ]; + path = [ config.services.garage.package pkgs.perl ]; + script = '' + set -xeuo pipefail + # give garage time to start up + sleep 3 + # XXX: this is very sensitive to being a single instance + # (bare minimum to get garage up and running) + # also, it's crazy that we have to parse command output like this + GARAGE_ID=$(garage node id 2>/dev/null | perl -ne '/(.*)@.*/ && print $1') + garage layout assign -z g1 -c 1G $GARAGE_ID + LAYOUT_VER=$(garage layout show | perl -ne '/Current cluster layout version: (\d*)/ && print $1') + garage layout apply --version $((LAYOUT_VER + 1)) + + garage bucket create mastodon + garage key import --yes -n mastodon "${snakeoil_key.id}" "${snakeoil_key.secret}" + garage bucket allow --read --write mastodon --key mastodon + garage bucket website --allow mastodon + ''; + }; + }; +} diff --git a/mastodon.nix b/mastodon.nix index 7fb1fe4..0db7738 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -1,4 +1,32 @@ +let + snakeoil_key = { + id = "GK3515373e4c851ebaad366558"; + secret = "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; + }; +in { config, lib, pkgs, ... }: lib.mkMerge [ + { # garage setup + services.mastodon = { + extraConfig = { + S3_ENABLED = "true"; + S3_ENDPOINT = "http://s3.garage.localhost:3900"; + S3_REGION = "garage"; + S3_BUCKET = "mastodon"; + # use . + S3_OVERRIDE_PATH_STLE = "true"; + AWS_ACCESS_KEY_ID = snakeoil_key.id; + AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; + S3_PROTOCOL = "http"; + S3_HOSTNAME = "web.garage.localhost:3902"; + # by default it tries to use "/" + # but we want "." + S3_ALIAS_HOST = "mastodon.web.garage.localhost:3902"; + # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ + # TODO: can we set up ACLs with garage? + S3_PERMISSION = ""; + }; + }; + } # mastodon setup { # open up access to the mastodon web interface From 48084fa6887dc8e160fad59142d8437db4e187ee Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 27 Mar 2024 05:59:50 -0400 Subject: [PATCH 11/87] options for ensuring garage buckets --- README.md | 2 ++ garage.nix | 73 +++++++++++++++++++++++++++++++++++++++++++++++++--- mastodon.nix | 17 ++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index acf1ba9..c7ec46c 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ You can then access the apps on your local machine (using the magic of port forw - [ ] share resources (e.g. s3 storage) between the services - [ ] get garage running on another machine - [ ] get garage replication running (multiple machines) +- [ ] some way of declaratively defining users? +- [ ] shared users between fediverse services # questions diff --git a/garage.nix b/garage.nix index 16e2adb..ca24ca1 100644 --- a/garage.nix +++ b/garage.nix @@ -9,6 +9,55 @@ in # TODO: expand to a multi-machine setup { config, lib, pkgs, ... }: { # add in options to ensure creation of buckets and keys + options = + let + inherit (lib) types mkOption; + in { + services.garage = { + ensureBuckets = mkOption { + type = types.attrsOf (types.submodule { + options = { + website = mkOption { + type = types.bool; + default = false; + }; + }; + }); + }; + ensureKeys = mkOption { + type = types.attrsOf (types.submodule { + options = { + id = mkOption { + type = types.string; + }; + secret = mkOption { + type = types.string; + }; + # TODO: assert at least one of these is true + ensureAccess = mkOption { + type = types.attrsOf (types.submodule { + options = { + read = mkOption { + type = types.bool; + default = false; + }; + write = mkOption { + type = types.bool; + default = false; + }; + owner = mkOption { + type = types.bool; + default = false; + }; + }; + }); + default = []; + }; + }; + }); + }; + }; + }; config = { virtualisation.vmVariant = { @@ -56,6 +105,7 @@ in set -xeuo pipefail # give garage time to start up sleep 3 + # XXX: this is very sensitive to being a single instance # (bare minimum to get garage up and running) # also, it's crazy that we have to parse command output like this @@ -64,10 +114,25 @@ in LAYOUT_VER=$(garage layout show | perl -ne '/Current cluster layout version: (\d*)/ && print $1') garage layout apply --version $((LAYOUT_VER + 1)) - garage bucket create mastodon - garage key import --yes -n mastodon "${snakeoil_key.id}" "${snakeoil_key.secret}" - garage bucket allow --read --write mastodon --key mastodon - garage bucket website --allow mastodon + ${ + lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website }: '' + garage bucket create ${bucket} + # XXX: should this --deny the website if `website` is false? + ${lib.optionalString website '' + garage bucket website --allow ${bucket} + ''} + '') config.services.garage.ensureBuckets) + } + ${ + lib.concatStringsSep "\n" (lib.mapAttrsToList (key: {id, secret, ensureAccess}: '' + garage key import --yes -n ${key} ${id} ${secret} + ${ + lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { read, write, owner }: '' + garage bucket allow ${lib.optionalString read "--read"} ${lib.optionalString write "--write"} ${lib.optionalString owner "--owner"} ${bucket} --key ${key} + '') ensureAccess) + } + '') config.services.garage.ensureKeys) + } ''; }; }; diff --git a/mastodon.nix b/mastodon.nix index 0db7738..ac43ea9 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -6,6 +6,23 @@ let in { config, lib, pkgs, ... }: lib.mkMerge [ { # garage setup + services.garage = { + ensureBuckets = { + mastodon = { website = true; }; + }; + ensureKeys = { + mastodon = { + inherit (snakeoil_key) id secret; + ensureAccess = { + mastodon = { + read = true; + write = true; + owner = true; + }; + }; + }; + }; + }; services.mastodon = { extraConfig = { S3_ENABLED = "true"; From af6e76134af44cc736b61fde1cb0d308c6840754 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 3 Apr 2024 08:40:19 -0400 Subject: [PATCH 12/87] peertube data in s3 storage --- README.md | 6 ++++ flake.nix | 2 +- garage.nix | 55 ++++++++++++++++++++++++++++++++---- peertube.nix | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c7ec46c..bea1a92 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,21 @@ You can then access the apps on your local machine (using the magic of port forw - [ ] get garage replication running (multiple machines) - [ ] some way of declaratively defining users? - [ ] shared users between fediverse services +- [ ] s3 cache server (SEE: https://docs.joinpeertube.org/maintain/remote-storage) +- [ ] is "s3" the right term, given that it's not an open protocol? # questions - what is meant to be shared between instances? - this is relevant to the security model. If garage is being shared between instances, we have to be careful having configurations depend on each other. +- we want to be able to migrate user's data. s3 migration is not supported by peertube. what do? (SEE: https://docs.joinpeertube.org/maintain/remote-storage) + # resources - Tutorial for setting up better logging: https://krisztianfekete.org/self-hosting-mastodon-on-nixos-a-proof-of-concept/ - Setting up development environment: https://docs.joinmastodon.org/dev/setup/ - Tutorial for PeerTube that doesn't use `createLocally`: https://nixos.wiki/wiki/PeerTube + +- garage settings for specific apps: https://garagehq.deuxfleurs.fr/documentation/connect/apps/ diff --git a/flake.nix b/flake.nix index 88c269e..be5a1f2 100644 --- a/flake.nix +++ b/flake.nix @@ -19,7 +19,7 @@ peertube = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./peertube.nix ]; + modules = [ ./common.nix ./peertube.nix ./garage.nix ]; }; pixelfed = nixpkgs.lib.nixosSystem { diff --git a/garage.nix b/garage.nix index ca24ca1..b3f2652 100644 --- a/garage.nix +++ b/garage.nix @@ -2,8 +2,8 @@ let # generate one using openssl (somehow) # XXX: when importing, garage tells you importing is only meant for keys previously generated by garage. is it okay to generate them using openssl? snakeoil_key = { - id = "GK3515373e4c851ebaad366558"; - secret = "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; + id = "GK22a15201acacbd51cd43e327"; + secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e"; }; in # TODO: expand to a multi-machine setup @@ -11,7 +11,7 @@ in # add in options to ensure creation of buckets and keys options = let - inherit (lib) types mkOption; + inherit (lib) types mkOption mkEnableOption; in { services.garage = { ensureBuckets = mkOption { @@ -21,11 +21,31 @@ in type = types.bool; default = false; }; + corsRules = { + enable = mkEnableOption "CORS Rules"; + allowedHeaders = mkOption { + type = types.listOf types.str; + default = []; + }; + allowedMethods = mkOption { + type = types.listOf types.str; + default = []; + }; + allowedOrigins = mkOption { + type = types.listOf types.str; + default = []; + }; + }; + aliases = mkOption { + type = types.listOf types.str; + default = []; + }; }; }); }; ensureKeys = mkOption { type = types.attrsOf (types.submodule { + # TODO: these should be managed as secrets, not in the nix store options = { id = mkOption { type = types.string; @@ -75,7 +95,7 @@ in } ]; }; - environment.systemPackages = [ pkgs.minio-client ]; + environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ 3901 3902 ]; services.garage = { @@ -100,7 +120,7 @@ in systemd.services.ensure-garage = { after = [ "garage.service" ]; wantedBy = [ "garage.service" ]; - path = [ config.services.garage.package pkgs.perl ]; + path = [ config.services.garage.package pkgs.perl pkgs.awscli ]; script = '' set -xeuo pipefail # give garage time to start up @@ -114,13 +134,36 @@ in LAYOUT_VER=$(garage layout show | perl -ne '/Current cluster layout version: (\d*)/ && print $1') garage layout apply --version $((LAYOUT_VER + 1)) + # XXX: this is a hack because we want to write to the buckets here but we're not guaranteed any access keys + garage key import --yes -n tmp ${snakeoil_key.id} ${snakeoil_key.secret} + export AWS_ACCESS_KEY_ID=${snakeoil_key.id}; + export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}; + ${ - lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website }: '' + lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website, aliases, corsRules }: '' garage bucket create ${bucket} # XXX: should this --deny the website if `website` is false? ${lib.optionalString website '' garage bucket website --allow ${bucket} ''} + ${lib.concatStringsSep "\n" (map (alias: '' + garage bucket alias ${bucket} ${alias} + '') aliases)} + + ${lib.optionalString corsRules.enable '' + export CORS=${lib.concatStrings [ + "'" + ''{"CORSRules":[{'' + ''"AllowedHeaders":${builtins.toJSON corsRules.allowedHeaders},'' + ''"AllowedMethods":${builtins.toJSON corsRules.allowedMethods},'' + ''"AllowedOrigins":${builtins.toJSON corsRules.allowedOrigins}'' + ''}]}'' + "'" + ]} + garage bucket allow --read --write --owner ${bucket} --key tmp + aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucket} --cors-configuration $CORS + garage bucket deny --read --write --owner ${bucket} --key tmp + ''} '') config.services.garage.ensureBuckets) } ${ diff --git a/peertube.nix b/peertube.nix index 7de11c8..777a726 100644 --- a/peertube.nix +++ b/peertube.nix @@ -1,6 +1,86 @@ +let + snakeoil_key = { + id = "GK1f9feea9960f6f95ff404c9b"; + secret = "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395"; + }; +in { config, lib, pkgs, ... }: { networking.firewall.allowedTCPPorts = [ 80 9000 ]; + services.garage = { + ensureBuckets = { + peertube-videos = { + website = true; + corsRules = { + enable = true; + allowedHeaders = [ "*" ]; + allowedMethods = [ "GET" ]; + allowedOrigins = [ "*" ]; + }; + }; + peertube-playlists = { + website = true; + corsRules = { + enable = true; + allowedHeaders = [ "*" ]; + allowedMethods = [ "GET" ]; + allowedOrigins = [ "*" ]; + }; + }; + }; + ensureKeys = { + peertube = { + inherit (snakeoil_key) id secret; + ensureAccess = { + peertube-videos = { + read = true; + write = true; + owner = true; + }; + peertube-playlists = { + read = true; + write = true; + owner = true; + }; + }; + }; + }; + }; + + services.peertube = { + settings = { + object_storage = { + enabled = true; + endpoint = "http://s3.garage.localhost:3900"; + region = "garage"; + + # not supported by garage + # SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube + proxy.proxyify_private_files = false; + + web_videos = { + bucket_name = "peertube-videos"; + prefix = ""; + base_url = "http://peertube-videos.web.garage.localhost:3902"; + }; + videos = { + bucket_name = "peertube-videos"; + prefix = ""; + base_url = "http://peertube-videos.web.garage.localhost:3902"; + }; + streaming_playlists = { + bucket_name = "peertube-playlists"; + prefix = ""; + base_url = "http://peertube-playlists.web.garage.localhost:3902"; + }; + }; + }; + serviceEnvironmentFile = "/etc/peertube-env"; + }; + environment.etc.peertube-env.text = '' + AWS_ACCESS_KEY_ID=${snakeoil_key.id} + AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret} + ''; # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) virtualisation.vmVariant = { config, ... }: { services.peertube = { From 2c7e3603b84d17ac7e73b33937f1b6a695b2ce06 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Fri, 24 May 2024 19:02:12 -0400 Subject: [PATCH 13/87] better documentation and readme --- README.md | 69 ++++++++++++++++++++++++---------------------------- common.nix | 21 +++++++++------- flake.nix | 4 +-- garage.nix | 24 +++++++++++++----- mastodon.nix | 12 ++++++++- peertube.nix | 4 ++- pixelfed.nix | 52 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 129 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index bea1a92..d8f6210 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fediverse VMs -This repo is, for now, an attempt to familiarize myself with NixOS options for Fediverse applications, and build up a configuration layer that will set most of the relevant options for you (in a semi-opinionated way) given some high-level configuration. This is in the same vein as [nixos-mailserver](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver). +This repo is, for now, an attempt to familiarize myself with NixOS options for Fediverse applications, and build up a configuration layer that will set most of the relevant options for you (in a semi-opinionated way) given some high-level configuration. The goal is something in the same vein as [nixos-mailserver](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver) but for fediversity. Eventually, this will be tailored to high-throughput multi-machine setups. For now, it's just a small configuration to run in VMs. @@ -19,13 +19,16 @@ and then run it with ./result/bin/run-nixos-vm ``` -You can then access the apps on your local machine (using the magic of port forwarding) at the following addresses +After the machine boots, you should be dropped into a root shell. + +Note that state will be persisted in the `nixos.cqow2` file. Delete that and restart the VM to reset the state. + +With the VM running, you can then access the apps on your local machine's web browser (using the magic of port forwarding) at the following addresses + +NOTE: it sometimes takes a while for the services to start up, and in the meantime you will get 502 Bad Gateway. - Mastodon: - - You will have to "accept the security risk" - - It may take a minute for the webpage to come online. Until then you will see "502 Bad Gateway" - - (NOTE: currently broken) email sent from the mastodon instance (e.g. for setting up an account) will be accessible at - - You can also create accounts on the machine itself by running `mastodon-tootctl accounts create --email --confirmed --approve` + - You can also create accounts on the machine itself by running `mastodon-tootctl accounts create test --email test@test.com --confirmed --approve` - PeerTube: - The root account can be accessed with username "root". The password can be obtained by running the following command on the VM: @@ -34,53 +37,45 @@ You can then access the apps on your local machine (using the magic of port forw ``` - Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account. +- Pixelfed: + - Account creation via the web interface won't work until we figure out email + - For now, they can be created on the VM command line + ```bash + pixelfed-manage user:create --name=test --username=test --email=test@test.com --password=testtest --confirm_email=1 + ``` + ## debugging notes - it is sometimes useful to `cat result/bin/run-nixos-vm` to see what's really going on (e.g. which ports are getting forwarded) - relevant systemd services: - mastodon-web.service - peertube.service - - unclear yet which pixelfed services are useful -- you can ssh to the machine using `ssh -p 2222 root@localhost` - -# TODOs - -- [ ] set up a domain name and a DNS service so we can do deploy this to an actual machine -- [ ] set up an email service -- [ ] add logging - - [ ] errors / logs - - [ ] performance -- [ ] switch to garage / s3 storage - - SEE: https://docs.joinmastodon.org/admin/optional/object-storage/ -- [ ] decouple the postgres database from this machine -- [ ] test with high use / throughput -- [ ] configure scaling behaviour - - SEE: https://docs.joinmastodon.org/admin/scaling/ -- [ ] remove the need for "accept security risk" dialogue if possible -- [ ] development environment does not work seamlessly. -- [x] don't require proxy server - - either forward 443 directly, or get mastodon to accept connections on a different port (maybe 3000? see development environment documentation) -- [ ] get letter_opener working -- [ ] share resources (e.g. s3 storage) between the services -- [ ] get garage running on another machine - - [ ] get garage replication running (multiple machines) -- [ ] some way of declaratively defining users? -- [ ] shared users between fediverse services -- [ ] s3 cache server (SEE: https://docs.joinpeertube.org/maintain/remote-storage) -- [ ] is "s3" the right term, given that it's not an open protocol? +- the `garage` CLI command gives information about garage storage, but cannot be used to actually inspect the contents. use `mc` (minio) for that # questions - what is meant to be shared between instances? - this is relevant to the security model. If garage is being shared between instances, we have to be careful having configurations depend on each other. - -- we want to be able to migrate user's data. s3 migration is not supported by peertube. what do? (SEE: https://docs.joinpeertube.org/maintain/remote-storage) + - they are to be shared, BUT the user will have no direct control over configuration. # resources - Tutorial for setting up better logging: https://krisztianfekete.org/self-hosting-mastodon-on-nixos-a-proof-of-concept/ -- Setting up development environment: https://docs.joinmastodon.org/dev/setup/ +- Setting up mastodon development environment: https://docs.joinmastodon.org/dev/setup/ - Tutorial for PeerTube that doesn't use `createLocally`: https://nixos.wiki/wiki/PeerTube - garage settings for specific apps: https://garagehq.deuxfleurs.fr/documentation/connect/apps/ + +- pixelfed has terrible / mostly non-existent documentation) + +- for when we start worry about scaling up: https://docs.joinmastodon.org/admin/scaling/ + +# notes + +When mastodon is running in production mode, we have a few problems: +- you have to click "accept the security risk" +- it takes a while for the webpage to come online. Until then you see "502 Bad Gateway" +- email sent from the mastodon instance (e.g. for account confirmation) should be accessible at , but it's not working. + + diff --git a/common.nix b/common.nix index d9674e8..150afaa 100644 --- a/common.nix +++ b/common.nix @@ -14,9 +14,19 @@ # automatically log in services.getty.autologinUser = "root"; - + services.getty.helpLine = '' + Type `C-a c` to access the qemu console + Type `C-a x` to quit + ''; # access to convenient things - environment.systemPackages = with pkgs; [ w3m python3 ]; + environment.systemPackages = with pkgs; [ + w3m + python3 + xterm # for `resize` + ]; + environment.loginShellInit = '' + eval "$(resize)" + ''; nix.extraOptions = '' extra-experimental-features = nix-command flakes ''; @@ -32,13 +42,6 @@ "-mon chardev=char0,mode=readline" "-device virtconsole,chardev=char0,nr=0" ]; - forwardPorts = [ - { - from = "host"; - host.port = 2222; - guest.port = 22; - } - ]; }; }; } diff --git a/flake.nix b/flake.nix index be5a1f2..9a6e011 100644 --- a/flake.nix +++ b/flake.nix @@ -24,12 +24,12 @@ pixelfed = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./pixelfed.nix ]; + modules = [ ./common.nix ./pixelfed.nix ./garage.nix ]; }; all = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./mastodon.nix ./peertube.nix ./pixelfed.nix ]; + modules = [ ./common.nix ./mastodon.nix ./peertube.nix ./pixelfed.nix ./garage.nix ]; }; }; diff --git a/garage.nix b/garage.nix index b3f2652..e7ad923 100644 --- a/garage.nix +++ b/garage.nix @@ -1,6 +1,6 @@ let # generate one using openssl (somehow) - # XXX: when importing, garage tells you importing is only meant for keys previously generated by garage. is it okay to generate them using openssl? + # XXX: when importing, garage tells you importing is only meant for keys previously generated by garage. is it okay to generate them using openssl? it seems to work fine snakeoil_key = { id = "GK22a15201acacbd51cd43e327"; secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e"; @@ -21,6 +21,7 @@ in type = types.bool; default = false; }; + # I think setting corsRules should allow another website to show images from your bucket corsRules = { enable = mkEnableOption "CORS Rules"; allowedHeaders = mkOption { @@ -48,10 +49,10 @@ in # TODO: these should be managed as secrets, not in the nix store options = { id = mkOption { - type = types.string; + type = types.str; }; secret = mkOption { - type = types.string; + type = types.str; }; # TODO: assert at least one of these is true ensureAccess = mkOption { @@ -95,6 +96,7 @@ in } ]; }; + environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ 3901 3902 ]; @@ -127,30 +129,37 @@ in sleep 3 # XXX: this is very sensitive to being a single instance - # (bare minimum to get garage up and running) + # (doing the bare minimum to get garage up and running) # also, it's crazy that we have to parse command output like this + # TODO: talk to garage maintainer about making this nicer to work with in Nix + # before I do that though, I should figure out how setting it up across multiple machines will work GARAGE_ID=$(garage node id 2>/dev/null | perl -ne '/(.*)@.*/ && print $1') garage layout assign -z g1 -c 1G $GARAGE_ID LAYOUT_VER=$(garage layout show | perl -ne '/Current cluster layout version: (\d*)/ && print $1') garage layout apply --version $((LAYOUT_VER + 1)) # XXX: this is a hack because we want to write to the buckets here but we're not guaranteed any access keys + # TODO: generate this key here rather than using a well-known key garage key import --yes -n tmp ${snakeoil_key.id} ${snakeoil_key.secret} export AWS_ACCESS_KEY_ID=${snakeoil_key.id}; export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}; ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website, aliases, corsRules }: '' - garage bucket create ${bucket} - # XXX: should this --deny the website if `website` is false? + # garage bucket info tells us if the bucket already exists + garage bucket info ${bucket} || garage bucket create ${bucket} + + # TODO: should this --deny the website if `website` is false? ${lib.optionalString website '' garage bucket website --allow ${bucket} ''} + ${lib.concatStringsSep "\n" (map (alias: '' garage bucket alias ${bucket} ${alias} '') aliases)} ${lib.optionalString corsRules.enable '' + # TODO: can i turn this whole thing into one builtins.toJSON? export CORS=${lib.concatStrings [ "'" ''{"CORSRules":[{'' @@ -160,6 +169,7 @@ in ''}]}'' "'" ]} + garage bucket allow --read --write --owner ${bucket} --key tmp aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucket} --cors-configuration $CORS garage bucket deny --read --write --owner ${bucket} --key tmp @@ -176,6 +186,8 @@ in } '') config.services.garage.ensureKeys) } + + garage key delete ${snakeoil_key.id} --yes ''; }; }; diff --git a/mastodon.nix b/mastodon.nix index ac43ea9..43e724f 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -8,7 +8,15 @@ in { # garage setup services.garage = { ensureBuckets = { - mastodon = { website = true; }; + mastodon = { + website = true; + # corsRules = { + # enable = true; + # allowedHeaders = [ "*" ]; + # allowedMethods = [ "GET" ]; + # allowedOrigins = [ "*" ]; + # }; + }; }; ensureKeys = { mastodon = { @@ -38,6 +46,8 @@ in # by default it tries to use "/" # but we want "." S3_ALIAS_HOST = "mastodon.web.garage.localhost:3902"; + # XXX: I think we need to set up a proper CDN host + CDN_HOST = "mastodon.web.garage.localhost:3902"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # TODO: can we set up ACLs with garage? S3_PERMISSION = ""; diff --git a/peertube.nix b/peertube.nix index 777a726..4e641b0 100644 --- a/peertube.nix +++ b/peertube.nix @@ -11,6 +11,7 @@ in ensureBuckets = { peertube-videos = { website = true; + # TODO: these are too broad, after getting everything works narrow it down to the domain we actually want corsRules = { enable = true; allowedHeaders = [ "*" ]; @@ -18,6 +19,7 @@ in allowedOrigins = [ "*" ]; }; }; + # TODO: these are too broad, after getting everything works narrow it down to the domain we actually want peertube-playlists = { website = true; corsRules = { @@ -81,7 +83,7 @@ in AWS_ACCESS_KEY_ID=${snakeoil_key.id} AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret} ''; - # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) + virtualisation.vmVariant = { config, ... }: { services.peertube = { enable = true; diff --git a/pixelfed.nix b/pixelfed.nix index 186fc8b..9a64071 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -1,9 +1,57 @@ +let + snakeoil_key = { + id = "GKb5615457d44214411e673b7b"; + secret = "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987"; + }; +in { config, lib, pkgs, ... }: { + + services.garage = { + ensureBuckets = { + pixelfed = { + website = true; + # TODO: these are too broad, after getting everything works narrow it down to the domain we actually want + corsRules = { + enable = true; + allowedHeaders = [ "*" ]; + allowedMethods = [ "GET" ]; + allowedOrigins = [ "*" ]; + }; + }; + }; + ensureKeys = { + pixelfed = { + inherit (snakeoil_key) id secret; + ensureAccess = { + pixelfed = { + read = true; + write = true; + owner = true; + }; + }; + }; + }; + }; + + # TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place + services.pixelfed.settings = { + FILESYSTEM_CLOUD = "s3"; + PF_ENABLE_CLOUD = true; + AWS_ACCESS_KEY_ID = snakeoil_key.id; + AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; + AWS_DEFAULT_REGION = "garage"; + AWS_URL = "http://pixelfed.s3.garage.localhost:3900"; + AWS_BUCKET = "pixelfed"; + AWS_ENDPOINT = "http://s3.garage.localhost:3900"; + AWS_USE_PATH_STYLE_ENDPOINT = false; + }; + virtualisation.vmVariant = { networking.firewall.allowedTCPPorts = [ 80 ]; services.pixelfed = { enable = true; domain = "pixelfed.localhost"; + # TODO: secrets management! secretFile = pkgs.writeText "secrets.env" '' APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA ''; @@ -11,9 +59,11 @@ OPEN_REGISTRATION = true; FORCE_HTTPS_URLS = false; }; - # TODO: I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options here + # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options here + # TODO: If that indeed makes sense, upstream it. nginx = {}; }; + virtualisation.memorySize = 2048; virtualisation.forwardPorts = [ { from = "host"; From 4e719da9d9d01e7abd09e9ef26d489204ff46d6c Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 5 Jun 2024 21:37:06 -0400 Subject: [PATCH 14/87] address roberth comments SEE https://git.fediversity.eu/taeer/simple-nixos-fediverse/compare/main...roberth:review --- README.md | 2 +- common.nix | 1 + garage.nix | 102 ++++++++++++++++++++++++++------------------------- mastodon.nix | 49 +++++++++++++++---------- 4 files changed, 84 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index d8f6210..6a8f983 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repo is, for now, an attempt to familiarize myself with NixOS options for Fediverse applications, and build up a configuration layer that will set most of the relevant options for you (in a semi-opinionated way) given some high-level configuration. The goal is something in the same vein as [nixos-mailserver](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver) but for fediversity. -Eventually, this will be tailored to high-throughput multi-machine setups. For now, it's just a small configuration to run in VMs. +Eventually, this will be tailored to high-throughput multi-machine setups. For now, it's just a small set of configurations to run in VMs. ## Running the VMs diff --git a/common.nix b/common.nix index 150afaa..2f98d29 100644 --- a/common.nix +++ b/common.nix @@ -1,4 +1,5 @@ { pkgs, ... }: { + # customize nixos-rebuild build-vm to be a bit more convenient virtualisation.vmVariant = { # let us log in users.mutableUsers = false; diff --git a/garage.nix b/garage.nix index e7ad923..c6baba7 100644 --- a/garage.nix +++ b/garage.nix @@ -7,12 +7,54 @@ let }; in # TODO: expand to a multi-machine setup -{ config, lib, pkgs, ... }: { +{ config, lib, pkgs, ... }: +let + inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep; + inherit (lib.strings) escapeShellArg; + cfg = config.services.garage; + concatMapAttrs = scriptFn: attrset: concatStringsSep "\n" (lib.mapAttrsToList scriptFn attrset); + ensureBucketScriptFn = bucket: { website, aliases, corsRules }: + let + bucketArg = escapeShellArg bucket; + corsRulesJSON = escapeShellArg (builtins.toJSON { + CORSRules = [{ + AllowedHeaders = corsRules.allowedHeaders; + AllowedMethods = corsRules.allowedMethods; + AllowedOrigins = corsRules.allowedOrigins; + }]; + }); + in '' + # garage bucket info tells us if the bucket already exists + garage bucket info ${bucketArg} || garage bucket create ${bucketArg} + + # TODO: should this --deny the website if `website` is false? + ${optionalString website '' + garage bucket website --allow ${bucketArg} + ''} + + ${concatStringsSep "\n" (map (alias: '' + garage bucket alias ${bucketArg} ${escapeShellArg alias} + '') aliases)} + + ${optionalString corsRules.enable '' + garage bucket allow --read --write --owner ${bucketArg} --key tmp + aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + garage bucket deny --read --write --owner ${bucketArg} --key tmp + ''} + ''; + ensureBucketsScript = concatMapAttrs ensureBucketScriptFn cfg.ensureBuckets; + ensureAccessScriptFn = key: bucket: { read, write, owner }: '' + garage bucket allow ${optionalString read "--read"} ${optionalString write "--write"} ${optionalString owner "--owner"} \ + ${escapeShellArg bucket} --key ${escapeShellArg key} + ''; + ensureKeyScriptFn = key: {id, secret, ensureAccess}: '' + garage key import --yes -n ${escapeShellArg key} ${escapeShellArg id} ${escapeShellArg secret} + ${concatMapAttrs (ensureAccessScriptFn key) ensureAccess} + ''; + ensureKeysScript = concatMapAttrs ensureKeyScriptFn cfg.ensureKeys; +in { # add in options to ensure creation of buckets and keys - options = - let - inherit (lib) types mkOption mkEnableOption; - in { + options = { services.garage = { ensureBuckets = mkOption { type = types.attrsOf (types.submodule { @@ -55,6 +97,7 @@ in type = types.str; }; # TODO: assert at least one of these is true + # NOTE: this currently needs to be done at the top level module ensureAccess = mkOption { type = types.attrsOf (types.submodule { options = { @@ -122,7 +165,7 @@ in systemd.services.ensure-garage = { after = [ "garage.service" ]; wantedBy = [ "garage.service" ]; - path = [ config.services.garage.package pkgs.perl pkgs.awscli ]; + path = [ cfg.package pkgs.perl pkgs.awscli ]; script = '' set -xeuo pipefail # give garage time to start up @@ -144,50 +187,11 @@ in export AWS_ACCESS_KEY_ID=${snakeoil_key.id}; export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}; - ${ - lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website, aliases, corsRules }: '' - # garage bucket info tells us if the bucket already exists - garage bucket info ${bucket} || garage bucket create ${bucket} + ${ensureBucketsScript} + ${ensureKeysScript} - # TODO: should this --deny the website if `website` is false? - ${lib.optionalString website '' - garage bucket website --allow ${bucket} - ''} - - ${lib.concatStringsSep "\n" (map (alias: '' - garage bucket alias ${bucket} ${alias} - '') aliases)} - - ${lib.optionalString corsRules.enable '' - # TODO: can i turn this whole thing into one builtins.toJSON? - export CORS=${lib.concatStrings [ - "'" - ''{"CORSRules":[{'' - ''"AllowedHeaders":${builtins.toJSON corsRules.allowedHeaders},'' - ''"AllowedMethods":${builtins.toJSON corsRules.allowedMethods},'' - ''"AllowedOrigins":${builtins.toJSON corsRules.allowedOrigins}'' - ''}]}'' - "'" - ]} - - garage bucket allow --read --write --owner ${bucket} --key tmp - aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucket} --cors-configuration $CORS - garage bucket deny --read --write --owner ${bucket} --key tmp - ''} - '') config.services.garage.ensureBuckets) - } - ${ - lib.concatStringsSep "\n" (lib.mapAttrsToList (key: {id, secret, ensureAccess}: '' - garage key import --yes -n ${key} ${id} ${secret} - ${ - lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { read, write, owner }: '' - garage bucket allow ${lib.optionalString read "--read"} ${lib.optionalString write "--write"} ${lib.optionalString owner "--owner"} ${bucket} --key ${key} - '') ensureAccess) - } - '') config.services.garage.ensureKeys) - } - - garage key delete ${snakeoil_key.id} --yes + # garage doesn't like deleting keys that once existed + # garage key delete ${snakeoil_key.id} --yes ''; }; }; diff --git a/mastodon.nix b/mastodon.nix index 43e724f..3ae312b 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -99,11 +99,7 @@ in # from the documentation: recommended is the amount of your CPU cores minus one. # but it also must be a positive integer - streamingProcesses = let - ncores = config.virtualisation.cores; - max = x: y: if x > y then x else y; - in - max 1 (ncores - 1); + streamingProcesses = lib.max 1 (config.virtualisation.cores - 1); }; security.acme = { @@ -159,22 +155,35 @@ in # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; }; - # run rails db:seed so that mastodon sets up the databases for us + # Currently, nixos seems to be able to create a single database per + # postgres user. This works for the production version of mastodon, which + # is what's packaged in nixpkgs. For development, we need two databases, + # mastodon_development and mastodon_test. This used to be possible with + # ensurePermissions, but that's broken and has been removed. Here I copy + # the mastodon-init-db script from upstream nixpkgs, but add the single + # line `rails db:setup`, which asks mastodon to create the postgres + # databases for us. + # FIXME: the commented out lines were breaking things, but presumably they're necessary for something. + # TODO: see if we can fix the upstream ensurePermissions stuff. See above for what that config would look like. systemd.services.mastodon-init-db.script = lib.mkForce '' - if [ `psql -c \ - "select count(*) from pg_class c \ - join pg_namespace s on s.oid = c.relnamespace \ - where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ - and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then - echo "Seeding database" - rails db:setup - # SAFETY_ASSURED=1 rails db:schema:load - rails db:seed - else - echo "Migrating database (this might be a noop)" - # TODO: this breaks for some reason - # rails db:migrate - fi + result="$(psql -t --csv -c \ + "select count(*) from pg_class c \ + join pg_namespace s on s.oid = c.relnamespace \ + where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ + and s.nspname not like 'pg_temp%';")" || error_code=$? + if [ "''${error_code:-0}" -ne 0 ]; then + echo "Failure checking if database is seeded. psql gave exit code $error_code" + exit "$error_code" + fi + if [ "$result" -eq 0 ]; then + echo "Seeding database" + rails db:setup + # SAFETY_ASSURED=1 rails db:schema:load + rails db:seed + else + # echo "Migrating database (this might be a noop)" + # rails db:migrate + fi ''; virtualisation.forwardPorts = [ { From 693e21b1a8f395b3adfb9bf98ba014be33e75db0 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 25 Jun 2024 06:39:04 -0400 Subject: [PATCH 15/87] first stab at a nixos test for now, had to get rid of vmVariant. we can figure out how to add it back when we understand how we should actually distinguish between real machines and VMs --- .gitignore | 1 + README.md | 10 +- common.nix | 19 ++++ flake.nix | 11 ++ garage.nix | 30 +++--- mastodon.nix | 206 +++++++++++++++++++------------------- tests/fediversity.png | Bin 0 -> 5640 bytes tests/mastodon-garage.nix | 58 +++++++++++ tests/rebuildableTest.nix | 149 +++++++++++++++++++++++++++ 9 files changed, 364 insertions(+), 120 deletions(-) create mode 100644 tests/fediversity.png create mode 100644 tests/mastodon-garage.nix create mode 100644 tests/rebuildableTest.nix diff --git a/.gitignore b/.gitignore index b83e248..e83c5f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ nixos.qcow2 result* .direnv +.nixos-test-history diff --git a/README.md b/README.md index 6a8f983..1a3e805 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,11 @@ With the VM running, you can then access the apps on your local machine's web br NOTE: it sometimes takes a while for the services to start up, and in the meantime you will get 502 Bad Gateway. -- Mastodon: - - You can also create accounts on the machine itself by running `mastodon-tootctl accounts create test --email test@test.com --confirmed --approve` +- Mastodon: through the reverse proxy at and directly at + - You can create accounts on the machine itself by running `mastodon-tootctl accounts create test --email test@test.com --confirmed --approve` + - Account-related activities (logging in/out; preferences) can only be done on the insecure direct page + - After you've logged in, you can go back to the secure page and you will remain logged in + - some operations may remove the port number from the URL. You'll have to add that back in manually - PeerTube: - The root account can be accessed with username "root". The password can be obtained by running the following command on the VM: @@ -51,6 +54,7 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti - mastodon-web.service - peertube.service - the `garage` CLI command gives information about garage storage, but cannot be used to actually inspect the contents. use `mc` (minio) for that +- in the chromium devtools, you can go to the networking tab and change things like response headers in a way that persists through reloads. this is much faster iteration time if that's what you need to epxeriment with. # questions @@ -77,5 +81,7 @@ When mastodon is running in production mode, we have a few problems: - you have to click "accept the security risk" - it takes a while for the webpage to come online. Until then you see "502 Bad Gateway" - email sent from the mastodon instance (e.g. for account confirmation) should be accessible at , but it's not working. +- mastodon is trying to fetch `missing.png` without ssl (`http://`). This isn't allowed, and i'm not sure why it's doing it. +- mastodon is trying to fetch `custom.css` from https://mastodon.localhost (no port), which is not the configured `LOCAL_DOMAIN`, so it's unclear why. diff --git a/common.nix b/common.nix index 2f98d29..431e487 100644 --- a/common.nix +++ b/common.nix @@ -44,5 +44,24 @@ "-device virtconsole,chardev=char0,nr=0" ]; }; + + + # we can't forward port 80 or 443, so let's run nginx on a different port + networking.firewall.allowedTCPPorts = [ 8443 8080 ]; + services.nginx.defaultSSLListenPort = 8443; + services.nginx.defaultHTTPListenPort = 8080; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 8080; + guest.port = 8080; + } + { + from = "host"; + host.port = 8443; + guest.port = 8443; + } + ]; + }; } diff --git a/flake.nix b/flake.nix index 9a6e011..bdc1b4f 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,13 @@ pkgs = nixpkgs.legacyPackages.${system}; in { + nixosModules = { + mastodon = import ./mastodon.nix; + peertube = import ./peertube.nix; + pixelfed = import ./pixelfed.nix; + garage = import ./garage.nix; + }; + nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; @@ -33,6 +40,10 @@ }; }; + checks.${system} = { + mastodon-garage = import ./tests/mastodon-garage.nix { inherit pkgs self; }; + }; + devShells.${system}.default = pkgs.mkShell { inputs = with pkgs; [ nil diff --git a/garage.nix b/garage.nix index c6baba7..2c47ec9 100644 --- a/garage.nix +++ b/garage.nix @@ -124,21 +124,19 @@ in { }; config = { - virtualisation.vmVariant = { - virtualisation.diskSize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 3901; - guest.port = 3901; - } - { - from = "host"; - host.port = 3902; - guest.port = 3902; - } - ]; - }; + virtualisation.diskSize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 3901; + guest.port = 3901; + } + { + from = "host"; + host.port = 3902; + guest.port = 3902; + } + ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; @@ -190,7 +188,7 @@ in { ${ensureBucketsScript} ${ensureKeysScript} - # garage doesn't like deleting keys that once existed + # garage doesn't like re-adding keys that once existed, so we can't delete / recreate it every time # garage key delete ${snakeoil_key.id} --yes ''; }; diff --git a/mastodon.nix b/mastodon.nix index 3ae312b..5527949 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -10,12 +10,12 @@ in ensureBuckets = { mastodon = { website = true; - # corsRules = { - # enable = true; - # allowedHeaders = [ "*" ]; - # allowedMethods = [ "GET" ]; - # allowedOrigins = [ "*" ]; - # }; + corsRules = { + enable = true; + allowedHeaders = [ "*" ]; + allowedMethods = [ "GET" ]; + allowedOrigins = [ "*" ]; + }; }; }; ensureKeys = { @@ -47,7 +47,7 @@ in # but we want "." S3_ALIAS_HOST = "mastodon.web.garage.localhost:3902"; # XXX: I think we need to set up a proper CDN host - CDN_HOST = "mastodon.web.garage.localhost:3902"; + # CDN_HOST = "mastodon.web.garage.localhost:3902"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # TODO: can we set up ACLs with garage? S3_PERMISSION = ""; @@ -82,116 +82,118 @@ in } # VM setup { - # these configurations only apply when producing a VM (e.g. nixos-rebuild build-vm) - virtualisation.vmVariant = { config, ... }: { - services.mastodon = { - # redirects to localhost, but allows it to have a proper domain name - localDomain = "mastodon.localhost"; + services.mastodon = { + # redirects to localhost, but allows it to have a proper domain name + localDomain = "mastodon.localhost"; - smtp = { - fromAddress = "mastodon@mastodon.localhost"; - createLocally = false; - }; - - extraConfig = { - EMAIL_DOMAIN_ALLOWLIST = "example.com"; - }; - - # from the documentation: recommended is the amount of your CPU cores minus one. - # but it also must be a positive integer - streamingProcesses = lib.max 1 (config.virtualisation.cores - 1); + smtp = { + fromAddress = "mastodon@mastodon.localhost"; + createLocally = false; }; - security.acme = { - defaults = { - # invalid server; the systemd service will fail, and we won't get properly signed certificates - # but let's not spam the letsencrypt servers (and we don't own this domain anyways) - server = "https://127.0.0.1"; - email = "none"; - }; + extraConfig = { + EMAIL_DOMAIN_ALLOWLIST = "example.com"; }; - virtualisation.memorySize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 44443; - guest.port = 443; - } - ]; + # from the documentation: recommended is the amount of your CPU cores minus one. + # but it also must be a positive integer + streamingProcesses = lib.max 1 (config.virtualisation.cores - 1); }; + + security.acme = { + defaults = { + # invalid server; the systemd service will fail, and we won't get properly signed certificates + # but let's not spam the letsencrypt servers (and we don't own this domain anyways) + server = "https://127.0.0.1"; + email = "none"; + }; + }; + + virtualisation.memorySize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 44443; + guest.port = 443; + } + ]; } # mastodon development environment { networking.firewall.allowedTCPPorts = [ 55001 ]; - virtualisation.vmVariant = { config, ... }: { - services.mastodon = { - # needed so we can directly access mastodon at port 55001 - # otherwise, mastodon has to be accessed *from* port 443, which we can't do via port forwarding - enableUnixSocket = false; - extraConfig = { - RAILS_ENV = "development"; - # to be accessible from outside the VM - BIND = "0.0.0.0"; - # for letter_opener (still doesn't work though) - REMOTE_DEV = "true"; - }; + services.mastodon = { + # needed so we can directly access mastodon at port 55001 + # otherwise, mastodon has to be accessed *from* port 443, which we can't do via port forwarding + enableUnixSocket = false; + extraConfig = { + RAILS_ENV = "development"; + # to be accessible from outside the VM + BIND = "0.0.0.0"; + # for letter_opener (still doesn't work though) + REMOTE_DEV = "true"; + LOCAL_DOMAIN = "mastodon.localhost:8443"; }; + }; + # services.nginx.virtualHosts."${config.services.mastodon.localDomain}" = { + # extraConfig = '' + # add_header Content-Security-Policy 'base-uri 'none'; default-src 'none'; frame-ancestors 'none'; font-src 'self' http://mastodon.localhost:8443; img-src * https: data: blob: http://mastodon.localhost:8443; style-src 'self' http://mastodon.localhost:8443 'nonce-QvwdQ3lNRMmEcQnhZ22MAg=='; media-src 'self' https: data: http://mastodon.localhost:8443; frame-src 'self' https:; manifest-src 'self' http://mastodon.localhost:8443; form-action 'self'; child-src 'self' blob: http://mastodon.localhost:8443; worker-src 'self' blob: http://mastodon.localhost:8443; connect-src 'self' data: blob: http://mastodon.localhost:8443 http://mastodon.web.garage.localhost:3902 ws://mastodon.localhost:4000 ws://localhost:3035 http://localhost:3035; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://mastodon.localhost:8443' + # ''; + # }; + # services.nginx.virtualHosts."${config.services.mastodon.localDomain}".locations."/sw.js" = - services.postgresql = { - enable = true; - ensureUsers = [ - { - name = config.services.mastodon.database.user; - ensureClauses.createdb = true; - # ensurePermissions doesn't work anymore - # ensurePermissions = { - # "mastodon_development.*" = "ALL PRIVILEGES"; - # "mastodon_test.*" = "ALL PRIVILEGES"; - # } - } - ]; - # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; - }; - - # Currently, nixos seems to be able to create a single database per - # postgres user. This works for the production version of mastodon, which - # is what's packaged in nixpkgs. For development, we need two databases, - # mastodon_development and mastodon_test. This used to be possible with - # ensurePermissions, but that's broken and has been removed. Here I copy - # the mastodon-init-db script from upstream nixpkgs, but add the single - # line `rails db:setup`, which asks mastodon to create the postgres - # databases for us. - # FIXME: the commented out lines were breaking things, but presumably they're necessary for something. - # TODO: see if we can fix the upstream ensurePermissions stuff. See above for what that config would look like. - systemd.services.mastodon-init-db.script = lib.mkForce '' - result="$(psql -t --csv -c \ - "select count(*) from pg_class c \ - join pg_namespace s on s.oid = c.relnamespace \ - where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ - and s.nspname not like 'pg_temp%';")" || error_code=$? - if [ "''${error_code:-0}" -ne 0 ]; then - echo "Failure checking if database is seeded. psql gave exit code $error_code" - exit "$error_code" - fi - if [ "$result" -eq 0 ]; then - echo "Seeding database" - rails db:setup - # SAFETY_ASSURED=1 rails db:schema:load - rails db:seed - else - # echo "Migrating database (this might be a noop)" - # rails db:migrate - fi - ''; - virtualisation.forwardPorts = [ + services.postgresql = { + enable = true; + ensureUsers = [ { - from = "host"; - host.port = 55001; - guest.port = 55001; + name = config.services.mastodon.database.user; + ensureClauses.createdb = true; + # ensurePermissions doesn't work anymore + # ensurePermissions = { + # "mastodon_development.*" = "ALL PRIVILEGES"; + # "mastodon_test.*" = "ALL PRIVILEGES"; + # } } ]; + # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; }; + + # Currently, nixos seems to be able to create a single database per + # postgres user. This works for the production version of mastodon, which + # is what's packaged in nixpkgs. For development, we need two databases, + # mastodon_development and mastodon_test. This used to be possible with + # ensurePermissions, but that's broken and has been removed. Here I copy + # the mastodon-init-db script from upstream nixpkgs, but add the single + # line `rails db:setup`, which asks mastodon to create the postgres + # databases for us. + # FIXME: the commented out lines were breaking things, but presumably they're necessary for something. + # TODO: see if we can fix the upstream ensurePermissions stuff. See above for what that config would look like. + systemd.services.mastodon-init-db.script = lib.mkForce '' + result="$(psql -t --csv -c \ + "select count(*) from pg_class c \ + join pg_namespace s on s.oid = c.relnamespace \ + where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ + and s.nspname not like 'pg_temp%';")" || error_code=$? + if [ "''${error_code:-0}" -ne 0 ]; then + echo "Failure checking if database is seeded. psql gave exit code $error_code" + exit "$error_code" + fi + if [ "$result" -eq 0 ]; then + echo "Seeding database" + rails db:setup + # SAFETY_ASSURED=1 rails db:schema:load + rails db:seed + # else + # echo "Migrating database (this might be a noop)" + # rails db:migrate + fi + ''; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 55001; + guest.port = 55001; + } + ]; } -] +] diff --git a/tests/fediversity.png b/tests/fediversity.png new file mode 100644 index 0000000000000000000000000000000000000000..24881fb4a0321f00a18d2c85d5859f9a04f6a108 GIT binary patch literal 5640 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 Date: Thu, 18 Jul 2024 06:44:13 -0400 Subject: [PATCH 16/87] interactive test is working --- garage.nix | 33 ++++++----- tests/fediversity.png | Bin 5640 -> 0 bytes tests/green.png | Bin 0 -> 692 bytes tests/mastodon-garage.nix | 112 +++++++++++++++++++++++++++++++------- 4 files changed, 110 insertions(+), 35 deletions(-) delete mode 100644 tests/fediversity.png create mode 100644 tests/green.png diff --git a/garage.nix b/garage.nix index 2c47ec9..3de3709 100644 --- a/garage.nix +++ b/garage.nix @@ -38,7 +38,8 @@ let ${optionalString corsRules.enable '' garage bucket allow --read --write --owner ${bucketArg} --key tmp - aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + # TODO: endpoin-url should not be hard-coded + aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} garage bucket deny --read --write --owner ${bucketArg} --key tmp ''} ''; @@ -124,21 +125,23 @@ in { }; config = { - virtualisation.diskSize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 3901; - guest.port = 3901; - } - { - from = "host"; - host.port = 3902; - guest.port = 3902; - } - ]; + virtualisation.vmVariant = { config, ... }: { + virtualisation.diskSize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 3901; + guest.port = 3901; + } + { + from = "host"; + host.port = 3902; + guest.port = 3902; + } + ]; - environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; + environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; + }; networking.firewall.allowedTCPPorts = [ 3901 3902 ]; services.garage = { diff --git a/tests/fediversity.png b/tests/fediversity.png deleted file mode 100644 index 24881fb4a0321f00a18d2c85d5859f9a04f6a108..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5640 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/garage-image.webp") + garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") + image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") + assert garage_image_hash == image_hash + + with subtest("Content security policy allows garage images"): + headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") + csp_match = re.match('^Content-Security-Policy: (.*)$', headers, re.M) + assert csp_match is not None + csp = csp_match.group(1) + # the content security policy should include the garage server + garage_csp = re.match(".*web\.garage\.localhost:3902.*", csp) + assert garage_csp is not None + + with subtest("image displays"): + server.succeed("selenium-script") + server.copy_from_vm("/mastodon-screenshot.png", "") + displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") + # check that the green image displayed somewhere + re.match(".*#00FF00.*", displayed_colors, re.S) ''; } From 0f8972a8f0ab8079900d44bebdc2e0e3f2956d7c Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 18 Jul 2024 08:22:47 -0400 Subject: [PATCH 17/87] for now, we have to stop using vmVariant so the test works --- README.md | 2 ++ garage.nix | 30 ++++++++++++++---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1a3e805..d85e960 100644 --- a/README.md +++ b/README.md @@ -84,4 +84,6 @@ When mastodon is running in production mode, we have a few problems: - mastodon is trying to fetch `missing.png` without ssl (`http://`). This isn't allowed, and i'm not sure why it's doing it. - mastodon is trying to fetch `custom.css` from https://mastodon.localhost (no port), which is not the configured `LOCAL_DOMAIN`, so it's unclear why. +NixOS tests do not take the configuration from `virtualisation.vmVariant`. This seems like an oversight since people don't tend to mix normal NixOS configurations with the ones they're using for tests. This should be pretty easy to rectify upstream. + diff --git a/garage.nix b/garage.nix index 3de3709..ff2ec36 100644 --- a/garage.nix +++ b/garage.nix @@ -125,23 +125,21 @@ in { }; config = { - virtualisation.vmVariant = { config, ... }: { - virtualisation.diskSize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 3901; - guest.port = 3901; - } - { - from = "host"; - host.port = 3902; - guest.port = 3902; - } - ]; + virtualisation.diskSize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 3901; + guest.port = 3901; + } + { + from = "host"; + host.port = 3902; + guest.port = 3902; + } + ]; - environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; - }; + environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ 3901 3902 ]; services.garage = { From acc4a1a2ef43453083dc4b97a62e0565ff6b3336 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 23 Jul 2024 09:43:18 -0400 Subject: [PATCH 18/87] better error messages --- tests/mastodon-garage.nix | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index c423f86..ed59b75 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -78,7 +78,8 @@ rebuildableTest { with subtest("Account creation"): account_creation_output = server.succeed("mastodon-tootctl accounts create test --email test@test.com --confirmed --approve") password_match = re.match('.*New password: ([^\n]*).*', account_creation_output, re.S) - assert password_match is not None + if password_match is None: + raise Exception(f"account creation did not generate a password.\n{account_creation_output}") password = password_match.group(1) with subtest("TTY Login"): @@ -105,26 +106,32 @@ rebuildableTest { with subtest("access image in garage"): image = server.succeed("mc find garage --regex original") image = image.rstrip() - assert image != "" + if image == "": + raise Exception("image posted to mastodon did not get stored in garage") server.succeed(f"mc cat {image} >/garage-image.webp") garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") - assert garage_image_hash == image_hash + if garage_image_hash != image_hash: + raise Exception("image stored in garage did not match image uploaded") with subtest("Content security policy allows garage images"): headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") csp_match = re.match('^Content-Security-Policy: (.*)$', headers, re.M) - assert csp_match is not None + if csp_match is None: + raise Exception("mastodon did not send a content security policy header") csp = csp_match.group(1) # the content security policy should include the garage server garage_csp = re.match(".*web\.garage\.localhost:3902.*", csp) - assert garage_csp is not None + if garage_csp is None: + raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") with subtest("image displays"): server.succeed("selenium-script") server.copy_from_vm("/mastodon-screenshot.png", "") displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere - re.match(".*#00FF00.*", displayed_colors, re.S) + green_check = re.match(".*#00FF00.*", displayed_colors, re.S) + if green_check is None: + raise Exception("cannot detect the uploaded image on mastodon page.") ''; } From bddfd95ee4b9fb96588c5abb60f80011f12d7367 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 25 Jul 2024 06:06:02 -0400 Subject: [PATCH 19/87] cleanup --- .gitignore | 1 + mastodon.nix | 11 ++--------- tests/mastodon-garage.nix | 3 --- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index e83c5f0..2cb15d6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ nixos.qcow2 result* .direnv .nixos-test-history +mastodon-screenshot.png diff --git a/mastodon.nix b/mastodon.nix index 5527949..facda0e 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -46,8 +46,6 @@ in # by default it tries to use "/" # but we want "." S3_ALIAS_HOST = "mastodon.web.garage.localhost:3902"; - # XXX: I think we need to set up a proper CDN host - # CDN_HOST = "mastodon.web.garage.localhost:3902"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # TODO: can we set up ACLs with garage? S3_PERMISSION = ""; @@ -80,6 +78,7 @@ in # defaults.email = "test@example.com"; }; } + # VM setup { services.mastodon = { @@ -119,7 +118,7 @@ in ]; } - # mastodon development environment + # run mastodon as development environment { networking.firewall.allowedTCPPorts = [ 55001 ]; services.mastodon = { @@ -135,12 +134,6 @@ in LOCAL_DOMAIN = "mastodon.localhost:8443"; }; }; - # services.nginx.virtualHosts."${config.services.mastodon.localDomain}" = { - # extraConfig = '' - # add_header Content-Security-Policy 'base-uri 'none'; default-src 'none'; frame-ancestors 'none'; font-src 'self' http://mastodon.localhost:8443; img-src * https: data: blob: http://mastodon.localhost:8443; style-src 'self' http://mastodon.localhost:8443 'nonce-QvwdQ3lNRMmEcQnhZ22MAg=='; media-src 'self' https: data: http://mastodon.localhost:8443; frame-src 'self' https:; manifest-src 'self' http://mastodon.localhost:8443; form-action 'self'; child-src 'self' blob: http://mastodon.localhost:8443; worker-src 'self' blob: http://mastodon.localhost:8443; connect-src 'self' data: blob: http://mastodon.localhost:8443 http://mastodon.web.garage.localhost:3902 ws://mastodon.localhost:4000 ws://localhost:3035 http://localhost:3035; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://mastodon.localhost:8443' - # ''; - # }; - # services.nginx.virtualHosts."${config.services.mastodon.localDomain}".locations."/sw.js" = services.postgresql = { enable = true; diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index ed59b75..f40d1ce 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -36,9 +36,6 @@ in rebuildableTest { name = "test-mastodon-garage"; - # skipLint = true; - # skipTypeCheck = true; - nodes = { server = {config, ...}: { virtualisation.memorySize = lib.mkVMOverride 4096; From 941d3bf2a9126f11d675a294e4d21763690ee123 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 25 Jul 2024 07:45:57 -0400 Subject: [PATCH 20/87] fix CSP check --- mastodon.nix | 5 ++--- tests/mastodon-garage.nix | 12 +++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/mastodon.nix b/mastodon.nix index facda0e..95fde42 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -32,7 +32,7 @@ in }; }; services.mastodon = { - extraConfig = { + extraConfig = rec { S3_ENABLED = "true"; S3_ENDPOINT = "http://s3.garage.localhost:3900"; S3_REGION = "garage"; @@ -44,8 +44,7 @@ in S3_PROTOCOL = "http"; S3_HOSTNAME = "web.garage.localhost:3902"; # by default it tries to use "/" - # but we want "." - S3_ALIAS_HOST = "mastodon.web.garage.localhost:3902"; + S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # TODO: can we set up ACLs with garage? S3_PERMISSION = ""; diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index f40d1ce..ce04e91 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -113,15 +113,21 @@ rebuildableTest { with subtest("Content security policy allows garage images"): headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") - csp_match = re.match('^Content-Security-Policy: (.*)$', headers, re.M) + csp_match = None + # I can't figure out re.MULTILINE + for header in headers.split("\n"): + csp_match = re.match('^Content-Security-Policy: (.*)$', header) + if csp_match is not None: + break if csp_match is None: raise Exception("mastodon did not send a content security policy header") csp = csp_match.group(1) - # the content security policy should include the garage server - garage_csp = re.match(".*web\.garage\.localhost:3902.*", csp) + # the img-src content security policy should include the garage server + garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) if garage_csp is None: raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") + # this could in theory give a false positive if mastodon changes it's colorscheme to include pure green. with subtest("image displays"): server.succeed("selenium-script") server.copy_from_vm("/mastodon-screenshot.png", "") From 366a67e1121f81f72d81553c7678bfdc3bca93e9 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 25 Jul 2024 07:49:22 -0400 Subject: [PATCH 21/87] update readme --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index d85e960..c8de4c4 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,28 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti - mastodon-web.service - peertube.service - the `garage` CLI command gives information about garage storage, but cannot be used to actually inspect the contents. use `mc` (minio) for that + - run `mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY` - in the chromium devtools, you can go to the networking tab and change things like response headers in a way that persists through reloads. this is much faster iteration time if that's what you need to epxeriment with. +## NixOS Tests + +Tests live in the aptly named `tests/` directory, and can be accessed at the flake URI `.#checks..` e.g. `nix build .#checks.x86_64-linux.mastodon-garage`. +They can also be run interactively with +``` +nix build .#checks...driverInteractive +./result/bin/nixos-test-driver 2>output +```` +you can `less -F output` from a different terminal to follow along. + +These tests are also equiped with the same port forwarding as the VMs, so when running interactively you should be able to access services through a browser running on your machine. + +While running interactively, `rebuildableTests` allows you to modify the test nodes and then redeploy without restarting the test and waiting for the VMs to start up again. To do this you must start the jumphost by running `redeploy_jumphost.start()` inside the driver. Then from the command line + +``` +nix build .#checks...driverInteractive +./result/bin/rebuild +``` + # questions - what is meant to be shared between instances? From 353c0a7ffabb1eb2d796fe176ed4cbfcf79b4dd9 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 28 Aug 2024 08:35:48 -0400 Subject: [PATCH 22/87] separate vm.nix files for vm-specific configuration --- common.nix | 67 ----------- flake.lock | 16 +-- flake.nix | 21 +++- interactive-vm.nix | 64 +++++++++++ mastodon-vm.nix | 118 +++++++++++++++++++ mastodon.nix | 230 ++++++++++---------------------------- peertube-vm.nix | 30 +++++ peertube.nix | 30 ----- pixelfed-vm.nix | 27 +++++ pixelfed.nix | 28 ----- tests/mastodon-garage.nix | 4 +- tests/rebuildableTest.nix | 2 +- 12 files changed, 324 insertions(+), 313 deletions(-) delete mode 100644 common.nix create mode 100644 interactive-vm.nix create mode 100644 mastodon-vm.nix create mode 100644 peertube-vm.nix create mode 100644 pixelfed-vm.nix diff --git a/common.nix b/common.nix deleted file mode 100644 index 431e487..0000000 --- a/common.nix +++ /dev/null @@ -1,67 +0,0 @@ -{ pkgs, ... }: { - # customize nixos-rebuild build-vm to be a bit more convenient - virtualisation.vmVariant = { - # let us log in - users.mutableUsers = false; - users.users.root.hashedPassword = ""; - services.openssh = { - enable = true; - settings = { - PermitRootLogin = "yes"; - PermitEmptyPasswords = "yes"; - UsePAM = "no"; - }; - }; - - # automatically log in - services.getty.autologinUser = "root"; - services.getty.helpLine = '' - Type `C-a c` to access the qemu console - Type `C-a x` to quit - ''; - # access to convenient things - environment.systemPackages = with pkgs; [ - w3m - python3 - xterm # for `resize` - ]; - environment.loginShellInit = '' - eval "$(resize)" - ''; - nix.extraOptions = '' - extra-experimental-features = nix-command flakes - ''; - - # no graphics. see nixos-shell - virtualisation = { - graphics = false; - qemu.consoles = [ "tty0" "hvc0" ]; - qemu.options = [ - "-serial null" - "-device virtio-serial" - "-chardev stdio,mux=on,id=char0,signal=off" - "-mon chardev=char0,mode=readline" - "-device virtconsole,chardev=char0,nr=0" - ]; - }; - - - # we can't forward port 80 or 443, so let's run nginx on a different port - networking.firewall.allowedTCPPorts = [ 8443 8080 ]; - services.nginx.defaultSSLListenPort = 8443; - services.nginx.defaultHTTPListenPort = 8080; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 8080; - guest.port = 8080; - } - { - from = "host"; - host.port = 8443; - guest.port = 8443; - } - ]; - - }; -} diff --git a/flake.lock b/flake.lock index 419cf1d..9e0adef 100644 --- a/flake.lock +++ b/flake.lock @@ -2,18 +2,14 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1708475490, - "narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "0e74ca98a74bc7270d28838369593635a5db3260", - "type": "github" + "lastModified": 1724846166, + "narHash": "sha256-Um1Ahz09XHepSA1QQmdQk8nbsJEwHe54gP3naWp6D94=", + "path": "/home/qolen/nixpkgs", + "type": "path" }, "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" + "path": "/home/qolen/nixpkgs", + "type": "path" } }, "root": { diff --git a/flake.nix b/flake.nix index bdc1b4f..ff723fa 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,8 @@ description = "Testing mastodon configurations"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + # nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + nixpkgs.url = "path:/home/qolen/nixpkgs"; }; outputs = { self, nixpkgs }: @@ -12,31 +13,41 @@ in { nixosModules = { + interactive-vm = import ./interactive-vm.nix; mastodon = import ./mastodon.nix; + mastodon-vm = import ./mastodon-vm.nix; peertube = import ./peertube.nix; + peertube-vm = import ./peertube-vm.nix; pixelfed = import ./pixelfed.nix; + pixelfed-vm = import ./pixelfed-vm.nix; garage = import ./garage.nix; }; nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./mastodon.nix ./garage.nix ]; + modules = with self.nixosModules; [ interactive-vm mastodon mastodon-vm garage ]; }; peertube = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./peertube.nix ./garage.nix ]; + modules = with self.nixosModules; [ interactive-vm peertube peertube-vm garage ]; }; pixelfed = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./pixelfed.nix ./garage.nix ]; + modules = with self.nixosModules; [ interactive-vm pixelfed pixelfed-vm garage ]; }; all = nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./common.nix ./mastodon.nix ./peertube.nix ./pixelfed.nix ./garage.nix ]; + modules = with self.nixosModules; [ + interactive-vm + peertube peertube-vm + pixelfed pixelfed-vm + mastodon mastodon-vm + garage + ]; }; }; diff --git a/interactive-vm.nix b/interactive-vm.nix new file mode 100644 index 0000000..dd94309 --- /dev/null +++ b/interactive-vm.nix @@ -0,0 +1,64 @@ +# customize nixos-rebuild build-vm to be a bit more convenient +{ pkgs, ... }: { + # let us log in + users.mutableUsers = false; + users.users.root.hashedPassword = ""; + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "yes"; + PermitEmptyPasswords = "yes"; + UsePAM = false; + }; + }; + + # automatically log in + services.getty.autologinUser = "root"; + services.getty.helpLine = '' + Type `C-a c` to access the qemu console + Type `C-a x` to quit + ''; + # access to convenient things + environment.systemPackages = with pkgs; [ + w3m + python3 + xterm # for `resize` + ]; + environment.loginShellInit = '' + eval "$(resize)" + ''; + nix.extraOptions = '' + extra-experimental-features = nix-command flakes + ''; + + # no graphics. see nixos-shell + virtualisation = { + graphics = false; + qemu.consoles = [ "tty0" "hvc0" ]; + qemu.options = [ + "-serial null" + "-device virtio-serial" + "-chardev stdio,mux=on,id=char0,signal=off" + "-mon chardev=char0,mode=readline" + "-device virtconsole,chardev=char0,nr=0" + ]; + }; + + + # we can't forward port 80 or 443, so let's run nginx on a different port + networking.firewall.allowedTCPPorts = [ 8443 8080 ]; + services.nginx.defaultSSLListenPort = 8443; + services.nginx.defaultHTTPListenPort = 8080; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 8080; + guest.port = 8080; + } + { + from = "host"; + host.port = 8443; + guest.port = 8443; + } + ]; +} diff --git a/mastodon-vm.nix b/mastodon-vm.nix new file mode 100644 index 0000000..fcfe3e5 --- /dev/null +++ b/mastodon-vm.nix @@ -0,0 +1,118 @@ +{ modulesPath, lib, config, ... }: { + + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + + config = lib.mkMerge [ + { + services.mastodon = { + # redirects to localhost, but allows it to have a proper domain name + localDomain = "mastodon.localhost"; + + smtp = { + fromAddress = "mastodon@mastodon.localhost"; + createLocally = false; + }; + + extraConfig = { + EMAIL_DOMAIN_ALLOWLIST = "example.com"; + }; + + # from the documentation: recommended is the amount of your CPU cores + # minus one. but it also must be a positive integer + streamingProcesses = lib.max 1 (config.virtualisation.cores - 1); + }; + + security.acme = { + defaults = { + # invalid server; the systemd service will fail, and we won't get + # properly signed certificates. but let's not spam the letsencrypt + # servers (and we don't own this domain anyways) + server = "https://127.0.0.1"; + email = "none"; + }; + }; + + virtualisation.memorySize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 44443; + guest.port = 443; + } + ]; + } + + #### run mastodon as development environment + { + + networking.firewall.allowedTCPPorts = [ 55001 ]; + services.mastodon = { + # needed so we can directly access mastodon at port 55001 + # otherwise, mastodon has to be accessed *from* port 443, which we can't do via port forwarding + enableUnixSocket = false; + extraConfig = { + RAILS_ENV = "development"; + # to be accessible from outside the VM + BIND = "0.0.0.0"; + # for letter_opener (still doesn't work though) + REMOTE_DEV = "true"; + LOCAL_DOMAIN = "mastodon.localhost:8443"; + }; + }; + + services.postgresql = { + enable = true; + ensureUsers = [ + { + name = config.services.mastodon.database.user; + ensureClauses.createdb = true; + # ensurePermissions doesn't work anymore + # ensurePermissions = { + # "mastodon_development.*" = "ALL PRIVILEGES"; + # "mastodon_test.*" = "ALL PRIVILEGES"; + # } + } + ]; + # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; + }; + + # Currently, nixos seems to be able to create a single database per + # postgres user. This works for the production version of mastodon, which + # is what's packaged in nixpkgs. For development, we need two databases, + # mastodon_development and mastodon_test. This used to be possible with + # ensurePermissions, but that's broken and has been removed. Here I copy + # the mastodon-init-db script from upstream nixpkgs, but add the single + # line `rails db:setup`, which asks mastodon to create the postgres + # databases for us. + # FIXME: the commented out lines were breaking things, but presumably they're necessary for something. + # TODO: see if we can fix the upstream ensurePermissions stuff. See commented out lines in services.postgresql above for what that config would look like. + systemd.services.mastodon-init-db.script = lib.mkForce '' + result="$(psql -t --csv -c \ + "select count(*) from pg_class c \ + join pg_namespace s on s.oid = c.relnamespace \ + where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ + and s.nspname not like 'pg_temp%';")" || error_code=$? + if [ "''${error_code:-0}" -ne 0 ]; then + echo "Failure checking if database is seeded. psql gave exit code $error_code" + exit "$error_code" + fi + if [ "$result" -eq 0 ]; then + echo "Seeding database" + rails db:setup + # SAFETY_ASSURED=1 rails db:schema:load + rails db:seed + # else + # echo "Migrating database (this might be a noop)" + # rails db:migrate + fi + ''; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 55001; + guest.port = 55001; + } + ]; + } + ]; +} diff --git a/mastodon.nix b/mastodon.nix index 95fde42..e0c2ea4 100644 --- a/mastodon.nix +++ b/mastodon.nix @@ -4,188 +4,78 @@ let secret = "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; }; in -{ config, lib, pkgs, ... }: lib.mkMerge [ - { # garage setup - services.garage = { - ensureBuckets = { - mastodon = { - website = true; - corsRules = { - enable = true; - allowedHeaders = [ "*" ]; - allowedMethods = [ "GET" ]; - allowedOrigins = [ "*" ]; - }; +{ config, lib, pkgs, ... }: { + #### garage setup + services.garage = { + ensureBuckets = { + mastodon = { + website = true; + corsRules = { + enable = true; + allowedHeaders = [ "*" ]; + allowedMethods = [ "GET" ]; + allowedOrigins = [ "*" ]; }; }; - ensureKeys = { - mastodon = { - inherit (snakeoil_key) id secret; - ensureAccess = { - mastodon = { - read = true; - write = true; - owner = true; - }; + }; + ensureKeys = { + mastodon = { + inherit (snakeoil_key) id secret; + ensureAccess = { + mastodon = { + read = true; + write = true; + owner = true; }; }; }; }; - services.mastodon = { - extraConfig = rec { - S3_ENABLED = "true"; - S3_ENDPOINT = "http://s3.garage.localhost:3900"; - S3_REGION = "garage"; - S3_BUCKET = "mastodon"; - # use . - S3_OVERRIDE_PATH_STLE = "true"; - AWS_ACCESS_KEY_ID = snakeoil_key.id; - AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; - S3_PROTOCOL = "http"; - S3_HOSTNAME = "web.garage.localhost:3902"; - # by default it tries to use "/" - S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; - # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ - # TODO: can we set up ACLs with garage? - S3_PERMISSION = ""; - }; + }; + services.mastodon = { + extraConfig = rec { + S3_ENABLED = "true"; + # TODO: this shouldn't be hard-coded, it should come from the garage configuration + S3_ENDPOINT = "http://s3.garage.localhost:3900"; + S3_REGION = "garage"; + S3_BUCKET = "mastodon"; + # use . + S3_OVERRIDE_PATH_STLE = "true"; + AWS_ACCESS_KEY_ID = snakeoil_key.id; + AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; + S3_PROTOCOL = "http"; + S3_HOSTNAME = "web.garage.localhost:3902"; + # by default it tries to use "/" + S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; + # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ + # TODO: can we set up ACLs with garage? + S3_PERMISSION = ""; }; - } - # mastodon setup - { - # open up access to the mastodon web interface - networking.firewall.allowedTCPPorts = [ 443 ]; + }; - services.mastodon = { - enable = true; + #### mastodon setup - # TODO: set up a domain name, and a DNS service so that this can run not in a vm - # localDomain = "domain.social"; - configureNginx = true; + # open up access to the mastodon web interface + networking.firewall.allowedTCPPorts = [ 443 ]; - # TODO: configure a mailserver so this works - # smtp.fromAddress = "mastodon@mastodon.localhost"; + services.mastodon = { + enable = true; - # TODO: this is hardware-dependent. let's figure it out when we have hardware - # streamingProcesses = 1; - }; + # TODO: set up a domain name, and a DNS service so that this can run not in a vm + # localDomain = "domain.social"; + configureNginx = true; - security.acme = { - acceptTerms = true; - preliminarySelfsigned = true; - # TODO: configure a mailserver so we can set up acme - # defaults.email = "test@example.com"; - }; - } + # TODO: configure a mailserver so this works + # smtp.fromAddress = "mastodon@domain.social"; - # VM setup - { - services.mastodon = { - # redirects to localhost, but allows it to have a proper domain name - localDomain = "mastodon.localhost"; + # TODO: this is hardware-dependent. let's figure it out when we have hardware + # streamingProcesses = 1; + }; - smtp = { - fromAddress = "mastodon@mastodon.localhost"; - createLocally = false; - }; + security.acme = { + acceptTerms = true; + preliminarySelfsigned = true; + # TODO: configure a mailserver so we can set up acme + # defaults.email = "test@example.com"; + }; +} - extraConfig = { - EMAIL_DOMAIN_ALLOWLIST = "example.com"; - }; - - # from the documentation: recommended is the amount of your CPU cores minus one. - # but it also must be a positive integer - streamingProcesses = lib.max 1 (config.virtualisation.cores - 1); - }; - - security.acme = { - defaults = { - # invalid server; the systemd service will fail, and we won't get properly signed certificates - # but let's not spam the letsencrypt servers (and we don't own this domain anyways) - server = "https://127.0.0.1"; - email = "none"; - }; - }; - - virtualisation.memorySize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 44443; - guest.port = 443; - } - ]; - } - - # run mastodon as development environment - { - networking.firewall.allowedTCPPorts = [ 55001 ]; - services.mastodon = { - # needed so we can directly access mastodon at port 55001 - # otherwise, mastodon has to be accessed *from* port 443, which we can't do via port forwarding - enableUnixSocket = false; - extraConfig = { - RAILS_ENV = "development"; - # to be accessible from outside the VM - BIND = "0.0.0.0"; - # for letter_opener (still doesn't work though) - REMOTE_DEV = "true"; - LOCAL_DOMAIN = "mastodon.localhost:8443"; - }; - }; - - services.postgresql = { - enable = true; - ensureUsers = [ - { - name = config.services.mastodon.database.user; - ensureClauses.createdb = true; - # ensurePermissions doesn't work anymore - # ensurePermissions = { - # "mastodon_development.*" = "ALL PRIVILEGES"; - # "mastodon_test.*" = "ALL PRIVILEGES"; - # } - } - ]; - # ensureDatabases = [ "mastodon_development_test" "mastodon_test" ]; - }; - - # Currently, nixos seems to be able to create a single database per - # postgres user. This works for the production version of mastodon, which - # is what's packaged in nixpkgs. For development, we need two databases, - # mastodon_development and mastodon_test. This used to be possible with - # ensurePermissions, but that's broken and has been removed. Here I copy - # the mastodon-init-db script from upstream nixpkgs, but add the single - # line `rails db:setup`, which asks mastodon to create the postgres - # databases for us. - # FIXME: the commented out lines were breaking things, but presumably they're necessary for something. - # TODO: see if we can fix the upstream ensurePermissions stuff. See above for what that config would look like. - systemd.services.mastodon-init-db.script = lib.mkForce '' - result="$(psql -t --csv -c \ - "select count(*) from pg_class c \ - join pg_namespace s on s.oid = c.relnamespace \ - where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \ - and s.nspname not like 'pg_temp%';")" || error_code=$? - if [ "''${error_code:-0}" -ne 0 ]; then - echo "Failure checking if database is seeded. psql gave exit code $error_code" - exit "$error_code" - fi - if [ "$result" -eq 0 ]; then - echo "Seeding database" - rails db:setup - # SAFETY_ASSURED=1 rails db:schema:load - rails db:seed - # else - # echo "Migrating database (this might be a noop)" - # rails db:migrate - fi - ''; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 55001; - guest.port = 55001; - } - ]; - } -] diff --git a/peertube-vm.nix b/peertube-vm.nix new file mode 100644 index 0000000..d38a625 --- /dev/null +++ b/peertube-vm.nix @@ -0,0 +1,30 @@ +{ pkgs, modulesPath, ... }: { + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + services.peertube = { + enable = true; + # redirects to localhost, but allows it to have a proper domain name + localDomain = "peertube.localhost"; + enableWebHttps = false; + settings = { + listen.hostname = "0.0.0.0"; + instance.name = "PeerTube Test VM"; + }; + # TODO: use agenix + secrets.secretsFile = pkgs.writeText "secret" '' + 574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24 + ''; + + # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. + redis.createLocally = true; + database.createLocally = true; + configureNginx = true; + }; + + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 9000; + guest.port = 9000; + } + ]; +} diff --git a/peertube.nix b/peertube.nix index 4e641b0..e0d4926 100644 --- a/peertube.nix +++ b/peertube.nix @@ -83,34 +83,4 @@ in AWS_ACCESS_KEY_ID=${snakeoil_key.id} AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret} ''; - - virtualisation.vmVariant = { config, ... }: { - services.peertube = { - enable = true; - # redirects to localhost, but allows it to have a proper domain name - localDomain = "peertube.localhost"; - enableWebHttps = false; - settings = { - listen.hostname = "0.0.0.0"; - instance.name = "PeerTube Test VM"; - }; - # TODO: use agenix - secrets.secretsFile = pkgs.writeText "secret" '' - 574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24 - ''; - - # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. - redis.createLocally = true; - database.createLocally = true; - configureNginx = true; - }; - - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 9000; - guest.port = 9000; - } - ]; - }; } diff --git a/pixelfed-vm.nix b/pixelfed-vm.nix new file mode 100644 index 0000000..c065e17 --- /dev/null +++ b/pixelfed-vm.nix @@ -0,0 +1,27 @@ +{ pkgs, modulesPath, ... }: { + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + networking.firewall.allowedTCPPorts = [ 80 ]; + services.pixelfed = { + enable = true; + domain = "pixelfed.localhost"; + # TODO: secrets management! + secretFile = pkgs.writeText "secrets.env" '' + APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA + ''; + settings = { + OPEN_REGISTRATION = true; + FORCE_HTTPS_URLS = false; + }; + # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx + # TODO: If that indeed makes sense, upstream it. + nginx = {}; + }; + virtualisation.memorySize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = 8000; + guest.port = 80; + } + ]; +} diff --git a/pixelfed.nix b/pixelfed.nix index 9a64071..2636117 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -5,7 +5,6 @@ let }; in { config, lib, pkgs, ... }: { - services.garage = { ensureBuckets = { pixelfed = { @@ -45,31 +44,4 @@ in AWS_ENDPOINT = "http://s3.garage.localhost:3900"; AWS_USE_PATH_STYLE_ENDPOINT = false; }; - - virtualisation.vmVariant = { - networking.firewall.allowedTCPPorts = [ 80 ]; - services.pixelfed = { - enable = true; - domain = "pixelfed.localhost"; - # TODO: secrets management! - secretFile = pkgs.writeText "secrets.env" '' - APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA - ''; - settings = { - OPEN_REGISTRATION = true; - FORCE_HTTPS_URLS = false; - }; - # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options here - # TODO: If that indeed makes sense, upstream it. - nginx = {}; - }; - virtualisation.memorySize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = 8000; - guest.port = 80; - } - ]; - }; } diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index ce04e91..f99d572 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -37,9 +37,9 @@ rebuildableTest { name = "test-mastodon-garage"; nodes = { - server = {config, ...}: { + server = { config, ... }: { virtualisation.memorySize = lib.mkVMOverride 4096; - imports = [ self.nixosModules.garage self.nixosModules.mastodon ]; + imports = with self.nixosModules; [ garage mastodon mastodon-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/tests/rebuildableTest.nix b/tests/rebuildableTest.nix index e734634..9513ca2 100644 --- a/tests/rebuildableTest.nix +++ b/tests/rebuildableTest.nix @@ -15,7 +15,7 @@ let settings = { PermitRootLogin = "yes"; PermitEmptyPasswords = "yes"; - UsePAM = "no"; + UsePAM = false; }; }; From e6dde311488a8f2350cd86a7162e8bc2867ed54f Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 28 Aug 2024 08:38:02 -0400 Subject: [PATCH 23/87] don't use local nixpkgs --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index ff723fa..3f59074 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,8 @@ description = "Testing mastodon configurations"; inputs = { - # nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - nixpkgs.url = "path:/home/qolen/nixpkgs"; + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + # nixpkgs.url = "path:/home/qolen/nixpkgs"; }; outputs = { self, nixpkgs }: From 5fd5c37834f63b133c29cc1b289e837a5aa9694a Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 28 Aug 2024 08:39:36 -0400 Subject: [PATCH 24/87] use rebuildable_tests branch of nixpkgs for now --- flake.nix | 3 +-- tests/mastodon-garage.nix | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 3f59074..5cda8c7 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,7 @@ description = "Testing mastodon configurations"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - # nixpkgs.url = "path:/home/qolen/nixpkgs"; + nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests"; }; outputs = { self, nixpkgs }: diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index f99d572..22605b8 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -1,7 +1,6 @@ { pkgs, self }: let lib = pkgs.lib; - # python = pkgs.python310.withPackages (ps: with ps; [ requests aiokafka ]); rebuildableTest = import ./rebuildableTest.nix pkgs; seleniumScript = pkgs.writers.writePython3Bin "selenium-script" { @@ -33,7 +32,7 @@ let driver.close() ''; in -rebuildableTest { +pkgs.nixosTest { name = "test-mastodon-garage"; nodes = { From cd277c0e98c729650710a93e08c17b2596a5d7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 30 Aug 2024 17:23:55 +0200 Subject: [PATCH 25/87] WIP --- README.md | 6 +- flake.lock | 16 ++-- flake.nix | 1 + tests/mastodon-garage.nix | 3 +- tests/pixelfed-garage.nix | 184 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 12 deletions(-) create mode 100644 tests/pixelfed-garage.nix diff --git a/README.md b/README.md index c8de4c4..73c3885 100644 --- a/README.md +++ b/README.md @@ -60,12 +60,12 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti ## NixOS Tests Tests live in the aptly named `tests/` directory, and can be accessed at the flake URI `.#checks..` e.g. `nix build .#checks.x86_64-linux.mastodon-garage`. -They can also be run interactively with +They can also be run interactively with ``` nix build .#checks...driverInteractive ./result/bin/nixos-test-driver 2>output ```` -you can `less -F output` from a different terminal to follow along. +you can `less output` and then `F` from a different terminal to follow along. These tests are also equiped with the same port forwarding as the VMs, so when running interactively you should be able to access services through a browser running on your machine. @@ -105,5 +105,3 @@ When mastodon is running in production mode, we have a few problems: - mastodon is trying to fetch `custom.css` from https://mastodon.localhost (no port), which is not the configured `LOCAL_DOMAIN`, so it's unclear why. NixOS tests do not take the configuration from `virtualisation.vmVariant`. This seems like an oversight since people don't tend to mix normal NixOS configurations with the ones they're using for tests. This should be pretty easy to rectify upstream. - - diff --git a/flake.lock b/flake.lock index 9e0adef..60b501f 100644 --- a/flake.lock +++ b/flake.lock @@ -2,14 +2,18 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1724846166, - "narHash": "sha256-Um1Ahz09XHepSA1QQmdQk8nbsJEwHe54gP3naWp6D94=", - "path": "/home/qolen/nixpkgs", - "type": "path" + "lastModified": 1723726852, + "narHash": "sha256-lRzlx4fPRtzA+dgz9Rh4WK5yAW3TsAXx335DQqxY2XY=", + "owner": "radvendii", + "repo": "nixpkgs", + "rev": "9286249a1673cf5b14a4793e22dd44b70cb69a0d", + "type": "github" }, "original": { - "path": "/home/qolen/nixpkgs", - "type": "path" + "owner": "radvendii", + "ref": "nixos_rebuild_tests", + "repo": "nixpkgs", + "type": "github" } }, "root": { diff --git a/flake.nix b/flake.nix index 5cda8c7..aeb7dba 100644 --- a/flake.nix +++ b/flake.nix @@ -52,6 +52,7 @@ checks.${system} = { mastodon-garage = import ./tests/mastodon-garage.nix { inherit pkgs self; }; + pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit pkgs self; }; }; devShells.${system}.default = pkgs.mkShell { diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index 22605b8..91e3f4f 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -26,7 +26,6 @@ let WebDriverWait(driver, 90).until( lambda x: x.find_element(By.CLASS_NAME, "status")) - # XXX: how do I save this to the derivation output? driver.save_screenshot("/mastodon-screenshot.png") driver.close() @@ -58,7 +57,7 @@ pkgs.nixosTest { }; }; - testScript = {nodes, ...}: '' + testScript = '' import re import time diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix new file mode 100644 index 0000000..0b1bc82 --- /dev/null +++ b/tests/pixelfed-garage.nix @@ -0,0 +1,184 @@ +{ pkgs, self }: +let + lib = pkgs.lib; + rebuildableTest = import ./rebuildableTest.nix pkgs; + seleniumScript = pkgs.writers.writePython3Bin "selenium-script" + { + libraries = with pkgs.python3Packages; [ selenium ]; + } '' + import sys + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.support.wait import WebDriverWait + from selenium.webdriver.chrome.options import Options + + email = sys.argv[1] + password = sys.argv[2] + + green_path = "${./green.png}" + screenshot_path = "/screenshot.png" + + # Create and configure driver. It is important to set the window size such that + # the “Create New Post” button is visible. + print("Create and configure driver...") + options = Options() + options.add_argument("--headless=new") + service = webdriver.ChromeService(executable_path="${lib.getExe pkgs.chromedriver}") # noqa: E501 + driver = webdriver.Chrome(options=options, service=service) + driver.implicitly_wait(10) + driver.set_window_size(1920, 1200) + + # FIXME: Replace the By.XPATH by By.CSS_SELECTOR. + + # Go to Pixelfed and login. + print("Open login page...") + driver.get("http://pixelfed.localhost/login") + print("Enter email...") + driver.find_element(By.ID, "email").send_keys(email) + print("Enter password...") + driver.find_element(By.ID, "password").send_keys(password) + # FIXME: This is disgusting. Find instead the input type submit in the form + # with action ending in "/login". + print("Click “Login” button...") + driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() + + # Find the new post form, fill it in with our pictureand a caption. + print("Click on “Create New Post”...") + driver.find_element(By.LINK_TEXT, "Create New Post").click() + print("Add file to input element...") + driver.find_element(By.XPATH, "//input[@type='file']").send_keys(green_path) + print("Click on “Post” button...") + driver.find_element(By.LINK_TEXT, "Post").click() + + # Wait until the post loads, and in particular its picture, then take a + # screenshot of the whole page. + print("Wait for post and image to be loaded...") + WebDriverWait(driver, timeout=10).until( + lambda d: d.execute_script( + "return arguments[0].complete", + d.find_element( + By.XPATH, "//div[@class='timeline-status-component-content']//img" + ), + ) + ) + print("Take screenshot...") + driver.save_screenshot(screenshot_path) + + # All done ^-^ + driver.quit() + ''; +in +pkgs.nixosTest { + name = "test-pixelfed-garage"; + + nodes = { + server = { config, ... }: { + virtualisation = { + memorySize = lib.mkVMOverride 8192; + cores = 8; + }; + imports = with self.nixosModules; [ garage pixelfed pixelfed-vm ]; + # TODO: pair down + environment.systemPackages = with pkgs; [ + python3 + chromium + xh + seleniumScript + helix + imagemagick + ]; + environment.variables = { + POST_MEDIA = ./green.png; + AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; + AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; + }; + }; + }; + + testScript = '' + import re + # import time + + server.start() + + with subtest("Pixelfed starts"): + server.wait_for_unit("phpfpm-pixelfed.service") + + # make sure pixelfed is fully up and running before we interact with it + # TODO: is there a way to test for this? + # REVIEW: this is copied from the Mastodon test; Pixelfed might be faster to start up. + # time.sleep(180) + + # # REVIEW: not sure why necessary. if it is, could we push this into the installation? + # # FIXME: this is an interactive command; look into flags to make it non-interactive + # with subtest("Passport install"): + # server.succeed("pixelfed-manage passport:install") + + with subtest("Account creation"): + password = "testtest" + server.succeed(f"pixelfed-manage user:create --name=test --username=test --email=test@test.com --password={password} --confirm_email=1") + + # # REVIEW: do we actually need TTY? + # with subtest("TTY Login"): + # server.wait_until_tty_matches("1", "login: ") + # server.send_chars("root\n"); + + # with subtest("Log in with toot"): + # # toot doesn't provide a way to just specify our login details as arguments, so we have to pretend we're typing them in at the prompt + # server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n") + # server.wait_until_tty_matches("1", "Password: ") + # server.send_chars(password + "\n") + # server.wait_until_tty_matches("1", "Successfully logged in.") + + # with subtest("post text"): + # server.succeed("echo 'hello mastodon' | toot post") + + # with subtest("post image"): + # server.succeed("toot post --media $POST_MEDIA") + + # with subtest("access garage"): + # server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + # server.succeed("mc ls garage/mastodon") + + # with subtest("access image in garage"): + # image = server.succeed("mc find garage --regex original") + # image = image.rstrip() + # if image == "": + # raise Exception("image posted to mastodon did not get stored in garage") + # server.succeed(f"mc cat {image} >/garage-image.webp") + # garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") + # image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") + # if garage_image_hash != image_hash: + # raise Exception("image stored in garage did not match image uploaded") + + # with subtest("Content security policy allows garage images"): + # headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") + # csp_match = None + # # I can't figure out re.MULTILINE + # for header in headers.split("\n"): + # csp_match = re.match('^Content-Security-Policy: (.*)$', header) + # if csp_match is not None: + # break + # if csp_match is None: + # raise Exception("mastodon did not send a content security policy header") + # csp = csp_match.group(1) + # # the img-src content security policy should include the garage server + # garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) + # if garage_csp is None: + # raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") + + # NOTE: This could in theory give a false positive if pixelfed changes it's + # colorscheme to include pure green. (see same problem in mastodon-garage.nix). + # TODO: For instance: post a red image and check that the green pixel IS NOT + # there, then post a green image and check that the green pixel IS there. + + with subtest("image displays"): + server.succeed(f"selenium-script test@test.com {password}") + server.copy_from_vm("/screenshot.png", "") + displayed_colors = server.succeed("convert /screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") + # check that the green image displayed somewhere + green_check = re.match(".*#00FF00.*", displayed_colors, re.S) + if green_check is None: + raise Exception("cannot detect the uploaded image on pixelfed page.") + ''; +} From d1691e4c8bbfa7a256665239604eda9959eed28f Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 2 Sep 2024 12:07:25 -0400 Subject: [PATCH 26/87] fix typo --- tests/mastodon-garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index 91e3f4f..d20c5ad 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -110,7 +110,7 @@ pkgs.nixosTest { raise Exception("image stored in garage did not match image uploaded") with subtest("Content security policy allows garage images"): - headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") + headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local") csp_match = None # I can't figure out re.MULTILINE for header in headers.split("\n"): From f8af95f9abd7af5cb5fa329266fce9ebe2446fbf Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 2 Sep 2024 12:08:14 -0400 Subject: [PATCH 27/87] garage defaults without this ensureKeys and ensureBuckets Must be set or nixos won't build --- garage.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/garage.nix b/garage.nix index ff2ec36..a8aa247 100644 --- a/garage.nix +++ b/garage.nix @@ -86,6 +86,7 @@ in { }; }; }); + default = {}; }; ensureKeys = mkOption { type = types.attrsOf (types.submodule { @@ -120,6 +121,7 @@ in { }; }; }); + default = {}; }; }; }; From 1a921084750632c07f9fcfe451573c4e5fa5f8b0 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 2 Sep 2024 12:09:10 -0400 Subject: [PATCH 28/87] attempt to access garage storage correctly nginx was trying to access the files on disk, rather than via s3 storage --- pixelfed-vm.nix | 5 +++-- pixelfed.nix | 2 ++ tests/pixelfed-garage.nix | 43 +++++++++++++++++++++++++-------------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/pixelfed-vm.nix b/pixelfed-vm.nix index c065e17..40f56e5 100644 --- a/pixelfed-vm.nix +++ b/pixelfed-vm.nix @@ -2,7 +2,6 @@ imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; networking.firewall.allowedTCPPorts = [ 80 ]; services.pixelfed = { - enable = true; domain = "pixelfed.localhost"; # TODO: secrets management! secretFile = pkgs.writeText "secrets.env" '' @@ -14,7 +13,9 @@ }; # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. - nginx = {}; + nginx = { + locations."/storage/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; + }; }; virtualisation.memorySize = 2048; virtualisation.forwardPorts = [ diff --git a/pixelfed.nix b/pixelfed.nix index 2636117..1235215 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -32,6 +32,8 @@ in }; }; + services.pixelfed.enable = true; + # TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place services.pixelfed.settings = { FILESYSTEM_CLOUD = "s3"; diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 0b1bc82..c16a370 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -12,11 +12,13 @@ let from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.chrome.options import Options + print("starting selenium script") + email = sys.argv[1] password = sys.argv[2] green_path = "${./green.png}" - screenshot_path = "/screenshot.png" + screenshot_path = "/home/seleniumUser/screenshot.png" # Create and configure driver. It is important to set the window size such that # the “Create New Post” button is visible. @@ -62,10 +64,12 @@ let ) ) print("Take screenshot...") - driver.save_screenshot(screenshot_path) + if not driver.save_screenshot(screenshot_path): + raise Exception("selenium could not save screenshot") - # All done ^-^ + print("Quitting...") driver.quit() + print("All done!") ''; in pkgs.nixosTest { @@ -77,11 +81,16 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ garage pixelfed pixelfed-vm ]; + imports = with self.nixosModules; [ + garage + pixelfed + pixelfed-vm + ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 chromium + chromedriver xh seleniumScript helix @@ -89,8 +98,12 @@ pkgs.nixosTest { ]; environment.variables = { POST_MEDIA = ./green.png; - AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; - AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; + # AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; + # AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; + }; + # chrome does not like being run as root + users.users.seleniumUser = { + isNormalUser = true; }; }; }; @@ -137,14 +150,14 @@ pkgs.nixosTest { # server.succeed("toot post --media $POST_MEDIA") # with subtest("access garage"): - # server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") - # server.succeed("mc ls garage/mastodon") + # server.succeed("mc alias set pixelfed http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + # server.succeed("mc ls garage/pixelfed") # with subtest("access image in garage"): # image = server.succeed("mc find garage --regex original") # image = image.rstrip() # if image == "": - # raise Exception("image posted to mastodon did not get stored in garage") + # raise Exception("image posted to pixelfed did not get stored in garage") # server.succeed(f"mc cat {image} >/garage-image.webp") # garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") # image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") @@ -152,7 +165,7 @@ pkgs.nixosTest { # raise Exception("image stored in garage did not match image uploaded") # with subtest("Content security policy allows garage images"): - # headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") + # headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local") # csp_match = None # # I can't figure out re.MULTILINE # for header in headers.split("\n"): @@ -160,21 +173,21 @@ pkgs.nixosTest { # if csp_match is not None: # break # if csp_match is None: - # raise Exception("mastodon did not send a content security policy header") + # raise Exception("pixelfed did not send a content security policy header") # csp = csp_match.group(1) # # the img-src content security policy should include the garage server # garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) # if garage_csp is None: - # raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") + # raise Exception("Pixelfed's content security policy does not include garage server. image will not be displayed properly on pixelfed.") # NOTE: This could in theory give a false positive if pixelfed changes it's - # colorscheme to include pure green. (see same problem in mastodon-garage.nix). + # colorscheme to include pure green. (see same problem in pixelfed-garage.nix). # TODO: For instance: post a red image and check that the green pixel IS NOT # there, then post a green image and check that the green pixel IS there. with subtest("image displays"): - server.succeed(f"selenium-script test@test.com {password}") - server.copy_from_vm("/screenshot.png", "") + server.succeed(f"su - seleniumUser -c 'selenium-script test@test.com {password}'") + server.copy_from_vm("/home/seleniumUser/screenshot.png", "") displayed_colors = server.succeed("convert /screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere green_check = re.match(".*#00FF00.*", displayed_colors, re.S) From 7b204eb2ce8369679b7f21ec8b6801831c9c40fb Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 2 Sep 2024 12:10:01 -0400 Subject: [PATCH 29/87] some more ignores --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2cb15d6..3276413 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ nixos.qcow2 result* .direnv .nixos-test-history -mastodon-screenshot.png +*screenshot.png +output +todo From afb8d56dd6650abee5c014231c76fba63b088e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Wed, 4 Sep 2024 18:30:55 +0200 Subject: [PATCH 30/87] DANGEROUSLY fix everything --- pixelfed-vm.nix | 2 +- pixelfed.nix | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pixelfed-vm.nix b/pixelfed-vm.nix index 40f56e5..fe890a8 100644 --- a/pixelfed-vm.nix +++ b/pixelfed-vm.nix @@ -14,7 +14,7 @@ # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. nginx = { - locations."/storage/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; + # locations."/storage/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; }; }; virtualisation.memorySize = 2048; diff --git a/pixelfed.nix b/pixelfed.nix index 1235215..fe51e72 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -36,6 +36,7 @@ in # TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place services.pixelfed.settings = { + DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; FILESYSTEM_CLOUD = "s3"; PF_ENABLE_CLOUD = true; AWS_ACCESS_KEY_ID = snakeoil_key.id; From 1932a131e0d6d8f47b6e563e40f9bb0ad4c01a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Thu, 5 Sep 2024 13:05:29 +0200 Subject: [PATCH 31/87] Proxy Garage backend --- pixelfed-vm.nix | 2 +- pixelfed.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pixelfed-vm.nix b/pixelfed-vm.nix index fe890a8..de88c18 100644 --- a/pixelfed-vm.nix +++ b/pixelfed-vm.nix @@ -14,7 +14,7 @@ # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. nginx = { - # locations."/storage/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; + locations."/public/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; }; }; virtualisation.memorySize = 2048; diff --git a/pixelfed.nix b/pixelfed.nix index fe51e72..03b6d61 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -42,7 +42,7 @@ in AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_DEFAULT_REGION = "garage"; - AWS_URL = "http://pixelfed.s3.garage.localhost:3900"; + AWS_URL = ""; ## Empty such that the URL is just relative to the pixelfed instance. AWS_BUCKET = "pixelfed"; AWS_ENDPOINT = "http://s3.garage.localhost:3900"; AWS_USE_PATH_STYLE_ENDPOINT = false; From 60cf77e66e8d199be619a35316ae0cc236aaaa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Thu, 5 Sep 2024 15:32:34 +0200 Subject: [PATCH 32/87] Make `ensure-garage` a oneshot service --- garage.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/garage.nix b/garage.nix index a8aa247..61d729a 100644 --- a/garage.nix +++ b/garage.nix @@ -166,6 +166,9 @@ in { systemd.services.ensure-garage = { after = [ "garage.service" ]; wantedBy = [ "garage.service" ]; + serviceConfig = { + Type = "oneshot"; + }; path = [ cfg.package pkgs.perl pkgs.awscli ]; script = '' set -xeuo pipefail From e4d84d78817c789bcf8ea9b0da4b1efa0caef669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Thu, 5 Sep 2024 15:33:47 +0200 Subject: [PATCH 33/87] Run `pixelfed-data-setup` only after `ensure-garage` --- pixelfed.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pixelfed.nix b/pixelfed.nix index 03b6d61..5649018 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -47,4 +47,10 @@ in AWS_ENDPOINT = "http://s3.garage.localhost:3900"; AWS_USE_PATH_STYLE_ENDPOINT = false; }; + + ## Only ever run `pixelfed-data-setup` after `ensure-garage` has done its job. + ## Otherwise, everything crashed dramatically. + systemd.services.pixelfed-data-setup = { + after = [ "ensure-garage.service" ]; + }; } From b24411f44b9d2ec1f76e0af69ee45377c1497cd2 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 5 Sep 2024 11:06:55 -0400 Subject: [PATCH 34/87] this configuration also works (without nginx config) --- pixelfed-vm.nix | 2 +- pixelfed.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pixelfed-vm.nix b/pixelfed-vm.nix index de88c18..451aeda 100644 --- a/pixelfed-vm.nix +++ b/pixelfed-vm.nix @@ -14,7 +14,7 @@ # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. nginx = { - locations."/public/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; + # locations."/public/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; }; }; virtualisation.memorySize = 2048; diff --git a/pixelfed.nix b/pixelfed.nix index 5649018..1448956 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -42,7 +42,7 @@ in AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_DEFAULT_REGION = "garage"; - AWS_URL = ""; ## Empty such that the URL is just relative to the pixelfed instance. + AWS_URL = "http://pixelfed.web.garage.localhost:3902/"; AWS_BUCKET = "pixelfed"; AWS_ENDPOINT = "http://s3.garage.localhost:3900"; AWS_USE_PATH_STYLE_ENDPOINT = false; From a24572976563ab1fc4ddaa0c114e93cc763f0cce Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 5 Sep 2024 12:03:35 -0400 Subject: [PATCH 35/87] patch pixelfed to give nginx read permissions this way we don't need DANGEROUSLY_SET_FILESYSTEM_DRIVER --- pixelfed-group-permissions.patch | 18 ++++++++++++++++++ pixelfed.nix | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pixelfed-group-permissions.patch diff --git a/pixelfed-group-permissions.patch b/pixelfed-group-permissions.patch new file mode 100644 index 0000000..d7dd442 --- /dev/null +++ b/pixelfed-group-permissions.patch @@ -0,0 +1,18 @@ +diff --git a/config/filesystems.php b/config/filesystems.php +index 00254e93..fc1a58f3 100644 +--- a/config/filesystems.php ++++ b/config/filesystems.php +@@ -49,11 +49,11 @@ return [ + 'permissions' => [ + 'file' => [ + 'public' => 0644, +- 'private' => 0600, ++ 'private' => 0640, + ], + 'dir' => [ + 'public' => 0755, +- 'private' => 0700, ++ 'private' => 0750, + ], + ], + ], diff --git a/pixelfed.nix b/pixelfed.nix index 1448956..9d2281f 100644 --- a/pixelfed.nix +++ b/pixelfed.nix @@ -36,7 +36,7 @@ in # TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place services.pixelfed.settings = { - DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; + # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; FILESYSTEM_CLOUD = "s3"; PF_ENABLE_CLOUD = true; AWS_ACCESS_KEY_ID = snakeoil_key.id; @@ -53,4 +53,8 @@ in systemd.services.pixelfed-data-setup = { after = [ "ensure-garage.service" ]; }; + + services.pixelfed.package = pkgs.pixelfed.overrideAttrs (old: { + patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; + }); } From b38ec1a62a5f1603cd43b8c4d94bab7108301e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Mon, 9 Sep 2024 14:09:54 +0200 Subject: [PATCH 36/87] Cleanup --- tests/pixelfed-garage.nix | 84 ++++++--------------------------------- 1 file changed, 13 insertions(+), 71 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index c16a370..21d4b4b 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -12,13 +12,13 @@ let from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.chrome.options import Options - print("starting selenium script") + print("Starting selenium script...") email = sys.argv[1] password = sys.argv[2] green_path = "${./green.png}" - screenshot_path = "/home/seleniumUser/screenshot.png" + screenshot_path = "/home/selenium/screenshot.png" # Create and configure driver. It is important to set the window size such that # the “Create New Post” button is visible. @@ -28,7 +28,7 @@ let service = webdriver.ChromeService(executable_path="${lib.getExe pkgs.chromedriver}") # noqa: E501 driver = webdriver.Chrome(options=options, service=service) driver.implicitly_wait(10) - driver.set_window_size(1920, 1200) + driver.set_window_size(1280, 960) # FIXME: Replace the By.XPATH by By.CSS_SELECTOR. @@ -67,6 +67,7 @@ let if not driver.save_screenshot(screenshot_path): raise Exception("selenium could not save screenshot") + # All done ^-^ print("Quitting...") driver.quit() print("All done!") @@ -81,10 +82,10 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ - garage - pixelfed - pixelfed-vm + imports = with self.nixosModules; [ + garage + pixelfed + pixelfed-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ @@ -102,7 +103,7 @@ pkgs.nixosTest { # AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; }; # chrome does not like being run as root - users.users.seleniumUser = { + users.users.selenium = { isNormalUser = true; }; }; @@ -110,84 +111,25 @@ pkgs.nixosTest { testScript = '' import re - # import time + import subprocess server.start() with subtest("Pixelfed starts"): server.wait_for_unit("phpfpm-pixelfed.service") - # make sure pixelfed is fully up and running before we interact with it - # TODO: is there a way to test for this? - # REVIEW: this is copied from the Mastodon test; Pixelfed might be faster to start up. - # time.sleep(180) - - # # REVIEW: not sure why necessary. if it is, could we push this into the installation? - # # FIXME: this is an interactive command; look into flags to make it non-interactive - # with subtest("Passport install"): - # server.succeed("pixelfed-manage passport:install") - with subtest("Account creation"): password = "testtest" server.succeed(f"pixelfed-manage user:create --name=test --username=test --email=test@test.com --password={password} --confirm_email=1") - # # REVIEW: do we actually need TTY? - # with subtest("TTY Login"): - # server.wait_until_tty_matches("1", "login: ") - # server.send_chars("root\n"); - - # with subtest("Log in with toot"): - # # toot doesn't provide a way to just specify our login details as arguments, so we have to pretend we're typing them in at the prompt - # server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n") - # server.wait_until_tty_matches("1", "Password: ") - # server.send_chars(password + "\n") - # server.wait_until_tty_matches("1", "Successfully logged in.") - - # with subtest("post text"): - # server.succeed("echo 'hello mastodon' | toot post") - - # with subtest("post image"): - # server.succeed("toot post --media $POST_MEDIA") - - # with subtest("access garage"): - # server.succeed("mc alias set pixelfed http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") - # server.succeed("mc ls garage/pixelfed") - - # with subtest("access image in garage"): - # image = server.succeed("mc find garage --regex original") - # image = image.rstrip() - # if image == "": - # raise Exception("image posted to pixelfed did not get stored in garage") - # server.succeed(f"mc cat {image} >/garage-image.webp") - # garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") - # image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") - # if garage_image_hash != image_hash: - # raise Exception("image stored in garage did not match image uploaded") - - # with subtest("Content security policy allows garage images"): - # headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local") - # csp_match = None - # # I can't figure out re.MULTILINE - # for header in headers.split("\n"): - # csp_match = re.match('^Content-Security-Policy: (.*)$', header) - # if csp_match is not None: - # break - # if csp_match is None: - # raise Exception("pixelfed did not send a content security policy header") - # csp = csp_match.group(1) - # # the img-src content security policy should include the garage server - # garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) - # if garage_csp is None: - # raise Exception("Pixelfed's content security policy does not include garage server. image will not be displayed properly on pixelfed.") - # NOTE: This could in theory give a false positive if pixelfed changes it's # colorscheme to include pure green. (see same problem in pixelfed-garage.nix). # TODO: For instance: post a red image and check that the green pixel IS NOT # there, then post a green image and check that the green pixel IS there. - with subtest("image displays"): - server.succeed(f"su - seleniumUser -c 'selenium-script test@test.com {password}'") - server.copy_from_vm("/home/seleniumUser/screenshot.png", "") + with subtest("Image displays"): + server.succeed(f"su - selenium -c 'selenium-script test@test.com {password}'") + server.copy_from_vm("/home/selenium/screenshot.png", "") displayed_colors = server.succeed("convert /screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere green_check = re.match(".*#00FF00.*", displayed_colors, re.S) From 680f54f7d5f8285265e393e190dc2be7b7d04755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Mon, 9 Sep 2024 14:10:00 +0200 Subject: [PATCH 37/87] Fix logging and Selenium script --- tests/pixelfed-garage.nix | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 21d4b4b..b06c438 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -128,11 +128,14 @@ pkgs.nixosTest { # there, then post a green image and check that the green pixel IS there. with subtest("Image displays"): - server.succeed(f"su - selenium -c 'selenium-script test@test.com {password}'") + server.succeed(f"su - selenium -c 'selenium-script test@test.com {password}' >&2") server.copy_from_vm("/home/selenium/screenshot.png", "") - displayed_colors = server.succeed("convert /screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") + displayed_colors = subprocess.run( + ["magick", "screenshot.png", "-define", "histogram:unique-colors=true", "-format", "%c", "histogram:info:"], + capture_output=True + ) # check that the green image displayed somewhere - green_check = re.match(".*#00FF00.*", displayed_colors, re.S) + green_check = re.match(".*#00FF00.*", displayed_colors.stdout.decode(), re.S) if green_check is None: raise Exception("cannot detect the uploaded image on pixelfed page.") ''; From 2f44a729c2d044720a4e4f603214a46490be5965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Mon, 9 Sep 2024 14:08:12 +0200 Subject: [PATCH 38/87] Follow things graphically --- garage.nix | 2 +- tests/pixelfed-garage.nix | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/garage.nix b/garage.nix index 61d729a..944c7f9 100644 --- a/garage.nix +++ b/garage.nix @@ -173,7 +173,7 @@ in { script = '' set -xeuo pipefail # give garage time to start up - sleep 3 + sleep 60 # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index b06c438..2431ef8 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -24,10 +24,10 @@ let # the “Create New Post” button is visible. print("Create and configure driver...") options = Options() - options.add_argument("--headless=new") + # options.add_argument("--headless=new") service = webdriver.ChromeService(executable_path="${lib.getExe pkgs.chromedriver}") # noqa: E501 driver = webdriver.Chrome(options=options, service=service) - driver.implicitly_wait(10) + driver.implicitly_wait(30) driver.set_window_size(1280, 960) # FIXME: Replace the By.XPATH by By.CSS_SELECTOR. @@ -78,6 +78,22 @@ pkgs.nixosTest { nodes = { server = { config, ... }: { + + services = { + xserver = { + enable = true; + displayManager.lightdm.enable = true; + desktopManager.lxqt.enable = true; + }; + + displayManager.autoLogin = { + enable = true; + user = "selenium"; + }; + }; + virtualisation.resolution = { x = 1680; y = 1050; }; + + virtualisation = { memorySize = lib.mkVMOverride 8192; cores = 8; From a49a2b57df43a9bb3f1cca5d4a810043b2cbaeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Mon, 9 Sep 2024 14:25:42 +0200 Subject: [PATCH 39/87] Run Magick on the server but with right path --- tests/pixelfed-garage.nix | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 2431ef8..096eab3 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -146,10 +146,7 @@ pkgs.nixosTest { with subtest("Image displays"): server.succeed(f"su - selenium -c 'selenium-script test@test.com {password}' >&2") server.copy_from_vm("/home/selenium/screenshot.png", "") - displayed_colors = subprocess.run( - ["magick", "screenshot.png", "-define", "histogram:unique-colors=true", "-format", "%c", "histogram:info:"], - capture_output=True - ) + displayed_colors = server.succeed("magick /home/selenium/screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere green_check = re.match(".*#00FF00.*", displayed_colors.stdout.decode(), re.S) if green_check is None: From a2da2f48b2abe7b1a29c54f8312552059f783c62 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 9 Sep 2024 10:12:54 -0400 Subject: [PATCH 40/87] use fediversity logo --- tests/fediversity.png | Bin 0 -> 5971 bytes tests/pixelfed-garage.nix | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 tests/fediversity.png diff --git a/tests/fediversity.png b/tests/fediversity.png new file mode 100644 index 0000000000000000000000000000000000000000..188ab23b71634a9b38aefbfc8e4970ee3be7f173 GIT binary patch literal 5971 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 Date: Mon, 9 Sep 2024 10:13:13 -0400 Subject: [PATCH 41/87] nicer timing for video --- garage.nix | 2 +- tests/pixelfed-garage.nix | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/garage.nix b/garage.nix index 944c7f9..bd2c38e 100644 --- a/garage.nix +++ b/garage.nix @@ -173,7 +173,7 @@ in { script = '' set -xeuo pipefail # give garage time to start up - sleep 60 + sleep 10 # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index d562390..e3f72f2 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -8,6 +8,7 @@ let } '' import sys import os + import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait @@ -42,14 +43,22 @@ let driver.find_element(By.ID, "password").send_keys(password) # FIXME: This is disgusting. Find instead the input type submit in the form # with action ending in "/login". + time.sleep(1) print("Click “Login” button...") driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() + time.sleep(3) + # Find the new post form, fill it in with our pictureand a caption. print("Click on “Create New Post”...") driver.find_element(By.LINK_TEXT, "Create New Post").click() print("Add file to input element...") - driver.find_element(By.XPATH, "//input[@type='file']").send_keys(green_path) + driver.find_element(By.XPATH, "//input[@type='file']").send_keys(media_path) + print("Add a caption") + driver.find_element(By.CSS_SELECTOR, ".media-body textarea").send_keys( + "Fediversity test of image upload to pixelfed with garage storage." + ) + time.sleep(3) print("Click on “Post” button...") driver.find_element(By.LINK_TEXT, "Post").click() @@ -64,6 +73,7 @@ let ), ) ) + time.sleep(3) print("Take screenshot...") if not driver.save_screenshot(screenshot_path): raise Exception("selenium could not save screenshot") From 3652a443b159ef137928a337172f1fa50e8d74d2 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 9 Sep 2024 10:13:23 -0400 Subject: [PATCH 42/87] test image gets uploaded to garage --- tests/pixelfed-garage.nix | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index e3f72f2..ded7778 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -125,9 +125,9 @@ pkgs.nixosTest { imagemagick ]; environment.variables = { - # AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; - # AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; POST_MEDIA = ./fediversity.png; + AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; + AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; }; # chrome does not like being run as root users.users.selenium = { @@ -161,5 +161,20 @@ pkgs.nixosTest { image_check = re.match(".*#FF0500.*", displayed_colors, re.S) if image_check is None: raise Exception("cannot detect the uploaded image on pixelfed page.") + + with subtest("access garage"): + server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + server.succeed("mc ls garage/pixelfed") + + with subtest("access image in garage"): + image = server.succeed("mc find garage --regex '\.png' --ignore '*_thumb.png'") + image = image.rstrip() + if image == "": + raise Exception("image posted to mastodon did not get stored in garage") + server.succeed(f"mc cat {image} >/garage-image.png") + garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.png") + image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") + if garage_image_hash != image_hash: + raise Exception("image stored in garage did not match image uploaded") ''; } From 9346bcca3db0ec196fbf477d32d4597adfbe1a54 Mon Sep 17 00:00:00 2001 From: Nicolas Jeannerod Date: Tue, 10 Sep 2024 12:17:32 +0000 Subject: [PATCH 43/87] Check that `src` points to Garage --- tests/pixelfed-garage.nix | 127 +++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 49 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index ded7778..853b01f 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -2,87 +2,110 @@ let lib = pkgs.lib; rebuildableTest = import ./rebuildableTest.nix pkgs; - seleniumScript = pkgs.writers.writePython3Bin "selenium-script" - { - libraries = with pkgs.python3Packages; [ selenium ]; - } '' + + # FIXME: Replace all the By.XPATH by By.CSS_SELECTOR. + + seleniumImports = '' import sys - import os - import time from selenium import webdriver from selenium.webdriver.common.by import By - from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.chrome.options import Options + ''; - print("Starting selenium script...") - - email = sys.argv[1] - password = sys.argv[2] - - media_path = os.environ['POST_MEDIA'] - screenshot_path = "/home/selenium/screenshot.png" - - # Create and configure driver. It is important to set the window size such that - # the “Create New Post” button is visible. - print("Create and configure driver...") + seleniumSetup = '' + print("Create and configure driver...", file=sys.stderr) options = Options() # options.add_argument("--headless=new") service = webdriver.ChromeService(executable_path="${lib.getExe pkgs.chromedriver}") # noqa: E501 driver = webdriver.Chrome(options=options, service=service) driver.implicitly_wait(30) driver.set_window_size(1280, 960) + ''; - # FIXME: Replace the By.XPATH by By.CSS_SELECTOR. - - # Go to Pixelfed and login. - print("Open login page...") + seleniumPixelfedLogin = email: password: '' + print("Open login page...", file=sys.stderr) driver.get("http://pixelfed.localhost/login") - print("Enter email...") - driver.find_element(By.ID, "email").send_keys(email) - print("Enter password...") - driver.find_element(By.ID, "password").send_keys(password) + print("Enter email...", file=sys.stderr) + driver.find_element(By.ID, "email").send_keys(${email}) + print("Enter password...", file=sys.stderr) + driver.find_element(By.ID, "password").send_keys(${password}) # FIXME: This is disgusting. Find instead the input type submit in the form # with action ending in "/login". - time.sleep(1) - print("Click “Login” button...") + print("Click “Login” button...", file=sys.stderr) driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() + ''; + seleniumTakeScreenshot = path: '' + print("Take screenshot...", file=sys.stderr) + if not driver.save_screenshot(${path}): + raise Exception("selenium could not save screenshot") + ''; + + seleniumQuit = '' + print("Quitting...", file=sys.stderr) + driver.quit() + ''; + + seleniumScriptPostPicture = pkgs.writers.writePython3Bin "selenium-script-post-picture" + { + libraries = with pkgs.python3Packages; [ selenium ]; + } '' + import os + import time + ${seleniumImports} + from selenium.webdriver.support.wait import WebDriverWait + + ${seleniumSetup} + ${seleniumPixelfedLogin "sys.argv[1]" "sys.argv[2]"} time.sleep(3) + media_path = os.environ['POST_MEDIA'] + # Find the new post form, fill it in with our pictureand a caption. - print("Click on “Create New Post”...") + print("Click on “Create New Post”...", file=sys.stderr) driver.find_element(By.LINK_TEXT, "Create New Post").click() - print("Add file to input element...") + print("Add file to input element...", file=sys.stderr) driver.find_element(By.XPATH, "//input[@type='file']").send_keys(media_path) - print("Add a caption") + print("Add a caption", file=sys.stderr) driver.find_element(By.CSS_SELECTOR, ".media-body textarea").send_keys( "Fediversity test of image upload to pixelfed with garage storage." ) time.sleep(3) - print("Click on “Post” button...") + print("Click on “Post” button...", file=sys.stderr) driver.find_element(By.LINK_TEXT, "Post").click() # Wait until the post loads, and in particular its picture, then take a # screenshot of the whole page. - print("Wait for post and image to be loaded...") + print("Wait for post and image to be loaded...", file=sys.stderr) + img = driver.find_element( + By.XPATH, + "//div[@class='timeline-status-component-content']//img" + ) WebDriverWait(driver, timeout=10).until( - lambda d: d.execute_script( - "return arguments[0].complete", - d.find_element( - By.XPATH, "//div[@class='timeline-status-component-content']//img" - ), - ) + lambda d: d.execute_script("return arguments[0].complete", img) ) time.sleep(3) - print("Take screenshot...") - if not driver.save_screenshot(screenshot_path): - raise Exception("selenium could not save screenshot") - # All done ^-^ - print("Quitting...") - driver.quit() - print("All done!") - ''; + ${seleniumTakeScreenshot "\"/home/selenium/screenshot.png\""} + ${seleniumQuit}''; + + seleniumScriptGetSrc = pkgs.writers.writePython3Bin "selenium-script-get-src" + { + libraries = with pkgs.python3Packages; [ selenium ]; + } '' + ${seleniumImports} + ${seleniumSetup} + ${seleniumPixelfedLogin "sys.argv[1]" "sys.argv[2]"} + + img = driver.find_element( + By.XPATH, + "//div[@class='timeline-status-component-content']//img" + ) + # REVIEW: Need to wait for it to be loaded? + print(img.get_attribute('src')) + + ${seleniumQuit}''; + in pkgs.nixosTest { name = "test-pixelfed-garage"; @@ -120,7 +143,8 @@ pkgs.nixosTest { chromium chromedriver xh - seleniumScript + seleniumScriptPostPicture + seleniumScriptGetSrc helix imagemagick ]; @@ -154,7 +178,7 @@ pkgs.nixosTest { # there, then post a green image and check that the green pixel IS there. with subtest("Image displays"): - server.succeed(f"su - selenium -c 'selenium-script test@test.com {password}' >&2") + server.succeed(f"su - selenium -c 'selenium-script-post-picture test@test.com {password}'") server.copy_from_vm("/home/selenium/screenshot.png", "") displayed_colors = server.succeed("magick /home/selenium/screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere @@ -176,5 +200,10 @@ pkgs.nixosTest { image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") if garage_image_hash != image_hash: raise Exception("image stored in garage did not match image uploaded") + + with subtest("Check that image comes from garage"): + src = server.succeed(f"su - selenium -c 'selenium-script-get-src test@test.com {password}'") + if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): + raise Exception("image does not come from garage") ''; } From 2dca2caca30bd0005b5a3f5b8daaa4cb1de98644 Mon Sep 17 00:00:00 2001 From: Nicolas Jeannerod Date: Tue, 10 Sep 2024 12:41:53 +0000 Subject: [PATCH 44/87] Wait until Garage is up by polling port 3900 --- garage.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/garage.nix b/garage.nix index bd2c38e..b58738d 100644 --- a/garage.nix +++ b/garage.nix @@ -172,8 +172,10 @@ in { path = [ cfg.package pkgs.perl pkgs.awscli ]; script = '' set -xeuo pipefail - # give garage time to start up - sleep 10 + + # Give garage time to start up by waiting until somethings speaks HTTP + # behind localhost:3900. + until curl -sio /dev/null http://localhost:3900/; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) From c2fd51baac863e5e310b106e192fa0388361b718 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Tue, 10 Sep 2024 08:50:50 -0400 Subject: [PATCH 45/87] stop threading email and password around as arguments --- tests/pixelfed-garage.nix | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 853b01f..e084cb0 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -3,6 +3,9 @@ let lib = pkgs.lib; rebuildableTest = import ./rebuildableTest.nix pkgs; + email = "test@test.com"; + password = "testtest"; + # FIXME: Replace all the By.XPATH by By.CSS_SELECTOR. seleniumImports = '' @@ -22,7 +25,7 @@ let driver.set_window_size(1280, 960) ''; - seleniumPixelfedLogin = email: password: '' + seleniumPixelfedLogin = '' print("Open login page...", file=sys.stderr) driver.get("http://pixelfed.localhost/login") print("Enter email...", file=sys.stderr) @@ -56,7 +59,7 @@ let from selenium.webdriver.support.wait import WebDriverWait ${seleniumSetup} - ${seleniumPixelfedLogin "sys.argv[1]" "sys.argv[2]"} + ${seleniumPixelfedLogin} time.sleep(3) media_path = os.environ['POST_MEDIA'] @@ -95,7 +98,7 @@ let } '' ${seleniumImports} ${seleniumSetup} - ${seleniumPixelfedLogin "sys.argv[1]" "sys.argv[2]"} + ${seleniumPixelfedLogin} img = driver.find_element( By.XPATH, @@ -169,8 +172,7 @@ pkgs.nixosTest { server.wait_for_unit("phpfpm-pixelfed.service") with subtest("Account creation"): - password = "testtest" - server.succeed(f"pixelfed-manage user:create --name=test --username=test --email=test@test.com --password={password} --confirm_email=1") + server.succeed(f"pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") # NOTE: This could in theory give a false positive if pixelfed changes it's # colorscheme to include pure green. (see same problem in pixelfed-garage.nix). @@ -178,7 +180,7 @@ pkgs.nixosTest { # there, then post a green image and check that the green pixel IS there. with subtest("Image displays"): - server.succeed(f"su - selenium -c 'selenium-script-post-picture test@test.com {password}'") + server.succeed(f"su - selenium -c 'selenium-script-post-picture ${email} ${password}'") server.copy_from_vm("/home/selenium/screenshot.png", "") displayed_colors = server.succeed("magick /home/selenium/screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere @@ -202,7 +204,7 @@ pkgs.nixosTest { raise Exception("image stored in garage did not match image uploaded") with subtest("Check that image comes from garage"): - src = server.succeed(f"su - selenium -c 'selenium-script-get-src test@test.com {password}'") + src = server.succeed(f"su - selenium -c 'selenium-script-get-src ${email} ${password}'") if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): raise Exception("image does not come from garage") ''; From 813c1ca8794619d01f868f5191e72450140a6449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 12:00:29 +0200 Subject: [PATCH 46/87] Some fixes to the Pixelfed/Garage test --- garage.nix | 2 +- tests/pixelfed-garage.nix | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/garage.nix b/garage.nix index b58738d..478443d 100644 --- a/garage.nix +++ b/garage.nix @@ -175,7 +175,7 @@ in { # Give garage time to start up by waiting until somethings speaks HTTP # behind localhost:3900. - until curl -sio /dev/null http://localhost:3900/; do sleep 1; done + until ${pkgs.curl}/bin/curl -sio /dev/null http://localhost:3900/; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index e084cb0..a08a312 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -29,15 +29,16 @@ let print("Open login page...", file=sys.stderr) driver.get("http://pixelfed.localhost/login") print("Enter email...", file=sys.stderr) - driver.find_element(By.ID, "email").send_keys(${email}) + driver.find_element(By.ID, "email").send_keys("${email}") print("Enter password...", file=sys.stderr) - driver.find_element(By.ID, "password").send_keys(${password}) + driver.find_element(By.ID, "password").send_keys("${password}") # FIXME: This is disgusting. Find instead the input type submit in the form # with action ending in "/login". print("Click “Login” button...", file=sys.stderr) driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() ''; + ## NOTE: `path` must be a valid python string, either a variable or _quoted_. seleniumTakeScreenshot = path: '' print("Take screenshot...", file=sys.stderr) if not driver.save_screenshot(${path}): @@ -172,7 +173,7 @@ pkgs.nixosTest { server.wait_for_unit("phpfpm-pixelfed.service") with subtest("Account creation"): - server.succeed(f"pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") + server.succeed("pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") # NOTE: This could in theory give a false positive if pixelfed changes it's # colorscheme to include pure green. (see same problem in pixelfed-garage.nix). @@ -180,7 +181,7 @@ pkgs.nixosTest { # there, then post a green image and check that the green pixel IS there. with subtest("Image displays"): - server.succeed(f"su - selenium -c 'selenium-script-post-picture ${email} ${password}'") + server.succeed("su - selenium -c 'selenium-script-post-picture ${email} ${password}'") server.copy_from_vm("/home/selenium/screenshot.png", "") displayed_colors = server.succeed("magick /home/selenium/screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") # check that the green image displayed somewhere @@ -193,7 +194,7 @@ pkgs.nixosTest { server.succeed("mc ls garage/pixelfed") with subtest("access image in garage"): - image = server.succeed("mc find garage --regex '\.png' --ignore '*_thumb.png'") + image = server.succeed("mc find garage --regex '\\.png' --ignore '*_thumb.png'") image = image.rstrip() if image == "": raise Exception("image posted to mastodon did not get stored in garage") @@ -204,7 +205,7 @@ pkgs.nixosTest { raise Exception("image stored in garage did not match image uploaded") with subtest("Check that image comes from garage"): - src = server.succeed(f"su - selenium -c 'selenium-script-get-src ${email} ${password}'") + src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): raise Exception("image does not come from garage") ''; From 059a76d9333ded6dd40e05e4c932441a72a35ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 14:24:23 +0200 Subject: [PATCH 47/87] Move VM-specific stuff in a subdirectory --- flake.nix | 17 ++++++++++------- interactive-vm.nix => vm/interactive-vm.nix | 0 mastodon-vm.nix => vm/mastodon-vm.nix | 0 peertube-vm.nix => vm/peertube-vm.nix | 0 pixelfed-vm.nix => vm/pixelfed-vm.nix | 0 5 files changed, 10 insertions(+), 7 deletions(-) rename interactive-vm.nix => vm/interactive-vm.nix (100%) rename mastodon-vm.nix => vm/mastodon-vm.nix (100%) rename peertube-vm.nix => vm/peertube-vm.nix (100%) rename pixelfed-vm.nix => vm/pixelfed-vm.nix (100%) diff --git a/flake.nix b/flake.nix index aeb7dba..913ea74 100644 --- a/flake.nix +++ b/flake.nix @@ -12,14 +12,17 @@ in { nixosModules = { - interactive-vm = import ./interactive-vm.nix; - mastodon = import ./mastodon.nix; - mastodon-vm = import ./mastodon-vm.nix; - peertube = import ./peertube.nix; - peertube-vm = import ./peertube-vm.nix; - pixelfed = import ./pixelfed.nix; - pixelfed-vm = import ./pixelfed-vm.nix; + ## Fediversity modules garage = import ./garage.nix; + mastodon = import ./mastodon.nix; + peertube = import ./peertube.nix; + pixelfed = import ./pixelfed.nix; + + ## VM-specific modules + interactive-vm = import ./vm/interactive-vm.nix; + mastodon-vm = import ./vm/mastodon-vm.nix; + peertube-vm = import ./vm/peertube-vm.nix; + pixelfed-vm = import ./vm/pixelfed-vm.nix; }; nixosConfigurations = { diff --git a/interactive-vm.nix b/vm/interactive-vm.nix similarity index 100% rename from interactive-vm.nix rename to vm/interactive-vm.nix diff --git a/mastodon-vm.nix b/vm/mastodon-vm.nix similarity index 100% rename from mastodon-vm.nix rename to vm/mastodon-vm.nix diff --git a/peertube-vm.nix b/vm/peertube-vm.nix similarity index 100% rename from peertube-vm.nix rename to vm/peertube-vm.nix diff --git a/pixelfed-vm.nix b/vm/pixelfed-vm.nix similarity index 100% rename from pixelfed-vm.nix rename to vm/pixelfed-vm.nix From 4b379af4ff8e7ede4b9fdd3f64bf18f03e6ded88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 14:27:24 +0200 Subject: [PATCH 48/87] Move Fediversity modules into own subdirectory --- garage.nix => fediversity/garage.nix | 0 mastodon.nix => fediversity/mastodon.nix | 0 peertube.nix => fediversity/peertube.nix | 0 .../pixelfed-group-permissions.patch | 0 pixelfed.nix => fediversity/pixelfed.nix | 0 flake.nix | 8 ++++---- 6 files changed, 4 insertions(+), 4 deletions(-) rename garage.nix => fediversity/garage.nix (100%) rename mastodon.nix => fediversity/mastodon.nix (100%) rename peertube.nix => fediversity/peertube.nix (100%) rename pixelfed-group-permissions.patch => fediversity/pixelfed-group-permissions.patch (100%) rename pixelfed.nix => fediversity/pixelfed.nix (100%) diff --git a/garage.nix b/fediversity/garage.nix similarity index 100% rename from garage.nix rename to fediversity/garage.nix diff --git a/mastodon.nix b/fediversity/mastodon.nix similarity index 100% rename from mastodon.nix rename to fediversity/mastodon.nix diff --git a/peertube.nix b/fediversity/peertube.nix similarity index 100% rename from peertube.nix rename to fediversity/peertube.nix diff --git a/pixelfed-group-permissions.patch b/fediversity/pixelfed-group-permissions.patch similarity index 100% rename from pixelfed-group-permissions.patch rename to fediversity/pixelfed-group-permissions.patch diff --git a/pixelfed.nix b/fediversity/pixelfed.nix similarity index 100% rename from pixelfed.nix rename to fediversity/pixelfed.nix diff --git a/flake.nix b/flake.nix index 913ea74..49d00cb 100644 --- a/flake.nix +++ b/flake.nix @@ -13,10 +13,10 @@ nixosModules = { ## Fediversity modules - garage = import ./garage.nix; - mastodon = import ./mastodon.nix; - peertube = import ./peertube.nix; - pixelfed = import ./pixelfed.nix; + garage = import ./fediversity/garage.nix; + mastodon = import ./fediversity/mastodon.nix; + peertube = import ./fediversity/peertube.nix; + pixelfed = import ./fediversity/pixelfed.nix; ## VM-specific modules interactive-vm = import ./vm/interactive-vm.nix; From 069b1ddb6c6b8c32555673f294995fa014a43120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 14:30:59 +0200 Subject: [PATCH 49/87] Move Fediversity modules under top-level module --- fediversity/default.nix | 27 +++++++++++++++++++++++++++ fediversity/garage.nix | 10 +++++++--- fediversity/mastodon.nix | 5 ++++- fediversity/peertube.nix | 5 ++++- fediversity/pixelfed.nix | 5 ++++- flake.nix | 21 +++++++++------------ tests/mastodon-garage.nix | 2 +- tests/pixelfed-garage.nix | 6 +----- vm/mastodon-vm.nix | 10 +++++++++- vm/pixelfed-vm.nix | 12 +++++++++++- 10 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 fediversity/default.nix diff --git a/fediversity/default.nix b/fediversity/default.nix new file mode 100644 index 0000000..6e38fbd --- /dev/null +++ b/fediversity/default.nix @@ -0,0 +1,27 @@ +{ lib, ... }: + +let + inherit (lib) mkOption; + inherit (lib.types) types; + +in { + imports = [ + ./garage.nix + ./mastodon.nix + ./pixelfed.nix + ./peertube.nix + ]; + + options = { + fediversity = { + enable = mkOption { + type = types.bool; + default = false; + }; + + mastodon.enable = mkOption { type = types.bool; default = false; }; + pixelfed.enable = mkOption { type = types.bool; default = false; }; + peertube.enable = mkOption { type = types.bool; default = false; }; + }; + }; +} diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 478443d..aad2925 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -6,8 +6,10 @@ let secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e"; }; in + # TODO: expand to a multi-machine setup -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, ... }: + let inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep; inherit (lib.strings) escapeShellArg; @@ -53,7 +55,9 @@ let ${concatMapAttrs (ensureAccessScriptFn key) ensureAccess} ''; ensureKeysScript = concatMapAttrs ensureKeyScriptFn cfg.ensureKeys; -in { +in + +{ # add in options to ensure creation of buckets and keys options = { services.garage = { @@ -126,7 +130,7 @@ in { }; }; - config = { + config = lib.mkIf config.fediversity.enable { virtualisation.diskSize = 2048; virtualisation.forwardPorts = [ { diff --git a/fediversity/mastodon.nix b/fediversity/mastodon.nix index e0c2ea4..5a349f6 100644 --- a/fediversity/mastodon.nix +++ b/fediversity/mastodon.nix @@ -4,7 +4,10 @@ let secret = "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; }; in -{ config, lib, pkgs, ... }: { + +{ config, lib, pkgs, ... }: + +lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { #### garage setup services.garage = { ensureBuckets = { diff --git a/fediversity/peertube.nix b/fediversity/peertube.nix index e0d4926..95e1c32 100644 --- a/fediversity/peertube.nix +++ b/fediversity/peertube.nix @@ -4,7 +4,10 @@ let secret = "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395"; }; in -{ config, lib, pkgs, ... }: { + +{ config, lib, pkgs, ... }: + +lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { networking.firewall.allowedTCPPorts = [ 80 9000 ]; services.garage = { diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 9d2281f..1d04f60 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -4,7 +4,10 @@ let secret = "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987"; }; in -{ config, lib, pkgs, ... }: { + +{ config, lib, pkgs, ... }: + +lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { services.garage = { ensureBuckets = { pixelfed = { diff --git a/flake.nix b/flake.nix index 49d00cb..737ad31 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,7 @@ nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests"; }; - outputs = { self, nixpkgs }: + outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; @@ -13,10 +13,7 @@ nixosModules = { ## Fediversity modules - garage = import ./fediversity/garage.nix; - mastodon = import ./fediversity/mastodon.nix; - peertube = import ./fediversity/peertube.nix; - pixelfed = import ./fediversity/pixelfed.nix; + fediversity = import ./fediversity; ## VM-specific modules interactive-vm = import ./vm/interactive-vm.nix; @@ -28,27 +25,27 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ interactive-vm mastodon mastodon-vm garage ]; + modules = with self.nixosModules; [ fediversity interactive-vm mastodon-vm ]; }; peertube = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ interactive-vm peertube peertube-vm garage ]; + modules = with self.nixosModules; [ fediversity interactive-vm peertube-vm ]; }; pixelfed = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ interactive-vm pixelfed pixelfed-vm garage ]; + modules = with self.nixosModules; [ fediversity interactive-vm pixelfed-vm ]; }; all = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + fediversity interactive-vm - peertube peertube-vm - pixelfed pixelfed-vm - mastodon mastodon-vm - garage + peertube-vm + pixelfed-vm + mastodon-vm ]; }; }; diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index d20c5ad..f98440d 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -37,7 +37,7 @@ pkgs.nixosTest { nodes = { server = { config, ... }: { virtualisation.memorySize = lib.mkVMOverride 4096; - imports = with self.nixosModules; [ garage mastodon mastodon-vm ]; + imports = with self.nixosModules; [ mastodon-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index a08a312..79e1edc 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -136,11 +136,7 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ - garage - pixelfed - pixelfed-vm - ]; + imports = with self.nixosModules; [ pixelfed-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/vm/mastodon-vm.nix b/vm/mastodon-vm.nix index fcfe3e5..0d13b89 100644 --- a/vm/mastodon-vm.nix +++ b/vm/mastodon-vm.nix @@ -1,9 +1,17 @@ { modulesPath, lib, config, ... }: { - imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + imports = [ + ../fediversity + (modulesPath + "/virtualisation/qemu-vm.nix") + ]; config = lib.mkMerge [ { + fediversity = { + enable = true; + mastodon.enable = true; + }; + services.mastodon = { # redirects to localhost, but allows it to have a proper domain name localDomain = "mastodon.localhost"; diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index 451aeda..be238f1 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -1,5 +1,15 @@ { pkgs, modulesPath, ... }: { - imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + + imports = [ + ../fediversity + (modulesPath + "/virtualisation/qemu-vm.nix") + ]; + + fediversity = { + enable = true; + pixelfed.enable = true; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; services.pixelfed = { domain = "pixelfed.localhost"; From 61eb2d90817826b44fb3ead0492c8a7663bd33f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 17:31:58 +0200 Subject: [PATCH 50/87] Factorise Garage URIs --- fediversity/default.nix | 32 +++++++++++++++++++++++++++++++- fediversity/garage.nix | 34 +++++++++++++++++++--------------- fediversity/mastodon.nix | 4 ++-- fediversity/peertube.nix | 14 +++++++------- fediversity/pixelfed.nix | 5 ++--- tests/mastodon-garage.nix | 2 ++ tests/pixelfed-garage.nix | 2 ++ vm/mastodon-vm.nix | 1 + vm/pixelfed-vm.nix | 3 ++- 9 files changed, 68 insertions(+), 29 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 6e38fbd..dfdffc8 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -1,6 +1,7 @@ -{ lib, ... }: +{ lib, config, ... }: let + inherit (builtins) toString; inherit (lib) mkOption; inherit (lib.types) types; @@ -19,9 +20,38 @@ in { default = false; }; + garage = mkOption { + type = types.anything; + }; + + domain = mkOption { + type = types.string; + }; + mastodon.enable = mkOption { type = types.bool; default = false; }; pixelfed.enable = mkOption { type = types.bool; default = false; }; peertube.enable = mkOption { type = types.bool; default = false; }; }; }; + + config.fediversity = { + garage = { + api = rec { + domain = "s3.garage.${config.fediversity.domain}"; + port = 3900; + url = "http://${domain}:${toString port}"; + }; + + rpc = rec { + port = 3901; + }; + + web = rec { + rootDomain = "web.garage.${config.fediversity.domain}"; + port = 3902; + rootDomainAndPort = "${rootDomain}:${toString port}"; + urlFor = bucket: "http://${bucket}.${rootDomainAndPort}"; + }; + }; + }; } diff --git a/fediversity/garage.nix b/fediversity/garage.nix index aad2925..92cbd88 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -11,6 +11,7 @@ in { config, lib, pkgs, ... }: let + inherit (builtins) toString; inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep; inherit (lib.strings) escapeShellArg; cfg = config.services.garage; @@ -41,7 +42,7 @@ let ${optionalString corsRules.enable '' garage bucket allow --read --write --owner ${bucketArg} --key tmp # TODO: endpoin-url should not be hard-coded - aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${config.fediversity.garage.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} garage bucket deny --read --write --owner ${bucketArg} --key tmp ''} ''; @@ -135,19 +136,22 @@ in virtualisation.forwardPorts = [ { from = "host"; - host.port = 3901; - guest.port = 3901; + host.port = config.fediversity.garage.rpc.port; + guest.port = config.fediversity.garage.rpc.port; } { from = "host"; - host.port = 3902; - guest.port = 3902; + host.port = config.fediversity.garage.web.port; + guest.port = config.fediversity.garage.web.port; } ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; - networking.firewall.allowedTCPPorts = [ 3901 3902 ]; + networking.firewall.allowedTCPPorts = [ + config.fediversity.garage.rpc.port + config.fediversity.garage.web.port + ]; services.garage = { enable = true; package = pkgs.garage_0_9; @@ -156,15 +160,15 @@ in # TODO: use a secret file rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; # TODO: why does this have to be set? is there not a sensible default? - rpc_bind_addr = "[::]:3901"; - rpc_public_addr = "[::1]:3901"; - s3_api.api_bind_addr = "[::]:3900"; - s3_web.bind_addr = "[::]:3902"; - s3_web.root_domain = ".web.garage.localhost"; + rpc_bind_addr = "[::]:${toString config.fediversity.garage.rpc.port}"; + rpc_public_addr = "[::1]:${toString config.fediversity.garage.rpc.port}"; + s3_api.api_bind_addr = "[::]:${toString config.fediversity.garage.api.port}"; + s3_web.bind_addr = "[::]:${toString config.fediversity.garage.web.port}"; + s3_web.root_domain = ".${config.fediversity.garage.web.rootDomain}"; index = "index.html"; s3_api.s3_region = "garage"; - s3_api.root_domain = ".s3.garage.localhost"; + s3_api.root_domain = ".${config.fediversity.garage.api.domain}"; }; }; systemd.services.ensure-garage = { @@ -177,9 +181,9 @@ in script = '' set -xeuo pipefail - # Give garage time to start up by waiting until somethings speaks HTTP - # behind localhost:3900. - until ${pkgs.curl}/bin/curl -sio /dev/null http://localhost:3900/; do sleep 1; done + # Give Garage time to start up by waiting until somethings speaks HTTP + # behind Garage's API URL. + until ${pkgs.curl}/bin/curl -sio /dev/null ${config.fediversity.garage.api.url}; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/fediversity/mastodon.nix b/fediversity/mastodon.nix index 5a349f6..e866b84 100644 --- a/fediversity/mastodon.nix +++ b/fediversity/mastodon.nix @@ -38,7 +38,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { extraConfig = rec { S3_ENABLED = "true"; # TODO: this shouldn't be hard-coded, it should come from the garage configuration - S3_ENDPOINT = "http://s3.garage.localhost:3900"; + S3_ENDPOINT = config.fediversity.garage.api.url; S3_REGION = "garage"; S3_BUCKET = "mastodon"; # use . @@ -46,7 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; S3_PROTOCOL = "http"; - S3_HOSTNAME = "web.garage.localhost:3902"; + S3_HOSTNAME = config.fediversity.garage.web.rootDomainAndPort; # by default it tries to use "/" S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ diff --git a/fediversity/peertube.nix b/fediversity/peertube.nix index 95e1c32..021e52f 100644 --- a/fediversity/peertube.nix +++ b/fediversity/peertube.nix @@ -56,27 +56,27 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { settings = { object_storage = { enabled = true; - endpoint = "http://s3.garage.localhost:3900"; + endpoint = config.fediversity.garage.api.url; region = "garage"; # not supported by garage # SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube proxy.proxyify_private_files = false; - web_videos = { + web_videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = "http://peertube-videos.web.garage.localhost:3902"; + base_url = config.fediversity.garage.web.urlFor bucket_name; }; - videos = { + videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = "http://peertube-videos.web.garage.localhost:3902"; + base_url = config.fediversity.garage.web.urlFor bucket_name; }; - streaming_playlists = { + streaming_playlists = rec { bucket_name = "peertube-playlists"; prefix = ""; - base_url = "http://peertube-playlists.web.garage.localhost:3902"; + base_url = config.fediversity.garage.web.urlFor bucket_name; }; }; }; diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 1d04f60..38153f5 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -37,7 +37,6 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { services.pixelfed.enable = true; - # TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place services.pixelfed.settings = { # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; FILESYSTEM_CLOUD = "s3"; @@ -45,9 +44,9 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_DEFAULT_REGION = "garage"; - AWS_URL = "http://pixelfed.web.garage.localhost:3902/"; + AWS_URL = config.fediversity.garage.web.urlFor "pixelfed"; AWS_BUCKET = "pixelfed"; - AWS_ENDPOINT = "http://s3.garage.localhost:3900"; + AWS_ENDPOINT = config.fediversity.garage.api.url; AWS_USE_PATH_STYLE_ENDPOINT = false; }; diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index f98440d..c35e799 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -95,6 +95,7 @@ pkgs.nixosTest { server.succeed("toot post --media $POST_MEDIA") with subtest("access garage"): + ## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way? server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/mastodon") @@ -121,6 +122,7 @@ pkgs.nixosTest { raise Exception("mastodon did not send a content security policy header") csp = csp_match.group(1) # the img-src content security policy should include the garage server + ## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way? garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) if garage_csp is None: raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 79e1edc..a474b1c 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -186,6 +186,7 @@ pkgs.nixosTest { raise Exception("cannot detect the uploaded image on pixelfed page.") with subtest("access garage"): + ## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way? server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/pixelfed") @@ -202,6 +203,7 @@ pkgs.nixosTest { with subtest("Check that image comes from garage"): src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") + ## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way? if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): raise Exception("image does not come from garage") ''; diff --git a/vm/mastodon-vm.nix b/vm/mastodon-vm.nix index 0d13b89..0bc12aa 100644 --- a/vm/mastodon-vm.nix +++ b/vm/mastodon-vm.nix @@ -9,6 +9,7 @@ { fediversity = { enable = true; + domain = "localhost"; mastodon.enable = true; }; diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index be238f1..be04bfc 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -7,6 +7,7 @@ fediversity = { enable = true; + domain = "localhost"; pixelfed.enable = true; }; @@ -24,7 +25,7 @@ # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. nginx = { - # locations."/public/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; + # locations."/public/".proxyPass = "${config.fediversity.garage.web.urlFor "pixelfed"}/public/"; }; }; virtualisation.memorySize = 2048; From 492a1998661cefa840bea133d36f3642d0bd7d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Sep 2024 17:58:09 +0200 Subject: [PATCH 51/87] Factorise services URIs --- fediversity/default.nix | 4 ++++ fediversity/mastodon.nix | 9 +++++---- fediversity/peertube.nix | 8 ++++++++ fediversity/pixelfed.nix | 5 ++++- vm/mastodon-vm.nix | 10 +--------- vm/peertube-vm.nix | 15 ++++++--------- vm/pixelfed-vm.nix | 1 - 7 files changed, 28 insertions(+), 24 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index dfdffc8..d31df29 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -53,5 +53,9 @@ in { urlFor = bucket: "http://${bucket}.${rootDomainAndPort}"; }; }; + + pixelfed.domain = "pixelfed.${config.fediversity.domain}"; + mastodon.domain = "mastdodon.${config.fediversity.domain}"; + peertube.domain = "peertube.${config.fediversity.domain}"; }; } diff --git a/fediversity/mastodon.nix b/fediversity/mastodon.nix index e866b84..8740c8c 100644 --- a/fediversity/mastodon.nix +++ b/fediversity/mastodon.nix @@ -63,12 +63,14 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { services.mastodon = { enable = true; - # TODO: set up a domain name, and a DNS service so that this can run not in a vm - # localDomain = "domain.social"; + localDomain = config.fediversity.mastodon.domain; configureNginx = true; # TODO: configure a mailserver so this works - # smtp.fromAddress = "mastodon@domain.social"; + smtp = { + fromAddress = "noreply@${config.fediversity.mastodon.domain}"; + createLocally = false; + }; # TODO: this is hardware-dependent. let's figure it out when we have hardware # streamingProcesses = 1; @@ -81,4 +83,3 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { # defaults.email = "test@example.com"; }; } - diff --git a/fediversity/peertube.nix b/fediversity/peertube.nix index 021e52f..41c6cd1 100644 --- a/fediversity/peertube.nix +++ b/fediversity/peertube.nix @@ -53,6 +53,14 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { }; services.peertube = { + enable = true; + localDomain = config.fediversity.peertube.domain; + + # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. + redis.createLocally = true; + database.createLocally = true; + configureNginx = true; + settings = { object_storage = { enabled = true; diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 38153f5..95703ba 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -35,7 +35,10 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { }; }; - services.pixelfed.enable = true; + services.pixelfed = { + enable = true; + domain = config.fediversity.pixelfed.domain; + }; services.pixelfed.settings = { # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; diff --git a/vm/mastodon-vm.nix b/vm/mastodon-vm.nix index 0bc12aa..caf1ca6 100644 --- a/vm/mastodon-vm.nix +++ b/vm/mastodon-vm.nix @@ -14,14 +14,6 @@ }; services.mastodon = { - # redirects to localhost, but allows it to have a proper domain name - localDomain = "mastodon.localhost"; - - smtp = { - fromAddress = "mastodon@mastodon.localhost"; - createLocally = false; - }; - extraConfig = { EMAIL_DOMAIN_ALLOWLIST = "example.com"; }; @@ -65,7 +57,7 @@ BIND = "0.0.0.0"; # for letter_opener (still doesn't work though) REMOTE_DEV = "true"; - LOCAL_DOMAIN = "mastodon.localhost:8443"; + LOCAL_DOMAIN = "${config.fediversity.mastodon.domain}:8443"; }; }; diff --git a/vm/peertube-vm.nix b/vm/peertube-vm.nix index d38a625..5f40f4f 100644 --- a/vm/peertube-vm.nix +++ b/vm/peertube-vm.nix @@ -1,9 +1,11 @@ { pkgs, modulesPath, ... }: { - imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + + imports = [ + ../fediversity + (modulesPath + "/virtualisation/qemu-vm.nix") + ]; + services.peertube = { - enable = true; - # redirects to localhost, but allows it to have a proper domain name - localDomain = "peertube.localhost"; enableWebHttps = false; settings = { listen.hostname = "0.0.0.0"; @@ -13,11 +15,6 @@ secrets.secretsFile = pkgs.writeText "secret" '' 574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24 ''; - - # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. - redis.createLocally = true; - database.createLocally = true; - configureNginx = true; }; virtualisation.forwardPorts = [ diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index be04bfc..f12b9c5 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -13,7 +13,6 @@ networking.firewall.allowedTCPPorts = [ 80 ]; services.pixelfed = { - domain = "pixelfed.localhost"; # TODO: secrets management! secretFile = pkgs.writeText "secrets.env" '' APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA From cf5139836effbf983a4fe953fe7cb29cafbae5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 16:34:08 +0200 Subject: [PATCH 52/87] s/mkOption/mkEnableOption --- fediversity/default.nix | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index d31df29..3f39485 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -2,7 +2,7 @@ let inherit (builtins) toString; - inherit (lib) mkOption; + inherit (lib) mkOption mkEnableOption; inherit (lib.types) types; in { @@ -15,10 +15,7 @@ in { options = { fediversity = { - enable = mkOption { - type = types.bool; - default = false; - }; + enable = mkEnableOption "the collection of services bundled under Fediversity"; garage = mkOption { type = types.anything; @@ -28,9 +25,9 @@ in { type = types.string; }; - mastodon.enable = mkOption { type = types.bool; default = false; }; - pixelfed.enable = mkOption { type = types.bool; default = false; }; - peertube.enable = mkOption { type = types.bool; default = false; }; + mastodon.enable = mkEnableOption "default Fediversity Mastodon configuration"; + pixelfed.enable = mkEnableOption "default Fediversity Pixelfed configuration"; + peertube.enable = mkEnableOption "default Fediversity PeerTube configuration"; }; }; From 29c86aedff5a0db16585f942c81473ea5999e26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 16:35:21 +0200 Subject: [PATCH 53/87] s/types.string/types.str/ `types.string` was being used for too many thing so it got deprecated and now there are several different string types. `types.str` is the one where you don't want to merge definitions if it's defined in more than one place Co-authored-by: Taeer Bar-Yam --- fediversity/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 3f39485..9c60d61 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -22,7 +22,7 @@ in { }; domain = mkOption { - type = types.string; + type = types.str; }; mastodon.enable = mkEnableOption "default Fediversity Mastodon configuration"; From ecda4f249d78c9191cf4469f8f2504897a359174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 17:13:35 +0200 Subject: [PATCH 54/87] =?UTF-8?q?Rework=20definition=20of=20=E2=80=9Cconst?= =?UTF-8?q?ants=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - make things such as `fediversity.garage.api.port` into actual options with the right default value - move them under `fediversity.internal` Co-authored-by: Taeer Bar-Yam --- fediversity/default.nix | 91 ++++++++++++++++++++++++++++----------- fediversity/garage.nix | 28 ++++++------ fediversity/mastodon.nix | 8 ++-- fediversity/peertube.nix | 10 ++--- fediversity/pixelfed.nix | 6 +-- tests/mastodon-garage.nix | 4 +- tests/pixelfed-garage.nix | 4 +- vm/mastodon-vm.nix | 2 +- vm/pixelfed-vm.nix | 2 +- 9 files changed, 98 insertions(+), 57 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 9c60d61..a6e0682 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -17,42 +17,83 @@ in { fediversity = { enable = mkEnableOption "the collection of services bundled under Fediversity"; - garage = mkOption { - type = types.anything; - }; - domain = mkOption { type = types.str; + description = '' + root domain for the Fediversity services + + For instance, if this option is set to `foo.example.com`, then + Pixelfed might be under `pixelfed.foo.example.com`. + ''; }; mastodon.enable = mkEnableOption "default Fediversity Mastodon configuration"; pixelfed.enable = mkEnableOption "default Fediversity Pixelfed configuration"; peertube.enable = mkEnableOption "default Fediversity PeerTube configuration"; - }; - }; - config.fediversity = { - garage = { - api = rec { - domain = "s3.garage.${config.fediversity.domain}"; - port = 3900; - url = "http://${domain}:${toString port}"; - }; + internal = mkOption { + description = "options that are only meant to be used internally; change at your own risk"; + default = {}; + type = types.submodule { + options = { + garage = { + api = { + domain = mkOption { + type = types.str; + default = "s3.garage.${config.fediversity.domain}"; + }; + port = mkOption { + type = types.int; + default = 3900; + }; + url = mkOption { + type = types.str; + default = "http://${config.fediversity.internal.garage.api.domain}:${toString config.fediversity.internal.garage.api.port}"; + }; + }; - rpc = rec { - port = 3901; - }; + rpc = { + port = mkOption { + type = types.int; + default = 3901; + }; + }; - web = rec { - rootDomain = "web.garage.${config.fediversity.domain}"; - port = 3902; - rootDomainAndPort = "${rootDomain}:${toString port}"; - urlFor = bucket: "http://${bucket}.${rootDomainAndPort}"; + web = { + rootDomain = mkOption { + type = types.str; + default = "web.garage.${config.fediversity.domain}"; + }; + port = mkOption { + type = types.int; + default = 3902; + }; + rootDomainAndPort = mkOption { + type = types.str; + default = "${config.fediversity.internal.garage.web.rootDomain}:${toString config.fediversity.internal.garage.web.port}"; + }; + urlFor = mkOption { + type = types.functionTo types.str; + default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomainAndPort}"; + }; + }; + }; + + pixelfed.domain = mkOption { + type = types.str; + default = "pixelfed.${config.fediversity.domain}"; + }; + mastodon.domain = mkOption { + type = types.str; + default = "mastdodon.${config.fediversity.domain}"; + }; + peertube.domain = mkOption { + type = types.str; + default = "peertube.${config.fediversity.domain}"; + }; + }; + }; }; }; - - pixelfed.domain = "pixelfed.${config.fediversity.domain}"; - mastodon.domain = "mastdodon.${config.fediversity.domain}"; - peertube.domain = "peertube.${config.fediversity.domain}"; }; } diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 92cbd88..84af662 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -42,7 +42,7 @@ let ${optionalString corsRules.enable '' garage bucket allow --read --write --owner ${bucketArg} --key tmp # TODO: endpoin-url should not be hard-coded - aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${config.fediversity.garage.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${config.fediversity.internal.garage.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} garage bucket deny --read --write --owner ${bucketArg} --key tmp ''} ''; @@ -136,21 +136,21 @@ in virtualisation.forwardPorts = [ { from = "host"; - host.port = config.fediversity.garage.rpc.port; - guest.port = config.fediversity.garage.rpc.port; + host.port = config.fediversity.internal.garage.rpc.port; + guest.port = config.fediversity.internal.garage.rpc.port; } { from = "host"; - host.port = config.fediversity.garage.web.port; - guest.port = config.fediversity.garage.web.port; + host.port = config.fediversity.internal.garage.web.port; + guest.port = config.fediversity.internal.garage.web.port; } ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ - config.fediversity.garage.rpc.port - config.fediversity.garage.web.port + config.fediversity.internal.garage.rpc.port + config.fediversity.internal.garage.web.port ]; services.garage = { enable = true; @@ -160,15 +160,15 @@ in # TODO: use a secret file rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; # TODO: why does this have to be set? is there not a sensible default? - rpc_bind_addr = "[::]:${toString config.fediversity.garage.rpc.port}"; - rpc_public_addr = "[::1]:${toString config.fediversity.garage.rpc.port}"; - s3_api.api_bind_addr = "[::]:${toString config.fediversity.garage.api.port}"; - s3_web.bind_addr = "[::]:${toString config.fediversity.garage.web.port}"; - s3_web.root_domain = ".${config.fediversity.garage.web.rootDomain}"; + rpc_bind_addr = "[::]:${toString config.fediversity.internal.garage.rpc.port}"; + rpc_public_addr = "[::1]:${toString config.fediversity.internal.garage.rpc.port}"; + s3_api.api_bind_addr = "[::]:${toString config.fediversity.internal.garage.api.port}"; + s3_web.bind_addr = "[::]:${toString config.fediversity.internal.garage.web.port}"; + s3_web.root_domain = ".${config.fediversity.internal.garage.web.rootDomain}"; index = "index.html"; s3_api.s3_region = "garage"; - s3_api.root_domain = ".${config.fediversity.garage.api.domain}"; + s3_api.root_domain = ".${config.fediversity.internal.garage.api.domain}"; }; }; systemd.services.ensure-garage = { @@ -183,7 +183,7 @@ in # Give Garage time to start up by waiting until somethings speaks HTTP # behind Garage's API URL. - until ${pkgs.curl}/bin/curl -sio /dev/null ${config.fediversity.garage.api.url}; do sleep 1; done + until ${pkgs.curl}/bin/curl -sio /dev/null ${config.fediversity.internal.garage.api.url}; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/fediversity/mastodon.nix b/fediversity/mastodon.nix index 8740c8c..62599b5 100644 --- a/fediversity/mastodon.nix +++ b/fediversity/mastodon.nix @@ -38,7 +38,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { extraConfig = rec { S3_ENABLED = "true"; # TODO: this shouldn't be hard-coded, it should come from the garage configuration - S3_ENDPOINT = config.fediversity.garage.api.url; + S3_ENDPOINT = config.fediversity.internal.garage.api.url; S3_REGION = "garage"; S3_BUCKET = "mastodon"; # use . @@ -46,7 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; S3_PROTOCOL = "http"; - S3_HOSTNAME = config.fediversity.garage.web.rootDomainAndPort; + S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomainAndPort; # by default it tries to use "/" S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ @@ -63,12 +63,12 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { services.mastodon = { enable = true; - localDomain = config.fediversity.mastodon.domain; + localDomain = config.fediversity.internal.mastodon.domain; configureNginx = true; # TODO: configure a mailserver so this works smtp = { - fromAddress = "noreply@${config.fediversity.mastodon.domain}"; + fromAddress = "noreply@${config.fediversity.internal.mastodon.domain}"; createLocally = false; }; diff --git a/fediversity/peertube.nix b/fediversity/peertube.nix index 41c6cd1..88d26e1 100644 --- a/fediversity/peertube.nix +++ b/fediversity/peertube.nix @@ -54,7 +54,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { services.peertube = { enable = true; - localDomain = config.fediversity.peertube.domain; + localDomain = config.fediversity.internal.peertube.domain; # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. redis.createLocally = true; @@ -64,7 +64,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { settings = { object_storage = { enabled = true; - endpoint = config.fediversity.garage.api.url; + endpoint = config.fediversity.internal.garage.api.url; region = "garage"; # not supported by garage @@ -74,17 +74,17 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { web_videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = config.fediversity.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlFor bucket_name; }; videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = config.fediversity.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlFor bucket_name; }; streaming_playlists = rec { bucket_name = "peertube-playlists"; prefix = ""; - base_url = config.fediversity.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlFor bucket_name; }; }; }; diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 95703ba..1edc914 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -37,7 +37,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { services.pixelfed = { enable = true; - domain = config.fediversity.pixelfed.domain; + domain = config.fediversity.internal.pixelfed.domain; }; services.pixelfed.settings = { @@ -47,9 +47,9 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_DEFAULT_REGION = "garage"; - AWS_URL = config.fediversity.garage.web.urlFor "pixelfed"; + AWS_URL = config.fediversity.internal.garage.web.urlFor "pixelfed"; AWS_BUCKET = "pixelfed"; - AWS_ENDPOINT = config.fediversity.garage.api.url; + AWS_ENDPOINT = config.fediversity.internal.garage.api.url; AWS_USE_PATH_STYLE_ENDPOINT = false; }; diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index c35e799..c109ea1 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -95,7 +95,7 @@ pkgs.nixosTest { server.succeed("toot post --media $POST_MEDIA") with subtest("access garage"): - ## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way? + ## REVIEW: could we grab `config.fediversity.internal.garage.api.url` here in some way? server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/mastodon") @@ -122,7 +122,7 @@ pkgs.nixosTest { raise Exception("mastodon did not send a content security policy header") csp = csp_match.group(1) # the img-src content security policy should include the garage server - ## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way? + ## REVIEW: could we grab `config.fediversity.internal.garage.web.url` here in some way? garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) if garage_csp is None: raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index a474b1c..9bf2944 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -186,7 +186,7 @@ pkgs.nixosTest { raise Exception("cannot detect the uploaded image on pixelfed page.") with subtest("access garage"): - ## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way? + ## REVIEW: could we grab `config.fediversity.internal.garage.api.url` here in some way? server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/pixelfed") @@ -203,7 +203,7 @@ pkgs.nixosTest { with subtest("Check that image comes from garage"): src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") - ## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way? + ## REVIEW: could we grab `config.fediversity.internal.garage.web.url` here in some way? if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): raise Exception("image does not come from garage") ''; diff --git a/vm/mastodon-vm.nix b/vm/mastodon-vm.nix index caf1ca6..ea17f27 100644 --- a/vm/mastodon-vm.nix +++ b/vm/mastodon-vm.nix @@ -57,7 +57,7 @@ BIND = "0.0.0.0"; # for letter_opener (still doesn't work though) REMOTE_DEV = "true"; - LOCAL_DOMAIN = "${config.fediversity.mastodon.domain}:8443"; + LOCAL_DOMAIN = "${config.fediversity.internal.mastodon.domain}:8443"; }; }; diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index f12b9c5..8f97180 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -24,7 +24,7 @@ # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # TODO: If that indeed makes sense, upstream it. nginx = { - # locations."/public/".proxyPass = "${config.fediversity.garage.web.urlFor "pixelfed"}/public/"; + # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/"; }; }; virtualisation.memorySize = 2048; From bbf5f5043283f496414ecd344e2aab9c5d7f72b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 17:20:31 +0200 Subject: [PATCH 55/87] Note on style choice for eg. `fediversity.internal.pixelfed.domain` --- fediversity/default.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fediversity/default.nix b/fediversity/default.nix index a6e0682..46ee05d 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -79,6 +79,10 @@ in { }; }; + ## REVIEW: Do we want to recreate options under + ## `fediversity.internal` or would we rather use the options from + ## the respective services? See Taeer's comment: + ## https://git.fediversity.eu/taeer/simple-nixos-fediverse/pulls/22#issuecomment-124 pixelfed.domain = mkOption { type = types.str; default = "pixelfed.${config.fediversity.domain}"; From 76656098270ccdd90fbe87b79b60c444f35bed82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 15:45:53 +0000 Subject: [PATCH 56/87] Use common options also in tests --- tests/mastodon-garage.nix | 7 +++---- tests/pixelfed-garage.nix | 10 ++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index c109ea1..672b70f 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -57,7 +57,7 @@ pkgs.nixosTest { }; }; - testScript = '' + testScript = { nodes, ... }: '' import re import time @@ -95,8 +95,7 @@ pkgs.nixosTest { server.succeed("toot post --media $POST_MEDIA") with subtest("access garage"): - ## REVIEW: could we grab `config.fediversity.internal.garage.api.url` here in some way? - server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/mastodon") with subtest("access image in garage"): @@ -122,7 +121,7 @@ pkgs.nixosTest { raise Exception("mastodon did not send a content security policy header") csp = csp_match.group(1) # the img-src content security policy should include the garage server - ## REVIEW: could we grab `config.fediversity.internal.garage.web.url` here in some way? + ## TODO: use `nodes.server.fediversity.internal.garage.api.url` same as above, but beware of escaping the regex. garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) if garage_csp is None: raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 9bf2944..b25bc66 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -160,7 +160,7 @@ pkgs.nixosTest { }; }; - testScript = '' + testScript = { nodes, ... }: '' import re server.start() @@ -186,15 +186,14 @@ pkgs.nixosTest { raise Exception("cannot detect the uploaded image on pixelfed page.") with subtest("access garage"): - ## REVIEW: could we grab `config.fediversity.internal.garage.api.url` here in some way? - server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc ls garage/pixelfed") with subtest("access image in garage"): image = server.succeed("mc find garage --regex '\\.png' --ignore '*_thumb.png'") image = image.rstrip() if image == "": - raise Exception("image posted to mastodon did not get stored in garage") + raise Exception("image posted to Pixelfed did not get stored in garage") server.succeed(f"mc cat {image} >/garage-image.png") garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.png") image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") @@ -203,8 +202,7 @@ pkgs.nixosTest { with subtest("Check that image comes from garage"): src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") - ## REVIEW: could we grab `config.fediversity.internal.garage.web.url` here in some way? - if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): + if not src.startswith("${nodes.server.fediversity.internal.garage.web.urlFor "pixelfed"}"): raise Exception("image does not come from garage") ''; } From 81ab439777b91f4d02b61ad29aa7f3bbf831bdf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 17:56:40 +0200 Subject: [PATCH 57/87] Move stuff from pixelfed-vm to pixelfed --- fediversity/pixelfed.nix | 21 +++++++++++++++++++++ vm/pixelfed-vm.nix | 12 +----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 1edc914..a44b8f1 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -38,9 +38,28 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { services.pixelfed = { enable = true; domain = config.fediversity.internal.pixelfed.domain; + + # TODO: secrets management!!! + secretFile = pkgs.writeText "secrets.env" '' + APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA + ''; + + ## Taeer feels like this way of configuring Nginx is odd; there should + ## instead be a `services.pixefed.nginx.enable` option and the actual Nginx + ## configuration should be in `services.nginx`. See eg. `pretix`. + ## + ## TODO: If that indeed makes sense, upstream. + nginx = { + # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/"; + }; }; services.pixelfed.settings = { + ## NOTE: This depends on the targets, eg. universities might want control + ## over who has an account. We probably want a universal + ## `fediversity.openRegistration` option. + OPEN_REGISTRATION = true; + # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; FILESYSTEM_CLOUD = "s3"; PF_ENABLE_CLOUD = true; @@ -62,4 +81,6 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { services.pixelfed.package = pkgs.pixelfed.overrideAttrs (old: { patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; }); + + networking.firewall.allowedTCPPorts = [ 80 ]; } diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index 8f97180..3320ddc 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -11,22 +11,12 @@ pixelfed.enable = true; }; - networking.firewall.allowedTCPPorts = [ 80 ]; services.pixelfed = { - # TODO: secrets management! - secretFile = pkgs.writeText "secrets.env" '' - APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA - ''; settings = { - OPEN_REGISTRATION = true; FORCE_HTTPS_URLS = false; }; - # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx - # TODO: If that indeed makes sense, upstream it. - nginx = { - # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/"; - }; }; + virtualisation.memorySize = 2048; virtualisation.forwardPorts = [ { From 351649c2dd900875da88ac374ef4307ca1e7e41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 18:25:21 +0200 Subject: [PATCH 58/87] [HACK] comment out virtualisation --- fediversity/garage.nix | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 84af662..2c3e006 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -132,19 +132,19 @@ in }; config = lib.mkIf config.fediversity.enable { - virtualisation.diskSize = 2048; - virtualisation.forwardPorts = [ - { - from = "host"; - host.port = config.fediversity.internal.garage.rpc.port; - guest.port = config.fediversity.internal.garage.rpc.port; - } - { - from = "host"; - host.port = config.fediversity.internal.garage.web.port; - guest.port = config.fediversity.internal.garage.web.port; - } - ]; + # virtualisation.diskSize = 2048; + # virtualisation.forwardPorts = [ + # { + # from = "host"; + # host.port = config.fediversity.internal.garage.rpc.port; + # guest.port = config.fediversity.internal.garage.rpc.port; + # } + # { + # from = "host"; + # host.port = config.fediversity.internal.garage.web.port; + # guest.port = config.fediversity.internal.garage.web.port; + # } + # ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; From c47256d62c0c2d5d60dacc058bc15cdc42bfeb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 18:35:22 +0200 Subject: [PATCH 59/87] Ignore errors of `garage key import` --- fediversity/garage.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 2c3e006..9822da7 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -52,7 +52,8 @@ let ${escapeShellArg bucket} --key ${escapeShellArg key} ''; ensureKeyScriptFn = key: {id, secret, ensureAccess}: '' - garage key import --yes -n ${escapeShellArg key} ${escapeShellArg id} ${escapeShellArg secret} + ## FIXME: Check whether the key exist and skip this step if that is the case. Get rid of this `|| :` + garage key import --yes -n ${escapeShellArg key} ${escapeShellArg id} ${escapeShellArg secret} || : ${concatMapAttrs (ensureAccessScriptFn key) ensureAccess} ''; ensureKeysScript = concatMapAttrs ensureKeyScriptFn cfg.ensureKeys; @@ -197,7 +198,8 @@ in # XXX: this is a hack because we want to write to the buckets here but we're not guaranteed any access keys # TODO: generate this key here rather than using a well-known key - garage key import --yes -n tmp ${snakeoil_key.id} ${snakeoil_key.secret} + # TODO: if the key already exists, we get an error; hacked with this `|| :` which needs to be removed + garage key import --yes -n tmp ${snakeoil_key.id} ${snakeoil_key.secret} || : export AWS_ACCESS_KEY_ID=${snakeoil_key.id}; export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}; From d1f58573d8adf0d30b2f85623b48b0cf04aa12ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 18:44:47 +0200 Subject: [PATCH 60/87] Also open HTTPS port --- fediversity/pixelfed.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index a44b8f1..da77fea 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -82,5 +82,5 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; }); - networking.firewall.allowedTCPPorts = [ 80 ]; + networking.firewall.allowedTCPPorts = [ 80 443 ]; } From a2c54ff172aa716253af191bd74591280d8078ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 18:51:21 +0200 Subject: [PATCH 61/87] ACME --- fediversity/default.nix | 10 ++++++++++ fediversity/pixelfed.nix | 2 ++ 2 files changed, 12 insertions(+) diff --git a/fediversity/default.nix b/fediversity/default.nix index 46ee05d..0fed04f 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -100,4 +100,14 @@ in { }; }; }; + + config = { + ## FIXME: This should clearly go somewhere else; and we should have a + ## `staging` vs. `production` setting somewhere. + security.acme = { + acceptTerms = true; + defaults.email = "nicolas.jeannerod+fediversity@moduscreate.com"; + defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + }; + }; } diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index da77fea..c9b48a0 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -50,6 +50,8 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { ## ## TODO: If that indeed makes sense, upstream. nginx = { + forceSSL = true; + enableACME = true; # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/"; }; }; From 900c92ac0111af9189ad30be59ee093d73287573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 20 Sep 2024 18:55:00 +0200 Subject: [PATCH 62/87] Exceptionally use non-staging LetsEncrypt servers --- fediversity/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 0fed04f..c8bc0f1 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -107,7 +107,7 @@ in { security.acme = { acceptTerms = true; defaults.email = "nicolas.jeannerod+fediversity@moduscreate.com"; - defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + # defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; }; }; } From a02a741ec4ae4bbe26cb7db108a61d1778214392 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 11:55:54 -0400 Subject: [PATCH 63/87] proxy garage web to port 80 --- fediversity/default.nix | 8 ++------ fediversity/garage.nix | 37 +++++++++++++++++++++++-------------- fediversity/mastodon.nix | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index c8bc0f1..768fb37 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -64,17 +64,13 @@ in { type = types.str; default = "web.garage.${config.fediversity.domain}"; }; - port = mkOption { + internalPort = mkOption { type = types.int; default = 3902; }; - rootDomainAndPort = mkOption { - type = types.str; - default = "${config.fediversity.internal.garage.web.rootDomain}:${toString config.fediversity.internal.garage.web.port}"; - }; urlFor = mkOption { type = types.functionTo types.str; - default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomainAndPort}"; + default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomain}"; }; }; }; diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 9822da7..811bf9a 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -5,6 +5,7 @@ let id = "GK22a15201acacbd51cd43e327"; secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e"; }; + cfg = config.fediversity.internal.garage; in # TODO: expand to a multi-machine setup @@ -42,7 +43,7 @@ let ${optionalString corsRules.enable '' garage bucket allow --read --write --owner ${bucketArg} --key tmp # TODO: endpoin-url should not be hard-coded - aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${config.fediversity.internal.garage.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${cfg.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} garage bucket deny --read --write --owner ${bucketArg} --key tmp ''} ''; @@ -137,21 +138,20 @@ in # virtualisation.forwardPorts = [ # { # from = "host"; - # host.port = config.fediversity.internal.garage.rpc.port; - # guest.port = config.fediversity.internal.garage.rpc.port; + # host.port = cfg.rpc.port; + # guest.port = cfg.rpc.port; # } # { # from = "host"; - # host.port = config.fediversity.internal.garage.web.port; - # guest.port = config.fediversity.internal.garage.web.port; + # host.port = cfg.web.port; + # guest.port = cfg.web.port; # } # ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ - config.fediversity.internal.garage.rpc.port - config.fediversity.internal.garage.web.port + cfg.rpc.port ]; services.garage = { enable = true; @@ -161,15 +161,24 @@ in # TODO: use a secret file rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; # TODO: why does this have to be set? is there not a sensible default? - rpc_bind_addr = "[::]:${toString config.fediversity.internal.garage.rpc.port}"; - rpc_public_addr = "[::1]:${toString config.fediversity.internal.garage.rpc.port}"; - s3_api.api_bind_addr = "[::]:${toString config.fediversity.internal.garage.api.port}"; - s3_web.bind_addr = "[::]:${toString config.fediversity.internal.garage.web.port}"; - s3_web.root_domain = ".${config.fediversity.internal.garage.web.rootDomain}"; + rpc_bind_addr = "[::]:${toString cfg.rpc.port}"; + rpc_public_addr = "[::1]:${toString cfg.rpc.port}"; + s3_api.api_bind_addr = "[::]:${toString cfg.api.port}"; + s3_web.bind_addr = "[::]:${toString cfg.web.port}"; + s3_web.root_domain = ".${cfg.web.rootDomain}"; index = "index.html"; s3_api.s3_region = "garage"; - s3_api.root_domain = ".${config.fediversity.internal.garage.api.domain}"; + s3_api.root_domain = ".${cfg.api.domain}"; + }; + }; + services.nginx.virtualHosts."garagePortProxy" = { + serverName = "${cfg.web.urlFor "*"}"; # wildcard bucket *.foo.com + locations."/" = { + proxyPass = "localhost:3902" + extraConfig = '' + proxy_set_header Host $host; + ''; }; }; systemd.services.ensure-garage = { @@ -184,7 +193,7 @@ in # Give Garage time to start up by waiting until somethings speaks HTTP # behind Garage's API URL. - until ${pkgs.curl}/bin/curl -sio /dev/null ${config.fediversity.internal.garage.api.url}; do sleep 1; done + until ${pkgs.curl}/bin/curl -sio /dev/null ${cfg.api.url}; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) diff --git a/fediversity/mastodon.nix b/fediversity/mastodon.nix index 62599b5..d19edca 100644 --- a/fediversity/mastodon.nix +++ b/fediversity/mastodon.nix @@ -46,7 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; S3_PROTOCOL = "http"; - S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomainAndPort; + S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomain; # by default it tries to use "/" S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ From 8d612a712d395ed86f801cfc437932770aceff9c Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 11:58:49 -0400 Subject: [PATCH 64/87] ; --- fediversity/garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 811bf9a..6a1e2d5 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -175,7 +175,7 @@ in services.nginx.virtualHosts."garagePortProxy" = { serverName = "${cfg.web.urlFor "*"}"; # wildcard bucket *.foo.com locations."/" = { - proxyPass = "localhost:3902" + proxyPass = "localhost:3902"; extraConfig = '' proxy_set_header Host $host; ''; From 581b64b77be14ff046a2a7cf09aa3b2838ca299f Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:09:16 -0400 Subject: [PATCH 65/87] had two 'cfg's. changed one to 'fedicfg' --- fediversity/garage.nix | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 6a1e2d5..bf5d827 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -5,7 +5,6 @@ let id = "GK22a15201acacbd51cd43e327"; secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e"; }; - cfg = config.fediversity.internal.garage; in # TODO: expand to a multi-machine setup @@ -16,6 +15,7 @@ let inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep; inherit (lib.strings) escapeShellArg; cfg = config.services.garage; + fedicfg = config.fediversity.internal.garage; concatMapAttrs = scriptFn: attrset: concatStringsSep "\n" (lib.mapAttrsToList scriptFn attrset); ensureBucketScriptFn = bucket: { website, aliases, corsRules }: let @@ -43,7 +43,7 @@ let ${optionalString corsRules.enable '' garage bucket allow --read --write --owner ${bucketArg} --key tmp # TODO: endpoin-url should not be hard-coded - aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${cfg.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} + aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${fedicfg.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} garage bucket deny --read --write --owner ${bucketArg} --key tmp ''} ''; @@ -138,20 +138,20 @@ in # virtualisation.forwardPorts = [ # { # from = "host"; - # host.port = cfg.rpc.port; - # guest.port = cfg.rpc.port; + # host.port = fedicfg.rpc.port; + # guest.port = fedicfg.rpc.port; # } # { # from = "host"; - # host.port = cfg.web.port; - # guest.port = cfg.web.port; + # host.port = fedicfg.web.port; + # guest.port = fedicfg.web.port; # } # ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ - cfg.rpc.port + fedicfg.rpc.port ]; services.garage = { enable = true; @@ -161,19 +161,19 @@ in # TODO: use a secret file rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; # TODO: why does this have to be set? is there not a sensible default? - rpc_bind_addr = "[::]:${toString cfg.rpc.port}"; - rpc_public_addr = "[::1]:${toString cfg.rpc.port}"; - s3_api.api_bind_addr = "[::]:${toString cfg.api.port}"; - s3_web.bind_addr = "[::]:${toString cfg.web.port}"; - s3_web.root_domain = ".${cfg.web.rootDomain}"; + rpc_bind_addr = "[::]:${toString fedicfg.rpc.port}"; + rpc_public_addr = "[::1]:${toString fedicfg.rpc.port}"; + s3_api.api_bind_addr = "[::]:${toString fedicfg.api.port}"; + s3_web.bind_addr = "[::]:${toString fedicfg.web.port}"; + s3_web.root_domain = ".${fedicfg.web.rootDomain}"; index = "index.html"; s3_api.s3_region = "garage"; - s3_api.root_domain = ".${cfg.api.domain}"; + s3_api.root_domain = ".${fedicfg.api.domain}"; }; }; services.nginx.virtualHosts."garagePortProxy" = { - serverName = "${cfg.web.urlFor "*"}"; # wildcard bucket *.foo.com + serverName = "${fedicfg.web.urlFor "*"}"; # wildcard bucket *.foo.com locations."/" = { proxyPass = "localhost:3902"; extraConfig = '' @@ -193,7 +193,7 @@ in # Give Garage time to start up by waiting until somethings speaks HTTP # behind Garage's API URL. - until ${pkgs.curl}/bin/curl -sio /dev/null ${cfg.api.url}; do sleep 1; done + until ${pkgs.curl}/bin/curl -sio /dev/null ${fedicfg.api.url}; do sleep 1; done # XXX: this is very sensitive to being a single instance # (doing the bare minimum to get garage up and running) From c15009f4902e62cb0b0efcd67e68696ab88f5727 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:11:04 -0400 Subject: [PATCH 66/87] mv {,internal}port --- fediversity/garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index bf5d827..be29e21 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -164,7 +164,7 @@ in rpc_bind_addr = "[::]:${toString fedicfg.rpc.port}"; rpc_public_addr = "[::1]:${toString fedicfg.rpc.port}"; s3_api.api_bind_addr = "[::]:${toString fedicfg.api.port}"; - s3_web.bind_addr = "[::]:${toString fedicfg.web.port}"; + s3_web.bind_addr = "[::]:${toString fedicfg.web.internalPort}"; s3_web.root_domain = ".${fedicfg.web.rootDomain}"; index = "index.html"; From 682f8d67764a56ede2451bcf8f147c80960449ba Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:14:40 -0400 Subject: [PATCH 67/87] remove http:// from nginx server name --- fediversity/garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index be29e21..b3d33a9 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -173,7 +173,7 @@ in }; }; services.nginx.virtualHosts."garagePortProxy" = { - serverName = "${fedicfg.web.urlFor "*"}"; # wildcard bucket *.foo.com + serverName = "*.${fedicfg.web.rootDomain}"; locations."/" = { proxyPass = "localhost:3902"; extraConfig = '' From a65c55f7a1ded57ee9eeb36db25a3c7b5f41489c Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:18:22 -0400 Subject: [PATCH 68/87] ADD http:// to proxypass --- fediversity/garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index b3d33a9..46fa6dc 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -175,7 +175,7 @@ in services.nginx.virtualHosts."garagePortProxy" = { serverName = "*.${fedicfg.web.rootDomain}"; locations."/" = { - proxyPass = "localhost:3902"; + proxyPass = "http://localhost:3902"; extraConfig = '' proxy_set_header Host $host; ''; From 2364e122a26f34ea34bccfa414331c4d39af87d2 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:22:40 -0400 Subject: [PATCH 69/87] httpS --- fediversity/garage.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index 46fa6dc..ea400df 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -173,6 +173,8 @@ in }; }; services.nginx.virtualHosts."garagePortProxy" = { + forceSSL = true; + enableACME = true; serverName = "*.${fedicfg.web.rootDomain}"; locations."/" = { proxyPass = "http://localhost:3902"; From 2a28e0289d3eec8c928fe073c3f1bedbc57b7c8e Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:39:15 -0400 Subject: [PATCH 70/87] acme fixup --- fediversity/garage.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index ea400df..e38acc8 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -172,10 +172,12 @@ in s3_api.root_domain = ".${fedicfg.api.domain}"; }; }; + services.nginx.virtualHosts."garagePortProxy" = { forceSSL = true; - enableACME = true; - serverName = "*.${fedicfg.web.rootDomain}"; + useACME = true; + serverName = fedicfg.web.rootDomain; + serverAliases = lib.mapAttrsToList (bucket: _: "${bucket}.${fedicfg.web.rootDomain}") cfg.ensureBuckets; ## TODO: use wildcard certificates? locations."/" = { proxyPass = "http://localhost:3902"; extraConfig = '' @@ -183,6 +185,7 @@ in ''; }; }; + systemd.services.ensure-garage = { after = [ "garage.service" ]; wantedBy = [ "garage.service" ]; From 72fa686540e9b3bae568b3fbd5b93bc1c9996be9 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 23 Sep 2024 12:39:55 -0400 Subject: [PATCH 71/87] acme fixup 2 --- fediversity/garage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index e38acc8..a627009 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -175,7 +175,7 @@ in services.nginx.virtualHosts."garagePortProxy" = { forceSSL = true; - useACME = true; + enableACME = true; serverName = fedicfg.web.rootDomain; serverAliases = lib.mapAttrsToList (bucket: _: "${bucket}.${fedicfg.web.rootDomain}") cfg.ensureBuckets; ## TODO: use wildcard certificates? locations."/" = { From 7f1e5b56d087fa842e2471ec2c5248ff44ac9120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:17:56 +0200 Subject: [PATCH 72/87] s/urlFor/urlForBucket --- fediversity/default.nix | 2 +- fediversity/peertube.nix | 6 +++--- fediversity/pixelfed.nix | 4 ++-- tests/pixelfed-garage.nix | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 768fb37..96a3d5b 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -68,7 +68,7 @@ in { type = types.int; default = 3902; }; - urlFor = mkOption { + urlForBucket = mkOption { type = types.functionTo types.str; default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomain}"; }; diff --git a/fediversity/peertube.nix b/fediversity/peertube.nix index 88d26e1..03e9e71 100644 --- a/fediversity/peertube.nix +++ b/fediversity/peertube.nix @@ -74,17 +74,17 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { web_videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = config.fediversity.internal.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; }; videos = rec { bucket_name = "peertube-videos"; prefix = ""; - base_url = config.fediversity.internal.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; }; streaming_playlists = rec { bucket_name = "peertube-playlists"; prefix = ""; - base_url = config.fediversity.internal.garage.web.urlFor bucket_name; + base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; }; }; }; diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index c9b48a0..894b99d 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -52,7 +52,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { nginx = { forceSSL = true; enableACME = true; - # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/"; + # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlForBucket "pixelfed"}/public/"; }; }; @@ -68,7 +68,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_DEFAULT_REGION = "garage"; - AWS_URL = config.fediversity.internal.garage.web.urlFor "pixelfed"; + AWS_URL = config.fediversity.internal.garage.web.urlForBucket "pixelfed"; AWS_BUCKET = "pixelfed"; AWS_ENDPOINT = config.fediversity.internal.garage.api.url; AWS_USE_PATH_STYLE_ENDPOINT = false; diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index b25bc66..f921c77 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -202,7 +202,7 @@ pkgs.nixosTest { with subtest("Check that image comes from garage"): src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") - if not src.startswith("${nodes.server.fediversity.internal.garage.web.urlFor "pixelfed"}"): + if not src.startswith("${nodes.server.fediversity.internal.garage.web.urlForBucket "pixelfed"}"): raise Exception("image does not come from garage") ''; } From 00384a594862b154d405b8bc17a464b720e085af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:23:29 +0200 Subject: [PATCH 73/87] domainForBucket --- fediversity/default.nix | 6 +++++- fediversity/garage.nix | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 96a3d5b..6a6b867 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -68,9 +68,13 @@ in { type = types.int; default = 3902; }; + domainForBucket = mkOption { + type = types.functionTo types.str; + default = bucket: "${bucket}.${config.fediversity.internal.garage.web.rootDomain}"; + }; urlForBucket = mkOption { type = types.functionTo types.str; - default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomain}"; + default = bucket: "http://${config.fediversity.internal.garage.web.domainForBucket bucket}"; }; }; }; diff --git a/fediversity/garage.nix b/fediversity/garage.nix index a627009..a3cbbeb 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -177,7 +177,7 @@ in forceSSL = true; enableACME = true; serverName = fedicfg.web.rootDomain; - serverAliases = lib.mapAttrsToList (bucket: _: "${bucket}.${fedicfg.web.rootDomain}") cfg.ensureBuckets; ## TODO: use wildcard certificates? + serverAliases = lib.mapAttrsToList (bucket: _: fedicfg.web.domainForBucket bucket) cfg.ensureBuckets; ## TODO: use wildcard certificates? locations."/" = { proxyPass = "http://localhost:3902"; extraConfig = '' From 08eac749ddeceef92ecc8fd104054ab625153c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:40:35 +0200 Subject: [PATCH 74/87] Move Garage VM stuff out of main file --- fediversity/garage.nix | 16 +--------------- flake.nix | 8 +++++--- tests/mastodon-garage.nix | 2 +- tests/pixelfed-garage.nix | 2 +- vm/garage-vm.nix | 25 +++++++++++++++++++++++++ 5 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 vm/garage-vm.nix diff --git a/fediversity/garage.nix b/fediversity/garage.nix index a3cbbeb..cc6187f 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -134,20 +134,6 @@ in }; config = lib.mkIf config.fediversity.enable { - # virtualisation.diskSize = 2048; - # virtualisation.forwardPorts = [ - # { - # from = "host"; - # host.port = fedicfg.rpc.port; - # guest.port = fedicfg.rpc.port; - # } - # { - # from = "host"; - # host.port = fedicfg.web.port; - # guest.port = fedicfg.web.port; - # } - # ]; - environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; networking.firewall.allowedTCPPorts = [ @@ -185,7 +171,7 @@ in ''; }; }; - + systemd.services.ensure-garage = { after = [ "garage.service" ]; wantedBy = [ "garage.service" ]; diff --git a/flake.nix b/flake.nix index 737ad31..5950b6b 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,7 @@ ## VM-specific modules interactive-vm = import ./vm/interactive-vm.nix; + garage-vm = import ./vm/garage-vm.nix; mastodon-vm = import ./vm/mastodon-vm.nix; peertube-vm = import ./vm/peertube-vm.nix; pixelfed-vm = import ./vm/pixelfed-vm.nix; @@ -25,17 +26,17 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm mastodon-vm ]; + modules = with self.nixosModules; [ fediversity interactive-vm garage-vm mastodon-vm ]; }; peertube = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm peertube-vm ]; + modules = with self.nixosModules; [ fediversity interactive-vm garage-vm peertube-vm ]; }; pixelfed = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm pixelfed-vm ]; + modules = with self.nixosModules; [ fediversity interactive-vm garage-vm pixelfed-vm ]; }; all = nixpkgs.lib.nixosSystem { @@ -43,6 +44,7 @@ modules = with self.nixosModules; [ fediversity interactive-vm + garage-vm peertube-vm pixelfed-vm mastodon-vm diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index 672b70f..ef53c6a 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -37,7 +37,7 @@ pkgs.nixosTest { nodes = { server = { config, ... }: { virtualisation.memorySize = lib.mkVMOverride 4096; - imports = with self.nixosModules; [ mastodon-vm ]; + imports = with self.nixosModules; [ garage-vm mastodon-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index f921c77..4137ede 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -136,7 +136,7 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ pixelfed-vm ]; + imports = with self.nixosModules; [ garage-vm pixelfed-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/vm/garage-vm.nix b/vm/garage-vm.nix new file mode 100644 index 0000000..cd9c81c --- /dev/null +++ b/vm/garage-vm.nix @@ -0,0 +1,25 @@ +{ config, modulesPath, ... }: + +let + fedicfg = config.fediversity.internal.garage; + +in { + imports = [ + ../fediversity + (modulesPath + "/virtualisation/qemu-vm.nix") + ]; + + virtualisation.diskSize = 2048; + virtualisation.forwardPorts = [ + { + from = "host"; + host.port = fedicfg.rpc.port; + guest.port = fedicfg.rpc.port; + } + { + from = "host"; + host.port = fedicfg.web.port; + guest.port = fedicfg.web.port; + } + ]; +} From af4f77a76731528e4d105ebcf8939b7e6f9721b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:42:18 +0200 Subject: [PATCH 75/87] s/port/internalPort --- vm/garage-vm.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/garage-vm.nix b/vm/garage-vm.nix index cd9c81c..31e3c41 100644 --- a/vm/garage-vm.nix +++ b/vm/garage-vm.nix @@ -18,8 +18,8 @@ in { } { from = "host"; - host.port = fedicfg.web.port; - guest.port = fedicfg.web.port; + host.port = fedicfg.web.internalPort; + guest.port = fedicfg.web.internalPort; } ]; } From 14de9dec556b38d3ec0a2cd695cb0902a4aacdb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:51:41 +0200 Subject: [PATCH 76/87] Remove SSL in VM --- vm/pixelfed-vm.nix | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index 3320ddc..3353648 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -1,5 +1,9 @@ -{ pkgs, modulesPath, ... }: { +{ pkgs, lib, modulesPath, ... }: +let + inherit (lib) mkVMOverride; + +in { imports = [ ../fediversity (modulesPath + "/virtualisation/qemu-vm.nix") @@ -15,6 +19,10 @@ settings = { FORCE_HTTPS_URLS = false; }; + nginx = { + forceSSL = mkVMOverride false; + enableACME = mkVMOverride false; + }; }; virtualisation.memorySize = 2048; From f160bc3f4c43a7a8bc0acf7e70e2f27ade548b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 14:55:20 +0200 Subject: [PATCH 77/87] Remove SSL in Garage VM --- fediversity/garage.nix | 3 +-- vm/garage-vm.nix | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/fediversity/garage.nix b/fediversity/garage.nix index cc6187f..0dd0d7f 100644 --- a/fediversity/garage.nix +++ b/fediversity/garage.nix @@ -159,10 +159,9 @@ in }; }; - services.nginx.virtualHosts."garagePortProxy" = { + services.nginx.virtualHosts.${fedicfg.web.rootDomain} = { forceSSL = true; enableACME = true; - serverName = fedicfg.web.rootDomain; serverAliases = lib.mapAttrsToList (bucket: _: fedicfg.web.domainForBucket bucket) cfg.ensureBuckets; ## TODO: use wildcard certificates? locations."/" = { proxyPass = "http://localhost:3902"; diff --git a/vm/garage-vm.nix b/vm/garage-vm.nix index 31e3c41..8deb49f 100644 --- a/vm/garage-vm.nix +++ b/vm/garage-vm.nix @@ -1,6 +1,8 @@ -{ config, modulesPath, ... }: +{ lib, config, modulesPath, ... }: let + inherit (lib) mkVMOverride; + fedicfg = config.fediversity.internal.garage; in { @@ -9,6 +11,11 @@ in { (modulesPath + "/virtualisation/qemu-vm.nix") ]; + services.nginx.virtualHosts.${fedicfg.web.rootDomain} = { + forceSSL = mkVMOverride false; + enableACME = mkVMOverride false; + }; + virtualisation.diskSize = 2048; virtualisation.forwardPorts = [ { From ed95912726a9947ee14495dc386eb4b684e42cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 16:42:53 +0200 Subject: [PATCH 78/87] Make Garage API domain be localhost --- fediversity/default.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index 6a6b867..e4c45cd 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -2,7 +2,7 @@ let inherit (builtins) toString; - inherit (lib) mkOption mkEnableOption; + inherit (lib) mkOption mkEnableOption mkForce; inherit (lib.types) types; in { @@ -109,5 +109,10 @@ in { defaults.email = "nicolas.jeannerod+fediversity@moduscreate.com"; # defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; }; + + ## NOTE: For a one-machine deployment, this removes the need to provide an + ## `s3.garage.` domain. However, this will quickly stop working once + ## we go to multi-machines deployment. + fediversity.internal.garage.api.domain = mkForce "localhost"; }; } From 4be54ae67b7ed8a7baab72b132b90b2143dc1eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 24 Sep 2024 16:59:37 +0200 Subject: [PATCH 79/87] Not localhost --- fediversity/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fediversity/default.nix b/fediversity/default.nix index e4c45cd..379b1b2 100644 --- a/fediversity/default.nix +++ b/fediversity/default.nix @@ -113,6 +113,6 @@ in { ## NOTE: For a one-machine deployment, this removes the need to provide an ## `s3.garage.` domain. However, this will quickly stop working once ## we go to multi-machines deployment. - fediversity.internal.garage.api.domain = mkForce "localhost"; + fediversity.internal.garage.api.domain = mkForce "s3.garage.localhost"; }; } From 473cc7dea062cb5bdb2151a73803e11098ae23f7 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 25 Sep 2024 00:40:53 -0400 Subject: [PATCH 80/87] take bleeding edge pixelfed --- fediversity/pixelfed.nix | 4 ---- flake.lock | 36 +++++++++++++++++++++++++++++++- flake.nix | 44 +++++++++++++++++++++++++++++++++++---- tests/mastodon-garage.nix | 2 +- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/fediversity/pixelfed.nix b/fediversity/pixelfed.nix index 894b99d..cef5d6d 100644 --- a/fediversity/pixelfed.nix +++ b/fediversity/pixelfed.nix @@ -80,9 +80,5 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { after = [ "ensure-garage.service" ]; }; - services.pixelfed.package = pkgs.pixelfed.overrideAttrs (old: { - patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; - }); - networking.firewall.allowedTCPPorts = [ 80 443 ]; } diff --git a/flake.lock b/flake.lock index 60b501f..d25c01c 100644 --- a/flake.lock +++ b/flake.lock @@ -16,9 +16,43 @@ "type": "github" } }, + "nixpkgs-latest": { + "locked": { + "lastModified": 1727220152, + "narHash": "sha256-6ezRTVBZT25lQkvaPrfJSxYLwqcbNWm6feD/vG1FO0o=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "24959f933187217890b206788a85bfa73ba75949", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "pixelfed": { + "flake": false, + "locked": { + "lastModified": 1719823820, + "narHash": "sha256-CKjqnxp7p2z/13zfp4HQ1OAmaoUtqBKS6HFm6TV8Jwg=", + "owner": "pixelfed", + "repo": "pixelfed", + "rev": "4c245cf429330d01fcb8ebeb9aa8c84a9574a645", + "type": "github" + }, + "original": { + "owner": "pixelfed", + "ref": "v0.12.3", + "repo": "pixelfed", + "type": "github" + } + }, "root": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "nixpkgs-latest": "nixpkgs-latest", + "pixelfed": "pixelfed" } } }, diff --git a/flake.nix b/flake.nix index 5950b6b..2a8a17b 100644 --- a/flake.nix +++ b/flake.nix @@ -3,15 +3,32 @@ inputs = { nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests"; + nixpkgs-latest.url = "github:nixos/nixpkgs"; + pixelfed = { + url = "github:pixelfed/pixelfed?ref=v0.12.3"; + flake = false; + }; }; - outputs = { self, nixpkgs }: + outputs = { self, nixpkgs, nixpkgs-latest, pixelfed }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; + pkgsLatest = nixpkgs-latest.legacyPackages.${system}; + bleedingFediverseOverlay = (self: super: { + services.pixelfed.package = pkgsLatest.pixelfed.overrideAttrs (old: { + src = pixelfed; + patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; + }); + ## TODO: give mastodon, peertube the same treatment + }); in { nixosModules = { + ## Bleeding-edge fediverse packages + bleedingFediverse = { + nixpkgs.overlays = [ bleedingFediverseOverlay ]; + }; ## Fediversity modules fediversity = import ./fediversity; @@ -26,22 +43,41 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm garage-vm mastodon-vm ]; + modules = with self.nixosModules; [ + bleedingFediverse + fediversity + interactive-vm + garage-vm + mastodon-vm + ]; }; peertube = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm garage-vm peertube-vm ]; + modules = with self.nixosModules; [ + bleedingFediverse + fediversity + interactive-vm + garage-vm + peertube-vm + ]; }; pixelfed = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ fediversity interactive-vm garage-vm pixelfed-vm ]; + modules = with self.nixosModules; [ + bleedingFediverse + fediversity + interactive-vm + garage-vm + pixelfed-vm + ]; }; all = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + bleedingFediverse fediversity interactive-vm garage-vm diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index ef53c6a..c02fe7d 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -37,7 +37,7 @@ pkgs.nixosTest { nodes = { server = { config, ... }: { virtualisation.memorySize = lib.mkVMOverride 4096; - imports = with self.nixosModules; [ garage-vm mastodon-vm ]; + imports = with self.nixosModules; [ bleedingFediverse garage-vm mastodon-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 From 1f4b4d62e0fe041a4cf3529020d602106043e0df Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Wed, 25 Sep 2024 11:25:21 -0400 Subject: [PATCH 81/87] fix the overlay --- flake.nix | 4 ++-- tests/pixelfed-garage.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 2a8a17b..08d1cdc 100644 --- a/flake.nix +++ b/flake.nix @@ -16,9 +16,9 @@ pkgs = nixpkgs.legacyPackages.${system}; pkgsLatest = nixpkgs-latest.legacyPackages.${system}; bleedingFediverseOverlay = (self: super: { - services.pixelfed.package = pkgsLatest.pixelfed.overrideAttrs (old: { + pixelfed = pkgsLatest.pixelfed.overrideAttrs (old: { src = pixelfed; - patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ]; + patches = (old.patches or [ ]) ++ [ ./fediversity/pixelfed-group-permissions.patch ]; }); ## TODO: give mastodon, peertube the same treatment }); diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 4137ede..43a26d5 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -136,7 +136,7 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ garage-vm pixelfed-vm ]; + imports = with self.nixosModules; [ bleedingFediverse garage-vm pixelfed-vm ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 From 401bf59e443906f6ab60dbdcf84e5e8eb03c2098 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 26 Sep 2024 01:41:06 -0400 Subject: [PATCH 82/87] fix frivolous errors in garage test --- tests/pixelfed-garage.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 43a26d5..92ef721 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -152,6 +152,8 @@ pkgs.nixosTest { POST_MEDIA = ./fediversity.png; AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; + ## without this we get frivolous errors in the logs + MC_REGION = "garage"; }; # chrome does not like being run as root users.users.selenium = { From ff75f07f6637fccf4405d1f178cac3e1be5405b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 27 Sep 2024 11:48:49 +0200 Subject: [PATCH 83/87] We are way past that! --- flake.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/flake.nix b/flake.nix index 08d1cdc..5354280 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,4 @@ { - description = "Testing mastodon configurations"; - inputs = { nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests"; nixpkgs-latest.url = "github:nixos/nixpkgs"; From 8412a3f1fe26c916feab6f613740eb9199979f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 1 Oct 2024 10:02:01 +0200 Subject: [PATCH 84/87] Create automatic installation ISOs (#26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Taeer Bar-Yam Co-authored-by: Valentin Gagarin Reviewed-on: https://git.fediversity.eu/Fediversity/simple-nixos-fediverse/pulls/26 Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- README.md | 20 +++++++++++++++++ deploy.nix | 13 ++++++++++++ disk-layout.nix | 36 +++++++++++++++++++++++++++++++ flake.lock | 53 ++++++++++++++++++++++++++++++++++++++-------- flake.nix | 27 +++++++++++++++++++++-- installer.nix | 37 ++++++++++++++++++++++++++++++++ vm/garage-vm.nix | 5 +---- vm/mastodon-vm.nix | 5 +---- vm/peertube-vm.nix | 5 +---- vm/pixelfed-vm.nix | 5 +---- 10 files changed, 179 insertions(+), 27 deletions(-) create mode 100644 deploy.nix create mode 100644 disk-layout.nix create mode 100644 installer.nix diff --git a/README.md b/README.md index 73c3885..66114e7 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,26 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti ```bash pixelfed-manage user:create --name=test --username=test --email=test@test.com --password=testtest --confirm_email=1 ``` +# Building an installer image + +Build an installer image for the desired configuration, e.g. for `peertube`: + +```bash +nix build .#installers.peertube +``` + +Upload the image in `./result` to Proxmox when creating a VM. +Booting the image will format the disk and install NixOS with the desired configuration. + +# Deploying an updated machine configuration + +> TODO: There is currently no way to specify an actual target machine by name. + +Assuming you have SSH configuration with access to the remote `root` user stored for a machine called e.g. `peertube`, deploy the configuration by the same name: + +```bash +nix run .#deploy.peertube +``` ## debugging notes diff --git a/deploy.nix b/deploy.nix new file mode 100644 index 0000000..5604488 --- /dev/null +++ b/deploy.nix @@ -0,0 +1,13 @@ +{ writeShellApplication }: +name: config: +writeShellApplication { + name = "deploy"; + text = '' + result="$(nix build --print-out-paths ${./.}#nixosConfigurations#${name} --eval-store auto --store ssh-ng://${name})" + # shellcheck disable=SC2087 + ssh ${name} << EOF + nix-env -p /nix/var/nix/profiles/system --set "$result" + "$result"/bin/switch-to-configuration switch + EOF + ''; +} diff --git a/disk-layout.nix b/disk-layout.nix new file mode 100644 index 0000000..13f1a20 --- /dev/null +++ b/disk-layout.nix @@ -0,0 +1,36 @@ +{ ... }: +{ + disko.devices.disk.main = { + device = "/dev/sda"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + MBR = { + priority = 0; + size = "1M"; + type = "EF02"; + }; + ESP = { + priority = 1; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + root = { + priority = 2; + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index d25c01c..5c10c66 100644 --- a/flake.lock +++ b/flake.lock @@ -1,17 +1,35 @@ { "nodes": { - "nixpkgs": { + "disko": { + "inputs": { + "nixpkgs": "nixpkgs" + }, "locked": { - "lastModified": 1723726852, - "narHash": "sha256-lRzlx4fPRtzA+dgz9Rh4WK5yAW3TsAXx335DQqxY2XY=", - "owner": "radvendii", - "repo": "nixpkgs", - "rev": "9286249a1673cf5b14a4793e22dd44b70cb69a0d", + "lastModified": 1727347829, + "narHash": "sha256-y7cW6TjJKy+tu7efxeWI6lyg4VVx/9whx+OmrhmRShU=", + "owner": "nix-community", + "repo": "disko", + "rev": "1879e48907c14a70302ff5d0539c3b9b6f97feaa", "type": "github" }, "original": { - "owner": "radvendii", - "ref": "nixos_rebuild_tests", + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1725194671, + "narHash": "sha256-tLGCFEFTB5TaOKkpfw3iYT9dnk4awTP/q4w+ROpMfuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b833ff01a0d694b910daca6e2ff4a3f26dee478c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } @@ -31,6 +49,22 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1723726852, + "narHash": "sha256-lRzlx4fPRtzA+dgz9Rh4WK5yAW3TsAXx335DQqxY2XY=", + "owner": "radvendii", + "repo": "nixpkgs", + "rev": "9286249a1673cf5b14a4793e22dd44b70cb69a0d", + "type": "github" + }, + "original": { + "owner": "radvendii", + "ref": "nixos_rebuild_tests", + "repo": "nixpkgs", + "type": "github" + } + }, "pixelfed": { "flake": false, "locked": { @@ -50,7 +84,8 @@ }, "root": { "inputs": { - "nixpkgs": "nixpkgs", + "disko": "disko", + "nixpkgs": "nixpkgs_2", "nixpkgs-latest": "nixpkgs-latest", "pixelfed": "pixelfed" } diff --git a/flake.nix b/flake.nix index 5354280..c3bdef7 100644 --- a/flake.nix +++ b/flake.nix @@ -6,11 +6,13 @@ url = "github:pixelfed/pixelfed?ref=v0.12.3"; flake = false; }; + disko.url = "github:nix-community/disko"; }; - outputs = { self, nixpkgs, nixpkgs-latest, pixelfed }: + outputs = { self, nixpkgs, nixpkgs-latest, pixelfed, disko }: let system = "x86_64-linux"; + lib = nixpkgs.lib; pkgs = nixpkgs.legacyPackages.${system}; pkgsLatest = nixpkgs-latest.legacyPackages.${system}; bleedingFediverseOverlay = (self: super: { @@ -21,7 +23,6 @@ ## TODO: give mastodon, peertube the same treatment }); in { - nixosModules = { ## Bleeding-edge fediverse packages bleedingFediverse = { @@ -36,12 +37,16 @@ mastodon-vm = import ./vm/mastodon-vm.nix; peertube-vm = import ./vm/peertube-vm.nix; pixelfed-vm = import ./vm/pixelfed-vm.nix; + + disk-layout = import ./disk-layout.nix; }; nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + disko.nixosModules.default + disk-layout bleedingFediverse fediversity interactive-vm @@ -53,6 +58,8 @@ peertube = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + disko.nixosModules.default + disk-layout bleedingFediverse fediversity interactive-vm @@ -64,6 +71,8 @@ pixelfed = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + disko.nixosModules.default + disk-layout bleedingFediverse fediversity interactive-vm @@ -75,6 +84,8 @@ all = nixpkgs.lib.nixosSystem { inherit system; modules = with self.nixosModules; [ + disko.nixosModules.default + disk-layout bleedingFediverse fediversity interactive-vm @@ -86,6 +97,18 @@ }; }; + installers = + let + installer = (import ./installer.nix) nixpkgs; + in + lib.mapAttrs (_: config: installer config) self.nixosConfigurations; + + deploy = + let + deployCommand = (pkgs.callPackage ./deploy.nix { }); + in + lib.mapAttrs (name: config: deployCommand name config) self.nixosConfigurations; + checks.${system} = { mastodon-garage = import ./tests/mastodon-garage.nix { inherit pkgs self; }; pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit pkgs self; }; diff --git a/installer.nix b/installer.nix new file mode 100644 index 0000000..cbd2ba5 --- /dev/null +++ b/installer.nix @@ -0,0 +1,37 @@ +/** + Convert a NixOS configuration to one for a minimal installer ISO + + WARNING: Running this installer will format the target disk! +*/ +nixpkgs: machine: + let + installer = { config, pkgs, lib, ... }: + let + bootstrap = pkgs.writeShellApplication { + name = "bootstrap"; + runtimeInputs = with pkgs; [ nixos-install-tools ]; + text = '' + ${machine.config.system.build.diskoScript} + nixos-install --no-root-password --no-channel-copy --system ${machine.config.system.build.toplevel} + ''; + }; + in + { + imports = [ + "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" + ]; + nixpkgs.hostPlatform = "x86_64-linux"; + services.getty.autologinUser = lib.mkForce "root"; + programs.bash.loginShellInit = '' + ${nixpkgs.lib.getExe bootstrap} + ''; + + isoImage = { + compressImage = false; + squashfsCompression = "gzip -Xcompression-level 1"; + isoName = lib.mkForce "installer.iso"; + }; + }; + in + (nixpkgs.lib.nixosSystem { modules = [installer];}).config.system.build.isoImage + diff --git a/vm/garage-vm.nix b/vm/garage-vm.nix index 8deb49f..a8f78f3 100644 --- a/vm/garage-vm.nix +++ b/vm/garage-vm.nix @@ -6,10 +6,7 @@ let fedicfg = config.fediversity.internal.garage; in { - imports = [ - ../fediversity - (modulesPath + "/virtualisation/qemu-vm.nix") - ]; + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; services.nginx.virtualHosts.${fedicfg.web.rootDomain} = { forceSSL = mkVMOverride false; diff --git a/vm/mastodon-vm.nix b/vm/mastodon-vm.nix index ea17f27..5a9734b 100644 --- a/vm/mastodon-vm.nix +++ b/vm/mastodon-vm.nix @@ -1,9 +1,6 @@ { modulesPath, lib, config, ... }: { - imports = [ - ../fediversity - (modulesPath + "/virtualisation/qemu-vm.nix") - ]; + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; config = lib.mkMerge [ { diff --git a/vm/peertube-vm.nix b/vm/peertube-vm.nix index 5f40f4f..58c4667 100644 --- a/vm/peertube-vm.nix +++ b/vm/peertube-vm.nix @@ -1,9 +1,6 @@ { pkgs, modulesPath, ... }: { - imports = [ - ../fediversity - (modulesPath + "/virtualisation/qemu-vm.nix") - ]; + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; services.peertube = { enableWebHttps = false; diff --git a/vm/pixelfed-vm.nix b/vm/pixelfed-vm.nix index 3353648..76fbb59 100644 --- a/vm/pixelfed-vm.nix +++ b/vm/pixelfed-vm.nix @@ -4,10 +4,7 @@ let inherit (lib) mkVMOverride; in { - imports = [ - ../fediversity - (modulesPath + "/virtualisation/qemu-vm.nix") - ]; + imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; fediversity = { enable = true; From 656b31e729641c4c62bebc7ab1c9bb6c9bf5a1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 1 Oct 2024 09:40:38 +0000 Subject: [PATCH 85/87] Add missing module in tests --- tests/mastodon-garage.nix | 7 ++++++- tests/pixelfed-garage.nix | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/mastodon-garage.nix b/tests/mastodon-garage.nix index c02fe7d..2a7d177 100644 --- a/tests/mastodon-garage.nix +++ b/tests/mastodon-garage.nix @@ -37,7 +37,12 @@ pkgs.nixosTest { nodes = { server = { config, ... }: { virtualisation.memorySize = lib.mkVMOverride 4096; - imports = with self.nixosModules; [ bleedingFediverse garage-vm mastodon-vm ]; + imports = with self.nixosModules; [ + bleedingFediverse + fediversity + garage-vm + mastodon-vm + ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 diff --git a/tests/pixelfed-garage.nix b/tests/pixelfed-garage.nix index 92ef721..b301dd3 100644 --- a/tests/pixelfed-garage.nix +++ b/tests/pixelfed-garage.nix @@ -136,7 +136,12 @@ pkgs.nixosTest { memorySize = lib.mkVMOverride 8192; cores = 8; }; - imports = with self.nixosModules; [ bleedingFediverse garage-vm pixelfed-vm ]; + imports = with self.nixosModules; [ + bleedingFediverse + fediversity + garage-vm + pixelfed-vm + ]; # TODO: pair down environment.systemPackages = with pkgs; [ python3 From 108949d3e18c68ed673a9228d3d8f1e12401e81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 1 Oct 2024 13:14:56 +0200 Subject: [PATCH 86/87] Expose `mkInstaller` --- flake.nix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index c3bdef7..dc4c149 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,7 @@ nixosConfigurations = { mastodon = nixpkgs.lib.nixosSystem { inherit system; - modules = with self.nixosModules; [ + modules = with self.nixosModules; [ disko.nixosModules.default disk-layout bleedingFediverse @@ -97,11 +97,9 @@ }; }; - installers = - let - installer = (import ./installer.nix) nixpkgs; - in - lib.mapAttrs (_: config: installer config) self.nixosConfigurations; + ## Fully-feature ISO installer + mkInstaller = import ./installer.nix; + installers = lib.mapAttrs (_: config: self.mkInstaller nixpkgs config) self.nixosConfigurations; deploy = let From a13b1e9372412d03f7b629694ecef5242e6e568d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 1 Oct 2024 13:29:06 +0200 Subject: [PATCH 87/87] Faster compression and note on `isoName` --- installer.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/installer.nix b/installer.nix index cbd2ba5..ba59526 100644 --- a/installer.nix +++ b/installer.nix @@ -28,8 +28,10 @@ nixpkgs: machine: isoImage = { compressImage = false; - squashfsCompression = "gzip -Xcompression-level 1"; + squashfsCompression = "lz4"; isoName = lib.mkForce "installer.iso"; + ## ^^ FIXME: Use a more interesting name or keep the default name and + ## use `isoImage.isoName` in the tests. }; }; in