blob: d831f62e1455a3dbacf62f761c1e6dc7483b3f0e [file] [log] [blame]
vuko740a52d2022-12-27 20:45:00 +01001{ config, pkgs, modulesPath, ... }:
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +02002
3let
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +02004 hw = builtins.fromJSON (builtins.readFile ./hw.json);
5 fw = import ./fw-7535.nix;
6 vuko-pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFhaCaC/CVYv6hphqmEdKaPrIn+Q946+myvL9SSnzFZk vuko@eagle";
7 q3k-pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG599UildOrAq+LIOQjKqtGMwjgjIxozI1jtQQRKHtCP q3k@mimeomia";
8 networks = {
9 uplink = {
10 description = "Hackerspace Internet Uplink";
11 hw_addr = builtins.elemAt fw.hw_addresses 0;
12 ipv4 = "185.236.240.5";
13 ipv6 = "2a0d:eb00:2137:1::3";
14 };
15 lan = {
16 description = "Hackerspace LAN";
17 hw_addr = builtins.elemAt fw.hw_addresses 1;
18 ipv4 = "10.8.1.2";
19 ipv6 = "2a0d:eb00:4242::1";
20 };
21 managment = {
22 description = "Management network (temporary routing)";
23 hw_addr = builtins.elemAt fw.hw_addresses 2;
24 };
25 lte = {
26 description = "temp LTE uplink";
27 hw_addr = builtins.elemAt fw.hw_addresses 3;
28 };
29 vpn = {
30 description = "Hackerspace members vpn";
31 ipv4 = "10.9.1.1";
32 };
33 };
34 hostname = "customs";
35 openvpn-auth = import ./openvpn-auth { inherit pkgs; };
36 secrets-path = "/etc/nixos/secrets/";
37 update_authorized_keys = pkgs.writeShellScriptBin "update_authorized_keys" ''
38 ${pkgs.python3.withPackages (pp: [ pp.ldap3 ])}/bin/python ${./update_authorized_keys.py} ${hostname} ${secrets-path}/ldap-password.txt
39 '';
40
41in {
42 imports =
43 [
44 ./ulogd2/service.nix
45 #./hardware-configuration.nix
vuko740a52d2022-12-27 20:45:00 +010046 (modulesPath + "/profiles/minimal.nix")
47 (modulesPath + "/profiles/all-hardware.nix")
Piotr Dobrowolski6f6187c2021-10-16 23:22:22 +020048 ../../../bgpwtf/machines/modules/routing.nix
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +020049 ./checkinator-tracker.nix
50 ./checkinator-web.nix
51 ./mikrotik-exporter.nix
52 ./netboot.nix
Piotr Dobrowolski9c5d8662022-05-08 02:17:41 +020053 ./beyondspace.nix
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +020054 ./laserproxy/service.nix
55 ];
56
Piotr Dobrowolskiab299d42023-10-16 19:10:23 +020057 # Prevent spurious rebuilds due to dbus override on minimal profile
58 environment.noXlibs = false;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +020059
60 boot.loader.grub.enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +020061 boot.loader.grub.device = "nodev";
62 boot.loader.grub.extraConfig = ''
63 serial --unit=0 --speed=115200
64 terminal_input serial
65 terminal_output serial
66 '';
67 boot.kernelParams = ["console=tty0" "console=ttyS0,115200"];
68
69 time.timeZone = "Europe/Warsaw";
70
71 fileSystems."/" = {
72 device = "/dev/disk/by-partuuid/${hw.rootUUID}";
73 fsType = "ext4";
74 };
75
76 services.postfix = let acme_dir = "/var/lib/acme"; in {
77 enable = true;
78 domain = "customs.hackerspace.pl";
79 hostname = "customs.hackerspace.pl";
80 destination = [ "localhost" ];
81 sslCert = "${acme_dir}/customs.hackerspace.pl/full.pem";
82 sslKey = "${acme_dir}/customs.hackerspace.pl/key.pem";
83 enableSmtp = true;
84 enableSubmission = false;
85 #relayHost = "hackerspace.pl";
86 extraConfig = ''
87 inet_interfaces = loopback-only
88 '';
89 };
90
91 fileSystems."/mnt/secrets" = {
92 fsType = "tmpfs";
93 options = [ "rw" "mode=755" "size=200M" "nosuid" "nodev" "relatime" "noexec" ];
94 };
95
96 networking.hostName = hostname;
97 networking.domain = "hackerspace.pl";
98
99 networking.useDHCP = false;
100 networking.vlans = {
101 laser = {
102 id = 4001;
103 interface = "lan";
104 };
105 bms = {
106 id = 4002;
107 interface = "lan";
108 };
109 };
110
111 systemd.services.secrets = {
112 enable = true;
113 description = "Copy secrets and fix permissions";
114 script = ''
115 ${pkgs.coreutils}/bin/install --owner=root --mode=700 --directory /mnt/secrets/nginx/
116 ${pkgs.coreutils}/bin/install --owner=root --mode=400 -t /mnt/secrets/nginx/ \
117 ${secrets-path}/nginx/at.hackerspace.pl.key \
118 ${secrets-path}/nginx/at.hackerspace.pl.crt
119 ${pkgs.acl}/bin/setfacl -m "u:nginx:rx" /mnt/secrets/nginx
120 ${pkgs.acl}/bin/setfacl -m "u:nginx:r" /mnt/secrets/nginx/*
121 '';
122 wantedBy = [ "nginx.service" ];
123 partOf = [ "nginx.service" ];
124 serviceConfig.Type = "oneshot";
125 serviceConfig.RemainAfterExit = "true";
126 serviceConfig.User = "root";
127 };
128
129 services.prometheus.exporters.node = {
130 enable = true;
131 listenAddress = "[::1]";
132 port = 9100;
133 enabledCollectors = [ "systemd" ];
134 };
135
136 systemd.network.links = builtins.listToAttrs (map (
137 name: { name = "10-link-${name}"; value = {
138 enable = true;
139 matchConfig = {
140 MACAddress = networks."${name}".hw_addr;
141 };
142 linkConfig = {
143 Name = "${name}";
144 };
145 }; }
146 ) (builtins.filter (name: builtins.hasAttr "hw_addr" networks."${name}") (builtins.attrNames networks)));
147
148 #networking.interfaces.vpn = {
149 # virtual = true;
150 # name = "vpn";
151 # #ipv4.addresses = [ { address = 10.9.1.1; prefixlen = 16; } ];
152 #};
153
154 boot.kernel.sysctl = {
155 "net.ipv4.ip_forward" = true;
156 "net.ipv6.conf.all.forwarding" = true;
157 };
158
159 # using nftables so firewall has to be disabled
160 networking.firewall.enable = false;
161 networking.nftables.enable = true;
162 networking.nftables.ruleset = ''
163 table inet filter {
164 chain input {
165 type filter hook input priority 0;
166
167 # accept any localhost traffic
168 iifname lo accept
169
170 # accept traffic originated from us
171 ct state {established, related} accept
172
173 # ICMP
174 ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld-listener-query, nd-router-solicit } accept
175 ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
176
177 # allow "ping"
178 ip6 nexthdr icmpv6 icmpv6 type echo-request accept
179 ip protocol icmp icmp type echo-request accept
180
181 # allow OSPFv3
182 ip6 nexthdr 89 accept
183
184 tcp dport 22 accept
185 tcp dport 53 accept
186 udp dport 53 accept
187 tcp dport 80 accept
188 tcp dport 443 accept
189 udp dport tftp accept
190
191 iifname managment udp dport tftp accept
192 iifname lan tcp dport 8080 accept
193
194 # openvpn-members
195 udp dport 20001 accept
196 tcp dport 20001 accept
197
198 # laserproxy
199 udp dport 40200 accept
200 udp dport 50200 accept
201
202 counter drop
203 }
204
205 # Allow all outgoing connections.
206 chain output {
207 type filter hook output priority 0; policy accept;
208 }
209
210 chain forward {
211 type filter hook forward priority 0; policy drop;
212 ct state {established, related} jump accepted
213 oifname "loop" jump accepted
214 ip saddr 10.8.0.0/16 iifname "lan" jump accepted
215 ip saddr 10.9.0.0/16 iifname "vpn" jump accepted
216 ip6 saddr 2a0d:eb00:4242::0/64 iifname "lan" jump accepted
217 ip6 saddr 2a0d:eb00:4242:1::0/64 iifname "vpn" jump accepted
218 ip6 saddr 2a0d:eb00:4242:1::1/128 iifname "loop" jump accepted
219 }
220
221 chain accepted {
222 # IMPORTANT
223 # Log all connections to the outside world from LAN interface, as we are
224 # required to do so
225 oifname != "uplink" accept
226 iifname "uplink" accept
227 ip daddr { 10.0.0.0/8, 225.225.225.225/32 } accept
228 ip6 daddr { 2a0d:eb00::/29, fe80::/8 } accept
229 log group 2 accept
230 }
231 }
232
233 table inet net {
234 chain postrouting {
235 type nat hook postrouting priority 100;
236 ip saddr 10.8.0.0/16 oifname uplink snat ${networks.uplink.ipv4}
237 ip saddr 10.9.0.0/16 oifname uplink snat ${networks.uplink.ipv4}
238 }
239
240 chain prerouting {
241 type nat hook prerouting priority -100;
242
243 # Access to staszkecoin from Internet
244 ip version 4 iifname "uplink" tcp dport 8333 dnat 10.8.1.49
245 }
246 }
247 '';
248
249 systemd.services."loop-netdev" = let n = "loop"; in {
250 description = "Dummy interface: loop";
251 wantedBy = [ "network-setup.service" "sys-subsystem-net-devices-${n}.device" ];
252 partOf = [ "network-setup.service" ];
253 after = [ "network-pre.target" ];
254 before = [ "network-setup.service" ];
255 serviceConfig.Type = "oneshot";
256 serviceConfig.RemainAfterExit = true;
257 path = [ pkgs.iproute ];
258 script = ''
259 # Remove Dead Interfaces
260 ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
261 ip link add "${n}" type dummy
262 ip link set "${n}" up
263 '';
264 postStop = ''
265 ip link delete "${n}"
266 '';
267 };
268
269
270 networking.interfaces = {
271 uplink = {
272 ipv4.addresses = [ { address = networks.uplink.ipv4; prefixLength = 31; } ];
273 ipv6.addresses = [
274 { address = networks.uplink.ipv6; prefixLength = 112; }
275 ];
276 };
277 lan = {
278 ipv4.addresses = [ { address = networks.lan.ipv4; prefixLength = 16; } ];
279 ipv6.addresses = [ { address = networks.lan.ipv6; prefixLength = 64; } ];
280 };
281 loop = {
282 ipv6.addresses = [ { address = "2a0d:eb00:4242:1::1"; prefixLength = 128; } ];
283 };
284 laser = {
285 ipv4.addresses = [ { address = "10.11.0.1"; prefixLength = 24; } ];
286 };
287 bms = {
288 ipv4.addresses = [ { address = "10.11.1.1"; prefixLength = 24; } ];
289 };
290 managment = {
291 ipv4.addresses = [ { address = "10.10.1.1"; prefixLength = 24; } ];
292 };
293 lte = {
294 ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
295 };
296 };
297
298 networking.defaultGateway = {
299 address = "185.236.240.4";
300 interface = "uplink";
301 };
302 networking.defaultGateway6 = {
303 address = "2a0d:eb00:2137:1::1";
304 interface = "uplink";
305 };
306
307
308 networking.nameservers = [ "1.0.0.1" "8.8.8.8" ];
309
310 services.openssh = {
311 enable = true;
Piotr Dobrowolskiab299d42023-10-16 19:10:23 +0200312 settings = {
313 PasswordAuthentication = false;
314 LogLevel = "INFO";
315 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200316 };
317
318 users.users.root.openssh.authorizedKeys.keys = [ vuko-pubkey q3k-pubkey ];
319
320 services.dhcpd4 = {
321 enable = true;
vukoee8f1d52022-12-31 01:04:42 +0100322 configFile = "${./dhcpd.conf}";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200323 interfaces = ["lan"];
324 };
325
vukoee8f1d52022-12-31 01:04:42 +0100326 # Checkinator needs access to leases file. When DynamicUser is enable this
327 # file is hidden in /var/lib/private
328 systemd.services.dhcpd4.serviceConfig.DynamicUser= pkgs.lib.mkForce false;
329 users.users.dhcpd = {
330 group = "dhcpd";
331 isSystemUser = true;
332 uid = 1005;
333 };
334 users.groups."dhcpd" = {};
335
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200336 hscloud.routing = {
337 enable = true;
338 # TODO(q3k): make this optional in upstream
339 extra = "";
340 routerID = "185.236.240.5";
341 tables.master.program = true;
342 pipe.v6.aggregate_to_kernel = {
343 table = "master";
344 peerTable = "aggregate";
345 filterIn = ''
346 if source = RTS_OSPF then accept;
347 if source = RTS_OSPF_EXT2 then accept;
348 reject;
349 '';
350 };
351 ospf.v6.upstream = {
352 table = "aggregate";
353 area."0.0.0.0" = {
354 interfaces.uplink = { type = "bcast"; };
355 interfaces.lan = { type = "bcast"; stub = true; };
356 interfaces.loop = { type = "ptp"; stub = true; };
357 };
358 };
359 };
360
361 services.radvd = {
362 enable = true;
363 config = ''
364 interface lan {
365 AdvSendAdvert on;
366 prefix 2a0d:eb00:4242::/64 {
367 };
368 route 0::/0 { };
369 };
370 interface vpn {
371 AdvSendAdvert on;
372 prefix 2a0d:eb00:4242:1::/64 {
373 AdvRouterAddr on;
374 };
375 route 0::/0 { };
376 };
377 '';
378 };
379
380 services.logrotate = {
381 enable = true;
vuko740a52d2022-12-27 20:45:00 +0100382 settings = {
383 "/var/log/ulogd.pcap" = {
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200384 frequency = "weekly";
vuko740a52d2022-12-27 20:45:00 +0100385 postrotate = ''
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200386 ${pkgs.killall}/bin/killall -HUP ulogd
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200387 '';
vuko740a52d2022-12-27 20:45:00 +0100388 rotate = 55;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200389 };
390 };
391 };
392
393 services.cron = let
394 log-neigh = pkgs.writeShellScript "log-neigh" ''
395 mkdir -p /var/log/arptables
396 chmod 700 /var/log/arptables
397
398 # Larger than 10MB? rotate.
399 if [[ $(find /var/log/arptables/arptables.log -type f -size +10485760c 2>/dev/null) ]]; then
400 f=/var/log/arptables/$(date "+%s").log
401 cp /var/log/arptables/arptables.log $f
402 gzip -9 $f
403 rm /var/log/arptables/arptables.log
404 fi
405
406 ip neigh >> /var/log/arptables/arptables.log
407 date --iso-8601=seconds >> /var/log/arptables/arptables.log
408 '';
409 in {
vukoca6dba92023-01-05 19:54:20 +0100410 mailto = "both@hackerspace.pl";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200411 enable = true;
412 systemCronJobs = [
413 "*/5 * * * * root ${log-neigh}"
414 "0 3 * * * root ${update_authorized_keys}/bin/update_authorized_keys"
415 ];
416 };
417
418 services.knot = {
419 enable = true;
420 extraConfig = ''
421 server:
422 listen: ${networks.uplink.ipv4}@53
423 listen: ${networks.uplink.ipv6}@53
424
425 zone:
426 - domain: waw.hackerspace.pl
427 storage: ${./zones}
428 file: waw.hackerspace.pl
429 - domain: i
430 storage: ${./zones}
431 file: i
432 - domain: api.ustream.tv
433 storage: ${./zones}
434 file: api.ustream.tv
435 - domain: api.eye.fi
436 storage: ${./zones}
437 file: api.eye.fi
438 log:
439 - target: syslog
440 any: info
441 '';
442 };
443
444 services.nginx.enable = true;
445 services.nginx.mapHashBucketSize = 64;
446 services.nginx.appendHttpConfig = ''
447 server_names_hash_bucket_size 64;
448 '';
Piotr Dobrowolski9c5d8662022-05-08 02:17:41 +0200449 services.nginx.resolver.addresses = [ "127.0.0.1" ];
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200450
vuko740a52d2022-12-27 20:45:00 +0100451 security.acme.acceptTerms = true;
452 security.acme.defaults.email = "bofh@hackerspace.pl";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200453
454 services.nginx.virtualHosts."customs.hackerspace.pl" = {
455 default = true;
456 enableACME = true;
457
458 locations."/" = {
459 extraConfig = ''
460 return 302 https://isztar.mf.gov.pl;
461 '';
462 };
463 locations."/metrics/luftdaten" = {
464 proxyPass = "http://10.8.0.146";
465 };
466 locations."/metrics/spejsiot" = {
vuko32624092022-12-22 23:09:06 +0100467 proxyPass = "http://10.8.1.16/metrics";
468 extraConfig = ''
469 proxy_set_header Host spejsiot.waw.hackerspace.pl;
470 '';
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200471 };
472 locations."/metrics/apm" = {
473 proxyPass = "http://10.8.1.40:5000/metrics";
474 };
475 locations."/metrics/vending" = {
476 proxyPass = "http://10.8.1.32:8000/";
477 };
478 locations."/metrics/sztancarka" = {
479 proxyPass = "http://10.8.0.96:8888/";
480 };
481 locations."/metrics/mikrotik" = {
482 proxyPass = "http://127.0.0.1:9436/metrics";
483 extraConfig = ''
484 allow 209.250.231.127;
485 deny all;
486 '';
487 };
488 locations."/metrics/node" = {
489 proxyPass = "http://[::1]:9100/metrics";
490 extraConfig = ''
491 allow 209.250.231.127;
492 deny all;
493 '';
494 };
495 locations."/stats/sztancarka-ppm" = {
496 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60";
497 };
498 locations."/stats/sztancarka-last-24h" = {
499 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))";
500 };
501 };
502
vuko3125aa12022-12-31 02:52:24 +0100503 services.unbound = let
504 local-zones = [ "waw.hackerspace.pl." "api.eye.fi." "api.ustream.tv." "i." ];
505 in {
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200506 enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200507
vuko3125aa12022-12-31 02:52:24 +0100508 #enableRootTrustAnchor = false;
509
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200510 settings = {
511 server = {
512 interface = [
513 networks.lan.ipv4
vuko3125aa12022-12-31 02:52:24 +0100514 networks.lan.ipv6
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200515 "127.0.0.1"
516 "::1"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200517 ];
518 access-control = [
vuko3125aa12022-12-31 02:52:24 +0100519 "::1/128 allow"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200520 "127.0.0.1/8 allow"
521 "10.0.0.0/8 allow"
vuko3125aa12022-12-31 02:52:24 +0100522 "${networks.lan.ipv6}/64 allow"
523 "${networks.lan.ipv4}/8 allow"
524 ];
525
526 # disable DNSSEC on locally resolved domains
527 domain-insecure = local-zones;
528
529 # allow LAN adresses only for local domains
530 private-domain = local-zones;
531 private-address = [
532 "10.0.0.0/8"
533 "${networks.lan.ipv6}/64"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200534 ];
535 };
536
vuko3125aa12022-12-31 02:52:24 +0100537 # authoritative DNS servers
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200538 stub-zone = map (name: {
vuko3125aa12022-12-31 02:52:24 +0100539 inherit name;
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200540 stub-addr = networks.uplink.ipv4;
vuko3125aa12022-12-31 02:52:24 +0100541 }) local-zones;
542
543 # recursive DNS servers
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200544 forward-zone = {
545 name = ".";
546 forward-addr = "185.236.240.1";
547 };
548 };
549 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200550
551 # Public VPN access for Hackerspace members
552 services.openvpn.servers.members.config = ''
553 script-security 3
554 auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env
555 verify-client-cert none
556 username-as-common-name
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200557
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200558 #user _openvpn
559 #group _openvpn
560 multihome
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200561
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200562 port 20001
563 proto udp
564 proto udp6
565 dev vpn
566 dev-type tun
567 ca ${secrets-path}/openvpn-public/ca.crt
568 cert ${secrets-path}/openvpn-public/server.crt
569 key ${secrets-path}/openvpn-public/server.key
570 dh ${secrets-path}/openvpn-public/dh.pem
571 server 10.9.1.0 255.255.255.0
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200572
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200573 push "route 10.8.0.0 255.255.0.0"
574 push "route 10.9.0.0 255.255.0.0"
575 push "route 10.10.0.0 255.255.0.0"
576 push "route 10.11.0.0 255.255.0.0"
577 push "dhcp-option DNS ${networks.lan.ipv4}"
578 push "dhcp-option DOMAIN waw.hackerspace.pl"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200579
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200580 ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt
581 #client-config-dir /var/lib/openvpn-public/ccd
582 client-to-client
583 keepalive 10 120
584 comp-lzo
585 persist-key
586 persist-tun
587 '';
588
589 environment.systemPackages = with pkgs; [
590 vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind
591 rxvt_unicode.terminfo update_authorized_keys
592 ];
593 programs.mtr.enable = true;
594
595 environment.variables = {
596 EDITOR = "vim";
597 };
598
599 system.stateVersion = "20.03";
600
601 boot.vesa = false;
602 boot.loader.grub.splashImage = null;
603}
604