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/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 = { };
+  };
+}