Skip to content

Instantly share code, notes, and snippets.

@mweinelt
Created January 12, 2024 17:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mweinelt/608a5b7d29a9f6c41d4ff71cc472c214 to your computer and use it in GitHub Desktop.
Save mweinelt/608a5b7d29a9f6c41d4ff71cc472c214 to your computer and use it in GitHub Desktop.
Bupstash NixOS module mock-up
{ config
, lib
, pkgs
, ...
}:
let
inherit (lib)
concatMapStringsSep
escapeShellArgs
mapAttrs'
mdDoc
mkIf
mkOption
mkPackageOptionMD
nullOr
nameValuePair
optionalAttrs
optionalString
types
;
cfg = config.service.bupstash;
in {
options.services.bupstash = let
putJob = with types; {
enable = mkOption {
type = bool;
default = true;
example = false;
description = ''
Whether to enable this backup job
'';
};
user = mkOption {
type = nullOr str;
default = null;
example = "postgres";
description = mdDoc ''
User to run this put job as. Defaults to `null`, which uses a dynamic user
named `bupstash`, which has permissions to read all files through `CAP_DAC_READ_OVERRIDE`.
'';
};
repository = mkOption {
type = nullOr str;
example = "ssh://offsite.example.com/machine.example.com";
description = mdDoc ''
The repository to backup the paths into. Available as `BUPSTASH_REPOSITORY`.
'';
};
repositoryCommand = mkOption {
type = nullOr str;
example = "ssh -l backup -i /run/keys/bupstash/id_bupstash offsite.example.com";
description = mdDoc ''
The command to run to connect to an instance of `bupstash-serve`. Available as
`BUPSTASH_REPOSITORY_COMMAND`.
'';
};
key = mkOption {
type = nullOr str;
description = mdDoc ''
Encryption key that is allowed to put to the repository. Available as `BUPSTASH_KEY`.
Relevant documentation:
- [Secure offline keys](https://bupstash.io/doc/guides/Secure%20Offline%20Keys.html)
'';
};
keyCommand = mkOption {
type = nullOr str;
example = "cat /run/keys/bupstash/put.key";
description = mdDoc ''
Command to retrieve the encryption key, that is allowed to put to the repository.
Available as `BUPSTASH_KEY_COMMAND`.
Relevant documentation:
- [Secure offline keys](https://bupstash.io/doc/guides/Secure%20Offline%20Keys.html)
'';
};
tags = mkOption {
type = attrsOf str;
default = { };
example = literalExample ''
{
name = "example.zip"
}
'';
description = ''
Key/value pairs attached to the data stored through the put call.
'';
};
paths = mkOption {
type = nullOr (listOf path);
default = null;
example = [
"/home/"
"/var/backup"
"/var/lib"
];
description = ''
List of files or directories to save.
'';
};
exec = mkOtion {
type = nullOr str;
default = null;
example = literalExample ''
''${config.services.postgresql.package}/bin/pg_dump
'';
};
extraArgs = mkOption {
type = listOf str;
default = [];
description = ''
List of parameters and arguments to append to the `bupstash put` command.
'';
};
preHook = mkOption {
type = nullOr lines;
default = null;
example = literalExpression ''\
# https://openzfs.github.io/openzfs-docs/man/8/zfs-snapshot.8.html
''${config.boot.zfs.package}/bin/zfs snapshot -r pool/state@backup
'';
description = ''
Commands that are run before the put job is run. Executed with root permissions.
Useful to create snapshots or modify the environment variables consumed by bupstash.
'';
};
postHook = mkOption {
type = nullOr lines;
default = null;
example = literalExpression ''
# https://openzfs.github.io/openzfs-docs/man/8/zfs-snapshot.8.html
''${config.boot.zfs.package}/bin/zfs destroy -r pool/state@backup
'';
description = ''
Commands that are run before the put job is run.
Useful to remove snapshots or modify the environment variables consumed by bupstash.
'';
};
};
in with types; {
package = mkPackageOptionMD pkgs "bupstash" { };
putJobs = mkOption {
description = mdDoc ''
List of bupstash put jobs.
Relevant documentation:
- [bupstash-put](https://bupstash.io/doc/man/bupstash-put.html)
'';
default = { };
type = attrsOf putJob;
};
};
config = let
mkPutService = name: job: nameValuePair "bupstash-put-${name}" {
description = "Bupstash Put Job";
documentation = [
"man:bupstash-put(1)"
"https://bupstash.io/doc/man/bupstash-put.html"
];
environment = {
BUPSTASH_REPOSITORY = job.repository;
BUPSTASH_KEY = job.key;
};
serviceConfig = {
ExecStart = ''
${lib.getExe cfg.package} put \
${optionalString job.exec != null "--exec ${escapeShellArgs job.exec}"} \
${concatMapStringsSep (k: v: "${k}=${v}\n") job.tags}
${optionalString job.paths != null (escapeShellArgs job.paths)} \
${concatMapStringsSep " " escapeShellArgs job.extraArgs}
'';
ExecStartPre = mkIf (job.preHook != null)
("+" + pkgs.writeShellScript "bupstash-put-${job.name}-prehook" job.preHook);
ExecStopPost = mkIf (job.postHook != null)
("+" + pkgs.writeShellScript "bupstash-put-${job.name}-posthook" job.postHook);
} // optionalAttrs (job.user == null) {
DynamicUser = true;
User = "bupstash";
AmbientCapabilities = [
"CAP_DAC_READ_OVERRIDE"
];
Capabilities = [
"CAP_DAC_READ_OVERRIDE"
];
} // optionalAttrs (job.user != null) {
User = job.user;
};
};
in mkIf (cfg.jobs != []) {
systemd.services = mapAttrs' mkPutService cfg.jobs;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment