summary refs log tree commit diff stats
path: root/system/nanopi.nix
diff options
context:
space:
mode:
Diffstat (limited to 'system/nanopi.nix')
-rwxr-xr-xsystem/nanopi.nix320
1 files changed, 193 insertions, 127 deletions
diff --git a/system/nanopi.nix b/system/nanopi.nix
index 45eae872..5083f9e7 100755
--- a/system/nanopi.nix
+++ b/system/nanopi.nix
@@ -1,16 +1,19 @@
 { config
 , pkgs
 , lib
-, inputs
 , ...
 }:
 let
   fsTypes = [ "f2fs" "ext" "exfat" "vfat" ];
+  domain = "home.arpa";
+  ts_domain = "hydra-pinecone.ts.net";
 in
 {
   imports = [
     ./nanopi-hardware.nix
-    (inputs.nixos-hardware + "/friendlyarm/nanopi-r5s")
+    <agenix/modules/age.nix>
+    <nixos-hardware/friendlyarm/nanopi-r5s>
+    <home-manager/nixos>
   ];
 
   age.secrets = {
@@ -68,6 +71,26 @@ in
     };
   };
 
+  systemd.services.backup-golink = {
+    enable = true;
+    startAt = "daily";
+    description = "Export short links from golink";
+    path = with pkgs; [ curl gitMinimal ];
+    script = ''
+      [ -d golink ] || git init --quiet golink --initial-branch=main --shared=world
+      git config --global user.email linde@alanpearce.eu
+      cd golink
+      curl https://go.${ts_domain}/.export > links.json
+      git add links.json
+      git commit -m $(date +%F)
+    '';
+    serviceConfig = {
+      Type = "oneshot";
+      User = "linde";
+      WorkingDirectory = config.users.users.linde.home;
+    };
+  };
+
   services.journald.extraConfig = ''
     MaxRetentionSec=1 month
   '';
@@ -85,61 +108,33 @@ in
   systemd.network.config.networkConfig = {
     SpeedMeter = true;
   };
+
   networking = {
     hostName = "nanopi";
-    domain = "lan";
+    domain = domain;
+    search = [ domain ];
+    hosts = {
+      "fd7a:115c:a1e0::53" = [ "tailscale" "ts" ];
+      "192.168.100.1" = [ "modem" "pyur" ];
+      "192.168.4.1" = [ "lte" ];
+    };
     useDHCP = false;
     useNetworkd = true;
-    nameservers = [
-      "176.9.93.198"
-      "176.9.1.117"
-      "2a01:4f8:151:34aa::198"
-      "2a01:4f8:141:316d::117"
-    ];
+    nat = {
+      enable = true;
+      internalInterfaces = [ "bridge0" "lan1" "lan2" ];
+      externalInterface = "wan0";
+    };
     firewall = {
       enable = true;
       rejectPackets = true;
       logRefusedConnections = false;
       pingLimit = "5/second";
       filterForward = true; # we are a router
-      allowedUDPPorts = [
-        53
-        123
-      ];
-      allowedTCPPorts = [
-        53
-        123
-        80
-        443
+      trustedInterfaces = [
+        "bridge0"
+        "tailscale0"
       ];
-      interfaces.bridge0 = {
-        allowedTCPPorts = [
-          53
-          67
-          139
-          445
-          1883
-          3000
-          3689
-          5357
-          5533 # SmartDNS
-          8096
-          9091 # Transmission
-        ];
-        allowedUDPPorts = [
-          53
-          67
-          69
-          137
-          4011 # PXE
-          5533 # SmartDNS
-          5353
-          5355 # LLMNR
-          3702 # Samba WSDD
-          41641
-          51827
-        ];
-      };
       interfaces.wan0 = {
         allowedTCPPorts = [
           6980 # aria2c
@@ -151,8 +146,10 @@ in
         ];
       };
       extraForwardRules = ''
-        iifname { "wan0", "wlan0", "wwan0" } oifname { "lan1", "lan2", "bridge0" } icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
-        iifname { "lan1", "lan2", "bridge0" } oifname { "wan0", "wlan0", "wwan0" } accept
+        iifname { "wlan0", "lte0" } oifname { "lan1", "lan2", "bridge0" } icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
+        iifname { "lan1", "lan2", "bridge0" } oifname { "wlan0", "lte0" } accept
+        iifname "tailscale0" oifname "bridge0" accept
+        iifname "bridge0" oifname "tailscale0" accept
       '';
     };
     nftables = {
@@ -163,7 +160,7 @@ in
           content = ''
             chain postrouting {
               type nat hook postrouting priority srcnat; policy accept;
-              oifname { "wan0", "wlan0", "wwan0" } masquerade
+              oifname { "wlan0", "lte0" } masquerade
             }
             chain prerouting {
               type nat hook prerouting priority dstnat;
@@ -185,7 +182,57 @@ in
       # };
     };
   };
-  services.resolved.enable = false;
+
+  networking = {
+    resolvconf = {
+      # having this enabled (the default) is pointless
+      # a) this device has fixed upstream nameservers
+      enable = false;
+      # b) it makes tailscale think it should change the search domains for MagicDNS
+      # ... due to this:
+      # useLocalResolver = false;
+      # which is set by kresd?!
+      # https://github.com/NixOS/nixpkgs/blob/7780e5160e011b39019797a4c4b1a4babc80d1bf/nixos/modules/services/networking/kresd.nix#L113
+    };
+    nameservers = lib.optionals config.services.dnsmasq.enable [
+      "::1"
+      "127.0.0.1"
+    ];
+  };
+  services.resolved = {
+    # this allows link-specific DNS configuration, which is useful.
+    enable = true;
+    # why use simple boolean when string do trick?
+    llmnr = "false";
+    dnssec = "true";
+    fallbackDns = [
+      "9.9.9.9"
+      "149.112.112.112"
+      "2620::fe:fe"
+      "2620::fe:9"
+      "116.203.248.56"
+      "2a01:4f8:c012:23a4::1"
+    ];
+  };
+
+  # leaving this here just in case I ever think about disabling both `resolvconf` and `resolved`
+  # I thought that there would have been a fallback that does this anyway, but apparently not.
+  environment.etc."resolv.conf".text = lib.mkDefault (lib.optionalString
+    (
+      !config.networking.resolvconf.enable
+      &&
+      !config.services.resolved.enable
+    ) ''
+    search ${domain} ${ts_domain}
+    nameserver ::1
+    nameserver 127.0.0.1
+    options edns0
+  '');
+
+  services.tailscale = {
+    enable = true;
+    extraUpFlags = [ "--accept-dns=false" "--advertise-routes=10.0.0.0/20,fd12:d04f:65d:42::/56" ];
+  };
 
   programs.command-not-found.enable = false;
 
@@ -193,6 +240,10 @@ in
     enable = true;
     openFirewall = true;
     startWhenNeeded = false;
+    settings = {
+      PasswordAuthentication = false;
+      KbdInteractiveAuthentication = false;
+    };
   };
   programs.mosh.enable = true;
   services.sshguard = {
@@ -203,7 +254,7 @@ in
   systemd.network = {
     enable = true;
     wait-online = {
-      ignoredInterfaces = [ "wan0" "wlan0" "wwan0" ];
+      extraArgs = [ "--interface" "bridge0" ];
     };
     links = {
       "10-name-lan1" = {
@@ -233,10 +284,10 @@ in
           Name = "wlan0";
         };
       };
-      "10-name-wwan0" = {
+      "10-name-lte0" = {
         matchConfig.MACAddress = "34:4b:50:00:00:00";
         linkConfig = {
-          Name = "wwan0";
+          Name = "lte0";
         };
       };
     };
@@ -254,7 +305,6 @@ in
         bridge = [ "bridge0" ];
         linkConfig = {
           MACAddress = "82:E0:06:9C:8E:7C";
-          RequiredForOnline = "no";
         };
         networkConfig.LinkLocalAddressing = "no";
       };
@@ -265,11 +315,20 @@ in
           "10.0.0.1/20"
           "fd12:d04f:65d:42::1/56"
         ];
+        addresses = [
+          {
+            Address = "fe80::1/64";
+            Scope = "link";
+          }
+        ];
         networkConfig = {
           IPv6AcceptRA = false;
-          IPv6SendRA = true;
+          IPv6SendRA = false;
           DHCPPrefixDelegation = true;
           ConfigureWithoutCarrier = true;
+          MulticastDNS = true;
+          BindCarrier = [ "lan0" "lan1" ];
+          Domains = [ domain ];
         };
         dhcpPrefixDelegationConfig = {
           UplinkInterface = "wan0";
@@ -277,17 +336,9 @@ in
           Assign = true;
           Token = "::1";
         };
-        ipv6SendRAConfig = {
-          RouterLifetimeSec = 1800;
-          EmitDNS = true;
-          DNS = "fd12:d04f:65d:42::1";
-          EmitDomains = true;
-          Domains = [ config.networking.domain ];
-        };
       };
-      "50-wwan0" = {
-        matchConfig.Name = "wwan0";
-        linkConfig.RequiredForOnline = false;
+      "50-lte0" = {
+        matchConfig.Name = "lte0";
         networkConfig = {
           DHCP = "yes";
           IPv6AcceptRA = true;
@@ -296,17 +347,16 @@ in
         dhcpV4Config = {
           UseDNS = false;
           SendHostname = false;
-          RouteMetric = 2048;
+          UseRoutes = false;
         };
         ipv6AcceptRAConfig.UseDNS = false;
         routes = [
           {
-            routeConfig = {
-              Gateway = "_dhcp4";
-              QuickAck = true;
-              InitialCongestionWindow = 30;
-              InitialAdvertisedReceiveWindow = 30;
-            };
+            Gateway = "_dhcp4";
+            Metric = 2048;
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -320,7 +370,6 @@ in
       };
       "50-wan" = {
         matchConfig.Name = "wan0";
-        linkConfig.RequiredForOnline = "no";
         networkConfig = {
           DHCP = "yes";
           IPv6AcceptRA = true;
@@ -328,6 +377,7 @@ in
         };
         dhcpV4Config = {
           UseDNS = false;
+          UseRoutes = false;
           SendHostname = false;
           SendRelease = false;
           UseHostname = false;
@@ -343,15 +393,28 @@ in
         };
         ipv6AcceptRAConfig = {
           UseDNS = false;
+          UseGateway = false;
         };
         addresses = [
           {
-            addressConfig = {
-              Address = "192.168.100.10/24";
-              # Peer = "192.168.100.1/32";
-              Label = "wan0:0";
-              # Scope = "link";
-            };
+            Address = "192.168.100.10/24";
+            # Peer = "192.168.100.1/32";
+            Label = "wan0:0";
+            # Scope = "link";
+          }
+        ];
+        routes = [
+          {
+            Gateway = "_dhcp4";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
+          }
+          {
+            Gateway = "_ipv6ra";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -365,7 +428,6 @@ in
       };
       "60-wlan" = {
         matchConfig.MACAddress = "9c:53:22:33:bf:e9";
-        linkConfig.RequiredForOnline = "no";
         networkConfig = {
           DHCP = "yes";
           IPForward = "yes";
@@ -380,13 +442,11 @@ in
         };
         routes = [
           {
-            routeConfig = {
-              Metric = 2048;
-              Gateway = "_dhcp4";
-              QuickAck = true;
-              InitialCongestionWindow = 30;
-              InitialAdvertisedReceiveWindow = 30;
-            };
+            Metric = 2048;
+            Gateway = "_dhcp4";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -415,12 +475,13 @@ in
 
   services.dnsmasq = {
     enable = true;
-    resolveLocalQueries = true;
+    # let systemd-resolved.do this
+    resolveLocalQueries = false;
     alwaysKeepRunning = true;
     settings = {
       local-ttl = 60;
-      domain = "lan";
-      dhcp-fqdn = false;
+      domain = domain;
+      dhcp-fqdn = true;
       domain-needed = true;
       bogus-priv = true;
       no-resolv = true;
@@ -434,34 +495,42 @@ in
         "2620::fe:9"
         "116.203.248.56"
         "2a01:4f8:c012:23a4::1"
-        # "127.0.0.1#5553"
-        # "::1#5553"
-        "127.0.0.1#5533"
-        "::1#5533"
+        # kresd
+        "127.0.0.1#5553"
+        "::1#5553"
+        # smartdns
+        # "127.0.0.1#5533"
+        # "::1#5533"
+        "/ts.net/tailscale"
       ];
       localise-queries = true;
       cname = [
-        "homeassistant,ha"
+        "ha,home-assistant"
       ];
       interface-name = [
-        "home.alanpearce.eu,wan0"
-        "nanopi.alanpearce.eu,wan0"
-        "nanopi.lan.alanpearce.eu,bridge0"
-        "syncthing.lan.alanpearce.eu,bridge0"
-        "wan,wan0"
-        "wlan,wlan0"
-        "wwan,wwan0"
+        "nanopi.${domain},bridge0"
+        "wan.${domain},wan0"
+        "wlan.${domain},wlan0"
       ];
       interface = [
+        "lo"
         "bridge0"
       ];
+      no-dhcp-interface = [
+        "tailscale0"
+      ];
       # auth-zone = "lan,wan0";
       # auth-server = [
       #   "nanopi.alanpearce.eu,wan0"
       # ];
-      bind-interfaces = false;
+      bind-interfaces = true;
 
-      no-hosts = true;
+      # if this is false, a remote query for nanopi returns 127.0.0.2, because that's in /etc/hosts
+      no-hosts = false;
+      expand-hosts = true;
+
+      dnssec = true;
+      trust-anchor = ".,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D";
 
       enable-ra = true;
       dhcp-lease-max = 240;
@@ -469,19 +538,18 @@ in
       dhcp-rapid-commit = true;
       dhcp-range = [
         "10.0.1.0,10.0.1.250,12h"
-        "::, constructor:bridge0, ra-stateless, 48h"
-        "fd12:d04f:65d::, ra-stateless, ra-names, 48h"
+        "fd12:d04f:65d:42::,slaac,ra-names,48h"
+        "::,constructor:bridge0,ra-stateless,48h"
       ];
       dhcp-host = [
         "00:a0:de:b3:0c:01,10.0.0.50,wxa-50"
         "10:f0:68:12:b1:e0,10.0.0.11,Ruckus"
         "9c:93:4e:ad:05:c8,10.0.0.210,xerox-b210"
         "00:08:9b:f5:b8:25,10.0.0.42,dontpanic"
-        "d8:3a:dd:34:85:cc,d8:3a:dd:34:85:cd,10.0.0.81,ha"
+        "d8:3a:dd:34:85:cc,d8:3a:dd:34:85:cd,10.0.0.81,home-assistant"
       ];
       dhcp-option = [
         "option:ntp-server,0.0.0.0"
-        "option:dns-server,0.0.0.0,10.0.0.81"
         "option:tftp-server,0.0.0.0"
         "option:ip-forward-enable,0" # ip-forwarding
         "252,\"\\n\""
@@ -512,7 +580,7 @@ in
 
   services.networkd-dispatcher = {
     # broken?
-    enable = false;
+    enable = true;
     rules = {
       update-home-address = {
         onState = [ "configured" "configuring" ];
@@ -527,6 +595,18 @@ in
           exit 0
         '';
       };
+      tailscale-subnet-router-optimisation = {
+        onState = [ "routable" ];
+        script = ''
+          #!${pkgs.runtimeShell}
+          set -eu
+
+          if [[ $IFACE == "wan0" && $OperationalState == "routable" ]]
+          then
+            ${pkgs.ethtool}/bin/ethtool -K $IFACE rx-udp-gro-forwarding on rx-gro-list off
+          fi
+        '';
+      };
     };
   };
 
@@ -555,6 +635,7 @@ in
       "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMvcW4Z9VxOQgEJjsRC1uSMwEJ4vru9BwjT+Z50nawp4 lan"
     ];
   };
+  home-manager.users.alan = import ../user/nanopi.nix;
 
   users.groups = {
     linde.members = [ ];
@@ -566,6 +647,7 @@ in
       isSystemUser = true;
       shell = "/bin/sh";
       home = "/srv/backup/linde";
+      homeMode = "755";
       createHome = true;
       packages = with pkgs; [ rdiff-backup ];
       openssh.authorizedKeys.keys = [
@@ -614,7 +696,7 @@ in
   };
   nixpkgs.config.allowUnfree = true;
   system.autoUpgrade = {
-    enable = false;
+    enable = true;
     dates = "04:15";
     randomizedDelaySec = "59 min";
     flake = "git+https://git.alanpearce.eu/nixfiles";
@@ -700,22 +782,6 @@ in
     '';
   };
 
-  services.avahi = {
-    enable = true;
-    nssmdns4 = true;
-    denyInterfaces = [ "wan0" "wwan0" "wlan0" ];
-    browseDomains = [
-      "alanpearce.eu"
-    ];
-    publish = {
-      enable = true;
-      hinfo = true;
-      addresses = true;
-      userServices = true;
-      workstation = true;
-    };
-  };
-
   services.samba = {
     enable = true;
     enableNmbd = false;
@@ -786,7 +852,7 @@ in
         "10.0.0.1:53 -group lan -exclude-default-group"
       ];
       nameserver = [
-        "/lan/lan"
+        "/${domain}/${domain}"
       ];
       dualstack-ip-selection = true;
       dualstack-ip-selection-threshold = 10;