main
1{
2 lib,
3 pkgs,
4 config,
5 ...
6}: let
7 utils = pkgs.callPackage "${pkgs.path}/nixos/lib/utils.nix" {};
8
9 cfg = config.services.mihomo;
10
11 # mihomo use YAML as config format, but set to JSON for secret inject
12 configFormat = pkgs.formats.json {};
13
14 AmbientCapabilities =
15 lib.optional cfg.tunMode "CAP_NET_ADMIN"
16 ++ lib.optionals cfg.processesInfo [
17 "CAP_DAC_READ_SEARCH"
18 "CAP_SYS_PTRACE"
19 ];
20 CapabilityBoundingSet = AmbientCapabilities;
21in {
22 disabledModules = ["services/networking/mihomo.nix"];
23
24 options.services.mihomo = {
25 enable = lib.mkEnableOption "Mihomo, A rule-based proxy in Go";
26
27 package = lib.mkPackageOption pkgs "mihomo" {};
28
29 config = lib.mkOption {
30 type = lib.types.submodule {
31 freeformType = configFormat.type;
32 options = {
33 tun.enable = lib.mkOption {
34 default = cfg.tunMode;
35 type = lib.types.bool;
36 example = true;
37 description = "Enable mihomo's tun mode";
38 };
39 };
40 };
41 default = {};
42 description = ''
43 The mihomo configuration, see <https://wiki.metacubex.one/en/config/> for documentation.
44
45 Options containing secret data should be set to an attribute set
46 containing the attribute `_secret` - a string pointing to a file
47 containing the value the option should be set to.
48
49 Set `quote = true` (default behavior) to quote the content of the
50 secret file as a string, or set `quote = false` to parse the content
51 of the secret file to JSON.
52 '';
53 };
54
55 webui = lib.mkOption {
56 default = null;
57 type = lib.types.nullOr lib.types.path;
58 example = lib.literalExpression "pkgs.metacubexd";
59 description = ''
60 Local web interface to use.
61
62 You can also use the following website:
63 - metacubexd:
64 - <https://d.metacubex.one>
65 - <https://metacubex.github.io/metacubexd>
66 - <https://metacubexd.pages.dev>
67 - yacd:
68 - <https://yacd.haishan.me>
69 - clash-dashboard:
70 - <https://clash.razord.top>
71 '';
72 };
73
74 extraOpts = lib.mkOption {
75 default = null;
76 type = lib.types.nullOr lib.types.str;
77 description = "Extra command line options to use.";
78 };
79
80 tunMode = lib.mkEnableOption ''
81 necessary capabilities for Mihomo's systemd service for TUN mode to function properly.
82 '';
83
84 processesInfo = lib.mkEnableOption ''
85 necessary capabilities for rules about process information such as `process-name`
86 '';
87 };
88
89 config = lib.mkIf cfg.enable {
90 systemd.services."mihomo" = {
91 description = "Mihomo daemon, A rule-based proxy in Go.";
92 documentation = ["https://wiki.metacubex.one/"];
93 requires = ["network-online.target"];
94 after = ["network-online.target"];
95 wantedBy = ["multi-user.target"];
96 serviceConfig =
97 {
98 User = "mihomo";
99 Group = "mihomo";
100 StateDirectory = "mihomo";
101 StateDirectoryMode = "0700";
102 RuntimeDirectory = "mihomo";
103 RuntimeDirectoryMode = "0700";
104 WorkingDirectory = "/var/lib/mihomo";
105
106 ExecStart = lib.concatStringsSep " " [
107 (lib.getExe cfg.package)
108 "-d /var/lib/mihomo"
109 "-f /run/mihomo/config.yaml"
110 (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
111 (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
112 ];
113
114 ExecStartPre = "+${pkgs.writeShellScript "mihomo-pre-start" ''
115 ${utils.genJqSecretsReplacementSnippet cfg.config "/run/mihomo/config.json"}
116 ${lib.getExe pkgs.yq-go} --input-format 'json' --output-format 'yaml' \
117 /run/mihomo/config.json > /run/mihomo/config.yaml
118 rm /run/mihomo/config.json
119 chown --reference=/run/mihomo /run/mihomo/config.yaml
120 ''}";
121
122 inherit AmbientCapabilities CapabilityBoundingSet;
123 DeviceAllow = "";
124 LockPersonality = true;
125 MemoryDenyWriteExecute = true;
126 NoNewPrivileges = true;
127 PrivateDevices = true;
128 PrivateMounts = true;
129 PrivateTmp = true;
130 PrivateUsers = true;
131 ProcSubset = "pid";
132 ProtectClock = true;
133 ProtectControlGroups = true;
134 ProtectHome = true;
135 ProtectHostname = true;
136 ProtectKernelLogs = true;
137 ProtectKernelModules = true;
138 ProtectKernelTunables = true;
139 ProtectProc = "invisible";
140 ProtectSystem = "strict";
141 RestrictRealtime = true;
142 RestrictSUIDSGID = true;
143 RestrictNamespaces = true;
144 RestrictAddressFamilies = "AF_INET AF_INET6";
145 SystemCallArchitectures = "native";
146 SystemCallFilter = "@system-service bpf";
147 UMask = "0077";
148 }
149 // lib.optionalAttrs cfg.tunMode {
150 PrivateDevices = false;
151 PrivateUsers = false;
152 RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
153 };
154 };
155
156 users.users.mihomo = {
157 isSystemUser = true;
158 group = "mihomo";
159 home = "/var/lib/mihomo";
160 };
161 users.groups.mihomo = {};
162 };
163}