feat(ap): init ap-dev
All checks were successful
Run pre-commit on all files / pre-commit (push) Successful in 22s

This commit is contained in:
sinavir 2025-02-06 10:32:24 +01:00
parent e8d446d2b0
commit 3b7662e45a
No known key found for this signature in database
12 changed files with 399 additions and 3 deletions

View file

@ -34,7 +34,6 @@ let
system = node: nodes'.${node}.nixpkgs.system;
category = node: nixpkgs'.categories.${system node};
nodePkgs = node: nixpkgs.${system node}.${version node};
# Builds a patched version of nixpkgs, only as the source
@ -112,7 +111,7 @@ in
args;
defaults =
{ name, nodeMeta, nodePath, ... }:
{ name, nodePath, ... }:
{
# Import the default modules
imports = [

View 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}/usteer"
"${modulesPath}/ssh"
"${modulesPath}/ntp"
"${modulesPath}/vlan"
"${modulesPath}/bridge"
"${modulesPath}/jitter-rng"
"${modulesPath}/pki"
"${modulesPath}/ubus"
# System-level configuration
./system.nix
# Configures our own WLAN.
./wlan.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
# ubus
./ipc.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;
}

View file

@ -0,0 +1,27 @@
# 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) ap-no;
adminIp = "192.168.1.${builtins.toString (ap-no + 10)}";
in
{
# Our bridging is a bit complicated, therefore, we need iproute2.
programs.iproute2.enable = true;
services.admin-ip = svc.network.address.build {
interface = config.hardware.networkInterfaces.lan;
address = adminIp;
prefixLength = 24;
family = "inet";
};
services.admin-defaultroute4 = svc.network.route.build {
via = "192.168.1.254";
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,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 { };
}

View file

@ -0,0 +1,17 @@
# 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; };
}

View 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";
};
};
};
}

View file

@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
config,
pkgs,
modulesPath,
...
}:
let
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"
(_: {
# FIXME: DHCP has a hidden deps on this, shoud be done in a more intelligent way upstream
programs.iproute2.enable = true;
services = {
inherit (parentConfig.services)
sshd
admin-ip
admin-defaultroute4
;
};
})
];
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

@ -0,0 +1,34 @@
# 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
iw
min-collect-garbage
ubus
];
}

View file

@ -0,0 +1,133 @@
# SPDX-FileCopyrightText: 2024 Ryan Lahfa <ryan.lahfa@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
config,
pkgs,
lib,
nodeMeta,
...
}:
let
svc = config.system.service;
inherit (nodeMeta.extraNodeSettings) ap-no;
hex = x: lib.fixedWidthString 2 "0" (lib.toHexString x);
mac-1 = "02:5B:6B:00:00:${hex ap-no}";
mac-2 = "02:5B:6B:01:00:${hex ap-no}";
secrets-1 = {
ssid = "DGNum";
};
secrets-2 = {
ssid = "DGNum";
};
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";
bss_transition = 1;
rrm_neighbor_report = 1;
rrm_beacon_report = 1;
};
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; # TODO understand interferences
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 = 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 = {
usteer = svc.usteer.build {
ifname = "lan";
dependencies = [
# TODO is it the right stuff to deend on ?
config.services.hostap-1-ready
config.services.hostap-2-ready
];
};
# 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;
};
};
}

View file

@ -18,6 +18,33 @@ let
nix-lib = import ../../lib/nix-lib;
inherit (nix-lib) mapFuse;
mkDevAp = ap-no: {
"ap-dev-${builtins.toString ap-no}" = {
site = "unknown";
adminGroups = [ "fai" ];
hashedPassword = "$y$j9T$DMOQEWOYFHjNS0myrXp4x/$MG33VSdXGvib.99eN.AbvyVdNNJw4ERjAwK4.ULJe/A";
stateVersion = null;
nodeDir = "ap-dev";
nixpkgs = {
system = "zyxel-nwa50ax";
version = "24.05";
};
deployment.tags = [
"ap-dev"
];
extraNodeSettings = {
inherit ap-no;
vendor-mac = null;
};
};
};
mkAP' = building: floor: ap-no: {
"ap-v01-${builtins.toString building}-${builtins.toString floor}-${builtins.toString ap-no}" = {
site = "unknown";
@ -95,3 +122,7 @@ let
in
{ }
// builtins.foldl' (nodes: building: nodes // mkAPs-building building) { } (builtins.attrValues APs)
// mapFuse mkDevAp [
0
1
]