# GCP SSH Relay — Reach your phones from anywhere

Expose the SSH server running inside **Termux (and/or Ubuntu-in-Termux)** on one or
more Android phones to the public internet, using a **free-tier Google Cloud VM**
as a relay. No app to install on the computer you connect *from* — just a plain
`ssh` client. No paid services. No port-forwarding on your router.

---

## Table of contents
1. [How it works](#how-it-works)
2. [What already exists on the GCP side](#what-already-exists-on-the-gcp-side)
3. [Cost](#cost)
4. [The two keys](#the-two-keys)
5. [The four scripts](#the-four-scripts)
6. [Onboarding a new phone](#onboarding-a-new-phone)
7. [Connecting from your PC](#connecting-from-your-pc)
8. [Seeing who is online — `list-phones`](#seeing-who-is-online--list-phones)
9. [Boot autostart & the battery watchdog](#boot-autostart--the-battery-watchdog)
10. [Taking a phone offline / freeing a port](#taking-a-phone-offline--freeing-a-port)
11. [Troubleshooting](#troubleshooting)
12. [Security notes](#security-notes)
13. [File & setting inventory](#file--setting-inventory)

---

## How it works

Each phone opens an **outbound** SSH connection to the VM and asks it to
*reverse-forward* a public port back to the phone's local sshd. Your PC then
connects to `VM_IP:PORT`, and the VM pipes it back to the phone.

```
   Your PC                 GCP VM (hub-server)              Phone (Termux)
 ssh -p PORT  ───────────►  136.67.4.216:PORT  ──reverse──►  localhost:8021 (Ubuntu sshd)
                                                        or    localhost:8022 (Termux sshd)
                            ▲  reverse tunnel dialed OUT by the phone  ▲
```

Because the phone dials **out**, it works from behind carrier NAT / firewalls,
and the PC never needs any special software or DNS — it talks to a stable public IP.

**One VM, many phones:** every phone uses a **different public port** on the same
VM (2222, 2223, 2224 …). That's the whole trick to multi-phone support.

---

## What already exists on the GCP side

Set up once; **no per-phone changes needed** (as long as the port is in range and
the shared VM key is reused).

| Thing | Value |
|---|---|
| Project | `personal-projects-hub` |
| VM | `hub-server` — e2-micro, zone `us-west1-b`, Ubuntu 24.04, 30 GB standard disk |
| External IP | `136.67.4.216` (ephemeral — changes only if the VM is **stopped/started**) |
| Relay user on VM | `karthik` |
| Firewall | `allow-relay-range` opens **tcp:2222–2240** (≈19 phones) |
| VM sshd tweaks | `GatewayPorts clientspecified` (allows the reverse binds); `ClientAliveInterval 30` / `ClientAliveCountMax 3` (auto-frees a dead phone's port in ~90 s) |
| Helper | `/usr/local/bin/list-phones` (see below) |

Manage the VM from a phone that has `gcloud`:
```
gcloud compute ssh karthik@hub-server --zone=us-west1-b
gcloud compute instances list --format='value(EXTERNAL_IP)'   # re-check IP
```

---

## Cost

**$0 / month** as configured — it fits GCP's *Always Free* tier:
- e2-micro in us-west1 running 24/7 is covered.
- 30 GB standard persistent disk is within the free allowance.
- The ephemeral external IP is free for a free-tier e2-micro.
- Interactive SSH traffic is a few KB/s — nowhere near the **1 GB/month** free egress.

You'd only pay if you push **big files** through the relay (~$0.12/GB beyond 1 GB/mo)
or reserve a **static IP** (~$3.7/mo — deliberately *not* done; we use the free
ephemeral IP instead).

---

## The two keys

| Key | What it is | Lives where | Purpose |
|---|---|---|---|
| **VM key** (`google_compute_engine`) | private key | on each phone, at `~/.ssh/gcp_vm_key` | lets the phone log in to the VM as `karthik` to open its tunnel. **Reused across all phones.** |
| **PC key** (`pc-access-key` / `.pub`) | keypair; you hold the private half | `.pub` goes into each phone's `authorized_keys`; you keep the private key on your PC | lets **your PC** log in to the phones. Private key is passphrase-protected. |

> Onboarding a new phone therefore needs two files copied to it: the **VM private
> key** (reuse the existing `google_compute_engine`) and the **PC public key**
> (`pc-access-key.pub`). Inbound access to every phone stays locked to your PC key.

---

## The four scripts

All live in the Termux home (`~/`). Two flavours × (setup + teardown).

| Script | Use when the new phone has… | Tunnels | You land in |
|---|---|---|---|
| `gcp-phone-setup.sh` | just Termux (no Ubuntu) | Termux sshd `:8022` | a Termux shell |
| `gcp-phone-teardown.sh` | — | — | — |
| `gcp-phone-setup-ubuntu.sh` | Ubuntu-in-Termux already installed | Ubuntu sshd `:8021` | a full Ubuntu |
| `gcp-phone-teardown-ubuntu.sh` | — | — | — |

> **Which flavour?** For a *brand-new* phone, the **Termux-native** pair is simpler
> and more robust (nothing extra to install; you can still `proot-distro login
> ubuntu` afterwards). Use the **Ubuntu** pair when the phone already runs an
> Ubuntu you specifically want to expose.

### `gcp-phone-setup.sh` (Termux-native)
```
bash gcp-phone-setup.sh --port 2223 \
     --vm-key ./google_compute_engine \
     --pc-pubkey ./pc-access-key.pub
```
Options (defaults): `--vm-ip 136.67.4.216` · `--vm-user karthik` ·
`--local-ssh-port 8022` · `--low-battery 20` · `--no-boot` · `--no-watchdog` · `--help`

Does: install `openssh termux-api util-linux` → key-only Termux sshd → install VM
key → write `gcp_relay.sh` (single-instance per port via `flock`) → wire `~/.bashrc`
+ Termux:Boot autostart + battery watchdog → **pre-check the port is free on the
VM** → start the tunnel → verify end-to-end.

### `gcp-phone-setup-ubuntu.sh`
```
bash gcp-phone-setup-ubuntu.sh --port 2224 \
     --vm-key ./google_compute_engine \
     --pc-pubkey ./pc-access-key.pub
```
Extra options: `--ssh-port 8021` · `--distro-type proot-distro|start-ubuntu22` ·
`--ubuntu-rootfs PATH` · `--enter-cmd "CMD"` (plus all the ones above).

Auto-detects the Ubuntu launcher (`proot-distro` **or** `start-ubuntu22.sh` +
`ubuntu22-fs`), installs `openssh-server` inside Ubuntu, writes config/keys/relay
**directly into the rootfs**, and sets Ubuntu sshd to key-only on 8021. The relay
runs *inside* Ubuntu and tunnels `localhost:8021`.

### Teardown scripts
```
bash gcp-phone-teardown.sh                 # Termux-native; uses ~/.relay-port
bash gcp-phone-teardown-ubuntu.sh --port 2224
```
Common flags: `--port N` · `--keep-sshd` · `--disable-autostart`
(Ubuntu one also: `--ubuntu-rootfs` · `--distro-type`). See
[Taking a phone offline](#taking-a-phone-offline--freeing-a-port).

---

## Onboarding a new phone

1. Install **Termux** (Google Play build) on the phone; open it once.
2. Copy **4 files** into the phone (cloud drive, USB, or `scp` through an existing
   tunnel): the two scripts for your chosen flavour, plus `google_compute_engine`
   (VM key) and `pc-access-key.pub` (PC public key).
3. Pick an unused **port** in 2222–2240 (run `list-phones` on the VM to see which
   are taken).
4. Run the matching **setup** command above with `--port <N>`.
5. Connect from your PC (next section). The setup script prints the exact command.
6. To make it survive reboots, finish the
   [boot autostart](#boot-autostart--the-battery-watchdog) steps.

---

## Connecting from your PC

```
# Termux-native phone (lands in Termux shell; user printed by the setup script):
ssh -i pc-access-key -p 2223 <termux-user>@136.67.4.216

# Ubuntu phone (lands in Ubuntu as root):
ssh -i pc-access-key -p 2224 root@136.67.4.216
```
You'll be asked for the **PC key passphrase**. On Linux/Mac `chmod 600 pc-access-key`
first; on Windows use the built-in OpenSSH and restrict the file's permissions.

If your network can't resolve/route to the IP, it's a plain IP so DNS is not
involved — see [Troubleshooting](#troubleshooting) if the VM IP ever changes.

---

## Seeing who is online — `list-phones`

SSH into the VM and run:
```
list-phones
```
```
Relay phones online — hub-server ...
PORT    PHONE_PUBLIC_IP        STATE
-------------------------------------------------
2222    106.192.160.41:46128   ONLINE
2224    49.37.x.x:52210        ONLINE
```
It scans ports 2222–2240 and shows the public IP of the phone holding each one.

---

## Boot autostart & the battery watchdog

The setup scripts install two things so the tunnel returns automatically:

- **`~/.termux/boot/00-start-relay.sh`** — run by **Termux:Boot** after the phone
  boots (post first-unlock on encrypted phones). It starts the watchdog, holds the
  wakelock, then starts sshd + the relay.
- **`~/battery-watchdog.sh`** — at **≤ 20 % and unplugged** it `sync`s and releases
  the wakelock so Android can shut down **gracefully** instead of dying mid-write.
  This exists because an ungraceful power-loss under load can corrupt `/data` and
  brick the phone. **Test it before trusting unattended boot.**

To enable boot autostart:
1. **Termux 2024.10.24+ (Google Play build)** has boot support **built in** — no
   separate "Termux:Boot" app needed. (Older/deprecated Termux needs the F-Droid
   build; `pkg install termux-boot` does **not** exist — it's app functionality.)
2. Grant realme/ColorOS **auto-launch** for Termux and set its battery to
   **Unrestricted** (the aggressive killer will otherwise stop it).
3. **Verify the watchdog first:**
   ```
   setsid bash ~/battery-watchdog.sh & ; sleep 2 ; cat ~/battery-watchdog.log
   ```
   (For a real trigger test: temporarily set `LOW=95`, unplug, watch the log, then
   set it back to `20`.)
4. Reboot, unlock, wait ~1 min, then connect from your PC.

> Manual fallback that always works even without boot autostart: just **open
> Termux** — the `~/.bashrc` hook starts everything (3-second countdown; press
> **Ctrl-C** for a plain shell; `~/.no-ubuntu-autostart` disables the auto-enter).

---

## Taking a phone offline / freeing a port

Run the matching teardown on the phone:
```
bash gcp-phone-teardown.sh              # or gcp-phone-teardown-ubuntu.sh
```
It (1) stops the relay loop, (2) **SSHes to the VM and kills the session holding
that port so it's free immediately** (instead of waiting ~90 s for the VM's
auto-reap), (3) stops the phone's sshd, (4) releases the wakelock.

- `--keep-sshd` — free the port but leave the phone's SSH server running.
- `--disable-autostart` — also neutralise the `~/.bashrc` + boot hooks so it does
  **not** come back on next open/reboot (full decommission).

After teardown the port is immediately reusable by another phone.

---

## Troubleshooting

| Symptom | Cause & fix |
|---|---|
| `Permission denied (publickey)` from your PC | Wrong/absent PC key, or you didn't enter the passphrase. Use `-i pc-access-key`; `chmod 600` it. |
| Connection **refused/timeout** to `VM_IP:PORT` | Phone's tunnel isn't up. On the phone check `~/relay.log` (Termux) or Ubuntu `/root/relay.log`. Re-open Termux to auto-restart. |
| Setup says **"port already in use on the VM"** | Another phone holds that port. Pick a different `--port`, or `list-phones` to see what's free. |
| `remote port forwarding failed for listen port` in the log | A stale session is holding the port on the VM. It self-clears in ~90 s (ClientAlive); or run the teardown's port-release, or `gcloud compute ssh … "sudo ss -tlnp | grep :PORT"` then `sudo kill <pid>`. |
| Tunnel was working, now dead after VM **stop/start** | The ephemeral IP changed. Get the new one: `gcloud compute instances list --format='value(EXTERNAL_IP)'`, then update `VM_IP` in each phone's `~/.gcp-relay.conf` (Termux) or `<rootfs>/root/.gcp-relay.conf` (Ubuntu). |
| Nothing runs at boot | Termux battery not **Unrestricted** / auto-launch not granted (realme killer). Also confirm you're on Termux 2024.10.24+ and opened it once. |
| Watchdog log says `termux-battery-status missing` | `pkg install termux-api` **and** install the Termux:API app. |

---

## Security notes

- Phones are **key-only** (password auth disabled). Good — a stable public endpoint
  attracts brute-force bots; keys make that futile.
- The **VM key is shared** across phones (that's why no per-phone VM change is
  needed). Anyone with that file can open a tunnel on the VM as `karthik`. Keep it
  safe; rotate it (and the VM's `authorized_keys`) if it leaks.
- Inbound access to each phone is restricted to **your PC key**; keep its
  passphrase private (don't paste it into chats or logs).
- The relay only forwards raw TCP; the SSH session inside it is end-to-end
  encrypted as usual.

---

## File & setting inventory

**On each phone (Termux `~/`):**
- `gcp-phone-setup.sh`, `gcp-phone-teardown.sh` — Termux-native flavour
- `gcp-phone-setup-ubuntu.sh`, `gcp-phone-teardown-ubuntu.sh` — Ubuntu flavour
- `gcp_relay.sh` — the reverse-tunnel loop (Termux `~/` or Ubuntu `/root/`)
- `battery-watchdog.sh`, `~/.termux/boot/00-start-relay.sh`
- `~/.gcp-relay.conf` (or `<rootfs>/root/.gcp-relay.conf`) — `VM_IP`, `VM_USER`,
  `LOCAL_SSH_PORT`
- `~/.relay-port` — this phone's assigned port
- `~/.ssh/gcp_vm_key` — the VM key; `~/.ssh/authorized_keys` — your PC key
- Logs: `~/relay.log`, `~/boot.log`, `~/battery-watchdog.log`

**On the VM:**
- `/usr/local/bin/list-phones`
- `/etc/ssh/sshd_config.d/00-reap.conf` (ClientAlive), `99-relay.conf` (GatewayPorts)
- Firewall: `allow-relay-range` (tcp:2222-2240)

---

*Relay VM IP: `136.67.4.216` · ports `2222–2240` · one port per phone.*
