diff --git a/default.nix b/default.nix index f12d53a..a56e937 100644 --- a/default.nix +++ b/default.nix @@ -187,11 +187,17 @@ in })) pkgs.npins + pkgs.rage + # SSO testing pkgs.kanidm 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; }) (pkgs.callPackage ./lib/colmena { colmena = pkgs.callPackage "${sources.colmena}/package.nix" { }; }) diff --git a/hive.nix b/hive.nix index df57365..666c79d 100644 --- a/hive.nix +++ b/hive.nix @@ -111,12 +111,12 @@ in args; defaults = - { name, nodePath, ... }: + { name, ... }: { # Import the default modules imports = [ # Import the base configuration for each node - ./${nodePath}/_configuration.nix + ./machines/liminix/ap-v01/_configuration.nix ./modules/generic ./modules/${category name} ]; diff --git a/machines/liminix/ap01/_configuration.nix b/machines/liminix/ap-v01/_configuration.nix similarity index 92% rename from machines/liminix/ap01/_configuration.nix rename to machines/liminix/ap-v01/_configuration.nix index 00c0a3e..86741f0 100644 --- a/machines/liminix/ap01/_configuration.nix +++ b/machines/liminix/ap-v01/_configuration.nix @@ -5,6 +5,7 @@ { modulesPath, sourcePkgs, + name, ... }: { @@ -12,6 +13,7 @@ "${modulesPath}/wlan.nix" "${modulesPath}/network" "${modulesPath}/hostapd" + "${modulesPath}/usteer" "${modulesPath}/ssh" "${modulesPath}/ntp" "${modulesPath}/vlan" @@ -29,6 +31,8 @@ ./addresses.nix # Configures a basic local DNS. ./dns.nix + # Add ubus daemon + ./ipc.nix # Configures our management layer, e.g. SSH server + DGNum FAI keys. ./management.nix # Configures our recovery system, e.g. a levitation script. @@ -39,6 +43,6 @@ (import "${modulesPath}/../devices/zyxel-nwa50ax").module ]; - hostname = "ap01-prototype"; + hostname = name; nixpkgs.source = sourcePkgs.path; } diff --git a/machines/liminix/ap-v01/addresses.nix b/machines/liminix/ap-v01/addresses.nix new file mode 100644 index 0000000..7dc61ff --- /dev/null +++ b/machines/liminix/ap-v01/addresses.nix @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2024 Ryan Lahfa +# +# SPDX-License-Identifier: EUPL-1.2 + +{ config, nodeMeta, ... }: +let + svc = config.system.service; + inherit (nodeMeta.extraNodeSettings) building floor ap-no; + # FIXME switch to ipv6 tu be able to scale + adminIp = "10.0.253.${builtins.toString (ap-no + floor * 8 + building * 32 + 2)}"; +in +{ + services.admin-ip = svc.network.address.build { + interface = config.services.admin-vlan; + address = adminIp; + prefixLength = 24; + family = "inet"; + }; + + services.admin-defaultroute4 = svc.network.route.build { + via = "10.0.253.1"; + target = "default"; + dependencies = [ config.services.admin-ip ]; + }; +} diff --git a/machines/liminix/ap-v01/dns.nix b/machines/liminix/ap-v01/dns.nix new file mode 100644 index 0000000..b371292 --- /dev/null +++ b/machines/liminix/ap-v01/dns.nix @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2024 Ryan Lahfa +# +# SPDX-License-Identifier: EUPL-1.2 + +{ pkgs, lib, ... }: +let + inherit (pkgs.pseudofile) dir symlink; + # TODO: imho, DNS should be static and provided by the router? + dns = [ + "8.8.8.8" + "8.8.4.4" + "1.0.0.1" + ]; + resolvconf = pkgs.writeText "resolv.conf" ( + lib.concatMapStringsSep "\n" (dns: ''echo "nameserver ${dns}" >> resolv.conf'') dns + ); +in +{ + # TODO: support dynamic reconfiguration once we are in the target VLAN? + filesystem = dir { + etc = dir { + "resolv.conf" = symlink "${resolvconf}"; + }; + }; +} diff --git a/machines/liminix/ap01/ipc.nix b/machines/liminix/ap-v01/ipc.nix similarity index 100% rename from machines/liminix/ap01/ipc.nix rename to machines/liminix/ap-v01/ipc.nix diff --git a/machines/liminix/ap-v01/lan.nix b/machines/liminix/ap-v01/lan.nix new file mode 100644 index 0000000..1d73ece --- /dev/null +++ b/machines/liminix/ap-v01/lan.nix @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2024 Ryan Lahfa +# +# SPDX-License-Identifier: EUPL-1.2 + +{ config, ... }: +let + svc = config.system.service; +in +{ + # Our bridging is a bit complicated, therefore, we need iproute2. + programs.iproute2.enable = true; + + services = { + admin-vlan = svc.vlan.build { + ifname = "admin"; + primary = config.hardware.networkInterfaces.lan; + vid = "3001"; + }; + }; +} diff --git a/machines/liminix/ap01/management.nix b/machines/liminix/ap-v01/management.nix similarity index 91% rename from machines/liminix/ap01/management.nix rename to machines/liminix/ap-v01/management.nix index aca367c..18f6b6f 100644 --- a/machines/liminix/ap01/management.nix +++ b/machines/liminix/ap-v01/management.nix @@ -10,6 +10,7 @@ in # SSH keys are handled by the access control module. dgn-access-control.enable = true; users.root = { + # TODO: Change this well-known password passwd = "$6$Z2MiaMXkpUJRPl2/$fxVE3iD/n208CISM2F6OnWj0Qq0QG2tTQqLCjU80PFJJGIwNLLyOp6SeYH3dH20OvJX1loZRETrThZfIPw.rb/"; }; services.sshd = svc.ssh.build { allowRoot = true; }; diff --git a/machines/liminix/ap01/metadata.nix b/machines/liminix/ap-v01/metadata.nix similarity index 100% rename from machines/liminix/ap01/metadata.nix rename to machines/liminix/ap-v01/metadata.nix diff --git a/machines/liminix/ap-v01/recovery.nix b/machines/liminix/ap-v01/recovery.nix new file mode 100644 index 0000000..35edaa7 --- /dev/null +++ b/machines/liminix/ap-v01/recovery.nix @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2024 Ryan Lahfa +# +# SPDX-License-Identifier: EUPL-1.2 + +{ + config, + pkgs, + modulesPath, + ... +}: +let + svc = config.system.service; + parentConfig = config; +in +{ + defaultProfile.packages = [ + # Levitate enable us to mass-reinstall the system on the fly. + # TODO: Test levitation + (pkgs.levitate.override { + config = { + imports = [ + "${modulesPath}/network" + "${modulesPath}/ssh" + "${modulesPath}/hardware.nix" + "${modulesPath}/kernel" + "${modulesPath}/outputs/tftpboot.nix" + "${modulesPath}/outputs.nix" + # FIXME: DHCP has a hidden deps on this, shoud be done in a more intelligent way upstream + "${modulesPath}/iproute2.nix" + ( + { config, ... }: + { + # FIXME: DHCP has a hidden deps on this, shoud be done in a more intelligent way upstream + programs.iproute2.enable = true; + services = { + # In this situation, we fallback to the appro VLAN but keep admin vlan. + # Simplest DHCPv4 we can find. + dhcpv4 = svc.network.dhcp.client.build { + interface = parentConfig.hardware.networkInterfaces.lan; + }; + inherit (parentConfig.services) + sshd + admin-vlan + admin-ip + admin-defaultroute4 + ; + 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; + + 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/machines/liminix/ap01/system.nix b/machines/liminix/ap-v01/system.nix similarity index 100% rename from machines/liminix/ap01/system.nix rename to machines/liminix/ap-v01/system.nix diff --git a/machines/liminix/ap01/wlan.nix b/machines/liminix/ap-v01/wlan.nix similarity index 69% rename from machines/liminix/ap01/wlan.nix rename to machines/liminix/ap-v01/wlan.nix index aede645..ec724a6 100644 --- a/machines/liminix/ap01/wlan.nix +++ b/machines/liminix/ap-v01/wlan.nix @@ -2,14 +2,28 @@ # # SPDX-License-Identifier: EUPL-1.2 -{ config, pkgs, ... }: +{ + config, + pkgs, + lib, + nodeMeta, + ... +}: let svc = config.system.service; + + inherit (nodeMeta.extraNodeSettings) building floor ap-no; + + hex = x: lib.fixedWidthString 2 "0" (lib.toHexString x); + + mac-1 = "02:5B:6A:${hex (building * 4)}:${hex floor}:${hex ap-no}"; + mac-2 = "02:5B:6A:${hex (building * 4 + 1)}:${hex floor}:${hex ap-no}"; + secrets-1 = { - ssid = "DGNum 2G (N)"; + ssid = "DGNum"; }; secrets-2 = { - ssid = "DGNum 5G (AX)"; + ssid = "DGNum"; }; baseParams = { country_code = "FR"; @@ -42,7 +56,7 @@ let vht_capab = "[MAX-MPDU-7991][SU-BEAMFORMEE][SU-BEAMFORMER][RXLDPC][SHORT-GI-80][MAX-A-MPDU-LEN-EXP3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][TX-STBC-2BY1][RX-STBC-1][MU-BEAMFORMER]"; vht_oper_chwidth = 1; he_oper_chwidth = 1; - channel = 36; + channel = 36; # TODO understand interferences vht_oper_centr_freq_seg0_idx = 42; he_oper_centr_freq_seg0_idx = 42; require_vht = 1; @@ -52,7 +66,7 @@ let ieee8021x = 1; eapol_version = 2; use_pae_group_addr = 1; - dynamic_vlan = 0; + dynamic_vlan = 3; vlan_tagged_interface = "lan"; }; @@ -64,7 +78,14 @@ let # No DNS here, hostapd do not support this mode. auth_server_addr = "129.199.195.129"; auth_server_port = 1812; - auth_server_shared_secret = "read it online"; + auth_server_shared_secret = + let + secret = builtins.getEnv "RADIUS_SECRET"; + in + if secret == "" then + lib.warn "Using a dummy RADIUS secret. Please do not use in production" "DUMMYSECRET" + else + secret; }; mkWifiSta = @@ -77,10 +98,14 @@ let }; in { + hardware.wlanMacAddresses = { + wlan0 = mac-1; + wlan1 = mac-2; + }; services = { # wlan0 is the 2.4GHz interface. hostap-1 = mkWifiSta ( - baseParams // radiusKeyMgmt + baseParams // clientRadius // externalRadius // radiusKeyMgmt ) config.hardware.networkInterfaces.wlan0 secrets-1; hostap-1-ready = svc.hostapd-ready.build { interface = config.hardware.networkInterfaces.wlan0; @@ -93,5 +118,14 @@ in hostap-2-ready = svc.hostapd-ready.build { interface = config.hardware.networkInterfaces.wlan1; }; + usteer = svc.usteer.build { + ifname = "lan"; + dependencies = with config.services; [ + # FIXME: is it the right stuff to depend on + hostap-1-ready + hostap-2-ready + admin-defaultroute4 + ]; + }; }; } diff --git a/machines/liminix/ap01/addresses.nix b/machines/liminix/ap01/addresses.nix deleted file mode 100644 index 0281105..0000000 --- a/machines/liminix/ap01/addresses.nix +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ryan Lahfa -# -# SPDX-License-Identifier: EUPL-1.2 - -{ config, ... }: -let - svc = config.system.service; -in -{ - services.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)"; - target = "default"; - dependencies = [ config.services.dhcpv4 ]; - }; -} diff --git a/machines/liminix/ap01/dns.nix b/machines/liminix/ap01/dns.nix deleted file mode 100644 index 905e5aa..0000000 --- a/machines/liminix/ap01/dns.nix +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ryan Lahfa -# -# SPDX-License-Identifier: EUPL-1.2 - -{ config, pkgs, ... }: -let - inherit (pkgs.liminix.services) oneshot; - inherit (pkgs.pseudofile) dir symlink; - inherit (pkgs) serviceFns; -in -{ - # TODO: support dynamic reconfiguration once we are in the target VLAN? - services.resolvconf = oneshot rec { - name = "resolvconf"; - up = '' - . ${serviceFns} - ( in_outputs ${name} - for i in $(output ${config.services.dhcpv4} dns); do - echo "nameserver $i" >> resolv.conf - done - ) - ''; - - dependencies = [ - config.services.dhcpv4 - ]; - }; - - filesystem = dir { - etc = dir { - "resolv.conf" = symlink "${config.services.resolvconf}/.outputs/resolv.conf"; - }; - }; -} diff --git a/machines/liminix/ap01/lan.nix b/machines/liminix/ap01/lan.nix deleted file mode 100644 index 5f15b71..0000000 --- a/machines/liminix/ap01/lan.nix +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ryan Lahfa -# -# SPDX-License-Identifier: EUPL-1.2 - -{ config, ... }: -let - svc = config.system.service; -in -{ - services.int = svc.bridge.primary.build { - ifname = "int"; - macAddressFromInterface = config.hardware.networkInterfaces.lan; - }; - - 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 ]; - }; - }; - }; - - # 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 deleted file mode 100644 index d76349c..0000000 --- a/machines/liminix/ap01/recovery.nix +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ryan Lahfa -# -# SPDX-License-Identifier: EUPL-1.2 - -{ - config, - pkgs, - modulesPath, - ... -}: -let - svc = config.system.service; -in -{ - defaultProfile.packages = with pkgs; [ - # Levitate enable us to mass-reinstall the system on the fly. - (levitate.override { - config = { - imports = [ - "${modulesPath}/network" - "${modulesPath}/ssh" - "${modulesPath}/hardware.nix" - "${modulesPath}/kernel" - "${modulesPath}/outputs/tftpboot.nix" - "${modulesPath}/outputs.nix" - ]; - 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 ]; - # Only keep root, which should inherit from DGN access control's root permissions. - users.root = config.users.root; - }; - }) - ]; -} diff --git a/machines/nixos/vault01/secrets/radius-ap-radius-secret_file b/machines/nixos/vault01/secrets/radius-ap-radius-secret_file index 0bb33d8..256cf6a 100644 --- a/machines/nixos/vault01/secrets/radius-ap-radius-secret_file +++ b/machines/nixos/vault01/secrets/radius-ap-radius-secret_file @@ -1,32 +1,32 @@ age-encryption.org/v1 --> ssh-ed25519 jIXfPA 2nFaxyP7O4GWU7U3wmET5sNrnFq72b9DEhiKEgWVrFk -l8uXfCBkTHogzVoUY0WOYhA99fodoT+N0HunacULydI --> ssh-ed25519 QlRB9Q qDalihZE404oPOVHYQR5GIvozXNh4wNxhUa5Zwfz2DU -X8qvWf7qprbh0xu/uOHGsNLTQc8efYsgveH9R9kZZZw --> ssh-ed25519 r+nK/Q mksHDhPoKKxQpk4sQPHapdq87EaJmgdmoVxMYjsAang -FTYHyxLp4nGOWJu1135yN/lQkGgAD9Jy4JJpMKFktrk +-> ssh-ed25519 jIXfPA TdfYeqsPJBf26CO1Bh74K8qxqR1MX3VUvZ/e73+oDXQ +KoA/I5kVXxryQ86qjfzq67Aiar+qDZF9OoF4MsNDqe4 +-> ssh-ed25519 QlRB9Q ZaPziTdzqf0vCkCiLWAUJbnROaZ7Mz+Xgw1viEMWM2s +I0peAEQPbaXL9eHQ/OraNuqJPCxIwjpxIxhvgAifATY +-> ssh-ed25519 r+nK/Q kgmK60IgdW4QFdKqBQ6S9JmQVoRvpmffVaoNWzfV5Bc +ru8etu+7QOmnAoJv8BLtEK0SuDfhB75l525ORrDirvM -> ssh-rsa krWCLQ -jEPt5eWP6NmpOikLhs1uPVo7kxHgg1y7WwdOPyR0z2vpFD2BWGlIi/BvnlE3OO5n -jtvDjAauWU0X2JarfdY9mY8MoPjT9qQ/ukxuVAHi5CoL/I1JCqcbuftssYY0B7Ab -SMfbyxjK8aIT1/4EQhMoWm0tuIylvgTBagL03Lw5mbyRqDkbpI/6YC9401YjT7Ts -dCDGIFAYM2BA7TuJiZr881ypUdU9rlm5rss1ZLMj90jyJPJC4SDYbzE0BoBat9l0 -dYUrYGhGgZ1cDd6D6mPf6H95muiGHIhxaE8c+LdK/rKCSH9Rf6mfn/Ab/xvnaDNn -GW/WD0EpmdzpWVPby68+KA --> ssh-ed25519 /vwQcQ 5DoMxdoK+KiHXKwwOpb7/1FZIEzAa/2/1l8yyxey6iw -RzmUkqZQLM5/jDXG9fxhZmfAywgVMjH9Y3O66BnhCSQ --> ssh-ed25519 0R97PA g+uW/jfwHB3m0AdWxb9vPRjeaowhEx1Uoc2R0CVStlA -m5XvSEVQ8DiA7BSTsxVn6S1zv92CpbyZxSgUI3ObE4c --> ssh-ed25519 JGx7Ng BtdJpskbfPyywYeFbmQw3HGPTLv5ri6x4bFocr9l6H8 -88aFw+MCJLqMU/W/ikYDUZEAi0ImaPVbSc7cAZPbs/I --> ssh-ed25519 5SY7Kg +JUMQfaxl7Orym43LVeqUyno0JfUbVnB+xv7smpdRhE -6K+Ewq1FhrXB2eYdljlsYpIfmVv49E4jSBsphgDpRJk --> ssh-ed25519 p/Mg4Q AITnEN+Q41fEA2tkvVOKGCDZiuCXanG+qaiF5X4ukiA -NvP/HXOliNvi8tngH9PU90E616CPlh/QgkZ052H8wtk --> ssh-ed25519 +mFdtQ RuaXIQNZ3s9C27XtpVTExJlAhYDYXRQni+Hwot0wrzU -WctqqoGS2hVfOZSU3ihCg5eI7PnxM7dkOJKM9DJ90Wk --> ssh-ed25519 5rrg4g cAqJQ8z6T46YwzahtcTJxXZHklCGrupVCja5U/g+ZmM -wERu5T6rOi5/0qPSXeOnfA0Szg7/pbYFTW0Ys1yWq40 --> ssh-ed25519 oRtTqQ NF73c0d1qM4nVt2bEdWTEDjDcz/ZMCObn/7cDZfkVGA -Mivm+WWVqAfNs5pLwGmINIsmxlEZi7m7bQIRxGkf3/Q ---- 8R1h+xsovrLq+5QI1CoTXc9TBTQugnROZpOAHWBwG1w -G"8&NF}xksy\.iקF}-ӚLb;{ \ No newline at end of file +QsgW7OvOB3cOz9MZI1PQ6Fe208WS+Sv/TWcucjD9i28U6Bty1KYeSwMH/zyzLuSe +51TqJTnkb+xGcqw3RvKiM58HMFcl6INmOI8otGxfCQSX7p3/QxiGQBbIgRblxtWB +8Jf55hgfh+1+vwTcM+BlBRWz4K581MeQiF2jj6ihfJNwTZ7Q9jNvgzF42znEyZyE +QTHoR9ROA/HqLgcrui1L7QnBlP1Y9Bt/oMCh4jFwHfcc6NeHF+I6AEeQNAHH9iNX +2+1RsJnQrTM+H204GrpVK78e1B5uCjvq/LeoWSQ3pFD9PwdM6JW2WfkB4FSCriAI +7ZAg64qNahyjX+J+KDlrwQ +-> ssh-ed25519 /vwQcQ MBPiBQdz65VVKMxJDlTCFUfG084K0ZcGpPJc5RKKND4 +jH9fRJ/tcGQpZQ+pGNw9lXcRbPS8LLsuwe4EUsaFGDM +-> ssh-ed25519 0R97PA bvY5a3GO1CfVmCPJwBfFGJcS+Zkr2QRENa0WyzqspGc +YgxthAE4TIPlweuH8cWaOmVGqomc2yfLdzjO8G8bytw +-> ssh-ed25519 JGx7Ng 11We2girRvmkDm8eWkTZnazm7Ly0tmECFTdSFnBKIQQ +VQ+jlP1sk+SPkHARgAly9U7W0HVbpvZvxLN4V5l6JwE +-> ssh-ed25519 bUjjig Zt2Br6ls9INAJ5aQZ/az+6+rIpDCf/NCJP2zusdggms +3k0NOSVDpbQFEflEvyTzKv1/zXUBVN5ub9jjOe4EybM +-> ssh-ed25519 +mFdtQ inTgQzJVaYt8JZjtrjVzZzW9PscvBnZWkXIpEQYtdFI +O/Z7ccZam386C6r2UVJS+OMwG8nZ57RmUy+VJEgWJEY +-> ssh-ed25519 5rrg4g ApGMepP+32epekSxCfLGJs6uI38WPjWxtdk+q1Lvx0I +huEBiiNzTcz5hPUs+INfDyfeqKtl+mYE38PUizHktyI +-> ssh-ed25519 oRtTqQ QBBeZ0kLMPuDmO0hT7LvMs31WuVZATUSyxtCxgMzHgQ +HooCKv78+xzYnOwaYXbRNVH1XpG1e8tY0PB246nkFU8 +-> G8<-grease +58RFQqg54Xu8pavoh6wbEnJl7J8XJ5rgaVq1bxokhQ +--- +gYhV/IjEqBw3YKDEeSbepgAIIO6A/BcpsYrwCy+Ezs +%b7}nx~YZ{OMlHW Xk@9 \ No newline at end of file diff --git a/meta/nodes/liminix.nix b/meta/nodes/liminix.nix index 5c6e952..8f654a2 100644 --- a/meta/nodes/liminix.nix +++ b/meta/nodes/liminix.nix @@ -14,19 +14,77 @@ # deployment = {}; # Colmena deployment options # nixpkgs = "unstable" or "22.11"; # nixpkgs version # } +let + nix-lib = import ../../lib/nix-lib; + inherit (nix-lib) mapFuse; -{ - ap01 = { - site = "unknown"; - adminGroups = [ "fai" ]; + mkAP' = building: floor: ap-no: { + "ap-v01-${builtins.toString building}-${builtins.toString floor}-${builtins.toString ap-no}" = { + site = "unknown"; + adminGroups = [ "fai" ]; - hashedPassword = "$y$j9T$DMOQEWOYFHjNS0myrXp4x/$MG33VSdXGvib.99eN.AbvyVdNNJw4ERjAwK4.ULJe/A"; + hashedPassword = "$y$j9T$DMOQEWOYFHjNS0myrXp4x/$MG33VSdXGvib.99eN.AbvyVdNNJw4ERjAwK4.ULJe/A"; - stateVersion = null; + stateVersion = null; - nixpkgs = { - system = "zyxel-nwa50ax"; - version = "24.05"; + nixpkgs = { + system = "zyxel-nwa50ax"; + version = "24.05"; + }; + + extraNodeSettings = { + inherit building floor ap-no; + vendor-mac = null; + }; }; }; -} + + /* + Generate all APs nodes for a building. It takes a building definition and returns + an attributes set containing all nodes of the AP for the building. + + Building definition: + * building-no is the index of the building + * floors is the range {from, to{ of floors that contains an AP + * ap-nos is the range [from, to{ of AP numbers + + Type: AttrSet -> AttrSet + */ + mkAPs-building = + { + building-no, + floors, + ap-nos, + }: + mapFuse (f: mapFuse (mkAP' building-no f) (mkRange ap-nos)) (mkRange floors); + + APs = { + hypnos-1 = { + building-no = 0; + floors = { + from = 0; + to = 3; + }; + ap-nos = { + from = 1; + to = 7; + }; + }; + + hypnos-2 = { + building-no = 1; + floors = { + from = 0; + to = 3; + }; + ap-nos = { + from = 1; + to = 3; + }; + }; + }; + + mkRange = { from, to }: builtins.genList (x: x + from) (to - from); +in +{ } +// builtins.foldl' (nodes: building: nodes // mkAPs-building building) { } (builtins.attrValues APs) diff --git a/meta/options.nix b/meta/options.nix index 539be1d..5b815d3 100644 --- a/meta/options.nix +++ b/meta/options.nix @@ -210,6 +210,12 @@ in default = null; description = "VM cluster where the VM is located"; }; + + extraNodeSettings = mkOption { + type = attrs; + default = { }; + description = "Freeform attribute set to customize the node"; + }; }; config = { diff --git a/npins/sources.json b/npins/sources.json index e19a154..80f920b 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -153,10 +153,10 @@ "type": "Git", "url": "https://git.dgnum.eu/DGNum/liminix" }, - "branch": "main", - "revision": "1322de1ee0cdb19fead79e12ab279ee0b575019a", + "branch": "mdebray/usteer", + "revision": "420b1764c5b5b91c3ddada6dec9bc594847b3cc5", "url": null, - "hash": "07nk6nik97k8a57cf17dcj3gn2lbhw1myymrxpqc2aqa3haj754k" + "hash": "168qhll6qw5x9v9300mizpshhgmsvvwzp6786z7l1s3jqk3ygf6d" }, "linkal": { "type": "Git", diff --git a/scripts/export_secret.sh b/scripts/export_secret.sh new file mode 100755 index 0000000..e14ce9c --- /dev/null +++ b/scripts/export_secret.sh @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2024 Maurice Debray +# +# SPDX-License-Identifier: EUPL-1.2 + +DECRYPT=() +if [ -f "$HOME/.ssh/id_rsa" ]; then + DECRYPT+=(-i "$HOME/.ssh/id_rsa") +fi +if [ -f "$HOME/.ssh/id_ed25519" ]; then + DECRYPT+=(-i "$HOME/.ssh/id_ed25519") +fi + +export RADIUS_SECRET=$(rage "${DECRYPT[@]}" -d ./machines/nixos/vault01/secrets/radius-ap-radius-secret_file) 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..3613dc1 --- /dev/null +++ b/scripts/extract-firmware-from-zyxel-nwa-fit.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p ubootTools + +# SPDX-FileCopyrightText: 2024 Ryan Lahfa +# +# SPDX-License-Identifier: EUPL-1.2 + +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/ftp_zeroday.sh b/scripts/ftp_zeroday.sh new file mode 100755 index 0000000..102f6cb --- /dev/null +++ b/scripts/ftp_zeroday.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env nix-shell +#!nix-shell -p inetutils -i bash +# SPDX-FileCopyrightText: 2024 Maurice Debray +# +# SPDX-License-Identifier: EUPL-1.2 +# +# Tribute to RaitoBezarius for finding the exploit + +set -euxo pipefail + +usage() { + echo "./ftp_zeroday.sh [FIT-IMAGE] [IP]" +} +if [ "$#" -ne 2 ]; then + usage + exit 1 +fi +if ! [ -e "$1" ]; then + echo "$1 not found" >&2 + exit 2 +fi + +IMAGE="$1" +IP="$2" + +echo "Trying to flash $IMAGE to $IP..." + +# TODO: make it exit cleanly +ftp -niv < +# +# SPDX-License-Identifier: MIT + +# 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