mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
Park phone DNS filtering: Private DNS fails closed, so the PC cannot host it
Measured on the Pixel 6a, twice: with private_dns_mode=hostname and the resolver unreachable, `ping example.com` returns "unknown host". Android's Private DNS does not fall back to the carrier's resolver -- hostname mode is RFC 8310's strict profile, and failing to establish TLS on 853 is a hard error. So a resolver living on this PC means "PC off" equals "phone has no internet", not merely "no filtering". That is disqualifying for a daily driver, and it disqualifies the whole home-hosted delivery path regardless of how well it works while the machine is up -- which it does; it was verified end to end. DNS_TRUSTED_DOT_HOST/IPS go back to empty, which restores the previous enforcer behaviour byte for byte. No code revert is needed, which is exactly why the flag was built to default empty. Two things found while testing are worth keeping. The nftables input chain is policy drop and admitted only ssh over wg0, so binding stunnel to the tunnel address was not sufficient on its own: ICMP passed while TCP 853 was dropped, which presents confusingly as "ping works but the port does not". That rule is now added explicitly and scoped to wg0. And setup_phone_wireguard.sh automates the client setup the tunnel needed, verifying handshake, reachability and DNS rather than assuming success, and deleting the pushed private key afterwards. The deeper reason the home endpoint could not work even with the tunnel up: dns.kuhy.duckdns.org resolves to the public IP while stunnel listens only on 10.8.0.1, and with AllowedIPs=10.8.0.0/24 that routes outside the tunnel to a closed port. Android never opened a connection at all. Split-horizon DNS does not fix it either, since on mobile data the phone uses the carrier's resolver. Research also ruled out the hosted-hybrid option that looked most attractive: no provider can ingest a 185k-domain list (NextDNS none, AdGuard 1-5K, ControlD 10K), and the free tiers fail OPEN -- they stop filtering past 300k queries a month while still resolving, which for a self-control tool is the worst failure mode available. Details and the remaining options are in the parked doc. The stunnel endpoint is left running deliberately: WireGuard-only, verified unreachable from LAN and WAN, and it makes the next attempt testable immediately. Removal commands are documented. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwepyL5Lxs5QKbKkMpxxxL
This commit is contained in:
parent
da6c5427ff
commit
143d260c1c
103
docs/dns-filtering-parked.md
Normal file
103
docs/dns-filtering-parked.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Phone DNS filtering: parked 2026-08-05
|
||||
|
||||
Unrooted DNS-level blocking is **on hold**, not abandoned. The code is in place
|
||||
and inert; what is missing is a resolver that is always reachable.
|
||||
|
||||
## Why it stopped
|
||||
|
||||
Android's Private DNS in `hostname` mode **fails closed**. Measured on the
|
||||
Pixel 6a (23181JEGR08034, Android 16), twice, by pinning
|
||||
`private_dns_specifier` to a host with no reachable DoT listener:
|
||||
|
||||
```
|
||||
settings put global private_dns_mode hostname
|
||||
ping example.com -> ping: unknown host example.com
|
||||
```
|
||||
|
||||
There is no fallback to the carrier's resolver. The phone simply has no DNS.
|
||||
This matches the spec rather than being a quirk: hostname mode is RFC 8310's
|
||||
strict profile, and Google's own Public DNS documentation states that failure to
|
||||
establish TLS on 853 is "a hard error and will result in no DNS service".
|
||||
|
||||
That disqualifies any design where the resolver lives on a machine that can be
|
||||
switched off. The home stack (dnsmasq + stunnel on this PC) works correctly and
|
||||
was verified end to end, but "PC off" means "phone has no internet", which is
|
||||
not acceptable for a daily driver.
|
||||
|
||||
## What was left running, and why
|
||||
|
||||
`stunnel-dot.service` is still active on `10.8.0.1:853` (WireGuard only, with
|
||||
`iifname "wg0" tcp dport 853 accept` in nftables). It is unreachable from the
|
||||
LAN and the WAN — verified — so it costs nothing to leave in place, and it
|
||||
makes the next attempt testable immediately.
|
||||
|
||||
To remove it entirely:
|
||||
|
||||
```bash
|
||||
sudo systemctl disable --now stunnel-dot.service dot-cert-sync.timer
|
||||
sudo rm -f /etc/stunnel/dot-resolver.conf /etc/systemd/system/stunnel-dot.service
|
||||
rm -f ~/gitea/sites/dns.caddy && docker exec gitea-caddy caddy reload \
|
||||
--config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
```
|
||||
|
||||
## What still needs deciding
|
||||
|
||||
An always-on resolver. Research (2026-08-05) narrowed this considerably, and
|
||||
**ruled out the hybrid hosted approach that looked most attractive**:
|
||||
|
||||
- **No hosted provider can ingest a 185k-domain list.** NextDNS supports no
|
||||
custom lists at all; AdGuard caps custom rules at 1K personal / 5K team;
|
||||
ControlD caps at 10K. All are two orders of magnitude short. "Provider lists
|
||||
for the bulk plus my custom entries" is therefore not available as designed —
|
||||
the bulk is the part that does not fit.
|
||||
- **Free tiers fail OPEN.** NextDNS and AdGuard both stop _filtering_ past
|
||||
300k queries/month while continuing to resolve. For a self-control tool that
|
||||
is the worst possible failure: it looks like it is working and silently is
|
||||
not. A single phone exceeds 300k without difficulty.
|
||||
- **Oracle Always Free reclaims idle instances** (<20% CPU over 7 days). A DNS
|
||||
resolver is idle by nature, so the free tier actively conflicts with the
|
||||
always-on requirement.
|
||||
- **NextDNS has no profile lock** — the account password is the only barrier,
|
||||
so it adds nothing on the tamper-resistance axis.
|
||||
|
||||
Remaining options, ranked:
|
||||
|
||||
1. **Cheap VPS running AdGuard Home** (Hetzner CAX11 ~€6/mo). Keeps full control
|
||||
of the 185,681-domain list, always on, and everything built this session
|
||||
ports unchanged. The only option that satisfies both always-on and list
|
||||
control.
|
||||
2. **Existing always-on hardware** (Pi, old phone, router), if any exists — same
|
||||
properties as the VPS, no monthly cost, but needs a device that genuinely
|
||||
stays powered.
|
||||
3. **NextDNS Pro ($19.90/yr)** — accept their category blocking (porn / gambling
|
||||
/ social / video, plus per-service YouTube) _instead of_ the StevenBlack list
|
||||
rather than alongside it. Covers the intent, not the exact list. Cheapest
|
||||
always-on path if list control is negotiable.
|
||||
|
||||
## What is already built and stays useful
|
||||
|
||||
- `python_pkg/focus_policy` — policy + hosts→domain conversion. Delivery
|
||||
agnostic; feeds any of the three options above. `to_dnsmasq_conf()` targets a
|
||||
self-hosted resolver, `to_domain_list()` a VpnService or an upload.
|
||||
- `phone_focus_mode/dns_enforcer.sh` — `DNS_TRUSTED_DOT_HOST` is **empty**, so
|
||||
behaviour is byte-identical to before. Set it to any trusted DoT hostname
|
||||
(hosted or self-hosted) and the enforcer allows that one endpoint through the
|
||||
853 block and pins Private DNS to it.
|
||||
- `setup_dot_resolver.sh` / `setup_phone_wireguard.sh` — both verified working.
|
||||
The WireGuard tunnel is up and handshaking; `--check` passes on both.
|
||||
- `focus_owner` — unaffected. Device Owner app-blocking is a separate layer and
|
||||
is the stronger one for tamper resistance anyway. Note that
|
||||
`DISALLOW_CONFIG_PRIVATE_DNS` (API 29+, device-owner only, applies globally)
|
||||
is confirmed to exist and is what would stop the phone's user editing Private
|
||||
DNS in Settings — so whichever resolver is eventually chosen, the lock comes
|
||||
from Device Owner, not from the provider. Pair it with `DISALLOW_CONFIG_VPN`.
|
||||
|
||||
## Gotcha for whoever picks this up
|
||||
|
||||
A DoT endpoint bound to a private address needs its **hostname to resolve to
|
||||
that private address**. `dns.kuhy.duckdns.org` resolves to the public IP, and
|
||||
with `AllowedIPs = 10.8.0.0/24` (split tunnel) that routes outside the tunnel to
|
||||
a closed port — Android never even opened a connection. Split-horizon DNS does
|
||||
not fix it either, because on mobile data the phone uses the carrier's resolver.
|
||||
A hosted provider avoids this entirely; a VPS avoids it by having a real public
|
||||
address that is meant to be reached.
|
||||
@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "Park unrooted DNS filtering; keep the WireGuard client setup and the firewall fix",
|
||||
"objective": "Stop pursuing phone-side DNS filtering until an always-on resolver exists. Android's Private DNS fails closed, so a resolver hosted on a machine that can be switched off leaves the phone with no internet at all - which is disqualifying for a daily driver. Success is: config.sh reverted so deploying is byte-identical to before, the phone left on working DNS, the reasoning and the ruled-out options recorded, and the working artifacts (WireGuard client setup, nftables scoping) kept rather than discarded.",
|
||||
"acceptance_criteria": [
|
||||
"DNS_TRUSTED_DOT_HOST and DNS_TRUSTED_DOT_IPS are empty, and the enforcer harness confirms an empty allowlist plus Private-DNS-forced-off, i.e. the pre-change behaviour",
|
||||
"The phone is left at private_dns_mode=off with DNS resolving",
|
||||
"The fail-closed behaviour is recorded as measured on-device, not inferred",
|
||||
"setup_dot_resolver.sh permits 853 on wg0 only - the input chain is policy drop and previously admitted only ssh over the tunnel, so binding to the tunnel address was not by itself sufficient",
|
||||
"setup_phone_wireguard.sh installs, pushes and verifies the tunnel, and deletes the pushed private key afterwards",
|
||||
"The parked doc names the specific reasons the hosted-hybrid option was ruled out, so it is not re-proposed"
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Signing up for any DNS provider",
|
||||
"Provisioning a VPS",
|
||||
"Tearing down the local stunnel DoT endpoint - it is WireGuard-only and unreachable from LAN and WAN, so it is left running for future testing",
|
||||
"Any Device Owner provisioning or factory reset"
|
||||
],
|
||||
"verifier": "The stubbed-iptables harness (14 checks, section A proves the revert); shellcheck + shfmt on both scripts; setup_phone_wireguard.sh --check; adb settings get global private_dns_mode plus a ping from the phone"
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
{
|
||||
"intent": "Stop pursuing phone-side DNS filtering until an always-on resolver exists, leaving the phone on working DNS and the config in its pre-change state, while keeping the artifacts that remain useful.",
|
||||
"scope": [
|
||||
"phone_focus_mode/config.sh - DNS_TRUSTED_DOT_HOST/IPS reverted to empty",
|
||||
"linux_configuration/scripts/single_use/features/setup_dot_resolver.sh - permit 853 on wg0",
|
||||
"linux_configuration/scripts/single_use/features/setup_phone_wireguard.sh - new",
|
||||
"docs/dns-filtering-parked.md - why it stopped and what was ruled out",
|
||||
"Non-goal: no provider signup, no VPS, no Device Owner provisioning",
|
||||
"Non-goal: the local stunnel DoT endpoint is left running deliberately"
|
||||
],
|
||||
"changes": [
|
||||
"Reverted DNS_TRUSTED_DOT_HOST/IPS to empty. That single change restores the previous behaviour exactly - no code revert needed, which is why the flag was built to default empty.",
|
||||
"Fixed a real gap found while testing: the nftables input chain is policy drop and admitted only ssh over wg0, so binding stunnel to the tunnel address was not sufficient. ICMP passed while TCP 853 was dropped, which presents as 'ping works but the port does not'. The rule is now added explicitly, scoped to wg0.",
|
||||
"Added setup_phone_wireguard.sh: installs the client, pushes the config, waits for the tunnel, deletes the pushed private key, and verifies handshake plus reachability plus DNS rather than assuming success.",
|
||||
"Recorded the ruled-out options with their specific reasons so the hosted-hybrid idea is not re-proposed."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "adb shell settings put global private_dns_mode hostname (resolver unreachable)",
|
||||
"result": "pass",
|
||||
"evidence": "'ping: unknown host example.com'. Reproduced twice on the Pixel 6a. Android Private DNS fails CLOSED with no carrier fallback, so a resolver on a machine that can be switched off leaves the phone with no internet. This is the measurement the whole decision rests on. Phone restored to private_dns_mode=off afterwards, pings resolving at 36ms."
|
||||
},
|
||||
{
|
||||
"command": "bash scratchpad/test_dot.sh against the reverted config",
|
||||
"result": "pass",
|
||||
"evidence": "14/14. Section A confirms zero ACCEPT rules and Private DNS forced off, i.e. byte-identical to the pre-change behaviour. The live config yields an empty v4 allowlist."
|
||||
},
|
||||
{
|
||||
"command": "setup_phone_wireguard.sh then --check",
|
||||
"result": "pass",
|
||||
"evidence": "Handshake from 31.61.251.198, phone reaches 10.8.0.1 over the tunnel, DNS resolves. Before the nftables fix the port test failed while ping succeeded; after it, nc to 853 connects and stunnel logs the connection."
|
||||
},
|
||||
{
|
||||
"command": "shellcheck + shfmt on both setup scripts and config.sh",
|
||||
"result": "pass",
|
||||
"evidence": "Clean. An SC2024 and an unused-variable warning were fixed by restructuring (install -m 600 instead of a sudo redirect, and asserting the client config assigns the expected peer address) rather than suppressed."
|
||||
},
|
||||
{
|
||||
"command": "ss -lnt | grep 853; systemctl is-active dnsmasq",
|
||||
"result": "pass",
|
||||
"evidence": "stunnel still bound to 10.8.0.1 only - never publicly bound. A split-horizon dnsmasq entry tried during debugging was removed and dnsmasq restarted; LAN DNS confirmed working afterwards."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"stunnel-dot.service and dot-cert-sync.timer are left running. They are WireGuard-only and verified unreachable from LAN and WAN, but they are live services with no current consumer; removal commands are in the parked doc.",
|
||||
"The dns.caddy site block remains, so Caddy keeps renewing a certificate for a hostname nothing consumes. Harmless but it is a live public endpoint that did not exist before.",
|
||||
"The nftables rule permitting 853 on wg0 was added at runtime and is not persisted by this repo's firewall management; it will not survive a ruleset reload unless setup_dot_resolver.sh is re-run.",
|
||||
"config.sh now carries the DNS_TRUSTED_DOT_* knobs with empty values. Harmless, but a future reader could set them without realising the always-on requirement that made them unusable here - hence the parked doc."
|
||||
],
|
||||
"rollback": [
|
||||
"The DNS_TRUSTED_DOT_* revert IS the rollback; nothing further is needed for the enforcer.",
|
||||
"To remove the DoT stack entirely, follow the commands in docs/dns-filtering-parked.md.",
|
||||
"After rollback validate: the enforcer harness still passes section A, and the phone still resolves DNS with private_dns_mode=off."
|
||||
]
|
||||
}
|
||||
@ -193,11 +193,23 @@ EOF
|
||||
}
|
||||
|
||||
open_firewall() {
|
||||
# Bound to WireGuard there is nothing to open: the tunnel is already
|
||||
# permitted and the listener is not reachable from the LAN or the WAN.
|
||||
# Binding to the WireGuard address is NOT sufficient on its own. The
|
||||
# input chain has `policy drop` and admits only specific ports over wg0
|
||||
# (ssh), so a DoT connection from the phone is dropped even though the
|
||||
# tunnel itself is up and ICMP replies fine. That combination is
|
||||
# confusing to debug -- ping works, the port does not -- so the rule is
|
||||
# added explicitly, scoped to the tunnel interface.
|
||||
if [[ "$BIND_ADDR" != "0.0.0.0" ]]; then
|
||||
log "No firewall change needed - $BIND_ADDR is WireGuard-only."
|
||||
log "No router port-forward needed either."
|
||||
if command -v nft >/dev/null 2>&1 && sudo nft list table inet filter >/dev/null 2>&1; then
|
||||
if sudo nft list table inet filter | grep -q "iifname \"wg0\" tcp dport $DOT_PORT"; then
|
||||
log "nftables already permits $DOT_PORT on wg0"
|
||||
else
|
||||
sudo nft add rule inet filter input iifname wg0 tcp dport "$DOT_PORT" accept ||
|
||||
log "WARNING: could not add nftables rule for $DOT_PORT on wg0"
|
||||
log "Permitted TCP $DOT_PORT on wg0 only"
|
||||
fi
|
||||
fi
|
||||
log "No router port-forward needed - $BIND_ADDR is WireGuard-only."
|
||||
return
|
||||
fi
|
||||
if command -v nft >/dev/null 2>&1 && sudo nft list table inet filter >/dev/null 2>&1; then
|
||||
|
||||
225
linux_configuration/scripts/single_use/features/setup_phone_wireguard.sh
Executable file
225
linux_configuration/scripts/single_use/features/setup_phone_wireguard.sh
Executable file
@ -0,0 +1,225 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# Get the phone onto the WireGuard tunnel, so it can reach the DoT resolver.
|
||||
#
|
||||
# Why this exists:
|
||||
# setup_dot_resolver.sh binds the DoT endpoint to 10.8.0.1 (WireGuard only),
|
||||
# which is what keeps it from being an open resolver on the internet. That
|
||||
# makes the tunnel a hard prerequisite: with it down, pinning Private DNS to
|
||||
# the resolver leaves the phone with no working DNS at all. This script
|
||||
# installs the client, pushes the config, and -- most importantly -- verifies
|
||||
# the tunnel actually carries DNS before anything starts depending on it.
|
||||
#
|
||||
# What it cannot do:
|
||||
# Android requires an explicit user tap to grant VPN permission and to
|
||||
# import a tunnel config; there is no ADB path around that, by design. The
|
||||
# script does everything either side of those taps and tells you exactly
|
||||
# what to press.
|
||||
#
|
||||
# Usage:
|
||||
# ./setup_phone_wireguard.sh [--serial <adb-serial>] [--check]
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
readonly DEFAULT_SERIAL="23181JEGR08034"
|
||||
readonly WG_PACKAGE="com.zaneschepke.wireguardautotunnel"
|
||||
readonly WG_FALLBACK_PACKAGE="com.wireguard.android"
|
||||
readonly SERVER_CONF="/etc/wireguard/wg0.conf"
|
||||
readonly CLIENT_CONF="/etc/wireguard/clients/phone.conf"
|
||||
readonly TUNNEL_ADDR="10.8.0.1"
|
||||
readonly PHONE_ADDR="10.8.0.2"
|
||||
readonly PUSH_PATH="/sdcard/Download/phone.conf"
|
||||
|
||||
SERIAL="$DEFAULT_SERIAL"
|
||||
CHECK_ONLY=0
|
||||
|
||||
log() { printf '[phone-wg] %s\n' "$1"; }
|
||||
die() {
|
||||
printf '[phone-wg] ERROR: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $SCRIPT_NAME [OPTIONS]
|
||||
--serial <id> adb serial (default: $DEFAULT_SERIAL)
|
||||
--check Verify the tunnel end to end, change nothing
|
||||
-h, --help Show this help
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
adb_sh() { adb -s "$SERIAL" shell "$@" 2>/dev/null | tr -d '\r'; }
|
||||
|
||||
require_device() {
|
||||
command -v adb >/dev/null 2>&1 ||
|
||||
die "adb not found; install android-tools"
|
||||
adb -s "$SERIAL" get-state >/dev/null 2>&1 ||
|
||||
die "device $SERIAL not connected"
|
||||
}
|
||||
|
||||
require_server() {
|
||||
sudo test -f "$SERVER_CONF" ||
|
||||
die "$SERVER_CONF missing - is WireGuard set up on this host?"
|
||||
sudo test -f "$CLIENT_CONF" ||
|
||||
die "$CLIENT_CONF missing - no client config to import"
|
||||
ip -brief addr show 2>/dev/null | grep -q "$TUNNEL_ADDR" ||
|
||||
die "$TUNNEL_ADDR is not up; start it with: sudo wg-quick up wg0"
|
||||
}
|
||||
|
||||
installed_client() {
|
||||
# Echo whichever WireGuard client is present, preferring the one already
|
||||
# whitelisted in phone_focus_mode.
|
||||
local pkg
|
||||
for pkg in "$WG_PACKAGE" "$WG_FALLBACK_PACKAGE"; do
|
||||
if adb_sh "pm list packages $pkg" | grep -q .; then
|
||||
printf '%s' "$pkg"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
install_client() {
|
||||
local pkg
|
||||
if pkg="$(installed_client)"; then
|
||||
log "WireGuard client already installed: $pkg"
|
||||
return 0
|
||||
fi
|
||||
# Play Store installs cannot be driven headlessly. Open the listing and
|
||||
# let the user tap Install; re-running the script then proceeds.
|
||||
log "No WireGuard client installed. Opening the Play Store listing..."
|
||||
adb -s "$SERIAL" shell am start -a android.intent.action.VIEW \
|
||||
-d "market://details?id=$WG_PACKAGE" >/dev/null 2>&1 || true
|
||||
die "install WireGuard on the phone, then re-run $SCRIPT_NAME"
|
||||
}
|
||||
|
||||
push_config() {
|
||||
# The config carries a private key, so it goes to Download/ only long
|
||||
# enough to be imported, and is deleted again in verify/cleanup.
|
||||
log "Pushing client config to $PUSH_PATH"
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
# shellcheck disable=SC2064 # expand $tmp now, not at trap time
|
||||
trap "rm -f '$tmp'" RETURN
|
||||
# The redirect is to our own mktemp file, not to the root-owned source;
|
||||
# install(1) makes that explicit and keeps the copy mode-0600.
|
||||
sudo install -m 600 -o "$USER" "$CLIENT_CONF" "$tmp" ||
|
||||
die "could not read $CLIENT_CONF"
|
||||
grep -q "^Address *= *$PHONE_ADDR" "$tmp" ||
|
||||
die "client config does not assign $PHONE_ADDR - wrong peer?"
|
||||
adb -s "$SERIAL" push "$tmp" "$PUSH_PATH" >/dev/null ||
|
||||
die "adb push failed"
|
||||
}
|
||||
|
||||
prompt_import() {
|
||||
local pkg
|
||||
pkg="$(installed_client)" || die "no WireGuard client installed"
|
||||
log "Opening $pkg - complete these steps on the phone:"
|
||||
log " 1. '+' -> Import from file -> Download/phone.conf"
|
||||
log " 2. Toggle the tunnel ON"
|
||||
log " 3. Accept Android's VPN permission dialog"
|
||||
adb -s "$SERIAL" shell monkey -p "$pkg" \
|
||||
-c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
cleanup_pushed_config() {
|
||||
# The pushed file contains a private key; do not leave it in Download/.
|
||||
adb_sh "rm -f $PUSH_PATH" >/dev/null 2>&1 || true
|
||||
log "Removed $PUSH_PATH from the phone"
|
||||
}
|
||||
|
||||
run_check() {
|
||||
local ok=0
|
||||
|
||||
if ! installed_client >/dev/null; then
|
||||
log "FAIL no WireGuard client installed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# A handshake proves the tunnel is established from the server's side;
|
||||
# the phone's own interface list is not visible without root.
|
||||
local handshake
|
||||
handshake="$(sudo wg show wg0 latest-handshakes 2>/dev/null | awk '{print $2}' | head -1)"
|
||||
if [[ -z "$handshake" || "$handshake" == "0" ]]; then
|
||||
log "FAIL no WireGuard handshake yet - is the tunnel toggled on?"
|
||||
ok=1
|
||||
else
|
||||
log "OK handshake $(($(date +%s) - handshake))s ago"
|
||||
fi
|
||||
|
||||
# The decisive test: not "is a VPN up" but "does the phone reach the
|
||||
# resolver". Everything downstream depends on this one answer.
|
||||
if adb_sh "ping -c 2 -W 3 $TUNNEL_ADDR" | grep -q " 0% packet loss"; then
|
||||
log "OK phone reaches $TUNNEL_ADDR over the tunnel"
|
||||
else
|
||||
log "FAIL phone cannot reach $TUNNEL_ADDR"
|
||||
ok=1
|
||||
fi
|
||||
|
||||
# And that DNS actually resolves through it, which is the thing that
|
||||
# broke when this was attempted without a tunnel.
|
||||
if adb_sh "ping -c 2 -W 3 example.com" | grep -q " 0% packet loss"; then
|
||||
log "OK DNS resolves on the phone"
|
||||
else
|
||||
log "FAIL DNS does not resolve on the phone"
|
||||
ok=1
|
||||
fi
|
||||
|
||||
if [[ $ok -eq 0 ]]; then
|
||||
log "Tunnel healthy. Safe to set in phone_focus_mode/config.sh:"
|
||||
log " DNS_TRUSTED_DOT_HOST=\"dns.kuhy.duckdns.org\""
|
||||
log " DNS_TRUSTED_DOT_IPS=\"$TUNNEL_ADDR\""
|
||||
fi
|
||||
return "$ok"
|
||||
}
|
||||
|
||||
main() {
|
||||
require_device
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
run_check
|
||||
exit $?
|
||||
fi
|
||||
|
||||
require_server
|
||||
install_client
|
||||
push_config
|
||||
prompt_import
|
||||
|
||||
log "Waiting up to 180s for the tunnel to come up..."
|
||||
local deadline=$((SECONDS + 180))
|
||||
while ((SECONDS < deadline)); do
|
||||
if adb_sh "ping -c 1 -W 2 $TUNNEL_ADDR" | grep -q " 0% packet loss"; then
|
||||
log "Tunnel is up."
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
cleanup_pushed_config
|
||||
run_check || {
|
||||
log "Tunnel not verified. Re-run with --check after toggling it on."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--serial)
|
||||
SERIAL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--check)
|
||||
CHECK_ONLY=1
|
||||
shift
|
||||
;;
|
||||
-h | --help) usage ;;
|
||||
*) die "Unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
main "$@"
|
||||
@ -242,11 +242,11 @@ export DNS_LOG="$STATE_DIR/dns_enforcer.log"
|
||||
# Leave EMPTY to keep the original behaviour (Private DNS forced off, all
|
||||
# 853 rejected). Do not point this at a public resolver: the whole point is
|
||||
# that the resolver applies your blocklist. See python_pkg/focus_policy.
|
||||
export DNS_TRUSTED_DOT_HOST="dns.kuhy.duckdns.org"
|
||||
export DNS_TRUSTED_DOT_HOST=""
|
||||
# Where to resolve DNS_TRUSTED_DOT_HOST when Private DNS is pinned to it.
|
||||
# Chicken-and-egg: the resolver's own name must resolve without using it.
|
||||
# Space-separated IPv4/IPv6 literals; leave empty to resolve at runtime.
|
||||
export DNS_TRUSTED_DOT_IPS="10.8.0.1"
|
||||
export DNS_TRUSTED_DOT_IPS=""
|
||||
# iptables chain used exclusively by us; we flush+refill it every check.
|
||||
export DNS_IPT_CHAIN="FOCUS_DNS_BLOCK"
|
||||
# DoH/DoT endpoints to DROP. Well-known public resolvers used by browsers
|
||||
|
||||
Loading…
Reference in New Issue
Block a user