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