feat: add test AP
Signed-off-by: Elias Coppens <elias@dgnum.eu>
This commit is contained in:
parent
b0e648b86f
commit
c4782f15c5
12 changed files with 408 additions and 2 deletions
5
hive.nix
5
hive.nix
|
@ -116,7 +116,12 @@ in
|
|||
# Import the default modules
|
||||
imports = [
|
||||
# Import the base configuration for each node
|
||||
(
|
||||
if name != "ap-test" then
|
||||
./machines/liminix/ap-v01/_configuration.nix
|
||||
else
|
||||
./machines/liminix/ap-test/_configuration.nix
|
||||
)
|
||||
./modules/generic
|
||||
./modules/${category name}
|
||||
];
|
||||
|
|
46
machines/liminix/ap-test/_configuration.nix
Normal file
46
machines/liminix/ap-test/_configuration.nix
Normal file
|
@ -0,0 +1,46 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{
|
||||
modulesPath,
|
||||
sourcePkgs,
|
||||
name,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/wlan.nix"
|
||||
"${modulesPath}/network"
|
||||
"${modulesPath}/hostapd"
|
||||
"${modulesPath}/ssh"
|
||||
"${modulesPath}/ntp"
|
||||
"${modulesPath}/vlan"
|
||||
"${modulesPath}/bridge"
|
||||
"${modulesPath}/jitter-rng"
|
||||
"${modulesPath}/pki"
|
||||
"${modulesPath}/ubus"
|
||||
"${modulesPath}/openwrt-prometheus-exporter"
|
||||
# System-level configuration
|
||||
./system.nix
|
||||
# Configures our own WLAN.
|
||||
./wlan.nix
|
||||
# Configures our LAN interfaces, e.g. bridge + VLANs.
|
||||
./lan.nix
|
||||
# Configures our IPv4/IPv6 addresses, e.g. DHCPv4 on VLAN 0, SLAAC on VLAN 3001.
|
||||
./addresses.nix
|
||||
# Configures a basic local DNS.
|
||||
./dns.nix
|
||||
# Configures our management layer, e.g. SSH server + DGNum FAI keys.
|
||||
./management.nix
|
||||
# Configures our recovery system, e.g. a levitation script.
|
||||
./recovery.nix
|
||||
# Metadata on the system for field recovery.
|
||||
./metadata.nix
|
||||
# TODO: god that's so a fucking hack.
|
||||
(import "${modulesPath}/../devices/zyxel-nwa50ax").module
|
||||
];
|
||||
|
||||
hostname = name;
|
||||
nixpkgs.source = sourcePkgs.path;
|
||||
}
|
24
machines/liminix/ap-test/addresses.nix
Normal file
24
machines/liminix/ap-test/addresses.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{ config, ... }:
|
||||
let
|
||||
svc = config.system.service;
|
||||
# FIXME switch to ipv6 tu be able to scale
|
||||
adminIp = "10.0.253.253";
|
||||
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 ];
|
||||
};
|
||||
}
|
25
machines/liminix/ap-test/dns.nix
Normal file
25
machines/liminix/ap-test/dns.nix
Normal 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}";
|
||||
};
|
||||
};
|
||||
}
|
12
machines/liminix/ap-test/ipc.nix
Normal file
12
machines/liminix/ap-test/ipc.nix
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{ config, ... }:
|
||||
let
|
||||
svc = config.system.service;
|
||||
in
|
||||
{
|
||||
# ubus socket for various needs.
|
||||
services.ubus = svc.ubus.build { };
|
||||
}
|
20
machines/liminix/ap-test/lan.nix
Normal file
20
machines/liminix/ap-test/lan.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
}
|
20
machines/liminix/ap-test/management.nix
Normal file
20
machines/liminix/ap-test/management.nix
Normal 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
|
||||
{
|
||||
# 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; };
|
||||
services.openwrt-prometheus-exporter = svc.openwrt-prometheus-exporter.build {
|
||||
httpPorts = [ 9100 ];
|
||||
};
|
||||
}
|
19
machines/liminix/ap-test/metadata.nix
Normal file
19
machines/liminix/ap-test/metadata.nix
Normal file
|
@ -0,0 +1,19 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir;
|
||||
in
|
||||
{
|
||||
filesystem = dir {
|
||||
etc = dir {
|
||||
"nixpkgs.version" = {
|
||||
type = "f";
|
||||
file = "${pkgs.lib.version}";
|
||||
mode = "0444";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
68
machines/liminix/ap-test/recovery.nix
Normal file
68
machines/liminix/ap-test/recovery.nix
Normal 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;
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
33
machines/liminix/ap-test/system.nix
Normal file
33
machines/liminix/ap-test/system.nix
Normal file
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{ pkgs, config, ... }:
|
||||
let
|
||||
svc = config.system.service;
|
||||
in
|
||||
{
|
||||
# Get moar random please
|
||||
services = {
|
||||
jitter = svc.jitter-rng.build { };
|
||||
packet_forwarding = svc.network.forward.build { };
|
||||
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";
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
zyxel-bootconfig
|
||||
min-collect-garbage
|
||||
hostapd-radius
|
||||
];
|
||||
}
|
120
machines/liminix/ap-test/wlan.nix
Normal file
120
machines/liminix/ap-test/wlan.nix
Normal file
|
@ -0,0 +1,120 @@
|
|||
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
svc = config.system.service;
|
||||
|
||||
mac-1 = "02:5B:6A:FF:FF:FE";
|
||||
mac-2 = "02:5B:6A:FF:FF:FF";
|
||||
|
||||
channel-1 = 1;
|
||||
channel-2 = 36;
|
||||
|
||||
secrets-1 = {
|
||||
ssid = "DGNum";
|
||||
};
|
||||
secrets-2 = {
|
||||
ssid = "DGNum 5G";
|
||||
};
|
||||
baseParams = {
|
||||
country_code = "FR";
|
||||
hw_mode = "g";
|
||||
channel = channel-1;
|
||||
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 = channel-2; # TODO understand interferences
|
||||
vht_oper_centr_freq_seg0_idx = channel-2 + 6;
|
||||
he_oper_centr_freq_seg0_idx = channel-2 + 6;
|
||||
require_vht = 1;
|
||||
};
|
||||
|
||||
clientRadius = {
|
||||
ieee8021x = 1;
|
||||
eapol_version = 2;
|
||||
use_pae_group_addr = 1;
|
||||
dynamic_vlan = 3;
|
||||
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 =
|
||||
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 =
|
||||
params: interface: secrets:
|
||||
svc.hostapd.build {
|
||||
inherit interface;
|
||||
package = pkgs.hostapd-radius;
|
||||
params = params // secrets;
|
||||
dependencies = [ config.services.jitter ];
|
||||
};
|
||||
in
|
||||
{
|
||||
hardware.wlanMacAddresses = {
|
||||
wlan0 = mac-1;
|
||||
wlan1 = mac-2;
|
||||
};
|
||||
services = {
|
||||
# wlan0 is the 2.4GHz interface.
|
||||
hostap-1 = mkWifiSta (
|
||||
baseParams // clientRadius // externalRadius // radiusKeyMgmt
|
||||
) config.hardware.networkInterfaces.wlan0 secrets-1;
|
||||
hostap-1-ready = svc.hostapd-ready.build {
|
||||
interface = config.hardware.networkInterfaces.wlan0;
|
||||
};
|
||||
# wlan1 is the 5GHz interface, e.g. AX capable.
|
||||
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.
|
||||
hostap-2-ready = svc.hostapd-ready.build {
|
||||
interface = config.hardware.networkInterfaces.wlan1;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -128,5 +128,19 @@ let
|
|||
|
||||
mkRange = { from, to }: builtins.genList (x: x + from) (to - from);
|
||||
in
|
||||
{ }
|
||||
{
|
||||
ap-test = {
|
||||
site = "unknown";
|
||||
adminGroups = [ "fai" ];
|
||||
|
||||
hashedPassword = "$y$j9T$DMOQEWOYFHjNS0myrXp4x/$MG33VSdXGvib.99eN.AbvyVdNNJw4ERjAwK4.ULJe/A";
|
||||
|
||||
stateVersion = null;
|
||||
|
||||
nixpkgs = {
|
||||
system = "zyxel-nwa50ax";
|
||||
version = "24.05";
|
||||
};
|
||||
};
|
||||
}
|
||||
// builtins.foldl' (nodes: building: nodes // mkAPs-building building) { } (builtins.attrValues APs)
|
||||
|
|
Loading…
Add table
Reference in a new issue