api gatewayのcustom authorizersでip制限するのはやっぱりできなかった話

結論:なんでできなかったか

https://gyazo.com/dd58ad4bfc8bba9a08818f7a5611f8b4

なんでip制限したかったか

api gatewayへのアクセスを制約する方法としてapi keyで認証させるケースをよく見るけど、結局エンドポイントとapikeyが盗まれると叩けてしまうよなぁと思っていて、

  • 会社が小さくて誰でもaws触れる
  • 誰か会社をやめる可能性がある
  • 金になるAPIである

って条件が揃った時、やっぱりIP制限するのが一番安心できると思ったのでした。
cloud frontとWAFを挟んでIP制限している例もみるのですが、結局api gatewayのエンドポイントとapikeyが盗まれたら一緒なのでは?と思っている。
間違ってたら指摘頂けると幸せです。

やってみたこと

カスタム認証のIdentity token source

api gatewayにカスタム認証(認可)を付ける方法があって、これに習って、特定のIPのみを認可するように作りたかったのでした。

Identity token sourceでサポートされるコンテキスト変数

Identity token source にIPが渡ってくるように書ければ認可用のlambdaにIP渡せそう!!このへん
この method.request.header.Auth をいい感じに書き換えれば、と思ってこのへんをみてみると
https://gyazo.com/c0b855ca9597ca5050e50adfcebe5ec6
きたっぽい!
サポートされるコンテキスト変数をみてみる。
https://gyazo.com/fc0ee3e78c3ad0f05a08f30994abcbaa
あったー!コレ設定したら勝ちだ!

設定してみる

https://gyazo.com/dd58ad4bfc8bba9a08818f7a5611f8b4
ぴゃーーーーーーー!!!!
まぁ Identity token source のとこにも書いてあったんですけどね。

https://gyazo.com/511574d47ca885d42cbb8c872a4e33ed

代替案

この方がやってるとおりlambdaにてipチェックはできそう。でもlambdaごとにipチェック挟むの書かなきゃではあるのかなぁ。。。
プロキシしてくれるlambdaに一旦引き入れてそこでチェックして、各lambdaにinvokeすれば良いのかも(未検証)

続きを読む

個人的mastodonの出来事(技術編)

何の話か

結果的に中規模インスタンス(一応某リストで上位100以内にはいっているから現状間違ってないはず)を4台立てることになったので、その経緯とかそこで知ったこととか書きます。

得られた知見

総括として、

  • 100-300人程度であればDBあたりはまだネックにならずメモリが真っ先に悲鳴をあげた(1GBしかない場合)
  • 100人超えると何もいじってないt2.microだときつい
  • 300人超えたくらいでもt2.smallでとりあえずスペック的には動く

ただし、金銭的な都合でRDSやElastiCache、複数EC2による冗長構成などをとっていないために、インスタンスサイズを変更する時など絶対にサービスが止まるので、そこは今回妥協しています。
また、CloudFrontやS3を使うこともできるのですがこちらも金銭的事情と今の所なんとかなっているので使ってないです。

環境としては

  • mailにmailgun
  • SSL証明書にLet’s Encrypt
  • サーバーはだいたいAWS
  • 最低限のメトリクス監視はmackerel

です。

mastodonをはじめたところ

4/12に多分Twitterでみかけてmastodonを始めた。現mstdn.jpインスタンスでアカウントを作りました。
しばらく楽しくやってたのですが、どんどん人が増えるのでコンセンプト的には分散させたほうがいいよなーという思いとか、mastodonで知り合った人にボドゲインスタンスほしいって言われたとか、この勢いなら人くるんじゃね?という企みとかそういうのがあって立てようかなと考え始めました。
結果、4/12は見送ったものの、4/13にmstdn.jpが不安定なこともあり分散する流れになるだろうと判断してそれなら急ごうとその夜から構築を開始することにしました。

1台目 tablegame.mstdn.cloud

どうせAWSとか使うと金銭的に維持できないと思い、家にあったサーバーを流用することに。
帰りにLANカードとバカHUBを買って帰宅。
マシン構成としては

  • ubuntu 14.04 LTS
  • CPU: Sempron 145
  • RAM: 4GB

と非常に貧弱ながらも、ジャンルがニッチなのでこれで試そうと突っ込みました。
ちなみに、Dockerは使っておらず、理由としてはDockerにそこまで明るくないので死活監視とかDB周りをいじる必要が出た時にうまく弄れる自信がなかったためです。

構築で苦労したことは、LANとFletsのPPPoE直の物理インタフェース2本構成になったので、そのroutingとかiptablesわすれたとかその辺です。
また、設定を間違えたままresorceをbuildしたところ、アプリの設定など以外の動的に変わる部分(メインのタイムラインですね)が何も表示されない事態に陥り、そこで困ったりしました。
消してrebuildしたら治りましたが。
あとは、ubuntu 14.04なのでデフォルトのdaemon化する設定が使えず、現状nohupで動かしています。streamのnodeがよく落ちるのでそこだけpythonで監視して落ちてれば再起動するスクリプトをcronで回すという手荒い対応です。

結果として現在300人弱ですが、快適に動いています。

2台目 socialgame.mstdn.cloud

思ったよりうまく動いて、昼休みに先輩と喋っていた勢いで2台目を作りました。
こちらはジャンル的に人が集まる可能性もあったので、AWSを使って、でもまずは最小構成でということでt2.microにすべて載せる構成で作りました。

こちらはUbuntu 16.04なので公式ドキュメント通りに構築しました。(ただしこちらも非Dockerで)

ちゃんと動いたのは良かったものの、その日の夜にメモリを使いきりswapし始めた途端にアクセスさえ困難な状況に。
この時、sshで入れない状況だったのですが公式ドキュメント通りに作っていたのが功をなし、AWSコンソールから直接停止、インスタンスサイズアップで難を逃れました。

結果t2.small一台で現状300人超えたくらいですが、快適に動いています。

3台目, 4台目

2台目を参考に構築しました。4台目のR18可能なソシャゲインスタンス構築の話についてはできれば別の記事で書きます。

技術的タスク

  • バージョンアップが早いのでバージョンアップをどう実施していくか。
  • SSL証明書の更新やLet’s Encryptについてもっと知る
  • mailgunが一部制限かかっているのでそれの対応

最後に

タスク整理とかもあったので書いてみたんですが、これ、得られる情報ありますかね。。。
とりあえずお一人様や100人未満のインスタンスなら多分t2.microで動くと思うので無料枠でお試しとかするといいですよ、くらいかもしれないです。

続きを読む

AWS上でインスタンスを構築してwordpressを起動するまで

About

wordpressをAWS上で立ち上げてみました。

利用技術

  • Terraform
  • Itamae

ソースコードはこちらです https://github.com/tjinjin/wordpress-sandbox

Terraform

main.tf
data "aws_ami" "centos" {
  most_recent = true

  filter {
    name   = "product-code"
    values = ["aw0evgkw8e5c1q413zgy5pjce"]
  }
}

resource "aws_instance" "wp" {
  ami                    = "${data.aws_ami.centos.id}"
  instance_type          = "t2.micro"
  key_name               = "tjinjin-terraform"
  vpc_security_group_ids = ["${aws_security_group.wp.id}"]

  root_block_device {
    delete_on_termination = "true"
  }

  tags {
    Name = "wordpress"
  }
}

resource "aws_security_group" "wp" {
  name        = "wordpress"
  description = "wordpress sg"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["${var.my_ipaddr}"]
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["${var.my_ipaddr}"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags {
    Name    = "wordpress"
    Created = "terraform"
  }
}

output "wordpress_ip" {
  value = "${aws_instance.wp.public_ip}"
}

AMIはcentosの公式のものを使用しています。product-codeはこちらのページから判断しました。 https://wiki.centos.org/Cloud/AWS

Itamae

一部プラグインを使いつつ、他は自分で書きました。もともとchefを使っている人間だったのでhttp_request というresource名を知らなかったため、苦労しましたw

$ itamae ssh --host aws-wordpress roles/wp.rb 

これをすれば適用できます。

残作業

ここまでくればあとはmysqlの設定とwordpress上での設定だけです。

$ cat /var/log/mysqld.log | grep generate
$ mysqladmin -u root password -p
$ mysql -u root -p

mysql> create database wordpress;
mysql> grant all privileges on wordpress.* to wordpress@localhost identified by '<password>';

あとは画面で設定をすれば一応起動するところまで到達できます。

続きを読む

AWSの無料SSLを使ってmastodonインスタンスを立てる手順

サーバー周りをAWSで固めてmastodonインスタンス( https://mastodon.kumano-ryo.tech )を立てたので、その手順と資料をまとめました。

SSLは無料で使えますが、サーバーの使用量とかは普通にかかります。とはいえ、お1人〜10人鯖ぐらいならEC2のmicroでも動かせるので、そんなにお金はかからなそう。
立て終わった後に書いたので、記憶違いなどあるかもしれません。コメント・編集リクエストしてください :pray:

なぜAWSなのか

人数が少ないときは安く済ませられるし、リソースが足りなくなってもお金を突っ込めばスケールできるから(要出典)。

必要なもの

  • ドメイン
  • メールを配信する手段
  • AWSのアカウント
  • docker・Web・Linux・AWSなどについての基本的な知識

構成

  • mastodonの公式レポジトリに出てくるdocker-compose.ymlを使う
  • 無料SSLを使うためにRoute53とEastic Load Balancerを使う
  • Redisを外に出すためにElastic Cacheを使う(Optional)
  • Postgresを外に出すためにRDSを使う(Optional)
  • メール送信サービスとしてはSendGridを使った
    • 本筋とは関係ないので今回は省略
    • SMTPが使えればなんでもいい

手順

  1. 設定ファイルを埋める
  2. SSL接続を可能にする
  3. HTTPでのアクセスを禁止する
  4. 画像や動画などをS3に保存するようにする
  5. RedisとPostgresを外に出す(Optional)

設定ファイルを埋める

まずはEC2のインスタンスを立てましょう。OSはubuntu、インスタンスタイプはmediumでいいと思います。microにすると、CPUだかメモリが足りなくてAssetsのコンパイルでコケます。
しかし、サーバー自体はmicroでもそこそこ快適に動くので、デプロイが終わったらmicroにしましょう。
Security Groupの設定については、とりあえず80番と22番が任意の場所から接続できるようになってれば良いです。

そしてEC2上で、mastodon本体をcloneしてきます:

$ git clone https://github.com/tootsuite/mastodon.git

公式のREADMEを参考に、mastodonの設定をします。この時、LOCAL_HTTPSについては、falseにしてください

sudo docker-compose up まで行けたら、EC2インスタンスの80番ポートを3000番ポートにリダイレクトします:

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000

これにて http://yourdomain.com/から、mastodonインスタンスにアクセスできるようになりました!!!!

SSL接続を可能にする

とはいえhttpでログインするのは怖いので、SSLの設定をしましょう。
Elastic Load Balancerの無料SSLを使います。

ELBの作成

Elastic Load Balancerを作ります。
EC2のダッシュボード > ロードバランサー > ロードバランサーの作成から、ロードバランサーを作成します:

  • リスナー

    • HTTPSとHTTPの両方を選んでください
  • 証明書の選択

  • セキュリティグループの設定

    • 任意のIPアドレスからHTTPSが受けられるようなセキュリティグループを作成して設定する
  • ターゲットグループ

    • HTTPだけを追加する
  • ヘルスチェック

    • HTTPで/aboutにする
  • ターゲットの登録

    • 先ほど作成したEC2のインスタンスを選択する

      • ポートは80番を選択する

※作成されたELBのarnはメモっておきましょう。

ELBとインスタンスの接続

EC2ダッシュボード > ターゲットグループから、先ほど作成したターゲットグループを選び、「ターゲット」に登録してあるEC2インスタンスの状態がhealthyであることを確認する。

もしunhealthyであれば、ドキュメント等を参考にして解決しましょう。

ドメインとRoute 53を連携する

ドメインをELBで使えるようにします。
Route 53 > HostedZones > Create Hosted ZonesからHostedZonesを作成します。
HostedZoneを作成したら、ドメインのNSをHostedZonesのNSの値で置き換えます(参考: お名前.comのドメインをAWSで使用する4つの方法
)。
そして、Create Record Setから、Type ARecord Setを作成します。この時、ALIASをYesにして、Alias Targetを先ほどのELBのarnに設定します。

Congrats!

ここまでの手順で、https://yourdomain.comから自分のmastodonインスタンスにアクセスできるようになったはずです!やったね!

もし繋がらなかった場合は、ELBのセキュリティグループやターゲットグループの設定を見直してみてください。

最終的な状態では、

  • ELBにはHTTPSかHTTPでアクセスできる

    • ELBのリスナーに80番ポートと443番ポートが設定されている
  • ELBに入ってきたHTTPSまたはHTTPのアクセスは、EC2の80番ポートに転送される
    • ターゲットグループの「ターゲット」のEC2インスタンスの「ポート」の欄が80である
  • EC2のインスタンスは80番ポートでアクセスを受ける
  • 以上を妨げないようなセキュリティグループの設定である
    • ELBとEC2やその他のAWSのサービスが、すべて別のセキュリティグループを持つ

が満たされているはずです。

HTTPでのアクセスを禁止する

前節まででSSLでのアクセスが実現されましたが、依然HTTPでのアクセスも可能です。
EC2インスタンスへのアクセスがELB経由で行われるようにし、また、HTTPでアクセスされたときはHTTPSにリダイレクトするようにしましょう。

まず、EC2インスタンスのセキュリティグループのインバウンドルールについて、HTTPの行の「送信元」の欄に、ELBのセキュリティグループのID(sg-********)を入力して保存します。
これによって、ELBからのみEC2インスタンスにHTTPSでアクセスできるようになりました。

次に、EC2インスタンスの中で、以下のような設定ファイルでnginxを起動します:

server {
  listen 80;

  location / {
    if ($http_x_forwarded_proto != 'https') {
      rewrite ^ https://$host$request_uri? permanent;
    }
  }
}

これにより、HTTPアクセスをHTTPS付きのURLにリダイレクトすることができます。

画像や動画などをS3に保存するようにする

この状態だと、アップロードされた画像・動画やユーザーのアイコン画像は、すべてdockerコンテナの中に保存されます。
そのため、sudo docker-compose downするたびに、画像と動画が消えてしまうし、アイコン画像もなくなってしまいます。
これではうまくないので、メディアファイルのストレージをS3に設定しましょう。

まず、S3のバケットを作成します。バケットの名前とリージョンは覚えておきましょう。
次に、AWSのIAMコンソールからユーザーを作成して、アクセスキーを取得します。このとき、既存のポリシーを直接アタッチから、** AmazonS3FullAccess**を追加しておきましょう。

そして、.env.production.sampleのコメントアウトしてある設定を利用して、以下のように設定を書き加えます:

S3_ENABLED=true
S3_BUCKET=${your-bucket-name}
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
S3_REGION=${your-bucket-region}
S3_PROTOCOL=https
S3_ENDPOINT=https://s3-${your-bucket-region}.amazonaws.com

これで、画像・動画がS3にアップロードされるようになりました。

RedisとPostgresを外に出す(Optional)

前節までで、所望のmastodonインスタンスが手に入ったと思います。
この構成では、中央のEC2インスタンスの中で、Rails・Redis・Postgres・Sidekiq・nodejsの5つのDockerコンテナが動いていることになり、負荷が集中しています。

ところで、AWSはRedis専用のインスンタンスを提供していますし(ElasticCache)、postgres専用のインスタンス(RDS)も提供しています。
実際どのぐらい効果があるのかわかりませんが、これらのコンテナを中央のEC2から切り離してみましょう。

この辺からは適当です。

インスタンスを立てる

PostgresとRedisのインスタンスを立てます。

このとき、Postgresの設定は、mastodonのレポジトリ内の設定ファイル(.env.production)のDB_*変数に合わせて適当に変えましょう。
DB_HOSTの値は後で取得します。Redisも同様です。

次に、PostgresとRedisのセキュリティグループを編集して、EC2のインスタンスからアクセスできるように設定します。
「HTTPでのアクセスを禁止する」の節と同じ要領でできます。

Postgresのインスタンスタイプについてですが、無料枠に収まるようにmicroにしましたが、ちゃんと動いてるようです。Redisのインスタンスは、無料枠が無いようなので、適当にmicroにしました。今のところ大丈夫そうです。

PostgresとRedisのEndpointをメモっておきます

.env.productionを編集

.env.productionREDIS_HOSTDB_HOSTを、先程メモったEndpointに書き換えます。

docker-compose.ymlを編集

mastodonのdocker-compose.ymlを修正して、dbコンテナとredisコンテナの部分をコメントアウトしましょう。また、他のコンテナのdepend_onの部分もそれに合わせてコメントアウトします。

再起動

$ sudo docker-compose build
$ sudo docker-compose up -d

にてmastodonを再起動します。
これによって、RedisとPostgresがAWSのサービスを利用する形で切り出されたことになります。

その他

続きを読む