1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.frp;
7 settingsFormat = pkgs.formats.toml { };
8 configFile = settingsFormat.generate "frp.toml" cfg.settings;
9 isClient = (cfg.role == "client");
10 isServer = (cfg.role == "server");
11in
12{
13 options = {
14 services.frp = {
15 enable = mkEnableOption "frp";
16
17 package = mkPackageOption pkgs "frp" { };
18
19 role = mkOption {
20 type = types.enum [ "server" "client" ];
21 description = ''
22 The frp consists of `client` and `server`. The server is usually
23 deployed on the machine with a public IP address, and
24 the client is usually deployed on the machine
25 where the Intranet service to be penetrated resides.
26 '';
27 };
28
29 settings = mkOption {
30 type = settingsFormat.type;
31 default = { };
32 description = ''
33 Frp configuration, for configuration options
34 see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml)
35 or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml) on github.
36 '';
37 example = {
38 serverAddr = "x.x.x.x";
39 serverPort = 7000;
40 };
41 };
42 };
43 };
44
45 config =
46 let
47 serviceCapability = optionals isServer [ "CAP_NET_BIND_SERVICE" ];
48 executableFile = if isClient then "frpc" else "frps";
49 in
50 mkIf cfg.enable {
51 systemd.services = {
52 frp = {
53 wants = optionals isClient [ "network-online.target" ];
54 after = if isClient then [ "network-online.target" ] else [ "network.target" ];
55 wantedBy = [ "multi-user.target" ];
56 description = "A fast reverse proxy frp ${cfg.role}";
57 serviceConfig = {
58 Type = "simple";
59 Restart = "on-failure";
60 RestartSec = 15;
61 ExecStart = "${cfg.package}/bin/${executableFile} --strict_config -c ${configFile}";
62 StateDirectoryMode = optionalString isServer "0700";
63 DynamicUser = true;
64 # Hardening
65 UMask = optionalString isServer "0007";
66 CapabilityBoundingSet = serviceCapability;
67 AmbientCapabilities = serviceCapability;
68 PrivateDevices = true;
69 ProtectHostname = true;
70 ProtectClock = true;
71 ProtectKernelTunables = true;
72 ProtectKernelModules = true;
73 ProtectKernelLogs = true;
74 ProtectControlGroups = true;
75 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ] ++ optionals isClient [ "AF_UNIX" ];
76 LockPersonality = true;
77 MemoryDenyWriteExecute = true;
78 RestrictRealtime = true;
79 RestrictSUIDSGID = true;
80 PrivateMounts = true;
81 SystemCallArchitectures = "native";
82 SystemCallFilter = [ "@system-service" ];
83 };
84 };
85 };
86 };
87
88 meta.maintainers = with maintainers; [ zaldnoay ];
89}