blob: 45cd64fa86275cb86d9ed43bfc75f7323df8f85c [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;
vukoee8f1d52022-12-31 01:04:42 +0100319 configFile = "${./dhcpd.conf}";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200320 interfaces = ["lan"];
321 };
322
vukoee8f1d52022-12-31 01:04:42 +0100323 # Checkinator needs access to leases file. When DynamicUser is enable this
324 # file is hidden in /var/lib/private
325 systemd.services.dhcpd4.serviceConfig.DynamicUser= pkgs.lib.mkForce false;
326 users.users.dhcpd = {
327 group = "dhcpd";
328 isSystemUser = true;
329 uid = 1005;
330 };
331 users.groups."dhcpd" = {};
332
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200333 hscloud.routing = {
334 enable = true;
335 # TODO(q3k): make this optional in upstream
336 extra = "";
337 routerID = "185.236.240.5";
338 tables.master.program = true;
339 pipe.v6.aggregate_to_kernel = {
340 table = "master";
341 peerTable = "aggregate";
342 filterIn = ''
343 if source = RTS_OSPF then accept;
344 if source = RTS_OSPF_EXT2 then accept;
345 reject;
346 '';
347 };
348 ospf.v6.upstream = {
349 table = "aggregate";
350 area."0.0.0.0" = {
351 interfaces.uplink = { type = "bcast"; };
352 interfaces.lan = { type = "bcast"; stub = true; };
353 interfaces.loop = { type = "ptp"; stub = true; };
354 };
355 };
356 };
357
358 services.radvd = {
359 enable = true;
360 config = ''
361 interface lan {
362 AdvSendAdvert on;
363 prefix 2a0d:eb00:4242::/64 {
364 };
365 route 0::/0 { };
366 };
367 interface vpn {
368 AdvSendAdvert on;
369 prefix 2a0d:eb00:4242:1::/64 {
370 AdvRouterAddr on;
371 };
372 route 0::/0 { };
373 };
374 '';
375 };
376
377 services.logrotate = {
378 enable = true;
vuko740a52d2022-12-27 20:45:00 +0100379 settings = {
380 "/var/log/ulogd.pcap" = {
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200381 frequency = "weekly";
vuko740a52d2022-12-27 20:45:00 +0100382 postrotate = ''
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200383 ${pkgs.killall}/bin/killall -HUP ulogd
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200384 '';
vuko740a52d2022-12-27 20:45:00 +0100385 rotate = 55;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200386 };
387 };
388 };
389
390 services.cron = let
391 log-neigh = pkgs.writeShellScript "log-neigh" ''
392 mkdir -p /var/log/arptables
393 chmod 700 /var/log/arptables
394
395 # Larger than 10MB? rotate.
396 if [[ $(find /var/log/arptables/arptables.log -type f -size +10485760c 2>/dev/null) ]]; then
397 f=/var/log/arptables/$(date "+%s").log
398 cp /var/log/arptables/arptables.log $f
399 gzip -9 $f
400 rm /var/log/arptables/arptables.log
401 fi
402
403 ip neigh >> /var/log/arptables/arptables.log
404 date --iso-8601=seconds >> /var/log/arptables/arptables.log
405 '';
406 in {
407 mailto = "vuko@hackerspace.pl";
408 enable = true;
409 systemCronJobs = [
410 "*/5 * * * * root ${log-neigh}"
411 "0 3 * * * root ${update_authorized_keys}/bin/update_authorized_keys"
412 ];
413 };
414
415 services.knot = {
416 enable = true;
417 extraConfig = ''
418 server:
419 listen: ${networks.uplink.ipv4}@53
420 listen: ${networks.uplink.ipv6}@53
421
422 zone:
423 - domain: waw.hackerspace.pl
424 storage: ${./zones}
425 file: waw.hackerspace.pl
426 - domain: i
427 storage: ${./zones}
428 file: i
429 - domain: api.ustream.tv
430 storage: ${./zones}
431 file: api.ustream.tv
432 - domain: api.eye.fi
433 storage: ${./zones}
434 file: api.eye.fi
435 log:
436 - target: syslog
437 any: info
438 '';
439 };
440
441 services.nginx.enable = true;
442 services.nginx.mapHashBucketSize = 64;
443 services.nginx.appendHttpConfig = ''
444 server_names_hash_bucket_size 64;
445 '';
Piotr Dobrowolski9c5d8662022-05-08 02:17:41 +0200446 services.nginx.resolver.addresses = [ "127.0.0.1" ];
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200447
vuko740a52d2022-12-27 20:45:00 +0100448 security.acme.acceptTerms = true;
449 security.acme.defaults.email = "bofh@hackerspace.pl";
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200450
451 services.nginx.virtualHosts."customs.hackerspace.pl" = {
452 default = true;
453 enableACME = true;
454
455 locations."/" = {
456 extraConfig = ''
457 return 302 https://isztar.mf.gov.pl;
458 '';
459 };
460 locations."/metrics/luftdaten" = {
461 proxyPass = "http://10.8.0.146";
462 };
463 locations."/metrics/spejsiot" = {
vuko32624092022-12-22 23:09:06 +0100464 proxyPass = "http://10.8.1.16/metrics";
465 extraConfig = ''
466 proxy_set_header Host spejsiot.waw.hackerspace.pl;
467 '';
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200468 };
469 locations."/metrics/apm" = {
470 proxyPass = "http://10.8.1.40:5000/metrics";
471 };
472 locations."/metrics/vending" = {
473 proxyPass = "http://10.8.1.32:8000/";
474 };
475 locations."/metrics/sztancarka" = {
476 proxyPass = "http://10.8.0.96:8888/";
477 };
478 locations."/metrics/mikrotik" = {
479 proxyPass = "http://127.0.0.1:9436/metrics";
480 extraConfig = ''
481 allow 209.250.231.127;
482 deny all;
483 '';
484 };
485 locations."/metrics/node" = {
486 proxyPass = "http://[::1]:9100/metrics";
487 extraConfig = ''
488 allow 209.250.231.127;
489 deny all;
490 '';
491 };
492 locations."/stats/sztancarka-ppm" = {
493 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60";
494 };
495 locations."/stats/sztancarka-last-24h" = {
496 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))";
497 };
498 };
499
vuko3125aa12022-12-31 02:52:24 +0100500 services.unbound = let
501 local-zones = [ "waw.hackerspace.pl." "api.eye.fi." "api.ustream.tv." "i." ];
502 in {
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200503 enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200504
vuko3125aa12022-12-31 02:52:24 +0100505 #enableRootTrustAnchor = false;
506
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200507 settings = {
508 server = {
509 interface = [
510 networks.lan.ipv4
vuko3125aa12022-12-31 02:52:24 +0100511 networks.lan.ipv6
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200512 "127.0.0.1"
513 "::1"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200514 ];
515 access-control = [
vuko3125aa12022-12-31 02:52:24 +0100516 "::1/128 allow"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200517 "127.0.0.1/8 allow"
518 "10.0.0.0/8 allow"
vuko3125aa12022-12-31 02:52:24 +0100519 "${networks.lan.ipv6}/64 allow"
520 "${networks.lan.ipv4}/8 allow"
521 ];
522
523 # disable DNSSEC on locally resolved domains
524 domain-insecure = local-zones;
525
526 # allow LAN adresses only for local domains
527 private-domain = local-zones;
528 private-address = [
529 "10.0.0.0/8"
530 "${networks.lan.ipv6}/64"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200531 ];
532 };
533
vuko3125aa12022-12-31 02:52:24 +0100534 # authoritative DNS servers
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200535 stub-zone = map (name: {
vuko3125aa12022-12-31 02:52:24 +0100536 inherit name;
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200537 stub-addr = networks.uplink.ipv4;
vuko3125aa12022-12-31 02:52:24 +0100538 }) local-zones;
539
540 # recursive DNS servers
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200541 forward-zone = {
542 name = ".";
543 forward-addr = "185.236.240.1";
544 };
545 };
546 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200547
548 # Public VPN access for Hackerspace members
549 services.openvpn.servers.members.config = ''
550 script-security 3
551 auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env
552 verify-client-cert none
553 username-as-common-name
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200554
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200555 #user _openvpn
556 #group _openvpn
557 multihome
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200558
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200559 port 20001
560 proto udp
561 proto udp6
562 dev vpn
563 dev-type tun
564 ca ${secrets-path}/openvpn-public/ca.crt
565 cert ${secrets-path}/openvpn-public/server.crt
566 key ${secrets-path}/openvpn-public/server.key
567 dh ${secrets-path}/openvpn-public/dh.pem
568 server 10.9.1.0 255.255.255.0
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200569
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200570 push "route 10.8.0.0 255.255.0.0"
571 push "route 10.9.0.0 255.255.0.0"
572 push "route 10.10.0.0 255.255.0.0"
573 push "route 10.11.0.0 255.255.0.0"
574 push "dhcp-option DNS ${networks.lan.ipv4}"
575 push "dhcp-option DOMAIN waw.hackerspace.pl"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200576
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200577 ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt
578 #client-config-dir /var/lib/openvpn-public/ccd
579 client-to-client
580 keepalive 10 120
581 comp-lzo
582 persist-key
583 persist-tun
584 '';
585
586 environment.systemPackages = with pkgs; [
587 vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind
588 rxvt_unicode.terminfo update_authorized_keys
589 ];
590 programs.mtr.enable = true;
591
592 environment.variables = {
593 EDITOR = "vim";
594 };
595
596 system.stateVersion = "20.03";
597
598 boot.vesa = false;
599 boot.loader.grub.splashImage = null;
600}
601