11d0a657ac
SECURITY: With Tor ON, all HTTP traffic now routes through SOCKS proxy. Previously only relay WebSocket connections used Tor; 8 other egress paths created bare OkHttpClient() instances bypassing Tor entirely. Fail-closed behavior: - When Tor expected but bootstrapping → dead SOCKS proxy on port 1 (requests fail instead of leaking IP) - lateinit var crashes on misconfiguration (loud failure vs silent leak) Leak sites fixed (all use DesktopHttpClient.currentClient()): - AnimatedGifImage: GIF fetch - SaveMediaAction: media downloads - EncryptedMediaService: NIP-17 DM media - ServerHealthCheck: Blossom server probes - NoteActions: zap/lightning LNURL resolution - DesktopBlossomClient: media uploads - AccountManager: NIP-46 bunker relay connections - Coil image loader: TODO (OkHttpNetworkFetcher import issue) Also: - Relay reconnect on Tor Active (prevents stale clearnet connections) - isTorExpected() for fail-closed logic - Tests updated for new torTypeProvider parameter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
135 lines
4.2 KiB
Markdown
135 lines
4.2 KiB
Markdown
# Manual Testing: Desktop Tor Support
|
|
|
|
## Prerequisites
|
|
|
|
All tools are available natively on macOS:
|
|
- `tcpdump` — DNS leak monitoring
|
|
- `curl` — Tor routing verification
|
|
- `lsof` — find SOCKS port
|
|
|
|
Optional:
|
|
```bash
|
|
brew install tor # For external Tor mode testing
|
|
```
|
|
|
|
---
|
|
|
|
## Step 1: Launch & Find SOCKS Port
|
|
|
|
```bash
|
|
cd AmethystMultiplatform-tor-support
|
|
./gradlew :desktopApp:run > /tmp/amethyst-tor.log 2>&1 &
|
|
|
|
# Wait ~30s for Tor bootstrap, then find the SOCKS port:
|
|
lsof -i -P -n | grep java | grep LISTEN
|
|
# Look for a high port (e.g. 35607) — that's the kmp-tor SOCKS port
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2: Verify Tor Routing
|
|
|
|
Replace `PORT` with the actual SOCKS port from Step 1.
|
|
|
|
```bash
|
|
# Your real IP
|
|
curl -s https://icanhazip.com
|
|
# → e.g. 203.0.113.42
|
|
|
|
# IP through Tor
|
|
curl -s --socks5-hostname 127.0.0.1:PORT https://icanhazip.com
|
|
# → e.g. 185.220.101.xx (different from above)
|
|
|
|
# Confirm Tor Project says it's Tor
|
|
curl -s --socks5-hostname 127.0.0.1:PORT https://check.torproject.org/api/ip
|
|
# → {"IsTor":true,"IP":"185.220.101.xx"}
|
|
```
|
|
|
|
---
|
|
|
|
## Step 3: Verify No DNS Leaks
|
|
|
|
```bash
|
|
# In a separate terminal:
|
|
sudo tcpdump -i any port 53 -n -l 2>/dev/null | grep --line-buffered -v "mdns"
|
|
|
|
# In the app: scroll feed, load profiles, open threads
|
|
# tcpdump should show NO relay hostname DNS queries from Java
|
|
```
|
|
|
|
---
|
|
|
|
## Checklist
|
|
|
|
### UI — Default State
|
|
- [ ] App launches with shield icon visible in sidebar (bottom)
|
|
- [ ] Shield starts gray/yellow, turns green after ~10-30s (Tor bootstrap)
|
|
- [ ] Hover shield → tooltip shows "Tor: Connected"
|
|
- [ ] Click shield → navigates to Settings
|
|
- [ ] Settings → Tor section shows **Internal** mode selected
|
|
- [ ] Click "Advanced..." → **Full Privacy** preset selected, all toggles ON
|
|
|
|
### UI — Mode Switching
|
|
- [ ] Select "Off" → shield turns gray, relays reconnect directly
|
|
- [ ] Select "Internal" → shield yellow → green, relays reconnect via Tor
|
|
- [ ] Select "External" → SOCKS port field appears
|
|
- [ ] Enter port 9050 (if system Tor running) → shield turns green
|
|
|
|
### UI — Advanced Dialog
|
|
- [ ] Preset radio buttons: Only When Needed / Default / Small Payloads / Full Privacy / Custom
|
|
- [ ] Selecting "Only When Needed" → only .onion toggle ON
|
|
- [ ] Selecting "Full Privacy" → all toggles ON
|
|
- [ ] Toggling individual item → preset auto-switches to "Custom"
|
|
- [ ] Scroll dialog content → Cancel/Save buttons stay at bottom (sticky)
|
|
- [ ] Cancel → no changes saved
|
|
- [ ] Save → settings applied and persisted
|
|
|
|
### UI — .onion Relay Badge
|
|
- [ ] Add a .onion relay URL → badge shows "via Tor" (green) when Tor ON
|
|
- [ ] Switch Tor OFF → badge shows "Requires Tor" (red)
|
|
|
|
### UI — Persistence
|
|
- [ ] Change to "Default" preset, close app
|
|
- [ ] Relaunch → shows Internal + Default preset, Tor auto-connects
|
|
|
|
### Network — Tor Routing Verified
|
|
- [ ] `curl -s https://icanhazip.com` → real IP: _______________
|
|
- [ ] `curl -s --socks5-hostname 127.0.0.1:PORT https://icanhazip.com` → Tor IP: _______________
|
|
- [ ] IPs are different: ___
|
|
- [ ] `curl --socks5-hostname 127.0.0.1:PORT https://check.torproject.org/api/ip` → `{"IsTor":true}`: ___
|
|
|
|
### Network — No DNS Leaks
|
|
- [ ] `sudo tcpdump -i any port 53 -n` shows no relay hostname queries while Tor active
|
|
|
|
### Build — No Regressions
|
|
- [ ] `./gradlew :commons:jvmTest` — all pass
|
|
- [ ] `./gradlew :desktopApp:test` — all pass (1 pre-existing failure in DesktopCachePipelineTest)
|
|
- [ ] `./gradlew :amethyst:testPlayDebugUnitTest` — all pass
|
|
- [ ] `./gradlew :amethyst:compilePlayDebugKotlin` — Android compiles clean
|
|
- [ ] `./gradlew spotlessApply` — formatting clean
|
|
|
|
---
|
|
|
|
## Proof of Work Evidence
|
|
|
|
Paste terminal output of:
|
|
|
|
```bash
|
|
# 1. SOCKS port discovered
|
|
lsof -i -P -n | grep java | grep LISTEN
|
|
|
|
# 2. Tor routing confirmed
|
|
curl -s --socks5-hostname 127.0.0.1:PORT https://check.torproject.org/api/ip
|
|
|
|
# 3. IP mismatch confirmed
|
|
echo "Real: $(curl -s https://icanhazip.com) | Tor: $(curl -s --socks5-hostname 127.0.0.1:PORT https://icanhazip.com)"
|
|
|
|
# 4. Tests passing
|
|
./gradlew :commons:jvmTest :desktopApp:test :amethyst:testPlayDebugUnitTest 2>&1 | tail -5
|
|
```
|
|
|
|
Screenshot of:
|
|
- Shield icon green in sidebar
|
|
- Tor settings section showing Internal + Full Privacy
|
|
- Advanced dialog with all toggles ON
|