Compare commits

..

1 Commits

Author SHA1 Message Date
Astro 4260ec2405 salt/switches: sort output stably 2021-11-07 01:18:25 +01:00
214 changed files with 29325 additions and 10755 deletions

4
.gitignore vendored
View File

@ -1,3 +1 @@
/contact.md
/config/secrets-production.nix
/config/secrets-production.nix.old
contact.md

View File

@ -23,11 +23,20 @@ Wir, ein kleiner Kreis von Menschen die das Netzwerk im Zentralwerk betreuen, ha
* [Verkabelung Patchpanel Haus B](cabling.md)
* TODO: tuerme, saal
* [Planung der IP-Adressbereiche](subnets.md)
* (verschlüsselte) [Kontakte zu den Personen mit den einzelnen Anschlüssen](contact.md.gpg)
* Dokumentation
* [WiFi-Provisionierung](doc/wifi-provisioning.md) und Erstellung von Privatnetzen
* `flake.nix` und Unterverzeichnis [nix/](nix): Konfigurationsskripte
### Nixification Roadmap
- [x] Einlesen der Salt-Daten in Nix
- [x] Containererstellung
- [x] Migration der Container
- [ ] device-scripts auf Site Config umstellen
- [ ] Site Config ohne Entschlüsselung dumpen, Salt-Daten löschen
### Development Setup
Get Nix with Flakes first:
@ -52,16 +61,11 @@ set.
#### Gerätekonfigurationen
```bash
```
nix build .#all-device-scripts
./result/bin/...
```
Oder direkt:
```bash
nix run .#switch-b2 && nix run .#ap3
```
### Server Setup
```
@ -73,15 +77,11 @@ auch `/etc/nixos` so dass `nixos-rebuild switch` problemlos
klappt. Ausserdem ist dieser lokale Checkout in der `nix registry`
eingetragen, was von bspw. von `build-container` verwendet wird.
Ausserdem werden dort immer wieder `nix run .#switch-to-production`
ausgeführt.
Der Flake-input `zentralwerk-network-key` ist mit einem lokalen
Repository überschrieben, weshalb die `flake.lock` dirty ist.
### LXC-Containers auf Server
Warum Container? Mehrere Routing-Tabellen verteile auf virtuelle
Router statt Policy Routing oder Virtual Routing Functions. Container
sind einfach zwischen Servern migrierbar.
Ein Server erwartet die für ihn (`location = hostName`) konfigurierten
Container. systemd versucht sie zu starten. Das wird erst nach
`build-container` funktionieren, welches das Rootfs anlegt.
@ -100,6 +100,9 @@ Innerhalb eines Containers gibts erneut `systemctl`, `journalctl`,
### Nix Flake
[Segfaulting nix?](https://github.com/NixOS/nix/issues/4178) Until
resolved, `export GC_DONT_GC=1`
#### Site Config
Wir verwenden das Module-System von NixOS eigenständig in
@ -119,14 +122,6 @@ Systemkonfigurationen für alle Hosts mit der `role` `"server"` oder
Alle NixOS-Einstellungen
#### Secrets
```bash
nix run .#decrypt-secrets
$EDITOR config/secrets-production.nix
nix run .#encrypt-secrets
```
### server1 als Cold Standby
Was ein Server kann, kann ein anderer auch. Er sollte gelegentlich
@ -137,14 +132,11 @@ werden, muss die Datei `/etc/start-containers` *vorhanden* sein. Zum
Umgang damit gibt es die zwei handlichen Befehle `enable-containers`
und `disable-containers`.
### Dinge die stets schieflaufen
#### IP Subnet Plans
#### pub-Netz läuft, aber kein DHCP+Internet darüber verfügbar
`nix build .#`[subnetplan4](https://hydra.hq.c3d2.de/job/c3d2/zentralwerk-network/subnetplans/latest/download/1)
Eventuell hat `uci` bei der ap-Konfiguration die Switch-Konfiguration
für dieses VLAN nicht in `/etc/config/network` angelegt.
`nix build .#`[subnetplan6](https://hydra.hq.c3d2.de/job/c3d2/zentralwerk-network/subnetplans/latest/download/2)
#### Network Maps
![Physikalische Vernetzung](https://hydra.hq.c3d2.de/job/c3d2/zentralwerk-network/network-graphs/latest/download/2)
![Logische Vernetzung](https://hydra.hq.c3d2.de/job/c3d2/zentralwerk-network/network-graphs/latest/download/1)
* **Überprüfen**: `swconfig dev switch0 show`

View File

@ -26,11 +26,11 @@ Alle Stecker im Haus sind in Schema A gecrimpt.
| | ![][gi] B 2.05.02 | ![][gi] UVB 1.09 | | 14 |
| ![][ri] B 4.02.01 *v* | ![][gi] B 2.05.05 | ![][gi] UVB 1.10 | | 15 |
| ![][ri] B 4.01.01 *v* | ![][gi] B 2.05.06 | ![][gi] 1.06 | | 16 |
| ![][ri] B 4.03.01 *v* | ![][gi] B 2.05.03 *v* | ![][gi] 1.16 *v* | | 17 |
| ![][ri] B 4.03.01 | ![][gi] B 2.05.03 *v* | | | 17 |
| ![][ri] B 4.04.01 *v* | ![][gi] B 2.05.07 *v* | | | 18 |
| ![][ri] B 4.05.02 *v* | ![][gi] B 2.06 | | | 19 |
| ![][ri] B 4.06.01 *v* | ![][ri] B 2.07 | | | 20 |
| ![][ri] B 4.07.05 *v* | | | | 21 |
| ![][ri] B 4.06.01 | ![][ri] B 2.07 | | | 20 |
| ![][ri] B 4.07.05 | | | | 21 |
| ![][ri] B 4.08.01 | | | | 22 |
| ![][ri] B 4.09.01 *v* | | | | 23 |
| ![][ri] B 4.10.01 *v* | | | | 24 |

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
{ config, lib, ... }:
let
# https://github.com/NixOS/nixpkgs/pull/206965
concatMapAttrsRecursive = with lib; f: flip pipe [ (mapAttrs f) attrValues (foldl' recursiveUpdate { }) ];
in
{
imports = [
# Secrets
./secrets.nix
# Hardware
./vlan.nix
./switch.nix
./ap.nix
./server.nix
]
# IP networks
++ lib.filesystem.listFilesRecursive ./net;
}

View File

@ -1,140 +0,0 @@
{ lib, ... }:
{
site.net.c3d2 = {
dhcp = {
server = "c3d2-gw3";
start = "172.22.99.100";
end = "172.22.99.199";
fixed-hosts = {
"172.22.99.96" = "08:00:27:bb:8c:b3";
"172.22.99.98" = "08:00:27:aa:90:e2";
# "astrom" = "aa:00:5b:08:f0:5c";
# "astron" = "aa:00:5b:08:f0:5b";
# "batman" = "5c:cf:7f:c0:05:28";
# "beere" = "b8:27:eb:ac:65:d2";
# "beere2" = "b8:27:eb:53:0b:27";
# "bender.hq.c3de.de" = "00:23:df:7e:c8:0a";
# "cider" = "00:0d:93:75:ee:fa";
"dacbert" = "dc:a6:32:e0:46:bf";
"dn42" = "aa:00:42:7a:32:46";
# "drucker" = "00:23:c3:d2:12:0f";
# "feile" = "aa:00:5b:12:c1:f7";
# "fernandopoo" = "aa:00:f7:52:85:27";
# "fhem" = "b8:27:eb:9e:8b:db";
# "git" = "aa:00:47:d8:57:10";
"glotzbert" = "90:1b:0e:88:da:0a";
# "wled-nix-snowflake" = "44:17:93:10:77:e8";
# "wled-fairy-dust" = "3c:61:05:e3:2f:ad";
# "wled-warnbert" = "3c:61:05:fc:21:37";
# "wled-matrix" = "e8:db:84:e4:f4:30";
# "ledball1" = "b8:27:eb:53:0b:27";
# Beleuchtungskiste auf Traverse über Fernseher
# "ledbeere" = "b8:27:eb:60:99:59";
# "leviathan" = "00:ff:08:31:db:e5";
# "lisbeth" = "b8:27:eb:a5:ee:5c";
# "marenz-build" = "44:1e:a1:59:2e:e8";
# "matemat" = "a2:1b:7c:e8:19:72";
# "minecraft" = "4a:57:d3:64:fe:e9";
# "moleflap" = "aa:00:0d:b1:6c:67";
# "monit" = "00:23:ae:94:e7:19";
"pipebert" = "ec:a8:6b:fe:b4:cb";
# "public-access-proxy" = "12:24:5f:bd:9b:e7";
"pulsebert" = "b8:27:eb:16:31:61";
# "ruststripe1" = "06:32:0e:39:21:69";
"schalter" = "b8:27:eb:ac:65:d2";
# "semanta" = "00:ff:e4:bb:ea:2a";
# "server2" = "d0:67:e5:f3:57:10";
# "server3" = "e4:1f:13:2e:4f:c0";
# "server4" = "00:9c:02:a9:26:01";
# "sharing" = "00:23:c3:d2:75:18";
# "sofafon" = "b8:27:eb:23:8d:01";
# "storage2" = "42:5e:0f:4e:f3:cc";
# "ustriper" = "aa:bb:95:33:bb:aa";
# "wiefelspuetz" = "aa:00:7f:01:8a:d0";
# "wormhole" = "00:23:c3:d2:00:76";
# "www1" = "aa:00:13:8b:03:47";
# "riscbert" = "6c:cf:39:00:05:95";
};
time = 300;
max-time = 30 * 24 * 3600;
router = "c3d2-gw3";
};
domainName = "c3d2.zentralwerk.org";
dynamicDomain = true;
subnet4 = "172.22.99.0/24";
hosts4 = {
c3d2-anon = "172.22.99.1";
c3d2-gw1 = "172.22.99.2";
c3d2-gw2 = "172.22.99.3";
c3d2-gw3 = "172.22.99.4";
dacbert = "172.22.99.203";
schalter = "172.22.99.204";
glotzbert = "172.22.99.205";
pulsebert = "172.22.99.208";
pipebert = "172.22.99.209";
bgp = "172.22.99.250";
dn42 = "172.22.99.253";
};
ipv6Router = "c3d2-gw3";
hosts6.dn42 = {
bgp = "fd23:42:c3d2:523::c3d2:ff0b";
c3d2-anon = "fd23:42:c3d2:523::c3d2:1";
c3d2-gw1 = "fd23:42:c3d2:523::c3d2:2";
c3d2-gw2 = "fd23:42:c3d2:523::c3d2:3";
c3d2-gw3 = "fd23:42:c3d2:523::c3d2:4";
};
hosts6.up4 = {
bgp = "2a00:8180:2c00:223::c3d2:ff0b";
c3d2-anon = "2a00:8180:2c00:223::c3d2:1";
c3d2-gw1 = "2a00:8180:2c00:223::c3d2:2";
c3d2-gw2 = "2a00:8180:2c00:223::c3d2:3";
c3d2-gw3 = "2a00:8180:2c00:223::c3d2:4";
pipebert = "2a00:8180:2c00:223:eea8:6bff:fefe:b4cb";
};
subnets6 = {
dn42 = "fd23:42:c3d2:523::/64";
up4 = "2a00:8180:2c00:223::/64";
};
};
site.hosts =
let
makeGateway = lib.recursiveUpdate {
interfaces = {
c3d2.type = "veth";
core.type = "veth";
};
role = "container";
};
in
{
c3d2-anon = makeGateway {
interfaces = {
c3d2.hwaddr = "0A:14:48:01:07:05";
core.hwaddr = "0A:14:48:01:07:04";
};
ospf.allowedUpstreams = [ "anon1" "freifunk" ];
};
c3d2-gw1 = makeGateway {
interfaces = {
c3d2.hwaddr = "0A:14:48:01:21:01";
core.hwaddr = "0A:14:48:01:21:00";
};
ospf.allowedUpstreams = [ "flpk-gw" "freifunk" "upstream4" "upstream3" "anon1" ];
};
c3d2-gw2 = makeGateway {
interfaces = {
c3d2.hwaddr = "0A:14:48:01:21:03";
core.hwaddr = "0A:14:48:01:21:02";
};
ospf.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ];
};
c3d2-gw3 = makeGateway {
interfaces = {
c3d2.hwaddr = "0A:14:48:01:21:05";
core.hwaddr = "0A:14:48:01:21:04";
};
ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ];
};
};
}

View File

@ -1,47 +0,0 @@
{
site.net.c3d2iot = {
dhcp = {
start = "10.22.0.2";
end = "10.22.255.253";
router = "iot-gw";
server = "iot-gw";
# devices don't often change and a missing DNS record causes trouble
time = 3600;
max-time = 24 * 3600;
};
dynamicDomain = true;
domainName = "c3d2iot.zentralwerk.org";
hosts4 = {
iot-gw = "10.22.0.1";
};
hosts6 = {
dn42 = {
iot-gw = "fd23:42:c3d2:587:ffff:ffff:ffff:ffff";
};
};
subnet4 = "10.22.0.0/16";
subnets6 = {
dn42 = "fd23:42:c3d2:587::/64";
up4 = "2a00:8180:2c00:287::/64";
};
};
site.hosts.iot-gw = {
# TODO: needs to be done more granular, aka allow c3d2 and serv network
# firewall.enable = true;
interfaces = {
core = {
hwaddr = "0A:22:48:01:24:01";
type = "veth";
};
c3d2iot = {
hwaddr = "0A:22:48:01:24:00";
type = "veth";
};
};
ospf = {
allowedUpstreams = [ "upstream4" "upstream3" "anon1" ];
};
role = "container";
};
}

View File

@ -1,179 +0,0 @@
{ lib, ... }:
let
cephMonServers = [ "server5" "server6" "server8" ];
in
{
site.net.cluster = {
ipv6Router = "cls-gw";
domainName = "cluster.zentralwerk.org";
extraRecords = map (host: {
data = {
service = "ceph-mon";
proto = "tcp";
priority = 1;
weight = 1;
port = 6789;
target = host;
};
name = "@";
type = "SRV";
}) cephMonServers
++
lib.lists.imap0 (i: host: {
name = "mon${toString i}";
type = "CNAME";
data = "${host}";
}) cephMonServers;
hosts4 = {
cls-gw = "172.20.77.1";
server1 = "172.20.77.30";
server2 = "172.20.77.2";
server3 = "172.20.77.3";
server4 = "172.20.77.4";
server5 = "172.20.77.5";
server6 = "172.20.77.6";
server7 = "172.20.77.7";
server8 = "172.20.77.8";
server9 = "172.20.77.9";
server10 = "172.20.77.10";
server11 = "172.20.77.11";
server12 = "172.20.77.12";
server13 = "172.20.77.13";
server14 = "172.20.77.14";
server15 = "172.20.77.15";
server16 = "172.20.77.16";
server17 = "172.20.77.17";
server18 = "172.20.77.18";
server19 = "172.20.77.19";
server20 = "172.20.77.20";
server21 = "172.20.77.21";
server22 = "172.20.77.22";
server23 = "172.20.77.23";
server24 = "172.20.77.24";
server25 = "172.20.77.25";
server26 = "172.20.77.26";
server27 = "172.20.77.27";
server28 = "172.20.77.28";
server29 = "172.20.77.29";
};
hosts6 = {
dn42 = {
cls-gw = "fd23:42:c3d2:586::1";
server1 = "fd23:42:c3d2:586::11";
server2 = "fd23:42:c3d2:586::12";
server3 = "fd23:42:c3d2:586::13";
server4 = "fd23:42:c3d2:586::14";
server5 = "fd23:42:c3d2:586::15";
server6 = "fd23:42:c3d2:586::16";
server7 = "fd23:42:c3d2:586::17";
server8 = "fd23:42:c3d2:586::18";
server9 = "fd23:42:c3d2:586::19";
server10 = "fd23:42:c3d2:586::1a";
server11 = "fd23:42:c3d2:586::1b";
server12 = "fd23:42:c3d2:586::1c";
server13 = "fd23:42:c3d2:586::1d";
server14 = "fd23:42:c3d2:586::1e";
server15 = "fd23:42:c3d2:586::1f";
server16 = "fd23:42:c3d2:586::20";
server17 = "fd23:42:c3d2:586::21";
server18 = "fd23:42:c3d2:586::22";
server19 = "fd23:42:c3d2:586::23";
server20 = "fd23:42:c3d2:586::24";
server21 = "fd23:42:c3d2:586::25";
server22 = "fd23:42:c3d2:586::26";
server23 = "fd23:42:c3d2:586::27";
server24 = "fd23:42:c3d2:586::28";
server25 = "fd23:42:c3d2:586::29";
server26 = "fd23:42:c3d2:586::2a";
server27 = "fd23:42:c3d2:586::2b";
server28 = "fd23:42:c3d2:586::2c";
server29 = "fd23:42:c3d2:586::2d";
};
up4 = {
cls-gw = "2a00:8180:2c00:284::1";
server1 = "2a00:8180:2c00:284::11";
server2 = "2a00:8180:2c00:284::12";
server3 = "2a00:8180:2c00:284::13";
server4 = "2a00:8180:2c00:284::14";
server5 = "2a00:8180:2c00:284::15";
server6 = "2a00:8180:2c00:284::16";
server7 = "2a00:8180:2c00:284::17";
server8 = "2a00:8180:2c00:284::18";
server9 = "2a00:8180:2c00:284::19";
server10 = "2a00:8180:2c00:284::1a";
server11 = "2a00:8180:2c00:284::1b";
server12 = "2a00:8180:2c00:284::1c";
server13 = "2a00:8180:2c00:284::1d";
server14 = "2a00:8180:2c00:284::1e";
server15 = "2a00:8180:2c00:284::1f";
server16 = "2a00:8180:2c00:284::20";
server17 = "2a00:8180:2c00:284::21";
server18 = "2a00:8180:2c00:284::22";
server19 = "2a00:8180:2c00:284::23";
server20 = "2a00:8180:2c00:284::24";
server21 = "2a00:8180:2c00:284::25";
server22 = "2a00:8180:2c00:284::26";
server23 = "2a00:8180:2c00:284::27";
server24 = "2a00:8180:2c00:284::28";
server25 = "2a00:8180:2c00:284::29";
server26 = "2a00:8180:2c00:284::2a";
server27 = "2a00:8180:2c00:284::2b";
server28 = "2a00:8180:2c00:284::2c";
server29 = "2a00:8180:2c00:284::2d";
};
};
subnet4 = "172.20.77.0/27";
subnets6 = {
dn42 = "fd23:42:c3d2:586::/64";
up4 = "2a00:8180:2c00:284::/64";
};
};
site.hosts =
let
makeServer = {
role = "client";
model = "nixos";
interfaces = builtins.foldl' (interfaces: net:
interfaces // {
"${net}".type = "bridge";
}
) {} [
"cluster"
"core"
"mgmt"
"serv"
"c3d2"
"c3d2iot"
"pub"
"priv23"
"priv31"
"priv45"
"bmx"
"flpk"
];
};
in {
cls-gw = {
role = "container";
interfaces = {
cluster = {
hwaddr = "0A:14:48:01:06:02";
type = "veth";
};
core = {
hwaddr = "0A:14:48:01:06:03";
type = "veth";
};
};
ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ];
};
server3 = makeServer;
server5 = makeServer;
server6 = makeServer;
server7 = makeServer;
server8 = makeServer;
server9 = makeServer;
server10 = makeServer;
};
}

View File

@ -1,38 +0,0 @@
{
site.net.coloradio = {
domainName = "coloradio.zentralwerk.org";
subnet4 = "192.168.9.0/24";
hosts4 = {
coloradio-gw = "192.168.9.1";
coloradio-in = "192.168.9.2";
};
ipv6Router = "coloradio-gw";
subnets6.dn42 = "fd23:42:c3d2:590::/64";
hosts6.dn42 = {
coloradio-gw = "fd23:42:c3d2:590::1";
};
};
site.hosts = {
coloradio-gw = {
role = "container";
interfaces = {
core = {
type = "veth";
hwaddr = "0A:14:48:01:06:08";
gw4 = null;
gw6 = null;
};
coloradio = {
type = "veth";
hwaddr = "0A:14:48:01:06:09";
gw4 = null;
gw6 = null;
};
};
ospf.allowedUpstreams =
[ "upstream4" "upstream3" "freifunk" ];
};
};
}

View File

@ -1,249 +0,0 @@
{
site.net.core = {
domainName = "core.zentralwerk.org";
hosts4 = {
anon1 = "172.20.72.7";
bgp = "172.20.72.27";
c3d2-anon = "172.20.72.9";
c3d2-gw1 = "172.20.72.8";
c3d2-gw2 = "172.20.72.63";
c3d2-gw3 = "172.20.72.64";
cls-gw = "172.20.72.29";
freifunk = "172.20.72.40";
mgmt-gw = "172.20.72.28";
priv1-gw = "172.20.72.4";
priv10-gw = "172.20.72.20";
priv11-gw = "172.20.72.21";
priv12-gw = "172.20.72.22";
priv13-gw = "172.20.72.23";
priv14-gw = "172.20.72.24";
priv15-gw = "172.20.72.25";
priv16-gw = "172.20.72.26";
priv17-gw = "172.20.72.30";
priv17-gw-up3 = "172.20.72.66";
priv18-gw = "172.20.72.31";
priv19-gw = "172.20.72.32";
priv2-gw = "172.20.72.5";
priv20-gw = "172.20.72.33";
priv21-gw = "172.20.72.34";
priv22-gw = "172.20.72.35";
priv23-gw = "172.20.72.36";
priv24-gw = "172.20.72.37";
priv25-gw = "172.20.72.38";
priv26-gw = "172.20.72.39";
priv27-gw = "172.20.72.41";
priv28-gw = "172.20.72.42";
priv29-gw = "172.20.72.43";
priv3-gw = "172.20.72.13";
priv30-gw = "172.20.72.44";
priv31-gw = "172.20.72.45";
priv32-gw = "172.20.72.46";
priv33-gw = "172.20.72.47";
priv34-gw = "172.20.72.48";
priv35-gw = "172.20.72.49";
priv36-gw = "172.20.72.50";
priv37-gw = "172.20.72.51";
priv38-gw = "172.20.72.52";
priv39-gw = "172.20.72.60";
priv4-gw = "172.20.72.14";
priv40-gw = "172.20.72.61";
priv41-gw = "172.20.72.65";
priv42-gw = "172.20.72.67";
priv43-gw = "172.20.72.68";
priv44-gw = "172.20.72.70";
priv45-gw = "172.20.72.72";
priv46-gw = "172.20.72.73";
priv47-gw = "172.20.72.74";
priv48-gw = "172.20.72.75";
priv49-gw = "172.20.72.76";
priv5-gw = "172.20.72.15";
priv6-gw = "172.20.72.16";
priv7-gw = "172.20.72.17";
priv8-gw = "172.20.72.18";
priv9-gw = "172.20.72.19";
pub-gw = "172.20.72.3";
serv-gw = "172.20.72.2";
server3 = "172.20.72.53";
server4 = "172.20.72.54";
server5 = "172.20.72.55";
server6 = "172.20.72.56";
server7 = "172.20.72.57";
server8 = "172.20.72.58";
upstream3 = "172.20.72.11";
upstream4 = "172.20.72.12";
coloradio-gw = "172.20.72.62";
vpn-gw = "172.20.72.69";
flpk-gw = "172.20.72.71";
iot-gw = "172.20.72.77";
};
hosts6 = {
dn42 = {
anon1 = "fd23:42:c3d2:581::9:1";
bgp = "fd23:42:c3d2:581::c3d2:b";
c3d2-anon = "fd23:42:c3d2:581::c3d2:a";
c3d2-gw1 = "fd23:42:c3d2:581::c3d2:1";
c3d2-gw2 = "fd23:42:c3d2:581::c3d2:2";
c3d2-gw3 = "fd23:42:c3d2:581::c3d2:3";
cls-gw = "fd23:42:c3d2:581::c3d2:4";
freifunk = "fd23:42:c3d2:581:8000::1";
mgmt-gw = "fd23:42:c3d2:581::8:3";
iot-gw = "fd23:42:c3d2:581::8:7";
priv1-gw = "fd23:42:c3d2:581::c:0";
priv10-gw = "fd23:42:c3d2:581::c:9";
priv11-gw = "fd23:42:c3d2:581::c:a";
priv12-gw = "fd23:42:c3d2:581::c:b";
priv13-gw = "fd23:42:c3d2:581::c:c";
priv14-gw = "fd23:42:c3d2:581::c:d";
priv15-gw = "fd23:42:c3d2:581::c:e";
priv16-gw = "fd23:42:c3d2:581::c:f";
priv17-gw = "fd23:42:c3d2:581::c:10";
priv18-gw = "fd23:42:c3d2:581::c:11";
priv19-gw = "fd23:42:c3d2:581::c:12";
priv2-gw = "fd23:42:c3d2:581::c:1";
priv20-gw = "fd23:42:c3d2:581::c:13";
priv21-gw = "fd23:42:c3d2:581::c:14";
priv22-gw = "fd23:42:c3d2:581::c:15";
priv23-gw = "fd23:42:c3d2:581::c:16";
priv24-gw = "fd23:42:c3d2:581::c:17";
priv25-gw = "fd23:42:c3d2:581::c:18";
priv26-gw = "fd23:42:c3d2:581::c:19";
priv27-gw = "fd23:42:c3d2:581::c:1a";
priv28-gw = "fd23:42:c3d2:581::c:1b";
priv29-gw = "fd23:42:c3d2:581::c:1c";
priv3-gw = "fd23:42:c3d2:581::c:2";
priv30-gw = "fd23:42:c3d2:581::c:1d";
priv31-gw = "fd23:42:c3d2:581::c:1e";
priv32-gw = "fd23:42:c3d2:581::c:1f";
priv33-gw = "fd23:42:c3d2:581::c:20";
priv34-gw = "fd23:42:c3d2:581::c:21";
priv35-gw = "fd23:42:c3d2:581::c:22";
priv36-gw = "fd23:42:c3d2:581::c:23";
priv37-gw = "fd23:42:c3d2:581::c:24";
priv38-gw = "fd23:42:c3d2:581::c:25";
priv39-gw = "fd23:42:c3d2:581::c:26";
priv4-gw = "fd23:42:c3d2:581::c:3";
priv40-gw = "fd23:42:c3d2:581::c:27";
priv41-gw = "fd23:42:c3d2:581::c:28";
priv42-gw = "fd23:42:c3d2:581::c:29";
priv43-gw = "fd23:42:c3d2:581::c:2a";
priv44-gw = "fd23:42:c3d2:581::c:2b";
priv45-gw = "fd23:42:c3d2:581::c:2c";
priv46-gw = "fd23:42:c3d2:581::c:2d";
priv47-gw = "fd23:42:c3d2:581::c:2e";
priv48-gw = "fd23:42:c3d2:581::c:2f";
priv49-gw = "fd23:42:c3d2:581::c:30";
priv5-gw = "fd23:42:c3d2:581::c:4";
priv6-gw = "fd23:42:c3d2:581::c:5";
priv7-gw = "fd23:42:c3d2:581::c:6";
priv8-gw = "fd23:42:c3d2:581::c:7";
priv9-gw = "fd23:42:c3d2:581::c:8";
pub-gw = "fd23:42:c3d2:581::8:2";
serv-gw = "fd23:42:c3d2:581::8:1";
upstream3 = "fd23:42:c3d2:581::b:2";
upstream4 = "fd23:42:c3d2:581::b:3";
vpn-gw = "fd23:42:c3d2:581:9001::1";
coloradio-gw = "fd23:42:c3d2:581:9009::1";
};
up4 = {
anon1 = "2a00:8180:2c00:281::9:1";
bgp = "2a00:8180:2c00:281::c3d2:b";
c3d2-anon = "2a00:8180:2c00:281::c3d2:a";
c3d2-gw1 = "2a00:8180:2c00:281::c3d2:1";
c3d2-gw2 = "2a00:8180:2c00:281::c3d2:2";
c3d2-gw3 = "2a00:8180:2c00:281::c3d2:3";
cls-gw = "2a00:8180:2c00:281::8:4";
freifunk = "2a00:8180:2c00:281:8000::1";
mgmt-gw = "2a00:8180:2c00:281::8:3";
iot-gw = "2a00:8180:2c00:281::8:7";
priv1-gw = "2a00:8180:2c00:281::c:0";
priv10-gw = "2a00:8180:2c00:281::c:9";
priv11-gw = "2a00:8180:2c00:281::c:a";
priv12-gw = "2a00:8180:2c00:281::c:b";
priv13-gw = "2a00:8180:2c00:281::c:c";
priv14-gw = "2a00:8180:2c00:281::c:d";
priv15-gw = "2a00:8180:2c00:281::c:e";
priv16-gw = "2a00:8180:2c00:281::c:f";
priv17-gw = "2a00:8180:2c00:281::c:10";
priv18-gw = "2a00:8180:2c00:281::c:11";
priv19-gw = "2a00:8180:2c00:281::c:12";
priv2-gw = "2a00:8180:2c00:281::c:1";
priv20-gw = "2a00:8180:2c00:281::c:13";
priv21-gw = "2a00:8180:2c00:281::c:14";
priv22-gw = "2a00:8180:2c00:281::c:15";
priv23-gw = "2a00:8180:2c00:281::c:16";
priv24-gw = "2a00:8180:2c00:281::c:17";
priv25-gw = "2a00:8180:2c00:281::c:18";
priv26-gw = "2a00:8180:2c00:281::c:19";
priv27-gw = "2a00:8180:2c00:281::c:1a";
priv28-gw = "2a00:8180:2c00:281::c:1b";
priv29-gw = "2a00:8180:2c00:281::c:1c";
priv3-gw = "2a00:8180:2c00:281::c:2";
priv30-gw = "2a00:8180:2c00:281::c:1d";
priv31-gw = "2a00:8180:2c00:281::c:1e";
priv32-gw = "2a00:8180:2c00:281::c:1f";
priv33-gw = "2a00:8180:2c00:281::c:20";
priv34-gw = "2a00:8180:2c00:281::c:21";
priv35-gw = "2a00:8180:2c00:281::c:22";
priv36-gw = "2a00:8180:2c00:281::c:23";
priv37-gw = "2a00:8180:2c00:281::c:24";
priv38-gw = "2a00:8180:2c00:281::c:25";
priv39-gw = "2a00:8180:2c00:281::c:26";
priv4-gw = "2a00:8180:2c00:281::c:3";
priv40-gw = "2a00:8180:2c00:281::c:27";
priv41-gw = "2a00:8180:2c00:281::c:28";
priv42-gw = "2a00:8180:2c00:281::c:29";
priv43-gw = "2a00:8180:2c00:281::c:2a";
priv44-gw = "2a00:8180:2c00:281::c:2b";
priv45-gw = "2a00:8180:2c00:281::c:2c";
priv46-gw = "2a00:8180:2c00:281::c:2d";
priv47-gw = "2a00:8180:2c00:281::c:2e";
priv48-gw = "2a00:8180:2c00:281::c:2f";
priv49-gw = "2a00:8180:2c00:281::c:30";
priv5-gw = "2a00:8180:2c00:281::c:4";
priv6-gw = "2a00:8180:2c00:281::c:5";
priv7-gw = "2a00:8180:2c00:281::c:6";
priv8-gw = "2a00:8180:2c00:281::c:7";
priv9-gw = "2a00:8180:2c00:281::c:8";
serv-gw = "2a00:8180:2c00:281::8:1";
upstream4 = "2a00:8180:2c00:281::b:1";
vpn-gw = "2a00:8180:2c00:281:9001::1";
coloradio-gw = "2a00:8180:2c00:281:9009::1";
};
};
subnet4 = "172.20.72.0/25";
subnets6 = {
dn42 = "fd23:42:c3d2:581::/64";
up4 = "2a00:8180:2c00:281::/64";
};
};
site.hosts = {
bgp = {
bgp = {
asn = 4242421127;
peers = {
"172.22.99.253" = { asn = 64699; };
"fe80::a800:42ff:fe7a:3246%c3d2" = { asn = 64699; };
};
};
interfaces = {
c3d2 = {
hwaddr = "0A:14:48:01:22:01";
type = "veth";
};
core = {
hwaddr = "0A:14:48:01:22:00";
type = "veth";
};
};
ospf = {
allowedUpstreams =
[ "upstream4" "upstream3" "anon1" "freifunk" ];
stubNets4 = [ "172.20.0.0/14" "10.0.0.0/8" ];
stubNets6 =
[ "fd00::/8" "2a00:8180:2c00:200::/56" ];
};
role = "container";
};
};
}

View File

@ -1,65 +0,0 @@
{
site.net.flpk = {
domainName = "flpk.zentralwerk.org";
ipv6Router = "flpk-gw";
subnet4 = "45.158.40.160/27";
# we get a /56
subnets6.flpk = "2a0f:5382:acab:1400::/64";
hosts4 = {
flpk-gw = "45.158.40.160";
notice-me-senpai = "45.158.40.162"; # tlms monitoring
sshlog = "45.158.40.163";
caveman = "45.158.40.164";
# tlms-37c3-ctf vm on server9
ctf = "45.158.40.165";
mastodon = "45.158.40.166";
c3d2-web = "45.158.40.167";
mail = "45.158.40.168";
dresden-zone-dns = "45.158.40.169";
# server7 = "45.158.40.170"; # unused
rtrlab = "45.158.40.171"; # temporary
};
hosts6.flpk = {
flpk-gw = "2a0f:5382:acab:1400::c3d2";
notice-me-senpai = "2a0f:5382:acab:1400:2de:5bff:fef9:e23e"; # tlms-monitoring
sshlog = "2a0f:5382:acab:1400::22";
caveman = "2a0f:5382:acab:1400::a4";
# tlms-37c3-ctf vm on server9
ctf = "2a0f:5382:acab:1400::a5";
mastodon = "2a0f:5382:acab:1400::a6";
c3d2-web = "2a0f:5382:acab:1400::a7";
# mail = "2a0f:5382:acab:1400::a8"; # we don't have an PTR for IPv6 and it gets way more often marked as spam
dresden-zone-dns = "2a0f:5382:acab:1400::a9";
# server7 = "2a0f:5382:acab:1400::aa";
rtrlab = "2a0f:5382:acab:1400::ab";
};
};
site.hosts.flpk-gw = {
interfaces = {
core = {
hwaddr = "0A:14:48:b7:e4:91";
type = "veth";
};
flpk = {
hwaddr = "0A:14:48:01:16:01";
type = "veth";
};
up-flpk = {
type = "wireguard";
upstream = {
provider = "flpk";
noNat = {
subnets4 = [ "45.158.40.160/27" ];
subnets6 = [ "2a0f:5382:acab:1400::/56" ];
};
};
};
};
ospf = {
allowedUpstreams = [ "upstream4" "upstream3" "freifunk" ];
upstreamInstance = 2;
};
role = "container";
};
}

View File

@ -1,218 +0,0 @@
{
site.net.mgmt = {
domainName = "mgmt.zentralwerk.org";
hosts4 = {
ap1 = "10.0.0.41";
ap10 = "10.0.0.50";
ap11 = "10.0.0.51";
ap12 = "10.0.0.52";
ap13 = "10.0.0.53";
ap14 = "10.0.0.54";
ap15 = "10.0.0.55";
ap16 = "10.0.0.56";
ap17 = "10.0.0.57";
ap18 = "10.0.0.58";
ap19 = "10.0.0.59";
ap2 = "10.0.0.42";
ap20 = "10.0.0.60";
ap21 = "10.0.0.61";
ap22 = "10.0.0.62";
ap23 = "10.0.0.63";
ap24 = "10.0.0.64";
ap25 = "10.0.0.65";
ap26 = "10.0.0.66";
ap27 = "10.0.0.67";
ap28 = "10.0.0.68";
ap29 = "10.0.0.69";
ap3 = "10.0.0.43";
ap30 = "10.0.0.70";
ap31 = "10.0.0.71";
ap32 = "10.0.0.72";
ap33 = "10.0.0.73";
ap34 = "10.0.0.74";
ap35 = "10.0.0.75";
ap36 = "10.0.0.76";
ap37 = "10.0.0.77";
ap38 = "10.0.0.78";
ap39 = "10.0.0.79";
ap4 = "10.0.0.44";
ap40 = "10.0.0.80";
ap41 = "10.0.0.81";
ap42 = "10.0.0.82";
ap43 = "10.0.0.83";
ap44 = "10.0.0.84";
ap45 = "10.0.0.85";
ap46 = "10.0.0.86";
ap47 = "10.0.0.87";
ap48 = "10.0.0.88";
ap49 = "10.0.0.89";
ap5 = "10.0.0.45";
ap50 = "10.0.0.90";
ap51 = "10.0.0.91";
ap52 = "10.0.0.92";
ap53 = "10.0.0.93";
ap54 = "10.0.0.94";
ap55 = "10.0.0.95";
ap56 = "10.0.0.96";
ap57 = "10.0.0.97";
ap58 = "10.0.0.98";
ap59 = "10.0.0.99";
ap6 = "10.0.0.46";
ap60 = "10.0.0.100";
ap61 = "10.0.0.101";
ap62 = "10.0.0.102";
ap63 = "10.0.0.103";
ap64 = "10.0.0.104";
ap65 = "10.0.0.105";
ap66 = "10.0.0.106";
ap67 = "10.0.0.107";
ap68 = "10.0.0.108";
ap69 = "10.0.0.109";
ap7 = "10.0.0.47";
ap70 = "10.0.0.110";
ap71 = "10.0.0.111";
ap72 = "10.0.0.112";
ap8 = "10.0.0.48";
ap9 = "10.0.0.49";
logging = "10.0.0.251";
mgmt-gw = "10.0.0.254";
monit = "10.0.0.250";
server1 = "10.0.0.1";
server1-ipmi = "10.0.0.201";
server2 = "10.0.0.2";
server2-ipmi = "10.0.0.202";
server3 = "10.0.0.3";
server3-ipmi = "10.0.0.203";
server4 = "10.0.0.4";
server4-ipmi = "10.0.0.204";
server5 = "10.0.0.5";
server5-ipmi = "10.0.0.205";
server6 = "10.0.0.6";
server6-ipmi = "10.0.0.206";
server7 = "10.0.0.7";
server7-ipmi = "10.0.0.207";
server8 = "10.0.0.8";
server8-ipmi = "10.0.0.208";
server9 = "10.0.0.9";
server9-ipmi = "10.0.0.209";
switch-a1 = "10.0.0.15";
switch-a1-cpe = "10.0.0.16";
switch-b1 = "10.0.0.10";
switch-b2 = "10.0.0.11";
switch-c1 = "10.0.0.12";
switch-c3d2-main = "10.0.0.14";
switch-d1 = "10.0.0.13";
switch-dach = "10.0.0.17";
switch-b3 = "10.0.0.18";
switch-ds1 = "10.0.0.20";
switch-ds2 = "10.0.0.21";
switch-ds3 = "10.0.0.22";
};
hosts6 = {
dn42 = {
ap1 = "fd23:42:c3d2:580::4:1";
ap10 = "fd23:42:c3d2:580::4:a";
ap11 = "fd23:42:c3d2:580::4:b";
ap12 = "fd23:42:c3d2:580::4:c";
ap13 = "fd23:42:c3d2:580::4:d";
ap14 = "fd23:42:c3d2:580::4:e";
ap15 = "fd23:42:c3d2:580::4:f";
ap16 = "fd23:42:c3d2:580::4:10";
ap17 = "fd23:42:c3d2:580::4:11";
ap18 = "fd23:42:c3d2:580::4:12";
ap19 = "fd23:42:c3d2:580::4:13";
ap2 = "fd23:42:c3d2:580::4:2";
ap20 = "fd23:42:c3d2:580::4:14";
ap21 = "fd23:42:c3d2:580::4:15";
ap22 = "fd23:42:c3d2:580::4:16";
ap23 = "fd23:42:c3d2:580::4:17";
ap24 = "fd23:42:c3d2:580::4:18";
ap25 = "fd23:42:c3d2:580::4:19";
ap26 = "fd23:42:c3d2:580::4:1a";
ap27 = "fd23:42:c3d2:580::4:1b";
ap28 = "fd23:42:c3d2:580::4:1c";
ap29 = "fd23:42:c3d2:580::4:1d";
ap3 = "fd23:42:c3d2:580::4:3";
ap30 = "fd23:42:c3d2:580::4:1e";
ap31 = "fd23:42:c3d2:580::4:1f";
ap32 = "fd23:42:c3d2:580::4:20";
ap33 = "fd23:42:c3d2:580::4:21";
ap34 = "fd23:42:c3d2:580::4:22";
ap35 = "fd23:42:c3d2:580::4:23";
ap36 = "fd23:42:c3d2:580::4:24";
ap37 = "fd23:42:c3d2:580::4:25";
ap38 = "fd23:42:c3d2:580::4:26";
ap39 = "fd23:42:c3d2:580::4:27";
ap4 = "fd23:42:c3d2:580::4:4";
ap40 = "fd23:42:c3d2:580::4:28";
ap41 = "fd23:42:c3d2:580::4:29";
ap42 = "fd23:42:c3d2:580::4:2a";
ap43 = "fd23:42:c3d2:580::4:2b";
ap44 = "fd23:42:c3d2:580::4:2c";
ap45 = "fd23:42:c3d2:580::4:2d";
ap46 = "fd23:42:c3d2:580::4:2e";
ap47 = "fd23:42:c3d2:580::4:2f";
ap48 = "fd23:42:c3d2:580::4:30";
ap49 = "fd23:42:c3d2:580::4:31";
ap5 = "fd23:42:c3d2:580::4:5";
ap50 = "fd23:42:c3d2:580::4:32";
ap51 = "fd23:42:c3d2:580::4:33";
ap52 = "fd23:42:c3d2:580::4:34";
ap53 = "fd23:42:c3d2:580::4:35";
ap54 = "fd23:42:c3d2:580::4:36";
ap55 = "fd23:42:c3d2:580::4:37";
ap56 = "fd23:42:c3d2:580::4:38";
ap57 = "fd23:42:c3d2:580::4:39";
ap58 = "fd23:42:c3d2:580::4:3a";
ap59 = "fd23:42:c3d2:580::4:3b";
ap6 = "fd23:42:c3d2:580::4:6";
ap60 = "fd23:42:c3d2:580::4:3c";
ap61 = "fd23:42:c3d2:580::4:3d";
ap62 = "fd23:42:c3d2:580::4:3e";
ap63 = "fd23:42:c3d2:580::4:3f";
ap64 = "fd23:42:c3d2:580::4:40";
ap65 = "fd23:42:c3d2:580::4:41";
ap66 = "fd23:42:c3d2:580::4:42";
ap67 = "fd23:42:c3d2:580::4:43";
ap68 = "fd23:42:c3d2:580::4:44";
ap69 = "fd23:42:c3d2:580::4:45";
ap7 = "fd23:42:c3d2:580::4:7";
ap70 = "fd23:42:c3d2:580::4:46";
ap71 = "fd23:42:c3d2:580::4:47";
ap72 = "fd23:42:c3d2:580::4:48";
ap8 = "fd23:42:c3d2:580::4:8";
ap9 = "fd23:42:c3d2:580::4:9";
mgmt-gw = "fd23:42:c3d2:580:ffff:ffff:ffff:ffff";
monit = "fd23:42:c3d2:580::250";
server1 = "fd23:42:c3d2:580::1";
server2 = "fd23:42:c3d2:580::2";
switch-b1 = "fd23:42:c3d2:580::10";
switch-b2 = "fd23:42:c3d2:580::11";
switch-c1 = "fd23:42:c3d2:580::12";
switch-c3d2-main = "fd23:42:c3d2:580::14";
switch-d1 = "fd23:42:c3d2:580::13";
};
};
subnet4 = "10.0.0.0/24";
subnets6 = { dn42 = "fd23:42:c3d2:580::/64"; };
};
site.hosts.mgmt-gw = {
firewall.enable = true;
interfaces = {
core = {
hwaddr = "0A:14:48:01:24:01";
type = "veth";
};
mgmt = {
hwaddr = "0A:14:48:01:24:00";
type = "veth";
};
};
ospf = {
allowedUpstreams =
[ "upstream4" "upstream3" "anon1" "freifunk" ];
};
role = "container";
};
}

View File

@ -1,773 +0,0 @@
{ lib, ... }:
let
privCount = 49;
seq = n: max:
if n <= max
then [ n ] ++ seq (n + 1) max
else [];
in
lib.mkMerge (
map (n:
let
toHex = n: with lib; toLower (toHexString n);
subnetUp4 = "2a00:8180:2c00:${toHex (703 + n)}::";
subnetDn42 = "fd23:42:c3d2:${toHex (1471 + n)}::";
in {
site.net."priv${toString n}" = {
dhcp = {
server = "priv${toString n}-gw";
time = 300;
max-time = 60 * 24 * 3600;
router = "priv${toString n}-gw";
};
domainName = "priv${toString n}.zentralwerk.org";
dynamicDomain = true;
hosts6 = {
dn42 = { "priv${toString n}-gw" = "${subnetDn42}1"; };
up4 = { "priv${toString n}-gw" = "${subnetUp4}1"; };
};
subnets6 = {
dn42 = "${subnetDn42}/64";
up4 = "${subnetUp4}/64";
};
};
site.hosts."priv${toString n}-gw" = {
role = "container";
interfaces = {
core.type = "veth";
"priv${toString n}".type = "veth";
};
ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ];
};
}
) (seq 1 privCount)
++
[ {
site.net = {
priv1 = {
subnet4 = "172.20.74.0/28";
hosts4 = { priv1-gw = "172.20.74.1"; };
dhcp = {
end = "172.20.74.14";
start = "172.20.74.2";
};
};
priv2 = {
hosts4 = { priv2-gw = "172.20.75.1"; };
subnet4 = "172.20.75.0/27";
dhcp = {
start = "172.20.75.2";
end = "172.20.75.30";
fixed-hosts = {
"172.20.75.2" = "ac:1f:6b:dc:93:8e";
"172.20.75.3" = "ac:1f:6b:dc:95:de";
"172.20.75.9" = "ac:1f:6b:dc:95:df";
"172.20.75.7" = "60:33:4b:0b:cd:fc";
};
};
};
priv3 = {
hosts4 = { priv3-gw = "172.20.74.129"; };
subnet4 = "172.20.74.128/28";
dhcp = {
start = "172.20.74.130";
end = "172.20.74.142";
};
};
priv4 = {
hosts4 = { priv4-gw = "172.20.75.129"; };
subnet4 = "172.20.75.128/28";
dhcp = {
start = "172.20.75.130";
end = "172.20.75.142";
};
};
priv5 = {
hosts4 = { priv5-gw = "172.20.74.65"; };
subnet4 = "172.20.74.64/28";
dhcp = {
start = "172.20.74.66";
end = "172.20.74.78";
};
};
priv6 = {
hosts4 = { priv6-gw = "172.20.74.193"; };
subnet4 = "172.20.74.192/28";
dhcp = {
start = "172.20.74.194";
end = "172.20.74.206";
};
};
priv7 = {
hosts4 = { priv7-gw = "172.20.75.65"; };
subnet4 = "172.20.75.64/28";
dhcp = {
start = "172.20.75.66";
end = "172.20.75.78";
};
};
priv8 = {
hosts4 = { priv8-gw = "172.20.75.193"; };
subnet4 = "172.20.75.192/28";
dhcp = {
start = "172.20.75.194";
end = "172.20.75.206";
};
};
priv9 = {
hosts4 = { priv9-gw = "172.20.74.33"; };
subnet4 = "172.20.74.32/28";
dhcp = {
start = "172.20.74.34";
end = "172.20.74.46";
};
};
priv10 = {
hosts4 = { priv10-gw = "172.20.74.97"; };
subnet4 = "172.20.74.96/28";
dhcp = {
start = "172.20.74.98";
end = "172.20.74.110";
};
};
priv11 = {
hosts4 = { priv11-gw = "172.20.74.161"; };
subnet4 = "172.20.74.160/28";
dhcp = {
start = "172.20.74.162";
end = "172.20.74.174";
};
};
priv12 = {
hosts4 = { priv12-gw = "172.20.74.225"; };
subnet4 = "172.20.74.224/28";
dhcp = {
start = "172.20.74.226";
end = "172.20.74.238";
};
};
priv13 = {
hosts4 = { priv13-gw = "172.20.75.33"; };
subnet4 = "172.20.75.32/28";
dhcp = {
start = "172.20.75.34";
end = "172.20.75.46";
};
};
priv14 = {
hosts4 = { priv14-gw = "172.20.75.97"; };
subnet4 = "172.20.75.96/28";
dhcp = {
start = "172.20.75.98";
end = "172.20.75.110";
};
};
priv15 = {
hosts4 = { priv15-gw = "172.20.75.161"; };
subnet4 = "172.20.75.160/28";
dhcp = {
start = "172.20.75.162";
end = "172.20.75.174";
};
};
priv16 = {
hosts4 = { priv16-gw = "172.20.75.225"; };
subnet4 = "172.20.75.224/28";
dhcp = {
start = "172.20.75.226";
end = "172.20.75.238";
};
};
priv17 = {
hosts4 = {
priv17-gw = "172.20.73.129";
priv17-gw-up3 = "172.20.73.130";
};
subnet4 = "172.20.73.128/27";
dhcp = {
start = "172.20.73.131";
end = "172.20.73.158";
};
};
priv18 = {
hosts4 = { priv18-gw = "172.20.74.49"; };
subnet4 = "172.20.74.48/28";
dhcp = {
start = "172.20.74.50";
end = "172.20.74.62";
};
};
priv19 = {
hosts4 = { priv19-gw = "172.20.73.193"; };
subnet4 = "172.20.73.192/26";
dhcp = {
start = "172.20.73.194";
end = "172.20.73.254";
};
};
priv20 = {
hosts4 = { priv20-gw = "172.20.74.113"; };
subnet4 = "172.20.74.112/28";
dhcp = {
start = "172.20.74.114";
end = "172.20.74.126";
};
};
priv21 = {
hosts4 = { priv21-gw = "172.20.74.145"; };
subnet4 = "172.20.74.144/28";
dhcp = {
start = "172.20.74.146";
end = "172.20.74.158";
};
};
priv22 = {
hosts4 = { priv22-gw = "172.20.74.177"; };
subnet4 = "172.20.74.176/28";
dhcp = {
start = "172.20.74.178";
end = "172.20.74.190";
};
};
priv23 = {
hosts4 = { priv23-gw = "172.20.73.161"; };
subnet4 = "172.20.73.160/27";
dhcp = {
start = "172.20.73.165";
end = "172.20.73.190";
fixed-hosts = {
"172.20.73.162" = "da:2c:3a:2c:87:22";
"172.20.73.163" = "b8:27:eb:16:31:61";
"172.20.73.164" = "ca:71:c4:90:3e:c7";
};
time = lib.mkForce 900;
};
};
priv24 = {
hosts4 = { priv24-gw = "172.20.74.241"; };
subnet4 = "172.20.74.240/28";
dhcp = {
start = "172.20.74.242";
end = "172.20.74.254";
};
};
priv25 = {
hosts4 = { priv25-gw = "172.20.74.81"; };
subnet4 = "172.20.74.80/28";
dhcp = {
start = "172.20.74.82";
end = "172.20.74.94";
};
};
priv26 = {
hosts4 = { priv26-gw = "172.20.75.49"; };
subnet4 = "172.20.75.48/28";
dhcp = {
start = "172.20.75.50";
end = "172.20.75.62";
};
};
priv27 = {
hosts4 = { priv27-gw = "172.20.75.81"; };
subnet4 = "172.20.75.80/28";
dhcp = {
start = "172.20.75.82";
end = "172.20.75.94";
};
};
priv28 = {
hosts4 = { priv28-gw = "172.20.75.113"; };
subnet4 = "172.20.75.112/28";
dhcp = {
start = "172.20.75.114";
end = "172.20.75.126";
};
};
priv29 = {
hosts4 = { priv29-gw = "172.20.75.145"; };
subnet4 = "172.20.75.144/28";
dhcp = {
start = "172.20.75.146";
end = "172.20.75.158";
};
};
priv30 = {
hosts4 = { priv30-gw = "172.20.75.177"; };
subnet4 = "172.20.75.176/28";
dhcp = {
start = "172.20.75.178";
end = "172.20.75.190";
};
};
priv31 = {
hosts4 = { priv31-gw = "172.20.75.209"; };
subnet4 = "172.20.75.208/28";
dhcp = {
start = "172.20.75.210";
end = "172.20.75.221";
fixed-hosts = {
# zw-ev-vm
"172.20.75.222" = "92:a8:00:49:a6:61";
};
};
};
priv32 = {
hosts4 = { priv32-gw = "172.20.75.241"; };
subnet4 = "172.20.75.240/28";
dhcp = {
start = "172.20.75.242";
end = "172.20.75.254";
};
};
priv33 = {
hosts4 = { priv33-gw = "172.20.74.17"; };
subnet4 = "172.20.74.16/28";
dhcp = {
start = "172.20.74.18";
end = "172.20.74.30";
};
};
priv34 = {
hosts4 = { priv34-gw = "172.20.74.209"; };
subnet4 = "172.20.74.208/28";
dhcp = {
start = "172.20.74.210";
end = "172.20.74.222";
};
};
priv35 = {
hosts4 = { priv35-gw = "172.20.76.1"; };
subnet4 = "172.20.76.0/28";
dhcp = {
start = "172.20.76.2";
end = "172.20.76.14";
};
};
priv36 = {
hosts4 = { priv36-gw = "172.20.76.65"; };
subnet4 = "172.20.76.64/28";
dhcp = {
start = "172.20.76.66";
end = "172.20.76.78";
};
};
priv37 = {
hosts4 = { priv37-gw = "172.20.76.129"; };
subnet4 = "172.20.76.128/28";
dhcp = {
start = "172.20.76.130";
end = "172.20.76.142";
};
};
priv38 = {
hosts4 = { priv38-gw = "172.20.76.193"; };
subnet4 = "172.20.76.192/28";
dhcp = {
start = "172.20.76.194";
end = "172.20.76.206";
};
};
priv39 = {
hosts4 = { priv39-gw = "172.20.77.129"; };
subnet4 = "172.20.77.128/28";
dhcp = {
start = "172.20.77.130";
end = "172.20.77.142";
};
};
priv40 = {
hosts4 = { priv40-gw = "172.20.77.65"; };
subnet4 = "172.20.77.64/28";
dhcp = {
start = "172.20.77.66";
end = "172.20.77.78";
};
};
priv41 = {
hosts4 = { priv41-gw = "172.20.77.193"; };
subnet4 = "172.20.77.192/28";
dhcp = {
start = "172.20.77.194";
end = "172.20.77.206";
};
};
priv42 = {
hosts4 = { priv42-gw = "172.20.76.33"; };
subnet4 = "172.20.76.32/28";
dhcp = {
start = "172.20.76.34";
end = "172.20.76.46";
};
};
priv43 = {
hosts4 = { priv43-gw = "172.20.76.97"; };
subnet4 = "172.20.76.96/28";
dhcp = {
start = "172.20.76.98";
end = "172.20.76.110";
};
};
priv44 = {
hosts4 = { priv44-gw = "172.20.77.97"; };
subnet4 = "172.20.77.96/28";
dhcp = {
start = "172.20.77.98";
end = "172.20.77.110";
};
};
priv45 = {
hosts4 = { priv45-gw = "172.20.77.161"; };
subnet4 = "172.20.77.160/28";
dhcp = {
start = "172.20.77.162";
end = "172.20.77.174";
};
};
priv46 = {
hosts4 = { priv46-gw = "172.20.77.225"; };
subnet4 = "172.20.77.224/28";
dhcp = {
start = "172.20.77.226";
end = "172.20.77.238";
};
};
priv47 = {
hosts4 = { priv47-gw = "172.20.76.161"; };
subnet4 = "172.20.76.160/28";
dhcp = {
start = "172.20.76.162";
end = "172.20.76.174";
};
};
priv48 = {
hosts4 = { priv48-gw = "172.20.77.33"; };
subnet4 = "172.20.77.32/28";
dhcp = {
start = "172.20.77.34";
end = "172.20.77.46";
};
};
priv49 = {
hosts4 = { priv49-gw = "172.20.76.49"; };
subnet4 = "172.20.76.48/28";
dhcp = {
start = "172.20.76.50";
end = "172.20.76.62";
};
};
};
site.hosts = {
priv1-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:19:00";
priv1.hwaddr = "0A:14:48:01:19:01";
};
};
priv2-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:18:00";
priv2.hwaddr = "0A:14:48:01:18:01";
};
};
priv3-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:08:00";
priv3.hwaddr = "0A:14:48:01:08:01";
};
};
priv4-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:17:01";
priv4.hwaddr = "0A:14:48:01:17:00";
};
};
priv5-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:12:00";
priv5.hwaddr = "0A:14:48:01:12:01";
};
};
priv6-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:11:00";
priv6.hwaddr = "0A:14:48:01:11:01";
};
};
priv7-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:10:00";
priv7.hwaddr = "0A:14:48:01:10:01";
};
};
priv8-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:09:00";
priv8.hwaddr = "0A:14:48:01:09:01";
};
};
priv9-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:20:00";
priv9.hwaddr = "0A:14:48:01:20:01";
};
};
priv10-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:13:02";
priv10.hwaddr = "0A:14:48:01:13:03";
};
};
priv11-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:29:00";
priv11.hwaddr = "0A:14:48:01:29:01";
};
};
priv12-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:00";
priv12.hwaddr = "0A:14:48:01:2A:01";
};
};
priv13-gw = {
firewall.enable = true;
interfaces = {
core.hwaddr = "0A:14:48:01:2A:10";
priv13.hwaddr = "0A:14:48:01:2A:11";
};
};
priv14-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:12";
priv14.hwaddr = "0A:14:48:01:2A:13";
};
};
priv15-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:14";
priv15.hwaddr = "0A:14:48:01:2A:15";
};
};
priv16-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:16";
priv16.hwaddr = "0A:14:48:01:2A:17";
};
};
priv17-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:18";
priv17.hwaddr = "0A:14:48:01:2A:19";
};
};
priv17-gw-up3 = {
role = "container";
interfaces = {
core = {
type = "veth";
hwaddr = "0A:14:47:02:2A:18";
};
priv17 = {
type = "veth";
hwaddr = "0A:14:47:02:2A:19";
};
};
ospf.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ];
};
priv18-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:1A";
priv18.hwaddr = "0A:14:48:01:2A:1B";
};
};
priv19-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:1C";
priv19.hwaddr = "0A:14:48:01:2A:1D";
};
};
priv20-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:1E";
priv20.hwaddr = "0A:14:48:01:2A:1F";
};
};
priv21-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:20";
priv21.hwaddr = "0A:14:48:01:2A:21";
};
};
priv22-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:24";
priv22.hwaddr = "0A:14:48:01:2A:25";
};
};
priv23-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:22";
priv23.hwaddr = "0A:14:48:01:2A:23";
};
};
priv24-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:26";
priv24.hwaddr = "0A:14:48:01:2A:27";
};
};
priv25-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:28";
priv25.hwaddr = "0A:14:48:01:2A:29";
};
};
priv26-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:2A";
priv26.hwaddr = "0A:14:48:01:2A:2B";
};
};
priv27-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:2C";
priv27.hwaddr = "0A:14:48:01:2A:2D";
};
};
priv28-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:2E";
priv28.hwaddr = "0A:14:48:01:2A:2F";
};
};
priv29-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:30";
priv29.hwaddr = "0A:14:48:01:2A:31";
};
};
priv30-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:32";
priv30.hwaddr = "0A:14:48:01:2A:33";
};
};
priv31-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:34";
priv31.hwaddr = "0A:14:48:01:2A:35";
};
};
priv32-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:36";
priv32.hwaddr = "0A:14:48:01:2A:37";
};
};
priv33-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:38";
priv33.hwaddr = "0A:14:48:01:2A:39";
};
};
priv34-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:40";
priv34.hwaddr = "0A:14:48:01:2A:41";
};
};
priv35-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:42";
priv35.hwaddr = "0A:14:48:01:2A:43";
};
};
priv36-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:44";
priv36.hwaddr = "0A:14:48:01:2A:45";
};
};
priv37-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:46";
priv37.hwaddr = "0A:14:48:01:2A:47";
};
};
priv38-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:48";
priv38.hwaddr = "0A:14:48:01:2A:49";
};
};
priv39-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:4A";
priv39.hwaddr = "0A:14:48:01:2A:4B";
};
};
priv40-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:4C";
priv40.hwaddr = "0A:14:48:01:2A:4D";
};
};
priv41-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:4E";
priv41.hwaddr = "0A:14:48:01:2A:4F";
};
};
priv42-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:50";
priv42.hwaddr = "0A:14:48:01:2A:51";
};
};
priv43-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:52";
priv43.hwaddr = "0A:14:48:01:2A:53";
};
};
priv44-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:54";
priv44.hwaddr = "0A:14:48:01:2A:55";
};
};
priv45-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:56";
priv45.hwaddr = "0A:14:48:01:2A:57";
};
};
priv46-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:58";
priv46.hwaddr = "0A:14:48:01:2A:59";
};
};
priv47-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:5A";
priv47.hwaddr = "0A:14:48:01:2A:5B";
};
};
priv48-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:5C";
priv48.hwaddr = "0A:14:48:01:2A:5D";
};
};
priv49-gw = {
interfaces = {
core.hwaddr = "0A:14:48:01:2A:5E";
priv49.hwaddr = "0A:14:48:01:2A:5F";
};
};
};
} ]
)

View File

@ -1,48 +0,0 @@
{
site.net.pub = {
dhcp = {
start = "172.20.78.2";
end = "172.20.79.253";
router = "pub-gw";
server = "pub-gw";
time = 120;
max-time = 12 * 3600;
};
domainName = "pub.zentralwerk.org";
dynamicDomain = true;
hosts4 = {
pub-gw = "172.20.78.1";
};
hosts6 = {
dn42 = {
pub-gw = "fd23:42:c3d2:583::1";
};
flpk = {
pub-gw = "2a0f:5382:acab:1403::1";
};
};
subnet4 = "172.20.78.0/23";
subnets6 = {
dn42 = "fd23:42:c3d2:583::/64";
flpk = "2a0f:5382:acab:1403::/64";
};
};
site.hosts.pub-gw = {
interfaces = {
core = {
hwaddr = "0A:14:48:01:16:00";
type = "veth";
};
pub = {
hwaddr = "0A:14:48:01:16:01";
type = "veth";
};
};
ospf = {
allowedUpstreams = [ "anon1" "freifunk" ];
allowedUpstreams6 = [ "flpk-gw" "anon1" "freifunk" ];
};
role = "container";
};
}

View File

@ -1,18 +0,0 @@
{
site.net.roof = {
# not switched, only a wifi
vlan = null;
# leave space for vxlan
mtu = 1574;
subnet4 = "10.0.57.0/24";
hosts4 = {
ap57 = "10.0.57.1";
ap58 = "10.0.57.2";
};
hosts6.dn42 = {
ap57 = "fd23:42:c3d2:584::39";
ap58 = "fd23:42:c3d2:584::40";
};
subnets6.dn42 = "fd23:42:c3d2:584::/64";
};
}

View File

@ -1,190 +0,0 @@
{ lib, ... }:
{
site.net.serv = {
domainName = "serv.zentralwerk.org";
subnet4 = "172.20.73.0/25";
hosts4 = {
serv-gw = "172.20.73.1";
dns = "172.20.73.2";
stats = "172.20.73.3";
dresden-zone = "172.20.73.4";
tlms-elastic = "172.20.73.7"; # tlms
dnscache = "172.20.73.8";
tlms-ctfd = "172.20.73.9"; # tlms
buzzrelay = "172.20.73.15";
matemat = "172.20.73.21";
spaceapi = "172.20.73.25";
mucbot = "172.20.73.27";
scrape = "172.20.73.32";
pretalx = "172.20.73.33";
vaultwarden = "172.20.73.34";
uranus = "172.20.73.37"; # tlms
tram-borzoi = "172.20.73.38"; # tlms
borken-data-hoarder = "172.20.73.39"; # tlms
matrix = "172.20.73.40";
activity-relay = "172.20.73.41";
luulaatsch-asterisk = "172.20.73.42";
grafana = "172.20.73.43";
public-access-proxy = "172.20.73.45";
marenz = "172.20.73.46";
network-homepage = "172.20.73.47";
home-assistant = "172.20.73.48";
hydra = "172.20.73.49";
owncast = "172.20.73.50";
nfsroot = "172.20.73.51";
ticker = "172.20.73.52";
gitea = "172.20.73.53";
stream = "172.20.73.54";
jabber = "172.20.73.55";
mobilizon = "172.20.73.56";
radiobert = "172.20.73.57";
# mail = "172.20.73.58";
sdrweb = "172.20.73.60";
knot = "172.20.73.61";
blogs = "172.20.73.62";
staging-data-hoarder = "172.20.73.64"; # tlms
oparl = "172.20.73.65";
hedgedoc = "172.20.73.66";
mediawiki = "172.20.73.67";
gnunet = "172.20.73.68";
data-hoarder = "172.20.73.69"; # tlms
broker = "172.20.73.70";
ftp = "172.20.73.71";
auth = "172.20.73.72";
doubleblind-science = "172.20.73.73";
prometheus = "172.20.73.75";
drone = "172.20.73.77";
# FILL IN THE HOLES BEFORE APPENDING!
};
ipv6Router = "serv-gw";
subnets6.dn42 = "fd23:42:c3d2:582::/64";
subnets6.up4 = "2a00:8180:2c00:282::/64";
hosts6.dn42 = {
knot = "fd23:42:c3d2:582:cd7:56ff:fe69:6366";
blogs = "fd23:42:c3d2:582:b8a8:7dff:fee8:5ac2";
dns = "fd23:42:c3d2:582:2:0:0:2";
dnscache = "fd23:42:c3d2:582:f096:dbff:fee8:427d";
gitea = "fd23:42:c3d2:582:702a:daff:fe35:83be";
grafana = "fd23:42:c3d2:582:4042:fbff:fe4b:2de8";
hydra = "fd23:42:c3d2:582:e2cb:4eff:fe3b:f94b";
jabber = "fd23:42:c3d2:582:b869:ccff:fe46:902a";
# mail = "fd23:42:c3d2:582:88c0:41ff:fe70:d6cd";
matemat = "fd23:42:c3d2:582:f82b:1bff:fedc:8572";
mobilizon = "fd23:42:c3d2:582:48d1:5cff:fea7:1676";
mongo = "fd23:42:c3d2:582:14ec:c8ff:fe0a:fc5c";
mucbot = "fd23:42:c3d2:582:28db:dff:fe6b:e89a";
radiobert = "fd23:42:c3d2:582:e65f:1ff:fe5d:1679";
radius = "fd23:42:c3d2:582:2:0:0:4";
sdrweb = "fd23:42:c3d2:582:3078:bbff:fe76:e9ef";
serv-gw = "fd23:42:c3d2:582::1";
spaceapi = "fd23:42:c3d2:582:1457:adff:fe93:62e9";
stats = "fd23:42:c3d2:582:2:0:0:3";
staging-data-hoarder = "fd23:42:c3d2:582:2de:5bff:fef9:e23d";
oparl = "fd23:42:c3d2:582:2de:9aff:fece:3879";
gnunet = "fd23:42:c3d2:582:44";
broker = "fd23:42:c3d2:582:46";
ftp = "fd23:42:c3d2:582:47";
network-homepage = "fd23:42:c3d2:582:2f";
owncast = "fd23:42:c3d2:582:32";
prometheus = "fd23:42:c3d2:582:4b";
buzzrelay = "fd23:42:c3d2:582:f";
oxigraph = "fd23:42:c3d2:582:4c";
luulaatsch-asterisk = "fd23:42:c3d2:582:2a";
stream = "fd23:42:c3d2:583:dc91:c7ff:fe51:d1c5";
};
hosts6.up4 = {
knot = "2a00:8180:2c00:282:cd7:56ff:fe69:6366";
blogs = "2a00:8180:2c00:282:b8a8:7dff:fee8:5ac2";
dns = "2a00:8180:2c00:282:2:0:0:2";
dnscache = "2a00:8180:2c00:282:f096:dbff:fee8:427d";
gitea = "2a00:8180:2c00:282:702a:daff:fe35:83be";
grafana = "2a00:8180:2c00:282:4042:fbff:fe4b:2de8";
hydra = "2a00:8180:2c00:282:e2cb:4eff:fe3b:f94b";
jabber = "2a00:8180:2c00:282:b869:ccff:fe46:902a";
# mail = "2a00:8180:2c00:282:88c0:41ff:fe70:d6cd";
matemat = "2a00:8180:2c00:282:f82b:1bff:fedc:8572";
mobilizon = "2a00:8180:2c00:282:48d1:5cff:fea7:1676";
mucbot = "2a00:8180:2c00:282:28db:dff:fe6b:e89a";
public-access-proxy = "2a00:8180:2c00:282:1024:5fff:febd:9be7";
radiobert = "2a00:8180:2c00:282:e65f:1ff:fe5d:1679";
radius = "2a00:8180:2c00:282:2:0:0:4";
scrape = "2a00:8180:2c00:282:e073:50ff:fef5:eb6e";
sdrweb = "2a00:8180:2c00:282:3078:bbff:fe76:e9ef";
spaceapi = "2a00:8180:2c00:282:1457:adff:fe93:62e9";
stats = "2a00:8180:2c00:282:2:0:0:3";
stream = "2a00:8180:2c00:282:dc91:c7ff:fe51:d1c5";
ticker = "2a00:8180:2c00:282:b407:40ff:fec1:81f2";
staging-data-hoarder = "2a00:8180:2c00:282:2de:5bff:fef9:e23d";
oparl = "2a00:8180:2c00:282:2de:9aff:fece:3879";
serv-gw = "2a00:8180:2c00:282::1";
luulaatsch-asterisk = "2a00:8180:2c00:282::2a";
drone = "2a00:8180:2c00:282::2b";
pretalx = "2a00:8180:2c00:282::2c";
matrix = "2a00:8180:2c00:282::2d";
activity-relay = "2a00:8180:2c00:282::2e";
network-homepage = "2a00:8180:2c00:282::2f";
vaultwarden = "2a00:8180:2c00:282::31";
owncast = "2a00:8180:2c00:282::32";
mediawiki = "2a00:8180:2c00:282::43";
gnunet = "2a00:8180:2c00:282::44";
data-hoarder = "2a00:8180:2c00:282::45";
broker = "2a00:8180:2c00:282::46";
ftp = "2a00:8180:2c00:282::47";
auth = "2a00:8180:2c00:282::48";
dresden-zone = "2a00:8180:2c00:282::49";
prometheus = "2a00:8180:2c00:282::4b";
oxigraph = "2a00:8180:2c00:282::4c";
hedgedoc = "2a00:8180:2c00:282::6";
buzzrelay = "2a00:8180:2c00:282::f";
};
};
site.hosts =
let
makeContainer = lib.recursiveUpdate {
role = "container";
interfaces = {
serv = {
gw4 = "serv-gw";
gw6 = "serv-gw";
type = "veth";
};
};
};
in {
dns = makeContainer {
interfaces.serv.hwaddr = "0A:14:48:01:23:00";
services.dns.enable = true;
};
dnscache = makeContainer {
services.dnscache.enable = true;
};
serv-gw = makeContainer {
interfaces = {
core = {
type = "veth";
hwaddr = "0A:14:48:01:06:01";
gw4 = null;
gw6 = null;
};
serv = {
hwaddr = "0A:14:48:01:06:00";
gw4 = null;
gw6 = null;
};
};
ospf.allowedUpstreams =
[ "upstream4" "upstream3" "anon1" "freifunk" ];
};
stats = makeContainer {
interfaces.serv.hwaddr = "0A:14:48:01:15:00";
};
hydra = {
role = "client";
model = "nixos";
interfaces.serv.type = "phys";
};
};
}

View File

@ -1,273 +0,0 @@
{ config, ... }:
let
servHosts = config.site.net.serv.hosts4;
inherit (config.site.net.c3d2.hosts4) dn42;
inherit (config.site.net.flpk.hosts4) c3d2-web;
in
{
site.hosts = {
upstream3 = {
interfaces = {
core = {
hwaddr = "0A:14:48:01:28:00";
type = "veth";
};
up3 = {
hwaddr = "00:23:74:D7:42:7D";
type = "veth";
upstream = {
link = null;
noNat = { subnets6 = [ ]; };
provider = "starlink";
staticIpv4Address = null;
upBandwidth = null;
};
};
};
ospf.upstreamInstance = 7;
role = "container";
};
upstream4 = rec {
forwardPorts = [
{ # http
destination = servHosts.public-access-proxy;
proto = "tcp";
sourcePort = 80;
}
{ # https
destination = servHosts.public-access-proxy;
proto = "tcp";
sourcePort = 443;
}
{ # gemini
destination = "${c3d2-web}:1965";
proto = "tcp";
sourcePort = 1965;
}
{
destination = servHosts.knot;
proto = "tcp";
sourcePort = 53;
}
{
destination = servHosts.knot;
proto = "udp";
sourcePort = 53;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2325;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2327;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2337;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2338;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2339;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2340;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2342;
}
{
destination = dn42;
proto = "udp";
sourcePort = 2399;
}
{
destination = dn42;
proto = "udp";
sourcePort = 24699;
}
{
destination = dn42;
proto = "udp";
sourcePort = 64699;
}
# ?
{
destination = "172.22.99.175:22";
proto = "tcp";
sourcePort = 2224;
}
{
destination = servHosts.gitea;
proto = "tcp";
sourcePort = 22;
}
{
destination = servHosts.jabber;
proto = "tcp";
sourcePort = 5222;
}
{
destination = servHosts.jabber;
proto = "tcp";
sourcePort = 5223;
}
{
destination = servHosts.jabber;
proto = "tcp";
sourcePort = 5269;
}
{
destination = servHosts.jabber;
proto = "tcp";
sourcePort = 3478;
}
{
destination = servHosts.jabber;
proto = "tcp";
sourcePort = 3479;
}
{
destination = servHosts.jabber;
proto = "udp";
sourcePort = 3478;
}
{
destination = servHosts.jabber;
proto = "udp";
sourcePort = 3479;
}
# poelzi
{
destination = "172.20.73.162:22";
proto = "tcp";
sourcePort = 2323;
}
# jan
{
destination = "172.20.75.3:51820";
proto = "udp";
sourcePort = 30057;
}
# zw-ev RDP
{
destination = "172.20.75.222:3389";
proto = "tcp";
sourcePort = 45000;
}
{
destination = config.site.net.core.hosts4.vpn-gw;
proto = "udp";
sourcePort = config.site.vpn.wireguard.port;
reflect = true;
}
{
destination = servHosts.gnunet;
proto = "tcp";
sourcePort = 2086;
}
# dresden zone
{
destination = servHosts.dresden-zone;
proto = "udp";
sourcePort = 51844;
}
# data-hoarder
{
destination = servHosts.data-hoarder;
proto = "udp";
sourcePort = 51820;
}
{
destination = "${servHosts.data-hoarder}:22";
proto = "tcp";
sourcePort = 2269;
}
# data-hoarder-staging
{
destination = "${servHosts.staging-data-hoarder}:51820";
proto = "udp";
sourcePort = 51821;
}
{
destination = "${servHosts.ftp}:22";
proto = "tcp";
sourcePort = 1022;
}
# coloRadio
{
proto = "tcp";
sourcePort = 8000;
destination = "192.168.9.127";
}
];
interfaces = {
core = {
hwaddr = "0A:14:48:01:28:01";
type = "veth";
};
up4 = {
hwaddr = "00:23:74:D7:42:7E";
type = "veth";
};
up4-pppoe = {
type = "pppoe";
upstream = {
link = "up4";
noNat = {
subnets6 =
[ "2a00:8180:2000:37::1/128" "2a00:8180:2c00:200::/56" ];
};
provider = "dsi";
staticIpv4Address = "81.201.149.152";
upBandwidth = 98000;
};
};
};
ospf = {
upstreamInstance = 8;
stubNets4 = [
"${interfaces.up4-pppoe.upstream.staticIpv4Address}/32"
];
};
role = "container";
};
freifunk.ospf.upstreamInstance = 6;
anon1 = {
interfaces = {
core = {
hwaddr = "0A:14:48:01:14:00";
type = "veth";
};
njalla = {
type = "wireguard";
upstream = {
provider = "njal.la";
upBandwidth = 45000;
};
};
};
ospf = {
allowedUpstreams = [ "upstream3" "upstream4" "freifunk" ];
upstreamInstance = 5;
};
role = "container";
};
};
}

View File

@ -1,40 +0,0 @@
{ ... }:
{
site.net.vpn = {
vlan = null;
domainName = "core.zentralwerk.org";
ipv6Router = "vpn-gw";
hosts4 = {
vpn-gw = "172.20.76.225";
};
hosts6 = {
dn42 = {
vpn-gw = "fd23:42:c3d2:585::1";
};
up4 = {
vpn-gw = "2a00:8180:2c00:285::1";
};
};
subnet4 = "172.20.76.224/28";
subnets6 = {
dn42 = "fd23:42:c3d2:585::/64";
up4 = "2a00:8180:2c00:285::/64";
};
};
site.hosts.vpn-gw = {
role = "container";
interfaces = {
core = {
hwaddr = "0A:14:42:01:26:01";
type = "veth";
};
vpn = {
type = "wireguard";
};
};
ospf = {
allowedUpstreams = [ "flpk-gw" "anon1" "freifunk" ];
};
};
}

View File

@ -1,84 +0,0 @@
-----BEGIN PGP MESSAGE-----
hQEMA2PKcvDMvlKLAQf9H11MeVsKpyi49/f8YxayUL8mbu8FEuUEW2F4N6MbThXz
/kEweXTs3m9KywyieiF17S2UE/02b5E2533sn41mRa5Jo3vmGPb6WANRkoB7YLOO
T770KStpvdIH8vljhGj3eFrNk64POch9fPrGMt8pz4uYcmKgjYhKNb4iUqFj8ZIi
RsdheQ78MnY37df7IzLQmFGZs1jIA773rHN6aVomOqNBTzfVTQO41zKATzbrKpW4
qqau3kn1fbQTab1FedJMEqk1YPhD1H8Pyhib3ff7JpPeWxkUzuZt+grAUD59aJsr
ZX93USKoHSuJ5tUCY3dfmpiaetH2qjOmjI2ysCYGENLqAW440k/1U/Z58B0/ezTZ
RTyU2GPbf7kpC2VL8EDZSTwLkDMB+mmXlZffF3bB1rdmZCahzjk4X9awKSXLIVTV
ShGx2z9sI1pjmtz5CzXA7Ry0wmfJJ8gFM4DUaPCs2KghHVurGY4lmn1VWw80VtXn
+u2dqhYTtdgxArwGV4EsKYBqRRAjsKn9NWXRo+WXIhH9EPzHioJGwKgbDw/n69Qa
zMBNfePDj1HapEILaTq7jL/K67+07hDMVOnkUJL/lE2yzwCKkb0PnWAGgzXyOCQG
p4ih9+samGMM7rIjxM1eI892MuAlrf+awkHY6P4cwU9vKxKHbqmqeRbOe8wUBuqg
kmF9MCOPsCrS/5Dtr0JoR9WTnByLvekPbChW3ANMzAuHtKrjE7H583Z+ql52iYcj
cLqlDP/1XzXu7dwfm3VcY0VD73Qy9gAHaOXcMHk6nOFzPiiAjVuqrw+pC7qS3SxJ
pnzbyRz4ourfMymsfGj0aMlZknCcWidR366gnVLVDw19hWQ+s76cBJ1sKh/dNpUo
d9h5qC7mlZ1RTHZFS8RKF1WODcq9h9W+CkQUg0y2R6EztEdVnc263fgwJH1VFAZW
vsYhFehx1TJ/X/miMvScUDedrA1xcD+gs2zhXi5lYpk2U3HpfvGpEkLJaTzAXQiX
7MxTBjnWviHv0s4ozeLICQDp81QqPNgCskaWf2I+9tYjU0Ob2I2ZqQ4LCx4UAraV
iHmrszgP3OvkqRnidX1sYxwu7NUxZQK+Nmpb+N0+PyDErZdHNfpgl0erh0YswPOf
Cc6WLsp9B2YkLNNGLxJnpYJEccE3I23JJ5/2A/4ZbfUZnDA6jKTx4qXj4ScygY0S
kBiCwIaX2xEPOpQ61fE0xzV2xXnfGBT7IgcTwjL1ilOIWrWRbCad1wIlaL1c5lfv
chCdv3ItWL9rwQvv3aiFm1oq3PL0Tp5qdth5ylmcyFwLRNVJKmZC79tD9/XYx1LG
NTAWtMqR2dsz44nTPvn3x+HI08Ll3UbGK64hKlfjUjjYUUMCDwXXSl1HiunzZVa9
5aVMD/Eg5TzzuS1aL1y01cfBhjZH5guFRxl6KUuNzIjAWh4rScCplTWfKtSQVhTO
ZwAAuY3TZ5R4M/PFNLXCByUMI+nivKazVhVZSVMPTU1ENYrSFVKURsvuLwju8Pan
K026oBtPdnaetOkERZJzayiPA2KCqN/LkPNMsmfniAZedj+7SUmyYigwMVRWFuB+
HLPzZSDb4WJD53OiJhYdFlblEtTATZ2JdOBEATHB7wQqxBTNKheYTKV3ALZnu0d/
6jOlRNHeAD6XlyAvTyuMFiU1qHEACmFN4EQ2y7yR9TI8KmCKkP6wXPzavtVjB/ll
qeozsjD1BDhHhQLmOra8yXuZ9NoWkgNUtn9HRhbMlv0XjrPerkJ+xv0HQudI6Hyy
sEj3fJY5l47CauUOaMmzSf/+ONSE5YQ6eh8Q6RNqpjKJ7HKJqgBDsVCcfkbA6Dtp
KaQcUr4Q8cGoYejqUt9glVnmJDYicebdZFt9BA8i/mCykKvPpsGc/oFyh+u5lFjd
GiFr4QlULSZsO3Yfv4xeFPWqkJ7xMeX65zPc+q5MH2+8pYXL6l/7U3yFI4gEXACX
xiWrnvi8KWhju0GMHji/obUHuaudzYRaWdQWb7ga/xw2Q3UagXZxvM0arOliQT1P
1EvHdN8U7xahKo9kGYAn2sgbOEMuoLC+MRkBE2PocC1TqU8LRootXXah4oJMglvu
80MJYCZ5AvbMeW/lpnrtx9v9gbumP1bl/fxzi+fksMJzjwrQlgjdTM2peNVRP2OT
EQFQm3jtqLqtKo0Oc7BtExevVzb0eGH8L/3Y2NUZCsLkY+KvIZe6O+8nBOcX4GeU
nxSypDDTNxP0bhjMYaSI+eIVFRhEvCHdtscbtzpiOdsAUq9C20HDNTjeJ4QlpP4t
/8nfywmBvgQ3ddujkRX7iGYzGebHOKfJwSXksT+md2wslx/CvMumEvK3CRN2OLK/
dS3deYWLy3DLRJA5bgcp/OAnVKNGt3EGQq11ucup23Fm3jINCgEVzccW/X2AP0uV
cyEFudLnMuHERcjcAHfXHqPCqqohSo+MNGZwoKLjXWSctY7dlofILlwShauBR/Gb
HnArSBPuQaWQRX8gtWIwwjUmy2a63cgKIrvhtSGQO7JY32/omZjAYSGJQ2zppZh+
aluD1TMos69ThHITEaLApO18xzWHlHFxdP0v9sPx1BUhd6NaZiXvjWQnTjBlepjn
xntPyEsM4xypmEAViQeWz7WJdm5O7NhafoFQyn1i+fVysO/ru+LBRFmXp2xwfVUw
Z8m1bCNAZ3ZOro2VIztQsgO9Zv1tycteII+wP+RicDIYaaHnLYbGznbuTtYNAyqb
rBd2uHwlUjVqEfSMlnmdsOcF34ssGF6hU8bBgU3d44BeQCzYpCaxN0MQCCPK/d+V
R/KDe1C1QkDEifDL0wvAZ6uyPPakpQlPi0pTA/20Vfc16LQiCISWBrLWoE3MmtGV
ocAQe1M+xt+1pjOMlv7vPJ5Wfb3zeQ3yL/+d+qL54kr0cHSAF5mE6GjMYscWjott
Vz81RjFgt8ci3pdzDK3W1Zt97qUcFBG+4liQpJZVB3we/v6mhJaQ9lcyb/G1Urk1
GynGtWzzk8aXHMeVi5cfNAGqigxuwl8lMDYlCRoT+q6lzCFqIk+P9avoCZRXCS62
FzVz8lwhDaslYoC+SdWQl7hq6gdxdRFvx+n+nHWcKprGVJ82f9wVid7fqwKh++2P
bNequ4JORLCofyJ1QWYTv/Ep/RQW1rNdgrEf+SUXtK+HSxz6ZwRYCyz2OdBqJgLt
df6s3ivk4ekkowF0h+8a62+aQpHmW9rWVFdP1ibUeThxsyLu41Y4/6LG8FhKxn8U
Qyz9xJvP3QhSrf4LgY8hDb4I8YbZFYrNqlkaeT9ulcbR9bilNJJ8vbpPt0/gsDNx
hbkaL6grhoyva/dDbnUIuYoSRJUCp4I6lBczyYXBtP900jSNH4ezJS0HRarRHf1V
vn5J2xWQnFzbbYAlY4wbzjDRqKwShx/koe9q4JSvs2cYc6tg7Aw1cjJcrmo+TORo
cdoN4S5iL3babk788B8FHjZJeMnoEyqFzzHgq+bSzR4RrhIUVPf6PTIiMMcUt20j
seYUAwZC0I4cJU81Y+ok8nVuOHxRucr9A0MWhjaY05+rdM+TmwFVHDuPGoy0fKOQ
Acj1EAPhhAQGQRXlq7tlbPrsJCHxHpQvCcawIGVYf9Ci0QwDd4q3s5nR5rSW2LUy
+MJh57Rutd7H0Exgj7+KDkEoBxAJMMYuiOm9FJBZ7rxBe9dYjih0vv0ORsMjWTgW
Jr5IxxXuLgIHsMOIL/Z3TS+omXqia6SAWcEogh6QwXdKrt/HZnKJIpUF5roTPmFK
iFTMh3brwkJ2FF8Xgf3MYKnb5WHplQvK8hkLb98rz7J2YqwPY52vaNPQInnocfEs
pYTQTVb2OhRop/nMSr/77vNirnGocySg6IWpQnwU8pc4evtdmX6G0Q4rdHpwy3DE
21w7SOIYAaDKJj9gmsUiik8lCIn58xL+lgZjt1OuB1LP3P5OZ8rZ7Ff+S6Cdltyy
Gn/Q0YBFRD1JtM2anXOGhX/bqsJyvmqD4NsO1dogT8GDd9TJLSbUFdY0zoCk6xfJ
BKQ/IwN7mfXMRmqlJaZgGwzywPpoViQ6iHHIfGNGnqjNwltGHJDefGMLPos03c40
fHqsfCJfseTEUZtreKp/B2RiZeqYg72emcpqWs+q9C2Warrn52GeKpFzKJlELeTL
WpIhuaYI1WEbeoMdBJrV7207U4Ufd4sHTC2QOQOXoRCNMBUHF77vMvgaPHPVETTg
fEzpuDpoTnU79LMWdME0MkGvsCudbBrupNwC15ULt7mNuA2SalfrYxkNskvPKYAh
sg87w/zkl/IVIk5tvSdN44Gvji7vVZvMkfVcyk5o8OU4IZUxy/iRTZUzvBZrqzaA
CFHl+QwXLgApwphApTBEZGWZMCdP1FD21QXNUt4sXWB8eW419p0a6oJFwGPEkRMD
XTJMjhCqP7SJUDPWIS1CuI33bdOuyjGeUSeb0Xe1IYuxWqPBI0oNJL3s6K+V98Ut
meIVIUCOo+sOS64UqdBmcbAkFSnE04xxiFN7jOd3Bfr+2aRDOMYlozM2j+H2oDBF
JyMEZAY2Nn7s2iqyK66q70DLku2x9kmuuY1U6jUxuvYn3id6D5tk4+BhvbSXpvHo
a0pGupBsx2pIY+uHQafwErqGNoH6BAhARq8aeRzNZrPlcfyTiTZ2xYsh40AVYx0S
fNcWNhi4/ZVamjveZD1Gg/w6u4ouzrrBS3PeOebuBrVkUExitZ4QnvZe8Vda/YcY
miV13I66Ks6ZTALSeZ4Hd0b7yjsZnNiBkmAMCClYM8qcrOfGJsAGhi/V4uIzrz0Z
LAMMX5Il3xwDhN/463hP89GTWymwQ2s6R2Y5e4TNUzt/2fajPy7ENDT1htnObeIR
cEsjz7vCkMplEK6tVNbfp7PxfOrH3SnBkkG1A+QlWPrHL3J8Ck4hVq6XKhRwPRgM
8ERpyTgTRmwvPEGXQptVsStFKvqhS8jsJDkxadGFJaOhgqzysIhJUgbO8335oW6M
efyeKPih0zwr2RUq1sKE9KW9T8vnwT2V5BRw3LjZ99NP3ONiuhXflVyLevnGLkmY
7VScu8rGlKij6jW9mIiAL/UA0nt9Dh/vJZGDTh4bMHVwJb0yFMfsbw==
=EhAz
-----END PGP MESSAGE-----

View File

@ -1,343 +0,0 @@
# Dummy secrets for testing
{
site.net = {
core.ospf.secret = "encrypted";
pub.wifi.ieee80211rKey = "2dc40abba46da9490ea0e00f93f18ce5";
c3d2.wifi.ieee80211rKey = "d1b1fa2461efc0df9e2d96579607b7f6";
};
site.hosts = {
ap1.password = "encrypted";
ap2.password = "encrypted";
ap3.password = "encrypted";
ap4.password = "encrypted";
ap5.password = "encrypted";
ap6.password = "encrypted";
ap7.password = "encrypted";
ap8.password = "encrypted";
ap9.password = "encrypted";
ap10.password = "encrypted";
ap11.password = "encrypted";
ap12.password = "encrypted";
ap15.password = "encrypted";
ap17.password = "encrypted";
ap18.password = "encrypted";
ap19.password = "encrypted";
ap21.password = "encrypted";
ap22.password = "encrypted";
ap23.password = "encrypted";
ap24.password = "encrypted";
ap25.password = "encrypted";
ap26.password = "encrypted";
ap27.password = "encrypted";
ap28.password = "encrypted";
ap29.password = "encrypted";
ap30.password = "encrypted";
ap31.password = "encrypted";
ap32.password = "encrypted";
ap33.password = "encrypted";
ap34.password = "encrypted";
ap35.password = "encrypted";
ap36.password = "encrypted";
ap37.password = "encrypted";
ap38.password = "encrypted";
ap39.password = "encrypted";
ap40.password = "encrypted";
ap41.password = "encrypted";
ap42.password = "encrypted";
ap44.password = "encrypted";
ap45.password = "encrypted";
ap46.password = "encrypted";
ap47.password = "encrypted";
ap48.password = "encrypted";
ap49.password = "encrypted";
ap50.password = "encrypted";
ap51.password = "encrypted";
ap52.password = "encrypted";
ap53.password = "encrypted";
ap54.password = "encrypted";
ap55.password = "encrypted";
ap56.password = "encrypted";
ap57.password = "encrypted";
ap58.password = "encrypted";
ap59.password = "encrypted";
ap60.password = "encrypted";
ap61.password = "encrypted";
ap63.password = "encrypted";
ap64.password = "encrypted";
ap65.password = "encrypted";
ap66.password = "encrypted";
ap67.password = "encrypted";
ap68.password = "encrypted";
ap69.password = "encrypted";
ap70.password = "encrypted";
ap71.password = "encrypted";
ap72.password = "encrypted";
switch-a1.password = "encrypted";
switch-b1.password = "encrypted";
switch-b2.password = "encrypted";
switch-b3.password = "encrypted";
switch-c1.password = "encrypted";
switch-c3d2-main.password = "encrypted";
switch-d1.password = "encrypted";
switch-dach.password = "encrypted";
switch-ds1.password = "encrypted";
switch-ds2.password = "encrypted";
switch-ds3.password = "encrypted";
upstream4.interfaces.up4-pppoe.upstream = {
user = "encrypted";
password = "encrypted";
};
anon1.interfaces.njalla.wireguard = {
addresses = [ "fec0::1/64" "192.168.0.1/24" ];
endpoint = "0.0.0.1";
privateKey = "encrypted";
publicKey = "encrypted";
};
flpk-gw.interfaces.up-flpk.wireguard = {
addresses = [ "fec0::1/64" "192.168.0.1/24" ];
endpoint = "0.0.0.1";
privateKey = "encrypted";
publicKey = "encrypted";
};
ap1.wifi."platform/qca953x_wmac".ssids."uebergangsnetz".psk = "encrypted";
ap10.wifi."platform/qca953x_wmac".ssids = {
"iz-dresden.org".psk = "encrypted";
};
ap11.wifi."platform/ahb/18100000.wmac".ssids."braeunigkoschnik".psk = "encrypted";
ap12.wifi."platform/ar934x_wmac".ssids = {
"IrèneMélix".psk = "encrypted";
"paperheart".psk = "encrypted";
};
ap15.wifi."platform/ahb/18100000.wmac".ssids."etz250".psk = "encrypted";
ap17.wifi."platform/ahb/18100000.wmac".ssids = {
"EDUB".psk = "encrypted";
"Zweitwohnsitz".psk = "encrypted";
"e-Stuetzpunkt".psk = "encrypted";
};
ap18.wifi."platform/qca953x_wmac".ssids."Restaurierung Wolff/Kober".psk = "encrypted";
ap19.wifi."platform/qca953x_wmac".ssids = {
"Bockwurst".psk = "encrypted";
"Walter".psk = "encrypted";
};
ap2.wifi = {
"pci0000:00/0000:00:00.0".ssids."C3D2".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids = {
"C3D2 legacy".psk = "encrypted";
"C3D2 IoT".psk = "encrypted";
};
};
ap23.wifi = {
"pci0000:00/0000:00:00.0".ssids."LBK Network".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."LBK Network".psk = "encrypted";
};
ap24.wifi."platform/ar933x_wmac".ssids."farbwerk".psk = "encrypted";
ap25.wifi."platform/ar933x_wmac".ssids."farbwerk".psk = "encrypted";
ap26.wifi."pci0000:00/0000:00:00.0".ssids."Dezember".psk = "encrypted";
ap29.wifi = {
"pci0000:00/0000:00:00.0".ssids."jungnickel-fotografie".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."jungnickel-fotografie".psk = "encrypted";
};
ap3.wifi = {
"pci0000:00/0000:00:00.0".ssids."C3D2".psk = "encrypted";
"platform/ar934x_wmac".ssids."C3D2 legacy".psk = "encrypted";
};
ap30.wifi."platform/ahb/18100000.wmac".ssids."WLANb0402".psk = "encrypted";
ap31.wifi = {
"pci0000:00/0000:00:00.0".ssids."C3D2".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids = {
"C3D2 legacy" = { "psk" = "encrypted"; };
"C3D2 IoT" = { "psk" = "encrypted"; };
"FOTOAKADEMIEdd" = { "psk" = "encrypted"; };
};
};
ap32.wifi = {
"pci0000:00/0000:00:00.0".ssids."ZW stage".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."ZW stage legacy".psk = "encrypted";
};
ap33.wifi = {
"pci0000:00/0000:00:00.0".ssids."C3D2".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."C3D2 legacy".psk = "encrypted";
};
ap34.wifi."pci0000:00/0000:00:00.0".ssids."etz250".psk = "encrypted";
ap34.wifi."platform/ahb/18100000.wmac".ssids."etz250".psk = "encrypted";
ap35.wifi."platform/ahb/18100000.wmac".ssids."Koch".psk = "encrypted";
ap36.wifi."platform/ar933x_wmac".ssids."C3D2 legacy".psk = "encrypted";
ap37.wifi = {
"pci0000:00/0000:00:00.0".ssids."hechtfilm.de".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."hechtfilm.de legacy".psk = "encrypted";
};
ap38.wifi = {
"pci0000:00/0000:00:00.0".ssids = {
"ZW heinrichsgarten" = { "psk" = "encrypted"; };
"plop" = { "psk" = "encrypted"; };
};
"platform/ahb/18100000.wmac".ssids = {
"ZW heinrichsgarten" = { "psk" = "encrypted"; };
"plop" = { "psk" = "encrypted"; };
"millimeter" = { "psk" = "encrypted"; };
};
};
ap39.wifi."platform/10180000.wmac".ssids."EckiTino".psk = "encrypted";
ap4.wifi."platform/ahb/18100000.wmac".ssids."jam-circle.de".psk = "encrypted";
ap40.wifi = {
"pci0000:00/0000:00:00.0".ssids."M".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."M legacy".psk = "encrypted";
};
ap41.wifi = {
"pci0000:00/0000:00:00.0".ssids."Walter".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Walter".psk = "encrypted";
};
ap42.wifi = {
"pci0000:00/0000:00:00.0".ssids."jam-circle.de".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."jam-circle.de legacy".psk = "encrypted";
};
ap44.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap45.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap46.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids = {
"EWW".psk = "encrypted";
"ZW stage legacy".psk = "encrypted";
};
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids = {
"EWW".psk = "encrypted";
"ZW stage".psk = "encrypted";
};
};
ap47.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap48.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap49.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap5.wifi."platform/ahb/18100000.wmac".ssids."verbalwerk.de".psk = "encrypted";
ap50.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids = {
"ZW stage legacy".psk = "encrypted";
"gerdwork".psk = "encrypted";
};
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap51.wifi = {
"pci0000:01/0000:01:00.0".ssids."antrares".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."antrares".psk = "encrypted";
};
ap52.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids."ZW stage legacy".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."ZW stage".psk = "encrypted";
};
ap53.wifi."platform/qca953x_wmac".ssids."Karen Koschnick".psk = "encrypted";
ap54.wifi = {
"pci0000:00/0000:00:00.0".ssids."Abyssinia".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Abyssinia".psk = "encrypted";
};
ap55.wifi = {
"pci0000:00/0000:00:00.0".ssids."MagLAN".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."MagLAN (legacy)".psk = "encrypted";
};
ap56.wifi = {
"pci0000:00/0000:00:00.0".ssids."MagLAN".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."MagLAN (legacy)".psk = "encrypted";
};
ap57.wifi = {
"pci0000:00/0000:00:00.0".ssids."Zentralwerk".psk = "encrypted";
};
ap58.wifi = {
"pci0000:00/0000:00:00.0".ssids."Zentralwerk".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."LIZA".psk = "encrypted";
};
ap59.wifi = {
"pci0000:00/0000:00:00.0".ssids."Ebs 5000".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Ebs 2000".psk = "encrypted";
};
ap60.wifi = {
"pci0000:00/0000:00:00.0".ssids."Abyssinia".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Abyssinia".psk = "encrypted";
};
ap61.wifi = {
"pci0000:00/0000:00:00.0".ssids."tomiru".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."tomiru".psk = "encrypted";
};
ap62.wifi = {
"pci0000:00/0000:00:00.0".ssids."Wolke7".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Wolke7 legacy".psk = "encrypted";
};
ap63.wifi = {
"pci0000:00/0000:00:00.0".ssids."EckiTino".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."EckiTino legacy".psk = "encrypted";
};
ap64.wifi = {
"platform/ahb/18100000.wmac".ssids."Princess Castle".psk = "encrypted";
};
ap65.wifi = {
"1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0".ssids = {
"farbwerk".psk = "encrypted";
"Kaffeetasse".psk = "encrypted";
};
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."farbwerk".psk = "encrypted";
};
ap66.wifi = {
"pci0000:00/0000:00:00.0".ssids."Buschfunk4.03".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."Buschfunk4.03 legacy".psk = "encrypted";
};
ap67.wifi = {
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."farbwerk".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0+1".ssids."farbwerk".psk = "encrypted";
};
ap68.wifi = {
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."farbwerk".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0+1".ssids."farbwerk".psk = "encrypted";
};
ap69.wifi = {
"pci0000:00/0000:00:00.0".ssids."LIZA".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."LIZA".psk = "encrypted";
};
ap7.wifi."platform/qca953x_wmac".ssids."mino".psk = "encrypted";
ap70.wifi = {
"pci0000:00/0000:00:00.0".ssids."M".psk = "encrypted";
"platform/ahb/18100000.wmac".ssids."M legacy".psk = "encrypted";
};
ap72.wifi = {
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0".ssids."farbwerk".psk = "encrypted";
"1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0+1".ssids."farbwerk".psk = "encrypted";
};
ap8.wifi = {
"pci0000:00/0000:00:00.0".ssids."C3D2".psk = "encrypted";
"platform/ar934x_wmac".ssids = {
"C3D2 legacy".psk = "encrypted";
"teknologi".psk = "encrypted";
};
};
ap9.wifi."platform/qca953x_wmac".ssids."Herzzbuehne".psk = "encrypted";
};
site.dyndnsKey = "oYmxXCIa0nArp0679L6v+y/UfnhripOudLv+R5Cop8I=";
site.vpn.wireguard = {
privateKey = "wPNXY4ED3Jz3Kz0KOmvfQOou6/wHrgqSsykaMYrtb28=";
peers = [ {
# privateKey: GOdfeizQZjPmyYnh3LMI3LrYeEtqYMyOvK8KASVgI1Q=
publicKey = "4aTjdm/APMTERczvtnLXRFYjSWYsmwPFTumjyno4nx4=";
allowedIPs = [
"172.20.76.226"
"fd23:42:c3d2:585::/64"
"2a00:8180:2c00:285::/64"
];
} ];
};
}

View File

@ -1,40 +0,0 @@
{ config, lib, ... }:
let
containers = lib.filterAttrs (_: { role, ... }:
role == "container"
) config.site.hosts;
bridgeInterfaces = builtins.foldl' (result: { interfaces, ... }:
result // builtins.mapAttrs (_: _: {
type = "bridge";
}) (
lib.filterAttrs (_: { type, ... }:
builtins.elem type [ "veth" "bridge" ]
) interfaces
)
) {} (builtins.attrValues containers);
makeServer = _name: {
role = "server";
model = "pc";
interfaces = bridgeInterfaces // {
cluster = {
type = "bridge";
gw4 = "cls-gw";
gw6 = "cls-gw";
};
};
};
in
{
site = {
hosts = {
server1 = makeServer "server1";
server2 = makeServer "server2";
};
sshPubKeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDOFs2LdK23ysS0SSkXZuULUOCZHe1ZxvfOKj002J6rkvAaDLar9g5aKuiIV70ZR33A2rchoLMiM4pLLwoSAPJg1FgIgJjU+DFoWtiW+IjzKXdHHVspb2iOIhpfbfk8WC5HZ/6fPz4RUqadGQ43ImnMhSN0ge3s/oM48hpc96ne6tH+mGiugdPx8097NE9yTqJHi8deBhi3daeJH4eQeg66Fi+kDIAZv5TJ0Oca5h7PBd253/vf3l21jRH8u1D1trALv9KStGycTk5Nwih+OHx+Rnvue/B/nxgAz4I3mmQa+jhRlGaQVG0MtOBRY3Ae7ZNqhjuefDUCM2hwG70toU9xDUw0AihC2ownY+P2PjssoG1O8f/D7ilw7qrXJHEeM8HwzqMH8X4ELYHaHTwjeWfZTTFev1Djr969LjdS1UZzqCZHO0jmQ5Pa3eXw8xcoprtt620kYLTKSMs6exLstE48o57Yqfn+eTJDy7EkcjiLN6GNIi42b9Z73xXNpZx1WR9O6OulJf/6pWgrApasvxiGmxxILq98s1/VnZkOFXR8JXnpvKHEIOIr3bFQu3GLCrzY2Yuh4NL5wy6lcZNTr/0rr6AO24IbEWM7TApbXnKA5XQhAbThrVsuFBdT3+bBP2nedvWQ0W+Q6SUf+8T2o5InnFqs5ABnTixBItiWw+9BiQ== root@server1"
];
};
}

View File

@ -1,469 +0,0 @@
{
site.hosts = {
switch-a1 = {
role = "switch";
model = "TL-SG3210";
location = "Saal A";
interfaces = { mgmt.type = "phys"; };
links = {
switch-a2.ports = [ "7" ];
priv25.ports = [
# A6: Kleiner Saal Schaltschrank
"1"
# Kabinett A10
"2"
"3"
# A16: Buehne rechts unten
"4"
# artnet node
"5"
# Panel A2: Foyer
"8"
# Panel A8: Kleiner Saal Buehne
];
priv31.ports = [
# A4: Buero
"6"
];
# A3: Techniklager
# (DS23: Hackcenter vor kleinem Saal)
# A17: Grosser Saal ueber der Buehne
# switch-a2 Port 13
# Panel A6: kl Saal hinten
};
};
switch-a2 = {
role = "switch";
model = "dumb";
location = "Saal A";
links = {
switch-c1.ports = [ "1" ];
switch-a1.ports = [ "2" ];
switch-ds1.ports = [ "3" ];
switch-ds2.ports = [ "4" ];
switch-ds3.ports = [ "5" ];
ap44.ports = [ "10" ];
ap45.ports = [ "11" ];
ap46.ports = [ "12" ];
ap47.ports = [ "13" ];
ap48.ports = [ "14" ];
ap49.ports = [ "15" ];
ap50.ports = [ "16" ];
ap52.ports = [ "17" ];
};
};
switch-b3 = {
role = "switch";
model = "junos";
location = "Haus B Souterrain";
interfaces = { mgmt.type = "phys"; };
# Ports 1-19 ungerade oben
# Ports 2-20 gerade unten
# (15, 16 gehen aktuell nach Haus A)
# Ports 21-24 unten seitlich (optional optisch)
# Port 7 geht aktuell nach Turm C Erdgeschoss und dadurch zur Ecce
links = {
ap23.ports = [ "ge-0/0/10" ];
ap8.ports = [ "ge-0/0/16" ];
iso1.ports = [ "ge-0/0/2" ];
iso2.ports = [ "ge-0/0/3" ];
iso3.ports = [ "ge-0/0/4" ];
coloradio.ports = [
# Patchpanel C8
"ge-0/0/22"
];
c3d2.ports = [
"ge-0/0/5"
"ge-0/0/8"
# riscbert
"ge-0/0/13"
];
server1 = {
group = "3";
ports = [
"ge-0/0/24"
"ge-0/0/25"
"ge-1/0/24"
"ge-1/0/25"
];
};
server2 = {
group = "1";
ports = [
"ge-0/0/38" "ge-0/0/39"
"ge-1/0/32" "ge-1/0/33"
];
};
hydra = {
group = "7";
trunk = false;
ports = [
"ge-0/0/14" "ge-0/0/15"
"ge-1/0/14" "ge-1/0/15"
];
};
server10 = {
group = "5";
ports = [
"ge-0/0/36" "ge-0/0/37"
"ge-1/0/36" "ge-1/0/37"
];
};
switch-c1 = {
group = "2";
ports = [ "ge-0/0/30" "ge-0/0/31" "ge-1/0/30" "ge-1/0/31" ];
};
switch-c3d2-main = {
group = "4";
ports = [
"ge-0/0/26"
"ge-0/0/27"
"ge-1/0/26"
"ge-1/0/27"
];
};
switch-d1.ports = [ "ge-0/0/34" ];
ap1.ports = [ "ge-1/0/8" ];
ap11.ports = [ "ge-1/0/10" ];
ap34.ports = [ "ge-1/0/12" ];
ap18.ports = [ "ge-1/0/18" ];
ap29.ports = [ "ge-0/0/46" ];
ap30.ports = [ "ge-1/0/22" ];
ap35.ports = [ "ge-1/0/23" ];
ap37.ports = [ "ge-1/0/39" ];
ap63.ports = [ "ge-1/0/17" ];
ap40.ports = [ "ge-1/0/21" ];
ap41.ports = [ "ge-0/0/47" ];
ap42.ports = [ "ge-1/0/6" ];
ap5.ports = [ "ge-1/0/7" ];
ap51.ports = [ "ge-1/0/13" ];
ap53.ports = [ "ge-0/0/7" ];
ap72.ports = [ "ge-1/0/38" ];
ap55.ports = [ "ge-1/0/19" ];
ap56.ports = [ "ge-1/0/9" ];
ap60.ports = [ "ge-1/0/20" ];
ap62.ports = [ "ge-0/0/11" ];
ap65.ports = [ "ge-0/0/9" ];
ap66.ports = [ "ge-1/0/43" ];
mgmt.ports = [
"ge-0/0/0"
"ge-0/0/1"
"ge-1/0/44"
# server7
"ge-1/0/45"
"ge-1/0/46"
# server8
"ge-1/0/47"
# server9
"ge-1/0/48"
];
flpk.ports = [
# server7
"ge-0/0/40"
];
priv1.ports = [ "ge-1/0/3" ];
priv19.ports = [ "ge-1/0/40" ];
priv2.ports = [ "ge-1/0/4" ];
priv24.ports = [ "ge-0/0/6" "ge-1/0/16" ];
priv3.ports = [ "ge-1/0/5" ];
priv30.ports = [ "ge-0/0/12" ];
priv49.ports = [ "ge-1/0/1" ];
ap67.ports = [ "ge-1/0/34" ];
ap68.ports = [ "ge-1/0/35" ];
ap69.ports = [ "ge-0/0/35" ];
ap64.ports = [ "ge-0/0/45" ];
pub.ports = [
"ge-1/0/11"
];
server9 = {
group = "10";
ports = [
"ge-0/0/28"
"ge-0/0/29"
"ge-1/0/28"
"ge-1/0/29"
];
};
server8 = {
group = "6";
ports = [
"ge-0/0/41"
"ge-0/0/42"
"ge-1/0/41"
"ge-1/0/42"
];
};
server6 = {
group = "9";
ports = [
"ge-0/0/18"
"ge-0/0/19"
"ge-1/0/0"
"ge-1/0/2"
];
};
};
};
switch-c1 = {
role = "switch";
model = "HP-procurve-2824";
location = "Turm C Keller, bei Kabelanschluessen";
interfaces = { mgmt.type = "phys"; };
links = {
# Saal A: durch dummen PoE-Switch mit Aggregation an ap44-50,52 + switch-a1
switch-a2 = {
group = "1";
ports = [ "15-16" ];
};
switch-b3 = {
group = "2";
ports = [ "21-24" ];
};
switch-dach.ports = [ "6" ];
# Vodafone Modems
up1.ports = [ "1" ];
up2.ports = [ "2" ];
# DSI
up4.ports = [ "4" ];
# Turm C APs
ap17.ports = [ "19" ];
ap19.ports = [ "17" ];
ap26.ports = [ "18" ];
ap38.ports = [ "7" ];
ap61.ports = [ "13" ];
# Iso nets
iso1.ports = [ "9" ];
iso2.ports = [ "10" ];
iso3.ports = [ "11" ];
iso4.ports = [ "12" ];
iso6.ports = [ "14" ];
# Saal Foyer
priv25.ports = [ "20" ];
};
};
switch-c3d2-main = {
role = "switch";
model = "HP-procurve-2824";
location = "C3D2";
interfaces = { mgmt.type = "phys"; };
links = {
mgmt.ports = [ "1" ];
switch-b3 = {
group = "1";
ports = [ "21-24" ];
};
# Kammer
ap2.ports = [ "3" ];
# Decke
ap31.ports = [ "2" ];
# Fenster
ap33.ports = [ "5" ];
c3d2.ports = [ "8-20" ];
# tmp Datenspuren: VOC
iso4.ports = [ "4" "6" "7" ];
};
};
switch-d1 = {
role = "switch";
model = "TL-SG3210";
location = "Turm D Elektroraum";
interfaces = { mgmt.type = "phys"; };
links = {
switch-b3 = {
group = "1";
ports = [ "1" ];
};
switch-d2.ports = [ "3" ];
# Turm D APs
ap7.ports = [ "8" ];
ap9.ports = [ "5" ];
ap10.ports = [ "4" ];
ap12.ports = [ "7" ];
ap22.ports = [ "2" ];
};
};
switch-d2 = {
role = "switch";
model = "dumb";
location = "Turm D Durchgang 1. Etage";
links = {
switch-d1 = {
group = "1";
ports = [ "12" ];
};
ap3.ports = [ "1" ];
ap59.ports = [ "2" ];
};
};
switch-dach = {
role = "switch";
model = "HP-procurve-2824";
location = "Dach";
interfaces = { mgmt.type = "phys"; };
links = {
mgmt.ports = [ "1" ];
switch-c1.ports = [ "24" ];
# Freifunk nodes
bmx.ports = [ "12,14,16" ];
# radiobert
serv.ports = [ "7" ];
# Starlink
up3.ports = [ "3" ];
# unifiac-mesh
ap57.ports = [ "10" ];
# TLMS tetra and traffic-stop-box
c3d2.ports = [ "19,20" ];
};
};
switch-ds1 = {
role = "switch";
model = "3com-5500G";
location = "Foyer";
interfaces = { mgmt.type = "phys"; };
links = {
# Public
pub.ports = [
"GigabitEthernet1/0/1"
"GigabitEthernet1/0/2"
"GigabitEthernet1/0/3"
"GigabitEthernet1/0/4"
"GigabitEthernet1/0/5"
"GigabitEthernet1/0/6"
"GigabitEthernet1/0/7"
"GigabitEthernet1/0/8"
"GigabitEthernet1/0/9"
"GigabitEthernet1/0/10"
"GigabitEthernet1/0/11"
"GigabitEthernet1/0/12"
"GigabitEthernet1/0/13"
"GigabitEthernet1/0/14"
"GigabitEthernet1/0/15"
];
# Stage uplink
priv25.ports = [
"GigabitEthernet1/0/16"
"GigabitEthernet1/0/17"
"GigabitEthernet1/0/18"
"GigabitEthernet1/0/19"
];
# Freifunk
bmx.ports = [
"GigabitEthernet1/0/20"
"GigabitEthernet1/0/21"
"GigabitEthernet1/0/22"
"GigabitEthernet1/0/23"
];
# Uplink
switch-a2.ports = [ "GigabitEthernet1/0/24" ];
};
};
switch-ds2 = {
role = "switch";
model = "3com-5500G";
location = "Grosser Saal oben";
interfaces = { mgmt.type = "phys"; };
links = {
# Public
pub.ports = [
"GigabitEthernet1/0/1"
"GigabitEthernet1/0/2"
"GigabitEthernet1/0/3"
"GigabitEthernet1/0/4"
"GigabitEthernet1/0/5"
"GigabitEthernet1/0/6"
"GigabitEthernet1/0/7"
"GigabitEthernet1/0/8"
"GigabitEthernet1/0/9"
"GigabitEthernet1/0/10"
"GigabitEthernet1/0/11"
"GigabitEthernet1/0/12"
"GigabitEthernet1/0/13"
"GigabitEthernet1/0/14"
"GigabitEthernet1/0/15"
"GigabitEthernet1/0/16"
"GigabitEthernet1/0/17"
"GigabitEthernet1/0/18"
"GigabitEthernet1/0/19"
];
# Stage uplink
priv25.ports = [
"GigabitEthernet1/0/20"
"GigabitEthernet1/0/21"
];
# VOC isolated
iso4.ports = [
"GigabitEthernet1/0/22"
"GigabitEthernet1/0/23"
];
# Uplink
switch-a2.ports = [ "GigabitEthernet1/0/24" ];
};
};
switch-ds3 = {
firstboot = true;
role = "switch";
model = "3com-5500G";
location = "Kleiner Saal";
interfaces = { mgmt.type = "phys"; };
links = {
# Public
pub.ports = [
"GigabitEthernet1/0/1"
"GigabitEthernet1/0/2"
"GigabitEthernet1/0/3"
"GigabitEthernet1/0/4"
"GigabitEthernet1/0/5"
"GigabitEthernet1/0/6"
"GigabitEthernet1/0/7"
"GigabitEthernet1/0/8"
"GigabitEthernet1/0/9"
"GigabitEthernet1/0/10"
"GigabitEthernet1/0/11"
"GigabitEthernet1/0/12"
"GigabitEthernet1/0/13"
"GigabitEthernet1/0/14"
"GigabitEthernet1/0/15"
"GigabitEthernet1/0/16"
"GigabitEthernet1/0/17"
"GigabitEthernet1/0/18"
"GigabitEthernet1/0/19"
];
# Stage uplink
priv25.ports = [
"GigabitEthernet1/0/20"
"GigabitEthernet1/0/21"
];
# VOC isolated
iso4.ports = [
"GigabitEthernet1/0/22"
"GigabitEthernet1/0/23"
];
# Uplink
switch-a2.ports = [ "GigabitEthernet1/0/24" ];
};
};
};
}

View File

@ -1,43 +0,0 @@
let
range = cur: max:
if cur <= max
then [ cur ] ++ range (cur + 1) max
else [];
in
{
site.net = builtins.mapAttrs (_: vlan: { inherit vlan; }) {
# switches and CPE only have IP addresses configured in the management vlan
mgmt = 1;
# routers, OSPF area 0
core = 2;
# servers...
serv = 3;
# ZW public
pub = 4;
# C3D2 home network
c3d2 = 5;
cluster = 6;
bmx = 7;
flpk = 8;
coloradio = 9;
# Modems
up1 = 10;
up2 = 11;
up3 = 12;
up4 = 13;
# Isolated other stuff
c3d2iot = 20;
# Isolated neighbors directly connectied with their modems
iso1 = 101;
iso2 = 102;
iso3 = 103;
iso4 = 104;
iso5 = 105;
iso6 = 106;
} // builtins.foldl' (result: i:
# Neighbor subnets
result // {
"priv${toString i}".vlan = i + 39;
}
) {} (range 1 61);
}

Binary file not shown.

23
doc/ca.pem Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID6DCCAtCgAwIBAgIJALTDMS9sfATBMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD
VQQGEwJERTEQMA4GA1UECAwHU2FjaHNlbjEQMA4GA1UEBwwHRHJlc2RlbjEUMBIG
A1UECgwLWmVudHJhbHdlcmsxEDAOBgNVBAsMB05ldHdvcmsxEDAOBgNVBAMMB0Yu
IE5vcmQxGzAZBgkqhkiG9w0BCQEWDHJvb3RAYzNkMi5kZTAeFw0xNzAyMDYyMzE1
MDRaFw0yNzAyMDQyMzE1MDRaMIGIMQswCQYDVQQGEwJERTEQMA4GA1UECAwHU2Fj
aHNlbjEQMA4GA1UEBwwHRHJlc2RlbjEUMBIGA1UECgwLWmVudHJhbHdlcmsxEDAO
BgNVBAsMB05ldHdvcmsxEDAOBgNVBAMMB0YuIE5vcmQxGzAZBgkqhkiG9w0BCQEW
DHJvb3RAYzNkMi5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKyk
jWtecgb49vxmmOyw0Lno91HjJ8n75/jb27KaE1eqib0vFL1G7qZeaR1IKdceCXE+
umieBpK0g83Bb01V9Vsg1vHC5hLq5u0bqbITpU5jtVRdO003GFa9yrtrvBsyw7tH
cR4cewWaewNzZPFuvb/dgzqSOAoxKg4gjsUKM+0TJN2Zz5Zqat4aks1Jig/EKjDl
zXIA3NkuzdUTmDB4JpYOOR+WZGKVAO9s6eFJ8AoJZRzSqlIonYjQCvRL02vCztIO
+BEgj3ezP6kuAaNcnifoS3APIBil2jfQB7+pl9XmxeOK/BKghOjOUypgcQVAgC/J
HM1tEJKm6goXKUrf428CAwEAAaNTMFEwHQYDVR0OBBYEFOfykHof0NOzWSZUORJh
wNjZAx/hMB8GA1UdIwQYMBaAFOfykHof0NOzWSZUORJhwNjZAx/hMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAASFNwwW5Xtf0ZkeAGhzi/Pnm2eu
pp2GyRHkvar8s/gYX7STqDkqxOtVcySHYxDELaw0BP4b71VkthmdxJXLdDDI5Zqg
zjttxfWGnRH6jvYEvFMWS3h6fBKZpkpNcg2tZ549uuFjdyqaDp+EYM82kT+HkRCP
hWQSHEW9uXi7wMd9dtz9rJV/wfClDQJp6GeKIoXHBJIb9PyQb0yoAjIw7XHGPkr/
MTxv6RKAGgxtmEAHLu8DeF9R8Z/r6kU2OVcq+xK90mp6zzV233lorlSDtQfev1kn
NG4kc8M9YiI0xCfNKJCkQMdZbxZ58uuAbF39ID9vyfyf6eVqQp8VxlvdJrI=
-----END CERTIFICATE-----

5
doc/encryption.md Normal file
View File

@ -0,0 +1,5 @@
# Encrypting salt-pillar/ values
```shell
echo -n $SECRET | gpg --armor --batch --trust-model always --encrypt -r 1F0F221A7483B5EF5D103D8B32EBADE870BAF886
```

View File

@ -55,14 +55,10 @@ Von geeigneten Routern haben wir stets zu wenige übrig, so dass wir sie
gemeinsam kaufen und bezahlen müssen. Such dir einen aus, dann
bestellen und konfigurieren wir ihn.
* Zyxel WSM20 (Multy M1) ([25€](https://geizhals.de/zyxel-multy-m1-v101058.html))
* TP-Link Archer C7 v2 ([58€](http://geizhals.de/tp-link-archer-c7-v2-a923544.html))
* Ubiquiti UniFi nanoHD ([150€](https://geizhals.de/ubiquiti-unifi-nanohd-uap-nanohd-a1802819.html))
* [Jedes Gerät auf dem OpenWRT läuft](https://openwrt.org/supported_devices)
Die genannten Preise sind unverbindlich und schwanken stark mit den
Situationen rund um die Straße von Malaka, Rotem Meer und
Suez-Kanal. Auf eBay gibts gebrauchte Geräte.
![WLAN-Router](https://upload.wikimedia.org/wikipedia/commons/thumb/3/34/Linksys-Wireless-G-Router.jpg/280px-Linksys-Wireless-G-Router.jpg)
### Netzverteilung
@ -158,8 +154,11 @@ mitbenutzen indem wir dir ein [VLAN](https://de.wikipedia.org/wiki/VLAN)
konfigurieren, welches nur dein Kabelmodem und deine Datendose bekommen.
---
# Sprechstunden
Dienstags und Donnerstags 16:00-18:00 Uhr
[C3D2](https://www.c3d2.de/space.html), Haus B Souterrain, genau in der Mitte
![Kernnetz visualisiert von eri!](core.png)

17
doc/port-mirroring.txt Normal file
View File

@ -0,0 +1,17 @@
Manchmal ist es notwendig einen Port auf dem Switch auf einen anderen zu duplizieren. Es kann auch über vlan ganz woanders hingeschickt werden, aber hier ist die Variante um es auf einen anderen Port auf demselben Switch zu legen:
#XX: eine numerische id
#YY: das interface was monitored werden soll
mirroring-group XX mirroring-port GigabitEthernet 1/0/YY both
#ZZ: ist das interface auf dem der traffic des anderen ports erscheinen soll
mirroring-group YY monitor-port GigabitEthernet 1/0/ZZ
#ggfs. stp ausmachen
interface GigabitEthernet 1/0/ZZ stp disable
#danach aufräumen
undo mirroring-group XX
Der monitor-port ist an sich noch funktional, er empfängt zB broadcasts der vlans, die auf ihm konfiguriert sind. Ggfs. die vlans deaktivieren, falls wirklich nur der Traffic des mirroring-port gesehen werden soll.

21
doc/server.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhDCCAmwCAQEwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAkRFMRAwDgYD
VQQIDAdTYWNoc2VuMRAwDgYDVQQHDAdEcmVzZGVuMRQwEgYDVQQKDAtaZW50cmFs
d2VyazEQMA4GA1UECwwHTmV0d29yazEQMA4GA1UEAwwHRi4gTm9yZDEbMBkGCSqG
SIb3DQEJARYMcm9vdEBjM2QyLmRlMB4XDTE3MDIwNjIzMTUzMFoXDTI3MDIwNDIz
MTUzMFowgYYxCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdTYWNoc2VuMRAwDgYDVQQH
DAdEcmVzZGVuMRQwEgYDVQQKDAtaZW50cmFsd2VyazEQMA4GA1UECwwHTmV0d29y
azEOMAwGA1UEAwwFRmlvbmExGzAZBgkqhkiG9w0BCQEWDHJvb3RAYzNkMi5kZTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOOE2AI7JlSS+o8ll85Bz4Gy
Uv+/I0bJOTOCRWYBuXIJV7eAHczpdlPoCz+SiTt5MNMalCAmme9SChrS7H4CFCBs
RNmMkgMiM0khUN6/EW+QaL3TdAwbcJFPGEhD/6HoJli5fvfsHV2sy1/Un+fr5ZDK
uv1xjeOzQaaMqATkIPhuvWHIRO6vUBGyepyssTKZbwdlwecAuTuAs/2UAO2j8ANs
hWJ+Ns7naZPzJ8YjyAYAoLcxHzOwz0cgtI7oveeudWGW6SX/U+pQyNMciQ6bv8f7
A9PAfX3d5oyy6UQp2gqUKEbSpRxa0I0ZO8hseLz70UnhDcQuuRnlr/pOFCKhDEkC
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAFqgRx/mqcqTdob3OJXIWIRXzcQP1YYLO
As3K485qo9e2b28TfrHdHuXWRvR5k7kbtbfqnRLhlfrXQtwYC/9//WzO51LbmKiE
gnteG6M/bOelxTsOcuvYL8prTcNxgkRUcVOxwm5rtLDhhc4Ibp/ciNzVNk0+9sTF
kYqy2qUuqAscyU5EpSrZ8SPUbK1Pf1Q6x3qU30xkyE5IPIv3sGx6VTwEJBWhyVw5
FLtpk/zc9768R/MKxe/WAtuK02x8i8oLb4WtqjSPcuUpo6GUXy3pvKskboJVrPbD
4Kn3vZqtKwxPv4HZ40abzMAq/0QOkUFGkWLJcVlyVegZEQE+O8DIwg==
-----END CERTIFICATE-----

View File

@ -1,16 +0,0 @@
# Wireguard VPN Dialin
## wg-quick template
```ini
[Interface]
Address = 172.20.76.TODO/28
Address = fd23:42:c3d2:585::TODO/64
Address = 2a00:8180:2c00:285::TODO/64
PrivateKey = TODO
[Peer]
PublicKey = PG2VD0EB+Oi+U5/uVMUdO5MFzn59fAck6hz8GUyLMRo=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = upstream4.dyn.zentralwerk.org:1337
```

58
doc/wifi-provisioning.md Normal file
View File

@ -0,0 +1,58 @@
# Provisionierung eines WLAN-Routers
TODO: nixify
Sofern der Router durch uns provisioniert wird (ansonsten siehe unten)
1. Kontaktdaten und Dosennummer in `contacts.md` vermerken
2. Nachbar will Privatnetz (verschlüsseltes WLAN)?
1. privX allokieren
2. `privX-gw` in `salt-pillar/lxc-containers` anlegen
3. Erweitere `salt-pillar/dhcp/init.sls` um das Netz anhand [der
Subnets](../subnets.md). Erste IPv4-Adresse muss für den `privX-gw`
frei bleiben, siehe `salt-pillar/hosts/init.sls`
4. `salt server1 state.apply lxc-containers`
5. `salt-key -A`
6. `salt privX-gw state.apply`
3. AP provisionieren in `salt-pillar/cpe/aps`
1. Frisches OpenWRT/LEDE? `firstboot: True`
2. Feld `model`
3. Feld `location`
4. LAN-Dosen in `lan-access` auf *privX* legen
5. WLANs anpassen:
1. SSID festlegen
2. `net` auf *privX*
3. `psk` vom Benutzer eingeben lassen, verschlüsselt
abspeichern
6. Kanäle je nach Umgebung des Nachbars optimieren
* Ist der Nachbar auf Kanal 1 mag man eher zum Kanal 6
* als weitere Option gibt es noch Kanal 11
7. mit root@server1 `salt server1 state.apply cpe` ausführen
* das generiert `/root/apX.sh`
8. mit root@server1 `/root/apX.sh` ausführen zum Anwenden des Konfigurationsskripts
9. Wenn Fehler gezeigt werden
* `entry not found` ist nicht schlimm
* `invalid` hingegen schon!
* dann "commit" entfernen und den Fehler suchen.
10. collectd installieren wie beim Ende des letzten Skripts angezeigt
11. Falls noch Platz ist, ist das Paket `tcpdump` pratisch
12. EAP-Netze brauchen große Features: `opkg remove wpad-mini && opkg install wpad`
4. Switch-Port konfigurieren in `salt-pillar/switches`
1. Switch raussuchen, z.B. *switch-b1* oder *switch-b2* im Haus B
2. Eintrag `apX` anlegen
3. `mode: trunk`
4. Der Port in welchen das Kabel gesteckt wird kommt unter `ports:`
* z.B.: `- GigabitEthernet 1/0/36`
5. `vlans:` immer *mgmt* und *pub*, sowie das *privX*
6. `salt server1 state.apply switches`
7. `/root/switch-X.sh` zum Anwenden des Konfigurationsskripts
Wenn wir nicht provisionieren, sondern ein mitgebrachter Router verwendet wird:
* kein ZW public konfigurieren!
* bridge-mode verwenden, dhcpd aus
* wenn VLAN möglich: switchport mode auf "trunk"; wenn VLAN nicht möglich: switchport mode auf "access"

View File

@ -1,100 +1,58 @@
{
"nodes": {
"dns-nix": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1703643450,
"narHash": "sha256-EUUF5oxFFPX/etKm0FNQg+7MPHQlNjmM1XhNgyDf7A0=",
"owner": "SuperSandro2000",
"repo": "dns.nix",
"rev": "70dcce71560d4253f63812fa36dee994c81ae814",
"type": "github"
},
"original": {
"owner": "SuperSandro2000",
"repo": "dns.nix",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1614513358,
"narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5466c5bbece17adaab2d82fae80b46e807611bf3",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1713634877,
"narHash": "sha256-+tmLKU8N+YMIIBRPmWFueaytsbSDu4wqGnxc3RKYZwk=",
"owner": "SuperSandro2000",
"lastModified": 1631792076,
"narHash": "sha256-dBRsZ3JB6i53nzC30SsltdwrzjIr8e0zU/y8HitKpT8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "84f20dcf85434cd2e2a163ec3a30937c78cc26b2",
"rev": "4d7170376374e74c7aa52938004e354de1947373",
"type": "github"
},
"original": {
"owner": "SuperSandro2000",
"ref": "nixos-23.11",
"owner": "NixOS",
"ref": "release-21.05",
"repo": "nixpkgs",
"type": "github"
}
},
"openwrt": {
"flake": false,
"nixpkgs-master": {
"locked": {
"lastModified": 1713442482,
"narHash": "sha256-OAcv1qiM2V6wPQm4Tz2QnnDpw34pifG6QRDZea7AP9o=",
"ref": "openwrt-23.05",
"rev": "9b33b74ef71225442361d5192d3a727be212c3cd",
"revCount": 58296,
"type": "git",
"url": "https://git.openwrt.org/openwrt/openwrt.git"
},
"original": {
"ref": "openwrt-23.05",
"type": "git",
"url": "https://git.openwrt.org/openwrt/openwrt.git"
}
},
"openwrt-imagebuilder": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1713693953,
"narHash": "sha256-DsJ/pzBSF3CxQWyiw4V3k96h7Q3UaRnQnL1N9tw+uWg=",
"owner": "astro",
"repo": "nix-openwrt-imagebuilder",
"rev": "d4dc8c84f4397be494ae834709276f099df892e7",
"lastModified": 1631810187,
"narHash": "sha256-GHEpi5KiRo8fT9Oma5AFWAZVbP/C/ZnENnsdK++dNEU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a8c43f8eb7ad74d8fa784d12a789195fdd8120f6",
"type": "github"
},
"original": {
"owner": "astro",
"repo": "nix-openwrt-imagebuilder",
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"dns-nix": "dns-nix",
"nixpkgs": "nixpkgs",
"openwrt": "openwrt",
"openwrt-imagebuilder": "openwrt-imagebuilder"
"nixpkgs-master": "nixpkgs-master",
"zentralwerk-network-key": "zentralwerk-network-key"
}
},
"zentralwerk-network-key": {
"locked": {
"dir": "nix/key",
"lastModified": 1631808463,
"narHash": "sha256-5xMZkqqQbpXECnKEK2THT7u4+/vL7SPp3Jvoicm1Moc=",
"ref": "master",
"rev": "e4a5aee0e44ca058d2f12d6c6f34db6d484187fc",
"revCount": 1172,
"type": "git",
"url": "https://gitea.c3d2.de/zentralwerk/network.git?dir=nix%2fkey"
},
"original": {
"dir": "nix/key",
"type": "git",
"url": "https://gitea.c3d2.de/zentralwerk/network.git?dir=nix%2fkey"
}
}
},

View File

@ -2,22 +2,14 @@
description = "Zentralwerk network";
inputs = {
dns-nix = {
url = "github:SuperSandro2000/dns.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs.url = "github:SuperSandro2000/nixpkgs/nixos-23.11";
openwrt = {
url = "git+https://git.openwrt.org/openwrt/openwrt.git?ref=openwrt-23.05";
flake = false;
};
openwrt-imagebuilder = {
url = "github:astro/nix-openwrt-imagebuilder";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs.url = "github:NixOS/nixpkgs/release-21.05";
nixpkgs-master.url = "github:NixOS/nixpkgs";
# `nix flake update --override-flake zentralwerk-network-key git+file:///...`
# to provide the GPG secret key
zentralwerk-network-key.url = "git+https://gitea.c3d2.de/zentralwerk/network.git?dir=nix/key";
};
outputs = inputs@{ self, dns-nix, nixpkgs, openwrt, openwrt-imagebuilder }:
outputs = inputs@{ self, nixpkgs, nixpkgs-master, zentralwerk-network-key }:
let
system = "x86_64-linux";
systems = [ system ];
@ -27,28 +19,31 @@
self.lib.nixosSystem {
inherit system;
modules = [ self.nixosModule ];
specialArgs = {
hostName = name;
inherit (self) lib;
inherit inputs dns-nix self;
};
specialArgs.hostName = name;
specialArgs.lib = self.lib;
specialArgs.self = self;
specialArgs.inputs = inputs;
specialArgs.nixpkgs-master = nixpkgs-master;
};
in {
# Config, and utilities
lib = nixpkgs.lib.extend (_final: _prev:
lib = nixpkgs.lib.extend (final: prev:
import ./nix/lib {
inherit self openwrt;
inherit (nixpkgs.legacyPackages.x86_64-linux) lib pkgs;
inherit self;
inherit (zentralwerk-network-key.lib) gpgKey;
pkgs = nixpkgs.legacyPackages.x86_64-linux;
});
# Everything that can be built locally outside of NixOS
packages = forAllSystems (system:
import ./nix/pkgs { inherit self nixpkgs system openwrt-imagebuilder; }
import ./nix/pkgs { inherit self nixpkgs system; }
);
# Configuration for nixosConfigurations
# (see nix/nixos-module/default.nix)
nixosModule = import ./nix/nixos-module;
nixosModule = { ... }: {
imports = [ ./nix/nixos-module ];
};
# NixOS host systems (servers, and containers)
nixosConfigurations =
@ -76,10 +71,13 @@
''
);
wrappers = {
"all-device-scripts" = hydraJob;
"all-device-scripts" = pkg:
hydraJob pkg;
"export-config" = exportFileWrapper;
".*-pillar" = exportFileWrapper;
"switch-.*" = exportFileWrapper;
"ap.*-image" = exportFileWrapper;
"ap.*" = exportFileWrapper;
};
in
builtins.mapAttrs (name: pkg:

14
nix/key/flake.nix Normal file
View File

@ -0,0 +1,14 @@
{
description = "Zentralwerk network secret GPG key";
outputs = { ... }: {
lib.gpgKey = null;
# test key
lib.dyndnsKey = "Dr1QHSfNtAwgbdoNBtCgl5NxsSXlaw9+qo7juiVTv58=";
# test credentials
lib.pppoe.upstream4 = {
user = "test@example.com";
password = "secret";
};
};
}

View File

@ -1,60 +1,34 @@
{ self
, pkgs ? import <nixpkgs> {}
, gpgKey
}:
let
result = pkgs.lib.evalModules {
args = {
inherit self pkgs;
};
modules = [
(
{ lib, ... }:
with lib;
{
config._module.args = {
inherit self pkgs;
};
options.assertions = mkOption {
type = with types; listOf unspecified;
internal = true;
};
options.warnings = mkOption {
type = types.listOf types.str;
default = [];
internal = true;
};
options.gpgKey = mkOption {
type = with types; nullOr path;
};
config = {
inherit gpgKey;
};
}
)
./options.nix
../../../config
./legacy.nix
];
};
inherit (result) config;
warn = result:
if builtins.length config.warnings > 0
then builtins.trace ''
Warnings:
${self.lib.concatStringsSep "\n" config.warnings}
'' result
else result;
error = result:
let
failed = builtins.filter ({ assertion, ... }:
!assertion
) config.assertions;
in
if failed != []
then throw ''
Errors:
${self.lib.concatMapStringsSep "\n" ({ message, ... }: message) failed}
''
else result;
in warn (error ({
inherit (result) options;
config = builtins.removeAttrs config [ "assertions" "warnings" ];
}))
in
result.config

435
nix/lib/config/legacy.nix Normal file
View File

@ -0,0 +1,435 @@
{ config, pkgs, lib, self, ... }:
let
mainServers = [ "server1" "server2" ];
pillar = self.lib.saltPillarFor "*";
renameAttr = from: to: attrset:
builtins.foldl' (result: name:
if name == from
then result // { "${to}" = attrset.${name}; }
else result // { "${name}" = attrset.${name}; }
) {} (builtins.attrNames attrset);
# HACK: `type = "phys"` works but once an LXC container is stopped
# the VLAN interface is not moved back.
forceVeth = interface: interface // {
type = "veth";
};
netHasDHCP = net:
net == "pub" ||
net == "serv" ||
builtins.match "priv[[:digit:]]+" net != null;
in
{
options.salt-pillar = lib.mkOption {};
config.salt-pillar = pillar;
config.site.net = lib.mkMerge ([
(builtins.mapAttrs (_: vlan: { vlan = vlan; }) pillar.vlans)
(builtins.mapAttrs (_: subnet4: { inherit subnet4; }) pillar.subnets-inet)
(builtins.mapAttrs (_: hosts4: { inherit hosts4; }) pillar.hosts-inet)
(builtins.mapAttrs (net: dhcpData: {
dhcp = {
inherit (dhcpData) start end time max-time;
server =
if netHasDHCP net
then "${net}-gw"
else null;
fixed-hosts =
if dhcpData ? fixed-hosts
then dhcpData.fixed-hosts
else {};
router = dhcpData.host-opts.routers;
};
domainName = dhcpData.string-opts.domain-name;
}) pillar.dhcp)
{
core.ospf.secret = pillar.ospf.secret;
pub.dynamicDomain = true;
c3d2.dynamicDomain = true;
c3d2.dhcp = {
server = "c3d2-gw3";
router = "c3d2-anon";
start = "172.22.99.100";
end = "172.22.99.199";
fixed-hosts = {
"astron.hq.c3d2.de" = "aa:00:5b:08:f0:5b";
"astrom.hq.c3d2.de" = "aa:00:5b:08:f0:5c";
"www1.hq.c3d2.de" = "aa:00:13:8b:03:47";
"dn42.hq.c3d2.de" = "aa:00:42:7a:32:46";
"icq.hq.c3d2.de" = "aa:00:30:f6:27:89";
"jabber1.hq.c3d2.de" = "aa:00:0b:19:8f:14";
"jabber2.hq.c3d2.de" = "aa:00:3d:6a:23:b8";
"wiefelspuetz.hq.c3d2.de" = "aa:00:7f:01:8a:d0";
"git.hq.c3d2.de" = "aa:00:47:d8:57:10";
"fernandopoo.hq.c3d2.de" = "aa:00:f7:52:85:27";
"moleflap.hq.c3d2.de" = "aa:00:0d:b1:6c:67";
"wormhole.hq.c3d2.de" = "00:23:c3:d2:00:76";
"sharing.hq.c3d2.de" = "00:23:c3:d2:75:18";
"drucker.hq.c3d2.de" = "00:23:c3:d2:12:0f";
"knot.hq.c3d2.de" = "52:54:cf:fd:ce:3f";
"bender.hq.c3de.de" = "00:23:df:7e:c8:0a";
"sofafon.hq.c3d2.de" = "b8:27:eb:23:8d:01";
"schalter.hq.c3d2.de" = "b8:27:eb:4c:be:ff";
"beere.hq.c3d2.de" = "b8:27:eb:ac:65:d2";
"ledball1.hq.c3d2.de" = "b8:27:eb:53:0b:27";
"cider.hq.c3d2.de" = "00:0d:93:75:ee:fa";
"semanta.hq.c3d2.de" = "00:ff:e4:bb:ea:2a";
"leviathan.hq.c3d2.de" = "00:ff:08:31:db:e5";
"beere2.hq.c3d2.de" = "b8:27:eb:53:0b:27";
"feile.hq.c3d2.de" = "aa:00:5b:12:c1:f7";
"matemat.hq.c3d2.de" = "a2:1b:7c:e8:19:72";
"172.22.99.98" = "08:00:27:aa:90:e2";
"172.22.99.96" = "08:00:27:bb:8c:b3";
"batman.hq.c3d2.de" = "5c:cf:7f:c0:05:28";
"monit.hq.c3d2.de" = "00:23:ae:94:e7:19";
"storage2.hq.c3d2.de" = "42:5e:0f:4e:f3:cc";
"server2.hq.c3d2.de" = "d0:67:e5:f3:57:10";
"server3.hq.c3d2.de" = "e4:1f:13:2e:4f:c0";
"server4.hq.c3d2.de" = "00:9c:02:a9:26:01";
"minecraft.hq.c3d2.de" = "4a:57:d3:64:fe:e9";
"ustriper.hq.c3d2.de" = "aa:bb:95:33:bb:aa";
"lisbeth.hq.c3d2.de" = "b8:27:eb:a5:ee:5c";
"ruststripe1.hq.c3d2.de" = "06:32:0e:39:21:69";
"fhem.hq.c3d2.de" = "b8:27:eb:9e:8b:db";
"glotzbert.hq.c3d2.de" = "ec:a8:6b:fe:b4:cb";
"pulsebert.hq.c3d2.de" = "b8:27:eb:16:31:61";
"dacbert.hq.c3d2.de" = "dc:a6:32:31:b6:32";
"public-access-proxy.hq.c3d2.de" = "12:24:5f:bd:9b:e7";
"marenz-build.hq.c3d2.de" = "44:1e:a1:59:2e:e8";
"ledbeere.hq.c3d2.de" = "b8:27:eb:60:99:59";
};
time = 86400;
max-time = 30 * 86400;
};
}
# net priv* settings
(
builtins.mapAttrs (netName: _: {
dynamicDomain = true;
}) (
lib.filterAttrs (netName: _:
builtins.match "priv[[:digit:]]+" netName != null
) pillar.hosts-inet
)
)
] ++ (
map (ctx:
builtins.mapAttrs (_: subnet: { subnets6.${ctx} = subnet; }) pillar.subnets-inet6.${ctx}
) (builtins.attrNames pillar.subnets-inet6)
) ++ (
map (ctx:
builtins.mapAttrs (_: subnet: { hosts6.${ctx} = subnet; }) pillar.hosts-inet6.${ctx}
) (builtins.attrNames pillar.hosts-inet6)
));
config.site.hosts = lib.mkMerge (
[
{ # Static definitions
mgmt-gw.firewall.enable = true;
priv13-gw.firewall.enable = true;
dns.services.dns.enable = true;
dnscache = {
role = "container";
interfaces.serv = {
gw4 = "serv-gw";
gw6 = "serv-gw";
type = "veth";
};
services.dnscache.enable = true;
};
upstream1.interfaces.up1.upstream = {
provider = "vodafone";
staticIpv4Address = "24.134.104.53";
noNat.subnets6 = [
"2a02:8106:208:5200::/56"
];
};
upstream2.interfaces.up2.upstream = {
provider = "vodafone";
noNat.subnets6 = [
"2a02:8106:208:e900::/56"
];
};
upstream3.interfaces.up3.upstream.provider = "starlink";
upstream4.interfaces.up4-pppoe = {
type = "pppoe";
upstream = {
provider = "dsi";
link = "up4";
staticIpv4Address = "81.201.149.152";
upBandwidth = 98000;
noNat.subnets6 = [
"2a00:8180:2000:37::1/128"
"2a00:8180:2c00:200::/56"
];
};
};
upstream1.ospf.upstreamInstance = 3;
upstream2.ospf.upstreamInstance = 4;
anon1.ospf.upstreamInstance = 5;
freifunk.ospf.upstreamInstance = 6;
upstream3.ospf.upstreamInstance = 7;
upstream4.ospf.upstreamInstance = 8;
c3d2-gw1.ospf.allowedUpstreams = [ "upstream3" "upstream4" "upstream1" "anon1" "freifunk" ];
c3d2-gw2.ospf.allowedUpstreams = [ "upstream1" "upstream3" "upstream4" "anon1" "freifunk" ];
c3d2-gw3.ospf.allowedUpstreams = [ "upstream4" "upstream3" "upstream1" "anon1" "freifunk" ];
serv-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ];
cls-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ];
mgmt-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ];
bgp.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ];
anon1.ospf.allowedUpstreams = [ "upstream1" "upstream3" "upstream4" "freifunk" ];
priv17-gw-up3.ospf.allowedUpstreams = [ "upstream3" "upstream4" "upstream1" "anon1" "freifunk" ];
pub-gw.ospf.allowedUpstreams = [ "anon1" "freifunk" ];
c3d2-anon.ospf.allowedUpstreams = [ "anon1" "freifunk" ];
upstream4.forwardPorts = [
{
destination = config.site.net.serv.hosts4.public-access-proxy;
proto = "tcp";
sourcePort = 80;
}
{
destination = config.site.net.serv.hosts4.public-access-proxy;
proto = "tcp";
sourcePort = 443;
}
{
destination = config.site.net.serv.hosts4.bind;
proto = "tcp";
sourcePort = 53;
reflect = false;
}
{
destination = config.site.net.serv.hosts4.bind;
proto = "udp";
sourcePort = 53;
reflect = false;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 2325;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 2399;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 2327;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 2338;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 2339;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 40533;
}
{
destination = config.site.net.c3d2.hosts4.dn42;
proto = "udp";
sourcePort = 61699;
}
{
destination = "${config.site.net.serv.hosts4.leonos}:22";
proto = "tcp";
sourcePort = 2223;
}
{
destination = config.site.net.serv.hosts4.minetest;
proto = "udp";
sourcePort = 30000;
}
{
destination = "172.22.99.175:22";
proto = "tcp";
sourcePort = 2224;
}
{ # Gitea ssh
destination = config.site.net.serv.hosts4.gitea;
proto = "tcp";
sourcePort = 22;
}
{ # Jabber C2S
destination = config.site.net.serv.hosts4.jabber;
proto = "tcp";
sourcePort = 5222;
}
{ # Jabber C2S+SSL
destination = config.site.net.serv.hosts4.jabber;
proto = "tcp";
sourcePort = 5223;
}
{ # Jabber S2S
destination = config.site.net.serv.hosts4.jabber;
proto = "tcp";
sourcePort = 5269;
}
{ # Jabber TURN
destination = config.site.net.serv.hosts4.jabber;
proto = "tcp";
sourcePort = 3478;
}
{ # Jabber TURN
destination = config.site.net.serv.hosts4.jabber;
proto = "tcp";
sourcePort = 3479;
}
{ # Jabber TURN
destination = config.site.net.serv.hosts4.jabber;
proto = "udp";
sourcePort = 3478;
}
{ # Jabber TURN
destination = config.site.net.serv.hosts4.jabber;
proto = "udp";
sourcePort = 3479;
}
{
destination = "${config.site.net.serv.hosts4.vps1}:22";
proto = "tcp";
sourcePort = 2225;
}
] ++ map (port: {
destination = config.site.net.serv.hosts4.mail;
proto = "tcp";
sourcePort = port;
}) [ 25 465 587 110 143 993 995 ];
}
# host priv*-gw settings
(
builtins.mapAttrs (hostName: _: {
ospf.allowedUpstreams = [ "upstream4" "upstream3" "upstream1" "anon1" "freifunk" ];
}) (
lib.filterAttrs (hostName: _:
builtins.match "priv[[:digit:]]+-gw" hostName != null
) pillar.containers
)
)
(builtins.foldl' (result: hostName: result // {
"${hostName}" = {
role = "server";
interfaces = builtins.mapAttrs (net: _: {
type = "phys";
} // lib.optionalAttrs (net == "cluster") {
gw4 = "cls-gw";
gw6 = "cls-gw";
}) (
lib.filterAttrs (_: hosts: hosts ? ${hostName}) (
pillar.hosts-inet // (
builtins.foldl' (result: hosts: result // hosts) {} (builtins.attrValues pillar.hosts-inet6)
)
)
);
};
}) {} mainServers)
(builtins.mapAttrs (_: switch: {
inherit (switch) model location password;
role = "switch";
}) pillar.switches)
(builtins.mapAttrs (_: ap: {
inherit (ap) model location password;
role = "ap";
}) pillar.cpe)
(builtins.mapAttrs (name: container:
let
ctPillar = self.lib.saltPillarFor name;
in {
role = "container";
interfaces =
builtins.mapAttrs (net: interface:
renameAttr "gw" "gw4"
(forceVeth interface) // (
if ctPillar ? upstream &&
ctPillar.upstream.interface == net
then {
upstream.upBandwidth = ctPillar.upstream.up-bandwidth;
}
else {}
)
) container.interfaces;
wireguard =
lib.optionalAttrs (ctPillar ? wireguard-instances) (
builtins.mapAttrs (net: wgData: {
inherit (builtins.head wgData.peers) endpoint;
publicKey = (builtins.head wgData.peers).public_key;
privateKey = wgData.private_key;
addresses = builtins.filter builtins.isString (
builtins.split "[, ]+" wgData.addr
);
upBandwidth = ctPillar.upstream.up-bandwidth;
}) ctPillar.wireguard-instances);
ospf =
let
ospfConf = ctPillar.ospf;
in lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet) {
stubNets4 = ospfConf.stubnets-inet;
} // lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet6) {
stubNets6 = ospfConf.stubnets-inet6;
};
bgp =
if ctPillar ? bgp
then
let
bgpConf = ctPillar.bgp;
in {
inherit (bgpConf) asn;
peers = bgpConf.peers-inet // bgpConf.peers-inet6;
}
else null;
forwardPorts =
if ctPillar ? port-forwarding
then map ({ proto, port, to }: {
proto = proto;
sourcePort = port;
destination = to;
}) ctPillar.port-forwarding
else [];
}) pillar.containers)
] ++
(map (net:
builtins.mapAttrs (_: addr4: {
}) pillar.hosts-inet.${net}
) (builtins.attrNames pillar.hosts-inet)) ++
(builtins.concatMap (ctx:
map (net:
builtins.mapAttrs (_: addr6: {
}) pillar.hosts-inet6.${ctx}.${net}
) (builtins.attrNames pillar.hosts-inet6.${ctx})
) (builtins.attrNames pillar.hosts-inet6))
);
}

View File

@ -2,71 +2,6 @@
with lib;
let
# A host needs to know a network if it
# - is a configured interface, or
# - is required behind at least two different links
getHostLinkNetworks = hostName: link:
let
hostConfig = config.site.hosts.${hostName};
# all the host's links
hostLinkNetworks = builtins.mapAttrs (link: _:
networksBehindLink hostName link
) hostConfig.links;
# how many links have a net in networksBehindLink
networkLinkCount = net:
builtins.length (
builtins.filter (builtins.elem net)
(builtins.attrValues hostLinkNetworks)
);
in
# access port
if config.site.net ? ${link}
then [ link ]
# multiple vlans on this link
else
builtins.filter (net:
# this port and local interface
hostConfig.interfaces ? ${net}
||
# this port and another
networkLinkCount net > 1
) hostLinkNetworks.${link};
networksBehindLink = hostName: name:
networksBehindLink' {
"${hostName}" = true;
} [ name ];
networksBehindLink' = seen: links:
if links == []
then
# Done, result is the seen link names that are networks:
builtins.filter (name:
config.site.net ? ${name}
) (builtins.attrNames seen)
else
let
link = builtins.head links;
seen' = seen // {
"${link}" = true;
};
onlyUnseen = builtins.filter (link: ! seen' ? ${link});
links' = builtins.tail links;
in
if config.site.hosts ? ${link}
then networksBehindLink' seen' (builtins.filter (link: ! seen' ? ${link}) (
links' ++ (
builtins.attrNames config.site.hosts.${link}.interfaces
) ++ (
onlyUnseen (builtins.attrNames config.site.hosts.${link}.links)
)
))
else if config.site.net ? ${link}
then networksBehindLink' seen' links'
else throw "Link to invalid target ${link}";
dhcpOpts = {
start = mkOption {
description = "First IP address in pool";
@ -102,7 +37,7 @@ let
options = {
vlan = mkOption {
description = "VLAN tag number";
type = with types; nullOr int;
type = types.int;
};
subnet4 = mkOption {
description = "v.w.x.y/z";
@ -119,7 +54,6 @@ let
if subnet4 != null && builtins.length s == 2
then builtins.head s
else null;
description = "Don't set, use subnet4 instead!";
};
subnet4Len = mkOption {
type = with types; nullOr types.int;
@ -131,7 +65,6 @@ let
if subnet4 != null && builtins.length s == 2
then lib.toInt (elemAt s 1)
else null;
description = "Don't set, use subnet4 instead!";
};
subnets6 = mkOption {
description = "IPv6 subnets w/o prefixlen (always 64)";
@ -158,49 +91,16 @@ let
type = with types; nullOr (submodule { options = dhcpOpts; });
default = null;
};
ipv6Router = mkOption {
description = "Who sends router advertisements?";
type = with types; nullOr str;
default = config.site.net.${name}.dhcp.router or null;
};
domainName = mkOption {
description = "Domain name option";
type = types.str;
default = "${name}.zentralwerk.org";
};
extraRecords = mkOption {
type = with types; listOf (submodule {
options = {
name = mkOption {
type = str;
};
type = mkOption {
type = enum [ "A" "AAAA" "MX" "SRV" "CNAME" "TXT" ];
};
data = mkOption {
type = oneOf [ str (attrsOf (oneOf [ int str ])) ];
};
};
});
default = [];
description = "Extraneous DNS records";
default = "${name}.zentralwerk.dn42";
};
dynamicDomain = mkOption {
type = types.bool;
default = false;
description = "Domain updated by DHCP server?";
};
mtu = mkOption {
type = with types; nullOr int;
default = null;
};
wifi.ieee80211rKey = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Key between WiFi access points for Fast Transition
'';
};
};
};
@ -221,27 +121,14 @@ let
type = with types; nullOr int;
default = null;
};
noNat.subnets4 = mkOption {
type = with types; listOf str;
default = [];
description = "Do not NAT traffic from these public static subnets";
};
noNat.subnets6 = mkOption {
type = with types; listOf str;
default = [];
description = "Do not NAT66 traffic from these public static subnets";
};
user = mkOption {
type = with types; nullOr str;
default = null;
};
password = mkOption {
type = with types; nullOr str;
default = null;
};
};
interfaceOpts = { ... }: {
interfaceOpts = { name, ... }: {
options = {
hwaddr = mkOption {
type = with types; nullOr str;
@ -249,7 +136,7 @@ let
description = "Static MAC address";
};
type = mkOption {
type = types.enum [ "phys" "veth" "pppoe" "bridge" "wireguard" "vxlan" ];
type = types.enum [ "phys" "veth" "pppoe" ];
description = ''
veth: Virtual ethernet to be attached to a bridge.
@ -273,35 +160,6 @@ let
default = null;
description = "Upstream interface configuration";
};
wireguard = mkOption {
default = null;
type = with types; nullOr (submodule {
options = {
endpoint = mkOption {
type = str;
};
publicKey = mkOption {
type = str;
};
privateKey = mkOption {
type = str;
};
addresses = mkOption {
type = listOf str;
};
};
});
};
vxlan = mkOption {
default = null;
type = with types; nullOr (submodule {
options = {
peer = mkOption {
type = str;
};
};
});
};
};
};
@ -314,13 +172,6 @@ let
Include the container system in the server's `build-container` script.
'';
};
firstboot = mkOption {
type = types.bool;
default = false;
description = ''
true if host is a newly flashed OpenWRT device with a default address
'';
};
role = mkOption {
type = types.enum [ "ap" "switch" "server" "container" "client" ];
default = "client";
@ -360,8 +211,7 @@ let
# isRouter = Part of the core network?
default =
config.site.hosts.${name}.interfaces ? core &&
config.site.net.core.hosts4 ? ${name} &&
config.site.hosts.${name}.role == "container";
config.site.net.core.hosts4 ? ${name};
description = "Should this host route?";
};
firewall.enable = mkOption {
@ -412,16 +262,35 @@ let
default = [];
description = "Accept default routes from these OSPF routers, in order of preference";
};
ospf.allowedUpstreams6 = mkOption {
type = with types; listOf str;
default = config.site.hosts.${name}.ospf.allowedUpstreams;
description = "Accept IPv6 default routes from these OSPF3 routers, in order of preference";
};
ospf.upstreamInstance = mkOption {
type = with types; nullOr int;
default = null;
description = "OSPF instance for advertising the default route";
};
wireguard = mkOption {
default = {};
type = with types; attrsOf (submodule (
{ name, ... }: {
options = {
endpoint = mkOption {
type = str;
};
publicKey = mkOption {
type = str;
};
privateKey = mkOption {
type = str;
};
addresses = mkOption {
type = listOf str;
};
upBandwidth = mkOption {
type = with types; nullOr int;
};
};
}
));
};
bgp = mkOption {
default = null;
type = with types; nullOr (submodule {
@ -438,81 +307,6 @@ let
type = types.bool;
default = false;
};
links = mkOption {
description = "Which port is connected to what other device? Keys are either network names or known hostnames.";
default = {};
type = with types; attrsOf (submodule (linkOpts name));
};
wifi = mkOption {
default = {};
type = with types; attrsOf (submodule (
{ config, ... }: {
options = {
band = mkOption {
type = enum [ "2g" "5g" ];
default =
if config.channel >= 1 && config.channel <= 14
then "2g"
else if config.channel >= 32 && config.channel <= 177
then "5g"
else throw "What band is channel ${toString config.channel}?";
};
htmode = mkOption {
type = enum [ "HT20" "HT40-" "HT40+" "HT40" "VHT80" ];
};
channel = mkOption {
type = int;
};
ssids = mkOption {
type = attrsOf (submodule ({ config, ... }: {
options = {
net = mkOption {
type = str;
};
psk = mkOption {
type = nullOr str;
default = null;
};
hidden = mkOption {
type = bool;
default = false;
};
encryption = mkOption {
type = enum [ "none" "owe" "wpa2" "wpa3" ];
default =
if config.psk == null
then "none"
else "wpa3";
};
mode = mkOption {
type = enum [ "ap" "sta" ];
default = "ap";
};
ifname = mkOption {
type = nullOr str;
default = null;
};
disassocLowAck = mkOption {
type = bool;
default = true;
description = ''
Disable for wireless bridges.
'';
};
};
}));
};
};
}
));
};
wifiOnLink.enable = mkOption {
type = types.bool;
default = true;
description = ''
Install the wifi-on-link.sh script on OpenWRT devices.
'';
};
};
};
@ -521,7 +315,7 @@ let
type = types.int;
};
peers = mkOption {
type = with types; attrsOf (submodule ({ ... }: {
type = with types; attrsOf (submodule ({ name, ... }: {
options = {
asn = mkOption {
type = types.int;
@ -531,70 +325,6 @@ let
default = {};
};
};
linkOpts = hostName: { name, ... }: {
options = {
ports = mkOption {
type = with types; listOf str;
description = "Port names";
};
group = mkOption {
type = with types; nullOr str;
default = null;
description = "Link aggregation group with a fixed number";
};
nets = mkOption {
type = with types; listOf str;
description = "Automatically generated";
default = getHostLinkNetworks hostName name;
};
vlans = mkOption {
type = with types; listOf int;
description = "Automatically generated, do not set";
default = builtins.concatMap (net:
let
inherit (config.site.net.${net}) vlan;
in if vlan != null
then [ vlan ]
else []
) config.site.hosts.${hostName}.links.${name}.nets;
};
trunk = mkOption {
type = types.bool;
description = "Trunk with tagged VLANs?";
default =
if config.site.net ? ${name}
then false
else if config.site.hosts ? ${name}
then true
else throw "Invalid link target: \"${name}\"";
};
};
};
vpnOpts = {
privateKey = mkOption {
type = types.str;
};
port = mkOption {
type = types.int;
default = 1337;
};
peers = mkOption {
type = with types; listOf (submodule {
options = {
publicKey = mkOption {
type = str;
};
allowedIPs = mkOption {
type = listOf str;
};
};
});
};
};
in
{
options.site = {
@ -603,38 +333,11 @@ in
default = {};
type = with types; attrsOf (submodule netOpts);
};
hosts = mkOption {
description = "All the static hosts";
default = {};
type = with types; attrsOf (submodule hostOpts);
};
sshPubKeys = mkOption {
type = with types; listOf str;
};
dyndnsKey = mkOption {
type = types.str;
};
vpn.wireguard = vpnOpts;
mqttServer = {
host = mkOption {
type = types.str;
default = config.site.net.serv.hosts4.broker;
};
user = mkOption {
type = types.str;
default = "user";
};
password = mkOption {
type = types.str;
default = "secret";
};
};
};
config.warnings =
@ -669,136 +372,8 @@ in
else []
) (builtins.attrNames config.site.hosts);
in
(reportCollisions "VLAN tag" (x: lib.optional (x.vlan != null) x.vlan) config.site.net) ++
(reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++
(reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++
(reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net) ++
ospfUpstreamXorGw;
config.assertions =
# Duplicate host/net name check
map (name: {
assertion = ! config.site.net ? ${name};
message = "Host \"${name}\" must be named differently if net \"${name}\" exists.";
}) (builtins.attrNames config.site.hosts)
++
# Duplicate address check
(let
addrHosts =
builtins.foldl' (result: { hosts4, ... }:
builtins.foldl' (result: host:
let
addr = hosts4.${host};
in
if result ? ${addr}
then result // {
"${addr}" = result.${addr} ++ [ host ];
}
else result // {
"${addr}" = [ host ];
}
) result (builtins.attrNames hosts4)
) {} (builtins.attrValues config.site.net)
//
builtins.foldl' (result: net:
builtins.foldl' (result: ctx:
builtins.foldl' (result: host:
let
addr = config.site.net.${net}.hosts6.${ctx}.${host};
in
if result ? ${addr}
then result // {
"${addr}" = result.${addr} ++ [ host ];
}
else result // {
"${addr}" = [ host ];
}
) result (builtins.attrNames config.site.net.${net}.hosts6.${ctx})
) result (builtins.attrNames config.site.net.${net}.hosts6)
) {} (builtins.attrNames config.site.net);
in map (addr: {
assertion = builtins.length addrHosts.${addr} == 1;
message = "Address ${addr} is assigned to more than one host: ${lib.concatStringsSep " " addrHosts.${addr}}";
}) (builtins.attrNames addrHosts))
++
# duplicate vlan check
(let
vlanNets =
builtins.foldl' (result: net:
let
vlan = toString config.site.net.${net}.vlan;
in
if config.site.net.${net}.vlan != null && result ? ${vlan}
then result // {
"${vlan}" = result.${vlan} ++ [ net ];
}
else result // {
"${vlan}" = [ net ];
}
) {} (builtins.attrNames config.site.net);
in map (vlan: {
assertion = builtins.length vlanNets.${vlan} == 1;
message = "VLAN ${vlan} is used by more than one network: ${lib.concatStringsSep " " vlanNets.${vlan}}";
}) (builtins.attrNames vlanNets))
++
# Duplicate switch port check
builtins.concatMap (hostName:
let
ports = lib.unique (
builtins.concatMap (linkName:
config.site.hosts.${hostName}.links.${linkName}.ports
) (builtins.attrNames config.site.hosts.${hostName}.links)
);
linksOfPort = port:
builtins.attrNames (
lib.filterAttrs (_: { ports, ... }: builtins.elem port ports)
config.site.hosts.${hostName}.links
);
in map (port: {
assertion = builtins.length (linksOfPort port) == 1;
message = "${hostName}: port ${port} is used in more than one link: ${lib.concatStringsSep " " (linksOfPort port)}";
}) ports
) (builtins.attrNames config.site.hosts)
++
# Duplicate switch port group check
builtins.concatMap (hostName:
let
groups = lib.unique (
builtins.filter builtins.isString (
builtins.map (linkName:
config.site.hosts.${hostName}.links.${linkName}.group
) (builtins.attrNames config.site.hosts.${hostName}.links)
)
);
linksOfGroup = wantedGroup:
builtins.attrNames (
lib.filterAttrs (_: { group, ... }: group == wantedGroup)
config.site.hosts.${hostName}.links
);
in map (group: {
assertion = builtins.length (linksOfGroup group) == 1;
message = "${hostName}: group ${group} is used in more than one link: ${lib.concatStringsSep " " (linksOfGroup group)}";
}) groups
) (builtins.attrNames config.site.hosts)
++
# wifi psk checks
builtins.concatMap (hostName:
builtins.concatMap (wifiPath:
map (ssid:
let
ssidConf = config.site.hosts.${hostName}.wifi.${wifiPath}.ssids.${ssid};
in
if builtins.elem ssidConf.encryption [ "none" "owe" ]
then {
assertion = ssidConf.psk == null;
message = "${hostName}: SSID ${ssid} has encryption ${ssidConf.encryption} but a PSK is set";
}
else if builtins.elem ssidConf.encryption [ "wpa2" "wpa3" ]
then {
assertion = ssidConf.psk != null;
message = "${hostName}: SSID ${ssid} has encryption ${ssidConf.encryption} but no PSK is set";
}
else throw "Unsupported WiFi encryption ${ssidConf.encryption}"
) (builtins.attrNames config.site.hosts.${hostName}.wifi.${wifiPath}.ssids)
) (builtins.attrNames config.site.hosts.${hostName}.wifi)
) (builtins.attrNames config.site.hosts);
}

View File

@ -1,32 +1,17 @@
{ self, lib, openwrt, pkgs }:
{ self, gpgKey, pkgs }:
rec {
inherit (import ./config { inherit self pkgs; }) config;
config = import ./config { inherit self pkgs gpgKey; };
saltPillarFor = import ./salt-support/salt-pillar.nix {
inherit pkgs gpgKey;
};
expandSaltTemplate = import ./salt-support/expand-template.nix { inherit pkgs; };
netmasks = import ./netmasks.nix;
subnet = import ./subnet { inherit pkgs; };
dns = import ./dns.nix { inherit config lib; };
openwrtModels = import ./openwrt-models.nix { inherit self openwrt; };
getOpenwrtModel = wantedModel:
let
models =
builtins.filter ({ models, ... }:
self.lib.any ({ model, vendor, ... }:
model == wantedModel ||
"${vendor}_${model}" == wantedModel
) models
) openwrtModels;
result =
builtins.foldl' (result: { data, ... }:
self.lib.recursiveUpdate result data
) {} models;
in
if builtins.length models > 0
then result
else builtins.trace "No data found for OpenWRT model ${wantedModel}"
{};
dns = import ./dns.nix { inherit pkgs config; };
}

View File

@ -1,18 +1,15 @@
{ config, lib }:
{ pkgs, config }:
let
lib = pkgs.lib;
in
rec {
ns = "dns.serv.zentralwerk.org";
internalNS = [ ns ];
# public servers (slaves)
publicNS = [
"ns.c3d2.de"
"ns.spaceboyz.net"
"ns1.supersandro.de"
];
publicIPv4 = config.site.hosts.upstream4.interfaces.up4-pppoe.upstream.staticIpv4Address;
dynamicReverseZones4 = [
publicNS = [ "ns.c3d2.de" "ns.spaceboyz.net" ];
dynamicReverseZones = [
"73.20.172.in-addr.arpa"
"74.20.172.in-addr.arpa"
"75.20.172.in-addr.arpa"
@ -21,25 +18,7 @@ rec {
"78.20.172.in-addr.arpa"
"79.20.172.in-addr.arpa"
"99.22.172.in-addr.arpa"
"22.10.in-addr.arpa"
];
dynamicReverseZones6 = [
"2.0.0.0.c.2.0.8.1.8.0.0.a.2.ip6.arpa"
"4.1.b.a.c.a.2.8.3.5.f.0.a.2.ip6.arpa"
"5.0.2.d.3.c.2.4.0.0.3.2.d.f.ip6.arpa"
];
mapI = start: end: f:
if start >= end
then []
else [ (f start) ] ++ mapI (start + 1) end f;
isRfc1918Reverse = reverse:
builtins.any (suffix: lib.hasSuffix suffix reverse) ([
"10.in-addr.arpa"
"168.192.in-addr.arpa"
] ++ mapI 0 32 (i:
"${toString (16 + i)}.172.in-addr.arpa"
));
localZones =
let
@ -64,9 +43,8 @@ rec {
);
# generate zones only for nets with hosts
namedNets = lib.filterAttrs (_name: { hosts4, hosts6, dynamicDomain, ... }:
hosts4 != {} ||
hosts6 != {} ||
namedNets = lib.filterAttrs (name: { hosts4, hosts6, dynamicDomain, ... }:
(hosts4 != [] && hosts6 != []) ||
dynamicDomain
) config.site.net;
@ -99,7 +77,7 @@ rec {
"${zone}" = true;
}
) {} (builtins.attrNames reverseHosts4)
) ++ dynamicReverseZones4
) ++ dynamicReverseZones
);
# turns `::` into `0000:0000:0000:0000:0000:0000:0000:0000`
@ -149,8 +127,10 @@ rec {
let
domain =
if ctx == "dn42"
then "${net}.zentralwerk.dn42"
else namedNets.${net}.domainName;
then namedNets.${net}.domainName
else if builtins.match "up.*" ctx != null
then "${net}.zentralwerk.org"
else throw "Invalid IPv6 context: ${ctx}";
in
lib.recursiveUpdate result {
"${ipv6ToReverse hosts.${host}}" = "${host}.${domain}";
@ -160,79 +140,60 @@ rec {
)) {} (builtins.attrNames namedNets);
# `{ dn42 = [ "....ip6.arpa" ]; }`
reverseZones6 = builtins.mapAttrs (_ctx: reverseHosts6ctx:
reverseZones6 = builtins.mapAttrs (ctx: reverseHosts6ctx:
builtins.attrNames (
builtins.foldl' (result: rname:
result // {
"${builtins.substring ((128 - reverseZone6Size) / 2) (72 - ((128 - reverseZone6Size) / 2)) rname}" = true;
}) {} (builtins.attrNames reverseHosts6ctx)
)
builtins.foldl' (result: rname: result // {
"${builtins.substring ((128 - reverseZone6Size) / 2) (72 - ((128 - reverseZone6Size) / 2)) rname}" = true;
}) {} (builtins.attrNames reverseHosts6ctx)
)
) reverseHosts6;
in [ {
name = "zentralwerk.org";
ns = publicNS;
records = [ {
name = "@";
type = "A";
data = publicIPv4;
} {
name = "www";
type = "A";
data = publicIPv4;
} {
name = "@";
type = "AAAA";
data = config.site.net.serv.hosts6.up4.network-homepage;
} {
name = "www";
type = "AAAA";
data = config.site.net.serv.hosts6.up4.network-homepage;
} ];
records = [];
} {
name = "zentralwerk.dn42";
ns = internalNS;
records = [ ];
records = [ {
name = "ipa";
type = "A";
data = config.site.net.serv.hosts4.ipa;
} ];
} {
name = "dyn.zentralwerk.org";
ns = publicNS;
records = [ {
name = "upstream4";
name = "upstream1";
type = "A";
data = publicIPv4;
data = "24.134.104.53";
} {
name = "upstream2";
type = "A";
data = "24.134.252.105";
} ];
} ]
++
builtins.concatLists (
} ] ++ builtins.concatLists (
builtins.attrValues (
builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, extraRecords, ... }: [
builtins.mapAttrs (net: { dynamicDomain, hosts4, hosts6, ... }: [
{
name = "${net}.zentralwerk.dn42";
ns = internalNS;
records =
lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42) ++
extraRecords;
lib.optionals (hosts6 ? dn42) (hosts6Records hosts6.dn42);
}
{
name = "${net}.zentralwerk.org";
ns = publicNS;
records =
hosts4Records hosts4 ++
lib.optionals (hosts6 ? up4) (hosts6Records hosts6.up4) ++
lib.optionals (hosts6 ? flpk) (hosts6Records hosts6.flpk) ++
extraRecords;
lib.optionals (hosts6 ? up4) (hosts6Records hosts6.up4);
dynamic = dynamicDomain;
}
]) namedNets
)
)
++
map (zone: {
) ++ map (zone: {
name = zone;
ns =
if isRfc1918Reverse zone
then internalNS
else publicNS;
ns = internalNS;
records =
map (reverse: {
name = builtins.head (
@ -244,16 +205,17 @@ rec {
builtins.filter (lib.hasSuffix ".${zone}")
(builtins.attrNames reverseHosts4)
);
dynamic = builtins.elem zone dynamicReverseZones4;
dynamic = builtins.elem zone dynamicReverseZones;
}) reverseZones4
++
builtins.concatMap (ctx:
++ builtins.concatMap (ctx:
map (zone: {
name = zone;
ns =
if ctx == "dn42"
then internalNS
else publicNS;
else if builtins.match "up.*" ctx != null
then publicNS
else throw "Invalid IPv6 context: ${ctx}";
records =
map (reverse: {
name = builtins.substring 0 ((128 - reverseZone6Size) / 2 - 1) reverse;
@ -263,7 +225,6 @@ rec {
builtins.filter (lib.hasSuffix ".${zone}")
(builtins.attrNames reverseHosts6.${ctx})
);
dynamic = builtins.elem zone dynamicReverseZones6;
}) reverseZones6.${ctx}
) (builtins.attrNames reverseZones6);
}

View File

@ -1,166 +0,0 @@
{ self, openwrt }:
let
# the files that contain port definitions
defFiles = builtins.filter
(self.lib.hasSuffix "/etc/board.d/02_network")
(self.lib.filesystem.listFilesRecursive "${openwrt}/target/linux");
# files contents as one string
defSource = builtins.concatStringsSep "\n" (
map builtins.readFile defFiles
);
defSourceLines = builtins.filter (s: s != []) (
builtins.split "\n" (
builtins.replaceStrings
[ "\\\n" ] [ "" ] defSource
));
parseCommand = line:
let
tokens =
builtins.concatMap (frag:
if builtins.isString frag
then builtins.split "[[:space:]]+" frag
else frag
) (
builtins.split "\"([^\"]*)\"" line
);
words =
builtins.filter (word:
word != [] && word != ""
) tokens;
command =
if words != []
then builtins.head words
else "-";
args =
if words != []
then builtins.tail words
else [];
makeLinkFromArg = port: arg:
builtins.foldl' (result: interface:
if port != []
then result // {
"${port}" = {
type = "phys";
inherit interface port;
};
}
else result
) {} (builtins.split "[[:space:]]+" arg);
commands = {
ucidef_add_switch.ports = builtins.foldl' (ports: arg:
let
switch = builtins.head args;
m1 = builtins.match "([[:digit:]]+):(.+)" arg;
m2 = builtins.match "([[:digit:]]+)([ut]?)@(.+)" arg;
m2flag = builtins.elemAt m2 1;
port = if m1 != null
then {
inherit switch;
type = "port";
index = builtins.elemAt m1 0;
port = builtins.elemAt m1 1;
}
else if m2 != null
then {
inherit switch;
type = "host";
index = builtins.elemAt m2 0;
interface = builtins.elemAt m2 2;
} // self.lib.optionalAttrs (m2flag == "u") {
only = "untagged";
} // self.lib.optionalAttrs (m2flag == "t") {
only = "tagged";
}
else throw "Unimplemented port scheme: ${arg}";
in if m1 != null || m2 != null
then ports // {
"${port.index}" = port;
}
else builtins.trace "Unimplemented port scheme: ${arg}" ports
) {} (builtins.tail args);
ucidef_set_interface_wan.ports = {
"${builtins.head args}" = {
type = "phys";
interface = builtins.head args;
port = "wan";
};
};
ucidef_set_interface_lan.ports =
makeLinkFromArg "lan" (builtins.elemAt args 0);
ucidef_set_interfaces_lan_wan.ports =
makeLinkFromArg "lan" (builtins.elemAt args 0) //
self.lib.optionalAttrs (builtins.length args > 1) (
makeLinkFromArg "wan" (builtins.elemAt args 1)
);
};
in
if commands ? ${command}
then commands.${command}
else {
unknown."${command}" = args;
};
in (
builtins.foldl' ({ state, result, models ? null, data ? {} }: line:
if state == "start"
then
if builtins.match "[[:space:]]*case \"?\\$board\"? in" line != null
then { state = "case"; inherit result; }
else { inherit state result; }
else if state == "case"
then
if builtins.match "[[:space:]]*esac" line != null
then { state = "start"; inherit result; }
else
let
m = builtins.match "[[:space:]]*(.+)\\)" line;
in
if m == null
then { inherit state result; }
else {
inherit result;
state = "model";
models =
builtins.filter (m: m != null) (
map (s:
let
m = builtins.split "," s;
in
if s != [] &&
m != null &&
builtins.length m == 3
then {
vendor = builtins.elemAt m 0;
model = builtins.elemAt m 2;
}
else null
) (
builtins.split "[[:space:]]*\\|[[:space:]]*" (
builtins.head m
)));
}
else if state == "model"
then
if builtins.match "[[:space:]]*;;" line != null
then {
state = "case";
result = result ++ [ {
inherit models data;
} ];
}
else {
inherit result state models;
data = self.lib.recursiveUpdate data (parseCommand line);
}
else throw "Invalid state ${state}"
) { state = "start"; result = []; } defSourceLines
).result

View File

@ -0,0 +1,23 @@
{ pkgs ? import <nixpkgs> {}
}:
name: template: data:
let
jsonFile =
builtins.toFile "data.json" (builtins.toJSON data);
j2custom =
builtins.toFile "j2custom.py" ''
def j2_environment(env):
env.globals.update(
zip=zip
)
return env
'';
in
pkgs.runCommandLocal name {
nativeBuildInputs = with pkgs; [
pythonPackages.j2cli yaml2json
];
} ''
j2 --customize ${j2custom} -f json ${template} ${jsonFile} > $out
''

View File

@ -0,0 +1,47 @@
{ pkgs ? import <nixpkgs> {}
, gpgKey
}:
with pkgs.lib;
let
loadYaml = import ./load-yaml.nix { inherit pkgs; };
decryptMessage = x:
if gpgKey == null
then "encrypted"
else
builtins.readFile (
pkgs.runCommandLocal "decrypted-salt-value" {
nativeBuildInputs = [ pkgs.gnupg ];
} ''
export GNUPGHOME=$(mktemp -d)
gpg --import ${gpgKey}
gpg -d > $out << EOF
${x}
EOF
''
);
decrypt = x:
if builtins.isString x
then if builtins.substring 0 27 x == "-----BEGIN PGP MESSAGE-----"
then decryptMessage x
else x
else if builtins.isList x
then map decrypt x
else if builtins.isAttrs x
then builtins.mapAttrs (_: decrypt) x
else x;
loadSls = files:
decrypt (
builtins.foldl' (result: filename:
recursiveUpdate result (loadYaml filename)
) {} files
);
in
files:
if builtins.isList files
then loadSls files
else loadSls [ files ]

View File

@ -0,0 +1,17 @@
{ pkgs ? import <nixpkgs> {}
}:
path:
let
json = pkgs.runCommandLocal "desalinated-${builtins.baseNameOf path}" {
nativeBuildInputs = with pkgs; [
pythonPackages.j2cli ruby yaml2json
];
} ''
j2 ${path} > expanded.yaml
yaml2json < expanded.yaml > $out
'';
in
builtins.fromJSON (
builtins.readFile json
)

View File

@ -0,0 +1,66 @@
{ pkgs ? import <nixpkgs> {}
, ...
}@args:
hostName:
let
loadSls = import ./load-sls.nix args;
pillarBase = (loadSls ../../../salt-pillar/top.sls).base;
globToRegex = builtins.replaceStrings ["*"] [".*"];
baseMatches =
if hostName == "*"
then
builtins.attrNames pillarBase
else
builtins.filter (patterns:
pkgs.lib.any (pattern:
builtins.match (globToRegex pattern) hostName != null
) (
builtins.filter builtins.isString (
builtins.split " or " patterns
)
)
) (builtins.attrNames pillarBase);
fileIds = builtins.foldl' (result: matchName:
result ++ pillarBase.${matchName}
) [] baseMatches;
allFilePaths = pkgs.lib.filesystem.listFilesRecursive ../../../salt-pillar;
files = map (fileId:
let
parts = builtins.filter builtins.isString (
builtins.split "\\." fileId
);
matches = builtins.filter (filePath:
let
suffix1 = builtins.concatStringsSep "/" (parts ++ [ "init.sls" ]);
suffix2 = (builtins.concatStringsSep "/" parts) + ".sls";
check = suffix:
endsWith suffix (builtins.toString filePath);
in
check suffix1 || check suffix2
) allFilePaths;
matchesLength = builtins.length matches;
in
if matchesLength == 0
then throw "No pillar file for ${fileId}"
else if matchesLength > 1
then throw "Ambiguous choice of files for ${fileId}"
else builtins.head matches
) fileIds;
endsWith = suffix: s:
let
suffixLen = builtins.stringLength suffix;
sLen = builtins.stringLength s;
in
builtins.substring (sLen - suffixLen) suffixLen s == suffix;
in
loadSls files

View File

@ -47,16 +47,7 @@ in
Forward true
Server "${config.site.net.serv.hosts4.spaceapi}" "${toString networkPort}"
Server "${config.site.net.serv.hosts4.grafana}" "${toString networkPort}"
Server "${config.site.net.serv.hosts4.prometheus}" "${toString networkPort}"
'';
plugins.mqtt = ''
<Publish "broker">
Host "${config.site.mqttServer.host}"
User "${config.site.mqttServer.user}"
Password "${config.site.mqttServer.password}"
ClientId "collectd-${hostName}"
</Publish>
'';
}) (lib.optionalAttrs (hostName != "stats") {
plugins.network = ''
Server "${config.site.net.serv.hosts6.dn42.stats}" "${toString networkPort}"
@ -90,7 +81,7 @@ in
Host "inbert.c3d2.de"
Host "heise.de"
'';
}) (lib.optionalAttrs config.services.kea.dhcp4.enable {
}) (lib.optionalAttrs config.services.dhcpd4.enable {
plugins.exec =
let
maxTimeout = builtins.foldl' (maxTimeout: net:
@ -104,8 +95,8 @@ in
else maxTimeout
) 180 (builtins.attrNames config.site.net);
in ''
Exec "${execUser}" "/run/wrappers/bin/collectd-dhcpcount" "${toString maxTimeout}"
'';
Exec "${execUser}" "${pkgs.ruby}/bin/ruby" "${./dhcpcount.rb}" "${toString maxTimeout}"
'';
}) (lib.optionalAttrs config.services.unbound.enable {
plugins.exec = ''
Exec "${execUser}" "${pkgs.ruby}/bin/ruby" "${./unbound.rb}"
@ -115,29 +106,4 @@ in
Exec "nobody" "${self.packages.${system}.starlink-stats}/bin/starlink-stats" "192.168.100.1:9200"
'';
}) ];
systemd.services.collectd = lib.mkIf config.services.kea.dhcp4.enable {
after = [ "kea-dhcp4-server.service" ];
};
security.wrappers = lib.mkIf config.services.kea.dhcp4.enable {
collectd-dhcpcount =
let
dhcpcount = pkgs.runCommand "dhcpcount" {
src = ./dhcpcount.rb;
buildInputs = [ pkgs.ruby ];
} ''
cp $src dhcpcount.rb
patchShebangs dhcpcount.rb
mkdir -p $out/bin
cp dhcpcount.rb $out/bin/dhcpcount
'';
in {
setuid = true;
owner = "root";
group = "root";
source = "${dhcpcount}/bin/dhcpcount";
};
};
}

38
nix/nixos-module/collectd/dhcpcount.rb Executable file → Normal file
View File

@ -1,28 +1,36 @@
#!/usr/bin/env ruby
require 'csv'
require 'date'
INTERVAL = 60
TIMEOUT = ARGV[0].to_i # TODO: now unused
hostname = CSV::readlines("/proc/sys/kernel/hostname").join.strip
INTERVAL = 10
TIMEOUT = ARGV[0].to_i
hostname = IO::readlines("/proc/sys/kernel/hostname").join.strip
STDOUT.sync = true
loop do
seen = {}
count = 0
now = Time.now.to_i
CSV::readlines("/var/lib/kea/kea-leases4.csv", headers: true).each do |rec|
h = rec.to_h
addr = h["hwaddr"]
next unless addr
last = h["expire"].to_i
elapsed = now - last
next if elapsed >= TIMEOUT
addr = nil
starts = nil
unless seen[addr]
count += 1
seen[addr] = true
IO::readlines("/var/lib/dhcp/dhcpd.leases").each do |line|
if line =~ /^lease (.+) \{/
addr = $1
starts = nil
elsif line =~ /starts \d+ (.+?);/
starts = DateTime.parse($1).to_time
elsif line =~ /^\}/
now = Time.now
if starts and
now >= starts and now < starts + TIMEOUT
unless seen[addr]
count += 1
seen[addr] = true
end
end
end
end
puts "PUTVAL \"#{hostname}/exec-dhcpd/current_sessions-leases\" interval=#{INTERVAL} N:#{count}"

0
nix/nixos-module/collectd/unbound.rb Executable file → Normal file
View File

View File

@ -1,11 +1,11 @@
# wireguard upstreams (like njalla and flpk, not like vpn dialup. see vpn-gw)
{ hostName, config, lib, pkgs, ... }:
let
gateway = "upstream1";
tunnels = lib.filterAttrs (_: { wireguard, ... }:
tunnels = lib.filterAttrs (_: wireguard:
wireguard != null
) config.site.hosts.${hostName}.interfaces;
) config.site.hosts.${hostName}.wireguard;
firstTunnel =
if builtins.length (builtins.attrNames tunnels) > 0
then builtins.head (builtins.attrNames tunnels)
@ -16,7 +16,7 @@ let
"/run/wireguard-keys/${ifName}.key";
wireguardMark = 3;
vpnTable = 100;
vpn4Table = 100;
in
{
systemd.services = builtins.foldl' (services: ifName: services // {
@ -33,7 +33,7 @@ in
chown systemd-network:systemd-network $(dirname $F)
rm -f $F
cat >$F <<EOF
${tunnels.${ifName}.wireguard.privateKey}
${tunnels.${ifName}.privateKey}
EOF
chmod 0400 $F
chown systemd-network:systemd-network $F
@ -45,13 +45,14 @@ in
pkgs.wireguard-tools
];
systemd.network.netdevs = builtins.mapAttrs (ifName: { wireguard, ... }: {
systemd.network.netdevs = builtins.mapAttrs (ifName: wireguard: {
netdevConfig = {
Name = ifName;
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = privateKeyFile ifName;
# Mark for routing with another routing table
FirewallMark = wireguardMark;
};
wireguardPeers = [ {
@ -67,13 +68,13 @@ in
# Wireguard transported through another routing table
# (containing upstream by bird ospf)
core.routingPolicyRules = [ {
# Marked wireguard packets take the vpn routing table
# Marked wireguard packets take the vpn4 routing table
routingPolicyRuleConfig = {
Table = vpn4Table;
FirewallMark = wireguardMark;
Table = vpnTable;
};
} ];
} // builtins.mapAttrs (ifName: { wireguard, upstream, ... }: {
} // builtins.mapAttrs (ifName: wireguard: {
# Wireguard interfaces
matchConfig.Name = ifName;
@ -83,17 +84,17 @@ in
# IPv4 default route
networkConfig.DefaultRouteOnDevice = true;
# IPv6 default route
routes = [ {
# IPv6 default route
routeConfig.Destination = "::/0";
} ];
extraConfig = lib.mkIf (upstream.upBandwidth != null) ''
extraConfig = ''
[CAKE]
Parent = root
# DOCSIS overhead
OverheadBytes = 18
Bandwidth = ${toString upstream.upBandwidth}K
Bandwidth = ${toString wireguard.upBandwidth}K
'';
}) tunnels);
@ -109,7 +110,7 @@ in
networking.iproute2 = lib.mkIf enabled {
enable = true;
rttablesExtraConfig = ''
${toString vpnTable} vpn
${toString vpn4Table} vpn4
'';
};

View File

@ -1,22 +1,24 @@
# Routing daemon configuration
{ hostName, config, lib, pkgs, ... }:
{ hostName, config, options, lib, pkgs, ... }:
let
hostNameEscaped = builtins.replaceStrings [ "-" ] [ "_" ] hostName;
hostConf = config.site.hosts.${hostName};
upstreamInterfaces = lib.filterAttrs (_: { upstream, ... }:
upstream != null
) hostConf.interfaces;
isUpstream =
builtins.match "upstream.*" hostName != null ||
builtins.match "anon.*" hostName != null;
isUpstream = upstreamInterfaces != {};
ipv6RouterNets = builtins.attrNames (
lib.filterAttrs (net: { ipv6Router, ... }:
ipv6Router == hostName
) config.site.net
);
# Configuring a gateway? If so, this is the associated net.
gatewayNet =
let
m = builtins.match "(.+)-gw" hostName;
in if hostName == "c3d2-gw3"
then "c3d2"
else if m == [ "cls" ]
then "cluster"
else if m == null
then null
else builtins.head m;
enumerate = n: list:
if list == []
@ -59,111 +61,76 @@ in
''
else ''
export all;
''}
''}
};
}
protocol device {
scan time 10;
}
# Import address ranges of upstream interfaces so that
# internal traffic to local public services take no detours
# if the default router takes another upstream gateway.
protocol direct {
ipv4 {
${if isUpstream
then ''
# No RFC1918, RFC6598
import where net !~ [ 100.64.0.0/10 ] && net !~ [ 10.0.0.0/8 ] && net !~ [ 172.16.0.0/12 ] && net !~ [ 192.168.0.0/16 ];
''
else ""}
};
ipv6;
interface ${lib.concatMapStringsSep ", " (iface:
''"${iface}"''
)(builtins.attrNames hostConf.interfaces)};
check link yes;
}
${lib.optionalString (
builtins.match "anon.*" hostName != null ||
hostName == "flpk-gw"
) ''
${lib.optionalString (builtins.match "anon.*" hostName != null) ''
# BIRD routing table for Wireguard transport
ipv4 table vpn_table;
ipv4 table vpn4_table;
# Kernel routing table for Wireguard transport
protocol kernel VPN {
# "vpn_table" configured on anon routers
protocol kernel VPN4 {
# "vpn4_table" configured on anon routers
kernel table 100;
ipv4 {
export all;
table vpn_table;
table vpn4_table;
};
}
''}
${lib.optionalString (ipv6RouterNets != []) ''
${lib.optionalString (gatewayNet != null) ''
# Router advertisements
protocol radv {
rdnss ${config.site.net.serv.hosts6.dn42.dnscache};
${lib.concatMapStrings (net: ''
interface "${net}" {
min ra interval 10;
max ra interval 60;
solicited ra unicast yes;
${if (config.site.net.${net}.dhcp.server or null) == null
then ''
# Do not use DHCP6.
managed no;
'' else ''
# Use DHCP6 for DynDNS.
managed yes;
''}
interface "${gatewayNet}" {
min ra interval 10;
max ra interval 60;
${builtins.concatStringsSep "\n" (
map (subnet6: ''
prefix ${subnet6} {
preferred lifetime 600;
valid lifetime 1800;
};
'') (builtins.attrValues config.site.net.${net}.subnets6)
)}
${builtins.concatStringsSep "\n" (
map (subnet6: ''
prefix ${subnet6} {
preferred lifetime 600;
valid lifetime 1800;
};
'') (builtins.attrValues config.site.net.${gatewayNet}.subnets6)
)}
dnssl "${config.site.net.${net}.domainName}";
};
'') ipv6RouterNets}
dnssl "${config.site.net.${gatewayNet}.domainName}";
};
}
''}
# OSPFv2 for site-local IPv4
protocol ospf v2 ZW4 {
ipv4 {
import all;
# OSPF is self-contained
export none;
export where net != 0.0.0.0/0 && source != RTS_BGP;
};
area 0 {
${builtins.concatStringsSep "\n" (
builtins.attrValues (
builtins.mapAttrs (net: _:
# Enable OSPF only on networks with a secret.
# Enable OSPF only on networks with a secret. Others
# are treated as a stubnet whose routes to
# advertise.
if config.site.net ? "${net}" && config.site.net.${net}.ospf.secret != null
then ''
interface "${net}" {
hello 10;
wait 20;
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
};
''
else ''
interface "${net}" {
stub yes;
cost 10;
};
else if config.site.net ? "${net}" && config.site.net.${net}.subnet4 != null
then ''
# Advertise route of network ${net}
stubnet ${config.site.net.${net}.subnet4} {};
''
else ""
) hostConf.interfaces
)
)}
@ -178,7 +145,7 @@ in
${lib.optionalString isUpstream ''
# OSPFv2 to advertise my default route
protocol ospf v2 ZW4_${hostNameEscaped} {
protocol ospf v2 ZW4_${hostName} {
ipv4 {
export where net = 0.0.0.0/0;
};
@ -191,8 +158,6 @@ in
interface "${net}" instance ${toString hostConf.ospf.upstreamInstance} {
# Become the designated router
priority 10;
hello 10;
wait 20;
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
@ -211,19 +176,14 @@ in
${text}
# OSPFv2 to receive a default route from ${upstream}
protocol ospf v2 ZW4_${
builtins.replaceStrings [ "-" ] [ "_" ] upstream
} {
protocol ospf v2 ZW4_${upstream} {
ipv4 {
import filter {
preference = preference + ${toString (100 - n)};
accept;
};
${lib.optionalString (
builtins.match "anon.*" hostName != null ||
hostName == "flpk-gw"
) ''
table vpn_table;
${lib.optionalString (builtins.match "anon.*" hostName != null) ''
table vpn4_table;
''}
};
area 0 {
@ -232,13 +192,7 @@ in
builtins.mapAttrs (net: _:
# Enable OSPF only on interfaces with a secret.
lib.optionalString (config.site.net.${net}.ospf.secret != null) ''
interface "${net}" instance ${
builtins.replaceStrings [ "-" ] [ "_" ] (
toString config.site.hosts.${upstream}.ospf.upstreamInstance
)
} {
hello 10;
wait 20;
interface "${net}" instance ${toString config.site.hosts.${upstream}.ospf.upstreamInstance} {
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
};
@ -256,31 +210,28 @@ in
# OSPFv3 for site-local IPv6
protocol ospf v3 ZW6 {
ipv6 {
import all;
# OSPF is self-contained
export none;
export where net != ::/0 && source != RTS_BGP;
};
area 0 {
${builtins.concatStringsSep "\n" (
builtins.attrValues (
builtins.mapAttrs (net: _:
# Enable OSPF only on networks with a secret.
# Enable OSPF only on networks with a secret. Others
# are treated as a stubnet whose routes to
# advertise.
if config.site.net.${net}.ospf.secret != null
then ''
interface "${net}" {
hello 10;
wait 20;
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
};
''
else ''
interface "${net}" {
stub yes;
cost 10;
};
''
else builtins.concatStringsSep "\n" (
map (subnet6: ''
# Advertise route of network ${net}
stubnet ${subnet6} {};
'') (builtins.attrValues config.site.net.${net}.subnets6)
)
) hostConf.physicalInterfaces
)
)}
@ -296,7 +247,7 @@ in
${lib.optionalString isUpstream ''
# OSPFv3 to advertise my default route
protocol ospf v3 ZW6_${hostNameEscaped} {
protocol ospf v3 ZW6_${hostName} {
ipv6 {
export where net = ::/0;
};
@ -309,8 +260,6 @@ in
interface "${net}" instance ${toString hostConf.ospf.upstreamInstance} {
# Become the designated router
priority 10;
hello 10;
wait 20;
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
@ -329,9 +278,7 @@ in
${text}
# OSPFv3 to receive a default route from ${upstream}
protocol ospf v3 ZW6_${
builtins.replaceStrings [ "-" ] [ "_" ] upstream
} {
protocol ospf v3 ZW6_${upstream} {
ipv6 {
import filter {
preference = preference + ${toString (100 - n)};
@ -344,13 +291,7 @@ in
builtins.mapAttrs (net: _:
# Enable OSPF only on interfaces with a secret.
lib.optionalString (config.site.net.${net}.ospf.secret != null) ''
interface "${net}" instance ${
builtins.replaceStrings [ "-" ] [ "_" ] (
toString config.site.hosts.${upstream}.ospf.upstreamInstance
)
} {
hello 10;
wait 20;
interface "${net}" instance ${toString config.site.hosts.${upstream}.ospf.upstreamInstance} {
authentication cryptographic;
password "${config.site.net.${net}.ospf.secret}";
};
@ -362,7 +303,7 @@ in
}
'';
n = n + 1;
}) { text = ""; n = 0; } hostConf.ospf.allowedUpstreams6
}) { text = ""; n = 0; } hostConf.ospf.allowedUpstreams
).text}
# Zentralwerk DN42
@ -373,8 +314,11 @@ in
protocol static {
ipv6;
route fd23:42:c3d2:580::/57 unreachable;
# TODO: remove
route 2a02:8106:208:5200::/56 unreachable;
# TODO: remove
route 2a02:8106:211:e900::/56 unreachable;
route 2a00:8180:2c00:200::/56 unreachable;
route 2a0f:5382:acab:1400::/56 unreachable;
}
${lib.optionalString (hostConf.bgp != null) ''
@ -436,8 +380,8 @@ in
];
};
instance = {
ipv4 = "ZW4_${hostNameEscaped}";
ipv6 = "ZW6_${hostNameEscaped}";
ipv4 = "ZW4_${hostName}";
ipv6 = "ZW6_${hostName}";
};
checkService = addressFamily: {
description = "Check connectivity for ${addressFamily}";
@ -447,7 +391,7 @@ in
User = "bird2";
Group = "bird2";
};
path = with pkgs; [ bird2 iputils ];
path = [ pkgs.bird2 "/run/wrappers" ];
script = ''
STATE=unknown

View File

@ -1,4 +1,4 @@
{ config, lib, modulesPath, pkgs, ... }:
{ config, lib, modulesPath, ... }:
{
imports = [
@ -6,19 +6,22 @@
(modulesPath + "/virtualisation/lxc-container.nix")
];
environment = {
etc."machine-id".text = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
systemPackages = with pkgs; [
ripgrep
];
boot = {
isContainer = true;
loader = {
initScript.enable = true;
};
};
environment.etc."machine-id".text =
builtins.substring 0 8 (
builtins.hashString "sha256" config.networking.hostName
);
nix = {
settings = {
sandbox = false;
max-jobs = lib.mkDefault 4;
cores = lib.mkDefault 4;
};
useSandbox = false;
maxJobs = lib.mkDefault 1;
buildCores = lib.mkDefault 1;
};
systemd.services =

View File

@ -1,5 +1,5 @@
# ISC DHCP/IPv4 server configuration
{ hostName, config, lib, ... }:
{ hostName, inputs, config, lib, ... }:
let
dhcpNets =
@ -8,331 +8,81 @@ let
dhcp.server == hostName
) config.site.net;
concatMapDhcpNets = f:
lib.pipe dhcpNets [
(builtins.mapAttrs f)
builtins.attrValues
(map (r: if builtins.isList r then r else [ r ]))
builtins.concatLists
];
enabled = builtins.length (builtins.attrNames dhcpNets) > 0;
in
{
services.kea.dhcp4 = lib.mkIf enabled {
services.dhcpd4 = lib.optionalAttrs enabled {
enable = true;
settings = {
interfaces-config.interfaces = builtins.attrNames dhcpNets;
dhcp-ddns.enable-updates = true;
ddns-send-updates = true;
# TODO: use with kea >= 2.5.0
# ddns-conflict-resolution-mode = "check-exists-with-dhcid";
ddns-use-conflict-resolution = false;
ddns-replace-client-name = "when-not-present";
expired-leases-processing.hold-reclaimed-time = builtins.foldl' lib.max
3600 (concatMapDhcpNets (net: { dhcp, ... }: dhcp.max-time));
interfaces = builtins.attrNames dhcpNets;
subnet4 = concatMapDhcpNets (net: { vlan, subnet4, hosts4, dhcp, domainName, ... }: {
id = vlan;
subnet = subnet4;
pools = [ {
pool = "${dhcp.start} - ${dhcp.end}";
} ];
renew-timer = builtins.ceil (.5 * dhcp.time);
rebind-timer = builtins.ceil (.85 * dhcp.time);
valid-lifetime = dhcp.time;
option-data = [ {
space = "dhcp4";
name = "routers";
code = 3;
data = config.site.net.${net}.hosts4.${dhcp.router};
} {
space = "dhcp4";
name = "domain-name";
code = 15;
data = domainName;
} {
space = "dhcp4";
name = "domain-name-servers";
code = 6;
data = "${config.site.net.serv.hosts4.dnscache}, 9.9.9.9";
} ];
ddns-qualifying-suffix = domainName;
reservations = lib.pipe dhcp.fixed-hosts [
(builtins.mapAttrs (fixedAddr: hwaddr:
if hosts4 ? ${fixedAddr}
then # fixedAddr is a known hostname
let
name = fixedAddr;
addr = hosts4.${fixedAddr};
in {
hostname = "${name}.${net}.zentralwerk.org";
hw-address = hwaddr;
ip-address = addr;
extraConfig = ''
${builtins.concatStringsSep "\n" (
builtins.attrValues (
builtins.mapAttrs (net: { dhcp, subnet4Net, subnet4Len, domainName, ...}:
''
ddns-update-style standard;
key dyndns {
algorithm hmac-sha256;
secret ${inputs.zentralwerk-network-key.lib.dyndnsKey};
};
zone ${domainName}. {
primary ${config.site.net.serv.hosts4.dns};
primary6 ${config.site.net.serv.hosts6.dn42.dns};
key dyndns;
}
else
let
names = builtins.attrNames (
lib.filterAttrs (_: hostAddr:
hostAddr == fixedAddr
) hosts4);
name = builtins.head names;
in
if builtins.length names > 0
then { # fixedAddr is IPv4 of a known hostname
hostname = "${name}.${net}.zentralwerk.org";
hw-address = hwaddr;
ip-address = hosts4.${name};
} # fixedAddr is IPv4?
else {
hw-address = hwaddr;
ip-address = fixedAddr;
${lib.concatMapStrings ({ name, dynamic, ... }:
lib.optionalString (
dynamic &&
lib.hasSuffix ".in-addr.arpa" name
) ''
zone ${name}. {
primary ${config.site.net.serv.hosts4.dns};
primary6 ${config.site.net.serv.hosts6.dn42.dns};
key dyndns;
}
''
) config.site.dns.localZones}
option arch code 93 = unsigned integer 16;
group {
default-lease-time ${toString dhcp.time};
max-lease-time ${toString dhcp.max-time};
option routers ${config.site.net.${net}.hosts4.${builtins.replaceStrings [".${net}"] [""] dhcp.router}};
option domain-name "${domainName}";
option domain-name-servers 172.20.73.8, 9.9.9.9;
ddns-domainname "${domainName}";
next-server ${config.site.net.serv.hosts4.netboot};
if option arch = 00:00 {
filename "netboot.xyz.kpxe";
} else {
filename "netboot.xyz.efi";
}
))
builtins.attrValues
(builtins.filter (r: r != null))
];
});
match-client-id = false;
host-reservation-identifiers = [ "hw-address" ];
subnet ${subnet4Net} netmask ${lib.netmasks.${toString subnet4Len}} {
range ${dhcp.start} ${dhcp.end};
}
# Netbooting
option-def = [ {
name = "PXEDiscoveryControl";
code = 6;
space = "vendor-encapsulated-options-space";
type = "uint8";
array = false;
} {
name = "PXEMenuPrompt";
code = 10;
space = "vendor-encapsulated-options-space";
type = "record";
array = false;
record-types = "uint8,string";
} {
name = "PXEBootMenu";
code = 9;
space = "vendor-encapsulated-options-space";
type = "record";
array = false;
record-types = "uint16,uint8,string";
} ];
client-classes =
let
rpi4Class = {
name = "rpi4-pxe";
test = "option[vendor-class-identifier].text == 'PXEClient:Arch:00000:UNDI:002001'";
option-data = [ {
name = "boot-file-name";
data = "bootcode.bin";
} {
name = "vendor-class-identifier";
data = "PXEClient";
} {
name = "vendor-encapsulated-options";
} {
name = "PXEBootMenu";
csv-format = true;
data = "0,17,Raspberry Pi Boot";
space = "vendor-encapsulated-options-space";
} {
name = "PXEDiscoveryControl";
data = "3";
space = "vendor-encapsulated-options-space";
} {
name = "PXEMenuPrompt";
csv-format = true;
data = "0,PXE";
space = "vendor-encapsulated-options-space";
} ];
};
update-static-leases on;
pxeClassData = {
PXE-Legacy = {
arch = "00000";
boot-file-name = "netboot.xyz.kpxe";
};
PXE-UEFI-32-1.arch = "00002";
PXE-UEFI-32-2.arch = "00006";
PXE-UEFI-64-1.arch = "00007";
PXE-UEFI-64-2.arch = "00008";
PXE-UEFI-64-3.arch = "00009";
};
makePxe = name: { boot-file-name ? "netboot.xyz.efi", arch }: {
inherit name boot-file-name;
test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:${arch}'";
next-server = config.site.net.serv.hosts4.nfsroot;
};
in
[ rpi4Class ]
++
builtins.attrValues (
builtins.mapAttrs makePxe pxeClassData
);
control-socket = {
socket-type = "unix";
socket-name = "/run/kea/dhcp4-socket";
};
hooks-libraries = [ {
library = "/run/current-system/sw/lib/kea/hooks/libdhcp_stat_cmds.so";
} {
library = "/run/current-system/sw/lib/kea/hooks/libdhcp_lease_cmds.so";
} ];
};
${builtins.concatStringsSep "\n" (
builtins.attrValues (
builtins.mapAttrs (addr: hwaddr:
''
host ${addr} {
hardware ethernet ${hwaddr};
fixed-address ${addr};
}
''
) dhcp.fixed-hosts
)
)}
}
''
) dhcpNets
)
)}
'';
};
services.kea.dhcp6 = lib.mkIf enabled {
enable = true;
settings = {
interfaces-config.interfaces = builtins.attrNames dhcpNets;
dhcp-ddns.enable-updates = true;
ddns-override-no-update = true;
ddns-override-client-update = true;
ddns-replace-client-name = "when-not-present";
# TODO: use with kea >= 2.5.0
# ddns-conflict-resolution-mode = "check-exists-with-dhcid";
ddns-use-conflict-resolution = false;
subnet6 = concatMapDhcpNets (net: { vlan, subnets6, dhcp, domainName, ... }:
let
subnet = subnets6.up4 or subnets6.flpk or null;
prefix = builtins.head (builtins.split "::/" subnet);
in
if subnet != null
then {
id = vlan;
interface = net;
inherit subnet;
pools = [ {
pool = "${prefix}:c3d2:c3d2:c3d2:1000 - ${prefix}:c3d2:c3d2:c3d2:ffff";
#pool = subnet;
} ];
valid-lifetime = dhcp.time;
max-valid-lifetime = dhcp.max-time;
option-data = [ {
space = "dhcp6";
name = "domain-search";
code = 24;
data = domainName;
} {
space = "dhcp6";
name = "dns-servers";
code = 23;
data = "${config.site.net.serv.hosts6.dn42.dnscache}, 2620:fe::9";
} ];
ddns-generated-prefix = "d";
ddns-qualifying-suffix = domainName;
}
else []
);
host-reservation-identifiers = [ "hw-address" ];
#reservations = concatMapDhcpNets (net: { hosts6, dhcp, ... }:
# builtins.filter (r: r != null) (
# builtins.attrValues (
# builtins.mapAttrs (name: hwaddr:
# let
# ip-addresses = lib.pipe hosts6 [
# (builtins.mapAttrs (_: hosts6: hosts6.${name} or null))
# builtins.attrValues
# (builtins.filter (a: a != null))
# ];
# in
# if builtins.trace (lib.generators.toPretty {} ip-addresses) (builtins.length ip-addresses) > 0
# then {
# hostname = "${name}.${net}.zentralwerk.org";
# hw-address = hwaddr;
# inherit ip-addresses;
# }
# else null
# ) dhcp.fixed-hosts
# )));
control-socket = {
socket-type = "unix";
socket-name = "/run/kea/dhcp6.socket";
};
hooks-libraries = [ {
library = "/run/current-system/sw/lib/kea/hooks/libdhcp_stat_cmds.so";
} {
library = "/run/current-system/sw/lib/kea/hooks/libdhcp_lease_cmds.so";
} ];
};
};
services.kea.dhcp-ddns = lib.mkIf enabled {
enable = true;
settings = {
tsig-keys = [ {
name = "dyndns";
algorithm = "hmac-sha256";
secret = config.site.dyndnsKey;
} ];
forward-ddns.ddns-domains = concatMapDhcpNets (net: { domainName, ... }: {
name = "${domainName}.";
key-name = "dyndns";
dns-servers = [ {
ip-address = config.site.net.serv.hosts4.dns;
} {
ip-address = config.site.net.serv.hosts6.dn42.dns;
} ];
});
reverse-ddns.ddns-domains = map ({ name, ...}: {
name = "${name}.";
key-name = "dyndns";
dns-servers = [ {
ip-address = config.site.net.serv.hosts4.dns;
} {
ip-address = config.site.net.serv.hosts6.dn42.dns;
} ];
}) (
builtins.filter ({ name, dynamic, ... }:
dynamic &&
(lib.hasSuffix ".in-addr.arpa" name ||
lib.hasSuffix ".ip6.arpa" name)
) config.site.dns.localZones
);
control-socket = {
socket-type = "unix";
socket-name = "/run/kea/dhcp-ddns.socket";
};
};
};
services.kea.ctrl-agent = lib.mkIf enabled {
enable = true;
settings.control-sockets = {
dhcp4 = {
socket-type = "unix";
socket-name = "/run/kea/dhcp4.socket";
};
dhcp6 = {
socket-type = "unix";
socket-name = "/run/kea/dhcp6.socket";
};
d2 = {
socket-type = "unix";
socket-name = "/run/kea/dhcp-ddns.socket";
};
};
};
# Increase reliablity
# (mostly for kea-dhcp-ddns-server.service)
systemd.services =
let
restartService.serviceConfig = {
RestartSec = 4;
Restart = "always";
};
in {
kea-dhcp4-server = restartService;
kea-dhcp6-server = restartService;
kea-dhcp-ddns-server = restartService;
};
}

View File

@ -1,26 +1,33 @@
{ config, dns-nix, hostName, lib, pkgs, self, ... }:
{ hostName, config, lib, pkgs, self, inputs, ... }:
let
serial = builtins.substring 0 10 self.lastModifiedDate;
serial =
let
timestamp = toString self.lastModified;
datePkg = pkgs.runCommandLocal "date-${timestamp}" {} ''
date -d @${timestamp} +%Y%m%d%H > $out
'';
in
toString (import datePkg);
generateZoneFile = let
util = dns-nix.util.${pkgs.system};
in { name, ns, records, ... }: util.writeZone name {
TTL = 60*60;
SOA = {
nameServer = "${lib.dns.ns}.";
adminEmail = "astro@spaceboyz.net";
serial = lib.toInt serial;
refresh = 1*60*60;
retry = 5*60;
expire = 2*60*60;
minimum = 1*60;
};
NS = map (a: a+".") ns;
subdomains = lib.foldl (a: b: lib.recursiveUpdate a b) { } (map ({ name, type, data }: {
${name}.${type} = [ data ];
}) records);
};
generateZoneFile = { name, ns, records, dynamic }:
builtins.toFile "${name}.zone" ''
$ORIGIN ${name}.
$TTL 1h
@ IN SOA ${lib.dns.ns}. astro.spaceboyz.net. (
${serial} ; serial
1h ; refresh
1m ; retry
2h ; expire
1m ; minimum
)
${lib.concatMapStrings (ns: " IN NS ${ns}.\n") ns}
${lib.concatMapStrings ({ name, type, data }:
"${name} IN ${type} ${data}\n"
) records}
'';
in
{
options =
@ -32,10 +39,10 @@ in
type = types.str;
};
type = mkOption {
type = types.enum [ "A" "AAAA" "MX" "SRV" "CNAME" "TXT" "PTR" ];
type = types.enum [ "A" "AAAA" "PTR" ];
};
data = mkOption {
type = types.oneOf [ types.str (types.attrsOf (types.oneOf [ types.int types.str ]))];
type = types.str;
};
};
@ -69,151 +76,90 @@ in
config = {
site.dns.localZones = lib.dns.localZones;
services.knot = lib.mkIf config.site.hosts.${hostName}.services.dns.enable (
services.bind = lib.mkIf config.site.hosts.${hostName}.services.dns.enable (
let
generateZone = zone@{ name, dynamic, ... }: {
domain = name;
template = "zentralwerk";
acl = [ "zone_xfr" ] ++ lib.optional dynamic "dyndns";
file = if dynamic
then "/var/lib/knot/zones/${name}.zone"
inherit name;
master = true;
# allowed for zone-transfer
slaves = [
# ns.c3d2.de
"217.197.84.53" "2001:67c:1400:2240::a"
config.site.net.serv.hosts4.bind
config.site.net.serv.hosts6.dn42.bind
config.site.net.serv.hosts6.up4.bind
# ns.spaceboyz.net
"172.22.24.4" "2a01:4f9:4b:39ec::4"
];
file =
if dynamic
then "/var/db/bind/${name}.zone"
else generateZoneFile zone;
notify = [ "all" ];
extraConfig = ''
also-notify {
# ns.c3d2.de
217.197.84.53;
2001:67c:1400:2240::a;
${config.site.net.serv.hosts4.bind};
${config.site.net.serv.hosts6.dn42.bind};
${config.site.net.serv.hosts6.up4.bind};
# ns.spaceboyz.net
172.22.24.4;
95.217.229.209;
2a01:4f9:4b:39ec::4;
};
notify-source ${config.site.net.serv.hosts4.dns};
notify-source-v6 ${config.site.net.serv.hosts6.up4.dns};
'' + lib.optionalString dynamic ''
allow-update { key "dyndns"; };
'';
};
in {
enable = true;
settings = {
acl = [
{
id = "dyndns";
action = "update";
key = "dyndns";
}
{
id = "zone_xfr";
address = with config.site.net.serv; [
# ns.c3d2.de
hosts4.knot hosts6.dn42.knot hosts6.up4.knot
"2a00:8180:2c00:282:2041:cbff:fe0c:8516"
"fd23:42:c3d2:582:2041:cbff:fe0c:8516"
# ns.spaceboyz.net
"172.22.24.4" "95.217.229.209" "2a01:4f9:4b:39ec::4"
# ns1.supersandro.de
"188.34.196.104" "2a01:4f8:1c1c:1d38::1"
];
action = "transfer";
}
];
zones = map generateZone config.site.dns.localZones;
key = [ {
id = "dyndns";
algorithm = "hmac-sha256";
secret = config.site.dyndnsKey;
} ];
log = [ {
target = "syslog";
any = "info";
} ];
mod-stats = [ {
id = "default";
query-type = "on";
} ];
remote = let
via = with config.site.net.serv; [ hosts4.dns hosts6.up4.dns ];
in [
{
id = "ns.c3d2.de";
address = with config.site.net.serv; [ hosts4.knot hosts6.dn42.knot hosts6.up4.knot ];
inherit via;
} {
id = "ns.spaceboyz.net";
address = [ "172.22.24.4" "95.217.229.209" "2a01:4f9:4b:39ec::4" ];
inherit via;
} {
id = "ns1.supersandro.de";
address = [ /*"188.34.196.104"*/ "2a01:4f8:1c1c:1d38::1" ];
inherit via;
}
];
remotes = [ {
id = "all";
remote = [ "ns.c3d2.de" "ns.spaceboyz.net" "ns1.supersandro.de" ];
} ];
server = {
answer-rotation = true;
automatic-acl = true;
identity = "dns.serv.zentralwerk.org";
listen = with config.site.net; [
"127.0.0.1" "::1"
serv.hosts4.dns serv.hosts6.up4.dns serv.hosts6.dn42.dns
];
tcp-fastopen = true;
version = null;
extraConfig = ''
key "dyndns" {
algorithm hmac-sha256;
secret "${inputs.zentralwerk-network-key.lib.dyndnsKey}";
};
template = [
{
# default is a magic name and is always loaded.
# Because we want to use catalog-role/catalog-zone settings for all zones *except* the catalog zone itself, we must split the templates
id = "default";
global-module = [ "mod-stats" ];
}
{
id = "zentralwerk";
catalog-role = "member";
catalog-zone = "zentralwerk.";
dnssec-signing = true;
journal-content = "all"; # required for zonefile-load=difference-no-serial and makes cold starts like zone reloads
module = "mod-stats/default";
semantic-checks = true;
serial-policy = "increment";
storage = "/var/lib/knot/zones";
zonefile-load = "difference-no-serial";
}
];
zone = [ {
acl = "zone_xfr";
catalog-role = "generate";
domain = "zentralwerk.";
notify = [ "ns1.supersandro.de" ];
storage = "/var/lib/knot/catalog";
} ] ++ map generateZone config.site.dns.localZones;
};
'';
extraOptions = ''
# allow underscores in dynamic hostnames
${lib.concatMapStringsSep "\n" (type: ''
check-names ${type} ignore;
'') [ "master" "slave" "response" ]}
'';
});
systemd.services = {
create-dynamic-zones = {
description = "Creates dynamic zone files";
requiredBy = [ "knot.service" ];
before = [ "knot.service" ];
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /var/lib/knot/zones
systemd.services.create-dynamic-zones = {
description = "Creates dynamic zone files";
requiredBy = [ "bind.service" ];
before = [ "bind.service" ];
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /var/db/bind
${lib.concatMapStringsSep "\n" (zone@{ name, ... }: ''
[ -e /var/lib/knot/zones/${name}.zone ] || \
cp ${generateZoneFile zone} /var/lib/knot/zones/${name}.zone
chown -R knot /var/lib/knot/zones
chmod -R u+rwX /var/lib/knot/zones
'') (builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones)}
'';
};
update-dynamic-zones = {
description = "Creates initial records in dynamic zone files";
requiredBy = [ "knot.service" ];
after = [ "knot.service" ];
serviceConfig.Type = "oneshot";
path = [ pkgs.dnsutils ];
script = lib.concatMapStrings (zone: ''
nsupdate -v -y "hmac-sha256:dyndns:${config.site.dyndnsKey}" <<EOF
${lib.concatMapStringsSep "\n" (zone@{ name, ... }: ''
[ -e /var/db/bind/${name}.zone ] || \
cp ${generateZoneFile zone} /var/db/bind/${name}.zone
chown -R named /var/db/bind
chmod -R u+rwX /var/db/bind
'') (
builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones
)}
'';
};
systemd.services.update-dynamic-zones = {
description = "Creates initial records in dynamic zone files";
requiredBy = [ "bind.service" ];
after = [ "bind.service" ];
serviceConfig.Type = "oneshot";
path = [ pkgs.dnsutils ];
script = ''
${lib.concatMapStrings (zone: ''
nsupdate -y "hmac-sha256:dyndns:${inputs.zentralwerk-network-key.lib.dyndnsKey}" <<EOF
server localhost
${lib.concatMapStringsSep "\n" ({ name, type, data }: ''
@ -223,8 +169,10 @@ in
send
EOF
'') (builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones);
};
'') (
builtins.filter ({ dynamic, ... }: dynamic) config.site.dns.localZones
)}
'';
};
};
}

View File

@ -1,99 +1,105 @@
{ hostName, config, lib, pkgs, ... }:
lib.mkIf config.site.hosts.${hostName}.services.dnscache.enable {
services.kresd = {
services.unbound = {
enable = true;
instances = 4;
listenPlain = [ "0.0.0.0:53" "[::0]:53" ];
package = pkgs.knot-resolver.override { extraFeatures = true; };
extraConfig = /* lua */ ''
modules = {
'http',
'policy',
'predict',
'prefill',
'serve_stale < cache', -- servce stail records while refreshing the record
'workarounds < iterate', -- solve problems around specific broken subdomains, mainly disables case randomization
'view'
}
settings = {
remote-control = {
control-enable = true;
control-use-cert = false;
};
server = {
num-threads = 4;
verbosity = 1;
prefetch = true;
serve-expired = true;
cache-min-ttl = 60;
cache-max-ttl = 3600;
cache.size = 500 * MB
cache.min_ttl(60)
interface = [ "0.0.0.0" "'::0'" ];
# TODO: generate
access-control = [
"fd23:42:c3d2:500::/56 allow"
# TODO: remove
"2a02:8106:208:5200::/56 allow"
# TODO: remove
"2a02:8106:211:e900::/56 allow"
"2a00:8180:2000:37::1/128 allow"
"2a00:8180:2c00:200::/56 allow"
"::172.20.72.0/117 allow"
"::172.22.99.0/120 allow"
"::1/128 allow"
"172.20.72.0/21 allow"
"10.0.0.0/24 allow"
"10.200.0.0/15 allow"
"172.22.99.0/24 allow"
"127.0.0.0/8 allow"
"0.0.0.0/0 deny"
"::/0 deny"
];
# For DNS over TLS
tls-cert-bundle = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
net.listen('127.0.0.1', 8453, { kind = 'webmgmt' })
http.prometheus.namespace = 'resolver_'
# allow reverse lookup of rfc1918 space, which includes the DN42 address space
unblock-lan-zones = true;
insecure-lan-zones = true;
-- dns42
policy.add(policy.suffix(
policy.STUB({'fd42:d42:d42:54::1', 'fd42:d42:d42:53::1', '172.20.0.53', '172.23.0.53'}),
policy.todnames({'dn42.', 'd.f.ip6.arpa', '20.172.in-addr.arpa', '21.172.in-addr.arpa', '22.172.in-addr.arpa', '23.172.in-addr.arpa'})
))
domain-insecure = [
"dn42"
"d.f.ip6.arpa"
"ffdd"
];
};
-- freifunk
policy.add(policy.suffix(
policy.STUB({'10.200.0.4', '10.200.0.16'}),
policy.todnames({'ffdd.', '200.10.in-addr.arpa', '201.10.in-addr.arpa'})
))
-- size.dns.localZones
policy.add(policy.suffix(
policy.STUB({'${config.site.net.serv.hosts4.dns}', ${lib.concatStringsSep ", " (map (hosts6: "'${hosts6.dns}'") (builtins.attrValues config.site.net.serv.hosts6))}}),
policy.todnames({${lib.concatStringsSep ", " (map (zone: "'${zone.name}'") config.site.dns.localZones)}})
))
-- forward to dns caches
policy.add(policy.slice(
policy.slice_randomize_psl(),
-- quad9
policy.TLS_FORWARD({
{'2620:fe::fe', hostname='dns.quad9.net'},
{'2620:fe::9', hostname='dns.quad9.net'},
{'9.9.9.9', hostname='dns.quad9.net'},
{'149.112.112.112', hostname='dns.quad9.net'}
}),
-- cloudflare
policy.TLS_FORWARD({
{'2606:4700:4700::1111', hostname='cloudflare-dns.com'},
{'2606:4700:4700::1001', hostname='cloudflare-dns.com'},
{'1.1.1.1', hostname='cloudflare-dns.com'},
{'1.0.0.1', hostname='cloudflare-dns.com'}
})
))
-- allow access from our networks
'' + lib.concatMapStringsSep "\n" (cidr: "view:addr('${cidr}', policy.all(policy.PASS))") [
# localhost
"::1/128" "127.0.0.0/8"
# mgmt
"${config.site.net.mgmt.subnet4}"
# dn42
"fd23:42:c3d2:500::/56" "::172.20.72.0/117" "::172.22.99.0/120"
"172.20.72.0/21" "172.22.99.0/24"
# freifunk
"10.200.0.0/15"
# DSI
"2a00:8180:2000:37::1/128" "2a00:8180:2c00:200::/56"
# flpk
"${config.site.net.flpk.subnet4}" "2a0f:5382:acab:1400::/56 allow"
] + "\n" + /* lua */ ''
-- drop everything that hasn't matched
view:addr('0.0.0.0/0', policy.all(policy.DROP))
view:addr('::/0', policy.all(policy.DROP))
predict = {
window = 15, -- sampling window
period = 24*(60/15) -- track last X hours, divide through sampling window
}
prefill.config({
['.'] = {
url = 'https://www.internic.net/domain/root.zone',
interval = 86400, -- seconds
}
})
trust_anchors.set_insecure({'dn42', 'd.f.ip6.arpa', 'ffdd'})
'';
forward-zone = let
mkFfddZone = name: {
inherit name;
forward-addr = [ "10.200.0.4" "10.200.0.16" ];
};
in [ {
name = ".";
forward-tls-upstream = true;
forward-addr = [
# Quad9
"2620:fe::fe@853#dns.quad9.net"
"9.9.9.9@853#dns.quad9.net"
"2620:fe::9@853#dns.quad9.net"
"149.112.112.112@853#dns.quad9.net"
# Cloudflare DNS
"2606:4700:4700::1111@853#cloudflare-dns.com"
"1.1.1.1@853#cloudflare-dns.com"
"2606:4700:4700::1001@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
} ] ++
# Local networks
map ({ name, ... }: {
name = "${name}";
forward-addr = [ "${config.site.net.serv.hosts4.dns}" ] ++
map (hosts6: hosts6.dns)
(builtins.attrValues config.site.net.serv.hosts6);
}) config.site.dns.localZones
# Freifunk
++ (map mkFfddZone [
"ffdd"
"200.10.in-addr.arpa"
"201.10.in-addr.arpa"
]);
# DN42
stub-zone = let
mkDn42Zone = name: {
inherit name;
stub-prime = true;
stub-addr = [
"172.20.0.53" "fd42:d42:d42:54::1"
"172.23.0.53" "fd42:d42:d42:53::1"
];
};
in map mkDn42Zone [
"dn42" "d.f.ip6.arpa"
"20.172.in-addr.arpa" "21.172.in-addr.arpa"
"22.172.in-addr.arpa" "23.172.in-addr.arpa"
];
};
};
}

View File

@ -1,114 +0,0 @@
{ config, lib, ... }:
let
inherit (config.networking) hostName;
interfaces = config.site.hosts.${hostName}.physicalInterfaces;
# linux iface name max length = 15
shortenNetName = name:
if builtins.match "priv(.*)" name != null
then "p" + builtins.substring 4 9 name
else if name == "coloradio"
then "cr"
else if name == "coloradio-gw"
then "cr-gw"
else name;
checkIfname = ifname: let
len = builtins.stringLength ifname;
in if len > 15
then throw "Interface name ${ifname} is ${toString (len - 15)} chars too long."
else ifname;
# `lxc.net.*` formatter for lxc.container.conf files
netConfig =
let
attrNamesOrdered = attrs:
if attrs ? type
then [ "type" ] ++ lib.remove "type" (builtins.attrNames attrs)
else builtins.attrNames attrs;
serialize = name: x:
if builtins.isString x
then "${name} = ${x}\n"
else if builtins.isAttrs x
then builtins.concatStringsSep "" (
map (n: serialize "${name}.${n}" x.${n}) (attrNamesOrdered x)
)
else if builtins.isList x
then
let
enumerate = xs: n:
if xs == []
then []
else [ {
e = builtins.head xs;
i = n;
} ] ++ enumerate (builtins.tail xs) (n + 1);
in
builtins.concatStringsSep "" (
map ({ e, i }: serialize "${name}.${toString i}" e) (enumerate x 0)
)
else throw "Invalid data in lxc net config for ${name}: ${lib.generators.toPretty {} x}";
in
serialize "lxc.net" (
map (netName:
let
ifData = interfaces.${netName};
in {
type = ifData.type;
name = checkIfname netName;
flags = "up";
hwaddr = if ifData ? hwaddr && ifData.hwaddr != null
then ifData.hwaddr
else "0A:14:48:xx:xx:xx";
} // (lib.optionalAttrs (ifData.type == "veth") {
veth.pair = checkIfname "${shortenNetName hostName}-${shortenNetName netName}";
veth.mode = checkIfname "bridge";
link = checkIfname netName;
}) // (lib.optionalAttrs (ifData.type == "phys") {
link = checkIfname "ext-${netName}";
})
) (builtins.attrNames interfaces)
);
in
{
system.build.lxcConfig = builtins.toFile "${hostName}.conf" ''
# For lxcfs and sane defaults
lxc.include = /etc/lxc/common.conf
lxc.uts.name = ${hostName}
# Handled by lxc@.service
lxc.start.auto = 0
lxc.rootfs.path = /var/lib/lxc/${hostName}/rootfs
lxc.init.cmd = "/init"
lxc.mount.entry = /nix/store nix/store none bind,ro 0 0
lxc.mount.entry = none tmp tmpfs defaults 0 0
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.autodev = 1
lxc.tty.max = 0
lxc.pty.max = 8
lxc.cap.drop = sys_module sys_time sys_nice sys_pacct sys_rawio
security.privileged = false
lxc.apparmor.profile = lxc-container-default-with-mounting
lxc.cgroup.memory.limit_in_bytes = 1G
lxc.cgroup.memory.kmem.tcp.limit_in_bytes = 128M
# tuntap
lxc.cgroup.devices.allow = c 10:200 rw
lxc.cgroup2.devices.allow = c 10:200 rw
# ppp
lxc.cgroup.devices.allow = c 108:0 rwm
lxc.cgroup2.devices.allow = c 108:0 rwm
${netConfig}
'';
}

View File

@ -0,0 +1,22 @@
{ lib, pkgs, ... }:
let
netboot_xyz_efi = pkgs.fetchurl {
url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.40/netboot.xyz.efi";
sha256 = "1gvgvlaxhjkr9i0b2bjq85h12ni9h5fn6r8nphsag3il9kificcc";
};
netboot_xyz_kpxe = pkgs.fetchurl {
url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.40/netboot.xyz.kpxe";
sha256 = "1crkr995i1hv3q548gx2xan1ymxmzcnr7mxaf77s2410mpqfcx82";
};
tftpRoot = pkgs.runCommand "tftproot" {} ''
mkdir -p $out
ln -s ${netboot_xyz_efi} $out/netboot.xyz.efi
ln -s ${netboot_xyz_kpxe} $out/netboot.xyz.kpxe
'';
in
{
services.atftpd = {
enable = true;
root = tftpRoot;
};
}

View File

@ -1,4 +1,4 @@
{ hostName, config, lib, pkgs, ... }:
{ hostName, config, lib, ... }:
let
hostConf = config.site.hosts.${hostName};
@ -15,8 +15,6 @@ let
else null;
enabled = firstUpstreamInterface != null;
inherit (upstreamInterfaces.${firstUpstreamInterface}.upstream) staticIpv4Address;
in
{
systemd.network.networks = {
@ -24,132 +22,64 @@ in
# systemd-networkd only requests Prefix Delegation via DHCPv6 on
# the upstream interface if another interface is configured for it.
# without this, the static ipv6 subnet won't be routed to us.
networkConfig.DHCPPrefixDelegation = true;
networkConfig.DHCPv6PrefixDelegation = true;
dhcpV6PrefixDelegationConfig = {
SubnetId = "81";
# because we have static addresses, we don't actually use this
Assign = false;
};
};
} // builtins.mapAttrs (_: { upstream, ... }:
# DHCP
lib.optionalAttrs (hostName != "flpk-gw") {
DHCP = "yes";
networkConfig.IPv6AcceptRA = true;
dhcpV6Config.PrefixDelegationHint = "::/56";
}
//
} // builtins.mapAttrs (_: { upstream, ... }: {
DHCP = "yes";
networkConfig.IPv6AcceptRA = true;
dhcpV6Config.PrefixDelegationHint = "::/56";
# Traffic Shaping
{
extraConfig = ''
[CAKE]
Parent = root
${lib.optionalString (upstream.provider == "vodafone") ''
# DOCSIS overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.provider == "dsi") ''
# PPPoE overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.upBandwidth != null) ''
Bandwidth = ${toString upstream.upBandwidth}K
''}
'';
}
) upstreamInterfaces;
extraConfig = ''
[CAKE]
Parent = root
${lib.optionalString (upstream.provider == "vodafone") ''
# DOCSIS overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.provider == "dsi") ''
# PPPoE overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.upBandwidth != null) ''
Bandwidth = ${toString upstream.upBandwidth}K
''}
'';
}) upstreamInterfaces;
networking.nat = lib.optionalAttrs enabled {
enable = true;
enableIPv6 = true;
internalInterfaces = [ "core" ];
externalInterface = firstUpstreamInterface;
externalIP = staticIpv4Address;
extraCommands = ''
# Add workaround for upstreams with wonky MTU
iptables -t mangle -A FORWARD \
-p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
ip6tables -t mangle -A FORWARD \
-p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
# Prohibit SMTP except for servers
iptables -N fwd_smtp || \
iptables -F fwd_smtp
iptables -A fwd_smtp --source ${config.site.net.serv.subnet4} -j RETURN
iptables -A fwd_smtp --dest ${config.site.net.serv.subnet4} -j RETURN
iptables -A fwd_smtp --source ${config.site.net.flpk.subnet4} -j RETURN
iptables -A fwd_smtp --dest ${config.site.net.flpk.subnet4} -j RETURN
iptables -A fwd_smtp -j REJECT
iptables -I FORWARD -p tcp --dport 25 -j fwd_smtp
ip6tables -N fwd_smtp || \
ip6tables -F fwd_smtp
${lib.concatMapStrings (subnet6: ''
ip6tables -A fwd_smtp --source ${subnet6} -j RETURN
ip6tables -A fwd_smtp --dest ${subnet6} -j RETURN
'') (builtins.concatMap builtins.attrValues [
config.site.net.serv.subnets6
config.site.net.flpk.subnets6
])}
ip6tables -A fwd_smtp -j REJECT
ip6tables -I FORWARD -p tcp --dport 25 -j fwd_smtp
${lib.optionalString (staticIpv4Address != null) ''
# Allow connections to ${staticIpv4Address} from other hosts behind NAT
${lib.concatMapStrings (fwd: let
m = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
destinationIP = if m == null then throw "bad ip:ports `${fwd.destination}'" else lib.elemAt m 0;
destinationPorts = if m == null then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (lib.elemAt m 1);
in ''
iptables -t nat -A nixos-nat-pre \
-d ${staticIpv4Address} -p ${fwd.proto} \
--dport ${builtins.toString fwd.sourcePort} \
-j DNAT --to-destination ${fwd.destination}
iptables -t nat -A nixos-nat-post \
-d ${destinationIP} -p ${fwd.proto} \
--dport ${destinationPorts} \
-s 172.20.72.0/21 -j MASQUERADE
iptables -t nat -A nixos-nat-post \
-d ${destinationIP} -p ${fwd.proto} \
--dport ${destinationPorts} \
-s ${config.site.net.c3d2.subnet4} -j MASQUERADE
'') config.networking.nat.forwardPorts}
''}
# Do not NAT our public IPv4 addresses
${lib.concatMapStringsSep "\n" (net:
lib.concatMapStrings (subnet: ''
iptables -t nat -I nixos-nat-post \
-o ${net} \
-s ${subnet} \
-j RETURN
'') upstreamInterfaces.${net}.upstream.noNat.subnets4 or []
) (builtins.attrNames hostConf.interfaces)}
externalIP = upstreamInterfaces.${firstUpstreamInterface}.upstream.staticIpv4Address;
extraCommands =
# Provide IPv6 upstream for everyone, using NAT66 when not from
# our static prefixes
${lib.concatMapStringsSep "\n" (net:
lib.concatMapStrings (subnet: ''
ip6tables -t nat -I nixos-nat-post \
-o ${net} \
-s ${subnet} \
-j RETURN
'') upstreamInterfaces.${net}.upstream.noNat.subnets6
) (builtins.attrNames upstreamInterfaces)}
lib.concatMapStringsSep "\n" (net: ''
ip6tables -t nat -N ${net}_nat || \
ip6tables -t nat -F ${net}_nat
${lib.concatMapStringsSep "\n" (subnet: ''
ip6tables -t nat -A ${net}_nat \
-s ${subnet} \
-j RETURN
'') upstreamInterfaces.${net}.upstream.noNat.subnets6}
ip6tables -t nat -A ${net}_nat -j MASQUERADE
# There just have been moments without a complete ruleset. Flush
# out invalid conntrack states!
${pkgs.conntrack-tools}/bin/conntrack -F
'';
extraStopCommands = ''
iptables -F FORWARD 2>/dev/null || true
ip6tables -F FORWARD 2>/dev/null || true
ip6tables -t nat -F POSTROUTING 2>/dev/null || true
'';
ip6tables -t nat -A POSTROUTING \
-o ${net} \
-j ${net}_nat
'') (builtins.attrNames upstreamInterfaces);
extraStopCommands =
lib.concatMapStringsSep "\n" (net: ''
ip6tables -t nat -F POSTROUTING 2>/dev/null || true
ip6tables -t nat -F ${net}_nat 2>/dev/null || true
ip6tables -t nat -X ${net}_nat 2>/dev/null || true
'') (builtins.attrNames upstreamInterfaces);
forwardPorts = map ({ destination, sourcePort, reflect, ... }@forwardedPort:
removeAttrs forwardedPort ["reflect"] // {
@ -159,7 +89,11 @@ in
else "${destination}:${toString sourcePort}";
loopbackIPs =
if reflect
then [ config.site.net.core.hosts4.${hostName} ]
then builtins.filter (ip: ip != null) (
map (net:
upstreamInterfaces.${net}.upstream.staticIpv4Address
) (builtins.attrNames upstreamInterfaces)
)
else [];
}
) hostConf.forwardPorts;

View File

@ -1,4 +1,4 @@
{ hostName, config, lib, ... }:
{ hostName, inputs, config, lib, ... }:
let
hostConf = config.site.hosts.${hostName};
@ -7,6 +7,8 @@ let
lib.filterAttrs (_: { type, ... }: type == "pppoe")
hostConf.interfaces;
inherit (inputs.zentralwerk-network-key.lib.pppoe.${hostName}) user password;
in lib.mkIf (pppoeInterfaces != {}) {
boot.postBootCommands = ''
if [ ! -c /dev/ppp ]; then
@ -14,23 +16,20 @@ in lib.mkIf (pppoeInterfaces != {}) {
fi
'';
environment.etc."ppp/pap-secrets".text = lib.concatMapStrings (ifName:
let
inherit (pppoeInterfaces.${ifName}.upstream) user password;
in ''
"${user}" * "${password}"
'') (builtins.attrNames pppoeInterfaces);
environment.etc."ppp/pap-secrets".text = ''
"${user}" * "${password}"
'';
services.pppd = {
enable = true;
peers = builtins.mapAttrs (ifName: { upstream, ... }: {
enable = true;
autostart = true;
config = ''
plugin pppoe.so
plugin rp-pppoe.so
nic-${upstream.link}
ifname ${ifName}
# Login settings. (PAP)
name "${upstream.user}"
name "${user}"
noauth
hide-password
# Connection settings.
@ -39,16 +38,17 @@ in lib.mkIf (pppoeInterfaces != {}) {
maxfail 0
# Seconds between reconnection attempts
holdoff 1
# LCP settings.
lcp-echo-interval 5
lcp-echo-failure 6
# PPPoE compliant settings.
noaccomp
default-asyncmap
mtu 1492
# IP settings.
#noipdefault
defaultroute
+ipv6
defaultroute6
@ -64,17 +64,14 @@ in lib.mkIf (pppoeInterfaces != {}) {
in networks // {
"${ifName}" = {
matchConfig.Name = "${ifName}";
linkConfig = lib.optionalAttrs (ifName == "core") {
# interface is stuck in "routable (configuring)" and blocks systemd-networkd-wait-online
# TODO: figure out why exactly
RequiredForOnline = false;
};
networkConfig = {
DHCP = lib.mkForce "ipv6";
DHCP = lib.mkOverride 900 "ipv6";
# accept config set by pppd
KeepConfiguration = "yes";
};
dhcpV6Config = {
RapidCommit = true;
ForceDHCPv6PDOtherInformation = true;
PrefixDelegationHint = "::/56";
};
};
@ -84,4 +81,11 @@ in lib.mkIf (pppoeInterfaces != {}) {
};
}) {} (builtins.attrNames pppoeInterfaces);
# TODO: needed?
networking.nat.extraCommands = ''
iptables -A FORWARD \
-p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
'';
}

View File

@ -1,58 +0,0 @@
{ config, pkgs, ... }:
let
privateKeyFile = ifName:
"/run/wireguard-keys/${ifName}.key";
ifName = "vpn";
in
{
systemd.services = {
"wireguard-key-${ifName}" = {
description = "Create key file for wireguard interface '${ifName}'";
requiredBy = [ "systemd-networkd.service" ];
before = [ "systemd-networkd.service" ];
serviceConfig.Type = "oneshot";
script = ''
#! ${pkgs.runtimeShell} -e
F=${privateKeyFile ifName}
mkdir -p -m 0700 $(dirname $F)
chown systemd-network:systemd-network $(dirname $F)
rm -f $F
cat >$F <<EOF
${config.site.vpn.wireguard.privateKey}
EOF
chmod 0400 $F
chown systemd-network:systemd-network $F
'';
};
};
systemd.network.netdevs.vpn = {
netdevConfig = {
Name = ifName;
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = privateKeyFile ifName;
ListenPort = config.site.vpn.wireguard.port;
};
wireguardPeers = map ({ publicKey, allowedIPs }: {
wireguardPeerConfig = {
PublicKey = publicKey;
AllowedIPs = allowedIPs ++ [ "fe80::/64" "ff02::/16" ];
};
}) config.site.vpn.wireguard.peers;
};
systemd.network.networks.vpn.addresses = [ {
addressConfig = {
Address = "fe80::1/64";
Scope = "link";
};
} ];
environment.systemPackages = [
pkgs.wireguard-tools
];
}

View File

@ -1,13 +1,13 @@
# Pulls together NixOS configuration modules according to the
# name/role of the host to be built.
{ hostName, lib, ... }:
{ hostName, config, lib, pkgs, ... }:
let
inherit (lib) optionals;
hostConfig = lib.config.site.hosts.${hostName};
in {
inherit (lib.config) site;
site = lib.config.site;
imports = [
../lib/config/options.nix
@ -20,27 +20,23 @@ in {
./server/default.nix
] ++
optionals (hostConfig.role == "container") [
./container/lxc-config.nix
./container/defaults.nix
./container/dhcp-server.nix
./container/wireguard.nix
./container/anon.nix
./container/dns.nix
./container/dnscache.nix
] ++
optionals lib.config.site.hosts.${hostName}.isRouter [
./container/bird.nix
] ++
optionals (
builtins.match "upstream.*" hostName != null ||
hostName == "flpk-gw"
) [
optionals (builtins.match "upstream.*" hostName != null) [
./container/upstream.nix
./container/upstream/pppoe.nix
] ++
optionals (hostName == "mgmt-gw") [
./container/mgmt-gw.nix
] ++
optionals (hostName == "vpn-gw") [
./container/vpn.nix
optionals (hostName == "netboot") [
./container/netboot.nix
];
}

View File

@ -1,4 +1,4 @@
{ hostName, inputs, lib, pkgs, ... }:
{ hostName, inputs, pkgs, config, options, lib, ... }:
{
boot.kernelParams = [
@ -7,9 +7,9 @@
# Prevents automatic creation of interface bond0 by the kernel
"bonding.max_bonds=0"
];
boot.tmp.useTmpfs = true;
boot.tmpOnTmpfs = true;
# Includes wireguard
boot.kernelPackages = pkgs.zfsUnstable.latestCompatibleLinuxPackages;
boot.kernelPackages = pkgs.linuxPackages_latest;
# Keep building
boot.zfs.enableUnstable = true;
@ -19,24 +19,44 @@
Storage=volatile
'';
# central logging
services.journalbeat = {
enable = true;
tags = [ "zentralwerk" ];
extraConfig = ''
journalbeat.inputs:
# Paths that should be crawled and fetched. Possible values files and directories.
# When setting a directory, all journals under it are merged.
# When empty starts to read from local journal.
- paths: []
journalbeat:
seek_position: cursor
cursor_seek_fallback: tail
write_cursor_state: true
cursor_flush_period: 5s
clean_field_names: true
convert_to_numbers: false
move_metadata_to_field: journal
default_type: journal
kernel: true
output.logstash:
# Boolean flag to enable or disable the output module.
enabled: true
hosts: ["${config.site.net.serv.hosts4.logging}:5044"]
'';
};
nix = {
package = pkgs.nixFlakes;
extraOptions = "experimental-features = nix-command flakes";
registry = {
nixpkgs.flake = inputs.nixpkgs;
};
settings = {
substituters = lib.mkBefore [ "https://hydra.hq.c3d2.de" ];
trusted-public-keys = [
"nix-serve.hq.c3d2.de:KZRGGnwOYzys6pxgM8jlur36RmkJQ/y8y62e52fj1ps=%"
];
};
};
documentation = {
enable = lib.mkForce false;
nixos.enable = lib.mkForce false;
enable = false;
nixos.enable = false;
};
environment.systemPackages = with pkgs; [
@ -44,8 +64,6 @@
bridge-utils
conntrack-tools
dhcpcd
dhcpdump
dig
ethtool
git
iftop
@ -58,7 +76,6 @@
screen
speedtest-cli
tcpdump
tmux
traceroute
vim
wget
@ -66,25 +83,6 @@
networking.hostName = hostName;
programs = {
fzf.keybindings = true;
git = {
enable = true;
config = {
alias = {
co = "checkout";
lg = "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold y
ow)%d%C(reset)'";
remote = "remote -v";
st = "status";
undo = "reset --soft HEAD^";
};
pull.rebase = true;
rebase.autoStash = true;
};
};
};
users.users.root.initialHashedPassword = "";
system.stateVersion = "20.09";

View File

@ -1,18 +1,11 @@
{ hostName, config, lib, ... }:
let
hostConfig = config.site.hosts.${hostName};
in {
networking.firewall = lib.mkIf hostConfig.firewall.enable {
lib.mkIf config.site.hosts.${hostName}.firewall.enable {
networking.firewall = {
enable = true;
extraCommands = ''
${lib.optionalString hostConfig.isRouter ''
ip46tables -I nixos-fw -p ospfigp -j ACCEPT
''}
ip46tables -A FORWARD -i core -m state --state ESTABLISHED,RELATED -j ACCEPT
ip46tables -A FORWARD -i core -j REJECT
ip46tables -A FORWARD -i core -j REJECT --reject-with net-unreach
'';
extraStopCommands = ''
ip46tables -F FORWARD

View File

@ -1,4 +1,4 @@
{ hostName, config, lib, ... }:
{ hostName, config, lib, pkgs, ... }:
let
# pick an address for a net's gateway
@ -29,7 +29,7 @@ in
builtins.mapAttrs (ifName: { gw4, gw6, ... }:
let
netConfig = config.site.net.${ifName};
in lib.mkIf (config.site.net ? ${ifName}) rec {
in rec {
matchConfig.Name = ifName;
networkConfig = {
@ -57,7 +57,7 @@ in
optional (gw4 != null) config.site.net.${ifName}.hosts4.${gw4} ++
optional (gw6 != null) (findGw6 ifName gw6);
}) config.site.hosts.${hostName}.interfaces;
}) config.site.hosts.${hostName}.physicalInterfaces;
};
# DNS settings

View File

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ pkgs, nixpkgs-master, ... }:
{
boot.kernelModules = [ "kvm-intel" "pppoe" ];
boot.kernelParams = [ "nomodeset" ];
@ -8,20 +8,16 @@
time.timeZone = "Europe/Berlin";
environment.systemPackages = with pkgs; [
git
inetutils # telnet
wget vim git screen
ipmitool
liboping # noping
screen
vim
wget
];
services.openssh = {
enable = true;
settings.PermitRootLogin = "prohibit-password";
};
services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password";
# additional config for bare metal
services.collectd.plugins.ipmi = "";
services.collectd = {
plugins.ipmi = "";
# FIXME: IPMI is only available with nixpkgs-21.11 onwards
package = nixpkgs-master.legacyPackages.${pkgs.system}.collectd;
};
}

View File

@ -1,6 +1,16 @@
{ self, config, lib, pkgs, ... }:
{ hostName, self, config, lib, pkgs, ... }:
let
# Downgrade: lxc-4.0.9 names network interfaces in containers eth0,
# and eth1 despite configured differently.
lxc = pkgs.lxc.overrideAttrs (_: rec {
version = "4.0.6";
src = pkgs.fetchurl {
url = "https://linuxcontainers.org/downloads/lxc/lxc-${version}.tar.gz";
sha256 = "0qz4l7mlhq7hx53q606qgvkyzyr01glsw290v8ppzvxn1fydlrci";
};
});
# Containers that are run on this host
containers =
lib.filterAttrs (_: { role, model, ... }:
@ -10,6 +20,65 @@ let
enabled = containers != {};
# linux iface name max length = 15
shortenNetName = name:
if builtins.match "priv(.*)" name != null
then "p" + builtins.substring 4 9 name
else name;
checkIfname = ifname: let
len = builtins.stringLength ifname;
in if len > 15
then throw "Interface name ${ifname} is ${toString (len - 15)} chars too long."
else ifname;
# `lxc.net.*` formatter for lxc.container.conf files
netConfig = ctName: interfaces:
let
config = map (netName:
let
ifData = interfaces.${netName};
in {
type = ifData.type;
name = checkIfname netName;
flags = "up";
hwaddr = if ifData ? hwaddr && ifData.hwaddr != null
then ifData.hwaddr
else "0A:14:48:xx:xx:xx";
} // (lib.optionalAttrs (ifData.type == "veth") {
veth.pair = checkIfname "${shortenNetName ctName}-${shortenNetName netName}";
veth.mode = checkIfname "bridge";
link = checkIfname netName;
}) // (lib.optionalAttrs (ifData.type == "phys") {
link = checkIfname "ext-${netName}";
})
) (builtins.attrNames interfaces);
serialize = name: x:
if builtins.isString x
then "${name} = ${x}\n"
else if builtins.isAttrs x
then builtins.concatStringsSep "" (
map (n: serialize "${name}.${n}" x.${n}) (builtins.attrNames x)
)
else if builtins.isList x
then
let
enumerate = xs: n:
if xs == []
then []
else [ {
e = builtins.head xs;
i = n;
} ] ++ enumerate (builtins.tail xs) (n + 1);
in
builtins.concatStringsSep "" (
map ({ e, i }: serialize "${name}.${toString i}" e) (enumerate x 0)
)
else throw "Invalid data in lxc net config for ${name}: ${lib.generators.toPretty {} x}";
in
serialize "lxc.net" config;
# User-facing script to build/update container NixOS systems
build-script = pkgs.writeScriptBin "build-container" ''
#! ${pkgs.runtimeShell} -e
@ -30,7 +99,6 @@ let
${ctName})
echo Using prebuilt system for container $c
SYSTEM=${self.packages.x86_64-linux."${ctName}-rootfs"}
CONFIG=${self.packages.x86_64-linux."${ctName}-lxc-config"}
;;
'') (
builtins.attrNames (
@ -42,8 +110,6 @@ let
echo Building $c
nix build -o /nix/var/nix/gcroots/lxc/$c zentralwerk-network#$c-rootfs
SYSTEM=$(readlink /nix/var/nix/gcroots/lxc/$c)
nix build -o /nix/var/nix/gcroots/lxc/$c.config zentralwerk-network#$c-lxc-config
CONFIG=$(readlink /nix/var/nix/gcroots/lxc/$c.config)
;;
esac
@ -56,13 +122,8 @@ let
mkdir -p /var/lib/lxc/$c/rootfs/$d
done
ln -fs $SYSTEM/init /var/lib/lxc/$c/rootfs/init
ln -fs $CONFIG /var/lib/lxc/$c/config
done
# Activate all the desired container after all of them are
# built
set +e
for c in $@; do
set +e
active=$(systemctl is-active lxc@$c)
if [[ "$active" = active ]] ; then
echo Activating $c
@ -74,8 +135,8 @@ let
echo Starting $c
systemctl start lxc@$c
fi
set -e
done
set -e
'';
enable-script = pkgs.writeScriptBin "enable-containers" ''
@ -102,19 +163,64 @@ in
virtualisation.lxc = lib.mkIf enabled {
enable = true;
# Container configs live in /etc so that they can be created
# through `environment.etc`.
systemConfig = ''
lxc.lxcpath = /var/lib/lxc
lxc.lxcpath = /etc/lxc/containers
'';
};
environment.systemPackages = [
# `lxc-attach` et al
pkgs.lxc build-script
lxc build-script
# User scripts
enable-script disable-script
];
environment.etc."lxc/common.conf".source = "${pkgs.lxc}/share/lxc/config/common.conf";
# Create lxc.container.conf files
environment.etc =
builtins.foldl' (etc: ctName: etc // {
"lxc/containers/${ctName}/config" = {
enable = true;
source =
builtins.toFile "${ctName}.conf" ''
# For lxcfs and sane defaults
lxc.include = /etc/lxc/common.conf
lxc.uts.name = ${ctName}
# Handled by lxc@.service
lxc.start.auto = 0
lxc.rootfs.path = /var/lib/lxc/${ctName}/rootfs
lxc.init.cmd = "/init"
lxc.mount.entry = /nix/store nix/store none bind,ro 0 0
lxc.mount.entry = none tmp tmpfs defaults 0 0
lxc,mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.autodev = 1
lxc.tty.max = 0
lxc.pty.max = 8
lxc.cap.drop = sys_module sys_time sys_nice sys_pacct sys_rawio
security.privileged = false
lxc.apparmor.profile = lxc-container-default-with-mounting
lxc.cgroup.memory.limit_in_bytes = 1G
lxc.cgroup.memory.kmem.tcp.limit_in_bytes = 128M
# tuntap
lxc.cgroup.devices.allow = c 10:200 rw
lxc.cgroup2.devices.allow = c 10:200 rw
# ppp
lxc.cgroup.devices.allow = c 108:0 rwm
lxc.cgroup2.devices.allow = c 108:0 rwm
${netConfig ctName containers.${ctName}.physicalInterfaces}
'';
};
}) {
"lxc/common.conf".source = "${lxc}/share/lxc/config/common.conf";
} (builtins.attrNames containers);
# Systemd service template for LXC containers
systemd.services."lxc@" = {
@ -124,14 +230,14 @@ in
"/var/lib/lxc/%i/rootfs/init"
"/etc/start-containers"
];
serviceConfig = with pkgs; {
serviceConfig = {
Type = "simple";
ExecStart = "${lxc}/bin/lxc-start -F -C -n %i";
ExecStop = "${lxc}/bin/lxc-stop -n %i";
ExecReload =
let
script = writeScript "reload-lxc-container.sh" ''
#! ${runtimeShell} -e
script = pkgs.writeScript "reload-lxc-container.sh" ''
#! ${pkgs.runtimeShell} -e
SYSTEM=$(dirname $(readlink /var/lib/lxc/$1/rootfs/init))
exec ${lxc}/bin/lxc-attach -n $1 $SYSTEM/bin/switch-to-configuration switch
@ -143,8 +249,6 @@ in
Restart = "always";
RestartSec = "1s";
};
# Prevent restart on host nixos-rebuild switch
restartIfChanged = false;
};
# Starts all the containers after boot

View File

@ -1,5 +1,5 @@
# Server network configuration
{ config, lib, ... }:
{ hostName, self, config, lib, pkgs, ... }:
let
# LXC containers on this host
@ -49,16 +49,9 @@ in
} // (
builtins.foldl' (result: net: result // {
# Bridges are named just like the corresponding net.
"${net}" = {
netdevConfig = {
Kind = "bridge";
Name = "${net}";
};
extraConfig = ''
[Bridge]
ForwardDelaySec=2
STP=true
'';
"${net}".netdevConfig = {
Kind = "bridge";
Name = "${net}";
};
}) {} bridgeNets
) // (
@ -114,7 +107,5 @@ in
networkConfig.Bridge = net;
};
}) {} ctNets;
wait-online.anyInterface = true;
};
}

View File

@ -1,18 +1,19 @@
{ config, ... }:
{ config, pkgs, ... }:
{
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.devices = [
"/dev/disk/by-path/pci-0000:05:00.0-scsi-0:1:0:0"
"/dev/disk/by-path/pci-0000:05:00.0-scsi-0:1:0:1"
];
networking.hostName = "server1"; # Define your hostname.
networking.hostId = "01010101";
networking.hostId = "12345678";
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "hpsa" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.extraModulePackages = [ ];
nixpkgs.config.allowBroken = true;
boot.zfs.enableUnstable = true;
boot.supportedFilesystems = [ "zfs" ];
boot.initrd.supportedFilesystems = [ "zfs" ];
# Required for Broadcom NICs
@ -44,8 +45,8 @@
};
fileSystems."/boot" =
{ device = "/dev/disk/by-label/ESP";
fsType = "vfat";
{ device = "/dev/disk/by-uuid/23f17e88-ab7e-4dcc-a119-2ed1b9c2c91d";
fsType = "ext2";
};
swapDevices = [ ];

View File

@ -1,12 +1,8 @@
{ ... }:
{ pkgs, ... }:
{
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "usbhid" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
nixpkgs.config.allowBroken = true;
boot.zfs.enableUnstable = true;
boot.supportedFilesystems = [ "zfs" ];
boot.initrd.supportedFilesystems = [ "zfs" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "server2/root";
@ -39,6 +35,7 @@
};
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
networking.hostName = "server2"; # Define your hostname.

479
nix/pkgs/ap.sh Normal file
View File

@ -0,0 +1,479 @@
#!/usr/bin/env bash
{% macro uci_network_mgmt(ifname) -%}
set network.mgmt=interface
set network.mgmt.ifname={{ ifname }}
set network.mgmt.proto=static
set network.mgmt.ipaddr={{ pillar['hosts-inet']['mgmt'][hostname] }}
set network.mgmt.netmask=255.255.255.0
set network.mgmt.gateway={{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}
set network.mgmt.ip6addr={{ pillar['hosts-inet6']['dn42']['mgmt'][hostname] }}/64
set network.mgmt.ip6gw={{ pillar['hosts-inet6']['dn42']['mgmt']['mgmt-gw'] }}
delete network.mgmt.dns
add_list network.mgmt.dns={{ pillar['hosts-inet']['serv']['dnscache'] }}
add_list network.mgmt.dns={{ pillar['hosts-inet6']['dn42']['serv']['dnscache'] }}
{%- endmacro %}
{%- if conf.get('firstboot') %}
ssh-keygen -R 192.168.1.1
ssh root@192.168.1.1 \
"ash -e -x" <<__SSH__
{%- else %}
ssh root@{{ pillar['hosts-inet']['mgmt'][hostname] }} \
"ash -e -x" <<__SSH__
{%- endif %}
# Set root password
echo -e '{{ conf['password'] }}\n{{ conf['password'] }}' | passwd
# add ssh pubkey
echo "{{ pillar['ssh']['pubkey'] }}" > /etc/dropbear/authorized_keys
# System configuration
uci batch <<__UCI__
set system.@system[0].hostname={{ hostname }}
set dhcp.@dnsmasq[0].enabled=0
set system.@system[0].log_ip={{ pillar['hosts-inet']['mgmt']['logging'] }}
set system.@system[0].log_proto=udp
delete network.globals.ula_prefix
delete network.lan
delete network.wan
delete network.wan6
delete wireless.default_radio0
delete wireless.default_radio1
{%- set bridges = {} %}
{%- if conf.get('lan-access') %}
{%- do bridges.__setitem__(conf['lan-access'], True) %}
{%- endif %}
{%- for path, radio in conf['radios'].items() %}
{%- for ssid, ssidconf in radio['ssids'].items() %}
{%- do bridges.__setitem__(ssidconf['net'], True) %}
{%- endfor %}
{%- endfor %}
{%- if conf['model'] == 'TL-WDR4300' %}
{# These models have a shared Ethernet chip for LAN/WAN and therefore need switching #}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='0t 1t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 1t 2 3 4 5'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 1t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endfor %}
{%- elif conf['model'] == 'TL-WR1043ND' %}
{# These models have a shared Ethernet chip with separate CPU ports for LAN/WAN and therefore need switching #}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='5t 6t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
# 0: eth1; 1-4: LAN ports in reverse; 5: WAN port; 6: eth0
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0 1 2 3 4 5t'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='5t 6t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{%- if conf.get('lan-access') == net %}
set network.{{ net }}.ifname='eth1'
{%- else %}
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endif %}
{%- endfor %}
{%- elif conf['model'] == 'TL-Archer-C7v2' %}
{# These models have a shared Ethernet chip with separate CPU ports for LAN/WAN and therefore need switching #}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='1t 6t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
# 0: eth1; 1: WAN port; 2-5: LAN ports; 6: eth0
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0 1t 2 3 4 5'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='1t 6t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{%- if conf.get('lan-access') == net %}
set network.{{ net }}.ifname='eth1'
{%- else %}
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endif %}
{%- endfor %}
{%- elif conf['model'] == 'TL-Archer-C7v4' or conf['model'] == 'TL-Archer-C7v5' %}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='0t 1t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
# 0: eth0; 1: WAN port; 2-5: LAN ports
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 1t 2 3 4 5'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 1t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endfor %}
{%- elif conf['model'] == 'TL-WR1043NDv4' or conf['model'] == 'TL-WR1043NDv5' %}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='0t 5t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
# 0: eth0; 1-4: LAN ports; 5: WAN port
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 1 2 3 4 5t'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='0t 5t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endfor %}
{%- elif conf['model'] == 'TL-WR841Nv8' %}
{# Like v9 but with eth0/1 switched #}
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{# Add WAN VLAN to bridge #}
{%- set ports = ['eth0.' ~ pillar['vlans'][net]] %}
{# Add LAN ports to bridge #}
{%- if conf.get('lan-access') == net %}
{%- do ports.append('eth1') %}
{%- endif %}
set network.{{ net }}.ifname='{{ ' '.join(ports) }}'
{%- endfor %}
{%- elif conf['model'] == 'TL-WR740Nv4' %}
{# Separate eth0/1 interfaces for LAN/WAN #}
{# eth0 - Port 0: eth0, Port 2: LAN1, Port 3: LAN2, Port 4: LAN3, Port 1: LAN4 #}
{# eth1 - WAN #}
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth1.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{# Add WAN VLAN to bridge #}
{%- set ports = ['eth1.' ~ pillar['vlans'][net]] %}
{# Add LAN ports to bridge #}
{%- if conf.get('lan-access') == net %}
{%- do ports.append('eth0') %}
{%- endif %}
set network.{{ net }}.ifname='{{ ' '.join(ports) }}'
{%- endfor %}
{%- elif conf['model'] == 'TL-WA901NDv3' or conf['model'] == 'Ubnt-UniFi-AP-AC-LR' %}
{# Only eth0 exists, no switch #}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{# Add WAN VLAN to bridge #}
set network.{{ net }}.ifname='{{ 'eth0.' ~ pillar['vlans'][net] }}'
{%- endfor %}
{%- elif conf['model'] == 'Ubnt-UAP-nanoHD' %}
{# no switch, eth0 exists but is not usable, using "lan" instead #}
{{ uci_network_mgmt('lan.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{# Add WAN VLAN to bridge #}
set network.{{ net }}.ifname='{{ 'lan.' ~ pillar['vlans'][net] }}'
{%- endfor %}
{%- elif conf['model'] == 'DIR-615H1' or conf['model'] == 'DIR-615D4' %}
{# All DIR-615 share the same port layout #}
delete network.lan_dev
delete network.wan_dev
{# switch is cpu port 6, wan:cpu port 4, lan port 1 is cpu port 3, lan port 2 is 2 etc #}
set network.@switch[0]=switch
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=1
set network.@switch[0].name=switch0
set network.@switch_vlan[0]=switch_vlan
set network.@switch_vlan[0].device='switch0'
set network.@switch_vlan[0].vlan='1'
set network.@switch_vlan[0].ports='4t 6t'
set network.@switch_vlan[0].comment='mgmt'
{%- for net, switchnum in zip(bridges.keys(), range(1, 999)) %}
set network.@switch_vlan[{{ switchnum }}]=switch_vlan
set network.@switch_vlan[{{ switchnum }}].device='switch0'
set network.@switch_vlan[{{ switchnum }}].vlan='{{ pillar['vlans'][net] }}'
{%- if conf.get('lan-access') == net %}
set network.@switch_vlan[{{ switchnum }}].ports='0 1 2 3 4t 6t'
{%- else %}
set network.@switch_vlan[{{ switchnum }}].ports='4t 6t'
{%- endif %}
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
#TODO: this should consider lan-access
set network.{{ net }}.ifname='eth0.{{ pillar['vlans'][net] }}'
{%- endfor %}
{%- else %}
{# All other models may have separate Ethernet chips for LAN/WAN #}
set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth1.1') }}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
set network.{{ net }}.type=bridge
set network.{{ net }}.proto=static
{# Add WAN VLAN to bridge #}
{%- set ports = ['eth1.' ~ pillar['vlans'][net]] %}
{# Add LAN ports to bridge #}
{%- if conf.get('lan-access') == net %}
{%- do ports.append('eth0') %}
{%- endif %}
set network.{{ net }}.ifname='{{ ' '.join(ports) }}'
{%- endfor %}
{%- endif %}
{%- set index = { 'radio': 0, 'iface': 0 } %}
{%- for path, radio in conf['radios'].items() %}
set wireless.radio{{ index.radio }}=wifi-device
set wireless.radio{{ index.radio }}.type=mac80211
set wireless.radio{{ index.radio }}.country=DE
set wireless.radio{{ index.radio }}.channel={{ radio['channel'] }}
set wireless.radio{{ index.radio }}.path={{ path }}
set wireless.radio{{ index.radio }}.hwmode={{ radio.get('hwmode') or '11n' }}
set wireless.radio{{ index.radio }}.htmode={{ radio.get('htmode') or 'HT20' }}
set wireless.radio{{ index.radio }}.noscan=1
delete wireless.radio{{ index.radio }}.disabled
{%- for ssid, ssidconf in radio['ssids'].items() %}
set wireless.wifi{{ index.iface }}=wifi-iface
{%- if radio['channel'] < 15 %}
{%- if conf['version'] == "nightly" %}
{%- set ifprefix = 'wlan2_' %}
{%- else %}
{%- set ifprefix = 'wlan2-' %}
{%- endif %}
{%- else %}
{%- if conf['version'] == "nightly" %}
{%- set ifprefix = 'wlan5_' %}
{%- else %}
{%- set ifprefix = 'wlan5-' %}
{%- endif %}
{%- endif %}
{%- if ssidconf.get('wpa-eap') %}
{%- if conf['version'] == "nightly" %}
{%- set ifsuffix = '_eap' %}
{%- else %}
{%- set ifsuffix = '-eap' %}
{%- endif %}
{%- else %}
{%- set ifsuffix = '' %}
{%- endif %}
set wireless.wifi{{ index.iface }}.ifname={{ ifprefix }}{{ ssidconf['net'] }}{{ ifsuffix }}
set wireless.wifi{{ index.iface }}.device=radio{{ index.radio }}
set wireless.wifi{{ index.iface }}.ssid='{{ ssid }}'
set wireless.wifi{{ index.iface }}.mode=ap
set wireless.wifi{{ index.iface }}.network={{ ssidconf['net'] }}
{%- if ssidconf.get('psk') %}
set wireless.wifi{{ index.iface }}.encryption=psk2
set wireless.wifi{{ index.iface }}.key='{{ ssidconf['psk'] }}'
{%- elif ssidconf.get('wpa-eap') %}
set wireless.wifi{{ index.iface }}.encryption=wpa2
set wireless.wifi{{ index.iface }}.server='{{ ssidconf['wpa-eap']['server'] }}'
set wireless.wifi{{ index.iface }}.port='{{ ssidconf['wpa-eap']['port'] }}'
set wireless.wifi{{ index.iface }}.auth_secret='{{ ssidconf['wpa-eap']['secret'] }}'
{%- else %}
set wireless.wifi{{ index.iface }}.encryption=none
delete wireless.wifi{{ index.iface }}.key
{%- endif %}
set wireless.wifi{{ index.iface }}.mcast_rate=18000
{%- set x = index.update({ 'iface': index.iface + 1 }) %}
{%- endfor %}
{%- set x = index.update({ 'radio': index.radio + 1 }) %}
{%- endfor %}
commit
__UCI__
# Cronjob that makes sure WiFi is only visible when server with all
# the gateways is reachable
cat >/etc/crontabs/root <<__CRON__
* * * * * /usr/sbin/wifi-on-link.sh
__CRON__
cat >/usr/sbin/wifi-on-link.sh <<__SH__
#!/bin/sh
if (ping -c 1 -W 3 {{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}) ; then
REACHABLE=y
else
REACHABLE=n
fi
if [ "\\\$(cat /sys/class/net/wlan2-pub/operstate)" == "up" ] ; then
UP=y
else
UP=n
fi
{%- if conf.get("error-led") %}
ERROR_LED=/sys/class/leds/{{ conf["error-led"] }}/brightness
[ \\\$REACHABLE = y ] && echo 0 > \\\$ERROR_LED
[ \\\$REACHABLE = n ] && echo 1 > \\\$ERROR_LED
{%- endif %}
[ \\\$REACHABLE = y ] && [ \\\$UP = n ] && wifi up
[ \\\$REACHABLE = n ] && [ \\\$UP = y ] && wifi down
exit 0
__SH__
chmod a+rx /usr/sbin/wifi-on-link.sh
/etc/init.d/cron restart
for svc in dnsmasq uhttpd ; do
rm /etc/rc.d/*\$svc
/etc/init.d/\$svc stop
done
{%- if conf.get('firstboot') %}
reboot
{%- endif %}
__SSH__
echo "Base configuration done \\o/"
echo "Later run: `dirname $0`/ap_install_collectd.sh {{ pillar['hosts-inet']['mgmt'][hostname] }}"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
for HOST in $@ ; do
ssh root@$HOST \

View File

@ -1,51 +1,29 @@
{ self, nixpkgs, system, openwrt-imagebuilder }:
{ self, nixpkgs, system }:
let
inherit (self.lib) config;
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
export-openwrt-models = pkgs.writeText "openwrt-models.nix" (
lib.generators.toPretty {} self.lib.openwrtModels
export-config-file = builtins.toFile "config.nix" (
nixpkgs.lib.generators.toPretty {} config
);
export-config = pkgs.writeText "config.nix" (
lib.generators.toPretty {} (
lib.recursiveUpdate
config
{ site.dns.localZones = self.lib.dns.localZones; }
));
encrypt-secrets = pkgs.writeScriptBin "encrypt-secrets" ''
#! ${pkgs.runtimeShell} -e
cd config
exec ${pkgs.gnupg}/bin/gpg --armor --batch --trust-model always \
--encrypt -r 1F0F221A7483B5EF5D103D8B32EBADE870BAF886 \
< secrets-production.nix \
> secrets-production.nix.gpg
'';
decrypt-secrets = pkgs.writeScriptBin "decrypt-secrets" ''
#! ${pkgs.runtimeShell} -e
cd config
[ -e secrets-production.nix ] && \
mv secrets-production.nix secrets-production.nix.old
exec ${pkgs.gnupg}/bin/gpg -d \
> secrets-production.nix \
< secrets-production.nix.gpg
'';
switch-to-production = pkgs.writeScriptBin "decrypt-secrets" ''
#! ${pkgs.runtimeShell} -e
${decrypt-secrets}/bin/decrypt-secrets
cd config
cp secrets-production.nix secrets.nix
export-config = pkgs.runCommandLocal "config.nix" {} ''
cp ${export-config-file} $out
'';
network-cypher-graphs = import ./network-cypher-graphs.nix { inherit config pkgs; };
network-graphs = import ./network-graphs.nix { inherit config lib pkgs; };
salt-pillar-file = hostName: builtins.toFile "${hostName}.yaml" (
nixpkgs.lib.generators.toPretty {} (self.lib.saltPillarFor hostName)
);
salt-pillars = builtins.foldl' (result: hostName: result // {
"${hostName}-pillar" = pkgs.runCommandLocal "${hostName}-pillar.nix" {} ''
cp ${salt-pillar-file hostName} $out
'';
}) {} (
builtins.filter (hostName:
builtins.elem config.site.hosts.${hostName}.role [ "server" "container" ]
) (builtins.attrNames config.site.hosts)
);
mkRootfs = hostName:
self.nixosConfigurations.${hostName}.config.system.build.toplevel;
@ -55,20 +33,7 @@ let
"${hostName}-rootfs" = mkRootfs hostName;
}) {} (
builtins.attrNames (
lib.filterAttrs (_: { role, ... }: builtins.elem role ["server" "container"])
config.site.hosts
)
);
mkLxcConfig = hostName:
self.nixosConfigurations.${hostName}.config.system.build.lxcConfig;
lxc-configs =
builtins.foldl' (rootfs: hostName: rootfs // {
"${hostName}-lxc-config" = mkLxcConfig hostName;
}) {} (
builtins.attrNames (
lib.filterAttrs (_: { role, ... }: role == "container")
nixpkgs.lib.filterAttrs (_: { role, ... }: builtins.elem role ["server" "container"])
config.site.hosts
)
);
@ -76,33 +41,18 @@ let
vm-packages =
builtins.foldl' (rootfs: hostName: rootfs // {
"${hostName}-vm" = self.nixosConfigurations.${hostName}.config.system.build.vm
.overrideAttrs (_oa: {
.overrideAttrs (oa: {
meta.mainProgram = "run-${hostName}-vm";
});
}) {} (
builtins.attrNames (
lib.filterAttrs (_: { role, ... }: role == "server")
nixpkgs.lib.filterAttrs (_: { role, ... }: role == "server")
config.site.hosts
)
);
openwrt = import ./openwrt { inherit self nixpkgs system openwrt-imagebuilder; };
openwrt-packages = builtins.foldl' (images: hostName: images // {
${hostName} = pkgs.writeScriptBin "${hostName}.sh" (
openwrt.sshScript hostName
);
"${hostName}-image" = openwrt.buildImage hostName;
}) {} (
builtins.attrNames (
lib.filterAttrs (_: { role, ... }:
role == "ap"
) config.site.hosts
)
);
device-templates = import ./device-templates.nix {
inherit self nixpkgs system openwrt;
inherit self nixpkgs system;
};
dns-slaves = import ./dns-slaves.nix {
@ -112,30 +62,7 @@ let
starlink = import ./starlink {
inherit pkgs;
};
subnetplans = import ./subnetplans.nix {
inherit self nixpkgs system;
};
gateway-report = import ./gateway-report.nix {
inherit self nixpkgs system;
};
switch-report = import ./switch-report.nix {
inherit self nixpkgs system;
};
vlan-report = import ./vlan-report.nix {
inherit self nixpkgs system;
};
homepage = pkgs.callPackage ./homepage {
inherit self;
};
in
rootfs-packages // lxc-configs // vm-packages // device-templates // openwrt-packages // network-graphs // network-cypher-graphs // starlink // subnetplans // {
inherit export-openwrt-models export-config dns-slaves
encrypt-secrets decrypt-secrets switch-to-production
homepage gateway-report switch-report vlan-report
;
salt-pillars // rootfs-packages // vm-packages // device-templates // starlink // {
inherit export-config dns-slaves;
}

View File

@ -1,23 +1,50 @@
{ self, nixpkgs, system, openwrt }:
{ self, nixpkgs, system }:
with nixpkgs.lib;
let
pkgs = nixpkgs.legacyPackages.${system};
config = self.lib.config;
templates = role: {
ap = _: ./ap.sh;
switch = model: ../../salt/switches + "/${model}.expect";
}.${role};
replaceNetmasks = template:
builtins.toFile (builtins.baseNameOf template) (
builtins.replaceStrings [''{%- import_yaml "netmasks.yaml" as netmasks -%}''] [""] (
builtins.readFile template
)
);
expandTemplate = name: template: data:
self.lib.expandSaltTemplate name (replaceNetmasks template) data;
wrapNixShell = script:
pkgs.runCommand (builtins.baseNameOf script) {
src = script;
} ''
(
echo '#! /usr/bin/env nix-shell'
echo '#! nix-shell -i "expect -f" -p expect telnet'
cat $src
) > $out
chmod a+x $out
'';
device-scripts =
builtins.mapAttrs (hostName: hostConfig@{ role, model, ... }:
pkgs.writeScriptBin "${hostName}.sh" (
let
args = {
inherit self hostName config hostConfig pkgs;
};
in import (./switches + "/${model}.nix") (
args // import ./switches/shared.nix args
)
builtins.mapAttrs (hostname: { role, model, ... }:
wrapNixShell (
expandTemplate "${hostname}.sh" (templates role model) ({
inherit hostname;
pillar = config.salt-pillar;
netmasks = self.lib.netmasks;
logging = config.salt-pillar.hosts-inet.mgmt.logging;
} // optionalAttrs (config.salt-pillar.switches ? ${hostname}) {
switch = config.salt-pillar.switches.${hostname};
} // optionalAttrs (config.salt-pillar.cpe ? ${hostname}) {
conf = config.salt-pillar.cpe.${hostname};
})
)
) (
filterAttrs (_: { role, model, ... }:
role == "switch" && model != "dumb"
filterAttrs (_: { role, ... }:
role == "ap" || role == "switch"
) config.site.hosts
);
@ -31,8 +58,8 @@ let
chmod a+x $out/bin/ap_install_collectd.sh
'' +
builtins.concatStringsSep "\n" (
map (hostName:
"ln -s ${device-scripts.${hostName}}/bin/${hostName}.sh $out/bin/${hostName}.sh"
map (hostname:
"ln -s ${device-scripts.${hostname}} $out/bin/${hostname}.sh"
) (builtins.attrNames device-scripts)
)
);

View File

@ -15,13 +15,13 @@ let
in
writeText "named.slave.conf" (
lib.concatMapStringsSep "\n" ({ name, ... }: ''
lib.concatMapStringsSep "\n" ({ name, ns, ... }: ''
zone "${name}" IN {
type slave;
masters {${mastersStr} };
file "/var/lib/bind/slave/${name}.zone";
allow-notify { ${mastersStr} };
allow-query { any; };
allow-notify {${mastersStr} };
allow-query { all; };
};
'') (
# public zones only

View File

@ -1,120 +0,0 @@
{ self, nixpkgs, system }:
with nixpkgs.legacyPackages.${system};
let
config = self.lib.config;
in
writeText "gateway-report.md" ''
# Gateway Report
${lib.concatMapStrings (net:
let
netConfig = config.site.net.${net};
routers4 = builtins.filter (hostName:
config.site.hosts ? ${hostName} &&
config.site.hosts.${hostName}.isRouter
) (
builtins.attrNames netConfig.hosts4
);
routers6 = builtins.filter (hostName:
config.site.hosts ? ${hostName} &&
config.site.hosts.${hostName}.isRouter
) (lib.unique (
builtins.concatMap builtins.attrNames (builtins.attrValues netConfig.hosts6)
));
upstreamAt = l: n:
if n < builtins.length l
then
let
hostName = builtins.elemAt l n;
hostConfig = config.site.hosts.${hostName};
providers =
map ({ upstream, ... }: upstream.provider) (
builtins.filter ({ upstream, ... }:
upstream.provider or null != null
) (builtins.attrValues hostConfig.interfaces)
);
in
"${hostName}${
lib.optionalString (providers != []) " (${lib.concatStringsSep ", " providers})"
}"
else "";
in
lib.optionalString (net != "core" && (routers4 != [] || routers6 != [])) ''
## Network ${net}
${lib.optionalString (routers4 != []) ''
### IPv4 `${netConfig.subnet4}`
| Address | Name | Primary Uplink | Fallback Uplink |
|-|-|-|-|
${lib.concatMapStrings (hostName:
let
hostConfig = config.site.hosts.${hostName};
isDhcpDefault = hostName == netConfig.dhcp.router or null;
emphasize = s:
if isDhcpDefault
then "**${s}**"
else s;
upstream4a = upstreamAt hostConfig.ospf.allowedUpstreams 0;
upstream4b = upstreamAt hostConfig.ospf.allowedUpstreams 1;
in ''
|${
emphasize netConfig.hosts4.${hostName}
}|${
emphasize hostName
}|${
upstream4a
}|${
upstream4b
}|
''
) (lib.naturalSort routers4)}
''}
${lib.optionalString (routers6 != {}) ''
### IPv6 ${lib.concatStringsSep " " (
map (s: "`${s}`") (
builtins.attrValues netConfig.subnets6
)
)}
| Address | Name | Primary Uplink | Fallback Uplink |
|-|-|-|-|
${lib.concatMapStrings (hostName:
let
hostConfig = config.site.hosts.${hostName};
isIpv6Router = hostName == netConfig.ipv6Router;
emphasize = s:
if isIpv6Router
then "**${s}**"
else s;
upstream6a = upstreamAt hostConfig.ospf.allowedUpstreams6 0;
upstream6b = upstreamAt hostConfig.ospf.allowedUpstreams6 1;
in ''
|${
emphasize (
lib.concatMapStringsSep " " (ctx:
"`${netConfig.hosts6.${ctx}.${hostName}}`"
) (builtins.filter (ctx:
netConfig.hosts6.${ctx} ? ${hostName}
) (builtins.attrNames netConfig.hosts6))
)
}|${
emphasize hostName
}|${
upstream6a
}|${
upstream6b
}|
''
) (lib.naturalSort routers6)}
''}
''
) (lib.naturalSort (builtins.attrNames config.site.net))}
''

View File

@ -1,74 +0,0 @@
{ self
, system
, substituteAll
, stdenv
, pandoc
, bat
, ansi2html
}:
let
inherit (self.packages.${system})
export-config
gateway-report network-graphs
subnetplans switch-report vlan-report;
in
stdenv.mkDerivation {
pname = "zentralwerk-network-homepage";
version = self.lastModifiedDate;
src = ./src;
nativeBuildInputs = [
pandoc
bat
ansi2html
];
buildPhase = ''
pandoc -t html ${../../../doc/hello.md} > index.html
cat ${./linked-data.html} >> index.html
pandoc -t html ${../../../doc/vpn.md} > vpn.html
pandoc -t html ${gateway-report} > gateway-report.html
cat ${../../../cabling.md} ${switch-report} >> switch-report.md
pandoc -t html switch-report.md > switch-report.html
pandoc -t html ${vlan-report} > vlan-report.html
echo '<pre>' > config.html
bat --color=always --theme=GitHub -p ${export-config} | \
ansi2html -il >> config.html
echo '</pre>' >> config.html
ln -s ${substituteAll {
src = ./figure.html;
img = "physical.png";
caption = "Physikalische Netzwerkstruktur";
}} physical.html
ln -s ${substituteAll {
src = ./figure.html;
img = "logical.png";
caption = "Logische Netzwerkstruktur";
}} logical.html
ln -s ${subnetplans}/share/doc/zentralwerk/* .
for F in *.html; do
cat ${./header.html} "$F" ${./footer.html} > "$F.new"
rm "$F"
mv "$F.new" "$F"
done
'';
installPhase = ''
DIR=$out/share/doc/zentralwerk/www
mkdir -p $DIR
ln -s ${network-graphs}/share/doc/zentralwerk/* $DIR/
ln -s ${../../../doc/core.png} $DIR/core.png
ln -s ${./security.txt} $DIR/security.txt
cp *.{html,css,png,svg} $DIR/
mkdir -p $out/nix-support
echo doc homepage $DIR index.html >> $out/nix-support/hydra-build-products
'';
}

View File

@ -1,4 +0,0 @@
<figure>
<img src="@img@" alt="@caption@">
<figcaption>@caption@</figcaption>
</figure>

View File

@ -1,14 +0,0 @@
</main>
<footer>
<p>
<a href="https://gitea.c3d2.de/zentralwerk/network">git</a>
vom
<a href="https://www.c3d2.de/">C3D2</a>
im
<a href="https://www.zentralwerk.de/">Zentralwerk</a>
</p>
</footer>
</body>
</html>

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="de" xml:lang="de">
<head>
<title>zentralwerk.org</title>
<meta charset="utf-8"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta name="robots" content="index,follow"/>
<meta name="description" content="zentralwerk-network"/>
<meta name="keywords" content="c3d2 ccc zentralwerk network"/>
<meta name="generator" content="zentralwerk-network"/>
<meta name="language" content="de"/>
<meta name="author" content="Astro"/>
<link rel="icon" type="image/png" href="zw.png"/>
<link rel="stylesheet" title="Default" type="text/css" href="style.css"/>
</head>
<body>
<header>
<h1>zentralwerk-network</h1>
<nav>
<ul>
<li><a href=".">Willkommen</a></li>
<li><a href="gateway-report.html">Router</a></li>
<li><a href="subnetplan4.html">IPv4</a></li>
<li><a href="subnetplan6.html">IPv6</a></li>
<li><a href="vlan-report.html">VLAN</a></li>
<li><a href="logical.html">Logisch</a></li>
<li><a href="physical.html">Physisch</a></li>
<li><a href="switch-report.html">Ports</a></li>
<li><a href="vpn.html">VPN</a></li>
<li><a href="config.html">Config</a></li>
</ul>
</nav>
</header>
<main>

View File

@ -1,30 +0,0 @@
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Project",
"name": "zentralwerk-network",
"image": "https://www.zentralwerk.org/zw.png",
"logo": "https://www.zentralwerk.org/zw.png",
"@id": "https://www.zentralwerk.org/",
"url": "https://www.zentralwerk.org/",
"sameAs": "https://gitea.c3d2.de/zentralwerk/network",
"address": {
"@type": "PostalAddress",
"streetAddress": "Riesaer Str. 32",
"addressLocality": "Dresden",
"postalCode": "01127",
"addressCountry": "DE"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 51.0811716,
"longitude": 13.7285633
},
"parentOrganization": {
"@type": "NGO",
"name": "Chaos Computer Club Dresden",
"@id": "https://www.c3d2.de/whois.html",
"url": "https://www.c3d2.de/whois.html"
}
}
</script>

View File

@ -1,3 +0,0 @@
Contact: mailto:astro@spaceboyz.net
Preferred-Languages: en, de
Hiring: https://www.c3d2.de/space.html

View File

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1920"
height="1080"
viewBox="0 0 507.99999 285.75"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="background.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#af1f00"
bordercolor="#cccccc"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.23657384"
inkscape:cx="1470.9995"
inkscape:cy="762.97531"
inkscape:window-width="1916"
inkscape:window-height="1057"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:type="star"
style="opacity:0.349515;fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#ffff00;stroke-width:1.21295;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
id="path163"
inkscape:flatsided="false"
sodipodi:sides="48"
sodipodi:cx="266.05429"
sodipodi:cy="144.57217"
sodipodi:r1="179.29391"
sodipodi:r2="44.823479"
sodipodi:arg1="0.36233915"
sodipodi:arg2="0.427789"
inkscape:rounded="-2.4286129e-17"
inkscape:randomized="0"
d="m 433.7067,208.12514 -126.86819,-44.95749 117.13857,66.29681 -119.91468,-61.13249 107.48296,81.01929 -110.9094,-76.26151 95.98829,94.3555 -100.00643,-90.08565 82.85123,106.07726 -87.39232,-102.36842 68.29656,115.98401 -73.28291,-112.89964 52.57333,123.90625 -57.9196,-121.4991 35.95054,129.7084 -41.56528,-128.01968 18.71264,133.29122 -24.49976,-132.34982 1.15456,134.59339 -7.01505,-134.41541 -16.42328,133.59262 10.5897,-134.18111 -33.72012,130.30605 28.01326,-131.65094 -50.43999,124.7899 44.95749,-126.86818 -66.29682,117.13856 61.1325,-119.91468 -81.01929,107.48296 76.2615,-110.9094 -94.3555,95.98829 90.08566,-100.00643 -106.07726,82.85123 102.36842,-87.39232 -115.98401,68.29657 112.89963,-73.28291 -123.90624,52.57332 121.4991,-57.9196 L 94.358103,196.21253 222.37779,154.64726 89.086568,173.35989 221.43638,148.86013 86.843,150.01469 221.2584,142.99964 87.665786,126.57636 221.8469,137.16606 91.54085,103.44594 223.19179,131.4592 98.401887,81.01921 225.27007,125.9767 108.1315,59.679885 228.04618,120.81238 120.56322,39.79309 231.47262,116.0546 135.48433,21.699094 235.49076,111.78475 152.63953,5.7074906 240.03185,108.07591 171.73529,-7.9080994 245.0182,104.99153 192.44487,-18.91471 250.36448,102.58439 214.41393,-27.124014 255.97921,100.89567 237.26657,-32.395549 261.76633,99.954267 260.61178,-34.639117 267.62682,99.776288 284.0501,-33.81633 273.4604,100.36478 307.18052,-29.941267 279.16727,101.70967 329.60725,-23.08023 284.64976,103.78795 350.94658,-13.350614 289.81408,106.56406 370.83338,-0.91889557 294.57187,109.9905 388.92737,14.002216 298.84171,114.00865 404.91897,31.157415 302.55055,118.54974 418.53456,50.253172 305.63493,123.53608 429.54117,70.962754 308.04207,128.88236 437.75048,92.931813 309.73079,134.49709 443.02201,115.78445 310.6722,140.28421 445.26558,139.12966 310.85018,146.1447 444.4428,162.56799 310.26169,151.97829 440.56773,185.6984 308.91679,157.68515 Z"
inkscape:transform-center-x="-1.5557927e-05"
inkscape:transform-center-y="1.8543187e-05"
transform="matrix(3.4901017,0,0,3.4901017,-674.55653,24.83979)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,108 +0,0 @@
body {
margin: 0;
padding: 0;
background: #AF1F00 url('background.svg') fixed no-repeat center/100%;
color: #FFEF00;
font-family: sans-serif;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100vh;
}
a {
text-decoration: none;
font-weight: bold;
color: #CF3F1F;
}
a:visited {
color: #AF1F00;
}
header, footer {
background-color: #FFEF00;
color: #AF1F00;
text-align: center;
}
header {
margin: 0 0 3rem;
border-bottom: 1rem solid #DFCF00;
}
header h1 {
margin: 0.5rem 0;
padding: 0rem 2rem;
font-family: serif;
}
footer {
margin: 3rem 0 0;
}
footer p {
margin: 0.3rem 0;
}
nav ul {
margin: 0.3rem 0;
padding: 0;
list-style-type: none;
}
nav ul li {
display: inline-block;
margin: 0 2rem;
padding: 0;
}
main {
min-width: 50vh;
max-width: 60rem;
margin: 1rem auto;
padding: 1.5rem 4rem;
background: #FFF;
color: #111;
border-top: 1rem solid #EFDF00;
border-left: 1rem solid #EFDF00;
border-right: 1.5rem solid #DFCF00;
border-bottom: 1.5rem solid #DFCF00;
overflow: auto;
}
main figure {
text-align: center;
}
main figure img {
max-width: 100%;
}
main table {
font-size: 75%;
border-collapse: collapse;
border: 1px solid #555;
}
main table th {
background-color: #555;
color: #FFF;
padding: 0.4rem;
}
main table td {
border: 1px solid #555;
padding: 0.2rem 0.4rem;
}
main h3 code {
margin-left: 0.6rem;
}
.sourceCode .kw {
font-weight: bold;
color: #1F2F6F;
}
.sourceCode .dt {
font-weight: bold;
color: #1F5F2F;
}
.sourceCode .ot {
color: #666;
}
.sourceCode .st {
color: #007F00;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,172 +0,0 @@
{ config, pkgs, ... }:
let
inherit (pkgs) lib runCommand curl;
netColor = net:
if net == "core"
then "grey"
else if net == "mgmt"
then "brown"
else if builtins.elem net [ "c3d2" "serv" "cluster" ]
then "green"
else if builtins.match "up.+" net != null ||
builtins.match "anon.+" net != null
then "red"
else if builtins.match "priv.+" net != null
then "blue"
else "black";
dedupLinks = links:
builtins.attrValues (
builtins.foldl' (result: { pair, attrs ? {}, startLabel ? "" }:
let
peer1 = builtins.elemAt pair 0;
peer2 = builtins.elemAt pair 1;
sorted =
if peer1 < peer2
then { from = peer1; to = peer2; }
else { from = peer2; to = peer1; };
key = with sorted; "${from} ${to}";
prevAttrs =
if result ? ${key}
then result.${key}.attrs
else {};
newAttrs = attrs // (
if peer1 < peer2
then { taillabel = startLabel; }
else { headlabel = startLabel; }
);
in result // {
"${key}" = {
inherit (sorted) from to;
attrs = prevAttrs // newAttrs;
};
}
) {} links
);
toCypher = { nodes, links, ... }: ''
${lib.concatMapStringsSep " " (name:
let
nodeAttrs = nodes.${name};
id = builtins.replaceStrings ["-"] ["_"] name;
nodeType = nodeAttrs.type;
in ''
CREATE (${id}:`${nodeType}`
{label:"${name}",
${lib.concatMapStringsSep ", " (attr:
let
value = nodeAttrs.${attr};
in
if builtins.isString value
then "${attr}:\"${value}\""
else "${attr}:${toString value}"
) (builtins.attrNames nodeAttrs)}
}
)
'') (builtins.attrNames nodes)}
${lib.concatMapStringsSep " " ({ from, to, attrs }:
let
idFrom = builtins.replaceStrings ["-"] ["_"] from;
idTo = builtins.replaceStrings ["-"] ["_"] to;
in ''
CREATE (${idFrom})-[:CONNECTED_TO
{${lib.concatMapStringsSep ", " (attr:
let
value = attrs.${attr};
in
if builtins.isString value
then "${attr}:\"${value}\""
else "${attr}:${toString value}"
) (builtins.attrNames attrs)}
}]->(${idTo})
'') (dedupLinks links)}
'';
cypherGraph = args@{ name, ... }:
runCommand "${name}.cypher" {
src = builtins.toFile "${name}.cypher" (
toCypher args
);
} ''
sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' $src | tr -d '\n' > $out
'';
in rec {
# Layer 2
physical-cypher-graph = cypherGraph {
name = "physical";
nodes =
builtins.mapAttrs (_: { role, ... }: {
type = {
switch = "Switch";
server = "Server";
container = "Container";
ap = "AccessPoint";
client = "Client";
}.${role};
}) (
lib.filterAttrs (_: { links, ... }:
links != {}
) config.site.hosts
) // builtins.mapAttrs (_: _: {
type = "Other";
}) (
lib.filterAttrs (net: _:
builtins.match "up.*" net != null
) config.site.net
);
links =
builtins.concatMap (hostName:
map (link: {
pair = [ hostName link ];
startLabel = ( #lib.optionalString (config.site.hosts.${hostName}.links ? ${link}) (
lib.concatStringsSep ","
config.site.hosts.${hostName}.links.${link}.ports
);
}) (
builtins.filter (link:
config.site.hosts ? ${link}
||
builtins.match "up.*" link != null
) (builtins.attrNames config.site.hosts.${hostName}.links)
)
) (builtins.attrNames config.site.hosts);
};
# Layer 3
logical-cypher-graph =
let
containers =
lib.filterAttrs (_: { role, ... }:
role == "container"
) config.site.hosts;
in
cypherGraph {
name = "logical";
nodes =
builtins.foldl' (result: hostName:
result // {
"${hostName}".type = "Container";
} // builtins.mapAttrs (_: _: {
type = "Container";
}) containers.${hostName}.interfaces
) {} (builtins.attrNames containers);
links =
builtins.concatMap (hostName:
map (net: {
pair = [ hostName net ];
attrs.color = netColor net;
}) (builtins.attrNames containers.${hostName}.interfaces)
) (builtins.attrNames containers);
};
import-network-graphs = with pkgs; writeScriptBin "import-network-graphs" ''
#! ${runtimeShell} -e
${curl}/bin/curl -X POST -H 'Content-type: application/json' http://localhost:7474/db/data/transaction/commit -d "{\"statements\": [{\"statement\": \"$(cat ${physical-cypher-graph})\"}]}"
${curl}/bin/curl -X POST -H 'Content-type: application/json' http://localhost:7474/db/data/transaction/commit -d "{\"statements\": [{\"statement\": \"$(cat ${logical-cypher-graph})\"}]}"
'';
}

View File

@ -1,173 +0,0 @@
{ config, lib, pkgs, ... }:
let
netColor = net:
if net == "core"
then "grey"
else if net == "mgmt"
then "brown"
else if net == "roof"
then "orange"
else if net == "flpk"
then "yellow"
else if builtins.elem net [ "c3d2" "serv" "cluster" ]
then "green"
else if builtins.match "up.+" net != null ||
builtins.match "anon.+" net != null
then "red"
else if builtins.match "priv.+" net != null
then "blue"
else "black";
dedupLinks = links:
builtins.attrValues (
builtins.foldl' (result: { pair, attrs ? {}, startLabel ? "" }:
let
peer1 = builtins.elemAt pair 0;
peer2 = builtins.elemAt pair 1;
sorted =
if peer1 < peer2
then { from = peer1; to = peer2; }
else { from = peer2; to = peer1; };
key = with sorted; "${from} ${to}";
prevAttrs =
if result ? ${key}
then result.${key}.attrs
else {};
newAttrs = attrs // (
if peer1 < peer2
then { taillabel = startLabel; }
else { headlabel = startLabel; }
);
in result // {
"${key}" = {
inherit (sorted) from to;
attrs = prevAttrs // newAttrs;
};
}
) {} links
);
toDot = { nodes, links, ... }: ''
digraph {
graph [splines=ortho, nodesep=64]
node []
edge [arrowhead=none, labelfontsize=8]
${lib.concatMapStrings (name:
let
nodeAttrs = nodes.${name};
in ''
"${name}" [${lib.concatMapStringsSep ", " (attr:
let
value = nodeAttrs.${attr};
in
if builtins.isString value
then "${attr}=\"${value}\""
else "${attr}=${toString value}"
) (builtins.attrNames nodeAttrs)}]
'') (builtins.attrNames nodes)}
${lib.concatMapStrings ({ from, to, attrs }: ''
"${from}" -> "${to}" [${lib.concatMapStringsSep ", " (attr:
let
value = attrs.${attr};
in
if builtins.isString value
then "${attr}=\"${value}\""
else "${attr}=${toString value}"
) (builtins.attrNames attrs)}]
'') (dedupLinks links)}
}
'';
renderGraph = args@{ name, engine, ... }:
pkgs.runCommand "${name}.png" {
src = builtins.toFile "${name}.dot" (
toDot args
);
} ''
echo $src
${pkgs.graphviz-nox}/bin/${engine} -Tpng $src > $out
'';
in rec {
# Layer 2
physical-graph = renderGraph {
name = "physical";
engine = "fdp";
nodes =
builtins.mapAttrs (_: { role, ... }: {
shape = {
switch = "box";
server = "house";
container = "oval";
ap = "doubleoctagon";
client = "doublecircle";
}.${role};
}) (
lib.filterAttrs (_: { links, ... }:
links != {}
) config.site.hosts
) // builtins.mapAttrs (_: _: {
shape = "circle";
}) (
lib.filterAttrs (net: _:
builtins.match "up.*" net != null
) config.site.net
);
links =
builtins.concatMap (hostName:
map (link: {
pair = [ hostName link ];
startLabel = ( #lib.optionalString (config.site.hosts.${hostName}.links ? ${link}) (
lib.concatStringsSep ","
config.site.hosts.${hostName}.links.${link}.ports
);
}) (
builtins.filter (link:
config.site.hosts ? ${link}
||
builtins.match "up.*" link != null
) (builtins.attrNames config.site.hosts.${hostName}.links)
)
) (builtins.attrNames config.site.hosts);
};
# Layer 3
logical-graph =
let
containers =
lib.filterAttrs (_: { role, ... }:
role == "container"
) config.site.hosts;
in
renderGraph {
name = "logical";
engine = "sfdp";
nodes =
builtins.foldl' (result: hostName:
result // {
"${hostName}".shape = "box";
} // builtins.mapAttrs (_: _: {
shape = "circle";
}) containers.${hostName}.interfaces
) {} (builtins.attrNames containers);
links =
builtins.concatMap (hostName:
map (net: {
pair = [ hostName net ];
attrs.color = netColor net;
}) (builtins.attrNames containers.${hostName}.interfaces)
) (builtins.attrNames containers);
};
network-graphs = pkgs.runCommand "network-graphs" {} ''
DIR=$out/share/doc/zentralwerk
mkdir -p $DIR
ln -s ${physical-graph} $DIR/physical.png
ln -s ${logical-graph} $DIR/logical.png
mkdir -p $out/nix-support
echo doc image $DIR/physical.png >> $out/nix-support/hydra-build-products
echo doc image $DIR/logical.png >> $out/nix-support/hydra-build-products
'';
}

View File

@ -1,144 +0,0 @@
{ self, nixpkgs, system, openwrt-imagebuilder }:
let
inherit (self.lib) config;
pkgs = nixpkgs.legacyPackages.${system};
uciConfig = hostName: import ./uci-config.nix { inherit self pkgs hostName; };
modelPackages = {
"tplink_archer-c7-v2" = [
"-kmod-ath10k-ct" "-ath10k-firmware-qca988x-ct-full-htt" "-ath10k-firmware-qca988x-ct"
"kmod-ath10k" "ath10k-firmware-qca988x"
];
"tplink_archer-c7-v5" = [
"-kmod-ath10k-ct" "-ath10k-firmware-qca988x-ct" "-ath10k-firmware-qca988x-ct-full-htt"
"kmod-ath10k" "ath10k-firmware-qca988x"
];
"ubnt_unifiac-lite" = [
"-kmod-ath10k-ct" "-ath10k-firmware-qca988x-ct"
"kmod-ath10k" "ath10k-firmware-qca988x"
];
"ubnt_unifiac-mesh" = [
"-kmod-ath10k-ct" "-ath10k-firmware-qca988x-ct"
"kmod-ath10k" "ath10k-firmware-qca988x"
];
"dir-615-d" = [
# flash size reasons
"-wpad-openssl"
"-tcpdump"
"wpad-wolfssl"
];
};
in rec {
sshScript = hostName:
let
address = config.site.net.mgmt.hosts4.${hostName};
in ''
#! ${pkgs.runtimeShell} -e
ssh root@${address} "cat > /tmp/openwrt-image" < ${buildImage hostName}/openwrt-*-${hostName}-*-sysupgrade.bin
ssh root@${address} "sysupgrade -n /tmp/openwrt-image" || true
# ssh hostkey will have changed after boot
ssh-keygen -R ${address}
'';
buildImage = hostName:
let
hostConfig = config.site.hosts.${hostName};
hasVxlan = builtins.any ({ type, ... }:
type == "vxlan"
) (builtins.attrValues hostConfig.interfaces);
inherit (hostConfig) model;
matches = (openwrt-imagebuilder.lib.profiles {
inherit pkgs;
}).identifyProfiles model;
fallbackProfile =
if model == "dir-615-d"
then (openwrt-imagebuilder.lib.profiles {
inherit pkgs;
release = "19.07.10";
}).identifyProfile model
else if builtins.match "tl-wr[78].*" model != null
then {
release = "18.06.9";
packagesArch = "mips_24kc";
target = "ar71xx";
variant = "tiny";
profile = model;
sha256 = "sha256-P7BJI6n6s53szYXKshnJRKL2fLIYgJLPiq/yd0oRKoE=";
feedsSha256 = {
base.sha256 = "sha256-IbND2snJ1UrDRhvGQIRxzGuSpftQ+AyiWqaVZqbGdHY=";
packages.sha256 = "sha256-18UvzdUL98CranBtzAY7hoUlEvafUdssAQOuqDQi4BU=";
};
}
else null;
build = args:
openwrt-imagebuilder.lib.build (args // {
extraImageName = "zw-${hostName}";
packages = [
# remove unused default .ipk
"-dnsmasq" "-firewall" "-firewall4"
"-ppp" "-ppp-mod-pppoe" "-kmod-ppp" "-kmod-pppoe" "-kmod-pppox"
"-iptables" "-ip6tables" "-kmod-ipt-offload"
"-odhcp6c" "-odhcpd-ipv6only"
"-wpad-basic-mbedtls"
# monitoring
"collectd"
"collectd-mod-iwinfo" "collectd-mod-network"
"collectd-mod-interface" "collectd-mod-load" "collectd-mod-cpu"
"collectd-mod-exec"
] ++ (
if args.variant != "tiny"
then [
# debugging
"htop"
"tcpdump"
# wpa3
"-wpad-basic-wolfssl" "-wpad-mini"
"wpad-openssl"
"usteer"
] else [
# debugging
"tcpdump-mini"
# wpa3
"-wpad-openssl" "-wpad-mini"
"wpad-wolfssl"
]
) ++ nixpkgs.lib.optionals hasVxlan [
"vxlan" "kmod-vxlan"
] ++ modelPackages.${model} or [];
disabledServices = [ "dnsmasq" "uhttpd" ];
files = pkgs.runCommandNoCC "image-files" {} ''
mkdir -p $out/etc/uci-defaults
cat > $out/etc/uci-defaults/99-zentralwerk <<EOF
${uciConfig hostName}
EOF
mkdir -p $out/usr/{bin,sbin}
cp ${./usteer-info.sh} $out/usr/sbin/usteer-info.sh
cp ${./usteer-stats.sh} $out/usr/bin/usteer-stats.sh
chmod +x $out/usr/bin/*.sh $out/usr/sbin/*.sh
'';
});
in
if matches == [] && fallbackProfile != null
then build fallbackProfile
else if matches == []
then builtins.trace "${hostName} (${model}) not supported by OpenWRT"
null
else if builtins.length matches == 1
then build (builtins.elemAt matches 0)
else builtins.trace "${hostName} (${model}) has multiple models!" (
build (builtins.elemAt matches 0)
);
}

View File

@ -1,528 +0,0 @@
{ self, pkgs, hostName }:
with pkgs;
with lib;
let
inherit (self.lib) config;
hostConfig = config.site.hosts.${hostName};
ports = self.lib.getOpenwrtPorts hostConfig.model;
uciDeleteAll = key: ''
while uci -q delete ${key}[-1]; do :; done
'';
openwrtModel = self.lib.getOpenwrtModel hostConfig.model;
hasSwitch =
if hostConfig.model == "ubnt_unifiac-mesh"
# ours don't come with a switch.
then false
else
openwrtModel ? ports
&&
any ({ switch ? null, ... }: switch != null)
(builtins.attrValues openwrtModel.ports);
hasDSA = (
all ({ switch ? null, ... }:
switch == null
) (builtins.attrValues openwrtModel.ports or {})
&&
any ({ port ? null, interface ? null, ... }:
port != null &&
interface != null &&
port == interface
) (builtins.attrValues openwrtModel.ports or {})
) || hostConfig.model == "ubnt_unifi-usg";
portsDoc =
let
portByIndex = builtins.foldl' (result: port:
let
key = if port ? index
then port.index
else if port ? interface
then port.interface
else "How to identify port ${generators.toPretty {} port}?";
in result // {
"${key}" = port;
}
) {} (builtins.attrValues openwrtModel.ports);
in
concatMapStringsSep ", " (index:
"${index}:${
if portByIndex.${index} ? port
then portByIndex.${index}.port
else if portByIndex.${index} ? interface
then portByIndex.${index}.interface
else throw "${hostName}: What is port ${generators.toPretty {} portByIndex.${index}.port}?"
}"
) (
builtins.sort builtins.lessThan (
builtins.attrNames portByIndex
)
);
switchHostInterface =
let
hostPorts = sort builtins.lessThan (
map ({ interface, ... }: interface) (
builtins.attrValues (
filterAttrs (_: { type, ... }: type == "host")
openwrtModel.ports
)
)
);
in if hostPorts == []
then throw "${hostName}: No host ports found for OpenWRT model ${hostConfig.model}"
else builtins.head hostPorts;
switchPortIndices = f:
map ({ index, ... }: index) (
builtins.attrValues (
filterAttrs (_: port: port ? index && f port)
openwrtModel.ports
)
);
trunked = map (index: "${index}t");
# OpenWRT switch ports string ("0t 1t 2 3 4") for a network (VLAN)
switchPortsConfig = net:
concatStringsSep " " (
# Host interface
trunked (switchPortIndices ({ interface ? null, ... }: interface == switchHostInterface))
++
# Access networks
optionals (hostConfig.links ? ${net}) (
builtins.concatMap (port':
switchPortIndices ({ port ? null, ... }: port == port')
) hostConfig.links.${net}.ports
)
++
# Trunk ports
builtins.concatMap (port':
trunked (switchPortIndices ({ port ? null, ... }: port == port'))
) (
builtins.concatMap ({ ports, ... }: ports) (
builtins.attrValues (
filterAttrs (_: { trunk, ... }:
trunk
) hostConfig.links
))
)
);
dsaPorts = net:
unique (
concatMap ({ ports, ... }: ports) (
builtins.filter ({ nets, ... }: builtins.elem net nets)
(builtins.attrValues hostConfig.links)
));
dsaPortType = net: port:
if any ({ ports, trunk, ... }: trunk && builtins.elem port ports) (
builtins.attrValues hostConfig.links
) || hostConfig.links.${net}.trunk or true
then "t"
else "u*";
networkInterfaces = net:
let
inherit (config.site.net.${net}) vlan;
in unique (
builtins.concatMap ({ trunk, ports, switch ? null, ... }:
builtins.concatMap (port:
builtins.concatMap (portData:
if portData ? port && port == portData.port
then [ ((
if portData ? switch
then switchHostInterface
else if portData ? interface
then portData.interface
else throw "${hostName}: Cannot find interface for ${port} on OpenWRT model ${hostConfig.model}"
) + (
if trunk || switch != null
then ".${toString vlan}"
else ""
)) ]
else []
) (builtins.attrValues openwrtModel.ports)
++
optionals (hostConfig.interfaces ? ${port} && vlan != null) [ "${port}.${toString vlan}" ]
) ports
) (
builtins.attrValues (
filterAttrs (link: { nets, ... }:
link == net || builtins.elem net nets
) hostConfig.links
)
)
);
mgmtInterface =
if hasDSA
then "br0.${toString config.site.net.mgmt.vlan}"
else
let
mgmtInterfaces = networkInterfaces "mgmt";
in if builtins.length mgmtInterfaces == 1
then builtins.head mgmtInterfaces
else "br-mgmt";
in
''
# Set root password
echo -e '${hostConfig.password}\n${hostConfig.password}' | passwd
# add ssh pubkeys
${concatMapStrings (sshPubKey: ''
echo "${sshPubKey}" > /etc/dropbear/authorized_keys
'') config.site.sshPubKeys}
# System configuration
${uciDeleteAll "network.@switch_vlan"}
${uciDeleteAll "wireless.@wifi-iface"}
uci set system.@system[0].hostname=${hostName}
uci set dhcp.@dnsmasq[0].enabled=0
uci set system.@system[0].log_ip=${config.site.net.mgmt.hosts4.logging}
uci set system.@system[0].log_proto=udp
${optionalString hasSwitch ''
# Switch config
# Ports ${portsDoc}
${concatMapStrings (net: ''
uci add network switch_vlan
uci set network.@switch_vlan[-1]=switch_vlan
uci set network.@switch_vlan[-1].device='switch0'
uci set network.@switch_vlan[-1].vlan='${toString config.site.net.${net}.vlan}'
uci set network.@switch_vlan[-1].ports='${switchPortsConfig net}'
uci set network.@switch_vlan[-1].comment='${net}'
'') (
sort (net1: net2:
config.site.net.${net1}.vlan < config.site.net.${net2}.vlan
) (
unique (
builtins.concatMap ({ nets, ... }: nets)
(builtins.attrValues hostConfig.links)
)
)
)}
''}
${optionalString hasDSA ''
# DSA
${uciDeleteAll "network.@device"}
uci add network device
uci set network.@device[-1].name='br0'
uci set network.@device[-1].type='bridge'
${concatMapStrings (port: ''
uci add_list network.@device[-1].ports='${port}'
'') (
unique (
builtins.concatMap ({ ports, ... }: ports)
(builtins.attrValues hostConfig.links)
)
)}
uci set network.br0='interface'
uci set network.br0.proto='none'
uci set network.br0.device='br0'
${concatMapStrings (net: ''
uci add network bridge-vlan
uci set network.@bridge-vlan[-1].device='br0'
uci set network.@bridge-vlan[-1].vlan='${toString config.site.net.${net}.vlan}'
${concatMapStrings (port: ''
uci add_list network.@bridge-vlan[-1].ports='${port}:${dsaPortType net port}'
'') (dsaPorts net)}
'') (
sort (net1: net2:
config.site.net.${net1}.vlan < config.site.net.${net2}.vlan
) (
unique (
builtins.concatMap ({ nets, ... }: nets)
(builtins.attrValues hostConfig.links)
)
)
)}
''}
# mgmt network
uci set network.mgmt=interface
${if hasDSA
then ''
uci set network.mgmt.device='br0.${toString config.site.net.mgmt.vlan}'
'' else ''
uci set network.mgmt.ifname='${
if builtins.length (networkInterfaces "mgmt") > 0
then concatStringsSep " " (networkInterfaces "mgmt")
else throw "${hostName}: No interface for mgmt"
}'
''}
uci set network.mgmt.proto=static
${optionalString (hostConfig.interfaces.mgmt.type == "bridge") ''
uci set network.mgmt.type=bridge
''}
uci set network.mgmt.ipaddr=${config.site.net.mgmt.hosts4.${hostName}}
uci set network.mgmt.netmask=${self.lib.netmasks.${toString config.site.net.mgmt.subnet4Len}}
uci set network.mgmt.gateway=${config.site.net.mgmt.hosts4.mgmt-gw}
uci set network.mgmt.ip6addr=${config.site.net.mgmt.hosts6.dn42.${hostName}}/64
uci set network.mgmt.ip6gw=${config.site.net.mgmt.hosts6.dn42.mgmt-gw}
uci -q delete network.mgmt.dns || true
uci add_list network.mgmt.dns=${config.site.net.serv.hosts4.dnscache}
uci add_list network.mgmt.dns=${config.site.net.serv.hosts6.dn42.dnscache}
uci -q delete network.globals.ula_prefix || true
# delete unused networks
${concatMapStrings (net:
optionalString (! hostConfig.interfaces ? ${net}) ''
uci -q delete network.${net} || true
''
) ([ "lan" "wan" "wan6" ] ++ builtins.attrNames config.site.net)}
# bridged and static networks
${concatMapStrings (net:
let
iface = hostConfig.interfaces.${net};
in optionalString (net != "mgmt" && builtins.elem iface.type ["bridge" "phys"]) ''
uci set network.${net}=interface
${optionalString (iface.type == "bridge") ''
uci set network.${net}.type=bridge
uci add network device
uci set network.@device[-1].name='${net}'
uci set network.@device[-1].type='bridge'
''}
uci set network.${net}.proto=static
${if hasDSA
then ''
uci set network.${net}.device='br0.${toString config.site.net.${net}.vlan}'
'' else ''
uci set network.${net}.ifname='${concatStringsSep " " (networkInterfaces net)}'
''}
${optionalString (config.site.net.${net}.mtu != null) ''
uci set network.${net}.mtu=${toString config.site.net.${net}.mtu}
''}
${optionalString (config.site.net.${net}.hosts4 ? ${hostName}) ''
# address in net
uci set network.${net}.ipaddr=${config.site.net.${net}.hosts4.${hostName}}
uci set network.${net}.netmask=${self.lib.netmasks.${toString config.site.net.${net}.subnet4Len}}
''}
${concatMapStrings (hosts6: optionalString (hosts6 ? ${hostName}) ''
uci set network.${net}.ip6addr=${hosts6.${hostName}}/64
'') (builtins.attrValues config.site.net.${net}.hosts6)}
'') (builtins.attrNames hostConfig.interfaces)
}
# vxlan trunks
${concatMapStrings (name:
let
iface = hostConfig.interfaces.${name};
in optionalString (iface.type == "vxlan") ''
uci set network.${name}=interface
uci set network.${name}.proto=vxlan6
uci set network.${name}.peer6addr='${iface.vxlan.peer}'
uci set network.${name}.port=4789
uci set network.${name}.rxcsum=0
uci set network.${name}.txcsum=0
uci set network.${name}.delegate=0
'') (builtins.attrNames hostConfig.interfaces)
}
${uciDeleteAll "wireless.radio"}
uci -q delete wireless.default_radio0 || true
uci -q delete wireless.default_radio1 || true
${concatStrings (imap0 (index: path:
let
radioConfig = hostConfig.wifi.${path};
ifPrefix = if radioConfig.channel < 15
then "wlan2"
else "wlan5";
in ''
uci set wireless.radio${toString index}=wifi-device
uci set wireless.radio${toString index}.type=mac80211
uci set wireless.radio${toString index}.country=DE
uci set wireless.radio${toString index}.band=${radioConfig.band}
uci set wireless.radio${toString index}.channel=${toString radioConfig.channel}
uci set wireless.radio${toString index}.path=${path}
uci set wireless.radio${toString index}.htmode=${radioConfig.htmode}
uci set wireless.radio${toString index}.noscan=1
uci -q delete wireless.radio${toString index}.disabled || true
${concatMapStrings (ssid:
let
ssidConfig = radioConfig.ssids.${ssid};
netConfig = config.site.net.${ssidConfig.net};
# mapping our option to openwrt/hostapd setting
encryption = {
none = "none";
owe = "owe";
wpa2 = "psk2";
wpa3 = "sae-mixed";
}.${ssidConfig.encryption};
ifname =
if ssidConfig.ifname != null
then ssidConfig.ifname
else "${ifPrefix}-${ssidConfig.net}";
pad = len: prefix: s:
if builtins.stringLength s < len
then pad len prefix "${prefix}${s}"
else s;
in ''
uci add wireless wifi-iface
uci set wireless.@wifi-iface[-1].ifname=${ifname}
uci set wireless.@wifi-iface[-1].device=radio${toString index}
uci set wireless.@wifi-iface[-1].ssid='${ssid}'
uci set wireless.@wifi-iface[-1].mode=${ssidConfig.mode}
uci set wireless.@wifi-iface[-1].network=${ssidConfig.net}
uci set wireless.@wifi-iface[-1].mcast_rate=18000
uci set wireless.@wifi-iface[-1].hidden=${if ssidConfig.hidden then "1" else "0"}
uci set wireless.@wifi-iface[-1].encryption='${encryption}'
${if (ssidConfig.psk != null)
then ''
uci set wireless.@wifi-iface[-1].key='${ssidConfig.psk}'
''
else ''
uci -q delete wireless.@wifi-iface[-1].key || true
''}
${lib.optionalString (!ssidConfig.disassocLowAck) ''
uci set wireless.@wifi-iface[-1].disassoc_low_ack='0'
''}
${lib.optionalString (netConfig.wifi.ieee80211rKey != null) ''
# for usteerd
# see https://www.libe.net/en-wlan-roaming#client-steering
# https://openwrt.org/docs/guide-user/network/wifi/usteer#configure_80211k_and_80211v_on_all_ap-nodes
uci set wireless.@wifi-iface[-1].bss_transition=1
uci set wireless.@wifi-iface[-1].wnm_sleep_mode=1
uci set wireless.@wifi-iface[-1].time_advertisement=2
uci set wireless.@wifi-iface[-1].time_zone=GMT0
uci set wireless.@wifi-iface[-1].ieee80211k=1
uci set wireless.@wifi-iface[-1].rrm_neighbor_report=1
uci set wireless.@wifi-iface[-1].rrm_beacon_report=1
# breaks Apple devices connecting to wifi when used together with wpa2/wpa3 mixed mode (sae-mixed)
# uci set wireless.@wifi-iface[-1].ieee80211r=1
# when unset derived from interface MAC
uci set wireless.@wifi-iface[-1].nasid=${pad 12 "0" (toString ((lib.toInt (lib.removePrefix "ap" hostName)) * 65536 + index))}
# when unset derived from the first 4 chars of the md5 hashed SSID
uci set wireless.@wifi-iface[-1].mobility_domain=${pad 4 "0" (lib.toHexString (49920 + netConfig.vlan))}
# https://github.com/openwrt/openwrt/issues/7907
# https://github.com/openwrt/openwrt/commit/2984a0420649733662ff95b0aff720b8c2c19f8a
uci set wireless.@wifi-iface[-1].ft_over_ds=0
# as recommend in 7907 and seems to fairly often trigger while testing
uci set wireless.@wifi-iface[-1].reassociation_deadline=20000
# might be unused if ft_over_ds is not used
uci set wireless.@wifi-iface[-1].ft_bridge=${mgmtInterface}
# otherwise the r0kh/r1kh options below are not applied
uci set wireless.@wifi-iface[-1].ft_psk_generate_local=0
# do not just rely on the monility domain for increased security
# https://forum.openwrt.org/t/802-11r-fast-transition-how-to-understand-that-ft-works/110920/81
uci set wireless.@wifi-iface[-1].r0kh=ff:ff:ff:ff:ff:ff,\*,${netConfig.wifi.ieee80211rKey}
uci set wireless.@wifi-iface[-1].r1kh=00:00:00:00:00:00,00:00:00:00:00:00,${netConfig.wifi.ieee80211rKey}
uci set wireless.@wifi-iface[-1].pmk_r1_push=1
''}
''
) (builtins.attrNames radioConfig.ssids)}
'') (builtins.attrNames hostConfig.wifi))}
uci set usteer.@usteer[0].network=mgmt
uci set usteer.@usteer[0].load_kick_enabled=1
uci set usteer.@usteer[0].load_kick_threshold=67
uci set usteer.@usteer[0].signal_diff_threshold=15
uci set usteer.@usteer[0].load_balancing_threshold=8
uci set usteer.@usteer[0].band_steering_threshold=16
uci commit
# Add hotfixes for MTU settings
cat >/etc/hotplug.d/iface/99-mtu <<__MTU__
#!/bin/sh
${concatMapStrings (net:
optionalString (config.site.net ? ${net} &&
config.site.net.${net}.mtu != null) ''
if [ "\\\$ACTION" = ifup -a "\\\$INTERFACE" = ${net} ]; then
ip link set \\\$DEVICE mtu ${toString config.site.net.${net}.mtu}
fi
'') (builtins.attrNames hostConfig.interfaces)
}
__MTU__
${optionalString hostConfig.wifiOnLink.enable ''
# Cronjob that makes sure WiFi is only visible when server with all
# the gateways is reachable
cat >/etc/crontabs/root <<__CRON__
* * * * * /usr/sbin/wifi-on-link.sh
* * * * * /usr/sbin/usteer-info.sh
__CRON__
cat >/usr/sbin/wifi-on-link.sh <<__SH__
#!/bin/sh
if (ping -c 1 -W 3 ${config.site.net.mgmt.hosts4.mgmt-gw}) ; then
REACHABLE=y
else
REACHABLE=n
fi
if [ "\\\$(cat /sys/class/net/wlan2-pub/operstate)" == "up" ] ; then
UP=y
else
UP=n
fi
if [ -e /sys/class/leds/blue:dome ] ; then
ERROR_LED=/sys/class/leds/blue:dome/brightness
[ \\\$REACHABLE = y ] && echo 0 > \\\$ERROR_LED
[ \\\$REACHABLE = n ] && echo 1 > \\\$ERROR_LED
fi
[ \\\$REACHABLE = y ] && [ \\\$UP = n ] && wifi up
[ \\\$REACHABLE = n ] && [ \\\$UP = y ] && wifi down
exit 0
__SH__
chmod a+rx /usr/sbin/wifi-on-link.sh
/etc/init.d/cron restart
cat > /etc/collectd.conf <<COLLECTD
Hostname "${hostName}"
FQDNLookup false
Interval 10
BaseDir "/var/run/collectd"
Include "/etc/collectd/conf.d"
PIDFile "/var/run/collectd.pid"
PluginDir "/usr/lib/collectd"
TypesDB "/usr/share/collectd/types.db"
LoadPlugin cpu
LoadPlugin load
LoadPlugin interface
LoadPlugin iwinfo
LoadPlugin network
LoadPlugin exec
<Plugin network>
Server "${config.site.net.serv.hosts6.dn42.stats}" "25826"
</Plugin>
<Plugin exec>
Exec "nobody" "/usr/bin/usteer-stats.sh"
</Plugin>
COLLECTD
''}
chmod +x /usr/bin/usteer-stats.sh /usr/sbin/usteer-info.sh
for svc in dnsmasq uhttpd ; do
rm -f /etc/rc.d/*\$svc
/etc/init.d/\$svc stop || true
done
''

View File

@ -1,3 +0,0 @@
#! /bin/sh
[ -p /tmp/usteer-info ] || exit 0
exec /bin/ubus call usteer local_info > /tmp/usteer-info

View File

@ -1,32 +0,0 @@
#! /bin/sh
HOSTNAME=`cat /proc/sys/kernel/hostname`
INTERVAL=60
[ -p /tmp/usteer-info ] || mkfifo /tmp/usteer-info
while true; do
if [ ! -p /tmp/usteer-info ]; then
echo "/tmp/usteer-info went missing!"
exit 1
fi
DATA="$(cat /tmp/usteer-info)"
cd /sys/class/net
for iface in wlan*; do
eval $( echo "$DATA" | jsonfilter \
-e 'LOAD=@["hostapd.'$iface'"].load' \
-e 'NOISE=@["hostapd.'$iface'"].noise' \
-e 'N_ASSOC=@["hostapd.'$iface'"].n_assoc' \
-e 'FREQ=@["hostapd.'$iface'"].freq' \
-e 'ROAM_SOURCE=@["hostapd.'$iface'"].roam_events.source' \
-e 'ROAM_TARGET=@["hostapd.'$iface'"].roam_events.target'
)
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/stations-load\" interval=$INTERVAL N:$LOAD"
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/signal_noise-noise\" interval=$INTERVAL N:$NOISE"
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/stations-n_assoc\" interval=$INTERVAL N:$N_ASSOC"
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/frequency-freq\" interval=$INTERVAL N:$FREQ"
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/transitions-roam_source\" interval=$INTERVAL N:$ROAM_SOURCE"
echo "PUTVAL \"$HOSTNAME/usteer_local_info-$iface/transitions-roam_target\" interval=$INTERVAL N:$ROAM_TARGET"
done
done

View File

@ -1,3 +0,0 @@
source "https://rubygems.org"
gem "ipaddress_2", "~> 0.14"

View File

@ -1,13 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
ipaddress_2 (0.14.0)
PLATFORMS
ruby
DEPENDENCIES
ipaddress_2 (~> 0.14)
BUNDLED WITH
2.1.4

View File

@ -1,15 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
with pkgs;
let
gems = bundlerEnv {
name = "gems-for-subnetplan";
gemdir = ./.;
};
in stdenv.mkDerivation rec {
name = "subnetplan";
buildInputs = [ gems.wrappedRuby ];
buildCommand = ''
install -D -m755 ${./render.rb} $out/bin/${name}
patchShebangs $out/bin/${name}
'';
}

View File

@ -1,12 +0,0 @@
{
ipaddress_2 = {
groups = ["default"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "1wdy1ka0i9mncjqid2kv3ng6gi95y5xb9ykl0ar8lnrriia42v1c";
type = "gem";
};
version = "0.14.0";
};
}

View File

@ -1,151 +0,0 @@
#!/usr/bin/env ruby
require 'ipaddress_2'
require 'erb'
TABLE_WIDTH = 8
class Subnet
attr_reader :addr, :desc
attr_accessor :addr_visible
def initialize addr, desc
@addr = addr
@desc = desc
@addr_visible = false
end
end
class Group
attr_accessor :net, :blocks
def initialize net, blocks
@net = net
@blocks = blocks
end
end
class Block
attr_accessor :label, :name, :span
def initialize label, name
@label = label
@name = name
@span = 1
end
end
nets = []
while line = gets
a, desc = line.split(/ /, 2)
addr = IPAddress.parse a
desc.chomp!
nets << Subnet.new(addr, desc)
end
nets.sort_by! { |net| net.addr }
collisions = 0
prev = nil
nets.each do |net|
if prev and (net.addr.include?(prev.addr) or prev.addr.include?(net.addr))
STDERR.puts "#{prev.addr} and #{net.addr} overlap"
collisions += 1
end
prev = net
end
exit 1 if collisions > 0
GROUP_PREFIX = 19
groups = {}
nets.each do |net|
if net.addr.prefix > GROUP_PREFIX
group = net.addr.supernet(GROUP_PREFIX).to_s
else
group = net.addr.to_s
end
(groups[group] ||= []) << net
end
max_prefix = [groups.collect { nets.collect { |net| net.addr.prefix }.max }.max, 64].min
groups = groups.collect do |group, nets|
allnet = nets[0].addr.clone
while allnet.prefix > 0 and not allnet.include?(nets[nets.size - 1].addr)
allnet = allnet.supernet(allnet.prefix - 1)
end
blocks = []
row = []
x = 0
allnet.subnet(max_prefix).each do |addr|
net = nets.select { |net| net.addr.include? addr }[0]
label = net ? "#{addr}/#{net.addr.prefix}" : addr.to_s
name = net ? net.desc : ""
if row.last and name != "" and row.last.name == name
row.last.span += 1
else
row << Block.new(label, name)
end
x += 1
if x >= TABLE_WIDTH
blocks << row
row = []
x = 0
end
end
blocks << row if row.size > 0
Group.new(allnet, blocks)
end
def background_color desc
case desc
when "core"
"#9F9F9F"
when "mgmt"
"#FF3F3F"
when "roof"
"#FF4F2F"
when "c3d2", "flpk"
"yellow"
when "serv", "cluster"
"orange"
when "pub"
"#7FFF7F"
when /priv(\d+)/
"hsl(240, 80%, #{60 + 5 * ($1.to_i % 8)}%)"
else
"white"
end
end
html = ERB::new <<~EOF
<html>
<head>
<title>Subnetwork Plan</title>
<style>
table {
margin: 3rem auto;
}
</style>
</head>
<body>
<table border="1">
<% groups.each do |group| %>
<tr>
<th colspan="<%= TABLE_WIDTH %>"><%= group.net.to_string %></th>
</tr>
<% group.blocks.each do |row| %>
<tr>
<% row.each do |block| %>
<td colspan="<%= block.span %>" style="background-color: <%= background_color(block.name) %>"><%= block.label %> <%= block.name %></td>
<% end %>
</tr>
<% end %>
<% end %>
</table>
</body>
</html>
EOF
puts html.result

View File

@ -1,54 +0,0 @@
{ self, nixpkgs, system }:
with nixpkgs.legacyPackages.${system};
let
config = self.lib.config;
in
rec {
subnetplan4 =
stdenv.mkDerivation {
name = "subnetplan4.html";
src = builtins.toFile "subnets4.txt" (
lib.concatMapStringsSep "\n" (net:
"${config.site.net.${net}.subnet4} ${net}"
) (builtins.attrNames (
lib.filterAttrs (_: { subnet4, ... }: subnet4 != null)
config.site.net
))
);
buildInputs = [
(import ./subnetplan { inherit pkgs; })
];
buildCommand = ''
subnetplan < $src > $out
'';
};
subnetplan6 =
stdenv.mkDerivation {
name = "subnetplan6.html";
src = builtins.toFile "subnets6.txt" (
lib.concatMapStrings (net:
lib.concatMapStrings (ctx: ''
${config.site.net.${net}.subnets6.${ctx}} ${net}
'') (builtins.attrNames config.site.net.${net}.subnets6)
) (builtins.attrNames config.site.net)
);
buildInputs = [
(import ./subnetplan { inherit pkgs; })
];
buildCommand = ''
subnetplan < $src > $out
'';
};
subnetplans = runCommand "subnetplans" {} ''
DIR=$out/share/doc/zentralwerk
mkdir -p $DIR $out/nix-support
${lib.concatMapStrings (pkg: ''
ln -s ${pkg} $DIR/${pkg.name}
echo doc report $DIR/${pkg.name} >> $out/nix-support/hydra-build-products
'') [ subnetplan4 subnetplan6 ]}
'';
}

View File

@ -1,97 +0,0 @@
{ self, nixpkgs, system }:
with nixpkgs.legacyPackages.${system};
let
config = self.lib.config;
reportHosts = lib.concatMapStrings (hostName:
let
inherit (config.site.hosts.${hostName}) links model location;
linksByPorts = builtins.foldl' (linksByPorts: linkName:
if links.${linkName}.group != null
then
# group ports
linksByPorts // {
${lib.concatMapStringsSep "<br>" (port:
"`${port}`"
) links.${linkName}.ports} = linkName;
}
else
# expand lists of access ports to seperate rows
builtins.foldl' (linksByPorts': port:
linksByPorts' // {
"`${port}`" = linkName;
}
) linksByPorts links.${linkName}.ports
) {} (builtins.attrNames links);
in ''
## ${hostName}
`${model}` @ **${location}**
|Ports|Target|Networks|
|-:|:-:|:-|
${lib.concatMapStrings (ports:
let
linkName = linksByPorts.${ports};
link = links.${linkName};
in ''
|${
lib.optionalString (link.group != null)
"**Group ${toString link.group}**<br>"
}${
ports
}|**${
linkName
}**|${
if link.trunk
then "**Trunk:** "
else "**Access:** "
}${
lib.concatStringsSep ", " (
map (net:
if config.site.net.${net}.vlan != null
then "${net}&nbsp;(`${toString config.site.net.${net}.vlan}`)"
else net
) (builtins.sort (a: b:
let
vlanA = config.site.net.${a}.vlan;
vlanB = config.site.net.${b}.vlan;
in
(
if vlanA == null
then 4096
else vlanA
) < (
if vlanB == null
then 4096
else vlanB
)
) link.nets)
)
}|
'') (lib.naturalSort (
builtins.attrNames linksByPorts
))}
'');
reportRole = role:
reportHosts (
lib.naturalSort (
builtins.filter (hostName:
config.site.hosts.${hostName}.role == role
) (builtins.attrNames config.site.hosts)
)
);
in
writeText "switch-report.md" ''
# Switch Report
${reportRole "switch"}
# Access Point Report
${reportRole "ap"}
''

View File

@ -1,167 +0,0 @@
# http://h20628.www2.hp.com/km-ext/kmcsdirect/emr_na-c02586144-1.pdf
{ self, pkgs, hostName, config, hostConfig
, sort, sortBy, sortNetsByVlan
, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "${hostConfig.password}\r"
expect ">"
send "system-view\r"
expect "]"
send "sysname ${hostName}\r"
expect "]"
send "user-interface vty 0 4\r"
expect "ui-vty0-4]"
send "screen-length 0\r"
expect "ui-vty0-4]"
send "user privilege level 3\r"
expect "ui-vty0-4]"
send "set authentication password simple ${hostConfig.password}\r"
expect "ui-vty0-4]"
send "quit\r"
expect "${hostName}]"
send "local-user admin\r"
expect -- "-luser-admin]"
send "password simple ${hostConfig.password}\r"
expect -- "-luser-admin]"
send "quit\r"
expect "${hostName}]"
# Enable logging
send "info-center enable\r"
expect "]"
send "info-center loghost ${config.site.net.mgmt.hosts4.logging} channel loghost facility local6\r"
expect "]"
send "info-center source default channel loghost log level informational\r"
expect "]"
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in
if netConfig.vlan != null
then ''
send "vlan ${vlan}\r"
expect -- "-vlan${vlan}]"
send "name ${net}\r"
expect -- "-vlan${vlan}]"
${optionalString (net == "mgmt") ''
# Actually only used for mgmt_vlan, switches are not routers
send "interface Vlan-interface ${vlan}\r"
expect "]"
${optionalString (hosts4 ? ${hostName}) ''
send "ip address ${hostAddr4} ${netmask}\r"
expect "]"
''}
''}
send "quit\r"
expect "${hostName}]"
''
else ""
) (sortNetsByVlan (builtins.attrNames config.site.net))
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
in
if isTrunk
then ''
${optionalString isBond ''
send "link-aggregation group ${linkConfig.group} mode static\r"
expect {
"This aggregation will be modified to static mode. Continue ?" {
send "Y\r"
}
"]" {}
}
send "link-aggregation group ${linkConfig.group} description ${name}\r"
expect "]"
''}
${concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo stp edged-port\r"
expect "]"
${if isBond
then ''
send "lacp enable\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-aggregation group ${linkConfig.group}\r"
'' else ''
send "undo lacp enable\r"
''}
expect "]"
send "jumboframe enable\r"
expect "]"
send "port link-type trunk\r"
expect "]"
# Set dummy default vlan
send "port trunk pvid vlan 4094\r"
expect "]"
# Deconfigure all but mgmt vlan
send "undo port trunk permit vlan 2 to 4094\r"
expect "]"
${concatMapStrings (vlan: ''
send "port trunk permit vlan ${toString vlan}\r"
expect "]"
'') (sort linkConfig.vlans)}
send "quit\r"
expect "${hostName}]"
'') (sort linkConfig.ports)}
'' else
concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-type access\r"
expect "]"
${if name == "mgmt"
then ''
send "undo port access vlan\r"
expect "]"
'' else ''
send "port access vlan ${toString netConfig.vlan}\r"
expect "]"
''}
send "quit\r"
expect "${hostName}]"
'') (sort linkConfig.ports)
) (sortBy (link: hostConfig.links.${link}.ports)
(builtins.attrNames hostConfig.links)
)}
send "save main\r"
expect "Y/N]"
send "YES\r"
expect "press the enter key):"
send "\r"
expect "]"
send "quit\r"
expect ">"
send "quit\r"
''

View File

@ -1,180 +0,0 @@
{ self, pkgs, hostName, config, hostConfig
, sort, sortBy, sortNetsByVlan
, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
${if hostConfig.firstboot
then ''
spawn sudo ${uucp}/bin/cu -s 19200 -l /dev/ttyUSB0
send "\r"
''
else ''
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "${hostConfig.password}\r"
''
}
expect ">"
send "system-view\r"
expect "]"
send "sysname ${hostName}\r"
expect "]"
send "user-interface vty 0 4\r"
expect "ui-vty0-4]"
send "screen-length 0\r"
expect "ui-vty0-4]"
send "user privilege level 3\r"
expect "ui-vty0-4]"
send "set authentication password simple ${hostConfig.password}\r"
expect "ui-vty0-4]"
send "quit\r"
expect "${hostName}]"
send "local-user admin\r"
expect -- "-luser-admin]"
send "password simple ${hostConfig.password}\r"
expect -- "-luser-admin]"
send "quit\r"
expect "${hostName}]"
# Enable logging
send "info-center enable\r"
expect "]"
send "info-center loghost ${config.site.net.mgmt.hosts4.logging} channel loghost facility local6\r"
expect "]"
send "info-center source default channel loghost log level informational\r"
expect "]"
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in
if netConfig.vlan != null
then ''
send "vlan ${vlan}\r"
expect -- "-vlan${vlan}]"
send "name ${net}\r"
expect -- "-vlan${vlan}]"
${optionalString (net == "mgmt") ''
# Actually only used for mgmt_vlan, switches are not routers
send "interface Vlan-interface ${vlan}\r"
expect "]"
${optionalString (hosts4 ? ${hostName}) ''
send "ip address ${hostAddr4} ${netmask}\r"
expect "]"
''}
''}
send "quit\r"
expect "${hostName}]"
''
else ""
) (sortNetsByVlan (builtins.attrNames config.site.net))
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
in
if isTrunk
then ''
${optionalString isBond ''
send "link-aggregation group ${linkConfig.group} mode static\r"
expect {
"This aggregation will be modified to static mode. Continue ?" {
send "Y\r"
}
"]" {}
}
send "link-aggregation group ${linkConfig.group} description ${name}\r"
expect "]"
''}
${concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo stp edged-port\r"
expect "]"
${if isBond
then ''
send "lacp enable\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-aggregation group ${linkConfig.group}\r"
'' else ''
send "undo lacp enable\r"
''}
expect "]"
send "jumboframe enable\r"
expect "]"
send "port link-type trunk\r"
expect "]"
# Set dummy default vlan
send "port trunk pvid vlan 4094\r"
expect "]"
# Deconfigure all but mgmt vlan
send "undo port trunk permit vlan 2 to 4094\r"
expect "]"
${concatMapStrings (vlan: ''
send "port trunk permit vlan ${toString vlan}\r"
expect "]"
'') (sort linkConfig.vlans)}
send "undo shutdown\r"
expect "]"
send "quit\r"
expect "${hostName}]"
'') (sort linkConfig.ports)}
'' else
concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-type access\r"
expect "]"
${if name == "mgmt"
then ''
send "undo port access vlan\r"
expect "]"
'' else ''
send "port access vlan ${toString netConfig.vlan}\r"
expect "]"
''}
send "undo shutdown\r"
expect "]"
send "quit\r"
expect "${hostName}]"
'') (sort linkConfig.ports)
) (sortBy (link: hostConfig.links.${link}.ports)
(builtins.attrNames hostConfig.links)
)}
send "save main\r"
expect "Y/N]"
send "YES\r"
expect "press the enter key):"
send "\r"
expect "]"
send "quit\r"
expect ">"
send "quit\r"
''

View File

@ -1,154 +0,0 @@
# http://ftp.hp.com/pub/networking/software/2600-2800-4100-6108-MgmtConfig-Oct2005-59906023.pdf
{ self, pkgs, hostName, config, hostConfig
, sort, sortBy, sortNetsByVlan
, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Press any key to continue"
send "\r"
expect "assword: "
send "${hostConfig.password}\r"
expect "#"
send "configure terminal\r"
expect "(config)# "
send "hostname ${hostName}\r"
expect "(config)# "
send "snmp-server location \"${hostConfig.location}\"\r"
expect "(config)# "
send "snmp-server contact \"astro@spaceboyz.net\"\r"
expect "(config)# "
send "password manager\r"
expect "New password for Manager: "
send "${hostConfig.password}\r"
expect "Please retype new password for Manager: "
send "${hostConfig.password}\r"
expect "(config)# "
# TODO: ssh, password
# Enable Logging
send "logging ${config.site.net.mgmt.hosts4.logging}\r"
expect "(config)# "
send "logging facility local6\r"
expect "(config)# "
# TODO ntp
# timesync sntp
# ip timep manual {#ntp#} interval 10
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in
if netConfig.vlan != null
then ''
send "vlan ${vlan}\r"
expect "(vlan-${vlan})#"
send "name ${net}\r"
expect "(vlan-${vlan})#"
send "jumbo\r"
expect "(vlan-${vlan})#"
${optionalString (hosts4 ? ${hostName}) ''
# Actually only used for mgmt_vlan, switches are not routers
send "ip address ${hostAddr4} ${netmask}\r"
expect "(vlan-${vlan})#"
''}
send "exit\r"
expect "(config)# "
${if net == "mgmt"
then ''
send "management-vlan ${vlan}\r"
expect "(config)# "
'' else ''
# If not mgmt, reset all VLAN mappings
send "no vlan ${vlan} tagged all\r"
expect "(config)# "
send "no vlan ${vlan} untagged all\r"
expect "(config)# "
''}
''
else ""
) (sortNetsByVlan (builtins.attrNames config.site.net))
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
port0 = builtins.head linkConfig.ports;
isBond =
builtins.length linkConfig.ports > 1
||
hasInfix "-" port0
||
hasInfix "," port0;
ports = concatStringsSep "," linkConfig.ports;
in
if isTrunk && isBond
then ''
send "interface ${ports} lacp active\r"
expect "(config)# "
send "trunk ${ports} trk${linkConfig.group} lacp\r"
expect "(config)# "
${concatMapStrings (vlan: ''
send "vlan ${toString vlan} tagged trk${linkConfig.group}\r"
expect "(config)# "
'') (sort linkConfig.vlans)}
''
else if isTrunk
then ''
send "no trunk ${ports}\r"
expect "(config)# "
send "no interface ${ports} lacp\r"
expect "(config)# "
${concatMapStrings (vlan: ''
send "vlan ${toString vlan} tagged ${ports}\r"
expect "(config)# "
'') (sort linkConfig.vlans)}
''
else ''
send "no trunk ${ports}\r"
expect "(config)# "
send "vlan ${toString netConfig.vlan} untagged ${ports}\r"
expect "(config)# "
''
) (sortBy (link: hostConfig.links.${link}.ports)
(builtins.attrNames hostConfig.links)
)}
send "exit\r"
expect "${hostName}# "
send "write memory\r"
expect "${hostName}# "
send "exit\r"
expect "${hostName}> "
send "exit\r"
expect "Do you want to log out "
expect "y/n]? "
send "y"
''

View File

@ -1,145 +0,0 @@
# http://static.tp-link.com/res/down/doc/TL-SG3210(UN)_V2.0_CLI_.pdf
{ self, pkgs, hostName, config, hostConfig
, sort, sortBy, sortNetsByVlan
, ... }:
with pkgs;
with lib;
''
#! ${pkgs.expect}/bin/expect -f
spawn ${pkgs.inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "${hostConfig.password}\r"
expect ">"
send "\r"
expect ">"
send "enable\r"
expect "Password:"
send "${hostConfig.password}\r"
expect "#"
send "configure\r"
expect "(config)#"
send "enable secret 0 ${hostConfig.password}\r"
expect "(config)#"
#send "enable password 0 ${hostConfig.password}\r"
#expect "(config)#"
send "service password-encryption\r"
expect "(config)#"
send "user name admin privilege admin secret 0 ${hostConfig.password}\r"
expect "(config)#"
send "hostname \"${hostName}\"\r"
expect "(config)#"
send "location \"${hostConfig.location}\"\r"
expect "(config)#"
send "logging host index 1 ${config.site.net.mgmt.hosts4.logging} 6\r"
expect "(config)#"
send "ip management-vlan ${toString config.site.net.mgmt.vlan}\r"
expect "(config)#"
send "ip ssh server\r"
expect "(config)#"
send "telnet enable\r"
expect "(config)#"
send "line vty 0 15\r"
expect "(config-line)#"
send "password 0 ${hostConfig.password}\r"
expect "(config-line)#"
send "exit\r"
expect "(config)#"
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in
if netConfig.vlan != null
then ''
${optionalString (net != "mgmt") ''
send "vlan ${vlan}\r"
expect "(config-vlan)#"
send "name \"${net}\"\r"
expect "(config-vlan)#"
send "exit\r"
expect "(config)#"
''}
send "interface vlan ${vlan}\r"
expect "(config-if)#"
${optionalString (hosts4 ? ${hostName}) ''
# Actually only used for mgmt_vlan, switches are not routers
send "ip address ${hostAddr4} ${netmask}\r"
expect "(config-if)#"
''}
send "exit\r"
expect "(config)#"
''
else ""
) (sortNetsByVlan (builtins.attrNames config.site.net))}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
port0 = builtins.head linkConfig.ports;
isBond =
builtins.length linkConfig.ports > 1
||
hasInfix "-" port0;
vlans = concatStringsSep "," (map toString (sort linkConfig.vlans));
ports = concatMapStringsSep "," (port:
"1/0/${port}"
) linkConfig.ports;
in
if isTrunk
then ''
send "interface range gigabitEthernet ${ports}\r"
expect "(config-if-range)#"
send "switchport mode trunk\r"
expect "(config-if-range)#"
${if isBond
then ''
send "channel-group ${linkConfig.group} mode active\r"
expect "(config-if-range)#"
#send "port-channel load-balance src-dst-ip\r"
#expect "(config-if-range)#"
'' else ''
send "no channel-group\r"
expect "(config-if-range)#"
''}
send "switchport trunk allowed vlan ${vlans}\r"
expect "(config-if-range)#"
send "exit\r"
expect "(config)#"
'' else ''
send "interface range gigabitEthernet ${ports}\r"
expect "(config-if-range)#"
send "switchport mode access\r"
expect "(config-if-range)#"
send "switchport access vlan ${toString netConfig.vlan}\r"
expect "(config-if-range)#"
send "exit\r"
expect "(config)#"
''
) (sortBy (link: hostConfig.links.${link}.ports)
(builtins.attrNames hostConfig.links)
)}
send "exit\r"
expect "#"
send "copy running-config startup-config\r"
expect "#"
send "exit\r"
expect ">"
send "exit\r"
''

View File

@ -1,134 +0,0 @@
{ pkgs, hostName, config, hostConfig
, sortBy, sortNetsByVlan
, ... }:
with pkgs;
with lib;
let
configFile = builtins.toFile "junos.config" ''
system {
host-name ${hostName};
time-zone Europe/Berlin;
root-authentication {
encrypted-password "%%HASH%%"; ## SECRET-DATA
ssh-ed25519 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHGgoLzQMeyX1wjsX/hgVkN//zyfOQPiBRYgO2ajEGH6 root@server2";
}
services {
ssh {
root-login allow;
}
netconf {
ssh;
}
web-management {
http {
interface [ vme.0 vlan.1 ];
}
}
}
}
protocols {
lldp {
interface all;
}
}
virtual-chassis {
no-split-detection;
member 0 {
mastership-priority 255;
}
member 1 {
mastership-priority 255;
}
}
chassis { aggregated-devices { ethernet { device-count 32; } } }
vlans {
${concatMapStrings (net:
let
netName = if net == "mgmt"
then "mgmt-vlan"
else net;
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
in
lib.optionalString (netConfig.vlan != null) ''
${netName} {
vlan-id ${vlan};
${lib.optionalString (net == "mgmt") ''
l3-interface vlan.${vlan};
''}
}
''
) (sortNetsByVlan (builtins.attrNames config.site.net))}
}
interfaces {
vlan {
unit ${toString config.site.net.mgmt.vlan} {
family inet {
address ${mgmtAddress}/${toString config.site.net.mgmt.subnet4Len};
}
}
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
group = linkConfig.group;
isBond = linkConfig.group != null &&
builtins.length linkConfig.ports > 1;
nets = map (net:
if net == "mgmt"
then "mgmt-vlan"
else net
) linkConfig.nets;
vlanConfig = ''
unit 0 {
family ethernet-switching {
port-mode ${if linkConfig.trunk then "trunk" else "access"};
vlan { members [ ${concatStringsSep " " nets} ]; }
}
}
'';
in
if isBond
then concatMapStrings (port: ''
${port} {
ether-options { 802.3ad ae${group}; }
}
'') (linkConfig.ports) + ''
ae${group} {
aggregated-ether-options { lacp { active; } }
${vlanConfig}
}
''
else concatMapStrings (port: ''
${port} {
${vlanConfig}
}
'') (linkConfig.ports)
) (sortBy (link: hostConfig.links.${link}.ports)
(builtins.attrNames hostConfig.links)
)}
}
'';
configFileWithHash = runCommand "junos.config" {
nativeBuildInputs = [ mkpasswd ];
} ''
HASH=$(echo "${hostConfig.password}" | mkpasswd --method=SHA-512 --stdin)
substitute ${configFile} $out \
--replace "%%HASH%%" "$HASH"
'';
mgmtAddress = config.site.net.mgmt.hosts4.${hostName};
in ''
#! ${runtimeShell} -e
scp ${configFileWithHash} root@${mgmtAddress}:/tmp/junos.config
ssh root@${mgmtAddress} cli <<EOF
configure
load override /tmp/junos.config
commit
EOF
''

Some files were not shown because too many files have changed in this diff Show More