mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:36 +01:00
feat(shoukei): pin home Wi-Fi to a static IPv4 & adjust networking.nix (#407)
This commit is contained in:
1 parent
e9d706aa96
commit
835b7f6df1
5 files changed
+237
-70
No files matched your search
@@ -157,6 +157,12 @@ niri mode="switch" verbosity="normal":
|
||||
use {{utils_nu}} *;
|
||||
nixos-switch $"(hostname)-niri" {{mode}} {{verbosity}}
|
||||
|
||||
# Pin shoukei's home Wi-Fi to a static IPv4 (run once; the profile persists).
|
||||
[linux]
|
||||
[group('desktop')]
|
||||
shoukei-home-wifi:
|
||||
sudo nu {{absolute_path("scripts/shoukei-home-wifi-static.nu")}}
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# Darwin related commands
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
This is NixOS's configuration for my Macbook Pro 2022 M2, 16G RAM.
|
||||
|
||||
This laptop joins untrusted networks and uses DHCP instead of a static IP, so it has no entry in
|
||||
`vars/networking.nix`.
|
||||
This laptop joins untrusted networks and uses DHCP by default. On the home Wi-Fi
|
||||
(`shadow_light_ryan`) run `just shoukei-home-wifi` to pin `192.168.5.108`; the recipe calls
|
||||
[`scripts/shoukei-home-wifi-static.nu`](../../scripts/shoukei-home-wifi-static.nu), and
|
||||
`vars/networking.nix` records the address with `homeOnly = true`.
|
||||
|
||||
## x86/x86_64 applications
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
let
|
||||
# Exporters bind to this host's static LAN IPv4 (the same address
|
||||
# VictoriaMetrics scrapes), so the socket exists only on the LAN interface —
|
||||
# defence in depth behind the firewall allowlist. Hosts without a static LAN
|
||||
# address (laptops like shoukei, DHCP VMs like akane) run no exporters.
|
||||
lanAddr = myvars.networking.hostsAddr.${config.networking.hostName}.ipv4 or null;
|
||||
# defence in depth behind the firewall allowlist. Hosts without an always-on
|
||||
# static LAN address (the `homeOnly` laptop, DHCP VMs like akane) run no
|
||||
# exporters.
|
||||
hostAddr = myvars.networking.hostsAddr.${config.networking.hostName} or { };
|
||||
lanAddr = if hostAddr.homeOnly or false then null else hostAddr.ipv4 or null;
|
||||
in
|
||||
{
|
||||
# enable the node exporter on all nixos hosts
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env nu
|
||||
#
|
||||
# Pin shoukei's home Wi-Fi profile to a static IPv4.
|
||||
#
|
||||
# NetworkManager matches connections by SSID. Run this ONCE, while connected to
|
||||
# HOME_SSID: the script refuses to run unless the current Wi-Fi network is that
|
||||
# exact SSID, so it can never pin the wrong network by accident. Every other
|
||||
# network keeps using DHCP. The change is stored in the profile under
|
||||
# /etc/NetworkManager/system-connections (preserved by impermanence), so it
|
||||
# survives reboots and rebuilds -- no need to run it again.
|
||||
#
|
||||
# just shoukei-home-wifi
|
||||
#
|
||||
# See hosts/12kingdoms-shoukei/README.md and WORKAROUNDS.md WA-024/025.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# The home network: 2.4 and 5 GHz are merged into this single SSID, so it is
|
||||
# the only network this script may touch.
|
||||
const HOME_SSID = "shadow_light_ryan"
|
||||
|
||||
# The address to pin on that SSID.
|
||||
const STATIC = {
|
||||
address: "192.168.5.108/24"
|
||||
gateway: "192.168.5.1"
|
||||
dns: "192.168.5.1"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# nmcli helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Run nmcli; abort the script if it exits non-zero.
|
||||
def nmcli [args: list<string>] {
|
||||
let result = (^nmcli ...$args | complete)
|
||||
if $result.exit_code != 0 {
|
||||
error make { msg: $"nmcli ($args | str join ' ') failed: ($result.stderr | str trim)" }
|
||||
}
|
||||
$result.stdout | str trim
|
||||
}
|
||||
|
||||
# Names of the connections that are active right now.
|
||||
def active-connections [] {
|
||||
nmcli ["-t" "-f" "NAME,ACTIVE" "connection" "show"]
|
||||
| lines
|
||||
| where { |line| ($line | split row ":" | last) == "yes" }
|
||||
| each { |line| $line | split row ":" | first }
|
||||
}
|
||||
|
||||
# Every Wi-Fi connection as a { name, ssid } record.
|
||||
def wifi-profiles [] {
|
||||
nmcli ["-t" "-f" "NAME" "connection" "show"]
|
||||
| lines
|
||||
| where { |name| ($name | str trim) != "" }
|
||||
| each { |name|
|
||||
if (nmcli ["-g" "connection.type" "connection" "show" $name]) == "802-11-wireless" {
|
||||
{ name: $name, ssid: (nmcli ["-g" "802-11-wireless.ssid" "connection" "show" $name]) }
|
||||
}
|
||||
}
|
||||
| compact
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Guards
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def assert-root [] {
|
||||
if (^id -u | str trim | into int) != 0 {
|
||||
error make { msg: "must run as root -- nmcli edits system connections; try: just shoukei-home-wifi" }
|
||||
}
|
||||
}
|
||||
|
||||
# Abort unless Wi-Fi is connected to HOME_SSID right now.
|
||||
def assert-on-home [active: list<string>, wifi: list] {
|
||||
let connected = ($wifi | where { |p| $p.name in $active })
|
||||
let current = (if ($connected | is-empty) { [] } else { $connected | get ssid })
|
||||
if ($current | is-empty) {
|
||||
error make { msg: $"not connected to Wi-Fi -- connect to '($HOME_SSID)' first" }
|
||||
}
|
||||
if (not ($HOME_SSID in $current)) {
|
||||
error make { msg: $"not connected to '($HOME_SSID)'; currently on ($current | str join ', ')" }
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Profile operations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# The profile names that carry HOME_SSID.
|
||||
def home-profiles [wifi: list] {
|
||||
$wifi | where { |p| $p.ssid == $HOME_SSID } | get name
|
||||
}
|
||||
|
||||
def set-static-ip [profile: string] {
|
||||
nmcli [
|
||||
"connection" "modify" $profile
|
||||
"ipv4.method" "manual"
|
||||
"ipv4.addresses" $STATIC.address
|
||||
"ipv4.gateway" $STATIC.gateway
|
||||
"ipv4.dns" $STATIC.dns
|
||||
] | ignore
|
||||
}
|
||||
|
||||
# Apply the new settings without dropping the link (avoids the brcmfmac
|
||||
# re-auth/password re-prompt, WA-024/025); reconnect only as a fallback.
|
||||
def apply-live [profile: string] {
|
||||
let device = (nmcli ["-g" "GENERAL.DEVICES" "connection" "show" $profile])
|
||||
if ($device | is-empty) { return }
|
||||
|
||||
let result = (^nmcli device reapply $device | complete)
|
||||
if $result.exit_code == 0 {
|
||||
print $" applied on ($device) via reapply, no reconnect"
|
||||
} else {
|
||||
print $" reapply failed, reconnecting '($profile)'"
|
||||
nmcli ["connection" "up" $profile] | ignore
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main [] {
|
||||
assert-root
|
||||
|
||||
let active = (active-connections)
|
||||
let wifi = (wifi-profiles)
|
||||
assert-on-home $active $wifi
|
||||
|
||||
let profiles = (home-profiles $wifi)
|
||||
if ($profiles | is-empty) {
|
||||
error make { msg: $"no Wi-Fi profile for SSID '($HOME_SSID)'" }
|
||||
}
|
||||
|
||||
print $"pinning '($HOME_SSID)': ($STATIC.address) via ($STATIC.gateway), dns ($STATIC.dns)"
|
||||
for p in $profiles {
|
||||
print $" set '($p)'"
|
||||
set-static-ip $p
|
||||
}
|
||||
|
||||
for p in $profiles {
|
||||
if $p in $active { apply-live $p }
|
||||
}
|
||||
|
||||
print ""
|
||||
print "done. check: nmcli -f NAME,ipv4.method,ipv4.addresses connection show; ip -4 addr show wld0"
|
||||
}
|
||||
+73
-65
@@ -36,39 +36,19 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
# Static LAN addresses, sorted by IPv4 ascending -- keep it sorted when adding
|
||||
# a host so the next free address is obvious. `homeOnly = true` marks an
|
||||
# address that only exists at home (the laptop's home Wi-Fi static); ssh and
|
||||
# monitoring skip those entries.
|
||||
hostsAddr = {
|
||||
# ============================================
|
||||
# Homelab's Physical Machines (VM hosts)
|
||||
# ============================================
|
||||
shoryu = {
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.181";
|
||||
};
|
||||
shushou = {
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.182";
|
||||
};
|
||||
youko = {
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.183";
|
||||
};
|
||||
|
||||
# ============================================
|
||||
# Other VMs and Physical Machines
|
||||
# ============================================
|
||||
ai = {
|
||||
# Desktop PC
|
||||
iface = "enp130s0";
|
||||
ipv4 = "192.168.5.100";
|
||||
ipv6 = "fe80::10"; # Link-local Address
|
||||
};
|
||||
# akane = {
|
||||
# # VM (running in macOS's UTM App), using DHCP instead of static ip.
|
||||
# iface = "enp0s1";
|
||||
# ipv4 = "192.168.64.2";
|
||||
# };
|
||||
aquamarine = {
|
||||
# VM
|
||||
# VM (retired)
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.101";
|
||||
};
|
||||
@@ -102,40 +82,12 @@ rec {
|
||||
iface = "end1";
|
||||
ipv4 = "192.168.5.107";
|
||||
};
|
||||
rakushun = {
|
||||
# Orange Pi 5 - ARM
|
||||
# RJ45 port 1 - enP4p65s0
|
||||
# RJ45 port 2 - enP3p49s0
|
||||
iface = "enP4p65s0";
|
||||
ipv4 = "192.168.5.179";
|
||||
};
|
||||
suzi = {
|
||||
iface = "enp2s0"; # fake iface, it's not used by the host
|
||||
ipv4 = "192.168.5.178";
|
||||
ipv6 = "fe80::8"; # Link-local Address, can be used as default gateway
|
||||
};
|
||||
mitsuha = {
|
||||
iface = "enp2s0"; # fake iface, it's not used by the host
|
||||
ipv4 = "192.168.5.177";
|
||||
};
|
||||
|
||||
# ============================================
|
||||
# Kubernetes Clusters
|
||||
# ============================================
|
||||
k3s-test-1-master-1 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.114";
|
||||
};
|
||||
k3s-test-1-master-2 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.115";
|
||||
};
|
||||
k3s-test-1-master-3 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.116";
|
||||
shoukei = {
|
||||
# Laptop: pinned on the home Wi-Fi only, by `just shoukei-home-wifi`; off
|
||||
# the home network it uses DHCP.
|
||||
iface = "wld0";
|
||||
ipv4 = "192.168.5.108";
|
||||
homeOnly = true;
|
||||
};
|
||||
k3s-test-1-worker-1 = {
|
||||
# VM
|
||||
@@ -152,6 +104,57 @@ rec {
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.113";
|
||||
};
|
||||
k3s-test-1-master-1 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.114";
|
||||
};
|
||||
k3s-test-1-master-2 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.115";
|
||||
};
|
||||
k3s-test-1-master-3 = {
|
||||
# VM
|
||||
iface = "enp2s0";
|
||||
ipv4 = "192.168.5.116";
|
||||
};
|
||||
mitsuha = {
|
||||
iface = "enp2s0"; # fake iface, it's not used by the host
|
||||
ipv4 = "192.168.5.177";
|
||||
};
|
||||
suzi = {
|
||||
iface = "enp2s0"; # fake iface, it's not used by the host
|
||||
ipv4 = "192.168.5.178";
|
||||
ipv6 = "fe80::8"; # Link-local Address, can be used as default gateway
|
||||
};
|
||||
rakushun = {
|
||||
# Orange Pi 5 - ARM, now running Armbian (not NixOS)
|
||||
# RJ45 port 1 - enP4p65s0
|
||||
# RJ45 port 2 - enP3p49s0
|
||||
iface = "enP4p65s0";
|
||||
ipv4 = "192.168.5.179";
|
||||
};
|
||||
shoryu = {
|
||||
# Homelab's physical machine (VM host)
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.181";
|
||||
};
|
||||
shushou = {
|
||||
# Homelab's physical machine (VM host)
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.182";
|
||||
};
|
||||
youko = {
|
||||
# Homelab's physical machine (VM host)
|
||||
iface = "eno1";
|
||||
ipv4 = "192.168.5.183";
|
||||
};
|
||||
# akane = {
|
||||
# # VM (running in macOS's UTM App), using DHCP instead of static ip.
|
||||
# iface = "enp0s1";
|
||||
# ipv4 = "192.168.64.2";
|
||||
# };
|
||||
};
|
||||
|
||||
ssh = {
|
||||
@@ -164,15 +167,20 @@ rec {
|
||||
# IdentityFile — the location of your SSH key authentication file for the account.
|
||||
# Format in details:
|
||||
# https://www.ssh.com/academy/ssh/config
|
||||
# `homeOnly` entries get no alias: their address is not reachable off the
|
||||
# home LAN, so an alias would break e.g. `ssh shoukei` over Tailscale.
|
||||
extraConfig = (
|
||||
lib.attrsets.foldlAttrs (
|
||||
acc: host: val:
|
||||
acc
|
||||
+ ''
|
||||
Host ${host}
|
||||
HostName ${val.ipv4}
|
||||
Port 22
|
||||
''
|
||||
if val.homeOnly or false then
|
||||
acc
|
||||
else
|
||||
acc
|
||||
+ ''
|
||||
Host ${host}
|
||||
HostName ${val.ipv4}
|
||||
Port 22
|
||||
''
|
||||
) "" hostsAddr
|
||||
);
|
||||
|
||||
|
||||
Reference in new issue
Block a user