1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.bird-lg;
7
8 stringOrConcat = sep: v: if builtins.isString v then v else concatStringsSep sep v;
9
10 frontend_args = let
11 fe = cfg.frontend;
12 in {
13 "--servers" = concatStringsSep "," fe.servers;
14 "--domain" = fe.domain;
15 "--listen" = fe.listenAddress;
16 "--proxy-port" = fe.proxyPort;
17 "--whois" = fe.whois;
18 "--dns-interface" = fe.dnsInterface;
19 "--bgpmap-info" = concatStringsSep "," cfg.frontend.bgpMapInfo;
20 "--title-brand" = fe.titleBrand;
21 "--navbar-brand" = fe.navbar.brand;
22 "--navbar-brand-url" = fe.navbar.brandURL;
23 "--navbar-all-servers" = fe.navbar.allServers;
24 "--navbar-all-url" = fe.navbar.allServersURL;
25 "--net-specific-mode" = fe.netSpecificMode;
26 "--protocol-filter" = concatStringsSep "," cfg.frontend.protocolFilter;
27 };
28
29 proxy_args = let
30 px = cfg.proxy;
31 in {
32 "--allowed" = concatStringsSep "," px.allowedIPs;
33 "--bird" = px.birdSocket;
34 "--listen" = px.listenAddress;
35 "--traceroute_bin" = px.traceroute.binary;
36 "--traceroute_flags" = concatStringsSep " " px.traceroute.flags;
37 "--traceroute_raw" = px.traceroute.rawOutput;
38 };
39
40 mkArgValue = value:
41 if isString value
42 then escapeShellArg value
43 else if isBool value
44 then boolToString value
45 else toString value;
46
47 filterNull = filterAttrs (_: v: v != "" && v != null && v != []);
48
49 argsAttrToList = args: mapAttrsToList (name: value: "${name} " + mkArgValue value ) (filterNull args);
50in
51{
52 options = {
53 services.bird-lg = {
54 package = mkOption {
55 type = types.package;
56 default = pkgs.bird-lg;
57 defaultText = literalExpression "pkgs.bird-lg";
58 description = lib.mdDoc "The Bird Looking Glass package to use.";
59 };
60
61 user = mkOption {
62 type = types.str;
63 default = "bird-lg";
64 description = lib.mdDoc "User to run the service.";
65 };
66
67 group = mkOption {
68 type = types.str;
69 default = "bird-lg";
70 description = lib.mdDoc "Group to run the service.";
71 };
72
73 frontend = {
74 enable = mkEnableOption (lib.mdDoc "Bird Looking Glass Frontend Webserver");
75
76 listenAddress = mkOption {
77 type = types.str;
78 default = "127.0.0.1:5000";
79 description = lib.mdDoc "Address to listen on.";
80 };
81
82 proxyPort = mkOption {
83 type = types.port;
84 default = 8000;
85 description = lib.mdDoc "Port bird-lg-proxy is running on.";
86 };
87
88 domain = mkOption {
89 type = types.str;
90 example = "dn42.lantian.pub";
91 description = lib.mdDoc "Server name domain suffixes.";
92 };
93
94 servers = mkOption {
95 type = types.listOf types.str;
96 example = [ "gigsgigscloud" "hostdare" ];
97 description = lib.mdDoc "Server name prefixes.";
98 };
99
100 whois = mkOption {
101 type = types.str;
102 default = "whois.verisign-grs.com";
103 description = lib.mdDoc "Whois server for queries.";
104 };
105
106 dnsInterface = mkOption {
107 type = types.str;
108 default = "asn.cymru.com";
109 description = lib.mdDoc "DNS zone to query ASN information.";
110 };
111
112 bgpMapInfo = mkOption {
113 type = types.listOf types.str;
114 default = [ "asn" "as-name" "ASName" "descr" ];
115 description = lib.mdDoc "Information displayed in bgpmap.";
116 };
117
118 titleBrand = mkOption {
119 type = types.str;
120 default = "Bird-lg Go";
121 description = lib.mdDoc "Prefix of page titles in browser tabs.";
122 };
123
124 netSpecificMode = mkOption {
125 type = types.str;
126 default = "";
127 example = "dn42";
128 description = lib.mdDoc "Apply network-specific changes for some networks.";
129 };
130
131 protocolFilter = mkOption {
132 type = types.listOf types.str;
133 default = [ ];
134 example = [ "ospf" ];
135 description = lib.mdDoc "Information displayed in bgpmap.";
136 };
137
138 nameFilter = mkOption {
139 type = types.str;
140 default = "";
141 example = "^ospf";
142 description = lib.mdDoc "Protocol names to hide in summary tables (RE2 syntax),";
143 };
144
145 timeout = mkOption {
146 type = types.int;
147 default = 120;
148 description = lib.mdDoc "Time before request timed out, in seconds.";
149 };
150
151 navbar = {
152 brand = mkOption {
153 type = types.str;
154 default = "Bird-lg Go";
155 description = lib.mdDoc "Brand to show in the navigation bar .";
156 };
157
158 brandURL = mkOption {
159 type = types.str;
160 default = "/";
161 description = lib.mdDoc "URL of the brand to show in the navigation bar.";
162 };
163
164 allServers = mkOption {
165 type = types.str;
166 default = "ALL Servers";
167 description = lib.mdDoc "Text of 'All server' button in the navigation bar.";
168 };
169
170 allServersURL = mkOption {
171 type = types.str;
172 default = "all";
173 description = lib.mdDoc "URL of 'All servers' button.";
174 };
175 };
176
177 extraArgs = mkOption {
178 type = with types; either lines (listOf str);
179 default = [ ];
180 description = lib.mdDoc ''
181 Extra parameters documented [here](https://github.com/xddxdd/bird-lg-go#frontend).
182
183 :::{.note}
184 Passing lines (plain strings) is deprecated in favour of passing lists of strings.
185 :::
186 '';
187 };
188 };
189
190 proxy = {
191 enable = mkEnableOption (lib.mdDoc "Bird Looking Glass Proxy");
192
193 listenAddress = mkOption {
194 type = types.str;
195 default = "127.0.0.1:8000";
196 description = lib.mdDoc "Address to listen on.";
197 };
198
199 allowedIPs = mkOption {
200 type = types.listOf types.str;
201 default = [ ];
202 example = [ "192.168.25.52" "192.168.25.53" ];
203 description = lib.mdDoc "List of IPs to allow (default all allowed).";
204 };
205
206 birdSocket = mkOption {
207 type = types.str;
208 default = "/var/run/bird/bird.ctl";
209 description = lib.mdDoc "Bird control socket path.";
210 };
211
212 traceroute = {
213 binary = mkOption {
214 type = types.str;
215 default = "${pkgs.traceroute}/bin/traceroute";
216 defaultText = literalExpression ''"''${pkgs.traceroute}/bin/traceroute"'';
217 description = lib.mdDoc "Traceroute's binary path.";
218 };
219
220 flags = mkOption {
221 type = with types; listOf str;
222 default = [ ];
223 description = lib.mdDoc "Flags for traceroute process";
224 };
225
226 rawOutput = mkOption {
227 type = types.bool;
228 default = false;
229 description = lib.mdDoc "Display traceroute output in raw format.";
230 };
231 };
232
233 extraArgs = mkOption {
234 type = with types; either lines (listOf str);
235 default = [ ];
236 description = lib.mdDoc ''
237 Extra parameters documented [here](https://github.com/xddxdd/bird-lg-go#proxy).
238
239 :::{.note}
240 Passing lines (plain strings) is deprecated in favour of passing lists of strings.
241 :::
242 '';
243 };
244 };
245 };
246 };
247
248 ###### implementation
249
250 config = {
251
252 warnings =
253 lib.optional (cfg.frontend.enable && builtins.isString cfg.frontend.extraArgs) ''
254 Passing strings to `services.bird-lg.frontend.extraOptions' is deprecated. Please pass a list of strings instead.
255 ''
256 ++ lib.optional (cfg.proxy.enable && builtins.isString cfg.proxy.extraArgs) ''
257 Passing strings to `services.bird-lg.proxy.extraOptions' is deprecated. Please pass a list of strings instead.
258 ''
259 ;
260
261 systemd.services = {
262 bird-lg-frontend = mkIf cfg.frontend.enable {
263 enable = true;
264 after = [ "network.target" ];
265 wantedBy = [ "multi-user.target" ];
266 description = "Bird Looking Glass Frontend Webserver";
267 serviceConfig = {
268 Type = "simple";
269 Restart = "on-failure";
270 ProtectSystem = "full";
271 ProtectHome = "yes";
272 MemoryDenyWriteExecute = "yes";
273 User = cfg.user;
274 Group = cfg.group;
275 };
276 script = ''
277 ${cfg.package}/bin/frontend \
278 ${concatStringsSep " \\\n " (argsAttrToList frontend_args)} \
279 ${stringOrConcat " " cfg.frontend.extraArgs}
280 '';
281 };
282
283 bird-lg-proxy = mkIf cfg.proxy.enable {
284 enable = true;
285 after = [ "network.target" ];
286 wantedBy = [ "multi-user.target" ];
287 description = "Bird Looking Glass Proxy";
288 serviceConfig = {
289 Type = "simple";
290 Restart = "on-failure";
291 ProtectSystem = "full";
292 ProtectHome = "yes";
293 MemoryDenyWriteExecute = "yes";
294 User = cfg.user;
295 Group = cfg.group;
296 };
297 script = ''
298 ${cfg.package}/bin/proxy \
299 ${concatStringsSep " \\\n " (argsAttrToList proxy_args)} \
300 ${stringOrConcat " " cfg.proxy.extraArgs}
301 '';
302 };
303 };
304 users = mkIf (cfg.frontend.enable || cfg.proxy.enable) {
305 groups."bird-lg" = mkIf (cfg.group == "bird-lg") { };
306 users."bird-lg" = mkIf (cfg.user == "bird-lg") {
307 description = "Bird Looking Glass user";
308 extraGroups = lib.optionals (config.services.bird2.enable) [ "bird2" ];
309 group = cfg.group;
310 isSystemUser = true;
311 };
312 };
313 };
314
315 meta.maintainers = with lib.maintainers; [
316 e1mo
317 tchekda
318 ];
319}