Commit a43411a

HPCesia <me@hpcesia.com>
2026-06-07 03:48:56
new module: mihomo
1 parent 2527f75
Changed files (2)
nixos-modules/default.nix
@@ -1,5 +1,5 @@
 {
   # Add your NixOS modules here
   #
-  # my-module = ./my-module;
+  mihomo = ./mihomo.nix;
 }
nixos-modules/mihomo.nix
@@ -0,0 +1,161 @@
+{
+  lib,
+  pkgs,
+  config,
+  inputs,
+  ...
+}: let
+  utils = import "${inputs.nixpkgs}/nixos/lib/utils.nix" {inherit lib pkgs config;};
+
+  cfg = config.services.mihomo;
+
+  # mihomo use YAML as config format, but set to JSON for secret inject
+  configFormat = pkgs.formats.json {};
+
+  AmbientCapabilities =
+    lib.optional cfg.tunMode "CAP_NET_ADMIN"
+    ++ lib.optionals cfg.processesInfo [
+      "CAP_DAC_READ_SEARCH"
+      "CAP_SYS_PTRACE"
+    ];
+  CapabilityBoundingSet = AmbientCapabilities;
+in {
+  disabledModules = ["services/networking/mihomo.nix"];
+
+  options.services.mihomo = {
+    enable = lib.mkEnableOption "Mihomo, A rule-based proxy in Go";
+
+    package = lib.mkPackageOption pkgs "mihomo" {};
+
+    config = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = configFormat.type;
+        options = {
+          tun.enable = lib.mkOption {
+            default = cfg.tunMode;
+            type = lib.types.bool;
+            example = true;
+            description = "Enable mihomo's tun mode";
+          };
+        };
+      };
+      default = {};
+      description = ''
+        The mihomo configuration, see <https://wiki.metacubex.one/en/config/> for documentation.
+
+        Options containing secret data should be set to an attribute set
+        containing the attribute `_secret` - a string pointing to a file
+        containing the value the option should be set to.
+
+        Set `quote = true` (default behavior) to quote the content of the
+        secret file as a string, or set `quote = false` to parse the content
+        of the secret file to JSON.
+      '';
+    };
+
+    webui = lib.mkOption {
+      default = null;
+      type = lib.types.nullOr lib.types.path;
+      example = lib.literalExpression "pkgs.metacubexd";
+      description = ''
+        Local web interface to use.
+
+        You can also use the following website:
+        - metacubexd:
+          - <https://d.metacubex.one>
+          - <https://metacubex.github.io/metacubexd>
+          - <https://metacubexd.pages.dev>
+        - yacd:
+          - <https://yacd.haishan.me>
+        - clash-dashboard:
+          - <https://clash.razord.top>
+      '';
+    };
+
+    extraOpts = lib.mkOption {
+      default = null;
+      type = lib.types.nullOr lib.types.str;
+      description = "Extra command line options to use.";
+    };
+
+    tunMode = lib.mkEnableOption ''
+      necessary capabilities for Mihomo's systemd service for TUN mode to function properly.
+    '';
+
+    processesInfo = lib.mkEnableOption ''
+      necessary capabilities for rules about process information such as `process-name`
+    '';
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services."mihomo" = {
+      description = "Mihomo daemon, A rule-based proxy in Go.";
+      documentation = ["https://wiki.metacubex.one/"];
+      requires = ["network-online.target"];
+      after = ["network-online.target"];
+      wantedBy = ["multi-user.target"];
+      serviceConfig =
+        {
+          User = "mihomo";
+          Group = "mihomo";
+          StateDirectory = "mihomo";
+          StateDirectoryMode = "0700";
+          RuntimeDirectory = "mihomo";
+          RuntimeDirectoryMode = "0700";
+          WorkingDirectory = "/var/lib/mihomo";
+
+          ExecStart = lib.concatStringsSep " " [
+            (lib.getExe cfg.package)
+            "-d /var/lib/mihomo"
+            "-f /run/mihomo/config.yaml"
+            (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
+            (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
+          ];
+
+          ExecStartPre = "+${pkgs.writeShellScript "mihomo-pre-start" ''
+            ${utils.genJqSecretsReplacementSnippet cfg.config "/run/mihomo/config.yaml"}
+            chown --reference=/run/mihomo /run/mihomo/config.yaml
+          ''}";
+
+          inherit AmbientCapabilities CapabilityBoundingSet;
+          DeviceAllow = "";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProcSubset = "pid";
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          RestrictNamespaces = true;
+          RestrictAddressFamilies = "AF_INET AF_INET6";
+          SystemCallArchitectures = "native";
+          SystemCallFilter = "@system-service bpf";
+          UMask = "0077";
+        }
+        // lib.optionalAttrs cfg.tunMode {
+          PrivateDevices = false;
+          PrivateUsers = false;
+          RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
+        };
+    };
+
+    users.users.mihomo = {
+      isSystemUser = true;
+      group = "mihomo";
+      home = "/var/lib/mihomo";
+    };
+    users.groups.mihomo = {};
+  };
+}