1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.dae;
10 assets = cfg.assets;
11 genAssetsDrv =
12 paths:
13 pkgs.symlinkJoin {
14 name = "dae-assets";
15 inherit paths;
16 };
17in
18{
19 meta.maintainers = with lib.maintainers; [
20 pokon548
21 oluceps
22 ];
23
24 options = {
25 services.dae = with lib; {
26 enable = mkEnableOption "dae, a Linux high-performance transparent proxy solution based on eBPF";
27
28 package = mkPackageOption pkgs "dae" { };
29
30 assets = mkOption {
31 type = with types; (listOf path);
32 default = with pkgs; [
33 v2ray-geoip
34 v2ray-domain-list-community
35 ];
36 defaultText = literalExpression "with pkgs; [ v2ray-geoip v2ray-domain-list-community ]";
37 description = ''
38 Assets required to run dae.
39 '';
40 };
41
42 assetsPath = mkOption {
43 type = types.str;
44 default = "${genAssetsDrv assets}/share/v2ray";
45 defaultText = literalExpression ''
46 (symlinkJoin {
47 name = "dae-assets";
48 paths = assets;
49 })/share/v2ray
50 '';
51 description = ''
52 The path which contains geolocation database.
53 This option will override `assets`.
54 '';
55 };
56
57 openFirewall = mkOption {
58 type =
59 with types;
60 submodule {
61 options = {
62 enable = mkEnableOption "opening {option}`port` in the firewall";
63 port = mkOption {
64 type = types.port;
65 description = ''
66 Port to be opened. Consist with field `tproxy_port` in config file.
67 '';
68 };
69 };
70 };
71 default = {
72 enable = true;
73 port = 12345;
74 };
75 defaultText = literalExpression ''
76 {
77 enable = true;
78 port = 12345;
79 }
80 '';
81 description = ''
82 Open the firewall port.
83 '';
84 };
85
86 configFile = mkOption {
87 type = with types; (nullOr path);
88 default = null;
89 example = "/path/to/your/config.dae";
90 description = ''
91 The path of dae config file, end with `.dae`.
92 '';
93 };
94
95 config = mkOption {
96 type = with types; (nullOr str);
97 default = null;
98 description = ''
99 WARNING: This option will expose store your config unencrypted world-readable in the nix store.
100 Config text for dae.
101
102 See <https://github.com/daeuniverse/dae/blob/main/example.dae>.
103 '';
104 };
105
106 disableTxChecksumIpGeneric = mkEnableOption "" // {
107 description = "See <https://github.com/daeuniverse/dae/issues/43>";
108 };
109
110 };
111 };
112
113 config =
114 lib.mkIf cfg.enable
115
116 {
117 environment.systemPackages = [ cfg.package ];
118 systemd.packages = [ cfg.package ];
119
120 networking = lib.mkIf cfg.openFirewall.enable {
121 firewall =
122 let
123 portToOpen = cfg.openFirewall.port;
124 in
125 {
126 allowedTCPPorts = [ portToOpen ];
127 allowedUDPPorts = [ portToOpen ];
128 };
129 };
130
131 systemd.services.dae =
132 let
133 daeBin = lib.getExe cfg.package;
134
135 configPath =
136 if cfg.configFile != null then cfg.configFile else pkgs.writeText "config.dae" cfg.config;
137
138 TxChecksumIpGenericWorkaround =
139 with lib;
140 (getExe pkgs.writeShellApplication {
141 name = "disable-tx-checksum-ip-generic";
142 text = with pkgs; ''
143 iface=$(${iproute2}/bin/ip route | ${lib.getExe gawk} '/default/ {print $5}')
144 ${lib.getExe ethtool} -K "$iface" tx-checksum-ip-generic off
145 '';
146 });
147 in
148 {
149 wantedBy = [ "multi-user.target" ];
150 serviceConfig = {
151 LoadCredential = [ "config.dae:${configPath}" ];
152 ExecStartPre = [
153 ""
154 "${daeBin} validate -c \${CREDENTIALS_DIRECTORY}/config.dae"
155 ] ++ (with lib; optional cfg.disableTxChecksumIpGeneric TxChecksumIpGenericWorkaround);
156 ExecStart = [
157 ""
158 "${daeBin} run --disable-timestamp -c \${CREDENTIALS_DIRECTORY}/config.dae"
159 ];
160 Environment = "DAE_LOCATION_ASSET=${cfg.assetsPath}";
161 };
162 };
163
164 assertions = [
165 {
166 assertion = lib.pathExists (toString (genAssetsDrv cfg.assets) + "/share/v2ray");
167 message = ''
168 Packages in `assets` has no preset paths included.
169 Please set `assetsPath` instead.
170 '';
171 }
172
173 {
174 assertion = !((config.services.dae.config != null) && (config.services.dae.configFile != null));
175 message = ''
176 Option `config` and `configFile` could not be set
177 at the same time.
178 '';
179 }
180
181 {
182 assertion = !((config.services.dae.config == null) && (config.services.dae.configFile == null));
183 message = ''
184 Either `config` or `configFile` should be set.
185 '';
186 }
187 ];
188 };
189}