WIP: hostapd with vlan works on laptop
This commit is contained in:
parent
55ce0f0be1
commit
b7e2ec02e3
7 changed files with 334 additions and 202 deletions
|
@ -15,13 +15,34 @@
|
|||
bpir3
|
||||
nixos-nftables-firewall
|
||||
;
|
||||
in {
|
||||
disabledModules = [
|
||||
# "services/networking/hostapd.nix"
|
||||
];
|
||||
|
||||
mkVlanIpv4HostAddr = { vlanid, host, ipv4Offset ? 20, cidr ? true }:
|
||||
builtins.concatStringsSep "."
|
||||
[ "192" "168" (toString (ipv4Offset + vlanid)) "${toString host}${lib.strings.optionalString cidr "/24"}" ];
|
||||
|
||||
# vlanRangeStart = 1;
|
||||
# vlanRangeEnd = 20;
|
||||
# vlanRange = (lib.lists.range vlanRangeStart vlanRangeEnd);
|
||||
vlanRange = builtins.map (vlanid: (lib.strings.toInt vlanid)) (builtins.attrNames vlans);
|
||||
vlanRangeWith0 = [ 0 ] ++ vlanRange;
|
||||
|
||||
defaultVlan = {
|
||||
name = "internal";
|
||||
packet_priority = 0;
|
||||
};
|
||||
vlans = {
|
||||
"1".name = "dmz.${defaultVlan.name}";
|
||||
"1".packet_priority = 0;
|
||||
"2".name = "iot.${defaultVlan.name}";
|
||||
"2".packet_priority = -10;
|
||||
"3".name = "office.${defaultVlan.name}";
|
||||
"3".packet_priority = -5;
|
||||
"4".name = "guests.${defaultVlan.name}";
|
||||
"4".packet_priority = 10;
|
||||
};
|
||||
getVlanDomain = { vlanid }: vlans."${toString vlanid}".name or defaultVlan.name;
|
||||
in {
|
||||
imports = [
|
||||
# nodeFlake.inputs.disko.nixosModules.disko
|
||||
repoFlake.inputs.sops-nix.nixosModules.sops
|
||||
|
||||
../../profiles/common/user.nix
|
||||
|
@ -30,6 +51,17 @@ in {
|
|||
|
||||
nixos-nftables-firewall.nixosModules.default
|
||||
|
||||
{
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
];
|
||||
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
}
|
||||
|
||||
# TODO
|
||||
# ./network.nix
|
||||
# ./monitoring.nix
|
||||
|
@ -43,11 +75,13 @@ in {
|
|||
rootPasswordFile = config.sops.secrets.passwords-root.path;
|
||||
};
|
||||
|
||||
sops.secrets.passwords-root = {
|
||||
sopsFile = ../../../../secrets/${nodeName}/secrets.yaml;
|
||||
neededForUsers = true;
|
||||
format = "yaml";
|
||||
};
|
||||
sops.defaultSopsFile = ../../../../secrets/${nodeName}/secrets.yaml;
|
||||
sops.defaultSopsFormat = "yaml";
|
||||
|
||||
sops.secrets.passwords-root.neededForUsers = true;
|
||||
|
||||
sops.secrets.wlan0_saePasswordsFile = { };
|
||||
sops.secrets.wlan0_wpaPskFile = { };
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -101,6 +135,8 @@ in {
|
|||
# Use the nftables firewall instead of the base nixos scripted rules.
|
||||
# This flake provides a similar utility to the base nixos scripting.
|
||||
# https://github.com/thelegy/nixos-nftables-firewall/tree/main
|
||||
|
||||
# TODO: configure packet_priority for VLANs (see https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_priority, https://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation#packet_priority)
|
||||
nftables = {
|
||||
enable = true;
|
||||
stopRuleset = "";
|
||||
|
@ -108,26 +144,69 @@ in {
|
|||
enable = true;
|
||||
zones = {
|
||||
lan.interfaces = ["br-lan"];
|
||||
vlan.interfaces = builtins.map (vlanid: "br-vlan.${toString vlanid}") vlanRange;
|
||||
# lan.ipv4Addresses = ["192.168.0.0/16"];
|
||||
wan.interfaces = ["wan" "lan0"];
|
||||
};
|
||||
rules = {
|
||||
lan = {
|
||||
rules = let
|
||||
ipv6IcmpTypes = [
|
||||
"destination-unreachable" "echo-reply" "echo-request"
|
||||
"packet-too-big" "parameter-problem" "time-exceeded"
|
||||
|
||||
# Without the nd-* ones ipv6 will not work.
|
||||
"nd-neighbor-solicit" "nd-router-advert" "nd-neighbor-advert"
|
||||
];
|
||||
ipv4IcmpTypes = [
|
||||
"destination-unreachable" "echo-reply" "echo-request" "source-quench" "time-exceeded"
|
||||
"router-advertisement"
|
||||
];
|
||||
allowIcmpLines = [
|
||||
"ip protocol icmp icmp type { ${builtins.concatStringsSep ", " ipv4IcmpTypes} } accept"
|
||||
"ip6 nexthdr icmpv6 icmpv6 type { ${builtins.concatStringsSep ", " ipv6IcmpTypes} } accept"
|
||||
];
|
||||
in {
|
||||
lan-to-fw = {
|
||||
from = ["lan"];
|
||||
to = ["fw"];
|
||||
verdict = "accept";
|
||||
};
|
||||
outbound = {
|
||||
from = ["lan"];
|
||||
to = ["lan" "wan"];
|
||||
verdict = "accept";
|
||||
};
|
||||
nat = {
|
||||
|
||||
lan-to-wan = {
|
||||
from = ["lan"];
|
||||
to = ["wan"];
|
||||
masquerade = true;
|
||||
verdict = "accept";
|
||||
};
|
||||
|
||||
incoming-wan = {
|
||||
vlan-to-wan = {
|
||||
from = ["vlan"];
|
||||
to = ["wan"];
|
||||
verdict = "accept";
|
||||
};
|
||||
|
||||
vlan-to-fw = {
|
||||
allowedUDPPortRanges = [
|
||||
{ from = 67; to = 68; }
|
||||
{ from = 53; to = 53; }
|
||||
];
|
||||
allowedTCPPortRanges = [
|
||||
{ from = 22; to = 22; }
|
||||
{ from = 53; to = 53; }
|
||||
];
|
||||
from = ["vlan"];
|
||||
to = ["fw"];
|
||||
extraLines = allowIcmpLines ++ [
|
||||
"drop"
|
||||
];
|
||||
};
|
||||
|
||||
to-wan-nat = {
|
||||
from = ["lan" "vlan"];
|
||||
to = ["wan"];
|
||||
masquerade = true;
|
||||
verdict = "accept";
|
||||
};
|
||||
|
||||
wan-to-fw = {
|
||||
from = ["wan"];
|
||||
to = ["fw"];
|
||||
allowedTCPPortRanges = [
|
||||
|
@ -136,7 +215,9 @@ in {
|
|||
to = 22;
|
||||
}
|
||||
];
|
||||
verdict = "drop";
|
||||
extraLines = allowIcmpLines ++ [
|
||||
"drop"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -157,50 +238,12 @@ in {
|
|||
extraConfig = ''
|
||||
[Bridge]
|
||||
STP=true
|
||||
VLANFiltering=yes
|
||||
DefaultPVID=none
|
||||
# VLANFiltering=yes
|
||||
# DefaultPVID=1
|
||||
'';
|
||||
};
|
||||
};
|
||||
networks = {
|
||||
# Connect the bridge ports to the bridge
|
||||
"30-lan1" = {
|
||||
matchConfig.Name = "lan1";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
"30-lan2" = {
|
||||
matchConfig.Name = "lan2";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
"30-lan3" = {
|
||||
matchConfig.Name = "lan3";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
# Configure the bridge for its desired function
|
||||
"40-br-lan" = {
|
||||
matchConfig.Name = "br-lan";
|
||||
bridgeConfig = {};
|
||||
address = [
|
||||
"192.168.10.1/24"
|
||||
];
|
||||
networkConfig = {
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
# Don't wait for it as it also would wait for wlan and DFS which takes around 5 min
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
};
|
||||
# use lan0 as secondary WAN interface
|
||||
"10-lan0-wan" = {
|
||||
matchConfig.Name = "lan0";
|
||||
|
@ -232,15 +275,88 @@ in {
|
|||
# make routing on this interface a dependency for network-online.target
|
||||
linkConfig.RequiredForOnline = "routable";
|
||||
};
|
||||
};
|
||||
|
||||
# Connect the bridge ports to the bridge
|
||||
"30-lan1" = {
|
||||
matchConfig.Name = "lan1";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
"30-lan2" = {
|
||||
matchConfig.Name = "lan2";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
"30-lan3" = {
|
||||
matchConfig.Name = "lan3";
|
||||
networkConfig = {
|
||||
Bridge = "br-lan";
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
linkConfig.RequiredForOnline = "enslaved";
|
||||
};
|
||||
# Configure the bridge for its desired function
|
||||
"40-br-lan" = {
|
||||
matchConfig.Name = "br-lan";
|
||||
bridgeConfig = {};
|
||||
address = [
|
||||
(mkVlanIpv4HostAddr { vlanid = 0; host = 1;})
|
||||
];
|
||||
networkConfig = {
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
# Don't wait for it as it also would wait for wlan and DFS which takes around 5 min
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
|
||||
# TODO: understand when this would be needed
|
||||
# bridgeVLANs = [
|
||||
# {
|
||||
# bridgeVLANConfig = {
|
||||
# VLAN = "${vlanRangeStart}-${vlanRangeEnd}";
|
||||
# };
|
||||
# }
|
||||
# ];
|
||||
};
|
||||
}
|
||||
# VLAN interface addresses
|
||||
//
|
||||
lib.attrsets.foldlAttrs
|
||||
(acc: _: value: acc // value)
|
||||
{}
|
||||
(lib.attrsets.genAttrs
|
||||
(builtins.map
|
||||
builtins.toString
|
||||
vlanRange
|
||||
)
|
||||
(vlanid: {
|
||||
"50-br-vlan.${vlanid}" = {
|
||||
matchConfig.Name = "br-vlan.${toString vlanid}";
|
||||
address = [
|
||||
(mkVlanIpv4HostAddr { vlanid = (lib.strings.toInt vlanid); host = 1; })
|
||||
];
|
||||
networkConfig = {
|
||||
ConfigureWithoutCarrier = true;
|
||||
};
|
||||
# Don't wait for it as it also would wait for wlan and DFS which takes around 5 min
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
# wireless access point
|
||||
services.hostapd = {
|
||||
enable = true;
|
||||
package = nodeFlake.packages.hostapd_main;
|
||||
radios = let
|
||||
mkBssid = i: # generated with https://miniwebtool.com/mac-address-generator/
|
||||
"34:56:ce:0f:ed:4${builtins.toString i}";
|
||||
# generated with https://miniwebtool.com/mac-address-generator/
|
||||
mkBssid = i: "34:56:ce:0f:ed:4${toString i}";
|
||||
in {
|
||||
wlan0 = {
|
||||
band = "2g";
|
||||
|
@ -254,64 +370,68 @@ in {
|
|||
};
|
||||
networks = {
|
||||
wlan0 = {
|
||||
ssid = "justtestingwifi-wpa3";
|
||||
authentication = {
|
||||
mode = "wpa3-sae";
|
||||
# saePasswordsFile = config.sops.secrets.wifiPassword.path;
|
||||
saePasswords = [
|
||||
{
|
||||
password = "normalnormal";
|
||||
}
|
||||
{
|
||||
password = "vlanvlan";
|
||||
vlanid = 1;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
ssid = "mlsia";
|
||||
bssid = mkBssid 0;
|
||||
settings = {
|
||||
bridge = "br-lan";
|
||||
};
|
||||
};
|
||||
|
||||
wlan0-1 = {
|
||||
ssid = "justtestingwifi-compat";
|
||||
authentication = {
|
||||
mode = "wpa3-sae-transition";
|
||||
# saePasswordsFile = config.sops.secrets.wifiPassword.path;
|
||||
saePasswords = [
|
||||
{
|
||||
password = "normalnormal";
|
||||
}
|
||||
{
|
||||
password = "vlanvlan";
|
||||
vlanid = 1;
|
||||
}
|
||||
];
|
||||
wpaPskFile = pkgs.writeText "wpa_psk" ''
|
||||
00:00:00:00:00:00 normalnormal
|
||||
vlanid=1 00:00:00:00:00:00 vlanvlan
|
||||
'';
|
||||
};
|
||||
# manually configure something close to wpa3-sae-transition
|
||||
authentication.mode = "none";
|
||||
# authentication.saePasswordsFile = config.sops.secrets.wlan0_saePasswordsFile.path;
|
||||
|
||||
bssid = mkBssid 1;
|
||||
settings = {
|
||||
bridge = "br-lan";
|
||||
# bridge = "br-lan";
|
||||
|
||||
logger_stdout_level= lib.mkForce 1;
|
||||
logger_syslog_level= lib.mkForce 1;
|
||||
|
||||
# resources on vlan tagging
|
||||
# https://wireless.wiki.kernel.org/en/users/Documentation/hostapd#dynamic_vlan_tagging
|
||||
# https://forum.openwrt.org/t/individual-per-passphrase-wifi-vlans-using-wpa-psk-file-no-radius-required/161696/4
|
||||
|
||||
vlan_tagged_interface = "br-lan";
|
||||
vlan_bridge = "br-vlan";
|
||||
vlan_naming = 1;
|
||||
vlan_bridge = "br-vlan.";
|
||||
dynamic_vlan = 1;
|
||||
vlan_file = builtins.toString (pkgs.writeText "hostapd.vlan" ''
|
||||
* wlan0-1.#
|
||||
vlan_file = toString (pkgs.writeText "hostapd.vlan" ''
|
||||
* wlan0.#
|
||||
'');
|
||||
|
||||
wpa_psk_file = config.sops.secrets.wlan0_wpaPskFile.path;
|
||||
sae_password_file = config.sops.secrets.wlan0_saePasswordsFile.path;
|
||||
|
||||
ieee80211w=1;
|
||||
auth_algs = 3;
|
||||
sae_require_mfp = 0;
|
||||
sae_groups = "19 20 21";
|
||||
wpa = 2;
|
||||
wpa_key_mgmt = "WPA-PSK WPA-PSK-SHA256 SAE";
|
||||
|
||||
# worked above here
|
||||
# testing below here
|
||||
|
||||
# ieee80211w = 2;
|
||||
|
||||
# IEEE 802.11i (authentication) related configuration
|
||||
# Encrypt management frames to protect against deauthentication and similar attacks
|
||||
# ieee80211w = mkDefault 1;
|
||||
# sae_require_mfp = mkDefault 1;
|
||||
|
||||
# sae_require_mfp = 1;
|
||||
# sae_groups = "19 20 21";
|
||||
};
|
||||
};
|
||||
|
||||
# wlan0-1 = {
|
||||
# ssid = "justtestingwifi-wpa3";
|
||||
# authentication = {
|
||||
# mode = "wpa3-sae";
|
||||
# saePasswordsFile = config.sops.secrets.wlan0_1_saePasswordFile.path;
|
||||
# };
|
||||
|
||||
# bssid = mkBssid 1;
|
||||
# settings = {
|
||||
# bridge = "br-lan";
|
||||
# };
|
||||
# };
|
||||
|
||||
# Uncomment when needed otherwise remove
|
||||
# wlan0-1 = {
|
||||
# ssid = "koteczkowo3";
|
||||
|
@ -423,94 +543,75 @@ in {
|
|||
|
||||
services.resolved.enable = false;
|
||||
|
||||
services.dnsmasq = {
|
||||
services.dnsmasq = let
|
||||
mkIfName = { vlanid }: if vlanid == 0 then "br-lan" else "br-vlan.${toString vlanid}";
|
||||
in {
|
||||
enable = true;
|
||||
settings = {
|
||||
# upstream DNS servers
|
||||
server = ["9.9.9.9" "8.8.8.8" "1.1.1.1"];
|
||||
# sensible behaviours
|
||||
domain-needed = true;
|
||||
bogus-priv = true;
|
||||
no-resolv = true;
|
||||
|
||||
dhcp-range = [
|
||||
# "br-lan,192.168.10.50,192.168.10.100,24h"
|
||||
"192.168.10.50,192.168.10.100,24h"
|
||||
];
|
||||
dhcp-range = let
|
||||
mkDhcpRange = { tag, vlanid }: builtins.concatStringsSep "," [
|
||||
tag
|
||||
(mkVlanIpv4HostAddr { inherit vlanid; host = 100; cidr = false; })
|
||||
(mkVlanIpv4HostAddr { inherit vlanid; host = 199; cidr = false; })
|
||||
"5m"
|
||||
];
|
||||
in
|
||||
builtins.map
|
||||
(vlanid:
|
||||
mkDhcpRange { tag = mkIfName {inherit vlanid;}; inherit vlanid; }
|
||||
)
|
||||
vlanRange
|
||||
;
|
||||
|
||||
# interface = "br-lan";
|
||||
# bind-interfaces = true;
|
||||
|
||||
# dhcp-host = "192.168.10.1";
|
||||
|
||||
# local domains
|
||||
local = "/lan/";
|
||||
domain = "lan";
|
||||
# local = "/${getVlanDomain {vlanid = 0;}/";
|
||||
# domain = getVlanDomain {vlanid = 0;};
|
||||
expand-hosts = true;
|
||||
|
||||
# don't use /etc/hosts as this would advertise ${nodeName} as localhost
|
||||
no-hosts = true;
|
||||
address = "/${nodeName}.lan/192.168.10.1";
|
||||
|
||||
# address = "/${nodeName}.lan/${fwLanHostAddr}";
|
||||
server = [
|
||||
# upstream DNS servers
|
||||
"9.9.9.9" "8.8.8.8" "1.1.1.1"
|
||||
] ++ builtins.map
|
||||
(vlanid: "/${nodeName}.${getVlanDomain {inherit vlanid;}}/")
|
||||
vlanRangeWith0
|
||||
;
|
||||
|
||||
# TODO: compare this to using `interface-name`
|
||||
dynamic-host = [
|
||||
] ++ builtins.map
|
||||
(vlanid:
|
||||
builtins.concatStringsSep "," [
|
||||
"${nodeName}.${getVlanDomain{inherit vlanid;}}" "0.0.0.1" (mkIfName {inherit vlanid;})
|
||||
]
|
||||
)
|
||||
vlanRangeWith0
|
||||
;
|
||||
|
||||
dhcp-option-force = builtins.map
|
||||
(vlanid: "option:domain-search,${getVlanDomain{inherit vlanid;}}")
|
||||
vlanRangeWith0
|
||||
;
|
||||
|
||||
localise-queries = true;
|
||||
};
|
||||
};
|
||||
|
||||
# The service irqbalance is useful as it assigns certain IRQ calls to specific CPUs instead of letting the first CPU core to handle everything. This is supposed to increase performance by hitting CPU cache more often.
|
||||
services.irqbalance.enable = true;
|
||||
|
||||
# disko.devices = {
|
||||
# disk = {
|
||||
# nvme0n1 = {
|
||||
# device = "/dev/nvme0n1";
|
||||
# type = "disk";
|
||||
# content = {
|
||||
# type = "table";
|
||||
# format = "gpt";
|
||||
# partitions = [
|
||||
# {
|
||||
# name = "var-log";
|
||||
# start = "1MiB";
|
||||
# end = "20G";
|
||||
# content = {
|
||||
# type = "filesystem";
|
||||
# format = "ext4";
|
||||
# mountpoint = "/var/log";
|
||||
# };
|
||||
# }
|
||||
# {
|
||||
# name = "tmp";
|
||||
# start = "20G";
|
||||
# end = "60G";
|
||||
# content = {
|
||||
# type = "filesystem";
|
||||
# format = "ext4";
|
||||
# mountpoint = "/tmp";
|
||||
# };
|
||||
# }
|
||||
# {
|
||||
# name = "var";
|
||||
# start = "60G";
|
||||
# end = "100G";
|
||||
# content = {
|
||||
# type = "filesystem";
|
||||
# format = "ext4";
|
||||
# mountpoint = "/var";
|
||||
# };
|
||||
# }
|
||||
# {
|
||||
# name = "swap";
|
||||
# start = "100G";
|
||||
# end = "100%";
|
||||
# content = {
|
||||
# type = "swap";
|
||||
# randomEncryption = false;
|
||||
# };
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
system.stateVersion = "23.05";
|
||||
|
||||
boot.kernelPackages = pkgs.linuxPackages_bpir3;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue