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