fix restic?
This commit is contained in:
parent
d9b081085f
commit
7891d24456
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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}";
|
||||
createWrapper = true;
|
||||
|
||||
pruneOpts = ["--keep-last 60"];
|
||||
#pruneOpts = ["--keep-last 60"];
|
||||
timerConfig = backupSchedules.restic.medium;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ in {
|
|||
profiles.nixos.nginx
|
||||
|
||||
(forEach [
|
||||
"storage"
|
||||
#"storage"
|
||||
"mail"
|
||||
#"jellyfin"
|
||||
] (name: ./containers + "/${name}/${name}.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 = {
|
||||
|
|
Loading…
Reference in a new issue