main
1{
2 lib,
3 pkgs,
4 config,
5 ...
6}: let
7 utils = pkgs.callPackage "${pkgs.path}/nixos/lib/utils.nix" {};
8
9 cfg = config.services.xray;
10
11 configFormat = pkgs.formats.json {};
12
13 runtimeConfig = "/run/xray/config.json";
14in {
15 disabledModules = ["services/networking/xray.nix"];
16
17 options.services.xray = {
18 enable = lib.mkOption {
19 type = lib.types.bool;
20 default = false;
21 description = ''
22 Whether to run xray server.
23
24 Either `settingsFile` or `settings` must be specified.
25 '';
26 };
27
28 package = lib.mkPackageOption pkgs "xray" {};
29
30 settingsFile = lib.mkOption {
31 type = lib.types.nullOr lib.types.path;
32 default = null;
33 example = "/etc/xray/config.json";
34 description = ''
35 The absolute path to the configuration file.
36
37 Either `settingsFile` or `settings` must be specified.
38
39 See <https://www.v2fly.org/en_US/config/overview.html>.
40 '';
41 };
42
43 settings = lib.mkOption {
44 type = lib.types.nullOr (lib.types.submodule {
45 freeformType = configFormat.type;
46 });
47 default = null;
48 example = {
49 inbounds = [
50 {
51 port = 1080;
52 listen = "127.0.0.1";
53 protocol = "http";
54 }
55 ];
56 outbounds = [
57 {
58 protocol = "freedom";
59 }
60 ];
61 };
62 description = ''
63 The configuration object.
64
65 Either `settingsFile` or `settings` must be specified.
66
67 See <https://www.v2fly.org/en_US/config/overview.html>.
68
69 Options containing secret data should be set to an attribute set
70 containing the attribute `_secret` - a string pointing to a file
71 containing the value the option should be set to.
72
73 Set `quote = true` (default behavior) to quote the content of the
74 secret file as a string, or set `quote = false` to parse the content
75 of the secret file to JSON.
76 '';
77 };
78 };
79
80 config = lib.mkIf cfg.enable {
81 assertions = [
82 {
83 assertion = (cfg.settingsFile == null) != (cfg.settings == null);
84 message = "Either but not both `settingsFile` and `settings` should be specified for xray.";
85 }
86 ];
87
88 systemd.services.xray = {
89 description = "xray Daemon";
90 after = ["network.target"];
91 wantedBy = ["multi-user.target"];
92 serviceConfig = {
93 ExecStart = "${lib.getExe cfg.package} run -config ${runtimeConfig}";
94
95 ExecStartPre = "+${pkgs.writeShellScript "xray-pre-start" ''
96 ${lib.optionalString (cfg.settings != null) (utils.genJqSecretsReplacementSnippet cfg.settings runtimeConfig)}
97 ${lib.optionalString (cfg.settings != null) "chown --reference=/run/xray ${runtimeConfig}"}
98 ${lib.getExe cfg.package} -test -config ${
99 if cfg.settings != null
100 then runtimeConfig
101 else cfg.settingsFile
102 }
103 ''}";
104
105 DynamicUser = true;
106 RuntimeDirectory = "xray";
107 RuntimeDirectoryMode = "0700";
108 CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
109 AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
110 NoNewPrivileges = true;
111 };
112 };
113 };
114}