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 {
67 ExecStart = lib.concatStringsSep " " [
68 (lib.getExe cfg.package)
69 "-d /var/lib/private/mihomo"
70 "-f \${CREDENTIALS_DIRECTORY}/config.yaml"
71 (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
72 (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
73 ];
74
75 DynamicUser = true;
76 StateDirectory = "mihomo";
77 LoadCredential = "config.yaml:${cfg.configFile}";
78
79 ### Hardening
80 AmbientCapabilities = "";
81 CapabilityBoundingSet = "";
82 DeviceAllow = "";
83 LockPersonality = true;
84 MemoryDenyWriteExecute = true;
85 NoNewPrivileges = true;
86 PrivateDevices = true;
87 PrivateMounts = true;
88 PrivateTmp = true;
89 PrivateUsers = true;
90 ProcSubset = "pid";
91 ProtectClock = true;
92 ProtectControlGroups = true;
93 ProtectHome = true;
94 ProtectHostname = true;
95 ProtectKernelLogs = true;
96 ProtectKernelModules = true;
97 ProtectKernelTunables = true;
98 ProtectProc = "invisible";
99 ProtectSystem = "strict";
100 RestrictRealtime = true;
101 RestrictSUIDSGID = true;
102 RestrictNamespaces = true;
103 RestrictAddressFamilies = "AF_INET AF_INET6";
104 SystemCallArchitectures = "native";
105 SystemCallFilter = "@system-service bpf";
106 UMask = "0077";
107 }
108 // lib.optionalAttrs cfg.tunMode {
109 AmbientCapabilities = "CAP_NET_ADMIN";
110 CapabilityBoundingSet = "CAP_NET_ADMIN";
111 PrivateDevices = false;
112 PrivateUsers = false;
113 RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
114 };
115 };
116 };
117
118 meta.maintainers = with lib.maintainers; [ Guanran928 ];
119}