IAM条件キー備忘録:ForAllValues/ForAnyValueとPrincipalIsAWSService/ViaAWSService

AWS IAMポリシーの条件演算子とグローバル条件コンテキストキー

AWS Identity and Access Management (IAM) のポリシーでは、「条件」によってアクセス許可の範囲を細かく制御できます。条件には ForAllValues と ForAnyValue のようなセット演算子や、aws:PrincipalIsAWSService や aws:ViaAWSService のようなコンテキストキーがあります。これらの挙動を実際に制御して分かり辛い部分があったため、備忘録も兼ねてまとめておきます。

ForAllValues / ForAnyValue の違い

IAM の条件では、単一値キーと複数値キーの区別があります。リクエスト側で複数値を持つコンテキストキー(例: aws:TagKeys や aws:VpceOrgPaths)を条件で比較する場合、比較対象が配列同士になるため ForAllValues または ForAnyValue というプレフィックスでセット演算を指定します。

ForAllValues – 全要素が一致するかを検証

ForAllValuesは「リクエスト側のすべての値がポリシー条件に指定した値集合に含まれるか」を判定します。 最初は真で開始し、コンテキストにある各値が演算子(例: StringEquals)に一致するかを検証し、一つでも不一致があれば偽になります。そのため、リクエスト側の値が存在しない場合でも真を返してしまいます

具体例
"Condition": {
  "ForAllValues:StringEquals": {
    "aws:TagKeys": ["project", "environment"]
  },
  "Null": {"aws:TagKeys": "false"}
}

この条件は、リクエストに含まれるすべてのタグキーが project または environment のいずれかに一致する場合にのみ真を返します。リクエストにタグキーが存在しない場合を考慮して Null 演算子空集合を拒否することが推奨されています。

リクエストの aws:TagKeys 判定 意味合い
(キー自体が無い) False 上記の例ではNull条件で弾いているため。Null条件がない場合はTrueとなる。
["project"] True 許可リスト外は含まない(ただし environment 必須は保証しない)
["project","environment"] True 許可リスト内に収まる
["project","costcenter"] False 許可リスト外が混ざったのでNG

ForAnyValue – 任意の要素が一致すれば真

ForAnyValueは「リクエスト側の少なくとも一つの値がポリシー条件に指定した値集合に含まれるか」を判定します。 リクエスト内のいずれかの値が条件値集合と一致すると真を返し、リクエスト側の値が存在しない場合は偽を返します。

具体例
"Condition": {
  "ForAnyValue:StringEquals": {
    "aws:TagKeys": ["project", "environment"]
  }
}

この条件は、aws:TagKeys に project または environment が含まれている場合にのみ真を返します。リクエストに該当するタグキーがない場合は判定が偽となりアクセスは拒否されます。

リクエストの aws:TagKeys 判定 意味合い
(キー自体が無い) False TagKeys が無いと一致しない
["project"] True どれか1つ一致でOK
["costcenter"] False 許可リストに無い
["project","costcenter"] True 1つでも一致すればOK

使い分けのまとめ

演算子 判定の基準 空集合の扱い 主な用途
ForAllValues リクエストに含まれる値が許可リストの範囲内であること 空集合の場合は真 タグキーの一覧が許可リスト以外を含まないことを要求する場合など
ForAnyValue リクエストの値のうち少なくとも一つが一致すればよい 空集合の場合は偽 複数の許可値のうちいずれかを含んでいればよい場合など

aws:PrincipalIsAWSService と aws:ViaAWSService の違い

IAM には、呼び出し元がどのようなエンティティか/どの経路で来たかを識別するグローバルコンテキストキーが用意されています。その中でも aws:PrincipalIsAWSService と aws:ViaAWSService は似ているようで異なる目的を持ちます。

aws:PrincipalIsAWSService – リクエスト主体が AWS サービスかどうか

意味: リクエストを発行しているプリンシパルAWS サービス(例: cloudtrail.amazonaws.com)である場合に true となるブール値です。 例: S3 バケットに対して CloudTrail などのサービスだけが書き込みできるようにし、その他の主体を拒否したい場合は次のような「Deny」条件を追加します。

"Effect": "Deny",
"Condition": {
  "Bool": {
    "aws:PrincipalIsAWSService": "false"
  }
}

このように指定すると、aws:PrincipalIsAWSService が false のリクエスト(ユーザーやロールが直接アクセスするリクエスト)を拒否し、サービス主体によるアクセスのみを許可します。

注意点: CloudTrail のログ配信、AWS Billing などサービス主体が必要なリソースへのアクセスを許可する一方でユーザー/ロールからの直接アクセスをブロックする時に用いることができますが aws:PrincipalIsAWSService は “AWSサービス経由” の判定キーではありません。サービスがあなたの資格情報やサービスロールで動く場合(後述の場合)、このキーは false になります。

aws:ViaAWSService – AWS サービス経由のリクエストかどうか

意味: AWS サービスが「ユーザの IAM 資格情報やロールの権限を使って」別のサービスを呼び出す際に true となるブール値です。これは Forward Access Session (FAS) と呼ばれる仕組みで、元のプリンシパルの権限が他のサービス呼び出しに引き継がれます。 例えば、CloudFormation スタックの作成ではユーザがリクエストした後に CloudFormation サービスが DynamoDB や KMS などのサービスをユーザの権限で呼び出します。

例: ネットワーク境界を VPC や IP に制限する SCP を作成する際、AWS サービス経由の呼び出しを例外として許可するには aws:ViaAWSService を用いた条件を追加します。

"Sid": "EnforceNetworkPerimeter",
"Effect": "Deny",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
"Condition": {
  "StringNotEqualsIfExists": {
    "aws:SourceVpc": ["vpc-111bbb22", "vpc-222ccc33"]
  },
  "NotIpAddressIfExists": {
    "aws:SourceIp": ["198.51.100.0/24", "192.0.2.0/24"]
  },
  "BoolIfExists": {
    "aws:ViaAWSService": "false"
  }
}

ここでは、組織外の IP や VPC からのアクセスをブロックすると同時に、aws:ViaAWSService が false であれば拒否します。つまり、許可されるのは 組織内のプリンシパル または AWS サービス経由 (FAS) の呼び出しだけです。

注意点: aws:ViaAWSService で許可するのは、サービスがユーザの資格情報で別のサービスにアクセスする場合のみです。Athena が外部の S3 バケットに書き込むようなケースでは、そのバケットの所有者が内部アカウントとは限らないため、ホワイトペーパーでは aws:ViaAWSService を使って無条件に例外を作ることは推奨されていません。特定サービスだけを例外にする必要がある場合は aws:CalledVia コンテキストキーを検討してみてください。 FAS を利用するサービスの一覧は公式には公開されておらず、CloudFormation、DynamoDB + KMS、S3 + KMS、Athena など複数の代表的サービスが該当します。常に検証しながらポリシーを作成する必要があります。

使い分けのまとめ

コンテキストキー true になる条件 用途
aws:PrincipalIsAWSService リクエスト主体が AWS サービス(cloudtrail.amazonaws.com など)の場合 サービス主体のみを許可したいリソースポリシーや SCP。CloudTrail のログ配信先バケットの保護など
aws:ViaAWSService AWS サービスが FAS を用いて元のプリンシパルの資格情報で別のサービスを呼び出している場合 CloudFormation や Athena など、サービス経由の呼び出しをネットワーク制御から例外とする場合

なお、aws:PrincipalIsAWSService は“サービスプリンシパルによる直接アクセス”を判定するキーですが、SCP はサービスプリンシパルやサービスリンクロールに適用されないため、SCPでこのキーを使って例外を作るのは通常意味がありません。サービスプリンシパルによる直接アクセスを許可する必要がある場合は、VPC エンドポイントポリシーやRCP側で aws:PrincipalIsAWSService を利用するとよいと思います。

まとめ

ForAllValuesとForAnyValue、PrincipalIsAWSServiceとViaAWSServiceは同じように見えても挙動が大きく異なります。 ForAllValues はリクエスト側のすべての値が条件を満たす場合に真となるため、タグやリストが許可リスト以外の値を含まないかを検証したいケースで使います。ForAnyValue はいずれか一つでも一致すれば許可されるため、複数の許可値のうち少なくとも一つを含めばよい場合に適しています。リクエストにタグキーが存在しない場合に対する挙動が異なるため、 Null 演算子と組み合わせることも考慮する必要があります。

サービス関連のコンテキストキーでは、aws:PrincipalIsAWSService は呼び出し主体が AWS サービスそのものであるかを検査し、CloudTrail などサービス主体のみを許可したい場合に活用できます。一方 aws:ViaAWSService は AWS サービスが FAS を用いてユーザの資格情報で他のサービスを呼び出している場合に true となるキーであり、CloudFormation や Athena のようなサービスチェーンを許可する例外条件として使用します。これらは似て非なるものであり、誤って利用すると意図せぬアクセスが許可される可能性があります。

本稿が IAM ポリシーの理解の助けになれば幸いです。

RIの推奨事項に表示されるEC2がSavings Plansで表示されない原因

はじめに

EC2が数台フル稼働しているAWSアカウントがあり、コスト削減しようと思ってCost Explorerから推奨事項を覗いてみました。
すると、RIの推奨事項では台数分きちんと表示されるにも関わらずEC2 Instance Savings Plansの推奨事項がないと出ました。
これはバグか...?と思ったのですが、ドキュメントに記載のある内容だったのでメモとして残しておきます。

事象

EC2が数台フル稼働しているアカウントでのRIの推奨事項

f:id:rioner2525:20210204142820p:plain
RIの推奨事項

ここでは3個のRIの購入を薦められました。

同じアカウントでEC2 Instance Savings Plansの推奨事項

f:id:rioner2525:20210204143503p:plain
EC2 Instance Savings Plansの推奨事項

こちらでは1つも推奨事項がありませんでした。

EC2のRIとEC2 Instance Savings Plansは削減率が同じで同等のものだと思っていたのでなんでだろう(´~`)?と不思議に思っていました。

原因

EC2 Instance Savings Plansの推奨事項の画像の一番下に答えが出ていました!

選択したルックバック期間中、平均オンデマンド支出が 0.10 USD/時間未満である。

こちらが原因でした。
たしかにEC2を数台フル稼働していたもののオンデマンド金額では$0.1/時間以下でした。
きちんと書かれているのにまったく気づかなかったので逆にびっくりしましたね...。
ちなみにAWS公式ドキュメントですと以下に書かれています。現在英語のみ。

Understanding your Savings Plans recommendations - Savings Plans

Recommendations are generated for customers that have an average On-Demand spend of $0.10/hour during the lookback period (7, 30, or 60 days). If you recently purchased a Savings Plan, or if your Savings Plans recently expired, it might not be reflected in your Recommendations for up to 24 hours.

EC2 Instance Savings PlansだけでなくCompute Savings Plansの方も基準額($0.1/時間)に満たない場合は推奨事項が表示されないようです。

おわりに

EC2 Instance Savings Plansにフル稼働のEC2が表示されないのにCompute Savings Plansの方は表示されたので、Compute Savings Plansの方にEC2分の料金が含まれていないのでは?と疑ってしまいました。
Compute Savings Plansの推奨事項は表示された時点でEC2,ECS,Lambdaのすべてのリソース分が含まれているんですね。
EC2が数台だけフル稼働しているアカウントでしか見られない事象で勉強になりました。 以上。

AWS 認定セキュリティ – 専門知識 (SCS-C01) に合格したのでメモ

はじめに

現状でAWSで出している認定は以下です。

f:id:rioner2525:20210105102349p:plain
現状の AWS 認定一覧

参考(AWS 認定 – AWS クラウドコンピューティング認定プログラム | AWS

プロフェッショナル認定は2019年1月に取得していて、そろそろ専門知識認定に手を出そうと思っていたので実務と関係があるセキュリティの専門知識を受けてみることにしました。
特にコンプリート欲はないので現業では他認定はスルー予定(゚ε゚)。

プロフェッショナル認定取得時の記事。
rioner2525.hatenablog.com

試験で出そうな分野

試験内容の主な概要は以下の通りかなと思います。

分野1 インシデント対応 (12%分)
  • セキュリティ侵害の疑いがあるインスタンスやアクセスキーをどうするか問題。
  • 自動的にアラート出す構成にできるか問題。
  • 具体的なインシデント例にどう対応するか問題。
分野2 ログ収集と監視 (20%分)
分野3 インフラストラクチャのセキュリティ (26%分)
分野4 ID とアクセスの管理 (20%分)
分野5 データ保護 (22%分)
  • KMSキーの管理および使用に関するシステムを構成にできるか、トラブルシューティングできるか問題。
  • 保存時および転送中のデータを暗号化するシステムを構成できるか問題。

参考(試験ガイド

やった方がいいかもなこと

AWSのセキュリティ関連サービスを理解する
  • AWS Black Belt の資料なら Security, Identity & Compliance あたりのサービスは確認しておく(できれば実機確認)。
  • 他にもセキュリティグループとかNACLの動作とか他サービスのセキュリティ関連リソースも確認。
  • 余裕があればKMSはCLIまで確認。
練習問題を解く

余裕があれば...

  • Whizlabsの練習問題($20ぐらい?)を購入して解いてみる。→ 投稿主はこちらで購入しました。
  • udemyで練習問題(1200円ぐらい?)を購入して解いてみる。

多分どちらもブラウザから日本語翻訳できると思います。少なくともWhizlabsの問題はある程度把握できるレベルで翻訳できていました。
問題文が意味不明に感じたら英語で読みましょう...

オンライン講習を受ける

余裕があれば...

  • Whizlabsのオンラインコース($30ぐらい?)を購入して受けてみる。
  • udemyでオンラインコース(1200円ぐらい?)を購入して受けてみる。

日本語字幕などは付けられないと思ったので投稿主は購入していません...
英語のリスニングができる人はudemyのコスパが良さそう。

感想

Whizlabsの練習問題をだいたい解けるレベルにしてから受験したら1発合格できました。
(・´ー・`)
全く同じ問題は1問も出ませんが似たような問題が出たので参考になりました。
ただやはり全く使ったことのないサービスについてはミスが多かったです。
出来る限り実機を触って覚えることが大切なので、練習問題でミスったときはホントか~?と疑って実機確認しましょう!

Configルール一覧まとめ

はじめに

AWS基盤のガードレール構築のためにConfigルールを調査する機会があったので共有です。
公式ドキュメントは以下です。
AWS Config マネージドルールのリスト - AWS Config

公式ドキュメントが見づらいなぁと思ったので一覧で見れるようにまとめたものを置いておきます。
※内容は投稿時のものであり、参考にする際は必ず公式ドキュメントも確認してください。

Configルール一覧

めっちゃ見づらくなってしまった..._(:3」∠)_
必要に応じてExcelだかスプレッドシートだかにコピペして見た方がいいかもです。

CloudWatch エージェントをインストールしてEC2から自動ログ吐き出し

はじめに

Cloudformation から EC2 インスタンスを立てる作業をしていたのですが、自動でログを吐き出して欲しいと言われて実装したときのメモです。
今回吐き出したいログは以下。

  • /var/log/messages
  • /var/log/secure
  • scriptコマンドで吐き出されるログ

ssm はよく分かっていないので今回は使っていません。
使えるとssm経由でCloudWatch エージェントを管理できるので、もうちょっと楽にできるかもです。

必要な素材を用意

CloudWatch エージェント

公式ドキュメントからサーバのプラットフォームに応じて最新版をダウンロード。
Amazon Linux2 だと以下です。

https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm

今回は自アカウントのS3バケットに事前に置いておきました。

CloudWatch 用設定ファイル作成

公式ドキュメントを参考に作成。
今回はカスタムメトリクスを吐き出しとかはないので metrics セクションは不要です。
サーバ内でログが置いてある場所とCloudwatch ロググループでの吐き出し先を設定するだけです。
タイムスタンプも出してくれるのでscript側で気にすることはありません。

以下、具体例です。
ファイル名:awslogs.json

{
      "agent": {
        "run_as_user": "root",
        "region": "ap-northeast-1",
        # Cloudwatch エージェント自体のログ吐き出し場所。ログ吐き出しがうまくいかないときに確認します。
        "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
      },
      "logs": {
        "logs_collected": {
          "files": {
            "collect_list": [
              {
                # サーバ内ログ配置場所
                "file_path": "/var/log/messages",
                # ログ吐き出し先 Cloudwatch ロググループ名設定
                "log_group_name": "ec2-messages.log",
                # ログ吐き出し先 Cloudwatch ログストリーム名設定
                "log_stream_name": "ec2-messages.log",
                # タイムスタンプ形式設定
                "timestamp_format": "%b %d %H:%M:%S"
              },
              ...吐き出したいログの分をそれぞれ記載
            ]
          }
        },
        # ログ吐き出し先のデフォルトになる Cloudwatch ログストリーム名設定
        "log_stream_name": "ec2"
      }
}

EC2起動時にユーザデータで自動設定するため、作成したawslogs.jsonファイルを自アカウントのS3バケットに事前に置いておきます。

ユーザデータ設定

messages、secureログは自動で出るのでscriptコマンドをユーザデータで仕込みます。
また、ユーザデータでコマンドを打ち込むときはエスケープが必要なこともあるので考慮しましょう。

以下、具体例です。

#!/bin/bash
# scriptコマンドを仕込む。
cat <<EOL >> /etc/profile
P_PROC=\`ps aux | grep \$PPID | grep sshd | awk '{ print \$11 }'\`
if [ "\$P_PROC" = sshd: ]; then
  sudo script -faq /var/log/script
  exit
fi
EOL
# 用意した CloudWatch エージェントをダウンロードしてインストール
aws s3 cp s3://バケット名/amazon-cloudwatch-agent.rpm /tmp/
rpm -U /tmp/amazon-cloudwatch-agent.rpm
# 用意した CloudWatch 用設定ファイルをダウンロード
aws s3 cp s3://バケット名/awslogs.json /tmp/
# 設定ファイルを指定して CloudWatch エージェント起動。
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/tmp/awslogs.json -s

ログ確認

うまくいくと CloudWatch ロググループからログを確認することができます。

f:id:rioner2525:20200703200738j:plain
設定ファイルで指定したロググループができた

f:id:rioner2525:20200703201034j:plain
scriptコマンドで操作ログも取れた

scriptコマンドの操作ログについてはエスケープシーケンスも記録されてしまうため、精査するときは整形する必要があります。

おわりに

サーバに入らなくてもログを確認できるので楽できていいなぁと思いました。
特に後でどんなコマンド打ったっけーというのがマネジメントコンソールで確認できるのが助かります。
CloudWatch エージェントは監視でよく使われますが監査用(調査用)対策としても優秀ですね。
以上です。