···
9
+
cfg = config.programs.nekoray;
13
+
programs.nekoray = {
14
+
enable = lib.mkEnableOption "nekoray, a GUI proxy configuration manager";
16
+
package = lib.mkPackageOption pkgs "nekoray" { };
19
+
enable = lib.mkEnableOption "TUN mode of nekoray";
21
+
setuid = lib.mkEnableOption ''
22
+
setting suid bit for nekobox_core to run as root, which is less
23
+
secure than default setcap method but closer to upstream assumptions.
24
+
Enable this if you find the default setcap method configured in
25
+
this module doesn't work for you
31
+
config = lib.mkIf cfg.enable {
32
+
environment.systemPackages = [ cfg.package ];
34
+
security.wrappers.nekobox_core = lib.mkIf cfg.tunMode.enable {
35
+
source = "${cfg.package}/share/nekoray/nekobox_core";
38
+
setuid = lib.mkIf cfg.tunMode.setuid true;
39
+
# Taken from https://github.com/SagerNet/sing-box/blob/dev-next/release/config/sing-box.service
40
+
capabilities = lib.mkIf (
42
+
) "cap_net_admin,cap_net_raw,cap_net_bind_service,cap_sys_ptrace,cap_dac_read_search+ep";
45
+
# avoid resolvectl password prompt popping up three times
46
+
# https://github.com/SagerNet/sing-tun/blob/0686f8c4f210f4e7039c352d42d762252f9d9cf5/tun_linux.go#L1062
47
+
# We use a hack here to determine whether the requested process is nekobox_core
48
+
# Detect whether its capabilities contain at least `net_admin` and `net_raw`.
49
+
# This does not reduce security, as we can already bypass `resolved` with them.
50
+
# Alternatives to consider:
51
+
# 1. Use suid to execute as a specific user, and check username with polkit.
52
+
# However, NixOS module doesn't let us to set setuid and capabilities at the
53
+
# same time, and it's tricky to make both work together because of some security
54
+
# considerations in the kernel.
55
+
# 2. Check cmdline to get executable path. This is insecure because the process can
56
+
# change its own cmdline. `/proc/<pid>/exe` is reliable but kernel forbids
57
+
# checking that entry of process from different users, and polkit runs `spawn`
58
+
# as an unprivileged user.
59
+
# 3. Put nekobox_core into a systemd service, and let polkit check service name.
60
+
# This is the most secure and convenient way but requires heavy modification
61
+
# to nekoray source code. Would be good to let upstream support that eventually.
62
+
security.polkit.extraConfig =
63
+
lib.mkIf (cfg.tunMode.enable && (!cfg.tunMode.setuid) && config.services.resolved.enable)
65
+
polkit.addRule(function(action, subject) {
66
+
const allowedActionIds = [
67
+
"org.freedesktop.resolve1.set-domains",
68
+
"org.freedesktop.resolve1.set-default-route",
69
+
"org.freedesktop.resolve1.set-dns-servers"
72
+
if (allowedActionIds.indexOf(action.id) !== -1) {
74
+
var parentPid = polkit.spawn(["${lib.getExe' pkgs.procps "ps"}", "-o", "ppid=", subject.pid]).trim();
75
+
var parentCap = polkit.spawn(["${lib.getExe' pkgs.libcap "getpcaps"}", parentPid]).trim();
76
+
if (parentCap.includes("cap_net_admin") && parentCap.includes("cap_net_raw")) {
77
+
return polkit.Result.YES;
79
+
return polkit.Result.NOT_HANDLED;
82
+
return polkit.Result.NOT_HANDLED;
89
+
meta.maintainers = with lib.maintainers; [ aleksana ];