chore: wip: begin building better hostapd module

This commit is contained in:
oddlama 2023-03-18 16:46:06 +01:00
parent 9758a6e1e9
commit 076db4963f
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
4 changed files with 326 additions and 59 deletions

View file

@ -8,63 +8,15 @@
enable = true;
interface = "wlan1";
ssid = "🍯🐝💨";
# We'll set the options ourselves
wpa = false;
wpa = 3;
# Use 2.4GHz, this network is ment for dumb embedded devices
hwMode = "g";
# Automatic Channel Selection (ACS) is unfortunately not implemented for mt7612u.
channel = 13;
# Respect the local regulations
countryCode = "DE";
# TODO away
logLevel = 0;
# This is made for a Mediatek mt7612u based device (ALFA AWUS036ACM)
extraConfig = ''
utf8_ssid=1
# Enable QoS, required for 802.11n/ac/ax
wmm_enabled=1
# DFS (IEEE 802.11d, IEEE 802.11h)
# Limit to frequencies allowed in country
ieee80211d=1
# Ensure TX Power and frequencies compliance with local regulatory requirements
ieee80211h=1
# IEEE 802.11ac (WiFi 4) - MIMO and channel bonding support
ieee80211n=1
ht_capab=[LDPC][HT40+][HT40-][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1]
# IEEE 802.11ac (WiFi 5) - adds wider channel-width support and MU-MIMO (multi user MIMO)
ieee80211ac=1
#vht_capab=[SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]
#vht_oper_chwidth=1
# WPA3
wpa=2
wpa_pairwise=CCMP CCMP-256
rsn_pairwise=CCMP CCMP-256
wpa_key_mgmt=SAE
# Require WPA, disable WEP
auth_algs=1
# Encrypt management frames to protect against deauthentication and similar attacks
ieee80211w=2
# Force WPA3-Personal without transition
transition_disable=0x01
# Derive PWE using both hunting-and-pecking loop and hash-to-element
sae_pwe=2
# SAE passwords can be set via wpa_passphrase but not via wpa_psk_file. This sucks
# and means we have to add the passwords in pre-start to prevent them being visible here
{{SAE_PASSWORDS}}
# Use a MAC-address access control list
macaddr_acl=1
accept_mac_file=/run/hostapd/client-macs
# Hide network and require devices to know the ssid in advance
#ignore_broadcast_ssid=1
# Don't allow clients to communicate with each other
ap_isolate=1
'';
};
# TODO dont adverttise!
#wpa_psk_file=${config.rekey.secrets.wifi-clients.path}

View file

@ -1,10 +1,11 @@
age-encryption.org/v1
-> X25519 J0OVJ0jJkIkBk0nFoeZ7QhFoH2KZtVNEaqVrPAPOMkU
gPL8EodGaHRmGU7SjCi0A+VSHX0Jki4QTSQJqKakOmc
-> piv-p256 xqSe8Q ApYjO1OYkLa5P5y/CUcreVv1D+XIuzmvL22b8xOn4KCo
zXbQ2bBEoNfRBccduRzhezOHir1NoFgSaNpB2Kz9iLM
-> 3}-b-grease vM C
9zBNWTL08GkY4ZkDLmiQQqc2Di2oFiHko39JdKAzdF53kRcEkpojS0MwOhii5673
Pg0s035+WayZNZkpKHelA27aA7Yo+u6kGZ0xLP2N0ZxxjgZabYau
--- CkGhrXo9Gfpf5A1h9A4ZVRtdr3KOlE78J7wXOUyMJjI
B6úµÖůĆ^áaµŇ@VÇ�hĘąą#ťG7›&…•ęőkË~ #¶_k™Hž™`ňxcčČÝk•ŐşËJ�ýwú¶ó±¸ĆĐ ‰hF¦®ž[HéĂťęަ—�!ż}UR>•g’ÔşđÍBo¨ô�5 ÂĐ9VĽ$ř’‡©Ŕ˝AŰ ob“g ,š/
-> X25519 JEieTSfpgYVOG4jpaPU2Ixo5gzKfA2jADiVp2mDzo3o
9rqppLh1oDh5+9OOIULyRc6wO6xHtuMUWlD3Cdd92cc
-> piv-p256 xqSe8Q AhmCYR/YwLhHnFGfM8ovMFKesiCRq3KZJHhCkZCjOI8U
JpsMBhEZSirrIhrJSrxzxoH3kMafZdnwSv6AqRZRqow
-> 0-grease HqN8M8 ;L H9mxj ?vjE*x$[
7V9ALzJ+IJAvP9aUkCaaGCCX/DKbqhJc7Ii/WWwhbX56NNXKAnMu+St1yfUdto86
qhxQbDuVBB17Ls42W0gJxYlfwb0
--- XFjv9Cuf8BHmKEgxH4g6CJaVjz0L7ojFgfWhFlHs884
�ˇ´*‹Ů.•\&ĎťŰ�;?–\"ôĺŚÉj¦`›DŚSiwŚśÉAźŐ™ j€‡’51• <_ÔXńů"{‹IµpÜ«±:Ŕ±§b=Č*µ ţ
ł†~#´

5
modules/default.nix Normal file
View file

@ -0,0 +1,5 @@
{
imports = [
./hostapd.nix
];
}

309
modules/hostapd.nix Normal file
View file

@ -0,0 +1,309 @@
{
config,
lib,
pkgs,
utils,
...
}:
with lib; let
disabledModules = ["services/networking/hostapd.nix"];
cfg = config.services.hostapd;
escapedInterface = utils.escapeSystemdPath cfg.interface;
configFile = pkgs.writeText "hostapd.conf" ''
# logging (debug level)
logger_syslog=-1
logger_syslog_level=${toString cfg.logLevel}
logger_stdout=-1
logger_stdout_level=${toString cfg.logLevel}
ctrl_interface=/run/hostapd
ctrl_interface_group=${cfg.group}
interface=${cfg.interface}
driver=${cfg.driver}
utf8_ssid=1
ssid=${cfg.ssid}
hw_mode=${cfg.hwMode}
channel=${toString cfg.channel}
${optionalString cfg.wpa ''
wpa=2
wpa_pairwise=CCMP
wpa_passphrase=${cfg.wpaPassphrase}
''}
${optionalString cfg.noScan "noscan=1"}
# Enable QoS, required for 802.11n/ac/ax
wmm_enabled=1
${optionalString (cfg.countryCode != null) ''
# DFS (IEEE 802.11d, IEEE 802.11h)
# Limit to frequencies allowed in country
ieee80211d=1
country_code=${cfg.countryCode}
# Ensure TX Power and frequencies compliance with local regulatory requirements
ieee80211h=1
''}
# IEEE 802.11ac (WiFi 4) - MIMO and channel bonding support
ieee80211n=1
ht_capab=[LDPC][HT40+][HT40-][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1]
# IEEE 802.11ac (WiFi 5) - adds wider channel-width support and MU-MIMO (multi user MIMO)
ieee80211ac=1
#vht_capab=[SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]
#vht_oper_chwidth=1
# WPA3
wpa=2
wpa_pairwise=CCMP CCMP-256
rsn_pairwise=CCMP CCMP-256
wpa_key_mgmt=SAE
# Require WPA, disable WEP
auth_algs=1
# Encrypt management frames to protect against deauthentication and similar attacks
ieee80211w=2
# Force WPA3-Personal without transition
transition_disable=0x01
# Derive PWE using both hunting-and-pecking loop and hash-to-element
sae_pwe=2
# SAE passwords can be set via wpa_passphrase but not via wpa_psk_file. This sucks
# and means we have to add the passwords in pre-start to prevent them being visible here
{{SAE_PASSWORDS}}
# Use a MAC-address access control list
macaddr_acl=1
${optionalString (cfg.macaddrAcl != null) ''
accept_mac_file=${cfg.macaddrAcl}
''}
# Hide network and require devices to know the ssid in advance
#ignore_broadcast_ssid=${cfg.ignoreBroadcastSsid}
# Don't allow clients to communicate with each other
ap_isolate=${cfg.apIsolate}
${cfg.extraConfig}
'';
in {
# TODO assert interfaces >= 1
options = with types; {
services.hostapd = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether to enable hostapd. hostapd is a user space daemon for access point and
authentication servers. It implements IEEE 802.11 access point management,
IEEE 802.1X/WPA/WPA2/EAP Authenticators, RADIUS client, EAP server, and RADIUS
authentication server.
'';
};
interfaces = mkOption {
default = {};
example = literalExpression ''
{
# WiFi 4 - 2.4GHz
"wlp2s0" = {
ssid = "";
};
# WiFi 5 - 5GHz
"wlp3s0" = {
};
}
'';
description = lib.mdDoc ''
This option allows you to define APs for one or multiple interfaces.
Each attribute specifies a interface and associates it to its configuration.
At least one interface must be specified.
'';
type = attrsOf (submodule {
options = {
noScan = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Disables scan for overlapping BSSs in HT40+/- mode.
Caution: turning this on will likely violate regulatory requirements!
'';
};
driver = mkOption {
default = "nl80211";
example = "none";
type = types.str;
description = lib.mdDoc ''
The driver {command}`hostapd` will use.
{var}`nl80211` is used with all Linux mac80211 drivers.
{var}`none` is used if building a standalone RADIUS server that does
not control any wireless/wired driver.
Most applications will probably use the default.
'';
};
};
});
};
ssid = mkOption {
default = config.system.nixos.distroId;
defaultText = literalExpression "config.system.nixos.distroId";
example = "mySpecialSSID";
type = types.str;
description = lib.mdDoc "SSID to be used in IEEE 802.11 management frames.";
};
hwMode = mkOption {
default = "g";
type = types.enum ["a" "b" "g"];
description = lib.mdDoc ''
Operation mode.
(a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
'';
};
channel = mkOption {
default = 7;
example = 11;
type = types.int;
description = lib.mdDoc ''
Channel number (IEEE 802.11)
Please note that some drivers do not use this value from
{command}`hostapd` and the channel will need to be configured
separately with {command}`iwconfig`.
'';
};
group = mkOption {
default = "wheel";
example = "network";
type = types.str;
description = lib.mdDoc ''
Members of this group can control {command}`hostapd`.
'';
};
wpa = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
'';
};
wpaPassphrase = mkOption {
default = "my_sekret";
example = "any_64_char_string";
type = types.str;
description = lib.mdDoc ''
WPA-PSK (pre-shared-key) passphrase. Clients will need this
passphrase to associate with this access point.
Warning: This passphrase will get put into a world-readable file in
the Nix store!
'';
};
logLevel = mkOption {
default = 2;
type = types.int;
description = lib.mdDoc ''
Levels (minimum value for logged events):
0 = verbose debugging
1 = debugging
2 = informational messages
3 = notification
4 = warning
'';
};
countryCode = mkOption {
default = null;
example = "US";
type = with types; nullOr str;
description = lib.mdDoc ''
Country code (ISO/IEC 3166-1). Used to set regulatory domain.
Set as needed to indicate country in which device is operating.
This can limit available channels and transmit power.
These two octets are used as the first two octets of the Country String
(dot11CountryString).
If set this enables IEEE 802.11d. This advertises the countryCode and
the set of allowed channels and transmit power levels based on the
regulatory limits.
'';
};
extraConfig = mkOption {
default = "";
example = ''
auth_algo=0
ieee80211n=1
ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
'';
type = types.lines;
description = lib.mdDoc "Extra configuration options to put in hostapd.conf.";
};
};
};
###### implementation
config = mkIf cfg.enable {
environment.systemPackages = [pkgs.hostapd];
services.udev.packages = optionals (cfg.countryCode != null) [pkgs.crda];
systemd.services.hostapd = {
description = "hostapd wireless AP";
path = [pkgs.hostapd];
after = ["sys-subsystem-net-devices-${escapedInterface}.device"];
bindsTo = ["sys-subsystem-net-devices-${escapedInterface}.device"];
requiredBy = ["network-link-${cfg.interface}.service"];
wantedBy = ["multi-user.target"];
preStart = lib.mkBefore ''
grep -o '^..:..:..:..:..:..' ${config.rekey.secrets.wifi-clients.path} > /run/hostapd/client-macs
hostapd_conf=$(cat ''${systemd.services.hostapd.serviceConfig.ExecStart})
sae_passwords=$(echo -e "sae_password=aa|mac=13:13:13:13:13:13\nsae_password=aa|mac=12:12:12:12:12:12")
hostapd_conf=''${hostapd_conf//"{{SAE_PASSWORDS}}"/$sae_passwords}
echo "$hostapd_conf" > /run/hostapd/config/$interface
'';
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
Restart = "always";
ExecReload = "/bin/kill -HUP $MAINPID";
RuntimeDirectory = "hostapd";
# Hardening
LockPersonality = true;
MemoryDenyWriteExecute = true;
DevicePolicy = "closed";
DeviceAllow = "/dev/rfkill rw";
NoNewPrivileges = true;
PrivateUsers = false; # hostapd requires real system root access.
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = ["AF_UNIX" "AF_NETLINK" "AF_INET" "AF_INET6"];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = ["@system-service" "~@privileged" "@chown"];
UMask = "0077";
};
};
};
}