summaryrefslogtreecommitdiff
path: root/os/kay/modules/network
diff options
context:
space:
mode:
Diffstat (limited to 'os/kay/modules/network')
-rw-r--r--os/kay/modules/network/default.nix72
-rw-r--r--os/kay/modules/network/headscale.nix112
-rw-r--r--os/kay/modules/network/hurricane.nix132
-rw-r--r--os/kay/modules/network/router.nix72
-rw-r--r--os/kay/modules/network/wireguard.nix71
5 files changed, 459 insertions, 0 deletions
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";
+ };
+}