blob: 36914240bc9f0c66e5f187e6c4afe28113ae80ca [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
57
58 boot.loader.grub.enable = true;
59 boot.loader.grub.version = 2;
60 boot.loader.grub.device = "nodev";
61 boot.loader.grub.extraConfig = ''
62 serial --unit=0 --speed=115200
63 terminal_input serial
64 terminal_output serial
65 '';
66 boot.kernelParams = ["console=tty0" "console=ttyS0,115200"];
67
68 time.timeZone = "Europe/Warsaw";
69
70 fileSystems."/" = {
71 device = "/dev/disk/by-partuuid/${hw.rootUUID}";
72 fsType = "ext4";
73 };
74
75 services.postfix = let acme_dir = "/var/lib/acme"; in {
76 enable = true;
77 domain = "customs.hackerspace.pl";
78 hostname = "customs.hackerspace.pl";
79 destination = [ "localhost" ];
80 sslCert = "${acme_dir}/customs.hackerspace.pl/full.pem";
81 sslKey = "${acme_dir}/customs.hackerspace.pl/key.pem";
82 enableSmtp = true;
83 enableSubmission = false;
84 #relayHost = "hackerspace.pl";
85 extraConfig = ''
86 inet_interfaces = loopback-only
87 '';
88 };
89
90 fileSystems."/mnt/secrets" = {
91 fsType = "tmpfs";
92 options = [ "rw" "mode=755" "size=200M" "nosuid" "nodev" "relatime" "noexec" ];
93 };
94
95 networking.hostName = hostname;
96 networking.domain = "hackerspace.pl";
97
98 networking.useDHCP = false;
99 networking.vlans = {
100 laser = {
101 id = 4001;
102 interface = "lan";
103 };
104 bms = {
105 id = 4002;
106 interface = "lan";
107 };
108 };
109
110 systemd.services.secrets = {
111 enable = true;
112 description = "Copy secrets and fix permissions";
113 script = ''
114 ${pkgs.coreutils}/bin/install --owner=root --mode=700 --directory /mnt/secrets/nginx/
115 ${pkgs.coreutils}/bin/install --owner=root --mode=400 -t /mnt/secrets/nginx/ \
116 ${secrets-path}/nginx/at.hackerspace.pl.key \
117 ${secrets-path}/nginx/at.hackerspace.pl.crt
118 ${pkgs.acl}/bin/setfacl -m "u:nginx:rx" /mnt/secrets/nginx
119 ${pkgs.acl}/bin/setfacl -m "u:nginx:r" /mnt/secrets/nginx/*
120 '';
121 wantedBy = [ "nginx.service" ];
122 partOf = [ "nginx.service" ];
123 serviceConfig.Type = "oneshot";
124 serviceConfig.RemainAfterExit = "true";
125 serviceConfig.User = "root";
126 };
127
128 services.prometheus.exporters.node = {
129 enable = true;
130 listenAddress = "[::1]";
131 port = 9100;
132 enabledCollectors = [ "systemd" ];
133 };
134
135 systemd.network.links = builtins.listToAttrs (map (
136 name: { name = "10-link-${name}"; value = {
137 enable = true;
138 matchConfig = {
139 MACAddress = networks."${name}".hw_addr;
140 };
141 linkConfig = {
142 Name = "${name}";
143 };
144 }; }
145 ) (builtins.filter (name: builtins.hasAttr "hw_addr" networks."${name}") (builtins.attrNames networks)));
146
147 #networking.interfaces.vpn = {
148 # virtual = true;
149 # name = "vpn";
150 # #ipv4.addresses = [ { address = 10.9.1.1; prefixlen = 16; } ];
151 #};
152
153 boot.kernel.sysctl = {
154 "net.ipv4.ip_forward" = true;
155 "net.ipv6.conf.all.forwarding" = true;
156 };
157
158 # using nftables so firewall has to be disabled
159 networking.firewall.enable = false;
160 networking.nftables.enable = true;
161 networking.nftables.ruleset = ''
162 table inet filter {
163 chain input {
164 type filter hook input priority 0;
165
166 # accept any localhost traffic
167 iifname lo accept
168
169 # accept traffic originated from us
170 ct state {established, related} accept
171
172 # ICMP
173 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
174 ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
175
176 # allow "ping"
177 ip6 nexthdr icmpv6 icmpv6 type echo-request accept
178 ip protocol icmp icmp type echo-request accept
179
180 # allow OSPFv3
181 ip6 nexthdr 89 accept
182
183 tcp dport 22 accept
184 tcp dport 53 accept
185 udp dport 53 accept
186 tcp dport 80 accept
187 tcp dport 443 accept
188 udp dport tftp accept
189
190 iifname managment udp dport tftp accept
191 iifname lan tcp dport 8080 accept
192
193 # openvpn-members
194 udp dport 20001 accept
195 tcp dport 20001 accept
196
197 # laserproxy
198 udp dport 40200 accept
199 udp dport 50200 accept
200
201 counter drop
202 }
203
204 # Allow all outgoing connections.
205 chain output {
206 type filter hook output priority 0; policy accept;
207 }
208
209 chain forward {
210 type filter hook forward priority 0; policy drop;
211 ct state {established, related} jump accepted
212 oifname "loop" jump accepted
213 ip saddr 10.8.0.0/16 iifname "lan" jump accepted
214 ip saddr 10.9.0.0/16 iifname "vpn" jump accepted
215 ip6 saddr 2a0d:eb00:4242::0/64 iifname "lan" jump accepted
216 ip6 saddr 2a0d:eb00:4242:1::0/64 iifname "vpn" jump accepted
217 ip6 saddr 2a0d:eb00:4242:1::1/128 iifname "loop" jump accepted
218 }
219
220 chain accepted {
221 # IMPORTANT
222 # Log all connections to the outside world from LAN interface, as we are
223 # required to do so
224 oifname != "uplink" accept
225 iifname "uplink" accept
226 ip daddr { 10.0.0.0/8, 225.225.225.225/32 } accept
227 ip6 daddr { 2a0d:eb00::/29, fe80::/8 } accept
228 log group 2 accept
229 }
230 }
231
232 table inet net {
233 chain postrouting {
234 type nat hook postrouting priority 100;
235 ip saddr 10.8.0.0/16 oifname uplink snat ${networks.uplink.ipv4}
236 ip saddr 10.9.0.0/16 oifname uplink snat ${networks.uplink.ipv4}
237 }
238
239 chain prerouting {
240 type nat hook prerouting priority -100;
241
242 # Access to staszkecoin from Internet
243 ip version 4 iifname "uplink" tcp dport 8333 dnat 10.8.1.49
244 }
245 }
246 '';
247
248 systemd.services."loop-netdev" = let n = "loop"; in {
249 description = "Dummy interface: loop";
250 wantedBy = [ "network-setup.service" "sys-subsystem-net-devices-${n}.device" ];
251 partOf = [ "network-setup.service" ];
252 after = [ "network-pre.target" ];
253 before = [ "network-setup.service" ];
254 serviceConfig.Type = "oneshot";
255 serviceConfig.RemainAfterExit = true;
256 path = [ pkgs.iproute ];
257 script = ''
258 # Remove Dead Interfaces
259 ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
260 ip link add "${n}" type dummy
261 ip link set "${n}" up
262 '';
263 postStop = ''
264 ip link delete "${n}"
265 '';
266 };
267
268
269 networking.interfaces = {
270 uplink = {
271 ipv4.addresses = [ { address = networks.uplink.ipv4; prefixLength = 31; } ];
272 ipv6.addresses = [
273 { address = networks.uplink.ipv6; prefixLength = 112; }
274 ];
275 };
276 lan = {
277 ipv4.addresses = [ { address = networks.lan.ipv4; prefixLength = 16; } ];
278 ipv6.addresses = [ { address = networks.lan.ipv6; prefixLength = 64; } ];
279 };
280 loop = {
281 ipv6.addresses = [ { address = "2a0d:eb00:4242:1::1"; prefixLength = 128; } ];
282 };
283 laser = {
284 ipv4.addresses = [ { address = "10.11.0.1"; prefixLength = 24; } ];
285 };
286 bms = {
287 ipv4.addresses = [ { address = "10.11.1.1"; prefixLength = 24; } ];
288 };
289 managment = {
290 ipv4.addresses = [ { address = "10.10.1.1"; prefixLength = 24; } ];
291 };
292 lte = {
293 ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
294 };
295 };
296
297 networking.defaultGateway = {
298 address = "185.236.240.4";
299 interface = "uplink";
300 };
301 networking.defaultGateway6 = {
302 address = "2a0d:eb00:2137:1::1";
303 interface = "uplink";
304 };
305
306
307 networking.nameservers = [ "1.0.0.1" "8.8.8.8" ];
308
309 services.openssh = {
310 enable = true;
311 passwordAuthentication = false;
312 logLevel = "INFO";
313 };
314
315 users.users.root.openssh.authorizedKeys.keys = [ vuko-pubkey q3k-pubkey ];
316
317 services.dhcpd4 = {
318 enable = true;
319 configFile = ./dhcpd.conf;
320 interfaces = ["lan"];
321 };
322
323 hscloud.routing = {
324 enable = true;
325 # TODO(q3k): make this optional in upstream
326 extra = "";
327 routerID = "185.236.240.5";
328 tables.master.program = true;
329 pipe.v6.aggregate_to_kernel = {
330 table = "master";
331 peerTable = "aggregate";
332 filterIn = ''
333 if source = RTS_OSPF then accept;
334 if source = RTS_OSPF_EXT2 then accept;
335 reject;
336 '';
337 };
338 ospf.v6.upstream = {
339 table = "aggregate";
340 area."0.0.0.0" = {
341 interfaces.uplink = { type = "bcast"; };
342 interfaces.lan = { type = "bcast"; stub = true; };
343 interfaces.loop = { type = "ptp"; stub = true; };
344 };
345 };
346 };
347
348 services.radvd = {
349 enable = true;
350 config = ''
351 interface lan {
352 AdvSendAdvert on;
353 prefix 2a0d:eb00:4242::/64 {
354 };
355 route 0::/0 { };
356 };
357 interface vpn {
358 AdvSendAdvert on;
359 prefix 2a0d:eb00:4242:1::/64 {
360 AdvRouterAddr on;
361 };
362 route 0::/0 { };
363 };
364 '';
365 };
366
367 services.logrotate = {
368 enable = true;
vuko740a52d2022-12-27 20:45:00 +0100369 settings = {
370 "/var/log/ulogd.pcap" = {
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200371 frequency = "weekly";
vuko740a52d2022-12-27 20:45:00 +0100372 postrotate = ''
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200373 ${pkgs.killall}/bin/killall -HUP ulogd
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200374 '';
vuko740a52d2022-12-27 20:45:00 +0100375 rotate = 55;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200376 };
377 };
378 };
379
380 services.cron = let
381 log-neigh = pkgs.writeShellScript "log-neigh" ''
382 mkdir -p /var/log/arptables
383 chmod 700 /var/log/arptables
384
385 # Larger than 10MB? rotate.
386 if [[ $(find /var/log/arptables/arptables.log -type f -size +10485760c 2>/dev/null) ]]; then
387 f=/var/log/arptables/$(date "+%s").log
388 cp /var/log/arptables/arptables.log $f
389 gzip -9 $f
390 rm /var/log/arptables/arptables.log
391 fi
392
393 ip neigh >> /var/log/arptables/arptables.log
394 date --iso-8601=seconds >> /var/log/arptables/arptables.log
395 '';
396 in {
397 mailto = "vuko@hackerspace.pl";
398 enable = true;
399 systemCronJobs = [
400 "*/5 * * * * root ${log-neigh}"
401 "0 3 * * * root ${update_authorized_keys}/bin/update_authorized_keys"
402 ];
403 };
404
405 services.knot = {
406 enable = true;
407 extraConfig = ''
408 server:
409 listen: ${networks.uplink.ipv4}@53
410 listen: ${networks.uplink.ipv6}@53
411
412 zone:
413 - domain: waw.hackerspace.pl
414 storage: ${./zones}
415 file: waw.hackerspace.pl
416 - domain: i
417 storage: ${./zones}
418 file: i
419 - domain: api.ustream.tv
420 storage: ${./zones}
421 file: api.ustream.tv
422 - domain: api.eye.fi
423 storage: ${./zones}
424 file: api.eye.fi
425 log:
426 - target: syslog
427 any: info
428 '';
429 };
430
431 services.nginx.enable = true;
432 services.nginx.mapHashBucketSize = 64;
433 services.nginx.appendHttpConfig = ''
434 server_names_hash_bucket_size 64;
435 '';
Piotr Dobrowolski9c5d8662022-05-08 02:17:41 +0200436 services.nginx.resolver.addresses = [ "127.0.0.1" ];
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200437
vuko740a52d2022-12-27 20:45:00 +0100438 security.acme.acceptTerms = true;
439 security.acme.defaults.email = "bofh@hackerspace.pl";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200440
441 services.nginx.virtualHosts."customs.hackerspace.pl" = {
442 default = true;
443 enableACME = true;
444
445 locations."/" = {
446 extraConfig = ''
447 return 302 https://isztar.mf.gov.pl;
448 '';
449 };
450 locations."/metrics/luftdaten" = {
451 proxyPass = "http://10.8.0.146";
452 };
453 locations."/metrics/spejsiot" = {
454 proxyPass = "http://spejsiot.waw.hackerspace.pl/metrics";
455 };
456 locations."/metrics/apm" = {
457 proxyPass = "http://10.8.1.40:5000/metrics";
458 };
459 locations."/metrics/vending" = {
460 proxyPass = "http://10.8.1.32:8000/";
461 };
462 locations."/metrics/sztancarka" = {
463 proxyPass = "http://10.8.0.96:8888/";
464 };
465 locations."/metrics/mikrotik" = {
466 proxyPass = "http://127.0.0.1:9436/metrics";
467 extraConfig = ''
468 allow 209.250.231.127;
469 deny all;
470 '';
471 };
472 locations."/metrics/node" = {
473 proxyPass = "http://[::1]:9100/metrics";
474 extraConfig = ''
475 allow 209.250.231.127;
476 deny all;
477 '';
478 };
479 locations."/stats/sztancarka-ppm" = {
480 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60";
481 };
482 locations."/stats/sztancarka-last-24h" = {
483 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))";
484 };
485 };
486
487 services.unbound = {
488 enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200489
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200490 settings = {
491 server = {
492 interface = [
493 networks.lan.ipv4
494 "127.0.0.1"
495 "::1"
496 # networks.lan.ipv6 TODO
497 ];
498 access-control = [
499 "127.0.0.1/8 allow"
500 "10.0.0.0/8 allow"
501 ];
502 };
503
504 stub-zone = map (name: {
505 name = name;
506 stub-addr = networks.uplink.ipv4;
507 }) [ "waw.hackerspace.pl" "api.eye.fi" "api.ustream.tv" "i" ];
508 forward-zone = {
509 name = ".";
510 forward-addr = "185.236.240.1";
511 };
512 };
513 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200514
515 # Public VPN access for Hackerspace members
516 services.openvpn.servers.members.config = ''
517 script-security 3
518 auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env
519 verify-client-cert none
520 username-as-common-name
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200521
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200522 #user _openvpn
523 #group _openvpn
524 multihome
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200525
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200526 port 20001
527 proto udp
528 proto udp6
529 dev vpn
530 dev-type tun
531 ca ${secrets-path}/openvpn-public/ca.crt
532 cert ${secrets-path}/openvpn-public/server.crt
533 key ${secrets-path}/openvpn-public/server.key
534 dh ${secrets-path}/openvpn-public/dh.pem
535 server 10.9.1.0 255.255.255.0
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200536
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200537 push "route 10.8.0.0 255.255.0.0"
538 push "route 10.9.0.0 255.255.0.0"
539 push "route 10.10.0.0 255.255.0.0"
540 push "route 10.11.0.0 255.255.0.0"
541 push "dhcp-option DNS ${networks.lan.ipv4}"
542 push "dhcp-option DOMAIN waw.hackerspace.pl"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200543
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200544 ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt
545 #client-config-dir /var/lib/openvpn-public/ccd
546 client-to-client
547 keepalive 10 120
548 comp-lzo
549 persist-key
550 persist-tun
551 '';
552
553 environment.systemPackages = with pkgs; [
554 vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind
555 rxvt_unicode.terminfo update_authorized_keys
556 ];
557 programs.mtr.enable = true;
558
559 environment.variables = {
560 EDITOR = "vim";
561 };
562
563 system.stateVersion = "20.03";
564
565 boot.vesa = false;
566 boot.loader.grub.splashImage = null;
567}
568