Removed vpn, using Maroka's instead, removes all containerization :')

This commit is contained in:
rasmus-kirk
2024-03-01 15:11:44 +01:00
parent a168966644
commit d396bd3a11
15 changed files with 309 additions and 941 deletions
Generated
+36 -1
View File
@@ -90,13 +90,30 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1709218635,
"narHash": "sha256-nytX/MkfqeTD4z7bMq4QRXcHxO9B3vRo9tM6fMtPFA8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "068d4db604958d05d0b46c47f79b507d84dbc069",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devshell": "devshell",
"flake-parts": "flake-parts",
"flake-root": "flake-root",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
"treefmt-nix": "treefmt-nix",
"vpnconfinement": "vpnconfinement"
}
},
"systems": {
@@ -133,6 +150,24 @@
"repo": "treefmt-nix",
"type": "github"
}
},
"vpnconfinement": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1709159289,
"narHash": "sha256-66eFi/SgygAMOLLLkH5oqwBiI4iE5Bj/kBJmmMhX8fg=",
"owner": "Maroka-chan",
"repo": "VPN-Confinement",
"rev": "93804a1050d3699418f0f9472e9c5eca1aa8153d",
"type": "github"
},
"original": {
"owner": "Maroka-chan",
"repo": "VPN-Confinement",
"type": "github"
}
}
},
"root": "root",
+11 -4
View File
@@ -8,6 +8,10 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
vpnconfinement = {
url = "github:Maroka-chan/VPN-Confinement";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-parts = {
url = "github:hercules-ci/flake-parts";
@@ -25,15 +29,15 @@
};
};
outputs = inputs @ {flake-parts, ...}:
outputs = inputs @ {flake-parts, vpnconfinement, nixpkgs, ...}:
flake-parts.lib.mkFlake {
inherit inputs;
}
rec {
} {
imports = with inputs; [
flake-root.flakeModule
treefmt-nix.flakeModule
devshell.flakeModule
#vpnconfinement.nixosModules.default
];
systems = [
"x86_64-linux"
@@ -41,7 +45,10 @@
flake = {
nixosModules = rec {
nixarr = import ./nixarr;
#vpnconfinement = vpnconfinement.nixosModules.default;
nixarr = (import ./nixarr vpnconfinement);
#imports = [ vpnconfinement.nixosModules.default ];
#nixarr.imports = [ vpnconfinement ];
default = nixarr;
};
};
+117
View File
@@ -0,0 +1,117 @@
{
pkgs,
config,
lib,
...
}:
with lib; let
cfg = config.nixarr.ddns;
in {
options.nixarr.ddns = {
njalla = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
**Required options:**
- [`nixarr.ddns.njalla.keysFile`](#nixarr.ddns.njalla.keysfile)
Whether or not to enable DDNS for a [Njalla](https://njal.la/)
domain.
'';
};
keysFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
A path to a JSON-file containing key value pairs of domains and keys.
To get the keys, create a dynamic njalla record. Upon creation
you should see something like the following command suggested:
```sh
curl "https://njal.la/update/?h=jellyfin.example.com&k=zeubesojOLgC2eJC&auto"
```
Then the JSON-file you pass here should contain:
```json
{
"jellyfin.example.com": "zeubesojOLgC2eJC"
}
```
You can, of course, add more key-value pairs than just one.
'';
};
};
};
config = mkIf cfg.njalla.enable {
assertions = [
{
assertion = cfg.njalla.enable -> cfg.njalla.keysFile != null;
message = ''
The nixarr.ddns.njalla.enable option requires the
nixarr.ddns.njalla.keysFile option to be set, but it was not.
'';
}
];
systemd.timers = mkIf cfg.njalla.enable {
ddnsNjalla = {
description = "Timer for setting the Njalla DDNS records";
timerConfig = {
OnBootSec = "30"; # Run 30 seconds after system boot
OnCalendar = "hourly";
Persistent = true; # Run service immediately if last window was missed
RandomizedDelaySec = "5min"; # Run service OnCalendar +- 5min
};
wantedBy = ["multi-user.target"];
};
};
systemd.services = let
ddns-njalla = pkgs.writeShellApplication {
name = "ddns-njalla";
runtimeInputs = with pkgs; [ curl jq ];
# Thanks chatgpt...
text = ''
# Path to the JSON file
json_file="${cfg.njalla.keysFile}"
# Convert the JSON object into a series of tab-separated key-value pairs using jq
# - `to_entries[]`: Convert the object into an array of key-value pairs.
# - `[.key, .value]`: For each pair, create an array containing the key and the value.
# - `@tsv`: Convert the array to a tab-separated string.
# The output will be a series of lines, each containing a key and a value separated by a tab.
jq_command='to_entries[] | [.key, .value] | @tsv'
# Read the converted output line by line
# - `IFS=$'\t'`: Use the tab character as the field separator.
# - `read -r key val`: For each line, split it into `key` and `val` based on the tab separator.
while IFS=$'\t' read -r key val; do
# For each key-value pair, execute the curl command
# Replace `''${key}` and `''${val}` in the URL with the actual key and value.
curl -s "https://njal.la/update/?h=''${key}&k=''${val}&auto"
done < <(jq -r "$jq_command" "$json_file")
'';
};
in mkIf cfg.njalla.enable {
ddnsNjalla = {
description = "Sets the Njalla DDNS records";
serviceConfig = {
ExecStart = getExe ddns-njalla;
Type = "oneshot";
};
};
};
};
}
+53 -148
View File
@@ -1,4 +1,4 @@
{
vpnconfinement: {
config,
lib,
pkgs,
@@ -8,7 +8,9 @@ with lib; let
cfg = config.nixarr;
in {
imports = [
vpnconfinement.nixosModules.default
./jellyfin
./ddns
./radarr
./lidarr
./readarr
@@ -70,46 +72,6 @@ in {
'';
};
ddns.njalla = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
**Required options:**
- [`nixarr.ddns.njalla.keysFile`](#nixarr.ddns.njalla.keysfile)
Whether or not to enable DDNS for a [Njalla](https://njal.la/)
domain.
'';
};
keysFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
A path to a JSON-file containing key value pairs of domains and keys.
To get the keys, create a dynamic njalla record. Upon creation
you should see something like the following command suggested:
```sh
curl "https://njal.la/update/?h=jellyfin.example.com&k=zeubesojOLgC2eJC&auto"
```
Then the JSON-file you pass here should contain:
```json
{
"jellyfin.example.com": "zeubesojOLgC2eJC"
}
```
You can, of course, add more key-value pairs than just one.
'';
};
};
vpn = {
enable = mkOption {
type = types.bool;
@@ -128,16 +90,6 @@ in {
description = "The path to the wireguard configuration file.";
};
dnsServers = mkOption {
type = with types; nullOr (listOf str);
default = null;
description = ''
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"];
};
vpnTestService = {
enable = mkEnableOption ''
the vpn test service. Useful for testing DNS leaks or if the VPN
@@ -145,10 +97,11 @@ in {
'';
port = mkOption {
type = types.port;
default = 12300;
type = with types; nullOr port;
default = null;
description = ''
The port that the vpn test service listens to.
The port that netcat listens to on the vpn test service. If set to
`null`, then netcat will not be started.
'';
example = 58403;
};
@@ -157,7 +110,7 @@ in {
openTcpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
description = ''
What TCP ports to allow traffic from. You might need this if you're
port forwarding on your VPN provider and you're setting up services
not covered in by this module that uses the VPN.
@@ -168,7 +121,7 @@ in {
openUdpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
description = ''
What UDP ports to allow traffic from. You might need this if you're
port forwarding on your VPN provider and you're setting up services
not covered in by this module that uses the VPN.
@@ -187,60 +140,22 @@ in {
to be set, but it was not.
'';
}
{
assertion = cfg.ddns.njalla.enable -> cfg.ddns.njalla.keysFile != null;
message = ''
The nixarr.ddns.njalla.enable option requires the
nixarr.ddns.njalla.keysFile option to be set, but it was not.
'';
}
];
# TODO: move this to modules, at least the "*Arrs"...
users.groups = {
media.gid = 992;
prowlarr = {};
media = {};
streamer = {};
torrenter = {};
};
# TODO: This is BAD. But seems necessary when using containers.
# The prefered solution is to just remove containerization.
# Look at https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/misc/ids.nix
# See also issue: https://github.com/rasmus-kirk/nixarr/issues/1
users.users = {
streamer = {
isSystemUser = true;
group = "streamer";
uid = lib.mkForce 316;
};
sonarr = {
isSystemUser = true;
group = "media";
uid = lib.mkForce 274;
};
radarr = {
isSystemUser = true;
group = "media";
uid = lib.mkForce 275;
};
lidarr = {
isSystemUser = true;
group = "media";
uid = lib.mkForce 306;
};
readarr = {
isSystemUser = true;
group = "media";
uid = lib.mkForce 309;
};
torrenter = {
isSystemUser = true;
group = "torrenter";
uid = lib.mkForce 70;
};
prowlarr = {
isSystemUser = true;
group = "prowlarr";
uid = lib.mkForce 293;
};
};
@@ -262,74 +177,64 @@ in {
"d '${cfg.mediaDir}/torrents/readarr' 0755 torrenter media - -"
];
util-nixarr.vpnnamespace = {
# TODO: wtf to do about openports
vpnnamespaces.wg = {
enable = cfg.vpn.enable ;
accessibleFrom = [
"192.168.1.0/24"
"127.0.0.1"
];
dnsServers = cfg.vpn.dnsServers;
wireguardAddressPath = cfg.vpn.wgAddress;
wireguardConfigFile = if cfg.vpn.wgConf != null then cfg.vpn.wgConf else "";
vpnTestService = {
wireguardConfigFile = cfg.vpn.wgConf;
};
# TODO: openports
systemd.services.vpn-test-service = {
enable = cfg.vpn.vpnTestService.enable;
port = cfg.vpn.vpnTestService.port;
};
openTcpPorts = cfg.vpn.openTcpPorts;
openUdpPorts = cfg.vpn.openUdpPorts;
vpnconfinement = {
enable = true;
vpnnamespace = "wg";
};
systemd.timers = mkIf cfg.ddns.njalla.enable {
ddnsNjalla = {
description = "Timer for setting the Njalla DDNS records";
script = let
vpn-test = pkgs.writeShellApplication {
name = "vpn-test";
timerConfig = {
OnBootSec = "30"; # Run 30 seconds after system boot
OnCalendar = "hourly";
Persistent = true; # Run service immediately if last window was missed
RandomizedDelaySec = "5min"; # Run service OnCalendar +- 5min
};
runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig];
wantedBy = ["multi-user.target"];
};
};
systemd.services = let
ddns-njalla = pkgs.writeShellApplication {
name = "ddns-njalla";
runtimeInputs = with pkgs; [ curl jq ];
# Thanks chatgpt...
text = ''
# Path to the JSON file
json_file="${cfg.ddns.njalla.keysFile}"
cd "$(mktemp -d)"
# Convert the JSON object into a series of tab-separated key-value pairs using jq
# - `to_entries[]`: Convert the object into an array of key-value pairs.
# - `[.key, .value]`: For each pair, create an array containing the key and the value.
# - `@tsv`: Convert the array to a tab-separated string.
# The output will be a series of lines, each containing a key and a value separated by a tab.
jq_command='to_entries[] | [.key, .value] | @tsv'
# Print resolv.conf
echo "/etc/resolv.conf contains:"
cat /etc/resolv.conf
# Read the converted output line by line
# - `IFS=$'\t'`: Use the tab character as the field separator.
# - `read -r key val`: For each line, split it into `key` and `val` based on the tab separator.
while IFS=$'\t' read -r key val; do
# For each key-value pair, execute the curl command
# Replace `''${key}` and `''${val}` in the URL with the actual key and value.
curl -s "https://njal.la/update/?h=''${key}&k=''${val}&auto"
done < <(jq -r "$jq_command" "$json_file")
'';
# Query resolvconf
echo "resolvconf output:"
resolvconf -l
echo ""
# Get ip
echo "Getting IP:"
curl -s ipinfo.io
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
'' + (if cfg.vpn.vpnTestService.port != null then ''
echo "starting netcat on port ${builtins.toString cfg.vpn.vpnTestService.port}:"
nc -vnlp ${builtins.toString cfg.vpn.vpnTestService.port}
'' else "");
};
in mkIf cfg.ddns.njalla.enable {
ddnsNjalla = {
description = "Sets the Njalla DDNS records";
in "${vpn-test}/bin/vpn-test";
bindsTo = ["netns@wg.service"];
requires = ["network-online.target"];
after = ["wg.service"];
serviceConfig = {
ExecStart = getExe ddns-njalla;
Type = "oneshot";
};
#User = "torrenter";
NetworkNamespacePath = "/var/run/netns/wg";
BindReadOnlyPaths = ["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"];
};
};
};
+7 -49
View File
@@ -7,7 +7,6 @@ let
cfg = config.nixarr.jellyfin;
defaultPort = 8096;
nixarr = config.nixarr;
dnsServers = config.lib.vpn.dnsServers;
in with lib; {
options.nixarr.jellyfin = {
enable = mkEnableOption "the Jellyfin service.";
@@ -228,57 +227,16 @@ in with lib; {
defaults.email = cfg.expose.https.acmeMail;
};
util-nixarr.vpnnamespace.portMappings = [
(
mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
}
)
];
systemd.services."container@jellyfin" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.jellyfin = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}/library".isReadOnly = false;
"${cfg.stateDir}".isReadOnly = false;
};
config = {
users.groups.streamer = {
gid = config.users.groups.streamer.gid;
};
users.users.streamer = {
uid = lib.mkForce config.users.users.streamer.uid;
isSystemUser = true;
group = "streamer";
};
# 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 and specify VPN namespace to confine service in.
systemd.services.jellyfin.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
user = "streamer";
group = "streamer";
logDir = "${cfg.stateDir}/log";
cacheDir = "${cfg.stateDir}/cache";
dataDir = "${cfg.stateDir}/data";
configDir = "${cfg.stateDir}/config";
vpnnamespace = "wg";
};
system.stateVersion = "23.11";
};
# Port mappings
# TODO: openports if expose.vpn
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
};
}
+7 -45
View File
@@ -5,7 +5,6 @@
}:
with lib; let
cfg = config.nixarr.lidarr;
dnsServers = config.lib.vpn.dnsServers;
nixarr = config.nixarr;
in {
options.nixarr.lidarr = {
@@ -50,53 +49,16 @@ in {
dataDir = cfg.stateDir;
};
util-nixarr.vpnnamespace.portMappings = [
(
mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
}
)
];
systemd.services."container@lidarr" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.lidarr = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}".isReadOnly = false;
"${cfg.stateDir}".isReadOnly = false;
};
config = {
users.groups.media = {
gid = config.users.groups.media.gid;
};
users.users.lidarr = {
uid = lib.mkForce config.users.users.lidarr.uid;
isSystemUser = true;
group = "media";
};
# 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.lidarr = {
# Enable and specify VPN namespace to confine service in.
systemd.services.lidarr.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
group = "media";
dataDir = "${cfg.stateDir}";
vpnnamespace = "wg";
};
system.stateVersion = "23.11";
};
# Port mappings
# TODO: openports
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
services.nginx = mkIf cfg.vpn.enable {
+8 -11
View File
@@ -75,19 +75,16 @@ in {
''
] else [];
util-nixarr.vpnnamespace = {
portMappings = builtins.map (x: { From = x; To = x; }) config.services.openssh.ports;
openUdpPorts = config.services.openssh.ports;
openTcpPorts = config.services.openssh.ports;
# Enable and specify VPN namespace to confine service in.
systemd.services.openssh.vpnconfinement = {
enable = true;
vpnnamespace = "wg";
};
systemd.services.openssh = {
bindsTo = [ "netns@wg.service" ];
requires = [ "network-online.target" ];
after = [ "wg.service" ];
serviceConfig = {
NetworkNamespacePath = "/var/run/netns/wg";
};
# Port mappings
# TODO: openports
vpnnamespaces.wg = {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
};
}
+8 -38
View File
@@ -6,7 +6,6 @@
}:
with lib; let
defaultPort = 9696;
dnsServers = config.lib.vpn.dnsServers;
nixarr = config.nixarr;
cfg = config.nixarr.prowlarr;
in {
@@ -49,51 +48,22 @@ in {
"d '${cfg.stateDir}' 0700 prowlarr root - -"
];
util-nixarr.services.prowlarr = mkIf (!cfg.vpn.enable) {
enable = true;
dataDir = cfg.stateDir;
};
util-nixarr.vpnnamespace.portMappings = [
(
mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
}
)
];
systemd.services."container@prowlarr" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.prowlarr = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts."${cfg.stateDir}".isReadOnly = false;
config = {
users.groups.prowlarr = {};
users.users.prowlarr = {
uid = lib.mkForce config.users.users.prowlarr.uid;
isSystemUser = true;
group = "prowlarr";
};
# 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;
util-nixarr.services.prowlarr = {
enable = true;
dataDir = cfg.stateDir;
};
system.stateVersion = "23.11";
# Enable and specify VPN namespace to confine service in.
systemd.services.prowlarr.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
vpnnamespace = "wg";
};
# Port mappings
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
services.nginx = mkIf cfg.vpn.enable {
+7 -46
View File
@@ -8,7 +8,6 @@ with lib; let
cfg = config.nixarr.radarr;
defaultPort = 7878;
nixarr = config.nixarr;
dnsServers = config.lib.vpn.dnsServers;
in {
options.nixarr.radarr = {
enable = mkEnableOption "Enable the Radarr service.";
@@ -45,60 +44,22 @@ in {
"d '${cfg.stateDir}' 0700 radarr root - -"
];
services.radarr = mkIf (!cfg.vpn.enable) {
services.radarr = {
enable = cfg.enable;
user = "radarr";
group = "media";
dataDir = cfg.stateDir;
};
util-nixarr.vpnnamespace.portMappings = [
(
mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
}
)
];
systemd.services."container@radarr" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.radarr = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}".isReadOnly = false;
"${cfg.stateDir}".isReadOnly = false;
};
config = {
users.groups.media = {
gid = config.users.groups.media.gid;
};
users.users.radarr = {
uid = lib.mkForce config.users.users.radarr.uid;
isSystemUser = true;
group = "media";
};
# 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.radarr = {
# Enable and specify VPN namespace to confine service in.
systemd.services.radarr.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
group = "media";
dataDir = cfg.stateDir;
vpnnamespace = "wg";
};
system.stateVersion = "23.11";
};
# Port mappings
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
services.nginx = mkIf cfg.vpn.enable {
+6 -45
View File
@@ -6,7 +6,6 @@
with lib; let
cfg = config.nixarr.readarr;
nixarr = config.nixarr;
dnsServers = config.lib.vpn.dnsServers;
in {
options.nixarr.readarr = {
enable = mkEnableOption "Enable the Readarr service";
@@ -50,53 +49,15 @@ in {
dataDir = cfg.stateDir;
};
util-nixarr.vpnnamespace.portMappings = [
(
mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
}
)
];
systemd.services."container@readarr" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.readarr = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}".isReadOnly = false;
"${cfg.stateDir}".isReadOnly = false;
};
config = {
users.groups.media = {
gid = config.users.groups.media.gid;
};
users.users.readarr = {
uid = lib.mkForce config.users.users.readarr.uid;
isSystemUser = true;
group = "media";
};
# 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.readarr = {
# Enable and specify VPN namespace to confine service in.
systemd.services.readarr.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
group = "media";
dataDir = "${cfg.stateDir}";
vpnnamespace = "wg";
};
system.stateVersion = "23.11";
};
# Port mappings
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
services.nginx = mkIf cfg.vpn.enable {
+8 -47
View File
@@ -8,7 +8,6 @@ with lib; let
cfg = config.nixarr.sonarr;
defaultPort = 8989;
nixarr = config.nixarr;
dnsServers = config.lib.vpn.dnsServers;
in {
options.nixarr.sonarr = {
enable = mkOption {
@@ -49,60 +48,22 @@ in {
"d '${cfg.stateDir}' 0700 sonarr root - -"
];
services.sonarr = mkIf (!cfg.vpn.enable) {
services.sonarr = {
enable = cfg.enable;
user = "sonarr";
group = "media";
dataDir = cfg.stateDir;
};
util-nixarr.vpnnamespace.portMappings = [
(mkIf cfg.vpn.enable {
From = defaultPort;
To = defaultPort;
})
];
systemd.services."container@sonarr" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
# Enable and specify VPN namespace to confine service in.
systemd.services.sonarr.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
vpnnamespace = "wg";
};
containers.sonarr = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}".isReadOnly = false;
"${cfg.stateDir}".isReadOnly = false;
};
config = {
users.groups.media = {
gid = config.users.groups.media.gid;
};
users.users.sonarr = {
uid = lib.mkForce config.users.users.sonarr.uid;
isSystemUser = true;
group = "media";
};
# 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;
users.groups.media = {};
services.sonarr = {
enable = cfg.enable;
group = "media";
dataDir = cfg.stateDir;
};
system.stateVersion = "23.11";
};
# Port mappings
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = defaultPort; To = defaultPort; }];
};
services.nginx = mkIf cfg.vpn.enable {
@@ -80,6 +80,7 @@ in {
users.users = mkIf (cfg.user == "cross-seed") {
cross-seed = {
isSystemUser = true;
group = cfg.group;
};
};
+16 -113
View File
@@ -7,7 +7,6 @@
with lib; let
cfg = config.nixarr.transmission;
nixarr = config.nixarr;
dnsServers = config.lib.vpn.dnsServers;
cfg-cross-seed = config.nixarr.transmission.privateTrackers.cross-seed;
transmissionCrossSeedScript = with builtins; pkgs.writeShellApplication {
name = "mk-cross-seed-credentials";
@@ -230,7 +229,7 @@ in {
)];
};
services.transmission = mkIf (!cfg.vpn.enable) {
services.transmission = {
enable = true;
user = "torrenter";
group = "torrenter";
@@ -250,10 +249,11 @@ in {
watch-dir-enabled = true;
watch-dir = "${nixarr.mediaDir}/torrents/.watch";
rpc-bind-address = "127.0.0.1";
rpc-bind-address = if cfg.vpn.enable then "192.168.15.1" else "127.0.0.1";
rpc-port = cfg.uiPort;
rpc-whitelist-enabled = true;
rpc-whitelist = "192.168.15.1,127.0.0.1";
# TODO: fix this for ssh tunneling...
rpc-whitelist-enabled = false;
rpc-whitelist = "192.168.15.1,127.0.0.1,192.168.1.*,192.168.0.*";
rpc-authentication-required = false;
blocklist-enabled = true;
@@ -269,8 +269,8 @@ in {
anti-brute-force-enabled = true;
anti-brute-force-threshold = 10;
script-torrent-done-enabled = true;
script-torrent-done-filename = getExe transmissionCrossSeedScript;
script-torrent-done-enabled = cfg-cross-seed.enable;
script-torrent-done-filename = if cfg-cross-seed.enable then transmissionCrossSeedScript else null;
message-level =
if cfg.messageLevel == "none"
@@ -292,115 +292,18 @@ in {
// cfg.extraSettings;
};
util-nixarr.vpnnamespace = mkIf cfg.vpn.enable {
portMappings = [
{
From = cfg.uiPort;
To = cfg.uiPort;
}
];
openUdpPorts = [cfg.peerPort];
openTcpPorts = [cfg.peerPort];
};
systemd.services."container@transmission" = mkIf cfg.vpn.enable {
requires = ["wg.service"];
};
containers.transmission = mkIf cfg.vpn.enable {
autoStart = true;
ephemeral = true;
extraFlags = ["--network-namespace-path=/var/run/netns/wg"];
bindMounts = {
"${nixarr.mediaDir}/torrents".isReadOnly = false;
"/var/lib/transmission" = {
hostPath = cfg.stateDir;
isReadOnly = false;
};
};
config = {
users.groups.torrenter = {
gid = config.users.groups.torrenter.gid;
};
users.users.torrenter = {
uid = lib.mkForce config.users.users.torrenter.uid;
isSystemUser = true;
group = "torrenter";
};
# 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;
systemd.services.transmission.serviceConfig = {
RootDirectoryStartOnly = lib.mkForce false;
RootDirectory = lib.mkForce "";
};
services.transmission = {
# Enable and specify VPN namespace to confine service in.
systemd.services.transmission.vpnconfinement = mkIf cfg.vpn.enable {
enable = true;
user = "torrenter";
group = "torrenter";
webHome =
if cfg.flood.enable
then pkgs.flood-for-transmission
else null;
package = pkgs.transmission_4;
openRPCPort = true;
openPeerPorts = true;
settings =
{
download-dir = "${nixarr.mediaDir}/torrents";
incomplete-dir-enabled = true;
incomplete-dir = "${nixarr.mediaDir}/torrents/.incomplete";
watch-dir-enabled = true;
watch-dir = "${nixarr.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;
blocklist-enabled = true;
blocklist-url = "https://github.com/Naunter/BT_BlockLists/raw/master/bt_blocklists.gz";
peer-port = cfg.peerPort;
dht-enabled = !cfg.privateTrackers.disableDhtPex;
pex-enabled = !cfg.privateTrackers.disableDhtPex;
utp-enabled = false;
encryption = 1;
port-forwarding-enabled = false;
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.extraSettings;
vpnnamespace = "wg";
};
environment.systemPackages = with pkgs; [
curl
wget
util-linux
unixtools.ping
coreutils
curl
bash
libressl
netcat-gnu
openresolv
dig
];
system.stateVersion = "23.11";
};
# Port mappings
# TODO: open peerPort
vpnnamespaces.wg = mkIf cfg.vpn.enable {
portMappings = [{ From = cfg.uiPort; To = cfg.uiPort; }];
#openUdpPorts = [cfg.peerPort];
#openTcpPorts = [cfg.peerPort];
};
services.nginx = mkIf cfg.vpn.enable {
-1
View File
@@ -1,6 +1,5 @@
{
imports = [
./upnp
./vpnNamespace
];
}
-369
View File
@@ -1,369 +0,0 @@
# Thank you Maroka-chan <3
{
lib,
pkgs,
config,
...
}:
with builtins;
with lib; let
cfg = config.util-nixarr.vpnnamespace;
in {
options.util-nixarr.vpnnamespace = {
enable = mkEnableOption ''
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.
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";
};
'';
accessibleFrom = mkOption {
type = types.listOf types.str;
default = [];
description = lib.mdDoc ''
Subnets or specific addresses that the namespace should be accessible to.
'';
example = [
"10.0.2.0/24"
"192.168.1.27"
];
};
namespaceAddress = mkOption {
type = types.str;
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.
'';
};
bridgeAddress = mkOption {
type = types.str;
default = "192.168.15.5";
description = lib.mdDoc ''
The address of the linux bridge on the default namespace.
The linux bridge sits on the default namespace and
needs an address to make communication between the
default namespace and other namespaces on the
bridge possible.
'';
};
wireguardAddressPath = mkOption {
type = types.path;
default = "";
description = lib.mdDoc ''
The address for the wireguard interface.
It is a path to a file containing the address.
This is done so the whole wireguard config can be specified
in a secret file.
'';
};
wireguardConfigFile = mkOption {
type = types.path;
default = "/etc/wireguard/wg0.conf";
description = lib.mdDoc ''
Path to the wireguard config to use.
Note that this _is_ a wg-quick config.
'';
};
portMappings = mkOption {
type = with types; listOf (attrsOf port);
default = [];
description = lib.mdDoc ''
A list of pairs mapping a port from the host to a port in the namespace.
'';
example = [
{
From = 80;
To = 80;
}
];
};
dnsServers = mkOption {
type = with types; nullOr (listOf str);
default = [];
description = lib.mdDoc ''
DNS servers to append to any found in the wg-quick config file.
'';
example = ["1.1.1.2"];
};
openTcpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
What TCP ports to allow incoming traffic from. You need this if
you're port forwarding on your VPN provider.
'';
example = [46382 38473];
};
openUdpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
What UDP ports to allow incoming traffic from. You need this if
you're port forwarding on your VPN provider.
'';
example = [46382 38473];
};
vpnTestService = {
enable = mkEnableOption "Enable the vpn test service.";
port = mkOption {
type = types.port;
default = [12300];
description = lib.mdDoc ''
The port that the vpn test service listens to.
'';
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);
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;
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";
};
};
wg = {
description = "wg network interface";
bindsTo = ["netns@wg.service"];
requires = ["network-online.target"];
after = ["netns@wg.service"];
wantedBy = ["multi-user.target"];
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;
};
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"];
};
};
};
};
}