AI warning: użyte. GLM-5.2
Snippety głównie do wykorzystania przez LLMy, przepycha cały ruch z apki dekstopowej odpalonej przez wrapper przez interfejs wireguardowy w dedykowanym ns. Użyte do odpalenia stremio bez dostępu do sieci spoza mullvada.
mullvad-pl.nix
# mullvad-pl network namespace + Mullvad WireGuard tunnel.
#
# Shared VPN-confinement infrastructure for the per-app modules under
# hosts/tiwi/apps/. Each confined app (popcorntime, stremio, ...) launches into
# the "mullvad-pl" namespace, so only its traffic egresses via Mullvad.
# Tailscale lives in the init namespace and is never touched.
#
# Built with a manual systemd service rather than networking.wireguard.interfaces
# because tiwi uses networking.useNetworkd, with which the namespace/preSetup/
# postShutdown options are incompatible.
{ pkgs, ... }:
let
netns = "mullvad-pl";
in
{
# Bring up the mullvad-pl netns + Mullvad WireGuard interface.
# Interface is created in the init namespace (so its UDP socket egresses via
# br0 to the endpoint) and then moved into the netns. All addressing and the
# default route live only inside the netns; Tailscale is never touched.
systemd.services.mullvad-pl-netns = {
description = "Mullvad WireGuard tunnel in the ${netns} network namespace";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
iproute2
wireguard-tools
kmod
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
ip netns del ${netns} 2>/dev/null || true
ip netns add ${netns}
ip -n ${netns} link set lo up
# Create and configure WireGuard in the init namespace, then move it.
modprobe wireguard || true
ip link add ${netns} type wireguard
wg set ${netns} \
private-key /etc/nixos/secrets/mullvad-privatekey \
peer nJEWae9GebEY7yJONXQ1j4gbURV4QULjx388woAlbDs= \
endpoint 45.134.212.79:51820 \
allowed-ips 0.0.0.0/0,::/0 \
persistent-keepalive 25
ip link set ${netns} netns ${netns}
ip -n ${netns} address add 10.65.101.79/32 dev ${netns}
ip -n ${netns} -6 address add fc00:bbbb:bbbb:bb01::2:654e/128 dev ${netns}
ip -n ${netns} link set ${netns} up mtu 1420
ip -n ${netns} route add default dev ${netns}
ip -n ${netns} -6 route add default dev ${netns}
'';
postStop = ''
ip netns del ${netns} 2>/dev/null || true
'';
};
# DNS inside the namespace (ip-netns bind-mounts this over /etc/resolv.conf).
environment.etc."netns/${netns}/resolv.conf".text = "nameserver 10.64.0.1\n";
}
_netns-app.nix
# Helper: confine a GUI app to the mullvad-pl network namespace.
#
# This file is NOT a NixOS module on its own -- it is a function that RETURNS
# one. Usage from a sibling app module:
#
# { pkgs, ... }: {
# imports = [ (import ./_netns-app.nix {
# name = "stremio";
# package = pkgs.stremio-linux-shell;
# binary = "stremio";
# desktopName = "Stremio (VPN)";
# icon = "${pkgs.stremio-linux-shell}/share/icons/hicolor/scalable/apps/com.stremio.Stremio.svg";
# extraArgs = [ "--password-store=basic" ];
# }) ];
# }
#
# Requires the "mullvad-pl" netns to exist (created by services/mullvad-pl.nix).
{
name,
package,
desktopName,
icon,
binary ? package.meta.mainProgram or name,
extraArgs ? [ ],
startupWMClass ? null,
netns ? "mullvad-pl",
user ? "tv",
}:
{ pkgs, lib, ... }:
let
launcherName = "${name}-vpn";
binPath = "${package}/bin/${binary}";
extraFlags = lib.optionalString (extraArgs != [ ]) " ${lib.concatStringsSep " " extraArgs}";
# Self-elevating launcher: ${user} -> sudo (NOPASSWD+SETENV, scoped to this
# binary) -> ip netns exec ${netns} setpriv ${user} -> app.
#
# `sudo -E` carries ${user}'s full Plasma/Wayland session env across the
# privilege boundary; `setpriv` drops to ${user} WITHOUT PAM env-rewriting
# (runuser rewrites via PAM), so the whole environment survives into the
# namespace -- nothing is hand-set (XDG_RUNTIME_DIR, WAYLAND_DISPLAY, DBUS_*,
# the correct PULSE_SERVER all come from the inherited session).
# Fail-closed: if the namespace/tunnel is down, the app has no network.
launcher = pkgs.writeShellScriptBin launcherName ''
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
exec sudo -n -E -- /run/current-system/sw/bin/${launcherName} "$@"
fi
exec ${pkgs.iproute2}/bin/ip netns exec ${netns} \
${pkgs.util-linux}/bin/setpriv \
--reuid="$(id -u ${user})" --regid="$(id -g ${user})" --init-groups \
--inh-caps=-all \
-- ${binPath}${extraFlags} "$@"
'';
# The app without its own (leaking) .desktop, so only our VPN entry shows.
package-noDesktop = pkgs.symlinkJoin {
name = "${name}-nodesktop";
paths = [ package ];
postBuild = "rm -f $out/share/applications/*.desktop";
};
desktopItem = pkgs.makeDesktopItem ({
inherit desktopName icon;
name = launcherName;
exec = launcherName;
comment = "${desktopName} (traffic routed via the ${netns} VPN namespace)";
categories = [
"AudioVideo"
"Video"
];
type = "Application";
} // lib.optionalAttrs (startupWMClass != null) { inherit startupWMClass; });
in
{
environment.systemPackages = [
package-noDesktop
launcher
desktopItem
];
# ${user} may launch only this one wrapper as root, no password.
security.sudo.extraRules = [
{
users = [ user ];
runAs = "root";
commands = [
{
command = "/run/current-system/sw/bin/${launcherName}";
options = [
"NOPASSWD"
"SETENV"
];
}
];
}
];
}
stremio.nix
# Stremio client, confined to the mullvad-pl VPN namespace.
# Uses stremio-linux-shell (Rust + CEF + libmpv); the old qt5 stremio was
# removed from nixpkgs on 2026-02-11. Requires nixpkgs.config.allowUnfree
# (its bundled server.js is unfree).
{ pkgs, ... }:
let
stremio = pkgs.stremio-linux-shell.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ ./stremio-zoom.patch ];
});
in
{
imports = [
(import ./_netns-app.nix {
name = "stremio";
package = stremio;
binary = "stremio";
desktopName = "Stremio (VPN)";
icon = "${stremio}/share/icons/hicolor/scalable/apps/com.stremio.Stremio.svg";
startupWMClass = "stremio";
extraArgs = [ "--password-store=basic" ];
})
];
}