diff --git a/nixos/modules/services/networking/sing-box.nix b/nixos/modules/services/networking/sing-box.nix index 104c75c8105cc..033b423ba2b22 100644 --- a/nixos/modules/services/networking/sing-box.nix +++ b/nixos/modules/services/networking/sing-box.nix @@ -12,7 +12,10 @@ in { meta = { - maintainers = with lib.maintainers; [ nickcao ]; + maintainers = with lib.maintainers; [ + nickcao + oluceps + ]; }; options = { @@ -34,47 +37,82 @@ in containing the value the option should be set to. ''; }; + configFile = lib.mkOption { + type = with lib.types; nullOr path; + default = null; + description = '' + The path of file that contains sing-box configuration. + ''; + }; }; }; - config = lib.mkIf cfg.enable { - assertions = - let - rules = cfg.settings.route.rules or [ ]; - in - [ - { - assertion = !lib.any (r: r ? source_geoip || r ? geoip) rules; - message = '' - Deprecated option `services.sing-box.settings.route.rules.*.{source_geoip,geoip}` is set. - See https://sing-box.sagernet.org/migration/#migrate-geoip-to-rule-sets for migration instructions. - ''; - } - { - assertion = !lib.any (r: r ? geosite) rules; + config = lib.mkIf cfg.enable ( + let + useConfigFile = cfg.configFile != null; + in + { + assertions = + (lib.singleton { + assertion = + let + settingsSet = cfg.settings != { }; + in + useConfigFile -> !settingsSet; message = '' - Deprecated option `services.sing-box.settings.route.rules.*.geosite` is set. - See https://sing-box.sagernet.org/migration/#migrate-geosite-to-rule-sets for migration instructions. + `services.sing-box.settings` or `services.sing-box.configFile` must be set exclusively. ''; - } - ]; + }) + ++ (lib.optionals (!useConfigFile) ( + let + rules = cfg.settings.route.rules or [ ]; + in + [ + { + assertion = !lib.any (r: r ? source_geoip || r ? geoip) rules; + message = '' + Deprecated option `services.sing-box.settings.route.rules.*.{source_geoip,geoip}` is set. + See https://sing-box.sagernet.org/migration/#migrate-geoip-to-rule-sets for migration instructions. + ''; + } + { + assertion = !lib.any (r: r ? geosite) rules; + message = '' + Deprecated option `services.sing-box.settings.route.rules.*.geosite` is set. + See https://sing-box.sagernet.org/migration/#migrate-geosite-to-rule-sets for migration instructions. + ''; + } + ] + )); - systemd.packages = [ cfg.package ]; + systemd.packages = [ cfg.package ]; - systemd.services.sing-box = { - preStart = utils.genJqSecretsReplacementSnippet cfg.settings "/run/sing-box/config.json"; - serviceConfig = { - StateDirectory = "sing-box"; - StateDirectoryMode = "0700"; - RuntimeDirectory = "sing-box"; - RuntimeDirectoryMode = "0700"; - ExecStart = [ - "" - "${lib.getExe cfg.package} -D \${STATE_DIRECTORY} -C \${RUNTIME_DIRECTORY} run" - ]; + systemd.services.sing-box = { + + preStart = lib.mkIf (!useConfigFile) ( + utils.genJqSecretsReplacementSnippet cfg.settings "/run/sing-box/config.json" + ); + + serviceConfig = { + StateDirectory = "sing-box"; + StateDirectoryMode = "0700"; + RuntimeDirectory = "sing-box"; + RuntimeDirectoryMode = "0700"; + LoadCredential = lib.mkIf useConfigFile [ ("config.json:" + cfg.configFile) ]; + ExecStart = [ + "" + ( + let + configArgs = + if useConfigFile then "-c $\{CREDENTIALS_DIRECTORY}/config.json" else "-C \${RUNTIME_DIRECTORY}"; + in + "${lib.getExe cfg.package} -D \${STATE_DIRECTORY} ${configArgs} run" + ) + ]; + }; + wantedBy = [ "multi-user.target" ]; }; - wantedBy = [ "multi-user.target" ]; - }; - }; + } + ); } diff --git a/nixos/tests/sing-box.nix b/nixos/tests/sing-box.nix index a8a287586af2d..e20891e3a50bb 100644 --- a/nixos/tests/sing-box.nix +++ b/nixos/tests/sing-box.nix @@ -210,17 +210,21 @@ import ./make-test-python.nix ( services.sing-box = { enable = true; - settings = { - inbounds = [ - vmessInbound - ]; - outbounds = [ - { - type = "direct"; - tag = "outbound:direct"; + configFile = toString ( + pkgs.writeText "test-sing-box-json-config" ( + builtins.toJSON { + inbounds = [ + vmessInbound + ]; + outbounds = [ + { + type = "direct"; + tag = "outbound:direct"; + } + ]; } - ]; - }; + ) + ); }; };