-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from rlad78/sabnzbd
WIP: SABnzbd module for usenet integration
- Loading branch information
Showing
3 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}"; | ||
}; | ||
}; | ||
}; | ||
}; | ||
} |