nixos/wstunnel: convert to RFC42-style settings
This commit is contained in:
parent
48adf72af2
commit
3c853295d9
@ -67,6 +67,8 @@
|
|||||||
|
|
||||||
- `vmalert` now supports multiple instances with the option `services.vmalert.instances."".enable`
|
- `vmalert` now supports multiple instances with the option `services.vmalert.instances."".enable`
|
||||||
|
|
||||||
|
- The `wstunnel` module was converted to RFC42-style settings, you will need to update your NixOS config if you make use of this module.
|
||||||
|
|
||||||
## Other Notable Changes {#sec-release-25.11-notable-changes}
|
## Other Notable Changes {#sec-release-25.11-notable-changes}
|
||||||
|
|
||||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||||
|
@ -8,21 +8,29 @@
|
|||||||
let
|
let
|
||||||
cfg = config.services.wstunnel;
|
cfg = config.services.wstunnel;
|
||||||
|
|
||||||
hostPortToString = { host, port }: "${host}:${toString port}";
|
argsFormat = {
|
||||||
|
type =
|
||||||
hostPortSubmodule = {
|
let
|
||||||
options = {
|
inherit (lib.types)
|
||||||
host = lib.mkOption {
|
attrsOf
|
||||||
description = "The hostname.";
|
listOf
|
||||||
type = lib.types.str;
|
oneOf
|
||||||
};
|
bool
|
||||||
port = lib.mkOption {
|
int
|
||||||
description = "The port.";
|
str
|
||||||
type = lib.types.port;
|
;
|
||||||
};
|
in
|
||||||
};
|
attrsOf (oneOf [
|
||||||
|
bool
|
||||||
|
int
|
||||||
|
str
|
||||||
|
(listOf str)
|
||||||
|
]);
|
||||||
|
generate = lib.cli.toGNUCommandLineShell { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hostPortToString = { host, port, ... }: "${host}:${toString port}";
|
||||||
|
|
||||||
commonOptions = {
|
commonOptions = {
|
||||||
enable = lib.mkEnableOption "this `wstunnel` instance" // {
|
enable = lib.mkEnableOption "this `wstunnel` instance" // {
|
||||||
default = true;
|
default = true;
|
||||||
@ -34,39 +42,6 @@ let
|
|||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
extraArgs = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
Extra command line arguments to pass to `wstunnel`.
|
|
||||||
Attributes of the form `argName = true;` will be translated to `--argName`,
|
|
||||||
and `argName = \"value\"` to `--argName value`.
|
|
||||||
'';
|
|
||||||
type = with lib.types; attrsOf (either str bool);
|
|
||||||
default = { };
|
|
||||||
example = {
|
|
||||||
"someNewOption" = true;
|
|
||||||
"someNewOptionWithValue" = "someValue";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval.
|
|
||||||
websocketPingInterval = lib.mkOption {
|
|
||||||
description = "Frequency at which the client will send websocket ping to the server.";
|
|
||||||
type = lib.types.nullOr lib.types.ints.unsigned;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
loggingLevel = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
Passed to --log-lvl
|
|
||||||
|
|
||||||
Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
|
|
||||||
For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax)
|
|
||||||
'';
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
example = "INFO";
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
environmentFile = lib.mkOption {
|
environmentFile = lib.mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Environment file to be passed to the systemd service.
|
Environment file to be passed to the systemd service.
|
||||||
@ -83,8 +58,45 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
serverSubmodule =
|
serverSubmodule =
|
||||||
|
let
|
||||||
|
outerConfig = config;
|
||||||
|
in
|
||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
certConfig = outerConfig.security.acme.certs.${config.useACMEHost};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
|
imports =
|
||||||
|
[
|
||||||
|
../../misc/assertions.nix
|
||||||
|
|
||||||
|
(lib.mkRenamedOptionModule
|
||||||
|
[
|
||||||
|
"enableHTTPS"
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"listen"
|
||||||
|
"enableHTTPS"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
++ lib.map
|
||||||
|
(
|
||||||
|
option:
|
||||||
|
lib.mkRemovedOptionModule [ option ] ''
|
||||||
|
The wstunnel module now uses RFC-42-style settings, please modify your config accordingly
|
||||||
|
''
|
||||||
|
)
|
||||||
|
[
|
||||||
|
"extraArgs"
|
||||||
|
"websocketPingInterval"
|
||||||
|
"loggingLevel"
|
||||||
|
|
||||||
|
"restrictTo"
|
||||||
|
"tlsCertificate"
|
||||||
|
"tlsKey"
|
||||||
|
];
|
||||||
|
|
||||||
options = commonOptions // {
|
options = commonOptions // {
|
||||||
listen = lib.mkOption {
|
listen = lib.mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
@ -92,57 +104,36 @@ let
|
|||||||
Setting the port to a value below 1024 will also give the process
|
Setting the port to a value below 1024 will also give the process
|
||||||
the required `CAP_NET_BIND_SERVICE` capability.
|
the required `CAP_NET_BIND_SERVICE` capability.
|
||||||
'';
|
'';
|
||||||
type = lib.types.submodule hostPortSubmodule;
|
type = lib.types.submodule {
|
||||||
default = {
|
options = {
|
||||||
host = "0.0.0.0";
|
host = lib.mkOption {
|
||||||
port = if config.enableHTTPS then 443 else 80;
|
description = "The hostname.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
port = lib.mkOption {
|
||||||
|
description = "The port.";
|
||||||
|
type = lib.types.port;
|
||||||
|
};
|
||||||
|
enableHTTPS = lib.mkOption {
|
||||||
|
description = "Use HTTPS for the tunnel server.";
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
defaultText = lib.literalExpression ''
|
default =
|
||||||
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
host = "0.0.0.0";
|
host = "0.0.0.0";
|
||||||
port = if enableHTTPS then 443 else 80;
|
port = if config.enableHTTPS then 443 else 80;
|
||||||
}
|
};
|
||||||
'';
|
defaultText = lib.literalExpression ''
|
||||||
};
|
{ config, ... }:
|
||||||
|
|
||||||
restrictTo = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
Accepted traffic will be forwarded only to this service.
|
|
||||||
'';
|
|
||||||
type = lib.types.listOf (lib.types.submodule hostPortSubmodule);
|
|
||||||
default = [ ];
|
|
||||||
example = [
|
|
||||||
{
|
{
|
||||||
host = "127.0.0.1";
|
host = "0.0.0.0";
|
||||||
port = 51820;
|
port = if config.enableHTTPS then 443 else 80;
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
enableHTTPS = lib.mkOption {
|
|
||||||
description = "Use HTTPS for the tunnel server.";
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
tlsCertificate = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
TLS certificate to use instead of the hardcoded one in case of HTTPS connections.
|
|
||||||
Use together with `tlsKey`.
|
|
||||||
'';
|
'';
|
||||||
type = lib.types.nullOr lib.types.path;
|
|
||||||
default = null;
|
|
||||||
example = "/var/lib/secrets/cert.pem";
|
|
||||||
};
|
|
||||||
|
|
||||||
tlsKey = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
TLS key to use instead of the hardcoded on in case of HTTPS connections.
|
|
||||||
Use together with `tlsCertificate`.
|
|
||||||
'';
|
|
||||||
type = lib.types.nullOr lib.types.path;
|
|
||||||
default = null;
|
|
||||||
example = "/var/lib/secrets/key.pem";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useACMEHost = lib.mkOption {
|
useACMEHost = lib.mkOption {
|
||||||
@ -154,12 +145,93 @@ let
|
|||||||
default = null;
|
default = null;
|
||||||
example = "example.com";
|
example = "example.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
freeformType = argsFormat.type;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
restrict-to = lib.mkOption {
|
||||||
|
type = lib.types.listOf (
|
||||||
|
lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
host = lib.mkOption {
|
||||||
|
description = "The hostname.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
port = lib.mkOption {
|
||||||
|
description = "The port.";
|
||||||
|
type = lib.types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
default = [ ];
|
||||||
|
example = [
|
||||||
|
{
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = 51820;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
Restrictions on the connections that the server will accept.
|
||||||
|
For more flexibility, and the possibility to also allow reverse tunnels,
|
||||||
|
look into the `restrict-config` option that takes a path to a yaml file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Command line arguments to pass to `wstunnel`.
|
||||||
|
Attributes of the form `argName = true;` will be translated to `--argName`,
|
||||||
|
and `argName = \"value\"` to `--argName value`.
|
||||||
|
'';
|
||||||
|
example = {
|
||||||
|
"someNewOption" = true;
|
||||||
|
"someNewOptionWithValue" = "someValue";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
settings = lib.mkIf (config.useACMEHost != null) {
|
||||||
|
tls-certificate = "${certConfig.directory}/fullchain.pem";
|
||||||
|
tls-private-key = "${certConfig.directory}/key.pem";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSubmodule =
|
clientSubmodule =
|
||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
|
imports =
|
||||||
|
[
|
||||||
|
../../misc/assertions.nix
|
||||||
|
]
|
||||||
|
++ lib.map
|
||||||
|
(
|
||||||
|
option:
|
||||||
|
lib.mkRemovedOptionModule [ option ] ''
|
||||||
|
The wstunnel module now uses RFC-42-style settings, please modify your config accordingly
|
||||||
|
''
|
||||||
|
)
|
||||||
|
[
|
||||||
|
"extraArgs"
|
||||||
|
"websocketPingInterval"
|
||||||
|
"loggingLevel"
|
||||||
|
|
||||||
|
"localToRemote"
|
||||||
|
"remoteToLocal"
|
||||||
|
"httpProxy"
|
||||||
|
"soMark"
|
||||||
|
"upgradePathPrefix"
|
||||||
|
"tlsSNI"
|
||||||
|
"tlsVerifyCertificate"
|
||||||
|
"upgradeCredentials"
|
||||||
|
"customHeaders"
|
||||||
|
];
|
||||||
|
|
||||||
options = commonOptions // {
|
options = commonOptions // {
|
||||||
connectTo = lib.mkOption {
|
connectTo = lib.mkOption {
|
||||||
description = "Server address and port to connect to.";
|
description = "Server address and port to connect to.";
|
||||||
@ -167,102 +239,36 @@ let
|
|||||||
example = "https://wstunnel.server.com:8443";
|
example = "https://wstunnel.server.com:8443";
|
||||||
};
|
};
|
||||||
|
|
||||||
localToRemote = lib.mkOption {
|
|
||||||
description = "Listen on local and forwards traffic from remote.";
|
|
||||||
type = lib.types.listOf (lib.types.str);
|
|
||||||
default = [ ];
|
|
||||||
example = [
|
|
||||||
"tcp://1212:google.com:443"
|
|
||||||
"unix:///tmp/wstunnel.sock:g.com:443"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
remoteToLocal = lib.mkOption {
|
|
||||||
description = "Listen on remote and forwards traffic from local. Only tcp is supported";
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
default = [ ];
|
|
||||||
example = [
|
|
||||||
"tcp://1212:google.com:443"
|
|
||||||
"unix://wstunnel.sock:g.com:443"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
|
addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
|
||||||
|
|
||||||
httpProxy = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.submodule {
|
||||||
Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`).
|
freeformType = argsFormat.type;
|
||||||
|
|
||||||
::: {.warning}
|
options = {
|
||||||
Passwords specified here will be world-readable in the Nix store!
|
http-headers = lib.mkOption {
|
||||||
To pass a password to the service, point the `environmentFile` option
|
type = lib.types.coercedTo (lib.types.attrsOf lib.types.str) (lib.mapAttrsToList (
|
||||||
to a file containing `PROXY_PASSWORD=<your-password-here>` and set
|
n: v: "${n}:${v}"
|
||||||
this option to `<user>:$PROXY_PASSWORD@<host>:<port>`.
|
)) (lib.types.listOf lib.types.str);
|
||||||
Note however that this will also locally leak the passwords at
|
default = { };
|
||||||
runtime via e.g. /proc/<pid>/cmdline.
|
example = {
|
||||||
:::
|
"X-Some-Header" = "some-value";
|
||||||
'';
|
};
|
||||||
type = lib.types.nullOr lib.types.str;
|
description = ''
|
||||||
default = null;
|
Custom headers to send in the upgrade request
|
||||||
};
|
'';
|
||||||
|
};
|
||||||
soMark = lib.mkOption {
|
};
|
||||||
description = ''
|
};
|
||||||
Mark network packets with the SO_MARK sockoption with the specified value.
|
|
||||||
Setting this option will also enable the required `CAP_NET_ADMIN` capability
|
|
||||||
for the systemd service.
|
|
||||||
'';
|
|
||||||
type = lib.types.nullOr lib.types.ints.unsigned;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
upgradePathPrefix = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
Use a specific HTTP path prefix that will show up in the upgrade
|
|
||||||
request to the `wstunnel` server.
|
|
||||||
Useful when running `wstunnel` behind a reverse proxy.
|
|
||||||
'';
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
example = "wstunnel";
|
|
||||||
};
|
|
||||||
|
|
||||||
tlsSNI = lib.mkOption {
|
|
||||||
description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls.";
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
tlsVerifyCertificate = lib.mkOption {
|
|
||||||
description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option.";
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
upgradeCredentials = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
Use these credentials to authenticate during the HTTP upgrade request
|
|
||||||
(Basic authorization type, `USER:[PASS]`).
|
|
||||||
|
|
||||||
::: {.warning}
|
|
||||||
Passwords specified here will be world-readable in the Nix store!
|
|
||||||
To pass a password to the service, point the `environmentFile` option
|
|
||||||
to a file containing `HTTP_PASSWORD=<your-password-here>` and set this
|
|
||||||
option to `<user>:$HTTP_PASSWORD`.
|
|
||||||
Note however that this will also locally leak the passwords at runtime
|
|
||||||
via e.g. /proc/<pid>/cmdline.
|
|
||||||
:::
|
|
||||||
'';
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
customHeaders = lib.mkOption {
|
|
||||||
description = "Custom HTTP headers to send during the upgrade request.";
|
|
||||||
type = lib.types.attrsOf lib.types.str;
|
|
||||||
default = { };
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Command line arguments to pass to `wstunnel`.
|
||||||
|
Attributes of the form `argName = true;` will be translated to `--argName`,
|
||||||
|
and `argName = \"value\"` to `--argName value`.
|
||||||
|
'';
|
||||||
example = {
|
example = {
|
||||||
"X-Some-Header" = "some-value";
|
"someNewOption" = true;
|
||||||
|
"someNewOptionWithValue" = "someValue";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -286,8 +292,6 @@ let
|
|||||||
];
|
];
|
||||||
wantedBy = lib.optional serverCfg.autoStart "multi-user.target";
|
wantedBy = lib.optional serverCfg.autoStart "multi-user.target";
|
||||||
|
|
||||||
environment.RUST_LOG = serverCfg.loggingLevel;
|
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "exec";
|
Type = "exec";
|
||||||
EnvironmentFile = lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
|
EnvironmentFile = lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
|
||||||
@ -296,7 +300,13 @@ let
|
|||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
AmbientCapabilities = lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
|
AmbientCapabilities = lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
|
||||||
NoNewPrivileges = true;
|
NoNewPrivileges = true;
|
||||||
RestrictNamespaces = "uts ipc pid user cgroup";
|
RestrictNamespaces = [
|
||||||
|
"uts"
|
||||||
|
"ipc"
|
||||||
|
"pid"
|
||||||
|
"user"
|
||||||
|
"cgroup"
|
||||||
|
];
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
ProtectHome = true;
|
ProtectHome = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectKernelTunables = true;
|
||||||
@ -309,35 +319,22 @@ let
|
|||||||
RestartSec = 2;
|
RestartSec = 2;
|
||||||
RestartSteps = 20;
|
RestartSteps = 20;
|
||||||
RestartMaxDelaySec = "5min";
|
RestartMaxDelaySec = "5min";
|
||||||
};
|
|
||||||
|
|
||||||
script = with serverCfg; ''
|
ExecStart =
|
||||||
${lib.getExe package} \
|
let
|
||||||
server \
|
convertedSettings = serverCfg.settings // {
|
||||||
${
|
restrict-to = lib.map hostPortToString serverCfg.settings.restrict-to;
|
||||||
lib.cli.toGNUCommandLineShell { } (
|
};
|
||||||
lib.recursiveUpdate {
|
in
|
||||||
restrict-to = map hostPortToString restrictTo;
|
''
|
||||||
websocket-ping-frequency-sec = websocketPingInterval;
|
${lib.getExe serverCfg.package} \
|
||||||
tls-certificate =
|
server \
|
||||||
if !enableHTTPS then
|
${argsFormat.generate convertedSettings} \
|
||||||
null
|
${lib.escapeShellArg "${
|
||||||
else if useACMEHost != null then
|
if serverCfg.listen.enableHTTPS then "wss" else "ws"
|
||||||
"${certConfig.directory}/fullchain.pem"
|
}://${hostPortToString serverCfg.listen}"}
|
||||||
else
|
'';
|
||||||
"${tlsCertificate}";
|
};
|
||||||
tls-private-key =
|
|
||||||
if !enableHTTPS then
|
|
||||||
null
|
|
||||||
else if useACMEHost != null then
|
|
||||||
"${certConfig.directory}/key.pem"
|
|
||||||
else
|
|
||||||
"${tlsKey}";
|
|
||||||
} extraArgs
|
|
||||||
)
|
|
||||||
} \
|
|
||||||
${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -355,8 +352,6 @@ let
|
|||||||
];
|
];
|
||||||
wantedBy = lib.optional clientCfg.autoStart "multi-user.target";
|
wantedBy = lib.optional clientCfg.autoStart "multi-user.target";
|
||||||
|
|
||||||
environment.RUST_LOG = clientCfg.loggingLevel;
|
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "exec";
|
Type = "exec";
|
||||||
EnvironmentFile = lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
|
EnvironmentFile = lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
|
||||||
@ -364,9 +359,15 @@ let
|
|||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
AmbientCapabilities =
|
AmbientCapabilities =
|
||||||
(lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ])
|
(lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ])
|
||||||
++ (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]);
|
++ (lib.optionals ((clientCfg.settings.socket-so-mark or null) != null) [ "CAP_NET_ADMIN" ]);
|
||||||
NoNewPrivileges = true;
|
NoNewPrivileges = true;
|
||||||
RestrictNamespaces = "uts ipc pid user cgroup";
|
RestrictNamespaces = [
|
||||||
|
"uts"
|
||||||
|
"ipc"
|
||||||
|
"pid"
|
||||||
|
"user"
|
||||||
|
"cgroup"
|
||||||
|
];
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
ProtectHome = true;
|
ProtectHome = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectKernelTunables = true;
|
||||||
@ -379,29 +380,14 @@ let
|
|||||||
RestartSec = 2;
|
RestartSec = 2;
|
||||||
RestartSteps = 20;
|
RestartSteps = 20;
|
||||||
RestartMaxDelaySec = "5min";
|
RestartMaxDelaySec = "5min";
|
||||||
};
|
|
||||||
|
|
||||||
script = with clientCfg; ''
|
ExecStart = ''
|
||||||
${lib.getExe package} \
|
${lib.getExe clientCfg.package} \
|
||||||
client \
|
client \
|
||||||
${
|
${argsFormat.generate clientCfg.settings} \
|
||||||
lib.cli.toGNUCommandLineShell { } (
|
${lib.escapeShellArg clientCfg.connectTo}
|
||||||
lib.recursiveUpdate {
|
'';
|
||||||
local-to-remote = localToRemote;
|
};
|
||||||
remote-to-local = remoteToLocal;
|
|
||||||
http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders;
|
|
||||||
http-proxy = httpProxy;
|
|
||||||
socket-so-mark = soMark;
|
|
||||||
http-upgrade-path-prefix = upgradePathPrefix;
|
|
||||||
tls-sni-override = tlsSNI;
|
|
||||||
tls-verify-certificate = tlsVerifyCertificate;
|
|
||||||
websocket-ping-frequency-sec = websocketPingInterval;
|
|
||||||
http-upgrade-credentials = upgradeCredentials;
|
|
||||||
} extraArgs
|
|
||||||
)
|
|
||||||
} \
|
|
||||||
${lib.escapeShellArg connectTo}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
@ -418,16 +404,18 @@ in
|
|||||||
listen = {
|
listen = {
|
||||||
host = "0.0.0.0";
|
host = "0.0.0.0";
|
||||||
port = 8080;
|
port = 8080;
|
||||||
|
enableHTTPS = true;
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
tls-certificate = "/var/lib/secrets/fullchain.pem";
|
||||||
|
tls-private-key = "/var/lib/secrets/key.pem";
|
||||||
|
restrict-to = [
|
||||||
|
{
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = 51820;
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
enableHTTPS = true;
|
|
||||||
tlsCertificate = "/var/lib/secrets/fullchain.pem";
|
|
||||||
tlsKey = "/var/lib/secrets/key.pem";
|
|
||||||
restrictTo = [
|
|
||||||
{
|
|
||||||
host = "127.0.0.1";
|
|
||||||
port = 51820;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -454,35 +442,56 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
systemd.services =
|
systemd.services =
|
||||||
(lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers))
|
(lib.mapAttrs' generateServerUnit (lib.filterAttrs (_: v: v.enable) cfg.servers))
|
||||||
// (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients));
|
// (lib.mapAttrs' generateClientUnit (lib.filterAttrs (_: v: v.enable) cfg.clients));
|
||||||
|
|
||||||
assertions =
|
assertions =
|
||||||
(lib.mapAttrsToList (name: serverCfg: {
|
(lib.mapAttrsToList (name: serverCfg: {
|
||||||
assertion = !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null);
|
assertion =
|
||||||
|
serverCfg.listen.enableHTTPS
|
||||||
|
->
|
||||||
|
(serverCfg.useACMEHost != null)
|
||||||
|
|| (
|
||||||
|
(serverCfg.settings.tls-certificate or null) != null
|
||||||
|
&& (serverCfg.settings.tls-private-key or null) != null
|
||||||
|
);
|
||||||
message = ''
|
message = ''
|
||||||
Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
|
If services.wstunnel.servers."${name}".listen.enableHTTPS is set to true, either services.wstunnel.servers."${name}".useACMEHost or both services.wstunnel.servers."${name}".settings.tls-private-key and services.wstunnel.servers."${name}".settings.tls-certificate need to be set.
|
||||||
'';
|
'';
|
||||||
}) cfg.servers)
|
}) cfg.servers)
|
||||||
++
|
++ (lib.foldlAttrs (
|
||||||
|
assertions: _: server:
|
||||||
|
assertions ++ server.assertions
|
||||||
|
) [ ] cfg.servers)
|
||||||
|
|
||||||
(lib.mapAttrsToList (name: serverCfg: {
|
++ (lib.mapAttrsToList (
|
||||||
|
name: clientCfg:
|
||||||
|
let
|
||||||
|
isListAttrDefined = settings: attr: (settings.${attr} or [ ]) != [ ];
|
||||||
|
in
|
||||||
|
{
|
||||||
assertion =
|
assertion =
|
||||||
serverCfg.enableHTTPS
|
isListAttrDefined clientCfg.settings "local-to-remote"
|
||||||
->
|
|| isListAttrDefined clientCfg.settings "remote-to-local";
|
||||||
(serverCfg.useACMEHost != null) || (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null);
|
|
||||||
message = ''
|
message = ''
|
||||||
If services.wstunnel.servers."${name}".enableHTTPS is set to true, either services.wstunnel.servers."${name}".useACMEHost or both services.wstunnel.servers."${name}".tlsKey and services.wstunnel.servers."${name}".tlsCertificate need to be set.
|
Either one of services.wstunnel.clients."${name}".settings.local-to-remote or services.wstunnel.clients."${name}".settings.remote-to-local must be set.
|
||||||
'';
|
'';
|
||||||
}) cfg.servers)
|
}
|
||||||
++
|
) cfg.clients)
|
||||||
|
++ (lib.foldlAttrs (
|
||||||
|
assertions: _: client:
|
||||||
|
assertions ++ client.assertions
|
||||||
|
) [ ] cfg.clients);
|
||||||
|
|
||||||
(lib.mapAttrsToList (name: clientCfg: {
|
warnings =
|
||||||
assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]);
|
(lib.foldlAttrs (
|
||||||
message = ''
|
warnings: _: server:
|
||||||
Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set.
|
warnings ++ server.warnings
|
||||||
'';
|
) [ ] cfg.servers)
|
||||||
}) cfg.clients);
|
++ (lib.foldlAttrs (
|
||||||
|
warnings: _: client:
|
||||||
|
warnings ++ client.warnings
|
||||||
|
) [ ] cfg.clients);
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.maintainers = with lib.maintainers; [
|
meta.maintainers = with lib.maintainers; [
|
||||||
|
@ -30,8 +30,10 @@ in
|
|||||||
host = "10.0.0.1";
|
host = "10.0.0.1";
|
||||||
port = 443;
|
port = 443;
|
||||||
};
|
};
|
||||||
tlsCertificate = certs.${domain}.cert;
|
settings = {
|
||||||
tlsKey = certs.${domain}.key;
|
tls-certificate = "${certs.${domain}.cert}";
|
||||||
|
tls-private-key = "${certs.${domain}.key}";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -45,9 +47,9 @@ in
|
|||||||
useNetworkd = true;
|
useNetworkd = true;
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
firewall.enable = false;
|
firewall.enable = false;
|
||||||
extraHosts = ''
|
hosts = {
|
||||||
10.0.0.1 ${domain}
|
"10.0.0.1" = [ domain ];
|
||||||
'';
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.network.networks."01-eth1" = {
|
systemd.network.networks."01-eth1" = {
|
||||||
@ -60,8 +62,10 @@ in
|
|||||||
clients.my-client = {
|
clients.my-client = {
|
||||||
autoStart = false;
|
autoStart = false;
|
||||||
connectTo = "wss://${domain}:443";
|
connectTo = "wss://${domain}:443";
|
||||||
localToRemote = [ "tcp://8080:localhost:2080" ];
|
settings = {
|
||||||
remoteToLocal = [ "tcp://2081:localhost:8081" ];
|
local-to-remote = [ "tcp://8080:localhost:2080" ];
|
||||||
|
remote-to-local = [ "tcp://2081:localhost:8081" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user