gooserelayvpn-socks5-tunnel
GooseRelayVPN SOCKS5 Tunnel Skill
Skill by ara.so — Daily 2026 Skills collection.
What It Does
GooseRelayVPN is a SOCKS5 proxy that tunnels raw TCP through a Google Apps Script web app to a self-hosted VPS exit server. The traffic path:
Browser/App
-> SOCKS5 (127.0.0.1:1080)
-> AES-256-GCM encrypted frames
-> HTTPS to Google edge IP (SNI=www.google.com, Host=script.google.com)
-> Apps Script doPost() — dumb forwarder, never sees plaintext
-> Your VPS :8443/tunnel — decrypts, dials real target
<- Same path in reverse via long-polling
Key properties:
- Domain fronting: TLS SNI shows
www.google.com; actual host isscript.google.com - End-to-end AES-256-GCM: Google never holds the key
- Raw TCP tunneling: SSH, IMAP, custom protocols — anything SOCKS5 carries
- Multi-deployment load balancing: Round-robin + health-aware blacklist across multiple Apps Script deployments
Architecture Overview
| Component | Location | Purpose |
|---|---|---|
goose-client |
Local machine | SOCKS5 listener, AES encrypt, HTTPS relay |
| Google Apps Script | Google cloud (free) | Dumb HTTP forwarder, domain-fronting layer |
goose-server |
Your VPS | AES decrypt, TCP dial to real targets |
Installation
Option A: Pre-built Binaries
# Client (local machine) - Linux example
wget https://github.com/kianmhz/GooseRelayVPN/releases/latest/download/GooseRelayVPN-client-vX.Y.Z-linux-amd64.tar.gz
tar -xzf GooseRelayVPN-client-vX.Y.Z-linux-amd64.tar.gz
# Server (VPS)
wget https://github.com/kianmhz/GooseRelayVPN/releases/latest/download/GooseRelayVPN-server-vX.Y.Z-linux-amd64.tar.gz
tar -xzf GooseRelayVPN-server-vX.Y.Z-linux-amd64.tar.gz
Platform suffixes: windows-amd64, darwin-amd64, darwin-arm64, linux-amd64, android-arm64
Option B: Build from Source (Go 1.22+)
git clone https://github.com/kianmhz/GooseRelayVPN.git
cd GooseRelayVPN
# Build both binaries
go build -o goose-client ./cmd/client
go build -o goose-server ./cmd/server
# Cross-compile for Linux VPS from macOS/Windows
GOOS=linux GOARCH=amd64 go build -o goose-server-linux ./cmd/server
Quick Start
Step 1: Generate Secret Key
bash scripts/gen-key.sh
# Outputs a 64-character hex string — use this as tunnel_key in both configs
Or generate manually:
openssl rand -hex 32
Step 2: Configure Client
client_config.json:
{
"socks_host": "127.0.0.1",
"socks_port": 1080,
"google_host": "216.239.38.120",
"sni": "www.google.com",
"script_keys": ["YOUR_APPS_SCRIPT_DEPLOYMENT_ID"],
"tunnel_key": "YOUR_64_CHAR_HEX_KEY"
}
Step 3: Configure Server
server_config.json:
{
"server_host": "0.0.0.0",
"server_port": 8443,
"tunnel_key": "SAME_64_CHAR_HEX_KEY_AS_CLIENT"
}
Step 4: Deploy Google Apps Script
- Go to script.google.com → New project
- Replace default code with
apps_script/Code.gscontents - Edit the VPS URL line:
const VPS_URL = 'http://YOUR_VPS_PUBLIC_IP:8443/tunnel'; - Deploy → New deployment → Type: Web app
- Execute as: Me
- Who has access: Anyone
- Copy the Deployment ID → paste into
script_keysin client config
⚠️ Every code edit requires a new deployment (not just saving). Old deployment IDs stop working if you only save without redeploying.
Step 5: Open VPS Firewall
# UFW
sudo ufw allow 8443/tcp
# Verify from local machine
curl http://YOUR_VPS_IP:8443/healthz
# Expected: HTTP 200 empty body
Also check cloud provider firewall (AWS Security Groups, DigitalOcean Firewall, etc.) for inbound TCP 8443.
Step 6: Run Server on VPS
./goose-server -config server_config.json
Step 7: Run Client Locally
./goose-client -config client_config.json
Expected output:
CLIENT INFO GooseRelayVPN client starting
CLIENT INFO SOCKS5 proxy: socks5://127.0.0.1:1080
CLIENT INFO pre-flight OK: relay healthy, AES key matches end-to-end
CLIENT INFO ready: local SOCKS5 is listening on 127.0.0.1:1080
CLI Reference
# Client
./goose-client -config client_config.json
./goose-client -config /path/to/custom_client.json
# Server
./goose-server -config server_config.json
./goose-server -config /path/to/custom_server.json
Both binaries take only -config flag pointing to their respective JSON config file.
Configuration Reference
Client Config (client_config.json)
| Field | Type | Description |
|---|---|---|
socks_host |
string | SOCKS5 listener address. Use 0.0.0.0 for LAN sharing |
socks_port |
int | SOCKS5 listener port (default: 1080) |
google_host |
string | Google edge IP for domain fronting (e.g. 216.239.38.120) |
sni |
string | TLS SNI value (must be www.google.com) |
script_keys |
[]string | One or more Apps Script Deployment IDs or full /exec URLs |
tunnel_key |
string | 64-char hex AES-256 key — must match server |
Server Config (server_config.json)
| Field | Type | Description |
|---|---|---|
server_host |
string | Bind address on VPS (use 0.0.0.0) |
server_port |
int | Listen port (default: 8443) |
tunnel_key |
string | 64-char hex AES-256 key — must match client |
Multiple Deployments (Scaling)
Each Apps Script deployment handles ~20,000 calls/day. Add multiple deployment IDs to scale:
{
"socks_host": "127.0.0.1",
"socks_port": 1080,
"google_host": "216.239.38.120",
"sni": "www.google.com",
"script_keys": [
"AKfycbx_DEPLOYMENT_ID_ONE_xxxx",
"AKfycbx_DEPLOYMENT_ID_TWO_xxxx",
"AKfycbx_DEPLOYMENT_ID_THREE_xxxx"
],
"tunnel_key": "YOUR_64_CHAR_HEX_KEY"
}
The client automatically:
- Round-robins across all deployments
- Backs off failing deployments (3s → 6s → 12s → up to ~48s)
- Retries failed polls on another deployment in the same cycle
All deployments point to the same VPS and use the same tunnel_key.
Systemd Service (VPS)
sudo nano /etc/systemd/system/goose-relay.service
[Unit]
Description=GooseRelayVPN exit server
After=network.target
[Service]
Type=simple
WorkingDirectory=/root
ExecStart=/root/goose-server -config /root/server_config.json
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable goose-relay
sudo systemctl start goose-relay
sudo systemctl status goose-relay --no-pager
# View logs
journalctl -u goose-relay -f
Google Apps Script (Code.gs)
The Apps Script is a thin forwarder. Key structure to understand:
// apps_script/Code.gs — dumb forwarder pattern
const VPS_URL = 'http://YOUR_VPS_PUBLIC_IP:8443/tunnel';
function doPost(e) {
// Forwards request body verbatim to VPS
// Never decrypts — AES key never touches Google
const response = UrlFetchApp.fetch(VPS_URL, {
method: 'post',
payload: e.postData.contents,
headers: { 'Content-Type': 'application/octet-stream' },
muteHttpExceptions: true
});
return ContentService.createTextOutput(response.getContent())
.setMimeType(ContentService.MimeType.OCTET_STREAM);
}
Code Examples
Programmatic Config Generation (Go)
package main
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"os"
)
type ClientConfig struct {
SocksHost string `json:"socks_host"`
SocksPort int `json:"socks_port"`
GoogleHost string `json:"google_host"`
SNI string `json:"sni"`
ScriptKeys []string `json:"script_keys"`
TunnelKey string `json:"tunnel_key"`
}
type ServerConfig struct {
ServerHost string `json:"server_host"`
ServerPort int `json:"server_port"`
TunnelKey string `json:"tunnel_key"`
}
func generateTunnelKey() (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func main() {
key, err := generateTunnelKey()
if err != nil {
panic(err)
}
clientCfg := ClientConfig{
SocksHost: "127.0.0.1",
SocksPort: 1080,
GoogleHost: "216.239.38.120",
SNI: "www.google.com",
ScriptKeys: []string{os.Getenv("APPS_SCRIPT_DEPLOYMENT_ID")},
TunnelKey: key,
}
serverCfg := ServerConfig{
ServerHost: "0.0.0.0",
ServerPort: 8443,
TunnelKey: key,
}
clientJSON, _ := json.MarshalIndent(clientCfg, "", " ")
serverJSON, _ := json.MarshalIndent(serverCfg, "", " ")
os.WriteFile("client_config.json", clientJSON, 0600)
os.WriteFile("server_config.json", serverJSON, 0600)
fmt.Printf("Generated key: %s\n", key)
fmt.Println("Configs written to client_config.json and server_config.json")
}
Health Check Script (Bash)
#!/usr/bin/env bash
# check-relay.sh — verify VPS endpoint is reachable before starting client
VPS_IP="${GOOSE_VPS_IP:?Set GOOSE_VPS_IP env var}"
VPS_PORT="${GOOSE_VPS_PORT:-8443}"
echo "Checking VPS health endpoint..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--connect-timeout 5 \
"http://${VPS_IP}:${VPS_PORT}/healthz")
if [ "$HTTP_STATUS" = "200" ]; then
echo "✓ VPS is reachable (HTTP $HTTP_STATUS)"
exit 0
else
echo "✗ VPS health check failed (HTTP $HTTP_STATUS)"
echo " Check: ufw, cloud firewall, server process running?"
exit 1
fi
SOCKS5 Proxy Test (Go)
package main
import (
"fmt"
"io"
"net"
"net/http"
"os"
"golang.org/x/net/proxy"
)
func main() {
// Test the GooseRelayVPN SOCKS5 proxy
socksAddr := "127.0.0.1:1080"
dialer, err := proxy.SOCKS5("tcp", socksAddr, nil, proxy.Direct)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create SOCKS5 dialer: %v\n", err)
os.Exit(1)
}
transport := &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)
},
}
client := &http.Client{Transport: transport}
resp, err := client.Get("https://api.ipify.org?format=json")
if err != nil {
fmt.Fprintf(os.Stderr, "Request failed: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Exit IP (should be your VPS IP): %s\n", body)
}
Docker Compose for VPS Deployment
# docker-compose.yml — run goose-server on VPS
version: '3.8'
services:
goose-server:
image: golang:1.22-alpine
working_dir: /app
volumes:
- ./GooseRelayVPN:/app
- ./server_config.json:/app/server_config.json:ro
command: >
sh -c "go build -o /tmp/goose-server ./cmd/server &&
/tmp/goose-server -config /app/server_config.json"
ports:
- "8443:8443"
restart: always
environment:
- CGO_ENABLED=0
Browser Configuration
Firefox
- Settings → Network Settings → Manual proxy configuration
- SOCKS Host:
127.0.0.1, Port:1080 - Select SOCKS v5
- ✅ Check Proxy DNS when using SOCKS v5 (prevents DNS leaks)
Chrome/Edge
Use FoxyProxy or SwitchyOmega:
- Protocol: SOCKS5
- Server:
127.0.0.1 - Port:
1080
System-wide (macOS)
System Preferences → Network → Advanced → Proxies → SOCKS Proxy: 127.0.0.1:1080
curl / wget
curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org
wget -e "use_proxy=yes" -e "http_proxy=socks5h://127.0.0.1:1080" https://api.ipify.org
SSH through tunnel
ssh -o ProxyCommand="nc -X 5 -x 127.0.0.1:1080 %h %p" user@remote-host
LAN Sharing
To allow other devices on your local network to use the tunnel:
{
"socks_host": "0.0.0.0",
"socks_port": 1080,
...
}
Other devices connect to YOUR_LOCAL_IP:1080 as their SOCKS5 proxy.
⚠️ Only do this on trusted networks — any LAN device can consume your Apps Script quota.
Troubleshooting
Pre-flight check fails at startup
The client runs automatic checks. Match error message to cause:
| Error | Likely Cause | Fix |
|---|---|---|
relay healthy fails |
Apps Script unreachable | Check deployment ID, redeploy script |
AES key mismatch |
tunnel_key differs between client/server |
Copy exact same key to both configs |
Connection refused on curl healthz |
Port 8443 blocked | ufw allow 8443/tcp + cloud firewall rule |
script_keys empty |
Forgot to paste deployment ID | Deploy Apps Script → copy Deployment ID |
Apps Script quota exhausted
Symptom: tunnel stops working, resets ~10:30 AM Iran time (GMT+3:30)
Fix: Add more deployment IDs to script_keys array
Each deployment: ~20,000 calls/day. Client polls ~1/second idle.
Server not receiving traffic
# On VPS: verify server is listening
ss -tlnp | grep 8443
# Test healthz endpoint locally on VPS
curl localhost:8443/healthz
# Test from external (your machine)
curl http://YOUR_VPS_IP:8443/healthz
# Check firewall
sudo ufw status
Apps Script returning errors
- Verify
VPS_URLinCode.gsuses correct VPS IP and port 8443 - Verify Apps Script is deployed as Web app with Anyone access
- After any code edit, create a New deployment — don't reuse old deployment ID
- Check Apps Script execution log: script.google.com → Executions tab
Key mismatch debugging
# Verify key lengths match (should both be 64 chars)
cat client_config.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['tunnel_key']), d['tunnel_key'][:8]+'...')"
cat server_config.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['tunnel_key']), d['tunnel_key'][:8]+'...')"
systemd service won't start
sudo systemctl status goose-relay --no-pager
journalctl -u goose-relay -n 50 --no-pager
# Common fix: wrong binary path in ExecStart
which goose-server # find actual path
DNS leaks
Ensure browser uses proxy DNS (Firefox: "Proxy DNS when using SOCKS v5"). Without this, DNS queries bypass the tunnel.
Security Notes
- Never share
tunnel_key— anyone with it can use your VPS as an exit node - Store configs with restricted permissions:
chmod 600 client_config.json server_config.json - Google never sees plaintext — AES key never touches Apps Script
- The
mitmtopic in the repo refers to traffic inspection capability; no local MITM cert is needed for this project (unlikeMasterHttpRelayVPN) - Quota resets daily; rotate deployment IDs periodically if quota is a concern
Common Patterns
Environment-based config generation
# Generate config from environment variables
cat > client_config.json << EOF
{
"socks_host": "127.0.0.1",
"socks_port": 1080,
"google_host": "216.239.38.120",
"sni": "www.google.com",
"script_keys": ["${APPS_SCRIPT_DEPLOYMENT_ID}"],
"tunnel_key": "${GOOSE_TUNNEL_KEY}"
}
EOF
cat > server_config.json << EOF
{
"server_host": "0.0.0.0",
"server_port": 8443,
"tunnel_key": "${GOOSE_TUNNEL_KEY}"
}
EOF
Multiple Google accounts for maximum capacity
{
"script_keys": [
"AKfycbx_account1_deployment1",
"AKfycbx_account1_deployment2",
"AKfycbx_account2_deployment1",
"AKfycbx_account2_deployment2"
]
}
Deploy Code.gs under different Google accounts for independent quotas. All point to the same VPS.
Verify exit IP after setup
# Should show your VPS IP, not your real IP
curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org