diff --git a/data/backupSchedules.nix b/data/backupSchedules.nix index 3b4ff09..35f0bf3 100644 --- a/data/backupSchedules.nix +++ b/data/backupSchedules.nix @@ -4,15 +4,15 @@ rec { in { low = { inherit OnStartupSec; - OnUnitActiveSec = "48h"; + OnUnitActiveSec = "108h"; }; medium = { inherit OnStartupSec; - OnUnitActiveSec = "16h"; + OnUnitActiveSec = "96h"; }; high = { inherit OnStartupSec; - OnUnitActiveSec = "6h"; + OnUnitActiveSec = "48h"; }; }; diff --git a/hosts/hetzner-arm/containers/jellyfin/data/rclone_config.template b/hosts/hetzner-arm/containers/jellyfin/data/rclone_config.template deleted file mode 100644 index ba06284..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/data/rclone_config.template +++ /dev/null @@ -1,31 +0,0 @@ -[PutIO-WebDAV] -type = webdav -url = https://webdav.put.io -vendor = other -user = chaoticryptidz -pass = PUTIO_PASSWORD - -[B2-Media-Source] -type = b2 -account = B2_CHAOS_MEDIA_ACCOUNT -key = B2_CHAOS_MEDIA_KEY -hard_delete = true - -[B2-Media] -type = alias -remote = B2-Media-Source:Chaos-Media - -[Media-Source] -type = crypt -remote = B2-Media: -password = STORAGE_MEDIA_CRYPT_PASSWORD -password2 = STORAGE_MEDIA_CRYPT_SALT - -[Media] -type = chunker -remote = Media-Source: -chunk_size = 256Mi - -[Media-Combine] -type = combine -upstreams = "Media=Media:" "PutIO=PutIO-WebDAV:" \ No newline at end of file diff --git a/hosts/hetzner-arm/containers/jellyfin/jellyfin.nix b/hosts/hetzner-arm/containers/jellyfin/jellyfin.nix deleted file mode 100644 index a419f4f..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/jellyfin.nix +++ /dev/null @@ -1,102 +0,0 @@ -{ - self, - tree, - inputs, - pkgs, - config, - lib, - ... -}: let - inherit (lib.lists) flatten; - - containerName = "jellyfin"; - - containerAddresses = import "${self}/hosts/hetzner-arm/data/containerAddresses.nix"; - - hostIP = containerAddresses.host; - containerIP = containerAddresses.containers.${containerName}; -in { - containers.jellyfin = { - autoStart = true; - privateNetwork = true; - hostAddress = hostIP; - localAddress = containerIP; - - bindMounts = { - "/dev/fuse" = { - hostPath = "/dev/fuse"; - isReadOnly = false; - }; - }; - - # Allow rclone mount in container - allowedDevices = [ - { - modifier = "rwm"; - node = "/dev/fuse"; - } - { - modifier = "rwm"; - node = "/dev/mapper/control"; - } - ]; - - specialArgs = { - inherit inputs; - inherit tree; - inherit self; - }; - - config = {...}: { - nixpkgs.pkgs = pkgs; - - imports = flatten (with tree; [ - presets.nixos.containerBase - - (with hosts.hetzner-arm.containers.jellyfin.profiles; [ - mediaMount - jellyfin - restic - ]) - - ./secrets.nix - ]); - - home-manager.users.root.home.stateVersion = "24.05"; - system.stateVersion = "24.05"; - }; - }; - - services.nginx.virtualHosts."jellyfin.owo.monster" = { - forceSSL = true; - enableACME = true; - extraConfig = '' - client_max_body_size 512M; - - # Security / XSS Mitigation Headers - # NOTE: X-Frame-Options may cause issues with the webOS app - add_header X-Frame-Options "SAMEORIGIN"; - add_header X-XSS-Protection "0"; # Do NOT enable. This is obsolete/dangerous - add_header X-Content-Type-Options "nosniff"; - - # COOP/COEP. Disable if you use external plugins/images/assets - add_header Cross-Origin-Opener-Policy "same-origin" always; - add_header Cross-Origin-Embedder-Policy "require-corp" always; - add_header Cross-Origin-Resource-Policy "same-origin" always; - - # Permissions policy. May cause issues on some clients - add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), display-capture=(), document-domain=(), encrypted-media=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=(), serial=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always; - - # Tell browsers to use per-origin process isolation - add_header Origin-Agent-Cluster "?1" always; - ''; - - locations."/" = { - proxyPass = "http://${containerIP}:8096"; - proxyWebsockets = true; - extraConfig = '' - proxy_buffering off; - ''; - }; - }; -} diff --git a/hosts/hetzner-arm/containers/jellyfin/profiles/jellyfin.nix b/hosts/hetzner-arm/containers/jellyfin/profiles/jellyfin.nix deleted file mode 100644 index e838d4d..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/profiles/jellyfin.nix +++ /dev/null @@ -1,8 +0,0 @@ -{...}: { - services.jellyfin = { - enable = true; - openFirewall = true; - }; - users.users.jellyfin.uid = 1000; - users.groups.jellyfin.gid = 1000; -} diff --git a/hosts/hetzner-arm/containers/jellyfin/profiles/mediaMount.nix b/hosts/hetzner-arm/containers/jellyfin/profiles/mediaMount.nix deleted file mode 100644 index dc05ecb..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/profiles/mediaMount.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - pkgs, - ... -}: let - inherit (config.services.secrets) secrets; - rcloneMedia = pkgs.writeShellScriptBin "rclone-media" '' - ${pkgs.rclone}/bin/rclone --config ${secrets.rclone_config.path} "$@" - ''; - mountMedia = pkgs.writeShellScriptBin "mount-media" '' - umount -flR /Media || true - ${rcloneMedia}/bin/rclone-media mount Media-Combine: /Media \ - --allow-other \ - --uid=${toString config.users.users.jellyfin.uid} \ - --gid=${toString config.users.groups.jellyfin.gid} \ - --fast-list \ - --umask=666 \ - --cache-dir=/caches/media \ - --dir-cache-time=1m \ - --vfs-cache-mode=full \ - --vfs-cache-max-size=5g \ - --vfs-cache-max-age=120m \ - --log-level=INFO "$@" - ''; -in { - environment.systemPackages = with pkgs; [ - rclone - rcloneMedia - fuse - fuse3 - mountMedia - ]; - - programs.fuse.userAllowOther = true; - - systemd.services.jellyfin = { - wants = ["media-mount.service"]; - after = ["media-mount.service"]; - serviceConfig.ReadWritePaths = "/Media"; - }; - - systemd.services.media-mount = { - wantedBy = ["jellyfin.service"]; - partOf = ["jellyfin.service"]; - path = with pkgs; [ - fuse - fuse3 - ]; - serviceConfig.ExecStart = "${mountMedia}/bin/mount-media --syslog"; - }; - - systemd.tmpfiles.rules = [ - "d /caches - root root" - "d /caches/media - jellyfin jellyfin" - - "d /Media - jellyfin jellyfin" - - "d /root/.config - root root" - "d /root/.config/rclone - root root" - "L /root/.config/rclone/rclone.conf - - - - ${secrets.rclone_config.path}" - ]; -} diff --git a/hosts/hetzner-arm/containers/jellyfin/profiles/restic.nix b/hosts/hetzner-arm/containers/jellyfin/profiles/restic.nix deleted file mode 100644 index fcd37f4..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/profiles/restic.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ - self, - config, - ... -}: let - inherit (config.services.secrets) secrets; - backupSchedules = import "${self}/data/backupSchedules.nix"; -in { - services.restic.backups.jellyfin = { - user = "root"; - paths = [ - "/var/lib/jellyfin" - ]; - - repository = "s3:s3.eu-central-003.backblazeb2.com/Chaos-Backups/Restic/Jellyfin"; - passwordFile = "${secrets.restic_password.path}"; - environmentFile = "${secrets.restic_env.path}"; - createWrapper = true; - - pruneOpts = ["--keep-last 10"]; - timerConfig = backupSchedules.restic.low; - }; -} diff --git a/hosts/hetzner-arm/containers/jellyfin/secrets.nix b/hosts/hetzner-arm/containers/jellyfin/secrets.nix deleted file mode 100644 index 551276c..0000000 --- a/hosts/hetzner-arm/containers/jellyfin/secrets.nix +++ /dev/null @@ -1,92 +0,0 @@ -{pkgs, ...}: { - services.secrets = { - enable = true; - - packages = with pkgs; [ - # for rclone obscure config file - rclone - ]; - - extraFunctions = '' - replace_slash_for_sed() { - sed "s#/#\\\/#" - } - - simple_get_obscure() { - rclone obscure "$(simple_get "$@")" - } - - simple_get_replace_b2() { - api_account=$(simple_get "$1" .keyID | replace_slash_for_sed) - api_key=$(simple_get "$1" .applicationKey | replace_slash_for_sed) - - replace_account=''${2}_ACCOUNT - replace_key=''${2}_KEY - - sed -i "s/$replace_account/$api_account/" "$3" - sed -i "s/$replace_key/$api_key/" "$3" - } - - simple_get_replace_crypt() { - password=$(simple_get_obscure "$1" .password) - salt=$(simple_get_obscure "$1" .salt) - - replace_password=''${2}_PASSWORD - replace_salt=''${2}_SALT - - sed -i "s/$replace_password/$password/" "$3" - sed -i "s/$replace_salt/$salt/" "$3" - } - ''; - - vaultLogin = { - enable = true; - loginUsername = "hetzner-arm-container-jellyfin"; - }; - - requiredVaultPaths = [ - "api-keys/data/backblaze/Chaos-Media" - "api-keys/data/putio" - "private-public-keys/data/rclone/Chaos-Media-Crypt" - - "api-keys/data/backblaze/Chaos-Backups" - "private-public-keys/data/restic/Jellyfin" - ]; - - secrets = { - vault_password = { - manual = true; - }; - - rclone_config = { - user = "jellyfin"; - group = "jellyfin"; - fetchScript = '' - cp ${./data/rclone_config.template} "$secretFile" - - simple_get_replace_b2 "/api-keys/backblaze/Chaos-Media" "B2_CHAOS_MEDIA" "$secretFile" - - PUTIO_PASSWORD="token/$(simple_get /api-keys/putio .oauth_token)" - PUTIO_PASSWORD="$(rclone obscure "$PUTIO_PASSWORD")" - sed -i "s/PUTIO_PASSWORD/$PUTIO_PASSWORD/" "$secretFile" - - simple_get_replace_crypt "/private-public-keys/rclone/Chaos-Media-Crypt" "STORAGE_MEDIA_CRYPT" "$secretFile" - ''; - }; - - restic_password = { - fetchScript = '' - simple_get "/private-public-keys/restic/Jellyfin" .password > "$secretFile" - ''; - }; - restic_env = { - fetchScript = '' - cat << EOF > "$secretFile" - AWS_ACCESS_KEY_ID=$(simple_get "/api-keys/backblaze/Chaos-Backups" .keyID) - AWS_SECRET_ACCESS_KEY=$(simple_get "/api-keys/backblaze/Chaos-Backups" .applicationKey) - EOF - ''; - }; - }; - }; -} diff --git a/hosts/hetzner-arm/containers/mail/profiles/restic.nix b/hosts/hetzner-arm/containers/mail/profiles/restic.nix index 93ed1ec..7ad9b1f 100644 --- a/hosts/hetzner-arm/containers/mail/profiles/restic.nix +++ b/hosts/hetzner-arm/containers/mail/profiles/restic.nix @@ -21,7 +21,7 @@ in { environmentFile = "${secrets.restic_env.path}"; createWrapper = true; - pruneOpts = ["--keep-last 60"]; + #pruneOpts = ["--keep-last 60"]; timerConfig = backupSchedules.restic.medium; }; } diff --git a/hosts/hetzner-arm/hetzner-arm.nix b/hosts/hetzner-arm/hetzner-arm.nix index 9cbcdf9..7d79f2c 100644 --- a/hosts/hetzner-arm/hetzner-arm.nix +++ b/hosts/hetzner-arm/hetzner-arm.nix @@ -16,7 +16,7 @@ in { profiles.nixos.nginx (forEach [ - "storage" + #"storage" "mail" #"jellyfin" ] (name: ./containers + "/${name}/${name}.nix")) diff --git a/hosts/hetzner-arm/profiles/restic.nix b/hosts/hetzner-arm/profiles/restic.nix index a7ef35c..c875448 100644 --- a/hosts/hetzner-arm/profiles/restic.nix +++ b/hosts/hetzner-arm/profiles/restic.nix @@ -21,6 +21,14 @@ in { ${concatStringsSep "\n" (forEach (attrNames config.services.restic.backups) ( name: "restic-${name} $@" ))} + machinectl shell mail /usr/bin/env restic-mail $@ + '') + + (pkgs.writeShellScriptBin "restic-backup-all" '' + ${concatStringsSep "\n" (forEach (attrNames config.services.restic.backups) ( + name: "systemctl start restic-backups-${name}.service" + ))} + machinectl shell mail /usr/bin/env systemctl start restic-backups-mail.service '') ]; @@ -36,7 +44,7 @@ in { passwordFile = secrets.restic_password_social.path; createWrapper = true; - pruneOpts = ["--keep-last 10"]; + #pruneOpts = ["--keep-last 10"]; timerConfig = backupSchedules.restic.medium; backupPrepareCommand = let @@ -80,7 +88,7 @@ in { passwordFile = secrets.restic_password_forgejo.path; createWrapper = true; - pruneOpts = ["--keep-last 50"]; + #pruneOpts = ["--keep-last 50"]; timerConfig = backupSchedules.restic.high; }; radicale = { @@ -94,7 +102,7 @@ in { passwordFile = secrets.restic_password_radicale.path; createWrapper = true; - pruneOpts = ["--keep-last 50"]; + #pruneOpts = ["--keep-last 50"]; timerConfig = backupSchedules.restic.high; }; vault = {