1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8
9let
10 inherit (lib)
11 getExe'
12 mkEnableOption
13 mkIf
14 mkPackageOption
15 mkOption
16 ;
17
18 inherit (lib.types)
19 listOf
20 enum
21 port
22 str
23 ;
24
25 inherit (utils) escapeSystemdExecArgs;
26
27 cfg = config.services.netbird.server.signal;
28in
29
30{
31 options.services.netbird.server.signal = {
32 enable = mkEnableOption "Netbird's Signal Service";
33
34 package = mkPackageOption pkgs "netbird" { };
35
36 enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird signal service";
37
38 domain = mkOption {
39 type = str;
40 description = "The domain name for the signal service.";
41 };
42
43 port = mkOption {
44 type = port;
45 default = 8012;
46 description = "Internal port of the signal server.";
47 };
48
49 metricsPort = mkOption {
50 type = port;
51 default = 9091;
52 description = "Internal port of the metrics server.";
53 };
54
55 extraOptions = mkOption {
56 type = listOf str;
57 default = [ ];
58 description = ''
59 Additional options given to netbird-signal as commandline arguments.
60 '';
61 };
62
63 logLevel = mkOption {
64 type = enum [
65 "ERROR"
66 "WARN"
67 "INFO"
68 "DEBUG"
69 ];
70 default = "INFO";
71 description = "Log level of the netbird signal service.";
72 };
73 };
74
75 config = mkIf cfg.enable {
76
77 assertions = [
78 {
79 assertion = cfg.port != cfg.metricsPort;
80 message = "The primary listen port cannot be the same as the listen port for the metrics endpoint";
81 }
82 ];
83
84 systemd.services.netbird-signal = {
85 after = [ "network.target" ];
86 wantedBy = [ "multi-user.target" ];
87
88 serviceConfig = {
89 ExecStart = escapeSystemdExecArgs (
90 [
91 (getExe' cfg.package "netbird-signal")
92 "run"
93 # Port to listen on
94 "--port"
95 cfg.port
96 # Port the internal prometheus server listens on
97 "--metrics-port"
98 cfg.metricsPort
99 # Log to stdout
100 "--log-file"
101 "console"
102 # Log level
103 "--log-level"
104 cfg.logLevel
105 ]
106 ++ cfg.extraOptions
107 );
108
109 Restart = "always";
110 RuntimeDirectory = "netbird-mgmt";
111 StateDirectory = "netbird-mgmt";
112 WorkingDirectory = "/var/lib/netbird-mgmt";
113
114 # hardening
115 LockPersonality = true;
116 MemoryDenyWriteExecute = true;
117 NoNewPrivileges = true;
118 PrivateMounts = true;
119 PrivateTmp = true;
120 ProtectClock = true;
121 ProtectControlGroups = true;
122 ProtectHome = true;
123 ProtectHostname = true;
124 ProtectKernelLogs = true;
125 ProtectKernelModules = true;
126 ProtectKernelTunables = true;
127 ProtectSystem = true;
128 RemoveIPC = true;
129 RestrictNamespaces = true;
130 RestrictRealtime = true;
131 RestrictSUIDSGID = true;
132 };
133
134 stopIfChanged = false;
135 };
136
137 services.nginx = mkIf cfg.enableNginx {
138 enable = true;
139
140 virtualHosts.${cfg.domain} = {
141 locations."/signalexchange.SignalExchange/".extraConfig = ''
142 # This is necessary so that grpc connections do not get closed early
143 # see https://stackoverflow.com/a/67805465
144 client_body_timeout 1d;
145
146 grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
147
148 grpc_pass grpc://localhost:${builtins.toString cfg.port};
149 grpc_read_timeout 1d;
150 grpc_send_timeout 1d;
151 grpc_socket_keepalive on;
152 '';
153 };
154 };
155 };
156}