開発環境用のトンネリングツールとそのしくみ（serveo編）

この記事は、[ゆるWeb勉強会@札幌 Advent Calendar 2019](https://adventar.org/calendars/4273)の8日目（2019-12-08）の記事です。<br>（書いてる間に日付が変わりました...）

## はじめに

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8064/ca2e0a64-6135-ae4a-2557-094cfd7a8c3b.png)

![fig02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8064/7b4da28c-cb44-0892-f826-3ecfb3d2a32e.jpeg)

[serveo](http://serveo.net/) は開発環境用のトンネリングツールとして「[ngrok](https://ngrok.com/)よりも手軽！無料で制限も少ない！」と昨年ぐらいから有名になりはじめたものの、次第にサービス停止期間が長くなり、上記のとおり *「誰かserveoがいつ復帰するか知らない？」⇒「来年か、二度とないんじゃない」* と嘆かれてしまうほどになっています。

serveo の問題というよりも、あまりに手軽にURLが発行できてしまい [フィッシングサイトに悪用されやすい為](https://twitter.com/trevordixon/status/1042010682756083712)で[^1]、こういうサービスの宿命なのかもしれません。

serveo.net が執筆当日も死んでいるのでどうしようかなと思ったのですが、[ゆるWeb勉強会＠札幌](https://mild-web-sap.connpass.com/)は特に初学者も意識した勉強会ですから、 **なぜ serveo.net を使って自分のPCのアプリに外部から接続できるようになるのか？** をこの記事の重心にしたい次第です。（開発当事者ではないので外観から。一部推測も入ります）

ちなみにこの夏、ゆるWeb札幌で別のトンネリングツールngrokの解説： [開発環境用のトンネリングツールとそのしくみ（ngrok編）](https://speakerdeck.com/gishi_yama/mild-web-sap06)  を発表しましたのでご興味がある方はぜひ。（ちなみに私はどちらかというとngrokの方が好きです）

## 開発用のトンネリングツール

### トンネリングツールの役割

ngrok や serveo のトンネリングツールの役割は、会社・学校・家庭などの内部（プライベート）ネットワークにあるパソコンへ外部からアクセス可能にすることにあります。

例えば、開発中のWebアプリを顧客に見てもらいたいとき、バックエンド（Web-APIなど）がインターネット越しにフロントエンド（or アプリなど）からアクセスできるかを確認したいとき、外部から開発用PCのWebアプリやバックエンドへ気軽にアクセスできると楽に思えますよね。

一般的にプライベートネットワークでは、ネットワークの出口（ゲートウェイやNAT）で外部から内部へのアクセス（侵入）を防いでいます。この部分を適切に設定すれば外部から内部の特定のPCにアクセスはできます。しかしながら全体のセキュリティに関わる点からも、会社などで開発者や個人が気軽に設定（or申請）するには、色々な面でハードルが高いこともよくあります。

**トンネリングツールは、これをアプリの起動やコマンドの実行だけで、そして起動・実行中のみ外部から内部へのアクセス可能を実現** しようとするものです[^2]。

### トンネリングツールの利用例

たとえば、LineBotを例に説明してみます。

LINEサービスは、ユーザーからLineBot宛にメッセージが送られると、これをトリガーにしてメッセージをバックエンドアプリ宛に送信します。バックエンドアプリがそのメッセージに対する返信を作り、LINEサービスに返答することで、LineBot上に返信が表示されます。

バックエンドアプリはLINEサービスからインターネット上でアクセスできる必要があります。そのため、開発時は開発用のサーバーなどに開発中のバックエンドアプリをデプロイし、動作確認を行います。

ここで、 serveo のような開発用のトンネリングツールを使うことにより、開発用のサーバーを用意しなくても、**LineBotのメッセージの宛先をプライベートネットワーク内の開発環境に直接向ける** ことができるようになります。（実際には、自動的に用意されている仲介サーバー...下の図で言えば、 **serveoサーバーがメッセージを受け取って開発用PCに渡し、開発用PCからの返答をLINEサービスに渡す仲立ち** をします）

![fig04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8064/64db8ce2-d5cd-03f3-57d9-8e9919d1eeff.jpeg)

## serveo のトンネリングのしくみ

serveo は、sshポートフォワーディングという技術をトンネリングの基盤に使っています。

### sshポートフォワーディング

sshポートフォワーディングは、sshを使ってアクセスしたリモートコンピューターやサーバーと連係して特定のポート番号へのアクセスや通信自体を転送する技術です[^3]。

たとえば、example.comにsshでログインできる状態で、アクセス元から

```sh
ssh -L 12345:localhost:2345 xxxx.example.com
```

というコマンドでログインすると、**`アクセス元：12345` へのネットワークアクセスをすべてssh通信経由で `xxxx.example.com:2345` に転送**することができます。

逆に、アクセス元から

```sh
ssh -R 12345:localhost:2345 xxxx.example.com
```

というコマンドでログインすると、**`xxxx.example.com：12345` へのネットワークアクセスをすべてssh通信経由で `アクセス元:2345` に転送**することができます[^4]。

元ネットワークエンジニアの方が**「どんなに厳しいルールにしようがsshが空いてれば何でもできる」**と言っていましたが、使いこなすとネットワーク上の制限を（ある程度）飛び越えて自他ネットワーク内のPC/サービスにアクセスできるので、知っておくと色々な場面で役に立ちます（例えば、ポートが制限されている別の部署から、自分の部署のPCに画面共有するとか）。

### serveo の sshポートフォワーディングによるトンネリング

上記で説明したとおり、sshポートフォワーディングでネットワークアクセスの転送を行うことができます。serveoは、このsshポートフォワーディングをそのままトンネリングに使っています。

たとえば、serveoのサイトの最初の使い方には

```sh
ssh -R 80:localhost:3000 serveo.net
```

とありますが、これはつまりアクセス元から**sshでserveo.netのサーバーにログインすることで `serveo.net：80` へのネットワークアクセスをすべてssh通信経由で `アクセス元:3000` に転送**するということになります。

ただしこれだと複数人が同じコマンドを使ってしまうと、ポートフォワーディングの設定が被ってしまいます。そのため serveo では、

- sshでアクセスしてきた元に対して、ユーザー名とIPアドレスを元に任意のサブドメインを発行<br>（例：xxxx.serveo.net）
- サブドメイン＋ポート番号へのネットワークアクセスを、全てアクセス元に転送<br>（例：xxxx.serveo.net:80をアクセス元：3000に転送）

という仕組みがserveoサーバー側に仕込まれています。

これにより、

```sh
ssh -R 80:localhost:3000 serveo.net
```

というコマンドを実行すると、アクセス元のIPとユーザーごとに `xxxx.serveo.net` のような個別のサブドメインが発行されます。

そして外部から `xxxx.serveo.net:80`（つまり、`http://xxxx.serveo.net/` ）にネットワークアクセスをすると、それが開発環境のアクセス元PCの3000番ポートへのネットワークアクセスにトンネリングされるようになっているようです。

### serveo のさらに便利なところ

serveoを使わなくても、インターネット上に窓口サーバーを置いて、これにポートフォワーディング（もしくはプロキシ）すればいいのでは...と思うのですが、いくつか単純には実現が面倒な部分にserveoは対応しています。

#### httpsへの対応

serveoを使って

```sh
ssh -R 80:localhost:3000 serveo.net
```

を実行すると、実際にserveoから発行されるサブドメインのURLは、 **`https://xxxx.serveo.net/`** になります。通常、httpsは443ポートなので、ポートフォワーディングの設定（ `-R 80:...` ）と一致していません。

serveoは（デフォルトでは）ユーザーからの指定無しに任意のサブドメインを発行しますし、ユーザー独自のドメインでなくても利用できるサービス[^5]なので、ユーザ側でssl/tls証明書を用意する必要がありません。serveo自身の証明書でssl/tls通信が転送されようになっています。

#### サブドメインの自動発行（複数端末からの同時利用）

コマンドで、httpsへのtlsアクセスを転送しようとする場合、serveoでなくても[プロキシサーバーや証明書を用意した上でtcpproxyなどを利用する](https://dev.to/k4ml/poor-man-ngrok-with-tcp-proxy-and-ssh-reverse-tunnel-1fm)方法があるようです。ただこの場合も、サブドメインの自動発行は難しいので、複数人で利用したい場合はフォーワーディングポートを事前に分けておく形になります。

serveoを使えば、プライベートネットワーク内から複数のトンネリングを設定しても、サブドメインは自動発行されます（例：`https://xxxx.serveo.net/`, `https://yyyy.serveo.net/`, ...）。開発が複数人の場合は、こちらの方が断然便利に思います。

#### sshのオプションがそのまま利用できる

sshには元から、接続を維持するための設定（例：`ServerAliveInterval`）などがあるわけですが、こういったオプションはもちろん使えます。（どうしても切断されたときに再接続したい場合は、[autossh](https://www.harding.motd.ca/autossh/)も利用できる）

また、ProxyCommandなどでの多段ssh接続でも利用できるようです。


## おわりに

sshコマンドだけで便利にトンネリングが実現できる serveo の仕組みを外観から紹介してきました。ngrokとあわせて違いを見ると、トンネリング手段ひとつとってもコンセプトが違って面白いなと思います。

これで安定的に稼働率が維持されればもっとユーザーにとっては嬉しいサービスになりますね。（無償なのに無茶言うなという意見もあります）。

ちなみに、ソースコードは公開されていませんが、サービスが落ちてても使いたい...！という方向けに、[serveo自体の評価版バイナリ](http://serveo.net/#self-host)が公開されており、これを使って[docker化](https://github.com/taichunmin/docker-serveo-server)している例もあります（本当はコレを試したかったけど時間がなくなった）。**評価版はトンネル数が3に制限されています**し、サーバーを自前で建てる必要もありますが、試される方はぜひ。


[^1]: ngrokも例外ではなく、[ngrokの日本リージョンドメインがフィッシングサイトの影響を受けていた](https://twitter.com/gishi_yama/status/1166777621583028224) ことがあります。
[^2]:  外部からアクセスを受け入れてしまうのは変わりないので、自分自身が管理していないネットワークでは管理者に利用のお伺いを立てた方がよいでしょう。
[^3]:  最近はクラウド/Dockerなどで使う機会も増えてきているように感じますが、sshや、sshポートフォワーディングの自体については、Qiitaにも無数に記事があるので：[例えばこちら](https://qiita.com/mechamogera/items/b1bb9130273deb9426f5) 、ご存じない方は検索してみてください
[^4]:  `-L` では localhost＝xxxx.example.com、`-R` では localhost＝アクセス元 を示すのがややこしいです。
[^5]:  DNSにtxtレコードを追加して、自分のドメインにも転送できるようです。