Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
284 lines
9.4 KiB
Nix
284 lines
9.4 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.burrow.garage;
|
|
in
|
|
{
|
|
options.services.burrow.garage = {
|
|
enable = lib.mkEnableOption "Burrow Garage S3-compatible object storage";
|
|
|
|
environmentFile = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/var/lib/burrow/garage/env";
|
|
description = "Host-local environment file containing Garage and S3 bootstrap secrets.";
|
|
};
|
|
|
|
region = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "garage";
|
|
description = "S3 region name advertised by Garage.";
|
|
};
|
|
|
|
apiListenAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address used by the Garage S3 API listener.";
|
|
};
|
|
|
|
apiPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 3900;
|
|
description = "Port used by the Garage S3 API listener.";
|
|
};
|
|
|
|
webListenAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address used by the Garage static-web listener.";
|
|
};
|
|
|
|
webPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 3902;
|
|
description = "Port used by the Garage static-web listener.";
|
|
};
|
|
|
|
adminListenAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address used by the Garage admin listener.";
|
|
};
|
|
|
|
adminPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 3903;
|
|
description = "Port used by the Garage admin listener.";
|
|
};
|
|
|
|
zone = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "burrow-forge";
|
|
description = "Garage layout zone assigned to the forge host.";
|
|
};
|
|
|
|
layoutCapacity = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "100GB";
|
|
description = "Initial Garage layout capacity assigned to the forge host.";
|
|
};
|
|
|
|
s3RootDomain = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "s3.burrow.net";
|
|
description = "Wildcard root domain used for bucket-style S3 API requests.";
|
|
};
|
|
|
|
webRootDomain = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "web.burrow.net";
|
|
description = "Wildcard root domain used for bucket static website requests.";
|
|
};
|
|
|
|
apiDomain = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "objects.burrow.net";
|
|
description = "Public S3 API reverse-proxy domain.";
|
|
};
|
|
|
|
enableApiProxy = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Expose the Garage S3 API through Caddy.";
|
|
};
|
|
|
|
enableWebProxy = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Expose the Garage static-web listener through Caddy wildcard routes.";
|
|
};
|
|
|
|
atticBucket = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "attic";
|
|
description = "Garage bucket used by the Burrow Nix cache.";
|
|
};
|
|
|
|
releaseBucket = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "burrow-releases";
|
|
description = "Garage bucket used for release artifacts.";
|
|
};
|
|
|
|
packageBucket = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "burrow-packages";
|
|
description = "Garage bucket used for signed package repositories.";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
services.garage = {
|
|
enable = true;
|
|
package = pkgs.garage;
|
|
environmentFile = cfg.environmentFile;
|
|
settings = {
|
|
metadata_dir = "/var/lib/garage/meta";
|
|
data_dir = "/var/lib/garage/data";
|
|
rpc_bind_addr = "127.0.0.1:3901";
|
|
rpc_public_addr = "127.0.0.1:3901";
|
|
replication_factor = 1;
|
|
s3_api = {
|
|
s3_region = cfg.region;
|
|
api_bind_addr = "${cfg.apiListenAddress}:${toString cfg.apiPort}";
|
|
root_domain = ".${cfg.s3RootDomain}";
|
|
};
|
|
s3_web = {
|
|
bind_addr = "${cfg.webListenAddress}:${toString cfg.webPort}";
|
|
root_domain = ".${cfg.webRootDomain}";
|
|
index = "index.html";
|
|
};
|
|
admin.api_bind_addr = "${cfg.adminListenAddress}:${toString cfg.adminPort}";
|
|
};
|
|
};
|
|
|
|
systemd.services.burrow-garage-env = {
|
|
description = "Create Burrow Garage bootstrap environment";
|
|
before = [ "garage.service" ];
|
|
wantedBy = [ "garage.service" ];
|
|
requiredBy = [ "garage.service" ];
|
|
path = [
|
|
pkgs.coreutils
|
|
pkgs.gnugrep
|
|
pkgs.openssl
|
|
];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = "root";
|
|
Group = "root";
|
|
RemainAfterExit = true;
|
|
};
|
|
script = ''
|
|
set -euo pipefail
|
|
|
|
install -d -m 0700 "$(dirname ${lib.escapeShellArg cfg.environmentFile})"
|
|
touch ${lib.escapeShellArg cfg.environmentFile}
|
|
chmod 0600 ${lib.escapeShellArg cfg.environmentFile}
|
|
chown root:root ${lib.escapeShellArg cfg.environmentFile}
|
|
|
|
has_var() {
|
|
grep -q "^$1=" ${lib.escapeShellArg cfg.environmentFile}
|
|
}
|
|
|
|
add_var() {
|
|
local name="$1"
|
|
local value="$2"
|
|
if ! has_var "$name"; then
|
|
printf '%s=%s\n' "$name" "$value" >> ${lib.escapeShellArg cfg.environmentFile}
|
|
fi
|
|
}
|
|
|
|
add_var GARAGE_RPC_SECRET "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
|
|
add_var GARAGE_ATTIC_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
|
|
add_var GARAGE_ATTIC_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
|
|
add_var GARAGE_RELEASE_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
|
|
add_var GARAGE_RELEASE_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
|
|
add_var GARAGE_PACKAGE_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
|
|
add_var GARAGE_PACKAGE_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
|
|
add_var GARAGE_BACKUP_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
|
|
add_var GARAGE_BACKUP_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
|
|
add_var ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64 "$(${pkgs.openssl}/bin/openssl genrsa -traditional 4096 | ${pkgs.coreutils}/bin/base64 -w0)"
|
|
|
|
. ${lib.escapeShellArg cfg.environmentFile}
|
|
add_var AWS_ACCESS_KEY_ID "$GARAGE_ATTIC_ACCESS_KEY_ID"
|
|
add_var AWS_SECRET_ACCESS_KEY "$GARAGE_ATTIC_SECRET_ACCESS_KEY"
|
|
'';
|
|
};
|
|
|
|
systemd.services.burrow-garage-bootstrap = {
|
|
description = "Bootstrap Burrow Garage layout, keys, and buckets";
|
|
after = [ "garage.service" ];
|
|
requires = [ "garage.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [
|
|
config.services.garage.package
|
|
pkgs.coreutils
|
|
pkgs.gawk
|
|
pkgs.gnugrep
|
|
];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = "root";
|
|
Group = "root";
|
|
};
|
|
script = ''
|
|
set -euo pipefail
|
|
set -a
|
|
. ${lib.escapeShellArg cfg.environmentFile}
|
|
set +a
|
|
|
|
for _ in $(seq 1 60); do
|
|
if garage status >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
node_id="$(garage status | tail -n1 | awk '{ print $1 }')"
|
|
if [ -n "$node_id" ]; then
|
|
garage layout assign -c ${lib.escapeShellArg cfg.layoutCapacity} -z ${lib.escapeShellArg cfg.zone} "$node_id" >/dev/null 2>&1 || true
|
|
garage layout apply --version 1 >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
ensure_key() {
|
|
local key_id="$1"
|
|
local secret="$2"
|
|
if ! garage key info "$key_id" >/dev/null 2>&1; then
|
|
garage key import "$key_id" "$secret" --yes >/dev/null
|
|
fi
|
|
}
|
|
|
|
ensure_bucket() {
|
|
local bucket="$1"
|
|
local key_id="$2"
|
|
if ! garage bucket info "$bucket" >/dev/null 2>&1; then
|
|
garage bucket create "$bucket" >/dev/null
|
|
fi
|
|
garage bucket allow --read --write --owner "$bucket" --key "$key_id" >/dev/null
|
|
}
|
|
|
|
ensure_bucket_reader() {
|
|
local bucket="$1"
|
|
local key_id="$2"
|
|
garage bucket allow --read "$bucket" --key "$key_id" >/dev/null
|
|
}
|
|
|
|
ensure_key "$GARAGE_ATTIC_ACCESS_KEY_ID" "$GARAGE_ATTIC_SECRET_ACCESS_KEY"
|
|
ensure_key "$GARAGE_RELEASE_ACCESS_KEY_ID" "$GARAGE_RELEASE_SECRET_ACCESS_KEY"
|
|
ensure_key "$GARAGE_PACKAGE_ACCESS_KEY_ID" "$GARAGE_PACKAGE_SECRET_ACCESS_KEY"
|
|
ensure_key "$GARAGE_BACKUP_ACCESS_KEY_ID" "$GARAGE_BACKUP_SECRET_ACCESS_KEY"
|
|
|
|
ensure_bucket ${lib.escapeShellArg cfg.atticBucket} "$GARAGE_ATTIC_ACCESS_KEY_ID"
|
|
ensure_bucket ${lib.escapeShellArg cfg.releaseBucket} "$GARAGE_RELEASE_ACCESS_KEY_ID"
|
|
ensure_bucket ${lib.escapeShellArg cfg.packageBucket} "$GARAGE_PACKAGE_ACCESS_KEY_ID"
|
|
ensure_bucket_reader ${lib.escapeShellArg cfg.atticBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
|
|
ensure_bucket_reader ${lib.escapeShellArg cfg.releaseBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
|
|
ensure_bucket_reader ${lib.escapeShellArg cfg.packageBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
|
|
'';
|
|
};
|
|
|
|
services.caddy.virtualHosts = lib.mkMerge [
|
|
(lib.mkIf cfg.enableApiProxy {
|
|
"${cfg.apiDomain}".extraConfig = ''
|
|
encode gzip zstd
|
|
reverse_proxy ${cfg.apiListenAddress}:${toString cfg.apiPort}
|
|
'';
|
|
})
|
|
(lib.mkIf cfg.enableWebProxy {
|
|
"*.${cfg.webRootDomain}".extraConfig = ''
|
|
encode gzip zstd
|
|
reverse_proxy ${cfg.webListenAddress}:${toString cfg.webPort}
|
|
'';
|
|
})
|
|
];
|
|
};
|
|
}
|