Merge nucleic/vivid-glass-urchin-xoym into main
This commit is contained in:
+48
-29
@@ -441,6 +441,11 @@ disabling sleep (`sudo pmset -a sleep 0 disablesleep 1`) so the Mac comes back i
|
||||
after a power event without a human present. This does mean the disk is effectively unlocked at
|
||||
boot — appropriate for a dedicated CI machine, not for a shared workstation.
|
||||
|
||||
On a terminal, `service install` also asks whether to configure Local Network access (§2.6) when it
|
||||
is not already, defaulting to no. It never blocks: a scripted install with no terminal prints a
|
||||
pointer and carries on. `--grant-local-network allowlist|prompt|none` decides it up front instead of
|
||||
being asked.
|
||||
|
||||
`service uninstall` removes the LaunchAgent; it does not delete images or config.
|
||||
|
||||
### 2.6 macOS 15+ Local Network privacy prompt
|
||||
@@ -449,32 +454,41 @@ Starting with macOS 15, a process that contacts other hosts on the local network
|
||||
one-time Local Network permission prompt. A LaunchAgent that is denied (or that never gets a human
|
||||
to click Allow) cannot reach the guest's NAT address, so VMs boot but SSH never connects.
|
||||
|
||||
**On a CI host, use the subnet allowlist.** It is the only deterministic option — no prompt, no GUI
|
||||
session, and nothing to redo after a rebuild:
|
||||
`service install` offers to configure this, and it can also be done at any time:
|
||||
|
||||
```sh
|
||||
sudo defaults write com.apple.network.local-network \
|
||||
AllowedEthernetLocalNetworkAddresses -array "192.168.64.0/18"
|
||||
sudo defaults write com.apple.network.local-network \
|
||||
AllowedWiFiLocalNetworkAddresses -array "192.168.64.0/18"
|
||||
gitea-macos-runner permissions status # what is configured, and what to do about it
|
||||
gitea-macos-runner permissions grant # configure it
|
||||
```
|
||||
|
||||
Then **reboot** — these are read at boot, so restarting the service alone is not enough.
|
||||
`grant` has two methods. Both are one command; neither needs anything pasted.
|
||||
|
||||
**Do not pin this to a single /24.** Virtualization.framework's NAT starts at `192.168.64.0/24` but
|
||||
picks the subnet at runtime and steps to the next free one when that range is already in use, so the
|
||||
same host can hand out `192.168.65.x` tomorrow. An allowlist naming only `192.168.64.0/24` then
|
||||
looks configured while silently blocking every guest — the failure surfaces as `No route to host`
|
||||
(errno 65) on the SSH connection, not as a permission error. The `/18` above spans
|
||||
`192.168.64.0`–`192.168.127.255`, which covers the drift; if you would rather not think about
|
||||
ranges at all, the RFC 1918 set `"10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16"` also works. `doctor`
|
||||
reports `local network access` as a **pass** once it sees an allowlist that covers that span, and as
|
||||
a **warning** when an allowlist exists but does not. Both keys are documented by Apple in
|
||||
[TN3179](https://developer.apple.com/documentation/technotes/tn3179-understanding-local-network-privacy)
|
||||
and are the workaround [Tart's FAQ](https://tart.run/faq/) recommends for the same problem.
|
||||
**`--method allowlist` (the default) is what a CI host wants.** It writes a subnet allowlist — the
|
||||
one deterministic option: no prompt, no GUI session, and nothing to redo after a rebuild. It asks
|
||||
for your sudo password, reports which preferences file the write actually landed in, and then offers
|
||||
to **reboot**, which is required: these values are read at boot, so restarting the service alone is
|
||||
not enough. Pass `--no-reboot` to defer that.
|
||||
|
||||
**Why not just approve the prompt?** Because on this host there is usually nothing able to show it,
|
||||
and when something does, it is attributed to the wrong program.
|
||||
The default grant is all of RFC 1918 — `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` — the same
|
||||
set [Tart](https://tart.run/faq/) and orchard use. Narrow it with `--subnet`, repeatable, but **do
|
||||
not pin it to a single /24**: Virtualization.framework's NAT starts at `192.168.64.0/24` but picks
|
||||
the subnet at runtime and steps to the next free one when that range is already in use, so the same
|
||||
host can hand out `192.168.65.x` tomorrow. An allowlist naming only `192.168.64.0/24` then looks
|
||||
configured while silently blocking every guest — and the failure surfaces as `No route to host`
|
||||
(errno 65) on the SSH connection, not as a permission error. `192.168.64.0/18` spans
|
||||
`192.168.64.0`–`192.168.127.255`, which is the narrowest entry that covers the drift. `doctor`
|
||||
reports `local network access` as a **pass** once it sees an allowlist covering that span, and as a
|
||||
**warning** when an allowlist exists but does not. Both keys are documented by Apple in
|
||||
[TN3179](https://developer.apple.com/documentation/technotes/tn3179-understanding-local-network-privacy).
|
||||
|
||||
**`--method prompt` takes effect immediately, with no reboot**, and is the better choice on a Mac
|
||||
you are sitting in front of. It launches the installed `.app` through LaunchServices — which is what
|
||||
makes the app its own responsible process — provokes the real system alert, and reports whether the
|
||||
grant took. It needs `make install` to have run, a GUI session to show the alert in, and a Developer
|
||||
ID signature for the grant to survive the next rebuild; `permissions status` reports that last one.
|
||||
|
||||
**Why the prompt needs that much machinery.** Left to itself, on this host there is usually nothing
|
||||
able to show it, and when something does, it is attributed to the wrong program.
|
||||
|
||||
An app appears under **System Settings → Privacy & Security → Local Network** only *after* it has
|
||||
attempted a connection to a guest, so an empty list on a fresh install is expected and means
|
||||
@@ -490,14 +504,19 @@ miss:
|
||||
approving it there does not carry over to the LaunchAgent. (If you are hunting for a row that
|
||||
seems missing, look for Terminal rather than for "Gitea macOS Runner".)
|
||||
|
||||
> **And under ad-hoc signing it is not durable anyway.** Local Network privacy does not use TCC; per
|
||||
> TN3179 it "uses your main executable UUID as part of its implementation", and the linker mints a
|
||||
> fresh `LC_UUID` on essentially every rebuild. So `make install` after a code change presents as a
|
||||
> new app that must be approved again — and macOS offers no way to reset a Local Network decision
|
||||
> back to undetermined, so stale entries accumulate. Signing with a Developer ID certificate fixes
|
||||
> the churn (see [Code signing](#code-signing)); the subnet allowlist above, keyed on the network
|
||||
> rather than on the app, sidesteps the whole mechanism and is the recommendation for an unattended
|
||||
> machine either way.
|
||||
`permissions grant --method prompt` exists to thread that needle: it starts the app through
|
||||
LaunchServices rather than from the shell, so the app is its own responsible process and the
|
||||
decision is recorded against *its* identity — the same identity the LaunchAgent runs under.
|
||||
|
||||
> **Under an ad-hoc signature it is not durable anyway.** Local Network privacy does not use TCC;
|
||||
> per TN3179 it "uses your main executable UUID as part of its implementation", and the linker mints
|
||||
> a fresh `LC_UUID` on essentially every rebuild. So `make install` after a code change presents as
|
||||
> a new app that must be approved again — and macOS offers no way to reset a Local Network decision
|
||||
> back to undetermined, so stale entries accumulate. A Developer ID signature fixes the churn, since
|
||||
> the identity is then anchored to the certificate rather than to the binary (see
|
||||
> [Code signing](#code-signing)) — that is what makes `--method prompt` worth using at all. The
|
||||
> subnet allowlist, keyed on the network rather than on the app, sidesteps the whole mechanism and
|
||||
> remains the recommendation for an unattended machine.
|
||||
|
||||
---
|
||||
|
||||
@@ -531,7 +550,7 @@ The checks, in order:
|
||||
| `runner download url` | The `gitea-runner` release asset is reachable |
|
||||
| `token file permissions` | Warns — not fails — when a token file is group- or world-readable |
|
||||
| `guest ssh` | Reachability of the most recent guest lease, when there is one. Warns on a timeout, which is most often a stale 24-hour lease for a guest that is already gone |
|
||||
| `local network access` | Passes when a subnet allowlist covers `192.168.64.0/18`; warns when an allowlist exists but is scoped too narrowly; otherwise an informational note about the macOS 15+ Local Network prompt (§2.6) |
|
||||
| `local network access` | Passes when a subnet allowlist covers `192.168.64.0/18`; warns when an allowlist exists but is scoped too narrowly; otherwise an informational note about the macOS 15+ Local Network prompt. Fix either with `permissions grant` (§2.6) |
|
||||
|
||||
If the config file is missing or invalid, the host checks still run and the rest
|
||||
are skipped — which is exactly the state a first-time operator is in. Resolve
|
||||
|
||||
Reference in New Issue
Block a user