summary refs log tree commit diff
path: root/modules/foundation/tailnet.nix
blob: 404c5ad7a042bb2266b7aee1ce9447b964752214 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
{
  config,
  pkgs,
  lib,
  ...
}:

let
  inherit (lib) mkOption types;
  inherit (pkgs) tailscale;

  cfg = config.foundation;

  tailnet-wait-for-ip = pkgs.writeShellScriptBin "tailnet-wait-for-ip.sh" ''
    echo "waiting for tailscale to acquire tailnet ip address..."

    while ! ${lib.getExe tailscale} ip -1; do
      echo "tailnet ip not yet available... sleeping for 1 second."
      sleep 1
    done

    ip=$(${lib.getExe tailscale} ip -1)
    echo "acquired tailnet address! ip: $ip"
    exit 0
  '';
in
{
  options.foundation = {
    tailnetServices = mkOption {
      type = with types; listOf str;

      default = [ ];
      example = [ "nginx" ];

      description = ''
        services that depend on the tailnet.
        will be launched only after tailscaled.service
        is fully up and online.
      '';
    };
  };

  config =
    let
      tailnetWaitOnlineService = {
        enable = true;

        after = [
          "tailscaled.service"
          "network.target"
        ];
        # kill service if tailscaled dies.
        bindsTo = [ "tailscaled.service" ];

        serviceConfig = {
          # unit is marked as active after script exits.
          Type = "oneshot";
          RemainAfterExit = true;
          # consider connection failed after 3 minutes.
          TimeoutStartSec = "3m";

          ExecStart = "${tailnet-wait-for-ip}/bin/tailnet-wait-for-ip.sh";
        };

        description = ''
          wait for tailscale device to be online and ready
        '';
      };

      tailnetOnlineTarget = {
        enable = true;

        after = [
          "tailnet-wait-online.service"
          "tailscaled.service"
        ];
        requires = [ "tailnet-wait-online.service" ];
        bindsTo = [ "tailscaled.service" ];

        description = "tailnet is online";
      };

      tailnetDependantUnit = {
        after = [ "tailnet-online.target" ];
        requires = [ "tailnet-online.target" ];
      };

      tailnetDependantServices = lib.genAttrs cfg.tailnetServices (x: tailnetDependantUnit);
    in
    {
      systemd = {
        services = {
          "tailnet-wait-online" = tailnetWaitOnlineService;
        } // tailnetDependantServices;

        targets = {
          "tailnet-online" = tailnetOnlineTarget;
        };
      };
    };
}