Unverified Commit 2897be36 authored by Doron Behar's avatar Doron Behar Committed by GitHub
Browse files

Merge pull request #287966 from Guanran928/clash-meta

nixos/mihomo: init
parents 48895ecb 84bbdc74
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ In addition to numerous new and upgraded packages, this release has the followin

- [ollama](https://ollama.ai), server for running large language models locally.

- [Mihomo](https://github.com/MetaCubeX/mihomo), a rule-based proxy in Go. Available as [services.mihomo.enable](#opt-services.mihomo.enable).

- [hebbot](https://github.com/haecker-felix/hebbot), a Matrix bot to generate "This Week in X" like blog posts. Available as [services.hebbot](#opt-services.hebbot.enable).

- [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
+1 −0
Original line number Diff line number Diff line
@@ -1018,6 +1018,7 @@
  ./services/networking/lxd-image-server.nix
  ./services/networking/magic-wormhole-mailbox-server.nix
  ./services/networking/matterbridge.nix
  ./services/networking/mihomo.nix
  ./services/networking/minidlna.nix
  ./services/networking/miniupnpd.nix
  ./services/networking/miredo.nix
+118 −0
Original line number Diff line number Diff line
# NOTE:
# cfg.configFile contains secrets such as proxy servers' credential!
# we dont want plaintext secrets in world-readable `/nix/store`.

{ lib
, config
, pkgs
, ...
}:
let
  cfg = config.services.mihomo;
in
{
  options.services.mihomo = {
    enable = lib.mkEnableOption "Mihomo, A rule-based proxy in Go.";

    package = lib.mkPackageOption pkgs "mihomo" { };

    configFile = lib.mkOption {
      default = null;
      type = lib.types.nullOr lib.types.path;
      description = "Configuration file to use.";
    };

    webui = lib.mkOption {
      default = null;
      type = lib.types.nullOr lib.types.path;
      description = ''
        Local web interface to use.

        You can also use the following website, just in case:
        - metacubexd:
          - https://d.metacubex.one
          - https://metacubex.github.io/metacubexd
          - https://metacubexd.pages.dev
        - yacd:
          - https://yacd.haishan.me
        - clash-dashboard (buggy):
          - 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 permission for Mihomo's systemd service for TUN mode to function properly.

      Keep in mind, that you still need to enable TUN mode manually in Mihomo's configuration.
    '';
  };

  config = lib.mkIf cfg.enable {
    ### systemd service
    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 =
        {
          ExecStart = lib.concatStringsSep " " [
            (lib.getExe cfg.package)
            "-d /var/lib/private/mihomo"
            (lib.optionalString (cfg.configFile != null) "-f \${CREDENTIALS_DIRECTORY}/config.yaml")
            (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
            (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
          ];

          DynamicUser = true;
          StateDirectory = "mihomo";
          LoadCredential = "config.yaml:${cfg.configFile}";

          ### Hardening
          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 {
          AmbientCapabilities = "CAP_NET_ADMIN";
          CapabilityBoundingSet = "CAP_NET_ADMIN";
          PrivateDevices = false;
          PrivateUsers = false;
          RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
        };
    };
  };

  meta.maintainers = with lib.maintainers; [ Guanran928 ];
}
+1 −0
Original line number Diff line number Diff line
@@ -529,6 +529,7 @@ in {
  memcached = handleTest ./memcached.nix {};
  merecat = handleTest ./merecat.nix {};
  metabase = handleTest ./metabase.nix {};
  mihomo = handleTest ./mihomo.nix {};
  mindustry = handleTest ./mindustry.nix {};
  minecraft = handleTest ./minecraft.nix {};
  minecraft-server = handleTest ./minecraft-server.nix {};

nixos/tests/mihomo.nix

0 → 100644
+44 −0
Original line number Diff line number Diff line
import ./make-test-python.nix ({ pkgs, ... }: {
  name = "mihomo";
  meta.maintainers = with pkgs.lib.maintainers; [ Guanran928 ];

  nodes.machine = {
    environment.systemPackages = [ pkgs.curl ];

    services.nginx = {
      enable = true;
      statusPage = true;
    };

    services.mihomo = {
      enable = true;
      configFile = pkgs.writeTextFile {
        name = "config.yaml";
        text = ''
          mixed-port: 7890
          external-controller: 127.0.0.1:9090
          authentication:
          - "user:supersecret"
        '';
      };
    };
  };

  testScript = ''
    # Wait until it starts
    machine.wait_for_unit("nginx.service")
    machine.wait_for_unit("mihomo.service")
    machine.wait_for_open_port(80)
    machine.wait_for_open_port(7890)
    machine.wait_for_open_port(9090)

    # Proxy
    machine.succeed("curl --fail --max-time 10 --proxy http://user:supersecret@localhost:7890 http://localhost")
    machine.succeed("curl --fail --max-time 10 --proxy socks5://user:supersecret@localhost:7890 http://localhost")
    machine.fail("curl --fail --max-time 10 --proxy http://user:supervillain@localhost:7890 http://localhost")
    machine.fail("curl --fail --max-time 10 --proxy socks5://user:supervillain@localhost:7890 http://localhost")

    # Web UI
    machine.succeed("curl --fail http://localhost:9090") == '{"hello":"clash"}'
  '';
})
Loading