dns module

This commit is contained in:
Vivian 2022-09-27 14:05:24 +02:00
parent e3771d84c4
commit 8e0a0ffe71
9 changed files with 115 additions and 10 deletions

View file

@ -0,0 +1,8 @@
{ ... }: {
imports = [
./dns.nix
./flood.nix
./unpackerr.nix
./vmagent.nix
];
}

View file

@ -0,0 +1,95 @@
{ config, pkgs, lib, hosts, flat_hosts, ... }:
# DNS Module to set up Unbound DNS with all my hosts in the config
# Used for DNS Servers and my laptop
with lib;
let
inherit (builtins) filter hasAttr attrNames;
domains = attrNames hosts;
ipv4Host = filter (hasAttr "ip") flat_hosts;
ipv6Hosts = filter (hasAttr "ip6") flat_hosts;
localData = { hostname, realm, ip, ... }: ''"${hostname}.${realm}. A ${ip}"'';
local6Data = { hostname, realm, ip6, ... }: ''"${hostname}.${realm}. AAAA ${ip6}"'';
ptrData = { hostname, realm, ip, ... }: ''"${ip} ${hostname}.${realm}"'';
ptr6Data = { hostname, realm, ip6, ... }: ''"${ip6} ${hostname}.${realm}"'';
cfg = config.services.v.dns;
in
{
options.services.v.dns = {
enable = mkEnableOption "v.dns";
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open port 53 in the firwall for unbound dns
'';
};
mode = mkOption {
type = types.strMatching "^(server|laptop)$";
default = "laptop";
description = ''
Whether to configure the DNS in server mode (listen on all interfaces) or laptop mode (just on localhost)
'';
};
};
config = mkIf cfg.enable {
networking.firewall = mkIf (cfg.openFirewall) {
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
services.unbound = {
enable = true;
package = pkgs.v.unbound;
settings = {
server = mkMerge [
{
use-syslog = "yes";
module-config = ''"validator iterator"'';
local-zone = map (localdomain: ''"${localdomain}}." transparent'') domains;
local-data = (map localData ipv4Host) ++ (map local6Data ipv6Hosts);
local-data-ptr = (map ptrData ipv4Host) ++ (map ptr6Data ipv6Hosts);
private-address = [
"127.0.0.0/8"
"10.0.0.0/8"
"::ffff:a00:0/104"
"172.16.0.0/12"
"::ffff:ac10:0/108"
"169.254.0.0/16"
"::ffff:a9fe:0/112"
"192.168.0.0/16"
"::ffff:c0a8:0/112"
"fd00::/8"
"fe80::/10"
];
}
(mkIf (cfg.mode == "server") {
interface-automatic = "yes";
interface = [ "0.0.0.0" "::0" ];
access-control = [
"127.0.0.1/32 allow_snoop"
"::1 allow_snoop"
"10.42.0.0/16 allow"
"127.0.0.0/8 allow"
"192.168.0.0/23 allow"
"192.168.2.0/24 allow"
"::1/128 allow"
];
})
(mkIf (cfg.mode == "laptop") {
interface = [ "127.0.0.1" "::1" ];
access-control = [
"127.0.0.1/32 allow_snoop"
"::1 allow_snoop"
];
})
];
};
};
};
}

View file

@ -0,0 +1,152 @@
{ config, pkgs, lib, ... }:
with lib;
let cfg = config.services.flood;
in {
options.services.flood = {
enable = mkEnableOption "flood";
user = mkOption {
default = "flood";
type = types.str;
description = ''
User account under which flood runs.
'';
};
group = mkOption {
type = types.str;
default = "rtorrent";
description = ''
Group under which flood runs.
Flood needs to have the correct permissions if accessing rtorrent through the socket.
'';
};
package = mkOption {
type = types.package;
default = pkgs.flood;
defaultText = "pkgs.flood";
description = ''
The flood package to use.
'';
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address flood binds to.
'';
};
port = mkOption {
type = types.port;
default = 3000;
description = ''
The flood web port.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open the firewall for the port in <option>services.flood.port</option>.
'';
};
rpcSocket = mkOption {
type = types.str;
readOnly = true;
default = "/run/rtorrent/rpc.sock";
description = ''
RPC socket path.
(Only used when auth=none).
'';
};
dataDir = mkOption {
type = types.str;
default = "/var/lib/flood";
description = ''
The directory where flood stores its data files.
'';
};
downloadDir = mkOption {
type = types.str;
default = "/var/lib/rtorrent/download";
description = ''
Root directory for downloaded files.
'';
};
authMode = mkOption {
type = types.str;
default = "none";
description = ''
Access control and user management method.
Either 'default' or 'none'.
'';
};
ssl = mkOption {
type = types.bool;
default = false;
description = ''
Enable SSL.
key.pem and fullchain.pem needed in runtime directory.
'';
};
baseURI = mkOption {
type = types.str;
default = "/";
description = ''
This URI will prefix all of Flood's HTTP requests
'';
};
};
config = mkIf cfg.enable {
# Create group if set to default
users.groups = mkIf (cfg.group == "rtorrent") { rtorrent = { }; };
# Create user if set to default
users.users = mkIf (cfg.user == "flood") {
flood = {
group = cfg.group;
shell = pkgs.bashInteractive;
home = cfg.dataDir;
description = "flood Daemon user";
isSystemUser = true;
};
};
# Open firewall if option is set to do so.
networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [ cfg.port ];
# The actual service
systemd.services.flood = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
description = "flood system service";
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Type = "simple";
Restart = "on-failure";
WorkingDirectory = cfg.dataDir;
ExecStart =
"${cfg.package}/bin/flood --baseuri ${cfg.baseURI} --rundir ${cfg.dataDir} --host ${cfg.host} --port ${
toString cfg.port
} ${
if cfg.ssl then "--ssl" else ""
} --auth ${cfg.authMode} --rtsocket ${cfg.rpcSocket} --allowedpath ${cfg.downloadDir}";
};
};
# This is needed to create the dataDir with the correct permissions.
systemd.tmpfiles.rules = [ "d '${cfg.dataDir}' 0755 ${cfg.user} ${cfg.group} -" ];
};
}

View file

@ -0,0 +1,314 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.unpackerr;
mkStarrOptions = { name, url }: {
url = mkOption {
type = types.str;
default = "";
example = "${url}";
description = ''
The URL to access ${name}
'';
};
apiKey = mkOption {
type = types.str;
default = "";
description = ''
The API key for accessing ${name}
'';
};
paths = mkOption {
type = types.str;
default = "";
example = "/downloads,/moreDownloads";
description = ''
List of paths where content is downloaded for ${name}
'';
};
protocols = mkOption {
type = types.str;
default = "torrent";
example = "torrent,usenet";
description = ''
Protocols to process
'';
};
timeout = mkOption {
type = types.str;
default = "10s";
description = ''
How long to wait for ${name} to respond
'';
};
deleteOrginal = mkOption {
type = types.bool;
default = false;
description = ''
Delete archives after import?
Recommend not setting this to true
'';
};
deleteDelay = mkOption {
type = types.str;
default = "5m";
description = ''
Extracts are deleted this long after import. `-1` to disable.
'';
};
};
in {
options.services.unpackerr = {
enable = mkEnableOption "unpackerr";
user = mkOption {
default = "unpackerr";
type = types.str;
description = ''
User account under which unpackerr runs.
'';
};
group = mkOption {
type = types.str;
default = "unpackerr";
description = ''
Group under which unpackerr runs.
'';
};
package = mkOption {
type = types.package;
default = pkgs.unpackerr;
defaultText = "pkgs.unpackerr";
description = ''
The unpackerr package to use.
'';
};
debug = mkOption {
type = types.bool;
default = false;
description = ''
Turns on more logs.
'';
};
interval = mkOption {
type = types.str;
default = "2m";
description = ''
How often apps are polled, recommended 1m to 5m
'';
};
startDelay = mkOption {
type = types.str;
default = "1m";
description = ''
Files are queued at least this long before extraction
'';
};
retryDelay = mkOption {
type = types.str;
default = "5m";
description = ''
Failed extractions are retried after at least this long
'';
};
maxRetries = mkOption {
type = types.int;
default = 3;
description = ''
Times to retry failed extractions. `0` = unlimited.
'';
};
parallel = mkOption {
type = types.int;
default = 1;
description = ''
Concurrent extractions, 1 is recommended.
'';
};
fileMode = mkOption {
type = types.str;
default = "0644";
description = ''
Extracted files are written with this mode
'';
};
dirMode = mkOption {
type = types.str;
default = "0755";
description = ''
Extracted folders are written with this mode
'';
};
sonarr = mkStarrOptions {
name = "Sonarr";
url = "http://localhost:8989";
};
radarr = mkStarrOptions {
name = "Radarr";
url = "http://localhost:7878";
};
lidarr = mkStarrOptions {
name = "Lidarr";
url = "http://localhost:8686";
};
readarr = mkStarrOptions {
name = "Readarr";
url = "http://localhost:8787";
};
folder = {
path = mkOption {
type = types.str;
default = "";
description = ''
folder path, not for Starr apps.
'';
};
extractPath = mkOption {
type = types.str;
default = "";
description = ''
Where to extract to, Defaults to <option>services.unpackerr.folder.path</option>.
'';
};
deleteAfter = mkOption {
type = types.str;
default = "";
example = "10m";
description = ''
Delete extracted files and/or archives after this duration, `0` to disable.
'';
};
deleteOrginal = mkOption {
type = types.bool;
default = false;
description = ''
Delete archives after extraction
'';
};
deleteFiles = mkOption {
type = types.bool;
default = false;
description = ''
Delete extracted files after successful extraction
'';
};
moveBack = mkOption {
type = types.bool;
default = false;
description = ''
Move extracted items back into original folder
'';
};
};
extraConfig = mkOption {
type = types.attrs;
default = { };
description = ''
Extra environment variables
'';
example = { UN_WEBHOOK_0_URL = "http://example.com"; };
};
};
config = mkIf cfg.enable {
# Create group if set to default
users.groups = mkIf (cfg.group == "unpackerr") { unpackerr = { }; };
# Create user if set to default
users.users = mkIf (cfg.user == "unpackerr") {
unpackerr = {
group = cfg.group;
shell = pkgs.bashInteractive;
createHome = false;
description = "unpackerr Daemon user";
isSystemUser = true;
};
};
# The actual service
systemd.services.unpackerr = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
description = "unpackerr system service";
# Filter out all unset variables else unpackerr complains
environment = filterAttrs (n: v: stringLength v > 0) {
# General options
UN_DEBUG = "${toString cfg.debug}";
UN_INTERVAL = "${cfg.interval}";
UN_START_DELAY = "${cfg.startDelay}";
UN_RETRY_DELAY = "${cfg.retryDelay}";
UN_MAX_RETRIES = "${toString cfg.maxRetries}";
UN_PARALLEL = "${toString cfg.parallel}";
UN_FILE_MODE = "${cfg.fileMode}";
UN_DIR_MODE = "${cfg.dirMode}";
# Sonarr
UN_SONARR_0_URL = "${cfg.sonarr.url}";
UN_SONARR_0_API_KEY = "${cfg.sonarr.apiKey}";
UN_SONARR_0_PATHS_0 = "${cfg.sonarr.paths}";
UN_SONARR_0_PROTOCOLS = "${cfg.sonarr.protocols}";
UN_SONARR_0_TIMEOUT = "${cfg.sonarr.timeout}";
UN_SONARR_0_DELETE_ORIG = "${toString cfg.sonarr.deleteOrginal}";
UN_SONARR_0_DELETE_DELAY = "${cfg.sonarr.deleteDelay}";
# Radarr
UN_RADARR_0_URL = "${cfg.radarr.url}";
UN_RADARR_0_API_KEY = "${cfg.radarr.apiKey}";
UN_RADARR_0_PATHS_0 = "${cfg.radarr.paths}";
UN_RADARR_0_PROTOCOLS = "${cfg.radarr.protocols}";
UN_RADARR_0_TIMEOUT = "${cfg.radarr.timeout}";
UN_RADARR_0_DELETE_ORIG = "${toString cfg.radarr.deleteOrginal}";
UN_RADARR_0_DELETE_DELAY = "${cfg.radarr.deleteDelay}";
# Lidarr
UN_LIDARR_0_URL = "${cfg.lidarr.url}";
UN_LIDARR_0_API_KEY = "${cfg.lidarr.apiKey}";
UN_LIDARR_0_PATHS_0 = "${cfg.lidarr.paths}";
UN_LIDARR_0_PROTOCOLS = "${cfg.lidarr.protocols}";
UN_LIDARR_0_TIMEOUT = "${cfg.lidarr.timeout}";
UN_LIDARR_0_DELETE_ORIG = "${toString cfg.lidarr.deleteOrginal}";
UN_LIDARR_0_DELETE_DELAY = "${cfg.lidarr.deleteDelay}";
# Readarr
UN_READARR_0_URL = "${cfg.readarr.url}";
UN_READARR_0_API_KEY = "${cfg.readarr.apiKey}";
UN_READARR_0_PATHS_0 = "${cfg.readarr.paths}";
UN_READARR_0_PROTOCOLS = "${cfg.readarr.protocols}";
UN_READARR_0_TIMEOUT = "${cfg.readarr.timeout}";
UN_READARR_0_DELETE_ORIG = "${toString cfg.readarr.deleteOrginal}";
UN_READARR_0_DELETE_DELAY = "${cfg.readarr.deleteDelay}";
# Folder
UN_FOLDER_0_PATH = "${cfg.folder.path}";
UN_FOLDER_0_EXTRACT_PATH = "${cfg.folder.extractPath}";
UN_FOLDER_0_DELETE_AFTER = "${cfg.folder.deleteAfter}";
UN_FOLDER_0_DELETE_ORIGINAL = "${toString cfg.folder.deleteOrginal}";
UN_FOLDER_0_DELETE_FILES = "${toString cfg.folder.deleteFiles}";
UN_FOLDER_0_MOVE_BACK = "${toString cfg.folder.moveBack}";
} // cfg.extraConfig;
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Type = "simple";
Restart = "on-failure";
ExecStart = "${cfg.package}/bin/unpackerr";
};
};
};
}

View file

@ -0,0 +1,108 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.vmagent;
settingsFormat = pkgs.formats.json { };
in
{
options.services.vmagent = {
enable = mkEnableOption "vmagent";
user = mkOption {
default = "vmagent";
type = types.str;
description = ''
User account under which vmagent runs.
'';
};
group = mkOption {
type = types.str;
default = "vmagent";
description = ''
Group under which vmagent runs.
'';
};
package = mkOption {
default = pkgs.vmagent;
defaultText = "pkgs.vmagent";
type = types.package;
description = ''
vmagent package to use.
'';
};
dataDir = mkOption {
type = types.str;
default = "/var/lib/vmagent";
description = ''
The directory where vmagent stores its data files.
'';
};
remoteWriteUrl = mkOption {
default = "http://localhost:8428/api/v1/write";
type = types.str;
description = ''
The remote storage endpoint such as VictoriaMetrics
'';
};
prometheusConfig = mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
};
description = ''
Config for prometheus style metrics
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open the firewall for the default ports.
'';
};
};
config = mkIf cfg.enable {
# Create group if set to default
users.groups = mkIf (cfg.group == "vmagent") { vmagent = { }; };
# Create user if set to default
users.users = mkIf (cfg.user == "vmagent") {
vmagent = {
group = cfg.group;
shell = pkgs.bashInteractive;
description = "vmagent Daemon user";
home = cfg.dataDir;
isSystemUser = true;
};
};
# Open firewall if option is set to do so.
networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [ 8429 ];
# The actual service
systemd.services.vmagent =
let prometheusConfig = settingsFormat.generate "prometheusConfig.yaml" cfg.prometheusConfig;
in {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
description = "vmagent system service";
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Type = "simple";
Restart = "on-failure";
WorkingDirectory = cfg.dataDir;
ExecStart =
"${cfg.package}/bin/vmagent -remoteWrite.url=${cfg.remoteWriteUrl} -promscrape.config=${prometheusConfig}";
};
};
systemd.tmpfiles.rules = [ "d '${cfg.dataDir}' 0755 ${cfg.user} ${cfg.group} -" ];
};
}