import ../make-test-python.nix (
{ lib, ... }:
let
snakeoil-keys = import ./snakeoil-keys.nix;
hosts = lib.attrNames snakeoil-keys;
subnetOf =
name: config:
subnets = config.services.tinc.networks.myNetwork.hostSettings.${name}.subnets;
in
(builtins.head subnets).address;
makeTincHost =
name:
{
subnet,
extraConfig ? { },
}:
lib.mkMerge [
subnets = [ { address = subnet; } ];
settings = {
Ed25519PublicKey = snakeoil-keys.${name}.ed25519Public;
};
rsaPublicKey = snakeoil-keys.${name}.rsaPublic;
}
extraConfig
];
makeTincNode =
{ config, ... }:
name: extraConfig:
services.tinc.networks.myNetwork = {
inherit name;
rsaPrivateKeyFile = builtins.toFile "rsa.priv" snakeoil-keys.${name}.rsaPrivate;
ed25519PrivateKeyFile = builtins.toFile "ed25519.priv" snakeoil-keys.${name}.ed25519Private;
hostSettings = lib.mapAttrs makeTincHost {
static = {
subnet = "10.0.0.11";
# Only specify the addresses in the node's vlans, Tinc does not
# seem to try each one, unlike the documentation suggests...
extraConfig.addresses = map (vlan: {
address = "192.168.${toString vlan}.11";
port = 655;
}) config.virtualisation.vlans;
dynamic1 = {
subnet = "10.0.0.21";
dynamic2 = {
subnet = "10.0.0.22";
networking.useDHCP = false;
networking.interfaces."tinc.myNetwork" = {
virtual = true;
virtualType = "tun";
ipv4.addresses = [
address = subnetOf name config;
prefixLength = 24;
# Prevents race condition between NixOS service and tinc creating the
# interface.
# See: https://github.com/NixOS/nixpkgs/issues/27070
systemd.services."tinc.myNetwork" = {
after = [ "network-addresses-tinc.myNetwork.service" ];
requires = [ "network-addresses-tinc.myNetwork.service" ];
networking.firewall.allowedTCPPorts = [ 655 ];
networking.firewall.allowedUDPPorts = [ 655 ];
name = "tinc";
meta.maintainers = with lib.maintainers; [ minijackson ];
nodes = {
static =
{ ... }@args:
makeTincNode args "static" {
virtualisation.vlans = [
1
2
networking.interfaces.eth1.ipv4.addresses = [
address = "192.168.1.11";
networking.interfaces.eth2.ipv4.addresses = [
address = "192.168.2.11";
dynamic1 =
makeTincNode args "dynamic1" {
virtualisation.vlans = [ 1 ];
dynamic2 =
makeTincNode args "dynamic2" {
virtualisation.vlans = [ 2 ];
testScript = ''
start_all()
static.wait_for_unit("tinc.myNetwork.service")
dynamic1.wait_for_unit("tinc.myNetwork.service")
dynamic2.wait_for_unit("tinc.myNetwork.service")
# Static is accessible by the other hosts
dynamic1.succeed("ping -c5 192.168.1.11")
dynamic2.succeed("ping -c5 192.168.2.11")
# The other hosts are in separate vlans
dynamic1.fail("ping -c5 192.168.2.11")
dynamic2.fail("ping -c5 192.168.1.11")
# Each host can ping themselves through Tinc
static.succeed("ping -c5 10.0.0.11")
dynamic1.succeed("ping -c5 10.0.0.21")
dynamic2.succeed("ping -c5 10.0.0.22")
# Static is accessible by the other hosts through Tinc
dynamic1.succeed("ping -c5 10.0.0.11")
dynamic2.succeed("ping -c5 10.0.0.11")
# Static can access the other hosts through Tinc
static.succeed("ping -c5 10.0.0.21")
static.succeed("ping -c5 10.0.0.22")
# The other hosts in separate vlans can access each other through Tinc
dynamic1.succeed("ping -c5 10.0.0.22")
dynamic2.succeed("ping -c5 10.0.0.21")
'';
)