1# NOTE:
2# cfg.configFile contains secrets such as proxy servers' credential!
3# we dont want plaintext secrets in world-readable `/nix/store`.
4
5{
6 lib,
7 config,
8 pkgs,
9 ...
10}:
11let
12 cfg = config.services.mihomo;
13in
14{
15 options.services.mihomo = {
16 enable = lib.mkEnableOption "Mihomo, A rule-based proxy in Go";
17
18 package = lib.mkPackageOption pkgs "mihomo" { };
19
20 configFile = lib.mkOption {
21 type = lib.types.path;
22 description = "Configuration file to use.";
23 };
24
25 webui = lib.mkOption {
26 default = null;
27 type = lib.types.nullOr lib.types.path;
28 example = lib.literalExpression "pkgs.metacubexd";
29 description = ''
30 Local web interface to use.
31
32 You can also use the following website:
33 - metacubexd:
34 - <https://d.metacubex.one>
35 - <https://metacubex.github.io/metacubexd>
36 - <https://metacubexd.pages.dev>
37 - yacd:
38 - <https://yacd.haishan.me>
39 - clash-dashboard:
40 - <https://clash.razord.top>
41 '';
42 };
43
44 extraOpts = lib.mkOption {
45 default = null;
46 type = lib.types.nullOr lib.types.str;
47 description = "Extra command line options to use.";
48 };
49
50 tunMode = lib.mkEnableOption ''
51 necessary permission for Mihomo's systemd service for TUN mode to function properly.
52
53 Keep in mind, that you still need to enable TUN mode manually in Mihomo's configuration
54 '';
55 };
56
57 config = lib.mkIf cfg.enable {
58 ### systemd service
59 systemd.services."mihomo" = {
60 description = "Mihomo daemon, A rule-based proxy in Go.";
61 documentation = [ "https://wiki.metacubex.one/" ];
62 requires = [ "network-online.target" ];
63 after = [ "network-online.target" ];
64 wantedBy = [ "multi-user.target" ];
65 serviceConfig = {
66 ExecStart = lib.concatStringsSep " " [
67 (lib.getExe cfg.package)
68 "-d /var/lib/private/mihomo"
69 "-f \${CREDENTIALS_DIRECTORY}/config.yaml"
70 (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
71 (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
72 ];
73
74 DynamicUser = true;
75 StateDirectory = "mihomo";
76 LoadCredential = "config.yaml:${cfg.configFile}";
77
78 ### Hardening
79 AmbientCapabilities = "";
80 CapabilityBoundingSet = "";
81 DeviceAllow = "";
82 LockPersonality = true;
83 MemoryDenyWriteExecute = true;
84 NoNewPrivileges = true;
85 PrivateDevices = true;
86 PrivateMounts = true;
87 PrivateTmp = true;
88 PrivateUsers = true;
89 ProcSubset = "pid";
90 ProtectClock = true;
91 ProtectControlGroups = true;
92 ProtectHome = true;
93 ProtectHostname = true;
94 ProtectKernelLogs = true;
95 ProtectKernelModules = true;
96 ProtectKernelTunables = true;
97 ProtectProc = "invisible";
98 ProtectSystem = "strict";
99 RestrictRealtime = true;
100 RestrictSUIDSGID = true;
101 RestrictNamespaces = true;
102 RestrictAddressFamilies = "AF_INET AF_INET6";
103 SystemCallArchitectures = "native";
104 SystemCallFilter = "@system-service bpf";
105 UMask = "0077";
106 }
107 // lib.optionalAttrs cfg.tunMode {
108 AmbientCapabilities = "CAP_NET_ADMIN";
109 CapabilityBoundingSet = "CAP_NET_ADMIN";
110 PrivateDevices = false;
111 PrivateUsers = false;
112 RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
113 };
114 };
115 };
116
117 meta.maintainers = with lib.maintainers; [ Guanran928 ];
118}