at 24.11-pre 5.7 kB view raw
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 ${concatStringsSep "\n" (mapAttrsToList (ipv4: ipv6: "map " + ipv4 + " " + ipv6) cfg.mappings)} 21 ''; 22 23 addrOpts = v: 24 assert v == 4 || v == 6; 25 { 26 options = { 27 address = mkOption { 28 type = types.str; 29 description = "IPv${toString v} address."; 30 }; 31 32 prefixLength = mkOption { 33 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 34 description = '' 35 Subnet mask of the interface, specified as the number of 36 bits in the prefix ("${if v == 4 then "24" else "64"}"). 37 ''; 38 }; 39 }; 40 }; 41 42 versionOpts = v: { 43 options = { 44 router = { 45 address = mkOption { 46 type = types.str; 47 description = "The IPv${toString v} address of the router."; 48 }; 49 }; 50 51 address = mkOption { 52 type = types.nullOr types.str; 53 default = null; 54 description = "The source IPv${toString v} address of the TAYGA server."; 55 }; 56 57 pool = mkOption { 58 type = with types; nullOr (submodule (addrOpts v)); 59 description = "The pool of IPv${toString v} addresses which are used for translation."; 60 }; 61 }; 62 }; 63in 64{ 65 options = { 66 services.tayga = { 67 enable = mkEnableOption "Tayga"; 68 69 package = mkPackageOption pkgs "tayga" { }; 70 71 ipv4 = mkOption { 72 type = types.submodule (versionOpts 4); 73 description = "IPv4-specific configuration."; 74 example = literalExpression '' 75 { 76 address = "192.0.2.0"; 77 router = { 78 address = "192.0.2.1"; 79 }; 80 pool = { 81 address = "192.0.2.1"; 82 prefixLength = 24; 83 }; 84 } 85 ''; 86 }; 87 88 ipv6 = mkOption { 89 type = types.submodule (versionOpts 6); 90 description = "IPv6-specific configuration."; 91 example = literalExpression '' 92 { 93 address = "2001:db8::1"; 94 router = { 95 address = "64:ff9b::1"; 96 }; 97 pool = { 98 address = "64:ff9b::"; 99 prefixLength = 96; 100 }; 101 } 102 ''; 103 }; 104 105 dataDir = mkOption { 106 type = types.path; 107 default = "/var/lib/tayga"; 108 description = "Directory for persistent data."; 109 }; 110 111 tunDevice = mkOption { 112 type = types.str; 113 default = "nat64"; 114 description = "Name of the nat64 tun device."; 115 }; 116 117 mappings = mkOption { 118 type = types.attrsOf types.str; 119 default = {}; 120 description = "Static IPv4 -> IPv6 host mappings."; 121 example = literalExpression '' 122 { 123 "192.168.5.42" = "2001:db8:1:4444::1"; 124 "192.168.5.43" = "2001:db8:1:4444::2"; 125 "192.168.255.2" = "2001:db8:1:569::143"; 126 } 127 ''; 128 }; 129 }; 130 }; 131 132 config = mkIf cfg.enable { 133 assertions = [ 134 { 135 assertion = allUnique (attrValues cfg.mappings); 136 message = "Neither the IPv4 nor the IPv6 addresses must be entered twice in the mappings."; 137 } 138 ]; 139 140 networking.interfaces."${cfg.tunDevice}" = { 141 virtual = true; 142 virtualType = "tun"; 143 virtualOwner = mkIf config.networking.useNetworkd ""; 144 ipv4 = { 145 addresses = [ 146 { address = cfg.ipv4.router.address; prefixLength = 32; } 147 ]; 148 routes = [ 149 cfg.ipv4.pool 150 ]; 151 }; 152 ipv6 = { 153 addresses = [ 154 { address = cfg.ipv6.router.address; prefixLength = 128; } 155 ]; 156 routes = [ 157 cfg.ipv6.pool 158 ]; 159 }; 160 }; 161 162 systemd.services.tayga = { 163 description = "Stateless NAT64 implementation"; 164 wantedBy = [ "multi-user.target" ]; 165 after = [ "network.target" ]; 166 167 serviceConfig = { 168 ExecStart = "${cfg.package}/bin/tayga -d --nodetach --config ${configFile}"; 169 ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; 170 Restart = "always"; 171 172 # Hardening Score: 173 # - nixos-scripts: 2.1 174 # - systemd-networkd: 1.6 175 ProtectHome = true; 176 SystemCallFilter = [ 177 "@network-io" 178 "@system-service" 179 "~@privileged" 180 "~@resources" 181 ]; 182 ProtectKernelLogs = true; 183 AmbientCapabilities = [ 184 "CAP_NET_ADMIN" 185 ]; 186 CapabilityBoundingSet = ""; 187 RestrictAddressFamilies = [ 188 "AF_INET" 189 "AF_INET6" 190 "AF_NETLINK" 191 ]; 192 StateDirectory = "tayga"; 193 DynamicUser = mkIf config.networking.useNetworkd true; 194 MemoryDenyWriteExecute = true; 195 RestrictRealtime = true; 196 RestrictSUIDSGID = true; 197 ProtectHostname = true; 198 ProtectKernelModules = true; 199 ProtectKernelTunables = true; 200 RestrictNamespaces = true; 201 NoNewPrivileges = true; 202 ProtectControlGroups = true; 203 SystemCallArchitectures = "native"; 204 PrivateTmp = true; 205 LockPersonality = true; 206 ProtectSystem = true; 207 PrivateUsers = true; 208 ProtectProc = "invisible"; 209 }; 210 }; 211 }; 212}