fix restic?

This commit is contained in:
chaos 2024-11-08 20:30:19 +00:00
parent d9b081085f
commit 7891d24456
10 changed files with 16 additions and 326 deletions

View file

@ -4,15 +4,15 @@ rec {
in { in {
low = { low = {
inherit OnStartupSec; inherit OnStartupSec;
OnUnitActiveSec = "48h"; OnUnitActiveSec = "108h";
}; };
medium = { medium = {
inherit OnStartupSec; inherit OnStartupSec;
OnUnitActiveSec = "16h"; OnUnitActiveSec = "96h";
}; };
high = { high = {
inherit OnStartupSec; inherit OnStartupSec;
OnUnitActiveSec = "6h"; OnUnitActiveSec = "48h";
}; };
}; };

View file

@ -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:"

View file

@ -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;
'';
};
};
}

View file

@ -1,8 +0,0 @@
{...}: {
services.jellyfin = {
enable = true;
openFirewall = true;
};
users.users.jellyfin.uid = 1000;
users.groups.jellyfin.gid = 1000;
}

View file

@ -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}"
];
}

View file

@ -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;
};
}

View file

@ -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
'';
};
};
};
}

View file

@ -21,7 +21,7 @@ in {
environmentFile = "${secrets.restic_env.path}"; environmentFile = "${secrets.restic_env.path}";
createWrapper = true; createWrapper = true;
pruneOpts = ["--keep-last 60"]; #pruneOpts = ["--keep-last 60"];
timerConfig = backupSchedules.restic.medium; timerConfig = backupSchedules.restic.medium;
}; };
} }

View file

@ -16,7 +16,7 @@ in {
profiles.nixos.nginx profiles.nixos.nginx
(forEach [ (forEach [
"storage" #"storage"
"mail" "mail"
#"jellyfin" #"jellyfin"
] (name: ./containers + "/${name}/${name}.nix")) ] (name: ./containers + "/${name}/${name}.nix"))

View file

@ -21,6 +21,14 @@ in {
${concatStringsSep "\n" (forEach (attrNames config.services.restic.backups) ( ${concatStringsSep "\n" (forEach (attrNames config.services.restic.backups) (
name: "restic-${name} $@" 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; passwordFile = secrets.restic_password_social.path;
createWrapper = true; createWrapper = true;
pruneOpts = ["--keep-last 10"]; #pruneOpts = ["--keep-last 10"];
timerConfig = backupSchedules.restic.medium; timerConfig = backupSchedules.restic.medium;
backupPrepareCommand = let backupPrepareCommand = let
@ -80,7 +88,7 @@ in {
passwordFile = secrets.restic_password_forgejo.path; passwordFile = secrets.restic_password_forgejo.path;
createWrapper = true; createWrapper = true;
pruneOpts = ["--keep-last 50"]; #pruneOpts = ["--keep-last 50"];
timerConfig = backupSchedules.restic.high; timerConfig = backupSchedules.restic.high;
}; };
radicale = { radicale = {
@ -94,7 +102,7 @@ in {
passwordFile = secrets.restic_password_radicale.path; passwordFile = secrets.restic_password_radicale.path;
createWrapper = true; createWrapper = true;
pruneOpts = ["--keep-last 50"]; #pruneOpts = ["--keep-last 50"];
timerConfig = backupSchedules.restic.high; timerConfig = backupSchedules.restic.high;
}; };
vault = { vault = {