add hostapd support
This commit is contained in:
parent
b7c46e01df
commit
f7b1c750aa
5 changed files with 335 additions and 13 deletions
|
@ -12,6 +12,7 @@
|
|||
nixos-hardware.nixosModules.common-cpu-amd
|
||||
nixos-hardware.nixosModules.common-gpu-nvidia
|
||||
nixos-hardware.nixosModules.common-pc-hdd
|
||||
./services/hostapd.nix
|
||||
];
|
||||
|
||||
hardware.cpu.amd.updateMicrocode = true;
|
||||
|
|
17
config/services/hostapd.nix
Normal file
17
config/services/hostapd.nix
Normal file
|
@ -0,0 +1,17 @@
|
|||
{ config, ... }: {
|
||||
imports = [
|
||||
../../modules/hostapd.nix
|
||||
];
|
||||
services.hostapd = {
|
||||
countryCode = "DE";
|
||||
interface = "wlp6s0";
|
||||
ssid = "🦝";
|
||||
wpa = true;
|
||||
wpaPassphraseFile = config.sops.secrets."services/hostapd".path;
|
||||
};
|
||||
sops.secrets."services/hostapd" = {
|
||||
restartUnits = [
|
||||
"hostapd.service"
|
||||
];
|
||||
};
|
||||
}
|
251
modules/hostapd.nix
Normal file
251
modules/hostapd.nix
Normal file
|
@ -0,0 +1,251 @@
|
|||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
# TODO:
|
||||
#
|
||||
# asserts
|
||||
# ensure that the nl80211 module is loaded/compiled in the kernel
|
||||
# wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.hostapd;
|
||||
|
||||
escapedInterface = utils.escapeSystemdPath cfg.interface;
|
||||
|
||||
configFile = pkgs.writeText "hostapd.conf" ''
|
||||
interface=${cfg.interface}
|
||||
driver=${cfg.driver}
|
||||
ssid=${cfg.ssid}
|
||||
hw_mode=${cfg.hwMode}
|
||||
channel=${toString cfg.channel}
|
||||
${optionalString (cfg.countryCode != null) "country_code=${cfg.countryCode}"}
|
||||
${optionalString (cfg.countryCode != null) "ieee80211d=1"}
|
||||
|
||||
# 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}
|
||||
|
||||
${optionalString cfg.wpa ''
|
||||
wpa=2
|
||||
wpa_passphrase=${if cfg.wpaPassphrase then cfg.wpaPassphrase else "#WPA_PASSPHRASE#"}
|
||||
''}
|
||||
${optionalString cfg.noScan "noscan=1"}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.hostapd = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable putting a wireless interface into infrastructure mode,
|
||||
allowing other wireless devices to associate with the wireless
|
||||
interface and do wireless networking. A simple access point will
|
||||
<option>enable hostapd.wpa</option>,
|
||||
<option>hostapd.wpaPassphrase</option>, and
|
||||
<option>hostapd.ssid</option>, as well as DHCP on the wireless
|
||||
interface to provide IP addresses to the associated stations, and
|
||||
NAT (from the wireless interface to an upstream interface).
|
||||
'';
|
||||
};
|
||||
|
||||
interface = mkOption {
|
||||
default = "";
|
||||
example = "wlp2s0";
|
||||
type = types.str;
|
||||
description = ''
|
||||
The interfaces <command>hostapd</command> will use.
|
||||
'';
|
||||
};
|
||||
|
||||
noScan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Do not scan for overlapping BSSs in HT40+/- mode.
|
||||
Caution: turning this on will violate regulatory requirements!
|
||||
'';
|
||||
};
|
||||
|
||||
driver = mkOption {
|
||||
default = "nl80211";
|
||||
example = "hostapd";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Which driver <command>hostapd</command> will use.
|
||||
Most applications will probably use the default.
|
||||
'';
|
||||
};
|
||||
|
||||
ssid = mkOption {
|
||||
default = "nixos";
|
||||
example = "mySpecialSSID";
|
||||
type = types.str;
|
||||
description = "SSID to be used in IEEE 802.11 management frames.";
|
||||
};
|
||||
|
||||
hwMode = mkOption {
|
||||
default = "g";
|
||||
type = types.enum [ "a" "b" "g" ];
|
||||
description = ''
|
||||
Operation mode.
|
||||
(a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
|
||||
'';
|
||||
};
|
||||
|
||||
channel = mkOption {
|
||||
default = 7;
|
||||
example = 11;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Channel number (IEEE 802.11)
|
||||
Please note that some drivers do not use this value from
|
||||
<command>hostapd</command> and the channel will need to be configured
|
||||
separately with <command>iwconfig</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
default = "wheel";
|
||||
example = "network";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Members of this group can control <command>hostapd</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
wpa = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
|
||||
'';
|
||||
};
|
||||
|
||||
wpaPassphrase = mkOption {
|
||||
default = null;
|
||||
example = "any_64_char_string";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
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!
|
||||
'';
|
||||
};
|
||||
|
||||
wpaPassphraseFile = mkOption {
|
||||
default = null;
|
||||
example = "/run/secrets/wpa_passphrase";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
File containing WPA-PSK passphrase. Clients will need this
|
||||
passphrase to associate with this access point.
|
||||
'';
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = ''
|
||||
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 = ''
|
||||
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 = "Extra configuration options to put in hostapd.conf.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
disabledModules = [ "services/networking/hostapd.nix" ];
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.wpa != null -> (cfg.wpaPassphrase != null || cfg.wpaPassphraseFile != null);
|
||||
message = "Either wpaPassphrase or wpaPassphraseFile must be set if wpa is enabled.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.wpaPassphraseFile != null -> cfg.wpaPassphrase == null;
|
||||
message = "You cannot provide a wpaPassphrase and a wpaPassphraseFile!";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ pkgs.hostapd ];
|
||||
|
||||
services.udev.packages = optional (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 = mkIf cfg.wpaPassphraseFile != null ''
|
||||
PASSPHRASE=$(cat ${cfg.wpaPassphraseFile})
|
||||
sed 's|#WPA_PASSPHRASE#|$PASSPHRASE|g' ${configFile} > /run/hostapd/hostapd.conf
|
||||
'';
|
||||
|
||||
serviceConfig =
|
||||
{
|
||||
ExecStart = "${pkgs.hostapd}/bin/hostapd ${if cfg.wpaPassphraseFile != null then "/run/hostapd/hostapd.conf" else configFile}";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
systemd.tmpfiles.rules = mkIf cfg.wpaPassphraseFile != null [
|
||||
"d '/run/hostapd' 0700 root root - -"
|
||||
];
|
||||
};
|
||||
}
|
|
@ -6,6 +6,7 @@ services:
|
|||
gitea_token: ENC[AES256_GCM,data:v0Ej8841I1F/dK5ZplRzZlvngpueMQKspM5USzX9VkOEmpCs2NA3+Q==,iv:fZisAuyqk7ATFx6qHYkScUeS8SsikjiPzVovZjGnUYM=,tag:7+O+Sn7unPDy88a6T70Jmg==,type:str]
|
||||
github_token: ENC[AES256_GCM,data:AWMeX+P8YHGpSuH+5KqvE9zNxkEPKGvdRaQjNysO4/XE4csGjCvmjA==,iv:MCRtws/SM7lWS2/2pp5tbeX7+I5h4LVd9bJp//ln9hs=,tag:LMEGWFAaOqH0fqfNgc87AQ==,type:str]
|
||||
aws_credentials: ENC[AES256_GCM,data:yxJU6d6BMi+LHUPimMkgr5h6accGXQXxFu9A0swdwKII/Xfo4ALAw4J4aEhpnNuK8JwmzuuDdTDGnilzuEATeaANa2cNXps6AWw8Hem8idw585xTcU1YBEOdbBSs/mKK6S+Da1OU5jC1atrCCWY7cg==,iv:tAEGsniZ7N/jBp7btLlD1pNcF4NvEmpO6zXji1H29t8=,tag:lmAB3QMfaT3ljDmr+8IBHA==,type:str]
|
||||
hostapd: ENC[AES256_GCM,data:XDLRbra/RadQE/t6cPG94A==,iv:EafhBPWviYaZLodGcIDTfxUoLnnaLQKyWLeSwZe9d0Q=,tag:yvfqt74qR/en/HoW2HmRKQ==,type:int]
|
||||
security:
|
||||
restic:
|
||||
password: ENC[AES256_GCM,data:n+M6pfe0YrONaYo3HSnijHxhThg=,iv:0J2t+58tYRJD1GmnJa8w30U+RwOl67eWeHhvLk0eeks=,tag:ivuZqpGrU7ZHFZ4IiMvxBw==,type:str]
|
||||
|
@ -36,8 +37,8 @@ sops:
|
|||
WnV3QWxtalIzWFdoQmpDTmJsNGdNOW8K++rFGXy0G6Gcu2gQwSP6xfXInQ/y5nh5
|
||||
2oGp8sfOLFWnNI4SWL0ChP47K3C/9ysUHwQnUYPbRafZ/4X6cN40ZQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2022-04-24T10:49:24Z"
|
||||
mac: ENC[AES256_GCM,data:gSBsJnw13ic3ysf+FevyXi3zhyfld5aChTRa49OE65FcedbqqToctALLUo4UXZbbPonu3ixBV4R297NDjWFlS+3QFR15uXMZpbHcVSLPtDF+TTx4c90MA+V2qrfTqPGaBb8zBqGOHqIOYBNEQWfjirYALnguvTBFiYUoGE9nswE=,iv:NYc+4/SLz92dYP+zdwW3WTpB5wyeTQ2SfpWLWP16HoY=,tag:qJ0AF8RtPpwJ+dNPlo+jNA==,type:str]
|
||||
lastmodified: "2022-04-24T20:20:10Z"
|
||||
mac: ENC[AES256_GCM,data:qANlJ5sFKi6OE6ZaI0mUYHkb1UvJN9cxIlZ213dN9vtOa3eaNAHqW8SHa3Gt1lSjgoY9ux0SoaDb4Pyevq2COVm2dHlHPtfhCzvpVpjy3xLI7Q69OFLRgKxUQj2ZEtXWKy7ea82EVSTEWavlDaXzd/lpaiKugYgM/xwQjzbifgQ=,iv:Oc+SPuQKA1awYDZwdQgHWbVGJFRbTcWPveNNlM3b7y0=,tag:Z4s44hHbdL4mwFaKPq9lmQ==,type:str]
|
||||
pgp:
|
||||
- created_at: "2022-04-24T10:34:20Z"
|
||||
enc: |
|
||||
|
|
|
@ -8,7 +8,7 @@ in
|
|||
SOA = {
|
||||
nameServer = "ns1.chir.rs.";
|
||||
adminEmail = "lotte@chir.rs";
|
||||
serial = 9;
|
||||
serial = 10;
|
||||
};
|
||||
NS = [
|
||||
"ns1.chir.rs."
|
||||
|
@ -38,14 +38,6 @@ in
|
|||
(ttl zoneTTL (aaaa "fd00:e621:e621::1"))
|
||||
];
|
||||
};
|
||||
nas = {
|
||||
A = [
|
||||
(ttl zoneTTL (a "10.0.2.2"))
|
||||
];
|
||||
AAAA = [
|
||||
(ttl zoneTTL (aaaa "fd00:e621:e621:2::2"))
|
||||
];
|
||||
};
|
||||
nixos-8gb-fsn1-1 = {
|
||||
AAAA = [
|
||||
(ttl zoneTTL (aaaa "fd0d:a262:1fa6:e621:b4e1:8ff:e658:6f49"))
|
||||
|
@ -187,13 +179,73 @@ in
|
|||
}
|
||||
];
|
||||
};
|
||||
nas = {
|
||||
AAAA = [
|
||||
(ttl zoneTTL (aaaa "fd0d:a262:1fa6:e621:bc9b:6a33:86e4:873b"))
|
||||
];
|
||||
SSHFP = [
|
||||
{
|
||||
algorithm = "rsa";
|
||||
mode = "sha1";
|
||||
fingerprint = "13e1173d96b822c98a7b3cd47be2e830f7758671";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
{
|
||||
algorithm = "rsa";
|
||||
mode = "sha256";
|
||||
fingerprint = "2e87a3fd00918e4f1e47d3b14b59e846ee016a0d3269cb2524c8d28b121e130e";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
{
|
||||
algorithm = "ed25519";
|
||||
mode = "sha1";
|
||||
fingerprint = "d1df2d244980a5e4dde37eed678b59a2239ca2ac";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
{
|
||||
algorithm = "ed25519";
|
||||
mode = "sha256";
|
||||
fingerprint = "33d6c993ee3789fb6a2e60c243da7095eb79ce8e522b087f8a31ea400d7b034e";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
];
|
||||
# TODO: add TLSA
|
||||
HTTPS = [
|
||||
{
|
||||
svcPriority = 1;
|
||||
targetName = ".";
|
||||
alpn = [ "http/1.1" "h2" "h3" ];
|
||||
ipv6hint = [ "fd0d:a262:1fa6:e621:bc9b:6a33:86e4:873b" ];
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
];
|
||||
CAA = [
|
||||
{
|
||||
issuerCritical = false;
|
||||
tag = "issue";
|
||||
value = "letsencrypt.org";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
{
|
||||
issuerCritical = false;
|
||||
tag = "issuewild";
|
||||
value = "letsencrypt.org";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
{
|
||||
issuerCritical = false;
|
||||
tag = "iodef";
|
||||
value = "mailto:lotte@chir.rs";
|
||||
ttl = zoneTTL;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
grafana.CNAME = [ "nixos-8gb-fsn1-1" ];
|
||||
minio.CNAME = [ "nixos-8gb-fsn1-1" ];
|
||||
minio-console.CNAME = [ "nixos-8gb-fsn1-1" ];
|
||||
backup.CNAME = [ "nas" ];
|
||||
cache.CNAME = [ "nutty-noon" ];
|
||||
hydra.CNAME = [ "nutty-noon" ];
|
||||
hydra.CNAME = [ "nas" ];
|
||||
_acme-challenge = delegateTo [
|
||||
"ns1.chir.rs."
|
||||
"ns2.chir.rs."
|
||||
|
|
Loading…
Reference in a new issue