続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

EC2 roleや ECS task role の知識をUpdateする (IMDSv2とかも)

まず最初に、本当にやりたかったことと、調べているうちにだいぶ脱線して得た知識を書いてます。少々規模がでかいです。

最初にやりたかったのは、 EC2 role (Instance Profile) を使っているときに awscliとかで Unable to locate credentials が稀に出るという問題の解消です。
で、いきなり(今わかっている) 答えを書くと、

  • AWS_METADATA_SERVICE_TIMEOUT
  • AWS_METADATA_SERVICE_NUM_ATTEMPTS

あたりの環境変数を指定しておくです。
AWS CLI Configuration Variables — AWS CLI 1.18.123 Command Reference

上記を含む、大部分が脱線した内容を掘り下げて行きますが、前提となる知識が結構多く、いちいち細かく書くと長すぎるので、AWS上級者向けです。

EC2ロール

EC2ロールってどうやって動いている?

そもそも私も理解が間違っていたのでここから始める。なぜEC2ロールを利用するのかは前提知識なので説明省くが、EC2を起動するときに、EC2ロール/InstanceProfileを指定したらなんでその権限が使えるのかを調べる。

これはググればすぐに出てくるが、AWSのドキュメントにもある。169.254.169.254でお馴染みのインスタンスメタデータに credentialが格納され、それをbotocoreとかaws-sdk/ライブラリが参照している。まず最初に私が間違っていたのは、メタデータにいきなりcredentialが入っていることはないと思っており、てっきりaws-sdkとかがstsを叩いて発行していると思っていたが、そうではなくていきなり使える状態でメタデータから取得できる。もちろんtoken付きなので有効期限はあるが、自動で更新された状態でメタデータに入ってくるらしい。Expireの5分前には新しいのに更新されているらしい。

早速自分の誤解が一つ減った。
VPC-Endpointを使い、インターネット辞めたいとき、stsへのエンドポイントがなくともEC2ロールは使える。
本当にstsのエンドポイントが必須なのはEC2でさらにassume-roleしたい場合のみ。

EC2ロールを使ってるときになんでUnable to locate credentials

おそらくメタデータの取得が失敗することがあるのではないかと予想している。つまり curl 169.254.169.254 が稀に失敗している?。それを見越してのAWS_METADATA_SERVICE_NUM_ATTEMPTSなのかなと思う。ただしこの実装は botocore など aws-sdk側の実装で実現されている。よって、sdkなんか使わない独自実装はともかく、場合によってはsdkの言語やバージョン次第では対応していないかもしれない。ここはちょっと確証が持てないので、いまも実験中、そもそもどれぐらいの頻度でエラーになるのか。

IMDSv2?

メタデータとさっきまで書いていたやつのv2版のことです,ということはいままではv1だった。v2が出た理由は一応はセキュリティー観点かららしい。ググればたくさんでてくるが

  • 問題点であるSSRFは、一発即死系のかなりやばいやつ
  • v1にくらべてv2はちょっと攻撃をやりづらくはするが、完璧では全然ない

詳細は下記で詳しく述べられています。AWSに限らずクラウド屋なら必読です・他のプラットフォームでも同じ問題があります。

SSRF対策としてAmazonから発表されたIMDSv2の効果と限界 | 徳丸浩の日記

このエントリでもIMDSv2関連を書きますが、SSRF対策は真剣に取り組むべきです。とりあえずすぐに対応できそうなことを一つ上げておくと、EC2ロールだからといって、無駄に強い権限を割り当ててはいけない

IMDSv2 / v1 の違い

違いを列挙

  • v2/v1ともにIPはおなじ、v2はHeaderが必須になる
  • v2は最初にPUTをうってTokenをもらい、そいつを使う、v1はTokenとかなしにいきなりデータ取れる
  • デフォルトで v2 / v1 両方が生きた状態で起動。実質セキュアにするならv1を殺す必要あり
  • IMDSを設定するためのmodify-instance-metadata-options
  • PUTはデフォルトでhop=1でしかたたけない、つまりdocker・コンテナ内からはデフォルトたたけない

今回ハマったのは最後のやつ、network的に、ルーターかます(forwardする)とあかん。ので、hostモードでdocker内からたたけない。

aws-sdkとかの credential取得方法

結構なところで言及されているが、環境変数・所定の設定ファイル・EC2ロールなど、優先順位別に試していき、見つかったものを使う。これはみんな知っているはず。

aws-sdkとかの IMDSv2 対応

上記より、EC2ロールが実際に評価される場合、どうやってとっているのか?botocoreで実装されているのでちょっと読んで調べた。

  • v2のためにPUTをいきなり打つ(Token取得)
  • Token取れなかったら v1方式で、取れたらv2方式
  • 最初からv1でやれ!という設定は今の所できない

言語によっては aws-sdkで IMDSv2が出たしたときにちょっと問題になったらしいが、今の所上記の通り。

さあ、感のいい人ならもう気づいたと思うが、コンテナ内からEC2ロールを使おうとしたときどうなるか。
答えは最初のv2のPUTがhopの問題で失敗し、デフォルト設定だとcredentialの取得が常に1秒遅くなります。
さらに冒頭で書いた AWS_METADATA_SERVICE_TIMEOUTを30とかに弄っている場合はどうなるか?常に30秒遅くなります。

いやいや、そもそもコンテナからEC2ロールつかうなよ・Taskロールつかえよ・きょうびFargateやろ普通

正論。

だけど俺の経験としてTaskロール使えないケースがちょいちょいあるんだよな。EC2ロール・Taskロールが使えるのは、aws-sdkのバージョンや別にSDK使わず自前実装しているならば、アプリ側の対応次第となる。特にどうしても古いアプリを使わなければならないときなので、ベースとなっているaws-sdkが古すぎて Taskロールガン無視・EC2ロールは使えたりとか結構ある。OSSなら新しいsdkを指すなり、独自実装ならPR出すなりすればいいと思うけど。

今回の問題の対策

AWS_METADATA_SERVICE_TIMEOUTのデフォ1秒を30秒とかにしたときにEC2ロールからcredentialの取得が常に遅い
の対応は modify-instance-metadata-options--http-put-response-hop-limit 2 を指定しろになる。

そもそもコンテナ内からEC2ロール使わなくて済むならこんな対処いらないし、Taskロール使うべき。

Taskロールについて

話がそれるが、ついでなのでTaskロールの実態について調べてみた。

取得方法

IAM Roles for Tasks - Amazon Elastic Container Service

curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

よく見るとIPアドレスが違います。170.2です。のでIMDSではない。次に $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 環境変数ですが、ECSでTaskロール指定して稼働しているとついているらしい。実際にそのようになってます。で、Taskロールが使える状態で、curlで IMDS(=つまりEC2ロール)もみえるかcurlでやってみた。すると「普通に見えたぞ」。

ちゃんとコード読んでないけど、状況証拠からaws-sdk

  • $AWS_CONTAINER_CREDENTIALS_RELATIVE_URIの有無をみてTaskロールがついてるか判断
  • Taskロールついてる判断したら 169.254.170.2のほうで取得する
  • Taskロールついてない判断したらEC2ロール試す

となっているはず。つまり、Taskロール指定しててもIMDSを直接叩くSSRFの攻撃の防御にはならない。

これに対応するには

  • おとなしくFargate使え
  • どうしてもEC2で行くなら、コンテナ->IMDSのアクセスをiptablesで殺すとか