diff --git a/checks/flake-module.nix b/checks/flake-module.nix index 1bca58838..89f62b82d 100644 --- a/checks/flake-module.nix +++ b/checks/flake-module.nix @@ -44,6 +44,9 @@ in # Ergochat test ergochat = import ./ergochat nixosTestArgs; + # Garage test + garage = import ./garage nixosTestArgs; + # Base Tests secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs; borgbackup-legacy = self.clanLib.test.baseTest ./borgbackup-legacy nixosTestArgs; diff --git a/checks/garage/default.nix b/checks/garage/default.nix new file mode 100644 index 000000000..300beafb6 --- /dev/null +++ b/checks/garage/default.nix @@ -0,0 +1,87 @@ +{ + pkgs, + nixosLib, + clan-core, + ... +}: +nixosLib.runTest ( + { ... }: + { + imports = [ + clan-core.modules.nixosVmTest.clanTest + ]; + + hostPkgs = pkgs; + + name = "garage"; + + clan = { + directory = ./.; + modules."@clan/garage" = ../../clanServices/garage/default.nix; + inventory = { + machines.server = { }; + + instances = { + garage-test = { + module.name = "@clan/garage"; + roles.default.machines."server".settings = { }; + }; + }; + }; + }; + + nodes = { + server = { + services.garage = { + enable = true; + package = pkgs.garage; + settings = { + + metadata_dir = "/var/lib/garage/meta"; + data_dir = "/var/lib/garage/data"; + db_engine = "sqlite"; + + replication_factor = 1; + + rpc_bind_addr = "127.0.0.1:3901"; + + s3_api = { + api_bind_addr = "127.0.0.1:3900"; + s3_region = "garage"; + root_domain = ".s3.garage"; + }; + + s3_web = { + bind_addr = "127.0.0.1:3902"; + root_domain = ".web.garage"; + }; + + admin = { + api_bind_addr = "127.0.0.1:3903"; + }; + }; + }; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("network-online.target") + server.wait_for_unit("garage") + + # Check that garage is running + server.succeed("systemctl status garage") + + # Check that the data directories exist + server.succeed("test -d /var/lib/garage/meta") + server.succeed("test -d /var/lib/garage/data") + + # Check that the ports are open to confirm that garage is running + server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3901") + server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3900") + server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3902") + server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3903") + ''; + } +) diff --git a/checks/garage/sops/machines/server/key.json b/checks/garage/sops/machines/server/key.json new file mode 100755 index 000000000..b9e2a3ac4 --- /dev/null +++ b/checks/garage/sops/machines/server/key.json @@ -0,0 +1,6 @@ +[ + { + "publickey": "age1xdtpzyr6jfvj3rzn0fl5l73az3lzml9mmdp5d7y2q4hlacxacdxq5a8ygl", + "type": "age" + } +] diff --git a/checks/garage/sops/secrets/server-age.key/secret b/checks/garage/sops/secrets/server-age.key/secret new file mode 100644 index 000000000..1f6290f73 --- /dev/null +++ b/checks/garage/sops/secrets/server-age.key/secret @@ -0,0 +1,15 @@ +{ + "data": "ENC[AES256_GCM,data:SnRy7rSi1A9N2soOOuk+fi4G+bPuPWLUdRIf/TgZbi4uO/S0t2SLBgs3yJ++0VwIXVHNoBIRgCIvJBPakux3dBP0/+AB2PO4ErY=,iv:LNBw013OPl4Nb5suDdDZOnkk/Ycxr8nkGS+LzY163mU=,tag:jrgQX64kjKJNnXm1OX+Olg==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwY0huVkhYaEtCOUh1ZENm\nVlJYYnZ5ZElIMm44NHZaMmxpc3JtNHhReVQ0CjUyYzdGNVZZOXpoOVNzajN5NjJz\nUXEvdERvVlRjRlA4bk1sZXVZTCtpK1kKLS0tIFBueEQ1djBhQXB5bTJWSjBzQzhT\nc3dLSG5yQUhiNTA3b3Q0UmhYK0RYK3MKRVgi+P7s2sNxH8CjLVYIcdoUzq3bk5m1\nhlq2qwlncGYJP8o8j7ds/yjHlBuk89JSeIirujrQ8Y8amhh+PrWAMw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-06-03T22:56:03Z", + "mac": "ENC[AES256_GCM,data:XvlmUpSijC/IYsjskvzlrVUD+WCXMHyHQ4/OJsdDRh3D5tQymGgDFSDSpvZshV9zt2cYFVFEMEDWD3GN++9IcVETrsB+a491viVMQZ77ihJRvHny0+EDOSw9YEOQmqKgtSiCKQhTM1Jyq9GOVxbU4czEA0XeDW7SIASmBGTLTng=,iv:3r2dngDmk0bUgFPlAY2T2WM46+R0HGXg8cEEpuDigPo=,tag:VremYh+y2Ba+SXloEozepQ==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/garage/sops/secrets/server-age.key/users/admin b/checks/garage/sops/secrets/server-age.key/users/admin new file mode 120000 index 000000000..9e21a9938 --- /dev/null +++ b/checks/garage/sops/secrets/server-age.key/users/admin @@ -0,0 +1 @@ +../../../users/admin \ No newline at end of file diff --git a/checks/garage/sops/users/admin/key.json b/checks/garage/sops/users/admin/key.json new file mode 100644 index 000000000..e408aa96b --- /dev/null +++ b/checks/garage/sops/users/admin/key.json @@ -0,0 +1,4 @@ +{ + "publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "type": "age" +} diff --git a/checks/garage/vars/per-machine/server/garage/admin_token/machines/server b/checks/garage/vars/per-machine/server/garage/admin_token/machines/server new file mode 120000 index 000000000..2bd819ecb --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/admin_token/machines/server @@ -0,0 +1 @@ +../../../../../../sops/machines/server \ No newline at end of file diff --git a/checks/garage/vars/per-machine/server/garage/admin_token/secret b/checks/garage/vars/per-machine/server/garage/admin_token/secret new file mode 100644 index 000000000..4d5359253 --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/admin_token/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:GE8duA3qHU+XqdTLRVjp4YmbaG1g4P2E+XF4DFNKDz/df5ZZcSQSf31DjPat,iv:bi/Ejq3npslSdcCVlwWjEnMFd3VxROfgztph5hfyemU=,tag:yT74edREda4dlBrkUCGp/w==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYVHdrZk9KZjNvaUNjbWVp\nU2JlZStQWUI0dWZtTmJJUzZ1VDdhbEg4QkNZCmFVMzM4YndxVHBaV2ZWdlBSVGda\nc09lbmhPNm4xNmMrZ3hoa0VtR2t6d0EKLS0tIHdZbTNWVzNPSGJ3MlowOExRZXd0\nVFEyeGViMXRLRTlubFVuNHRhUG5WTkkK64h5/D/3amcymHpikfcX38AliQy6cz0s\njYwUTLVzak3CL8tm7wlDW54r7leeWT0+PvF4aYSzYEkWq+3IkyYBzg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1xdtpzyr6jfvj3rzn0fl5l73az3lzml9mmdp5d7y2q4hlacxacdxq5a8ygl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2U3UwcWN5a1pGUzNvV3Zu\ndXJaelBBa0crTktQeEhUWnZkUFZ3TFVyYkdRCmVXbW1Hd2NtZ0VlckZTNmoyd3cz\ncjhhSnowVVhNcUdqOEtnSmhnYWlNbDAKLS0tIGRpZFlqZTFqRjJoaWxzcVc5NTQz\nTm9JeE1KaWdMWE05SThrbVUwQTBtRTAKo1/BvRkTB6LOVIVS/s55Nhmkb4rhOxWG\nV8greuVYDm2wr6RfVjf0njnzxjgP2JrU3FAH78OZ1P8/bsEbotAe0w==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-06-03T22:56:06Z", + "mac": "ENC[AES256_GCM,data:w7jk5qXyV9CvbwTgqLN7eFvRNeMi/EoqfOc0yq83ox0/GExO2gJ0tgVgtn01npFDyTm66VyCZIjavpbO47py9gaMx5u6Fl2f2IUPWv2uYra75fuo6z4ZN8+zlZTxdzJ1tdWveqFcj10QKqoEUT62vfXcqYoHkHzQmbC/E4Mlm7I=,iv:Lr7KU/lnbHT+4YxZIx6riVqdDkqBOuJ+0TgE9CD8x8k=,tag:+sFgemXoxIhNIZxOSuenBA==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/garage/vars/per-machine/server/garage/admin_token/users/admin b/checks/garage/vars/per-machine/server/garage/admin_token/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/admin_token/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/garage/vars/per-machine/server/garage/metrics_token/machines/server b/checks/garage/vars/per-machine/server/garage/metrics_token/machines/server new file mode 120000 index 000000000..2bd819ecb --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/metrics_token/machines/server @@ -0,0 +1 @@ +../../../../../../sops/machines/server \ No newline at end of file diff --git a/checks/garage/vars/per-machine/server/garage/metrics_token/secret b/checks/garage/vars/per-machine/server/garage/metrics_token/secret new file mode 100644 index 000000000..b615b9322 --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/metrics_token/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:00vwnDLkJ8A7aIe+SdUoFLrY/zL6o4Ty+/Mp43A7wrUDDLdM5OrWAJkf+dDV,iv:Uh7Z4lxQ5Da22H2giG1XzvjAZlLDq86j0LsiyM+AEhQ=,tag:JUEOJtoIue6s9XV5B/PTLA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0Q01QbzltUk0xT2FIcGMz\nR01SUGQ2bWJaR0MybHltMDVWZHR2VXE4MUVNClB6MGt4VktLQXJwM3ZYbktaTnFx\nR0NOdjVZd1RzQUphQXRnUFFsRDNoUE0KLS0tIGNmK3FHY2kwMXFGaTNxN0x5bVlO\nWXUzc2ZzOTE0SGhscHZib09WRmVRRVkKxI8b/ofVxe5P9oAiFv/Ais8vZwk0GhJe\nfrmM0JcHy+CPC2wOwY1eiegkX0gEvyKSXGWGh694PoRFUMgKAdMyrA==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1xdtpzyr6jfvj3rzn0fl5l73az3lzml9mmdp5d7y2q4hlacxacdxq5a8ygl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEeTBmM2dBTzdaaS9oaGVy\nNTVvSElMQzZTTHF3ODFsSjhBQUMrZktNSDFvCk5iRHhUYmU1UnJFYklBaVg2TEpq\nYW1pQkljNXFLYXVxZGJWRE5YeStyaWsKLS0tIEpqQlVSYk1IRFlhMlp2NXJCSTdG\nUk1IbVBzR1pkcGMrOG56TDhkMm94NkUKqHFjy12JfJv54d6YMAL/I7o+FH2kC6wh\nPhC5Nn5bcaAbYYvHq8710zzhVPiNZJbm7PcTwM+ExaJ/oWYriMIgZw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-06-03T22:56:06Z", + "mac": "ENC[AES256_GCM,data:2HvUaM2hf+2h5TLn5ZsL+gyW4mt8BzCvouTv+k0FHq4LI5TaheAsLJfsLrcsztLit5IjaJUDQaBcagTZXYatWzVha0bOmaihDWw3IJ7YYghLwDlIQRdzOvnVLND1vIqmZxqMMqYHR1xUipIKo3jcoMmt8oRzyriuDUSMbtDZUlE=,iv:OevTs0OduLQT9f01g6f/mAJnAGON8rCLYEp6QC0xSc8=,tag:ZVHB7vWzEiSRyNXF3cQECQ==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/garage/vars/per-machine/server/garage/metrics_token/users/admin b/checks/garage/vars/per-machine/server/garage/metrics_token/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/garage/vars/per-machine/server/garage/metrics_token/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/garage/vars/shared/garage-shared/rpc_secret/machines/server b/checks/garage/vars/shared/garage-shared/rpc_secret/machines/server new file mode 120000 index 000000000..c328f1963 --- /dev/null +++ b/checks/garage/vars/shared/garage-shared/rpc_secret/machines/server @@ -0,0 +1 @@ +../../../../../sops/machines/server \ No newline at end of file diff --git a/checks/garage/vars/shared/garage-shared/rpc_secret/secret b/checks/garage/vars/shared/garage-shared/rpc_secret/secret new file mode 100644 index 000000000..ffae16f6f --- /dev/null +++ b/checks/garage/vars/shared/garage-shared/rpc_secret/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:TMn/EhTq4BASohm3yzyjHt3kPjHEZvtl4mGfHpTrVkjhYec5f/w/GSYQrInyiq2mMeAJ667oEW8xpQcwMgXRPUU=,iv:HgO8oJmCLBftaAxWELnDqlSIhIjWqzL4VCdXLGtIZuU=,tag:HuQpbeFS3f6pfeVImZjLqA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFUlRaSnVyRFFnUHRWejZh\ncS9pYmR0RzZ3VGtrcnBYVDlQenBacHVLS1Y0CkF0ZXdpMEJaR2dkVHN6endvY1B4\nWERkb3BFSGZCNEJRbTRRMFdxUDRiUnMKLS0tIC91cXoyR2JZQXl4T28rVjFXUDcr\nSHRoTXhiUWVuYldHVWdRUnZGNVk0dTQKyreayLzb2yZYfhmgsEyKnJjf3N+b7Cjh\nrI+vIsvqsVUkJ+zJG4A7lvcpJ5Q8A2FMd/2CbmyvqchXNXUF3CYM3Q==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1xdtpzyr6jfvj3rzn0fl5l73az3lzml9mmdp5d7y2q4hlacxacdxq5a8ygl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNmo0NEVxL015MVJ5Ujc3\nVVAvN0ExaFpwaks5akJtME81YWxKTFAveVZNCkhvdEdxVFFqSzZuZ3haV09YSnZP\nOUtXc2NLbDREWmZKN1UyTFd1bDR5RlEKLS0tIEpNS2xMVDRieTR5a2lBOUt4cWlr\naENMM1dCTHdiYmxWamZUSFFJS2YzWmsK34J6QeaMFDhFoXHj9wOYwm2HDwjajUjT\nsMnU17aRf2IiYDC1lq2Gx6hXDsfezdgfpNRWBOQQ3OtUCen4BXDpaA==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-06-03T22:56:08Z", + "mac": "ENC[AES256_GCM,data:8dfY+Nc1sxyYWNXkqQwfxXRdWnMKXljUfUIjxCQN/jwdmW5mLQ9qL9+nHohNnyb6T3i1md9kdhJ5pA1Ce2mp/l9OUhYS/AhdD1fALxUKqJpTXWV81Rcbah4M0CElr3YrPHrRdGjXELAo1Lhi65a3I+ykR+PTzNx0yka2CRF0Y3I=,iv:TRgOggchvpqvMtGT6eQi0NmJvhXYO3gXDGj2yUfwXjs=,tag:YyR+YqdzYUp3doSNjWBz6Q==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/garage/vars/shared/garage-shared/rpc_secret/users/admin b/checks/garage/vars/shared/garage-shared/rpc_secret/users/admin new file mode 120000 index 000000000..f14859ae0 --- /dev/null +++ b/checks/garage/vars/shared/garage-shared/rpc_secret/users/admin @@ -0,0 +1 @@ +../../../../../sops/users/admin \ No newline at end of file diff --git a/clanModules/garage/README.md b/clanModules/garage/README.md index 72771bc7d..0d7646ed3 100644 --- a/clanModules/garage/README.md +++ b/clanModules/garage/README.md @@ -1,5 +1,7 @@ --- description = "S3-compatible object store for small self-hosted geo-distributed deployments" +categories = ["System"] +features = [ "inventory", "deprecated" ] --- This module generates garage specific keys automatically. diff --git a/clanModules/garage/default.nix b/clanModules/garage/roles/default.nix similarity index 100% rename from clanModules/garage/default.nix rename to clanModules/garage/roles/default.nix diff --git a/clanServices/flake-module.nix b/clanServices/flake-module.nix index b16e2923e..9fc2cef8c 100644 --- a/clanServices/flake-module.nix +++ b/clanServices/flake-module.nix @@ -4,6 +4,7 @@ ./admin/flake-module.nix ./deltachat/flake-module.nix ./ergochat/flake-module.nix + ./garage/flake-module.nix ./auto-upgrade/flake-module.nix ./hello-world/flake-module.nix ./wifi/flake-module.nix diff --git a/clanServices/garage/default.nix b/clanServices/garage/default.nix new file mode 100644 index 000000000..a84d34a1c --- /dev/null +++ b/clanServices/garage/default.nix @@ -0,0 +1,55 @@ +{ ... }: +{ + _class = "clan.service"; + manifest.name = "clan-core/garage"; + manifest.description = "S3-compatible object store for small self-hosted geo-distributed deployments"; + manifest.categories = [ "System" ]; + + roles.default = { + + perInstance.nixosModule = + { config, pkgs, ... }: + { + systemd.services.garage.serviceConfig = { + LoadCredential = [ + "rpc_secret_path:${config.clan.core.vars.generators.garage-shared.files.rpc_secret.path}" + "admin_token_path:${config.clan.core.vars.generators.garage.files.admin_token.path}" + "metrics_token_path:${config.clan.core.vars.generators.garage.files.metrics_token.path}" + ]; + Environment = [ + "GARAGE_ALLOW_WORLD_READABLE_SECRETS=true" + "GARAGE_RPC_SECRET_FILE=%d/rpc_secret_path" + "GARAGE_ADMIN_TOKEN_FILE=%d/admin_token_path" + "GARAGE_METRICS_TOKEN_FILE=%d/metrics_token_path" + ]; + }; + + clan.core.vars.generators.garage = { + files.admin_token = { }; + files.metrics_token = { }; + runtimeInputs = [ + pkgs.coreutils + pkgs.openssl + ]; + script = '' + openssl rand -base64 -out "$out"/admin_token 32 + openssl rand -base64 -out "$out"/metrics_token 32 + ''; + }; + + clan.core.vars.generators.garage-shared = { + share = true; + files.rpc_secret = { }; + runtimeInputs = [ + pkgs.coreutils + pkgs.openssl + ]; + script = '' + openssl rand -hex -out "$out"/rpc_secret 32 + ''; + }; + + clan.core.state.garage.folders = [ config.services.garage.settings.metadata_dir ]; + }; + }; +} diff --git a/clanServices/garage/flake-module.nix b/clanServices/garage/flake-module.nix new file mode 100644 index 000000000..6f02bfcf4 --- /dev/null +++ b/clanServices/garage/flake-module.nix @@ -0,0 +1,6 @@ +{ lib, ... }: +{ + clan.modules = { + garage = lib.modules.importApply ./default.nix { }; + }; +} diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 40b24e261..35dce7283 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -87,6 +87,7 @@ nav: - reference/clanServices/borgbackup.md - reference/clanServices/deltachat.md - reference/clanServices/ergochat.md + - reference/clanServices/garage.md - reference/clanServices/hello-world.md - reference/clanServices/wifi.md - Clan Modules: