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