mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-09 19:13:26 +03:00
nekoray: fix TUN functionality (#410840)
This commit is contained in:
commit
a6f4ca139b
5 changed files with 195 additions and 1 deletions
|
@ -262,6 +262,7 @@
|
|||
./programs/nano.nix
|
||||
./programs/nautilus-open-any-terminal.nix
|
||||
./programs/nbd.nix
|
||||
./programs/nekoray.nix
|
||||
./programs/neovim.nix
|
||||
./programs/nethoscope.nix
|
||||
./programs/nexttrace.nix
|
||||
|
|
90
nixos/modules/programs/nekoray.nix
Normal file
90
nixos/modules/programs/nekoray.nix
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.nekoray;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
programs.nekoray = {
|
||||
enable = lib.mkEnableOption "nekoray, a GUI proxy configuration manager";
|
||||
|
||||
package = lib.mkPackageOption pkgs "nekoray" { };
|
||||
|
||||
tunMode = {
|
||||
enable = lib.mkEnableOption "TUN mode of nekoray";
|
||||
|
||||
setuid = lib.mkEnableOption ''
|
||||
setting suid bit for nekobox_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
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
setuid = lib.mkIf cfg.tunMode.setuid true;
|
||||
# Taken from https://github.com/SagerNet/sing-box/blob/dev-next/release/config/sing-box.service
|
||||
capabilities = lib.mkIf (
|
||||
!cfg.tunMode.setuid
|
||||
) "cap_net_admin,cap_net_raw,cap_net_bind_service,cap_sys_ptrace,cap_dac_read_search+ep";
|
||||
};
|
||||
|
||||
# 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
|
||||
# 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:
|
||||
# 1. Use suid to execute as a specific user, and check username with polkit.
|
||||
# However, NixOS module doesn't let us to set setuid and capabilities at the
|
||||
# same time, and it's tricky to make both work together because of some security
|
||||
# considerations in the kernel.
|
||||
# 2. Check cmdline to get executable path. This is insecure because the process can
|
||||
# change its own cmdline. `/proc/<pid>/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.
|
||||
# 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.
|
||||
security.polkit.extraConfig =
|
||||
lib.mkIf (cfg.tunMode.enable && (!cfg.tunMode.setuid) && config.services.resolved.enable)
|
||||
''
|
||||
polkit.addRule(function(action, subject) {
|
||||
const allowedActionIds = [
|
||||
"org.freedesktop.resolve1.set-domains",
|
||||
"org.freedesktop.resolve1.set-default-route",
|
||||
"org.freedesktop.resolve1.set-dns-servers"
|
||||
];
|
||||
|
||||
if (allowedActionIds.indexOf(action.id) !== -1) {
|
||||
try {
|
||||
var parentPid = polkit.spawn(["${lib.getExe' pkgs.procps "ps"}", "-o", "ppid=", subject.pid]).trim();
|
||||
var parentCap = polkit.spawn(["${lib.getExe' pkgs.libcap "getpcaps"}", parentPid]).trim();
|
||||
if (parentCap.includes("cap_net_admin") && parentCap.includes("cap_net_raw")) {
|
||||
return polkit.Result.YES;
|
||||
} else {
|
||||
return polkit.Result.NOT_HANDLED;
|
||||
}
|
||||
} catch (e) {
|
||||
return polkit.Result.NOT_HANDLED;
|
||||
}
|
||||
}
|
||||
})
|
||||
'';
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ aleksana ];
|
||||
}
|
43
pkgs/by-name/ne/nekoray/core-also-check-capabilities.patch
Normal file
43
pkgs/by-name/ne/nekoray/core-also-check-capabilities.patch
Normal file
|
@ -0,0 +1,43 @@
|
|||
diff --git a/server.go b/server.go
|
||||
index c2a6be0..8aeca1c 100644
|
||||
--- a/server.go
|
||||
+++ b/server.go
|
||||
@@ -11,6 +11,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
|
||||
}
|
||||
|
||||
func (s *server) IsPrivileged(ctx context.Context, _ *gen.EmptyReq) (*gen.IsPrivilegedResponse, error) {
|
||||
- if runtime.GOOS == "windows" {
|
||||
- return &gen.IsPrivilegedResponse{
|
||||
- HasPrivilege: false,
|
||||
- }, nil
|
||||
+ ret := false
|
||||
+ if runtime.GOOS == "windows" || os.Geteuid() == 0 {
|
||||
+ ret = true
|
||||
+ } else 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: os.Geteuid() == 0}, nil
|
||||
+ return &gen.IsPrivilegedResponse{HasPrivilege: ret}, nil
|
||||
}
|
||||
|
||||
func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.SpeedTestResponse, error) {
|
47
pkgs/by-name/ne/nekoray/nixos-disable-setuid-request.patch
Normal file
47
pkgs/by-name/ne/nekoray/nixos-disable-setuid-request.patch
Normal file
|
@ -0,0 +1,47 @@
|
|||
diff --git a/src/global/NekoGui.cpp b/src/global/NekoGui.cpp
|
||||
index 7943d7a..5bb20cc 100644
|
||||
--- a/src/global/NekoGui.cpp
|
||||
+++ b/src/global/NekoGui.cpp
|
||||
@@ -355,6 +355,12 @@ namespace NekoGui {
|
||||
// System Utils
|
||||
|
||||
QString FindNekoBoxCoreRealPath() {
|
||||
+ // find in PATH first
|
||||
+ QString path = QStandardPaths::findExecutable("nekobox_core");
|
||||
+ if (!path.isEmpty()) {
|
||||
+ return path;
|
||||
+ }
|
||||
+
|
||||
auto fn = QApplication::applicationDirPath() + "/nekobox_core";
|
||||
auto fi = QFileInfo(fn);
|
||||
if (fi.isSymLink()) return fi.symLinkTarget();
|
||||
diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp
|
||||
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;
|
||||
|
||||
- auto core_path = QApplication::applicationDirPath() + "/";
|
||||
- core_path += "nekobox_core";
|
||||
+ auto core_path = NekoGui::FindNekoBoxCoreRealPath();
|
||||
|
||||
QStringList args;
|
||||
args.push_back("nekobox");
|
||||
@@ -844,6 +843,15 @@ bool MainWindow::get_elevated_permissions(int reason) {
|
||||
return true;
|
||||
}
|
||||
if (NekoGui::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."),
|
||||
+ QMessageBox::Ok
|
||||
+ );
|
||||
+ return false;
|
||||
+ // The following code isn't effective, preserve to avoid merge conflict
|
||||
+
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!Linux_HavePkexec()) {
|
||||
MessageBoxWarning(software_name, "Please install \"pkexec\" first.");
|
|
@ -60,6 +60,11 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
# 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 = ''
|
||||
|
@ -99,6 +104,11 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
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
|
||||
];
|
||||
|
||||
vendorHash = "sha256-hZiEIJ4/TcLUfT+pkqs6WfzjqppSTjKXEtQC+DS26Ug=";
|
||||
|
||||
# ldflags and tags are taken from script/build_go.sh
|
||||
|
@ -127,7 +137,10 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
homepage = "https://github.com/Mahdi-zarei/nekoray";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
mainProgram = "nekoray";
|
||||
maintainers = with lib.maintainers; [ tomasajt ];
|
||||
maintainers = with lib.maintainers; [
|
||||
tomasajt
|
||||
aleksana
|
||||
];
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue