Files

183 lines
6.1 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.frp;
settingsFormat = pkgs.formats.toml { };
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 = {
instances = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
enable = lib.mkEnableOption "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.
'';
};
environmentFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
description = ''
List of paths files that follows systemd environmentfile structure.
Can be used to pass secrets to settings attribute.
Example content of a file: SECRET_TOKEN=1234
'';
default = [ ];
};
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;
proxies = [
{
name = "ssh";
type = "tcp";
localIP = "127.0.0.1";
localPort = 22;
remotePort = 6000;
}
];
};
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
description = ''
Extra frp TOML configuration included at the end of the generated configuration file.
Especially useful for [port range mapping].
[port range mapping]: https://github.com/fatedier/frp#port-range-mapping
'';
example = ''
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
[[proxies]]
name = "tcp-{{ $v.First }}"
type = "tcp"
localPort = {{ $v.First }}
remotePort = {{ $v.Second }}
{{- end }}
'';
};
};
}
);
default = { };
description = ''
Frp instances.
'';
};
package = lib.mkPackageOption pkgs "frp" { };
};
};
config = lib.mkIf (enabledInstances != { }) {
systemd.services = lib.mapAttrs' (
instance: options:
let
serviceName = "frp" + lib.optionalString (instance != "") ("-" + instance);
baseConfigFile = settingsFormat.generate "${serviceName}-base.toml" options.settings;
configFile =
if options.extraConfig == "" then
baseConfigFile
else
pkgs.writeText "${serviceName}.toml" ''
# Nixos Module settings
${builtins.readFile baseConfigFile}
# Nixos Module extraConfig
${options.extraConfig}
'';
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;
EnvironmentFile = options.environmentFiles;
# 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
epireyn
];
}