Skip to content
Merged
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 12 additions & 26 deletions nix/nixos-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,31 @@ in {
};

config = mkIf cfg.enable {
# Add client package
environment.systemPackages = [cfg.package];

# Setup systemd service for the intrerface management daemon
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";
ExecReload = "/bin/kill -HUP $MAINPID";
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;
Restart = "on-failure";
RestartSec = 2;
KillMode = "process";
KillSignal = "SIGINT";
LimitNOFILE = 65536;
LimitNPROC = "infinity";
TasksMax = "infinity";
OOMScoreAdjust = -1000;
};
};

users.users.defguard = {
isSystemUser = true;
group = "defguard";
};

# Make sure the defguard group exists
users.groups.defguard = {};
};
}
76 changes: 61 additions & 15 deletions nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
rustc,
cargo,
makeDesktopItem,
pnpmConfigHook,
fetchPnpmDeps,
}: let
pname = "defguard-client";
version = "1.6.2"; # TODO: Get this from Cargo.toml or git
# Automatically read version from Cargo.toml
version = (fromTOML (builtins.readFile ../src-tauri/Cargo.toml)).workspace.package.version;

desktopItem = makeDesktopItem {
name = pname;
Expand All @@ -29,15 +32,21 @@
gdk-pixbuf
glib
glib-networking
gtk4
gtk3
harfbuzz
librsvg
libsoup_3
pango
webkitgtk_4_1
openssl
libayatana-appindicator
libayatana-indicator
ayatana-ido
libdbusmenu-gtk3
desktop-file-utils
iproute2
lsb-release
openresolv
];

nativeBuildInputs = [
Expand All @@ -50,11 +59,12 @@
pkgs.protobuf
pnpm
# configures pnpm to use pre-fetched dependencies
pnpm.configHook
pnpmConfigHook
# configures cargo to use pre-fetched dependencies
rustPlatform.cargoSetupHook
# helper to add dynamic library paths
# helper to add runtime binary & library deps paths
pkgs.makeWrapper
pkgs.wrapGAppsHook3
];
in
stdenv.mkDerivation (finalAttrs: rec {
Expand All @@ -71,7 +81,7 @@ in
};

# prefetch pnpm dependencies
pnpmDeps = pkgs.pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
inherit
(finalAttrs)
pname
Expand All @@ -80,38 +90,74 @@ in
;

fetcherVersion = 2;
hash = "sha256-v47yaNnt7vLDPR7WVLSonmZBBOkYWnmTUqMiPZ/WCGo=";
hash = "sha256-Xtn0FIq097sLEl/iodLeVVOYxVLx1ePJ8UjJUmgB2f0=";
};

buildPhase = ''
pnpm tauri build
runHook preBuild

pnpm tauri build --verbose

runHook postBuild
'';

postInstall = ''
installPhase = ''
runHook preInstall

mkdir -p $out/bin

# copy client binary
cp src-tauri/target/release/${pname} $out/bin/
install -Dm755 src-tauri/target/release/${pname} $out/bin/${pname}

# copy background service binary
cp src-tauri/target/release/defguard-service $out/bin/
install -Dm755 src-tauri/target/release/defguard-service $out/bin/defguard-service

# copy CLI binary
cp src-tauri/target/release/dg $out/bin/
install -Dm755 src-tauri/target/release/dg $out/bin/dg

# add required library to client binary RPATH
wrapProgram $out/bin/${pname} \
--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [pkgs.libayatana-appindicator pkgs.desktop-file-utils]}
# Copy resources directory (for tray icons, etc.)
mkdir -p $out/lib/${pname}
cp -r src-tauri/resources $out/lib/${pname}/

# install desktop entry
mkdir -p $out/share/applications
cp ${desktopItem}/share/applications/* $out/share/applications/

# install icon files
mkdir -p $out/share/icons/hicolor/{32x32,128x128}/apps
install -Dm644 src-tauri/icons/32x32.png $out/share/icons/hicolor/32x32/apps/${pname}.png
install -Dm644 src-tauri/icons/128x128.png $out/share/icons/hicolor/128x128/apps/${pname}.png

runHook postInstall
'';

# add extra args to wrapGAppsHook3 wrapper
preFixup = ''
gappsWrapperArgs+=(
--prefix PATH : ${
lib.makeBinPath [
# `defguard-service` needs `ip` to manage WireGuard
pkgs.iproute2
# `defguard-service` needs `resolvconf` to manage DNS
pkgs.openresolv
# `defguard-client` needs `update-desktop-database` and `lsb_release`
pkgs.desktop-file-utils
pkgs.lsb-release
]
}
--prefix LD_LIBRARY_PATH : ${
lib.makeLibraryPath [
pkgs.libayatana-appindicator
]
}
)
'';

meta = with lib; {
description = "Defguard VPN Client";
homepage = "https://defguard.net";
# license = licenses.gpl3Only;
maintainers = with maintainers; [];
maintainers = with maintainers; [wojcik91];
platforms = platforms.linux;
};
})
4 changes: 4 additions & 0 deletions src-tauri/src/service/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,9 +508,11 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> {

// Remove existing socket if it exists
if Path::new(DAEMON_SOCKET_PATH).exists() {
debug!("Removing existing socket file at {DAEMON_SOCKET_PATH}");
fs::remove_file(DAEMON_SOCKET_PATH)?;
}

debug!("Binding socket file at {DAEMON_SOCKET_PATH}");
let uds = UnixListener::bind(DAEMON_SOCKET_PATH)?;

// change owner group for socket file
Expand All @@ -521,10 +523,12 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> {
})?;

// change ownership - keep current user, change group
debug!("Changing owner group of socket file at {DAEMON_SOCKET_PATH} to group {DAEMON_SOCKET_GROUP}");
chown(DAEMON_SOCKET_PATH, None, Some(group.gid))?;

// Set socket permissions to allow client access
// 0o660 allows read/write for owner and group only
debug!("Setting permissions for socket file at {DAEMON_SOCKET_PATH} to 0x660");
fs::set_permissions(DAEMON_SOCKET_PATH, fs::Permissions::from_mode(0o660))?;

let uds_stream = UnixListenerStream::new(uds);
Expand Down
Loading