Personal Nix setup
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add dynamic IP calculation for networking

+401 -50
+320
lib/ipv4.nix
··· 1 + /* See: https://github.com/djacu/nixpkgs/blob/adb22cf/lib/network.nix */ 2 + { lib, ... }: 3 + 4 + let 5 + /* 6 + Converts an IP address from a list of ints to a string. 7 + 8 + Type: prettyIp :: [ Int ] -> String 9 + 10 + Examples: 11 + prettyIp [ 192 168 70 9 ] 12 + => "192.168.70.9" 13 + */ 14 + prettyIp = addr: 15 + lib.concatStringsSep "." (builtins.map builtins.toString addr); 16 + 17 + /* 18 + Given a bit mask, return the associated subnet mask. 19 + 20 + Type: bitMaskToSubnetMask :: Int -> [ Int ] 21 + 22 + Examples: 23 + bitMaskToSubnetMask 15 24 + => [ 255 254 0 0 ] 25 + bitMaskToSubnetMask 24 26 + => [ 255 255 255 0 ] 27 + */ 28 + bitMaskToSubnetMask = bitMask: let 29 + numOctets = 4; 30 + octetBits = 8; 31 + octetMin = 0; 32 + octetMax = 255; 33 + # How many initial parts of the mask are full (=255) 34 + fullParts = bitMask / octetBits; 35 + in 36 + lib.genList ( 37 + idx: 38 + # Fill up initial full parts 39 + if idx < fullParts 40 + then octetMax 41 + # If we're above the first non-full part, fill with 0 42 + else if fullParts < idx 43 + then octetMin 44 + # First non-full part generation 45 + else _genPartialMask (lib.mod bitMask octetBits) 46 + ) 47 + numOctets; 48 + 49 + /* 50 + Generate a the partial portion of a subnet mask. 51 + 52 + Type: _genPartialMask :: Int -> Int 53 + 54 + Examples: 55 + _genPartialMask 0 56 + => 0 57 + _genPartialMask 1 58 + => 128 59 + _genPartialMask 2 60 + => 192 61 + _genPartialMask 3 62 + => 224 63 + _genPartialMask 4 64 + => 240 65 + _genPartialMask 5 66 + => 248 67 + _genPartialMask 6 68 + => 252 69 + _genPartialMask 7 70 + => 254 71 + */ 72 + _genPartialMask = n: 73 + if n == 0 74 + then 0 75 + else _genPartialMask (n - 1) / 2 + 128; 76 + 77 + /* 78 + Given a subnet mask, return the associated bit mask. 79 + 80 + Type: subnetMaskToBitMask :: [ Int ] -> Int 81 + 82 + Examples: 83 + subnetMaskToBitMask [ 255 254 0 0 ] 84 + => 15 85 + subnetMaskToBitMask [ 255 255 255 0 ] 86 + => 24 87 + */ 88 + subnetMaskToBitMask = subnetMask: let 89 + partialBits = octet: 90 + if octet == 0 91 + then 0 92 + else (lib.mod octet 2) + partialBits (octet / 2); 93 + in 94 + builtins.foldl' 95 + (x: y: x + y) 96 + 0 97 + (builtins.map partialBits subnetMask); 98 + 99 + /* 100 + Given a CIDR, return the IP Address. 101 + 102 + Type: cidrToIpAddress :: String -> [ Int ] 103 + 104 + Examples: 105 + cidrToIpAddress "192.168.70.9/15" 106 + => [ 192 168 70 9 ] 107 + */ 108 + cidrToIpAddress = cidr: let 109 + splitParts = lib.splitString "/" cidr; 110 + addr = lib.elemAt splitParts 0; 111 + parsed = 112 + builtins.map 113 + lib.toInt 114 + (builtins.match "([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)" addr); 115 + checkBounds = octet: 116 + (octet >= 0) && (octet <= 255); 117 + in 118 + if (builtins.all checkBounds parsed) 119 + then parsed 120 + else builtins.throw "IP ${prettyIp addr} has out of bounds octet(s)"; 121 + 122 + /* 123 + Given a CIDR, return the bitmask. 124 + 125 + Type: cidrToBitMask :: String -> Int 126 + 127 + Examples: 128 + cidrToBitMask "192.168.70.9/15" 129 + => 15 130 + */ 131 + cidrToBitMask = cidr: let 132 + splitParts = lib.splitString "/" cidr; 133 + mask = lib.toInt (lib.elemAt splitParts 1); 134 + checkBounds = mask: 135 + (mask >= 0) && (mask <= 32); 136 + in 137 + if (checkBounds mask) 138 + then mask 139 + else builtins.throw "Bitmask ${builtins.toString mask} is invalid."; 140 + 141 + /* 142 + Given a CIDR, return the associated subnet mask. 143 + 144 + Type: cidrToSubnetMask :: String -> [ Int ] 145 + 146 + Examples: 147 + cidrToSubnetMask "192.168.70.9/15" 148 + => [ 255 254 0 0 ] 149 + */ 150 + cidrToSubnetMask = cidr: 151 + bitMaskToSubnetMask (cidrToBitMask cidr); 152 + 153 + /* 154 + Given a CIDR, return the associated network ID. 155 + 156 + Type: cidrToNetworkId :: String -> [ Int ] 157 + 158 + Examples: 159 + cidrToNetworkId "192.168.70.9/15" 160 + => [ 192 168 0 0 ] 161 + */ 162 + cidrToNetworkId = cidr: let 163 + ip = cidrToIpAddress cidr; 164 + subnetMask = cidrToSubnetMask cidr; 165 + in 166 + lib.zipListsWith lib.bitAnd ip subnetMask; 167 + 168 + /* 169 + Given a CIDR, return the associated first usable IP address. 170 + 171 + Type: cidrToFirstUsableIp :: String -> [ Int ] 172 + 173 + Examples: 174 + cidrToFirstUsableIp "192.168.70.9/15" 175 + => [ 192 168 0 1 ] 176 + */ 177 + cidrToFirstUsableIp = cidr: let 178 + networkId = cidrToNetworkId cidr; 179 + in 180 + incrementIp networkId 1; 181 + 182 + /* 183 + Given a CIDR, return the associated broadcast address. 184 + 185 + Type: cidrToBroadcastAddress :: String -> [ Int ] 186 + 187 + Examples: 188 + cidrToBroadcastAddress "192.168.70.9/15" 189 + => [ 192 169 255 255 ] 190 + */ 191 + cidrToBroadcastAddress = cidr: let 192 + subnetMask = cidrToSubnetMask cidr; 193 + networkId = cidrToNetworkId cidr; 194 + in 195 + getBroadcastAddress networkId subnetMask; 196 + 197 + /* 198 + Given a network ID and subnet mask, return the associated broadcast address. 199 + 200 + Type: getBroadcastAddress :: [ Int ] -> [ Int ] -> [ Int ] 201 + 202 + Examples: 203 + getBroadcastAddress [ 192 168 0 0 ] [ 255 254 0 0 ] 204 + => [ 192 169 255 255 ] 205 + */ 206 + getBroadcastAddress = networkId: subnetMask: 207 + lib.zipListsWith (nid: mask: 255 - mask + nid) networkId subnetMask; 208 + 209 + /* 210 + Given a CIDR, return the associated last usable IP address. 211 + 212 + Type: cidrToLastUsableIp :: String -> [ Int ] 213 + 214 + Examples: 215 + cidrToLastUsableIp "192.168.70.9/15" 216 + => [ 192 169 255 254 ] 217 + */ 218 + cidrToLastUsableIp = cidr: let 219 + broadcast = cidrToBroadcastAddress cidr; 220 + in 221 + incrementIp broadcast (-1); 222 + 223 + /* 224 + Increment the last octet of a given IP address. 225 + 226 + Type: incrementIp :: [ Int ] -> Int -> [ Int ] 227 + 228 + Examples: 229 + incrementIp [ 192 168 70 9 ] 3 230 + => [ 192 168 70 12 ] 231 + incrementIp [ 192 168 70 9 ] (-2) 232 + => [ 192 168 70 7 ] 233 + */ 234 + incrementIp = addr: offset: let 235 + lastOctet = lib.last addr; 236 + firstThree = lib.init addr; 237 + in 238 + firstThree ++ [(lastOctet + offset)]; 239 + 240 + /* 241 + Given an IP address and bit mask, return the associated CIDR. 242 + 243 + Type: ipAndBitMaskToCidr :: [ Int ] -> Int -> String 244 + 245 + Examples: 246 + ipAndBitMaskToCidr [ 192 168 70 9 ] 15 247 + => "192.168.70.9/15" 248 + */ 249 + ipAndBitMaskToCidr = addr: bitMask: 250 + lib.concatStringsSep "/" 251 + [ 252 + (prettyIp addr) 253 + (builtins.toString bitMask) 254 + ]; 255 + 256 + /* 257 + Given an IP address and subnet mask, return the associated CIDR. 258 + 259 + Type: ipAndSubnetMaskToCidr :: [ Int ] -> Int -> String 260 + 261 + Examples: 262 + ipAndSubnetMaskToCidr [ 192 168 70 9 ] [ 255 254 0 0 ] 263 + => "192.168.70.9/15" 264 + */ 265 + ipAndSubnetMaskToCidr = addr: subnetMask: 266 + ipAndBitMaskToCidr addr (subnetMaskToBitMask subnetMask); 267 + 268 + /* 269 + Given a CIDR, return an attribute set of: 270 + the IP Address, 271 + the bit mask, 272 + the first usable IP address, 273 + the last usable IP address, 274 + the network ID, 275 + the subnet mask, 276 + the broadcast address. 277 + 278 + Type: getNetworkProperties :: str -> attrset 279 + 280 + Examples: 281 + getNetworkProperties "192.168.70.9/15" 282 + => { 283 + bitMask = 15; 284 + broadcast = "192.169.255.255"; 285 + firstUsableIp = "192.168.0.1"; 286 + ipAddress = "192.168.70.9"; 287 + lastUsableIp = "192.169.255.254"; 288 + networkId = "192.168.0.0"; 289 + subnetMask = "255.254.0.0"; 290 + } 291 + */ 292 + getNetworkProperties = cidr: let 293 + ipAddress = prettyIp (cidrToIpAddress cidr); 294 + bitMask = cidrToBitMask cidr; 295 + firstUsableIp = prettyIp (cidrToFirstUsableIp cidr); 296 + lastUsableIp = prettyIp (cidrToLastUsableIp cidr); 297 + networkId = prettyIp (cidrToNetworkId cidr); 298 + subnetMask = prettyIp (cidrToSubnetMask cidr); 299 + broadcast = prettyIp (cidrToBroadcastAddress cidr); 300 + in {inherit ipAddress bitMask firstUsableIp lastUsableIp networkId subnetMask broadcast;}; 301 + in { 302 + ipv4 = { 303 + inherit 304 + prettyIp 305 + incrementIp 306 + bitMaskToSubnetMask 307 + subnetMaskToBitMask 308 + ipAndBitMaskToCidr 309 + ipAndSubnetMaskToCidr 310 + cidrToIpAddress 311 + cidrToBitMask 312 + cidrToFirstUsableIp 313 + cidrToLastUsableIp 314 + cidrToNetworkId 315 + cidrToSubnetMask 316 + cidrToBroadcastAddress 317 + getNetworkProperties 318 + ; 319 + }; 320 + }
+45 -29
modules/router/dnsmasq.nix
··· 2 2 3 3 with lib; 4 4 let 5 + inherit (import ../../lib/ipv4.nix inputs) ipv4; 6 + 5 7 cfg = config.modules.router; 8 + intern = cfg.interfaces.internal; 9 + extern = cfg.interfaces.external; 6 10 7 11 leaseType = types.submodule { 8 12 options = { ··· 22 26 else [ "1.1.1.1" "1.0.0.1" ]; 23 27 24 28 dhcpHost = builtins.map (lease: "${lease.macAddress},${lease.ipAddress}") cfg.dnsmasq.leases; 29 + 30 + dhcpIPv4Range = let 31 + subnetMask = ipv4.prettyIp (ipv4.cidrToSubnetMask intern.cidr); 32 + firstIP = ipv4.prettyIp (ipv4.incrementIp (ipv4.cidrToFirstUsableIp intern.cidr) 1); 33 + lastIP = ipv4.prettyIp (ipv4.cidrToLastUsableIp intern.cidr); 34 + in "${firstIP}, ${lastIP}, ${subnetMask}, 12h"; 35 + 36 + localDomains = builtins.map (host: "/${host}/${cfg.address}") cfg.dnsmasq.localDomains; 25 37 in { 26 38 options.modules.router = { 27 39 dnsmasq = { ··· 31 43 type = types.bool; 32 44 }; 33 45 34 - leases = lib.mkOption { 46 + leases = mkOption { 35 47 default = []; 36 - type = lib.types.listOf leaseType; 48 + type = types.listOf leaseType; 37 49 description = "List of reserved IP address leases"; 38 50 }; 51 + 52 + localDomains = lib.mkOption { 53 + default = []; 54 + type = types.listOf types.str; 55 + }; 39 56 }; 40 57 }; 41 58 42 59 config = mkIf cfg.dnsmasq.enable { 60 + modules.router.dnsmasq.localDomains = [ 61 + "time.apple.com" 62 + "time1.apple.com" 63 + "time2.apple.com" 64 + "time3.apple.com" 65 + "time4.apple.com" 66 + "time5.apple.com" 67 + "time6.apple.com" 68 + "time7.apple.com" 69 + "time.euro.apple.com" 70 + "time.windows.com" 71 + "0.android.pool.ntp.org" 72 + "1.android.pool.ntp.org" 73 + "2.android.pool.ntp.org" 74 + "3.android.pool.ntp.org" 75 + ]; 76 + 43 77 networking.nameservers = [ "127.0.0.1" ]; 44 78 45 79 services.resolved.extraConfig = mkDefault '' ··· 69 103 addn-hosts = "/etc/hosts"; 70 104 71 105 dhcp-range = [ 72 - "10.0.0.2, 10.0.0.255, 255.255.255.0, 12h" 73 - "tag:${cfg.interfaces.internal}, ::1, constructor:${cfg.interfaces.internal}, ra-names, slaac, 12h" 106 + dhcpIPv4Range 107 + "tag:${intern.name}, ::1, constructor:${intern.name}, ra-names, slaac, 12h" 74 108 ]; 75 109 76 110 dhcp-option = [ 77 111 "option6:information-refresh-time, 6h" 78 - "option:router,10.0.0.1" 79 - "ra-param=${cfg.interfaces.internal},high,0,0" 80 - ]; 81 - 82 - dhcp-option = mkIf cfg.timeserver.enable [ 83 - "option:ntp-server,10.0.0.1" 84 - ]; 112 + "option:router,${cfg.address}" 113 + "ra-param=${intern.name},high,0,0" 114 + ] ++ ( 115 + if cfg.timeserver.enable then [ "option:ntp-server,${cfg.address}" ] else [] 116 + ); 85 117 86 118 dhcp-host = dhcpHost; 87 119 88 120 # listen only on intern0 by excluding extern0 89 - except-interface = cfg.interfaces.external; 121 + except-interface = extern.name; 90 122 91 123 # set the DHCP server to authoritative and rapic commit mode 92 124 dhcp-authoritative = true; ··· 95 127 # Detect attempts by Verisign to send queries to unregistered hosts 96 128 bogus-nxdomain = "64.94.110.11"; 97 129 98 - address = [ 99 - "/cola.fable-pancake.ts.net/10.0.0.1" 100 - "/time.apple.com/10.0.0.1" 101 - "/time1.apple.com/10.0.0.1" 102 - "/time2.apple.com/10.0.0.1" 103 - "/time3.apple.com/10.0.0.1" 104 - "/time4.apple.com/10.0.0.1" 105 - "/time5.apple.com/10.0.0.1" 106 - "/time6.apple.com/10.0.0.1" 107 - "/time7.apple.com/10.0.0.1" 108 - "/time.euro.apple.com/10.0.0.1" 109 - "/time.windows.com/10.0.0.1" 110 - "/0.android.pool.ntp.org/10.0.0.1" 111 - "/1.android.pool.ntp.org/10.0.0.1" 112 - "/2.android.pool.ntp.org/10.0.0.1" 113 - "/3.android.pool.ntp.org/10.0.0.1" 114 - ]; 130 + address = localDomains; 115 131 }; 116 132 }; 117 133 };
+26 -16
modules/router/network.nix
··· 1 - { lib, config, ... }: 1 + { lib, config, ... } @ inputs: 2 2 3 3 with lib; 4 4 let 5 + inherit (import ../../lib/ipv4.nix inputs) ipv4; 6 + 5 7 cfg = config.modules.router; 6 8 7 9 interfaceType = types.submodule { ··· 14 16 type = types.str; 15 17 example = "00:00:00:00:00:00"; 16 18 }; 19 + cidr = mkOption { 20 + type = types.str; 21 + default = "0.0.0.0/0"; 22 + example = "10.0.0.1/24"; 23 + }; 17 24 }; 18 25 }; 19 26 20 - extern0 = cfg.interfaces.external.name; 21 - extern0MAC = cfg.interfaces.external.macAddress; 22 - intern0 = cfg.interfaces.internal.name; 23 - intern0MAC = cfg.interfaces.internal.macAddress; 27 + extern = cfg.interfaces.external; 28 + intern = cfg.interfaces.internal; 24 29 in { 25 30 options.modules.router = { 31 + address = { 32 + type = types.str; 33 + default = ipv4.prettyIp (ipv4.cidrToIpAddress intern.cidr); 34 + example = "127.0.0.1"; 35 + }; 26 36 interfaces = { 27 37 external = interfaceType; 28 38 internal = interfaceType; ··· 32 42 config = mkIf cfg.enable { 33 43 services.irqbalance.enable = true; 34 44 35 - networking.firewall.trustedInterfaces = [ "lo" intern0 ]; 45 + networking.firewall.trustedInterfaces = [ "lo" intern.name ]; 36 46 37 47 systemd.network = { 38 48 enable = true; 39 49 40 - links."10-${extern0}" = { 41 - matchConfig.PermanentMACAddress = extern0MAC; 50 + links."10-${extern.name}" = { 51 + matchConfig.PermanentMACAddress = extern.macAddress; 42 52 linkConfig = { 43 53 Description = "External Network Interface"; 44 - Name = extern0; 54 + Name = extern.name; 45 55 # MACAddress = "64:20:9f:16:70:a6"; 46 56 MTUBytes = "1500"; 47 57 }; 48 58 }; 49 59 50 - links."11-${intern0}" = { 51 - matchConfig.PermanentMACAddress = intern0MAC; 60 + links."11-${intern.name}" = { 61 + matchConfig.PermanentMACAddress = intern.macAddress; 52 62 linkConfig = { 53 63 Description = "Internal Network Interface"; 54 - Name = intern0; 64 + Name = intern.name; 55 65 MTUBytes = "1500"; 56 66 }; 57 67 }; 58 68 59 - networks."10-${extern0}" = { 69 + networks."10-${extern.name}" = { 60 70 name = extern0; 61 71 networkConfig = { 62 72 DHCP = "ipv4"; ··· 70 80 }; 71 81 }; 72 82 73 - networks."11-${intern0}" = { 74 - name = intern0; 83 + networks."11-${intern.name}" = { 84 + name = intern.name; 75 85 networkConfig = { 76 - Address = "10.0.0.1/24"; 86 + Address = cfg.address; 77 87 DHCPServer = false; 78 88 IPForward = true; 79 89 ConfigureWithoutCarrier = true;
+6 -4
modules/router/upnp.nix
··· 3 3 with lib; 4 4 let 5 5 cfg = config.modules.router; 6 + extern = cfg.interfaces.external; 7 + intern = cfg.interfaces.internal; 6 8 in { 7 9 options.modules.router = { 8 10 upnp = { ··· 19 21 enable = true; 20 22 upnp = true; 21 23 natpmp = true; 22 - internalIPs = [ cfg.interfaces.internal.name ]; 23 - externalInterface = cfg.interfaces.external.name; 24 + internalIPs = [ intern.name ]; 25 + externalInterface = extern.name; 24 26 appendConfig = '' 25 27 secure_mode=yes 26 28 notify_interval=60 27 29 clean_ruleset_interval=600 28 30 uuid=78b8b903-83c1-4036-8fcd-f64aee25baca 29 - allow 1024-65535 10.0.0.0/24 1024-65535 30 - deny 0-65535 0.0.0.0/0 0-65535 31 + allow 1024-65535 ${intern.cidr} 1024-65535 32 + deny 0-65535 ${extern.cidr} 0-65535 31 33 ''; 32 34 }; 33 35 };
+4 -1
modules/server/tailscale.nix
··· 4 4 let 5 5 cfgRoot = config.modules.server; 6 6 cfg = config.modules.server.tailscale; 7 + address = config.modules.router.address; 7 8 in { 8 9 options.modules.server.tailscale = { 9 10 enable = mkOption { ··· 20 21 }; 21 22 22 23 config = mkIf cfg.enable && cfgRoot.enable { 24 + modules.router.dnsmasq.localDomains = [ "${hostname}.fable-pancake.ts.net" ]; 25 + 23 26 networking = { 24 27 domain = "fable-pancake.ts.net"; 25 28 firewall.trustedInterfaces = [ "tailscale0" ]; 26 - hosts."10.0.0.1" = [ "${hostname}.fable-pancake.ts.net" hostname ]; 29 + hosts."${address}" = [ "${hostname}.fable-pancake.ts.net" hostname ]; 27 30 }; 28 31 29 32 age.secrets."tailscale" = {