SpotProxy

Bootstrap scripts and resources for the SpotProxy prototype.

Setup Spot Server

Run on a newly provisioned Spot VM:

curl -fsSL https://spotproxy-setup.webdemos.online/scripts/setup-spot-server.sh | bash

Downloads

Scripts

provision-spot-server.sh

#!/usr/bin/env bash
set -euo pipefail

NAME="${1:-spotproxy-test}"
ZONE="${SPOT_ZONE:-us-central1-a}"

echo "Creating $NAME in $ZONE..."

gcloud compute instances create "$NAME" \
  --zone="$ZONE" \
  --machine-type=e2-micro \
  --provisioning-model=SPOT \
  --instance-termination-action=DELETE \
  --image-family=ubuntu-2404-lts-amd64 \
  --image-project=ubuntu-os-cloud \
  --boot-disk-size=10GB \
  --boot-disk-type=pd-standard

IP="$(
  gcloud compute instances describe "$NAME" \
    --zone="$ZONE" \
    --format='get(networkInterfaces[0].accessConfigs[0].natIP)'
)"

echo
echo "Spot VM created"
echo "Name: $NAME"
echo "IP:   $IP"
echo
echo "SSH:"
echo "gcloud compute ssh $NAME --zone=$ZONE"

setup-spot-server.sh

#!/usr/bin/env bash
set -euo pipefail

BASE_URL="https://spotproxy-setup.webdemos.online"
PUBKEY_URL="$BASE_URL/ssh/id_rsa_proxy_client.pub"

PROXY_USER="${1:-$USER}"
PROXY_HOME="$(getent passwd "$PROXY_USER" | cut -d: -f6)"

if [[ -z "$PROXY_HOME" ]]; then
    echo "Unknown user: $PROXY_USER" >&2
    exit 1
fi

echo "Installing SpotProxy prototype server..."

sudo apt-get update
sudo apt-get install -y openssh-server curl

sudo systemctl enable --now ssh

sudo install -d \
  -m 700 \
  -o "$PROXY_USER" \
  -g "$PROXY_USER" \
  "$PROXY_HOME/.ssh"

TMPKEY="$(mktemp)"
trap 'rm -f "$TMPKEY"' EXIT

curl -fsSL "$PUBKEY_URL" -o "$TMPKEY"

grep -q '^ssh-' "$TMPKEY" || {
    echo "Downloaded file does not look like an SSH public key." >&2
    exit 1
}

sudo touch "$PROXY_HOME/.ssh/authorized_keys"

if ! sudo grep -qxF "$(cat "$TMPKEY")" \
    "$PROXY_HOME/.ssh/authorized_keys"; then

    cat "$TMPKEY" |
      sudo tee -a "$PROXY_HOME/.ssh/authorized_keys" >/dev/null
fi

sudo chown "$PROXY_USER:$PROXY_USER" \
    "$PROXY_HOME/.ssh/authorized_keys"

sudo chmod 600 \
    "$PROXY_HOME/.ssh/authorized_keys"

PUBLIC_IP="$(curl -4fsSL https://ifconfig.me)"

echo
echo "========================================"
echo " SpotProxy server ready"
echo "========================================"
echo
echo "Public IP: $PUBLIC_IP"
echo "SSH user:  $PROXY_USER"
echo
echo "Run this ON THE CLIENT:"
echo
echo "ssh -i ~/.ssh/id_rsa_proxy_client \\"
echo "  -N -D 127.0.0.1:1080 \\"
echo "  ${PROXY_USER}@${PUBLIC_IP}"
echo
echo "Test with:"
echo
echo "curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me"
echo

spot-proxy-client.sh

#!/usr/bin/env bash
set -euo pipefail

sudo apt-get update
sudo apt-get install -y redsocks nftables

sudo tee /etc/redsocks.conf >/dev/null <<'EOF'
base {
    log_debug = off;
    log_info = on;
    daemon = on;
    redirector = iptables;
}

redsocks {
    local_ip = 127.0.0.1;
    local_port = 12345;
    ip = 127.0.0.1;
    port = 1080;
    type = socks5;
}
EOF

sudo redsocks -t -c /etc/redsocks.conf
sudo systemctl enable --now redsocks

echo "SpotProxy client prerequisites installed."

spot-route.sh

#!/usr/bin/env bash
set -euo pipefail

TABLE="spotproxy"
REDSOCKS_PORT=12345

usage() {
    echo "Usage:"
    echo "  sudo $0 enable USERNAME"
    echo "  sudo $0 disable USERNAME"
    echo "  sudo $0 status"
    exit 1
}

[[ $EUID -eq 0 ]] || {
    echo "Must run as root."
    exit 1
}

ACTION="${1:-}"
USERNAME="${2:-}"

enable_user() {
    id "$USERNAME" >/dev/null 2>&1 || {
        echo "Unknown user: $USERNAME"
        exit 1
    }

    UID_NUM="$(id -u "$USERNAME")"

    # Recreate our tables from a known state.
    nft delete table ip "$TABLE" 2>/dev/null || true
    nft delete table ip6 "$TABLE" 2>/dev/null || true

    # IPv4: transparently redirect HTTP/HTTPS to redsocks.
    nft add table ip "$TABLE"
    nft "add chain ip $TABLE output {
        type nat hook output priority -100;
        policy accept;
    }"

    nft add rule ip "$TABLE" output \
        meta skuid "$UID_NUM" \
        ip daddr != 127.0.0.0/8 \
        tcp dport '{ 80, 443 }' \
        redirect to :"$REDSOCKS_PORT"

    # IPv6: prevent HTTP/HTTPS from bypassing the IPv4 proxy.
    nft add table ip6 "$TABLE"
    nft "add chain ip6 $TABLE output {
        type filter hook output priority 0;
        policy accept;
    }"

    nft add rule ip6 "$TABLE" output \
        meta skuid "$UID_NUM" \
        tcp dport '{ 80, 443 }' \
        reject

    echo "SpotProxy enabled for:"
    echo "  User: $USERNAME"
    echo "  UID:  $UID_NUM"
    echo
    echo "IPv4 TCP 80/443 -> redsocks :$REDSOCKS_PORT"
    echo "IPv6 TCP 80/443 -> REJECT"
}

disable_user() {
    # Prototype currently has one proxied user at a time.
    nft delete table ip "$TABLE" 2>/dev/null || true
    nft delete table ip6 "$TABLE" 2>/dev/null || true

    echo "SpotProxy routing disabled."
}

status() {
    echo "=== IPv4 ==="
    nft list table ip "$TABLE" 2>/dev/null ||
        echo "Not enabled"

    echo
    echo "=== IPv6 ==="
    nft list table ip6 "$TABLE" 2>/dev/null ||
        echo "Not enabled"
}

case "$ACTION" in
    enable)
        [[ -n "$USERNAME" ]] || usage
        enable_user
        ;;
    disable)
        disable_user
        ;;
    status)
        status
        ;;
    *)
        usage
        ;;
esac