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
|
@ -1,4 +1,4 @@
|
||||||
# This example uses YAML anchors which allows reuse of multiple keys
|
# This example uses YAML anchors which allows reuse of multiple keys
|
||||||
# without having to repeat yourself.
|
# without having to repeat yourself.
|
||||||
# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
|
# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
|
||||||
# for a more complex example.
|
# for a more complex example.
|
||||||
|
@ -72,4 +72,4 @@ creation_rules:
|
||||||
- pgp:
|
- pgp:
|
||||||
- *steveej
|
- *steveej
|
||||||
age:
|
age:
|
||||||
- *sj-bm-hostkey0
|
- *sj-bm-hostkey0
|
||||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix",
|
"nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix",
|
||||||
"[nix]": {
|
"[nix]": {
|
||||||
"editor.defaultFormatter": "kamadorueda.alejandra"
|
"editor.defaultFormatter": "jnoortheen.nix-ide"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,34 @@
|
||||||
bpir3
|
bpir3
|
||||||
nixos-nftables-firewall
|
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 = [
|
imports = [
|
||||||
# nodeFlake.inputs.disko.nixosModules.disko
|
|
||||||
repoFlake.inputs.sops-nix.nixosModules.sops
|
repoFlake.inputs.sops-nix.nixosModules.sops
|
||||||
|
|
||||||
../../profiles/common/user.nix
|
../../profiles/common/user.nix
|
||||||
|
@ -30,6 +51,17 @@ in {
|
||||||
|
|
||||||
nixos-nftables-firewall.nixosModules.default
|
nixos-nftables-firewall.nixosModules.default
|
||||||
|
|
||||||
|
{
|
||||||
|
nix.nixPath = [
|
||||||
|
"nixpkgs=${pkgs.path}"
|
||||||
|
];
|
||||||
|
|
||||||
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# ./network.nix
|
# ./network.nix
|
||||||
# ./monitoring.nix
|
# ./monitoring.nix
|
||||||
|
@ -43,11 +75,13 @@ in {
|
||||||
rootPasswordFile = config.sops.secrets.passwords-root.path;
|
rootPasswordFile = config.sops.secrets.passwords-root.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
sops.secrets.passwords-root = {
|
sops.defaultSopsFile = ../../../../secrets/${nodeName}/secrets.yaml;
|
||||||
sopsFile = ../../../../secrets/${nodeName}/secrets.yaml;
|
sops.defaultSopsFormat = "yaml";
|
||||||
neededForUsers = true;
|
|
||||||
format = "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.
|
# Use the nftables firewall instead of the base nixos scripted rules.
|
||||||
# This flake provides a similar utility to the base nixos scripting.
|
# This flake provides a similar utility to the base nixos scripting.
|
||||||
# https://github.com/thelegy/nixos-nftables-firewall/tree/main
|
# 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 = {
|
nftables = {
|
||||||
enable = true;
|
enable = true;
|
||||||
stopRuleset = "";
|
stopRuleset = "";
|
||||||
|
@ -108,26 +144,69 @@ in {
|
||||||
enable = true;
|
enable = true;
|
||||||
zones = {
|
zones = {
|
||||||
lan.interfaces = ["br-lan"];
|
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"];
|
wan.interfaces = ["wan" "lan0"];
|
||||||
};
|
};
|
||||||
rules = {
|
rules = let
|
||||||
lan = {
|
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"];
|
from = ["lan"];
|
||||||
to = ["fw"];
|
to = ["fw"];
|
||||||
verdict = "accept";
|
verdict = "accept";
|
||||||
};
|
};
|
||||||
outbound = {
|
|
||||||
from = ["lan"];
|
lan-to-wan = {
|
||||||
to = ["lan" "wan"];
|
|
||||||
verdict = "accept";
|
|
||||||
};
|
|
||||||
nat = {
|
|
||||||
from = ["lan"];
|
from = ["lan"];
|
||||||
to = ["wan"];
|
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"];
|
from = ["wan"];
|
||||||
to = ["fw"];
|
to = ["fw"];
|
||||||
allowedTCPPortRanges = [
|
allowedTCPPortRanges = [
|
||||||
|
@ -136,7 +215,9 @@ in {
|
||||||
to = 22;
|
to = 22;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
verdict = "drop";
|
extraLines = allowIcmpLines ++ [
|
||||||
|
"drop"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -157,50 +238,12 @@ in {
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
[Bridge]
|
[Bridge]
|
||||||
STP=true
|
STP=true
|
||||||
VLANFiltering=yes
|
# VLANFiltering=yes
|
||||||
DefaultPVID=none
|
# DefaultPVID=1
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
networks = {
|
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
|
# use lan0 as secondary WAN interface
|
||||||
"10-lan0-wan" = {
|
"10-lan0-wan" = {
|
||||||
matchConfig.Name = "lan0";
|
matchConfig.Name = "lan0";
|
||||||
|
@ -232,15 +275,88 @@ in {
|
||||||
# make routing on this interface a dependency for network-online.target
|
# make routing on this interface a dependency for network-online.target
|
||||||
linkConfig.RequiredForOnline = "routable";
|
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
|
# wireless access point
|
||||||
services.hostapd = {
|
services.hostapd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = nodeFlake.packages.hostapd_main;
|
||||||
radios = let
|
radios = let
|
||||||
mkBssid = i: # generated with https://miniwebtool.com/mac-address-generator/
|
# generated with https://miniwebtool.com/mac-address-generator/
|
||||||
"34:56:ce:0f:ed:4${builtins.toString i}";
|
mkBssid = i: "34:56:ce:0f:ed:4${toString i}";
|
||||||
in {
|
in {
|
||||||
wlan0 = {
|
wlan0 = {
|
||||||
band = "2g";
|
band = "2g";
|
||||||
|
@ -254,64 +370,68 @@ in {
|
||||||
};
|
};
|
||||||
networks = {
|
networks = {
|
||||||
wlan0 = {
|
wlan0 = {
|
||||||
ssid = "justtestingwifi-wpa3";
|
ssid = "mlsia";
|
||||||
authentication = {
|
|
||||||
mode = "wpa3-sae";
|
|
||||||
# saePasswordsFile = config.sops.secrets.wifiPassword.path;
|
|
||||||
saePasswords = [
|
|
||||||
{
|
|
||||||
password = "normalnormal";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
password = "vlanvlan";
|
|
||||||
vlanid = 1;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
bssid = mkBssid 0;
|
bssid = mkBssid 0;
|
||||||
settings = {
|
|
||||||
bridge = "br-lan";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
wlan0-1 = {
|
# manually configure something close to wpa3-sae-transition
|
||||||
ssid = "justtestingwifi-compat";
|
authentication.mode = "none";
|
||||||
authentication = {
|
# authentication.saePasswordsFile = config.sops.secrets.wlan0_saePasswordsFile.path;
|
||||||
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
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
bssid = mkBssid 1;
|
|
||||||
settings = {
|
settings = {
|
||||||
bridge = "br-lan";
|
# bridge = "br-lan";
|
||||||
|
|
||||||
|
logger_stdout_level= lib.mkForce 1;
|
||||||
|
logger_syslog_level= lib.mkForce 1;
|
||||||
|
|
||||||
# resources on vlan tagging
|
# resources on vlan tagging
|
||||||
# https://wireless.wiki.kernel.org/en/users/Documentation/hostapd#dynamic_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
|
# 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_tagged_interface = "br-lan";
|
||||||
vlan_bridge = "br-vlan";
|
vlan_naming = 1;
|
||||||
|
vlan_bridge = "br-vlan.";
|
||||||
dynamic_vlan = 1;
|
dynamic_vlan = 1;
|
||||||
vlan_file = builtins.toString (pkgs.writeText "hostapd.vlan" ''
|
vlan_file = toString (pkgs.writeText "hostapd.vlan" ''
|
||||||
* wlan0-1.#
|
* 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
|
# Uncomment when needed otherwise remove
|
||||||
# wlan0-1 = {
|
# wlan0-1 = {
|
||||||
# ssid = "koteczkowo3";
|
# ssid = "koteczkowo3";
|
||||||
|
@ -423,94 +543,75 @@ in {
|
||||||
|
|
||||||
services.resolved.enable = false;
|
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;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
# upstream DNS servers
|
|
||||||
server = ["9.9.9.9" "8.8.8.8" "1.1.1.1"];
|
|
||||||
# sensible behaviours
|
# sensible behaviours
|
||||||
domain-needed = true;
|
domain-needed = true;
|
||||||
bogus-priv = true;
|
bogus-priv = true;
|
||||||
no-resolv = true;
|
no-resolv = true;
|
||||||
|
|
||||||
dhcp-range = [
|
dhcp-range = let
|
||||||
# "br-lan,192.168.10.50,192.168.10.100,24h"
|
mkDhcpRange = { tag, vlanid }: builtins.concatStringsSep "," [
|
||||||
"192.168.10.50,192.168.10.100,24h"
|
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";
|
# interface = "br-lan";
|
||||||
# bind-interfaces = true;
|
# bind-interfaces = true;
|
||||||
|
|
||||||
# dhcp-host = "192.168.10.1";
|
# dhcp-host = "192.168.10.1";
|
||||||
|
|
||||||
# local domains
|
# local domains
|
||||||
local = "/lan/";
|
# local = "/${getVlanDomain {vlanid = 0;}/";
|
||||||
domain = "lan";
|
# domain = getVlanDomain {vlanid = 0;};
|
||||||
expand-hosts = true;
|
expand-hosts = true;
|
||||||
|
|
||||||
# don't use /etc/hosts as this would advertise ${nodeName} as localhost
|
# don't use /etc/hosts as this would advertise ${nodeName} as localhost
|
||||||
no-hosts = true;
|
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.
|
# 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;
|
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";
|
system.stateVersion = "23.05";
|
||||||
|
|
||||||
boot.kernelPackages = pkgs.linuxPackages_bpir3;
|
boot.kernelPackages = pkgs.linuxPackages_bpir3;
|
||||||
|
|
|
@ -9,6 +9,7 @@ in {
|
||||||
meta.nodeSpecialArgs.${nodeName} = {
|
meta.nodeSpecialArgs.${nodeName} = {
|
||||||
inherit repoFlake nodeName nodeFlake system;
|
inherit repoFlake nodeName nodeFlake system;
|
||||||
packages' = repoFlake.packages.${system};
|
packages' = repoFlake.packages.${system};
|
||||||
|
nodePackages' = nodeFlake.packages.${system};
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
(nodeFlake.inputs.bpir3.packages.${system})
|
(nodeFlake.inputs.bpir3.packages.${system})
|
||||||
|
|
62
nix/os/devices/router0-dmz0/flake.lock
generated
62
nix/os/devices/router0-dmz0/flake.lock
generated
|
@ -7,10 +7,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dirtyRev": "4210480bdebbf3a7953e22d5d9f183f47b725bff-dirty",
|
"lastModified": 1703182100,
|
||||||
"dirtyShortRev": "4210480-dirty",
|
"narHash": "sha256-zl2G9ex86b8G6J9+QT4n9g26G8dtandIt1LlFhZiaxE=",
|
||||||
"lastModified": 1688620001,
|
"ref": "refs/heads/linux-6.6",
|
||||||
"narHash": "sha256-INxwGchokdU3ESpnvmfkMWZhocM134FmhWQoyPqtg60=",
|
"rev": "953a04e6792c412a664212db6a64bbaaa35bff0a",
|
||||||
|
"revCount": 31,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "file:///home/steveej/src/steveej/nixos-bpir3"
|
"url": "file:///home/steveej/src/steveej/nixos-bpir3"
|
||||||
},
|
},
|
||||||
|
@ -47,11 +48,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702569759,
|
"lastModified": 1703162528,
|
||||||
"narHash": "sha256-Ze3AdEEsVZBRJ4wn13EZpV1Uubkzi59TkC4j2G9xoFI=",
|
"narHash": "sha256-pQ41wN6JlStkZOhRTIHEpuwVywLdh+xzZQW1+FzdjVs=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "98ab91109716871f50ea8cb0e0ac7cc1e1e14714",
|
"rev": "a050895e4eb06e0738680021a701ea05dc8dbfc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -82,11 +83,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702814335,
|
"lastModified": 1703368619,
|
||||||
"narHash": "sha256-Qck7BAMi3eydzT1WFOzp/SgECetyPpOn1dLgmxH2ebQ=",
|
"narHash": "sha256-ZGPMYL7FMA6enhuwby961bBANmoFX14EA86m2/Jw5Jo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "e4dba0bd01956170667458be7b45f68170a63651",
|
"rev": "a2523ea0343b056ba240abbac90ab5f116a7aa7b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -96,6 +97,22 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hostapd": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703346062,
|
||||||
|
"narHash": "sha256-SHSBKIgKc5zEGhKDT2v+yGERTJHf8pe+9ZPUwJBTJKQ=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "196d6c83b9cb7d298fdc92684dc37115348b159e",
|
||||||
|
"revCount": 19119,
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://w1.fi/hostap.git?branch=main"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://w1.fi/hostap.git?branch=main"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixos-nftables-firewall": {
|
"nixos-nftables-firewall": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"dependencyDagOfSubmodule": "dependencyDagOfSubmodule",
|
"dependencyDagOfSubmodule": "dependencyDagOfSubmodule",
|
||||||
|
@ -104,11 +121,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702744409,
|
"lastModified": 1703279052,
|
||||||
"narHash": "sha256-dcDkc+6TF9EvfWpsLdmGz4hhrNVbQZDgFwvk5SOjYTI=",
|
"narHash": "sha256-0rbG/9SwaWtXT7ZuifMq+7wvfxDpZrjr0zdMcM4KK+E=",
|
||||||
"owner": "thelegy",
|
"owner": "thelegy",
|
||||||
"repo": "nixos-nftables-firewall",
|
"repo": "nixos-nftables-firewall",
|
||||||
"rev": "a33df9d2f586b85e8e7e546d9b99b39f3187c382",
|
"rev": "3bf23aeb346e772d157816e6b72a742a6c97db80",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -119,11 +136,11 @@
|
||||||
},
|
},
|
||||||
"nixos-stable": {
|
"nixos-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702346276,
|
"lastModified": 1702921762,
|
||||||
"narHash": "sha256-eAQgwIWApFQ40ipeOjVSoK4TEHVd6nbSd9fApiHIw5A=",
|
"narHash": "sha256-O/rP7gulApQAB47u6szEd8Pn8Biw0d84j5iuP2tcxzY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "cf28ee258fd5f9a52de6b9865cdb93a1f96d09b7",
|
"rev": "d02ffbbe834b5599fc5f134e644e49397eb07188",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -135,11 +152,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702312524,
|
"lastModified": 1703255338,
|
||||||
"narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=",
|
"narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a9bf124c46ef298113270b1f84a164865987a91c",
|
"rev": "6df37dc6a77654682fe9f071c62b4242b5342e04",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -155,6 +172,7 @@
|
||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
"get-flake": "get-flake",
|
"get-flake": "get-flake",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
|
"hostapd": "hostapd",
|
||||||
"nixos-nftables-firewall": "nixos-nftables-firewall",
|
"nixos-nftables-firewall": "nixos-nftables-firewall",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"srvos": "srvos"
|
"srvos": "srvos"
|
||||||
|
@ -168,11 +186,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702518612,
|
"lastModified": 1703258052,
|
||||||
"narHash": "sha256-AGqIpvEMqo0FKXslmKL8ydt01pJFs8q3nUtz7gksoig=",
|
"narHash": "sha256-gWGQxht/xRJRnA+35aHtpmev7snsM+2GBdaPyarXNqU=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "srvos",
|
"repo": "srvos",
|
||||||
"rev": "cd802e2933c567ea91de48dbe8968f41a5d9a642",
|
"rev": "0c7eefd13776730f33ea28fb984dd95cb5357e8e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -19,9 +19,10 @@
|
||||||
|
|
||||||
nixos-nftables-firewall.url = "github:thelegy/nixos-nftables-firewall";
|
nixos-nftables-firewall.url = "github:thelegy/nixos-nftables-firewall";
|
||||||
nixos-nftables-firewall.inputs.nixpkgs.follows = "nixpkgs";
|
nixos-nftables-firewall.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
|
||||||
|
|
||||||
# outputs = _: {};
|
hostapd.url = "git://w1.fi/hostap.git?branch=main";
|
||||||
|
hostapd.flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
self,
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
} @ attrs: let
|
} @ attrs: let
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
nodeName = "router0-dmz0";
|
nodeName = "router0-dmz0";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
mkNixosConfiguration = {extraModules ? [], ...} @ attrs:
|
mkNixosConfiguration = {extraModules ? [], ...} @ attrs:
|
||||||
nixpkgs.lib.nixosSystem (
|
nixpkgs.lib.nixosSystem (
|
||||||
|
@ -67,6 +69,7 @@
|
||||||
linuxPackages_bpir3
|
linuxPackages_bpir3
|
||||||
;
|
;
|
||||||
})
|
})
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -88,5 +91,12 @@
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
packages = {
|
||||||
|
hostapd_main = pkgs.hostapd.overrideDerivation(attrs: {
|
||||||
|
src = self.inputs.hostapd;
|
||||||
|
version = self.inputs.hostapd.rev;
|
||||||
|
});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ ssh_host_ed25519_key: ENC[AES256_GCM,data:XQjTqNADLhisxPBIJ7x0bs3qgQk0u4q9HKSDuk
|
||||||
ssh_host_ed25519_key_pub: ENC[AES256_GCM,data:MQ0q/I6clKNz6uzoztGA06vOjIbpK6Dsf3WbgddRA0B8nEJ4EUmRBT0KkX3o+LZmQPhmURHWWFtOSqvAzkyoxAoBZEh98H3IDsLE5PgcNbxK3dAh36+AAMPLzVFnHLyaWLQW,iv:9XIw29PkSHCeU7C2GuSJ+J+mBrwOrbSMmm7kOtCkiyI=,tag:x3JqFF08f2eVfOrrQ1gzYw==,type:str]
|
ssh_host_ed25519_key_pub: ENC[AES256_GCM,data:MQ0q/I6clKNz6uzoztGA06vOjIbpK6Dsf3WbgddRA0B8nEJ4EUmRBT0KkX3o+LZmQPhmURHWWFtOSqvAzkyoxAoBZEh98H3IDsLE5PgcNbxK3dAh36+AAMPLzVFnHLyaWLQW,iv:9XIw29PkSHCeU7C2GuSJ+J+mBrwOrbSMmm7kOtCkiyI=,tag:x3JqFF08f2eVfOrrQ1gzYw==,type:str]
|
||||||
ssh_host_rsa_key: ENC[AES256_GCM,data:tFGQ77X5Y1TRR2F0EJ4hmauE9ABILP6V0CSmzb1QLaH6VlhriXSE1UQcQ2Rc73CR7+JLjLbggL7RKpkA8gJQq/ubhiXHJokBEo6rfBXETVepv0HlyX5UvzWhi6iKBE5YsYyyBI1VWDcx+oTR8+daqnKwbbqPeDUpC2coT3S9svEsKXeb3YOMxJ9X/Rvh96UtMmlk7WeZa2JvOP3k62HROVo8QRYXeQWTO87TCzVdU7OWnbRIuC6bu+32Uy70AsIu39fazX7WqIUxaeO/oNHsay0/TBXKNu2El0JRG8tHCHdXe1tahniGbGH5xeEZmgLTOjHntw5UIdxZbgvcbxmt9seDXUxjhhEMS8eHWaxnRDSI7n2KOb/3UaBQ3BHnYpuRjW++uFBgRHxxANlYfpfEdz/LNexdPb9+QCw80r38uZxP8yD5/PxFV/Y+gSX+WnNE0YQnBDtHFjKhHpqpm2P4Ek3hLzjXh6CPzUZru6LAO/FklcLCGaq94fDC5wW3K0x1UvyfMjBwwyeSymcV95YcZu8Ty280jRhG8l719KkEJjnBdnB25PQ5gEwc34dG5xH5HwVUDPIM9v9m+cXtldUIk3BH4PiTEI1aZsZx50BtBhxybAxhTqNElrP+/2W73muTSQboDIB1Xb2NhArLB6XnnVhVUD/Pg/9wiDUY0mYyr0eIp9AmBfSmSIDjFyVUB9o+gv/B+LpxwxPKmsPt3MRKWoZw+bIGTI82UqBv0eX6Xqq71H4DYFnJ3J+n+Jzp5ww5mSPPHffZZFSBbZSpTk8El4L5II/hyyxk7yJopt19AAsw+UgLJDO31XnyiGAEbbDdwjuCWQMmuzKJ6H7c7UGDXLO92jAlXwEWT5fwzG6Wvu5+n/OToz3CN1Cv40uwVb/fOqmzep9hoOrXeuzmnGULfGmwItjuJoMkfmPUq8obAuj5ml0YduU2eLbe15OlD2Vg2I+BH0WuCPYnnCrLyX8ixhJEuGGDkhGhaKMHaMjNuMOGmDQ6oRCVU8BA/zDgnF/GrcHgIe742Yh68PyaH2j+iX6/5YvUB+R1sDnrgfXlgET1V+nny+fD4GBnP+RKYtdGG0P4y3AYlACQE9sJ6Nkb8CTVb2Va6L3rXzeyJG2FtYkUKxxwDUv/KyieclHiJpdawunOLVkmI6p/iaZIThrdGA5p7b01/WOyJFA9aI3oU2f2rMYBLGHYirrRs3WtS7ExhKriHpgax574UIcW0mecFto9dHkGlBOTQy+Zp1GARnePXKZcyKm2qhTIjw0ho/DUe3+t1knbR6WJCwl1LDtfpPD2KPDDH+HpHm3EoiA1mDSp6lYMVxwBr2eBmFPKuPkkAL/3Bf6SKlEp1UCDee7BPlzS9kwmIEt/EuIohbMDdyaHtulW31Hdrsai8fCxc7AAzgsmoMBp2Smwoe3C8K5K8RaNp6v4boY8WBH3rLjAFTmOPB7TiZfplQjV8triZS3JFopNjvCglfZSXxSs+RjZUgSgL/1fFLT2m0Mp1XPMvGZlD73TzJ5RH5WWlxliaau42Q4vXRFUK+ZFx0SvwOO5xZvRNtAOfipEoqscKOopV+HHl74DJEk/xLibWJmkEBqoVbf1BFDUAiRSSb/0RdfCAGaj1mqV68szVtLt3jB1AJm9iu9RNU047HHQeOi++8g6lOpHmhsksfQLAucLVtLTrXpHDEAugDbSXrwPkhWa2Ej+Iva1p2vteIExcT+8h3WiXkamDBZALpJcLkDSazgAmmrB+6u6odsyin9Z5zB55EN2iz24nGNgyylt9FehC4/2SNHMX42hZ/JPQw51+vZQLoLQv+oOJAVXGBHeDy3kDl60fL6Fr5jZ0YOfO+rFGk4bDpXYjq27ahLv763tVs8SrgMseNWNWTakZUAXuYPbP3GBFEjTPHM4216WABj2cC3zSmrnbEyNRMWTdsm46ASZvhZo4wO8eTrndvy44Q2UKOFOh1sYCY4jjCWCI74pEV3rRcBJASuUipep65ZwXOlFOXu7uiaG01/KWofn4JzzrlX3MfhKsUBaEDTUXwDGw0RAEHQXb3rvfiIjCQBcpy8kM1fBR97K5LFJzZ2/qf2bPGs0yxma1O0Z6TT/2Uk2n62jK2xIM7gvTjPOVDP2etHKVxMpMjhTAbRj4K3568HZM/POBC5AORLAtBAo14CNxRMN8f05Gs13wn9ZI+pqiaOpTTnfH1xL9fd/6I03utzMKnoR8IVhrQLDLT6OAIkdeJX8s99R/J/nTOApk3XACWqjmMTcWtwt6g3x2iTk/1jTTn70rYVr8JLHHCt+bd5H/eDUiq9HpG1oFEZPXuvgATOovru0YVtmVx+yea0jWpJOsK+/SZjYAfvAKh0ZNr8dKHug/gGEpp6SfFBI+c4ywR1kM83OUHLaI/dOU9rLeCKktB+UcvTPoLhHLbTAlTrizeRmYY16Z1a+47LvwX944js3TZZkxqylz73cekWidYiiLNbiHwACftOK/GwZCG0WrVfcSi6pJYDgPgbcqFohaKI7ltKZgTlHGT1mZr1IH1RnauZmN/MkBEKVHO3E/PFgKkWNcXQi4uV2P3j8aHHc0RJrxx7qAFFXCYEwu02pv9nmul/HClLMmDYHDY1lHZvIIyPcsmr1ZkCFRV4UtnZRtXhHNAZCGFRX+y5HQXnubjPmV5I2R/gcLLi//2vIE6V2i8SpEJZlVweLpjRYwb6H6xFTuvHN+9Y5pd3rFx2BapR0AzYdOXUVqKF5ewrfE/1iitsEeDJj8OqmNzpmLBrnROnPyPh/+KGWBG5Nm4QKVMhP7XN1F+Fr7sTxw1ignXsfSwH8v/ELMzxVLu3ijWxvmk3/eOsrKYB/x3h6I1SFWZUoTjVPAmlNAnYl90Xf+FMPiII11+zrYwsJbCh25gmSvtKR+hmoTxXbJ2w5E2cFoVHMBOmS8Uo8L0BDOUzUE64cPl/v119BafVUOohLF1l3Ob19td+sMvqX8OHLGgB+/r6jKmUPnNNEzbDAogMydcVVjtPRIoDScw4ExxuxILN4/V+VulyAgU29OvFWS6+r5Y+bjqgqMg55Of0le3HUqt65qMuqiaUR5P/9WP+H666GsYTSg5I5gPJv0FF6tqXCWE9mpQ4sk2/BiWWaNeojxyhyH/cv4fbUmcee6K3+P+liXyFGGAWdBP5uGb+BzIfSaXVN3xEBPWAAe+BMkJrxbc6FSS12Wkh+bLM+NJH7XaoDl6+8LtF3bstsCoXvQgNlXHlbt4/aIIFEBShlYSQwLMDcey1VB4pu4SBv2HXwaX9zXPd+MGH77DeQU4yBkrElly51/68yzdSGpe6mNFy57Hc3UJBW1pNbds5Gxu+jFbQ6Phxvn38u9V8cphjxzgig0uZjkKHYx0EwFjBqmfrP0hQDnpcyg5QCPhnXOthmVL4jkJs3OrRHhoYRgbXpaVRMpVMLFeeSqcQABaRK5ibpB81WP8yCJPIMsbWW7sDTCiIyLq6qW5pKNq8/l3sq042+6bJgJ3CfDYdNKJCTF/09F5JKkpXeIxL4MGqvg5nOoyiahDQsUzMT3dwGg7IWqxQidcJ576XSBkR2qNwUrl6qq87JP+Zo3RJbA5bpPubm6sUonWE3hRGg4LWceVBO34J/wutWVt4W7dXnK5WVhJv8UHJcbgWR9dMpWkbeMpakqlRRLOTibztMFkx3xyIMmZS+cNyhRyatw+DwX/opuY+bl8X4yKgNOuW/w6r06r4MrL78MTjqZDQ4xkCSL3x3KWr2Tf4lAX6sdwKTzdBdzvFuLeuijngrvRYRyk/3jk/o4ujfHMdrergMjlP5Js7ICZSLhXHHOc2Hc5oPaBIx59r6FynA+W6ROmk5kVF2BNkRlP6/oV5Apn3MMUnDc4YjHaowt8UVkIHZOEL8hxymYDR4g9y1wOoIwKCorBQa5jsXH+AEop3hlsv4uqzrrcGGQhfQEW0Vb1fG4N0VAyWEodaw7wOY3XCrW7yHjIgRM3Is+juT7jMeACW+OQuvQHTS/9bc9n9sXjjVwsvoHROLxsXMFO9HablXaEKzFL1oGXpnavYf/MuZMwALsPish2Mmj9fONNMBo1yYy/j+8GzHCqByDS1ZPnlzPQD0ztGNwNfOOhNOqamqaE9GNHv92yQIf/KKGhnjFH/I9IlzA28eaaSVql/1vdhRAI2G1WxpgyQ1ryRPLYHA/Qw9OYrb+jIxHj9uqfohShgIqnv6TCXRmByfyU6Te3oqKLyezKj7tPCqv1la1ostycmE4msAs4EI7pe1OZcRulPkUJrZCPkvo+EYTw8AfGmwHFb5foQ4wk8pkjTvo1zHmjy0GjMAZpcmDHfyHAyoaED6DUKAHRBbMdSqQJWmzhHkzn5oRCR6UlWSyxRZ1wBIKn+T+kcn28XiLlJrJVK3n2CQkE1c3EfFemnimo0Yc9yNQZygfZjr2W2TnmtAZ5jHiMmv7E8CPxBQBd/pf29z/uAEwQIqVFSHxkVaVjHW5wlhfWwOuj0xZFly,iv:mXE8xpXFBYSJce9pg+g3OedMS9+ZHOHHwydCY0NbGRQ=,tag:cEqbUu9Y1PFKXwaeqioXWA==,type:str]
|
ssh_host_rsa_key: ENC[AES256_GCM,data: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,iv:mXE8xpXFBYSJce9pg+g3OedMS9+ZHOHHwydCY0NbGRQ=,tag:cEqbUu9Y1PFKXwaeqioXWA==,type:str]
|
||||||
ssh_host_rsa_key_pub: ENC[AES256_GCM,data:N60bGf/6KNRhVUq1EIbPVo3aBDDKEpMBr5+Gt3+FMPt3uQEaKk8jBg5mOdxWMTPoLg1ZP/Pme8afoM+Skc0b50WnpErF3Ox1w+4eM0oMJYOhIvHLGURNM3Dba5MgA7YfhPdTsVdjD2yks2vYqhdtEvzTTgCJbFimVJlp+wDqE6czPgMjD03c7oJDtv38OBtc1vRMzVw3cIuyxz2yNnXQxiMgTR6pZN7+Brami2dfXOHEVgymmlU5PRE8Ykerq2fB36N5uqu4/xSPaHaM+/f2OA/TLlYYB+sGMDExZfbO/vsiRBLvTY/f4KG2mEkmH+IFH1bk6UF47xTFEe8tHN/TlLo+9OmjZTph221ZYnOsIqBY+F822ctZEe8Ikz9Ti4F1ApvxxRcWHajbgQnDJdDiHJvt3OHal4rNBtYwxxV/MDZtvKSVxmFwgx7nwNP0oKhAigQkU7Mvp1q5p3dZsdbGCUeFm2S5/qIxWPfr7wg4xocLNSsLW1EpGo6A2RUXWIV+lPuZd9dNEjGC5zKKAgMI94is6MtMXgqlFqTcZuQ9hvhoVDcFhVSJylu8pzk9d/tKviwcd98jHAhdfGpnc9eJbtyBU6/HvxLzQpsbFjwa3LGirEdtgxRZn2nJx++0U6XuLcbGwjOVAhkde6g2vFv5hsC6KaZQcp4AFvMvEdJyrnb0b2TOeOD8zEljb8u2q/eexCRSjGpobEINwu5qV+tF9eHIJ1YFzhCSmmLGKXjc7bC8uv5ffl39JmAbUrffd18zqae+Xpijd+QzwF425NG9+PksAt+PPzt4SDgGfKBIpMNFxIb18oo88z4YDLuNzRy/HVF90JV0LlAxES4ZOxoWUjJPrR6dGxNRANYOyFGmoN+yG3B9kd1NRGRNGh5P9EtZBxlPIi24djzF1n4GQSW1NFDgoGcxaXhk0PlpPxwuHK0X9FkFDDzQUYNBhx7py+hev5rBUCs7Yhj5xgcM88fdLRZi8MulNws=,iv:8c3hDcJ8wzTugmJ3Mhzx/qEXnnlpFefBmRTG/MqyeEg=,tag:uSz6+CYu9uQa0C2DXnHPUA==,type:str]
|
ssh_host_rsa_key_pub: ENC[AES256_GCM,data:N60bGf/6KNRhVUq1EIbPVo3aBDDKEpMBr5+Gt3+FMPt3uQEaKk8jBg5mOdxWMTPoLg1ZP/Pme8afoM+Skc0b50WnpErF3Ox1w+4eM0oMJYOhIvHLGURNM3Dba5MgA7YfhPdTsVdjD2yks2vYqhdtEvzTTgCJbFimVJlp+wDqE6czPgMjD03c7oJDtv38OBtc1vRMzVw3cIuyxz2yNnXQxiMgTR6pZN7+Brami2dfXOHEVgymmlU5PRE8Ykerq2fB36N5uqu4/xSPaHaM+/f2OA/TLlYYB+sGMDExZfbO/vsiRBLvTY/f4KG2mEkmH+IFH1bk6UF47xTFEe8tHN/TlLo+9OmjZTph221ZYnOsIqBY+F822ctZEe8Ikz9Ti4F1ApvxxRcWHajbgQnDJdDiHJvt3OHal4rNBtYwxxV/MDZtvKSVxmFwgx7nwNP0oKhAigQkU7Mvp1q5p3dZsdbGCUeFm2S5/qIxWPfr7wg4xocLNSsLW1EpGo6A2RUXWIV+lPuZd9dNEjGC5zKKAgMI94is6MtMXgqlFqTcZuQ9hvhoVDcFhVSJylu8pzk9d/tKviwcd98jHAhdfGpnc9eJbtyBU6/HvxLzQpsbFjwa3LGirEdtgxRZn2nJx++0U6XuLcbGwjOVAhkde6g2vFv5hsC6KaZQcp4AFvMvEdJyrnb0b2TOeOD8zEljb8u2q/eexCRSjGpobEINwu5qV+tF9eHIJ1YFzhCSmmLGKXjc7bC8uv5ffl39JmAbUrffd18zqae+Xpijd+QzwF425NG9+PksAt+PPzt4SDgGfKBIpMNFxIb18oo88z4YDLuNzRy/HVF90JV0LlAxES4ZOxoWUjJPrR6dGxNRANYOyFGmoN+yG3B9kd1NRGRNGh5P9EtZBxlPIi24djzF1n4GQSW1NFDgoGcxaXhk0PlpPxwuHK0X9FkFDDzQUYNBhx7py+hev5rBUCs7Yhj5xgcM88fdLRZi8MulNws=,iv:8c3hDcJ8wzTugmJ3Mhzx/qEXnnlpFefBmRTG/MqyeEg=,tag:uSz6+CYu9uQa0C2DXnHPUA==,type:str]
|
||||||
|
wlan0_saePasswordsFile: ENC[AES256_GCM,data:Lq81rCQNUmrZi047UxvFI+Sg6YfBzPaTkK23FTwyfEtMIgHlAtY7lrW7lqR1iDEafOlK0uX8dUkQXRZzjuIbhP3UP/WYUtZ/RxoQdoQI9HAFXBG8g3RgD3OsW/3RY6VLkNFD5p3PST8wGpO1iXZzjJC8UH9WeAg3CcKdtPK+,iv:5rcrcVsbYaDhUTseIIDIC6oFEonCFkx9kYsA5DhMu8I=,tag:QQ3jX6kPiAd9IJfS7TkuvA==,type:str]
|
||||||
|
wlan0_wpaPskFile: ENC[AES256_GCM,data:rgPROqPL6wd4RacWU1loCG6v0h4Rh30sWdVuDKu72byzh1wyufP5+hNdJkw4zb69IdVmQGRekt9HGX6sQ4DlZz2MrDTYPH1dj+IXpJEHxCPM07a2rwCm+X9mgEkOkr2NURXVVaf9H7EpVnyXYvcNYmAVn9ZwC7rbYS3Xg7Y5OzMOQ7zabtw/8C1EZiJ9hZAzN8jyE1Gu9bgWSaRqGjLI08zbh6UqiNiTTcZhH5Wr2Z3sIfqlr6QJoQp+rAD2yQm6qefOJZWM+1ZlPtv1VCW8AN6oGKiVVdlK,iv:jEnkYarguNECjO2cTjSSgshJMszCuRAjqLQpDzXyTxs=,tag:Rx1wR3L3LCErHMud9goY5g==,type:str]
|
||||||
sops:
|
sops:
|
||||||
kms: []
|
kms: []
|
||||||
gcp_kms: []
|
gcp_kms: []
|
||||||
|
@ -19,8 +21,8 @@ sops:
|
||||||
THRNR0tEUzhPdFFhWWxvZlpKYmZKM2MKxc5s1jsci8jPOrvZAoofVNvHT4o9P6yv
|
THRNR0tEUzhPdFFhWWxvZlpKYmZKM2MKxc5s1jsci8jPOrvZAoofVNvHT4o9P6yv
|
||||||
J8rALQQXgql6obK51Q/Doyzvo1RJ0T7epiWEAZm5B3vDrf6KqbWBYw==
|
J8rALQQXgql6obK51Q/Doyzvo1RJ0T7epiWEAZm5B3vDrf6KqbWBYw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2023-08-11T16:46:38Z"
|
lastmodified: "2023-12-24T18:56:12Z"
|
||||||
mac: ENC[AES256_GCM,data:W9aRsPPRKro6rGbNvBV8bftPklQn6LN6Lq+G45vYTVRZs5t0F1qFqUpXDXKTrZ040mkYnECi7JSRWeJvyfGqHK5KPY1uWtBxDoghYfO/J7VXBNv+NbROO4KoAKYAoOpZSECVqXgm6U69G1GGu8yyrDPDFAcfbFXivXqH+e7t42A=,iv:uUndgDmUHBYCKvb2LHC9zRp+eBwcy6107ocaJFniV6o=,tag:VGKODnvz107hvEoCT0risw==,type:str]
|
mac: ENC[AES256_GCM,data:PF4gJL0u0vbU5o79IYWByWo4bZgZ7qWw/+mJN+YNzG4Jti+2D7gwKOs9edCHUuSti8iVov1RciretQHfkis5JDydykjb03uMaV8r6lVCmB8i/JARHEr5Amq0wld30qHaCtCayTTWQNA05jdzZB4a2dSIAqJh/R5YTaJ7HY+eZ38=,iv:3ZEFA4YR5BfcOlA+0Zevk7MaLJN0zPbfLOP2Ci/I3bg=,tag:vLhoKPTNIE7U2VcGiH964Q==,type:str]
|
||||||
pgp:
|
pgp:
|
||||||
- created_at: "2023-08-11T16:15:11Z"
|
- created_at: "2023-08-11T16:15:11Z"
|
||||||
enc: |-
|
enc: |-
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue