Merge pull request #32 from rasmus-kirk/dev

Merge dev
This commit is contained in:
Rasmus Kirk
2024-07-19 07:43:34 +00:00
committed by GitHub
4 changed files with 272 additions and 0 deletions
+21
View File
@@ -27,6 +27,7 @@ with lib; let
fi
chown -R torrenter:media "${cfg.mediaDir}/torrents"
chown -R usenet:media "${cfg.mediaDir}/usenet"
chown -R streamer:media "${cfg.mediaDir}/library"
find "${cfg.mediaDir}" \( -type d -exec chmod 0775 {} + -true \) -o \( -exec chmod 0664 {} + \)
'' + strings.optionalString cfg.jellyfin.enable ''
@@ -35,6 +36,9 @@ with lib; let
'' + strings.optionalString cfg.transmission.enable ''
chown -R torrenter:cross-seed "${cfg.transmission.stateDir}"
find "${cfg.transmission.stateDir}" \( -type d -exec chmod 0750 {} + -true \) -o \( -exec chmod 0640 {} + \)
'' + strings.optionalString cfg.sabnzbd.enable ''
chown -R usenet:root "${cfg.sabnzbd.stateDir}"
find "${cfg.sabnzbd.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \)
'' + strings.optionalString cfg.transmission.privateTrackers.cross-seed.enable ''
chown -R cross-seed:root "${cfg.transmission.privateTrackers.cross-seed.stateDir}"
find "${cfg.transmission.privateTrackers.cross-seed.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \)
@@ -70,6 +74,7 @@ in {
./openssh
./prowlarr
./transmission
./sabnzbd
../util
];
@@ -105,6 +110,7 @@ in {
- [Readarr](#nixarr.readarr.enable)
- [Sonarr](#nixarr.sonarr.enable)
- [Transmission](#nixarr.transmission.enable)
- [SABnzbd](#nixarr.sabnzbd.enable)
Remember to read the options.
'';
@@ -231,6 +237,7 @@ in {
media.members = cfg.mediaUsers;
streamer = {};
torrenter = {};
usenet = {};
};
users.users = {
streamer = {
@@ -241,6 +248,10 @@ in {
isSystemUser = true;
group = "torrenter";
};
usenet = {
isSystemUser = true;
group = "usenet";
};
};
systemd.tmpfiles.rules = [
@@ -259,6 +270,16 @@ in {
"d '${cfg.mediaDir}/torrents/radarr' 0755 torrenter media - -"
"d '${cfg.mediaDir}/torrents/sonarr' 0755 torrenter media - -"
"d '${cfg.mediaDir}/torrents/readarr' 0755 torrenter media - -"
] ++ lists.optionals cfg.sabnzbd.enable [
# only create usenet dirs if sabnzbd is enabled
"d '${cfg.mediaDir}/usenet' 0755 usenet media - -"
"d '${cfg.mediaDir}/usenet/.incomplete' 0755 usenet media - -"
"d '${cfg.mediaDir}/usenet/.watch' 0755 usenet media - -"
"d '${cfg.mediaDir}/usenet/manual' 0775 usenet media - -"
"d '${cfg.mediaDir}/usenet/liadarr' 0775 usenet media - -"
"d '${cfg.mediaDir}/usenet/radarr' 0775 usenet media - -"
"d '${cfg.mediaDir}/usenet/sonarr' 0775 usenet media - -"
"d '${cfg.mediaDir}/usenet/readarr' 0775 usenet media - -"
];
environment.systemPackages = with pkgs; [
+85
View File
@@ -0,0 +1,85 @@
{
config,
pkgs,
lib,
...
}: let
cfg = config.nixarr.sabnzbd;
nixarr = config.nixarr;
ini-file-target = "${cfg.stateDir}/sabnzbd.ini";
concatStringsCommaIfExists = with lib.strings;
stringList: (
optionalString (builtins.length stringList > 0) (
concatStringsSep "," stringList
)
);
user-configs = {
misc = {
host =
if cfg.openFirewall
then "0.0.0.0"
else "127.0.0.1";
port = cfg.guiPort;
download_dir = "${nixarr.mediaDir}/usenet/.incomplete";
complete_dir = "${nixarr.mediaDir}/usenet/manual";
dirscan_dir = "${nixarr.mediaDir}/usenet/watch";
host_whitelist = concatStringsCommaIfExists cfg.whitelistHostnames;
local_ranges = concatStringsCommaIfExists cfg.whitelistRanges;
permissions = "775";
};
};
ini-base-config-file = pkgs.writeTextFile {
name = "base-config.ini";
text = lib.generators.toINI {} user-configs;
};
fix-config-permissions-script = pkgs.writeShellApplication {
name = "sabnzbd-fix-config-permissions";
runtimeInputs = with pkgs; [util-linux];
text = ''
if [ ! -f ${ini-file-target} ]; then
echo 'FAILURE: cannot change permissions of ${ini-file-target}, file does not exist'
exit 1
fi
chmod 600 ${ini-file-target}
chown usenet:media ${ini-file-target}
'';
};
user-configs-to-python-list = with lib;
attrsets.collect (f: !builtins.isAttrs f) (
attrsets.mapAttrsRecursive (
path: value:
"sab_config_map['"
+ (lib.strings.concatStringsSep "']['" path)
+ "'] = '"
+ (builtins.toString value)
+ "'"
)
user-configs
);
apply-user-configs-script = with lib; (pkgs.writers.writePython3Bin
"sabnzbd-set-user-values" {libraries = [pkgs.python3Packages.configobj];} ''
from pathlib import Path
from configobj import ConfigObj
sab_config_path = Path("${ini-file-target}")
if not sab_config_path.is_file() or sab_config_path.suffix != ".ini":
raise Exception(f"{sab_config_path} is not a valid config file path.")
sab_config_map = ConfigObj(str(sab_config_path))
${lib.strings.concatStringsSep "\n" user-configs-to-python-list}
sab_config_map.write()
'');
in {
systemd.tmpfiles.rules = ["C ${cfg.stateDir}/sabnzbd.ini - - - - ${ini-base-config-file}"];
systemd.services.sabnzbd.serviceConfig.ExecStartPre = lib.mkBefore [
("+" + fix-config-permissions-script + "/bin/sabnzbd-fix-config-permissions")
(apply-user-configs-script + "/bin/sabnzbd-set-user-values")
];
}
+154
View File
@@ -0,0 +1,154 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.nixarr.sabnzbd;
nixarr = config.nixarr;
in {
options.nixarr.sabnzbd = {
enable = mkEnableOption "Enable the SABnzbd service.";
stateDir = mkOption {
type = types.path;
default = "${nixarr.stateDir}/sabnzbd";
defaultText = literalExpression ''"''${nixarr.stateDir}/sabnzbd"'';
example = "/nixarr/.state/sabnzbd";
description = ''
The location of the state directory for the SABnzbd service.
**Warning:** Setting this to any path, where the subpath is not
owned by root, will fail! For example:
```nix
stateDir = /home/user/nixarr/.state/sabnzbd
```
Is not supported, because `/home/user` is owned by `user`.
'';
};
guiPort = mkOption {
type = types.port;
default = 8080;
example = 9999;
description = ''
The port that SABnzbd's GUI will listen on for incomming connections.
'';
};
openFirewall = mkOption {
type = types.bool;
defaultText = literalExpression ''!nixarr.SABnzbd.vpn.enable'';
default = !cfg.vpn.enable;
example = true;
description = "Open firewall for SABnzbd";
};
whitelistHostnames = mkOption {
type = types.listOf types.str;
default = [config.networking.hostName];
defaultText = "[ config.networking.hostName ]";
example = ''[ "mediaserv" "media.example.com" ]'';
description = ''
A list that specifies what URLs that are allowed to represent your
SABnzbd instance. If you see an error message like this when
trying to connect to SABnzbd from another device...
```
Refused connection with hostname "your.hostname.com"
```
...then you should add your hostname(s) to this list.
SABnzbd only allows connections matching these URLs in order to prevent
DNS hijacking. See <https://sabnzbd.org/wiki/extra/hostname-check.html>
for more info.
'';
};
whitelistRanges = mkOption {
type = types.listOf types.str;
default = [];
defaultText = "[ ]";
example = ''[ "192.168.1.0/24" "10.0.0.0/23" ]'';
description = ''
A list of IP ranges that will be allowed to connect to SABnzbd's
web GUI. This only needs to be set if SABnzbd needs to be accessed
from another machine besides its host.
'';
};
vpn.enable = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
**Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable)
Route SABnzbd traffic through the VPN.
'';
};
};
imports = [./config.nix];
config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '${cfg.stateDir}' 0700 usenet root - -"
];
services.sabnzbd = {
enable = true;
user = "usenet";
group = "media";
configFile = "${cfg.stateDir}/sabnzbd.ini";
};
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.guiPort];
systemd.services.sabnzbd.serviceConfig = {
Restart = "on-failure";
StartLimitBurst = 5;
};
# Enable and specify VPN namespace to confine service in.
systemd.services.sabnzbd.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
vpnnamespace = "wg";
};
# Port mappings
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [
{
from = cfg.guiPort;
to = cfg.guiPort;
}
];
};
services.nginx = mkIf cfg.vpn.enable {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
virtualHosts."127.0.0.1:${builtins.toString cfg.guiPort}" = {
listen = [
{
addr = "0.0.0.0";
port = cfg.guiPort;
}
];
locations."/" = {
recommendedProxySettings = true;
proxyWebsockets = true;
proxyPass = "http://192.168.15.1:${builtins.toString cfg.guiPort}";
};
};
};
};
}
+12
View File
@@ -234,6 +234,17 @@ in {
description = "Transmission web-UI port.";
};
credentialsFile = mkOption {
type = types.path;
description = ''
Path to a JSON file to be merged with the settings.
Useful to merge a file which is better kept out of the Nix store
to set secret config parameters like `rpc-password`.
'';
default = "/dev/null";
example = "/var/lib/secrets/transmission/settings.json";
};
extraSettings = mkOption {
type = types.attrs;
default = {};
@@ -339,6 +350,7 @@ in {
package = pkgs.transmission_4;
openRPCPort = cfg.openFirewall;
openPeerPorts = cfg.openFirewall;
credentialsFile = cfg.credentialsFile;
settings =
{
download-dir = downloadDir;