1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10{
11 options = {
12
13 services.xray = {
14 enable = mkOption {
15 type = types.bool;
16 default = false;
17 description = ''
18 Whether to run xray server.
19
20 Either `settingsFile` or `settings` must be specified.
21 '';
22 };
23
24 package = mkPackageOption pkgs "xray" { };
25
26 settingsFile = mkOption {
27 type = types.nullOr types.path;
28 default = null;
29 example = "/etc/xray/config.json";
30 description = ''
31 The absolute path to the configuration file.
32
33 Either `settingsFile` or `settings` must be specified.
34
35 See <https://www.v2fly.org/en_US/config/overview.html>.
36 '';
37 };
38
39 settings = mkOption {
40 type = types.nullOr (types.attrsOf types.unspecified);
41 default = null;
42 example = {
43 inbounds = [
44 {
45 port = 1080;
46 listen = "127.0.0.1";
47 protocol = "http";
48 }
49 ];
50 outbounds = [
51 {
52 protocol = "freedom";
53 }
54 ];
55 };
56 description = ''
57 The configuration object.
58
59 Either `settingsFile` or `settings` must be specified.
60
61 See <https://www.v2fly.org/en_US/config/overview.html>.
62 '';
63 };
64 };
65
66 };
67
68 config =
69 let
70 cfg = config.services.xray;
71 settingsFile =
72 if cfg.settingsFile != null then
73 cfg.settingsFile
74 else
75 pkgs.writeTextFile {
76 name = "xray.json";
77 text = builtins.toJSON cfg.settings;
78 checkPhase = ''
79 ${cfg.package}/bin/xray -test -config $out
80 '';
81 };
82
83 in
84 mkIf cfg.enable {
85 assertions = [
86 {
87 assertion = (cfg.settingsFile == null) != (cfg.settings == null);
88 message = "Either but not both `settingsFile` and `settings` should be specified for xray.";
89 }
90 ];
91
92 systemd.services.xray = {
93 description = "xray Daemon";
94 after = [ "network.target" ];
95 wantedBy = [ "multi-user.target" ];
96 script = ''
97 exec "${cfg.package}/bin/xray" -config "$CREDENTIALS_DIRECTORY/config.json"
98 '';
99 serviceConfig = {
100 DynamicUser = true;
101 LoadCredential = "config.json:${settingsFile}";
102 CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
103 AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
104 NoNewPrivileges = true;
105 };
106 };
107 };
108}