nixos/easytier: init module

L-Trump 725a756d 4206c4cb

Changed files
+422 -1
nixos
doc
manual
release-notes
modules
services
networking
tests
pkgs
by-name
ea
easytier
+2
nixos/doc/manual/release-notes/rl-2505.section.md
···
- [Readeck](https://readeck.org/), a read-it later web-application. Available as [services.readeck](#opt-services.readeck.enable).
+
- [EasyTier](https://github.com/EasyTier/EasyTier), a decentralized VPN solution. Available as [services.easytier](#opt-services.easytier.enable).
+
- [Traccar](https://www.traccar.org/), a modern GPS Tracking Platform. Available as [services.traccar](#opt-services.traccar.enable).
- [Schroot](https://codeberg.org/shelter/reschroot), a lightweight virtualisation tool. Securely enter a chroot and run a command or login shell. Available as [programs.schroot](#opt-programs.schroot.enable).
+1
nixos/modules/module-list.nix
···
./services/networking/dnsproxy.nix
./services/networking/doh-proxy-rust.nix
./services/networking/doh-server.nix
+
./services/networking/easytier.nix
./services/networking/ejabberd.nix
./services/networking/envoy.nix
./services/networking/epmd.nix
+292
nixos/modules/services/networking/easytier.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
+
with lib;
+
let
+
cfg = config.services.easytier;
+
settingsFormat = pkgs.formats.toml { };
+
+
genFinalSettings =
+
inst:
+
attrsets.filterAttrsRecursive (_: v: v != { }) (
+
attrsets.filterAttrsRecursive (_: v: v != null) (
+
{
+
inherit (inst.settings)
+
instance_name
+
hostname
+
ipv4
+
dhcp
+
listeners
+
;
+
network_identity = {
+
inherit (inst.settings) network_name network_secret;
+
};
+
peer = map (p: { uri = p; }) inst.settings.peers;
+
}
+
// inst.extraSettings
+
)
+
);
+
+
genConfigFile =
+
name: inst:
+
if inst.configFile == null then
+
settingsFormat.generate "easytier-${name}.toml" (genFinalSettings inst)
+
else
+
inst.configFile;
+
+
activeInsts = filterAttrs (_: inst: inst.enable) cfg.instances;
+
+
settingsModule = name: {
+
options = {
+
instance_name = mkOption {
+
type = types.str;
+
default = name;
+
description = "Identify different instances on same host";
+
};
+
+
hostname = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
description = "Hostname shown in peer list and web console.";
+
};
+
+
network_name = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
description = "EasyTier network name.";
+
};
+
+
network_secret = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
description = ''
+
EasyTier network credential used for verification and
+
encryption. It can also be set in environmentFile.
+
'';
+
};
+
+
ipv4 = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
description = ''
+
IPv4 cidr address of this peer in the virtual network. If
+
empty, this peer will only forward packets and no TUN device
+
will be created.
+
'';
+
example = "10.144.144.1/24";
+
};
+
+
dhcp = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Automatically determine the IPv4 address of this peer based on
+
existing peers on network.
+
'';
+
};
+
+
listeners = mkOption {
+
type = with types; listOf str;
+
default = [
+
"tcp://0.0.0.0:11010"
+
"udp://0.0.0.0:11010"
+
];
+
description = ''
+
Listener addresses to accept connections from other peers.
+
Valid format is: `<proto>://<addr>:<port>`, where the protocol
+
can be `tcp`, `udp`, `ring`, `wg`, `ws`, `wss`.
+
'';
+
};
+
+
peers = mkOption {
+
type = with types; listOf str;
+
default = [ ];
+
description = ''
+
Peers to connect initially. Valid format is: `<proto>://<addr>:<port>`.
+
'';
+
example = [
+
"tcp://example.com:11010"
+
];
+
};
+
};
+
};
+
+
instanceModule =
+
{ name, ... }:
+
{
+
options = {
+
enable = mkOption {
+
type = types.bool;
+
default = true;
+
description = "Enable the instance.";
+
};
+
+
configServer = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
description = ''
+
Configure the instance from config server. When this option
+
set, any other settings for configuring the instance manually
+
except `hostname` will be ignored. Valid formats are:
+
+
- full uri for custom server: `udp://example.com:22020/<token>`
+
- username only for official server: `<token>`
+
'';
+
example = "udp://example.com:22020/myusername";
+
};
+
+
configFile = mkOption {
+
type = with types; nullOr path;
+
default = null;
+
description = ''
+
Path to easytier config file. Setting this option will
+
override `settings` and `extraSettings` of this instance.
+
'';
+
};
+
+
environmentFiles = mkOption {
+
type = with types; listOf path;
+
default = [ ];
+
description = ''
+
Environment files for this instance. All command-line args
+
have corresponding environment variables.
+
'';
+
example = literalExpression ''
+
[
+
/path/to/.env
+
/path/to/.env.secret
+
]
+
'';
+
};
+
+
settings = mkOption {
+
type = types.submodule (settingsModule name);
+
default = { };
+
description = ''
+
Settings to generate {file}`easytier-${name}.toml`
+
'';
+
};
+
+
extraSettings = mkOption {
+
type = settingsFormat.type;
+
default = { };
+
description = ''
+
Extra settings to add to {file}`easytier-${name}.toml`.
+
'';
+
};
+
+
extraArgs = mkOption {
+
type = with types; listOf str;
+
default = [ ];
+
description = ''
+
Extra args append to the easytier command-line.
+
'';
+
};
+
};
+
};
+
+
in
+
{
+
options.services.easytier = {
+
enable = mkEnableOption "EasyTier daemon";
+
+
package = mkPackageOption pkgs "easytier" { };
+
+
allowSystemForward = mkEnableOption ''
+
Allow the system to forward packets from easytier. Useful when
+
`proxy_forward_by_system` enabled.
+
'';
+
+
instances = mkOption {
+
description = ''
+
EasyTier instances.
+
'';
+
type = types.attrsOf (types.submodule instanceModule);
+
default = { };
+
example = {
+
settings = {
+
network_name = "easytier";
+
network_secret = "easytier";
+
ipv4 = "10.144.144.1/24";
+
peers = [
+
"tcp://public.easytier.cn:11010"
+
"wss://example.com:443"
+
];
+
};
+
extraSettings = {
+
flags.dev_name = "tun1";
+
};
+
};
+
};
+
};
+
+
config = mkIf cfg.enable {
+
environment.systemPackages = [ cfg.package ];
+
+
systemd.services = mapAttrs' (
+
name: inst:
+
let
+
configFile = genConfigFile name inst;
+
in
+
nameValuePair "easytier-${name}" {
+
description = "EasyTier Daemon - ${name}";
+
wants = [
+
"network-online.target"
+
"nss-lookup.target"
+
];
+
after = [
+
"network-online.target"
+
"nss-lookup.target"
+
];
+
wantedBy = [ "multi-user.target" ];
+
path = with pkgs; [
+
cfg.package
+
iproute2
+
bash
+
];
+
restartTriggers = inst.environmentFiles ++ (optionals (inst.configServer == null) [ configFile ]);
+
serviceConfig = {
+
Type = "simple";
+
Restart = "on-failure";
+
EnvironmentFile = inst.environmentFiles;
+
StateDirectory = "easytier/easytier-${name}";
+
StateDirectoryMode = "0700";
+
WorkingDirectory = "/var/lib/easytier/easytier-${name}";
+
ExecStart = escapeShellArgs (
+
[
+
"${cfg.package}/bin/easytier-core"
+
]
+
++ optionals (inst.configServer != null) (
+
[
+
"-w"
+
"${inst.configServer}"
+
]
+
++ (optionals (inst.settings.hostname != null) [
+
"--hostname"
+
"${inst.settings.hostname}"
+
])
+
)
+
++ optionals (inst.configServer == null) [
+
"-c"
+
"${configFile}"
+
]
+
++ inst.extraArgs
+
);
+
};
+
}
+
) activeInsts;
+
+
boot.kernel.sysctl = mkIf cfg.allowSystemForward {
+
"net.ipv4.conf.all.forwarding" = mkOverride 97 true;
+
"net.ipv6.conf.all.forwarding" = mkOverride 97 true;
+
};
+
};
+
+
meta.maintainers = with maintainers; [
+
ltrump
+
];
+
}
+1
nixos/tests/all-tests.nix
···
fscrypt = runTest ./fscrypt.nix;
fastnetmon-advanced = runTest ./fastnetmon-advanced.nix;
lauti = runTest ./lauti.nix;
+
easytier = handleTest ./easytier.nix { };
ejabberd = runTest ./xmpp/ejabberd.nix;
elk = handleTestOn [ "x86_64-linux" ] ./elk.nix { };
emacs-daemon = runTest ./emacs-daemon.nix;
+121
nixos/tests/easytier.nix
···
+
import ./make-test-python.nix (
+
{ lib, ... }:
+
{
+
name = "easytier";
+
meta.maintainers = with lib.maintainers; [ ltrump ];
+
+
nodes =
+
let
+
genPeer =
+
hostConfig: settings:
+
lib.mkMerge [
+
{
+
services.easytier = {
+
enable = true;
+
instances.default = {
+
settings = {
+
network_name = "easytier_test";
+
network_secret = "easytier_test_secret";
+
} // settings;
+
};
+
};
+
+
networking.useDHCP = false;
+
networking.firewall.allowedTCPPorts = [
+
11010
+
11011
+
];
+
networking.firewall.allowedUDPPorts = [
+
11010
+
11011
+
];
+
}
+
hostConfig
+
];
+
in
+
{
+
relay =
+
genPeer
+
{
+
virtualisation.vlans = [
+
1
+
2
+
];
+
+
networking.interfaces.eth1.ipv4.addresses = [
+
{
+
address = "192.168.1.11";
+
prefixLength = 24;
+
}
+
];
+
+
networking.interfaces.eth2.ipv4.addresses = [
+
{
+
address = "192.168.2.11";
+
prefixLength = 24;
+
}
+
];
+
}
+
{
+
ipv4 = "10.144.144.1";
+
listeners = [
+
"tcp://0.0.0.0:11010"
+
"wss://0.0.0.0:11011"
+
];
+
};
+
+
peer1 =
+
genPeer
+
{
+
virtualisation.vlans = [ 1 ];
+
}
+
{
+
ipv4 = "10.144.144.2";
+
peers = [ "tcp://192.168.1.11:11010" ];
+
};
+
+
peer2 =
+
genPeer
+
{
+
virtualisation.vlans = [ 2 ];
+
}
+
{
+
ipv4 = "10.144.144.3";
+
peers = [ "wss://192.168.2.11:11011" ];
+
};
+
};
+
+
testScript = ''
+
start_all()
+
+
relay.wait_for_unit("easytier-default.service")
+
peer1.wait_for_unit("easytier-default.service")
+
peer2.wait_for_unit("easytier-default.service")
+
+
# relay is accessible by the other hosts
+
peer1.succeed("ping -c5 192.168.1.11")
+
peer2.succeed("ping -c5 192.168.2.11")
+
+
# The other hosts are in separate vlans
+
peer1.fail("ping -c5 192.168.2.11")
+
peer2.fail("ping -c5 192.168.1.11")
+
+
# Each host can ping themselves through EasyTier
+
relay.succeed("ping -c5 10.144.144.1")
+
peer1.succeed("ping -c5 10.144.144.2")
+
peer2.succeed("ping -c5 10.144.144.3")
+
+
# Relay is accessible by the other hosts through EasyTier
+
peer1.succeed("ping -c5 10.144.144.1")
+
peer2.succeed("ping -c5 10.144.144.1")
+
+
# Relay can access the other hosts through EasyTier
+
relay.succeed("ping -c5 10.144.144.2")
+
relay.succeed("ping -c5 10.144.144.3")
+
+
# The other hosts in separate vlans can access each other through EasyTier
+
peer1.succeed("ping -c5 10.144.144.3")
+
peer2.succeed("ping -c5 10.144.144.2")
+
'';
+
}
+
)
+5 -1
pkgs/by-name/ea/easytier/package.nix
···
fetchFromGitHub,
rustPlatform,
protobuf,
+
nixosTests,
nix-update-script,
withQuic ? false, # with QUIC protocol support
}:
···
doCheck = false; # tests failed due to heavy rely on network
-
passthru.updateScript = nix-update-script { };
+
passthru = {
+
tests = { inherit (nixosTests) easytier; };
+
updateScript = nix-update-script { };
+
};
meta = {
homepage = "https://github.com/EasyTier/EasyTier";