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