From bce44930b10736ed9c8d04edf4871a4be68148f5 Mon Sep 17 00:00:00 2001 From: Yureka Date: Sun, 4 Aug 2024 18:12:02 +0200 Subject: [PATCH] builders: provision ssh hostkeys on boot --- hosts/wob-vpn-gw/netboot.nix | 50 ++++++++++++++++++++------ services/baremetal-builder/netboot.nix | 19 ++++++++++ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/hosts/wob-vpn-gw/netboot.nix b/hosts/wob-vpn-gw/netboot.nix index 5afb137..08d39ae 100644 --- a/hosts/wob-vpn-gw/netboot.nix +++ b/hosts/wob-vpn-gw/netboot.nix @@ -1,26 +1,30 @@ -{ lib, pkgs, nodes, config, modulesPath, ... }: +{ config, lib, pkgs, nodes, modulesPath, ... }: # The way the connection is established is specific to the wob01 site and the Intel S2600KPR blades. # Proper netboot is not possible, because while the blades and the APU board (which is the netboot # server here) are in the same L2 network, the uplink connection of each blade is an LACP LAG, # meaning that the switch on the other side will only enable the port if it sees valid LACP packets. -# IPXE sends out these LACP packets while it is probing the ports, however the NICs of the blades -# do not have a flash which IPXE could be written to. # We work around this by presenting a virtual floppy drive using the "IUSB" protocol of the BMC. -# This virtual floppy drive contains an per-blade customized IPXE script which will initialize the -# network connection including IP configuration and chainload the actual script off the netboot -# server. +# This virtual floppy drive contains an per-blade customized initramfs which will initialize the +# network connection including IP configuration and load the actual image off hydra. let netboot-server-ip = "2a01:584:11::2"; + netbootNodes = lib.filterAttrs (_: node: node.config.bagel.baremetal.builders.enable && node.config.bagel.baremetal.builders.netboot) nodes; in { - networking.firewall.allowedTCPPorts = [ 80 ]; + + assertions = [ + { + assertion = !(lib.elem 443 config.networking.firewall.allowedTCPPorts); + message = '' + Port 443 is in networking.firewalls.allowedTCPPorts, but should be only manually + allowed for specific IPs and source ports in ${builtins.toJSON __curPos} + ''; + } + ]; systemd.services = lib.mapAttrs' (nodename: node: let - ip = "2a01:584:11::1:${toString node.config.bagel.baremetal.builders.num}"; bmcIp = "192.168.1.${toString (node.config.bagel.baremetal.builders.num * 4 + 2)}"; - gw = "2a01:584:11::1"; - dns = "2a01:580:6000::ff01"; notipxe = node.config.system.build.notipxe.config.system.build.usbImage; in lib.nameValuePair "iusb-spoof-${nodename}" { wantedBy = [ "multi-user.target" ]; @@ -31,5 +35,29 @@ in { AUTH_TOKEN=$(${pkgs.iusb-spoof}/bin/make-token ${bmcIp}) exec ${pkgs.iusb-spoof}/bin/iusb-spoof -r ${bmcIp} 5123 $AUTH_TOKEN ${notipxe} ''; - }) (lib.filterAttrs (_: node: node.config.bagel.baremetal.builders.enable && node.config.bagel.baremetal.builders.netboot) nodes); + }) netbootNodes; + + # Since the builders are stateless, they can not store their ssh hostkeys + networking.firewall.allowedTCPPorts = [ 80 ]; # for ACME + networking.firewall.extraInputRules = '' + ip6 saddr 2a01:584:11::/64 tcp sport < 1024 tcp dport 443 accept; + ''; + security.acme.acceptTerms = true; + security.acme.defaults.email = "infra@forkos.org"; + services.nginx = { + enable = true; + virtualHosts."vpn-gw.wob01.infra.forkos.org" = { + enableACME = true; + forceSSL = true; + locations = lib.mapAttrs' (nodename: node: let + ip = "2a01:584:11::1:${toString node.config.bagel.baremetal.builders.num}"; + in lib.nameValuePair "/${nodename}/" { + root = "/var/www"; + extraConfig = '' + allow ${ip}; + deny all; + ''; + }) netbootNodes; + }; + }; } diff --git a/services/baremetal-builder/netboot.nix b/services/baremetal-builder/netboot.nix index 06a46be..16705e3 100644 --- a/services/baremetal-builder/netboot.nix +++ b/services/baremetal-builder/netboot.nix @@ -4,6 +4,25 @@ let in { config = lib.mkIf (cfg.enable && cfg.netboot) { + + system.activationScripts.agenixInstall.deps = ["provisionSshHostKey"]; + system.activationScripts.provisionSshHostKey = { + text = '' + echo provisioning ssh hostkey + if [ ! -f /etc/ssh/ssh_host_ed25519_key ] + then + mkdir -p /etc/ssh + ( + umask 0077 + curl --local-port 25-1024 https://vpn-gw.wob01.infra.forkos.org/${config.networking.hostName}/ssh_host_ed25519_key > /etc/ssh/ssh_host_ed25519_key + ) + fi + ''; + deps = [ + "specialfs" + ]; + }; + system.build = { # Build a kernel and initramfs which will download the IPXE script from hydra using