hswaw/machines/customs: check in code.hackerspace.pl/vuko/customs
Change-Id: Ic698cce2ef0060a54b195cf90574696b8be1eb0f
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1162
Reviewed-by: informatic <informatic@hackerspace.pl>
diff --git a/hswaw/machines/customs.hackerspace.pl/configuration.nix b/hswaw/machines/customs.hackerspace.pl/configuration.nix
new file mode 100644
index 0000000..c00debb
--- /dev/null
+++ b/hswaw/machines/customs.hackerspace.pl/configuration.nix
@@ -0,0 +1,578 @@
+{ config, pkgs, ... }:
+
+let
+ # hscloud checkout, hscloud.routing used to set up dynamic routing (OSPFv6 via bird)
+ hscloud = fetchGit {
+ url = "https://gerrit.hackerspace.pl/hscloud.git";
+ name = "hscloud";
+ rev = "e401735fdd241b25dac4cb82d828dcfa6f84b198";
+ };
+
+ 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";
+ };
+ };
+ 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
+ <nixpkgs/nixos/modules/profiles/minimal.nix>
+ <nixpkgs/nixos/modules/profiles/all-hardware.nix>
+ "${hscloud}/bgpwtf/machines/modules/routing.nix"
+ ./checkinator-tracker.nix
+ ./checkinator-web.nix
+ ./mikrotik-exporter.nix
+ ./netboot.nix
+ ./laserproxy/service.nix
+ ];
+
+
+ boot.loader.grub.enable = true;
+ boot.loader.grub.version = 2;
+ 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;
+ };
+
+ # 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
+
+ # 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 = "10.11.1.1"; 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;
+ passwordAuthentication = false;
+ logLevel = "INFO";
+ };
+
+ users.users.root.openssh.authorizedKeys.keys = [ vuko-pubkey q3k-pubkey ];
+
+ services.dhcpd4 = {
+ enable = true;
+ configFile = ./dhcpd.conf;
+ interfaces = ["lan"];
+ };
+
+ 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;
+ paths = {
+ ulogd = {
+ enable = true;
+ frequency = "weekly";
+ path = "/var/log/ulogd.pcap";
+ extraConfig = ''
+ postrotate
+ ${pkgs.killall}/bin/killall -HUP ulogd
+ endscript
+ '';
+ keep = 55;
+ };
+ };
+ };
+
+ 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 = "vuko@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;
+ '';
+
+ security.acme = {
+ email = "bofh@hackerspace.pl";
+ acceptTerms = true;
+ };
+
+ services.nginx.virtualHosts."customs.hackerspace.pl" = {
+ default = true;
+ 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://spejsiot.waw.hackerspace.pl/metrics";
+ };
+ 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 = {
+ enable = true;
+ interfaces = [
+ networks.lan.ipv4
+ "127.0.0.1"
+ "::1"
+ # networks.lan.ipv6 TODO
+ ];
+ allowedAccess = [
+ "127.0.0.1/8"
+ "10.0.0.0/8"
+ ];
+ extraConfig = builtins.concatStringsSep "\n" ((map (
+ name: ''
+ stub-zone:
+ name: ${name}
+ stub-addr: ${networks.uplink.ipv4}
+ ''
+ ) [ "waw.hackerspace.pl" "api.eye.fi" "api.ustream.tv" "i"]) ++ [''
+ 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;
+}
+