社内でngrokを利用するにあたって、情シス部門にサービスの概要を説明する必要があった話

#目的
社内でngrokを利用するにあたり、どういうサービスなのかを情シス部門に説明しておく必要がありました。
とは言ってもサービスの詳細な仕組みは調べても出てこず、公式のドキュメントを見たら英語で書いてあったので自分なりに読み解いてまとめてみました。
念の為ですが、私が読み解いた結果ですので、誤り等あっても責任は負いかねます。

#TL;DR
[まとめ](#まとめ)をどうぞ。

#そもそもngrokとは
そもそも、の話です。
ngrokは、「ローカル環境で開発しているサーバ（例えばWebサーバ）を、一瞬でインターネットに公開するためのサービス」です。
本来こういうことをするときは、社内ネットワークならDMZの話やらファイアウォールやら色々と通らなければいけない壁が大きく、結果断念してしまうことがありますが、このサービスはそういった障害をアッサリと無視します。

#相談した経緯
確かに開発には便利だけど、何かあったら責任取れないので一応弊社情報システム部門に問い合わせました。残念ながら、どういうサービスなのかまでは把握されていない様子だったので、こちらから説明することにしました。
とは言え、ngrokのWebページにはざっくりとした説明のみで、そこまで詳細には触れられていませんでした。

#GitHubに上がっていた公式ドキュメントとソースを読んだ
ドキュメントは[こちら](https://github.com/inconshreveable/ngrok/blob/master/docs/DEVELOPMENT.md)に色々載っていました。

ngrokサーバとやりとりをしているメッセージはstructで定義されているようで、ソースは[ここ](https://github.com/inconshreveable/ngrok/blob/master/src/ngrok/msg/msg.go)にありました。

##大まかな流れ
要はこういうことのようです。
そもそも、まずはじめにインストールせよと言われるngrokクライアント。これが相当な曲者です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/21b1771f-6ffc-a344-e0d8-a45d6e495a3d.png)

###1. ngrokクライアントの初動
ngrokクライアントは、まずngrokサーバへのTCPコネクションを開始します。これ自体は多くの場合問題無いでしょう。このTCPコネクションは「Control Connection」と呼ばれており、長期間張り続けています。

###2. 認証情報の受け渡し
直後に、クライアントから認証情報等が送信され、それに基づいた応答がngrokサーバから返されます。この応答は「AuthResp」と呼ばれています。

###3. トンネルの作成
クライアントはngrokサーバにトンネルを作成せよというメッセージを送ります。これは「ReqTunnelメッセージ」と呼ばれています。ReqTunnelメッセージの中身はこんな感じです。

```go
type ReqTunnel struct {
	ReqId    string
	Protocol string

	Hostname  string
	Subdomain string
	HttpAuth  string

	RemotePort uint16
}
```

ngrokサーバはトンネルを作成し、NewTunnelメッセージを返します。中身はこんな感じです。この時点で、パブリックなURLが発行されています。無料プランだとこのパブリックURLは[http://xxxxxxxx.ngrok.io](http://xxxxxxxx.ngrok.io)のようなランダムなものになっています。トンネルが閉じてしまうとこのURLは無効化されます。これ以降、パブリックURLを利用した外部クライアントからのリクエストはすべてngrokサーバが一旦受信することになります。

```go
type NewTunnel struct {
	ReqId    string
	Url      string
	Protocol string
	Error    string
}
```
###4. 外部クライアントがやってきた！
外部クライアントは、発行済のパブリックURLを使用してHTTPリクエストを出します。
それを受け取ったngrokサーバは、元々張ってあったControl Connectionを使用して、新たなReqProxyメッセージを送信します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/620f9e41-7a47-b7ea-f1f5-157a65b56976.png)

これを受け取ったクライアントはProxy Connectionと呼ばれる新しいTCPコネクションを開始します。外部クライアントからの情報は、このProxy Connectionを伝ってやりとりされます。
ngrokサーバではどのProxy ConnectionとControl Connectionが同一デバイスからのものであるかを把握しており、制御用の情報はControl Connectionで、外部クライアントとの通信はProxy Connectionを通じて行います。

###5. 小細工
最初のConctrol Connection、外部クライアントが来た後のProxy Connectionいずれに関しても言えることですが、通信が内側から始まっています。
通常はサービスはクライアントのリクエストから始まりますが、そうしてしまうとファイアウォール等諸々のハードルがあります。すべての通信を内側から行うことにより、これを回避しているということですね。

#まとめ
まとめると、こういうことです。
最終的にTCPコネクションは2本存在しており、いずれも社内から通信が始まります。
Control Connectionが先に作られ、後からProxy Connectionが作られます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/7a0cd49d-84dc-a88f-e244-aaf57e8a4da4.png)

1. 社内PC「ngrokサーバよ、これからよろしくな！認証情報とか色々渡すからトンネル作ってや」
2. ngrokサーバ「OK、作った。ついでにパブリックURL発行しておいた」
3. 外部クライアントがパブリックURLを使用してリクエストを送信する
4. ngrokサーバ「おい、客が来たぞ。あとは打ち合わせたとおりによろしくな」
5. 社内PC「おけ」
6. （別通信で）社内PC「ngrokサーバよ、もしかしてもしかして俺宛の通信が来てたりしてないかね？」←ひどい茶番
7. ngrokサーバ「なんと奇遇な！ちょうど来てるよ。はいどうぞ」
8. 社内PC「どれどれ、ふむふむ。これがレスポンスや。あとはよろしく」
9. ngrokサーバ「おけ」

#考察
というわけで、導入時にインストールするngrokクライアントが相当な曲者（良い意味で）であることがわかりました。ホントよく考えますね、こういうの。

ちなみに、こういった通信の方法は「コネクトバック通信」とも呼ばれるらしく、バックドアを作成するマルウェア等で見られる手法らしいです。
非常に便利なサービスですが、正しい知識を持って運用したいですね。

ちなみに情シスとは現在進行系で交渉中です。