{ pkgs, options, lib, ... }: let inherit (lib) getExe getExe' genAttrs; # we use tailscale as the main network provider # for multiple essential services. # it's important that tailscale starts up prior # to them. vitalServices = [ "sshd" "docker" "nginx" ]; # all metrics exporters are exposed through the tailnet, # they also need to wait for the tailscale0 interface to be up. # note that we modify all existing monitoringServices = map (e: "prometheus-${e}-exporter") ( builtins.attrNames options.services.prometheus.exporters.value ); servicesNeedingTailscale = vitalServices ++ monitoringServices; serviceConfigAfterTailscale = { after = [ "tailscaled.service" ]; }; in { # start tailscale :) services.tailscale = { enable = true; useRoutingFeatures = "both"; extraUpFlags = [ "--ssh" ]; }; # tailscale0 is the only interface that is allowed # to fully bypass the firewall. networking.firewall.trustedInterfaces = [ "tailscale0" ]; systemd.services = { # the tailscaled systemd service is # too eager in marking itself ready, # so we need to ensure it's actually # up before proceeding with other units. # see: https://github.com/tailscale/tailscale/issues/11504 "tailscaled".postStart = with pkgs; '' echo "Waiting for tailscale0 to come online..." for try in in {1..30}; do if ${getExe' iproute2 "ip"} addr show dev tailscale0 | ${getExe gnugrep} -q 'inet '; then echo "tailscale0 is up!" break fi sleep 1 done ''; } // genAttrs servicesNeedingTailscale (x: serviceConfigAfterTailscale); }