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