はじめに
Goを使いちょっとしたリバースプロキシを書くことで、遠隔にあるGoogle Cloud Workstationsに立てたサーバーに対し、ローカルPCからhttp://localhost:{port}
でアクセスできたので、その方法を紹介します。
Google Cloud Workstations とは
Google Cloudが提供するフルマネージドな開発環境として使えるサービスです。
2023年5月に正式リリースされました。
特徴としては
- ローカルPCにソースコードを置くことなく、ブラウザやIDEからアクセスできるセキュアな開発環境を提供する。
- コンテナ定義として開発環境の設定を記述しておけるので、エンジニアは一貫した開発環境を利用できる。
などがあります。
Google Cloud Workstations の制約
一方、フルマネージドになるがゆえの制約もあります。ポートへのアクセスの仕方がその一つです。
例えば、http://localhost:3000
で待ち受けるサーバーを、Workstation上で立ち上げていた場合、Workstationの外からアクセスするには、次のURLでアクセスする必要があります。
https://3000-workstation1.cluster-xxx.cloudworkstations.dev
https://{port}-{workstation}.{cluster}.cloudworkstations.dev
というフォーマットです。
すでにhttp://localhost:3000
で接続しにいくクライアントがある場合、この変換が必要になるのは使いづらいため、URLを変えずにすむ方法はないかと調査しました。
Goでリバースプロキシを書く
リバースプロキシをローカルPC側に立てておき、そこがよしなに変換してくれれば対応できそうです。
自分が比較的よく書くGoでは、リバースプロキシを立てるための機能が標準パッケージで提供されています。
そのため、ローカルのポートで待ち受けて任意のURLに転送するプロキシを簡単に書けます。
また、Google Cloud Workstationsのドキュメントに、WorkstationへHTTPリクエストを送る方法が書かれています。
具体的には、以下のメソッドを使って、Workstationへのアクセストークンを取得し、それをAuthorizationヘッダーにセットします。
projects.locations.workstationClusters.workstationConfigs.workstations.generateAccessToken
アクセストークンはブラウザ上の"Try this method"から取得することもできます。
以上の2点を組み合わせると、サンプルレベルではありますが50行程度で、localhost:{port}
を待ち受けてWorkstation側へ転送するリバースプロキシを書くことができました。
転送するポートが複数ある場合でも、listenProxy
をgoroutineで複数立てておけば対応できます。
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
// 3000番ポートでリバースプロキシを起動
if err := listenProxy(3000); err != nil {
panic(err)
}
}
func listenProxy(port uint) error {
target, err := url.Parse(fmt.Sprintf(workstationURLFormat(), port))
if err != nil {
return err
}
bearer := fmt.Sprintf("Bearer %s", generateWorkstationToken())
proxy := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.SetXForwarded()
r.SetURL(target)
r.Out.Header.Set("Authorization", bearer)
},
}
return http.ListenAndServe(fmt.Sprintf(":%d", port), proxy)
}
func workstationURLFormat() string {
// サンプルのため転送先のURLはハードコード
return "https://%d-workstation1.cluster-xxxxxxx.cloudworkstations.dev/"
}
func generateWorkstationToken() string {
// WorkstationのアクセストークンはTTLがあるので、実際は自動で生成したり更新する機構が必要。
// see: https://cloud.google.com/workstations/docs/authentication?hl=ja#http
return "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.xxxxxxxxxxxxxxxxxxxxxxxxx"
}
まとめ
実運用に向けた検証はこれからですが、リバースプロキシを介すことでこれまでと変わらないアクセスを実現できそうなことはわかりました。
このようなユースケースが必要となる場面は少ないかもしれませんが、同じような課題に困っている方への参考になれば幸いです。