GCEとSSHでお手軽プロキシサーバ

Posted by rhoboro on 2019-02-15

GCEインスタンスを経由してインターネットにアクセスする手順をまとめました。 ターミナルのほか普段使っているブラウザ(Google Chrome)でも利用できるようにします。
アクセス元情報をjsonで返してくれるサイトにアクセスし、日本国内(尾道とか!)からアクセスしてもUSとして表示されたらゴールです。

巷ではSNIフィールドを使った特定サイトのアクセス遮断がホットな話題になっているので、こういったネットワーク系の知識は少し持っていても良いかもしれません。

本題はGCEインスタンスに限った話ではないので、EC2やDigitalOceanのDropletでも同じです。

環境

今回動かした環境は次の通りです。

  • OS: macOS Sierra
  • プロキシサーバ: GCEインスタンス
  • ブラウザ: Google Chrome
  • 使うコマンド: gcloud, ssh

それでは実際にプロキシサーバを用意してみましょう。

プロキシサーバの用意

やることは次の2つだけです。

  • インスタンスの作成
  • SSHコマンドの実行

それでは実際にやってみます。

# インスタンスの作成
$ gcloud compute instances create --zone us-west1-a myproxy
Created [https://www.googleapis.com/compute/v1/projects/myproject/zones/us-west1-a/instances/myproxy].
NAME     ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
myproxy  us-west1-a  n1-standard-1               10.138.0.3   35.233.196.31  RUNNING

# プロキシサーバにする
$ gcloud compute ssh --zone us-west1-a myproxy -- -N -p 22 -D localhost:10080
Warning: Permanently added 'compute.2212974415330211298' (ECDSA) to the list of known hosts.

gcloud compute sshコマンドの行は--以降がsshコマンドのオプションになっています。 通常のsshコマンドだとこんな感じになります。

$ ssh -N -p 22 -D localhost:10080 www.example.com

たったこれだけでプロキシサーバになっています。 -Nでリモートでのシェル実行の抑制を、-p 22で接続先ポート番号を指定しています。 そして、-Dlocalhostの10080番ポートを使ってSOCKS Proxyを作成しています。

確認する

本当にこれだけでプロキシサーバとして動くのか確認してみます。 まずは通常通りcurlを実行してみましょう。

$ curl https://api.ip2geo.pl/json/
{"db":"MaxMind","country":"JP","city":"Iwakuni","lat":"34.1500","lon":"132.1833"}

岩国からのアクセスと認識されました。わたしは尾道に住んでいるのでだいたい合ってそうです笑
続いてオプションでプロキシサーバを指定して実行してみましょう。

$ curl --proxy socks5://localhost:10080 https://api.ip2geo.pl/json/
{"db":"MaxMind","country":"US","city":"Mountain View","lat":"37.4192","lon":"-122.0574"}

"US"と表示されました!!
オプションを毎回つけるのが大変なときは、環境変数http(s)_proxy=socks5://localhost:10080を設定するといいようです。

$ export http_proxy=socks5://localhost:10080 https_proxy=socks5://localhost:10080
$ curl https://api.ip2geo.pl/json/
{"db":"MaxMind","country":"US","city":"Mountain View","lat":"37.4192","lon":"-122.0574"}

リモートでの名前解決

プロキシとしてsocks5://を指定するとローカルでの名前解決になりますが、socks5h://を指定するとリモートで名前解決をリモートでしてくれるようです。

実際に試して確認してみましょう。
まず前提として、GCEでは同一プロジェクト内のGCEインスタンス同士はインスタンス名だけで名前解決してくれます。 そこでGCEインスタンスを新たに1台作成し、Webサーバを起動させておきます。

今回はWebコンソールからOSにContainer-Optimized OSを指定、Allow HTTP(S) trafficにチェックをつけたインスタンスを作成しました。 名前はデフォルトのままinstance-1としました。

インスタンス起動後、SSHで中に入り次のコマンドを実行すると80番ポートで待ち受けてくれます。

$ docker run --rm -it -w /usr/src/app -p 80:8080 python:3.7 python -m http.server 8080

それでは、リモートで名前解決されることを確認してみます。 環境変数をsocks5h://に変更し、curlを実行すると次のようにinstance-1という名前でアクセスできます。 これでリモートにあるmyproxyインスタンスで名前解決されていることが確認できました。

$ export http_proxy=socks5h://localhost:1080 https_proxy=socks5h://localhost:1080
$ curl instance-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
</ul>
<hr>
</body>
</html>

ブラウザで利用する

ブラウザなどのアプリケーションでSOCKS proxyを利用するにはネットワーク設定を変更します。 macOSの場合はSystem Preferences... > Network > Wi-Fi (環境により異なる) > Advanced... > Proxiesを開いてください。 Proxiesの画面がひらけたら、SOCKS Proxyを選択しlocalhost10080を指定します。 設定を反映させたら完了です。

pref

実際にGoogle Chromeで試すとアクセス元が"US"になっていることがわかります。

chrome_geo

名前解決もリモートで行われているようですね。

chrome_name

クリーンアップ

プロキシの設定は使い終わったらオフにしてください。 このプロキシの設定は、一度設定しておけば次のコマンドでON/OFFの切り替えができます。

# SOCKS Proxyの設定をOFFにする(必要に応じてsudoで実行してください)
$ networksetup -setsocksfirewallproxystate Wi-Fi off
# SOCKS Proxyの設定をONにする
$ networksetup -setsocksfirewallproxystate Wi-Fi on

最後に作成したインスタンスを削除しておきましょう。 コマンドでやるならこんな感じになります。

$ gcloud compute instances delete --zone us-west1-a myproxy
$ gcloud compute instances delete --zone us-east1-b instance-1

まとめ

名前解決をリモートで行えるのは面白いですね。
これなら巷で話題のSNIフィールドを使った特定サイトのアクセス遮断も回避できそうな気がします(未確認)。 どこかに詳しい人がいましたら@rhoboroに教えてください笑
SSHのポートフォワーディング機能は他にも面白い使い方がたくさんあるのでこれからももっと遊んでいきたいと思います。

参考サイト

tags: ssh, gcp, gce, ec2, network