diff --git a/nix/kexec-installer.nix b/nix/kexec-installer.nix index 698f850..2518bae 100644 --- a/nix/kexec-installer.nix +++ b/nix/kexec-installer.nix @@ -29,6 +29,11 @@ for p in /etc/ssh/ssh_host_*; do cp -a "$p" ssh done + + # save the networking config for later use + ip --json addr > addrs.json + ip --json route > routes.json + find | cpio -o -H newc | gzip -9 > ../extra.gz popd cat "''${SCRIPT_DIR}/initrd" extra.gz > final-initrd @@ -76,14 +81,29 @@ # for detection if we are on kexec environment.etc.is_kexec.text = "true"; + systemd.services.restoreNetwork = { + path = [ + pkgs.iproute2 + ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig.ExecStart = "/run/current-system/sw/bin/restore_network /root/network/addrs.json /root/network/routes.json"; + }; + + environment.systemPackages = [ + (pkgs.writers.writePython3Bin "restore_network" { flakeIgnore = ["E501"]; } ./restore_routes.py) + ]; + # Restore ssh host and user keys if they are available. # This avoids warnings of unknown ssh keys. boot.initrd.postMountCommands = '' mkdir -m 700 -p /mnt-root/root/.ssh mkdir -m 755 -p /mnt-root/etc/ssh + mkdir -m 755 -p /mnt-root/root/network if [[ -f ssh/authorized_keys ]]; then install -m 400 ssh/authorized_keys /mnt-root/root/.ssh fi install -m 400 ssh/ssh_host_* /mnt-root/etc/ssh + cp *.json /mnt-root/root/network/ ''; } diff --git a/nix/restore_routes.py b/nix/restore_routes.py new file mode 100644 index 0000000..7ca4969 --- /dev/null +++ b/nix/restore_routes.py @@ -0,0 +1,83 @@ +import json +import sys +import subprocess + + +def filter_interfaces(network): + output = [] + for net in network: + if net["ifname"] == "lo": + continue + addr_info = [] + for addr in net["addr_info"]: + if addr.get("dynamic", False): + pass + elif addr["local"].startswith("fe80"): + pass + else: + addr_info.append(addr) + if addr_info != []: + net["addr_info"] = addr_info + output.append(net) + + return output + + +def main(): + with open(sys.argv[1]) as f: + addresses = json.load(f) + with open(sys.argv[2]) as f: + routes = json.load(f) + relevant_interfaces = filter_interfaces(addresses) + current_interfaces = json.loads( + subprocess.run( + ["ip", "--json", "addr"], + capture_output=True, + ).stdout + ) + + for interface in relevant_interfaces: + for current_interface in current_interfaces: + if "address" in interface and "address" in current_interface: + if interface["address"] == current_interface["address"]: + for addr in interface["addr_info"]: + subprocess.run( + [ + "ip", + "addr", + "add", + "dev", + current_interface["ifname"], + f'{addr["local"]}/{addr["prefixlen"]}', + ] + ) + for route in routes: + if route["dev"] == interface["ifname"]: + if route.get("gateway", False): + subprocess.run( + [ + "ip", + "route", + "add", + route["dst"], + "via", + route["gateway"], + "dev", + current_interface["ifname"], + ] + ) + else: + subprocess.run( + [ + "ip", + "route", + "add", + route["dst"], + "dev", + current_interface["ifname"], + ] + ) + + +if __name__ == "__main__": + main()