Skip to main content

gooserelayvpn-android-client

Android VPN client that tunnels SOCKS5 traffic through Google Apps Script to a VPS exit server with AES-256-GCM encryption and domain fronting

インストールへ移動

ソース情報

リポジトリ
reason-machines/devtools-skills
ソースの最終更新活動
2026年5月17日 22:50
検出された SKILL.md の言語
英語
スター
4
フォーク
0

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
gooserelayvpn-android-client
description
Android VPN client that tunnels SOCKS5 traffic through Google Apps Script to a VPS exit server with AES-256-GCM encryption and domain fronting
triggers
["how do I build the GooseRelayVPN Android client","configure GooseRelay Android VPN profile","setup Android VPN with Google Apps Script tunnel","GooseRelayVPN Android profile JSON format","build GooseRelay mobile AAR library","troubleshoot GooseRelay Android connection","import GooseRelay VPN profile on Android","setup domain-fronted VPN on Android"]
# GooseRelayVPN Android Client Skill > Skill by [ara.so](https://ara.so) — Devtools Skills collection. ## Overview GooseRelayVPN Android Client is an Android application that provides a VPN service tunneling TCP traffic through Google Apps Script to a VPS exit server. It uses: - **Local SOCKS5 proxy** for app/browser traffic - **AES-256-GCM encryption** for tunnel security - **Domain fronting** via Google infrastructure (Apps Script) - **Android VpnService** integration with tun2socks - **Profile-based configuration** with JSON import/export **Architecture flow:** 1. Android app traffic → SOCKS5 (127.0.0.1:1080) 2. GooseRelay core encrypts with AES key 3. HTTPS transport through Google Apps Script deployment 4. VPS exit server decrypts and forwards to target ## Prerequisites Before using the Android client, you must set up upstream infrastructure: 1. **VPS server** running `goose-server` (from main GooseRelayVPN project) 2. **Google Apps Script** deployment with `apps_script/Code.gs` 3. **Tunnel encryption key** generated via `scripts/gen-key.sh` 4. **Deployment ID(s)** from Apps Script Upstream project: https://github.com/kianmhz/GooseRelayVPN ## Building the Android Client ### Requirements - Android Studio - JDK 17 - Go 1.22+ - Android SDK with NDK - `gomobile` tool ### Build Go Mobile AAR The core GooseRelay logic is compiled to an Android AAR library: ```bash # From project root bash android/build_go_mobile.sh ``` This script: - Installs `gomobile` if needed - Compiles Go code to AAR for arm64-v8a, armeabi-v7a, x86, x86_64 - Outputs to `android/app/libs/gooserelay.aar` **Manual AAR build:** ```bash go install golang.org/x/mobile/cmd/gomobile@latest gomobile init gomobile bind \ -target=android \ -androidapi=21 \ -o android/app/libs/gooserelay.aar \ -v \ ./mobile ``` ### Build Debug APK ```bash cd android ./gradlew :app:assembleDebug # Output: android/app/build/outputs/apk/debug/app-debug.apk ``` ### Build Release APK ```bash cd android ./gradlew :app:assembleRelease # Requires signing configuration in android/app/build.gradle ``` ## Profile Configuration ### Profile JSON Structure ```json { "debug_timing": false, "socks_host": "127.0.0.1", "socks_port": 1080, "google_host": "216.239.38.120", "sni": [ "www.google.com", "mail.google.com", "accounts.google.com" ], "script_keys": [ "DEPLOYMENT_ID_FROM_APPS_SCRIPT", "OPTIONAL_SECOND_DEPLOYMENT_ID" ], "tunnel_key": "BASE64_ENCODED_AES_KEY_FROM_GEN_KEY_SCRIPT" } ``` ### Field Descriptions | Field | Type | Description | |-------|------|-------------| | `debug_timing` | bool | Enable timing debug logs | | `socks_host` | string | Local SOCKS5 bind address (usually 127.0.0.1) | | `socks_port` | int | Local SOCKS5 port (default 1080) | | `google_host` | string | Google IP for domain fronting (216.239.38.120 is common) | | `sni` | []string | SNI hostnames for TLS handshake rotation | | `script_keys` | []string | Apps Script deployment IDs (one or more for redundancy) | | `tunnel_key` | string | Base64 AES-256 key (must match server-side key) | ### Generating Tunnel Key Use the upstream script to generate a secure key: ```bash # From GooseRelayVPN main repo bash scripts/gen-key.sh # Outputs base64-encoded key # Example: a3d7f9e2b1c4... ``` **Important:** The same `tunnel_key` must be used on both the Android client and the VPS `goose-server`. ### In-App Profile Management **Create new profile:** 1. Open app → Profiles tab 2. Tap "+" button 3. Enter profile name and configuration 4. Save **Import profile from JSON:** 1. Profiles tab → menu → Import 2. Select JSON file from storage 3. Profile is added to list **Export profile to JSON:** 1. Profiles tab → long-press profile 2. Select Export 3. JSON saved to Downloads ## Android VPN Service Integration ### VpnService Implementation The app uses Android's `VpnService` API to capture device traffic: ```kotlin // Simplified example from Android codebase class GooseVpnService : VpnService() { private val socksHost = "127.0.0.1" private val socksPort = 1080 fun startVpn(profile: Profile) { // 1. Start GooseRelay core (via JNI to Go AAR) GooseRelay.start(profile.toJson()) // 2. Configure VPN interface val builder = Builder() .setSession("GooseRelayVPN") .addAddress("10.0.0.2", 24) .addRoute("0.0.0.0", 0) .addDnsServer("8.8.8.8") // 3. Exclude apps if split tunneling enabled if (profile.splitTunnel) { profile.allowedApps.forEach { pkg -> builder.addAllowedApplication(pkg) } } val vpnInterface = builder.establish() // 4. Start tun2socks to forward traffic to SOCKS5 Tun2Socks.start( vpnInterface.fd, socksHost, socksPort ) } } ``` ### Permissions Required **AndroidManifest.xml:** ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application> <service android:name=".service.GooseVpnService" android:permission="android.permission.BIND_VPN_SERVICE"> <intent-filter> <action android:name="android.net.VpnService" /> </intent-filter> </service> </application> </manifest> ``` ## Go Mobile Bridge The Go core is exposed to Android via `gomobile`: ### Mobile Package Interface **mobile/mobile.go:** ```go package mobile import ( "encoding/json" "github.com/Hidden-Node/GooseRelayVPN/client" ) // Config matches Android profile JSON structure type Config struct { DebugTiming bool `json:"debug_timing"` 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"` } var relayClient *client.Client // Start initializes and starts the GooseRelay client func Start(configJSON string) error { var cfg Config if err := json.Unmarshal([]byte(configJSON), &cfg); err != nil { return err } relayClient = client.NewClient(client.Config{ DebugTiming: cfg.DebugTiming, SocksAddr: fmt.Sprintf("%s:%d", cfg.SocksHost, cfg.SocksPort), GoogleHost: cfg.GoogleHost, SNI: cfg.SNI, ScriptKeys: cfg.ScriptKeys, TunnelKey: cfg.TunnelKey, }) return relayClient.Start() } // Stop gracefully stops the relay client func Stop() error { if relayClient != nil { return relayClient.Stop() } return nil } // GetStats returns JSON statistics func GetStats() string { if relayClient == nil { return "{}" } stats := relayClient.GetStats() data, _ := json.Marshal(stats) return string(data) } ``` ### Calling from Android (Kotlin) ```kotlin import gooserelay.Gooserelay // Generated from AAR class RelayManager { fun startRelay(profile: Profile) { val configJson = profile.toJson() try { Gooserelay.start(configJson) Log.i("GooseRelay", "Started successfully") } catch (e: Exception) { Log.e("GooseRelay", "Start failed: ${e.message}") } } fun stopRelay() { try { Gooserelay.stop() } catch (e: Exception) { Log.e("GooseRelay", "Stop failed: ${e.message}") } } fun getStats(): Stats? { return try { val json = Gooserelay.getStats() Json.decodeFromString<Stats>(json) } catch (e: Exception) { null } } } ``` ## Common Usage Patterns ### Full Device VPN Route all device traffic through the tunnel: ```kotlin // In profile configuration val profile = Profile( name = "Full VPN", socksPort = 1080, scriptKeys = listOf(System.getenv("APPS_SCRIPT_DEPLOYMENT_ID")), tunnelKey = System.getenv("GOOSE_TUNNEL_KEY"), splitTunnel = false, // All apps excludeLocalNetwork = true ) vpnService.startVpn(profile) ``` ### Split Tunneling Route only specific apps through the tunnel: ```kotlin val profile = Profile( name = "Split Tunnel", socksPort = 1080, scriptKeys = listOf(System.getenv("APPS_SCRIPT_DEPLOYMENT_ID")), tunnelKey = System.getenv("GOOSE_TUNNEL_KEY"), splitTunnel = true, allowedApps = listOf( "com.android.chrome", "org.telegram.messenger" ) ) vpnService.startVpn(profile) ``` ### Manual SOCKS5 Proxy Use without VpnService (manual app configuration): ```kotlin // Start only the SOCKS5 server, no VPN fun startSocksOnly(profile: Profile) { GooseRelay.start(profile.toJson()) // Apps must be configured to use 127.0.0.1:1080 as SOCKS5 proxy } ``` ## Logging and Telemetry ### Real-time Logs The app provides a Logs tab showing Android and Go core logs: ```kotlin // Android side logger forwarding to UI object LogCollector { private val logs = mutableListOf<LogEntry>() fun addLog(level: String, tag: String, message: String) { logs.add(LogEntry( timestamp = System.currentTimeMillis(), level = level, tag = tag, message = message )) // Notify UI observers notifyLogListeners() } } // Go side: logs are captured via custom writer ``` ### Telemetry Stats Real-time connection statistics: ```kotlin data class TelemetryStats( val bytesUploaded: Long, val bytesDownloaded: Long, val activeConnections: Int, val successRate: Float, val avgLatency: Long ) fun updateTelemetry() { val statsJson = Gooserelay.getStats() val stats = Json.decodeFromString<TelemetryStats>(statsJson) // Update UI cards uploadCard.text = formatBytes(stats.bytesUploaded) downloadCard.text = formatBytes(stats.bytesDownloaded) latencyCard.text = "${stats.avgLatency}ms" } ``` ## CI/CD and Release ### GitHub Actions Workflows **Debug CI (.github/workflows/android-ci.yml):** ```yaml name: Android CI on: push: branches: [main] pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.22' - name: Build Go Mobile AAR run: bash android/build_go_mobile.sh - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Build Debug APK run: | cd android ./gradlew assembleDebug - name: Upload APK uses: actions/upload-artifact@v3 with: name: app-debug path: android/app/build/outputs/apk/debug/app-debug.apk ``` **Release Workflow (.github/workflows/release.yml):** ```yaml name: Release Build on: push: tags: - 'v*' jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build AAR run: bash android/build_go_mobile.sh - name: Decode Keystore run: | echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/keystore.jks - name: Build Release APK env: KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} run: | cd android ./gradlew assembleRelease - name: Create Release uses: softprops/action-gh-release@v1 with: files: android/app/build/outputs/apk/release/app-release.apk ``` **Required secrets:**
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る