From 30579a433e2111e17a30b29e9df390769debaca1 Mon Sep 17 00:00:00 2001 From: oddlama Date: Mon, 1 Apr 2024 02:26:52 +0200 Subject: [PATCH] feat(topology): extract more service info and add helper functions to define stuff --- flake.nix | 168 +++++------------- hosts/sire/default.nix | 10 ++ hosts/sire/guests/loki.nix | 1 + hosts/ward/default.nix | 28 +-- hosts/ward/guests/adguardhome.nix | 1 + hosts/ward/guests/radicale.nix | 1 + cloud.svg => topology/icons/devices/cloud.svg | 0 topology/nixos/extractors/services.nix | 60 +++++-- topology/options/interfaces.nix | 8 +- topology/options/nodes.nix | 53 ++++++ topology/topology/renderers/elk/default.nix | 2 +- topology/topology/renderers/svg/default.nix | 17 +- 12 files changed, 174 insertions(+), 175 deletions(-) rename cloud.svg => topology/icons/devices/cloud.svg (100%) diff --git a/flake.nix b/flake.nix index 13da006..a48d82b 100644 --- a/flake.nix +++ b/flake.nix @@ -183,151 +183,65 @@ { inherit pkgs; modules = [ - { + ({config, ...}: let + inherit + (config.lib.helpers) + mkInternet + mkSwitch + mkRouter + mkConnection + mkConnectionRev + ; + in { renderer = "elk"; nixosConfigurations = self.nodes; - nodes.internet = { - name = "Internet"; - deviceType = "internet"; - hardware.image = ./cloud.svg; - # interfaces.eth0.network = "internet"; - interfaces.eth0.physicalConnections = [ - { - node = "fritzbox"; - interface = "wan0"; - } - { - node = "sentinel"; - interface = "wan"; - } - ]; - }; + nodes.internet = mkInternet {}; + nodes.sentinel.interfaces.wan.physicalConnections = [(mkConnectionRev "internet" "*")]; - nodes.fritzbox = { - name = "FritzBox"; - deviceType = "router"; - hardware.info = "FRITZ!Box 7520"; - hardware.image = ./fritzbox.png; - # interfaces.wan0.network = "internet"; - interfaces.wan0 = {}; - interfaces.eth0.physicalConnections = [ - { - node = "ward"; - interface = "wan"; - } + nodes.fritzbox = mkRouter "FritzBox" { + info = "FRITZ!Box 7520"; + image = ./fritzbox.png; + interfaceGroups = [ + ["eth1" "eth2" "eth3" "eth4"] + ["wan1"] ]; + connections.eth1 = mkConnection "ward" "wan"; + connections.wan1 = mkConnectionRev "internet" "*"; }; # TODO: extract from kea! networks.home-lan = { name = "Home LAN"; cidrv4 = "192.168.1.0/24"; - #color = "#78dba9"; }; + networks.home-fritzbox = { name = "Home Fritzbox"; cidrv4 = "192.168.178.0/24"; - #color = "#f1cf8a"; }; + nodes.switch-attic = mkSwitch "Switch Attic" { + info = "D-Link DGS-1016D"; + image = ./dlink-dgs1016d.png; + interfaceGroups = [["eth1" "eth2" "eth3" "eth4" "eth5" "eth6"]]; + connections.eth1 = mkConnection "ward" "lan"; + connections.eth2 = mkConnection "sire" "lan"; + connections.eth3 = []; + }; + + nodes.switch-bedroom-1 = mkSwitch "Switch Bedroom 1" { + info = "D-Link DGS-105"; + image = ./dlink-dgs105.png; + interfaceGroups = [["eth1" "eth2" "eth3" "eth4" "eth5"]]; + connections.eth1 = mkConnection "switch-attic" "eth3"; + connections.eth2 = mkConnection "kroma" "lan1"; + connections.eth3 = mkConnection "nom" "lan1"; + }; + }) + { nodes.ward.interfaces.lan.network = "home-lan"; - nodes.fritzbox.interfaces.eth0.network = "home-fritzbox"; - - nodes.switch-attic = { - name = "Switch Attic"; - deviceType = "switch"; - hardware.info = "D-Link DGS-1016D"; - hardware.image = ./dlink-dgs1016d.png; - - interfaces.eth0.sharesNetworkWith = _: true; - interfaces.eth1.sharesNetworkWith = _: true; - interfaces.eth2.sharesNetworkWith = _: true; - - interfaces.eth0.physicalConnections = [ - { - node = "ward"; - interface = "lan"; - } - ]; - interfaces.eth1.physicalConnections = [ - { - node = "sire"; - interface = "lan"; - } - ]; - interfaces.eth2 = {}; - }; - - nodes.switch-bedroom-1 = { - name = "Switch Bedroom 1"; - deviceType = "switch"; - hardware.info = "D-Link DGS-105"; - hardware.image = ./dlink-dgs105.png; - - interfaces.eth0.sharesNetworkWith = _: true; - interfaces.eth1.sharesNetworkWith = _: true; - interfaces.eth2.sharesNetworkWith = _: true; - - interfaces.eth0.physicalConnections = [ - { - node = "switch-attic"; - interface = "eth2"; - } - ]; - interfaces.eth1.physicalConnections = [ - { - node = "kroma"; - interface = "lan1"; - } - ]; - interfaces.eth2.physicalConnections = [ - { - node = "nom"; - interface = "lan1"; - } - ]; - }; - - #nodes.fritzbox-no-img = { - # name = "FritzBox No HImg"; - # deviceType = "router"; - # interfaces.wan0.physicalConnections = [ - # { - # node = "ward"; - # interface = "wan"; - # } - # ]; - #}; - - #nodes.fritzbox-device-nd = { - # name = "FritzBox No DImg"; - # deviceType = "device"; - # hardware.image = ./fritzbox.png; - # interfaces.wan0.physicalConnections = [ - # { - # node = "ward"; - # interface = "wan"; - # } - # ]; - #}; - - #nodes.fritzbox-device = { - # name = "FritzBox No D&HImg"; - # deviceType = "device"; - # interfaces.wan0.physicalConnections = [ - # { - # node = "ward"; - # interface = "wan"; - # } - # ]; - #}; - - # TODO: - #nodes.fritzbox = config.lib.nodes.mkRouter {}; - #nodes.fritzbox = config.lib.nodes.mkSwitch {}; - #nodes.fritzbox = config.lib.nodes.mkWifiAP {}; - #nodes.printer = config.lib.nodes.mkWifiAP {}; + nodes.fritzbox.interfaces.eth1.network = "home-fritzbox"; } ]; }; diff --git a/hosts/sire/default.nix b/hosts/sire/default.nix index 566000e..c2fd163 100644 --- a/hosts/sire/default.nix +++ b/hosts/sire/default.nix @@ -20,6 +20,9 @@ ./net.nix ]; + topology.self.hardware.info = "AMD Ryzen Threadripper 1950X, 96GB RAM"; + topology.self.interfaces.lan.sharesNetworkWith = x: x == "lan-self"; + boot.mode = "efi"; boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "e1000e" "alx"]; @@ -81,6 +84,13 @@ networking.nftables.firewall = { zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; }; + topology.self.interfaces.lan.physicalConnections = [ + { + node = config.node.name; + interface = "lan-self"; + renderer.reverse = true; + } + ]; } ]; }; diff --git a/hosts/sire/guests/loki.nix b/hosts/sire/guests/loki.nix index e9721be..2b15fdc 100644 --- a/hosts/sire/guests/loki.nix +++ b/hosts/sire/guests/loki.nix @@ -64,6 +64,7 @@ in { } ]; + topology.self.services.loki.info = "https://" + lokiDomain; services.loki = let lokiDir = "/var/lib/loki"; in { diff --git a/hosts/ward/default.nix b/hosts/ward/default.nix index 9b4a928..384d766 100644 --- a/hosts/ward/default.nix +++ b/hosts/ward/default.nix @@ -23,26 +23,7 @@ topology.self.hardware.image = ../../odroid-h3.png; topology.self.hardware.info = "ODROID H3, 64GB RAM"; - # TODO FIXME topology bogus - topology.self.interfaces.lan-self.physicalConnections = [ - { - node = config.node.name; - interface = "lan"; - } - ]; - topology.self.interfaces.lan.physicalConnections = - lib.flip map [ - "adguardhome" - "forgejo" - "kanidm" - "radicale" - "vaultwarden" - ] ( - x: { - node = "ward-${x}"; - interface = "lan"; - } - ); + topology.self.interfaces.lan.sharesNetworkWith = x: x == "lan-self"; boot.mode = "efi"; boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" "sdhci_pci" "r8169"]; @@ -87,6 +68,13 @@ networking.nftables.firewall = { zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; }; + topology.self.interfaces.lan.physicalConnections = [ + { + node = config.node.name; + interface = "lan-self"; + renderer.reverse = true; + } + ]; } ]; }; diff --git a/hosts/ward/guests/adguardhome.nix b/hosts/ward/guests/adguardhome.nix index eb8d59b..b4979be 100644 --- a/hosts/ward/guests/adguardhome.nix +++ b/hosts/ward/guests/adguardhome.nix @@ -48,6 +48,7 @@ in { allowedUDPPorts = [53]; }; + topology.self.services.adguardhome.info = "https://" + adguardhomeDomain; services.adguardhome = { enable = true; # TODO allow mutable settings, replace 123.123.123.123 with diff --git a/hosts/ward/guests/radicale.nix b/hosts/ward/guests/radicale.nix index 068c01f..00af9be 100644 --- a/hosts/ward/guests/radicale.nix +++ b/hosts/ward/guests/radicale.nix @@ -43,6 +43,7 @@ in { } ]; + topology.self.services.radicale.info = "https://" + radicaleDomain; services.radicale = { enable = true; settings = { diff --git a/cloud.svg b/topology/icons/devices/cloud.svg similarity index 100% rename from cloud.svg rename to topology/icons/devices/cloud.svg diff --git a/topology/nixos/extractors/services.nix b/topology/nixos/extractors/services.nix index 151b6d4..65d949c 100644 --- a/topology/nixos/extractors/services.nix +++ b/topology/nixos/extractors/services.nix @@ -21,30 +21,57 @@ in { options.topology.extractors.services.enable = mkEnableOption "topology service extractor" // {default = true;}; config.topology.self.services = mkIf config.topology.extractors.services.enable { - adguardhome = mkIf config.services.adguardhome.enable { - name = "AdGuard Home"; - icon = "services.adguardhome"; - }; + adguardhome = let + address = config.services.adguardhome.settings.bind_host or null; + port = config.services.adguardhome.settings.bind_port or null; + in + mkIf config.services.adguardhome.enable { + name = "AdGuard Home"; + icon = "services.adguardhome"; + details.listen = mkIf (address != null && port != null) {text = "${address}:${toString port}";}; + }; - forgejo = mkIf config.services.forgejo.enable { - name = "Forgejo"; - icon = "services.forgejo"; - }; + forgejo = let + address = config.services.forgejo.settings.server.HTTP_ADDR or null; + port = config.services.forgejo.settings.server.HTTP_PORT or null; + in + mkIf config.services.forgejo.enable { + name = + if config.services.forgejo.settings ? DEFAULT.APP_NAME + then "Forgejo (${config.services.forgejo.settings.DEFAULT.APP_NAME})" + else "Forgejo"; + icon = "services.forgejo"; + info = mkIf (config.services.forgejo.settings ? server.ROOT_URL) config.services.forgejo.settings.server.ROOT_URL; + details.listen = mkIf (address != null && port != null) {text = "${address}:${toString port}";}; + }; - grafana = mkIf config.services.grafana.enable { - name = "Grafana"; - icon = "services.grafana"; - }; + grafana = let + address = config.services.grafana.settings.server.http_addr or null; + port = config.services.grafana.settings.server.http_port or null; + in + mkIf config.services.grafana.enable { + name = "Grafana"; + icon = "services.grafana"; + info = config.services.grafana.settings.server.root_url; + details.listen = mkIf (address != null && port != null) {text = "${address}:${toString port}";}; + }; kanidm = mkIf config.services.kanidm.enableServer { name = "Kanidm"; icon = "services.kanidm"; + info = config.services.kanidm.serverSettings.origin; + details.listen.text = config.services.kanidm.serverSettings.bindaddress; }; - loki = mkIf config.services.loki.enable { - name = "Loki"; - icon = "services.loki"; - }; + loki = let + address = config.services.loki.configuration.server.http_listen_address or null; + port = config.services.loki.configuration.server.http_listen_port or null; + in + mkIf config.services.loki.enable { + name = "Loki"; + icon = "services.loki"; + details.listen = mkIf (address != null && port != null) {text = "${address}:${toString port}";}; + }; nginx = mkIf config.services.nginx.enable { name = "NGINX"; @@ -70,6 +97,7 @@ in { radicale = mkIf config.services.radicale.enable { name = "Radicale"; icon = "services.radicale"; + details.listen = mkIf (config.services.radicale.settings ? server.hosts) {text = toString config.services.radicale.settings.server.hosts;}; }; samba = mkIf config.services.samba.enable { diff --git a/topology/options/interfaces.nix b/topology/options/interfaces.nix index f040818..50d23c2 100644 --- a/topology/options/interfaces.nix +++ b/topology/options/interfaces.nix @@ -327,6 +327,11 @@ in icon = mkIf (config.topology.isMainModule && config.icons.interfaces ? ${submod.config.type}) ( mkDefault ("interfaces." + submod.config.type) ); + + # For switches: Share the network with any other interface by default + sharesNetworkWith = mkIf (config.topology.isMainModule && nodeSubmod.config.deviceType == "switch") ( + mkDefault (const true) + ); }; })); }; @@ -335,9 +340,6 @@ in }; config = { - lib.a.a = connections; - lib.a.b = networkDefiningInterfaces; - lib.a.c = propagatedNetworks; assertions = flatten (flip map (attrValues config.nodes) ( node: flip map (attrValues node.interfaces) ( diff --git a/topology/options/nodes.nix b/topology/options/nodes.nix index fbe3c0c..7b28473 100644 --- a/topology/options/nodes.nix +++ b/topology/options/nodes.nix @@ -6,14 +6,17 @@ f: { inherit (lib) attrValues + concatMap elem flatten flip literalExpression + mapAttrsToList mkDefault mkIf mkMerge mkOption + toList types ; in @@ -125,6 +128,56 @@ in }; config = { + lib.helpers = rec { + # Finally we found it, the one-and-only true cloud. + mkInternet = {connections ? [], ...}: { + name = "Internet"; + deviceType = "internet"; + hardware.image = ../icons/devices/cloud.svg; + interfaces."*".physicalConnections = toList connections; + }; + + mkConnection = node: interface: {inherit node interface;}; + mkConnectionRev = node: interface: { + inherit node interface; + renderer.reverse = true; + }; + + mkSwitch = name: { + info ? null, + image ? null, + interfaceGroups ? [], + connections ? {}, + ... + }: { + inherit name; + deviceType = "switch"; + hardware = { + info = mkIf (info != null) info; + image = mkIf (image != null) image; + }; + interfaces = mkMerge ( + flip mapAttrsToList connections (interface: conns: { + ${interface}.physicalConnections = toList conns; + }) + ++ flip concatMap interfaceGroups ( + group: + flip map group ( + interface: { + ${interface}.sharesNetworkWith = x: elem x group; + } + ) + ) + ); + }; + + mkRouter = name: args: + mkSwitch name args + // { + deviceType = "router"; + }; + }; + assertions = flatten ( flip map (attrValues config.nodes) ( node: [ diff --git a/topology/topology/renderers/elk/default.nix b/topology/topology/renderers/elk/default.nix index 8956f6a..943f355 100644 --- a/topology/topology/renderers/elk/default.nix +++ b/topology/topology/renderers/elk/default.nix @@ -205,7 +205,7 @@ "portLabels.placement" = "OUTSIDE"; } // optionalAttrs (node.renderer.preferredType == "card") { - "portConstraints" = "FIXED_SIDE"; + # "portConstraints" = "FIXED_SIDE"; }; }; } diff --git a/topology/topology/renderers/svg/default.nix b/topology/topology/renderers/svg/default.nix index 69d8b64..f26b9aa 100644 --- a/topology/topology/renderers/svg/default.nix +++ b/topology/topology/renderers/svg/default.nix @@ -1,4 +1,5 @@ # TODO: +# - macvtap interface type svg with small link # - address port label render make newline capable (multiple port labels) # - NAT indication # - embed font globally, try removing satori embed? @@ -115,9 +116,9 @@ else net.style.secondaryColor; in { - solid = ''
''; - dashed = ''
''; - dotted = ''
''; + solid = ''
''; + dashed = ''
''; + dotted = ''
''; } .${net.style.pattern}; @@ -130,7 +131,7 @@ */ ''
- ${html.net.netStylePreview net "w-8 h-4 mr-4"} + ${html.net.netStylePreview net "w-12 h-4 mr-4 rounded-md"}

${net.name}

${mkImageMaybe "w-12 h-12 ml-4" (config.lib.icons.get net.icon)} @@ -160,12 +161,12 @@ */ ''
-
- ${html.net.netStylePreview net "w-12 h-6 mr-4"} +
+ ${html.net.netStylePreview net "w-12 h-4 mt-2 mr-4 rounded-md"}

${net.name}

- ${optionalString (net.cidrv4 != null) ''
CIDRv4${net.cidrv4}
''} - ${optionalString (net.cidrv6 != null) ''
CIDRv6${net.cidrv6}
''} + ${optionalString (net.cidrv4 != null) ''
CIDRv4${net.cidrv4}
''} + ${optionalString (net.cidrv6 != null) ''
CIDRv6${net.cidrv6}
''}