- PHP 64.3%
- Python 35.7%
| client | ||
| server | ||
| tools | ||
| .gitignore | ||
| LICENSE | ||
| README.md | ||
Tube
Turn a cheap PHP + nginx shared host (FTP-only deploy, PHP running as FastCGI) into an internet egress point. You run a local SOCKS5 proxy; its traffic is tunneled over plain HTTPS to a PHP endpoint on the host, which holds the real TCP sockets and — optionally — forwards everything through an upstream SOCKS5 / HTTP proxy.
It works within the harsh limits of shared hosting:
- nginx fully buffers request bodies → no full-duplex in one request, so up/down traffic is split.
- Very few PHP-FPM workers (often ~5) → all tunneled connections are multiplexed over one streaming response + one batched uplink, so a browser opening hundreds of sockets still only keeps 2–3 workers busy.
- A persistent daemon survives FPM requests (spawned via
setsid) and owns the sockets in a non-blocking event loop. - The public domain renders a decoy video-hosting site; the proxy endpoint returns a normal-looking 404 to anyone without the secret.
Run this only on infrastructure you own or are authorized to use. Forwarding third-party traffic through a host may violate its ToS. Authentication (a strong
TUBE_SECRET) is mandatory — without it you expose an open proxy. The wire is protected by the host's HTTPS; there is no extra end-to-end crypto layer.
Layout
server/ # deploy these to the host docroot (via FTP)
index.php # decoy cover site (public landing page)
api.php # proxy control endpoint (disguised, auth-gated)
tubed.php # persistent daemon (CLI only; holds sockets, multiplexes)
upstream.php # optional SOCKS5 / HTTP-CONNECT upstream dialer
config.example.php # copy to config.php, set your secret
client/
client.py # local SOCKS5 proxy (stdlib only)
tools/
hostcheck.py # verify a host + measure its FPM worker pool
_hostcheck.php # probe uploaded by hostcheck.py (auto-deleted)
deploy.py # upload server/ over FTP
1. Check your host
Confirm the host supports everything (sockets, proc_open, CLI php, egress, detached-process
survival) and see how many FPM workers it gives you:
python3 tools/hostcheck.py --url https://YOUR-DOMAIN \
--ftp-host H --ftp-user U --ftp-pass P
It prints a capability report, the worker-pool estimate, and a PASS/FAIL verdict.
2. Configure & deploy
cp server/config.example.php server/config.php
php -r "echo bin2hex(random_bytes(24)).PHP_EOL;" # generate a secret
# edit server/config.php: set TUBE_SECRET (and TUBE_UPSTREAM if you want chaining)
python3 tools/deploy.py --ftp-host H --ftp-user U --ftp-pass P
config.php is gitignored — it holds your secret. Verify the cover site loads in a browser
(https://YOUR-DOMAIN/) and that api.php returns a 404 page without the secret.
3. Run the client
python3 client/client.py --url https://YOUR-DOMAIN/api.php --secret <TUBE_SECRET>
# then point apps/OS at socks5h://127.0.0.1:1080
Test it:
curl -x socks5h://127.0.0.1:1080 https://api.ipify.org # shows the host's (or upstream's) IP
Upstream chaining
Forward all egress through an external proxy by setting TUBE_UPSTREAM in config.php
(URL-encode special characters in the password):
const TUBE_UPSTREAM = 'socks5://user:pass@1.2.3.4:1080'; // or http://user:pass@host:port
Protocol (for the curious)
api.php relays line-framed JSON control ops to the daemon over a Unix socket:
open{host,port}→{id}— open a target connection.push{ups:[[id,b64]...], closes:[id...]}→{ok}— batched client→target data + closes.sub→ a streamed response of length-prefixed frames<u32 len><json {id,d:b64}|{id,eof:1}>carrying target→client data for all connections; reopened every ~20s.ping→{pong, conns}.
License
BSD3-Clear — see LICENSE.