blob: a52d92e76a52b31bfab6fe128f085588c448940e [file] [log] [blame]
{ 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;
}