7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

世間のリバースプロキシサービスが怖いのでワンライナーでAWS上に立てられるようにした

Last updated at Posted at 2020-08-11

きっかけ

Slack botの開発を始めたところ非常にやりにくかった。
手元のPCにSlackアプリがあり、自作のSlack botも起動している状態で、それがお互いに通信するにはSlackのサーバーを経由する必要があるからだ。
そうすると、Slackサーバーから手元のSlack botにアクセスできるようにしなければならない。

ということで、色々調べてみるとリバースプロキシサービスを使って開発している人が多いようだった。
例えば、ngrok, servo, localhost.runなどを使っている人がいた。
ただ、外部のサービスに社内のチャット内容とかトークンとか流したくない・・・と思ったので自分の管理下のAWS VPC内にリバースプロキシを立てることにした。

最終的にこんな感じで起動できるようになった。

$ yarn -s generate 2> /dev/null | aws ec2 run-instances --launch-template LaunchTemplateName=reverse-proxy --user-data file:///dev/stdin

出来ること

リバースプロキシで出来るようにしたこと。
実際はCaddyがやってくれてるので、自分では大したことしてない。

  • HTTPSのリバースプロキシになる
    HTTPSでアクセスでき上流にプロキシする。
    ここで上流とはSlack bot = お手元のPCのこと。

  • 自動でDNSにドメインを登録する
    起動時にDNSにドメインを自動登録する。
    AWS VPC上に起動する前提なのでDNSはRoute53を使う。

  • 自動でTLS証明書を取得する
    登録したドメインでLet's Encryptから証明書を取得する。
    ここがCaddyの機能。

  • 自動で停止する
    SSHのセッションが切れたら指定時間後に自動でターミネートする。
    開発を終えてリモートポートフォワーディングを切断したら放っておけば停止する。
    停止前に再接続するとカウンターがリセットされる。

遣り残したこと

  • NPM
    手元で作って使ってただけなのでNPMモジュールとして動かそうとしてなかった。

  • Let's Encryptからのお叱り
    何度も起動していると都度TLS証明書を取得してしまうので行儀が悪い。
    EIPを割り当てればよさそうだが、今のところその前にDNS登録してしまいそう・・・

  • Slackサーバーからのリクエストの検証
    これはSlack限定の話だが、SlackサーバーからSlack botへのリクエストは SigningSecret で検証ができる。
    Caddyのプラグインとしてリクエストの検証機能があれば、botを作らずともリバースプロキシだけ立てておいてエンドポイントのVerifyを通すことが出来るようになると思う。
    あと、お手元のPCでリクエストの検証を行う危険も回避できる。

実際はただのuser-data

ここの bin/generate.js を見てもらうと分かる通り、実際はサーバーを実装したのではなくほぼCaddyにお任せのEC2インスタンス用user-dataを動的に生成するスクリプトである。

cloud-init の per-instance でCaddyのインストールや起動設定を行い per-boot でRoute53にドメインをUPSERTしている。

使用方法

起動

NPMモジュールになっていないので clone しないといけない。

$ git clone https://github.com/ryo0301/instant-reverse-proxy
$ cd instant-reverse-proxy

.env に設定を書く。
HOSTED_ZONE_DOMAIN に設定するドメインはあらかじめRoute53に用意しておく。
Hosted Zone IDはこのドメイン名から逆引きしている。
残りは適宜設定。

ちなみに、Slack botのエンドポイントとしてSlack側に設定するURLはこんな感じ。

https:// HOST_NAME . HOSTED_ZONE_DOMAIN /slack/events/

$ cat <<EOF > .env
HOST_NAME=$USER
HOSTED_ZONE_DOMAIN=proxy.example.com
CADDY_DL_URL=https://github.com/caddyserver/caddy/releases/download/v2.0.0/caddy_2.0.0_linux_amd64.tar.gz
SHUTDOWN_TIMER_HOUR=1
UPSTREAM_PORT=3000
LOCALE=ja_JP.UTF-8
TIMEZONE=Asia/Tokyo
EOF

これでuser-dataが出力されるはず。
これを手動で貼り付けてもいい。

$ yarn -s generate

また、起動するEC2インスタンスに付けるIAMロールも必要。
必要なアクションはこの2つ。

  • route53:ChangeResourceRecordSets
  • route53:ListHostedZonesByName

EC2のローンチテンプレートを使うと起動が楽になる。
テンプレートにはさきほどのIAMロールの設定など諸々設定しておく。
これで1行で起動できるようになった。

$ yarn -s generate 2> /dev/null | aws ec2 run-instances --launch-template LaunchTemplateName=reverse-proxy --user-data file:///dev/stdin

接続

リモートポートフォワーディング

リバースプロキシが起動したらリモート→お手元のPCにポートフォワードする。
接続できなかったらEC2インスタンスの状態を見たりSSHでログインしてみて欲しい。

$ ssh -i ~/.ssh/id_rsa -l ec2-user i-XXXXXX -N -R 3000:localhost:3000

SessionManagerを使う場合

普段はEC2 Instance ConnectとSessionManagerを使って接続している。
事前にSessionManager Pluginのインストールや ~/.ssh/config に設定を追加する必要がある。

$ cat <<EOF >> ~/.ssh/config
Host i-* mi-*
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
EOF

こちらはEC2 Instance Connectを使う場合。
公開鍵をEC2メタデータサービスに送ったあと、対象のEC2インスタンスに60秒間だけログイン可能になる。
送信に成功したら対応する秘密鍵でポートフォワードする。

$ ssh-keygen -t rsa -b 4096
$ aws ec2-instance-connect send-ssh-public-key --instance-id i-XXXXXX --instance-os-user ec2-user --availability-zone ap-northeast-1a --ssh-public-key file:///$HOME/.ssh/id_rsa.pub

最終的な開発環境

Slack botの開発環境は、AWSの構成も含めて最終的にこんな感じになった。
AWSの構成についてはこちらにまとめた。
[AWS]踏み台不要、キーペア登録不要、ポート開放不要のSSH接続環境を作る | Qiita

reverse-proxy.png

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?