fix(apple): ATS blocked the library on every non-RFC1918 host
The game library rides the management REST API over HTTPS (TCP 47990) through URLSession, authenticated by mTLS and pinned by SHA-256 fingerprint. The app declared no App Transport Security policy at all, so it ran under default ATS -- which exempts only "local" destinations (.local, unqualified names, RFC1918 and link-local literals) and applies the full policy everywhere else. The host certificate is self-signed (and carries no SubjectAltName), so it cannot satisfy that policy: the library loaded at 192.168.x and failed at the TLS layer on any other address. Field-reported against a Tailscale host. 100.64.0.0/10 is CGNAT, not RFC1918, so the library failed there while streaming to the very same address worked -- the QUIC stream plane is raw UDP and never enters the URL loading system. A WireGuard peer or a public-IP host breaks identically. Declares NSAllowsArbitraryLoads, which must stay the dict's only key: iOS 10+ and macOS 10.12+ ignore it whenever a more granular ATS key sits alongside it. Trust is unchanged -- LibraryTLSDelegate still pins the host by fingerprint and hands every other origin (cover-art CDNs) to full system trust evaluation.
This commit is contained in:
@@ -19,6 +19,25 @@
|
||||
<array>
|
||||
<string>_punktfunk._udp</string>
|
||||
</array>
|
||||
<!-- App Transport Security. We talk to the user's OWN host over HTTPS with a SELF-SIGNED
|
||||
certificate at a user-supplied address, and verify it by SHA-256 fingerprint pinning
|
||||
established during PIN pairing (LibraryTLSDelegate) — for a box on someone's LAN there is
|
||||
no CA that could vouch for it, so pinning is the stronger check, not a weaker one.
|
||||
Default ATS exempts only "local" destinations (.local, unqualified names, RFC1918 and
|
||||
link-local literals). Every other address gets the full policy, which a self-signed cert
|
||||
cannot satisfy — so the management API (game library) worked at 192.168.x but died at the
|
||||
TLS layer on Tailscale's 100.64/10 CGNAT range, a WireGuard peer, or a public IP. The QUIC
|
||||
stream plane never showed it: that is raw UDP and never enters the URL loading system.
|
||||
⚠ This must stay the ONLY key in this dict. On iOS 10+/macOS 10.12+ the system IGNORES
|
||||
NSAllowsArbitraryLoads whenever NSAllowsLocalNetworking, NSAllowsArbitraryLoadsInWebContent
|
||||
or NSAllowsArbitraryLoadsForMedia is present alongside it — adding one silently restores
|
||||
the bug. Other origins (cover-art CDNs) keep full system trust evaluation regardless:
|
||||
LibraryTLSDelegate hands every non-host challenge to .performDefaultHandling. -->
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<!-- Background keep-alive (opt-in, iOS/iPadOS): the ONLY sanctioned way to keep the long-lived
|
||||
QUIC socket + pump-thread set alive while backgrounded is the audio background mode, backed
|
||||
by the session's real, audible remote audio (AVAudioEngine keeps rendering). Video decode is
|
||||
|
||||
Reference in New Issue
Block a user