forked from aylac.top/nixcfg
this repo has no description

ok so along many things fail2ban actually works now

Changed files
+559 -441
homes
hosts
modules
home
nixos
profiles
backups
services
snippets
tailnet
+3 -3
flake.lock
···
"secrets": {
"flake": false,
"locked": {
-
"lastModified": 1755789931,
-
"narHash": "sha256-4BJKIbWkdajOZxVJhdDROMVtK6WfFbZjNXBt8KRtE18=",
+
"lastModified": 1755914550,
+
"narHash": "sha256-3idodE8ltAGjE+j76EVNHtlHX5cufwdslUCUtDOK4BM=",
"owner": "ayla6",
"repo": "secrets",
-
"rev": "145a93b5fd6db45596d4c7607c0ef91a86aee593",
+
"rev": "3c93b9f5d1b28a8614a9f36a2816ff6369258207",
"type": "github"
},
"original": {
+6 -1
homes/ayla/default.nix
···
imageViewer.package = pkgs.loupe;
pdfViewer.package = pkgs.papers;
terminal.package = pkgs.ptyxis;
-
terminalEditor.package = config.programs.micro.package;
+
terminalEditor.package = config.programs.helix.package;
webBrowser.package = config.programs.firefox.finalPackage;
#webBrowser = {
# exec = lib.getExe config.programs.zen-browser.finalPackage;
···
flare-signal
audacious
audacious-plugins
+
+
zip
+
xz
+
unzip
+
p7zip
];
};
})
+5
hosts/nanpi/default.nix
···
enable = true;
deduplicate = true;
};
+
swap = {
+
enable = true;
+
size = 4096;
+
location = "/.swap";
+
};
arr.enable = true;
};
services = {
+6
hosts/nanpi/glance.nix
···
check-url = "http://${config.mySnippets.tailnet.networkMap.miniflux.hostName}:${toString config.mySnippets.tailnet.networkMap.miniflux.port}/";
icon = "di:miniflux";
}
+
{
+
title = "audiobookshelf";
+
url = "https://${config.mySnippets.tailnet.networkMap.audiobookshelf.vHost}/";
+
check-url = "http://${config.mySnippets.tailnet.networkMap.audiobookshelf.hostName}:${toString config.mySnippets.tailnet.networkMap.audiobookshelf.port}/";
+
icon = "di:miniflux";
+
}
];
}
];
+1
hosts/nanpi/secrets.nix
···
age.secrets = {
cloudflareCertificate.file = "${self.inputs.secrets}/cloudflare/certificate.age";
cloudflareCredentials.file = "${self.inputs.secrets}/cloudflare/credentials.age";
+
cloudflareFail2ban.file = "${self.inputs.secrets}/cloudflare/fail2ban.age";
pds.file = "${self.inputs.secrets}/pds.age";
resticPassword.file = "${self.inputs.secrets}/restic-passwd.age";
rclone.file = "${self.inputs.secrets}/rclone.age";
+70 -148
hosts/nanpi/services.nix
···
...
}: let
dataDirectory = "/var/lib";
+
+
mkCaddyVHosts = services:
+
pkgs.lib.listToAttrs (map (service: let
+
netMap = config.mySnippets.${service.location or "tailnet"}.networkMap.${service.name};
+
flush = service.flushInterval or false;
+
proxyConfig =
+
if flush
+
then ''
+
reverse_proxy ${netMap.hostName}:${toString netMap.port} {
+
flush_interval -1
+
}
+
''
+
else "reverse_proxy ${netMap.hostName}:${toString netMap.port}";
+
in
+
pkgs.lib.nameValuePair "${netMap.vHost}" {
+
extraConfig = ''
+
bind tailscale/${service.name}
+
encode zstd gzip
+
${proxyConfig}
+
'';
+
})
+
services);
+
+
mkCloudflareIngress = services:
+
pkgs.lib.listToAttrs (map (service: let
+
netMap = config.mySnippets.${service.location or "aylac-top"}.networkMap.${service.name};
+
in
+
pkgs.lib.nameValuePair netMap.vHost "http://${netMap.hostName}:${toString netMap.port}")
+
services);
in {
services = {
pds = {
···
certificateFile = config.age.secrets.cloudflareCertificate.path;
credentialsFile = config.age.secrets.cloudflareCredentials.path;
default = "http_status:404";
-
ingress = {
-
"${config.mySnippets.aylac-top.networkMap.pds.vHost}" = "http://${config.mySnippets.aylac-top.networkMap.pds.hostName}:${toString config.mySnippets.aylac-top.networkMap.pds.port}";
-
-
"${config.mySnippets.aylac-top.networkMap.vaultwarden.vHost}" = "http://${config.mySnippets.aylac-top.networkMap.vaultwarden.hostName}:${toString config.mySnippets.aylac-top.networkMap.vaultwarden.port}";
-
-
"${config.mySnippets.aylac-top.networkMap.forgejo.vHost}" = "http://${config.mySnippets.aylac-top.networkMap.forgejo.hostName}:${toString config.mySnippets.aylac-top.networkMap.forgejo.port}";
-
-
"${config.mySnippets.aylac-top.networkMap.ntfy.vHost}" = "http://${config.mySnippets.aylac-top.networkMap.ntfy.hostName}:${toString config.mySnippets.aylac-top.networkMap.ntfy.port}";
-
-
"${config.mySnippets.aylac-top.networkMap.glance.vHost}" = "http://${config.mySnippets.aylac-top.networkMap.glance.hostName}:${toString config.mySnippets.aylac-top.networkMap.glance.port}";
-
};
+
ingress = mkCloudflareIngress [
+
{name = "pds";}
+
{name = "vaultwarden";}
+
{name = "forgejo";}
+
{name = "ntfy";}
+
{name = "glance";}
+
];
};
};
};
-
caddy.virtualHosts = {
-
"${config.mySnippets.tailnet.networkMap.jellyfin.vHost}" = {
-
extraConfig = ''
-
bind tailscale/jellyfin
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.jellyfin.hostName}:${toString config.mySnippets.tailnet.networkMap.jellyfin.port} {
-
flush_interval -1
-
}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.qbittorrent.vHost}" = {
-
extraConfig = ''
-
bind tailscale/qbittorrent
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.qbittorrent.hostName}:${toString config.mySnippets.tailnet.networkMap.qbittorrent.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.radicale.vHost}" = {
-
extraConfig = ''
-
bind tailscale/radicale
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.radicale.hostName}:${toString config.mySnippets.tailnet.networkMap.radicale.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.webdav.vHost}" = {
-
extraConfig = ''
-
bind tailscale/webdav
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.webdav.hostName}:${toString config.mySnippets.tailnet.networkMap.webdav.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.bazarr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/bazarr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.bazarr.hostName}:${toString config.mySnippets.tailnet.networkMap.bazarr.port}
-
'';
-
};
-
-
#"${config.mySnippets.tailnet.networkMap.lidarr.vHost}" = {
-
# extraConfig = ''
-
# bind tailscale/lidarr
-
# encode zstd gzip
-
# reverse_proxy ${config.mySnippets.tailnet.networkMap.lidarr.hostName}:${toString config.mySnippets.tailnet.networkMap.lidarr.port}
-
# '';
-
#};
-
-
"${config.mySnippets.tailnet.networkMap.prowlarr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/prowlarr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.prowlarr.hostName}:${toString config.mySnippets.tailnet.networkMap.prowlarr.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.radarr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/radarr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.radarr.hostName}:${toString config.mySnippets.tailnet.networkMap.radarr.port}
-
'';
-
};
+
caddy.virtualHosts = mkCaddyVHosts [
+
{
+
name = "jellyfin";
+
flushInterval = true;
+
}
+
{name = "qbittorrent";}
+
{name = "radicale";}
+
{name = "webdav";}
+
{name = "bazarr";}
+
{name = "prowlarr";}
+
{name = "radarr";}
+
{name = "sonarr";}
+
{name = "autobrr";}
+
{name = "glance";}
+
{name = "karakeep";}
+
{name = "copyparty";}
+
{name = "redlib";}
+
{name = "miniflux";}
+
{name = "jellyseerr";}
+
{name = "audiobookshelf";}
+
];
-
"${config.mySnippets.tailnet.networkMap.sonarr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/sonarr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.sonarr.hostName}:${toString config.mySnippets.tailnet.networkMap.sonarr.port}
-
'';
-
};
+
#immich = {
+
# enable = true;
+
# host = "0.0.0.0";
+
# mediaLocation = "${dataDirectory}/immich";
+
# openFirewall = true;
+
# inherit (config.mySnippets.tailnet.networkMap.immich) port;
+
#};
-
"${config.mySnippets.tailnet.networkMap.autobrr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/autobrr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.autobrr.hostName}:${toString config.mySnippets.tailnet.networkMap.autobrr.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.glance.vHost}" = {
-
extraConfig = ''
-
bind tailscale/glance
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.glance.hostName}:${toString config.mySnippets.tailnet.networkMap.glance.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.karakeep.vHost}" = {
-
extraConfig = ''
-
bind tailscale/karakeep
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.karakeep.hostName}:${toString config.mySnippets.tailnet.networkMap.karakeep.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.copyparty.vHost}" = {
-
extraConfig = ''
-
bind tailscale/copyparty
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.copyparty.hostName}:${toString config.mySnippets.tailnet.networkMap.copyparty.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.redlib.vHost}" = {
-
extraConfig = ''
-
bind tailscale/redlib
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.redlib.hostName}:${toString config.mySnippets.tailnet.networkMap.redlib.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.miniflux.vHost}" = {
-
extraConfig = ''
-
bind tailscale/miniflux
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.miniflux.hostName}:${toString config.mySnippets.tailnet.networkMap.miniflux.port}
-
'';
-
};
-
-
"${config.mySnippets.tailnet.networkMap.jellyseerr.vHost}" = {
-
extraConfig = ''
-
bind tailscale/jellyseerr
-
encode zstd gzip
-
reverse_proxy ${config.mySnippets.tailnet.networkMap.jellyseerr.hostName}:${toString config.mySnippets.tailnet.networkMap.jellyseerr.port}
-
'';
-
};
+
audiobookshelf = {
+
enable = true;
+
host = "0.0.0.0";
+
openFirewall = true;
+
inherit (config.mySnippets.tailnet.networkMap.audiobookshelf) port;
};
-
# it's failing to build because it can't download some stuff
-
# immich = {
-
# enable = true;
-
# host = "0.0.0.0";
-
# mediaLocation = "${dataDirectory}/immich";
-
# openFirewall = true;
-
# inherit (config.mySnippets.tailnet.networkMap.immich) port;
-
# };
-
vaultwarden = {
enable = true;
···
SIGNUPS_ALLOWED = false;
ICON_SERVICE = "bitwarden";
ICON_CACHE_TTL = 0;
+
IP_HEADER = "CF-Connecting-IP";
};
environmentFile = config.age.secrets.vaultwarden.path;
+1 -1
modules/home/default.nix
···
./programs
./services
./profiles
-
./packages.nix
+
./snippets
inputs.agenix.homeManagerModules.default
inputs.zen-browser.homeModules.beta
];
-9
modules/home/packages.nix
···
-
{pkgs, ...}: {
-
home.packages = with pkgs; [
-
# --- System Utilities ---
-
zip
-
xz
-
unzip
-
p7zip
-
];
-
}
-1
modules/home/programs/default.nix
···
./chromium
./fastfetch
./firefox
-
./firefox-based
./helix
./git
./micro
modules/home/programs/firefox-based/betterfox/fastfox.nix modules/home/snippets/firefox-based/betterfox/fastfox.nix
modules/home/programs/firefox-based/betterfox/peskyfox.nix modules/home/snippets/firefox-based/betterfox/peskyfox.nix
modules/home/programs/firefox-based/betterfox/securefox.nix modules/home/snippets/firefox-based/betterfox/securefox.nix
modules/home/programs/firefox-based/betterfox/smoothfox.nix modules/home/snippets/firefox-based/betterfox/smoothfox.nix
-1
modules/home/programs/firefox-based/default.nix modules/home/snippets/firefox-based/default.nix
···
snowflake
sponsorblock
karakeep
-
ff2mpv
bitwarden
];
modules/home/programs/firefox-based/engines.nix modules/home/snippets/firefox-based/engines.nix
+116
modules/home/programs/helix/default.nix
···
{
config,
lib,
+
pkgs,
...
}: {
options.myHome.programs.helix.enable = lib.mkEnableOption "helix";
···
o = "extend_char_right";
};
};
+
};
+
languages = {
+
language-server = {
+
bash-language-server = {
+
command = "bash-language-server";
+
args = ["start"];
+
};
+
+
vscode-css-languageserver = {
+
command = lib.getExe pkgs.vscode-css-languageserver;
+
args = ["--stdio"];
+
};
+
+
fish-lsp = {
+
command = lib.getExe pkgs.fish-lsp;
+
args = ["--stdio"];
+
};
+
+
lua-language-server = {
+
command = lib.getExe pkgs.lua-language-server;
+
args = ["--stdio"];
+
};
+
+
marksman = {
+
command = lib.getExe pkgs.marksman;
+
args = ["--stdio"];
+
};
+
+
nixd = {
+
command = lib.getExe pkgs.nixd;
+
};
+
+
vscode-json-languageserver = {
+
command = lib.getExe pkgs.vscode-json-languageserver;
+
args = ["--stdio"];
+
};
+
+
typescript-language-server = with pkgs.nodePackages; {
+
command = "${typescript-language-server}/bin/typescript-language-server";
+
args = ["--stdio" "--tsserver-path=${typescript}/lib/node_modules/typescript/lib"];
+
};
+
+
superhtml = {
+
command = lib.getExe pkgs.superhtml;
+
args = ["--stdio"];
+
};
+
};
+
+
language = [
+
{
+
name = "bash";
+
auto-format = true;
+
file-types = ["sh" "bash" "dash" "ksh" "mksh"];
+
+
formatter = {
+
command = lib.getExe pkgs.shfmt;
+
args = ["-i" "2"];
+
};
+
+
language-servers = ["bash-language-server"];
+
}
+
{
+
name = "css";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.prettier;};
+
language-servers = ["vscode-css-languageserver"];
+
}
+
{
+
name = "fish";
+
auto-format = true;
+
language-servers = ["fish-lsp"];
+
}
+
{
+
name = "html";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.prettier;};
+
language-servers = ["superhtml"];
+
}
+
{
+
name = "javascript";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.prettier;};
+
language-servers = ["typescript-language-server"];
+
}
+
{
+
name = "json";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.prettier;};
+
language-servers = ["vscode-json-languageserver"];
+
}
+
{
+
name = "lua";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.stylua;};
+
language-servers = ["lua-language-server"];
+
}
+
{
+
name = "markdown";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.mdformat;};
+
language-servers = ["marksman"];
+
}
+
{
+
name = "nix";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.alejandra;};
+
language-servers = ["nixd"];
+
}
+
{
+
name = "typescript";
+
auto-format = true;
+
formatter = {command = lib.getExe pkgs.prettier;};
+
language-servers = ["typescript-language-server"];
+
}
+
];
};
};
};
+5
modules/home/snippets/default.nix
···
+
{...}: {
+
imports = [
+
./firefox-based
+
];
+
}
+211 -264
modules/nixos/profiles/backups/default.nix
···
https://${config.mySnippets.aylac-top.networkMap.ntfy.vHost}/${channel}
'';
-
backupDestinationA = "rclone:a_gdrive:/backups/${config.networking.hostName}";
-
mkRepoA = service: "${backupDestinationA}/${service}";
-
#backupDestinationB = "rclone:b_gdrive:/backups/${config.networking.hostName}";
-
#mkRepoB = service: "${backupDestinationB}/${service}";
+
repoMap = {
+
A = "rclone:a_gdrive:/backups/${config.networking.hostName}";
+
B = "rclone:b_gdrive:/backups/${config.networking.hostName}";
+
};
+
mkRepo = {
+
repo,
+
service,
+
}: "${repoMap.${repo}}/${service}";
-
stop = service: ''
+
stop = {
+
service,
+
repoPath,
+
}: ''
#!${pkgs.bash}/bin/bash
${mkNotify {
-
message = "Backing up ${service}, stopping service";
+
message = "Backing up ${service} to ${repoPath}, stopping service";
channel = "network-status";
}}
${pkgs.systemd}/bin/systemctl stop ${service}
'';
-
start = service: ''
+
start = {
+
service,
+
repoPath,
+
}: ''
#!${pkgs.bash}/bin/bash
${mkNotify {
-
message = "Back up for ${service} was completed (idk if successfully tho), starting service";
+
message = "Back up for ${service} to ${repoPath} was completed (idk if successfully tho), starting service";
channel = "network-status";
}}
${pkgs.systemd}/bin/systemctl start ${service}
'';
-
prepareNoService = service: ''
+
prepareNoService = {
+
service,
+
repoPath,
+
}: ''
#!${pkgs.bash}/bin/bash
${mkNotify {
-
message = "Backing up ${service}";
+
message = "Backing up ${service} to ${repoPath}";
channel = "network-status";
}}
'';
-
cleanupNoService = service: ''
+
cleanupNoService = {
+
service,
+
repoPath,
+
}: ''
#!${pkgs.bash}/bin/bash
${mkNotify {
-
message = "Back up for ${service} was completed (idk if successfully tho)";
+
message = "Back up for ${service} to ${repoPath} was completed (idk if successfully tho)";
channel = "network-status";
}}
'';
-
in {
-
options.myNixOS.profiles.backups = {
-
enable = lib.mkEnableOption "automatically back up enabled services to gdrive";
-
};
-
config = lib.mkIf config.myNixOS.profiles.backups.enable {
-
services.restic.backups = {
-
audiobookshelf = lib.mkIf config.services.audiobookshelf.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "audiobookshelf";
-
backupPrepareCommand = stop "audiobookshelf";
-
paths = [config.services.audiobookshelf.dataDir];
-
repository = mkRepoA "audiobookshelf";
-
}
-
);
-
-
bazarr = lib.mkIf config.services.bazarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "bazarr";
-
backupPrepareCommand = stop "bazarr";
-
paths = [config.services.bazarr.dataDir];
-
repository = mkRepoA "bazarr";
-
}
-
);
-
-
couchdb = lib.mkIf config.services.couchdb.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "couchdb";
-
backupPrepareCommand = stop "couchdb";
-
paths = [config.services.couchdb.databaseDir];
-
repository = mkRepoA "couchdb";
-
}
-
);
-
-
forgejo = lib.mkIf (config.services.forgejo.enable && config.services.forgejo.settings.storage.STORAGE_TYPE != "minio") (
-
config.mySnippets.restic
-
// {
-
paths = [config.services.forgejo.stateDir];
-
repository = mkRepoA "forgejo";
-
}
-
);
-
-
# immich = lib.mkIf config.services.immich.enable (
-
# config.mySnippets.restic
-
# // {
-
# backupCleanupCommand = start "immich-server";
-
# backupPrepareCommand = stop "immich-server";
-
#
-
# paths = [
-
# "${config.services.immich.mediaLocation}/library"
-
# "${config.services.immich.mediaLocation}/profile"
-
# "${config.services.immich.mediaLocation}/upload"
-
# "${config.services.immich.mediaLocation}/backups"
-
# ];
-
#
-
# repository = mkRepoB "immich";
-
# }
-
# );
-
-
jellyfin = lib.mkIf config.services.jellyfin.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "jellyfin";
-
backupPrepareCommand = stop "jellyfin";
-
paths = [config.services.jellyfin.dataDir];
-
repository = mkRepoA "jellyfin";
-
}
-
);
+
mkBackups = services:
+
lib.listToAttrs (map (service: let
+
repoKey = service.repo or "A";
+
repoPath = mkRepo {
+
repo = repoKey;
+
service = service.name;
+
};
+
systemdService = service.name;
+
backupMode = service.backupMode or "stop"; # "stop", "notify", "none"
-
lidarr = lib.mkIf config.services.lidarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "lidarr";
-
backupPrepareCommand = stop "lidarr";
-
paths = [config.services.lidarr.dataDir];
-
repository = mkRepoA "lidarr";
+
commands =
+
if backupMode == "stop"
+
then {
+
backupCleanupCommand = start {
+
service = systemdService;
+
inherit repoPath;
+
};
+
backupPrepareCommand = stop {
+
service = systemdService;
+
inherit repoPath;
+
};
}
-
);
-
-
ombi = lib.mkIf config.services.ombi.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "ombi";
-
backupPrepareCommand = stop "ombi";
-
paths = [config.services.ombi.dataDir];
-
repository = mkRepoA "ombi";
+
else if backupMode == "notify"
+
then {
+
backupCleanupCommand = cleanupNoService {
+
service = service.name;
+
inherit repoPath;
+
};
+
backupPrepareCommand = prepareNoService {
+
service = service.name;
+
inherit repoPath;
+
};
}
-
);
-
-
pds = lib.mkIf config.services.pds.enable (
+
else {};
+
in
+
lib.nameValuePair service.name (
config.mySnippets.restic
// {
-
backupCleanupCommand = start "pds";
-
backupPrepareCommand = stop "pds";
-
paths = [config.services.pds.settings.PDS_DATA_DIRECTORY];
-
repository = mkRepoA "pds";
+
repository = repoPath;
+
inherit (service) paths;
}
-
);
+
// commands
+
// (service.extraConfig or {})
+
)) (lib.filter (s: s.enable) services));
+
in {
+
options.myNixOS.profiles.backups = {
+
enable = lib.mkEnableOption "automatically back up enabled services";
+
};
-
plex = lib.mkIf config.services.plex.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "plex";
-
backupPrepareCommand = stop "plex";
+
config = lib.mkIf config.myNixOS.profiles.backups.enable {
+
services.restic.backups = mkBackups [
+
{
+
name = "audiobookshelf";
+
inherit (config.services.audiobookshelf) enable;
+
paths = [config.services.audiobookshelf.dataDir];
+
}
+
{
+
name = "bazarr";
+
inherit (config.services.bazarr) enable;
+
paths = [config.services.bazarr.dataDir];
+
}
+
{
+
name = "couchdb";
+
inherit (config.services.couchdb) enable;
+
paths = [config.services.couchdb.databaseDir];
+
}
+
{
+
name = "forgejo";
+
enable = config.services.forgejo.enable && config.services.forgejo.settings.storage.STORAGE_TYPE != "minio";
+
paths = [config.services.forgejo.stateDir];
+
backupMode = "none";
+
}
+
# {
+
# name = "immich";
+
# inherit (config.services.immich) enable;
+
# name = "immich-server";
+
# paths = [
+
# "${config.services.immich.mediaLocation}/library"
+
# "${config.services.immich.mediaLocation}/profile"
+
# "${config.services.immich.mediaLocation}/upload"
+
# "${config.services.immich.mediaLocation}/backups"
+
# ];
+
# repo = "B";
+
# }
+
{
+
name = "jellyfin";
+
inherit (config.services.jellyfin) enable;
+
paths = [config.services.jellyfin.dataDir];
+
}
+
{
+
name = "lidarr";
+
inherit (config.services.lidarr) enable;
+
paths = [config.services.lidarr.dataDir];
+
}
+
{
+
name = "ombi";
+
inherit (config.services.ombi) enable;
+
paths = [config.services.ombi.dataDir];
+
}
+
{
+
name = "pds";
+
inherit (config.services.pds) enable;
+
paths = [config.services.pds.settings.PDS_DATA_DIRECTORY];
+
}
+
{
+
name = "plex";
+
inherit (config.services.plex) enable;
+
paths = [config.services.plex.dataDir];
+
extraConfig = {
exclude = ["${config.services.plex.dataDir}/Plex Media Server/Plug-in Support/Databases"];
-
paths = [config.services.plex.dataDir];
-
repository = mkRepoA "plex";
-
}
-
);
-
-
postgresql = lib.mkIf config.services.postgresql.enable (
-
config.mySnippets.restic
-
// {
-
paths = [config.services.postgresql.dataDir];
-
repository = mkRepoA "postgresql";
-
}
-
);
-
-
prowlarr = lib.mkIf config.services.prowlarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "prowlarr";
-
backupPrepareCommand = stop "prowlarr";
-
paths = [config.services.prowlarr.dataDir];
-
repository = mkRepoA "prowlarr";
-
}
-
);
-
-
qbittorrent = lib.mkIf config.myNixOS.services.qbittorrent.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "qbittorrent";
-
backupPrepareCommand = stop "qbittorrent";
-
paths = [config.myNixOS.services.qbittorrent.dataDir];
-
repository = mkRepoA "qbittorrent";
-
}
-
);
-
-
radarr = lib.mkIf config.services.radarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "radarr";
-
backupPrepareCommand = stop "radarr";
-
paths = [config.services.radarr.dataDir];
-
repository = mkRepoA "radarr";
-
}
-
);
-
-
readarr = lib.mkIf config.services.readarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "readarr";
-
backupPrepareCommand = stop "readarr";
-
paths = [config.services.readarr.dataDir];
-
repository = mkRepoA "readarr";
-
}
-
);
-
-
sonarr = lib.mkIf config.services.sonarr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "sonarr";
-
backupPrepareCommand = stop "sonarr";
-
paths = [config.services.sonarr.dataDir];
-
repository = mkRepoA "sonarr";
-
}
-
);
-
-
autobrr = lib.mkIf config.services.autobrr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "autobrr";
-
backupPrepareCommand = stop "autobrr";
-
paths = ["${config.myNixOS.profiles.arr.dataDir}/autobrr"];
-
repository = mkRepoA "autobrr";
-
}
-
);
-
-
tautulli = lib.mkIf config.services.tautulli.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "tautulli";
-
backupPrepareCommand = stop "tautulli";
-
paths = [config.services.tautulli.dataDir];
-
repository = mkRepoA "tautulli";
-
}
-
);
-
-
uptime-kuma = lib.mkIf config.services.uptime-kuma.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "uptime-kuma";
-
backupPrepareCommand = stop "uptime-kuma";
-
paths = ["/var/lib/uptime-kuma"];
-
repository = mkRepoA "uptime-kuma";
-
}
-
);
-
-
vaultwarden = lib.mkIf config.services.vaultwarden.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "vaultwarden";
-
backupPrepareCommand = stop "vaultwarden";
-
paths = ["/var/lib/vaultwarden"];
-
repository = mkRepoA "vaultwarden";
-
}
-
);
-
-
passwords = lib.mkIf (builtins.elem config.networking.hostName config.mySnippets.syncthing.folders."Passwords".devices) (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = cleanupNoService "passwords";
-
backupPrepareCommand = prepareNoService "passwords";
-
paths = [config.mySnippets.syncthing.folders."Passwords".path];
-
repository = mkRepoA "passwords";
-
}
-
);
-
-
radicale = lib.mkIf config.services.radicale.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "radicale";
-
backupPrepareCommand = stop "radicale";
-
paths = ["/var/lib/radicale"];
-
repository = mkRepoA "radicale";
-
}
-
);
-
-
webdav = lib.mkIf config.services.webdav-server-rs.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = cleanupNoService "webdav";
-
backupPrepareCommand = prepareNoService "webdav";
-
paths = ["/var/lib/webdav"];
-
repository = mkRepoA "webdav";
-
}
-
);
-
-
miniflux = lib.mkIf config.services.miniflux.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "miniflux";
-
backupPrepareCommand = stop "miniflux";
-
paths = ["/var/lib/miniflux"];
-
repository = mkRepoA "miniflux";
-
}
-
);
-
-
jellyseerr = lib.mkIf config.services.jellyseerr.enable (
-
config.mySnippets.restic
-
// {
-
backupCleanupCommand = start "jellyseerr";
-
backupPrepareCommand = stop "jellyseerr";
-
paths = ["/var/lib/jellyseerr"];
-
repository = mkRepoA "jellyseerr";
-
}
-
);
-
};
+
};
+
}
+
{
+
name = "postgresql";
+
inherit (config.services.postgresql) enable;
+
paths = [config.services.postgresql.dataDir];
+
backupMode = "none";
+
}
+
{
+
name = "prowlarr";
+
inherit (config.services.prowlarr) enable;
+
paths = [config.services.prowlarr.dataDir];
+
}
+
{
+
name = "qbittorrent";
+
inherit (config.services.qbittorrent) enable;
+
paths = [config.services.qbittorrent.dataDir];
+
}
+
{
+
name = "radarr";
+
inherit (config.services.radarr) enable;
+
paths = [config.services.radarr.dataDir];
+
}
+
{
+
name = "readarr";
+
inherit (config.services.readarr) enable;
+
paths = [config.services.readarr.dataDir];
+
}
+
{
+
name = "sonarr";
+
inherit (config.services.sonarr) enable;
+
paths = [config.services.sonarr.dataDir];
+
}
+
{
+
name = "autobrr";
+
inherit (config.services.autobrr) enable;
+
paths = ["${config.myNixOS.profiles.arr.dataDir}/autobrr"];
+
}
+
{
+
name = "tautulli";
+
inherit (config.services.tautulli) enable;
+
paths = [config.services.tautulli.dataDir];
+
}
+
{
+
name = "uptime-kuma";
+
inherit (config.services.uptime-kuma) enable;
+
paths = ["/var/lib/uptime-kuma"];
+
}
+
{
+
name = "vaultwarden";
+
inherit (config.services.vaultwarden) enable;
+
paths = ["/var/lib/vaultwarden"];
+
}
+
{
+
name = "passwords";
+
enable = builtins.elem config.networking.hostName config.mySnippets.syncthing.folders."Passwords".devices;
+
paths = [config.mySnippets.syncthing.folders."Passwords".path];
+
backupMode = "notify";
+
}
+
{
+
name = "radicale";
+
inherit (config.services.radicale) enable;
+
paths = ["/var/lib/radicale"];
+
}
+
{
+
name = "webdav";
+
inherit (config.services.webdav-server-rs) enable;
+
paths = ["/var/lib/webdav"];
+
backupMode = "notify";
+
}
+
{
+
name = "miniflux";
+
inherit (config.services.miniflux) enable;
+
paths = ["/var/lib/miniflux"];
+
}
+
{
+
name = "jellyseerr";
+
inherit (config.services.jellyseerr) enable;
+
paths = ["/var/lib/jellyseerr"];
+
}
+
];
};
}
+88
modules/nixos/services/fail2ban/README.md
···
+
my actual config for cloudflare is hidden because duh there's api tokens in there so here's the one i'm using
+
+
because of the way cloudflare bans work, the jails don't act independently when banning an ip, so if vaultwarden blocks one ip, the ip can't access anything on aylac.top anymore. i don't think there's a way of having independent ones with cloudflare, but honestly it doesn't change much for me does it
+
+
a modified version of:
+
https://github.com/l4rm4nd/F2BFilters/blob/main/data/action.d/action-ban-cloudflare.conf
+
+
```conf
+
#
+
# Author: Mike Rushton
+
#
+
# IMPORTANT
+
#
+
# Please set jail.local's permission to 640 because it contains your CF API key.
+
#
+
# This action depends on curl (and optionally jq).
+
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
+
#
+
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
+
#
+
# CloudFlare API error codes: https://www.cloudflare.com/docs/host-api.html#s4.2
+
+
[Definition]
+
+
# Option: actionstart
+
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
+
# Values: CMD
+
#
+
#actionstart = bash /data/action.d/telegram_notif.sh -a start
+
+
# Option: actionstop
+
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
+
# Values: CMD
+
#
+
#actionstop = bash /data/action.d/telegram_notif.sh -a stop
+
+
# Option: actioncheck
+
# Notes.: command executed once before each actionban command
+
# Values: CMD
+
#
+
actioncheck =
+
+
# Option: actionban
+
# Notes.: command executed when banning an IP. Take care that the
+
# command is executed with Fail2Ban user rights.
+
# Tags: <ip> IP address
+
# <failures> number of failures
+
# <time> unix timestamp of the ban time
+
# Values: CMD
+
#
+
# API v1
+
#actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
+
# API v4
+
actionban = curl -s -o /dev/null -X POST <_cf_api_prms> \
+
-d '{"mode":"block","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"Fail2Ban <name>"}' \
+
<_cf_api_url>
+
+
# Option: actionunban
+
# Notes.: command executed when unbanning an IP. Take care that the
+
# command is executed with Fail2Ban user rights.
+
# Tags: <ip> IP address
+
# <failures> number of failures
+
# <time> unix timestamp of the ban time
+
# Values: CMD
+
#
+
# API v1
+
#actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
+
# API v4
+
actionunban = id=$(curl -s -X GET <_cf_api_prms> \
+
"<_cf_api_url>?mode=block&configuration_target=<cftarget>&configuration_value=<ip>&page=1&per_page=1&notes=Fail2Ban%%20<name>" \
+
| { jq -r '.result[0].id' 2>/dev/null || tr -d '\n' | sed -nE 's/^.*"result"\s*:\s*\[\s*\{\s*"id"\s*:\s*"([^"]+)".*$/\1/p'; })
+
if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
+
curl -s -o /dev/null -X DELETE <_cf_api_prms> "<_cf_api_url>/$id"
+
+
_cf_api_url = https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules
+
_cf_api_prms = -H 'Authorization: Bearer <cftoken>' -H 'Content-Type: application/json'
+
+
[Init]
+
+
# cloudflare API token
+
# https://dash.cloudflare.com/profile/api-tokens create a custom api here and enable account firewall access rules both read and write
+
cftoken = <putithere>
+
+
cftarget = ip
+
+
[Init?family=inet6]
+
cftarget = ip6
+
```
+36 -8
modules/nixos/services/fail2ban/default.nix
···
pkgs,
...
}: let
-
# idk how to share this across files :(
mkNotify = {
message,
channel,
priority ? 1,
}: ''
-
LOGIN=$(cat "${config.age.secrets.ntfyAuto.path}")
-
${pkgs.curl}/bin/curl -u $LOGIN \
-
-H "X-Priority: ${toString priority}" \
+
curl -u $(cat "${config.age.secrets.ntfyAuto.path}") \
+
-H "X-Priority: ${toString priority}" \
-d '${message}' \
https://${config.mySnippets.aylac-top.networkMap.ntfy.vHost}/${channel}
'';
···
config = lib.mkIf config.myNixOS.services.fail2ban.enable {
environment.etc = {
+
"fail2ban/action.d/mycloudflare.conf" = {
+
user = "root";
+
group = "root";
+
mode = "0640";
+
source = config.age.secrets.cloudflareFail2ban.path;
+
};
+
"fail2ban/action.d/ntfy.conf".text = ''
[Definition]
-
actionbanned = ${mkNotify {
-
message = "Banned <ip> from <jail> at ${config.networking.hostName}";
-
channel = "network-status";
+
actionban = ${mkNotify {
+
message = "Arrested <ip> for trying to rob <name> at ${config.networking.hostName}";
+
channel = "fail2ban";
priority = 3;
}}
+
actionunban = ${mkNotify {
+
message = "Released <ip> from the jail at ${config.networking.hostName}";
+
channel = "fail2ban";
+
priority = 2;
+
}}
'';
"fail2ban/filter.d/forgejo.conf".text = ''
···
ignoreIP = ["100.64.0.0/10"];
bantime = "24h";
bantime-increment.enable = true;
+
extraPackages = [pkgs.curl pkgs.jq pkgs.uutils-coreutils-noprefix];
jails = {
forgejo.settings = {
-
action = "iptables-allports";
+
action = ''
+
mycloudflare
+
iptables-allports
+
ntfy'';
bantime = 900;
filter = "forgejo";
findtime = 3600;
···
# HTTP basic-auth failures, 5 tries → 1-day ban
nginx-http-auth = {
settings = {
+
action = ''
+
mycloudflare
+
iptables-allports
+
ntfy'';
enabled = true;
maxretry = 5;
findtime = 300;
···
# Generic scanner / bot patterns (wp-login.php, sqladmin, etc.)
nginx-botsearch = {
settings = {
+
action = ''
+
mycloudflare
+
iptables-allports
+
ntfy'';
enabled = true;
maxretry = 10;
findtime = 300;
···
filter = vaultwarden
port = 80,443,${toString config.services.vaultwarden.config.ROCKET_PORT}
maxretry = 5
+
action = mycloudflare
+
iptables-allports
+
ntfy
'';
vaultwarden-admin = ''
···
maxretry = 3
bantime = 14400
findtime = 14400
+
action = mycloudflare
+
iptables-allports
+
ntfy
'';
};
};
+11 -5
modules/snippets/tailnet/default.nix
···
vHost = "jellyfin.${config.mySnippets.tailnet.name}";
};
-
immich = {
-
hostName = "nanpi";
-
port = 2283;
-
vHost = "immich.${config.mySnippets.tailnet.name}";
-
};
+
#immich = {
+
# hostName = "nanpi";
+
# port = 2283;
+
# vHost = "immich.${config.mySnippets.tailnet.name}";
+
#};
radicale = {
hostName = "nanpi";
···
hostName = "nanpi";
port = 5055;
vHost = "jellyseerr.${config.mySnippets.tailnet.name}";
+
};
+
+
audiobookshelf = {
+
hostName = "nanpi";
+
port = 13378;
+
vHost = "audiobookshelf.${config.mySnippets.tailnet.name}";
};
};
};