blob: ce13373c9b58530ce7b400a438ddde828df13fb4 [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" = {
vuko32624092022-12-22 23:09:06 +0100454 proxyPass = "http://10.8.1.16/metrics";
455 extraConfig = ''
456 proxy_set_header Host spejsiot.waw.hackerspace.pl;
457 '';
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200458 };
459 locations."/metrics/apm" = {
460 proxyPass = "http://10.8.1.40:5000/metrics";
461 };
462 locations."/metrics/vending" = {
463 proxyPass = "http://10.8.1.32:8000/";
464 };
465 locations."/metrics/sztancarka" = {
466 proxyPass = "http://10.8.0.96:8888/";
467 };
468 locations."/metrics/mikrotik" = {
469 proxyPass = "http://127.0.0.1:9436/metrics";
470 extraConfig = ''
471 allow 209.250.231.127;
472 deny all;
473 '';
474 };
475 locations."/metrics/node" = {
476 proxyPass = "http://[::1]:9100/metrics";
477 extraConfig = ''
478 allow 209.250.231.127;
479 deny all;
480 '';
481 };
482 locations."/stats/sztancarka-ppm" = {
483 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60";
484 };
485 locations."/stats/sztancarka-last-24h" = {
486 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))";
487 };
488 };
489
490 services.unbound = {
491 enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200492
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200493 settings = {
494 server = {
495 interface = [
496 networks.lan.ipv4
497 "127.0.0.1"
498 "::1"
499 # networks.lan.ipv6 TODO
500 ];
501 access-control = [
502 "127.0.0.1/8 allow"
503 "10.0.0.0/8 allow"
504 ];
505 };
506
507 stub-zone = map (name: {
508 name = name;
509 stub-addr = networks.uplink.ipv4;
510 }) [ "waw.hackerspace.pl" "api.eye.fi" "api.ustream.tv" "i" ];
511 forward-zone = {
512 name = ".";
513 forward-addr = "185.236.240.1";
514 };
515 };
516 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200517
518 # Public VPN access for Hackerspace members
519 services.openvpn.servers.members.config = ''
520 script-security 3
521 auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env
522 verify-client-cert none
523 username-as-common-name
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200524
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200525 #user _openvpn
526 #group _openvpn
527 multihome
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200528
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200529 port 20001
530 proto udp
531 proto udp6
532 dev vpn
533 dev-type tun
534 ca ${secrets-path}/openvpn-public/ca.crt
535 cert ${secrets-path}/openvpn-public/server.crt
536 key ${secrets-path}/openvpn-public/server.key
537 dh ${secrets-path}/openvpn-public/dh.pem
538 server 10.9.1.0 255.255.255.0
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200539
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200540 push "route 10.8.0.0 255.255.0.0"
541 push "route 10.9.0.0 255.255.0.0"
542 push "route 10.10.0.0 255.255.0.0"
543 push "route 10.11.0.0 255.255.0.0"
544 push "dhcp-option DNS ${networks.lan.ipv4}"
545 push "dhcp-option DOMAIN waw.hackerspace.pl"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200546
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200547 ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt
548 #client-config-dir /var/lib/openvpn-public/ccd
549 client-to-client
550 keepalive 10 120
551 comp-lzo
552 persist-key
553 persist-tun
554 '';
555
556 environment.systemPackages = with pkgs; [
557 vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind
558 rxvt_unicode.terminfo update_authorized_keys
559 ];
560 programs.mtr.enable = true;
561
562 environment.variables = {
563 EDITOR = "vim";
564 };
565
566 system.stateVersion = "20.03";
567
568 boot.vesa = false;
569 boot.loader.grub.splashImage = null;
570}
571