docs(us4): sing-box :6443 代理 + 根盘回收 91%→43% (Plane VPS-99/100)

新增独立 HTTPS forward proxy:Traefik 新 entrypoint proxyin(:6443) 终止 TLS,
后端 sing-box(HTTP inbound + users 认证 + direct 出站),与既有 :443
nghttpx/squid 链路并存。宿主机只新增 6443/tcp;容器不发布 host 端口。

新增 compose/sing-box/(镜像 digest 钉版)与两份 runbook:
- proxy-health.md::443/:6443 只读健康检查,含开放代理负向测试
- proxy-client-setup.md:curl / env / 浏览器扩展配置与三类失败排查
  (407 无认证头、页面加载不全=CDN 域名漏配、urllib/wget 不支持 TLS-to-proxy)

同批根盘回收 91%→43%(约 14G):rust/node/.go/podman 孤儿存储/apt/journal/
未用镜像/容器日志;journald 加 SystemMaxUse=200M;nghttpx+squid+traefik 加
json-file 轮转 10m×3;删除 ~/.cargo 后修好 zshenv/bashrc/profile 的失效 source。

验证::6443 LE 证书、匿名 407、CONNECT 与绝对 URI 均返回 185.201.226.122;
us4-gate 401 / update 200 / trlm 302;8 容器全部 Up;validate-repo.sh PASS。
This commit is contained in:
windyboy
2026-09-26 10:34:09 +08:00
parent 1512dde3ec
commit 7ec29627fb
7 changed files with 574 additions and 2 deletions
+127 -2
View File
@@ -52,7 +52,8 @@ do not build its firewall allowlist from the WireGuard role alone.
| TCP `3000` | Semaphore UI direct Docker publish | Open; redundant with the Traefik route and should be removed or bound to loopback |
| TCP `8080` | Traefik direct Docker publish | Open; redundant with the authenticated dashboard route and should be removed or bound to loopback |
| UDP `51820` | WireGuard | Required public endpoint |
| TCP `9443` | Host nghttpx-to-Squid proxy | Listening but blocked by the current firewall |
| TCP `6443` | Traefik `proxyin` entrypoint → `sing-box` HTTP proxy (`/opt/sing-box`) | Open (Docker publish); added 2026-10-01, VPS-99 |
| TCP `9443` | Host nghttpx-to-Squid proxy — **legacy, retirement pending** (see HTTPS forward proxy below) | Listening but blocked by the current firewall |
| UDP `123` | ntpsec | Listening but blocked by the current firewall |
PostgreSQL (`5433`/`5434`/`5435`), MariaDB (`3306`), and the host Squid TCP
@@ -77,6 +78,93 @@ and reverify Docker routing. Tracking: Linear `W1N-60`.
has no global IPv6 address or IPv6 default route. Treat that as a separate
secondary-MX reachability issue.
## HTTPS forward proxy (verified 2026-09-26)
The canonical HTTPS forward proxy on this host is:
| Item | Value |
|------|-------|
| Endpoint | `https://us4.wsvc.info:443` (Traefik TLS, ACME-managed) |
| Chain | Traefik `:443` `HostSNI(us4.wsvc.info)` → container `nghttpx-proxy:3000` (no-TLS) → `squid-backend:3128` |
| Config source | `/opt/nghttpx/compose.yml`, `nghttpx.conf`, `squid.conf` (server files are authoritative; not yet repo-owned) |
| Credential | `/opt/nghttpx/passwords`, owner `root:proxy` (`0:13`) mode `0640`, bcrypt (`$2y$`, cost 12). Squid reads it through a read-only bind mount; the helper runs as `13:13`, so any permission change must keep group `proxy` read access. |
| Username | `windy` |
| Password | **Not stored in this repository or Plane.** The plaintext lives only in the operator's password manager; the server holds the bcrypt hash. Rotate with `htpasswd -B -C 12`, validate against `basic_ncsa_auth` before replacing the file, then `docker stop squid-backend` → write in place → `docker start squid-backend` (a rename would break the bind mount). |
| Auth behaviour | No/incorrect credentials → `407`; correct credentials → tunnel. Verified egress IP `185.201.226.122` |
| Destination policy | HTTPS-tunnel only: `Safe_ports`/`SSL_ports` = `443`; CONNECT to other ports and destinations in `127/8`, `10/8`, `172.16/12`, `192.168/16`, `169.254/16` (metadata), `100.64/10`, `224/4`, `::1`, `fc00::/7`, `fe80::/10`, `ff00::/8` return `403` `ERR_ACCESS_DENIED` |
| Limitation | Plain-HTTP (absolute-URI) proxying is **not** supported; non-CONNECT requests fail with `400`. Clients must use HTTPS URLs. |
| Credential scope | No longer shared with `us2.wsvc.info` (the two hosts had an identical `/opt/nghttpx/passwords` hash before 2026-09-26; us4 now has its own) |
Client settings (username `windy`, password from the password manager):
```text
Proxy type: HTTP/HTTPS proxy (CONNECT)
Host: us4.wsvc.info
Port: 443
Username: windy
Password: <from password manager>
```
`curl` equivalent (replace `$PROXY_PASSWORD` from the password manager; never inline it in shell history on a shared host):
```bash
curl -x https://us4.wsvc.info:443 -U "windy:$PROXY_PASSWORD" https://api.ipify.org
# expected: 185.201.226.122
```
The `9443` path (`nghttpx.service` → host `squid.service` on `127.0.0.1:3128`) is a legacy
stack: its certbot-issued certificate expired 2026-01-11, the host Squid has no
authentication at all, and no historical client traffic was found in the nghttpx access
log. It is blocked by the firewall and reachable only from the host itself. Retirement
(stop + disable both units, keep config for rollback) is pending.
Rollback material from the 2026-09-26 change: `/opt/nghttpx/backups/20260926T091907+0800`
(mode `0700`, includes the pre-change password file). Keep it protected and delete it once
the change is accepted.
### Independent `sing-box` proxy on `:6443` (added 2026-10-01, VPS-99)
A second, independent HTTPS forward proxy on `6443`. It does not touch the `:443`
chain above, and the `:443`/`:9443` stacks keep running.
| Item | Value |
|------|-------|
| Endpoint | `https://us4.wsvc.info:6443` (Traefik TLS, ACME-managed) |
| Chain | Traefik TCP router `singbox-proxy` (entrypoint `proxyin`, `HostSNI(us4.wsvc.info)`, `tls=true`, resolver `letsencrypt`) → `sing-box:8080` |
| Config source | `/opt/sing-box/compose.yml` + `config.json` (server authoritative); repo copy `compose/sing-box/compose.yml` |
| Image | `ghcr.io/sagernet/sing-box:v1.14.2@sha256:afbffd294c5eb3519cc7b4587299ef189bb0a2ca2f667cb6301fdb6b9bce9558` |
| Credential | sing-box `users` in `config.json` (mode `0600`); operator copy in `/opt/sing-box/.proxy-password`. Not in this repo or Plane. |
| Capability | Supports **both** CONNECT tunnels and absolute-URI plain-HTTP requests, unlike the `:443` path |
| Host port | The container publishes no port; `6443` is bound by Traefik |
Traefik changes live in `/opt/traefik/compose.yml`:
`--entrypoints.proxyin.address=:6443` plus the `6443:6443` publish. Changing either
requires recreating the Traefik container (`docker compose up -d`), not
`docker compose restart`.
`6443` was free before the change (nothing listening; `nginx` inactive,
`certbot`/`apache2` failed). Docker-published ports land in the firewalld `docker`
zone, not `public`, so a public-zone `--add-port=6443` is neither needed nor
effective; source restriction belongs in the `DOCKER-USER` chain or a Traefik TCP
middleware. `6443` is a public TCP port beyond the target exposure recorded in
Plane VPS-47 and still needs an explicit amendment there.
Client notes — full detail in
[runbooks/proxy-client-setup.md](../runbooks/proxy-client-setup.md):
- The client's proxy type must be **HTTPS** (TLS to the proxy). A plaintext HTTP
proxy entry reaches Traefik in cleartext and gets `404`, not a proxied request.
- Browser extensions (Zero Omega / SwitchyOmega) must have the username and
password saved in the proxy profile, and the routing conditions must include
the site's CDN hostnames (for `x.com`: `twimg.com`, `api.x.com`, `t.co`), not
only the landing domain — otherwise the page loads incompletely while the
server-side log shows no `inbound connection to` line for those hosts.
- `python3 urllib` and `wget` cannot speak TLS to a proxy and fail with
`Tunnel connection failed: 404 Not Found`.
- Client-side symptoms are diagnosed from the server with
`docker logs -f sing-box | grep 'inbound connection to'`; `no
Proxy-Authorization header` means the client sent no credentials.
## Safety
- Private keys, preshared keys, peer configuration files, and QR codes remain
@@ -144,6 +232,10 @@ ansible-playbook playbooks/wireguard-harden.yml --limit wireguard \
-e '{"wireguard_harden_confirm": true}'
```
Forward-proxy health for both endpoints (`:443` and `:6443`) is read-only:
follow [runbooks/proxy-health.md](../runbooks/proxy-health.md). No proxy
credential is stored in this repository.
## Verification
- Container `wireguard` is running and interface `wg0` exists.
@@ -175,7 +267,8 @@ Open host-level follow-up (do not conflate these with a WireGuard outage):
1. **Disk capacity:** `/` was 90% used with about 3.4 GiB free. Docker reported
about 2.48 GB of reclaimable images and the system journal used about 1.9
GB, but do not prune or vacuum without reviewing retention and rollback
needs first.
needs first. — **Resolved 2026-09-26 (91% → 43%); see *Root disk recovery*
below.**
2. **Docker exposure:** the firewalld public-zone cleanup is complete, but
Docker still publishes `3000` and `8080` outside the ordinary host INPUT
path. Remove those redundant Compose bindings in separate maintenance units
@@ -191,3 +284,35 @@ Open host-level follow-up (do not conflate these with a WireGuard outage):
limit and uses Docker's `json-file` log driver without a per-container
rotation setting. Current log size was small, but limits/rotation should be
considered during a reviewed Compose update.
## Root disk recovery (2026-09-26, Plane VPS-100)
`/` went from **91% used (3.0G free)** to **43% (18G free)** — about 14G
reclaimed, following [runbooks/host-disk-cleanup.md](../runbooks/host-disk-cleanup.md).
Removed: `~/.nvm` 2.6G, `~/.go` 1.9G, `~/.rustup` 1.4G, `~/.cargo` 505M,
`~/.npm` 78M, stale rootless-podman storage under `~/.local/share/containers`
1.7G, `~/sigstore` 953M, `~/softz` 910M, `~/.cache` 295M, apt archives 613M,
the container JSON logs (594M → 320K), and six unreferenced Docker images
(`postgres:17`, `postgres:18`, `traefik:v3.4`, `zadam/trilium:0.60.4`,
`linuxserver/wireguard:latest`, `traefik/whoami:latest` — all re-pullable).
Durable changes, not just deletions:
- `journalctl --vacuum-size=100M` plus drop-in
`/etc/systemd/journald.conf.d/00-disk-us4.conf` (`SystemMaxUse=200M`);
journald was restarted so the cap is effective (journal back to 83M). Without
this the journal regrows toward its default 10%-of-filesystem limit.
- Log rotation `max-size: 10m` / `max-file: 3` added to `nghttpx-proxy`,
`squid-backend` (`/opt/nghttpx/compose.yml`) and `traefik`
(`/opt/traefik/compose.yml`); both stacks were recreated and verified. This
closes the unbounded `json.log` growth for these containers.
- `~/.zshenv`, `~/.bashrc`, `~/.profile` and `~/.zshrc` had their `~/.cargo/env`
and nvm references commented out after those trees were deleted; backups are
`~/.<file>.bak-20260926-disk`. Also note `~/.go/bin/cosign` was removed and is
re-installable.
Deliberately kept: `~/.local/share/trilium-data` (88M) is a 2022 Trilium
database, not a cache. Still removable if unneeded: `/opt/zeroclaw/target`
1.4G, `/opt/trilium-linux-x64-server` 592M (2023 host install, unit disabled),
`/opt/go` 167M (2017).