feat: create backups module
We currently have backups via a shell script on the machine
vermilion.bravo. Unfortunately, we can't activate these backups without
setting up a user, and that wasn't done yet for teal.
This commit adds backups, and also enforces their activation by failing
to evaluate if a backups key has not been provided and backups have not
been explicitly disabled.
Change-Id: Ia37bd13cb8de6e20cc77e735630a59cb0c5d0fb4
Reviewed-on: https://git.clicks.codes/c/Infra/NixFiles/+/756
Tested-by: Skyler Grey <minion@clicks.codes>
Reviewed-by: Samuel Shuert <coded@clicks.codes>
diff --git a/modules/nixos/clicks/security/doas/default.nix b/modules/nixos/clicks/security/doas/default.nix
index 424d9d7..9778b00 100644
--- a/modules/nixos/clicks/security/doas/default.nix
+++ b/modules/nixos/clicks/security/doas/default.nix
@@ -28,6 +28,10 @@
noPass = true;
keepEnv = true;
}
+ {
+ users = config.clicks.users.backups;
+ noPass = true;
+ }
];
};
diff --git a/modules/nixos/clicks/users/backups/README.md b/modules/nixos/clicks/users/backups/README.md
new file mode 100644
index 0000000..7c49c68
--- /dev/null
+++ b/modules/nixos/clicks/users/backups/README.md
@@ -0,0 +1,52 @@
+<!--
+SPDX-FileCopyrightText: 2024 Clicks Codes
+
+SPDX-License-Identifier: GPL-3.0-only
+-->
+
+# The backups user
+
+The backups user is not a "regular" clicks user. It's used by our backup
+infrastructure to SSH into the machine and create btrfs snapshots.
+
+Here's the script we use to backup. We run it once a day via cron
+```sh
+#!/bin/sh
+
+SERVER=$DOAS_USER
+HOME=$(eval echo ~$SERVER)
+
+LAST=$(ls $HOME/snapshots -1 | tail -n1)
+CURRENT=$(date -u -I)
+
+AREA=$(cat $HOME/area)
+
+DATA=$(cat $HOME/data_dir || echo "/")
+SNAPSHOTS=$(cat $HOME/snapshots_dir || echo $DATA/snapshots/)
+
+echo "Backing up $CURRENT incrementally (comparing to $LAST)"
+
+echo "Ensuring $SERVER.$AREA has a $SNAPSHOTS directory"
+echo " $SERVER# mkdir -p $SNAPSHOTS"
+ssh backups@$SERVER.$AREA.clicks.domains -i /root/.ssh/id_$SERVER "doas mkdir -p $SNAPSHOTS"
+
+echo "Creating a snapshot on $SERVER.$AREA"
+echo " $SERVER# btrfs subvolume snapshot -r $DATA ${SNAPSHOTS}${CURRENT}"
+ssh backups@$SERVER.$AREA.clicks.domains -i /root/.ssh/id_$SERVER "doas btrfs subvolume snapshot -r $DATA ${SNAPSHOTS}${CURRENT}"
+
+echo "Sending backup from $SERVER.$AREA and receiving it locally"
+echo " $SERVER# btrfs send -p ${SNAPSHOTS}${LAST} ${SNAPSHOTS}${CURRENT} | @# btrfs receive $HOME/snapshots"
+ssh backups@$SERVER.$AREA.clicks.domains -i /root/.ssh/id_$SERVER "doas btrfs send -p ${SNAPSHOTS}${LAST} ${SNAPSHOTS}${CURRENT}" | pv | btrfs receive $HOME/snapshots
+
+echo "Completed incremental backup"
+```
+
+For impermanence machines, DATA and SNAPSHOTS should be explicitly set to
+directories that won't get wiped. For example, `teal.alpha` has `/persist/data`
+as the data dir, and `/persist/snapshots` as the snapshots dir.
+
+DATA must be set to the root of a BTRFS subvolume.
+
+As someone trying to set up a Clicks server, you should contact
+`minion@clicks.codes`, who manages our backups. She will be able to help you set
+up backups for the server and maintain them over its lifetime.
diff --git a/modules/nixos/clicks/users/backups/default.nix b/modules/nixos/clicks/users/backups/default.nix
new file mode 100644
index 0000000..66fc8ce
--- /dev/null
+++ b/modules/nixos/clicks/users/backups/default.nix
@@ -0,0 +1,38 @@
+# SPDX-FileCopyrightText: 2024 Clicks Codes
+#
+# SPDX-License-Identifier: GPL-3.0-only
+
+{ lib, config, ... }: let
+ cfg = config.clicks.backups;
+in {
+ options = {
+ clicks.backups = {
+ key = lib.mkOption {
+ type = lib.types.str;
+ description = "The public key to use for backups";
+ };
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = "It is mandatory for any Clicks server to have the backups enabled, please only disable this if you are backing up in a different way";
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ clicks.users.backups = ["backups"];
+
+ users.users.backups = {
+ isNormalUser = true;
+ group = "backups";
+
+ extraGroups = ["wheel"];
+
+ openssh.authorizedKeys.keys = [
+ cfg.key
+ ];
+ };
+
+ users.groups.backups = { };
+ };
+}
diff --git a/modules/nixos/clicks/users/default.nix b/modules/nixos/clicks/users/default.nix
index 4e169ce..798d98b 100644
--- a/modules/nixos/clicks/users/default.nix
+++ b/modules/nixos/clicks/users/default.nix
@@ -10,10 +10,17 @@
}:
{
options = {
- clicks.users.deployers = lib.mkOption {
- type = lib.types.listOf lib.types.str;
- default = [ ];
- description = "Users who should be allowed to deploy to this server. They will be given passwordless access to run commands as root";
+ clicks.users = {
+ deployers = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = "Users who should be allowed to deploy to this server. They will be given passwordless access to run commands as root";
+ };
+ backups = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = "Accounts used to create snapshots to backup this server. They will be given passwordless access to run commands as root";
+ };
};
};
}