From 65165b4a9d637c731d199c0277e21fc326ba9cd9 Mon Sep 17 00:00:00 2001 From: chaos Date: Tue, 10 Oct 2023 22:22:54 +0100 Subject: [PATCH] add caldav/carddav server & redo some htpasswd stuff --- home/apps/thunderbird.nix | 3 + .../hetzner-arm/containers/caldav/default.nix | 58 ++++++ .../containers/caldav/profiles/radicale.nix | 31 ++++ .../containers/caldav/profiles/restic.nix | 39 +++++ .../hetzner-arm/containers/caldav/secrets.nix | 60 +++++++ .../containers/forgejo/secrets.nix | 11 +- hosts/hetzner-arm/containers/mail/secrets.nix | 5 +- .../containers/postgresql/profiles/restic.nix | 5 +- .../containers/postgresql/secrets.nix | 5 +- .../containers/quassel/secrets.nix | 5 +- .../hetzner-arm/containers/social/secrets.nix | 5 +- .../containers/storage/data/ports.nix | 4 + .../containers/storage/default.nix | 3 + .../storage/profiles/rcloneServe.nix | 41 +++++ .../containers/storage/secrets.nix | 82 +++++---- hosts/hetzner-arm/data/containerAddresses.nix | 1 + hosts/hetzner-arm/hetzner-arm.nix | 1 + hosts/lappy-t495/secrets.nix | 6 +- hosts/nixos.nix | 1 + hosts/vault/secrets.nix | 5 +- modules/nixos/postgreSQLRemoteBackup.nix | 165 ++++++++++++++++++ outputs.nix | 2 +- presets/nixos/containerBase.nix | 1 + presets/nixos/desktopBase.nix | 1 + profiles/base/nix.nix | 3 +- 25 files changed, 481 insertions(+), 62 deletions(-) create mode 100644 home/apps/thunderbird.nix create mode 100644 hosts/hetzner-arm/containers/caldav/default.nix create mode 100644 hosts/hetzner-arm/containers/caldav/profiles/radicale.nix create mode 100644 hosts/hetzner-arm/containers/caldav/profiles/restic.nix create mode 100644 hosts/hetzner-arm/containers/caldav/secrets.nix create mode 100644 modules/nixos/postgreSQLRemoteBackup.nix diff --git a/home/apps/thunderbird.nix b/home/apps/thunderbird.nix new file mode 100644 index 0000000..9be04dc --- /dev/null +++ b/home/apps/thunderbird.nix @@ -0,0 +1,3 @@ +{pkgs, ...}: { + home.packages = with pkgs; [thunderbird]; +} diff --git a/hosts/hetzner-arm/containers/caldav/default.nix b/hosts/hetzner-arm/containers/caldav/default.nix new file mode 100644 index 0000000..a4eff24 --- /dev/null +++ b/hosts/hetzner-arm/containers/caldav/default.nix @@ -0,0 +1,58 @@ +{ + self, + hostPath, + tree, + inputs, + config, + pkgs, + ... +}: let + containerAddresses = import "${hostPath}/data/containerAddresses.nix"; + hostIP = containerAddresses.host; + containerIP = containerAddresses.containers.caldav; +in { + containers.caldav = { + autoStart = true; + privateNetwork = true; + hostAddress = hostIP; + localAddress = containerIP; + + specialArgs = { + inherit inputs; + inherit tree; + inherit self; + inherit hostPath; + }; + + config = {...}: { + nixpkgs.pkgs = pkgs; + + imports = with tree; + [ + presets.nixos.containerBase + ./secrets.nix + + #./profiles/postgres.nix + #./profiles/restic.nix + ] + ++ (with hosts.hetzner-arm.containers.caldav.profiles; [ + radicale + restic + ]); + + networking.firewall.allowedTCPPorts = [5232]; + + home-manager.users.root.home.stateVersion = "23.05"; + system.stateVersion = "23.05"; + }; + }; + + services.nginx = { + enable = true; + virtualHosts."radicale.owo.monster" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://${containerIP}:5232"; + }; + }; +} diff --git a/hosts/hetzner-arm/containers/caldav/profiles/radicale.nix b/hosts/hetzner-arm/containers/caldav/profiles/radicale.nix new file mode 100644 index 0000000..0428bd1 --- /dev/null +++ b/hosts/hetzner-arm/containers/caldav/profiles/radicale.nix @@ -0,0 +1,31 @@ +{config, ...}: let + secrets = config.services.secrets.secrets; +in { + services.radicale = { + enable = true; + settings = { + server = { + hosts = ["0.0.0.0:5232" "[::]:5232"]; + }; + auth = { + type = "htpasswd"; + htpasswd_filename = "${secrets.radicale_htpasswd.path}"; + htpasswd_encryption = "bcrypt"; + }; + rights = { + type = "owner_only"; + }; + storage = { + filesystem_folder = "/var/lib/radicale/collections"; + }; + }; + }; + + users.users.radicale.uid = 1000; + users.groups.radicale.gid = 1000; + + systemd.tmpfiles.rules = [ + "d /var/lib/radicale - radicale radicale" + "d /var/lib/radicale/collections - radicale radicale" + ]; +} diff --git a/hosts/hetzner-arm/containers/caldav/profiles/restic.nix b/hosts/hetzner-arm/containers/caldav/profiles/restic.nix new file mode 100644 index 0000000..a39eb45 --- /dev/null +++ b/hosts/hetzner-arm/containers/caldav/profiles/restic.nix @@ -0,0 +1,39 @@ +{ + pkgs, + config, + ... +}: let + secrets = config.services.secrets.secrets; +in { + environment.systemPackages = with pkgs; [ + restic + (pkgs.writeShellScriptBin "restic-caldav" '' + env \ + RESTIC_PASSWORD_FILE=${secrets.restic_password.path} \ + $(cat ${secrets.restic_env.path}) \ + ${pkgs.restic}/bin/restic $@ + '') + ]; + + services.restic.backups.caldav = { + user = "root"; + paths = [ + "/var/lib/radicale" + ]; + + # repository is overrided in environmentFile to contain auth + # make sure to keep up to date when changing repository + repository = "rest:https://storage-restic.owo.monster/CalDAV"; + passwordFile = "${secrets.restic_password.path}"; + environmentFile = "${secrets.restic_env.path}"; + + pruneOpts = [ + "--keep-last 50" + ]; + + timerConfig = { + OnBootSec = "1m"; + OnCalendar = "4h"; + }; + }; +} diff --git a/hosts/hetzner-arm/containers/caldav/secrets.nix b/hosts/hetzner-arm/containers/caldav/secrets.nix new file mode 100644 index 0000000..9cd7731 --- /dev/null +++ b/hosts/hetzner-arm/containers/caldav/secrets.nix @@ -0,0 +1,60 @@ +{pkgs, ...}: { + services.secrets = { + enable = true; + + packages = with pkgs; [ + apacheHttpd + ]; + + vaultLogin = { + enable = true; + loginUsername = "hetzner-arm-container-caldav"; + }; + + autoSecrets = { + enable = true; + }; + + requiredVaultPaths = [ + "api-keys/data/caldav" + "api-keys/data/storage/restic/CalDAV" + "private-public-keys/data/restic/CalDAV" + ]; + + secrets = { + vault_password = { + manual = true; + }; + + radicale_htpasswd = { + user = "radicale"; + group = "radicale"; + fetchScript = '' + if [ -f "$secretFile" ]; then + rm "$secretFile" + fi + + touch "$secretFile" + + data=$(kv_get "/api-keys/caldav" | base64) + for username in $(echo "$data" | base64 -d | jq -r ".data.data | keys | .[]"); do + password=$(echo "$data" | base64 -d | jq -r ".data.data.\"$username\"") + htpasswd -bB "$secretFile" "$username" "$password" 2>/dev/null + done + ''; + }; + + restic_password = { + fetchScript = '' + simple_get "/private-public-keys/restic/CalDAV" .password > "$secretFile" + ''; + }; + restic_env = { + fetchScript = '' + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/CalDAV" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/CalDAV" > "$secretFile" + ''; + }; + }; + }; +} diff --git a/hosts/hetzner-arm/containers/forgejo/secrets.nix b/hosts/hetzner-arm/containers/forgejo/secrets.nix index 06d5033..5ff4ef0 100644 --- a/hosts/hetzner-arm/containers/forgejo/secrets.nix +++ b/hosts/hetzner-arm/containers/forgejo/secrets.nix @@ -1,4 +1,4 @@ -{pkgs, ...}: { +{...}: { services.secrets = { enable = true; @@ -16,10 +16,6 @@ "private-public-keys/data/restic/Forgejo" ]; - packages = with pkgs; [ - apacheHttpd - ]; - secrets = { vault_password = { manual = true; @@ -32,9 +28,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Forgejo" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Forgejo" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Forgejo" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Forgejo" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Forgejo" > "$secretFile" ''; }; }; diff --git a/hosts/hetzner-arm/containers/mail/secrets.nix b/hosts/hetzner-arm/containers/mail/secrets.nix index 145b8cb..4da29bd 100644 --- a/hosts/hetzner-arm/containers/mail/secrets.nix +++ b/hosts/hetzner-arm/containers/mail/secrets.nix @@ -36,9 +36,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Mail" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Mail" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Mail" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Mail" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Mail" > "$secretFile" ''; }; private_mail_aliases = { diff --git a/hosts/hetzner-arm/containers/postgresql/profiles/restic.nix b/hosts/hetzner-arm/containers/postgresql/profiles/restic.nix index 1f53e18..d5e7abd 100644 --- a/hosts/hetzner-arm/containers/postgresql/profiles/restic.nix +++ b/hosts/hetzner-arm/containers/postgresql/profiles/restic.nix @@ -47,14 +47,13 @@ in { inherit backupPrepareCommand; }; - services.postgresqlBackup = { + services.postgreSQLRemoteBackup = { enable = true; - backupAll = false; + backupUser = "postgres"; databases = [ "piped" "gotosocial" "quassel" ]; - compression = "zstd"; }; } diff --git a/hosts/hetzner-arm/containers/postgresql/secrets.nix b/hosts/hetzner-arm/containers/postgresql/secrets.nix index 4798bf6..2ecf56b 100644 --- a/hosts/hetzner-arm/containers/postgresql/secrets.nix +++ b/hosts/hetzner-arm/containers/postgresql/secrets.nix @@ -28,9 +28,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/PostgreSQL" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/PostgreSQL" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/PostgreSQL" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/PostgreSQL" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/PostgreSQL" > "$secretFile" ''; }; }; diff --git a/hosts/hetzner-arm/containers/quassel/secrets.nix b/hosts/hetzner-arm/containers/quassel/secrets.nix index eb5c55d..126d8fe 100644 --- a/hosts/hetzner-arm/containers/quassel/secrets.nix +++ b/hosts/hetzner-arm/containers/quassel/secrets.nix @@ -28,9 +28,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Quassel" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Quassel" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Quassel" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Quassel" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Quassel" > "$secretFile" ''; }; }; diff --git a/hosts/hetzner-arm/containers/social/secrets.nix b/hosts/hetzner-arm/containers/social/secrets.nix index 2a0c8e7..a5e64e8 100644 --- a/hosts/hetzner-arm/containers/social/secrets.nix +++ b/hosts/hetzner-arm/containers/social/secrets.nix @@ -31,9 +31,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Social" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Social" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Social" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Social" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Social" > "$secretFile" ''; }; env_secrets = { diff --git a/hosts/hetzner-arm/containers/storage/data/ports.nix b/hosts/hetzner-arm/containers/storage/data/ports.nix index 2d5c75d..73138d9 100644 --- a/hosts/hetzner-arm/containers/storage/data/ports.nix +++ b/hosts/hetzner-arm/containers/storage/data/ports.nix @@ -2,6 +2,8 @@ rclone_serve_webdav_main = 4200; rclone_serve_webdav_media = 4201; rclone_serve_webdav_music_ro = 4202; + rclone_serve_webdav_public = 4202; + rclone_serve_webdav_uploads = 4202; rclone_serve_restic_music = 4210; rclone_serve_restic_vault = 4211; @@ -10,7 +12,9 @@ rclone_serve_restic_postgresql = 4214; rclone_serve_restic_mail = 4215; rclone_serve_restic_forgejo = 4216; + rclone_serve_restic_caldav = 4217; rclone_serve_http_music = 4220; rclone_serve_http_public = 4221; + rclone_serve_http_uploads_public = 4221; } diff --git a/hosts/hetzner-arm/containers/storage/default.nix b/hosts/hetzner-arm/containers/storage/default.nix index cd24c7e..cae4e83 100644 --- a/hosts/hetzner-arm/containers/storage/default.nix +++ b/hosts/hetzner-arm/containers/storage/default.nix @@ -69,6 +69,7 @@ in { "/Main/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_main}"; "/Media/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_media}"; "/MusicRO/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_music_ro}"; + "/Uploads/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_uploads}"; }; extraConfig = '' client_max_body_size ${clientMaxBodySize}; @@ -81,6 +82,7 @@ in { locations = { "/Music/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_music}"; "/Public/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_public}"; + "/Uploads/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_uploads_public}"; }; extraConfig = '' client_max_body_size ${clientMaxBodySize}; @@ -98,6 +100,7 @@ in { "/PostgreSQL/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_postgresql}"; "/Mail/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_mail}"; "/Forgejo/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_forgejo}"; + "/CalDAV/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_caldav}"; }; extraConfig = '' client_max_body_size ${clientMaxBodySize}; diff --git a/hosts/hetzner-arm/containers/storage/profiles/rcloneServe.nix b/hosts/hetzner-arm/containers/storage/profiles/rcloneServe.nix index 145d0af..130912d 100644 --- a/hosts/hetzner-arm/containers/storage/profiles/rcloneServe.nix +++ b/hosts/hetzner-arm/containers/storage/profiles/rcloneServe.nix @@ -56,6 +56,27 @@ in { "--baseurl=/MusicRO/" ]; } + { + id = "public"; + remote = "StorageBox:Public"; + type = "webdav"; + extraArgs = [ + "--addr=0.0.0.0:${toString ports.rclone_serve_webdav_public}" + "--htpasswd=${secrets.webdav_media_htpasswd.path}" + "--baseurl=/Public/" + ]; + } + { + id = "uploads"; + remote = "StorageBox:Uploads"; + type = "webdav"; + extraArgs = [ + "--addr=0.0.0.0:${toString ports.rclone_serve_webdav_uploads}" + "--htpasswd=${secrets.webdav_uploads_htpasswd.path}" + "--baseurl=/Uploads/" + ]; + } + { id = "music-ro"; remote = "StorageBox:Music"; @@ -76,6 +97,16 @@ in { "--read-only" ]; } + { + id = "uploads-public"; + remote = "StorageBox:Uploads/Public"; + type = "http"; + extraArgs = [ + "--addr=0.0.0.0:${toString ports.rclone_serve_http_uploads_public}" + "--baseurl=/Uploads/" + "--read-only" + ]; + } { id = "restic-music"; remote = "StorageBox:Backups/Restic/Music"; @@ -126,6 +157,16 @@ in { "--baseurl=/PostgreSQL/" ]; } + { + id = "restic-caldav"; + remote = "StorageBox:Backups/Restic/CalDAV"; + type = "restic"; + extraArgs = [ + "--addr=0.0.0.0:${toString ports.rclone_serve_restic_caldav}" + "--htpasswd=${secrets.restic_caldav_htpasswd.path}" + "--baseurl=/CalDAV/" + ]; + } { id = "restic-mail"; remote = "StorageBox:Backups/Restic/Mail"; diff --git a/hosts/hetzner-arm/containers/storage/secrets.nix b/hosts/hetzner-arm/containers/storage/secrets.nix index 288eabf..dd94669 100644 --- a/hosts/hetzner-arm/containers/storage/secrets.nix +++ b/hosts/hetzner-arm/containers/storage/secrets.nix @@ -31,9 +31,11 @@ "api-keys/data/storage/restic/PostgreSQL" "api-keys/data/storage/restic/Mail" "api-keys/data/storage/restic/Forgejo" + "api-keys/data/storage/restic/CalDAV" - "api-keys/data/storage/webdav/main" - "api-keys/data/storage/webdav/media" + "api-keys/data/storage/webdav/Main" + "api-keys/data/storage/webdav/Media" + "api-keys/data/storage/webdav/Public" "private-public-keys/data/rclone/Chaos-Media-Crypt" ]; @@ -75,36 +77,46 @@ sed -i "s/$replace_password/$password/" "$3" sed -i "s/$replace_salt/$salt/" "$3" } + + simple_get_htpasswd() { + if [ -f "$2" ]; then + rm "$2" + fi + + touch "$2" + + data=$(kv_get "$1" | base64) + for username in $(echo "$data" | base64 -d | jq -r ".data.data | keys | .[]"); do + password=$(echo "$data" | base64 -d | jq -r ".data.data.\"$username\"") + htpasswd -b "$2" "$username" "$password" 2>/dev/null + done + } ''; secrets = { - vault_password = {manual = true;}; + vault_password = { + manual = true; + }; restic_music_htpasswd = { user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Music" .username) - password=$(simple_get "/api-keys/storage/restic/Music" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Music" "$secretFile" ''; }; restic_vault_htpasswd = { user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Vault" .username) - password=$(simple_get "/api-keys/storage/restic/Vault" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Vault" "$secretFile" ''; }; restic_social_htpasswd = { user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Social" .username) - password=$(simple_get "/api-keys/storage/restic/Social" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Social" "$secretFile" ''; }; @@ -112,9 +124,7 @@ user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Quassel" .username) - password=$(simple_get "/api-keys/storage/restic/Quassel" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Quassel" "$secretFile" ''; }; @@ -122,9 +132,7 @@ user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/PostgreSQL" .username) - password=$(simple_get "/api-keys/storage/restic/PostgreSQL" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/PostgreSQL" "$secretFile" ''; }; @@ -132,9 +140,7 @@ user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Mail" .username) - password=$(simple_get "/api-keys/storage/restic/Mail" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Mail" "$secretFile" ''; }; @@ -142,9 +148,15 @@ user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/restic/Forgejo" .username) - password=$(simple_get "/api-keys/storage/restic/Forgejo" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/restic/Forgejo" "$secretFile" + ''; + }; + + restic_caldav_htpasswd = { + user = "storage"; + group = "storage"; + fetchScript = '' + simple_get_htpasswd "/api-keys/storage/restic/CalDAV" "$secretFile" ''; }; @@ -152,18 +164,28 @@ user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/webdav/main" .username) - password=$(simple_get "/api-keys/storage/webdav/main" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/webdav/Main" "$secretFile" ''; }; webdav_media_htpasswd = { user = "storage"; group = "storage"; fetchScript = '' - username=$(simple_get "/api-keys/storage/webdav/media" .username) - password=$(simple_get "/api-keys/storage/webdav/media" .password) - htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null + simple_get_htpasswd "/api-keys/storage/webdav/Media" "$secretFile" + ''; + }; + webdav_public_htpasswd = { + user = "storage"; + group = "storage"; + fetchScript = '' + simple_get_htpasswd "/api-keys/storage/webdav/Public" "$secretFile" + ''; + }; + webdav_uploads_htpasswd = { + user = "storage"; + group = "storage"; + fetchScript = '' + simple_get_htpasswd "/api-keys/storage/webdav/Uploads" "$secretFile" ''; }; diff --git a/hosts/hetzner-arm/data/containerAddresses.nix b/hosts/hetzner-arm/data/containerAddresses.nix index cc285f0..adf8814 100644 --- a/hosts/hetzner-arm/data/containerAddresses.nix +++ b/hosts/hetzner-arm/data/containerAddresses.nix @@ -8,5 +8,6 @@ forgejo = "10.0.1.6"; postgresql = "10.0.1.7"; piped-fi = "10.0.1.8"; + caldav = "10.0.1.9"; }; } diff --git a/hosts/hetzner-arm/hetzner-arm.nix b/hosts/hetzner-arm/hetzner-arm.nix index 0c5624f..7b91d2e 100644 --- a/hosts/hetzner-arm/hetzner-arm.nix +++ b/hosts/hetzner-arm/hetzner-arm.nix @@ -28,6 +28,7 @@ in { "piped-fi" "mail" "forgejo" + "caldav" ] (name: ./containers + "/${name}")) ++ (with hosts.hetzner-arm.profiles; [ staticSites diff --git a/hosts/lappy-t495/secrets.nix b/hosts/lappy-t495/secrets.nix index 228f248..ea6a013 100644 --- a/hosts/lappy-t495/secrets.nix +++ b/hosts/lappy-t495/secrets.nix @@ -18,12 +18,12 @@ restic_music_env = { user = "chaos"; fetchScript = '' - api_username=$(simple_get "/api-keys/storage/restic/Music" .username) - api_password=$(simple_get "/api-keys/storage/restic/Music" .password) + api_username=restic + api_password=$(simple_get "/api-keys/storage/restic/Music" ".$api_username") restic_password=$(simple_get "/private-public-keys/restic/Music" .password) echo > "$secretFile" - echo "RESTIC_REPOSITORY=rest:https://''${api_username}:''${api_password}@storage-restic.owo.monster/Music" >> "$secretFile" + echo "RESTIC_REPOSITORY=rest:https://$api_username:$api_password@storage-restic.owo.monster/Music" >> "$secretFile" echo "RESTIC_PASSWORD=''${restic_password}" >> "$secretFile" ''; }; diff --git a/hosts/nixos.nix b/hosts/nixos.nix index 763af1e..1b02f34 100644 --- a/hosts/nixos.nix +++ b/hosts/nixos.nix @@ -35,6 +35,7 @@ tree.modules.nixos.rcloneServe tree.modules.nixos.rcloneSync tree.modules.nixos.secrets + tree.modules.nixos.postgreSQLRemoteBackup ]; nixosUnstableSystem = nixpkgs-unstable.lib.nixosSystem; diff --git a/hosts/vault/secrets.nix b/hosts/vault/secrets.nix index 0b534e8..632dbff 100644 --- a/hosts/vault/secrets.nix +++ b/hosts/vault/secrets.nix @@ -65,9 +65,8 @@ }; restic_env = { fetchScript = '' - RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Vault" .username) - RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Vault" .password) - echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Vault" > "$secretFile" + RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Vault" .restic) + echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Vault" > "$secretFile" ''; }; diff --git a/modules/nixos/postgreSQLRemoteBackup.nix b/modules/nixos/postgreSQLRemoteBackup.nix new file mode 100644 index 0000000..7451346 --- /dev/null +++ b/modules/nixos/postgreSQLRemoteBackup.nix @@ -0,0 +1,165 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (builtins) listToAttrs getAttr; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.options) mkOption mkEnableOption mdDoc; + inherit (lib.strings) optionalString; + inherit (lib.attrsets) attrValues; + inherit (lib) types; + + cfg = config.services.postgreSQLRemoteBackup; +in { + options = { + # TODO: add host, port, user options + services.postgreSQLRemoteBackup = { + enable = mkEnableOption (mdDoc "PostgreSQL database dumps"); + + keepPrev = mkOption { + default = true; + type = types.bool; + description = mdDoc '' + Keep the previous run's backups but rename them to $name.prev + ''; + }; + + startAt = mkOption { + default = "4h"; + type = with types; either (listOf str) str; + description = mdDoc '' + This option defines (see `systemd.time` for format) when the + databases should be dumped. + The default is run every 4 hours. + ''; + }; + + backupUser = mkOption { + default = "root"; + type = types.str; + description = mdDoc '' + User which will be used for backup job and files + ''; + }; + + databases = mkOption { + default = []; + type = types.listOf types.str; + description = mdDoc '' + List of database names to dump. + ''; + }; + + location = mkOption { + default = "/var/backup/postgresql"; + type = types.path; + description = mdDoc '' + Path of directory where the PostgreSQL database dumps will be placed. + ''; + }; + + pgdumpOptions = mkOption { + type = types.separatedString " "; + default = "-C"; + description = mdDoc '' + Command line options for pg_dump. + ''; + }; + + compression = mkOption { + type = types.enum ["none" "zstd"]; + default = "zstd"; + description = mdDoc '' + The type of compression to use on the generated database dump. + ''; + }; + + compressionLevel = mkOption { + type = types.int; + default = 9; + description = mdDoc '' + The compression level used when compression is enabled. + zstd accepts levels 1 to 19. + ''; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d '${cfg.location}' 0700 ${cfg.backupUser} - - -" + ]; + }) + (mkIf (cfg.enable) { + systemd.services = listToAttrs (map (db: { + name = "remotePostgreSQLBackup-${db}"; + value = let + compressSuffixes = { + "none" = ""; + "zstd" = ".zstd"; + }; + compressSuffix = getAttr cfg.compression compressSuffixes; + + compressCmd = getAttr cfg.compression { + "none" = "cat"; + "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel}"; + }; + + mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}"; + curFile = mkSqlPath "" compressSuffix; + prevFile = mkSqlPath ".prev" compressSuffix; + prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes); + inProgressFile = mkSqlPath ".in-progress" compressSuffix; + in { + enable = true; + + description = "Backup of ${db} database(s)"; + + requires = mkIf (config.services.postgresql.enable) [ + "postgresql.service" + ]; + + path = [ + pkgs.coreutils + (let + pgCfg = config.services.postgresql; + in + if pgCfg.enable + then pgCfg.package + else pkgs.postgresql) + ]; + + script = '' + set -e -o pipefail + + umask 0077 # ensure backup is only readable by backup user + + ${optionalString (cfg.keepPrev) '' + if [ -e ${curFile} ]; then + rm -f ${toString prevFiles} + mv ${curFile} ${prevFile} + fi + ''} + + pg_dump ${cfg.pgdumpOptions} ${db} \ + | ${compressCmd} \ + > ${inProgressFile} + + mv ${inProgressFile} ${curFile} + ''; + + serviceConfig = { + Type = "oneshot"; + User = cfg.backupUser; + }; + + startAt = cfg.startAt; + }; + }) + cfg.databases); + }) + ]; +} diff --git a/outputs.nix b/outputs.nix index af8c089..40a5e8e 100644 --- a/outputs.nix +++ b/outputs.nix @@ -141,7 +141,7 @@ in # All machines/containers with secrets.nix machines = rec { "hetzner-arm" = { - containers = ["storage" "music" "quassel" "social" "mail" "postgresql" "piped-fi" "forgejo"]; + containers = ["storage" "music" "quassel" "social" "mail" "postgresql" "piped-fi" "forgejo" "caldav"]; sshAddress = "hetzner-arm.servers.genderfucked.monster"; }; "vault" = { diff --git a/presets/nixos/containerBase.nix b/presets/nixos/containerBase.nix index 2362875..e3ae0a2 100644 --- a/presets/nixos/containerBase.nix +++ b/presets/nixos/containerBase.nix @@ -11,6 +11,7 @@ modules.nixos.rcloneServe modules.nixos.rcloneSync modules.nixos.secrets + modules.nixos.postgreSQLRemoteBackup ]) ++ [ # Default modules which are usually included in nixos.nix diff --git a/presets/nixos/desktopBase.nix b/presets/nixos/desktopBase.nix index ef673bb..638a475 100644 --- a/presets/nixos/desktopBase.nix +++ b/presets/nixos/desktopBase.nix @@ -21,6 +21,7 @@ home.apps.firefox home.apps.telegram + home.apps.thunderbird home.apps.quassel home.apps.session home.apps.toot-cli diff --git a/profiles/base/nix.nix b/profiles/base/nix.nix index bdba7b5..9dbfac0 100644 --- a/profiles/base/nix.nix +++ b/profiles/base/nix.nix @@ -22,8 +22,7 @@ in { config = { allowUnfree = true; - permittedInsecurePackages = [ - ]; + permittedInsecurePackages = []; }; overlays = [ (import ../../overlay)