From 64484cd484192b70572526b85182a5159d66c6bf Mon Sep 17 00:00:00 2001 From: windyboy Date: Mon, 3 Aug 2026 21:10:57 +0800 Subject: [PATCH] Document UniFi OpenClash proxy bypass --- AGENTS.md | 3 ++ docs/unifi-openclash-localhost.md | 60 +++++++++++++++++++++++++++++++ hosts/gfw.windy.lan.md | 40 +++++++++++++++++++++ hosts/ubnt.md | 58 ++++++++++++++++++++++++++++++ inventory/hosts.md | 2 ++ 5 files changed, 163 insertions(+) create mode 100644 docs/unifi-openclash-localhost.md create mode 100644 hosts/gfw.windy.lan.md create mode 100644 hosts/ubnt.md diff --git a/AGENTS.md b/AGENTS.md index cf1e26a..e8ebc6c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,8 @@ Also readable as `agent.md` (symlink → this file). | **mx2.windy.me** | mailcow (`/opt/mail`, project `cow`) | `ssh -4 windy@mx2.windy.me` | [hosts/mx2.windy.me.md](hosts/mx2.windy.me.md) | | **us2.wsvc.info** | Vaultwarden + Traefik (+ Soft Serve, …) | `ssh -4 windy@us2.wsvc.info` | [hosts/us2.wsvc.info.md](hosts/us2.wsvc.info.md) | | **hk2.chans.xyz** | PowerDNS auth ns1 (`/opt/pdns`) | `ssh -4 windy@hk2.chans.xyz` | [hosts/hk2.chans.xyz.md](hosts/hk2.chans.xyz.md) | +| **gfw.windy.lan** | OpenWrt LAN gateway / OpenClash | `ssh -4 root@192.168.66.1` | [hosts/gfw.windy.lan.md](hosts/gfw.windy.lan.md) | +| **ubnt** | UniFi Network Controller | `ssh -4 windy@192.168.66.46` | [hosts/ubnt.md](hosts/ubnt.md) | ### Public services @@ -68,6 +70,7 @@ Also readable as `agent.md` (symlink → this file). | Matrix health | [runbooks/matrix-health.md](runbooks/matrix-health.md) | | Matrix upstream refs | [docs/matrix-upstream.md](docs/matrix-upstream.md) | | Hermes Agent Matrix channel | [docs/hermes-matrix.md](docs/hermes-matrix.md) | +| UniFi local-service proxy bypass | [docs/unifi-openclash-localhost.md](docs/unifi-openclash-localhost.md) | | Routine Ansible operations | [runbooks/ansible-operations.md](runbooks/ansible-operations.md) | ```bash diff --git a/docs/unifi-openclash-localhost.md b/docs/unifi-openclash-localhost.md new file mode 100644 index 0000000..7e133ac --- /dev/null +++ b/docs/unifi-openclash-localhost.md @@ -0,0 +1,60 @@ +# UniFi local HTTPS through OpenClash — proxy bypass contract + +## Symptom + +OpenClash on `gfw.windy.lan` can log: + +```text +[TCP] dial DIRECT (match IPCIDR/127.0.0.0/8) 192.168.66.46: --> localhost:8443 +error: dial tcp 127.0.0.1:8443: connect: connection refused +``` + +This does **not** mean OpenClash should listen on port 8443. The gateway's +loopback interface has no UniFi service. + +## Cause + +`ubnt` runs the UniFi controller locally on `*:8443`. Its container also uses +HTTP/HTTPS proxy variables. If `NO_PROXY` / `no_proxy` omit loopback addresses, +a request for its own `localhost:8443` is sent to the OpenClash HTTP proxy. +OpenClash correctly applies its loopback direct rule, but that direct request is +then made from the gateway, where port 8443 is not open. + +## Required configuration + +In `/home/windy/unifi-9/compose.yml`, keep both environment entries enabled: + +```yaml +environment: + - NO_PROXY=localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 + - no_proxy=localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 +``` + +Keep the upper- and lowercase forms because different processes and libraries +consult different spellings. + +## Apply and verify + +```bash +cd /home/windy/unifi-9 +docker compose config -q +docker compose up -d --force-recreate unifi +docker inspect --format '{{.State.Health.Status}}' unifi-controller +curl -k -sS -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8443/ +``` + +Expected result: Docker health is `healthy` and the local HTTPS request returns +an HTTP success or redirect response. Recheck OpenClash logs afterwards; the +`localhost:8443` connection-refused entries should stop. + +## Safety + +- Do not add a dummy listener on the gateway's `127.0.0.1:8443`. +- Do not solve this with a router redirect rule; that would hide a client-side + proxy error and could send local controller traffic to the wrong host. +- Back up `compose.yml` before changing it and avoid `docker compose down -v`. + +## References + +- [gfw.windy.lan host facts](../hosts/gfw.windy.lan.md) +- [ubnt host facts](../hosts/ubnt.md) diff --git a/hosts/gfw.windy.lan.md b/hosts/gfw.windy.lan.md new file mode 100644 index 0000000..9b012bf --- /dev/null +++ b/hosts/gfw.windy.lan.md @@ -0,0 +1,40 @@ +# gfw.windy.lan — OpenWrt LAN gateway + +## Role and access + +| Item | Value | +|---|---| +| Role | LAN gateway and OpenClash transparent/explicit-proxy host | +| IPv4 | `192.168.66.1` | +| SSH | `ssh -4 root@192.168.66.1` | +| OS | OpenWrt, Linux `6.12.87` | + +Do not store the root password in this repository. Prefer a verified SSH key for +future management access. + +## OpenClash + +OpenClash runs `/etc/openclash/clash` with configuration +`/etc/openclash/pass-cat.yaml`. + +| Listener | Port | +|---|---:| +| HTTP | `7890` | +| SOCKS | `7891` | +| Redirect | `7892` | +| Mixed | `7893` | +| TPROXY | `7895` | +| DNS | `7874` | + +`8443` is not an OpenClash listener and has no runtime nftables forwarding rule. +It is included only in OpenClash's common TLS-sniffing port list. + +## Operational note + +When OpenClash logs a LAN client request such as +` --> localhost:8443` followed by a connection refusal, it is normally +an explicit-proxy client sending its own loopback request to the gateway. Fix +the originating host's `NO_PROXY` / `no_proxy` configuration; do not create a +listener on this router to mask the problem. See +[the UniFi localhost proxy note](../docs/unifi-openclash-localhost.md). + diff --git a/hosts/ubnt.md b/hosts/ubnt.md new file mode 100644 index 0000000..4499c77 --- /dev/null +++ b/hosts/ubnt.md @@ -0,0 +1,58 @@ +# ubnt — UniFi Network Controller + +## Role and access + +| Item | Value | +|---|---| +| Role | UniFi Network Controller host | +| IPv4 | `192.168.66.46` | +| SSH | `ssh -4 windy@192.168.66.46` | +| Hostname | `ubnt` | +| Privilege | `windy` has passwordless sudo and is in the `docker` group | + +The current SSH ED25519 host-key fingerprint is +`SHA256:TTZtonJnnnNS6vk4xqo+CJPuZGXwwkf46wR1SLmqc10` (verified 2026-08-03). +Verify a changed key out of band before accepting it. + +## UniFi stack + +| Item | Value | +|---|---| +| Compose directory | `/home/windy/unifi-9` | +| Compose file | `/home/windy/unifi-9/compose.yml` | +| Compose service | `unifi` | +| Container | `unifi-controller` | +| Image | `jacobalberty/unifi:v9.5.21` | +| Network mode | host | +| HTTPS UI | `https://192.168.66.46:8443` | + +The controller binds `*:8443`. A local HTTPS request to +`https://127.0.0.1:8443/` is the primary quick health check. + +## Proxy contract + +The container has HTTP/HTTPS proxy environment variables. Its Compose +`environment` list must keep **both** `NO_PROXY` and `no_proxy` enabled and +include at least: + +```text +localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 +``` + +Without this bypass, a controller request to `localhost:8443` can be sent to +the OpenClash gateway instead of remaining local. Details and verification are +in [the UniFi localhost proxy note](../docs/unifi-openclash-localhost.md). + +## Safe reconciliation and verification + +```bash +cd /home/windy/unifi-9 +docker compose config -q +docker compose up -d --force-recreate unifi +docker inspect --format '{{.State.Health.Status}}' unifi-controller +curl -k -sS -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8443/ +``` + +Recreating the service briefly interrupts the controller. Do not use `docker +compose down -v`; it can remove persistent data. + diff --git a/inventory/hosts.md b/inventory/hosts.md index 71594b1..cd27b7f 100644 --- a/inventory/hosts.md +++ b/inventory/hosts.md @@ -20,6 +20,8 @@ diagnosis and procedures that are deliberately interactive or destructive; see | pdns.wsvc.info | Poweradmin UI | — (HTTPS) | → hk2 | active | see hk2 | | pgweb.wsvc.info | PowerDNS Postgres UI | — (HTTPS) | → hk2 | active | see hk2 | | **synapse.chans.xyz** | Matrix homeserver (ESS: Synapse + MAS + Element) | `ssh -4 windy@synapse.chans.xyz` | `169.58.86.13` | **active** | [hosts/synapse.chans.xyz.md](../hosts/synapse.chans.xyz.md) | +| **gfw.windy.lan** | OpenWrt LAN gateway / OpenClash | `ssh -4 root@192.168.66.1` | `192.168.66.1` | **active** | [hosts/gfw.windy.lan.md](../hosts/gfw.windy.lan.md) | +| **ubnt** | UniFi Network Controller | `ssh -4 windy@192.168.66.46` | `192.168.66.46` | **active** | [hosts/ubnt.md](../hosts/ubnt.md) | `status: stub` = known to exist; fill `hosts/.md` when next touched.