diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..275befb --- /dev/null +++ b/flake.lock @@ -0,0 +1,192 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1705332421, + "narHash": "sha256-USpGLPme1IuqG78JNqSaRabilwkCyHmVWY0M9vYyqEA=", + "owner": "numtide", + "repo": "devshell", + "rev": "83cb93d6d063ad290beee669f4badf9914cc16ec", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-root": { + "locked": { + "lastModified": 1692742795, + "narHash": "sha256-f+Y0YhVCIJ06LemO+3Xx00lIcqQxSKJHXT/yk1RTKxw=", + "owner": "srid", + "repo": "flake-root", + "rev": "d9a70d9c7a5fd7f3258ccf48da9335e9b47c3937", + "type": "github" + }, + "original": { + "owner": "srid", + "repo": "flake-root", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1708294481, + "narHash": "sha256-DZtxmeb4OR7iCaKUUuq05ADV2rX8WReZEF7Tq//W0+Y=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "a54e05bc12d88ff2df941d0dc1183cb5235fa438", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1708118438, + "narHash": "sha256-kk9/0nuVgA220FcqH/D2xaN6uGyHp/zoxPNUmPCMmEE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5863c27340ba4de8f83e7e3c023b9599c3cb3c80", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-flood": { + "locked": { + "lastModified": 1701810456, + "narHash": "sha256-jOl3DMrDgJYZeE/w5p7zQe+Ift7R307mXG/tyhH8WHE=", + "owner": "3JlOy-PYCCKUi", + "repo": "nixpkgs", + "rev": "aff5b3aa1a95876fd426ab024d68ab2d87b0ebbd", + "type": "github" + }, + "original": { + "owner": "3JlOy-PYCCKUi", + "ref": "flood-module", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1708407374, + "narHash": "sha256-EECzarm+uqnNDCwaGg/ppXCO11qibZ1iigORShkkDf0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "f33dd27a47ebdf11dc8a5eb05e7c8fbdaf89e73f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "flake-parts": "flake-parts", + "flake-root": "flake-root", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs_2", + "nixpkgs-flood": "nixpkgs-flood", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1708335038, + "narHash": "sha256-ETLZNFBVCabo7lJrpjD6cAbnE11eDOjaQnznmg/6hAE=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e504621290a1fd896631ddbc5e9c16f4366c9f65", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix index 4de80a0..314fe9f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Kirk nix modules"; + description = "The servarr.enable nixos module"; nixConfig = { extra-substituters = ["https://nix-community.cachix.org"]; @@ -46,12 +46,8 @@ flake = { nixosModules = rec { - kirk = import ./nixos; - default = kirk; - }; - homeManagerModules = rec { - kirk = import ./home-manager; - default = kirk; + servarr = import ./servarr; + default = servarr; }; }; @@ -71,8 +67,8 @@ }; packages = { - docs = pkgs.callPackage ./mkDocs.nix { inherit inputs; }; - hugo = pkgs.callPackage ./mkHugo.nix { inherit inputs; }; + docs = pkgs.callPackage ./mkDocs.nix {inherit inputs;}; + hugo = pkgs.callPackage ./mkHugo.nix {inherit inputs;}; }; devshells.default = { @@ -89,20 +85,4 @@ }; }; }; - - # outputs = { - # self, - # }: { - # nixosModules.kirk = import ./nixos; - # nixosModules.default = self.nixosModules.kirk; - # - # homeManagerModules.kirk = import ./home-manager; - # homeManagerModules.default = self.homeManagerModules.kirk; - # - # # TODO: Find a way to generate documentation from modules using the same - # # tools as nixos. See ./mkDocs.nix - # - # #packages.x86_64-linux.mkdocs = {}; - # #defaultPackage.x86_64-linux = self.packages.x86_64-linux.report; - # }; } diff --git a/mkDocs.nix b/mkDocs.nix index e744854..d7991bb 100644 --- a/mkDocs.nix +++ b/mkDocs.nix @@ -55,7 +55,6 @@ optionsDocNixos = nixosOptionsDoc { inherit (evalNixos) options; }; - in # create a derivation for capturing the markdown output runCommand "options-doc.md" {} '' diff --git a/mkHugo.nix b/mkHugo.nix index 2660655..441a824 100644 --- a/mkHugo.nix +++ b/mkHugo.nix @@ -1,12 +1,11 @@ -{ pkgs, ... }: - - pkgs.writeShellApplication { - name = "my-script"; - runtimeInputs = with pkgs; [ hugo ]; - text = '' - cat hugo/content/header.md result/home.md | sed "s/DATE-TIMESTAMP/$(date -u +%Y-%m-%d)/g" > hugo/content/home-manager/index.md - cat hugo/content/header.md result/nixos.md | sed "s/DATE-TIMESTAMP/$(date -u +%Y-%m-%d)/g" > hugo/content/nixos/index.md - cd hugo - hugo - ''; - } +{pkgs, ...}: +pkgs.writeShellApplication { + name = "my-script"; + runtimeInputs = with pkgs; [hugo]; + text = '' + cat hugo/content/header.md result/home.md | sed "s/DATE-TIMESTAMP/$(date -u +%Y-%m-%d)/g" > hugo/content/home-manager/index.md + cat hugo/content/header.md result/nixos.md | sed "s/DATE-TIMESTAMP/$(date -u +%Y-%m-%d)/g" > hugo/content/nixos/index.md + cd hugo + hugo + ''; +} diff --git a/servarr/default.nix b/servarr/default.nix index c9ce46b..b0dec7e 100644 --- a/servarr/default.nix +++ b/servarr/default.nix @@ -14,8 +14,9 @@ in { ./sonarr ./prowlarr ./transmission + ../util ]; - + options.kirk.servarr = { enable = mkEnableOption '' My servarr setup. Lets you host the servarr services optionally @@ -71,7 +72,7 @@ in { Extra DNS servers for the VPN. If your wg config has a DNS field, then this should not be necessary. ''; - example = [ "1.1.1.2" ]; + example = ["1.1.1.2"]; }; vpnTestService = { @@ -95,7 +96,7 @@ in { if you're port forwarding on your VPN provider and you're setting up services that is not covered in by this module. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; openUdpPorts = mkOption { @@ -106,7 +107,7 @@ in { if you're port forwarding on your VPN provider and you're setting up services that is not covered in by this module. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; }; }; diff --git a/servarr/jellyfin/default.nix b/servarr/jellyfin/default.nix index 5980d2c..a114dbb 100644 --- a/servarr/jellyfin/default.nix +++ b/servarr/jellyfin/default.nix @@ -44,94 +44,99 @@ in { }; }; - config = + config = #assert (!(cfg.useVpn && cfg.nginx.enable)) || abort "useVpn not compatible with nginx.enable."; #assert (cfg.nginx.enable -> (cfg.nginx.domainName != null && cfg.nginx.acmeMail != null)) || abort "Both nginx.domain and nginx.acmeMail needs to be set if nginx.enable is set."; - mkIf cfg.enable - { - services.jellyfin.enable = cfg.enable; + mkIf cfg.enable + { + services.jellyfin.enable = cfg.enable; - networking.firewall.allowedTCPPorts = if cfg.nginx.enable then [ - 80 # http - 443 # https - ] else []; + networking.firewall.allowedTCPPorts = + if cfg.nginx.enable + then [ + 80 # http + 443 # https + ] + else []; - services.nginx = mkIf (cfg.nginx.enable || cfg.useVpn) { - enable = true; + services.nginx = mkIf (cfg.nginx.enable || cfg.useVpn) { + enable = true; - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; - virtualHosts."${builtins.replaceStrings ["\n"] [""] cfg.nginx.domainName}" = mkIf cfg.nginx.enable { - enableACME = true; - forceSSL = true; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://127.0.0.1:${builtins.toString defaultPort}"; + virtualHosts."${builtins.replaceStrings ["\n"] [""] cfg.nginx.domainName}" = mkIf cfg.nginx.enable { + enableACME = true; + forceSSL = true; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://127.0.0.1:${builtins.toString defaultPort}"; + }; + }; + + virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = mkIf cfg.useVpn { + listen = [ + { + addr = "0.0.0.0"; + port = defaultPort; + } + ]; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }; }; }; - virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = mkIf cfg.useVpn { - listen = [ - { - addr = "0.0.0.0"; - port = defaultPort; + security.acme = mkIf cfg.nginx.enable { + acceptTerms = true; + defaults.email = cfg.nginx.acmeMail; + }; + + kirk.vpnnamespace.portMappings = [ + ( + mkIf cfg.useVpn { + From = defaultPort; + To = defaultPort; } - ]; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + ) + ]; + + containers.jellyfin = mkIf cfg.useVpn { + autoStart = true; + ephemeral = true; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; + + bindMounts = { + "${servarr.mediaDir}/library".isReadOnly = false; + "${cfg.stateDir}".isReadOnly = false; + }; + + config = { + users.groups.jellyfin = {}; + users.users.jellyfin = { + uid = lib.mkForce config.users.users.jellyfin.uid; + isSystemUser = true; + group = "jellyfin"; + }; + + # Use systemd-resolved inside the container + # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 + networking.useHostResolvConf = lib.mkForce false; + services.resolved.enable = true; + networking.nameservers = dnsServers; + + services.jellyfin = { + enable = true; + group = "jellyfin"; + dataDir = "${cfg.stateDir}"; + }; + + system.stateVersion = "23.11"; }; }; }; - - security.acme = mkIf cfg.nginx.enable { - acceptTerms = true; - defaults.email = cfg.nginx.acmeMail; - }; - - kirk.vpnnamespace.portMappings = [( - mkIf cfg.useVpn { - From = defaultPort; - To = defaultPort; - } - )]; - - containers.jellyfin = mkIf cfg.useVpn { - autoStart = true; - ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; - - bindMounts = { - "${servarr.mediaDir}/library".isReadOnly = false; - "${cfg.stateDir}".isReadOnly = false; - }; - - config = { - users.groups.jellyfin = {}; - users.users.jellyfin = { - uid = lib.mkForce config.users.users.jellyfin.uid; - isSystemUser = true; - group = "jellyfin"; - }; - - # Use systemd-resolved inside the container - # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 - networking.useHostResolvConf = lib.mkForce false; - services.resolved.enable = true; - networking.nameservers = dnsServers; - - services.jellyfin = { - enable = true; - group = "jellyfin"; - dataDir = "${cfg.stateDir}"; - }; - - system.stateVersion = "23.11"; - }; - }; - }; } diff --git a/servarr/lidarr/default.nix b/servarr/lidarr/default.nix index 5052247..eb752f2 100644 --- a/servarr/lidarr/default.nix +++ b/servarr/lidarr/default.nix @@ -35,17 +35,19 @@ in { dataDir = cfg.stateDir; }; - kirk.vpnnamespace.portMappings = [( - mkIf cfg.useVpn { - From = defaultPort; - To = defaultPort; - } - )]; + kirk.vpnnamespace.portMappings = [ + ( + mkIf cfg.useVpn { + From = defaultPort; + To = defaultPort; + } + ) + ]; - containers.lidarr= mkIf cfg.useVpn { + containers.lidarr = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "${servarr.mediaDir}".isReadOnly = false; diff --git a/servarr/prowlarr/default.nix b/servarr/prowlarr/default.nix index e219abd..e99ba66 100644 --- a/servarr/prowlarr/default.nix +++ b/servarr/prowlarr/default.nix @@ -38,18 +38,20 @@ in { enable = true; openFirewall = true; }; - - kirk.vpnnamespace.portMappings = [( - mkIf cfg.useVpn { - From = defaultPort; - To = defaultPort; - } - )]; + + kirk.vpnnamespace.portMappings = [ + ( + mkIf cfg.useVpn { + From = defaultPort; + To = defaultPort; + } + ) + ]; containers.prowlarr = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "/var/lib/prowlarr" = { diff --git a/servarr/radarr/default.nix b/servarr/radarr/default.nix index f0930b4..8faef19 100644 --- a/servarr/radarr/default.nix +++ b/servarr/radarr/default.nix @@ -39,17 +39,19 @@ in { dataDir = cfg.stateDir; }; - kirk.vpnnamespace.portMappings = [( - mkIf cfg.useVpn { - From = defaultPort; - To = defaultPort; - } - )]; + kirk.vpnnamespace.portMappings = [ + ( + mkIf cfg.useVpn { + From = defaultPort; + To = defaultPort; + } + ) + ]; - containers.radarr= mkIf cfg.useVpn { + containers.radarr = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "${servarr.mediaDir}".isReadOnly = false; @@ -103,6 +105,5 @@ in { }; }; }; - }; } diff --git a/servarr/readarr/default.nix b/servarr/readarr/default.nix index 575e6af..5c6bdeb 100644 --- a/servarr/readarr/default.nix +++ b/servarr/readarr/default.nix @@ -35,17 +35,19 @@ in { dataDir = cfg.stateDir; }; - kirk.vpnnamespace.portMappings = [( - mkIf cfg.useVpn { - From = defaultPort; - To = defaultPort; - } - )]; + kirk.vpnnamespace.portMappings = [ + ( + mkIf cfg.useVpn { + From = defaultPort; + To = defaultPort; + } + ) + ]; containers.readarr = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "${servarr.mediaDir}".isReadOnly = false; @@ -99,6 +101,5 @@ in { }; }; }; - }; } diff --git a/servarr/sonarr/default.nix b/servarr/sonarr/default.nix index 695489a..02653c6 100644 --- a/servarr/sonarr/default.nix +++ b/servarr/sonarr/default.nix @@ -1,6 +1,5 @@ # TODO: Dir creation and file permissions in nix { - pkgs, config, lib, ... @@ -49,7 +48,7 @@ in { containers.sonarr = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "${servarr.mediaDir}".isReadOnly = false; diff --git a/servarr/transmission/default.nix b/servarr/transmission/default.nix index ac07603..f5a1303 100644 --- a/servarr/transmission/default.nix +++ b/servarr/transmission/default.nix @@ -67,47 +67,54 @@ in { enable = true; group = "media"; #home = cfg.stateDir; - webHome = if cfg.useFlood then pkgs.flood-for-transmission else null; + webHome = + if cfg.useFlood + then pkgs.flood-for-transmission + else null; package = pkgs.transmission_4; openRPCPort = true; openPeerPorts = true; - settings = { - download-dir = "${servarr.mediaDir}/torrents"; - incomplete-dir-enabled = true; - incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; - watch-dir-enabled = true; - watch-dir = "${servarr.mediaDir}/torrents/.watch"; + settings = + { + download-dir = "${servarr.mediaDir}/torrents"; + incomplete-dir-enabled = true; + incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; + watch-dir-enabled = true; + watch-dir = "${servarr.mediaDir}/torrents/.watch"; - rpc-port = cfg.uiPort; - rpc-whitelist-enabled = true; - rpc-whitelist = "192.168.15.1,127.0.0.1"; - rpc-authentication-required = true; + rpc-port = cfg.uiPort; + rpc-whitelist-enabled = true; + rpc-whitelist = "192.168.15.1,127.0.0.1"; + rpc-authentication-required = true; - blocklist-enabled = true; - blocklist-url = "https://github.com/Naunter/BT_BlockLists/raw/master/bt_blocklists.gz"; + blocklist-enabled = true; + blocklist-url = "https://github.com/Naunter/BT_BlockLists/raw/master/bt_blocklists.gz"; - encryption = 1; - utp-enabled = true; - port-forwarding-enabled = false; + encryption = 1; + utp-enabled = true; + port-forwarding-enabled = false; - anti-brute-force-enabled = true; - anti-brute-force-threshold = 10; - } // cfg.extraConfig; + anti-brute-force-enabled = true; + anti-brute-force-threshold = 10; + } + // cfg.extraConfig; }; kirk.vpnnamespace = mkIf cfg.useVpn { - portMappings = [{ - From = cfg.uiPort; - To = cfg.uiPort; - }]; - openUdpPorts = [ cfg.peerPort ]; - openTcpPorts = [ cfg.peerPort ]; + portMappings = [ + { + From = cfg.uiPort; + To = cfg.uiPort; + } + ]; + openUdpPorts = [cfg.peerPort]; + openTcpPorts = [cfg.peerPort]; }; containers.transmission = mkIf cfg.useVpn { autoStart = true; ephemeral = true; - extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; + extraFlags = ["--network-namespace-path=/var/run/netns/wg"]; bindMounts = { "${servarr.mediaDir}/torrents".isReadOnly = false; @@ -142,39 +149,44 @@ in { enable = true; # This is maybe wrong, too afraid to fix it lol group = "media"; - webHome = if cfg.useFlood then pkgs.flood-for-transmission else null; + webHome = + if cfg.useFlood + then pkgs.flood-for-transmission + else null; package = pkgs.transmission_4; openRPCPort = true; openPeerPorts = true; - settings = { - download-dir = "${servarr.mediaDir}/torrents"; - incomplete-dir-enabled = true; - incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; - watch-dir-enabled = true; - watch-dir = "${servarr.mediaDir}/torrents/.watch"; + settings = + { + download-dir = "${servarr.mediaDir}/torrents"; + incomplete-dir-enabled = true; + incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; + watch-dir-enabled = true; + watch-dir = "${servarr.mediaDir}/torrents/.watch"; - rpc-bind-address = "192.168.15.1"; - rpc-port = cfg.uiPort; - rpc-whitelist-enabled = false; - rpc-whitelist = "192.168.15.1,127.0.0.1"; - rpc-authentication-required = false; + rpc-bind-address = "192.168.15.1"; + rpc-port = cfg.uiPort; + rpc-whitelist-enabled = false; + rpc-whitelist = "192.168.15.1,127.0.0.1"; + rpc-authentication-required = false; - blocklist-enabled = true; - blocklist-url = "https://github.com/Naunter/BT_BlockLists/raw/master/bt_blocklists.gz"; + blocklist-enabled = true; + blocklist-url = "https://github.com/Naunter/BT_BlockLists/raw/master/bt_blocklists.gz"; - peer-port = cfg.peerPort; - dht-enabled = true; - pex-enabled = true; - utp-enabled = false; - encryption = 1; - port-forwarding-enabled = false; + peer-port = cfg.peerPort; + dht-enabled = true; + pex-enabled = true; + utp-enabled = false; + encryption = 1; + port-forwarding-enabled = false; - anti-brute-force-enabled = true; - anti-brute-force-threshold = 10; + anti-brute-force-enabled = true; + anti-brute-force-threshold = 10; - # 0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace - message-level = 3; - } // cfg.extraConfig; + # 0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace + message-level = 3; + } + // cfg.extraConfig; }; environment.systemPackages = with pkgs; [ diff --git a/util/default.nix b/util/default.nix new file mode 100644 index 0000000..1643aa4 --- /dev/null +++ b/util/default.nix @@ -0,0 +1,19 @@ +{ + imports = [ + ./fonts + ./foot + ./fzf + ./git + ./gruvboxTheme + ./helix + ./homeManagerScripts + ./jiten + ./joshuto + ./kakoune + ./ssh + ./terminalTools + ./userDirs + ./zathura + ./zsh + ]; +} diff --git a/util/upnp/default.nix b/util/upnp/default.nix index fcdd40a..299af3f 100644 --- a/util/upnp/default.nix +++ b/util/upnp/default.nix @@ -17,7 +17,7 @@ in { description = lib.mdDoc '' What TCP ports to open using UPNP. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; openUdpPorts = mkOption { @@ -26,7 +26,7 @@ in { description = lib.mdDoc '' What UDP ports to open using UPNP. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; }; @@ -64,28 +64,29 @@ in { ''; systemd = { - services.upnpc = let + services.upnpc = let upnp-ports = pkgs.writeShellApplication { name = "upnp-ports"; runtimeInputs = with pkgs; [miniupnpc]; text = ( - strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} UDP" + "\n") cfg.openUpdPorts ++ - strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} TCP" + "\n") cfg.openTcpPorts ++ - ''echo "Successfully requested upnp ports to be opened".'' + strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} UDP" + "\n") cfg.openUpdPorts + ++ strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} TCP" + "\n") cfg.openTcpPorts + ++ ''echo "Successfully requested upnp ports to be opened".'' ); }; - in mkIf cfg.upnp.enable { - enable = true; - description = "Sets port on router"; - script = "${upnp-ports}/bin/upnp-ports"; + in + mkIf cfg.upnp.enable { + enable = true; + description = "Sets port on router"; + script = "${upnp-ports}/bin/upnp-ports"; - serviceConfig = { - User = "root"; - Type = "oneshot"; + serviceConfig = { + User = "root"; + Type = "oneshot"; + }; }; - }; timers = { upnpc = mkIf cfg.upnp.enable { diff --git a/util/vpnNamespace/default.nix b/util/vpnNamespace/default.nix index e87a835..110be2a 100644 --- a/util/vpnNamespace/default.nix +++ b/util/vpnNamespace/default.nix @@ -1,39 +1,45 @@ -{ lib, pkgs, config, ... }: +{ + lib, + pkgs, + config, + ... +}: # Thanks to Maroka-chan... # TODO: Make it so you can make multiple namespaces by giving a list of # objects with settings as attributes. Also add an option to enable whether # the namespace should use a vpn or not. with builtins; -with lib; -let +with lib; let cfg = config.kirk.vpnnamespace; in { options.kirk.vpnnamespace = { - enable = mkEnableOption (lib.mdDoc "VPN Namespace") // { - description = lib.mdDoc '' - Whether to enable the VPN namespace. + enable = + mkEnableOption (lib.mdDoc "VPN Namespace") + // { + description = lib.mdDoc '' + Whether to enable the VPN namespace. - To access the namespace a veth pair is used to - connect the vpn namespace and the default namespace - through a linux bridge. One end of the pair is - connected to the linux bridge on the default namespace. - The other end is connected to the vpn namespace. + To access the namespace a veth pair is used to + connect the vpn namespace and the default namespace + through a linux bridge. One end of the pair is + connected to the linux bridge on the default namespace. + The other end is connected to the vpn namespace. - Systemd services can be run within the namespace by - adding these options: + Systemd services can be run within the namespace by + adding these options: - bindsTo = [ "netns@wg.service" ]; - requires = [ "network-online.target" ]; - after = [ "wg.service" ]; - serviceConfig = { - NetworkNamespacePath = "/var/run/netns/wg"; - }; - ''; - }; + bindsTo = [ "netns@wg.service" ]; + requires = [ "network-online.target" ]; + after = [ "wg.service" ]; + serviceConfig = { + NetworkNamespacePath = "/var/run/netns/wg"; + }; + ''; + }; accessibleFrom = mkOption { type = types.listOf types.str; - default = [ ]; + default = []; description = lib.mdDoc '' Subnets or specific addresses that the namespace should be accessible to. ''; @@ -48,7 +54,7 @@ in { default = "192.168.15.1"; description = lib.mdDoc '' The address of the veth interface connected to the vpn namespace. - + This is the address used to reach the vpn namespace from other namespaces connected to the linux bridge. ''; @@ -83,7 +89,7 @@ in { default = "/etc/wireguard/wg0.conf"; description = lib.mdDoc '' Path to the wireguard config to use. - + Note that this is not a wg-quick config. ''; }; @@ -94,10 +100,12 @@ in { description = lib.mdDoc '' A list of pairs mapping a port from the host to a port in the namespace. ''; - example = [{ - From = 80; - To = 80; - }]; + example = [ + { + From = 80; + To = 80; + } + ]; }; dnsServers = mkOption { @@ -106,7 +114,7 @@ in { description = lib.mdDoc '' YOUR VPN WILL LEAK IF THIS IS NOT SET. The dns address of your vpn. ''; - example = [ "1.1.1.2" ]; + example = ["1.1.1.2"]; }; openTcpPorts = mkOption { @@ -116,7 +124,7 @@ in { What TCP ports to allow incoming traffic from. You need this if you're port forwarding on your VPN provider. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; openUdpPorts = mkOption { @@ -126,7 +134,7 @@ in { What UDP ports to allow incoming traffic from. You need this if you're port forwarding on your VPN provider. ''; - example = [ 46382 38473 ]; + example = [46382 38473]; }; vpnTestService = { @@ -134,214 +142,235 @@ in { port = mkOption { type = types.port; - default = [ 12300 ]; + default = [12300]; description = lib.mdDoc '' The port that the vpn test service listens to. ''; - example = [ 58403 ]; + example = [58403]; }; }; }; - config = - let - headMay = list: if list == [] then null else head list; - # Checks if string is ipv4, from SO, hope it works well - # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses - isIpv4 = address: - let pat = "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])?/?[0-9]?[0-9]"; - regex = match pat address; - in regex != null; - # Checks if string is ipv6, from SO, hope it works well - # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses - isIpv6 = address: - let pat = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; - regex = match pat address; - in regex != null; - isIp = ip: (isIpv4 ip || isIpv6 ip); + config = let + headMay = list: + if list == [] + then null + else head list; + # Checks if string is ipv4, from SO, hope it works well + # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + isIpv4 = address: let + pat = "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])?/?[0-9]?[0-9]"; + regex = match pat address; + in + regex != null; + # Checks if string is ipv6, from SO, hope it works well + # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + isIpv6 = address: let + pat = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; + regex = match pat address; + in + regex != null; + isIp = ip: (isIpv4 ip || isIpv6 ip); in - mkIf cfg.enable { - lib.vpn = { - dnsServers = - let lines = split "\n" (readFile cfg.wireguardConfigFile); - dnsLine = headMay (filter (x: typeOf x == "string" && match ".*DNS.*" x != null) lines); - in if dnsLine == null then [] else let - ipsUnsplit = head (match "DNS ?=(.*)" dnsLine); - in if ipsUnsplit == null then [] else let - ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); - ipsNoSpaces = map (replaceStrings [" "] [""]) ips; - correctIps = filter isIp ipsNoSpaces; + mkIf cfg.enable { + lib.vpn = { + dnsServers = let + lines = split "\n" (readFile cfg.wireguardConfigFile); + dnsLine = headMay (filter (x: typeOf x == "string" && match ".*DNS.*" x != null) lines); in - assert ( correctIps != [] ) || abort "There must be at least 1 DNS server set."; - correctIps; - }; - - boot.kernel.sysctl."net.ipv4.ip_forward" = 1; - - systemd.services = { - "netns@" = { - description = "%I network namespace"; - before = [ "network.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${pkgs.iproute2}/bin/ip netns add %I"; - ExecStop = "${pkgs.iproute2}/bin/ip netns del %I"; - }; + if dnsLine == null + then [] + else let + ipsUnsplit = head (match "DNS ?=(.*)" dnsLine); + in + if ipsUnsplit == null + then [] + else let + ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); + ipsNoSpaces = map (replaceStrings [" "] [""]) ips; + correctIps = filter isIp ipsNoSpaces; + in + assert (correctIps != []) || abort "There must be at least 1 DNS server set."; correctIps; }; - wg = { - description = "wg network interface"; - bindsTo = [ "netns@wg.service" ]; - requires = [ "network-online.target" ]; - after = [ "netns@wg.service" ]; - wantedBy = [ "netns@wg.service" ]; + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; - serviceConfig = let - lines = split "\n" (readFile cfg.wireguardConfigFile); - addrLine = headMay (filter (x: typeOf x == "string" && match ".*Address.*" x != null) lines); - in if addrLine == null then [] else let - ipsUnsplit = head (match "Address ?=(.*)" addrLine); - in if ipsUnsplit == null then [] else let - ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); - ipsNoSpaces = map (replaceStrings [" "] [""]) ips; - wgIpv4Address = headMay (filter isIpv4 ipsNoSpaces); - - vpn-namespace = pkgs.writeShellApplication { - name = "vpn-namespace"; - - runtimeInputs = with pkgs; [ iproute2 wireguard-tools iptables ]; - - text = '' - # Set up the wireguard interface - tmpdir=$(mktemp -d) - cat ${cfg.wireguardConfigFile} > "$tmpdir/wg.conf" - - # Get dns servers - grep "DNS =" "$tmpdir/wg.conf" | sed 's/DNS =//g' | sed 's/,/\n/g' | sed 's/ //g' | sed 's/^/nameserver: /g' > "$tmpdir/resolv.conf" - - ip link add wg0 type wireguard - ip link set wg0 netns wg - ip -n wg address add "${wgIpv4Address}" dev wg0 - ip netns exec wg wg setconf wg0 <(wg-quick strip "$tmpdir/wg.conf") - ip -n wg link set wg0 up - ip -n wg route add default dev wg0 - - # Start the loopback interface - ip -n wg link set dev lo up - - # Create a bridge - ip link add v-net-0 type bridge - ip addr add ${cfg.bridgeAddress}/24 dev v-net-0 - ip link set dev v-net-0 up - - # Set up veth pair to link namespace with host network - ip link add veth-vpn-br type veth peer name veth-vpn netns wg - ip link set veth-vpn-br master v-net-0 - - ip -n wg addr add ${cfg.namespaceAddress}/24 dev veth-vpn - ip -n wg link set dev veth-vpn up - - echo "setting dns" - # DNS test, see: - # https://www.man7.org/linux/man-pages/man8/wg-quick.8.html - # Absolutely no luck... - #echo "nameserver 1.1.1.1" | ip netns exec wg resolvconf -a wg0 -m 0 -x - - echo "Hello test" - '' - - # Add routes to make the namespace accessible - + strings.concatMapStrings (x: - "ip -n wg route add ${x} via ${cfg.bridgeAddress}" + "\n" - ) cfg.accessibleFrom - - # Add prerouting rules - + strings.concatMapStrings (x: - "iptables -t nat -A PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}" + - "\n" - ) cfg.portMappings - - # Allow VPN TCP ports - + strings.concatMapStrings (x: - "ip netns exec wg iptables -I INPUT -p tcp --dport ${builtins.toString x} -j ACCEPT" + - "\n" - ) cfg.openTcpPorts - - # Allow VPN UDP ports - + strings.concatMapStrings (x: - "ip netns exec wg iptables -I INPUT -p udp --dport ${builtins.toString x} -j ACCEPT" + - "\n" - ) cfg.openUdpPorts; + systemd.services = { + "netns@" = { + description = "%I network namespace"; + before = ["network.target"]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.iproute2}/bin/ip netns add %I"; + ExecStop = "${pkgs.iproute2}/bin/ip netns del %I"; }; - in assert ( wgIpv4Address != null ) || abort "No address found in config file."; { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${vpn-namespace}/bin/vpn-namespace"; - - ExecStopPost = with pkgs; writers.writeBash "wg-down" ('' - ${iproute2}/bin/ip -n wg route del default dev wg0 - ${iproute2}/bin/ip -n wg link del wg0 - ${iproute2}/bin/ip -n wg link del veth-vpn - ${iproute2}/bin/ip link del v-net-0 - - # DNS test, see: - # https://www.man7.org/linux/man-pages/man8/wg-quick.8.html - #${iproute2}/bin/ip netns exec wg resolvconf -d wg0 - '' - - # Delete prerouting rules - + strings.concatMapStrings (x: "${iptables}/bin/iptables -t nat -D PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}" + "\n") cfg.portMappings); }; - }; - vpn-test-service = { - enable = cfg.vpnTestService.enable; + wg = { + description = "wg network interface"; + bindsTo = ["netns@wg.service"]; + requires = ["network-online.target"]; + after = ["netns@wg.service"]; + wantedBy = ["netns@wg.service"]; - script = let - vpn-test = pkgs.writeShellApplication { - name = "vpn-test"; + serviceConfig = let + lines = split "\n" (readFile cfg.wireguardConfigFile); + addrLine = headMay (filter (x: typeOf x == "string" && match ".*Address.*" x != null) lines); + in + if addrLine == null + then [] + else let + ipsUnsplit = head (match "Address ?=(.*)" addrLine); + in + if ipsUnsplit == null + then [] + else let + ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); + ipsNoSpaces = map (replaceStrings [" "] [""]) ips; + wgIpv4Address = headMay (filter isIpv4 ipsNoSpaces); - runtimeInputs = with pkgs; [ util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig ]; + vpn-namespace = pkgs.writeShellApplication { + name = "vpn-namespace"; - text = '' - cd "$(mktemp -d)" + runtimeInputs = with pkgs; [iproute2 wireguard-tools iptables]; - # Print resolv.conf - echo "/etc/resolv.conf contains:" - cat /etc/resolv.conf + text = + '' + # Set up the wireguard interface + tmpdir=$(mktemp -d) + cat ${cfg.wireguardConfigFile} > "$tmpdir/wg.conf" - # Query resolvconf - echo "resolvconf output:" - resolvconf -l - echo "" + # Get dns servers + grep "DNS =" "$tmpdir/wg.conf" | sed 's/DNS =//g' | sed 's/,/\n/g' | sed 's/ //g' | sed 's/^/nameserver: /g' > "$tmpdir/resolv.conf" - # Get ip - echo "Getting IP:" - curl -s ipinfo.io + ip link add wg0 type wireguard + ip link set wg0 netns wg + ip -n wg address add "${wgIpv4Address}" dev wg0 + ip netns exec wg wg setconf wg0 <(wg-quick strip "$tmpdir/wg.conf") + ip -n wg link set wg0 up + ip -n wg route add default dev wg0 - cat /etc/test.file + # Start the loopback interface + ip -n wg link set dev lo up - echo -ne "DNS leak test:" - curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh - chmod +x dnsleaktest.sh - ./dnsleaktest.sh + # Create a bridge + ip link add v-net-0 type bridge + ip addr add ${cfg.bridgeAddress}/24 dev v-net-0 + ip link set dev v-net-0 up - echo "starting netcat on port ${builtins.toString cfg.vpnTestService.port}:" - nc -vnlp ${builtins.toString cfg.vpnTestService.port} - ''; + # Set up veth pair to link namespace with host network + ip link add veth-vpn-br type veth peer name veth-vpn netns wg + ip link set veth-vpn-br master v-net-0 + + ip -n wg addr add ${cfg.namespaceAddress}/24 dev veth-vpn + ip -n wg link set dev veth-vpn up + + echo "setting dns" + # DNS test, see: + # https://www.man7.org/linux/man-pages/man8/wg-quick.8.html + # Absolutely no luck... + #echo "nameserver 1.1.1.1" | ip netns exec wg resolvconf -a wg0 -m 0 -x + + echo "Hello test" + '' + # Add routes to make the namespace accessible + + strings.concatMapStrings ( + x: + "ip -n wg route add ${x} via ${cfg.bridgeAddress}" + "\n" + ) + cfg.accessibleFrom + # Add prerouting rules + + strings.concatMapStrings ( + x: + "iptables -t nat -A PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}" + + "\n" + ) + cfg.portMappings + # Allow VPN TCP ports + + strings.concatMapStrings ( + x: + "ip netns exec wg iptables -I INPUT -p tcp --dport ${builtins.toString x} -j ACCEPT" + + "\n" + ) + cfg.openTcpPorts + # Allow VPN UDP ports + + strings.concatMapStrings ( + x: + "ip netns exec wg iptables -I INPUT -p udp --dport ${builtins.toString x} -j ACCEPT" + + "\n" + ) + cfg.openUdpPorts; + }; + in + assert (wgIpv4Address != null) || abort "No address found in config file."; { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${vpn-namespace}/bin/vpn-namespace"; + + ExecStopPost = with pkgs; + writers.writeBash "wg-down" ('' + ${iproute2}/bin/ip -n wg route del default dev wg0 + ${iproute2}/bin/ip -n wg link del wg0 + ${iproute2}/bin/ip -n wg link del veth-vpn + ${iproute2}/bin/ip link del v-net-0 + + # DNS test, see: + # https://www.man7.org/linux/man-pages/man8/wg-quick.8.html + #${iproute2}/bin/ip netns exec wg resolvconf -d wg0 + '' + # Delete prerouting rules + + strings.concatMapStrings (x: "${iptables}/bin/iptables -t nat -D PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}" + "\n") cfg.portMappings); + }; + }; + + vpn-test-service = { + enable = cfg.vpnTestService.enable; + + script = let + vpn-test = pkgs.writeShellApplication { + name = "vpn-test"; + + runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig]; + + text = '' + cd "$(mktemp -d)" + + # Print resolv.conf + echo "/etc/resolv.conf contains:" + cat /etc/resolv.conf + + # Query resolvconf + echo "resolvconf output:" + resolvconf -l + echo "" + + # Get ip + echo "Getting IP:" + curl -s ipinfo.io + + cat /etc/test.file + + echo -ne "DNS leak test:" + curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh + chmod +x dnsleaktest.sh + ./dnsleaktest.sh + + echo "starting netcat on port ${builtins.toString cfg.vpnTestService.port}:" + nc -vnlp ${builtins.toString cfg.vpnTestService.port} + ''; + }; + in "${vpn-test}/bin/vpn-test"; + + bindsTo = ["netns@wg.service"]; + requires = ["network-online.target"]; + after = ["wg.service"]; + serviceConfig = { + User = "prowlarr"; + NetworkNamespacePath = "/var/run/netns/wg"; + BindReadOnlyPaths = ["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"]; }; - in "${vpn-test}/bin/vpn-test"; - - bindsTo = [ "netns@wg.service" ]; - requires = [ "network-online.target" ]; - after = [ "wg.service" ]; - serviceConfig = { - User="prowlarr"; - NetworkNamespacePath = "/var/run/netns/wg"; - BindReadOnlyPaths=["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"]; }; }; }; - }; }