From 84c39b405972516a9be2e8be8729097f8691050f Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Fri, 17 Oct 2025 08:31:18 +0530 Subject: chore(os/kay): refactor --- os/kay/modules/network/default.nix | 72 +++++++++++++++++++ os/kay/modules/network/headscale.nix | 112 +++++++++++++++++++++++++++++ os/kay/modules/network/hurricane.nix | 132 +++++++++++++++++++++++++++++++++++ os/kay/modules/network/router.nix | 72 +++++++++++++++++++ os/kay/modules/network/wireguard.nix | 71 +++++++++++++++++++ 5 files changed, 459 insertions(+) create mode 100644 os/kay/modules/network/default.nix create mode 100644 os/kay/modules/network/headscale.nix create mode 100644 os/kay/modules/network/hurricane.nix create mode 100644 os/kay/modules/network/router.nix create mode 100644 os/kay/modules/network/wireguard.nix (limited to 'os/kay/modules/network') diff --git a/os/kay/modules/network/default.nix b/os/kay/modules/network/default.nix new file mode 100644 index 0000000..56371c7 --- /dev/null +++ b/os/kay/modules/network/default.nix @@ -0,0 +1,72 @@ +{ config, ... }: + +let + inetVlan = 1003; + wanInterface = "enp3s0"; + nameServer = [ + "1.0.0.1" + "1.1.1.1" + ]; +in +{ + imports = [ + ./router.nix + ./hurricane.nix + ./wireguard.nix + ./headscale.nix + ]; + + sops.secrets = { + "ppp/chap-secrets" = { }; + "ppp/pap-secrets" = { }; + "ppp/username" = { }; + }; + + networking = { + tempAddresses = "disabled"; + vlans.wan = { + id = inetVlan; + interface = wanInterface; + }; + }; + + services = { + dnsmasq = { + enable = true; + settings = { + server = nameServer; + bind-interfaces = true; + }; + }; + + pppd = { + enable = true; + + config = '' + plugin pppoe.so + debug + + nic-wan + defaultroute + ipv6 ::1, + noauth + + persist + lcp-echo-adaptive + lcp-echo-interval 1 + lcp-echo-failure 5 + ''; + + peers.keralavision = { + enable = true; + autostart = true; + configFile = config.sops.secrets."ppp/username".path; + }; + + secret = { + chap = config.sops.secrets."ppp/chap-secrets".path; + pap = config.sops.secrets."ppp/pap-secrets".path; + }; + }; + }; +} diff --git a/os/kay/modules/network/headscale.nix b/os/kay/modules/network/headscale.nix new file mode 100644 index 0000000..24df170 --- /dev/null +++ b/os/kay/modules/network/headscale.nix @@ -0,0 +1,112 @@ +{ + config, + pkgs, + lib, + ... +}: +let + domain = "headscale.${config.global.userdata.domain}"; + stunPort = 3478; + + # A workaround generate a valid Headscale config accepted by Headplane when `config_strict == true`. + settings = lib.recursiveUpdate config.services.headscale.settings { + tls_cert_path = "/dev/null"; + tls_key_path = "/dev/null"; + policy.path = "/dev/null"; + }; + format = pkgs.formats.yaml { }; + headscaleConfig = format.generate "headscale.yml" settings; + + policyFormat = pkgs.formats.json { }; + policy = { + groups = { + "group:owner" = [ "sinan@" ]; + "group:bud" = [ + "sinan@" + "ann@" + ]; + }; + tagOwners = { + "tag:bud_clients" = [ "group:bud" ]; + "tag:internal" = [ "group:owner" ]; + "tag:cusat" = [ "group:owner" ]; + "tag:gaijin" = [ "group:owner" ]; + }; + acls = [ + { + action = "accept"; + src = [ "group:owner" ]; + dst = [ "*:*" ]; + } + + { + action = "accept"; + src = [ "group:bud" ]; + dst = [ "tag:bud_clients:*" ]; + } + ]; + }; +in +{ + sops.secrets = { + "headplane/cookie_secret".owner = config.services.headscale.user; + "headplane/preauth_key".owner = config.services.headscale.user; + "headscale/noise_private_key".owner = config.services.headscale.user; + "headscale/derp_private_key".owner = config.services.headscale.user; + }; + + networking.firewall.interfaces.ppp0.allowedUDPPorts = [ stunPort ]; + + services = { + headscale = { + enable = true; + port = 8139; + + settings = { + logtail.enabled = false; + server_url = "https://${domain}"; + noise.private_key_path = config.sops.secrets."headscale/noise_private_key".path; + dns = { + base_domain = "tsnet.${config.global.userdata.domain}"; + override_local_dns = false; + }; + derp = { + server = { + enabled = true; + private_key_path = config.sops.secrets."headscale/derp_private_key".path; + region_code = config.networking.hostName; + region_name = config.networking.hostName; + stun_listen_addr = "0.0.0.0:${toString stunPort}"; + region_id = 6969; + automatically_add_embedded_derp_region = true; + }; + urls = [ ]; + }; + policy = { + mode = "file"; + path = policyFormat.generate "acl.json" policy; + }; + }; + }; + + headplane = { + enable = true; + settings = { + server = { + port = 8140; + cookie_secret_path = config.sops.secrets."headplane/cookie_secret".path; + }; + headscale = { + url = "https://${domain}"; + config_path = "${headscaleConfig}"; + }; + integration.agent = { + enabled = true; + pre_authkey_path = config.sops.secrets."headplane/preauth_key".path; + }; + }; + }; + }; + + environment.systemPackages = [ config.services.headscale.package ]; +} diff --git a/os/kay/modules/network/hurricane.nix b/os/kay/modules/network/hurricane.nix new file mode 100644 index 0000000..e815136 --- /dev/null +++ b/os/kay/modules/network/hurricane.nix @@ -0,0 +1,132 @@ +{ + config, + pkgs, + lib, + ... +}: + +let + iface = "hurricane"; + remote = "216.218.221.42"; + + clinet = "2001:470:35:72a::2"; + server = "2001:470:35:72a::1"; + + prefix64 = "2001:470:36:72a::/64"; + prefix48 = "2001:470:ee65::/48"; + + makeAddr = + prefix: host: + let + split = lib.strings.splitString "/" prefix; + in + { + address = "${lib.head split}${host}"; + prefixLength = lib.toInt (lib.last split); + }; +in +{ + networking = { + sits.${iface} = { + inherit remote; + ttl = 225; + }; + interfaces.${iface} = { + mtu = 1472; # 1492(ppp0) - 20 + ipv6.addresses = [ + { + address = clinet; + prefixLength = 64; + } + + (makeAddr prefix64 "1") + (makeAddr prefix48 "1") + (makeAddr prefix48 "1337") + ]; + }; + + iproute2 = { + enable = true; + rttablesExtraConfig = "200 hurricane"; + }; + + firewall = { + extraCommands = "iptables -A INPUT --proto 41 --source ${remote} --jump ACCEPT"; + extraStopCommands = "iptables -D INPUT --proto 41 --source ${remote} --jump ACCEPT"; + }; + }; + + sops.secrets = { + "hurricane/username" = { }; + "hurricane/update_key" = { }; + "hurricane/tunnel_id" = { }; + }; + + systemd.services."network-route-${iface}" = { + description = "Routing configuration of ${iface}"; + wantedBy = [ + "network-setup.service" + "network.target" + ]; + before = [ "network-setup.service" ]; + bindsTo = [ "network-addresses-hurricane.service" ]; + after = [ + "network-pre.target" + "network-addresses-hurricane.service" + ]; + # restart rather than stop+start this unit to prevent the + # network from dying during switch-to-configuration. + stopIfChanged = false; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + path = [ pkgs.iproute2 ]; + script = '' + echo -n "adding route" + + ip -6 rule add from ${clinet}/64 table hurricane || exit 1 + ip -6 rule add from ${prefix64} table hurricane || exit 1 + ip -6 rule add from ${prefix48} table hurricane || exit 1 + + ip -6 route add default via ${server} dev hurricane table hurricane || exit 1 + ''; + preStop = '' + echo -n "deleting route" + + ip -6 route del default via ${server} dev hurricane table hurricane || exit 1 + + ip -6 rule del from ${prefix48} table hurricane || exit 1 + ip -6 rule del from ${prefix64} table hurricane || exit 1 + ip -6 rule del from ${clinet}/64 table hurricane || exit 1 + ''; + }; + + services.pppd.script."01-${iface}" = { + runtimeInputs = with pkgs; [ + curl + coreutils + iproute2 + iputils + ]; + text = '' + wan_ip="$4" + username="$(cat ${config.sops.secrets."hurricane/username".path})" + update_key="$(cat ${config.sops.secrets."hurricane/update_key".path})" + tunnel_id="$(cat ${config.sops.secrets."hurricane/tunnel_id".path})" + + auth_url="https://$username:$update_key@ipv4.tunnelbroker.net/nic/update?hostname=$tunnel_id" + until curl --silent "$auth_url"; do + sleep 1 + done + + while [ ! -e /sys/class/net/${iface} ]; do + sleep 1 # make sure ${iface} is up + done + + ip tunnel change ${iface} local "$wan_ip" mode sit + ''; + }; +} diff --git a/os/kay/modules/network/router.nix b/os/kay/modules/network/router.nix new file mode 100644 index 0000000..aeb008c --- /dev/null +++ b/os/kay/modules/network/router.nix @@ -0,0 +1,72 @@ +{ ... }: +let + wanInterface = "ppp0"; + + gponInterface = "enp3s0"; + gponHost = "192.168.38.1"; + gponPrefix = 24; + + lanInterface = "enp8s0f3u1c2"; + bridgeInterface = "lan"; + subnet = "192.168.43.0"; + prefix = 24; + host = "192.168.43.1"; + leaseRangeStart = "192.168.43.100"; + leaseRangeEnd = "192.168.43.254"; + + wapMac = "40:86:cb:d7:40:49"; + wapIp = "192.168.43.2"; +in +{ + networking = { + bridges.${bridgeInterface}.interfaces = [ lanInterface ]; + + nat = { + enable = true; + externalInterface = wanInterface; + internalInterfaces = [ bridgeInterface ]; + }; + interfaces = { + ${bridgeInterface}.ipv4.addresses = [ + { + address = host; + prefixLength = prefix; + } + ]; + ${gponInterface}.ipv4.addresses = [ + { + address = gponHost; + prefixLength = gponPrefix; + } + ]; + }; + firewall = { + allowedUDPPorts = [ + 53 + 67 + ]; + allowedTCPPorts = [ 53 ]; + extraCommands = '' + iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ + -o ${wanInterface} \ + -j TCPMSS --clamp-mss-to-pmtu + ''; + extraStopCommands = '' + iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN \ + -o ${wanInterface} \ + -j TCPMSS --clamp-mss-to-pmtu + ''; + }; + }; + + services.dnsmasq.settings = { + dhcp-range = [ "${leaseRangeStart},${leaseRangeEnd}" ]; + dhcp-host = "${wapMac},${wapIp}"; + interface = [ bridgeInterface ]; + }; + + services.prometheus.exporters.dnsmasq = { + enable = true; + listenAddress = "127.0.0.1"; + }; +} diff --git a/os/kay/modules/network/wireguard.nix b/os/kay/modules/network/wireguard.nix new file mode 100644 index 0000000..fd00804 --- /dev/null +++ b/os/kay/modules/network/wireguard.nix @@ -0,0 +1,71 @@ +{ + config, + pkgs, + lib, + ... +}: +let + wgInterface = "wg"; + wanInterface = "ppp0"; + port = 51820; + + wgConf = pkgs.writeText "wg.conf" '' + [interface] + Address = 10.0.1.1/24 + MTU = 1412 + ListenPort = 51820 + PostUp = ${ + lib.getExe ( + pkgs.writeShellApplication { + name = "wg_set_key"; + runtimeInputs = with pkgs; [ wireguard-tools ]; + text = '' + wg set ${wgInterface} private-key <(cat ${config.sops.secrets."misc/wireguard".path}) + ''; + } + ) + } + + [Peer] + # friendly_name = cez + PublicKey = IcMpAs/D0u8O/AcDBPC7pFUYSeFQXQpTqHpGOeVpjS8= + AllowedIPs = 10.0.1.2/32 + + [Peer] + # friendly_name = exy + PublicKey = bJ9aqGYD2Jh4MtWIL7q3XxVHFuUdwGJwO8p7H3nNPj8= + AllowedIPs = 10.0.1.3/32 + + [Peer] + # friendly_name = dad + PublicKey = q70IyOS2IpubIRWqo5sL3SeEjtUy2V/PT8yqVExiHTQ= + AllowedIPs = 10.0.1.4/32 + ''; +in +{ + sops.secrets."misc/wireguard" = { }; + + networking = { + nat = { + enable = true; + externalInterface = wanInterface; + internalInterfaces = [ wgInterface ]; + }; + + firewall.allowedUDPPorts = [ port ]; + wg-quick.interfaces.${wgInterface}.configFile = builtins.toString wgConf; + }; + + services.dnsmasq.settings = { + no-dhcp-interface = wgInterface; + interface = [ wgInterface ]; + }; + + services.prometheus.exporters.wireguard = { + enable = true; + withRemoteIp = true; + wireguardConfig = builtins.toString wgConf; + singleSubnetPerField = true; + listenAddress = "127.0.0.1"; + }; +} -- cgit v1.2.3