This commit is contained in:
rasmus-kirk
2024-02-20 15:38:34 +01:00
parent d0a87c984a
commit f5b6c56797
15 changed files with 674 additions and 432 deletions
Generated
+192
View File
@@ -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
}
+5 -25
View File
@@ -1,5 +1,5 @@
{ {
description = "Kirk nix modules"; description = "The servarr.enable nixos module";
nixConfig = { nixConfig = {
extra-substituters = ["https://nix-community.cachix.org"]; extra-substituters = ["https://nix-community.cachix.org"];
@@ -46,12 +46,8 @@
flake = { flake = {
nixosModules = rec { nixosModules = rec {
kirk = import ./nixos; servarr = import ./servarr;
default = kirk; default = servarr;
};
homeManagerModules = rec {
kirk = import ./home-manager;
default = kirk;
}; };
}; };
@@ -71,8 +67,8 @@
}; };
packages = { packages = {
docs = pkgs.callPackage ./mkDocs.nix { inherit inputs; }; docs = pkgs.callPackage ./mkDocs.nix {inherit inputs;};
hugo = pkgs.callPackage ./mkHugo.nix { inherit inputs; }; hugo = pkgs.callPackage ./mkHugo.nix {inherit inputs;};
}; };
devshells.default = { 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;
# };
} }
-1
View File
@@ -55,7 +55,6 @@
optionsDocNixos = nixosOptionsDoc { optionsDocNixos = nixosOptionsDoc {
inherit (evalNixos) options; inherit (evalNixos) options;
}; };
in in
# create a derivation for capturing the markdown output # create a derivation for capturing the markdown output
runCommand "options-doc.md" {} '' runCommand "options-doc.md" {} ''
+4 -5
View File
@@ -1,12 +1,11 @@
{ pkgs, ... }: {pkgs, ...}:
pkgs.writeShellApplication {
pkgs.writeShellApplication {
name = "my-script"; name = "my-script";
runtimeInputs = with pkgs; [ hugo ]; runtimeInputs = with pkgs; [hugo];
text = '' 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/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 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 cd hugo
hugo hugo
''; '';
} }
+4 -3
View File
@@ -14,6 +14,7 @@ in {
./sonarr ./sonarr
./prowlarr ./prowlarr
./transmission ./transmission
../util
]; ];
options.kirk.servarr = { options.kirk.servarr = {
@@ -71,7 +72,7 @@ in {
Extra DNS servers for the VPN. If your wg config has a DNS field, Extra DNS servers for the VPN. If your wg config has a DNS field,
then this should not be necessary. then this should not be necessary.
''; '';
example = [ "1.1.1.2" ]; example = ["1.1.1.2"];
}; };
vpnTestService = { vpnTestService = {
@@ -95,7 +96,7 @@ in {
if you're port forwarding on your VPN provider and you're setting if you're port forwarding on your VPN provider and you're setting
up services that is not covered in by this module. up services that is not covered in by this module.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
openUdpPorts = mkOption { openUdpPorts = mkOption {
@@ -106,7 +107,7 @@ in {
if you're port forwarding on your VPN provider and you're setting if you're port forwarding on your VPN provider and you're setting
up services that is not covered in by this module. up services that is not covered in by this module.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
}; };
}; };
+10 -5
View File
@@ -51,10 +51,13 @@ in {
{ {
services.jellyfin.enable = cfg.enable; services.jellyfin.enable = cfg.enable;
networking.firewall.allowedTCPPorts = if cfg.nginx.enable then [ networking.firewall.allowedTCPPorts =
if cfg.nginx.enable
then [
80 # http 80 # http
443 # https 443 # https
] else []; ]
else [];
services.nginx = mkIf (cfg.nginx.enable || cfg.useVpn) { services.nginx = mkIf (cfg.nginx.enable || cfg.useVpn) {
enable = true; enable = true;
@@ -93,17 +96,19 @@ in {
defaults.email = cfg.nginx.acmeMail; defaults.email = cfg.nginx.acmeMail;
}; };
kirk.vpnnamespace.portMappings = [( kirk.vpnnamespace.portMappings = [
(
mkIf cfg.useVpn { mkIf cfg.useVpn {
From = defaultPort; From = defaultPort;
To = defaultPort; To = defaultPort;
} }
)]; )
];
containers.jellyfin = mkIf cfg.useVpn { containers.jellyfin = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}/library".isReadOnly = false; "${servarr.mediaDir}/library".isReadOnly = false;
+6 -4
View File
@@ -35,17 +35,19 @@ in {
dataDir = cfg.stateDir; dataDir = cfg.stateDir;
}; };
kirk.vpnnamespace.portMappings = [( kirk.vpnnamespace.portMappings = [
(
mkIf cfg.useVpn { mkIf cfg.useVpn {
From = defaultPort; From = defaultPort;
To = defaultPort; To = defaultPort;
} }
)]; )
];
containers.lidarr= mkIf cfg.useVpn { containers.lidarr = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}".isReadOnly = false; "${servarr.mediaDir}".isReadOnly = false;
+5 -3
View File
@@ -39,17 +39,19 @@ in {
openFirewall = true; openFirewall = true;
}; };
kirk.vpnnamespace.portMappings = [( kirk.vpnnamespace.portMappings = [
(
mkIf cfg.useVpn { mkIf cfg.useVpn {
From = defaultPort; From = defaultPort;
To = defaultPort; To = defaultPort;
} }
)]; )
];
containers.prowlarr = mkIf cfg.useVpn { containers.prowlarr = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"/var/lib/prowlarr" = { "/var/lib/prowlarr" = {
+6 -5
View File
@@ -39,17 +39,19 @@ in {
dataDir = cfg.stateDir; dataDir = cfg.stateDir;
}; };
kirk.vpnnamespace.portMappings = [( kirk.vpnnamespace.portMappings = [
(
mkIf cfg.useVpn { mkIf cfg.useVpn {
From = defaultPort; From = defaultPort;
To = defaultPort; To = defaultPort;
} }
)]; )
];
containers.radarr= mkIf cfg.useVpn { containers.radarr = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}".isReadOnly = false; "${servarr.mediaDir}".isReadOnly = false;
@@ -103,6 +105,5 @@ in {
}; };
}; };
}; };
}; };
} }
+5 -4
View File
@@ -35,17 +35,19 @@ in {
dataDir = cfg.stateDir; dataDir = cfg.stateDir;
}; };
kirk.vpnnamespace.portMappings = [( kirk.vpnnamespace.portMappings = [
(
mkIf cfg.useVpn { mkIf cfg.useVpn {
From = defaultPort; From = defaultPort;
To = defaultPort; To = defaultPort;
} }
)]; )
];
containers.readarr = mkIf cfg.useVpn { containers.readarr = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}".isReadOnly = false; "${servarr.mediaDir}".isReadOnly = false;
@@ -99,6 +101,5 @@ in {
}; };
}; };
}; };
}; };
} }
+1 -2
View File
@@ -1,6 +1,5 @@
# TODO: Dir creation and file permissions in nix # TODO: Dir creation and file permissions in nix
{ {
pkgs,
config, config,
lib, lib,
... ...
@@ -49,7 +48,7 @@ in {
containers.sonarr = mkIf cfg.useVpn { containers.sonarr = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}".isReadOnly = false; "${servarr.mediaDir}".isReadOnly = false;
+23 -11
View File
@@ -67,11 +67,15 @@ in {
enable = true; enable = true;
group = "media"; group = "media";
#home = cfg.stateDir; #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; package = pkgs.transmission_4;
openRPCPort = true; openRPCPort = true;
openPeerPorts = true; openPeerPorts = true;
settings = { settings =
{
download-dir = "${servarr.mediaDir}/torrents"; download-dir = "${servarr.mediaDir}/torrents";
incomplete-dir-enabled = true; incomplete-dir-enabled = true;
incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete";
@@ -92,22 +96,25 @@ in {
anti-brute-force-enabled = true; anti-brute-force-enabled = true;
anti-brute-force-threshold = 10; anti-brute-force-threshold = 10;
} // cfg.extraConfig; }
// cfg.extraConfig;
}; };
kirk.vpnnamespace = mkIf cfg.useVpn { kirk.vpnnamespace = mkIf cfg.useVpn {
portMappings = [{ portMappings = [
{
From = cfg.uiPort; From = cfg.uiPort;
To = cfg.uiPort; To = cfg.uiPort;
}]; }
openUdpPorts = [ cfg.peerPort ]; ];
openTcpPorts = [ cfg.peerPort ]; openUdpPorts = [cfg.peerPort];
openTcpPorts = [cfg.peerPort];
}; };
containers.transmission = mkIf cfg.useVpn { containers.transmission = mkIf cfg.useVpn {
autoStart = true; autoStart = true;
ephemeral = true; ephemeral = true;
extraFlags = [ "--network-namespace-path=/var/run/netns/wg" ]; extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = { bindMounts = {
"${servarr.mediaDir}/torrents".isReadOnly = false; "${servarr.mediaDir}/torrents".isReadOnly = false;
@@ -142,11 +149,15 @@ in {
enable = true; enable = true;
# This is maybe wrong, too afraid to fix it lol # This is maybe wrong, too afraid to fix it lol
group = "media"; 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; package = pkgs.transmission_4;
openRPCPort = true; openRPCPort = true;
openPeerPorts = true; openPeerPorts = true;
settings = { settings =
{
download-dir = "${servarr.mediaDir}/torrents"; download-dir = "${servarr.mediaDir}/torrents";
incomplete-dir-enabled = true; incomplete-dir-enabled = true;
incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete"; incomplete-dir = "${servarr.mediaDir}/torrents/.incomplete";
@@ -174,7 +185,8 @@ in {
# 0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace # 0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace
message-level = 3; message-level = 3;
} // cfg.extraConfig; }
// cfg.extraConfig;
}; };
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
+19
View File
@@ -0,0 +1,19 @@
{
imports = [
./fonts
./foot
./fzf
./git
./gruvboxTheme
./helix
./homeManagerScripts
./jiten
./joshuto
./kakoune
./ssh
./terminalTools
./userDirs
./zathura
./zsh
];
}
+7 -6
View File
@@ -17,7 +17,7 @@ in {
description = lib.mdDoc '' description = lib.mdDoc ''
What TCP ports to open using UPNP. What TCP ports to open using UPNP.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
openUdpPorts = mkOption { openUdpPorts = mkOption {
@@ -26,7 +26,7 @@ in {
description = lib.mdDoc '' description = lib.mdDoc ''
What UDP ports to open using UPNP. What UDP ports to open using UPNP.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
}; };
@@ -71,12 +71,13 @@ in {
runtimeInputs = with pkgs; [miniupnpc]; runtimeInputs = with pkgs; [miniupnpc];
text = ( text = (
strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} UDP" + "\n") cfg.openUpdPorts ++ strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} UDP" + "\n") cfg.openUpdPorts
strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} TCP" + "\n") cfg.openTcpPorts ++ ++ strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} TCP" + "\n") cfg.openTcpPorts
''echo "Successfully requested upnp ports to be opened".'' ++ ''echo "Successfully requested upnp ports to be opened".''
); );
}; };
in mkIf cfg.upnp.enable { in
mkIf cfg.upnp.enable {
enable = true; enable = true;
description = "Sets port on router"; description = "Sets port on router";
script = "${upnp-ports}/bin/upnp-ports"; script = "${upnp-ports}/bin/upnp-ports";
+92 -63
View File
@@ -1,15 +1,21 @@
{ lib, pkgs, config, ... }: {
lib,
pkgs,
config,
...
}:
# Thanks to Maroka-chan... # Thanks to Maroka-chan...
# TODO: Make it so you can make multiple namespaces by giving a list of # 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 # objects with settings as attributes. Also add an option to enable whether
# the namespace should use a vpn or not. # the namespace should use a vpn or not.
with builtins; with builtins;
with lib; with lib; let
let
cfg = config.kirk.vpnnamespace; cfg = config.kirk.vpnnamespace;
in { in {
options.kirk.vpnnamespace = { options.kirk.vpnnamespace = {
enable = mkEnableOption (lib.mdDoc "VPN Namespace") // { enable =
mkEnableOption (lib.mdDoc "VPN Namespace")
// {
description = lib.mdDoc '' description = lib.mdDoc ''
Whether to enable the VPN namespace. Whether to enable the VPN namespace.
@@ -33,7 +39,7 @@ in {
accessibleFrom = mkOption { accessibleFrom = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [];
description = lib.mdDoc '' description = lib.mdDoc ''
Subnets or specific addresses that the namespace should be accessible to. Subnets or specific addresses that the namespace should be accessible to.
''; '';
@@ -94,10 +100,12 @@ in {
description = lib.mdDoc '' description = lib.mdDoc ''
A list of pairs mapping a port from the host to a port in the namespace. A list of pairs mapping a port from the host to a port in the namespace.
''; '';
example = [{ example = [
{
From = 80; From = 80;
To = 80; To = 80;
}]; }
];
}; };
dnsServers = mkOption { dnsServers = mkOption {
@@ -106,7 +114,7 @@ in {
description = lib.mdDoc '' description = lib.mdDoc ''
YOUR VPN WILL LEAK IF THIS IS NOT SET. The dns address of your vpn. 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 { openTcpPorts = mkOption {
@@ -116,7 +124,7 @@ in {
What TCP ports to allow incoming traffic from. You need this if What TCP ports to allow incoming traffic from. You need this if
you're port forwarding on your VPN provider. you're port forwarding on your VPN provider.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
openUdpPorts = mkOption { openUdpPorts = mkOption {
@@ -126,7 +134,7 @@ in {
What UDP ports to allow incoming traffic from. You need this if What UDP ports to allow incoming traffic from. You need this if
you're port forwarding on your VPN provider. you're port forwarding on your VPN provider.
''; '';
example = [ 46382 38473 ]; example = [46382 38473];
}; };
vpnTestService = { vpnTestService = {
@@ -134,46 +142,55 @@ in {
port = mkOption { port = mkOption {
type = types.port; type = types.port;
default = [ 12300 ]; default = [12300];
description = lib.mdDoc '' description = lib.mdDoc ''
The port that the vpn test service listens to. The port that the vpn test service listens to.
''; '';
example = [ 58403 ]; example = [58403];
}; };
}; };
}; };
config = config = let
let headMay = list:
headMay = list: if list == [] then null else head list; if list == []
then null
else head list;
# Checks if string is ipv4, from SO, hope it works well # Checks if string is ipv4, from SO, hope it works well
# https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
isIpv4 = address: isIpv4 = address: let
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]"; 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; regex = match pat address;
in regex != null; in
regex != null;
# Checks if string is ipv6, from SO, hope it works well # Checks if string is ipv6, from SO, hope it works well
# https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
isIpv6 = address: isIpv6 = address: let
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]))"; 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; regex = match pat address;
in regex != null; in
regex != null;
isIp = ip: (isIpv4 ip || isIpv6 ip); isIp = ip: (isIpv4 ip || isIpv6 ip);
in in
mkIf cfg.enable { mkIf cfg.enable {
lib.vpn = { lib.vpn = {
dnsServers = dnsServers = let
let lines = split "\n" (readFile cfg.wireguardConfigFile); lines = split "\n" (readFile cfg.wireguardConfigFile);
dnsLine = headMay (filter (x: typeOf x == "string" && match ".*DNS.*" x != null) lines); dnsLine = headMay (filter (x: typeOf x == "string" && match ".*DNS.*" x != null) lines);
in if dnsLine == null then [] else let in
if dnsLine == null
then []
else let
ipsUnsplit = head (match "DNS ?=(.*)" dnsLine); ipsUnsplit = head (match "DNS ?=(.*)" dnsLine);
in if ipsUnsplit == null then [] else let in
if ipsUnsplit == null
then []
else let
ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); ips = filter (x: typeOf x == "string") (split "," ipsUnsplit);
ipsNoSpaces = map (replaceStrings [" "] [""]) ips; ipsNoSpaces = map (replaceStrings [" "] [""]) ips;
correctIps = filter isIp ipsNoSpaces; correctIps = filter isIp ipsNoSpaces;
in in
assert ( correctIps != [] ) || abort "There must be at least 1 DNS server set."; assert (correctIps != []) || abort "There must be at least 1 DNS server set."; correctIps;
correctIps;
}; };
boot.kernel.sysctl."net.ipv4.ip_forward" = 1; boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
@@ -181,7 +198,7 @@ in {
systemd.services = { systemd.services = {
"netns@" = { "netns@" = {
description = "%I network namespace"; description = "%I network namespace";
before = [ "network.target" ]; before = ["network.target"];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
@@ -192,17 +209,23 @@ in {
wg = { wg = {
description = "wg network interface"; description = "wg network interface";
bindsTo = [ "netns@wg.service" ]; bindsTo = ["netns@wg.service"];
requires = [ "network-online.target" ]; requires = ["network-online.target"];
after = [ "netns@wg.service" ]; after = ["netns@wg.service"];
wantedBy = [ "netns@wg.service" ]; wantedBy = ["netns@wg.service"];
serviceConfig = let serviceConfig = let
lines = split "\n" (readFile cfg.wireguardConfigFile); lines = split "\n" (readFile cfg.wireguardConfigFile);
addrLine = headMay (filter (x: typeOf x == "string" && match ".*Address.*" x != null) lines); addrLine = headMay (filter (x: typeOf x == "string" && match ".*Address.*" x != null) lines);
in if addrLine == null then [] else let in
if addrLine == null
then []
else let
ipsUnsplit = head (match "Address ?=(.*)" addrLine); ipsUnsplit = head (match "Address ?=(.*)" addrLine);
in if ipsUnsplit == null then [] else let in
if ipsUnsplit == null
then []
else let
ips = filter (x: typeOf x == "string") (split "," ipsUnsplit); ips = filter (x: typeOf x == "string") (split "," ipsUnsplit);
ipsNoSpaces = map (replaceStrings [" "] [""]) ips; ipsNoSpaces = map (replaceStrings [" "] [""]) ips;
wgIpv4Address = headMay (filter isIpv4 ipsNoSpaces); wgIpv4Address = headMay (filter isIpv4 ipsNoSpaces);
@@ -210,9 +233,10 @@ in {
vpn-namespace = pkgs.writeShellApplication { vpn-namespace = pkgs.writeShellApplication {
name = "vpn-namespace"; name = "vpn-namespace";
runtimeInputs = with pkgs; [ iproute2 wireguard-tools iptables ]; runtimeInputs = with pkgs; [iproute2 wireguard-tools iptables];
text = '' text =
''
# Set up the wireguard interface # Set up the wireguard interface
tmpdir=$(mktemp -d) tmpdir=$(mktemp -d)
cat ${cfg.wireguardConfigFile} > "$tmpdir/wg.conf" cat ${cfg.wireguardConfigFile} > "$tmpdir/wg.conf"
@@ -250,36 +274,42 @@ in {
echo "Hello test" echo "Hello test"
'' ''
# Add routes to make the namespace accessible # Add routes to make the namespace accessible
+ strings.concatMapStrings (x: + strings.concatMapStrings (
x:
"ip -n wg route add ${x} via ${cfg.bridgeAddress}" + "\n" "ip -n wg route add ${x} via ${cfg.bridgeAddress}" + "\n"
) cfg.accessibleFrom )
cfg.accessibleFrom
# Add prerouting rules # Add prerouting rules
+ strings.concatMapStrings (x: + strings.concatMapStrings (
"iptables -t nat -A PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}" + x:
"\n" "iptables -t nat -A PREROUTING -p tcp --dport ${builtins.toString x.From} -j DNAT --to-destination ${cfg.namespaceAddress}:${builtins.toString x.To}"
) cfg.portMappings + "\n"
)
cfg.portMappings
# Allow VPN TCP ports # Allow VPN TCP ports
+ strings.concatMapStrings (x: + strings.concatMapStrings (
"ip netns exec wg iptables -I INPUT -p tcp --dport ${builtins.toString x} -j ACCEPT" + x:
"\n" "ip netns exec wg iptables -I INPUT -p tcp --dport ${builtins.toString x} -j ACCEPT"
) cfg.openTcpPorts + "\n"
)
cfg.openTcpPorts
# Allow VPN UDP ports # Allow VPN UDP ports
+ strings.concatMapStrings (x: + strings.concatMapStrings (
"ip netns exec wg iptables -I INPUT -p udp --dport ${builtins.toString x} -j ACCEPT" + x:
"\n" "ip netns exec wg iptables -I INPUT -p udp --dport ${builtins.toString x} -j ACCEPT"
) cfg.openUdpPorts; + "\n"
)
cfg.openUdpPorts;
}; };
in assert ( wgIpv4Address != null ) || abort "No address found in config file."; { in
assert (wgIpv4Address != null) || abort "No address found in config file."; {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = "${vpn-namespace}/bin/vpn-namespace"; ExecStart = "${vpn-namespace}/bin/vpn-namespace";
ExecStopPost = with pkgs; writers.writeBash "wg-down" ('' ExecStopPost = with pkgs;
writers.writeBash "wg-down" (''
${iproute2}/bin/ip -n wg route del default dev wg0 ${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 wg0
${iproute2}/bin/ip -n wg link del veth-vpn ${iproute2}/bin/ip -n wg link del veth-vpn
@@ -289,7 +319,6 @@ in {
# https://www.man7.org/linux/man-pages/man8/wg-quick.8.html # https://www.man7.org/linux/man-pages/man8/wg-quick.8.html
#${iproute2}/bin/ip netns exec wg resolvconf -d wg0 #${iproute2}/bin/ip netns exec wg resolvconf -d wg0
'' ''
# Delete prerouting rules # 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); + 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);
}; };
@@ -302,7 +331,7 @@ in {
vpn-test = pkgs.writeShellApplication { vpn-test = pkgs.writeShellApplication {
name = "vpn-test"; name = "vpn-test";
runtimeInputs = with pkgs; [ util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig ]; runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig];
text = '' text = ''
cd "$(mktemp -d)" cd "$(mktemp -d)"
@@ -333,13 +362,13 @@ in {
}; };
in "${vpn-test}/bin/vpn-test"; in "${vpn-test}/bin/vpn-test";
bindsTo = [ "netns@wg.service" ]; bindsTo = ["netns@wg.service"];
requires = [ "network-online.target" ]; requires = ["network-online.target"];
after = [ "wg.service" ]; after = ["wg.service"];
serviceConfig = { serviceConfig = {
User="prowlarr"; User = "prowlarr";
NetworkNamespacePath = "/var/run/netns/wg"; NetworkNamespacePath = "/var/run/netns/wg";
BindReadOnlyPaths=["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"]; BindReadOnlyPaths = ["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"];
}; };
}; };
}; };