2026-09-15 17:45:58 +03:00
upd
2026-09-15 17:45:58 +03:00
upd
2026-09-15 17:45:58 +03:00

GSproxy

GSproxy is a standalone Perl tool that reads a CSV list of remote targets and keeps a working SOCKS proxy alive for each of them. It brings up remote SOCKS servers over an MCP shell bridge, starts local gs-netcat SOCKS clients, watches every proxy, and exposes a single gateway SOCKS port that always points at the first healthy proxy.

Overview

The tool turns a list of endpoint,secret,port rows into a self-healing set of SOCKS proxies. For each row it does three things:

  • Brings up a remote SOCKS server through an MCP Tools_Shell_Command call, using a gs-netcat listener wrapped to look like python.
  • Starts a local gs-netcat SOCKS client bound to 127.0.0.1 on the row's port.
  • Monitors the local SOCKS proxy and logs every status change.

It then listens on a gateway SOCKS port and relays each incoming connection to the first proxy that is currently alive.

The whole tool is written with core Perl modules only, so it runs without extra installs.

Features

  • CSV driven config, one proxy per row.
  • Remote bring-up through an MCP shell call, reusing the gs-netcat listener command shape.
  • Local gs-netcat SOCKS client launched per row.
  • Continuous monitoring with configurable interval and per-check timeout.
  • Automatic re-bring-up of dead proxies in the background.
  • A gateway SOCKS port that follows the first alive proxy.
  • Daemon mode with pidfile, status, and stop commands.
  • Remote and local teardown that kills leftover gs-netcat processes.
  • Optional SOCKS5 username/password auth on the gateway port.
  • Optional SHA-256 pinning of the remote gs-netcat binary.
  • Offline self-tests that cover config parsing, command building, secret redaction, the single-instance lock, gateway auth, and the SOCKS handshake.

Requirements

  • Perl 5 with the core modules Getopt::Long, IO::Socket::INET, IO::Select, POSIX, HTTP::Tiny, JSON::PP, Time::HiRes, File::Spec, Errno, File::Basename, Fcntl, and IO::Handle.
  • The gs-netcat binary in PATH, or a path supplied with --gs-netcat.
  • Network access from the MCP endpoint so the remote bring-up command can download and run gs-netcat.
  • A reachable MCP endpoint that exposes the Tools_Shell_Command tool.

Installation

  • Copy gsproxy.pl somewhere on the host and make it executable with chmod +x gsproxy.pl.
  • Make sure gs-netcat is installed and on the PATH.
  • Create a gsproxy.csv next to the script, or point --config at another file.
  • The script runs with umask 0077; runtime files are created mode 0600.

Configuration

The config file is CSV. Each non-empty, non-comment line has the form:

endpoint,secret,port[,label]
  • endpoint is the base URL of the MCP server that exposes Tools_Shell_Command.
  • secret is the gs-netcat shared secret used by both the remote listener and the local client.
  • port is the local SOCKS port for that row. It must be a number from 1 to 65535.
  • label is an optional friendly name. When missing it defaults to row plus the line number.

Blank lines and lines starting with # are ignored. A header line such as endpoint,secret,port,label is detected and skipped when the third field is not a number. Invalid rows are reported on standard error and skipped.

Example:

endpoint,secret,port,label
https://mcp.example.org,sekret123,20001,alpha
https://mcp.other.org,topsecret9,20002,beta

The endpoint may be a base URL or a full MCP URL. If it does not already end in /gradio_api/mcp or /mcp, the value of --mcp-path is appended.

Keep the config file mode 0600; the script warns when it is group/world readable.

Usage

Run the tool as a daemon:

./gsproxy.pl

Run one monitor pass and exit:

./gsproxy.pl --once

Run in the foreground:

./gsproxy.pl --no-daemon

Options

The script accepts these command line options:

  • --config FILE sets the CSV config path. The default is gsproxy.csv next to the script.
  • --gateway-port PORT sets the gateway SOCKS port. The default is 1080.
  • --bind ADDR sets the address the gateway listens on. The default is 0.0.0.0.
  • --gateway-auth FILE requires SOCKS5 username/password auth on the gateway. The first non-empty, non-comment line of FILE is user:pass; keep the file mode 0600.
  • --interval SEC sets the monitor interval in seconds. The default is 30.
  • --timeout SEC sets the per-check timeout in seconds. The default is 10.
  • --mcp-timeout SEC sets the MCP shell command timeout in seconds. The default is 60.
  • --test-host HOST sets the host probed through each SOCKS proxy. The default is www.google.com.
  • --test-port PORT sets the port probed through each SOCKS proxy. The default is 443.
  • --workdir DIR sets the remote working directory for the MCP call. The default is ..
  • --gs-netcat PATH sets the gs-netcat binary. The default is gs-netcat from PATH.
  • --target-sha256 HASH pins the SHA-256 of the remote gs-netcat binary (64 hex chars). The remote bring-up verifies the extracted binary before running it; a mismatch aborts the bring-up.
  • --log FILE sets the log file. The default is gsproxy.log next to the script.
  • --pidfile FILE sets the pidfile. The default is gsproxy.pid next to the script.
  • --state FILE sets the runtime state file. The default is gsproxy.state next to the script.
  • --insecure disables TLS certificate checks for the MCP endpoint.
  • --no-mcp skips the remote MCP bring-up call.
  • --no-local skips the local gs-netcat client start.
  • --no-bringup runs monitor only and never restarts proxies.
  • --no-daemon runs in the foreground.
  • --once runs one monitor pass and then exits.
  • --list parses the config, prints the rows, and exits.
  • --status reports whether the daemon is running.
  • --stop stops running daemons and tears down proxies.
  • --selftest runs the offline self-tests.
  • -h, --help shows the help text.

The --daemon option is a negatable boolean and defaults to on. --verbose and -v increase logging to standard error.

How it works

gsproxy has four moving parts: the remote bring-up, the local client, the monitor, and the gateway.

  • Remote bring-up never reuses a listener: it first pkills any gs-netcat already running for the same secret, then downloads a fresh gs-netcat build with curl, unpacks it, and runs a gs-netcat listener with -lSD -s SECRET. The listening process is renamed to python so it blends into the host.
  • The local client runs gs-netcat -D -p PORT -s SECRET, which opens a SOCKS listener on 127.0.0.1 at the row's port.
  • The monitor performs a full SOCKS5 handshake against each local port, testing a real CONNECT to a probe host and port. A passing handshake marks the proxy alive.
  • The gateway accepts SOCKS5 connections on --bind:--gateway-port and relays them to the first alive proxy listed in the state file.

MCP bring-up

The remote call is an MCP JSON-RPC exchange over HTTP. The script sends an initialize request, captures the Mcp-Session-Id header, sends the notifications/initialized notification, then calls tools/call with the name Tools_Shell_Command. Responses may be plain JSON or server-sent events, so the body is parsed for the last data: line before decoding. Requests are retried up to three times on transport failures. Before downloading, the script probes for an existing listener with the same secret and pkills it, so every bring-up reinstalls a fresh remote gs-netcat rather than stacking duplicates.

Monitoring and state

On every interval the monitor checks each proxy, logs only status changes, and writes the list of alive ports to the state file. The first alive port from that list is the current gateway target. When a proxy is found dead, the tool forks a background bring-up unless --no-bringup is set. In --once mode the bring-up runs inline instead of forking.

Gateway relay

The gateway listens, forks per connection, and reads the target port from the state file. It opens a TCP connection to that port and copies bytes in both directions with IO::Select, shutting down each direction as its side closes. If the state file has no valid port, the connection is dropped.

Without --gateway-auth the gateway is a transparent relay. With --gateway-auth the gateway performs the SOCKS5 username/password exchange itself (RFC 1929), then greets the upstream proxy and forwards the client's CONNECT request, so only authenticated clients can route through it.

Teardown

Stopping the tool cleans up both sides. Locally it scans /proc for gs-netcat processes that match the -D -p PORT shape and sends them TERM. Remotely it runs a shell command over MCP that scans for processes whose environment contains GSOCKET_ARGS=-lSD -s SECRET and kills them. The remote scan uses paths relative to the remote workdir (the default workdir . is the host root), so proc/... addresses /proc/...; absolute paths are avoided because the MCP sandbox rejects them.

Command reference

  • Start as a daemon: ./gsproxy.pl
  • Start and stop: ./gsproxy.pl --stop
  • Check status: ./gsproxy.pl --status
  • List config rows: ./gsproxy.pl --list
  • One pass, foreground: ./gsproxy.pl --once --no-daemon
  • Run self-tests: ./gsproxy.pl --selftest
  • Run with gateway auth: ./gsproxy.pl --gateway-auth /etc/gsproxy.auth

Files

  • gsproxy.csv is the default config file.
  • gsproxy.log is the append-only log, one UTC-stamped line per event.
  • gsproxy.pid holds the daemon and gateway process ids.
  • gsproxy.state holds the first alive port on the first line and all alive ports on the second.
  • gsproxy.auth (with --gateway-auth) holds one user:pass line.

All runtime files are created mode 0600.

Logging

Every event is written with a UTC timestamp in the form [YYYY-MM-DDTHH:MM:SSZ] message. Bring-up, MCP results, status changes, gateway startup, and teardown are all logged. The log is appended, never truncated, so it grows over time. With --verbose, log lines are also printed to standard error.

Gateway behavior

  • The gateway always targets the first alive proxy in listing order.
  • It re-reads the state file on every new connection, so failover is immediate.
  • A dead or missing target port causes the connection to be dropped without reply.
  • Each connection is handled by a forked child, keeping the listener responsive.
  • With --gateway-auth, clients must complete a SOCKS5 username/password login before any relay happens.

Self-tests

The --selftest mode runs offline checks and prints PASS or FAIL per case. It writes a temporary CSV, a temporary state file, and a temporary log under /tmp. It covers config parsing, command string quoting, gs-netcat process discovery, secret redaction, the single-instance lock, zombie detection, gateway auth accept/reject, live and dead SOCKS detection, and a full gateway relay (with and without auth) against a fake SOCKS server. It exits non-zero if any check fails.

Hardening

  • File permissions. The script runs with umask 0077, creates the log, pidfile, state, and auth files with mode 0600, and tightens pre-existing files at startup. It warns when the CSV config or the gateway auth file is group/world readable.
  • Secret redaction. Every log line passes through a redactor that replaces configured secrets with **. This includes MCP tool results that echo the full remote command, so secrets no longer leak into the log.
  • Single instance. An advisory flock on the pidfile plus a /proc scan for already-daemonized gsproxy processes prevents the overlapping-instance failure mode. A second instance refuses to start, and --once/foreground runs refuse while a daemon is active.
  • Gateway supervision. The gateway child reports bind success through a pipe before serving. A bind failure aborts startup instead of leaving a half-alive instance, and the monitor restarts a crashed gateway (reaping zombies on every pass).
  • Verified stop. --stop only signals processes that are really gsproxy instances, waits up to five seconds for them to exit, escalates to KILL if needed, and also finds daemons that are missing from the pidfile.
  • Gateway auth. --gateway-auth FILE turns the gateway into a SOCKS5 username/password front-end. Without it the gateway is an open relay, and a warning is logged whenever the gateway binds to a non-loopback address.
  • Binary pinning. --target-sha256 HASH verifies the downloaded gs-netcat binary on the remote host before it runs, so a tampered download fails the bring-up.

Security notes

  • Secrets are masked in list output and redacted from every log line, including MCP tool results that echo remote commands.
  • All runtime files (log, pidfile, state, gateway auth file) are created with mode 0600.
  • The remote process is deliberately renamed to python to hide on the target host.
  • The --insecure flag turns off TLS verification and should be used only when the MCP endpoint is trusted.
  • The gateway binds to 0.0.0.0 by default and is an open relay unless --gateway-auth is set. Restrict --bind to 127.0.0.1 or enable auth if the host should not expose the proxy to the network.
  • Anyone who can reach the gateway SOCKS port can route through the current proxy, so the port should be firewalled.

Troubleshooting

  • If the script reports no usable rows, check that the CSV has valid endpoint,secret,port fields and a numeric port.
  • If the daemon will not start, run --status to see whether it is already running, then use --stop first. A live daemon detected in /proc or a locked pidfile also refuses startup.
  • If MCP bring-up fails, confirm the endpoint exposes Tools_Shell_Command and try --insecure only if the certificate is the problem.
  • If no proxy becomes alive, run --list to confirm the rows and check that gs-netcat is on the PATH.
  • If the gateway has no target, watch the log for SOCKS ... is dead lines to see which proxies are failing.
  • If the gateway port is already in use, the script aborts at startup with gateway failed to start instead of lingering.

Credits

Created by Luxferre in 2026, released into the public domain with no warranties.

S
Description
gs-netcat-based proxy deployment manager
Readme
97 KiB
Languages
Perl 100%