| { config, pkgs, modulesPath, ... }: |
| |
| let |
| hw = builtins.fromJSON (builtins.readFile ./hw.json); |
| fw = import ./fw-7535.nix; |
| vuko-pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFhaCaC/CVYv6hphqmEdKaPrIn+Q946+myvL9SSnzFZk vuko@eagle"; |
| q3k-pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG599UildOrAq+LIOQjKqtGMwjgjIxozI1jtQQRKHtCP q3k@mimeomia"; |
| networks = { |
| uplink = { |
| description = "Hackerspace Internet Uplink"; |
| hw_addr = builtins.elemAt fw.hw_addresses 0; |
| ipv4 = "185.236.240.5"; |
| ipv6 = "2a0d:eb00:2137:1::3"; |
| }; |
| lan = { |
| description = "Hackerspace LAN"; |
| hw_addr = builtins.elemAt fw.hw_addresses 1; |
| ipv4 = "10.8.1.2"; |
| ipv6 = "2a0d:eb00:4242::1"; |
| }; |
| managment = { |
| description = "Management network (temporary routing)"; |
| hw_addr = builtins.elemAt fw.hw_addresses 2; |
| }; |
| lte = { |
| description = "temp LTE uplink"; |
| hw_addr = builtins.elemAt fw.hw_addresses 3; |
| }; |
| vpn = { |
| description = "Hackerspace members vpn"; |
| ipv4 = "10.9.1.1"; |
| }; |
| bms = { |
| ipv4 = "10.11.1.1"; |
| }; |
| }; |
| hostname = "customs"; |
| openvpn-auth = import ./openvpn-auth { inherit pkgs; }; |
| secrets-path = "/etc/nixos/secrets/"; |
| update_authorized_keys = pkgs.writeShellScriptBin "update_authorized_keys" '' |
| ${pkgs.python3.withPackages (pp: [ pp.ldap3 ])}/bin/python ${./update_authorized_keys.py} ${hostname} ${secrets-path}/ldap-password.txt |
| ''; |
| |
| in { |
| imports = |
| [ |
| ./ulogd2/service.nix |
| #./hardware-configuration.nix |
| (modulesPath + "/profiles/minimal.nix") |
| (modulesPath + "/profiles/all-hardware.nix") |
| ../../../bgpwtf/machines/modules/routing.nix |
| ./checkinator-tracker.nix |
| ./checkinator-web.nix |
| ./mikrotik-exporter.nix |
| ./netboot.nix |
| ./doorman/service.nix |
| ./beyondspace.nix |
| ./laserproxy/service.nix |
| ]; |
| |
| # Prevent spurious rebuilds due to dbus override on minimal profile |
| environment.noXlibs = false; |
| |
| boot.loader.grub.enable = true; |
| boot.loader.grub.device = "nodev"; |
| boot.loader.grub.extraConfig = '' |
| serial --unit=0 --speed=115200 |
| terminal_input serial |
| terminal_output serial |
| ''; |
| boot.kernelParams = ["console=tty0" "console=ttyS0,115200"]; |
| |
| time.timeZone = "Europe/Warsaw"; |
| |
| fileSystems."/" = { |
| device = "/dev/disk/by-partuuid/${hw.rootUUID}"; |
| fsType = "ext4"; |
| }; |
| |
| services.postfix = let acme_dir = "/var/lib/acme"; in { |
| enable = true; |
| domain = "customs.hackerspace.pl"; |
| hostname = "customs.hackerspace.pl"; |
| destination = [ "localhost" ]; |
| sslCert = "${acme_dir}/customs.hackerspace.pl/full.pem"; |
| sslKey = "${acme_dir}/customs.hackerspace.pl/key.pem"; |
| enableSmtp = true; |
| enableSubmission = false; |
| #relayHost = "hackerspace.pl"; |
| extraConfig = '' |
| inet_interfaces = loopback-only |
| ''; |
| }; |
| |
| fileSystems."/mnt/secrets" = { |
| fsType = "tmpfs"; |
| options = [ "rw" "mode=755" "size=200M" "nosuid" "nodev" "relatime" "noexec" ]; |
| }; |
| |
| networking.hostName = hostname; |
| networking.domain = "hackerspace.pl"; |
| |
| networking.useDHCP = false; |
| networking.vlans = { |
| laser = { |
| id = 4001; |
| interface = "lan"; |
| }; |
| bms = { |
| id = 4002; |
| interface = "lan"; |
| }; |
| }; |
| |
| systemd.services.secrets = { |
| enable = true; |
| description = "Copy secrets and fix permissions"; |
| script = '' |
| ${pkgs.coreutils}/bin/install --owner=root --mode=700 --directory /mnt/secrets/nginx/ |
| ${pkgs.coreutils}/bin/install --owner=root --mode=400 -t /mnt/secrets/nginx/ \ |
| ${secrets-path}/nginx/at.hackerspace.pl.key \ |
| ${secrets-path}/nginx/at.hackerspace.pl.crt |
| ${pkgs.acl}/bin/setfacl -m "u:nginx:rx" /mnt/secrets/nginx |
| ${pkgs.acl}/bin/setfacl -m "u:nginx:r" /mnt/secrets/nginx/* |
| ''; |
| wantedBy = [ "nginx.service" ]; |
| partOf = [ "nginx.service" ]; |
| serviceConfig.Type = "oneshot"; |
| serviceConfig.RemainAfterExit = "true"; |
| serviceConfig.User = "root"; |
| }; |
| |
| services.prometheus.exporters.node = { |
| enable = true; |
| listenAddress = "[::1]"; |
| port = 9100; |
| enabledCollectors = [ "systemd" ]; |
| }; |
| |
| systemd.network.links = builtins.listToAttrs (map ( |
| name: { name = "10-link-${name}"; value = { |
| enable = true; |
| matchConfig = { |
| MACAddress = networks."${name}".hw_addr; |
| }; |
| linkConfig = { |
| Name = "${name}"; |
| }; |
| }; } |
| ) (builtins.filter (name: builtins.hasAttr "hw_addr" networks."${name}") (builtins.attrNames networks))); |
| |
| #networking.interfaces.vpn = { |
| # virtual = true; |
| # name = "vpn"; |
| # #ipv4.addresses = [ { address = 10.9.1.1; prefixlen = 16; } ]; |
| #}; |
| |
| boot.kernel.sysctl = { |
| "net.ipv4.ip_forward" = true; |
| "net.ipv6.conf.all.forwarding" = true; |
| }; |
| |
| hswaw.doorman-proxy = { |
| enable = true; |
| address = networks.bms.ipv4; |
| port = 8000; |
| password-file = "/root/secrets/ac-ldap-password.txt"; |
| }; |
| |
| # using nftables so firewall has to be disabled |
| networking.firewall.enable = false; |
| networking.nftables.enable = true; |
| networking.nftables.ruleset = '' |
| table inet filter { |
| chain input { |
| type filter hook input priority 0; |
| |
| # accept any localhost traffic |
| iifname lo accept |
| |
| # accept traffic originated from us |
| ct state {established, related} accept |
| |
| # ICMP |
| 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 |
| ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept |
| |
| # allow "ping" |
| ip6 nexthdr icmpv6 icmpv6 type echo-request accept |
| ip protocol icmp icmp type echo-request accept |
| |
| # allow OSPFv3 |
| ip6 nexthdr 89 accept |
| |
| tcp dport 22 accept |
| tcp dport 53 accept |
| udp dport 53 accept |
| tcp dport 80 accept |
| tcp dport 443 accept |
| udp dport tftp accept |
| |
| iifname managment udp dport tftp accept |
| iifname lan tcp dport 8080 accept |
| |
| # mosquitto |
| iifname bms tcp dport 1883 accept |
| iifname bms tcp dport ${toString config.hswaw.doorman-proxy.port} accept |
| |
| # openvpn-members |
| udp dport 20001 accept |
| tcp dport 20001 accept |
| |
| # laserproxy |
| udp dport 40200 accept |
| udp dport 50200 accept |
| |
| counter drop |
| } |
| |
| # Allow all outgoing connections. |
| chain output { |
| type filter hook output priority 0; policy accept; |
| } |
| |
| chain forward { |
| type filter hook forward priority 0; policy drop; |
| ct state {established, related} jump accepted |
| oifname "loop" jump accepted |
| ip saddr 10.8.0.0/16 iifname "lan" jump accepted |
| ip saddr 10.9.0.0/16 iifname "vpn" jump accepted |
| ip6 saddr 2a0d:eb00:4242::0/64 iifname "lan" jump accepted |
| ip6 saddr 2a0d:eb00:4242:1::0/64 iifname "vpn" jump accepted |
| ip6 saddr 2a0d:eb00:4242:1::1/128 iifname "loop" jump accepted |
| } |
| |
| chain accepted { |
| # IMPORTANT |
| # Log all connections to the outside world from LAN interface, as we are |
| # required to do so |
| oifname != "uplink" accept |
| iifname "uplink" accept |
| ip daddr { 10.0.0.0/8, 225.225.225.225/32 } accept |
| ip6 daddr { 2a0d:eb00::/29, fe80::/8 } accept |
| log group 2 accept |
| } |
| } |
| |
| table inet net { |
| chain postrouting { |
| type nat hook postrouting priority 100; |
| ip saddr 10.8.0.0/16 oifname uplink snat ${networks.uplink.ipv4} |
| ip saddr 10.9.0.0/16 oifname uplink snat ${networks.uplink.ipv4} |
| } |
| |
| chain prerouting { |
| type nat hook prerouting priority -100; |
| |
| # Access to staszkecoin from Internet |
| ip version 4 iifname "uplink" tcp dport 8333 dnat 10.8.1.49 |
| } |
| } |
| ''; |
| |
| systemd.services."loop-netdev" = let n = "loop"; in { |
| description = "Dummy interface: loop"; |
| wantedBy = [ "network-setup.service" "sys-subsystem-net-devices-${n}.device" ]; |
| partOf = [ "network-setup.service" ]; |
| after = [ "network-pre.target" ]; |
| before = [ "network-setup.service" ]; |
| serviceConfig.Type = "oneshot"; |
| serviceConfig.RemainAfterExit = true; |
| path = [ pkgs.iproute ]; |
| script = '' |
| # Remove Dead Interfaces |
| ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" |
| ip link add "${n}" type dummy |
| ip link set "${n}" up |
| ''; |
| postStop = '' |
| ip link delete "${n}" |
| ''; |
| }; |
| |
| |
| networking.interfaces = { |
| uplink = { |
| ipv4.addresses = [ { address = networks.uplink.ipv4; prefixLength = 31; } ]; |
| ipv6.addresses = [ |
| { address = networks.uplink.ipv6; prefixLength = 112; } |
| ]; |
| }; |
| lan = { |
| ipv4.addresses = [ { address = networks.lan.ipv4; prefixLength = 16; } ]; |
| ipv6.addresses = [ { address = networks.lan.ipv6; prefixLength = 64; } ]; |
| }; |
| loop = { |
| ipv6.addresses = [ { address = "2a0d:eb00:4242:1::1"; prefixLength = 128; } ]; |
| }; |
| laser = { |
| ipv4.addresses = [ { address = "10.11.0.1"; prefixLength = 24; } ]; |
| }; |
| bms = { |
| ipv4.addresses = [ { address = networks.bms.ipv4; prefixLength = 24; } ]; |
| }; |
| managment = { |
| ipv4.addresses = [ { address = "10.10.1.1"; prefixLength = 24; } ]; |
| }; |
| lte = { |
| ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; |
| }; |
| }; |
| |
| networking.defaultGateway = { |
| address = "185.236.240.4"; |
| interface = "uplink"; |
| }; |
| networking.defaultGateway6 = { |
| address = "2a0d:eb00:2137:1::1"; |
| interface = "uplink"; |
| }; |
| |
| |
| networking.nameservers = [ "1.0.0.1" "8.8.8.8" ]; |
| |
| services.openssh = { |
| enable = true; |
| settings = { |
| PasswordAuthentication = false; |
| LogLevel = "INFO"; |
| }; |
| }; |
| |
| users.users.root.openssh.authorizedKeys.keys = [ vuko-pubkey q3k-pubkey ]; |
| |
| services.dhcpd4 = { |
| enable = true; |
| configFile = "${./dhcpd.conf}"; |
| interfaces = ["lan" "bms"]; |
| }; |
| |
| services.mosquitto.enable = true; |
| services.mosquitto.listeners = [ |
| { |
| address = networks.bms.ipv4; |
| port = 1883; |
| settings = { |
| allow_anonymous = true; |
| }; |
| acl = [ |
| "topic readwrite #" |
| ]; |
| } |
| ]; |
| |
| # Checkinator needs access to leases file. When DynamicUser is enable this |
| # file is hidden in /var/lib/private |
| systemd.services.dhcpd4.serviceConfig.DynamicUser= pkgs.lib.mkForce false; |
| users.users.dhcpd = { |
| group = "dhcpd"; |
| isSystemUser = true; |
| uid = 1005; |
| }; |
| users.groups."dhcpd" = {}; |
| |
| hscloud.routing = { |
| enable = true; |
| # TODO(q3k): make this optional in upstream |
| extra = ""; |
| routerID = "185.236.240.5"; |
| tables.master.program = true; |
| pipe.v6.aggregate_to_kernel = { |
| table = "master"; |
| peerTable = "aggregate"; |
| filterIn = '' |
| if source = RTS_OSPF then accept; |
| if source = RTS_OSPF_EXT2 then accept; |
| reject; |
| ''; |
| }; |
| ospf.v6.upstream = { |
| table = "aggregate"; |
| area."0.0.0.0" = { |
| interfaces.uplink = { type = "bcast"; }; |
| interfaces.lan = { type = "bcast"; stub = true; }; |
| interfaces.loop = { type = "ptp"; stub = true; }; |
| }; |
| }; |
| }; |
| |
| services.radvd = { |
| enable = true; |
| config = '' |
| interface lan { |
| AdvSendAdvert on; |
| prefix 2a0d:eb00:4242::/64 { |
| }; |
| route 0::/0 { }; |
| }; |
| interface vpn { |
| AdvSendAdvert on; |
| prefix 2a0d:eb00:4242:1::/64 { |
| AdvRouterAddr on; |
| }; |
| route 0::/0 { }; |
| }; |
| ''; |
| }; |
| |
| services.logrotate = { |
| enable = true; |
| settings = { |
| "/var/log/ulogd.pcap" = { |
| frequency = "weekly"; |
| postrotate = '' |
| ${pkgs.killall}/bin/killall -HUP ulogd |
| ''; |
| rotate = 55; |
| delaycompress = true; |
| compress = true; |
| compresscmd = "${pkgs.zstd}/bin/zstd"; |
| uncompresscmd = "${pkgs.zstd}/bin/unzstd"; |
| compressext = ".zst"; |
| compressoptions = "--rm"; |
| }; |
| }; |
| }; |
| |
| services.cron = let |
| log-neigh = pkgs.writeShellScript "log-neigh" '' |
| mkdir -p /var/log/arptables |
| chmod 700 /var/log/arptables |
| |
| # Larger than 10MB? rotate. |
| if [[ $(find /var/log/arptables/arptables.log -type f -size +10485760c 2>/dev/null) ]]; then |
| f=/var/log/arptables/$(date "+%s").log |
| cp /var/log/arptables/arptables.log $f |
| gzip -9 $f |
| rm /var/log/arptables/arptables.log |
| fi |
| |
| ip neigh >> /var/log/arptables/arptables.log |
| date --iso-8601=seconds >> /var/log/arptables/arptables.log |
| ''; |
| in { |
| mailto = "both@hackerspace.pl"; |
| enable = true; |
| systemCronJobs = [ |
| "*/5 * * * * root ${log-neigh}" |
| "0 3 * * * root ${update_authorized_keys}/bin/update_authorized_keys" |
| ]; |
| }; |
| |
| services.knot = { |
| enable = true; |
| extraConfig = '' |
| server: |
| listen: ${networks.uplink.ipv4}@53 |
| listen: ${networks.uplink.ipv6}@53 |
| |
| zone: |
| - domain: waw.hackerspace.pl |
| storage: ${./zones} |
| file: waw.hackerspace.pl |
| - domain: i |
| storage: ${./zones} |
| file: i |
| - domain: api.ustream.tv |
| storage: ${./zones} |
| file: api.ustream.tv |
| - domain: api.eye.fi |
| storage: ${./zones} |
| file: api.eye.fi |
| log: |
| - target: syslog |
| any: info |
| ''; |
| }; |
| |
| services.nginx.enable = true; |
| services.nginx.mapHashBucketSize = 64; |
| services.nginx.appendHttpConfig = '' |
| server_names_hash_bucket_size 64; |
| ''; |
| services.nginx.resolver.addresses = [ "127.0.0.1" ]; |
| |
| security.acme.acceptTerms = true; |
| security.acme.defaults.email = "bofh@hackerspace.pl"; |
| |
| services.nginx.virtualHosts."customs.hackerspace.pl" = { |
| enableACME = true; |
| |
| locations."/" = { |
| extraConfig = '' |
| return 302 https://isztar.mf.gov.pl; |
| ''; |
| }; |
| locations."/metrics/luftdaten" = { |
| proxyPass = "http://10.8.0.146"; |
| }; |
| locations."/metrics/spejsiot" = { |
| proxyPass = "http://10.8.1.16/metrics"; |
| extraConfig = '' |
| proxy_set_header Host spejsiot.waw.hackerspace.pl; |
| ''; |
| }; |
| locations."/metrics/apm" = { |
| proxyPass = "http://10.8.1.40:5000/metrics"; |
| }; |
| locations."/metrics/vending" = { |
| proxyPass = "http://10.8.1.32:8000/"; |
| }; |
| locations."/metrics/sztancarka" = { |
| proxyPass = "http://10.8.0.96:8888/"; |
| }; |
| locations."/metrics/mikrotik" = { |
| proxyPass = "http://127.0.0.1:9436/metrics"; |
| extraConfig = '' |
| allow 209.250.231.127; |
| deny all; |
| ''; |
| }; |
| locations."/metrics/node" = { |
| proxyPass = "http://[::1]:9100/metrics"; |
| extraConfig = '' |
| allow 209.250.231.127; |
| deny all; |
| ''; |
| }; |
| locations."/stats/sztancarka-ppm" = { |
| proxyPass = "http://10.8.0.96:9090/api/v1/query?query=rate%28cut_count_total%5B15m%5D%29+*+60"; |
| }; |
| locations."/stats/sztancarka-last-24h" = { |
| proxyPass = "http://10.8.0.96:9090/api/v1/query?query=round(increase(cut_count_total[24h]))"; |
| }; |
| }; |
| |
| services.unbound = let |
| local-zones = [ "waw.hackerspace.pl." "api.eye.fi." "api.ustream.tv." "i." ]; |
| in { |
| enable = true; |
| |
| #enableRootTrustAnchor = false; |
| |
| settings = { |
| server = { |
| interface = [ |
| networks.lan.ipv4 |
| networks.lan.ipv6 |
| "127.0.0.1" |
| "::1" |
| ]; |
| access-control = [ |
| "::1/128 allow" |
| "127.0.0.1/8 allow" |
| "10.0.0.0/8 allow" |
| "${networks.lan.ipv6}/64 allow" |
| "${networks.lan.ipv4}/8 allow" |
| ]; |
| |
| # disable DNSSEC on locally resolved domains |
| domain-insecure = local-zones; |
| |
| # allow LAN adresses only for local domains |
| private-domain = local-zones; |
| private-address = [ |
| "10.0.0.0/8" |
| "${networks.lan.ipv6}/64" |
| ]; |
| }; |
| |
| # authoritative DNS servers |
| stub-zone = map (name: { |
| inherit name; |
| stub-addr = networks.uplink.ipv4; |
| }) local-zones; |
| |
| # recursive DNS servers |
| forward-zone = { |
| name = "."; |
| forward-addr = "185.236.240.1"; |
| }; |
| }; |
| }; |
| |
| # Public VPN access for Hackerspace members |
| services.openvpn.servers.members.config = '' |
| script-security 3 |
| auth-user-pass-verify ${openvpn-auth}/bin/openvpn-auth-member via-env |
| verify-client-cert none |
| username-as-common-name |
| |
| #user _openvpn |
| #group _openvpn |
| multihome |
| |
| port 20001 |
| proto udp |
| proto udp6 |
| dev vpn |
| dev-type tun |
| ca ${secrets-path}/openvpn-public/ca.crt |
| cert ${secrets-path}/openvpn-public/server.crt |
| key ${secrets-path}/openvpn-public/server.key |
| dh ${secrets-path}/openvpn-public/dh.pem |
| server 10.9.1.0 255.255.255.0 |
| |
| push "route 10.8.0.0 255.255.0.0" |
| push "route 10.9.0.0 255.255.0.0" |
| push "route 10.10.0.0 255.255.0.0" |
| push "route 10.11.0.0 255.255.0.0" |
| push "dhcp-option DNS ${networks.lan.ipv4}" |
| push "dhcp-option DOMAIN waw.hackerspace.pl" |
| |
| ifconfig-pool-persist /var/lib/openvpn-public/ipp.txt |
| #client-config-dir /var/lib/openvpn-public/ccd |
| client-to-client |
| keepalive 10 120 |
| comp-lzo |
| persist-key |
| persist-tun |
| ''; |
| |
| environment.systemPackages = with pkgs; [ |
| vim tcpdump htop nmon tmux git file procps parted dmidecode ack utillinux nmap mosh ncdu tree lz4 bind |
| rxvt_unicode.terminfo update_authorized_keys |
| ]; |
| programs.mtr.enable = true; |
| |
| environment.variables = { |
| EDITOR = "vim"; |
| }; |
| |
| system.stateVersion = "20.03"; |
| |
| boot.vesa = false; |
| boot.loader.grub.splashImage = null; |
| } |
| |