1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8let
9 cfg = config.services.sing-box;
10 settingsFormat = pkgs.formats.json { };
11in
12{
13
14 meta = {
15 maintainers = with lib.maintainers; [
16 nickcao
17 prince213
18 ];
19 };
20
21 options = {
22 services.sing-box = {
23 enable = lib.mkEnableOption "sing-box universal proxy platform";
24
25 package = lib.mkPackageOption pkgs "sing-box" { };
26
27 settings = lib.mkOption {
28 type = lib.types.submodule {
29 freeformType = settingsFormat.type;
30 };
31 default = { };
32 description = ''
33 The sing-box configuration, see <https://sing-box.sagernet.org/configuration/> for documentation.
34
35 Options containing secret data should be set to an attribute set
36 containing the attribute `_secret` - a string pointing to a file
37 containing the value the option should be set to.
38 '';
39 };
40 };
41 };
42
43 config = lib.mkIf cfg.enable {
44 assertions =
45 let
46 rules = cfg.settings.route.rules or [ ];
47 in
48 [
49 {
50 assertion = !lib.any (r: r ? source_geoip || r ? geoip) rules;
51 message = ''
52 Deprecated option `services.sing-box.settings.route.rules.*.{source_geoip,geoip}` is set.
53 See https://sing-box.sagernet.org/migration/#migrate-geoip-to-rule-sets for migration instructions.
54 '';
55 }
56 {
57 assertion = !lib.any (r: r ? geosite) rules;
58 message = ''
59 Deprecated option `services.sing-box.settings.route.rules.*.geosite` is set.
60 See https://sing-box.sagernet.org/migration/#migrate-geosite-to-rule-sets for migration instructions.
61 '';
62 }
63 ];
64
65 # for polkit rules
66 environment.systemPackages = [ cfg.package ];
67 services.dbus.packages = [ cfg.package ];
68 systemd.packages = [ cfg.package ];
69
70 systemd.services.sing-box = {
71 serviceConfig = {
72 User = "sing-box";
73 Group = "sing-box";
74 StateDirectory = "sing-box";
75 StateDirectoryMode = "0700";
76 RuntimeDirectory = "sing-box";
77 RuntimeDirectoryMode = "0700";
78 ExecStartPre =
79 let
80 script = pkgs.writeShellScript "sing-box-pre-start" ''
81 ${utils.genJqSecretsReplacementSnippet cfg.settings "/run/sing-box/config.json"}
82 chown --reference=/run/sing-box /run/sing-box/config.json
83 '';
84 in
85 "+${script}";
86 ExecStart = [
87 ""
88 "${lib.getExe cfg.package} -D \${STATE_DIRECTORY} -C \${RUNTIME_DIRECTORY} run"
89 ];
90 };
91 wantedBy = [ "multi-user.target" ];
92 };
93
94 users = {
95 users.sing-box = {
96 isSystemUser = true;
97 group = "sing-box";
98 };
99 groups.sing-box = { };
100 };
101 };
102}