水深1024m

技術的なメモとか日記的なもの

ISUCON5 本選でトップスコア fail した

ISUCON5 に .dat として @y_matsuwitter@TakatoshiMaeda で参加してきました。本選6位だった昨年に引き続き、今年で2年目です。 幸運にも予選を(2日目)1位で通過することができてたのですが、主にネットワークやミドルウェア周りを担当していた自分としては結構なニート状態だったので、本選ではもうちっとは貢献したいなーという気持ちがありました。

Go - ISUCON5予選でスコア34000を出す方法 - Qiita

で、本選ですが、アプリケーションレイヤでやったことは @y_matsuwitter の記事にほぼ全て書いてあります。 ISUCON5本選にてスコアトップの18万点でfailしました - Qiita

サーバ構成に関することも書いてあるので僕が記事書く必要もなさそうですが、せっかくなのでやったことをちょこまかと書きます。

基本的なセットアップ

@TakatoshiMaeda が完璧に仕上げたセットアップスクリプトで一撃でした。Ubuntu 15.04 + systemd だった予選と違い Ubuntu 14.04 + upstart + supervisord だったわけですが、そのへんの差異も特に気にせずドーンという感じです。 @TakatoshiMaeda++

開幕 tcpdump

改善に限られる時間が限られており、特に今回の問題では外部 API という要素が入ったこともあって、可能な限り早い段階でそれらを含めた全体像を把握する必要がありました。 アプリケーションレイヤの改善を僕以外の二人に任せていたので、アプリケーションの変更をせずある程度の分析を行っておきたいと思い、ベンチマークのパケットダンプを取っていました。 把握できたのは大体以下のようなことです。

  • ベンチマーカが送ってくるヘッダ、リクエストパターン
    • Accept-Encoding: gzip とか (最終的には忘れてたけど。。。)
  • 外部 API が返してくるヘッダ
    • リクエスト/レスポンスの仕様
    • tenki API が Last-Modified を返していたのはこの時に分かってた
  • 外部 API に接続するときに DNS リクエストをしていない
    • /etc/hosts に書いてあった
    • ハードコードしても問題ない

まあ、初手の分析としては十分なのかなーと思いました。 で、API の中にわざわざ HTTPS のエンドポイントが含まれてるのを見かけたので、おもむろに nghttp2 とリンクした curl をビルドして接続したところ HTTP/2 接続に成功しました。ベンチマーカーや外部 API に仕込まれていた要素については開始45分くらいで大体把握できていたと思います。

js の改変

一通りのチェックが終わり、大体の方針を決めて @y_matsuwitter が作業に入り、僕と @TakatoshiMaeda は airisu.js から行う XHR 先を分散できないかという検証をやっていました。結果としてそもそも js の改変そのものがどのようなレベルでも許されていなさそうなことが分かったのですが、CORS 周りの設定にハマり少し時間を無駄にしてしまったのが後悔でした。そもそも静的ファイルの変更がどのくらい許されるか自体はもっと小さな変更で確認できたはずで、検証すべきことを切り分けず最小限のことだけをやらなかったのは大きなミスでした。

ミドルウェア類の設定

正直ここについては、ざっと絵を描いた段階でチーム3人とも意見が一致していたこともあり、"あとは書くだけ"状態だったので特に考えることもハマることもなかったです。nginx, Redis, kernel 周りの設定など必要のあることをやっただけ。 @y_matsuwitter の Redis 実装をマージして1台で実行して13万点を超えたので、そこから複数台構成になるように修正。 先頭に置いた nginx (静的ファイル配信 + リバースプロキシ) で CPU affinity を固定してあげたりするとこのマシンは CPU を使い切れる状態に達しました。

終了まで

ちょうどこのタイミングで行われたベンチマーカーの修正により "ちゃんと fail するようになった" バグが最後まで取りきれずに敗北することになります。 正直この修正が発表された時はあまり真剣に考えてなかったのが痛かったです。会場内で2つだけ用意された会議室 (先着順で使える) を扉を閉めて使っていたこともあったのかもしれないけれど、アナウンスはちゃんと聞いて自分たちへの影響を確かめようというのは反省でした…

ミドルウェア側での大きな構成変更は3台構成にしてからはほぼなく、先述のバグの他にも fail がいくつかあったため他の2人にその修正をひとまず任せ、再起動耐性のチェックを行いました。バグが最後まで取りきれなかったため、再起動テストをそもそも行うことができなかったのですが、結果として再起動後もきちんと動いていたようなのでまあここはよかったのかな。。。 その後はバグ取りのサポートなどをしていましたが、結局取りきれず提出。

他にやりたかったこと

大体のことは @y_matsuwitter の記事に書いてあるのでそちらを。 他には ken API のデータを見ていて、ああこれはみんな大好きKEN_ALL.csvじゃないか、ということでこれを作りなおしてそもそも API リクエストをやめる、という案はありましたが、それ以前の問題が多すぎたので却下。

感想

Cache-Control に泣いた去年と比べ、初手でそういった目に見えないポイントを洗い出すことができていたのは進歩だったかなーと思います。スコアがどうだろうと fail というのは変わらず、”僕と @TakatoshiMaeda がこまい改善を積み上げておいて @y_matsuwitter がかめはめ波を撃つ” というのが毎回のスタイルではあるのですが、もっとやれることはあったなーと思うし実力不足を痛感しました。 あと1つのfailがなければスコア的には優勝だった、というのは事実そうなのですが、このfailを落ち着いて潰せるかどうかというところに大きな差があったわけで、そういう意味でも「次やったらきっと勝てる」というレベルの話でもないよなーという実感があります。悔しい。

とはいえチームとしての動き方も毎回良くなっていることは確かなので、来年は足回りの力をがつっと上げて優勝しに行きたいと思っています。

今回は、予選本選通して特にベンチマークがとても快適だったのが印象的でした。 本選の Microservices 問題も、自分としてはリアリティのある問題だったこともあり楽しかったです。 予選、本選、懇親会 (ハロウィンで渋谷の治安が崩壊してましたが) どれをとっても最高でした。関係者のみなさまありがとうございました!来年もぜひよろしくおねがいします。

AWS re:Invent 2015 これが見たいリストと tips とか

来週から AWS re:Invent 2015 ですね。 今年は幸運にも 無料で行ける ことになったし、日本からの参加者も多いようなので 気になってるセッションなどについて書いておきます。

選び方

セッションは カテゴリ+レベル という表記がされていて、自分の興味分野やレベルに合わせて選びやすくなっています。 まあ普段 AWS を使ってる人であれば Level 4 (Expert) を中心に3 (Advanced) のもので興味のあるやつを拾っていくと良いんじゃないかなーと思います。

Deep Dive 系セッションについて

○○ Deep Dive というタイトルの、AWS の SA や PM が担当しているセッションが多く用意されています。(だいたい Level 4)
だいたいドキュメントに書いてあることの話かと思いきや、書かれていない内部実装の話や初公開される数値 (ベンチマークなど) 、さらにはしれっと新機能がアナウンスされることがあります。VPC ClassicLink とかそうでしたね。
ただ、資料がよくできていて、知りたいことは大体書いてあったりするので、これらのセッションに行きすぎてもあんまり意味ないかなーと最近は思っています。 新サービスの Deep Dive もいきなり発表される (そしてすぐ埋まる) ので、気になる人は早めに行きましょう。

ユーザセッションについて

AWS の人ではなく、AWS を使っている人たちのセッションです。Netflix や Riot Games (LoL 作ってる会社) など日本でも知られているような規模の会社の話が聞けます。
去年行った時は Chaos Engineering でおなじみ Netflix の障害挿入に関する話とか、Adroll が100万 req/s を 5ms 以内で返す話 とか見てて面白かったです。
ただ、スポンサーセッションもあって、自社製品の紹介がメインみたいなセッションもあるので、abstract をよく読んで決めたほうが良いと思います。 ホテル内はアホかってくらい広いので、あーやっぱ違うわと思ってから移動するのに結構時間を食います。

個人的に気になってるセッション

まあだから何という話ですが、個人的に気になってるセッションについて簡単に書いておきます。選んでる時にメモつけたやつもそのまま載せているので参考までに。間違ってたこと書いてたら教えてください。
個々のセッションへのリンク貼るのはめんどうだったので、気になる人は Session Catalog をどうぞ。

  • ISM301 - Engineering Netflix Global Operations in the Cloud
    • Netflix のオペレーション全体像について聞けそう
  • STG403 - Amazon EBS: Designing for Performance
  • CMP405 - Containerizing Video: Creating the Next Generation Video Transcoding Pipeline
    • 動画変換を ECS や Lambda, S3, EFS (!!) などを使ってうまいことやってる話っぽい。SONY (グローバルのほう) の人
  • GAM402 - Turbine: A Microservice Approach to Three Billion Game Requests a Day
    • Turbine という Warner Brothers Games 傘下のゲームスタジオがゲームバックエンドを microservices で作っている話
  • CMP404 - Cloud Rendering at Walt Disney Animation Studios
    • 書いてあるのそのままだけど、Walt Disney のレンダリング環境に関する話
  • SEC302 - IAM Best Practices to Live By
  • SEC312 - Reliable Design and Deployment of Security and Compliance
  • SEC304 - Architecting for HIPAA Compliance on AWS
    • 病院の臨床情報とかを共有するサービスで AWS を使いながら HIPAA 準拠する話
  • ARC403 - From One to Many: Evolving VPC Design
    • 毎年おなじみのセッション。VPC を使うネットワークアーキテクチャについて、毎年のアップデートを盛り込みながら最新ベストプラクティスみたいなのが聞ける
  • CMP407 - Lambda as Cron: Scheduling Invocations in AWS Lambda
    • セキュリティベンダーでおなじみ Sophos のエンジニアのセッション。タイトルで吹いた
  • DAT407 - Amazon ElastiCache: Deep Dive
    • ElastiCache の Deep Dive と Riot Games の話が聞ける
  • SEC320 - AWS Security Beyond the Host: Leveraging the Power of AWS to Automate Security and Compliance
    • evident.io のスポンサーセッション。Intuit (会計ソフト) の DevSecOps の話も聞けるぽい
  • SEC402 - Enterprise Cloud Security via DevSecOps 2.0
    • 最近ちまちま聞く DevSecOps についてのセッション
  • SEC401 - Encryption Key Storage with AWS KMS at Okta
  • SEC326 - Security Science Using Big Data
    • これも IntuitAWS が吐くセキュリティ系のログをうまいこと解析する話っぽい
  • CMP403 - AWS Lambda: Simplifying Big Data Workloads
  • NET404 - Making Every Packet Count
    • 実践ハイパフォーマンスEC2ネットワーキング
  • CMP310 - Building Robust Data Processing Pipelines Using Containers and Spot Instances
  • NET402 - Consolidating DNS Data in the Cloud with Amazon Route 53
  • SEC403 - Timely Security Alerts and Analytics: Diving into AWS CloudTrail Events by Using Apache Spark on Amazon EMR

そもそも時間が重複してるとか、予定があるとか、体力的な問題 (結構体力使う) で絶対全部は回れないんですが、このへんの話を聞きたいと思っています。あとはせっかく現地行ってるので、セッション後に細かい話を直接聞きに行ったりするとよさそう。
ランチ食べながらやる BoF とか, サービスチームと話ができる Ask me about コーナーとかもありますね。コミュ力

ちなみに全セッション基本的に先着順で、人数上限が設定されてるセッションだと waitlist とか表示されてることもあるのですが、特に関係なく先着枠に入れば入場できるはず (逆に Scheduler に入れてても入場できない) です。

その他

内容が一新されて ISUCON みたいになってる GameDay とか、 ホテル中のバーが貸し切りで無限に飲んで話せる Pub Crawl とか、 Pub Crawl の腹ごしらえに便利っぽいチキン大食いイベントの Tatonka Challenge とか、 人数規模が爆発してついに会場が隣のホテルの駐車場という謎スケールになった re:Play とか、 セッション以外でも面白イベントがいろいろありますね。英語力のなさには定評があるけど勇気を出してがんばっていきたい。

はー楽しみだ。参加されるエンジニア各位ぜひ声かけてください。よろしくおねがいします。

iOS9 で導入される ATS とは結局何なのか

iOS9 で導入される ATS (App Transport Security) の話です。 ATS は OSX, iOS アプリケーションが NSURLConnection, CFURL, NSURLSession を利用してサーバに接続する際 現時点で最善に近いセキュアな接続をデフォルトとする仕組みです。 この記事で言いたいことはだいたい 公式ドキュメント に書いてあるのですが、"よくわからんから全てにおいて ATS を無効化する" で終わらないためにどうすれば良いかについて書きます。 個人的に AWS をよく使っているので、AWS における事情なども適宜付加します。

何が変わるのか

"Default Behavior" の項目に記載されていますが、 ATS が有効になっていることによって外部接続先に要求される要素は、以下のものです。

  • サーバは TLS 1.2 をサポートしていなければならない
  • 利用できる暗号 (正確には Cipher Suite) は、Forward Secrecy を提供できる (かつ、ドキュメントに示されている) ものに限られる
  • 利用されるサーバ証明書は SHA256 以上のハッシュアルゴリズムによって署名されており、2048ビット以上の RSA 鍵、もしくは 256ビット以上の ECC 鍵が使われている必要がある
    • 検証できない証明書はエラーとなり接続ができない

また当然というか前提ですが、サーバは HTTPS に対応している必要があります。

対応の要否と対応方法について

接続先サーバが HTTPS に既に対応しているのであれば、あとは上に書いた3つの条件を満たせば問題なく通信できます。 cipherscanQUALYS SSL Labs の SSL Test を使うとこの条件を一度に検証できるでしょう。 以下に、SSL Test を利用した場合の例について記載します。 (検査対象のドメインがしばらくトップページに載るので、イヤな方は "Do not show the results on the boards" にチェックを入れてください)

TLS 1.2 に対応しているかどうかは "Protocols" 欄を見れば分かります。 f:id:kani_b:20150908000129p:plain

対応していない場合は TLS 1.2 を有効にする必要があります。現代のソフトウェアで TLS1.2 を有効にできない環境は意図的に設定しない限りそう無いと思いますので、 有効にしてください。

提供する Cipher Suite の確認は "Cipher Suites" 欄で確認できます。 横に小さく FS と記載されているものが Forward Secrecy (前方秘匿性、詳細はググれば分かります) を提供する CipherSuite です。 より厳密には、Apple の公式ドキュメント に記載されている "accepted ciphers" に記載があるものが含まれていれば OK です。 f:id:kani_b:20150908000149p:plain

こちらも、ここ数年のサーバ環境であれば問題なく設定できると思います (AWS ELB であれば、対応する CipherSuite にチェックを入れるだけです)。

最後に気づきにくいのが、サーバ証明書のハッシュアルゴリズムと鍵の強度について。 RSA 公開鍵長については2048ビット未満の鍵長を使った証明書発行が既に禁止されているため、 この条件を満たせないことがほぼ無いかと思われます。 問題となるのは "SHA256 以上のハッシュアルゴリズムによって署名されている" 部分でしょう。 AWS 公式サイトの結果を例とすると、以下のように、Signature Algorithm として SHA256 という表記が含まれていれば問題ありません。 f:id:kani_b:20150908000758p:plain

対応していない場合は、例えば SHA1withRSA といった表記になります。 こちらはまさに移行期間真っ最中 (いわゆる SHA-2 移行) というところですので、対応がサーバによってまちまちだと思われます。 最近証明書を購入された方であれば、明示的に SHA-1 証明書を購入していない限り問題はありません。 長い有効期間の証明書を購入している場合でも、多くのケースでは既に認証局側に SHA-2 証明書を再発行してもらえば移行することが可能なはずです。 ただし、SHA-2 証明書に移行することにより、いわゆるガラケーはじめ未対応端末での接続ができなくなりますので、クライアント環境をよく確認して移行するようにしてください。

対応ができないケース

多くの場合、特に iOS アプリケーションは開発者が直接面倒を見ていない (例: 広告配信) サーバに接続することがあると思います。 そうした場合、 Web サービス全体の HTTPS 事情を見ていても、 iOS9 リリースと同時に全接続先で ATS を有効にすることは困難だと思います。 そのため、最初のうちはまず自社 (あるいは自身) で作っている接続先を ATS 対応できるようにした上で、接続先サーバの対応を待って完全 ATS に切り替えていく必要があるでしょう。 私は検証できていませんが、ATS はドメインごとに有効/無効を指定できるようです。 またドキュメントにも書いてありますが、個々の要素 (TLS サポート、Cipher Suite など) を個別に無効にすることもできます。 SHA-2 証明書にまだ移行できない場合でも他の要素は有効にできるというケースも多いのではないでしょうか。

ATS 無効化は "セキュアではない" のか

諸説あるとは思いますが、ATS を切ることそのものが危険な状態に繋がるわけではなく、 こうしたものをあまり理解せずに切ってしまったり、切ったまま対応を考えないといったことが危険な状態を招くと考えています。 (ATS はあくまでベストプラクティスへの準拠を要求するものであり、ATS を切ったとしても安全な通信は可能です) 個人的には、世の中の HTTPS 化の流れは避けられない (かつ、移行すべき) と考えているので、歓迎している動きではあるのですが、 Apple が ATS で示しているものについて、あまり理解されていないのかなーと感じたので書いてみました。

なお、私は普段 iOS / OSX 開発に携わっているわけではありませんので、間違いなどがありましたらご指摘いただけると嬉しいです。

AWS ウルトラクイズで優勝してきた (AWS Summit 2015)

ちょっと日が経ってしまったけど、先週AWS Summit 2015 という AWS のカンファレンスに行ってきました。

http://www.awssummit.tokyo/

参加自体は今年で3回目。ちょうど社会人になってから毎年参加していることになります。 個人的に今年のメインはデベロッパーカンファレンス (DevCon) でした。 去年の Summit に行った時に、だんだんターゲット層が変わってきたなーというのを 発表内容とかを見ながら思っていたのだけど、ちょうどそう思う人達に向けたようなカンファレンスでした。来年もぜひお願いします。

どのセッションでも発表中はカメラのシャッター音が鳴りまくっていて、登壇者が「資料あとで公開します」と言っても鳴り止まなくて、大変だなーという気持ちになりました。

個々の発表内容については資料とか映像を参照するとして、全体的な感想としては、 これまであまり繋がりのなかった分野の技術者 (テレビ局とかもっと大きい規模のところ) と AWS で繋がって交流できているのは面白いなーと思いつつ参加していました。 データセンターという枠がなくなって、共通化される部分が大きくなったというのがあるのかな。

全セッション終了後に最後のイベントとして JAWS-UG (勉強会) があり、そこでは AWS ウルトラクイズという、AWS に関するクイズ大会がありました。優勝賞品はラスベガスで行われるカンファレンス re:Invent の参加一式 (渡航費宿泊費参加費)。 新卒1年目、2年目と参加して両年とも4位(入賞は3位から)という微妙すぎる順位で毎年帰ってきていたので、今年は一発当てたいと思っていたところ、優勝しました。 ガソリン入れて気合注入と思っていたら緊張のためか開始時には4缶空けていて途中の問題をよく覚えていないのですが、最後の問題はその日の朝にリリースされた Kinesis のパラメータの変更 (PutRecord の最大サイズ) でした。 ちょうど Kinesis 使いたい構成を考えていた時に読んでいたリリースだったこともあり、まあ運が良かったと思います。

ちなみに AWS は色々なリリースを粒度に応じてか、色々な場所で公開してくる (AWS Blog, 各サービス (Mobile とか Security) の Blog, forum, etc) ので、フィード購読して流し読むと"えっそんな変更あったの"が少なくなります。
今の自分の仕事は "AWS を使うこと"ではなく "サービスをユーザに届けること" なので、そんなん細かく読んでる意味あるの?みたいな自問自答もしますが、むしろ自分たちが(半分)身を預けてるサービスだし、とことん使い倒さないと意味ないなーと。 別に AWS だろうと GCE だろうと Azure だろうと同じですが、いわゆるロックインを恐れて中途半端に使う状態が一番最悪ですね。 むしろ他のサービスもとことん使っていった上で最適なものを選んで使うことができれば、それが一番健全なのかなーと思います。各社色々なサービスを出してきていますし、もうちょっとするといわゆるマルチクラウドのような環境で色々できるようになるんでしょうか。楽しみです。

というわけで、同じく1年目から運良く参加し続けている re:Invent に今年も無事参加できることになりました。10月なのでまだまだ先ですが楽しんできます。わーい。

ISUCON4 予選2日目2位通過

できました。楽しかったー
ISUCON については公式ページをご覧ください。
ISUCON公式Blog

.dat というチームで参戦しました。チームメイトは @TakatoshiMaeda@y_matsuwitter というメンバー。
以前一緒の会社で働いてて、@TakatoshiMaeda とは現職でも同僚。
一緒に朝までデバッグしてたりつらいことを共有してきた仲ということもあり、色々やりやすかったです。

今回の ISUCON4 予選最大の誤算は ShellShock と AWS リブート祭でした。 ISUCON の性質上 Web インフラ界隈の参加者が多いようですし、 他の参加者でも巻き込まれた方々が多かったんじゃないでしょうか。お疲れ様でした。
ほんとは僕の自宅でモニタとか持ち込んで前日からワイワイやる予定だったのですが、 状況を冷静に考えて会社のオフィスでやることにしました。
祭が 3:00 AM - 7:00 AM JST という時間ゆえに当日影響を確認しつつ眠れたのは 5:30 AM という状態で、 @TakatoshiMaeda に「オフィスのこのへんで寝てるから起こして」と書き残して眠りについたら、
本人曰く「遠足前の小学生」だったテンションでその3時間後に叩き起こしてくれました。感謝。

#isucon 4予選参戦記 - 2日目暫定3位でした - takatoshi-maeda’s diary
#isucon 2014予選二日目を三位で通過した話 - Qiita

うちのチームがやったことは大体上記 URL の二人の記事に書いてありますので今更書くこともそんなに無いと思います。
@TakatoshiMaeda が全体の分析や初期のサーバセットアップ周りをめっちゃ前から準備してくれていて、当日非常にスムーズに開始できました。 大体11時前には環境構築、初期のベンチマーク、ひと通りの計測、コードリーディングをざっと済ますことができたことは非常に大きかった。
うまくいった理由は特に以下の3点だと思っています。

  • 3人の得意分野がいい感じにばらけていて補完できた
  • 上にも書いたように初期準備が非常にスムーズにいった。@TakatoshiMaeda++
  • ベンチを取りながら最初にガッとコードを読み、当初決めた戦略 (martini の入れ替えとフル Redis 化) がそのままピッタリハマった。

最初にコード読んだ段階で、ロジックの整理しやすい login_log 周りにかなりのコストがかかっており、レギュレーションの確認(再起動後もデータを保っている必要がある)がとれたので、かなり初期の段階で方針については合意できました。
僕は kernel, nginx, Redis のチューニング、あとはアーキテクトっぽいこととデバッガぽいことくらいしか仕事がなかった気がします。実際ノーチューンというか削れるだけ削りましたみたいな my.cnf は一度も触りませんでした。
あとはベンチマーク中の I/O などを眺めつつ、できるだけパフォーマンスを上げられるような設定を詰めていきました。
全部1サーバでやるという制約上、多少なりともオーバーヘッドのあるネットワーク通信を使いたくなかったので、 nginx - Go App - Redis 間は全て UNIX domain socket に変更していて、ベンチマーク的にはこれも結構効果が大きかったのではと思っています。
カーネル周りのチューニングは ISUCON 頻出というか現実世界でも頻出な TCP 周りの設定を入れ、 nginx は基本的なチューニングと静的ファイルを nginx で返すように設定。 あとは細かい点を直したり、他の二人の実装レビューしたりデバッグ手伝ったり、で終了。
何度も再起動したり AMI から作りなおしたりして確認後に AMI 提出。 暫定3位でしたが正式順位発表で2位となりました。
ISUCON4 本戦出場者決定のお知らせと本選出場者の利用言語比率 : ISUCON公式Blog

ISUCON では普段やっていないことはできないし、大胆なことをやるなら初期の段階にやるのが良いというのをなんとなく察していたので、状況によっては巻き戻して MySQL でやれるだけやることも(自分の中では)考えていたのですが、 @y_matsuwitter の進撃の実装力により何ら問題なく変更が完了したので杞憂に終わりました。 よかった。

感想と本戦に向けて

予選は1サーバのみで全てやるという制約上、とにかくメモリに載せるという戦略がどうしても取りやすくなってしまうとは思うのですが、単にメモリに載せるだけではダメで保持しているデータをどうするか、などの問題も絡んでくるので、やっていてとても楽しかったです。普段の仕事でもやるようなことを考えるので、"あ、これゼミでやったところだ!!!" 的なことも色々とありました。
本戦は過去の例を見ていると複数のサーバを使うことになりそう、つまり今回あまり重要な要素ではなかったネットワークをどう使うかという部分が重要になってきそうで、非常に楽しみにしています。
尻に火を付けられてガリガリやっていく感覚というのは健康上あまりずっと味わいたくはないわけですが、たまにやるエクササイズとしては最高ですね。
直前になって ShellShock みたくおしゃれニックネームとかおしゃれアイコンとか特設 Web サイトが登場するような脆弱性が出ないこと、インスタンスが大量リブートしたりすることのないことを祈りつつ、楽しみに本戦を待ちたいと思います。
運営のみなさま、大変だとは思いますが引き続きよろしくお願い致します!

AWS で作るシステムのセキュリティ考

AWS アカウントを複数人で使ってシステムを作っていく時に、 セキュリティの面からやるべきことについて。 主に Web アプリケーションを想定した内容ですが、特に書いてあることは特殊ではないと思います。 各所の Blog にも記事書かれてますが思っていることをつらつらと書いてみます。 なんか変なこと言ってたらご指摘ください。

参考: AWSのセキュリティが気になるなら読んでおくべきAWSセキュリティのベストプラクティス - yoshidashingo

はじめに (AWS アカウントと IAM ユーザ)

前提というか用語の話。

  • AWS アカウント
    • アカウント作成時のメールアドレス、パスワードでログインして使うユーザ
  • IAM ユーザ
    • AWS アカウントから発行できる、ユーザ名とパスワードでログインして使うユーザ

AWS アカウント周り

AWS アカウント (ルートユーザ) で作業できないようにする

AWS アカウントは Linux で言う root ユーザみたいなものなので、アカウント内全てのリソースにアクセス可能です。 構築運用面で最初に IAM ユーザを作成する時以外、アカウントで作業する必要があることはほとんどないです。

  • 操作用の IAM ユーザを作る
  • AWS アカウントに紐付いてる Access Key とか証明書を消す
  • AWS アカウントに MFA を設定する

あとは、重要なシステムのあるアカウントであれば MFA のハードウェアキー買って設定して 金庫にでも突っ込んでおくと良いです。

IAM ユーザを使う人ごとに分割する

IAM を使うと、作業者個人単位で専用のユーザを持てるので、これを使いまわす必要はどこにもないです。 使う人それぞれに IAM ユーザを作成して、必要に応じて権限設定して使いましょう。 CloudTrail 使えば大体の操作は後からトラックできるようになります。 例えばいわゆる Admin な人たちにはほぼ全権を渡しておいて、そうでない人には Describe 系の API だけ渡すとかの切り分けをしておきます。 IAM にも MFA が設定できるので、好みに応じてハードウェア買うかスマートフォンにアプリ入れて MFA を有効化しておきましょう。 アクセスキーとシークレットキーはいずれにせよそれなりに死守する必要があることに気をつけてください。 Mac な人なら envchain 使うとなお良いと思います。

OS X キーチェーンから環境変数をセットするツールを作りました - クックパッド開発者ブログ

難点はユーザ数が増加していくと管理するのが難しくなるところでしょうか。ここはまだうまい解が分からないところです。。。

アプリにキーを埋めない

例えば EC2 で特定の S3 バケットにファイルアップロードするアプリとかバッチを動かす時など。 一昔前まではそのアプリ用の IAM ユーザ発行してキー埋めたりすることもあったと思いますが、 キー埋めないで IAM Role for EC2 Instance 使いましょう。

Amazon EC2 の IAM ロール - Amazon Elastic Compute Cloud

AWS SDK 使える環境であれば大体の実装ではキーの取得とローテーションへの追随もやってくれます。 サーバ設定もデプロイも自動化したけどキーの配布どうしよう、みたいな問題はこれで大体解決するのでは。 難点は今のところインスタンス"起動"時にしかロール指定できないこと。Stop/Start じゃダメです。 イミュータボーでディスポーザボーなインフラをお持ちの各位であればそんな難しいことではないのでは。

Unix アカウント周り

IAM アカウントは分離していても EC2 インスタンスのユーザは ec2-user や ubuntu ユーザを共有して使っている方も多いと思います。 が、インスタンスを扱うユーザが多くなるのであれば、OS レイヤでの権限分離もちゃんとやる必要があります。 Chef とか Puppet で管理する手もあるんですが、ユーザ情報を変更するたびに全台への apply が必要なのも厳しい感じがします。 ここを分離したいのであれば今のところはおとなしく LDAP を使うのが良いでしょう。 OpenSSH を使っていると、ちょっと前まではパッチ当てない限り公開鍵認証にするために LDAP ユーザであっても 対象のインスタンスに事前に公開鍵を配布しておく必要があったのですが、 OpenSSH 6.2 から LDAP 側に保持している公開鍵を読む、みたいなことができるようになったので、ここも心配する必要がなくなりました。

ref. OpenSSH 6.2を使って公開鍵認証もLDAPで行いたい。 - Qiita

ただし LDAP サーバが壊れると全てが崩壊するので、cloud-init で作成されるユーザと EC2 のキーペアは無効化せずに あくまでキーペアを共有しない (これも金庫とかに放り込んでおく) だけに留めておいたほうが良いかもしれません。

暗号化

AWS の例えば S3 や EBS にはサーバサイド暗号化 (SSE) という機能がついてます。 暗号化鍵の管理を AWS 側がやってくれて、使う側としては透過的にデータを暗号化できる機能です。 透過的にというのは要は、API 越しにデータ送信する時は平文で、送信後格納されるタイミングで暗号化されて、 読み出しの際は復号されたデータが降ってくるというやつです。 これを有効化するだけで全てのデータが暗号化されて最高便利という機能に見えるのですが、 API の認証が通れば復号化されたデータが降ってくるので、実際に権限外のアクセスから保護できるケースはそんなに多くないです。 例えば AWS 側の管理者が悪意を持ってアクセスしてきたり、AWSAPI のバグで他人のオブジェクトにアクセスできたり、 AWS がセキュリティペーパーに書いてある通りじゃなく使い終わった HDD を適当に捨てていた場合なんかには保護できるかもしれませんが、 それよりも開発者の誰かがアクセスキーを Github に visible な状態で突っ込んでいたり、メールアドレスとパスワードを使いまわしていて AWS アカウントにログインされたりして死ぬ可能性のほうが遥かに高いんじゃないでしょうか。 というわけで暗号化については"それで守れる範囲"をちゃんと考えた上で、必要な場合はちゃんと実装したほうが良いでしょう。 例えば S3 を例に挙げると、 AWS SDK for Java, .NET, Ruby には S3 にアップロードする際クライアントサイドで任意のキーを渡して暗号化してからアップロードできるようになっています。 これは API がどうこうという話ではなく完全にクライアントサイドで暗号化しています。

Protecting Data Using Client-Side Encryption - Amazon Simple Storage Service

あるいは鍵を AWS に渡して暗号化してもらう SSE-C というのもあります。

Protecting Data Using Server-Side Encryption with Customer-Provided Encryption Keys (SSE-C) - Amazon Simple Storage Service

完全な SSE より、R/W に実際の鍵が必要な分保護できるケースはもちろん増えますが、端的には "暗号鍵を渡すことと引き換えに暗号化処理をオフロードする"というものなので、 暗号化を考えた時にこれがしっくりくるケースってあるのかなーと感じています。

EBS でも、AWS サイドで暗号化の手段が用意されています。

Amazon EBS Encryption - Amazon Elastic Compute Cloud

副次的な効果として、スナップショットを public にしたりアカウントをまたぐことが仕組み上できなくなりますので、 この効果だけ欲しいという方には良いのかも。そもそもの懸念については S3 のものと大差ないと考えています。 自前で暗号化やるのであれば、パートナーが提供してるソフトウェア使う手もありますし、 Linux であれば dm-crypt 使っても良いと思います。

ここだけ長くなっちゃいましたが、要は何を何から守るのかちゃんと考えて実装しようよというだけの話です。

ロギング/監視

オンプレ環境との大きな違いを挙げるとすれば、AWS 上ではコアスイッチでミラーポートを使ってネットワーク監視するみたいなことが難しいので、 基本的に各インスタンス上で監視を行う必要がある点でしょうか。 トレンドマイクロの DeepSecurity とかはそういったアプローチだと思います。

AWS API 周りであれば前述の CloudTrail を使うのが一番良いんじゃないでしょうか。 全サービスをカバーしているわけではないということに留意する必要がありますが。 ファイルは S3 に json で吐かれるので、MongoDB なり ElasticSearch に突っ込んで検索可能にしておくと トラブルシューティングの時などにも捗るとおもいます。 ログを S3 に置いておくのであれば、そのバケットのアクセス権をちゃんと設定するのは必須として、MFA Delete も有効化しておきましょう。

MFA Delete - Amazon Simple Storage Service

おわり

正直粒度もバラバラでまとまらないですが、ざっと書いてみました。 AWS 固有のことが(アカウントとか)それなりにはありますが、その他はこれまで普通にやられてきたことと大差ないと思います。 ここでカバーしていない内容も多々ありますが、そういうのも徐々に書いていきたいです。

完全にオフトピックですが、イミュータブルインフラとかテスト駆動インフラみたいな話って セキュリティ運用とも相性が良いよなあと最近考えています。 例えば Heartbleed なんかが記憶に新しいですが、ミドルウェア脆弱性があってバージョンアップしないとなんて時も パッチしたミドルウェアの検証は(もちろんカバレッジによりますが) Serverspecinfrataster で可能ですし、 実際の入れ替え作業も、既存のサーバを止めてバージョンアップするのではなく新しい構成のサーバを起動して入れ替えるだけで良ければ、かかる労力はかなり小さくなるでしょう。

最近の Web インフラ界隈の流れにうまいこと乗りつつ、より便利でセキュアな環境を作りたいですね。 以上です。

触って試す AWS MFA

相次ぐパスワードリスト攻撃もあり、いわゆる MFA (Multi-Factor Authentication) が使えるサービスが増えてきました。 AWS でもデバイスによる MFA ができるようになっています。 この仕組みとかについて書きます。 AWS について主に書いていますが他のサービスで使われているものも大体同じ (少なくとも GitHub とかは) はずです。

AWS MFA で使われている仕組み

IAM の FAQ でも書かれていますが、 AWS で使うことができるのは TOTP (Time-based One-Time Password Algorithm) です。 アルゴリズムの説明は こちら などがわかりやすかったです。 TOTP の RFC もそんなに分量ないので読んでみると良いと思います。 ざっくり言うと TOTP がしているのはすごく単純なことで、

  • 認証を行う側と認証される側で同じ鍵を共有しておく
  • お互いが同じ鍵を用いて同じ計算方法でワンタイムパスワードを生成する。生成時、変化する要素として現在時刻が使われる

これだけです。鍵の共有時を除けば認証する側とされる側で通信する必要がないので、 スマートフォンを MFA デバイスにしている場合それがオンラインである必要はありません。

ちょっとコード書くとその様子を眺めることができるので後述します。

ハードウェア MFA とバーチャル MFA

AWS の MFA ではハードウェア MFA デバイスとバーチャル MFA デバイスの2種類が使えるようになってます。

ハードウェア MFA

AWS の場合、ハードウェア MFA は専用のベンダーが一社あり ここ から購入します。 現在は小さい USB メモリみたいなものとカード型のものを選ぶことができますが、 カード型の方は日本から買えないようです。無念。

ベンダーのページに記載されていますが、これらも Time-based との記載がありますので TOTP が使われているものと思われます。 使っているアルゴリズムは同じですが、ハードウェア MFA とバーチャル MFA の違いは鍵の共有方法にあります。 若干推測ですがハードウェア MFA の場合、ベンダーはハードウェアのシリアルナンバーとそれに紐づく鍵を何らかの安全な方法で AWS 側に渡しているはずです。 そのため、このハードウェアを購入して AWS アカウントに使おうとすると、シリアルナンバーを入力するよう求められます。 このシリアルナンバーは実際の生成に使われるわけではなく、入力するとそれに紐づく鍵が AWS 側で呼び出されて、 以降その seed を元にワンタイムパスワードを計算するようになるという仕組みです。 また、このハードウェアには耐タンパー性がありますので、外から鍵を盗みとられるという可能性も少ないと思います。

バーチャル MFA

ハードウェア MFA の他にバーチャル MFA を選択することもできます。多くの人はこちらを使っていると思います。 バーチャル MFA の場合もパスワードの生成方法はハードウェアと全く同様ですが、スマートフォンアプリ (Google Authenticator, Amazon MFA) など 専用ハードウェアではないものの上で生成を行うところが差異です。 また鍵の共有方法も違います。ハードウェア MFA の場合鍵は予め AWS 側に渡っていますが、 バーチャル MFA の場合 AWS 側で生成した鍵を手元のデバイスで受け取る必要があります。 手段は多くの場合2通りで、QR コードを使うか鍵を手で入力します。

QR コードには、アカウントの名前や鍵, 認証の種類 (TOTP or HOTP) が含まれています。 Google Authenticator の wiki に詳しい説明が載っているので興味のある方はご参照ください。

キーを手動で入力する場合は、アカウントの名前などは情報として含まれないため手動で入力する必要があります。 また、鍵が格納されるのは結局スマートフォン上の記憶領域なので、その安全性はそのアプリや端末のセキュリティレベルによるというところが ポイントかと思います。

自分でやって観察してみる

ruby だと ROTP という gem があって、この OTP 周りの処理をやってくれます。 コード読んでみると実際どういう処理をやる必要があるのか見えてくると思います。 https://github.com/mdp/rotp

require 'rotp'
key = ROTP::Base32.random_base32
totp = ROTP::TOTP.new(key)
puts key
puts totp.now

こんなコードを書くと、16文字の base32 で生成されたキーと、それを元に生成したパスワードが返ってきます。

$ ruby rotp.rb 
amxz5qyc54sap3qp
271576

ここで生成されたキーを Google Authenticator に入力してみます。 サービスを新規追加する画面から、手動で入力 を選択し、アカウント(適当な名前) とキー (生成されたキー) を入力します。 時間ベース (TOTP) にチェックが入っていることを確認して完了ボタンを押すと、他のサービスに並んでパスワードが表示されると思います。

では、コード側でも生成されたキーでパスワードを生成するようにしてみます。

require 'rotp'
totp = ROTP::TOTP.new("amxz5qyc54sap3qp")
puts totp.now

これで totp.now を watch -n 1 とかで Google Authenticator といっしょに眺めてみると、 お互い大体同じタイミングで値が切り替わるのがわかるとおもいます。

手元で OTP を生成して AWS 認証してみる

注: 大体どんな感じで MFA が動いてるのか観察するためにやっているので、Web アプリ置いて生成させてこれでデバイス要らず便利〜みたいなことはやらないほうが良いと思います。

IAM で適当なユーザを作って MFA を有効にします。(実際の値は雑に消しました)

f:id:kani_b:20140706173554p:plain

ぱっと見切り替わるように見えないのですが、この画面で "Show secret key for manual configuration" をクリックすると 設定用のキーが出てきます。これを先ほどのコードで使うようにします。

f:id:kani_b:20140706173612p:plain

require 'rotp'
totp = ROTP::TOTP.new("your_mfa_key")
puts totp.now

これで6桁のワンタイムパスワードが出力されるようになったと思います。watch -n 1 ./rotp.rb とかしておくと時間経過とともに変化していくはずです。 出力されたワンタイムパスワード2つ (最初に出力されたものと次に出力されたもの) を入力するとアクティベーションが完了します。

実際にログインしようとしてみると、コード側で生成されたワンタイムパスワードが使えることが確認できます。 面白いですね。

その他

さっきも書いた通り、QR コードは鍵やその他の情報をやりとりするだけのものなので、 同じ鍵を持つデバイスを複数個作ることができます。 つまり同じ QR コードをスキャンしておけば iPhone と併用している Android とかもう使わなくなったスマートフォンなどを MFA デバイスのバックアップとして持っておくことが可能です。 その分鍵がある場所が増えることになるのでユースケースによりますが。

MFA はもはや使わない理由が特に無いので、心当たりのある人は今すぐ有効にしましょう。