mirror of
https://github.com/vale981/phoebe
synced 2025-03-04 17:31:41 -05:00
Add support for configuring an entire WireGuard network
This commit is contained in:
parent
2f89d37b29
commit
4043563dbc
4 changed files with 216 additions and 0 deletions
|
@ -39,6 +39,10 @@ Module List
|
|||
|
||||
Simple backups for PostgreSQL via `pg_dump`.
|
||||
|
||||
* `phoebe.services.networking.wireguard`:
|
||||
|
||||
Simple way to configure a whole network of WireGuard machines.
|
||||
|
||||
|
||||
[nixos]: https://nixos.org/
|
||||
[nixpkgs]: https://nixos.org/nixpkgs/
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
./builder
|
||||
./databases
|
||||
./monitoring
|
||||
./networking
|
||||
./web
|
||||
];
|
||||
}
|
||||
|
|
7
modules/services/networking/default.nix
Normal file
7
modules/services/networking/default.nix
Normal file
|
@ -0,0 +1,7 @@
|
|||
{ config, lib, pkgs, ...}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./wireguard
|
||||
];
|
||||
}
|
204
modules/services/networking/wireguard/default.nix
Normal file
204
modules/services/networking/wireguard/default.nix
Normal file
|
@ -0,0 +1,204 @@
|
|||
# The common situation where you have a distributed network of
|
||||
# machines that want to talk to one another over WireGuard.
|
||||
{ config, lib, pkgs, ...}: with lib;
|
||||
|
||||
let
|
||||
# Shorthand:
|
||||
cfg = config.phoebe.services.networking.wireguard;
|
||||
|
||||
# Private library functions:
|
||||
plib = config.phoebe.lib;
|
||||
|
||||
##############################################################################
|
||||
# Per-machine options:
|
||||
machineOpts = { name, ... }: {
|
||||
#### Interface:
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
example = "myhost";
|
||||
description = "Host name without domain.";
|
||||
};
|
||||
|
||||
publicKey = mkOption {
|
||||
type = types.str;
|
||||
example = "SOKmBk+ZcKIXql49vuWc+uaVYxsvb8EaJZDOiQdUSFU=";
|
||||
description = "WireGuard public key for this machine.";
|
||||
};
|
||||
|
||||
ip = mkOption {
|
||||
type = types.str;
|
||||
example = "192.168.1.2/32";
|
||||
description = "WireGuard IP address with mask.";
|
||||
};
|
||||
|
||||
routes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = [ "10.10.0.0/24" ];
|
||||
default = [ ];
|
||||
description = ''List of IP addresses with masks. Traffic
|
||||
destined for an IP address in this list will be routed
|
||||
through this machine.'';
|
||||
};
|
||||
|
||||
endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
example = "myhost.example.com:51820";
|
||||
default = null;
|
||||
description = ''Optional FQDN and port number to reach this
|
||||
machine from outside WireGuard. Only needed if you want to
|
||||
make outbound connections to this machine. Not necessary to
|
||||
allow inbound connetions from this machine.'';
|
||||
};
|
||||
|
||||
keepAlive = mkOption {
|
||||
type = types.nullOr types.ints.positive;
|
||||
example = 25;
|
||||
default = null;
|
||||
description = ''If set, send a keep-alive packet every N
|
||||
seconds. Usually not necessary.'';
|
||||
};
|
||||
|
||||
current = mkOption {
|
||||
type = types.bool;
|
||||
example = true;
|
||||
description = ''Is this machine the one currently being
|
||||
configured? If so, all other machines are configured as
|
||||
peers. By default, if this machine's name matches the
|
||||
current host name this option will be set to true.'';
|
||||
};
|
||||
};
|
||||
|
||||
#### Implementation:
|
||||
config = {
|
||||
name = mkDefault name;
|
||||
current = mkDefault (name == config.networking.hostName);
|
||||
};
|
||||
};
|
||||
|
||||
##############################################################################
|
||||
# Per-network options:
|
||||
networkOpts = { name, ... }: {
|
||||
#### Interface:
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
example = "wg0";
|
||||
description = "Network (and interface) name.";
|
||||
};
|
||||
|
||||
privateKey = mkOption {
|
||||
type = with types; either path str;
|
||||
example = "/run/keys/wireguard";
|
||||
description = ''WireGuard private key. Can be given as a path
|
||||
or a string but a path is preferred for security to keep
|
||||
the private key out of the nix store. When using a path
|
||||
that looks like a NixOps key, the WireGuard service will
|
||||
automatically wait for the key to appear before starting.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.nullOr types.ints.positive;
|
||||
default = 51820;
|
||||
example = null;
|
||||
description = ''Port number to listen on, or
|
||||
<literal>null</literal> to disable listening.'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = "Open the firewall for the UDP WireGuard port.";
|
||||
};
|
||||
|
||||
machines = mkOption {
|
||||
type = types.attrsOf (types.submodule machineOpts);
|
||||
example = {
|
||||
myhost = {
|
||||
publicKey = "SOKmBk+ZcKIXql49vuWc+uaVYxsvb8EaJZDOiQdUSFU=";
|
||||
ip = "10.0.1.2/32";
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = ''The machines on this network, only one of which
|
||||
may be the current machine'';
|
||||
};
|
||||
};
|
||||
|
||||
#### Implementation:
|
||||
config = {
|
||||
name = mkDefault name;
|
||||
};
|
||||
};
|
||||
|
||||
############################################################################
|
||||
# Configure a WireGuard peer:
|
||||
mkPeer = machine: {
|
||||
publicKey = machine.publicKey;
|
||||
allowedIPs = [ machine.ip ] ++ machine.routes;
|
||||
persistentKeepalive = mkIf (machine.keepAlive != null) machine.keepAlive;
|
||||
endpoint = mkIf (machine.endpoint != null) machine.endpoint;
|
||||
};
|
||||
|
||||
############################################################################
|
||||
# Configure a network:
|
||||
mkNetwork = nw: {
|
||||
# FIXME: Assert there's exactly one current machine!
|
||||
"${nw.name}" = {
|
||||
ips = map (m: m.ip) (filter (m: m.current) (attrValues nw.machines));
|
||||
listenPort = mkIf (nw.port != null) nw.port;
|
||||
peers = map (m: mkPeer m) (filter (m: !m.current) (attrValues nw.machines));
|
||||
|
||||
privateKeyFile =
|
||||
if builtins.substring 0 1 (toString nw.privateKey) == "/"
|
||||
then nw.privateKey
|
||||
else toString (pkgs.writeText "${nw.name}-pk" nw.privateKey);
|
||||
};
|
||||
};
|
||||
|
||||
# Make wireguard wait for its private key.
|
||||
mkWait = nw: {
|
||||
"wireguard-${nw.name}" =
|
||||
mkIf (plib.isKeyFile nw.privateKey) {
|
||||
after = plib.keyService nw.privateKey;
|
||||
wants = plib.keyService nw.privateKey;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
#### Interface:
|
||||
options.phoebe.services.networking.wireguard = {
|
||||
networks = mkOption {
|
||||
type = types.attrsOf (types.submodule networkOpts);
|
||||
default = { };
|
||||
description = "Networks to configure.";
|
||||
};
|
||||
};
|
||||
|
||||
#### Implementation:
|
||||
config = mkIf (length (attrValues cfg.networks) > 0) {
|
||||
# Allow routing through the WireGuard interfaces:
|
||||
networking.nat.enable = true;
|
||||
networking.nat.internalInterfaces =
|
||||
map (n: n.name) (attrValues cfg.networks);
|
||||
|
||||
# Trust WireGuard interfaces:
|
||||
networking.firewall.trustedInterfaces =
|
||||
map (n: n.name) (attrValues cfg.networks);
|
||||
|
||||
# Optionally open the firewall for WireGuard ports:
|
||||
networking.firewall.allowedUDPPorts =
|
||||
map (n: n.port) (filter (n: n.openFirewall)
|
||||
(attrValues cfg.networks));
|
||||
|
||||
# Configure the WireGuard interfaces:
|
||||
networking.wireguard.interfaces =
|
||||
foldr (a: b: mkNetwork a // b) { } (attrValues cfg.networks);
|
||||
|
||||
# Extra systemd service configuration:
|
||||
systemd.services =
|
||||
foldr (a: b: mkWait a // b) { } (attrValues cfg.networks);
|
||||
};
|
||||
}
|
Loading…
Add table
Reference in a new issue