mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-21 22:43:01 +00:00
nixos/rathole: init module
Adds a module for rathole package. The package itself and this module is very similar to frp, so the options and tests are not very far off from those for frp.
This commit is contained in:
parent
918a1f795e
commit
e3e6e94010
@ -68,6 +68,8 @@
|
||||
|
||||
- [OpenGFW](https://github.com/apernet/OpenGFW), an implementation of the Great Firewall on Linux. Available as [services.opengfw](#opt-services.opengfw.enable).
|
||||
|
||||
- [Rathole](https://github.com/rapiz1/rathole), a lightweight and high-performance reverse proxy for NAT traversal. Available as [services.rathole](#opt-services.rathole.enable).
|
||||
|
||||
## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
|
||||
|
||||
- `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage:
|
||||
|
@ -1160,6 +1160,7 @@
|
||||
./services/networking/r53-ddns.nix
|
||||
./services/networking/radicale.nix
|
||||
./services/networking/radvd.nix
|
||||
./services/networking/rathole.nix
|
||||
./services/networking/rdnssd.nix
|
||||
./services/networking/realm.nix
|
||||
./services/networking/redsocks.nix
|
||||
|
165
nixos/modules/services/networking/rathole.nix
Normal file
165
nixos/modules/services/networking/rathole.nix
Normal file
@ -0,0 +1,165 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.rathole;
|
||||
settingsFormat = pkgs.formats.toml { };
|
||||
py-toml-merge =
|
||||
pkgs.writers.writePython3Bin "py-toml-merge"
|
||||
{
|
||||
libraries = with pkgs.python3Packages; [
|
||||
tomli-w
|
||||
mergedeep
|
||||
];
|
||||
}
|
||||
''
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import tomli_w
|
||||
import tomllib
|
||||
from mergedeep import merge
|
||||
|
||||
parser = argparse.ArgumentParser(description="Merge multiple TOML files")
|
||||
parser.add_argument(
|
||||
"files",
|
||||
type=Path,
|
||||
nargs="+",
|
||||
help="List of TOML files to merge",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
merged: dict[str, Any] = {}
|
||||
|
||||
for file in args.files:
|
||||
with open(file, "rb") as fh:
|
||||
loaded_toml = tomllib.load(fh)
|
||||
merged = merge(merged, loaded_toml)
|
||||
|
||||
print(tomli_w.dumps(merged))
|
||||
'';
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
services.rathole = {
|
||||
enable = lib.mkEnableOption "Rathole";
|
||||
|
||||
package = lib.mkPackageOption pkgs "rathole" { };
|
||||
|
||||
role = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"server"
|
||||
"client"
|
||||
];
|
||||
description = ''
|
||||
Select whether rathole needs to be run as a `client` or a `server`.
|
||||
Server is a machine with a public IP and client is a device behind NAT,
|
||||
but running some services that need to be exposed to the Internet.
|
||||
'';
|
||||
};
|
||||
|
||||
credentialsFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = "/dev/null";
|
||||
description = ''
|
||||
Path to a TOML file to be merged with the settings.
|
||||
Useful to set secret config parameters like tokens, which
|
||||
should not appear in the Nix Store.
|
||||
'';
|
||||
example = "/var/lib/secrets/rathole/config.toml";
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = settingsFormat.type;
|
||||
default = { };
|
||||
description = ''
|
||||
Rathole configuration, for options reference
|
||||
see the [example](https://github.com/rapiz1/rathole?tab=readme-ov-file#configuration) on GitHub.
|
||||
Both server and client configurations can be specified at the same time, regardless of the selected role.
|
||||
'';
|
||||
example = {
|
||||
server = {
|
||||
bind_addr = "0.0.0.0:2333";
|
||||
services.my_nas_ssh = {
|
||||
token = "use_a_secret_that_only_you_know";
|
||||
bind_addr = "0.0.0.0:5202";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.rathole = {
|
||||
requires = [ "network.target" ];
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
description = "Rathole ${cfg.role} Service";
|
||||
|
||||
serviceConfig =
|
||||
let
|
||||
name = "rathole";
|
||||
configFile = settingsFormat.generate "${name}.toml" cfg.settings;
|
||||
runtimeDir = "/run/${name}";
|
||||
ratholePrestart =
|
||||
"+"
|
||||
+ (pkgs.writeShellScript "rathole-prestart" ''
|
||||
DYNUSER_UID=$(stat -c %u ${runtimeDir})
|
||||
DYNUSER_GID=$(stat -c %g ${runtimeDir})
|
||||
${lib.getExe py-toml-merge} ${configFile} '${cfg.credentialsFile}' |
|
||||
install -m 600 -o $DYNUSER_UID -g $DYNUSER_GID /dev/stdin ${runtimeDir}/${mergedConfigName}
|
||||
'');
|
||||
mergedConfigName = "merged.toml";
|
||||
in
|
||||
{
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
ExecStartPre = ratholePrestart;
|
||||
ExecStart = "${lib.getExe cfg.package} --${cfg.role} ${runtimeDir}/${mergedConfigName}";
|
||||
DynamicUser = true;
|
||||
LimitNOFILE = "1048576";
|
||||
RuntimeDirectory = name;
|
||||
RuntimeDirectoryMode = "0700";
|
||||
# Hardening
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateTmp = true;
|
||||
# PrivateUsers=true breaks AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0066";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ xokdvium ];
|
||||
}
|
@ -823,6 +823,7 @@ in {
|
||||
radicle = runTest ./radicle.nix;
|
||||
ragnarwm = handleTest ./ragnarwm.nix {};
|
||||
rasdaemon = handleTest ./rasdaemon.nix {};
|
||||
rathole = handleTest ./rathole.nix {};
|
||||
readarr = handleTest ./readarr.nix {};
|
||||
realm = handleTest ./realm.nix {};
|
||||
redis = handleTest ./redis.nix {};
|
||||
|
89
nixos/tests/rathole.nix
Normal file
89
nixos/tests/rathole.nix
Normal file
@ -0,0 +1,89 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
successMessage = "Success 3333115147933743662";
|
||||
in
|
||||
{
|
||||
name = "rathole";
|
||||
meta.maintainers = with lib.maintainers; [ xokdvium ];
|
||||
nodes = {
|
||||
server = {
|
||||
networking = {
|
||||
useNetworkd = true;
|
||||
useDHCP = false;
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.1/24";
|
||||
};
|
||||
|
||||
services.rathole = {
|
||||
enable = true;
|
||||
role = "server";
|
||||
settings = {
|
||||
server = {
|
||||
bind_addr = "0.0.0.0:2333";
|
||||
services = {
|
||||
success-message = {
|
||||
bind_addr = "0.0.0.0:80";
|
||||
token = "hunter2";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
client = {
|
||||
networking = {
|
||||
useNetworkd = true;
|
||||
useDHCP = false;
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.2/24";
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts."127.0.0.1" = {
|
||||
root = pkgs.writeTextDir "success-message.txt" successMessage;
|
||||
};
|
||||
};
|
||||
|
||||
services.rathole = {
|
||||
enable = true;
|
||||
role = "client";
|
||||
credentialsFile = pkgs.writeText "rathole-credentials.toml" ''
|
||||
[client.services.success-message]
|
||||
token = "hunter2"
|
||||
'';
|
||||
settings = {
|
||||
client = {
|
||||
remote_addr = "10.0.0.1:2333";
|
||||
services.success-message = {
|
||||
local_addr = "127.0.0.1:80";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
server.wait_for_unit("rathole.service")
|
||||
server.wait_for_open_port(2333)
|
||||
client.wait_for_unit("rathole.service")
|
||||
server.wait_for_open_port(80)
|
||||
response = server.succeed("curl http://127.0.0.1/success-message.txt")
|
||||
assert "${successMessage}" in response, "Got invalid response"
|
||||
response = client.succeed("curl http://10.0.0.1/success-message.txt")
|
||||
assert "${successMessage}" in response, "Got invalid response"
|
||||
'';
|
||||
}
|
||||
)
|
Loading…
Reference in New Issue
Block a user