Harden breadcrumbs: fix real bugs, restructure as lib, stop storing PSKs twice
Bug fixes: - mask() panicked on multi-byte UTF-8 passwords (byte-slicing a char boundary); now masks by char count and never echoes a real character - `cd --shell` interpolated the config path into a shell -c string via Debug formatting, which doesn't neutralize shell metacharacters; now passed as a positional shell argument instead - connecting to open (no-password) networks failed because an empty PSK was always sent to nmcli, which nmcli treats as secured-with-no-password instead of open; the password arg is now omitted entirely when empty - five nmcli terse-output parse sites used a raw splitn(2, ':'), which mis-splits any device/connection name containing a literal ':'; unified on the existing escape-aware field splitter - watch's health classifier silently read a config-deleted profile as "healthy" off a bare internet check instead of surfacing the misconfig - the nmcli-monitor thread seeded its debounce clock with `Instant::now() - 10s`, which panics on the monotonic clock near boot — exactly when the generated systemd unit tends to start the watcher Architecture: - extracted src/lib.rs + src/app.rs so command logic can be exercised in-process by tests instead of only by spawning the compiled binary - added a Runner trait (src/util.rs) so subprocess calls can be faked in tests; flow::run and watch::classify are now covered by real in-process tests of the connect state machine and health transitions, not just their pure helpers - Wi-Fi passwords are no longer kept in breadcrumbs' config once NetworkManager durably holds them: NetworkDef.password is now optional, and a successful password-based connect clears + persists it immediately, so it's never sent again on subsequent connects - saved networks (SSID + optional local password) moved out of breadcrumbs.toml into a separate networks.toml; old configs with inline [[networks]] still load and migrate automatically on next save - corrected a false README claim that passwords are never in nmcli argv Test count: 20 -> 89 (52 unit, 24 CLI integration, 13 in-process state-machine tests). Full clean run: cargo build/build --release/ test/clippy --all-targets, verified from a `cargo clean` rebuild.
This commit is contained in:
parent
d177cc8d82
commit
037c6e54c9
16 changed files with 2688 additions and 834 deletions
26
README.md
26
README.md
|
|
@ -10,7 +10,7 @@ breadcrumbs sits on top of NetworkManager (`nmcli`) and manages your Wi-Fi based
|
|||
- **Bootstrap + Tailscale gating** — connect to an interim network first, bring up Tailscale, then move to the target network
|
||||
- **Self-healing watch daemon** — monitors for drops, auto-recovers, reacts within seconds via `nmcli monitor`
|
||||
- **Auto-detection** — scans visible SSIDs and guesses your location from config-defined markers
|
||||
- **Secure credential handling** — passwords fed to `nmcli` via stdin (never in argv/`ps`), config stored at 0600
|
||||
- **Credential handling** — a saved network's password is only needed the *first* time breadcrumbs connects to it. Once that connect succeeds, NetworkManager durably owns the credential (a new connection profile, or an updated PSK on an existing one), so breadcrumbs clears its own local copy and stops writing it to disk. Both config files are `0600` (owner-only); saved networks live in a separate `networks.toml` from settings/profiles (see [Configuration](#configuration)). Note: on that first connect, the PSK is still passed to `nmcli` as a command argument, so it's briefly visible to other local users via `/proc/<pid>/cmdline` for the lifetime of that `nmcli` child — a known limitation (see the note in `src/nm.rs`); a `nmcli --ask`/D-Bus secret-agent path that avoids argv exposure entirely is not yet wired up. In practice this window now only exists once per network, not on every connect.
|
||||
- **Desktop notifications** via `notify-send` (optional)
|
||||
- **systemd user service** generation via `breadcrumbs install-service`
|
||||
|
||||
|
|
@ -34,17 +34,21 @@ cp target/release/breadcrumbs ~/.local/bin/
|
|||
|
||||
## Configuration
|
||||
|
||||
On first run, breadcrumbs creates `~/.config/breadcrumbs/breadcrumbs.toml` with default profiles. Copy `breadcrumbs.example.toml` as a starting point and fill in your real network credentials:
|
||||
On first run, breadcrumbs creates `~/.config/breadcrumbs/breadcrumbs.toml` (settings + profiles) and `~/.config/breadcrumbs/networks.toml` (saved networks) with default profiles. Copy `breadcrumbs.example.toml` as a starting point for the former:
|
||||
|
||||
```bash
|
||||
cp breadcrumbs.example.toml ~/.config/breadcrumbs/breadcrumbs.toml
|
||||
breadcrumbs edit # opens in $EDITOR
|
||||
breadcrumbs edit # opens breadcrumbs.toml in $EDITOR
|
||||
```
|
||||
|
||||
...then add your real networks with `breadcrumbs add`/`scan` rather than hand-editing `networks.toml` (see `networks.example.toml` if you want to see its shape or write it by hand anyway).
|
||||
|
||||
Config paths respect `$XDG_CONFIG_HOME` and `$XDG_STATE_HOME`.
|
||||
|
||||
### Config structure
|
||||
|
||||
Settings and location profiles live in `breadcrumbs.toml` — the file people actually hand-edit or dotfile:
|
||||
|
||||
```toml
|
||||
[settings]
|
||||
dns = "1.1.1.1" # DNS server pinned on every connection
|
||||
|
|
@ -55,11 +59,6 @@ watch_interval = 12 # seconds between health checks (minimum 4)
|
|||
connectivity_url = "http://connectivitycheck.gstatic.com/generate_204"
|
||||
ping_host = "1.1.1.1"
|
||||
|
||||
[[networks]]
|
||||
ssid = "MyHomeNetwork"
|
||||
password = "hunter2"
|
||||
hidden = false
|
||||
|
||||
[profiles.home]
|
||||
networks = ["MyHomeNetwork"] # priority-ordered SSIDs
|
||||
tailscale = false
|
||||
|
|
@ -74,6 +73,17 @@ exit_node = "jump-host" # per-profile override
|
|||
detect_ssids = ["CorpWifi", "Corp-5G"]
|
||||
```
|
||||
|
||||
Saved networks (SSID + optional local password) live separately, in `networks.toml`, managed via `add`/`scan`/`forget`:
|
||||
|
||||
```toml
|
||||
[[networks]]
|
||||
ssid = "MyHomeNetwork"
|
||||
password = "hunter2" # optional — see "Credential handling" below
|
||||
hidden = false
|
||||
```
|
||||
|
||||
`password` is only needed the first time breadcrumbs connects to a network. Once NetworkManager durably saves the credential, breadcrumbs clears its local copy and omits the key on the next save — an existing config with `password = "..."` still loads fine either way, no migration step needed. A config with `[[networks]]` still written inline in `breadcrumbs.toml` (from before this split) also still loads: it's read once, then migrated into `networks.toml` automatically on the next save.
|
||||
|
||||
### Profiles
|
||||
|
||||
Each profile defines:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue