The prayer-room laptop's 0.2.5 migration built a NetworkManager connection, could not bring it up, rolled back, and left the wireless adapter belonging to nobody. Four separate defects, each of which would have been enough on its own. THE HIDDEN FLAG WAS THROWN AWAY. The stanza said wpa-scan-ssid 1 and the parser read it — into options, where the only accessor ignored it. The profile came out with 802-11-wireless.hidden: no, and NetworkManager will not associate with an SSID it has not seen in a scan. A hidden one is never in a scan. credentials() returned a pair; profile() now returns the whole network, and add() writes the flag in BOTH directions rather than only when true, because a default that happens to be right is not the same as an answer. IFDOWN RAN AFTER THE STANZA WAS COMMENTED OUT. ifupdown reads /etc/network/interfaces to find out how to bring an interface down; with the stanza gone it says "Ignoring unknown interface" and does nothing. So ifupdown's own wpa_supplicant and DHCP client were still holding the radio when NetworkManager was asked to associate with it, which is a fight NetworkManager loses. Down first, while the file still says how; rewrite the file second. THE ROLLBACK LEFT THE ADAPTER UNMANAGED. restore() ended with `device set <dev> managed no`, which is right while ifupdown can bring the interface up and is a radio nobody owns when ifup has failed too. That is the state the laptop was found in, and why `nmcli device set wlp2s0 managed yes` was a manual recovery step. It now escalates: if ifupdown cannot get the network back either, the interface goes to NetworkManager rather than to neither, and it says so loudly. A '#' IN A PASSWORD WAS SILENTLY TRUNCATED. The parser cut every line at its first '#'. interfaces(5) is explicit that only whole-line comments exist, so a pre-shared key containing one became a prefix of itself — and the only symptom is an association failing exactly the way a wrong password fails. Two smaller ones went with them. The rollback un-comments its own marked lines in place rather than copying the backup over the whole file, so a wired change made between the migration and the rollback is no longer silently reverted. And nmcli terse output is split on its own escaping rules under LC_ALL=C, so a network whose name contains a colon cannot make a successful migration look like a failed one. --adopt now also refuses a network it cannot recreate before taking the interface away from ifupdown to find out, and reports an unmanaged-devices line in NetworkManager's configuration, which survives the stanza going away. Verified against a real NetworkManager 1.52.1: the keyfile carries hidden=true, the SSID keeps its space, and a psk containing '#' is intact, at 0600 root:root with no connection.permissions. The new test drives the whole hand-over against stubbed tools and asserts the ordering, the rollback and the managed state — including that a machine already migrated, which is the laptop, is not migrated again. Co-Authored-By: Claude Opus 5 <[email protected]>
239 lines
11 KiB
Markdown
239 lines
11 KiB
Markdown
# Classroom networking
|
|
|
|
Ethernet remains the normal classroom connection. The package also installs
|
|
and enables NetworkManager so an administrator can add Wi-Fi when a room needs
|
|
it; there is no classroom Wi-Fi screen and no network name or password in the
|
|
package.
|
|
|
|
## Add Wi-Fi
|
|
|
|
From a terminal or SSH session, run:
|
|
|
|
```
|
|
sudo nmtui
|
|
```
|
|
|
|
Choose **Activate a connection**, select the church Wi-Fi network, enter its
|
|
password, and activate it. NetworkManager saves the connection as a system
|
|
connection and reconnects it automatically after reboot.
|
|
|
|
Useful checks:
|
|
|
|
```
|
|
nmcli device status
|
|
nmcli connection show
|
|
systemctl status NetworkManager
|
|
```
|
|
|
|
There is nothing to configure on a computer without Wi-Fi hardware.
|
|
NetworkManager starts normally, sees no Wi-Fi device, and Ethernet continues
|
|
to work.
|
|
|
|
## Existing Ethernet configuration
|
|
|
|
Do not remove or rewrite `/etc/network/interfaces` as part of this procedure.
|
|
Debian Trixie's NetworkManager configuration uses the `ifupdown` plugin with
|
|
`managed=false`, so any Ethernet interface declared there remains under
|
|
ifupdown's control. NetworkManager manages other interfaces, including Wi-Fi.
|
|
This preserves an existing static or ifupdown Ethernet setup.
|
|
|
|
The package disables `NetworkManager-wait-online.service`; no classroom
|
|
feature is allowed to hold boot waiting for Wi-Fi or Internet access. Saved
|
|
connections still reconnect automatically.
|
|
|
|
## Wi-Fi passwords, and who may see them
|
|
|
|
These are shared computers in rooms full of people. The classroom account
|
|
has to be able to USE the church Wi-Fi and must never be able to READ its
|
|
password. In a stock Plasma desktop those two are much closer together
|
|
than they look.
|
|
|
|
**Provision Wi-Fi with `classroom-wifi`, as root.** Not from the desktop.
|
|
|
|
```
|
|
classroom-wifi "Church Wi-Fi" # prompts, never echoes, never in ps
|
|
classroom-wifi --list
|
|
classroom-wifi --forget "Old Network"
|
|
```
|
|
|
|
This is a requirement, not a convenience. A wireless network added from
|
|
the desktop becomes a **user** connection: NetworkManager records
|
|
`connection.permissions` naming whoever added it, and Debian allows that
|
|
user to read its secrets with **no authentication at all** —
|
|
`org.freedesktop.NetworkManager.settings.modify.own` ships as `yes` for an
|
|
active session. Plasma's applet then offers *Show Network's QR Code* on
|
|
every saved wireless connection, and that QR code contains the pre-shared
|
|
key. So a password typed in at the desktop is readable by the next person
|
|
to sit down.
|
|
|
|
`classroom-wifi` creates a **system** connection instead: no
|
|
`connection.permissions`, secrets in a root-only file
|
|
(`/etc/NetworkManager/system-connections/*.nmconnection`, 0600 root:root),
|
|
`psk-flags=0` so nothing lands in a login keyring. Every account can use
|
|
it, and NetworkManager reconnects at boot on its own — no teacher ever
|
|
re-enters it.
|
|
|
|
### What is refused, and what is deliberately not
|
|
|
|
`49-ekklesia-classroom-network.rules` answers at NetworkManager rather
|
|
than in a menu, because hiding buttons is not a control: the applet's
|
|
*Show Network's QR Code* and *Configure…* are contextual actions that do
|
|
**not** consult the Kiosk key which hides its settings button.
|
|
|
|
Refused for `classroom`: `settings.modify.own`, `settings.modify.system`,
|
|
`wifi.share.protected`, `wifi.share.open`, `settings.modify.hostname`,
|
|
`settings.modify.global-dns`, `reload`, `checkpoint-rollback`.
|
|
|
|
Left alone on purpose: `network-control`, `wifi.scan`,
|
|
`enable-disable-network`, `enable-disable-wifi`. None of them returns a
|
|
secret, and they are what connecting and seeing a network are made of. A
|
|
rule that protected a password by taking the room off the internet would
|
|
not be a trade this room can make. Automatic reconnection does not go
|
|
through polkit at all — NetworkManager does it as root — so nothing here
|
|
can stop the room coming back after a reboot.
|
|
|
|
An administrator's own account and root are untouched. To lift any of it
|
|
for maintenance, drop a file of the same name into
|
|
`/etc/polkit-1/rules.d/`, which polkit reads first.
|
|
|
|
## A Wi-Fi the Debian installer set up
|
|
|
|
Symptom: the internet works, Chrome loads pages, and the panel insists
|
|
there is no connection. Scanning finds nothing either.
|
|
|
|
Both are true at once, and they have one cause. If the installer was
|
|
given the Wi-Fi, it wrote the network into `/etc/network/interfaces`.
|
|
ifupdown brings that up with its own `wpa_supplicant` and `dhcpcd`, which
|
|
is the working internet. NetworkManager ships here with
|
|
`plugins=ifupdown` and `[ifupdown] managed=false`, so an interface named
|
|
in that file is marked **unmanaged** — and an unmanaged device is not a
|
|
radio NetworkManager will report on or scan with.
|
|
|
|
Measured on the Bible study laptop:
|
|
|
|
```
|
|
$ nmcli device status
|
|
wlp2s0 wifi unmanaged --
|
|
$ nmcli networking connectivity
|
|
none # while curl fetched fine
|
|
$ nmcli device wifi list
|
|
SSID SIGNAL # nothing, and the radio is enabled
|
|
```
|
|
|
|
`managed=false` stays. It is what keeps a tower's static Ethernet under
|
|
ifupdown's control where an administrator put it. Only a **wireless**
|
|
interface is moved.
|
|
|
|
### Moving it, while watching
|
|
|
|
This is never done by an upgrade. It moves a machine's only link between
|
|
two managers, and the machine it could strand is a laptop in a locked
|
|
room. `classroom-migrate` only looks and says what it found. An
|
|
administrator does the rest, over SSH, watching:
|
|
|
|
```
|
|
classroom-wifi --check # who owns it; changes nothing
|
|
classroom-wifi --adopt --dry-run # what would change; changes nothing
|
|
classroom-wifi --adopt # do it
|
|
classroom-wifi --rollback # undo it, at any time afterwards
|
|
```
|
|
|
|
`--adopt` creates a NetworkManager **system** connection from the SSID
|
|
and key already in that file — so the password never leaves the machine,
|
|
is never printed, and lands in a 0600 root-only file with `psk-flags=0`,
|
|
exactly as `classroom-wifi` provisions any other network. The stanza is
|
|
then commented out, the interface handed over, and the result checked:
|
|
the connection must be active **on that interface** and the machine must
|
|
really fetch something. Checking only "is the machine online" would pass
|
|
on a machine with a second link while the Wi-Fi sat dead.
|
|
|
|
If any of that fails — including a command that is not installed, or an
|
|
interrupt — the original file is put back and ifupdown gets the interface
|
|
again. It refuses to start at all if `ifdown`/`ifup` are missing, because
|
|
that would be a one-way door.
|
|
|
|
The original is kept at `/etc/network/interfaces.before-ekklesia-classroom`.
|
|
|
|
### What 0.2.5 got wrong, and what 0.2.6 changed
|
|
|
|
The prayer-room laptop's Wi-Fi is **hidden** — `ROGC Classroom`, an SSID
|
|
with a space in it, announced by nothing. `--adopt` built a connection,
|
|
failed to bring it up, rolled back, and left the adapter belonging to
|
|
nobody. Four separate defects, all fixed:
|
|
|
|
**1. The hidden flag was thrown away.** The stanza said `wpa-scan-ssid 1`
|
|
and the parser read it — into `options`, where the only accessor ignored
|
|
it. The generated profile had `802-11-wireless.hidden: no`, and
|
|
NetworkManager will not associate with an SSID it has not seen in a scan.
|
|
A hidden one is never in a scan. `credentials()` returned a pair; there
|
|
is now a `profile()` that returns the whole network, and `add()` writes
|
|
`802-11-wireless.hidden` in **both** directions rather than only when
|
|
true — a default that happens to be right is not an answer.
|
|
|
|
**2. `ifdown` ran after the stanza was commented out.** ifupdown reads
|
|
`/etc/network/interfaces` to find out how to bring an interface down.
|
|
With the stanza gone it says *"Ignoring unknown interface"* and does
|
|
nothing — so ifupdown's own `wpa_supplicant` and DHCP client were still
|
|
holding the radio when NetworkManager was asked to associate with it.
|
|
Two supplicants on one interface is a fight NetworkManager loses. The
|
|
order is now: **`ifdown` first, while the file still says how; rewrite
|
|
the file second.**
|
|
|
|
**3. The rollback left the adapter unmanaged.** `restore()` ended with
|
|
`nmcli device set <dev> managed no`, which is right while ifupdown can
|
|
actually bring the interface up. When `ifup` fails too, those two lines
|
|
are a wireless adapter neither manager owns: no connection, and a panel
|
|
that cannot even scan. That is the state the laptop was found in, and it
|
|
is why `sudo nmcli device set wlp2s0 managed yes` was a manual recovery
|
|
step. The rollback now escalates — if ifupdown cannot get the network
|
|
back either, the interface is handed to NetworkManager rather than left
|
|
with neither, and it says so loudly.
|
|
|
|
**4. A `#` in a password was silently truncated.** The parser cut every
|
|
line at its first `#`. `interfaces(5)` is explicit that only whole-line
|
|
comments exist — *"end-of-line comments are NOT supported"* — so a
|
|
pre-shared key containing `#` became a prefix of itself, and the only
|
|
symptom is an association that fails exactly the way a wrong password
|
|
fails.
|
|
|
|
Two smaller ones went with them. The rollback un-comments its own marked
|
|
lines in place instead of copying the backup over the whole file, because
|
|
the backup is written once and never refreshed — a whole-file restore
|
|
silently reverted any *wired* change an administrator made in between.
|
|
And `nmcli -t` output is now split on its own escaping rules rather than
|
|
on `:`, with `LC_ALL=C`, so a network whose name contains a colon cannot
|
|
make a successful migration look like a failed one.
|
|
|
|
`--adopt` also now refuses a network it cannot recreate — WPA-Enterprise
|
|
has no pre-shared key to carry across — *before* taking the interface
|
|
away from ifupdown to find out, and reports any `unmanaged-devices` line
|
|
in NetworkManager's own configuration, which survives the stanza going
|
|
away and would otherwise leave the interface belonging to nobody after a
|
|
restart.
|
|
|
|
Verified against a real NetworkManager 1.52.1: the keyfile it writes
|
|
carries `hidden=true`, `ssid=ROGC Classroom` with its space, and a psk
|
|
containing `#` intact, at 0600 root:root with no `connection.permissions`.
|
|
`tests/test-classroom-wifi-adopt.sh` drives the whole hand-over against
|
|
stubbed tools and asserts the ordering, the rollback and the managed
|
|
state.
|
|
|
|
**The prayer-room laptop must not be migrated again.** Its stanza is
|
|
already gone, so `--adopt` reports "nothing to adopt" and exits 0 without
|
|
touching anything; there is a test for exactly that.
|
|
|
|
After the migration the panel shows the connection at the next login, the
|
|
room can scan, and the credential protection below applies to it like any
|
|
other system connection.
|
|
|
|
### Recovery when there is no network at all
|
|
|
|
There is deliberately no teacher-facing "join a network" dialog in this
|
|
release. Anything that lets the room add a connection also lets the room
|
|
own it, and an owned connection is a readable one. Recovery is an
|
|
administrator running `classroom-wifi` over SSH, or at the machine
|
|
through the maintenance terminal chord. If a room ever needs to join a
|
|
network unattended, that workflow has to be designed so the connection it
|
|
creates still belongs to the machine — not bolted on by relaxing the rule
|
|
above.
|