blob: e74b50a91c113d008b4f1e14df8a157bfafd5f5e [file] [log] [blame]
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +02001{ config, pkgs, ... }:
2
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
46 <nixpkgs/nixos/modules/profiles/minimal.nix>
47 <nixpkgs/nixos/modules/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;
369 paths = {
370 ulogd = {
371 enable = true;
372 frequency = "weekly";
373 path = "/var/log/ulogd.pcap";
374 extraConfig = ''
375 postrotate
376 ${pkgs.killall}/bin/killall -HUP ulogd
377 endscript
378 '';
379 keep = 55;
380 };
381 };
382 };
383
384 services.cron = let
385 log-neigh = pkgs.writeShellScript "log-neigh" ''
386 mkdir -p /var/log/arptables
387 chmod 700 /var/log/arptables
388
389 # Larger than 10MB? rotate.
390 if [[ $(find /var/log/arptables/arptables.log -type f -size +10485760c 2>/dev/null) ]]; then
391 f=/var/log/arptables/$(date "+%s").log
392 cp /var/log/arptables/arptables.log $f
393 gzip -9 $f
394 rm /var/log/arptables/arptables.log
395 fi
396
397 ip neigh >> /var/log/arptables/arptables.log
398 date --iso-8601=seconds >> /var/log/arptables/arptables.log
399 '';
400 in {
401 mailto = "vuko@hackerspace.pl";
402 enable = true;
403 systemCronJobs = [
404 "*/5 * * * * root ${log-neigh}"
405 "0 3 * * * root ${update_authorized_keys}/bin/update_authorized_keys"
406 ];
407 };
408
409 services.knot = {
410 enable = true;
411 extraConfig = ''
412 server:
413 listen: ${networks.uplink.ipv4}@53
414 listen: ${networks.uplink.ipv6}@53
415
416 zone:
417 - domain: waw.hackerspace.pl
418 storage: ${./zones}
419 file: waw.hackerspace.pl
420 - domain: i
421 storage: ${./zones}
422 file: i
423 - domain: api.ustream.tv
424 storage: ${./zones}
425 file: api.ustream.tv
426 - domain: api.eye.fi
427 storage: ${./zones}
428 file: api.eye.fi
429 log:
430 - target: syslog
431 any: info
432 '';
433 };
434
435 services.nginx.enable = true;
436 services.nginx.mapHashBucketSize = 64;
437 services.nginx.appendHttpConfig = ''
438 server_names_hash_bucket_size 64;
439 '';
Piotr Dobrowolski9c5d8662022-05-08 02:17:41 +0200440 services.nginx.resolver.addresses = [ "127.0.0.1" ];
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200441
442 security.acme = {
443 email = "bofh@hackerspace.pl";
444 acceptTerms = true;
445 };
446
447 services.nginx.virtualHosts."customs.hackerspace.pl" = {
448 default = true;
449 enableACME = true;
450
451 locations."/" = {
452 extraConfig = ''
453 return 302 https://isztar.mf.gov.pl;
454 '';
455 };
456 locations."/metrics/luftdaten" = {
457 proxyPass = "http://10.8.0.146";
458 };
459 locations."/metrics/spejsiot" = {
460 proxyPass = "http://spejsiot.waw.hackerspace.pl/metrics";
461 };
462 locations."/metrics/apm" = {
463 proxyPass = "http://10.8.1.40:5000/metrics";
464 };
465 locations."/metrics/vending" = {
466 proxyPass = "http://10.8.1.32:8000/";
467 };
468 locations."/metrics/sztancarka" = {
469 proxyPass = "http://10.8.0.96:8888/";
470 };
471 locations."/metrics/mikrotik" = {
472 proxyPass = "http://127.0.0.1:9436/metrics";
473 extraConfig = ''
474 allow 209.250.231.127;
475 deny all;
476 '';
477 };
478 locations."/metrics/node" = {
479 proxyPass = "http://[::1]:9100/metrics";
480 extraConfig = ''
481 allow 209.250.231.127;
482 deny all;
483 '';
484 };
485 locations."/stats/sztancarka-ppm" = {
486 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60";
487 };
488 locations."/stats/sztancarka-last-24h" = {
489 proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))";
490 };
491 };
492
493 services.unbound = {
494 enable = true;
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200495
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200496 settings = {
497 server = {
498 interface = [
499 networks.lan.ipv4
500 "127.0.0.1"
501 "::1"
502 # networks.lan.ipv6 TODO
503 ];
504 access-control = [
505 "127.0.0.1/8 allow"
506 "10.0.0.0/8 allow"
507 ];
508 };
509
510 stub-zone = map (name: {
511 name = name;
512 stub-addr = networks.uplink.ipv4;
513 }) [ "waw.hackerspace.pl" "api.eye.fi" "api.ustream.tv" "i" ];
514 forward-zone = {
515 name = ".";
516 forward-addr = "185.236.240.1";
517 };
518 };
519 };
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200520
521 # Public VPN access for Hackerspace members
522 services.openvpn.servers.members.config = ''
523 script-security 3
524 auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env
525 verify-client-cert none
526 username-as-common-name
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200527
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200528 #user _openvpn
529 #group _openvpn
530 multihome
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200531
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200532 port 20001
533 proto udp
534 proto udp6
535 dev vpn
536 dev-type tun
537 ca ${secrets-path}/openvpn-public/ca.crt
538 cert ${secrets-path}/openvpn-public/server.crt
539 key ${secrets-path}/openvpn-public/server.key
540 dh ${secrets-path}/openvpn-public/dh.pem
541 server 10.9.1.0 255.255.255.0
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200542
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200543 push "route 10.8.0.0 255.255.0.0"
544 push "route 10.9.0.0 255.255.0.0"
545 push "route 10.10.0.0 255.255.0.0"
546 push "route 10.11.0.0 255.255.0.0"
547 push "dhcp-option DNS ${networks.lan.ipv4}"
548 push "dhcp-option DOMAIN waw.hackerspace.pl"
Piotr Dobrowolskib6bc3e62021-10-16 21:56:59 +0200549
Piotr Dobrowolskia01905a2021-10-16 18:22:46 +0200550 ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt
551 #client-config-dir /var/lib/openvpn-public/ccd
552 client-to-client
553 keepalive 10 120
554 comp-lzo
555 persist-key
556 persist-tun
557 '';
558
559 environment.systemPackages = with pkgs; [
560 vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind
561 rxvt_unicode.terminfo update_authorized_keys
562 ];
563 programs.mtr.enable = true;
564
565 environment.variables = {
566 EDITOR = "vim";
567 };
568
569 system.stateVersion = "20.03";
570
571 boot.vesa = false;
572 boot.loader.grub.splashImage = null;
573}
574