A draft spec for the owner's review and 13 offline tasks with their given tests: shared tool arguments and host rules in proto, the sealed fetch target (M3a finding 14), the toolkit tools and SOCKS5 egress proxy, and brokerd's [runner], podman argument lists, runtime and proxy lifecycle. Each task's tests were run against a reference at that task's end state (560 to 638 tests, clippy clean); the reference is not in the repository. Adds the runner-unavailable runbook entry and tip T23 (ETXTBSY in script tests). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
83 lines
3.4 KiB
Markdown
83 lines
3.4 KiB
Markdown
# M3b task 07: which addresses are public
|
|
|
|
**Branch:** `m3b` (run `git switch m3b`; `git status --short` must be empty, otherwise stop)
|
|
**Commit subject:** `toolkit: is_public, the addresses the egress proxy may reach`
|
|
|
|
## Goal
|
|
|
|
The egress proxy (task 08) connects only to **public** addresses. A grant allows a host on the
|
|
internet; if its name resolves into the tailnet (`100.64.0.0/10`), the host (`127.0.0.1`) or a
|
|
private network, the connection is refused even though the name is allowed. This task is the one
|
|
pure function that decides it. Spec section 5, "Public".
|
|
|
|
## Files
|
|
|
|
- Copy: `crates/toolkit/tests/addr.rs`
|
|
- Create: `crates/toolkit/src/addr.rs`
|
|
- Modify: `crates/toolkit/src/lib.rs` (`pub mod addr;`), `docs/implementer-log.md`
|
|
|
|
## Interface
|
|
|
|
```rust
|
|
/// True if `ip` is a public unicast address.
|
|
pub fn is_public(ip: std::net::IpAddr) -> bool;
|
|
```
|
|
|
|
## Refused ranges: every one of these is **not** public
|
|
|
|
IPv4 (with `[a, b, c, _] = ip.octets()`):
|
|
|
|
| Range | Test |
|
|
|---|---|
|
|
| `0.0.0.0/8` | `a == 0` |
|
|
| `10.0.0.0/8` | `a == 10` |
|
|
| `100.64.0.0/10` (the tailnet) | `a == 100 && (64..=127).contains(&b)` |
|
|
| `127.0.0.0/8` | `a == 127` |
|
|
| `169.254.0.0/16` | `a == 169 && b == 254` |
|
|
| `172.16.0.0/12` | `a == 172 && (16..=31).contains(&b)` |
|
|
| `192.0.0.0/24` | `a == 192 && b == 0 && c == 0` |
|
|
| `192.0.2.0/24` | `a == 192 && b == 0 && c == 2` |
|
|
| `192.168.0.0/16` | `a == 192 && b == 168` |
|
|
| `198.18.0.0/15` | `a == 198 && (b == 18 \|\| b == 19)` |
|
|
| `198.51.100.0/24` | `a == 198 && b == 51 && c == 100` |
|
|
| `203.0.113.0/24` | `a == 203 && b == 0 && c == 113` |
|
|
| `224.0.0.0/4` and `240.0.0.0/4` | `a >= 224` |
|
|
|
|
IPv6 (with `s = ip.segments()`, eight `u16`s):
|
|
|
|
| Range | Test |
|
|
|---|---|
|
|
| `::/96` (holds `::`, `::1`, and the old IPv4-compatible form) | the first six segments are all 0 |
|
|
| IPv4-mapped `::ffff:0:0/96` | `s[0..5]` all 0 and `s[5] == 0xffff`: judge the last 32 bits as IPv4 |
|
|
| NAT64 `64:ff9b::/96` | `s[0..6] == [0x64, 0xff9b, 0, 0, 0, 0]`: judge the last 32 bits as IPv4 |
|
|
| `fc00::/7` | `s[0] & 0xfe00 == 0xfc00` |
|
|
| `fe80::/10` | `s[0] & 0xffc0 == 0xfe80` |
|
|
| `ff00::/8` | `s[0] & 0xff00 == 0xff00` |
|
|
| `2001:db8::/32` | `s[0] == 0x2001 && s[1] == 0x0db8` |
|
|
|
|
Check the three "judge as IPv4" rows **before** the others. The last 32 bits as IPv4 are
|
|
`Ipv4Addr::from(((s[6] as u32) << 16) | s[7] as u32)` in spirit, but **no `as` casts**: use
|
|
`u32::from(s[6])` and `u32::from(s[7])`, or `s[6].to_be_bytes()` and `s[7].to_be_bytes()`.
|
|
|
|
Everything else is public. Do not use the standard library's `is_global` (unstable) or add ranges
|
|
that are not in these tables: the test checks public neighbours just outside each range too.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Copy.** `git switch m3b`, then
|
|
`cp docs/plans/M3b/files/crates/toolkit/tests/addr.rs crates/toolkit/tests/`
|
|
- [ ] **2. See it fail.** `cargo test -p toolkit --test addr`. Expected: it does not compile.
|
|
- [ ] **3. Write `addr.rs`.** Run `cargo fmt --all`.
|
|
- [ ] **4. See it pass.** `cargo test -p toolkit --test addr`. Expected: 4 passed.
|
|
- [ ] **5. Walk the tables.** Point at the line for each row of both tables.
|
|
- [ ] **6. Run the gate.** `make gate`. Expected last line: `gate: ok`.
|
|
- [ ] **7. Log and commit.** `git add crates/toolkit docs/implementer-log.md && git commit`
|
|
|
|
## Done when
|
|
|
|
- `cargo test -p toolkit --test addr` reports 4 passed; `make gate` prints `gate: ok`.
|
|
|
|
## Stop and report if
|
|
|
|
- A test's expectation disagrees with these tables.
|