Slack の Custom Slash Commands とかを開発しようとすると、Slack サーバからの Webhook を受け取れるサーバを用意しないといけないので、ローカル環境のみで開発をすることが難しい。
今回は、SSH のリモートフォワードを利用してリモートサーバで受け取った Webhook をローカル環境のサーバにフォワーディングする仕組みについて書く。
前置き
SSH リモートフォワードを行うと、ローカル環境のサーバがインターネットからアクセスできる状態になるため、開発中のアプリケーションの動作確認等で使う場合は注意して欲しい。
リモートサーバを立てる
Webhook を受け取ってローカル環境に流すためのリモートサーバを立てる。
インターネットからアクセスできて sshd が動けばあとはなんでも良い。
今回は AWS で Amazon Linux の t.nano インスタンスを立てて、セキュリティグループで TCP, port: 3000, source: 0.0.0.0/0
のインバウンドトラフィックを許可した。
テキトーな HTTP サーバをローカル環境に立てる
検証のためにテキトーな HTTP サーバを作る。
ローカル環境で以下のようなテキトーな HTML ファイルを作って、
<!doctype html>
<html>
<body>
This is local HTML file!
</body>
</html>
Python 3 の HTTP モジュールを使って HTTP サーバを起動する。1
$ python3 -m http.server 4000
これでローカル環境から http://localhost:4000/ でページにアクセスできるようになったと思う。
SSH トンネルを作る
SSH トンネル (ポートフォワーディング) には「ローカルフォワード」と「リモートフォワード」の二種類があり、今回の用途では「リモートフォワード」を利用する。
ポートフォワーディングについては以下の記事が詳しい。
sshポートフォワーディング - Qiita
リモートフォワードをするコマンドはこんな感じ。
$ ssh -R 3000:localhost:4000 -N <user>@<host>
-N
は「SSH 接続時にコマンドを実行しない」オプション。
バックグラウンドで起動したい場合は -f
オプションを追加する。
-R
オプションでリモートフォワードの設定をしていて、この場合は「リモート側のポート 3000
をローカル側の localhost:4000
にフォワーディングする」という意味になる。
sshd の GatewayPorts を設定する
sshd のデフォルト設定では、リモートフォワード時にループバックアドレス 127.0.0.1
がバインドされるため2、このままではインターネットからアクセスすることができない。
リモートフォワード時のバインドアドレスを変更するには sshd の GatewayPorts の設定を変更する。
GatewayPorts の値 | 効果 |
---|---|
yes | ワイルドカードアドレス 0.0.0.0 をバインドする |
clientspecified | バインドアドレスをクライアント側で指定できる |
no (default) | ループバックアドレス 127.0.0.1 をバインドする |
Webhook が飛んでくるアドレスが分かっているなら clientspecified
を指定してそのアドレスのみをバインドすることでよりセキュアにできると思うが、Slack API の場合は IP Range が固定されていない3とのことなのでこの方法は使えなかった。
開発中のサーバがインターネットに晒されることをそれほど気にしないのであれば yes
を指定すれば問題ない。
設定を変更したあとは sshd を再起動する。
インターネットからアクセスしてみる
http://<リモートサーバ>:3000/
にアクセスしてみて、ローカル環境のサーバに繋がれば正常にリモートフォワードできている。
[追記] 時間が経つとアクセスできなくなってしまう場合は sshd 側で ClientAliveInterval を設定すると良い。