Skip to main content

Wi‑Fi, dual-homed networking, and SSH hardening

Ubuntu Server 24.04 on the MSI laptop — Ethernet carries public API traffic (static IP); Wi‑Fi is for admin SSH (office LAN / VPN path). This doc covers enabling Wi‑Fi on a headless server and locking SSH to the wireless interface at office cutover.

Target topology

                    ┌─────────────────────────────────────┐
Internet / API │ eth0 static public IP │
clients ─────────►│ Nginx :80 / :443 │
│ (UFW: allow 80, 443 on eth0) │
└─────────────────────────────────────┘

Admins (VPN / ┌─────────────────────────────────────┐
office Wi‑Fi) ───►│ wlan0 private / office network │
│ SSH :22 only on wlan0 │
│ (UFW: allow 22 on wlan0 only) │
└─────────────────────────────────────┘
InterfaceRoleSSHHTTP/S
enp58s0Production static IP (this server)NoYes (Nginx)
wlo1 (wlp0s20f3)Admin accessYesNo

Other hosts may use eth0, enp*, wlan0, wlp* — always run ip link first.


This server — interface names

From ip link on msi-laptop-3:

NameNotes
enp58s0Ethernet, UP
wlo1Wi‑Fi — primary netplan name
wlp0s20f3Same radio as wlo1 (altname) — use wlo1 in netplan

If Wi‑Fi shows state DOWN, it is not broken — it just has not been brought up yet.


Runbook — connect Wi‑Fi (repeat in server room)

Use this checklist whenever you join a new wireless network (home lab now, office later). Ethernet config stays in 50-cloud-init.yaml; Wi‑Fi lives in 02-wifi.yaml.

Netplan file strategy

FilePurposeEdit when
/etc/netplan/50-cloud-init.yamlEthernet (enp58s0)Static public IP in server room
/etc/netplan/02-wifi.yamlWi‑Fi only (wlo1)Every new SSID/password

Netplan merges all files in /etc/netplan/. Do not delete 50-cloud-init.yaml. Add or edit 02-wifi.yaml for Wi‑Fi.

Template: templates/netplan/02-wifi.yaml.template

Step 1 — One-time packages (skip if already installed)

sudo apt update
sudo apt install -y iw wpasupplicant wireless-tools rfkill net-tools linux-firmware firmware-iwlwifi

Step 2 — Scan for SSIDs

Pick the 2.4 GHz SSID when offered (_2.4, 2G, etc.) — better range for admin SSH. Use 5 GHz only if 2.4 is unavailable.

WIFI=wlo1

sudo rfkill unblock wifi
sudo ip link set "${WIFI}" up

sudo iw dev "${WIFI}" scan | grep -E 'SSID:|signal:'

Note the exact SSID string (case and punctuation matter). Example from home setup: Wi-not-Fi?_2.4.

Step 3 — Create or edit Wi‑Fi netplan

sudo nano /etc/netplan/02-wifi.yaml

New network — copy-paste and edit SSID + password:

network:
version: 2
renderer: networkd

wifis:
wlo1:
optional: true
dhcp4: true
access-points:
"YOUR_SSID_EXACTLY_AS_SCANNED":
password: "your-wifi-password"

SSID must be quoted if it contains ?, spaces, or special characters.

Server room — optional static Wi‑Fi IP (recommended before binding SSH to ListenAddress):

network:
version: 2
renderer: networkd

wifis:
wlo1:
optional: true
dhcp4: no
addresses:
- 192.168.x.x/24 # reserve on office AP/router
routes: [] # no default route via Wi‑Fi
nameservers:
addresses: [192.168.x.1]
access-points:
"OFFICE_SSID":
password: "office-wifi-password"

Do not add a default route on Wi‑Fi when Ethernet carries production traffic.

Step 4 — Apply

sudo chmod 600 /etc/netplan/02-wifi.yaml
sudo netplan generate
sudo netplan try

Press Enter within 120 seconds to keep the config. Then:

sudo netplan apply

Step 5 — Verify

ip -4 addr show wlo1
ip route show default
networkctl status wlo1 --no-pager
ping -I wlo1 -c 3 8.8.8.8

Record the Wi‑Fi IP:

ip -4 addr show wlo1 | grep inet

Test SSH from your workstation (via VPN path when in production):

ssh gqc@<wlo1-ip>

Default route should still use Ethernet after office static IP is configured: ip route | grep default.

Step 6 — Update this doc’s table

Fill in Server-specific values at the bottom: SSID, Wi‑Fi IP, date/location.

Server room redo (quick reference)

When the office Wi‑Fi differs from home lab:

  1. Scan: sudo iw dev wlo1 scan | grep SSID
  2. Edit /etc/netplan/02-wifi.yaml — new SSID and password (and static IP if used)
  3. sudo netplan trysudo netplan apply
  4. Confirm ip -4 addr show wlo1
  5. Update ListenAddress in /etc/ssh/sshd_config if Wi‑Fi IP changed
  6. Proceed with UFW + SSH hardening (Phase 2)

To remove old home Wi‑Fi config, edit 02-wifi.yaml in place — only one access-points block is needed unless you want fallback SSIDs.


Phase 1 — Enable Wi‑Fi (detail)

1. Discover hardware

ip link
lshw -C network 2>/dev/null | grep -A12 "Wireless\|Ethernet"
rfkill list

Note the wireless interface name (often wlp2s0, wlan0, etc.). If rfkill shows Soft blocked: yes:

sudo rfkill unblock wifi

2. Install packages and firmware

Headless Server often ships without Wi‑Fi userland tools. MSI laptops usually need Intel firmware.

sudo apt update
sudo apt install -y \
iw \
wpasupplicant \
wireless-tools \
rfkill \
net-tools \
linux-firmware

# Intel Wi‑Fi (common on MSI) — safe to install even if not Intel
sudo apt install -y firmware-iwlwifi

iw is required for manual scans (iw: command not found without it).

Reboot if the interface still does not appear:

sudo reboot

After reboot, confirm the wireless interface exists:

ip link show

3. Scan for networks (sanity check)

Common mistakes:

  • sudo iw dev wlo1 alone prints help — you must include the scan subcommand.
  • Interface must be UP before scanning (state DOWN in ip link will fail or hang).
  • Use the primary name wlo1, not only the altname wlp0s20f3.

Copy-paste for msi-laptop-3:

WIFI=wlo1

sudo rfkill unblock wifi
sudo ip link set "${WIFI}" up
ip link show "${WIFI}"
# expect: state UP

sudo iw dev "${WIFI}" scan | grep -E 'SSID:|signal:'

If scan runs but shows no SSIDs, you may be on 5 GHz-only APs while driver scans 2.4 — try moving closer to the AP or check router SSID.

Alternative scan (sometimes clearer output):

sudo iwlist wlo1 scan | grep -E 'ESSID|Signal level'
# requires wireless-tools (installed above)

If scan fails, check driver/firmware:

rfkill list
dmesg | grep -iE 'iwl|wifi|wlan|firmware' | tail -30
lsmod | grep iwl

If rfkill shows Hard blocked: yes, check physical Wi‑Fi switch / BIOS wireless setting on the laptop.

4. Configure netplan (Wi‑Fi + Ethernet)

See Runbook — connect Wi‑Fi above for the standard 02-wifi.yaml approach.

Below is a single-file alternative (both interfaces in one file) for office static IP cutover.

Ubuntu Server uses Netplan. Find existing files:

ls /etc/netplan/
sudo cat /etc/netplan/*.yaml

On msi-laptop-3 today, Ethernet is only in 50-cloud-init.yaml:

network:
version: 2
ethernets:
enp58s0:

Wi‑Fi belongs in separate 02-wifi.yaml — do not require editing this stub until server room static IP.

Single-file dual-homed example (for later office cutover — adjust IPs, SSID):

# /etc/netplan/01-dual-homed.yaml
# chmod 600 — contains Wi‑Fi password
network:
version: 2
renderer: networkd

ethernets:
enp58s0: # ← msi-laptop-3 wired
dhcp4: no
addresses:
- 203.0.113.10/24 # ← static public/LAN IP (office)
routes:
- to: default
via: 203.0.113.1 # ← default gateway on wired
nameservers:
addresses: [1.1.1.1, 8.8.8.8]

wifis:
wlo1: # ← msi-laptop-3 wireless (not wlp0s20f3)
optional: true # boot continues if Wi‑Fi unavailable
dhcp4: true # or static — see below
access-points:
"OfficeWiFi-SSID":
password: "your-wifi-password"
# Do NOT add default route via Wi‑Fi if Ethernet is primary:
routes: []
routing-policy: [] # avoid Wi‑Fi becoming default when both up

Wi‑Fi static IP (recommended for stable SSH ListenAddress):

  wifis:
wlo1:
optional: true
dhcp4: no
addresses:
- 192.168.50.100/24 # ← reserve this on the AP/router
routes: [] # no default via Wi‑Fi
nameservers:
addresses: [192.168.50.1]
access-points:
"OfficeWiFi-SSID":
password: "your-wifi-password"

WPA2-Enterprise (802.1X) — use auth: key-management: 802.1x and identity/password; see Netplan Wi‑Fi docs.

Apply and test (single-file example only):

sudo chmod 600 /etc/netplan/01-dual-homed.yaml
sudo netplan generate
sudo netplan try
# Confirm within 120s — netplan rolls back if you don't accept

sudo netplan apply
ip addr show
ip route show

For 02-wifi.yaml only, use the Step 4 — Apply commands in the runbook.

Verify:

  • Ethernet has the static/production address.
  • Wi‑Fi has an address on the office/admin subnet.
  • Default route goes via Ethernet (for API traffic): ip route | grep default.

If Wi‑Fi steals the default route, remove default from Wi‑Fi in netplan (only via on eth0).


Phase 2 — Office cutover: UFW + SSH on Wi‑Fi only

Do this when in the office on the same network you will use for admin SSH. Keep a local console session or IPMI open in case of lockout.

Order of operations

  1. Confirm Wi‑Fi works and you can SSH over Wi‑Fi IP.
  2. Configure UFW (SSH on wlan0 only; web on eth0).
  3. Configure sshd ListenAddress to Wi‑Fi IP (optional extra layer).
  4. Test new SSH session over Wi‑Fi before closing the old session.
  5. Enable UFW.

Replace interface names and IPs:

WIFI=wlo1
WIFI_IP=192.168.50.100 # ip -4 addr show dev wlo1
ETH=enp58s0

UFW rules

sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH — wireless only
sudo ufw allow in on ${WIFI} to any port 22 proto tcp comment 'SSH admin wlan'

# HTTP/S — wired only (public API)
sudo ufw allow in on ${ETH} to any port 80 proto tcp comment 'HTTP public eth'
sudo ufw allow in on ${ETH} to any port 443 proto tcp comment 'HTTPS public eth'

# Do NOT add generic "ufw allow OpenSSH" — that opens 22 on all interfaces

sudo ufw enable
sudo ufw status verbose

Confirm:

sudo ufw status numbered

Port 22 should show on ${WIFI} only; 80/443 on ${ETH} only.

SSH: bind to Wi‑Fi address

Edit sshd config:

sudo nano /etc/ssh/sshd_config

Set (use your Wi‑Fi IPv4):

# Remove or comment any ListenAddress 0.0.0.0 or ListenAddress ::
ListenAddress 192.168.50.100
Port 22

Validate and reload:

sudo sshd -t
sudo systemctl reload ssh
# or: sudo systemctl reload sshd # some installs use ssh.service

From a second terminal (connected via Wi‑Fi IP, not Ethernet):

ssh gqc@192.168.50.100

Only after that succeeds, disconnect the old session.

Already planned elsewhere — apply together:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
sudo sshd -t && sudo systemctl reload ssh

VPN note

If admins reach the server only through a VPN (WireGuard/OpenVPN) and SSH should listen on the tunnel interface instead of (or in addition to) Wi‑Fi:

ip link | grep -E 'wg|tun'

Then either:

  • UFW: sudo ufw allow in on wg0 to any port 22 proto tcp
  • sshd: ListenAddress 10.x.x.x (VPN address on the server)

Wi‑Fi + VPN is common: Wi‑Fi joins office network → VPN client connects → SSH over VPN IP. Pick one admin path and document the chosen IP in the README.


Troubleshooting

SymptomCheck
iw: command not foundsudo apt install -y iw
sudo iw dev wlo1 dumps help onlyAdd scan: sudo iw dev wlo1 scan
wlo1 shows state DOWNsudo rfkill unblock wifi && sudo ip link set wlo1 up
No wireless interfacerfkill list; firmware-iwlwifi; dmesg \| grep iwl
Interface up, no IPjournalctl -u systemd-networkd -n 50; netplan YAML syntax
Wrong default routeip route; remove default from Wi‑Fi in netplan
Locked out of SSHLocal console; fix UFW: sudo ufw disable; fix sshd ListenAddress
SSH works on eth, not wlanUFW may still allow OpenSSH globally — remove and use interface rules
Wi‑Fi drops at lid closeAlready masked suspend; confirm HandleLidSwitch=ignore in logind

Netplan debug

sudo netplan --debug apply
networkctl status
networkctl status wlo1

See what is listening

sudo ss -tlnp | grep ':22'

Should show the Wi‑Fi IP only after hardening.


Checklist — Wi‑Fi enable (any location)

  • Packages installed (iw, wpasupplicant, firmware-iwlwifi)
  • Scan shows target SSID (sudo iw dev wlo1 scan)
  • /etc/netplan/02-wifi.yaml created (SSID + password quoted correctly)
  • 50-cloud-init.yaml left in place for Ethernet
  • sudo netplan try / apply successful
  • wlo1 has IPv4 address
  • SSH works via Wi‑Fi IP
  • Server-specific values table updated below

Checklist — office cutover (with UFW)

  • Wi‑Fi IP documented (prefer DHCP reservation or static)
  • Second SSH session tested on Wi‑Fi IP
  • UFW: 22 on wlan0 only; 80/443 on eth0 only
  • sshd ListenAddress set to Wi‑Fi IP
  • PasswordAuthentication no / PermitRootLogin no (after key auth verified)
  • Verify SSH fails on Ethernet public IP: ssh gqc@<eth-static-ip> (expected)
  • Verify API still reachable on Ethernet :443 from outside (when DNS/SSL ready)

  • Firewall baseline: README.md (deferred until server room)
  • SSH keys (workstation → server): existing server SSH setup
  • Power / lid: prior logind configuration

Server-specific values (fill in)

ItemHome labServer room (office)
Ethernet interfaceenp58s0enp58s0
Wi‑Fi interfacewlo1wlo1
Wi‑Fi SSIDWi-not-Fi?_2.4TBD
Wi‑Fi IP (SSH)TBDTBD
Ethernet static IPDHCP / stub74.83.141.228
Netplan Wi‑Fi file/etc/netplan/02-wifi.yamlsame file, edit SSID
Last updatedTBDTBD