mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-13 21:50:33 +03:00
nixos/yggdrasil: add service
This commit is contained in:
parent
3cc9bcb338
commit
b8cb8c39d6
4 changed files with 306 additions and 0 deletions
|
@ -730,6 +730,7 @@
|
||||||
./services/networking/xinetd.nix
|
./services/networking/xinetd.nix
|
||||||
./services/networking/xl2tpd.nix
|
./services/networking/xl2tpd.nix
|
||||||
./services/networking/xrdp.nix
|
./services/networking/xrdp.nix
|
||||||
|
./services/networking/yggdrasil.nix
|
||||||
./services/networking/zerobin.nix
|
./services/networking/zerobin.nix
|
||||||
./services/networking/zeronet.nix
|
./services/networking/zeronet.nix
|
||||||
./services/networking/zerotierone.nix
|
./services/networking/zerotierone.nix
|
||||||
|
|
181
nixos/modules/services/networking/yggdrasil.nix
Normal file
181
nixos/modules/services/networking/yggdrasil.nix
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.yggdrasil;
|
||||||
|
configProvided = (cfg.config != {});
|
||||||
|
configAsFile = (if configProvided then
|
||||||
|
toString (pkgs.writeTextFile {
|
||||||
|
name = "yggdrasil-conf";
|
||||||
|
text = builtins.toJSON cfg.config;
|
||||||
|
})
|
||||||
|
else null);
|
||||||
|
configFileProvided = (cfg.configFile != null);
|
||||||
|
generateConfig = (
|
||||||
|
if configProvided && configFileProvided then
|
||||||
|
"${pkgs.jq}/bin/jq -s add /run/yggdrasil/configFile.json ${configAsFile}"
|
||||||
|
else if configProvided then
|
||||||
|
"cat ${configAsFile}"
|
||||||
|
else if configFileProvided then
|
||||||
|
"cat /run/yggdrasil/configFile.json"
|
||||||
|
else
|
||||||
|
"${cfg.package}/bin/yggdrasil -genconf"
|
||||||
|
);
|
||||||
|
|
||||||
|
in {
|
||||||
|
options = with types; {
|
||||||
|
services.yggdrasil = {
|
||||||
|
enable = mkEnableOption "the yggdrasil system service";
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
default = null;
|
||||||
|
example = "/run/keys/yggdrasil.conf";
|
||||||
|
description = ''
|
||||||
|
A file which contains JSON configuration for yggdrasil.
|
||||||
|
|
||||||
|
You do not have to supply a complete configuration, as
|
||||||
|
yggdrasil will use default values for anything which is
|
||||||
|
omitted. If the encryption and signing keys are omitted,
|
||||||
|
yggdrasil will generate new ones each time the service is
|
||||||
|
started, resulting in a random IPv6 address on the yggdrasil
|
||||||
|
network each time.
|
||||||
|
|
||||||
|
If both this option and <option>config</option> are
|
||||||
|
supplied, they will be combined, with values from
|
||||||
|
<option>config</option> taking precedence.
|
||||||
|
|
||||||
|
You can use the command <code>nix-shell -p yggdrasil --run
|
||||||
|
"yggdrasil -genconf -json"</code> to generate a default
|
||||||
|
JSON configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkOption {
|
||||||
|
type = attrs;
|
||||||
|
default = {};
|
||||||
|
example = {
|
||||||
|
Peers = [
|
||||||
|
"tcp://aa.bb.cc.dd:eeeee"
|
||||||
|
"tcp://[aaaa:bbbb:cccc:dddd::eeee]:fffff"
|
||||||
|
];
|
||||||
|
Listen = [
|
||||||
|
"tcp://0.0.0.0:xxxxx"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
Configuration for yggdrasil, as a Nix attribute set.
|
||||||
|
|
||||||
|
Warning: this is stored in the WORLD-READABLE Nix store!
|
||||||
|
Therefore, it is not appropriate for private keys. If you
|
||||||
|
do not specify the keys, yggdrasil will generate a new set
|
||||||
|
each time the service is started, creating a random IPv6
|
||||||
|
address on the yggdrasil network each time.
|
||||||
|
|
||||||
|
If you wish to specify the keys, use
|
||||||
|
<option>configFile</option>. If both
|
||||||
|
<option>configFile</option> and <option>config</option> are
|
||||||
|
supplied, they will be combined, with values from
|
||||||
|
<option>config</option> taking precedence.
|
||||||
|
|
||||||
|
You can use the command <code>nix-shell -p yggdrasil --run
|
||||||
|
"yggdrasil -genconf"</code> to generate default
|
||||||
|
configuration values with documentation.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
openMulticastPort = mkOption {
|
||||||
|
type = bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to open the UDP port used for multicast peer
|
||||||
|
discovery. The NixOS firewall blocks link-local
|
||||||
|
communication, so in order to make local peering work you
|
||||||
|
will also need to set <code>LinkLocalTCPPort</code> in your
|
||||||
|
yggdrasil configuration (<option>config</option> or
|
||||||
|
<option>configFile</option>) to a port number other than 0,
|
||||||
|
and then add that port to
|
||||||
|
<option>networking.firewall.allowedTCPPorts</option>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
denyDhcpcdInterfaces = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
default = [];
|
||||||
|
example = [ "tap*" ];
|
||||||
|
description = ''
|
||||||
|
Disable the DHCP client for any interface whose name matches
|
||||||
|
any of the shell glob patterns in this list. Use this
|
||||||
|
option to prevent the DHCP client from broadcasting requests
|
||||||
|
on the yggdrasil network. It is only necessary to do so
|
||||||
|
when yggdrasil is running in TAP mode, because TUN
|
||||||
|
interfaces do not support broadcasting.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = package;
|
||||||
|
default = pkgs.yggdrasil;
|
||||||
|
defaultText = "pkgs.yggdrasil";
|
||||||
|
description = "Yggdrasil package to use.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{ assertion = config.networking.enableIPv6;
|
||||||
|
message = "networking.enableIPv6 must be true for yggdrasil to work";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.etc."yggdrasil.conf" = {
|
||||||
|
enable = true;
|
||||||
|
mode = "symlink";
|
||||||
|
source = "/run/yggdrasil/yggdrasil.conf";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.yggdrasil = {
|
||||||
|
description = "Yggdrasil Network Service";
|
||||||
|
path = [ cfg.package ] ++ optional (configProvided && configFileProvided) pkgs.jq;
|
||||||
|
bindsTo = [ "network-online.target" ];
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
${generateConfig} | yggdrasil -normaliseconf -useconf > /run/yggdrasil/yggdrasil.conf
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/bin/yggdrasil -useconffile /etc/yggdrasil.conf";
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
|
Restart = "always";
|
||||||
|
|
||||||
|
RuntimeDirectory = "yggdrasil";
|
||||||
|
RuntimeDirectoryMode = "0700";
|
||||||
|
BindReadOnlyPaths = mkIf configFileProvided
|
||||||
|
[ "${cfg.configFile}:/run/yggdrasil/configFile.json" ];
|
||||||
|
|
||||||
|
DynamicUser = true;
|
||||||
|
AmbientCapabilities = "CAP_NET_ADMIN";
|
||||||
|
CapabilityBoundingSet = "CAP_NET_ADMIN";
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = "tmpfs";
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces;
|
||||||
|
networking.firewall.allowedUDPPorts = mkIf cfg.openMulticastPort [ 9001 ];
|
||||||
|
|
||||||
|
# Make yggdrasilctl available on the command line.
|
||||||
|
environment.systemPackages = [ cfg.package ];
|
||||||
|
};
|
||||||
|
meta.maintainers = with lib.maintainers; [ gazally ];
|
||||||
|
}
|
|
@ -293,5 +293,6 @@ in
|
||||||
xrdp = handleTest ./xrdp.nix {};
|
xrdp = handleTest ./xrdp.nix {};
|
||||||
xss-lock = handleTest ./xss-lock.nix {};
|
xss-lock = handleTest ./xss-lock.nix {};
|
||||||
yabar = handleTest ./yabar.nix {};
|
yabar = handleTest ./yabar.nix {};
|
||||||
|
yggdrasil = handleTest ./yggdrasil.nix {};
|
||||||
zookeeper = handleTest ./zookeeper.nix {};
|
zookeeper = handleTest ./zookeeper.nix {};
|
||||||
}
|
}
|
||||||
|
|
123
nixos/tests/yggdrasil.nix
Normal file
123
nixos/tests/yggdrasil.nix
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
let
|
||||||
|
aliceIp6 = "200:3b91:b2d8:e708:fbf3:f06:fdd5:90d0";
|
||||||
|
aliceKeys = {
|
||||||
|
EncryptionPublicKey = "13e23986fe76bc3966b42453f479bc563348b7ff76633b7efcb76e185ec7652f";
|
||||||
|
EncryptionPrivateKey = "9f86947b15e86f9badac095517a1982e39a2db37ca726357f95987b898d82208";
|
||||||
|
SigningPublicKey = "e2c43349083bc1e998e4ec4535b4c6a8f44ca9a5a8e07336561267253b2be5f4";
|
||||||
|
SigningPrivateKey = "fe3add8da35316c05f6d90d3ca79bd2801e6ccab6d37e5339fef4152589398abe2c43349083bc1e998e4ec4535b4c6a8f44ca9a5a8e07336561267253b2be5f4";
|
||||||
|
};
|
||||||
|
bobIp6 = "201:ebbd:bde9:f138:c302:4afa:1fb6:a19a";
|
||||||
|
bobConfig = {
|
||||||
|
InterfacePeers = {
|
||||||
|
eth1 = [ "tcp://192.168.1.200:12345" ];
|
||||||
|
};
|
||||||
|
MulticastInterfaces = [ "eth1" ];
|
||||||
|
LinkLocalTCPPort = 54321;
|
||||||
|
EncryptionPublicKey = "c99d6830111e12d1b004c52fe9e5a2eef0f6aefca167aca14589a370b7373279";
|
||||||
|
EncryptionPrivateKey = "2e698a53d3fdce5962d2ff37de0fe77742a5c8b56cd8259f5da6aa792f6e8ba3";
|
||||||
|
SigningPublicKey = "de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b";
|
||||||
|
SigningPrivateKey = "2a6c21550f3fca0331df50668ffab66b6dce8237bcd5728e571e8033b363e247de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b";
|
||||||
|
};
|
||||||
|
|
||||||
|
in import ./make-test.nix ({ pkgs, ...} : {
|
||||||
|
name = "yggdrasil";
|
||||||
|
meta = with pkgs.stdenv.lib.maintainers; {
|
||||||
|
maintainers = [ gazally ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = rec {
|
||||||
|
# Alice is listening for peerings on a specified port,
|
||||||
|
# but has multicast peering disabled. Alice has part of her
|
||||||
|
# yggdrasil config in Nix and part of it in a file.
|
||||||
|
alice =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
networking = {
|
||||||
|
interfaces.eth1.ipv4.addresses = [{
|
||||||
|
address = "192.168.1.200";
|
||||||
|
prefixLength = 24;
|
||||||
|
}];
|
||||||
|
firewall.allowedTCPPorts = [ 80 12345 ];
|
||||||
|
};
|
||||||
|
services.httpd.enable = true;
|
||||||
|
services.httpd.adminAddr = "foo@example.org";
|
||||||
|
|
||||||
|
services.yggdrasil = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
Listen = ["tcp://0.0.0.0:12345"];
|
||||||
|
MulticastInterfaces = [ ];
|
||||||
|
};
|
||||||
|
configFile = toString (pkgs.writeTextFile {
|
||||||
|
name = "yggdrasil-alice-conf";
|
||||||
|
text = builtins.toJSON aliceKeys;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Bob is set up to peer with Alice, and also to do local multicast
|
||||||
|
# peering. Bob's yggdrasil config is in a file.
|
||||||
|
bob =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
networking.firewall.allowedTCPPorts = [ 54321 ];
|
||||||
|
services.yggdrasil = {
|
||||||
|
enable = true;
|
||||||
|
openMulticastPort = true;
|
||||||
|
configFile = toString (pkgs.writeTextFile {
|
||||||
|
name = "yggdrasil-bob-conf";
|
||||||
|
text = builtins.toJSON bobConfig;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Carol only does local peering. Carol's yggdrasil config is all Nix.
|
||||||
|
carol =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
networking.firewall.allowedTCPPorts = [ 43210 ];
|
||||||
|
services.yggdrasil = {
|
||||||
|
enable = true;
|
||||||
|
denyDhcpcdInterfaces = [ "ygg0" ];
|
||||||
|
config = {
|
||||||
|
IfTAPMode = true;
|
||||||
|
IFName = "ygg0";
|
||||||
|
MulticastInterfaces = [ "eth1" ];
|
||||||
|
LinkLocalTCPPort = 43210;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
''
|
||||||
|
# Give Alice a head start so she is ready when Bob calls.
|
||||||
|
$alice->start;
|
||||||
|
$alice->waitForUnit("yggdrasil.service");
|
||||||
|
|
||||||
|
$bob->start;
|
||||||
|
$carol->start;
|
||||||
|
$bob->waitForUnit("yggdrasil.service");
|
||||||
|
$carol->waitForUnit("yggdrasil.service");
|
||||||
|
|
||||||
|
$carol->waitUntilSucceeds("[ `ip -o -6 addr show dev ygg0 scope global | grep -v tentative | wc -l` -ge 1 ]");
|
||||||
|
my $carolIp6 = (split /[ \/]+/, $carol->succeed("ip -o -6 addr show dev ygg0 scope global"))[3];
|
||||||
|
|
||||||
|
# If Alice can talk to Carol, then Bob's outbound peering and Carol's
|
||||||
|
# local peering have succeeded and everybody is connected.
|
||||||
|
$alice->waitUntilSucceeds("ping -c 1 $carolIp6");
|
||||||
|
$alice->succeed("ping -c 1 ${bobIp6}");
|
||||||
|
|
||||||
|
$bob->succeed("ping -c 1 ${aliceIp6}");
|
||||||
|
$bob->succeed("ping -c 1 $carolIp6");
|
||||||
|
|
||||||
|
$carol->succeed("ping -c 1 ${aliceIp6}");
|
||||||
|
$carol->succeed("ping -c 1 ${bobIp6}");
|
||||||
|
|
||||||
|
$carol->fail("journalctl -u dhcpcd | grep ygg0");
|
||||||
|
|
||||||
|
$alice->waitForUnit("httpd.service");
|
||||||
|
$carol->succeed("curl --fail -g http://[${aliceIp6}]");
|
||||||
|
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Add table
Add a link
Reference in a new issue