forked from Deuxfleurs/nixcfg
Compare commits
1 commit
main
...
wgautomesh
Author | SHA1 | Date | |
---|---|---|---|
2add84963d |
3 changed files with 161 additions and 124 deletions
|
@ -7,9 +7,9 @@ copy cluster/$CLUSTER/cluster.nix /etc/nixos/cluster.nix
|
|||
copy cluster/$CLUSTER/node/$NIXHOST.nix /etc/nixos/node.nix
|
||||
copy cluster/$CLUSTER/node/$NIXHOST.site.nix /etc/nixos/site.nix
|
||||
|
||||
cmd mkdir -p /var/lib/wgautomesh
|
||||
write_pass deuxfleurs/cluster/$CLUSTER/wgautomesh_gossip_secret /var/lib/wgautomesh/gossip_secret
|
||||
copy nix/wgautomesh.nix /etc/nixos/wgautomesh.nix
|
||||
cmd mkdir -p /var/lib/deuxfleurs
|
||||
write_pass deuxfleurs/cluster/$CLUSTER/wgautomesh_gossip_secret /var/lib/deuxfleurs/wgautomesh_gossip_secret
|
||||
|
||||
if [ "$CLUSTER" = "staging" ]; then
|
||||
copy nix/nomad-driver-nix2.nix /etc/nixos/nomad-driver-nix2.nix
|
||||
|
|
|
@ -207,18 +207,19 @@ in
|
|||
};
|
||||
services.wgautomesh = {
|
||||
enable = true;
|
||||
interface = "wg0";
|
||||
gossipPort = 1666;
|
||||
gossipSecretFile = "/var/lib/wgautomesh/gossip_secret";
|
||||
persistFile = "/var/lib/wgautomesh/state";
|
||||
upnpForwardPublicPort =
|
||||
if clusterNodeCfg.endpoint != null then
|
||||
strings.toInt (lists.last (split ":" clusterNodeCfg.endpoint))
|
||||
else null;
|
||||
peers = attrValues (mapAttrs (hostname: { publicKey, endpoint, address, ... }: {
|
||||
inherit address endpoint;
|
||||
pubkey = publicKey;
|
||||
}) cfg.clusterNodes);
|
||||
gossipSecretFile = "/var/lib/deuxfleurs/wgautomesh_gossip_secret";
|
||||
settings = {
|
||||
interface = "wg0";
|
||||
gossip_port = 1666;
|
||||
upnp_forward_external_port =
|
||||
if clusterNodeCfg.endpoint != null then
|
||||
strings.toInt (lists.last (split ":" clusterNodeCfg.endpoint))
|
||||
else null;
|
||||
peers = attrValues (mapAttrs (hostname: { publicKey, endpoint, address, ... }: {
|
||||
inherit address endpoint;
|
||||
pubkey = publicKey;
|
||||
}) cfg.clusterNodes);
|
||||
};
|
||||
};
|
||||
# Old code for wg-quick, we can use this as a fallback if we fail to make wgautomesh work
|
||||
# systemd.services."wg-quick-wg0".after = [ "unbound.service" ];
|
||||
|
|
|
@ -7,123 +7,159 @@ in
|
|||
|
||||
{ lib, config, pkgs, ... }:
|
||||
with lib;
|
||||
let
|
||||
let
|
||||
cfg = config.services.wgautomesh;
|
||||
settingsFormat = pkgs.formats.toml { };
|
||||
configFile =
|
||||
# Have to remove nulls manually as TOML generator will not just skip key
|
||||
# if value is null
|
||||
settingsFormat.generate "wgautomesh-config.toml"
|
||||
(filterAttrs (k: v: v != null)
|
||||
(mapAttrs
|
||||
(k: v:
|
||||
if k == "peers"
|
||||
then map (e: filterAttrs (k: v: v != null) e) v
|
||||
else v)
|
||||
cfg.settings));
|
||||
runtimeConfigFile =
|
||||
if cfg.enableGossipEncryption
|
||||
then "/run/wgautomesh/wgautomesh.toml"
|
||||
else configFile;
|
||||
in
|
||||
with builtins;
|
||||
{
|
||||
options.services.wgautomesh = {
|
||||
enable = mkEnableOption "wgautomesh";
|
||||
logLevel = mkOption {
|
||||
type = types.enum [ "trace" "debug" "info" "warn" "error" ];
|
||||
default = "info";
|
||||
description = "wgautomesh log level (trace/debug/info/warn/error)";
|
||||
};
|
||||
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";
|
||||
};
|
||||
{
|
||||
options.services.wgautomesh = {
|
||||
enable = mkEnableOption (mdDoc "the wgautomesh daemon");
|
||||
logLevel = mkOption {
|
||||
type = types.enum [ "trace" "debug" "info" "warn" "error" ];
|
||||
default = "info";
|
||||
description = mdDoc "wgautomesh log level.";
|
||||
};
|
||||
enableGossipEncryption = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Enable encryption of gossip traffic.";
|
||||
};
|
||||
gossipSecretFile = mkOption {
|
||||
type = types.path;
|
||||
description = mdDoc ''
|
||||
File containing the shared secret key to use for gossip encryption.
|
||||
Required if `enableGossipEncryption` is set.
|
||||
'';
|
||||
};
|
||||
enablePersistence = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Enable persistence of Wireguard peer info between restarts.";
|
||||
};
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Automatically open gossip port in firewall (recommended).";
|
||||
};
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
interface = mkOption {
|
||||
type = types.str;
|
||||
description = mdDoc ''
|
||||
Wireguard interface to manage (it is NOT created by wgautomesh, you
|
||||
should use another NixOS option to create it such as
|
||||
`networking.wireguard.interfaces.wg0 = {...};`).
|
||||
'';
|
||||
example = "wg0";
|
||||
};
|
||||
});
|
||||
description = "wgautomesh peer list";
|
||||
gossip_port = mkOption {
|
||||
type = types.port;
|
||||
description = mdDoc ''
|
||||
wgautomesh gossip port, this MUST be the same number on all nodes in
|
||||
the wgautomesh network.
|
||||
'';
|
||||
default = 1666;
|
||||
};
|
||||
lan_discovery = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Enable discovery of peers on the same LAN using UDP broadcast.";
|
||||
};
|
||||
upnp_forward_external_port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Public port number to try to redirect to this machine's Wireguard
|
||||
daemon using UPnP IGD.
|
||||
'';
|
||||
};
|
||||
peers = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = mdDoc "Wireguard public key of this peer.";
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
description = mdDoc ''
|
||||
Wireguard address of this peer (a single IP address, multliple
|
||||
addresses or address ranges are not supported).
|
||||
'';
|
||||
example = "10.0.0.42";
|
||||
};
|
||||
endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = mdDoc ''
|
||||
Bootstrap endpoint for connecting to this Wireguard peer if no
|
||||
other address is known or none are working.
|
||||
'';
|
||||
default = null;
|
||||
example = "wgnode.mydomain.example:51820";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [ ];
|
||||
description = mdDoc "wgautomesh peer list.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = mdDoc "Configuration for wgautomesh.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.wgautomesh.settings = {
|
||||
gossip_secret_file = mkIf cfg.enableGossipEncryption "$CREDENTIALS_DIRECTORY/gossip_secret";
|
||||
persist_file = mkIf cfg.enablePersistence "/var/lib/wgautomesh/state";
|
||||
};
|
||||
|
||||
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}
|
||||
systemd.services.wgautomesh = {
|
||||
path = [ pkgs.wireguard-tools ];
|
||||
environment = { RUST_LOG = "wgautomesh=${cfg.logLevel}"; };
|
||||
description = "wgautomesh";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
|
||||
${concatStringsSep "\n" peerDefs}
|
||||
'';
|
||||
in {
|
||||
systemd.services.wgautomesh = {
|
||||
enable = true;
|
||||
path = [ pkgs.wireguard-tools ];
|
||||
environment = {
|
||||
RUST_LOG = "wgautomesh=${cfg.logLevel}";
|
||||
};
|
||||
description = "wgautomesh";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${wgautomesh}/bin/wgautomesh ${runtimeConfigFile}";
|
||||
Restart = "always";
|
||||
RestartSec = "30";
|
||||
LoadCredential = mkIf cfg.enableGossipEncryption [ "gossip_secret:${cfg.gossipSecretFile}" ];
|
||||
|
||||
ExecStart = "${wgautomesh}/bin/wgautomesh ${configfile}";
|
||||
Restart = "always";
|
||||
RestartSec = "30";
|
||||
ExecStartPre = mkIf cfg.enableGossipEncryption [
|
||||
''${pkgs.envsubst}/bin/envsubst \
|
||||
-i ${configFile} \
|
||||
-o ${runtimeConfigFile}''
|
||||
];
|
||||
|
||||
ExecStartPre = [ "+${pkgs.coreutils}/bin/chown wgautomesh /var/lib/wgautomesh/gossip_secret" ];
|
||||
|
||||
DynamicUser = true;
|
||||
User = "wgautomesh";
|
||||
StateDirectory = "wgautomesh";
|
||||
StateDirectoryMode = "0700";
|
||||
AmbientCapabilities = "CAP_NET_ADMIN";
|
||||
CapabilityBoundingSet = "CAP_NET_ADMIN";
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
DynamicUser = true;
|
||||
StateDirectory = "wgautomesh";
|
||||
StateDirectoryMode = "0700";
|
||||
RuntimeDirectory = "wgautomesh";
|
||||
AmbientCapabilities = "CAP_NET_ADMIN";
|
||||
CapabilityBoundingSet = "CAP_NET_ADMIN";
|
||||
};
|
||||
networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.gossipPort ];
|
||||
});
|
||||
}
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
networking.firewall.allowedUDPPorts =
|
||||
mkIf cfg.openFirewall [ cfg.settings.gossip_port ];
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue