nixos/frp: support multiple instances
This commit is contained in:
@@ -922,7 +922,7 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
|
||||
|
||||
- [frp](https://github.com/fatedier/frp), a fast reverse proxy to help you
|
||||
expose a local server behind a NAT or firewall to the Internet. Available as
|
||||
[services.frp](#opt-services.frp.enable).
|
||||
`services.frp`.
|
||||
|
||||
- [river](https://github.com/riverwm/river), A dynamic tiling wayland
|
||||
compositor. Available as `programs.river`.
|
||||
|
||||
@@ -26,4 +26,6 @@
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- `services.frp` now supports multiple instances through `services.frp.instances` to make it possible to run multiple frp clients or servers at the same time.
|
||||
|
||||
- `services.openssh` now supports generating host SSH keys by setting `services.openssh.generateHostKeys = true` while leaving `services.openssh.enable` disabled. This is particularly useful for systems that have no need of an SSH daemon but want SSH host keys for other purposes such as using agenix or sops-nix.
|
||||
|
||||
@@ -7,95 +7,121 @@
|
||||
let
|
||||
cfg = config.services.frp;
|
||||
settingsFormat = pkgs.formats.toml { };
|
||||
configFile = settingsFormat.generate "frp.toml" cfg.settings;
|
||||
isClient = (cfg.role == "client");
|
||||
isServer = (cfg.role == "server");
|
||||
enabledInstances = lib.filterAttrs (name: conf: conf.enable) cfg.instances;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "frp" "enable" ]
|
||||
[ "services" "frp" "instances" "" "enable" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule [ "services" "frp" "role" ] [ "services" "frp" "instances" "" "role" ])
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "frp" "settings" ]
|
||||
[ "services" "frp" "instances" "" "settings" ]
|
||||
)
|
||||
];
|
||||
|
||||
options = {
|
||||
services.frp = {
|
||||
enable = lib.mkEnableOption "frp";
|
||||
instances = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "frp";
|
||||
|
||||
package = lib.mkPackageOption pkgs "frp" { };
|
||||
role = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"server"
|
||||
"client"
|
||||
];
|
||||
description = ''
|
||||
The frp consists of `client` and `server`. The server is usually
|
||||
deployed on the machine with a public IP address, and
|
||||
the client is usually deployed on the machine
|
||||
where the Intranet service to be penetrated resides.
|
||||
'';
|
||||
};
|
||||
|
||||
role = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"server"
|
||||
"client"
|
||||
];
|
||||
description = ''
|
||||
The frp consists of `client` and `server`. The server is usually
|
||||
deployed on the machine with a public IP address, and
|
||||
the client is usually deployed on the machine
|
||||
where the Intranet service to be penetrated resides.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = settingsFormat.type;
|
||||
settings = lib.mkOption {
|
||||
type = settingsFormat.type;
|
||||
default = { };
|
||||
description = ''
|
||||
Frp configuration, for configuration options
|
||||
see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml)
|
||||
or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml) on github.
|
||||
'';
|
||||
example = {
|
||||
serverAddr = "x.x.x.x";
|
||||
serverPort = 7000;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
Frp configuration, for configuration options
|
||||
see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml)
|
||||
or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml) on github.
|
||||
Frp instances.
|
||||
'';
|
||||
example = {
|
||||
serverAddr = "x.x.x.x";
|
||||
serverPort = 7000;
|
||||
};
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "frp" { };
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
serviceCapability = lib.optionals isServer [ "CAP_NET_BIND_SERVICE" ];
|
||||
executableFile = if isClient then "frpc" else "frps";
|
||||
in
|
||||
lib.mkIf cfg.enable {
|
||||
systemd.services = {
|
||||
frp = {
|
||||
wants = lib.optionals isClient [ "network-online.target" ];
|
||||
after = if isClient then [ "network-online.target" ] else [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
description = "A fast reverse proxy frp ${cfg.role}";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 15;
|
||||
ExecStart = "${cfg.package}/bin/${executableFile} --strict_config -c ${configFile}";
|
||||
DynamicUser = true;
|
||||
# Hardening
|
||||
CapabilityBoundingSet = serviceCapability;
|
||||
AmbientCapabilities = serviceCapability;
|
||||
PrivateDevices = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
]
|
||||
++ lib.optionals isClient [ "AF_UNIX" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
PrivateMounts = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" ];
|
||||
}
|
||||
// lib.optionalAttrs isServer {
|
||||
StateDirectory = "frp";
|
||||
StateDirectoryMode = "0700";
|
||||
UMask = "0007";
|
||||
};
|
||||
config = lib.mkIf (enabledInstances != { }) {
|
||||
systemd.services = lib.mapAttrs' (
|
||||
instance: options:
|
||||
let
|
||||
serviceName = "frp" + lib.optionalString (instance != "") ("-" + instance);
|
||||
configFile = settingsFormat.generate "${serviceName}.toml" options.settings;
|
||||
isClient = (options.role == "client");
|
||||
isServer = (options.role == "server");
|
||||
serviceCapability = lib.optionals isServer [ "CAP_NET_BIND_SERVICE" ];
|
||||
executableFile = if isClient then "frpc" else "frps";
|
||||
in
|
||||
lib.nameValuePair serviceName {
|
||||
wants = lib.optionals isClient [ "network-online.target" ];
|
||||
after = if isClient then [ "network-online.target" ] else [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
description = "A fast reverse proxy frp ${options.role} for instance ${instance}";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 15;
|
||||
ExecStart = "${cfg.package}/bin/${executableFile} --strict_config -c ${configFile}";
|
||||
DynamicUser = true;
|
||||
# Hardening
|
||||
CapabilityBoundingSet = serviceCapability;
|
||||
AmbientCapabilities = serviceCapability;
|
||||
PrivateDevices = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
]
|
||||
++ lib.optionals isClient [ "AF_UNIX" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
PrivateMounts = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" ];
|
||||
}
|
||||
// lib.optionalAttrs isServer {
|
||||
StateDirectory = "frp";
|
||||
StateDirectoryMode = "0700";
|
||||
UMask = "0007";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
) enabledInstances;
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ zaldnoay ];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user