0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 13:40:28 +03:00

nixos/easytier: init module

This commit is contained in:
L-Trump 2025-04-12 23:31:16 +08:00
parent 4206c4cb56
commit 725a756dbb
6 changed files with 422 additions and 1 deletions

View file

@ -88,6 +88,8 @@ Alongside many enhancements to NixOS modules and general system improvements, th
- [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).

View file

@ -1115,6 +1115,7 @@
./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

View file

@ -0,0 +1,292 @@
{
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
];
}

View file

@ -433,6 +433,7 @@ in
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 Normal file
View file

@ -0,0 +1,121 @@
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")
'';
}
)

View file

@ -4,6 +4,7 @@
fetchFromGitHub,
rustPlatform,
protobuf,
nixosTests,
nix-update-script,
withQuic ? false, # with QUIC protocol support
}:
@ -33,7 +34,10 @@ rustPlatform.buildRustPackage rec {
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";