gooserelayvpn-socks5-tunnel

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GooseRelayVPN SOCKS5 Tunnel Skill

GooseRelayVPN SOCKS5 隧道技能

Skill by ara.so — Daily 2026 Skills collection.
ara.so提供的技能——属于Daily 2026 Skills合集。

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 is
    script.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

GooseRelayVPN是一款SOCKS5代理,通过Google Apps Script Web应用将原始TCP流量隧道传输到自行托管的VPS出口服务器。流量路径:
Browser/App
  -> SOCKS5 (127.0.0.1:1080)
  -> AES-256-GCM加密帧
  -> HTTPS到Google边缘IP(SNI=www.google.com,Host=script.google.com)
  -> Apps Script doPost() — 仅做转发,不会看到明文
  -> 你的VPS :8443/tunnel — 解密,连接真实目标
  <- 通过长轮询沿原路返回
关键特性:
  • 域名前置:TLS SNI显示
    www.google.com
    ;实际主机为
    script.google.com
  • 端到端AES-256-GCM加密:Google不会持有密钥
  • 原始TCP隧道:支持SSH、IMAP、自定义协议——任何SOCKS5可承载的流量
  • 多部署负载均衡:在多个Apps Script部署间实现轮询+健康感知黑名单机制

Architecture Overview

架构概述

ComponentLocationPurpose
goose-client
Local machineSOCKS5 listener, AES encrypt, HTTPS relay
Google Apps ScriptGoogle cloud (free)Dumb HTTP forwarder, domain-fronting layer
goose-server
Your VPSAES decrypt, TCP dial to real targets

组件位置用途
goose-client
本地机器SOCKS5监听器,AES加密,HTTPS转发
Google Apps ScriptGoogle云端(免费)简易HTTP转发器,域名前置层
goose-server
你的VPSAES解密,TCP连接真实目标

Installation

安装

Option A: Pre-built Binaries

选项A:预编译二进制文件

bash
undefined
bash
undefined

Client (local machine) - Linux example

Client (local machine) - Linux example

Server (VPS)

Server (VPS)


Platform suffixes: `windows-amd64`, `darwin-amd64`, `darwin-arm64`, `linux-amd64`, `android-arm64`

平台后缀:`windows-amd64`, `darwin-amd64`, `darwin-arm64`, `linux-amd64`, `android-arm64`

Option B: Build from Source (Go 1.22+)

选项B:从源码构建(Go 1.22+)

bash
git clone https://github.com/kianmhz/GooseRelayVPN.git
cd GooseRelayVPN
bash
git clone https://github.com/kianmhz/GooseRelayVPN.git
cd GooseRelayVPN

Build both binaries

Build both binaries

go build -o goose-client ./cmd/client go build -o goose-server ./cmd/server
go build -o goose-client ./cmd/client go build -o goose-server ./cmd/server

Cross-compile for Linux VPS from macOS/Windows

Cross-compile for Linux VPS from macOS/Windows

GOOS=linux GOARCH=amd64 go build -o goose-server-linux ./cmd/server

---
GOOS=linux GOARCH=amd64 go build -o goose-server-linux ./cmd/server

---

Quick Start

快速开始

Step 1: Generate Secret Key

步骤1:生成密钥

bash
bash scripts/gen-key.sh
bash
bash scripts/gen-key.sh

Outputs a 64-character hex string — use this as tunnel_key in both configs

输出64位十六进制字符串 — 在两个配置文件中都将其用作tunnel_key


Or generate manually:
```bash
openssl rand -hex 32

或手动生成:
```bash
openssl rand -hex 32

Step 2: Configure Client

步骤2:配置客户端

client_config.json
:
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"
}
client_config.json
:
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

步骤3:配置服务器

server_config.json
:
json
{
  "server_host": "0.0.0.0",
  "server_port": 8443,
  "tunnel_key":  "SAME_64_CHAR_HEX_KEY_AS_CLIENT"
}
server_config.json
:
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

步骤4:部署Google Apps Script

  1. Go to script.google.com → New project
  2. Replace default code with
    apps_script/Code.gs
    contents
  3. Edit the VPS URL line:
    javascript
    const VPS_URL = 'http://YOUR_VPS_PUBLIC_IP:8443/tunnel';
  4. Deploy → New deployment → Type: Web app
    • Execute as: Me
    • Who has access: Anyone
  5. Copy the Deployment ID → paste into
    script_keys
    in client config
⚠️ Every code edit requires a new deployment (not just saving). Old deployment IDs stop working if you only save without redeploying.
  1. 访问script.google.com → 新建项目
  2. 将默认代码替换为
    apps_script/Code.gs
    中的内容
  3. 编辑VPS URL行:
    javascript
    const VPS_URL = 'http://YOUR_VPS_PUBLIC_IP:8443/tunnel';
  4. 部署 → 新建部署 → 类型:Web应用
    • 执行方式:
    • 访问权限:任何人
  5. 复制部署ID → 粘贴到客户端配置的
    script_keys
⚠️ 每次代码编辑都需要创建新部署(不仅仅是保存)。如果仅保存而不重新部署,旧部署ID将失效。

Step 5: Open VPS Firewall

步骤5:开放VPS防火墙

bash
undefined
bash
undefined

UFW

UFW

sudo ufw allow 8443/tcp
sudo ufw allow 8443/tcp

Verify from local machine

从本地机器验证

curl http://YOUR_VPS_IP:8443/healthz
curl http://YOUR_VPS_IP:8443/healthz

Expected: HTTP 200 empty body

预期结果:HTTP 200 空响应体


Also check cloud provider firewall (AWS Security Groups, DigitalOcean Firewall, etc.) for inbound TCP 8443.

同时检查云服务商防火墙(AWS安全组、DigitalOcean防火墙等)是否允许TCP 8443入站流量。

Step 6: Run Server on VPS

步骤6:在VPS上运行服务器

bash
./goose-server -config server_config.json
bash
./goose-server -config server_config.json

Step 7: Run Client Locally

步骤7:在本地运行客户端

bash
./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

bash
./goose-client -config client_config.json
预期输出:
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

CLI参考

bash
undefined
bash
undefined

Client

Client

./goose-client -config client_config.json ./goose-client -config /path/to/custom_client.json
./goose-client -config client_config.json ./goose-client -config /path/to/custom_client.json

Server

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.

---
./goose-server -config server_config.json ./goose-server -config /path/to/custom_server.json

两个二进制文件仅接受指向各自JSON配置文件的`-config`参数。

---

Configuration Reference

配置参考

Client Config (
client_config.json
)

客户端配置(
client_config.json

FieldTypeDescription
socks_host
stringSOCKS5 listener address. Use
0.0.0.0
for LAN sharing
socks_port
intSOCKS5 listener port (default:
1080
)
google_host
stringGoogle edge IP for domain fronting (e.g.
216.239.38.120
)
sni
stringTLS SNI value (must be
www.google.com
)
script_keys
[]stringOne or more Apps Script Deployment IDs or full
/exec
URLs
tunnel_key
string64-char hex AES-256 key — must match server
字段类型描述
socks_host
stringSOCKS5监听地址。使用
0.0.0.0
可实现局域网共享
socks_port
intSOCKS5监听端口(默认:
1080
google_host
string用于域名前置的Google边缘IP(例如
216.239.38.120
sni
stringTLS SNI值(必须为
www.google.com
script_keys
[]string一个或多个Apps Script部署ID或完整的
/exec
URL
tunnel_key
string64位十六进制AES-256密钥 — 必须与服务器端一致

Server Config (
server_config.json
)

服务器配置(
server_config.json

FieldTypeDescription
server_host
stringBind address on VPS (use
0.0.0.0
)
server_port
intListen port (default:
8443
)
tunnel_key
string64-char hex AES-256 key — must match client

字段类型描述
server_host
stringVPS上的绑定地址(使用
0.0.0.0
server_port
int监听端口(默认:
8443
tunnel_key
string64位十六进制AES-256密钥 — 必须与客户端一致

Multiple Deployments (Scaling)

多部署(扩容)

Each Apps Script deployment handles ~20,000 calls/day. Add multiple deployment IDs to scale:
json
{
  "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
.

每个Apps Script部署每天可处理约20,000次调用。添加多个部署ID可实现扩容:
json
{
  "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"
}
客户端会自动:
  • 在所有部署间轮询
  • 对失败的部署进行退避(3秒→6秒→12秒→最长约48秒)
  • 在同一周期内尝试在其他部署上重试失败的轮询
所有部署都指向同一个VPS并使用相同的
tunnel_key

Systemd Service (VPS)

Systemd服务(VPS)

bash
sudo nano /etc/systemd/system/goose-relay.service
ini
[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
bash
sudo systemctl daemon-reload
sudo systemctl enable goose-relay
sudo systemctl start goose-relay
sudo systemctl status goose-relay --no-pager
bash
sudo nano /etc/systemd/system/goose-relay.service
ini
[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
bash
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

---
journalctl -u goose-relay -f

---

Google Apps Script (
Code.gs
)

Google Apps Script(
Code.gs

The Apps Script is a thin forwarder. Key structure to understand:
javascript
// 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);
}

Apps Script是一个轻量转发器。核心结构如下:
javascript
// 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)

程序化配置生成(Go)

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")
}
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)

健康检查脚本(Bash)

bash
#!/usr/bin/env bash
bash
#!/usr/bin/env bash

check-relay.sh — verify VPS endpoint is reachable before starting client

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
undefined
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
undefined

SOCKS5 Proxy Test (Go)

SOCKS5代理测试(Go)

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)
}
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部署VPS

yaml
undefined
yaml
undefined

docker-compose.yml — run goose-server on VPS

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

---
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

Firefox

  1. Settings → Network Settings → Manual proxy configuration
  2. SOCKS Host:
    127.0.0.1
    , Port:
    1080
  3. Select SOCKS v5
  4. ✅ Check Proxy DNS when using SOCKS v5 (prevents DNS leaks)
  1. 设置 → 网络设置 → 手动配置代理
  2. SOCKS主机:
    127.0.0.1
    ,端口:
    1080
  3. 选择SOCKS v5
  4. ✅ 勾选使用SOCKS v5时代理DNS(防止DNS泄漏)

Chrome/Edge

Chrome/Edge

  • Protocol: SOCKS5
  • Server:
    127.0.0.1
  • Port:
    1080
  • 协议:SOCKS5
  • 服务器:
    127.0.0.1
  • 端口:
    1080

System-wide (macOS)

系统级配置(macOS)

System Preferences → Network → Advanced → Proxies → SOCKS Proxy:
127.0.0.1:1080
系统偏好设置 → 网络 → 高级 → 代理 → SOCKS代理:
127.0.0.1:1080

curl / wget

curl / wget

bash
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
bash
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

bash
ssh -o ProxyCommand="nc -X 5 -x 127.0.0.1:1080 %h %p" user@remote-host

bash
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:
json
{
  "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.

要允许本地网络中的其他设备使用该隧道:
json
{
  "socks_host": "0.0.0.0",
  "socks_port": 1080,
  ...
}
其他设备连接到
YOUR_LOCAL_IP:1080
作为SOCKS5代理。
⚠️ 仅在可信网络中执行此操作——局域网内任何设备都可能消耗你的Apps Script配额。

Troubleshooting

故障排除

Pre-flight check fails at startup

启动时预检查失败

The client runs automatic checks. Match error message to cause:
ErrorLikely CauseFix
relay healthy
fails
Apps Script unreachableCheck 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 IDDeploy Apps Script → copy Deployment ID
客户端会自动运行检查。根据错误信息匹配原因:
错误可能原因修复方法
relay healthy
失败
Apps Script无法访问检查部署ID,重新部署脚本
AES key mismatch
客户端和服务器的
tunnel_key
不一致
将相同的密钥复制到两个配置文件中
curl healthz
连接被拒绝
8443端口被阻塞
ufw allow 8443/tcp
+ 配置云防火墙规则
script_keys
为空
忘记粘贴部署ID部署Apps Script → 复制部署ID

Apps Script quota exhausted

Apps Script配额耗尽

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.
症状:隧道停止工作,约伊朗时间上午10:30(GMT+3:30)重置
修复方法:向script_keys数组添加更多部署ID
每个部署每天约可处理20,000次调用。客户端空闲时每秒轮询约1次。

Server not receiving traffic

服务器未收到流量

bash
undefined
bash
undefined

On VPS: verify server is listening

在VPS上:验证服务器是否在监听

ss -tlnp | grep 8443
ss -tlnp | grep 8443

Test healthz endpoint locally on VPS

在VPS本地测试healthz端点

curl localhost:8443/healthz
curl localhost:8443/healthz

Test from external (your machine)

从外部(你的机器)测试

curl http://YOUR_VPS_IP:8443/healthz
curl http://YOUR_VPS_IP:8443/healthz

Check firewall

检查防火墙

sudo ufw status
undefined
sudo ufw status
undefined

Apps Script returning errors

Apps Script返回错误

  • Verify
    VPS_URL
    in
    Code.gs
    uses 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
  • 验证
    Code.gs
    中的
    VPS_URL
    使用了正确的VPS IP和8443端口
  • 验证Apps Script部署为Web应用且访问权限为任何人
  • 任何代码编辑后,创建新部署——不要重用旧部署ID
  • 检查Apps Script执行日志:script.google.com → 执行标签页

Key mismatch debugging

密钥不匹配调试

bash
undefined
bash
undefined

Verify key lengths match (should both be 64 chars)

验证密钥长度是否一致(都应为64位)

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]+'...')"
undefined
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]+'...')"
undefined

systemd service won't start

systemd服务无法启动

bash
sudo systemctl status goose-relay --no-pager
journalctl -u goose-relay -n 50 --no-pager
bash
sudo systemctl status goose-relay --no-pager
journalctl -u goose-relay -n 50 --no-pager

Common fix: wrong binary path in ExecStart

常见修复方法:ExecStart中的二进制路径错误

which goose-server # find actual path
undefined
which goose-server # 查找实际路径
undefined

DNS leaks

DNS泄漏

Ensure browser uses proxy DNS (Firefox: "Proxy DNS when using SOCKS v5"). Without this, DNS queries bypass the tunnel.

确保浏览器使用代理DNS(Firefox:“使用SOCKS v5时代理DNS”)。否则,DNS查询将绕过隧道。

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
    mitm
    topic in the repo refers to traffic inspection capability; no local MITM cert is needed for this project (unlike
    MasterHttpRelayVPN
    )
  • Quota resets daily; rotate deployment IDs periodically if quota is a concern

  • 切勿共享
    tunnel_key
    ——任何人拥有该密钥都可以将你的VPS用作出口节点
  • 以受限权限存储配置文件:
    chmod 600 client_config.json server_config.json
  • Google不会看到明文——AES密钥永远不会接触Apps Script
  • 仓库中的
    mitm
    主题指流量检查能力;本项目不需要本地MITM证书(与
    MasterHttpRelayVPN
    不同)
  • 配额每天重置;如果配额是问题,定期轮换部署ID

Common Patterns

常见模式

Environment-based config generation

基于环境变量的配置生成

bash
undefined
bash
undefined

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
undefined
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
undefined

Multiple Google accounts for maximum capacity

多Google账号实现最大容量

json
{
  "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.
json
{
  "script_keys": [
    "AKfycbx_account1_deployment1",
    "AKfycbx_account1_deployment2",
    "AKfycbx_account2_deployment1",
    "AKfycbx_account2_deployment2"
  ]
}
在不同的Google账号下部署
Code.gs
以获取独立配额。所有部署都指向同一个VPS。

Verify exit IP after setup

设置后验证出口IP

bash
undefined
bash
undefined

Should show your VPS IP, not your real IP

应显示你的VPS IP,而非真实IP

curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org
undefined
curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org
undefined