2024-03-27 10:42:11 +01:00
|
|
|
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 = {
|
2024-04-03 14:40:19 +02:00
|
|
|
id = "GK22a15201acacbd51cd43e327";
|
|
|
|
secret = "82b2b4cbef27bf8917b350d5b10a87c92fa9c8b13a415aeeea49726cf335d74e";
|
2024-03-27 10:42:11 +01:00
|
|
|
};
|
|
|
|
in
|
|
|
|
# TODO: expand to a multi-machine setup
|
|
|
|
{ config, lib, pkgs, ... }: {
|
|
|
|
# add in options to ensure creation of buckets and keys
|
2024-03-27 10:59:50 +01:00
|
|
|
options =
|
|
|
|
let
|
2024-04-03 14:40:19 +02:00
|
|
|
inherit (lib) types mkOption mkEnableOption;
|
2024-03-27 10:59:50 +01:00
|
|
|
in {
|
|
|
|
services.garage = {
|
|
|
|
ensureBuckets = mkOption {
|
|
|
|
type = types.attrsOf (types.submodule {
|
|
|
|
options = {
|
|
|
|
website = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
};
|
2024-04-03 14:40:19 +02:00
|
|
|
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 = [];
|
|
|
|
};
|
2024-03-27 10:59:50 +01:00
|
|
|
};
|
|
|
|
});
|
|
|
|
};
|
|
|
|
ensureKeys = mkOption {
|
|
|
|
type = types.attrsOf (types.submodule {
|
2024-04-03 14:40:19 +02:00
|
|
|
# TODO: these should be managed as secrets, not in the nix store
|
2024-03-27 10:59:50 +01:00
|
|
|
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 = [];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
2024-03-27 10:42:11 +01:00
|
|
|
|
|
|
|
config = {
|
|
|
|
virtualisation.vmVariant = {
|
|
|
|
virtualisation.diskSize = 2048;
|
|
|
|
virtualisation.forwardPorts = [
|
|
|
|
{
|
|
|
|
from = "host";
|
|
|
|
host.port = 3901;
|
|
|
|
guest.port = 3901;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
from = "host";
|
|
|
|
host.port = 3902;
|
|
|
|
guest.port = 3902;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
2024-04-03 14:40:19 +02:00
|
|
|
environment.systemPackages = [ pkgs.minio-client pkgs.awscli ];
|
2024-03-27 10:42:11 +01:00
|
|
|
|
|
|
|
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" ];
|
2024-04-03 14:40:19 +02:00
|
|
|
path = [ config.services.garage.package pkgs.perl pkgs.awscli ];
|
2024-03-27 10:42:11 +01:00
|
|
|
script = ''
|
|
|
|
set -xeuo pipefail
|
|
|
|
# give garage time to start up
|
|
|
|
sleep 3
|
2024-03-27 10:59:50 +01:00
|
|
|
|
2024-03-27 10:42:11 +01:00
|
|
|
# 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))
|
|
|
|
|
2024-04-03 14:40:19 +02:00
|
|
|
# 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};
|
|
|
|
|
2024-03-27 10:59:50 +01:00
|
|
|
${
|
2024-04-03 14:40:19 +02:00
|
|
|
lib.concatStringsSep "\n" (lib.mapAttrsToList (bucket: { website, aliases, corsRules }: ''
|
2024-03-27 10:59:50 +01:00
|
|
|
garage bucket create ${bucket}
|
|
|
|
# XXX: should this --deny the website if `website` is false?
|
|
|
|
${lib.optionalString website ''
|
|
|
|
garage bucket website --allow ${bucket}
|
|
|
|
''}
|
2024-04-03 14:40:19 +02:00
|
|
|
${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
|
|
|
|
''}
|
2024-03-27 10:59:50 +01:00
|
|
|
'') 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)
|
|
|
|
}
|
2024-03-27 10:42:11 +01:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|