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 ]
156 ++ (with lib; optional cfg.disableTxChecksumIpGeneric TxChecksumIpGenericWorkaround);
157 ExecStart = [
158 ""
159 "${daeBin} run --disable-timestamp -c \${CREDENTIALS_DIRECTORY}/config.dae"
160 ];
161 Environment = "DAE_LOCATION_ASSET=${cfg.assetsPath}";
162 };
163 };
164
165 assertions = [
166 {
167 assertion = lib.pathExists (toString (genAssetsDrv cfg.assets) + "/share/v2ray");
168 message = ''
169 Packages in `assets` has no preset paths included.
170 Please set `assetsPath` instead.
171 '';
172 }
173
174 {
175 assertion = !((config.services.dae.config != null) && (config.services.dae.configFile != null));
176 message = ''
177 Option `config` and `configFile` could not be set
178 at the same time.
179 '';
180 }
181
182 {
183 assertion = !((config.services.dae.config == null) && (config.services.dae.configFile == null));
184 message = ''
185 Either `config` or `configFile` should be set.
186 '';
187 }
188 ];
189 };
190}