概要
ReverseProxyをGAEで作成すると、既設のサーバを簡単にSSL対応にできる。
ここでいう簡単とは...
- サーバ証明書取得が不要
- httpサーバ(nginx/apache)の設定が不要
- (GCP上にプロジェクトを作成して)30分程度あればSSL化可能
経緯
Androidアプリを作成するチームから下記の困りごとを相談された。
「Android Pでアプリの動作確認をしていたら評価用サーバとの接続に問題が生じた。商用サーバでは問題ないんだけど...」
ここで、商用サーバはSSL化済み。評価用サーバは非SSL。
原因
Android P で TLS のデフォルト化によるユーザー保護
上記のページに記載されている通り、Android PではアプリがアクセスするサイトがSSLでない場合、アクセスを拒否する模様。これはアプリが使用するWeb上のAPIだけでなくWebViewに表示するコンテンツも対象になる模様?
もちろん回避方法はあるけど、せっかく商用サーバがSSL化しているのに評価用サーバが非SSLのままなのはよろしくない。じゃ評価用サーバもサーバ証明書を取得するかというと事務手続きも面倒だ。費用がかかるなら決済も必要だし...
対策
GAEでReverse Proxyを立てることにした。
GCP上にプロジェクトを作って下記の2つのファイルを用意しデプロイするだけ。
app.yaml
runtime: go112
handlers:
- url: /.*
script: auto
secure: always
main.go
package main
import (
"context"
"log"
"net/http"
"net/http/httputil"
"os"
)
// TargetHost はhttps化する対象のホスト
var TargetHost = "your.host"
type myTransport struct {
Transport http.RoundTripper
Context context.Context
}
func main() {
http.HandleFunc("/", handleFunc)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
log.Printf("Listening on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}
func handleFunc(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
director := func(request *http.Request) {
request.URL.Scheme = "http"
request.URL.Host = TargetHost
}
client := http.DefaultClient
transport := &myTransport{
Transport: client.Transport,
Context: ctx,
}
rp := &httputil.ReverseProxy{
Director: director,
Transport: transport,
}
rp.ServeHTTP(w, r)
}
func (t *myTransport) transport() http.RoundTripper {
if t.Transport == nil {
return http.DefaultTransport
}
return t.Transport
}
func (t *myTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return t.transport().RoundTrip(req)
}
main.go内のTargetHostに対象のhttpサイトを指定する。
以下のコマンドでデプロイ
gcloud app deploy --project [project-id]
上記のデプロイが完了したらブラウザに"https://[project-id].appspot.com"を入力してSSL化できているか確認する。
なお、プロジェクトの課金設定を変更して「非課金」にしても評価目的であれば耐えられるはず。
タイトルに「GoogleAppEngineでサーバをSSL化する」とか書いちゃったけど、主な対象としてはグローバルIPだけ取得して、ドメイン取得していない評価用サーバ等になります。
Web上のAPIなら上記で問題ないと思いますが、WebViewに表示するコンテンツの場合、追加でリンクを適切に修正しないといけないですね。
更新
2020/08/18
元々 Go1.9(appengine 1st gen)用に書いていたコードをGo1.12(appengine 2nd gen)用に改めました。