diff --git a/hosts/jupiter/users/storm/configs/console/podman/default.nix b/hosts/jupiter/users/storm/configs/console/podman/default.nix index ca51872..faf0621 100644 --- a/hosts/jupiter/users/storm/configs/console/podman/default.nix +++ b/hosts/jupiter/users/storm/configs/console/podman/default.nix @@ -19,6 +19,7 @@ in (import ./traefik { inherit user home; }) (import ./vaultwarden { inherit user home; }) (import ./whoami { inherit user home; }) + (import ./wireguard { inherit user home; }) ]; boot.kernel.sysctl = { diff --git a/hosts/jupiter/users/storm/configs/console/podman/wireguard/default.nix b/hosts/jupiter/users/storm/configs/console/podman/wireguard/default.nix new file mode 100644 index 0000000..e87f683 --- /dev/null +++ b/hosts/jupiter/users/storm/configs/console/podman/wireguard/default.nix @@ -0,0 +1,201 @@ +{ + user ? throw "user argument is required", + home ? throw "home argument is required", +}: +{ + config, + inputs, + system, + lib, + pkgs, + ... +}: +let + selfPkgs = inputs.self.packages.${system}; + inherit (config.virtualisation.quadlet) containers volumes; + wireguardPort = 51820; +in +{ + networking.firewall.allowedUDPPorts = [ wireguardPort ]; + + boot = { + kernelModules = [ "wireguard" ]; + kernel.sysctl."net.ipv4.ip_forward" = 1; + }; + + sops = { + secrets = { + "wireguard/server".sopsFile = ../../../../../../secrets/secrets.yaml; + "wireguard-ui/sessionSecret".sopsFile = ../../../../../../secrets/secrets.yaml; + "wireguard-ui/smtp".sopsFile = ../../../../../../secrets/secrets.yaml; + }; + templates = { + wireguard-env.content = '' + SESSION_SECRET=${config.sops.placeholder."wireguard-ui/sessionSecret"} + SMTP_PASSWORD=${config.sops.placeholder."wireguard-ui/smtp"} + ''; + + wireguard-keypair.content = builtins.readFile ( + (pkgs.formats.json { }).generate "keypair.json" { + private_key = config.sops.placeholder."wireguard/server"; + public_key = "zz8jO+/rQbp2/GKuYrowcBe/LfO+zeJfEbF7/WIl+WQ="; + updated_at = "1970-01-01T00:00:00.000000000Z"; + } + ); + }; + }; + + virtualisation.quadlet = { + volumes.wireguard = { }; + + containers.wireguard = { + containerConfig = { + image = "docker-archive:${selfPkgs.docker-wireguard-ui}"; + networks = [ "host" ]; + volumes = [ + "${volumes.wireguard.ref}:/var/lib/wireguard-ui/db/clients" + "${config.sops.templates.wireguard-keypair.path}:/var/lib/wireguard-ui/db/server/keypair.json:ro" + ]; + environments = + let + publicIPs = [ + "0.0.0.0/5" + "8.0.0.0/7" + "11.0.0.0/8" + "12.0.0.0/6" + "16.0.0.0/4" + "32.0.0.0/3" + "64.0.0.0/2" + "128.0.0.0/3" + "160.0.0.0/5" + "168.0.0.0/6" + "172.0.0.0/12" + "172.32.0.0/11" + "172.64.0.0/10" + "172.128.0.0/9" + "173.0.0.0/8" + "174.0.0.0/7" + "176.0.0.0/4" + "192.0.0.0/9" + "192.128.0.0/11" + "192.160.0.0/13" + "192.169.0.0/16" + "192.170.0.0/15" + "192.172.0.0/14" + "192.176.0.0/12" + "192.192.0.0/10" + "193.0.0.0/8" + "194.0.0.0/7" + "196.0.0.0/6" + "200.0.0.0/5" + "208.0.0.0/4" + "1.1.1.1/32" + ]; + in + { + EMAIL_FROM_ADDRESS = "jupiter@karaolidis.com"; + EMAIL_FROM_NAME = "WireGuard"; + SMTP_HOSTNAME = "smtp.protonmail.ch"; + SMTP_PORT = "587"; + SMTP_USERNAME = "jupiter@karaolidis.com"; + SMTP_AUTH_TYPE = "PLAIN"; + SMTP_ENCRYPTION = "STARTTLS"; + DISABLE_LOGIN = "true"; + WGUI_SERVER_INTERFACE_ADDRESSES = "10.252.1.1/24"; + WGUI_ENDPOINT_ADDRESS = "vpn.karaolidis.com"; + WGUI_CONFIG_FILE_PATH = "/etc/wireguard/wg1.conf"; + WGUI_SERVER_POST_UP_SCRIPT = lib.strings.concatStringsSep "; " ( + [ + "ipset create wireguard-public hash:net || true" + "ipset flush wireguard-public" + ] + ++ builtins.map (ip: "ipset add wireguard-public ${ip}") publicIPs + ++ [ + "iptables -I FORWARD -i wg1 -m set --match-set wireguard-public dst -j ACCEPT" + "iptables -A FORWARD -i wg1 -j DROP" + "iptables -t nat -A POSTROUTING -s 10.252.1.0/24 -o enp2s0 -j MASQUERADE" + ] + ); + WGUI_SERVER_POST_DOWN_SCRIPT = lib.strings.concatStringsSep "; " [ + "iptables -t nat -D POSTROUTING -s 10.252.1.0/24 -o enp2s0 -j MASQUERADE" + "iptables -D FORWARD -i wg1 -j DROP" + "iptables -D FORWARD -i wg1 -m set --match-set wireguard-public dst -j ACCEPT" + "ipset destroy wireguard-public" + ]; + WGUI_DEFAULT_CLIENT_ALLOWED_IPS = lib.strings.concatStringsSep "," publicIPs; + }; + environmentFiles = [ config.sops.templates.wireguard-env.path ]; + addCapabilities = [ + "NET_RAW" + "NET_ADMIN" + ]; + }; + + serviceConfig.Sockets = [ "wireguard-ui.socket" ]; + + unitConfig = { + After = [ + "sops-nix.service" + "wireguard-ui.socket" + ]; + + Requires = [ "wireguard-ui.socket" ]; + }; + }; + }; + + systemd.sockets.wireguard-ui = { + listenStreams = [ "/run/wireguard/wireguard-ui.sock" ]; + + socketConfig = { + Service = "${containers.wireguard._serviceName}.service"; + SocketMode = 660; + SocketUser = "root"; + SocketGroup = config.users.users.${user}.group; + }; + + wantedBy = [ "default.target" ]; + }; + + home-manager.users.${user} = { + virtualisation.quadlet = { + containers = { + authelia-init.containerConfig.volumes = + let + config = (pkgs.formats.yaml { }).generate "wireguard.yaml" { + access_control.rules = [ + { + domain = "vpn.karaolidis.com"; + policy = "two_factor"; + subject = [ "group:admins" ]; + } + ]; + }; + in + [ "${config}:/etc/authelia/conf.d/wireguard.yaml:ro" ]; + + traefik.containerConfig = { + volumes = + let + wireguard = (pkgs.formats.yaml { }).generate "wireguard.yaml" { + http = { + routers.wireguard = { + service = "wireguard"; + rule = "Host(`vpn.karaolidis.com`)"; + middlewares = "authelia@docker"; + }; + + services.wireguard.loadBalancer.servers = [ { url = "unix:///run/wireguard/wireguard-ui.sock"; } ]; + }; + }; + in + [ + "/run/wireguard/wireguard-ui.sock:/run/wireguard/wireguard-ui.sock" + "${wireguard}:/etc/traefik/wireguard.yaml:ro" + ]; + exec = [ "--providers.file.filename=/etc/traefik/wireguard.yaml" ]; + }; + }; + }; + }; +} diff --git a/packages/default.nix b/packages/default.nix index 68b6abd..315f008 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -35,6 +35,7 @@ docker-sish = import ./docker/sish { inherit pkgs; }; docker-traefik = import ./docker/traefik { inherit pkgs; }; docker-whoami = import ./docker/whoami { inherit pkgs; }; + docker-wireguard-ui = import ./docker/wireguard-ui { inherit pkgs; }; docker-yq = import ./docker/yq { inherit pkgs; }; linux-firmware-latest = import ./linux-firmware-latest { inherit pkgs; }; diff --git a/packages/docker/traefik/default.nix b/packages/docker/traefik/default.nix index 68e62d1..02b9cdb 100644 --- a/packages/docker/traefik/default.nix +++ b/packages/docker/traefik/default.nix @@ -1,11 +1,22 @@ { pkgs, ... }: +let + # FIXME: https://github.com/traefik/traefik/issues/4881 + traefik = pkgs.traefik.overrideAttrs (oldAttrs: { + patches = oldAttrs.patches or [ ] ++ [ + (builtins.fetchurl { + url = "https://github.com/traefik/traefik/commit/e877a94b2a759b93fc886cba53f5fa9bc1e973ed.patch"; + sha256 = "sha256:178s4m2jnvr081slvgv48b6g4sispfwj2k7mfwskcyry1g2nbfvb"; + }) + ]; + }); +in pkgs.dockerTools.buildImage { name = "traefik"; fromImage = import ../base { inherit pkgs; }; copyToRoot = pkgs.buildEnv { name = "root"; - paths = with pkgs; [ traefik ]; + paths = [ traefik ]; pathsToLink = [ "/bin" ]; }; diff --git a/packages/docker/wireguard-ui/customization.patch b/packages/docker/wireguard-ui/customization.patch new file mode 100644 index 0000000..8815de0 --- /dev/null +++ b/packages/docker/wireguard-ui/customization.patch @@ -0,0 +1,1152 @@ +diff --git a/README.md b/README.md +index 74c446e..67c5718 100644 +--- a/README.md ++++ b/README.md +@@ -9,7 +9,7 @@ A web user interface to manage your WireGuard setup. + - Friendly UI + - Authentication + - Manage extra client information (name, email, etc.) +-- Retrieve client config using QR code / file / email / Telegram ++- Retrieve client config using QR code / file / email + +  + +@@ -39,7 +39,6 @@ docker-compose up + | Variable | Description | Default | + |-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------| + | `BASE_PATH` | Set this variable if you run wireguard-ui under a subpath of your reverse proxy virtual host (e.g. /wireguard) | N/A | +-| `BIND_ADDRESS` | The addresses that can access to the web interface and the port, use unix:///abspath/to/file.socket for unix domain socket. | 0.0.0.0:80 | + | `SESSION_SECRET` | The secret key used to encrypt the session cookies. Set this to a random value | N/A | + | `SESSION_SECRET_FILE` | Optional filepath for the secret key used to encrypt the session cookies. Leave `SESSION_SECRET` blank to take effect | N/A | + | `SESSION_MAX_DURATION` | Max time in days a remembered session is refreshed and valid. Non-refreshed session is valid for 7 days max, regardless of this setting. | 90 | +@@ -71,9 +70,6 @@ docker-compose up + | `SMTP_AUTH_TYPE` | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE` | `NONE` | + | `SMTP_ENCRYPTION` | The encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS` | `STARTTLS` | + | `SMTP_HELO` | Hostname to use for the HELO message. smtp-relay.gmail.com needs this set to anything but `localhost` | `localhost` | +-| `TELEGRAM_TOKEN` | Telegram bot token for distributing configs to clients | N/A | +-| `TELEGRAM_ALLOW_CONF_REQUEST` | Allow users to get configs from the bot by sending a message | `false` | +-| `TELEGRAM_FLOOD_WAIT` | Time in minutes before the next conf request is processed | `60` | + + ### Defaults for server configuration + +diff --git a/custom/js/helper.js b/custom/js/helper.js +index d98eacb..931320a 100644 +--- a/custom/js/helper.js ++++ b/custom/js/helper.js +@@ -1,20 +1,5 @@ + function renderClientList(data) { + $.each(data, function(index, obj) { +- // render telegram button +- let telegramButton = '' +- if (obj.Client.telegram_userid) { +- telegramButton = `