Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

20 changed files with 510 additions and 1132 deletions

1
.gitignore vendored
View file

@ -6,4 +6,3 @@ result*
output output
todo todo
/.pre-commit-config.yaml

View file

@ -46,26 +46,6 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti
```bash ```bash
pixelfed-manage user:create --name=test --username=test --email=test@test.com --password=testtest --confirm_email=1 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 ## debugging notes

View file

@ -1,13 +0,0 @@
{ 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
'';
}

View file

@ -1,36 +0,0 @@
{ ... }:
{
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 = "/";
};
};
};
};
};
}

View file

@ -2,11 +2,10 @@
let let
inherit (builtins) toString; inherit (builtins) toString;
inherit (lib) mkOption mkEnableOption mkForce; inherit (lib) mkOption mkEnableOption;
inherit (lib.types) types; inherit (lib.types) types;
in in {
{
imports = [ imports = [
./garage.nix ./garage.nix
./mastodon.nix ./mastodon.nix
@ -32,27 +31,9 @@ in
pixelfed.enable = mkEnableOption "default Fediversity Pixelfed configuration"; pixelfed.enable = mkEnableOption "default Fediversity Pixelfed configuration";
peertube.enable = mkEnableOption "default Fediversity PeerTube configuration"; peertube.enable = mkEnableOption "default Fediversity PeerTube configuration";
temp = mkOption {
description = "options that are only used while developing; should be removed eventually";
default = { };
type = types.submodule {
options = {
cores = mkOption {
description = "number of cores; should be obtained from NixOps4";
type = types.int;
};
peertubeSecretsFile = mkOption {
description = "should it be provided by NixOps4? or maybe we should just ask for a main secret from which to derive all the others?";
type = types.path;
};
};
};
};
internal = mkOption { internal = mkOption {
description = "options that are only meant to be used internally; change at your own risk"; description = "options that are only meant to be used internally; change at your own risk";
default = { }; default = {};
type = types.submodule { type = types.submodule {
options = { options = {
garage = { garage = {
@ -83,17 +64,17 @@ in
type = types.str; type = types.str;
default = "web.garage.${config.fediversity.domain}"; default = "web.garage.${config.fediversity.domain}";
}; };
internalPort = mkOption { port = mkOption {
type = types.int; type = types.int;
default = 3902; default = 3902;
}; };
domainForBucket = mkOption { rootDomainAndPort = mkOption {
type = types.functionTo types.str; type = types.str;
default = bucket: "${bucket}.${config.fediversity.internal.garage.web.rootDomain}"; default = "${config.fediversity.internal.garage.web.rootDomain}:${toString config.fediversity.internal.garage.web.port}";
}; };
urlForBucket = mkOption { urlFor = mkOption {
type = types.functionTo types.str; type = types.functionTo types.str;
default = bucket: "http://${config.fediversity.internal.garage.web.domainForBucket bucket}"; default = bucket: "http://${bucket}.${config.fediversity.internal.garage.web.rootDomainAndPort}";
}; };
}; };
}; };
@ -108,7 +89,7 @@ in
}; };
mastodon.domain = mkOption { mastodon.domain = mkOption {
type = types.str; type = types.str;
default = "mastodon.${config.fediversity.domain}"; default = "mastdodon.${config.fediversity.domain}";
}; };
peertube.domain = mkOption { peertube.domain = mkOption {
type = types.str; type = types.str;
@ -119,19 +100,4 @@ 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";
};
## NOTE: For a one-machine deployment, this removes the need to provide an
## `s3.garage.<domain>` domain. However, this will quickly stop working once
## we go to multi-machines deployment.
fediversity.internal.garage.api.domain = mkForce "s3.garage.localhost";
};
} }

View file

@ -8,49 +8,25 @@ let
in in
# TODO: expand to a multi-machine setup # TODO: expand to a multi-machine setup
{ { config, lib, pkgs, ... }:
config,
lib,
pkgs,
...
}:
let let
inherit (builtins) toString; inherit (builtins) toString;
inherit (lib) inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep;
types
mkOption
mkEnableOption
optionalString
concatStringsSep
;
inherit (lib.strings) escapeShellArg; inherit (lib.strings) escapeShellArg;
inherit (lib.attrsets) filterAttrs mapAttrs';
cfg = config.services.garage; cfg = config.services.garage;
fedicfg = config.fediversity.internal.garage;
concatMapAttrs = scriptFn: attrset: concatStringsSep "\n" (lib.mapAttrsToList scriptFn attrset); concatMapAttrs = scriptFn: attrset: concatStringsSep "\n" (lib.mapAttrsToList scriptFn attrset);
ensureBucketScriptFn = ensureBucketScriptFn = bucket: { website, aliases, corsRules }:
bucket:
{
website,
aliases,
corsRules,
}:
let let
bucketArg = escapeShellArg bucket; bucketArg = escapeShellArg bucket;
corsRulesJSON = escapeShellArg ( corsRulesJSON = escapeShellArg (builtins.toJSON {
builtins.toJSON { CORSRules = [{
CORSRules = [
{
AllowedHeaders = corsRules.allowedHeaders; AllowedHeaders = corsRules.allowedHeaders;
AllowedMethods = corsRules.allowedMethods; AllowedMethods = corsRules.allowedMethods;
AllowedOrigins = corsRules.allowedOrigins; AllowedOrigins = corsRules.allowedOrigins;
} }];
]; });
} in ''
);
in
''
# garage bucket info tells us if the bucket already exists # garage bucket info tells us if the bucket already exists
garage bucket info ${bucketArg} || garage bucket create ${bucketArg} garage bucket info ${bucketArg} || garage bucket create ${bucketArg}
@ -59,41 +35,24 @@ let
garage bucket website --allow ${bucketArg} garage bucket website --allow ${bucketArg}
''} ''}
${concatStringsSep "\n" ( ${concatStringsSep "\n" (map (alias: ''
map (alias: ''
garage bucket alias ${bucketArg} ${escapeShellArg alias} garage bucket alias ${bucketArg} ${escapeShellArg alias}
'') aliases '') aliases)}
)}
${optionalString corsRules.enable '' ${optionalString corsRules.enable ''
garage bucket allow --read --write --owner ${bucketArg} --key tmp garage bucket allow --read --write --owner ${bucketArg} --key tmp
# TODO: endpoin-url should not be hard-coded # TODO: endpoin-url should not be hard-coded
aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${fedicfg.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 garage bucket deny --read --write --owner ${bucketArg} --key tmp
''} ''}
''; '';
ensureBucketsScript = concatMapAttrs ensureBucketScriptFn cfg.ensureBuckets; ensureBucketsScript = concatMapAttrs ensureBucketScriptFn cfg.ensureBuckets;
ensureAccessScriptFn = ensureAccessScriptFn = key: bucket: { read, write, owner }: ''
key: bucket:
{
read,
write,
owner,
}:
''
garage bucket allow ${optionalString read "--read"} ${optionalString write "--write"} ${optionalString owner "--owner"} \ garage bucket allow ${optionalString read "--read"} ${optionalString write "--write"} ${optionalString owner "--owner"} \
${escapeShellArg bucket} --key ${escapeShellArg key} ${escapeShellArg bucket} --key ${escapeShellArg key}
''; '';
ensureKeyScriptFn = ensureKeyScriptFn = key: {id, secret, ensureAccess}: ''
key: garage key import --yes -n ${escapeShellArg key} ${escapeShellArg id} ${escapeShellArg secret}
{
id,
secret,
ensureAccess,
}:
''
## 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} ${concatMapAttrs (ensureAccessScriptFn key) ensureAccess}
''; '';
ensureKeysScript = concatMapAttrs ensureKeyScriptFn cfg.ensureKeys; ensureKeysScript = concatMapAttrs ensureKeyScriptFn cfg.ensureKeys;
@ -104,8 +63,7 @@ in
options = { options = {
services.garage = { services.garage = {
ensureBuckets = mkOption { ensureBuckets = mkOption {
type = types.attrsOf ( type = types.attrsOf (types.submodule {
types.submodule {
options = { options = {
website = mkOption { website = mkOption {
type = types.bool; type = types.bool;
@ -116,29 +74,27 @@ in
enable = mkEnableOption "CORS Rules"; enable = mkEnableOption "CORS Rules";
allowedHeaders = mkOption { allowedHeaders = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [];
}; };
allowedMethods = mkOption { allowedMethods = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [];
}; };
allowedOrigins = mkOption { allowedOrigins = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [];
}; };
}; };
aliases = mkOption { aliases = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [];
}; };
}; };
} });
); default = {};
default = { };
}; };
ensureKeys = mkOption { ensureKeys = mkOption {
type = types.attrsOf ( type = types.attrsOf (types.submodule {
types.submodule {
# TODO: these should be managed as secrets, not in the nix store # TODO: these should be managed as secrets, not in the nix store
options = { options = {
id = mkOption { id = mkOption {
@ -150,8 +106,7 @@ in
# TODO: assert at least one of these is true # TODO: assert at least one of these is true
# NOTE: this currently needs to be done at the top level module # NOTE: this currently needs to be done at the top level module
ensureAccess = mkOption { ensureAccess = mkOption {
type = types.attrsOf ( type = types.attrsOf (types.submodule {
types.submodule {
options = { options = {
read = mkOption { read = mkOption {
type = types.bool; type = types.bool;
@ -166,26 +121,36 @@ in
default = false; default = false;
}; };
}; };
} });
); default = [];
default = [ ];
}; };
}; };
} });
); default = {};
default = { };
}; };
}; };
}; };
config = lib.mkIf config.fediversity.enable { config = lib.mkIf config.fediversity.enable {
environment.systemPackages = [ virtualisation.diskSize = 2048;
pkgs.minio-client virtualisation.forwardPorts = [
pkgs.awscli {
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 ];
networking.firewall.allowedTCPPorts = [ networking.firewall.allowedTCPPorts = [
fedicfg.rpc.port config.fediversity.internal.garage.rpc.port
config.fediversity.internal.garage.web.port
]; ];
services.garage = { services.garage = {
enable = true; enable = true;
@ -195,59 +160,30 @@ in
# TODO: use a secret file # TODO: use a secret file
rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625";
# TODO: why does this have to be set? is there not a sensible default? # TODO: why does this have to be set? is there not a sensible default?
rpc_bind_addr = "[::]:${toString fedicfg.rpc.port}"; rpc_bind_addr = "[::]:${toString config.fediversity.internal.garage.rpc.port}";
rpc_public_addr = "[::1]:${toString fedicfg.rpc.port}"; rpc_public_addr = "[::1]:${toString config.fediversity.internal.garage.rpc.port}";
s3_api.api_bind_addr = "[::]:${toString fedicfg.api.port}"; s3_api.api_bind_addr = "[::]:${toString config.fediversity.internal.garage.api.port}";
s3_web.bind_addr = "[::]:${toString fedicfg.web.internalPort}"; s3_web.bind_addr = "[::]:${toString config.fediversity.internal.garage.web.port}";
s3_web.root_domain = ".${fedicfg.web.rootDomain}"; s3_web.root_domain = ".${config.fediversity.internal.garage.web.rootDomain}";
index = "index.html"; index = "index.html";
s3_api.s3_region = "garage"; s3_api.s3_region = "garage";
s3_api.root_domain = ".${fedicfg.api.domain}"; s3_api.root_domain = ".${config.fediversity.internal.garage.api.domain}";
}; };
}; };
## Create a proxy from <bucket>.web.garage.<domain> to localhost:3902 for
## each bucket that has `website = true`.
services.nginx.virtualHosts =
let
value = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:3902";
extraConfig = ''
## copied from https://garagehq.deuxfleurs.fr/documentation/cookbook/reverse-proxy/
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Disable buffering to a temporary file.
proxy_max_temp_file_size 0;
'';
};
};
in
mapAttrs' (bucket: _: {
name = fedicfg.web.domainForBucket bucket;
inherit value;
}) (filterAttrs (_: { website, ... }: website) cfg.ensureBuckets);
systemd.services.ensure-garage = { systemd.services.ensure-garage = {
after = [ "garage.service" ]; after = [ "garage.service" ];
wantedBy = [ "garage.service" ]; wantedBy = [ "garage.service" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
}; };
path = [ path = [ cfg.package pkgs.perl pkgs.awscli ];
cfg.package
pkgs.perl
pkgs.awscli
];
script = '' script = ''
set -xeuo pipefail set -xeuo pipefail
# Give Garage time to start up by waiting until somethings speaks HTTP # Give Garage time to start up by waiting until somethings speaks HTTP
# behind Garage's API URL. # behind Garage's API URL.
until ${pkgs.curl}/bin/curl -sio /dev/null ${fedicfg.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 # XXX: this is very sensitive to being a single instance
# (doing the bare minimum to get garage up and running) # (doing the bare minimum to get garage up and running)
@ -261,8 +197,7 @@ in
# XXX: this is a hack because we want to write to the buckets here but we're not guaranteed any access keys # 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 # TODO: generate this key here rather than using a well-known key
# 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}
garage key import --yes -n tmp ${snakeoil_key.id} ${snakeoil_key.secret} || :
export AWS_ACCESS_KEY_ID=${snakeoil_key.id}; export AWS_ACCESS_KEY_ID=${snakeoil_key.id};
export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}; export AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret};

View file

@ -5,11 +5,7 @@ let
}; };
in in
{ { config, lib, pkgs, ... }:
config,
lib,
...
}:
lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) { lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
#### garage setup #### garage setup
@ -50,7 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_ACCESS_KEY_ID = snakeoil_key.id;
AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret;
S3_PROTOCOL = "http"; S3_PROTOCOL = "http";
S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomain; S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomainAndPort;
# by default it tries to use "<S3_HOSTNAME>/<S3_BUCKET>" # by default it tries to use "<S3_HOSTNAME>/<S3_BUCKET>"
S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}";
# SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/
@ -61,11 +57,8 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
#### mastodon setup #### mastodon setup
# open up access to the mastodon web interface. 80 is necessary if only for ACME # open up access to the mastodon web interface
networking.firewall.allowedTCPPorts = [ networking.firewall.allowedTCPPorts = [ 443 ];
80
443
];
services.mastodon = { services.mastodon = {
enable = true; enable = true;
@ -73,10 +66,6 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
localDomain = config.fediversity.internal.mastodon.domain; localDomain = config.fediversity.internal.mastodon.domain;
configureNginx = true; configureNginx = true;
# 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.fediversity.temp.cores - 1);
# TODO: configure a mailserver so this works # TODO: configure a mailserver so this works
smtp = { smtp = {
fromAddress = "noreply@${config.fediversity.internal.mastodon.domain}"; fromAddress = "noreply@${config.fediversity.internal.mastodon.domain}";

View file

@ -5,17 +5,10 @@ let
}; };
in in
{ { config, lib, pkgs, ... }:
config,
lib,
...
}:
lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
networking.firewall.allowedTCPPorts = [ networking.firewall.allowedTCPPorts = [ 80 9000 ];
80
443
];
services.garage = { services.garage = {
ensureBuckets = { ensureBuckets = {
@ -66,8 +59,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
# TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to. # TODO: in most of nixpkgs, these are true by default. upstream that unless there's a good reason not to.
redis.createLocally = true; redis.createLocally = true;
database.createLocally = true; database.createLocally = true;
configureNginx = true;
secrets.secretsFile = config.fediversity.temp.peertubeSecretsFile;
settings = { settings = {
object_storage = { object_storage = {
@ -82,17 +74,17 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
web_videos = rec { web_videos = rec {
bucket_name = "peertube-videos"; bucket_name = "peertube-videos";
prefix = ""; prefix = "";
base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; base_url = config.fediversity.internal.garage.web.urlFor bucket_name;
}; };
videos = rec { videos = rec {
bucket_name = "peertube-videos"; bucket_name = "peertube-videos";
prefix = ""; prefix = "";
base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; base_url = config.fediversity.internal.garage.web.urlFor bucket_name;
}; };
streaming_playlists = rec { streaming_playlists = rec {
bucket_name = "peertube-playlists"; bucket_name = "peertube-playlists";
prefix = ""; prefix = "";
base_url = config.fediversity.internal.garage.web.urlForBucket bucket_name; base_url = config.fediversity.internal.garage.web.urlFor bucket_name;
}; };
}; };
}; };
@ -102,12 +94,4 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
AWS_ACCESS_KEY_ID=${snakeoil_key.id} AWS_ACCESS_KEY_ID=${snakeoil_key.id}
AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret} AWS_SECRET_ACCESS_KEY=${snakeoil_key.secret}
''; '';
## Proxying through Nginx
services.peertube.configureNginx = true;
services.nginx.virtualHosts.${config.services.peertube.localDomain} = {
forceSSL = true;
enableACME = true;
};
} }

View file

@ -5,12 +5,7 @@ let
}; };
in in
{ { config, lib, pkgs, ... }:
config,
lib,
pkgs,
...
}:
lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) { lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) {
services.garage = { services.garage = {
@ -43,37 +38,16 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) {
services.pixelfed = { services.pixelfed = {
enable = true; enable = true;
domain = config.fediversity.internal.pixelfed.domain; 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 = {
forceSSL = true;
enableACME = true;
# locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlForBucket "pixelfed"}/public/";
};
}; };
services.pixelfed.settings = { 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"; # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3";
FILESYSTEM_CLOUD = "s3"; FILESYSTEM_CLOUD = "s3";
PF_ENABLE_CLOUD = true; PF_ENABLE_CLOUD = true;
AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_ACCESS_KEY_ID = snakeoil_key.id;
AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret;
AWS_DEFAULT_REGION = "garage"; AWS_DEFAULT_REGION = "garage";
AWS_URL = config.fediversity.internal.garage.web.urlForBucket "pixelfed"; AWS_URL = config.fediversity.internal.garage.web.urlFor "pixelfed";
AWS_BUCKET = "pixelfed"; AWS_BUCKET = "pixelfed";
AWS_ENDPOINT = config.fediversity.internal.garage.api.url; AWS_ENDPOINT = config.fediversity.internal.garage.api.url;
AWS_USE_PATH_STYLE_ENDPOINT = false; AWS_USE_PATH_STYLE_ENDPOINT = false;
@ -85,8 +59,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) {
after = [ "ensure-garage.service" ]; after = [ "ensure-garage.service" ];
}; };
networking.firewall.allowedTCPPorts = [ services.pixelfed.package = pkgs.pixelfed.overrideAttrs (old: {
80 patches = (old.patches or [ ]) ++ [ ./pixelfed-group-permissions.patch ];
443 });
];
} }

View file

@ -1,151 +1,12 @@
{ {
"nodes": { "nodes": {
"disko": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1727347829,
"narHash": "sha256-y7cW6TjJKy+tu7efxeWI6lyg4VVx/9whx+OmrhmRShU=",
"owner": "nix-community",
"repo": "disko",
"rev": "1879e48907c14a70302ff5d0539c3b9b6f97feaa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs_2",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1730814269,
"narHash": "sha256-fWPHyhYE6xvMI1eGY3pwBTq85wcy1YXqdzTZF+06nOg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "d70155fdc00df4628446352fc58adc640cd705c2",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1725194671, "lastModified": 1723726852,
"narHash": "sha256-tLGCFEFTB5TaOKkpfw3iYT9dnk4awTP/q4w+ROpMfuw=", "narHash": "sha256-lRzlx4fPRtzA+dgz9Rh4WK5yAW3TsAXx335DQqxY2XY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b833ff01a0d694b910daca6e2ff4a3f26dee478c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"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"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1730768919,
"narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1730137230,
"narHash": "sha256-0kW6v0alzWIc/Dc/DoVZ7A9qNScv77bj/zYTKI67HZM=",
"owner": "radvendii", "owner": "radvendii",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "df815998652a1d00ce7c059a1e5ef7d7c0548c90", "rev": "9286249a1673cf5b14a4793e22dd44b70cb69a0d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -155,30 +16,9 @@
"type": "github" "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": { "root": {
"inputs": { "inputs": {
"disko": "disko", "nixpkgs": "nixpkgs"
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs_3",
"nixpkgs-latest": "nixpkgs-latest",
"pixelfed": "pixelfed"
} }
} }
}, },

View file

@ -1,108 +1,48 @@
{ {
description = "Testing mastodon configurations";
inputs = { inputs = {
nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests"; nixpkgs.url = "github:radvendii/nixpkgs/nixos_rebuild_tests";
nixpkgs-latest.url = "github:nixos/nixpkgs";
git-hooks.url = "github:cachix/git-hooks.nix";
pixelfed = {
url = "github:pixelfed/pixelfed?ref=v0.12.3";
flake = false;
};
disko.url = "github:nix-community/disko";
}; };
outputs = outputs = { self, nixpkgs }:
{
self,
nixpkgs,
nixpkgs-latest,
git-hooks,
pixelfed,
disko,
}:
let let
system = "x86_64-linux"; system = "x86_64-linux";
lib = nixpkgs.lib;
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
pkgsLatest = nixpkgs-latest.legacyPackages.${system}; in {
bleedingFediverseOverlay = (
_: _: {
pixelfed = pkgsLatest.pixelfed.overrideAttrs (old: {
src = pixelfed;
patches = (old.patches or [ ]) ++ [ ./fediversity/pixelfed-group-permissions.patch ];
});
## TODO: give mastodon, peertube the same treatment
}
);
in
{
nixosModules = { nixosModules = {
## Bleeding-edge fediverse packages
bleedingFediverse = {
nixpkgs.overlays = [ bleedingFediverseOverlay ];
};
## Fediversity modules ## Fediversity modules
fediversity = import ./fediversity; fediversity = import ./fediversity;
## VM-specific modules ## VM-specific modules
interactive-vm = import ./vm/interactive-vm.nix; interactive-vm = import ./vm/interactive-vm.nix;
garage-vm = import ./vm/garage-vm.nix;
mastodon-vm = import ./vm/mastodon-vm.nix; mastodon-vm = import ./vm/mastodon-vm.nix;
peertube-vm = import ./vm/peertube-vm.nix; peertube-vm = import ./vm/peertube-vm.nix;
pixelfed-vm = import ./vm/pixelfed-vm.nix; pixelfed-vm = import ./vm/pixelfed-vm.nix;
disk-layout = import ./disk-layout.nix;
}; };
nixosConfigurations = { nixosConfigurations = {
mastodon = nixpkgs.lib.nixosSystem { mastodon = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = with self.nixosModules; [ modules = with self.nixosModules; [ fediversity interactive-vm mastodon-vm ];
disko.nixosModules.default
disk-layout
bleedingFediverse
fediversity
interactive-vm
garage-vm
mastodon-vm
];
}; };
peertube = nixpkgs.lib.nixosSystem { peertube = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = with self.nixosModules; [ modules = with self.nixosModules; [ fediversity interactive-vm peertube-vm ];
disko.nixosModules.default
disk-layout
bleedingFediverse
fediversity
interactive-vm
garage-vm
peertube-vm
];
}; };
pixelfed = nixpkgs.lib.nixosSystem { pixelfed = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = with self.nixosModules; [ modules = with self.nixosModules; [ fediversity interactive-vm pixelfed-vm ];
disko.nixosModules.default
disk-layout
bleedingFediverse
fediversity
interactive-vm
garage-vm
pixelfed-vm
];
}; };
all = nixpkgs.lib.nixosSystem { all = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = with self.nixosModules; [ modules = with self.nixosModules; [
disko.nixosModules.default
disk-layout
bleedingFediverse
fediversity fediversity
interactive-vm interactive-vm
garage-vm
peertube-vm peertube-vm
pixelfed-vm pixelfed-vm
mastodon-vm mastodon-vm
@ -110,34 +50,15 @@
}; };
}; };
## Fully-feature ISO installer
mkInstaller = import ./installer.nix;
installers = lib.mapAttrs (_: config: self.mkInstaller nixpkgs config) self.nixosConfigurations;
deploy =
let
deployCommand = (pkgs.callPackage ./deploy.nix { });
in
lib.mapAttrs (name: config: deployCommand name config) self.nixosConfigurations;
checks.${system} = { checks.${system} = {
mastodon-garage = import ./tests/mastodon-garage.nix { inherit pkgs self; }; mastodon-garage = import ./tests/mastodon-garage.nix { inherit pkgs self; };
pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit pkgs self; }; pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit pkgs self; };
pre-commit = git-hooks.lib.${system}.run {
src = ./.;
hooks = {
nixfmt-rfc-style.enable = true;
deadnix.enable = true;
};
};
}; };
devShells.${system}.default = pkgs.mkShell { devShells.${system}.default = pkgs.mkShell {
inputs = with pkgs; [ inputs = with pkgs; [
nil nil
]; ];
shellHook = self.checks.${system}.pre-commit.shellHook;
}; };
}; };
} }

View file

@ -1,61 +0,0 @@
/**
Convert a NixOS configuration to one for a minimal installer ISO
WARNING: Running this installer will format the target disk!
*/
{
nixpkgs,
hostKeys ? { },
}:
machine:
let
inherit (builtins) concatStringsSep attrValues mapAttrs;
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}
${concatStringsSep "\n" (
attrValues (
mapAttrs (kind: keys: ''
cp ${keys.private} /mnt/etc/ssh/ssh_host_${kind}_key
chmod 600 /mnt/etc/ssh/ssh_host_${kind}_key
cp ${keys.public} /mnt/etc/ssh/ssh_host_${kind}_key.pub
chmod 644 /mnt/etc/ssh/ssh_host_${kind}_key.pub
'') hostKeys
)
)}
poweroff
'';
};
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 = "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
(nixpkgs.lib.nixosSystem { modules = [ installer ]; }).config.system.build.isoImage

View file

@ -1,16 +1,11 @@
{ pkgs, self }: { pkgs, self }:
let let
lib = pkgs.lib; lib = pkgs.lib;
rebuildableTest = import ./rebuildableTest.nix pkgs;
## FIXME: this binding was not used, but maybe we want a side-effect or something? seleniumScript = pkgs.writers.writePython3Bin "selenium-script"
# rebuildableTest = import ./rebuildableTest.nix pkgs;
seleniumScript =
pkgs.writers.writePython3Bin "selenium-script"
{ {
libraries = with pkgs.python3Packages; [ selenium ]; libraries = with pkgs.python3Packages; [ selenium ];
} } ''
''
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options from selenium.webdriver.firefox.options import Options
@ -40,16 +35,9 @@ pkgs.nixosTest {
name = "test-mastodon-garage"; name = "test-mastodon-garage";
nodes = { nodes = {
server = server = { config, ... }: {
{ config, ... }:
{
virtualisation.memorySize = lib.mkVMOverride 4096; virtualisation.memorySize = lib.mkVMOverride 4096;
imports = with self.nixosModules; [ imports = with self.nixosModules; [ mastodon-vm ];
bleedingFediverse
fediversity
garage-vm
mastodon-vm
];
# TODO: pair down # TODO: pair down
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
python3 python3
@ -69,9 +57,7 @@ pkgs.nixosTest {
}; };
}; };
testScript = testScript = { nodes, ... }: ''
{ nodes, ... }:
''
import re import re
import time import time
@ -135,8 +121,8 @@ pkgs.nixosTest {
raise Exception("mastodon did not send a content security policy header") raise Exception("mastodon did not send a content security policy header")
csp = csp_match.group(1) csp = csp_match.group(1)
# the img-src content security policy should include the garage server # the img-src content security policy should include the garage server
## TODO: use `nodes.server.fediversity.internal.garage.api.url` same as above, but beware of escaping the regex. Be careful with port 80 though. ## 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.*", csp) garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp)
if garage_csp is 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.") raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.")

View file

@ -1,9 +1,7 @@
{ pkgs, self }: { pkgs, self }:
let let
lib = pkgs.lib; lib = pkgs.lib;
rebuildableTest = import ./rebuildableTest.nix pkgs;
## FIXME: this binding was not used but maybe we want a side effect or something?
# rebuildableTest = import ./rebuildableTest.nix pkgs;
email = "test@test.com"; email = "test@test.com";
password = "testtest"; password = "testtest";
@ -52,12 +50,10 @@ let
driver.quit() driver.quit()
''; '';
seleniumScriptPostPicture = seleniumScriptPostPicture = pkgs.writers.writePython3Bin "selenium-script-post-picture"
pkgs.writers.writePython3Bin "selenium-script-post-picture"
{ {
libraries = with pkgs.python3Packages; [ selenium ]; libraries = with pkgs.python3Packages; [ selenium ];
} } ''
''
import os import os
import time import time
${seleniumImports} ${seleniumImports}
@ -97,12 +93,10 @@ let
${seleniumTakeScreenshot "\"/home/selenium/screenshot.png\""} ${seleniumTakeScreenshot "\"/home/selenium/screenshot.png\""}
${seleniumQuit}''; ${seleniumQuit}'';
seleniumScriptGetSrc = seleniumScriptGetSrc = pkgs.writers.writePython3Bin "selenium-script-get-src"
pkgs.writers.writePython3Bin "selenium-script-get-src"
{ {
libraries = with pkgs.python3Packages; [ selenium ]; libraries = with pkgs.python3Packages; [ selenium ];
} } ''
''
${seleniumImports} ${seleniumImports}
${seleniumSetup} ${seleniumSetup}
${seleniumPixelfedLogin} ${seleniumPixelfedLogin}
@ -121,9 +115,7 @@ pkgs.nixosTest {
name = "test-pixelfed-garage"; name = "test-pixelfed-garage";
nodes = { nodes = {
server = server = { config, ... }: {
{ config, ... }:
{
services = { services = {
xserver = { xserver = {
@ -137,21 +129,14 @@ pkgs.nixosTest {
user = "selenium"; user = "selenium";
}; };
}; };
virtualisation.resolution = { virtualisation.resolution = { x = 1680; y = 1050; };
x = 1680;
y = 1050;
};
virtualisation = { virtualisation = {
memorySize = lib.mkVMOverride 8192; memorySize = lib.mkVMOverride 8192;
cores = 8; cores = 8;
}; };
imports = with self.nixosModules; [ imports = with self.nixosModules; [ pixelfed-vm ];
bleedingFediverse
fediversity
garage-vm
pixelfed-vm
];
# TODO: pair down # TODO: pair down
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
python3 python3
@ -167,8 +152,6 @@ pkgs.nixosTest {
POST_MEDIA = ./fediversity.png; POST_MEDIA = ./fediversity.png;
AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id;
AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; 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 # chrome does not like being run as root
users.users.selenium = { users.users.selenium = {
@ -177,9 +160,7 @@ pkgs.nixosTest {
}; };
}; };
testScript = testScript = { nodes, ... }: ''
{ nodes, ... }:
''
import re import re
server.start() server.start()
@ -221,7 +202,7 @@ pkgs.nixosTest {
with subtest("Check that image comes from garage"): with subtest("Check that image comes from garage"):
src = server.succeed("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("${nodes.server.fediversity.internal.garage.web.urlForBucket "pixelfed"}"): if not src.startswith("${nodes.server.fediversity.internal.garage.web.urlFor "pixelfed"}"):
raise Exception("image does not come from garage") raise Exception("image does not come from garage")
''; '';
} }

View file

@ -1,16 +1,9 @@
pkgs: test: pkgs: test:
let let
inherit (pkgs.lib) inherit (pkgs.lib) mapAttrsToList concatStringsSep genAttrs mkIf;
mapAttrsToList
concatStringsSep
genAttrs
mkIf
;
inherit (builtins) attrNames; inherit (builtins) attrNames;
interactiveConfig = ( interactiveConfig = ({ config, ... }: {
{ config, ... }:
{
# so we can run `nix shell nixpkgs#foo` on the machines # so we can run `nix shell nixpkgs#foo` on the machines
nix.extraOptions = '' nix.extraOptions = ''
extra-experimental-features = nix-command flakes extra-experimental-features = nix-command flakes
@ -27,16 +20,13 @@ let
}; };
virtualisation = mkIf (config.networking.hostName == "jumphost") { virtualisation = mkIf (config.networking.hostName == "jumphost") {
forwardPorts = [ forwardPorts = [{
{
from = "host"; from = "host";
host.port = 2222; host.port = 2222;
guest.port = 22; guest.port = 22;
} }];
];
}; };
} });
);
sshConfig = pkgs.writeText "ssh-config" '' sshConfig = pkgs.writeText "ssh-config" ''
Host * Host *
@ -60,11 +50,10 @@ let
# create an association array from machine names to the path to their # create an association array from machine names to the path to their
# configuration in the nix store # configuration in the nix store
declare -A configPaths=(${ declare -A configPaths=(${
concatStringsSep " " ( concatStringsSep " "
mapAttrsToList ( (mapAttrsToList
n: v: ''["${n}"]="${v.system.build.toplevel}"'' (n: v: ''["${n}"]="${v.system.build.toplevel}"'')
) rebuildableTest.driverInteractive.nodes rebuildableTest.driverInteractive.nodes)
)
}) })
rebuild_one() { rebuild_one() {
@ -124,14 +113,16 @@ let
# we're at it) # we're at it)
rebuildableTest = rebuildableTest =
let let
preOverride = pkgs.nixosTest ( preOverride = pkgs.nixosTest (test // {
test
// {
interactive = (test.interactive or { }) // { interactive = (test.interactive or { }) // {
# no need to // with test.interactive.nodes here, since we are iterating # no need to // with test.interactive.nodes here, since we are iterating
# over all of them, and adding back in the config via `imports` # over all of them, and adding back in the config via `imports`
nodes = nodes = genAttrs
genAttrs (attrNames test.nodes or { } ++ attrNames test.interactive.nodes or { } ++ [ "jumphost" ]) (
attrNames test.nodes or { } ++
attrNames test.interactive.nodes or { } ++
[ "jumphost" ]
)
(n: { (n: {
imports = [ imports = [
(test.interactive.${n} or { }) (test.interactive.${n} or { })
@ -140,20 +131,14 @@ let
}); });
}; };
# override with test.passthru in case someone wants to overwrite us. # override with test.passthru in case someone wants to overwrite us.
passthru = { passthru = { inherit rebuildScript sshConfig; } // (test.passthru or { });
inherit rebuildScript sshConfig; });
} // (test.passthru or { });
}
);
in in
preOverride preOverride // {
// {
driverInteractive = preOverride.driverInteractive.overrideAttrs (old: { driverInteractive = preOverride.driverInteractive.overrideAttrs (old: {
# this comes from runCommand, not mkDerivation, so this is the only # this comes from runCommand, not mkDerivation, so this is the only
# hook we have to override # hook we have to override
buildCommand = buildCommand = old.buildCommand + ''
old.buildCommand
+ ''
ln -s ${sshConfig} $out/ssh-config ln -s ${sshConfig} $out/ssh-config
ln -s ${rebuildScript}/bin/rebuild $out/bin/rebuild ln -s ${rebuildScript}/bin/rebuild $out/bin/rebuild
''; '';
@ -161,3 +146,4 @@ let
}; };
in in
rebuildableTest rebuildableTest

View file

@ -1,44 +0,0 @@
{
lib,
config,
modulesPath,
...
}:
let
inherit (lib) mkVMOverride mapAttrs' filterAttrs;
cfg = config.services.garage;
fedicfg = config.fediversity.internal.garage;
in
{
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ];
services.nginx.virtualHosts =
let
value = {
forceSSL = mkVMOverride false;
enableACME = mkVMOverride false;
};
in
mapAttrs' (bucket: _: {
name = fedicfg.web.domainForBucket bucket;
inherit value;
}) (filterAttrs (_: { website, ... }: website) cfg.ensureBuckets);
virtualisation.diskSize = 2048;
virtualisation.forwardPorts = [
{
from = "host";
host.port = fedicfg.rpc.port;
guest.port = fedicfg.rpc.port;
}
{
from = "host";
host.port = fedicfg.web.internalPort;
guest.port = fedicfg.web.internalPort;
}
];
}

View file

@ -1,6 +1,5 @@
# customize nixos-rebuild build-vm to be a bit more convenient # customize nixos-rebuild build-vm to be a bit more convenient
{ pkgs, ... }: { pkgs, ... }: {
{
# let us log in # let us log in
users.mutableUsers = false; users.mutableUsers = false;
users.users.root.hashedPassword = ""; users.users.root.hashedPassword = "";
@ -35,10 +34,7 @@
# no graphics. see nixos-shell # no graphics. see nixos-shell
virtualisation = { virtualisation = {
graphics = false; graphics = false;
qemu.consoles = [ qemu.consoles = [ "tty0" "hvc0" ];
"tty0"
"hvc0"
];
qemu.options = [ qemu.options = [
"-serial null" "-serial null"
"-device virtio-serial" "-device virtio-serial"
@ -48,19 +44,12 @@
]; ];
}; };
# we can't forward port 80 or 443, so let's run nginx on a different port # we can't forward port 80 or 443, so let's run nginx on a different port
networking.firewall.allowedTCPPorts = [ networking.firewall.allowedTCPPorts = [ 8443 8080 ];
8443
8080
];
services.nginx.defaultSSLListenPort = 8443; services.nginx.defaultSSLListenPort = 8443;
services.nginx.defaultHTTPListenPort = 8080; services.nginx.defaultHTTPListenPort = 8080;
virtualisation.forwardPorts = [ virtualisation.forwardPorts = [
{
from = "host";
host.port = 22222;
guest.port = 22;
}
{ {
from = "host"; from = "host";
host.port = 8080; host.port = 8080;

View file

@ -1,12 +1,9 @@
{ { modulesPath, lib, config, ... }: {
modulesPath,
lib,
config,
...
}:
{
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; imports = [
../fediversity
(modulesPath + "/virtualisation/qemu-vm.nix")
];
config = lib.mkMerge [ config = lib.mkMerge [
{ {
@ -14,17 +11,19 @@
enable = true; enable = true;
domain = "localhost"; domain = "localhost";
mastodon.enable = true; mastodon.enable = true;
temp.cores = config.virtualisation.cores;
}; };
services.mastodon = { services.mastodon = {
extraConfig = { extraConfig = {
EMAIL_DOMAIN_ALLOWLIST = "example.com"; 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 = lib.mkVMOverride { security.acme = {
defaults = { defaults = {
# invalid server; the systemd service will fail, and we won't get # invalid server; the systemd service will fail, and we won't get
# properly signed certificates. but let's not spam the letsencrypt # properly signed certificates. but let's not spam the letsencrypt

View file

@ -1,8 +1,9 @@
{ modulesPath, ... }: { pkgs, modulesPath, ... }: {
{ imports = [
../fediversity
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; (modulesPath + "/virtualisation/qemu-vm.nix")
];
services.peertube = { services.peertube = {
enableWebHttps = false; enableWebHttps = false;
@ -10,6 +11,10 @@
listen.hostname = "0.0.0.0"; listen.hostname = "0.0.0.0";
instance.name = "PeerTube Test VM"; instance.name = "PeerTube Test VM";
}; };
# TODO: use agenix
secrets.secretsFile = pkgs.writeText "secret" ''
574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24
'';
}; };
virtualisation.forwardPorts = [ virtualisation.forwardPorts = [

View file

@ -1,16 +1,9 @@
{ { pkgs, modulesPath, ... }: {
lib,
modulesPath,
...
}:
let imports = [
inherit (lib) mkVMOverride; ../fediversity
(modulesPath + "/virtualisation/qemu-vm.nix")
in ];
{
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ];
fediversity = { fediversity = {
enable = true; enable = true;
@ -18,16 +11,22 @@ in
pixelfed.enable = true; pixelfed.enable = true;
}; };
networking.firewall.allowedTCPPorts = [ 80 ];
services.pixelfed = { services.pixelfed = {
# TODO: secrets management!
secretFile = pkgs.writeText "secrets.env" ''
APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA
'';
settings = { settings = {
OPEN_REGISTRATION = true;
FORCE_HTTPS_URLS = false; 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 = { nginx = {
forceSSL = mkVMOverride false; # locations."/public/".proxyPass = "${config.fediversity.internal.garage.web.urlFor "pixelfed"}/public/";
enableACME = mkVMOverride false;
}; };
}; };
virtualisation.memorySize = 2048; virtualisation.memorySize = 2048;
virtualisation.forwardPorts = [ virtualisation.forwardPorts = [
{ {