nixos/modules/home/gluetun-user.nix
2026-01-14 21:24:19 +01:00

124 lines
3.7 KiB
Nix

# Gluetun User Service Module (Home Manager)
# Provides: Rootless Gluetun VPN container as user systemd service
#
# Usage:
# myModules.gluetunUser = {
# enable = true;
# environmentFile = "/run/secrets/rendered/gluetun.env";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.gluetunUser;
in
{
options.myModules.gluetunUser = {
enable = lib.mkEnableOption "Rootless Gluetun VPN container service";
image = lib.mkOption {
type = lib.types.str;
default = "qmcgaw/gluetun:latest";
description = "Gluetun container image";
};
webPort = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Web UI port for Gluetun";
};
environmentFile = lib.mkOption {
type = lib.types.str;
description = "Path to environment file with VPN credentials";
};
secretsDir = lib.mkOption {
type = lib.types.str;
default = "/run/secrets";
description = "Path to secrets directory";
};
dnsAddress = lib.mkOption {
type = lib.types.str;
default = "1.1.1.1";
description = "DNS server address for VPN";
};
extraPorts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra ports to map (e.g. ['3000:80'])";
};
wireguardMtu = lib.mkOption {
type = lib.types.int;
default = 1280;
description = "WireGuard MTU setting";
};
keepaliveInterval = lib.mkOption {
type = lib.types.str;
default = "15s";
description = "WireGuard persistent keepalive interval";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.gluetun = {
Unit = {
Description = "Gluetun VPN Container (Rootless)";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Restart = "always";
ExecStartPre = [
"-${pkgs.podman}/bin/podman system migrate"
"-${pkgs.podman}/bin/podman stop gluetun"
];
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --replace --name gluetun \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--sysctl=net.ipv4.conf.all.src_valid_mark=1 \
--add-host=host.containers.internal:host-gateway \
-v ${cfg.environmentFile}:${cfg.environmentFile}:ro \
-v ${cfg.secretsDir}:${cfg.secretsDir}:ro \
-p 127.0.0.1:${toString cfg.webPort}:8080 \
${lib.concatMapStringsSep " " (p: "-p ${p}") cfg.extraPorts} \
-e VPN_SERVICE_PROVIDER=custom \
-e VPN_TYPE=wireguard \
-e WIREGUARD_IMPLEMENTATION=userspace \
-e WIREGUARD_PRIVATE_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_private_key \
-e WIREGUARD_PRESHARED_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_preshared_key \
-e WIREGUARD_ADDRESSES_SECRETFILE=${cfg.secretsDir}/wireguard_addresses \
-e WIREGUARD_PUBLIC_KEY=''${WIREGUARD_PUBLIC_KEY} \
-e WIREGUARD_ENDPOINT_IP=''${WIREGUARD_ENDPOINT_IP} \
-e WIREGUARD_ENDPOINT_PORT=''${WIREGUARD_ENDPOINT_PORT} \
-e WIREGUARD_MTU=1280 \
-e WIREGUARD_PERSISTENT_KEEPALIVE_INTERVAL=15s \
-e DNS_ADDRESS=${cfg.dnsAddress} \
-e DOT=off \
-e DOT_CACHING=on \
-e HEALTH_RESTART_VPN=off \
-e LOG_LEVEL=info \
-e FIREWALL_VPN_INPUT_PORTS=4000,3001,3000,9090,36630 \
${cfg.image}
'';
EnvironmentFile = [ cfg.environmentFile ];
ExecStop = "${pkgs.podman}/bin/podman stop gluetun";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}