WIP: ap01-prepare-poc #280

Draft
mdebray wants to merge 19 commits from ap01-prepare-poc into main
24 changed files with 422 additions and 201 deletions

View file

@ -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" { };
})

View file

@ -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}
];

View file

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

View file

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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 ];
};
}

View file

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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}";
};
};
}

View file

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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";
};
};
}

View file

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

View file

@ -0,0 +1,68 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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;
};
})
];
}

View file

@ -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
];
};
};
}

View file

@ -1,22 +0,0 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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 ];
};
}

View file

@ -1,34 +0,0 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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";
};
};
}

View file

@ -1,43 +0,0 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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;
# };
}

View file

@ -1,49 +0,0 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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;
};
})
];
}

View 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}x£ksyÖ\£.i§<69>קF¢¯}ê-ÍÁÓšLbì;{
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
萠%7図殤チx盟~YヨワZチ{儖情リM<EFBE98>Hハ<48>ソ Xセ<58>ナk@モ9<EFBE93>

View file

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

View file

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

View file

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

13
scripts/export_secret.sh Executable file
View file

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2024 Maurice Debray <maurice.debray@dgnum.eu>
#
# 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)

View file

@ -0,0 +1,16 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p ubootTools
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# 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_image_path> <firmware_output_file>"
}
ZYXEL_NWA_FIT="$1"
FIRMWARE_OUTPUT="$2"
dumpimage -T flat_dt -p 0 $ZYXEL_NWA_FIT -o $FIRMWARE_OUTPUT

35
scripts/ftp_zeroday.sh Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env nix-shell
#!nix-shell -p inetutils -i bash
# SPDX-FileCopyrightText: 2024 Maurice Debray <maurice.debray@dgnum.eu>
#
# 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 <<EOF
open $IP
user admin 1234
binary
put $IMAGE
EOF

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

@ -0,0 +1,58 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2024 Daniel Barlow <dan@telent.net>
#
# 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