diff --git a/default.nix b/default.nix index 2041484..6f889f9 100644 --- a/default.nix +++ b/default.nix @@ -116,6 +116,13 @@ in pkgs.freeradius pkgs.picocom # for serial access + # Daemon-less copy closure for Liminix systems. + (pkgs.callPackage (sources.liminix + "/pkgs/min-copy-closure") { nix = pkgs.lix; }) + # Daemon-less garbage collection for Liminix systems. + (pkgs.callPackage (sources.liminix + "/pkgs/min-collect-garbage") { nix = pkgs.lix; }) + # TFTP server, friendly for Nix builds. + (pkgs.callPackage (sources.liminix + "/pkgs/tufted") { }) + (pkgs.callPackage ./lib/colmena { colmena = pkgs.callPackage "${sources.colmena}/package.nix" { }; }) diff --git a/machines/liminix/ap01/addresses.nix b/machines/liminix/ap01/addresses.nix index b3d0840..932a805 100644 --- a/machines/liminix/ap01/addresses.nix +++ b/machines/liminix/ap01/addresses.nix @@ -3,16 +3,18 @@ let svc = config.system.service; in { - services.dhcpv4 = svc.network.dhcp.client.build { + services.init-dhcpv4 = svc.network.dhcp.client.build { interface = config.services.int; dependencies = [ config.services.bridge.components.lan ]; }; - services.defaultroute4 = svc.network.route.build { - via = "$(output ${config.services.dhcpv4} router)"; + services.init-defaultroute4 = svc.network.route.build { + via = "$(output ${config.services.init-dhcpv4} router)"; target = "default"; - dependencies = [ config.services.dhcpv4 ]; + dependencies = [ config.services.init-dhcpv4 ]; }; + + # TODO: ensure SLAAC for admin-vlan. } diff --git a/machines/liminix/ap01/dns.nix b/machines/liminix/ap01/dns.nix index d8346f8..0cbe3f6 100644 --- a/machines/liminix/ap01/dns.nix +++ b/machines/liminix/ap01/dns.nix @@ -8,17 +8,18 @@ in # TODO: support dynamic reconfiguration once we are in the target VLAN? services.resolvconf = oneshot rec { name = "resolvconf"; + # TODO: imho, DNS should be static and provided by the router? up = '' . ${serviceFns} ( in_outputs ${name} - for i in $(output ${config.services.dhcpv4} dns); do + for i in $(output ${config.services.init-dhcpv4} dns); do echo "nameserver $i" >> resolv.conf done ) ''; dependencies = [ - config.services.dhcpv4 + config.services.init-dhcpv4 ]; }; diff --git a/machines/liminix/ap01/lan.nix b/machines/liminix/ap01/lan.nix index 4916706..f8b0051 100644 --- a/machines/liminix/ap01/lan.nix +++ b/machines/liminix/ap01/lan.nix @@ -3,37 +3,41 @@ let svc = config.system.service; in { - services.int = svc.bridge.primary.build { - ifname = "int"; - macAddressFromInterface = config.hardware.networkInterfaces.lan; - }; + # Our bridging is a bit complicated, therefore, we need iproute2. + programs.iproute2.enable = true; - services.bridge = svc.bridge.members.build { - primary = config.services.int; - members = { - lan.member = config.hardware.networkInterfaces.lan; - wlan0 = { - member = config.hardware.networkInterfaces.wlan0; - # Bridge only once hostapd is ready. - dependencies = [ config.services.hostap-1-ready ]; - }; - wlan1 = { - member = config.hardware.networkInterfaces.wlan1; - # Bridge only once hostapd is ready. - dependencies = [ config.services.hostap-2-ready ]; + services = { + int = svc.bridge.primary.build { + ifname = "int"; + macAddressFromInterface = config.hardware.networkInterfaces.lan; + untagged = { + enable = true; + pvid = 1; + default-pvid = 1; }; }; + + bridge = svc.bridge.members.build { + primary = config.services.int; + members = { + lan.member = config.hardware.networkInterfaces.lan; + wlan0 = { + member = config.hardware.networkInterfaces.wlan0; + # Bridge only once hostapd is ready. + dependencies = [ config.services.hostap-1-ready ]; + }; + wlan1 = { + member = config.hardware.networkInterfaces.wlan1; + # Bridge only once hostapd is ready. + dependencies = [ config.services.hostap-2-ready ]; + }; + }; + }; + + admin-vlan = svc.vlan.build { + ifname = "admin"; + primary = config.services.int; + vid = "3001"; + }; }; - - # Default VLAN - # services.vlan-apro = svc.vlan.build { - # vlanId = 0; - # interface = config.services.int; - # }; - - # # Administration VLAN - # services.vlan-admin = svc.vlan.build { - # vlan = 3001; - # interface = config.services.int; - # }; } diff --git a/machines/liminix/ap01/recovery.nix b/machines/liminix/ap01/recovery.nix index 38271f3..70c3e2e 100644 --- a/machines/liminix/ap01/recovery.nix +++ b/machines/liminix/ap01/recovery.nix @@ -6,11 +6,12 @@ }: let svc = config.system.service; + parentConfig = config; in { - defaultProfile.packages = with pkgs; [ + defaultProfile.packages = [ # Levitate enable us to mass-reinstall the system on the fly. - (levitate.override { + (pkgs.levitate.override { config = { imports = [ "${modulesPath}/network" @@ -19,24 +20,33 @@ in "${modulesPath}/kernel" "${modulesPath}/outputs/tftpboot.nix" "${modulesPath}/outputs.nix" + ( + { config, ... }: + { + services = { + # In this situation, we fallback to the appro VLAN. + # TODO: add support for the admin VLAN. + # Simplest DHCPv4 we can find. + dhcpv4 = svc.network.dhcp.client.build { + interface = parentConfig.hardware.networkInterfaces.lan; + }; + inherit (parentConfig.services) sshd; + defaultroute4 = svc.network.route.build { + via = "$(output ${config.services.dhcpv4} router)"; + target = "default"; + dependencies = [ config.services.dhcpv4 ]; + }; + }; + } + ) ]; + hostname = "${parentConfig.hostname}-live"; nixpkgs.buildPlatform = builtins.currentSystem; - services = { - # In this situation, we fallback to the appro VLAN. - # TODO: add support for the admin VLAN. - # Simplest DHCPv4 we can find. - dhcpv4 = svc.network.dhcp.client.build { - interface = config.hardware.networkInterfaces.lan; - }; - inherit (config.services) sshd; - defaultroute4 = svc.network.route.build { - via = "$(output ${config.services.dhcpv4} router)"; - target = "default"; - dependencies = [ config.services.dhcpv4 ]; - }; - }; - defaultProfile.packages = [ mtdutils ]; + defaultProfile.packages = with pkgs; [ + mtdutils + zyxel-bootconfig + ]; # Only keep root, which should inherit from DGN access control's root permissions. users.root = config.users.root; }; diff --git a/npins/sources.json b/npins/sources.json index d376076..a9adcb1 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -131,9 +131,9 @@ "url": "https://git.dgnum.eu/DGNum/liminix" }, "branch": "main", - "revision": "473d6acc3de70bd6dbbb4a77af54f508f25c3c9c", + "revision": "1322de1ee0cdb19fead79e12ab279ee0b575019a", "url": null, - "hash": "00slsh0yqd8n8jcx3sbxgcmw1z28bnszy87pfs0ynfkl3bldzs3d" + "hash": "07nk6nik97k8a57cf17dcj3gn2lbhw1myymrxpqc2aqa3haj754k" }, "linkal": { "type": "Git", diff --git a/scripts/extract-firmware-from-zyxel-nwa-fit.sh b/scripts/extract-firmware-from-zyxel-nwa-fit.sh new file mode 100755 index 0000000..12a8cd2 --- /dev/null +++ b/scripts/extract-firmware-from-zyxel-nwa-fit.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p ubootTools + +usage() { + echo "extract the firmware part to write it manually from a Zyxel NWA FIT image" + echo "$0 " +} + +ZYXEL_NWA_FIT="$1" +FIRMWARE_OUTPUT="$2" + +dumpimage -T flat_dt -p 0 $ZYXEL_NWA_FIT -o $FIRMWARE_OUTPUT diff --git a/scripts/liminix-rebuild.sh b/scripts/liminix-rebuild.sh new file mode 100755 index 0000000..5dd9c2b --- /dev/null +++ b/scripts/liminix-rebuild.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# TODO: support automatic levitation when the rebuild counts does not fit in the target. +# TODO: support preflight checks where we detect the environment we are booted in (this requires the levitation to write a special file). +# TODO: use colmena to know the target host and focus on the node name. +set -Eeuo pipefail + +ssh_command=${SSH_COMMAND-ssh} +root_prefix=${ROOT_PREFIX-/} + +reboot="reboot" + +case "$1" in + "--no-reboot") + unset reboot + shift + ;; + "--fast") + reboot="soft" + shift + ;; + "--live") + echo "Root prefix changed from $root_prefix to /mnt" + root_prefix="/mnt" + shift + ;; +esac + +target_host=$1 +shift + +if [ -z "$target_host" ] ; then + echo Usage: liminix-rebuild \[--no-reboot\] target-host params + exit 1 +fi + +if toplevel="$(nom-build $(colmena eval -E "{ nodes, ... }: nodes.$@.config.system.outputs.systemConfiguration" --instantiate))"; then + echo systemConfiguration $toplevel aimed at $root_prefix + sleep 3 + min-copy-closure --root "$root_prefix" $target_host $toplevel + $ssh_command $target_host "$root_prefix/$toplevel/bin/install" "$root_prefix" + case "$reboot" in + reboot) + $ssh_command $target_host "sync; source /etc/profile; reboot" + ;; + soft) + $ssh_command $target_host $toplevel/bin/restart-services + ;; + *) + ;; + esac +else + echo Rebuild failed +fi