1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.tayga;
6
7 # Converts an address set to a string
8 strAddr = addr: "${addr.address}/${toString addr.prefixLength}";
9
10 configFile = pkgs.writeText "tayga.conf" ''
11 tun-device ${cfg.tunDevice}
12
13 ipv4-addr ${cfg.ipv4.address}
14 ${optionalString (cfg.ipv6.address != null) "ipv6-addr ${cfg.ipv6.address}"}
15
16 prefix ${strAddr cfg.ipv6.pool}
17 dynamic-pool ${strAddr cfg.ipv4.pool}
18 data-dir ${cfg.dataDir}
19 '';
20
21 addrOpts = v:
22 assert v == 4 || v == 6;
23 {
24 options = {
25 address = mkOption {
26 type = types.str;
27 description = lib.mdDoc "IPv${toString v} address.";
28 };
29
30 prefixLength = mkOption {
31 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
32 description = lib.mdDoc ''
33 Subnet mask of the interface, specified as the number of
34 bits in the prefix ("${if v == 4 then "24" else "64"}").
35 '';
36 };
37 };
38 };
39
40 versionOpts = v: {
41 options = {
42 router = {
43 address = mkOption {
44 type = types.str;
45 description = lib.mdDoc "The IPv${toString v} address of the router.";
46 };
47 };
48
49 address = mkOption {
50 type = types.nullOr types.str;
51 default = null;
52 description = lib.mdDoc "The source IPv${toString v} address of the TAYGA server.";
53 };
54
55 pool = mkOption {
56 type = with types; nullOr (submodule (addrOpts v));
57 description = lib.mdDoc "The pool of IPv${toString v} addresses which are used for translation.";
58 };
59 };
60 };
61in
62{
63 options = {
64 services.tayga = {
65 enable = mkEnableOption (lib.mdDoc "Tayga");
66
67 package = mkOption {
68 type = types.package;
69 default = pkgs.tayga;
70 defaultText = lib.literalMD "pkgs.tayga";
71 description = lib.mdDoc "This option specifies the TAYGA package to use.";
72 };
73
74 ipv4 = mkOption {
75 type = types.submodule (versionOpts 4);
76 description = lib.mdDoc "IPv4-specific configuration.";
77 example = literalExpression ''
78 {
79 address = "192.0.2.0";
80 router = {
81 address = "192.0.2.1";
82 };
83 pool = {
84 address = "192.0.2.1";
85 prefixLength = 24;
86 };
87 }
88 '';
89 };
90
91 ipv6 = mkOption {
92 type = types.submodule (versionOpts 6);
93 description = lib.mdDoc "IPv6-specific configuration.";
94 example = literalExpression ''
95 {
96 address = "2001:db8::1";
97 router = {
98 address = "64:ff9b::1";
99 };
100 pool = {
101 address = "64:ff9b::";
102 prefixLength = 96;
103 };
104 }
105 '';
106 };
107
108 dataDir = mkOption {
109 type = types.path;
110 default = "/var/lib/tayga";
111 description = lib.mdDoc "Directory for persistent data";
112 };
113
114 tunDevice = mkOption {
115 type = types.str;
116 default = "nat64";
117 description = lib.mdDoc "Name of the nat64 tun device";
118 };
119 };
120 };
121
122 config = mkIf cfg.enable {
123 networking.interfaces."${cfg.tunDevice}" = {
124 virtual = true;
125 virtualType = "tun";
126 virtualOwner = mkIf config.networking.useNetworkd "";
127 ipv4 = {
128 addresses = [
129 { address = cfg.ipv4.router.address; prefixLength = 32; }
130 ];
131 routes = [
132 cfg.ipv4.pool
133 ];
134 };
135 ipv6 = {
136 addresses = [
137 { address = cfg.ipv6.router.address; prefixLength = 128; }
138 ];
139 routes = [
140 cfg.ipv6.pool
141 ];
142 };
143 };
144
145 systemd.services.tayga = {
146 description = "Stateless NAT64 implementation";
147 wantedBy = [ "multi-user.target" ];
148 after = [ "network.target" ];
149
150 serviceConfig = {
151 ExecStart = "${cfg.package}/bin/tayga -d --nodetach --config ${configFile}";
152 ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
153 Restart = "always";
154
155 # Hardening Score:
156 # - nixos-scripts: 2.1
157 # - systemd-networkd: 1.6
158 ProtectHome = true;
159 SystemCallFilter = [
160 "@network-io"
161 "@system-service"
162 "~@privileged"
163 "~@resources"
164 ];
165 ProtectKernelLogs = true;
166 AmbientCapabilities = [
167 "CAP_NET_ADMIN"
168 ];
169 CapabilityBoundingSet = "";
170 RestrictAddressFamilies = [
171 "AF_INET"
172 "AF_INET6"
173 "AF_NETLINK"
174 ];
175 StateDirectory = "tayga";
176 DynamicUser = mkIf config.networking.useNetworkd true;
177 MemoryDenyWriteExecute = true;
178 RestrictRealtime = true;
179 RestrictSUIDSGID = true;
180 ProtectHostname = true;
181 ProtectKernelModules = true;
182 ProtectKernelTunables = true;
183 RestrictNamespaces = true;
184 NoNewPrivileges = true;
185 ProtectControlGroups = true;
186 SystemCallArchitectures = "native";
187 PrivateTmp = true;
188 LockPersonality = true;
189 ProtectSystem = true;
190 PrivateUsers = true;
191 ProtectProc = "invisible";
192 };
193 };
194 };
195}