diff --git a/nixos/modules/programs/throne.nix b/nixos/modules/programs/throne.nix index 0a9e1a599eeb..2023055892dd 100644 --- a/nixos/modules/programs/throne.nix +++ b/nixos/modules/programs/throne.nix @@ -23,7 +23,7 @@ in enable = lib.mkEnableOption "TUN mode of Throne"; setuid = lib.mkEnableOption '' - setting suid bit for throne-core to run as root, which is less + setting suid bit for ThroneCore 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 @@ -36,8 +36,8 @@ in environment.systemPackages = [ cfg.package ]; - security.wrappers.throne-core = lib.mkIf cfg.tunMode.enable { - source = "${cfg.package}/share/throne/Core"; + security.wrappers."ThroneCore" = lib.mkIf cfg.tunMode.enable { + source = "${cfg.package}/share/throne/ThroneCore"; owner = "root"; group = "root"; setuid = lib.mkIf cfg.tunMode.setuid true; @@ -49,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 throne-core + # We use a hack here to determine whether the requested process is ThroneCore # 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: @@ -61,7 +61,7 @@ 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 throne-core into a systemd service, and let polkit check service name. + # 3. Put ThroneCore into a systemd service, and let polkit check service name. # This is the most secure and convenient way but requires heavy modification # to Throne source code. Would be good to let upstream support that eventually. security.polkit.extraConfig = @@ -69,6 +69,7 @@ in '' polkit.addRule(function(action, subject) { const allowedActionIds = [ + "org.freedesktop.resolve1.revert", "org.freedesktop.resolve1.set-domains", "org.freedesktop.resolve1.set-default-route", "org.freedesktop.resolve1.set-dns-servers" diff --git a/pkgs/by-name/th/throne/core-also-check-capabilities.patch b/pkgs/by-name/th/throne/core-also-check-capabilities.patch index 5192ddee870f..5a762ef91ec7 100644 --- a/pkgs/by-name/th/throne/core-also-check-capabilities.patch +++ b/pkgs/by-name/th/throne/core-also-check-capabilities.patch @@ -1,42 +1,40 @@ diff --git a/server.go b/server.go -index e2f2ab3..f81812f 100644 +index 4499a6d..4c14d1a 100644 --- a/server.go +++ b/server.go -@@ -16,6 +16,7 @@ import ( +@@ -24,6 +24,7 @@ import ( "github.com/sagernet/sing-box/experimental/clashapi" + "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "golang.org/x/sys/unix" "github.com/sagernet/sing/service" - "github.com/throneproj/clash2singbox/convert" - "github.com/throneproj/clash2singbox/model" - "github.com/throneproj/clash2singbox/model/clash" -@@ -349,12 +350,25 @@ func (s *server) CompileGeoSiteToSrs(in *gen.CompileGeoSiteToSrsRequest, out *ge - } + "github.com/xtls/xray-core/core" + ) +@@ -462,6 +463,27 @@ func (s *server) IsPrivileged(ctx context.Context, _ *gen.EmptyReq) (*gen.IsPriv + }, nil + } - func (s *server) IsPrivileged(in *gen.EmptyReq, out *gen.IsPrivilegedResponse) error { -- if runtime.GOOS == "windows" { -- out.HasPrivilege = To(false) -- return nil -+ ret := false -+ if runtime.GOOS == "windows" || os.Geteuid() == 0 { -+ ret = true -+ } else if runtime.GOOS == "linux" { ++ if runtime.GOOS == "linux" { + caps := unix.CapUserHeader{ + Version: unix.LINUX_CAPABILITY_VERSION_3, + Pid: 0, // current + } ++ + var data [2]unix.CapUserData + err := unix.Capget(&caps, &data[0]) ++ + if err != nil { -+ ret = false -+ } else { -+ // CAP_NET_ADMIN = 12 -+ ret = (data[0].Effective & (1 << unix.CAP_NET_ADMIN)) != 0 ++ return &gen.IsPrivilegedResponse{ ++ HasPrivilege: To(false), ++ }, nil + } - } - -- out.HasPrivilege = To(os.Geteuid() == 0) -+ out.HasPrivilege = To(ret) - return nil ++ ++ // CAP_NET_ADMIN = 12 ++ return &gen.IsPrivilegedResponse{ ++ HasPrivilege: To((data[0].Effective & (1 << unix.CAP_NET_ADMIN)) != 0), ++ }, nil ++ } ++ + return &gen.IsPrivilegedResponse{HasPrivilege: To(os.Geteuid() == 0)}, nil } diff --git a/pkgs/by-name/th/throne/fix-autorun-desktop-exec.patch b/pkgs/by-name/th/throne/fix-autorun-desktop-exec.patch new file mode 100644 index 000000000000..2a6d6b357c54 --- /dev/null +++ b/pkgs/by-name/th/throne/fix-autorun-desktop-exec.patch @@ -0,0 +1,24 @@ +diff --git a/src/sys/linux/AutoRun.cpp b/src/sys/linux/AutoRun.cpp +index 1fafe35..fe04c75 100644 +--- a/src/sys/linux/AutoRun.cpp ++++ b/src/sys/linux/AutoRun.cpp +@@ -31,18 +31,9 @@ void AutoRun_SetEnabled(bool enable) { + QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop"); + QStringList appCmdList; + +- if (QProcessEnvironment::systemEnvironment().contains("APPIMAGE")) { +- appCmdList << QProcessEnvironment::systemEnvironment().value("APPIMAGE"); +- } else { +- appCmdList << QApplication::applicationFilePath(); +- } +- ++ appCmdList << "Throne"; + appCmdList << "-tray"; + +- if (Configs::dataManager->settingsRepo->flag_use_appdata) { +- appCmdList << "-appdata"; +- } +- + if (enable) { + if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) { + // qCWarning(lcUtility) << "Could not create autostart folder" diff --git a/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch b/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch index 3b3eaec2b69d..b5aec5855a8a 100644 --- a/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch +++ b/pkgs/by-name/th/throne/nixos-disable-setuid-request.patch @@ -1,42 +1,42 @@ diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp -index e7bec9c..f0dfe53 100644 +index 37b69be..2d458f3 100644 --- a/src/global/Configs.cpp +++ b/src/global/Configs.cpp -@@ -404,6 +404,12 @@ namespace Configs { - // System Utils +@@ -45,6 +45,12 @@ namespace Configs { + } QString FindCoreRealPath() { + // find in PATH first -+ QString path_for_nixos = QStandardPaths::findExecutable("throne-core"); ++ QString path_for_nixos = QStandardPaths::findExecutable("ThroneCore"); + if (!path_for_nixos.isEmpty()) { + return path_for_nixos; + } + - auto fn = QApplication::applicationDirPath() + "/Core"; + auto fn = QApplication::applicationDirPath() + "/ThroneCore"; #ifdef Q_OS_WIN fn += ".exe"; diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp -index d31c311..ce4c357 100644 +index 9acfee4..c0c313a 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp -@@ -151,8 +151,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi - Configs::dataStore->core_port = MkPort(); - if (Configs::dataStore->core_port <= 0) Configs::dataStore->core_port = 19810; +@@ -163,8 +163,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi + Configs::dataManager->settingsRepo->core_port = MkPort(); + if (Configs::dataManager->settingsRepo->core_port <= 0) Configs::dataManager->settingsRepo->core_port = 19810; - auto core_path = QApplication::applicationDirPath() + "/"; -- core_path += "Core"; +- core_path += "ThroneCore"; + auto core_path = Configs::FindCoreRealPath(); QStringList args; args.push_back("-port"); -@@ -1045,6 +1044,15 @@ bool MainWindow::get_elevated_permissions(int reason) { +@@ -1296,6 +1295,15 @@ bool MainWindow::get_elevated_permissions(int reason) { 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 throne-core. If you are using NixOS, please install Throne via `programs.throne.enable` and then set the `programs.throne.tunMode.enable` option to elevate privileges."), ++ tr("Due to the read-only property of the Nix store, we cannot set suid for ThroneCore. If you are using NixOS, please install Throne via `programs.throne.enable` and then set the `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 index f9ab2d669b64..35d719bf9cbe 100644 --- a/pkgs/by-name/th/throne/package.nix +++ b/pkgs/by-name/th/throne/package.nix @@ -9,7 +9,7 @@ protobuf, protoc-gen-go, - protorpc, + protoc-gen-go-grpc, cmake, copyDesktopItems, @@ -19,23 +19,20 @@ # override if you want to have more up-to-date rulesets throne-srslist ? fetchurl { - url = "https://raw.githubusercontent.com/throneproj/routeprofiles/0fca735ff2759422c407ac04fac819aef2fc88f9/srslist.h"; - hash = "sha256-G2WUStxFtN0fbZm/KoD9ldUvkMWf9cDA+9fvYt8dcqo="; + url = "https://raw.githubusercontent.com/throneproj/routeprofiles/05793e2da7ca10a7acb2494f60a27ac5f7ec924c/srslist.h"; + hash = "sha256-NHer5Vy1zBL9yJlVDLVFNRG4ITzb2GTjmt718KsSrGw="; }, }: stdenv.mkDerivation (finalAttrs: { pname = "throne"; - version = "1.0.13"; + version = "1.1.1-unstable-2026-03-28"; src = fetchFromGitHub { owner = "throneproj"; repo = "Throne"; - # the release CI job was triggered on the xhttp branch (https://github.com/throneproj/Throne/actions/runs/20588046213), - # but the 1.0.13 tag was wrongly created on the dev branch - # we'll use the revision that was used for the job as well - rev = "3b737ec8cf29e03e4b7d5a09b1f502bdb8ef52e2"; - hash = "sha256-OVgmhiKL4BaFYBeUqIX3LRNa54zq5oYyNMUYwKNvo1A="; + rev = "f53bb73790782a9a9b7bfeb30c8d6e6bcc2b05f0"; + hash = "sha256-hEjbzS0JV5OA0c9kWTFGc5kv04qzobN0TFBjMJZ1ohc="; }; strictDeps = true; @@ -54,33 +51,36 @@ stdenv.mkDerivation (finalAttrs: { env.INPUT_VERSION = finalAttrs.version; - 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) - ]; + # suppress errors in 3rdparty/simple-protobuf + env.NIX_CFLAGS_COMPILE = "-Wno-error=maybe-uninitialized"; 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 + # disable suid request as it cannot be applied to ThroneCore in nix store + # and prompt users to use NixOS module instead. And use ThroneCore from PATH # to make use of security wrappers ./nixos-disable-setuid-request.patch + + # sets the Exec field of the auto-run .desktop file to use the Throne binary from PATH + ./fix-autorun-desktop-exec.patch ]; preBuild = '' ln -s ${throne-srslist} ./srslist.h ''; + # we'll wrap manually + dontWrapQtApps = true; + 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/" + makeQtWrapper "$out/share/throne/Throne" "$out/bin/Throne" \ + --append-flag "-appdata" # use writable config dir - ln -s ${finalAttrs.passthru.core}/bin/Core "$out/share/throne/Core" + ln -s ${finalAttrs.passthru.core}/bin/ThroneCore "$out/share/throne/ThroneCore" runHook postInstall ''; @@ -108,18 +108,18 @@ stdenv.mkDerivation (finalAttrs: { ]; proxyVendor = true; - vendorHash = "sha256-cPo/2bUXEF9jomr0Pnty7ZutAaC0TFG397FSIqefrjw="; + vendorHash = "sha256-HNd0HI4JRPZiiSxDzOKgyAOW7tzZPCTPvOC5t+3yhoQ="; nativeBuildInputs = [ protobuf protoc-gen-go - protorpc + protoc-gen-go-grpc ]; # taken from script/build_go.sh preBuild = '' pushd gen - protoc -I . --go_out=. --protorpc_out=. libcore.proto + protoc -I . --go_out=. --go-grpc_out=. libcore.proto popd VERSION_SINGBOX=$(go list -m -f '{{.Version}}' github.com/sagernet/sing-box) @@ -130,6 +130,9 @@ stdenv.mkDerivation (finalAttrs: { ldflags = [ "-w" "-s" + "-X" + "internal/godebug.defaultGODEBUG=multipathtcp=0" + "-checklinkname=0" ]; tags = [ @@ -140,10 +143,12 @@ stdenv.mkDerivation (finalAttrs: { "with_utls" "with_dhcp" "with_tailscale" + "badlinkname" + "tfogo_checklinkname" ]; }; - # this tricks nix-update into also updating the vendorHash of throne-core + # this tricks nix-update into also updating the vendorHash of passthru.core passthru.goModules = finalAttrs.passthru.core.goModules; meta = {