diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4e385e1ac485..de5978a41e78 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -269,7 +269,6 @@ ./programs/nano.nix ./programs/nautilus-open-any-terminal.nix ./programs/nbd.nix - ./programs/nekoray.nix ./programs/neovim.nix ./programs/nethoscope.nix ./programs/nexttrace.nix @@ -327,6 +326,7 @@ ./programs/systemtap.nix ./programs/tcpdump.nix ./programs/television.nix + ./programs/throne.nix ./programs/thunar.nix ./programs/thunderbird.nix ./programs/tmux.nix diff --git a/nixos/modules/programs/nekoray.nix b/nixos/modules/programs/throne.nix similarity index 80% rename from nixos/modules/programs/nekoray.nix rename to nixos/modules/programs/throne.nix index e5ad81b950aa..0a9e1a599eeb 100644 --- a/nixos/modules/programs/nekoray.nix +++ b/nixos/modules/programs/throne.nix @@ -6,20 +6,24 @@ }: let - cfg = config.programs.nekoray; + cfg = config.programs.throne; in { - options = { - programs.nekoray = { - enable = lib.mkEnableOption "nekoray, a GUI proxy configuration manager"; + imports = [ + (lib.mkRenamedOptionModule [ "programs" "nekoray" ] [ "programs" "throne" ]) + ]; - package = lib.mkPackageOption pkgs "nekoray" { }; + options = { + programs.throne = { + enable = lib.mkEnableOption "Throne, a GUI proxy configuration manager"; + + package = lib.mkPackageOption pkgs "throne" { }; tunMode = { - enable = lib.mkEnableOption "TUN mode of nekoray"; + enable = lib.mkEnableOption "TUN mode of Throne"; setuid = lib.mkEnableOption '' - setting suid bit for nekobox_core to run as root, which is less + setting suid bit for throne-core to run as root, which is less secure than default setcap method but closer to upstream assumptions. Enable this if you find the default setcap method configured in this module doesn't work for you @@ -29,10 +33,11 @@ in }; config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; - security.wrappers.nekobox_core = lib.mkIf cfg.tunMode.enable { - source = "${cfg.package}/share/nekoray/nekobox_core"; + security.wrappers.throne-core = lib.mkIf cfg.tunMode.enable { + source = "${cfg.package}/share/throne/Core"; owner = "root"; group = "root"; setuid = lib.mkIf cfg.tunMode.setuid true; @@ -44,7 +49,7 @@ in # avoid resolvectl password prompt popping up three times # https://github.com/SagerNet/sing-tun/blob/0686f8c4f210f4e7039c352d42d762252f9d9cf5/tun_linux.go#L1062 - # We use a hack here to determine whether the requested process is nekobox_core + # We use a hack here to determine whether the requested process is throne-core # Detect whether its capabilities contain at least `net_admin` and `net_raw`. # This does not reduce security, as we can already bypass `resolved` with them. # Alternatives to consider: @@ -56,9 +61,9 @@ in # change its own cmdline. `/proc//exe` is reliable but kernel forbids # checking that entry of process from different users, and polkit runs `spawn` # as an unprivileged user. - # 3. Put nekobox_core into a systemd service, and let polkit check service name. + # 3. Put throne-core into a systemd service, and let polkit check service name. # This is the most secure and convenient way but requires heavy modification - # to nekoray source code. Would be good to let upstream support that eventually. + # to Throne source code. Would be good to let upstream support that eventually. security.polkit.extraConfig = lib.mkIf (cfg.tunMode.enable && (!cfg.tunMode.setuid) && config.services.resolved.enable) '' diff --git a/pkgs/by-name/ne/nekoray/package.nix b/pkgs/by-name/ne/nekoray/package.nix deleted file mode 100644 index ba6d1084e792..000000000000 --- a/pkgs/by-name/ne/nekoray/package.nix +++ /dev/null @@ -1,149 +0,0 @@ -{ - lib, - stdenv, - - buildGoModule, - fetchFromGitHub, - fetchpatch, - makeDesktopItem, - - cmake, - copyDesktopItems, - ninja, - - protobuf, - qt6Packages, - - sing-geoip, - sing-geosite, -}: - -stdenv.mkDerivation (finalAttrs: { - pname = "nekoray"; - version = "4.3.7"; - - src = fetchFromGitHub { - owner = "Mahdi-zarei"; - repo = "nekoray"; - tag = finalAttrs.version; - hash = "sha256-oRoHu9mt4LiGJFe2OEATbPQ8buYT/6o9395BxYg1qKI="; - }; - - strictDeps = true; - - nativeBuildInputs = [ - cmake - copyDesktopItems - ninja - qt6Packages.wrapQtAppsHook - ]; - - buildInputs = [ - protobuf - qt6Packages.qtbase - qt6Packages.qttools - ]; - - cmakeFlags = [ - # makes sure the app uses the user's config directory to store it's non-static content - # it's essentially the same as always setting the -appdata flag when running the program - (lib.cmakeBool "NKR_PACKAGE" true) - ]; - - patches = [ - # if compiled with NKR_PACKAGE, nekoray assumes geoip.db and geosite.db will be found in ~/.config/nekoray - # we already package those two files in nixpkgs - # we can't place file at that location using our builder so we must change the search directory to be relative to the built executable - ./search-for-geodata-in-install-location.patch - - # disable suid request as it cannot be applied to nekobox_core in nix store - # and prompt users to use NixOS module instead. And use nekobox_core from PATH - # to make use of security wrappers - ./nixos-disable-setuid-request.patch - ]; - - installPhase = '' - runHook preInstall - - install -Dm755 nekoray "$out/share/nekoray/nekoray" - install -Dm644 "$src/res/public/nekobox.png" "$out/share/icons/hicolor/256x256/apps/nekoray.png" - - mkdir -p "$out/bin" - ln -s "$out/share/nekoray/nekoray" "$out/bin" - - # nekoray looks for other files and cores in the same directory it's located at - ln -s ${finalAttrs.passthru.nekobox-core}/bin/nekobox_core "$out/share/nekoray/nekobox_core" - - # our patch also makes nekoray look for geodata files next to the executable - ln -s ${sing-geoip}/share/sing-box/geoip.db "$out/share/nekoray/geoip.db" - ln -s ${sing-geosite}/share/sing-box/geosite.db "$out/share/nekoray/geosite.db" - - runHook postInstall - ''; - - desktopItems = [ - (makeDesktopItem { - name = "nekoray"; - desktopName = "Nekoray"; - exec = "nekoray"; - icon = "nekoray"; - comment = finalAttrs.meta.description; - terminal = false; - categories = [ "Network" ]; - }) - ]; - - passthru.nekobox-core = buildGoModule { - pname = "nekobox-core"; - inherit (finalAttrs) version src; - sourceRoot = "${finalAttrs.src.name}/core/server"; - - patches = [ - # also check cap_net_admin so we don't have to set suid - ./core-also-check-capabilities.patch - - # adds missing entries to the lockfile - # can be removed next update - (fetchpatch { - name = "fix-lockfile.patch"; - url = "https://github.com/Mahdi-zarei/nekoray/commit/6f9b2c69e21b0b86242fcc5731f21561373d0963.patch"; - stripLen = 2; - hash = "sha256-LDLgCQUXOqaV++6Z4/8r2IaBM+Kz/LckjVsvZn/0lLM="; - }) - ]; - - vendorHash = "sha256-6Q6Qi3QQOmuLBaV4t/CEER6s1MUvL7ER6Hfm44sQk4M="; - - # ldflags and tags are taken from script/build_go.sh - ldflags = [ - "-w" - "-s" - "-X github.com/sagernet/sing-box/constant.Version=${finalAttrs.version}" - ]; - - tags = [ - "with_clash_api" - "with_gvisor" - "with_quic" - "with_wireguard" - "with_utls" - "with_ech" - "with_dhcp" - ]; - }; - - # this tricks nix-update into also updating the vendorHash of nekobox-core - passthru.goModules = finalAttrs.passthru.nekobox-core.goModules; - - meta = { - description = "Qt based cross-platform GUI proxy configuration manager"; - homepage = "https://github.com/Mahdi-zarei/nekoray"; - license = lib.licenses.gpl3Plus; - mainProgram = "nekoray"; - maintainers = with lib.maintainers; [ - tomasajt - aleksana - ]; - platforms = lib.platforms.linux; - }; -}) diff --git a/pkgs/by-name/pr/protorpc/package.nix b/pkgs/by-name/pr/protorpc/package.nix new file mode 100644 index 000000000000..72764652d51f --- /dev/null +++ b/pkgs/by-name/pr/protorpc/package.nix @@ -0,0 +1,32 @@ +{ + lib, + buildGoModule, + fetchFromGitHub, +}: + +buildGoModule (finalAttrs: { + pname = "protorpc"; + version = "1.1.2"; + + src = fetchFromGitHub { + owner = "chai2010"; + repo = "protorpc"; + tag = "v${finalAttrs.version}"; + hash = "sha256-yu4aSWM0TNnGGLAA6NApMekHMi6e+McGRndi8ptdXfY="; + }; + + vendorHash = "sha256-qGFPUNomcFHRBX33WWdYaAY7196RljwFKuS+EZhKKz8="; + + ldflags = [ + "-s" + "-w" + ]; + + meta = { + description = "Google Protocol Protobufs RPC for Go"; + homepage = "https://github.com/chai2010/protorpc"; + changelog = "https://github.com/chai2010/protorpc/blob/${finalAttrs.src.rev}/changelog.md"; + license = lib.licenses.bsd3; + maintainers = with lib.maintainers; [ tomasajt ]; + }; +}) diff --git a/pkgs/by-name/ne/nekoray/core-also-check-capabilities.patch b/pkgs/by-name/th/throne/core-also-check-capabilities.patch similarity index 53% rename from pkgs/by-name/ne/nekoray/core-also-check-capabilities.patch rename to pkgs/by-name/th/throne/core-also-check-capabilities.patch index d477100b172f..4e8312f14a38 100644 --- a/pkgs/by-name/ne/nekoray/core-also-check-capabilities.patch +++ b/pkgs/by-name/th/throne/core-also-check-capabilities.patch @@ -1,23 +1,22 @@ diff --git a/server.go b/server.go -index c2a6be0..8aeca1c 100644 +index e2f2ab3..f81812f 100644 --- a/server.go +++ b/server.go -@@ -11,6 +11,7 @@ import ( +@@ -16,6 +16,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/service" + "golang.org/x/sys/unix" "log" - "nekobox_core/gen" - "nekobox_core/internal/boxbox" -@@ -359,13 +360,25 @@ func (s *server) CompileGeoSiteToSrs(ctx context.Context, in *gen.CompileGeoSite + "os" + "runtime" +@@ -349,12 +350,25 @@ func (s *server) CompileGeoSiteToSrs(in *gen.CompileGeoSiteToSrsRequest, out *ge } - func (s *server) IsPrivileged(ctx context.Context, _ *gen.EmptyReq) (*gen.IsPrivilegedResponse, error) { + func (s *server) IsPrivileged(in *gen.EmptyReq, out *gen.IsPrivilegedResponse) error { - if runtime.GOOS == "windows" { -- return &gen.IsPrivilegedResponse{ -- HasPrivilege: false, -- }, nil +- out.HasPrivilege = To(false) +- return nil + ret := false + if runtime.GOOS == "windows" || os.Geteuid() == 0 { + ret = true @@ -36,8 +35,8 @@ index c2a6be0..8aeca1c 100644 + } } -- return &gen.IsPrivilegedResponse{HasPrivilege: os.Geteuid() == 0}, nil -+ return &gen.IsPrivilegedResponse{HasPrivilege: ret}, nil +- out.HasPrivilege = To(os.Geteuid() == 0) ++ out.HasPrivilege = To(ret) + return nil } - func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.SpeedTestResponse, error) { diff --git a/pkgs/by-name/ne/nekoray/nixos-disable-setuid-request.patch b/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch similarity index 60% rename from pkgs/by-name/ne/nekoray/nixos-disable-setuid-request.patch rename to pkgs/by-name/th/throne/nixos-disable-setuid-request.patch index ade76eb9decd..2bf3dfbc411a 100644 --- a/pkgs/by-name/ne/nekoray/nixos-disable-setuid-request.patch +++ b/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch @@ -1,18 +1,18 @@ -diff --git a/src/global/NekoGui.cpp b/src/global/NekoGui.cpp +diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp index 7943d7a..5bb20cc 100644 ---- a/src/global/NekoGui.cpp -+++ b/src/global/NekoGui.cpp -@@ -355,6 +355,12 @@ namespace NekoGui { +--- a/src/global/Configs.cpp ++++ b/src/global/Configs.cpp +@@ -355,6 +355,12 @@ namespace Configs { // System Utils - QString FindNekoBoxCoreRealPath() { + QString FindCoreRealPath() { + // find in PATH first -+ QString path = QStandardPaths::findExecutable("nekobox_core"); ++ QString path = QStandardPaths::findExecutable("throne-core"); + if (!path.isEmpty()) { + return path; + } + - auto fn = QApplication::applicationDirPath() + "/nekobox_core"; + auto fn = QApplication::applicationDirPath() + "/Core"; auto fi = QFileInfo(fn); if (fi.isSymLink()) return fi.symLinkTarget(); diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp @@ -20,23 +20,23 @@ index 9aa46b2..ba7137a 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -125,8 +125,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi - NekoGui::dataStore->core_port = MkPort(); - if (NekoGui::dataStore->core_port <= 0) NekoGui::dataStore->core_port = 19810; + Configs::dataStore->core_port = MkPort(); + if (Configs::dataStore->core_port <= 0) Configs::dataStore->core_port = 19810; - auto core_path = QApplication::applicationDirPath() + "/"; -- core_path += "nekobox_core"; -+ auto core_path = NekoGui::FindNekoBoxCoreRealPath(); +- core_path += "Core"; ++ auto core_path = Configs::FindCoreRealPath(); QStringList args; - args.push_back("nekobox"); + args.push_back("-port"); @@ -844,6 +843,15 @@ bool MainWindow::get_elevated_permissions(int reason) { return true; } - if (NekoGui::IsAdmin()) return true; + if (Configs::IsAdmin()) return true; + QMessageBox::critical( + GetMessageBoxParent(), + tr("Unable to elevate privileges when installed with Nix"), -+ tr("Due to the read-only property of Nix store, we cannot set suid for nekobox_core. If you are using NixOS, please set `programs.nekoray.tunMode.enable` option to elevate privileges."), ++ tr("Due to the read-only property of Nix store, we cannot set suid for throne-core. If you are using NixOS, please set `programs.throne.tunMode.enable` option to elevate privileges."), + QMessageBox::Ok + ); + return false; diff --git a/pkgs/by-name/th/throne/package.nix b/pkgs/by-name/th/throne/package.nix new file mode 100644 index 000000000000..ae562625411c --- /dev/null +++ b/pkgs/by-name/th/throne/package.nix @@ -0,0 +1,153 @@ +{ + lib, + stdenv, + + buildGoModule, + fetchFromGitHub, + fetchurl, + makeDesktopItem, + + protobuf, + protoc-gen-go, + protorpc, + + cmake, + copyDesktopItems, + ninja, + + qt6Packages, + + # override if you want to have more up-to-date rulesets + throne-srslist ? fetchurl { + url = "https://raw.githubusercontent.com/throneproj/routeprofiles/60eb41122de3aa53c701ec948cd52d7a26adafea/srslist.h"; + hash = "sha256-k9vPtcusML4GR81UVeJ7jhuDHGk5Qh0eKw/cSOxBd5g="; + }, +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "throne"; + version = "1.0.8-unstable-2025-10-29"; + + src = fetchFromGitHub { + owner = "throneproj"; + repo = "Throne"; + rev = "54af50fc414ffaf98b3ff88e3dd8aa041c65e041"; + hash = "sha256-kfvsGw0RUYHkOUSeSA4egLl+gQqN4KkZKXX3CQQzYks="; + }; + + strictDeps = true; + + nativeBuildInputs = [ + cmake + copyDesktopItems + ninja + qt6Packages.wrapQtAppsHook + ]; + + buildInputs = [ + qt6Packages.qtbase + qt6Packages.qttools + ]; + + cmakeFlags = [ + # makes sure the app uses the user's config directory to store it's non-static content + # it's essentially the same as always setting the -appdata flag when running the program + (lib.cmakeBool "NKR_PACKAGE" true) + ]; + + patches = [ + # disable suid request as it cannot be applied to throne-core in nix store + # and prompt users to use NixOS module instead. And use throne-core from PATH + # to make use of security wrappers + ./nixos-disable-setuid-request.patch + ]; + + preBuild = '' + ln -s ${throne-srslist} ./srslist.h + ''; + + installPhase = '' + runHook preInstall + + install -Dm755 Throne -t "$out/share/throne/" + install -Dm644 "$src/res/public/Throne.png" -t "$out/share/icons/hicolor/512x512/apps/" + + mkdir -p "$out/bin" + ln -s "$out/share/throne/Throne" "$out/bin/" + + ln -s ${finalAttrs.passthru.core}/bin/Core "$out/share/throne/Core" + + runHook postInstall + ''; + + desktopItems = [ + (makeDesktopItem { + name = "throne"; + desktopName = "Throne"; + exec = "Throne"; + icon = "Throne"; + comment = finalAttrs.meta.description; + terminal = false; + categories = [ "Network" ]; + }) + ]; + + passthru.core = buildGoModule { + pname = "throne-core"; + inherit (finalAttrs) version src; + sourceRoot = "${finalAttrs.src.name}/core/server"; + + patches = [ + # also check cap_net_admin so we don't have to set suid + ./core-also-check-capabilities.patch + ]; + + proxyVendor = true; + vendorHash = "sha256-thMRkbs5fS7KsUSRSeUaB2xkTjs7kJ9AKXW0+OXN3io="; + + nativeBuildInputs = [ + protobuf + protoc-gen-go + protorpc + ]; + + # taken from script/build_go.sh + preBuild = '' + pushd gen + protoc -I . --go_out=. --protorpc_out=. libcore.proto + popd + ''; + + # ldflags and tags are taken from script/build_go.sh + ldflags = [ + "-w" + "-s" + "-X github.com/sagernet/sing-box/constant.Version=${finalAttrs.version}" + ]; + + tags = [ + "with_clash_api" + "with_gvisor" + "with_quic" + "with_wireguard" + "with_utls" + "with_dhcp" + "with_tailscale" + ]; + }; + + # this tricks nix-update into also updating the vendorHash of throne-core + passthru.goModules = finalAttrs.passthru.core.goModules; + + meta = { + description = "Qt based cross-platform GUI proxy configuration manager"; + homepage = "https://github.com/throneproj/Throne"; + license = lib.licenses.gpl3Plus; + mainProgram = "Throne"; + maintainers = with lib.maintainers; [ + tomasajt + aleksana + ]; + platforms = lib.platforms.linux; + }; +}) diff --git a/pkgs/by-name/ne/nekoray/search-for-geodata-in-install-location.patch b/pkgs/by-name/th/throne/search-for-geodata-in-install-location.patch similarity index 61% rename from pkgs/by-name/ne/nekoray/search-for-geodata-in-install-location.patch rename to pkgs/by-name/th/throne/search-for-geodata-in-install-location.patch index 1af2e727bc41..91e5ad16d923 100644 --- a/pkgs/by-name/ne/nekoray/search-for-geodata-in-install-location.patch +++ b/pkgs/by-name/th/throne/search-for-geodata-in-install-location.patch @@ -1,8 +1,8 @@ -diff --git a/src/global/NekoGui.cpp b/src/global/NekoGui.cpp +diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp index d9be515..15de64b 100644 ---- a/src/global/NekoGui.cpp -+++ b/src/global/NekoGui.cpp -@@ -426,8 +426,6 @@ namespace NekoGui { +--- a/src/global/Configs.cpp ++++ b/src/global/Configs.cpp +@@ -426,8 +426,6 @@ namespace Configs { }; QString GetBasePath() { diff --git a/pkgs/top-level/aliases.nix b/pkgs/top-level/aliases.nix index 12eaaf201822..71e7b8bda41b 100644 --- a/pkgs/top-level/aliases.nix +++ b/pkgs/top-level/aliases.nix @@ -1078,6 +1078,7 @@ mapAliases { nats-streaming-server = throw "'nats-streaming-server' has been removed as critical bug fixes and security fixes will no longer be performed as of June of 2023"; # added 2025-10-13 ncdu_2 = throw "'ncdu_2' has been renamed to/replaced by 'ncdu'"; # Converted to throw 2025-10-27 neardal = throw "neardal has been removed because it has been marked as broken since at least November 2024."; # Added 2025-09-29 + nekoray = lib.warnOnInstantiate "nekoray has been renamed to throne" throne; # Added 2025-11-10 neocities-cli = throw "'neocities-cli' has been renamed to/replaced by 'neocities'"; # Converted to throw 2025-10-27 netbox_4_1 = throw "netbox 4.1 series has been removed as it was EOL"; # Added 2025-10-14 netbsdCross = throw "'netbsdCross' has been renamed to/replaced by 'netbsd'"; # Converted to throw 2025-10-27