Compare commits

...

29 commits

Author SHA1 Message Date
d1de030fdf feat: putain
Some checks failed
Run pre-commit on all files / check (push) Successful in 23s
Check meta / check_dns (pull_request) Failing after 19s
Check meta / check_meta (pull_request) Failing after 19s
Build all the nodes / bridge01 (pull_request) Failing after 21s
Build all the nodes / ap01 (pull_request) Failing after 22s
Build all the nodes / compute01 (pull_request) Failing after 21s
Check workflows / check_workflows (pull_request) Successful in 24s
Build all the nodes / geo02 (pull_request) Failing after 20s
Build all the nodes / geo01 (pull_request) Failing after 21s
Build all the nodes / vault01 (pull_request) Failing after 21s
Build all the nodes / rescue01 (pull_request) Failing after 21s
Build all the nodes / storage01 (pull_request) Failing after 21s
Build all the nodes / web01 (pull_request) Failing after 22s
Build all the nodes / web02 (pull_request) Failing after 20s
Build all the nodes / web03 (pull_request) Failing after 20s
Run pre-commit on all files / check (pull_request) Successful in 24s
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:17:03 +01:00
4b6ba491a5 feat(scripts/liminix): fail HARD and support root prefixes
Now that we possess the levitation technology, it's necessary to support
root prefixes for rebuild otherwise we will just overwrite our RAM disk
configuration.

Also, to avoid any issue, let's fail hard and avoid rebooting while
being in levitation for nothing.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
f952352297 feat(ap01): use levitation for mass rebuilds
To deal with mass rebuild disk space requirements, we can just levitate.

In the future, we will use levitation as a general-purpose mechanism for
partition layout migration, etc.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
6917a9dc40 feat(ap01): write nixpkgs version in /etc
It's useful for debugging.
We should also track our own revisions at some point.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
598a45843f feat(ap01): re-enable 2GHz
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
180da3a14a fix(hive): misc fixes regarding the nixpkgs usage
Previously, we were using <nixpkgs> actually, we fixed liminix and this
file to ensure that we are instantiating the right nixpkgs.

This helps to deal with unexpected mass rebuilds.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
9b3a6c716b chore: upgrade liminix
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
37ec30194d fix(ap01): bridge only once the WLAN is in operational state
It required a lot of technology, but hey, we now have it.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
48c05701ce feat(scripts): add Android profile building via Nix
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
301fbcc26d fix(ap01): fix _one_ race condition when bridging the LAN
The remaining is hostapd notification that the WLAN interface is ready
to bridge.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
dd25537352 chore(ap01): cleanup
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
98ef1d0a50 feat(ap01): add ubus
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
cbc2f04737 feat(ap01): disable IPv6
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
84ed2028cd feat(ap01): external RADIUS + DHCPv4 + jitterRNGd + stable bridge + default route
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
d77fda073d fix(sources): update to the latest version of Liminix
With bridge fixes and many other things.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
32f4c33b76 feat(liminix-rebuild): support hive names directly
Thanks @catvayor, I'm so dumb.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:58 +01:00
6d5ed92b2c feat(devshell): add kanidm & freeradius
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:11:49 +01:00
a82b3e25d9 chore(ap01): cleanup
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:10:50 +01:00
7cebe68c7c feat(scripts): add liminix-oriented scripts
- extraction for manual recovery via nandwrite
- liminix rebuild to interactively develop the AP

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:10:50 +01:00
c678bbb8df feat(ap01): enable fully RADIUS via internal RADIUS server
This adds two public keys.

For the private keys, heh…

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:10:50 +01:00
9d17167495 feat(devshell): add liminix-related tools
serial console, deployment tools and TFTP tool

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:06:04 +01:00
f9a6330d6a feat: add access control for APs via our custom modules
It works!

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:03:09 +01:00
3e42ea7098 feat: ensure that colmena repl can evaluate nodes.ap01
Bump to latest Liminix and use the new `defaults` function.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:00:20 +01:00
f553fe9e05 feat: achieve custom evaluation in the framework
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 12:00:19 +01:00
ae130a47dc feat: Specialize nixpkgs as a function of the system 2024-12-07 11:58:01 +01:00
0f1cee70e1 feat: wip! wip! wip! 2024-12-07 11:54:58 +01:00
5c03bd91fb feat: enable liminix-rebuild
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 11:54:32 +01:00
336bbcbcfc feat: add liminix-rebuild in the shell
Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 11:54:31 +01:00
8a65248d66 feat: init Liminix evaluation system
Very rudimentary; undocumented, untested in production. This is for testing purposes.

Signed-off-by: Ryan Lahfa <ryan@dgnum.eu>
2024-12-07 11:50:45 +01:00
19 changed files with 690 additions and 69 deletions

View file

@ -1,4 +1,15 @@
jobs:
ap01:
runs-on: nix
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: ap01
STORE_ENDPOINT: https://tvix-store.dgnum.eu/infra-signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Build and cache ap01
run: nix-shell -A eval-nodes --run cache-node
bridge01:
runs-on: nix
steps:

View file

@ -112,6 +112,20 @@ in
}))
pkgs.npins
pkgs.kanidm # for remote SSO operations
pkgs.freeradius # for radtest
pkgs.picocom
(pkgs.callPackage (sources.liminix + "/pkgs/min-copy-closure") {
nix = pkgs.lix;
})
(pkgs.callPackage (sources.liminix + "/pkgs/min-collect-garbage") {
nix = pkgs.lix;
})
(pkgs.callPackage (sources.liminix + "/pkgs/tufted")
{
}
)
(pkgs.callPackage ./lib/colmena { inherit (nix-pkgs) colmena; })
(pkgs.callPackage "${sources.agenix}/pkgs/agenix.nix" { })
(pkgs.callPackage "${sources.lon}/nix/packages/lon.nix" { })

192
hive.nix
View file

@ -4,6 +4,8 @@ let
# Patch sources directly
sources = builtins.mapAttrs (patch.base { pkgs = import sources'.nixos-unstable { }; })
.applyPatches' sources';
lib = import (sources.nix-lib + "/src/trivial.nix");
lib' = (import sources.nixos-unstable { }).lib;
nix-lib = import ./lib/nix-lib;
@ -14,16 +16,29 @@ let
mkNode = node: {
# Import the base configuration for each node
imports = [ ./machines/${node}/_configuration.nix ];
imports = builtins.map (lib.mkRel (./machines/${node})) [
"_configuration.nix"
"_hardware-configuration.nix"
];
deployment.systemType = systemType node;
};
nixpkgs' = import ./meta/nixpkgs.nix;
# All supported nixpkgs versions, instanciated
nixpkgs = nix-lib.mapSingleFuse mkNixpkgs nixpkgs'.supported;
nixpkgs = nix-lib.mapSingleFuse (
s: nix-lib.mapSingleFuse (mkSystemNixpkgs s) nixpkgs'.versions.supported
) nixpkgs'.systems.supported;
# Get the configured nixos version for the node,
# defaulting to the one defined in meta/nixpkgs
version = node: nodes'.${node}.nixpkgs or nixpkgs'.default;
version = node: nodes'.${node}.nixpkgs or nixpkgs'.versions.default;
system = node: nodes'.${node}.system or nixpkgs'.systems.default;
systemType =
node:
nodes'.${node}.system
or (lib'.warn "Not specifying the `deployment.systemType` is deprecated!" "nixos");
# Builds a patched version of nixpkgs, only as the source
mkNixpkgs' =
@ -33,26 +48,129 @@ let
name = "nixos-${v}";
};
# Instanciates the required nixpkgs version
mkNixpkgs = version: import (mkNixpkgs' version) { };
mkNixpkgsConfigPerSystem =
system: _:
if system == "nixos" then
{ }
else
(import "${sources.liminix}/devices/${system}").system
// {
overlays = [ (import "${sources.liminix}/overlay.nix") ];
config = {
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.8" # Python < 3 is needed for kernel backports.
];
};
};
# Instanciate a specialized version of nixpkgs
mkSystemNixpkgs =
system: version:
let
args = mkNixpkgsConfigPerSystem system version;
in
import (mkNixpkgs' version) args;
###
# Function to create arguments based on the node
#
mkArgs = node: rec {
lib = nixpkgs.${version node}.lib // {
extra = nix-lib;
mkArgs =
node:
let
pkgs = nixpkgs.${system node};
in
rec {
lib =
import sources.nix-lib {
inherit (pkgs.${version node}) lib;
nixpkgs = pkgs;
keysRoot = ./keys;
}
// {
extra = nix-lib;
};
meta = (import ./meta) lib;
nodeMeta = meta.nodes.${node};
};
meta = (import ./meta) lib;
nodeMeta = meta.nodes.${node};
};
in
{
registry = {
zyxel-nwa50ax = {
evalConfig = import "${sources.liminix}/lib/eval-config.nix" {
nixpkgs = sources.nixos-unstable;
};
defaults = _: {
nixpkgs = {
source = sources.nixos-unstable;
config = {
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.8" # Python < 3 is needed for kernel backports.
];
};
hostPlatform = {
config = "mipsel-unknown-linux-musl";
gcc = {
abi = "32";
arch = "mips32"; # mips32r2?
};
};
# It's impure, but who cares?
# Can Flakes do that?
buildPlatform = builtins.currentSystem;
};
};
};
nixos = {
evalConfig = import "${sources.nixos-unstable}/nixos/lib/eval-config.nix";
defaults =
{ nodeMeta, name, ... }:
{
# Import the default modules
imports = [ ./modules ];
# Include default secrets
age-secrets.sources = [ ./machines/${name}/secrets ];
# Deployment config is specified in meta.nodes.${node}.deployment
inherit (nodeMeta) deployment;
nix = {
# Set NIX_PATH to the patched version of nixpkgs
nixPath = [ "nixpkgs=${mkNixpkgs' (version name)}" ];
optimise.automatic = true;
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
};
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
# Use the stateVersion declared in the metadata
system = {
inherit (nodeMeta) stateVersion;
};
};
};
};
meta = {
nodeNixpkgs = nix-lib.mapSingleFuse (n: nixpkgs.${version n}) nodes;
nodeNixpkgs = nix-lib.mapSingleFuse (n: nixpkgs.${version n}) nodes // {
ap01 = apNixpkgs;
};
specialArgs = {
inherit nixpkgs sources;
@ -63,40 +181,24 @@ in
nodeSpecialArgs = nix-lib.mapSingleFuse mkArgs nodes;
};
defaults =
{ name, nodeMeta, ... }:
ap01 =
let
device = import "${sources.liminix}/devices/zyxel-nwa50ax";
in
{
# Import the default modules
imports = [
./modules
(import "${sources.lix-module}/module.nix" { inherit (sources) lix; })
];
# Include default secrets
age-secrets.sources = [ ./machines/${name}/secrets ];
# Deployment config is specified in meta.nodes.${node}.deployment
inherit (nodeMeta) deployment;
nix = {
# Set NIX_PATH to the patched version of nixpkgs
nixPath = [ "nixpkgs=${mkNixpkgs' (version name)}" ];
optimise.automatic = true;
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
deployment.systemType = "liminix";
nixpkgs.hostPlatform = {
config = "mipsel-unknown-linux-musl";
gcc = {
abi = "32";
arch = "mips32"; # mips32r2?
};
};
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
# Use the stateVersion declared in the metadata
system = {
inherit (nodeMeta) stateVersion;
};
nixpkgs.buildPlatform = "x86_64-linux";
imports = [
./machines/ap/configuration.nix
device.module
];
};
}
// (nix-lib.mapSingleFuse mkNode nodes)

View file

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC6TCCAdECFEbjeqNNzKWyfs2GJekipWK+yO4uMA0GCSqGSIb3DQEBCwUAMHMx
NDAyBgNVBAMMK0RHTnVtIFRlc3QgQVAgQ0EgLS0gRE8gTk9UIFVTRSBPUiBHRVQg
RklSRUQxCzAJBgNVBAYTAkZSMQ4wDAYDVQQIDAVQYXJpczEOMAwGA1UEBwwFUGFy
aXMxDjAMBgNVBAoMBURHTnVtMB4XDTI0MDgyNjE5MDQxMFoXDTI2MDgyNjE5MDQx
MFowdzE4MDYGA1UEAwwvREdOdW0gVGVzdCBBUCBzZXJ2ZXIgLS0gRE8gTk9UIFVT
RSBPUiBHRVQgRklSRUQxCzAJBgNVBAYTAkZSMQ4wDAYDVQQIDAVQYXJpczEOMAwG
A1UEBwwFUGFyaXMxDjAMBgNVBAoMBURHTnVtMIGbMBAGByqGSM49AgEGBSuBBAAj
A4GGAAQBDrTf0SH/YOkOfvOSnB3BbICb80jSsxwQH50y4jylbXcrUZnegLYjW/lF
QknuMBzzE5fnE9lAeOxqsn0ec+sL3zEBrV0LSG2LgxhAkahZS9U4Spt9Qc84U7cG
AFQ3GXDMTEb/COHJSu7sIfV4gFRVesFez30gb94lMxckkq/6nkXXaEUwDQYJKoZI
hvcNAQELBQADggEBAEfPHMAXwftYQ0lDYPlr9b+GZDl7/JAavEfBXKzj1U8O0sJz
daNOHEX3a5ZOaQoean2zmBLROgQpDlwsjAFNA9dg0ef2f4RgJvr/l2fspHwG0Uaq
4JEOKTj3htd8aZX2i6AR02UC2oxCtf7ZVa+a6NOeeKl53QPzjduPO60ruz8tD2Xr
YnQwVinQX0fJo7TmyQKDIxwld/Q5pMoDMfVlS71M/vISFfQ/Rx1PqYvBQyG1dvIA
qn9cNNVnjEGrk7zXjCfehMYiCtDZ+D3VyXeZ6A7YZNpc6RUj8rbWcOtKLayRFlwf
DTjV3/nPqV0M2nU6jXFBMfQ47VSfB7ibINt94xo=
-----END CERTIFICATE-----

View file

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDxzCCAq+gAwIBAgIUPuHEZeZoCidp+w5ME2CrkZEn3T8wDQYJKoZIhvcNAQEL
BQAwczE0MDIGA1UEAwwrREdOdW0gVGVzdCBBUCBDQSAtLSBETyBOT1QgVVNFIE9S
IEdFVCBGSVJFRDELMAkGA1UEBhMCRlIxDjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQH
DAVQYXJpczEOMAwGA1UECgwFREdOdW0wHhcNMjQwODI2MTg1ODQ4WhcNMjkwODI2
MTg1ODQ4WjBzMTQwMgYDVQQDDCtER051bSBUZXN0IEFQIENBIC0tIERPIE5PVCBV
U0UgT1IgR0VUIEZJUkVEMQswCQYDVQQGEwJGUjEOMAwGA1UECAwFUGFyaXMxDjAM
BgNVBAcMBVBhcmlzMQ4wDAYDVQQKDAVER051bTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKLgCNXLI6FQWJY5JQDqZcO1hmZpp0upT59/JvJXmEl4St1O
FF3frSAoFcgn2Bv3kYQQ6wEhD3S7JBxRmoDtx/7sqsXthNpBaymVdphb9XhnVOC2
NDBKV4WH06Hr06oKVfDSBhIldPJr1vfQLehOnz6uqK7walqPvid3tMv0lwt7mHZ9
qQpgC2C/tkHwD1kh1RszoIZKIQWDnSNXPhYnB3X/DMCUWIKiz6P/0rVANEDDZER6
b6eJRjv2l8jPlOt7CUTAOrsoJGCnSg2SV4lgr1u3mE/2AvmLdO0l5Dz0qCuQNbb3
uWqYUonooR8rox171On/Rd0zvtihycSDxofVJ+MCAwEAAaNTMFEwHQYDVR0OBBYE
FIPUT3v8AoeBS6VbcEvgVc1dC38hMB8GA1UdIwQYMBaAFIPUT3v8AoeBS6VbcEvg
Vc1dC38hMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACX1aqYU
9PIwZ/dBS7cpsBsCm9M0ueInTlQpvv6xioKuPhIet40YgawTRakxniAr0WXHTBV5
a8ZQ4ff4uI+sdaxN7Ueufr4ltWVLuSc9DfIxjLVZ+41G6Ehy9Xc2zoDBfYURrXjd
ISvPSXIKjM0yuS/249C77HOdzwbliS65Io2zubQStGfSaZ3sLAfPJoig+QiVyOtG
sPoYzzrjXDBym+plfGTWqHv+gwo6DZarXrK4yaMn4hYkkf95NsY2ywwHzcy/4hsu
+bMm4IeCrB9uNOZtQrqW81/+4oxjGiKLbhnFPNQOg2pzb+iOJTPKVicAqKDSCnou
WXG5pjBKzojPvxU=
-----END CERTIFICATE-----

1
liminix-rebuild.nix Normal file
View file

@ -0,0 +1 @@
{ liminix-system }: (import ./liminix-hive.nix { }).${liminix-system}.primary

View file

@ -0,0 +1,259 @@
{
config,
pkgs,
modulesPath,
...
}:
let
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs.pseudofile) symlink dir;
inherit (pkgs) serviceFns;
svc = config.system.service;
secrets-1 = {
ssid = "DGNum 2G prototype (N)";
};
secrets-2 = {
ssid = "DGNum 5G prototype (AX)";
};
baseParams = {
country_code = "FR";
hw_mode = "g";
channel = 6;
wmm_enabled = 1;
ieee80211n = 1;
ht_capab = "[LDPC][GF][HT40-][HT40+][SHORT-GI-40][MAX-AMSDU-7935][TX-STBC]";
auth_algs = 1;
wpa = 2;
wpa_pairwise = "TKIP CCMP";
rsn_pairwise = "CCMP";
};
radiusKeyMgmt = {
wpa_key_mgmt = "WPA-EAP";
};
modernParams = {
hw_mode = "a";
he_su_beamformer = 1;
he_su_beamformee = 1;
he_mu_beamformer = 1;
preamble = 1;
# Allow radar detection.
ieee80211d = 1;
ieee80211h = 1;
ieee80211ac = 1;
ieee80211ax = 1;
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;
vht_oper_centr_freq_seg0_idx = 42;
he_oper_centr_freq_seg0_idx = 42;
require_vht = 1;
};
clientRadius = {
ieee8021x = 1;
eapol_version = 2;
use_pae_group_addr = 1;
dynamic_vlan = 0;
vlan_tagged_interface = "lan";
};
externalRadius = {
# TODO: when we have proper IPAM, set the right value here.
own_ip_addr = "127.0.0.1";
nas_identifier = "ap01.dgnum.eu";
# 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";
};
mkWifiSta =
params: interface: secrets:
svc.hostapd.build {
inherit interface;
package = pkgs.hostapd-radius;
params = params // secrets;
dependencies = [ config.services.jitter ];
};
in
rec {
imports = [
"${modulesPath}/wlan.nix"
"${modulesPath}/network"
"${modulesPath}/dhcp6c"
"${modulesPath}/hostapd"
"${modulesPath}/ssh"
"${modulesPath}/ntp"
"${modulesPath}/vlan"
"${modulesPath}/bridge"
"${modulesPath}/jitter-rng"
"${modulesPath}/pki"
"${modulesPath}/ubus"
../../modules/dgn-access-control.nix
# TODO: god that's so a fucking hack.
(import "${modulesPath}/../devices/zyxel-nwa50ax").module
];
hostname = "ap01-prototype";
# Get moar random please
services.jitter = svc.jitter-rng.build { };
services.ubus = svc.ubus.build { };
# SSH keys are handled by the access control module.
dgn-access-control.enable = true;
users.root = {
passwd = "$6$jVXFFOp8HBYmgINR$lutB4kvw.W1jlXRby9ZYAgBitQ32RxQdYAGN.s2x4ris8J07vM6tzlRBQoeLELOIEMClDzbciQV0itfHQnTqd1";
};
services.int = svc.bridge.primary.build {
ifname = "int";
macAddressFromInterface = config.hardware.networkInterfaces.lan;
};
services.bridge = svc.bridge.members.build {
primary = 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.resolvconf = oneshot rec {
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${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";
"nixpkgs.version" = {
type = "f";
file = "${pkgs.lib.version}";
mode = "0444";
};
};
};
services.dhcpv4 = svc.network.dhcp.client.build {
interface = config.services.int;
dependencies = [
config.services.hostname
config.services.bridge.components.lan
];
};
# TODO(raito): these won't work with RAs
# fix them in Liminix directly and re-enable.
# services.dhcpv6 = svc.dhcp6c.client.build {
# interface = config.services.int;
# dependencies = [
# config.services.hostname
# config.services.bridge
# ];
# };
# services.ipv6 = svc.dhcp6c.address.build {
# interface = config.services.int;
# client = config.services.dhcpv6;
# dependencies = [ config.services.hostname ];
# };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} router)";
target = "default";
dependencies = [ services.dhcpv4 ];
};
services.packet_forwarding = svc.network.forward.build { };
services.sshd = svc.ssh.build { allowRoot = true; };
services.ntp = config.system.service.ntp.build {
pools = {
"pool.ntp.org" = [ "iburst" ];
};
dependencies = [ config.services.jitter ];
};
boot.tftp = {
serverip = "192.0.2.10";
ipaddr = "192.0.2.12";
};
# wlan0 is the 2.4GHz interface.
services.hostap-1 = mkWifiSta (
baseParams // radiusKeyMgmt
) config.hardware.networkInterfaces.wlan0 secrets-1;
services.hostap-1-ready = svc.hostapd-ready.build {
interface = config.hardware.networkInterfaces.wlan0;
};
# wlan1 is the 5GHz interface, e.g. AX capable.
services.hostap-2 = mkWifiSta (
baseParams // clientRadius // externalRadius // radiusKeyMgmt // modernParams
) config.hardware.networkInterfaces.wlan1 secrets-2;
# Oneshot that waits until the hostapd has set the interface in operational state.
services.hostap-2-ready = svc.hostapd-ready.build {
interface = config.hardware.networkInterfaces.wlan1;
};
defaultProfile.packages = with pkgs; [
zyxel-bootconfig
min-collect-garbage
iwinfo
ifwait
# 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"
];
services = {
# 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 ${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

@ -0,0 +1 @@
{ }

View file

@ -1,11 +1,24 @@
{
# Default version of nixpkgs to use
default = "24.05";
versions = {
# Default version of nixpkgs to use
default = "24.05";
# Supported nixpkgs versions
supported = [
"unstable"
"23.11"
"24.05"
];
# Supported nixpkgs versions
supported = [
"unstable"
"23.11"
"24.05"
];
};
systems = {
# Default target system
default = "nixos";
# Supported target systems
supported = [
"nixos"
"zyxel-nwa50ax"
];
};
}

View file

@ -136,4 +136,12 @@
nixpkgs = "unstable";
vm-cluster = "Hyperviseur NPS";
};
# Access points definition
ap01 = {
site = "unknown";
adminGroups = [ "fai" ];
system = "zyxel-nwa50ax";
};
}

View file

@ -11,6 +11,7 @@ let
inherit (lib.types)
attrs
attrsOf
enum
ints
listOf
nullOr
@ -35,6 +36,7 @@ let
};
org = config.organization;
nixpkgs = import ./nixpkgs.nix;
in
{
@ -138,8 +140,8 @@ in
};
nixpkgs = mkOption {
type = str;
inherit (import ./nixpkgs.nix) default;
type = enum nixpkgs.versions.supported;
inherit (nixpkgs.versions) default;
description = ''
Version of nixpkgs to use.
'';
@ -188,6 +190,14 @@ in
default = null;
description = "VM cluster where the VM is located";
};
system = mkOption {
type = enum nixpkgs.systems.supported;
inherit (nixpkgs.systems) default;
description = ''
Type of system for the node, will impact how it is evaluated and deployed.
'';
};
};
config = {

View file

@ -114,6 +114,12 @@
"fai"
];
# AP administration DGNum
ap.adminGroups = [
"root"
"fai"
];
# Videos DGNum
peertube.admins = [ "thubrecht" ];
};

View file

@ -84,18 +84,24 @@ in
{
# Admins have root access to the node
dgn-access-control.users.root = mkDefault admins;
users.users = builtins.mapAttrs (_: members: {
openssh.authorizedKeys.keys = dgn-keys.getKeys members;
}) cfg.users;
}
{
(mkIf (nodeMeta.system == "nixos") {
users = {
mutableUsers = false;
users.root = {
inherit (nodeMeta) hashedPassword;
};
};
}
})
(mkIf (nodeMeta.system == "nixos") {
users.users = builtins.mapAttrs (_: members: {
openssh.authorizedKeys.keys = dgn-keys.getKeys members;
}) cfg.users;
})
(mkIf (nodeMeta.system == "zyxel-nwa50ax") {
users = builtins.mapAttrs (_: members: {
openssh.authorizedKeys.keys = dgn-keys.getKeys members;
}) cfg.users;
})
]);
}

View file

@ -42,6 +42,7 @@ let
builtins.fetchGit {
inherit (repository) url;
rev = revision;
allRefs = true;
# hash = hash;
};

View file

@ -49,6 +49,17 @@
"url": null,
"hash": "0b1pmfzckdbha9j7bvnkvqccf62dqyll8ip6mrdm90m0y8kdkzvg"
},
"colmena": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.dgnum.eu/DGNum/colmena.git"
},
"branch": "custom-activation",
"revision": "1037471d800d3db42ec85b89787bec5472f5e574",
"url": null,
"hash": "0k287292yr78zymdglppa8jd0b4kh0x277d4bycwcdsqprw00a4x"
},
"disko": {
"type": "GitRelease",
"repository": {
@ -116,14 +127,13 @@
"liminix": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "RaitoBezarius",
"repo": "liminix"
"type": "Git",
"url": "https://git.dgnum.eu/DGNum/liminix.git"
},
"branch": "nwa50ax",
"revision": "a4aa10dcc30225a8bb8eb465abfe908629175f2c",
"url": "https://github.com/RaitoBezarius/liminix/archive/a4aa10dcc30225a8bb8eb465abfe908629175f2c.tar.gz",
"hash": "1m1sc6agg5z65lmyjl48i7sddlwm8d0zgvs8z81iammfy4jpy7qd"
"branch": "main",
"revision": "6970d811e87a3a99a093705cb089a5a63961cfe7",
"url": null,
"hash": "08fwry3zkhlkcl1lrqhhhqj3a47mgc41bvfv518zg8xhwzg7ifnz"
},
"linkal": {
"type": "Git",
@ -322,4 +332,4 @@
}
},
"version": 3
}
}

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="US-ASCII"?>
<WLANProfile xmlns="https://www.microsoft.com/networking/WLAN/profile/v1">
<name>DGNumRadius</name>
<SSIDConfig>
<SSID>
<name>@SSID@</name>
</SSID>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<MSM>
<security>
<authEncryption>
<authentication>WPA2</authentication>
<encryption>AES</encryption>
<useOneX>true</useOneX>
</authEncryption>
<OneX xmlns="https://www.microsoft.com/networking/OneX/v1">
<EAPConfig>
<EapHostConfig xmlns="https://www.microsoft.com/provisioning/EapHostConfig"
xmlns:eapCommon="https://www.microsoft.com/provisioning/EapCommon"
xmlns:baseEap="https://www.microsoft.com/provisioning/BaseEapMethodConfig">
<EapMethod>
<eapCommon:Type>25</eapCommon:Type>
<eapCommon:AuthorId>0</eapCommon:AuthorId>
</EapMethod>
<Config xmlns:baseEap="https://www.microsoft.com/provisioning/BaseEapConnectionPropertiesV1"
xmlns:msPeap="https://www.microsoft.com/provisioning/MsPeapConnectionPropertiesV1"
xmlns:msChapV2="https://www.microsoft.com/provisioning/MsChapV2ConnectionPropertiesV1"
xmlns:msChapV2User="https://www.microsoft.com/provisioning/MsChapV2UserPropertiesV1">
<baseEap:Eap>
<baseEap:Type>25</baseEap:Type>
<msPeap:EapType>
<msPeap:ServerValidation>
<msPeap:DisableUserPromptForServerValidation>false</msPeap:DisableUserPromptForServerValidation>
<msPeap:TrustedRootCA />
</msPeap:ServerValidation>
<msPeap:FastReconnect>true</msPeap:FastReconnect>
<msPeap:InnerEapOptional>0</msPeap:InnerEapOptional>
<msPeap:ServerNames>radius.dgnum.eu</msPeap:ServerNames>
<baseEap:Eap>
<baseEap:Type>26</baseEap:Type>
<msChapV2:EapType>
<msChapV2:UseWinLogonCredentials>false</msChapV2:UseWinLogonCredentials>
</msChapV2:EapType>
<msChapV2User:EapType>
<msChapV2User:Username>@USERNAME@</msChapV2User:Username>
<msChapV2User:Password>@PASSWORD@</msChapV2User:Password>
</msChapV2User>
</baseEap:Eap>
<msPeap:EnableQuarantineChecks>false</msPeap:EnableQuarantineChecks>
<msPeap:RequireCryptoBinding>false</msPeap:RequireCryptoBinding>
<msPeap:PeapExtensions />
</msPeap:EapType>
</baseEap:Eap>
</Config>
</EapHostConfig>
</EAPConfig>
</OneX>
</security>
</MSM>
</WLANProfile>

View file

@ -0,0 +1,19 @@
{
pkgs ? import <nixpkgs> { },
username,
domain ? "sso.dgnum.eu",
ssid ? "DGNum 5G prototype (AX)",
password,
}:
pkgs.runCommand "connection-${username}-android-profile.xml"
{
SSID = ssid;
USERNAME = "${username}@${domain}";
PASSWORD = password;
}
''
substitute ${./android-profile-template.xml} $out \
--subst-var SSID \
--subst-var USERNAME \
--subst-var PASSWORD
''

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

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

@ -0,0 +1,45 @@
#!/usr/bin/env bash
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
;;
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