diff --git a/.gitignore b/.gitignore index cd36e384..3f17dab1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,11 @@ dist-ssr *.db-shm *.db-wal +src-tauri/gen/ + .direnv .envrc +.aider* -src-tauri/gen/ +# nix stuff +result diff --git a/flake.lock b/flake.lock index 0384f507..7cc2ca6b 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1752596105, - "narHash": "sha256-lFNVsu/mHLq3q11MuGkMhUUoSXEdQjCHvpReaGP1S2k=", + "lastModified": 1755706679, + "narHash": "sha256-WJ6eaSiN6xtz3vyH2bTYLQ3+ct0W8ai/BkYaq1n1jP8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dab3a6e781554f965bde3def0aa2fda4eb8f1708", + "rev": "c3fc1fe6d8765d99c8614c6f82d611dc56b9ae37", "type": "github" }, "original": { @@ -60,11 +60,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1752633862, - "narHash": "sha256-Bj7ozT1+5P7NmvDcuAXJvj56txcXuAhk3Vd9FdWFQzk=", + "lastModified": 1755743804, + "narHash": "sha256-M6qT02voARH5e9eTXQBzpYIE/hAp6jPgBCyxLmw5uBM=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8668ca94858206ac3db0860a9dec471de0d995f8", + "rev": "80322e975e27d834451d6b66e63f8abae9d74bf2", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c83e4447..f4de5c37 100644 --- a/flake.nix +++ b/flake.nix @@ -3,6 +3,9 @@ nixpkgs.url = "nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; rust-overlay.url = "github:oxalica/rust-overlay"; + + # include git submodules + # self.submodules = true; }; outputs = { @@ -12,65 +15,23 @@ rust-overlay, }: flake-utils.lib.eachDefaultSystem (system: let + # add rust overlay pkgs = import nixpkgs { inherit system; overlays = [rust-overlay.overlays.default]; }; - - toolchain = pkgs.rust-bin.stable.latest.default.override { - extensions = ["rust-analyzer" "rust-src" "rustfmt" "clippy"]; - targets = ["wasm32-unknown-unknown" "x86_64-apple-darwin" "aarch64-apple-darwin" "x86_64-pc-windows-gnu"]; - }; - packages = with pkgs; [ - cargo - cargo-tauri - toolchain - rust-analyzer-unwrapped - nodejs_20 - nodePackages.pnpm - trunk - sqlx-cli - vtsls - ]; - nativeBuildPackages = with pkgs; [ - pkg-config - dbus - openssl - glib - gtk3 - libsoup_2_4 - webkitgtk_4_0 - librsvg - protobuf - libayatana-appindicator - ]; - libraries = with pkgs; [ - gtk3 - cairo - gdk-pixbuf - glib - dbus - openssl - librsvg - libsoup_3 - webkitgtk_4_0 - libayatana-appindicator - ]; in { - devShells.default = pkgs.mkShell { - buildInputs = packages; - nativeBuildInputs = nativeBuildPackages; - shellHook = with pkgs; '' - export LD_LIBRARY_PATH="${ - lib.makeLibraryPath libraries - }:$LD_LIBRARY_PATH" - export OPENSSL_INCLUDE_DIR="${openssl.dev}/include/openssl" - export OPENSSL_LIB_DIR="${openssl.out}/lib" - export OPENSSL_ROOT_DIR="${openssl.out}" - # https://discourse.nixos.org/t/which-package-includes-org-gtk-gtk4-settings-filechooser/38063/12 - export XDG_DATA_DIRS="${gtk3}/share/gsettings-schemas/gtk+3-${gtk3.dev.version}:$XDG_DATA_DIRS" - export RUST_SRC_PATH="${toolchain}/lib/rustlib/src/rust/library" - ''; + devShells.default = import ./nix/shell.nix { + inherit pkgs; }; - }); + + packages.default = pkgs.callPackage ./nix/package.nix { + inherit pkgs; + }; + + formatter = pkgs.alejandra; + }) + // { + nixosModules.default = import ./nix/nixos-module.nix; + }; } diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 00000000..b21b0b1f --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,75 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + defguard-client = pkgs.callPackage ./package.nix {}; + cfg = config.programs.defguard-client; +in { + options.programs.defguard-client = { + enable = mkEnableOption "Defguard VPN client and service"; + + package = mkOption { + type = types.package; + default = defguard-client; + description = "defguard-client package to use"; + }; + + logLevel = mkOption { + type = types.str; + default = "info"; + description = "Log level for defguard-service"; + }; + + statsPeriod = mkOption { + type = types.int; + default = 30; + description = "Interval in seconds for interface statistics updates"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + systemd.services.defguard-service = { + description = "Defguard VPN Service"; + wantedBy = ["multi-user.target"]; + wants = ["network-online.target"]; + after = ["network-online.target"]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/defguard-service --log-level ${cfg.logLevel} --stats-period ${toString cfg.statsPeriod}"; + Restart = "on-failure"; + RestartSec = 5; + User = "defguard"; + Group = "defguard"; + StateDirectory = "defguard"; + LogsDirectory = "defguard"; + # Add capabilities to manage network interfaces + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE"; + AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE"; + # Allow access to /dev/net/tun for TUN/TAP devices + DeviceAllow = "/dev/net/tun rw"; + # Access to /sys for network configuration + BindReadOnlyPaths = [ + "/sys" + "/proc" + ]; + # Protect the system while giving necessary access + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + # Allow the service to manage network namespaces + PrivateNetwork = false; + }; + }; + + users.users.defguard = { + isSystemUser = true; + group = "defguard"; + }; + + users.groups.defguard = {}; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 00000000..2fed2680 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,122 @@ +{ + pkgs, + lib, + stdenv, + rustPlatform, + makeDesktopItem, +}: let + pname = "defguard-client"; + version = "1.5.1"; # TODO: Get this from Cargo.toml or git + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = pname; + desktopName = "Defguard"; + genericName = "Defguard VPN Client"; + categories = ["Network" "Security"]; + }; + + rustToolchain = pkgs.rust-bin.stable.latest.default; + + buildInputs = with pkgs; [ + at-spi2-atk + atkmm + cairo + dbus + gdk-pixbuf + glib + glib-networking + gtk4 + harfbuzz + librsvg + libsoup_3 + pango + webkitgtk_4_1 + openssl + libayatana-appindicator + mesa + libGL + libGLU + ]; + + nativeBuildInputs = with pkgs; [ + rustToolchain + pkg-config + gobject-introspection + cargo-tauri + nodejs_24 + protobuf + pnpm + # configures pnpm to use pre-fetched dependencies + pnpm.configHook + # configures cargo to use pre-fetched dependencies + rustPlatform.cargoSetupHook + perl + wrapGAppsHook + # helper to add dynamic library paths + makeWrapper + ]; +in + stdenv.mkDerivation (finalAttrs: rec { + inherit pname version buildInputs nativeBuildInputs; + + src = ../.; + + # prefetch cargo dependencies + cargoRoot = "src-tauri"; + buildAndTestSubdir = "src-tauri"; + + cargoDeps = rustPlatform.importCargoLock { + lockFile = ../src-tauri/Cargo.lock; + # specify hashes for git dependencies + # outputHashes = { + # "defguard_wireguard_rs-0.7.5" = "sha256-pxwN43BntOEYtp+TlpQFX78gg1ko4zuXEGctZIfSrhg="; + # "tauri-plugin-log-0.0.0" = "sha256-jGzlN/T29Hya4bKe9Dwl2mRRFLXMywrHk+32zgwrpJ0="; + # }; + }; + + # prefetch pnpm dependencies + pnpmDeps = pkgs.pnpm.fetchDeps { + inherit + (finalAttrs) + pname + version + src + ; + + fetcherVersion = 2; + hash = "sha256-ccSwlPY3sOnUJoYfB4MWs0gU8/Aq/CiCrLWouQ7PqhY="; + }; + + buildPhase = '' + pnpm tauri build + ''; + + postInstall = '' + # copy client binary + mkdir -p $out/bin + cp src-tauri/target/release/${pname} $out/bin/ + # copy service binary + mkdir -p $out/bin + cp src-tauri/target/release/defguard-service $out/bin/ + # copy cli binary + mkdir -p $out/bin + cp src-tauri/target/release/dg $out/bin/ + + # add required library to client binary RPATH + wrapProgram $out/bin/${pname} \ + --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [pkgs.libayatana-appindicator]} + + mkdir -p $out/share/applications + cp ${desktopItem}/share/applications/* $out/share/applications/ + ''; + + meta = with lib; { + description = "Defguard VPN Client"; + homepage = "https://defguard.net"; + # license = licenses.gpl3Only; + maintainers = with maintainers; []; + platforms = platforms.linux; + }; + }) diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 00000000..3de733fd --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1,37 @@ +{pkgs ? import {}}: let + # add development-related cargo tooling + rustToolchain = pkgs.rust-bin.stable.latest.default.override { + extensions = ["rust-analyzer" "rust-src" "rustfmt" "clippy"]; + targets = ["x86_64-apple-darwin" "aarch64-apple-darwin" "x86_64-pc-windows-gnu"]; + }; + + defguard-client = pkgs.callPackage ./package.nix {}; + + # runtime libraries needed to run the dev server + libraries = with pkgs; [ + libayatana-appindicator + ]; +in + pkgs.mkShell { + # inherit build inputs from the package + inputsFrom = [defguard-client]; + + # add additional dev tools + packages = with pkgs; [ + trunk + sqlx-cli + vtsls + ]; + + shellHook = with pkgs; '' + export LD_LIBRARY_PATH="${ + lib.makeLibraryPath libraries + }:$LD_LIBRARY_PATH" + export OPENSSL_INCLUDE_DIR="${pkgs.openssl.dev}/include/openssl" + export OPENSSL_LIB_DIR="${pkgs.openssl.out}/lib" + export OPENSSL_ROOT_DIR="${pkgs.openssl.out}" + # https://discourse.nixos.org/t/which-package-includes-org-gtk-gtk4-settings-filechooser/38063/12 + export XDG_DATA_DIRS="${pkgs.gtk3}/share/gsettings-schemas/gtk+3-${pkgs.gtk3.dev.version}:$XDG_DATA_DIRS" + export RUST_SRC_PATH="${rustToolchain}/lib/rustlib/src/rust/library" + ''; + }