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 = {
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;
# };
}
-1
View File
@@ -55,7 +55,6 @@
optionsDocNixos = nixosOptionsDoc {
inherit (evalNixos) options;
};
in
# create a derivation for capturing the markdown output
runCommand "options-doc.md" {} ''
+11 -12
View File
@@ -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
'';
}
+5 -4
View File
@@ -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];
};
};
};
+82 -77
View File
@@ -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";
};
};
};
}
+10 -8
View File
@@ -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;
+10 -8
View File
@@ -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" = {
+10 -9
View File
@@ -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 {
};
};
};
};
}
+9 -8
View File
@@ -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 {
};
};
};
};
}
+1 -2
View File
@@ -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;
+63 -51
View File
@@ -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; [
+19
View File
@@ -0,0 +1,19 @@
{
imports = [
./fonts
./foot
./fzf
./git
./gruvboxTheme
./helix
./homeManagerScripts
./jiten
./joshuto
./kakoune
./ssh
./terminalTools
./userDirs
./zathura
./zsh
];
}
+15 -14
View File
@@ -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 {
+242 -213
View File
@@ -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"];
};
};
};
};
}