aboutsummaryrefslogtreecommitdiff
path: root/nix/wgautomesh.nix
blob: 0e0b7b3d9e0b2d2fc06023c41911b407b8795a15 (plain) (blame)
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
let
  src = builtins.fetchGit {
    url = "https://git.deuxfleurs.fr/lx/wgautomesh";
    rev = "65e979de801daa5f6ef77ed875e6505aa902fd9c";
  };
  wgautomesh = (import src).packages.x86_64-linux.default;
in

{ lib, config, pkgs, ... }:
with lib;
let 
  cfg = config.services.wgautomesh;
in
  with builtins;
  {
    options.services.wgautomesh = {
      enable = mkEnableOption "wgautomesh";
      interface = mkOption {
        type = types.str;
        description = "Wireguard interface to manage";
      };
      gossipPort = mkOption {
        type = types.port;
        description = "wgautomesh gossip port";
      };
      gossipSecretFile = mkOption {
        type = types.nullOr types.str;
        description = "File containing the gossip secret encryption key";
      };
      persistFile = mkOption {
        type = types.nullOr types.str;
        description = "Path where to persist known peer addresses";
      };
      lanDiscovery = mkOption {
        type = types.bool;
        default = true;
        description = "Enable discovery using LAN broadcast";
      };
      openFirewall = mkOption {
        type = types.bool;
        default = true;
        description = "Automatically open gossip port in firewall";
      };
      upnpForwardPublicPort = mkOption {
        type = types.nullOr types.port;
        default = null;
        description = "Public port number to try to redirect to this machine using UPnP IGD";
      };
      peers = mkOption {
        type = types.listOf (types.submodule {
          options = {
            pubkey = mkOption {
              type = types.str;
              description = "Wireguard public key";
            };
            address = mkOption {
              type = types.str;
              description = "Wireguard peer address";
            };
            endpoint = mkOption {
              type = types.nullOr types.str;
              description = "bootstrap endpoint";
            };
          };
        });
        description = "wgautomesh peer list";
      };
    };

    config = mkIf cfg.enable ( 
    let
      peerDefs = map (peer:
        let endpointDef = if peer.endpoint == null then ""
        else ''endpoint = "${peer.endpoint}"'';
        in
        ''
          [[peers]]
          pubkey = "${peer.pubkey}"
          address = "${peer.address}"
          ${endpointDef}
        '') cfg.peers;
      extraDefs = (if cfg.lanDiscovery then ["lan_discovery = true"] else [])
        ++ (if (cfg.gossipSecretFile != null)
          then [''gossip_secret_file = "${cfg.gossipSecretFile}"''] else [])
        ++ (if (cfg.persistFile != null)
          then [''persist_file = "${cfg.persistFile}"''] else [])
        ++ (if (cfg.upnpForwardPublicPort != null)
          then [''upnp_forward_external_port = ${toString cfg.upnpForwardPublicPort}''] else []);
      configfile = pkgs.writeText "wgautomesh.toml" ''
        interface = "${cfg.interface}"
        gossip_port = ${toString cfg.gossipPort}
        ${concatStringsSep "\n" extraDefs}

        ${concatStringsSep "\n" peerDefs}
        '';
    in {
      systemd.services.wgautomesh = {
        enable = true;
        path = [ pkgs.wireguard-tools ];
        environment = {
          RUST_LOG = "wgautomesh=info";
        };
        description = "wgautomesh";
        serviceConfig = {
          Type = "simple";

          ExecStart = "${wgautomesh}/bin/wgautomesh ${configfile}";
          Restart = "always";
          RestartSec = "30";

          ExecStartPre = [ "+${pkgs.coreutils}/bin/chown wgautomesh /var/lib/wgautomesh/gossip_secret" ];

          DynamicUser = true;
          User = "wgautomesh";
          StateDirectory = "wgautomesh";
          StateDirectoryMode = "0700";
          AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
          CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
        };
        wantedBy = [ "multi-user.target" ];
      };
      networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.gossipPort ];
    });
  }