fix restic?
This commit is contained in:
parent
d9b081085f
commit
7891d24456
|
@ -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";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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:"
|
|
|
@ -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;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{...}: {
|
|
||||||
services.jellyfin = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true;
|
|
||||||
};
|
|
||||||
users.users.jellyfin.uid = 1000;
|
|
||||||
users.groups.jellyfin.gid = 1000;
|
|
||||||
}
|
|
|
@ -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}"
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in a new issue