Compare commits
2 Commits
terst
...
init-resti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2beaf6f061 | ||
|
|
b059709c83 |
215
clanServices/restic/default.nix
Normal file
215
clanServices/restic/default.nix
Normal file
@@ -0,0 +1,215 @@
|
||||
{ nixpkgs, ... }:
|
||||
|
||||
let
|
||||
|
||||
mkResticGenerator = machineName: rec {
|
||||
|
||||
files."restic-rest-pass" = { };
|
||||
files."restic-rest-env" = { };
|
||||
files."restic-repokey" = { };
|
||||
|
||||
share = true;
|
||||
|
||||
validation.name = script;
|
||||
|
||||
runtimeInputs = [ nixpkgs.xkcdpass ];
|
||||
|
||||
script = ''
|
||||
xkcdpass -n 4 -d - > "$out/restic-rest-pass"
|
||||
xkcdpass -n 4 -d - > "$out/restic-repokey"
|
||||
echo "RESTIC_REST_USERNAME=${machineName}" > $out/restic-rest-env
|
||||
echo "RESTIC_REST_PASSWORD=$(cat $out/restic-rest-pass)" >> $out/restic-rest-env
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "restic";
|
||||
|
||||
# Define what roles exist
|
||||
|
||||
roles.server = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
# system ,
|
||||
# {
|
||||
# ...
|
||||
# }:
|
||||
# # let pkgs = import nixpkgs { inherit system; }; in
|
||||
{
|
||||
# These options can be set via 'roles.server.settings'
|
||||
|
||||
# options.restic.package = lib.mkPackageOption pkgs "restic" { };
|
||||
|
||||
options.directory = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/restic";
|
||||
description = ''
|
||||
The directory where the restic repositories are stored.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
perInstance =
|
||||
{ settings, roles, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
clan.core.vars.generators = {
|
||||
|
||||
restic-cert = rec {
|
||||
|
||||
files."restic-key" = { };
|
||||
files."restic-cert".secret = false;
|
||||
|
||||
share = true;
|
||||
|
||||
runtimeInputs = with pkgs; [
|
||||
coreutils
|
||||
openssl
|
||||
];
|
||||
|
||||
validation.script = script;
|
||||
|
||||
# TODO openssl will ask for Country, City, etc.
|
||||
# Can we pass those as prompts?
|
||||
|
||||
# We use a wildcard cert for *.restic
|
||||
script = ''
|
||||
openssl req -newkey rsa:2048 -nodes -x509 \
|
||||
-keyout $out/restic-key \
|
||||
-out $out/restic-cert \
|
||||
-addext "subjectAltName = DNS:*.restic"
|
||||
'';
|
||||
};
|
||||
|
||||
restic-server = {
|
||||
files."htpasswd" = { };
|
||||
|
||||
runtimeInputs = [ pkgs.apacheHttpd ];
|
||||
|
||||
# depend on all client's generators
|
||||
dependencies = lib.unique (
|
||||
map (machine: "restic-${machine}") (lib.attrNames roles.client.machines)
|
||||
);
|
||||
|
||||
script = lib.concatMapStringsSep "\n" (machine: ''
|
||||
cat $in/restic-${machine}/restic-rest-pass | \
|
||||
htpasswd -B -i -n ${machine} >> $out/htpasswd
|
||||
'') (lib.attrNames roles.client.machines);
|
||||
};
|
||||
|
||||
}
|
||||
// builtins.listToAttrs (
|
||||
map (machine: {
|
||||
name = "restic-${machine}";
|
||||
value = (mkResticGenerator machine);
|
||||
}) (lib.attrNames roles.client.machines)
|
||||
);
|
||||
|
||||
systemd.paths.restic-htpasswd = {
|
||||
description = "Watch restic-rest .htpasswd for changes";
|
||||
pathConfig = {
|
||||
PathChanged = config.clan.core.vars.generators."restic-server".files."htpasswd".path;
|
||||
Unit = "restic-rest-server.service";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.restic-rest-server.serviceConfig = {
|
||||
|
||||
# TODO not sure if this is the correct fix
|
||||
RestrictAddressFamilies = lib.mkForce "AF_INET AF_INET6";
|
||||
|
||||
LoadCredential = [
|
||||
"restic-cert:${config.clan.core.vars.generators."restic-cert".files."restic-cert".path}"
|
||||
"restic-key:${config.clan.core.vars.generators."restic-cert".files."restic-key".path}"
|
||||
"htpasswd:${config.clan.core.vars.generators."restic-server".files."htpasswd".path}"
|
||||
];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8124 ];
|
||||
|
||||
services.restic.server = rec {
|
||||
enable = true;
|
||||
# prometheus = true;
|
||||
privateRepos = true;
|
||||
listenAddress = "0.0.0.0:8124";
|
||||
dataDir = settings.directory;
|
||||
extraFlags = [
|
||||
"--htpasswd-file=%d/htpasswd"
|
||||
"--listen=${listenAddress}"
|
||||
"--tls"
|
||||
"--tls-cert=%d/restic-cert"
|
||||
"--tls-key=%d/restic-key"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
roles.client = {
|
||||
# TODO
|
||||
};
|
||||
# roles.client = {
|
||||
# interface = {
|
||||
# # These options can be set via 'roles.client.settings'
|
||||
# options.ipRanges = mkOption { type = listOf str; };
|
||||
# };
|
||||
#
|
||||
# # Maps over all instances and produces one result per instance.
|
||||
# perInstance = { instanceName, settings, machine, roles, ... }: {
|
||||
# # Analog to 'perSystem' of flake-parts.
|
||||
# # For every instance of this service we will add a nixosModule to a client-machine
|
||||
# nixosModule = { config, ... }: {
|
||||
# # Interaction examples what you could do here:
|
||||
# # - Get some settings of this machine
|
||||
# # settings.ipRanges
|
||||
# #
|
||||
# # - Get all controller names:
|
||||
# # allControllerNames = lib.attrNames roles.controller.machines
|
||||
# #
|
||||
# # - Get all roles of the machine:
|
||||
# # machine.roles
|
||||
# #
|
||||
# # - Get the settings that where applied to a specific controller machine:
|
||||
# # roles.controller.machines.jon.settings
|
||||
# #
|
||||
# # Add one systemd service for every instance
|
||||
# systemd.services.zerotier-client-${instanceName} = {
|
||||
# # ... depend on the '.config' and 'perInstance arguments'
|
||||
# };
|
||||
# };
|
||||
# }
|
||||
# };
|
||||
|
||||
# Maps over all machines and produces one result per machine.
|
||||
# perMachine = { instances, machine, ... }: {
|
||||
# # Analog to 'perSystem' of flake-parts.
|
||||
# # For every machine of this service we will add exactly one nixosModule to a machine
|
||||
# nixosModule = { config, ... }: {
|
||||
# # Interaction examples what you could do here:
|
||||
# # - Get the name of this machine
|
||||
# # machine.name
|
||||
# #
|
||||
# # - Get all roles of this machine across all instances:
|
||||
# # machine.roles
|
||||
# #
|
||||
# # - Get the settings of a specific instance of a specific machine
|
||||
# # instances.foo.roles.peer.machines.jon.settings
|
||||
# #
|
||||
# # Globally enable something
|
||||
# networking.enable = true;
|
||||
# };
|
||||
# };
|
||||
|
||||
}
|
||||
25
clanServices/restic/flake-module.nix
Normal file
25
clanServices/restic/flake-module.nix
Normal file
@@ -0,0 +1,25 @@
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
restic-module = import ../restic { inherit (inputs) nixpkgs; };
|
||||
in
|
||||
{
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
nixosTestArgs = {
|
||||
inherit pkgs;
|
||||
inherit self;
|
||||
};
|
||||
in
|
||||
{
|
||||
checks = {
|
||||
restic = import ./tests {
|
||||
# inherit (self) clanLib;
|
||||
inherit restic-module
|
||||
pkgs self;
|
||||
} nixosTestArgs;
|
||||
};
|
||||
};
|
||||
|
||||
clan.inventory.modules.restic = restic-module;
|
||||
}
|
||||
81
clanServices/restic/tests/default.nix
Normal file
81
clanServices/restic/tests/default.nix
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
restic-module,
|
||||
...
|
||||
}:
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
nixosTest = (
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
name = "restic";
|
||||
|
||||
clan = {
|
||||
|
||||
/*
|
||||
# Desired inventory for a complete test:
|
||||
inventory = {
|
||||
machines = {
|
||||
# - Two backup servers (destinations)
|
||||
# - Three backup clients:
|
||||
# - client_1 and client_2 should backup to both servers
|
||||
# - client_3 should only backup to server_2
|
||||
server_1 = { };
|
||||
server_2 = { };
|
||||
client_1.tags = [ "backup_to1" "backup_to2" ];
|
||||
client_2.tags = [ "backup_to1" "backup_to2" ];
|
||||
client_3.tags = [ "backup_to1" ];
|
||||
};
|
||||
|
||||
services = {
|
||||
# Apply roles based on tags for clients
|
||||
restic.backup_dest1.roles.client.tags = [ "backup_to1" ];
|
||||
restic.backup_dest2.roles.client.tags = [ "backup_to2" ];
|
||||
|
||||
# Apply roles based on machine names for servers
|
||||
restic.backup_dest1.roles.server.machines = ["server_1"];
|
||||
restic.backup_dest2.roles.server.machines = ["server_2"];
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
inventory = {
|
||||
|
||||
# Define machines
|
||||
machines.client_machine = { };
|
||||
machines.server_machine = { };
|
||||
modules.restic = restic-module;
|
||||
|
||||
instances.test-restic = {
|
||||
|
||||
module.name = "restic";
|
||||
|
||||
roles.server.machines.server_machine = {
|
||||
# Server settings
|
||||
settings.directory = "/var/lib/restic";
|
||||
};
|
||||
|
||||
roles.client.machines.client_machine = {
|
||||
# Client settings
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
directory = ./.;
|
||||
};
|
||||
|
||||
# defaults = { };
|
||||
#
|
||||
# nodes = { };
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
server_machine.wait_for_unit("restic-rest-server")
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
1
clanServices/restic/tests/sops/users/admin/key.json
Normal file
1
clanServices/restic/tests/sops/users/admin/key.json
Normal file
@@ -0,0 +1 @@
|
||||
{"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", "type": "age"}
|
||||
Reference in New Issue
Block a user