feat: autobrr

This commit is contained in:
Simon Elsbrock
2025-03-18 20:47:47 +01:00
parent 2e7d3a901d
commit 6652d174dc
2 changed files with 208 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.nixarr.autobrr;
nixarr = config.nixarr;
# Externalize username and group
user = "autobrr";
group = "autobrr";
# Define config format and template
configFormat = pkgs.formats.toml {};
configTemplate = configFormat.generate "autobrr.toml" cfg.settings;
in {
options.nixarr.autobrr = {
enable = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether or not to enable the Autobrr service.
**Required options:** [`nixarr.enable`](#nixarr.enable)
'';
};
package = mkPackageOption pkgs "autobrr" {};
openFirewall = mkOption {
type = types.bool;
defaultText = literalExpression ''!nixarr.autobrr.vpn.enable'';
default = !cfg.vpn.enable;
example = true;
description = "Open firewall for the Autobrr port.";
};
vpn.enable = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
**Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable)
Route Autobrr traffic through the VPN.
'';
};
settings = lib.mkOption {
type = lib.types.submodule {freeformType = configFormat.type;};
default = {
host = "0.0.0.0";
port = 7474;
checkForUpdates = false;
};
example = {
logLevel = "DEBUG";
};
description = ''
Autobrr configuration options.
See https://autobrr.com/configuration/autobrr for more information.
`sessionSecret` is automatically generated upon first installation and will be overridden.
'';
};
stateDir = mkOption {
type = types.path;
default = "${nixarr.stateDir}/autobrr";
defaultText = literalExpression ''"''${nixarr.stateDir}/autobrr"'';
example = "/nixarr/.state/autobrr";
description = "The location of the state directory for the Autobrr service.";
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.vpn.enable -> nixarr.vpn.enable;
message = ''
The nixarr.autobrr.vpn.enable option requires the
nixarr.vpn.enable option to be set, but it was not.
'';
}
{
assertion = cfg.enable -> nixarr.enable;
message = ''
The nixarr.autobrr.enable option requires the nixarr.enable
option to be set, but it was not.
'';
}
];
users = {
groups.${group} = {};
users.${user} = {
isSystemUser = true;
group = group;
};
};
# Create state directory with proper permissions
systemd.tmpfiles.rules = [
"d '${cfg.stateDir}' 0700 ${user} ${group} - -"
];
# Configure the autobrr service
services.autobrr = {
enable = true;
package = cfg.package;
# We need to provide a secretFile even though we're handling it ourselves
secretFile = "/dev/null"; # This is a placeholder that won't be used
settings = mkMerge [
# User settings
cfg.settings
# Override host if VPN is enabled
(mkIf cfg.vpn.enable {host = "192.168.15.1";})
];
};
# Override the autobrr service to use our state directory and session secret handling
systemd.services.autobrr = {
description = "Autobrr";
after = ["syslog.target" "network-online.target"];
wants = ["network-online.target"];
wantedBy = ["multi-user.target"];
path = [pkgs.openssl pkgs.dasel];
serviceConfig = {
Type = "simple";
User = user;
Group = "root";
UMask = 066;
DynamicUser = lib.mkForce false;
# disable state directory
StateDirectory = lib.mkForce null;
ExecStartPre = lib.mkForce (pkgs.writeShellScript "autobrr-config-prep" ''
# Generate session secret if it doesn't exist
SESSION_SECRET_FILE="${cfg.stateDir}/session-secret"
if [ ! -f "$SESSION_SECRET_FILE" ]; then
openssl rand -base64 32 > "$SESSION_SECRET_FILE"
chmod 600 "$SESSION_SECRET_FILE"
fi
# Create config with session secret
SESSION_SECRET=$(cat "$SESSION_SECRET_FILE")
cp '${configTemplate}' "${cfg.stateDir}/config.toml"
chmod 600 "${cfg.stateDir}/config.toml"
${pkgs.dasel}/bin/dasel put -f "${cfg.stateDir}/config.toml" -v "$SESSION_SECRET" -o "${cfg.stateDir}/config.toml" "sessionSecret"
'');
ExecStart = lib.mkForce "${lib.getExe cfg.package} --config ${cfg.stateDir}";
Restart = "on-failure";
};
# Enable and specify VPN namespace to confine service in
vpnConfinement = mkIf cfg.vpn.enable {
enable = true;
vpnNamespace = "wg";
};
};
# Port mappings for VPN
vpnNamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [
{
from = cfg.settings.port;
to = cfg.settings.port;
}
];
};
# Nginx proxy for VPN-confined service
services.nginx = mkIf cfg.vpn.enable {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
virtualHosts."127.0.0.1:${builtins.toString cfg.settings.port}" = {
listen = [
{
addr = "0.0.0.0";
port = cfg.settings.port;
}
];
locations."/" = {
recommendedProxySettings = true;
proxyWebsockets = true;
proxyPass = "http://192.168.15.1:${builtins.toString cfg.settings.port}";
};
};
};
# Open firewall ports if needed
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [cfg.settings.port];
};
};
}
+6
View File
@@ -81,6 +81,10 @@ with lib; let
chown -R jellyseerr:root "${cfg.jellyseerr.stateDir}"
find "${cfg.jellyseerr.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \)
''
+ strings.optionalString cfg.autobrr.enable ''
chown -R autobrr:root "${cfg.autobrr.stateDir}"
find "${cfg.autobrr.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \)
''
+ strings.optionalString cfg.recyclarr.enable ''
chown -R recyclarr:root "${cfg.recyclarr.stateDir}"
find "${cfg.recyclarr.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \)
@@ -88,6 +92,7 @@ with lib; let
};
in {
imports = [
./autobrr
./jellyfin
./jellyseerr
./plex
@@ -141,6 +146,7 @@ in {
- [Sonarr](#nixarr.sonarr.enable)
- [Transmission](#nixarr.transmission.enable)
- [SABnzbd](#nixarr.sabnzbd.enable)
- [Autobrr](#nixarr.autobrr.enable)
Remember to read the options.