WIP: ap01: towards our VLAN model #181

Draft
rlahfa wants to merge 7 commits from ap01-vlan into main
8 changed files with 143 additions and 54 deletions

View file

@ -116,6 +116,13 @@ in
pkgs.freeradius pkgs.freeradius
pkgs.picocom # for serial access 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 { (pkgs.callPackage ./lib/colmena {
colmena = pkgs.callPackage "${sources.colmena}/package.nix" { }; colmena = pkgs.callPackage "${sources.colmena}/package.nix" { };
}) })

View file

@ -3,16 +3,18 @@ let
svc = config.system.service; svc = config.system.service;
in in
{ {
services.dhcpv4 = svc.network.dhcp.client.build { services.init-dhcpv4 = svc.network.dhcp.client.build {
interface = config.services.int; interface = config.services.int;
dependencies = [ dependencies = [
config.services.bridge.components.lan config.services.bridge.components.lan
]; ];
}; };
services.defaultroute4 = svc.network.route.build { services.init-defaultroute4 = svc.network.route.build {
via = "$(output ${config.services.dhcpv4} router)"; via = "$(output ${config.services.init-dhcpv4} router)";
target = "default"; target = "default";
dependencies = [ config.services.dhcpv4 ]; dependencies = [ config.services.init-dhcpv4 ];
}; };
# TODO: ensure SLAAC for admin-vlan.
} }

View file

@ -8,17 +8,18 @@ in
# TODO: support dynamic reconfiguration once we are in the target VLAN? # TODO: support dynamic reconfiguration once we are in the target VLAN?
services.resolvconf = oneshot rec { services.resolvconf = oneshot rec {
name = "resolvconf"; name = "resolvconf";
# TODO: imho, DNS should be static and provided by the router?
up = '' up = ''
. ${serviceFns} . ${serviceFns}
( in_outputs ${name} ( 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 echo "nameserver $i" >> resolv.conf
done done
) )
''; '';
dependencies = [ dependencies = [
config.services.dhcpv4 config.services.init-dhcpv4
]; ];
}; };

View file

@ -3,37 +3,41 @@ let
svc = config.system.service; svc = config.system.service;
in in
{ {
services.int = svc.bridge.primary.build { # Our bridging is a bit complicated, therefore, we need iproute2.
ifname = "int"; programs.iproute2.enable = true;
macAddressFromInterface = config.hardware.networkInterfaces.lan;
};
services.bridge = svc.bridge.members.build { services = {
primary = config.services.int; int = svc.bridge.primary.build {
members = { ifname = "int";
lan.member = config.hardware.networkInterfaces.lan; macAddressFromInterface = config.hardware.networkInterfaces.lan;
wlan0 = { untagged = {
member = config.hardware.networkInterfaces.wlan0; enable = true;
# Bridge only once hostapd is ready. pvid = 1;
dependencies = [ config.services.hostap-1-ready ]; default-pvid = 1;
};
wlan1 = {
member = config.hardware.networkInterfaces.wlan1;
# Bridge only once hostapd is ready.
dependencies = [ config.services.hostap-2-ready ];
}; };
}; };
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;
# };
} }

View file

@ -6,11 +6,12 @@
}: }:
let let
svc = config.system.service; svc = config.system.service;
parentConfig = config;
in in
{ {
defaultProfile.packages = with pkgs; [ defaultProfile.packages = [
# Levitate enable us to mass-reinstall the system on the fly. # Levitate enable us to mass-reinstall the system on the fly.
(levitate.override { (pkgs.levitate.override {
config = { config = {
imports = [ imports = [
"${modulesPath}/network" "${modulesPath}/network"
@ -19,24 +20,33 @@ in
"${modulesPath}/kernel" "${modulesPath}/kernel"
"${modulesPath}/outputs/tftpboot.nix" "${modulesPath}/outputs/tftpboot.nix"
"${modulesPath}/outputs.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; 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. # Only keep root, which should inherit from DGN access control's root permissions.
users.root = config.users.root; users.root = config.users.root;
}; };

View file

@ -131,9 +131,9 @@
"url": "https://git.dgnum.eu/DGNum/liminix" "url": "https://git.dgnum.eu/DGNum/liminix"
}, },
"branch": "main", "branch": "main",
"revision": "473d6acc3de70bd6dbbb4a77af54f508f25c3c9c", "revision": "1322de1ee0cdb19fead79e12ab279ee0b575019a",
"url": null, "url": null,
"hash": "00slsh0yqd8n8jcx3sbxgcmw1z28bnszy87pfs0ynfkl3bldzs3d" "hash": "07nk6nik97k8a57cf17dcj3gn2lbhw1myymrxpqc2aqa3haj754k"
}, },
"linkal": { "linkal": {
"type": "Git", "type": "Git",

View file

@ -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_image_path> <firmware_output_file>"
}
ZYXEL_NWA_FIT="$1"
FIRMWARE_OUTPUT="$2"
dumpimage -T flat_dt -p 0 $ZYXEL_NWA_FIT -o $FIRMWARE_OUTPUT

53
scripts/liminix-rebuild.sh Executable file
View file

@ -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