Chrome の SameSite 属性の仕様によりアプリケーション不具合が起きた話

これは エキサイトホールディングス Advent Calendar 2023 の3日目の記事です。

最近は業務でたまに PHP を書いているのですが、Chrome の SameSite 属性の挙動により四苦八苦したのでその内容をまとめてみようと思います。

目次

結論

先に結論です。 前提として、Chrome ブラウザに限った話です。Firefox など他のブラウザでは微妙に仕様が異なります。
Cookie に設定できる SameSite 属性に Lax を設定していると、Cross Site な POST リクエストを利用する場合は Cookie が発行されてから 2分間だけその POST リクエストに Cookie がセットされます。2分を過ぎると POST リクエストには Cookie がセットされなくなるので、アプリケーションの挙動としておかしくなる可能性があります。 SameSite=None を利用するか、POST を GET にするか、Cookie を利用しないようにするか、Cookie の発行タイミングを調整するなどの対応を取る必要があるでしょう。

SameSite 属性と Chrome の仕様

自分が四苦八苦したケースを説明する前に簡単に SameSite 属性と SameSite に対する Chrome の仕様の説明をしておきます。今回の事象を解説するための最低限の説明なので詳しくは参考 URL などをご覧ください。

Same Site の条件

以下の全てが一致すれば Same Site となり、1つでも異なれば Cross Site です。

  • スキーマ(http, https など)
  • eTLD + 1 (例: example.co.jp , example.com など)
    ※ eTLD(effective Top Level Domain) とは、.com.org など Root Zone Database に掲載されてるTLD とそのすぐ左側を合わせたものです。.co.jp.github.io など、実質的にTLDとして扱われてるものもあり、これらは Public Suffix List にまとめられています

例えば、https://sub1.example.comhttps://sub2.example.com は Same Site であり、https://example1.comhttps://example2.com は Same Site ではありません。

SameSite 属性について

Cookie を発行する際に設定できる SameSite 属性は 3 つの値を設定できます。

設定値 説明
None 全ての cross-site なリクエストに対して Cookie が付与されます
Strict same-site に対するリクエストにのみ Cookie が付与されます
Lax GET リクエストでの Cross Site のページ遷移や Same Site の POST, GET リクエストにのみ Cookie が付与されます。一方、POST メソッドのような CSRF の危険性が高い HTTP メソッドによる Cross Site なリクエストに対しては Cookie が付与されません。Cross Site で POST リクエストする場合は Cookie が飛ばないので悪意のあるサイトからの POST による CSRF 攻撃の対策になります。

※ ちなみに、Chrome 84 以降 SameSite 値が設定されていない Cookie は SameSite=Lax として扱われるようになりました

SameSite 属性に関する Chrome の仕様

Chrome では SameSite=Lax かつ Cross Site な POST リクエストをした際の挙動が上記で説明した Lax の挙動と少し異なります。 それは、Cookie が生成されたタイミングから2分以内であれば Cross Site な POST リクエストに Cookie が付与され、2分以降は Cookie を付与しないという点です。

実際に自分が遭遇したケースを見てみましょう。

不具合が起きたケース

PHP で他社システムにリダイレクトをしながら行うある登録処理を開発をしていたときに、Chrome で登録処理が成功したり失敗したりする現象にあいました。
A サイトを自社で管理するサイト、B サイトを他社で管理するサイトとして、その処理の流れを下記に示します。前提としてユーザが A サイトにログイン済みでセッションIDが SameSite=Lax で Cookie にセットさているものとします。

  1. A サイトでユーザが情報を入力し POST リクエストを送信
  2. A サイトで登録処理のトランザクションIDを発行しキーバリューストアにセッションIDをキーとしてトランザクションIDを登録
  3. B サイトへリダイレクト
  4. B サイトでももろもろの処理後に A サイトへ POST リクエストのリダイレクト
  5. キーバリューストアからセッションIDをキーとしてトランザクションIDを取得
  6. トランザクションIDを利用して登録処理実施

不具合が起きた一連の処理

A サイトと B サイトは完全に別ドメインなので Cross Site となります。また、B サイトから A サイトへのリダイレクトは POST リクエストなので今回紹介した仕様にあてはまります。起こる現象としては、セッションIDがセットされた Cookie が発行されるのはログインの時なので、ログインから2分以内に A サイトにリダイレクトで戻ってこれるとその A サイトへの POST リクエストに Cookie が付与され、キーバリューストアからトランザクションIDを取得することができ登録が成功します。しかし、ログインから2分以降に A サイトにリダイレクトで戻ってくるとその POST リクエストには Cookie がセットされていないので、サーバ側にセッションIDが伝わらずキーバリューストアからのトランザクションIDの取得に失敗します。

対応方法

対応としては以下の方法があるかと思います。

  1. Cookie を利用しなくてもいいか検討する
  2. POST を GET にできないか検討する
  3. SameSite=None を利用する
  4. Cookie の発行タイミングを調整する

今回はアプリケーション側で CSRF 対策をした上で 3 の SameSite=None で対応しました。

また、今回紹介した Lax + POST の組み合わせによる2分縛りはいずれなくなり、2分経過することなく Cookie がセットされなくなるようです。時間をリセットするためにセッションIDを再生成して Cookie に設定し直す方法もあるかとは思いますが、いつ仕様が変わるか分からないのでこの方法は取りませんでした。 https://www.chromium.org/updates/same-site/

Note that the 2-minute window for "Lax+POST" is a temporary intervention and will be removed at some point in the future (some time after the Stable launch of Chrome 80), at which point cookies involved in these flows will require SameSite=None and Secure even if under 2 minutes old.

まとめ

今回は Chrome の SameSite 属性仕様によるアプリケーションの不具合事例を紹介しました。 過去に SameSite 属性の仕様変更があった際にこの2分の仕様が色々な記事で投稿されていましたが、今回の件で自分が詰まったときになかなかそれらの記事に辿り着けませんでした。さまざまな技術動向にアンテナを貼ることは大事だと改めて感じました。 今回の記事が自分のような人に届けば幸いです。

参考URL

やりつくされた IAM アクセスアドバイザーの紹介をあえてやる

これは エキサイトホールディングス Advent Calendar 2022 の21日目の記事です。

みなさん、AWS を利用していて IAM の最小権限を割り当てるって難しくない?と思ったことはありませんか。そんな時は IAM アクセスアドバイザーが便利ですよね。ネット上に IAM アクセスアドバイザーについての説明はごろごろありますが、あまり目立たない機能なのであえて紹介してみたいと思います。

目次

IAM アクセスアドバイザーとは

IAM アクセスアドバイザーとは、IAM リソースがアクセス可能なサービスや IAM リソースの過去のアクセス履歴などの情報を提供してくれる IAM の機能です。この機能はマネージメントコンソール上で利用したり、API が提供されているので CLI で利用したりすることができます。

どんな時に役に立つか

権限を整理し最小権限に近づけることができます。例えば、高権限な IAM ポリシーを持った IAM ロールが存在しているが、その IAM ロールが利用しているサービスが限定されていることが分かれば、割り当てているポリシーを見直すきっかけになるでしょう。

他にも、簡易的な監査に利用できます。例えば、意図していない AWS サービスにアクセス可能となっていないか確認できたり、過去一定期間でアクセスした AWS サービスの履歴の表示をできたりします。なおサービスの追跡期間は過去 400 日間です。十分すぎますね。

実例

マネージメントコンソール上での見え方を紹介します。まず、IAM サービスに移動して適当に IAM ユーザや IAM ロールを1つ選択します。そうすると画面中央あたりに「アクセスアドバイザー」というタブがあるので押下すると下記のような画面が出るかと思います。

アクセスアドバイザータブ押下後の画面

ここでは、選択した IAM エンティティが過去に利用したサービス一覧と日付が表示されています。ここを見れば、普段から使用している・使用していないサービスを把握することができます。つまり、使用していないサービスが見つかれば、そのサービスに対する権限を剥奪することにより、最小権限な IAM ポリシーを作成することができます。

さらに、EC2, IAM, S3, Lambda サービスについてはリンクになっており、飛び先でより詳細な情報を確認できます。今回は EC2 の画面を見てみます。EC2 を押下した画面が下記となっています。

アクションレベルで使用サービスを確認する画面

EC2, IAM, S3, Lambda サービスについてはアクションごとにアクセス時間とアクセスリージョンを表示してくれます。ここではサービスのどのアクションを普段から使用している・使用していないかを確認できます。つまり、使用していないアクションが見つかれば、そのアクションの権限を剥奪することで、より最小権限な IAM ポリシーを作成することができます

どうしても適切な IAM ポリシーの作成に困ったら少しだけ緩めに権限を作成し、数日後に実際に IAM エンティティが利用しているサービス(EC2, IAM, S3, Lambdaに限る)のアクションが分かるのでそのタイミングで最小権限な IAM ポリシーを設定するといった方法もとれますね。

まとめ

簡単ではありますが、今回は IAM アクセスアドバイザーを紹介しました。
あまり目立たない機能ですが、権限整理にはもってこいの機能ですし追加料金もかからないので、ぜひ使ってみてはいかがでしょうか。

2022年の AWS IAM Identity Center の進化を振り返ってみよう

これは エキサイトホールディングス Advent Calendar 2022 の4日目の記事です。

業務上、AWS IAM Identity Center(旧AWS Single Sign-On)をよく使うので、この記事では 2022 年の AWS IAM Identity Center 関連の嬉しいアップデートを振り返ろうと思います。ただし、re:Invent 2022 の内容は入っていません。 また AWS IAM Identity Center は名称として長いので、以降の文章内では IIC と省略させていただきます。

目次

AWS IAM Identity Center とは

アップデートを紹介する前に簡単に IIC を説明させてください。 IIC は「どのユーザーどの AWS アカウントどの権限でログインするか」ということを一元管理する目的で利用する AWS のサービスです。 IIC を使用してユーザーやグループを 1 か所で作成したり接続したりすることで、従業員のサインインセキュリティを管理できます。 AWS Organizations を利用して複数の AWS アカウントを運用している企業では重宝するサービスかと思います。

また、今年の 7 月には AWS Single Sign-On (AWS SSO) というサービス名自体が AWS IAM Identity Center に変わりました。個人的な話ですが、最近やっとアイデンティティセンターという呼び方に慣れてきました。

aws.amazon.com

AWS IAM Identity Center が大阪リージョンで利用可能に

aws.amazon.com

IIC が大阪リージョンで利用可能になりました。 弊社では BCP 対策として東京リージョン以外のリージョンを検討しているところで、リージョンを選択する上で IIC の大阪リージョン上陸は好材料となりました。

大阪リージョンの利用を検討している方にとっては良いアップデートかと思います。

組織内で委任されたメンバーアカウントからの AWS IAM Identity Center の管理

aws.amazon.com

AWS Organizations で組織を管理している場合、組織の親 AWS アカウントが存在します。AWS のベストプラクティスとしては、親 AWS アカウントでは親 AWS アカウントで必要となる AWS サービスのみを利用し、親 AWS アカウントで扱う必要のない AWS サービスは組織内の別 AWS アカウント(以降、子 AWS アカウントと呼びます)に委任しましょうということになっています。親 AWS アカウントから AWS サービスの利用権限を委任された子 AWS アカウントのことを委任管理者アカウントと呼びます。

今回のアップデートで IIC を委任管理者アカウントで利用できるようになりました。ベストプラクティス通り、親 AWS アカウントでできるだけ AWS サービスを利用しないようにしている企業にとっては良いアップデートだったのかと思います。弊社も親 AWS アカウントではなく、委任管理者アカウントで IIC を運用するようにしました。

AWS IAM Identity Center が AWS Identity and Access Management (IAM) カスタマー管理ポリシー (CMP) のサポートを追加

aws.amazon.com

今まで IIC でログインユーザのポリシーを管理するには、親 AWS アカウントもしくは委任管理者アカウントでインラインポリシーを指定する必要がありました。しかし、このアップデートにより、カスタムポリシーや許可の境界用のポリシーを子 AWS アカウントで作成し管理することができるようになりました。このアップデートにより、親 AWS アカウントもしくは委任管理者アカウントでコントロールしたい権限は引き続き今まで通りインラインポリシーを使用し、子 AWS アカウントで管理しても問題ないユーザの権限はカスタムポリシーや許可の境界用のポリシーを使用して子 AWS アカウントで管理することができます。

ユーザの権限管理を子 AWS アカウントに委任することができるようになり、権限管理の柔軟性が上がった良いアップデートだったかと思います。

IAM アイデンティティセンターがユーザーエクスペリエンスとクラウドセキュリティ向上のため、セッション管理機能を追加

aws.amazon.com

このアップデートでユーザのセッション時間の調整が15分〜7日間でできるようになり、それに加えて個別のユーザセッションを終了することも可能となりました。企業のセキュリティーポリシーにもよりますがセッション時間を長めに設定しておくと、ユーザがいちいち毎日ログインしなければならないみたいなことが避けられますね。ユーザセッションの無効化については、何かあったときにユーザを強制的にログアウトさせることができますが、ログアウトまで最長1時間かかるので即時ログアウトにならない点は注意が必要です。

タイトル通り、ユーザーエクスペリエンスとクラウドセキュリティが向上した良いアップデートだったかと思います。

大規模にユーザーとグループを管理する、新しい AWS IAM Identity Center API を発表

aws.amazon.com

これまで Get や List などの取得系の API しか存在しなかったのですが、このアップデートにより作成・削除・更新系の API も増えました。これにより IIC で管理しているユーザの作成や削除などのワークフローが組みやすくなり、入退社時の人の出入りやユーザの棚卸しなどが自動化できるかもしれません。

ユーザ管理の自動化のようなワークフローを組めるようになる良いアップデートだったかと思います。

まとめ

AWS IAM Identity Center に関する 2022 年のアップデートの中で役に立ちそうなものを紹介しました。 AWS IAM Identity Center を利用している企業において、今回紹介したアップデートで既存の問題が解消された!なんてことがあれば幸いです。 他にも AWS IAM Identity Center のアップデートはありますので、What's new でこの機会に振り返ってみるのもいいかもしれません。

EC2 Image Builder で作成した AMI が OU 単位で共有できるようになった話

これは エキサイトホールディングス Advent Calendar 2021 の25日目の記事です。

みなさん,AWS Organizations と EC2 Image Builder は使っていますでしょうか.AWS Organizations はマルチアカウントを一元的に管理し統制できるサービスです.そして,EC2 Image Builder は Amazon マシンイメージ(AMI)の構築フローを自動化するサービスです.AWS Organizations は AWS のさまざまなサービスと連携することでどんどん便利になってきており,最近のアップデートで EC2 Image Builder と連携して AWS Organizations が管理する各 AWS アカウントへの AMI の共有が容易になったので,今回はその紹介をしたいと思います.アップデートが発表された 2021/11/24 からつい最近までは AWS CLI でしか設定ができなかったのですが,マネージメントコンソールからも設定できるようになっていたのでマネージメントコンソール上での設定を紹介します.

aws.amazon.com

目次

AWS Organizations とは

AWS Organizations は組織のマルチアカウント運用に利用されるサービスで,アカウントの一元管理や各アカウントの統制などに役立ちます.

一般的に,管理するアカウントとなる親アカウントと管理されるアカウントとなる子アカウントで分けて,アカウント管理チームは親アカウントを利用してアカウントを一元管理する構成が多いかと思います.AWS Organizations は他の数多くの AWS のサービスと連携しているため,子アカウントの AWS サービスの設定を親アカウントで一括設定する使い方ができます.また,AWS Organizations では Organization Unit(OU) と呼ばれるアカウントをグループ化する機能を利用してアカウントを管理することができ,ガバナンス目的でそれらの OU に対して「S3 のパブリック公開を禁止する」のようなポリシーを適用することもよくある使い方です.

f:id:a-mochan:20211223090126p:plain

EC2 Image Builder とは

EC2 Image Builder は AMI の構築フローを自動化するサービスです.

AMI は事前構成された OS イメージで,EC2 インスタンスを作成するために必要な OS とソフトウェアを含んでいます.サービスに必要なパッケージやミドルウェアの設定などをカスタムして AMI を作成し,カスタムした AMI から EC2 インスタンスを起動することでサービス提供可能な EC2 インスタンスを迅速に用意できます.このような目的でカスタムされた AMI をゴールデンイメージと呼ぶこともあります.

ゴールデンイメージを利用する場合,OS のバージョンアップやパッケージの脆弱性対応などさまざまな理由でゴールデンイメージを作り直すことがあり,その都度新しいイメージを構築・テストして必要な環境へイメージを共有しなければなりません.その一連のプロセスをサービス化したのが EC2 Image Builder です.

ここでは EC2 Image Builder の詳細は割愛しますが,用語のざっくりとした解説と AMI の構築フローを図にしておきます.

f:id:a-mochan:20211223091440p:plain

アップデートの概要

今回のアップデートにより AWS アカウントにゴールデンイメージを配布することが容易になりました.従来の機能で実現できていた手法と今回のアップデートでできるようになったことを説明します.

従来可能であった運用

従来までは以下 2 つの方法で AMI を他アカウントに共有できていました.

  • AMI の許可設定を変えて共有する
  • EC2 Image Builder 上で設定するパイプラインを AWS Resource Access Manager と AWS Organizations を利用して共有する

前者の場合は,作成されたカスタム AMI の許可設定に 特定の AWS アカウント ID を指定することで他の AWS アカウントに AMI を共有する仕組みです.これは組織が管理しているアカウントが少なければよいですが,数が多くなってくるとアカウントの数だけ許可設定を追加しないといけないので別途 AWS Lambda を使ってプログラムを書いて自動化していくことになるかと思います.現状の運用はこの方法なのですが,AWS Lambda を書いてなんとか頑張るのは他の方法がなかったときの最終手段にしたい気持ちがあります.

後者の場合は,EC2 Image Builder 上で設定するパイプライン自体を共有することを意味しています.AWS のリソース共有を管理する AWS Resource Access Manager というサービスと AWS Organizations を使って OU 単位で共有が可能なのですが,子アカウントでは共有されたパイプラインを EC2 Image Builder で実行して AMI を作成しなければならず,単純に AMI を共有するよりステップが 1 つ増えてしまいます.可能であれば子アカウントの利用者に運用負荷を与えたくないので採用しませんでした.

新しく可能となった運用

今回のアップデートでは EC2 Image Builder と AWS Organizations が連携することにより,パイプラインの流れの中で指定した OU に AMI を共有できる機能が実装されました.具体的には,下図のようにパイプラインの中のディストリビューションフェーズで AMI をどこの OU に配布するか設定できます.

f:id:a-mochan:20211223091628p:plain

これにより,OU 配下のアカウントに対して AWS Lambda を使わず AMI を共有できるようになりました!
また,OU を使って AMI を共有できるようになるので,OU にアカウントを「入れる・外す」のアクションが AMI を共有「する・しない」の状態に紐づきます.注意点としては,ディストリビューションで OU を設定したパイプラインを一度実行して AMI を作成しないと AMI の共有は機能しないので,ディストリビューションを設定しただけで終わらないようにお気をつけください.

まとめ

  • EC2 Image Builder と AWS Organizations による AMI の共有設定が容易になった話をしました
  • AWS Organizations を利用しているのであれば,AMI の共有をどうするかということに頭を使う必要がなくなりました
  • 従来の運用を楽にするために利用サービスのアップデートをウォッチしていく重要性を改めて感じました

NameSpace 機能を使ったネットワークからインターネットに出れなかった話

コンテナのネットワーク周りを理解するためにカーネルの NameSpace 機能を使って Ubuntu20.04 サーバ上に仮想的なネットワークを構築してみた.その際,作成したネットワークからインターネットに出るのに四苦八苦したのでメモとして残しておく.この記事では,ネットワークの構築の詳細は話さず構築後のトラブルシュートをメインとする.

目次

概要

NameSpace 機能を使ったコンテナネットワークの実験をした.同じような実験についてはインターネットにいくつか記事があるが,今回は以下の記事を参考にした.

christina04.hatenablog.com

christina04.hatenablog.com

構築した環境は図のようになっている.

f:id:a-mochan:20211123122417p:plain
全体のネットワーク

上記ネットワークを構築するコマンドをまとめておいた.もしよければどうぞ.

クリックすると展開されます

#!/bin/bash

ip netns add host1
ip netns add host2
ip netns exec host1 ip link set lo up
ip netns exec host2 ip link set lo up
ip link add name veth1 type veth peer name br-veth1
ip link add name host1 type veth peer name br-host1
ip link add name host2 type veth peer name br-host2
ip link set host1 netns host1
ip link set host2 netns host2
ip link add br0 type bridge
ip link set dev br-veth1 master br0
ip link set dev br-host1 master br0
ip link set dev br-host2 master br0
ip addr add 10.0.0.100/24 dev veth1
ip netns exec host1 ip addr add 10.0.0.1/24 dev host1
ip netns exec host2 ip addr add 10.0.0.2/24 dev host2
ip netns exec host1 ip link set host1 up
ip netns exec host2 ip link set host2 up
ip link set veth1 up
ip link set br-veth1 up
ip link set br-host1 up
ip link set br-host2 up
ip link set br0 up

echo 1 > /proc/sys/net/ipv4/ip_forward
ip netns exec host1 ip route add default via 10.0.0.100
ip netns exec host2 ip route add default via 10.0.0.100

また,環境としては EC2 の Ubuntu20.04 を使ったので環境構築として以下の記事も参考にさせていただいた.また,EC2 の設定としてインターネットと通信できる状態にしておくことが前提となるのでご注意ください.

dev.classmethod.jp

こちらも Ubuntu で実行できるようコマンドをまとめておいた.

クリックすると展開されます

#!/bin/bash

apt-get update
apt-get install -y apt-transport-https ca-certificates curl software-properties-common jq
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get install -y docker-ce cgdb cgroup-tools uuid-runtime tree iputils-ping make gcc
git clone git://git.kernel.org/pub/scm/linux/kernel/git/morgan/libcap.git /usr/src/libcap
cd /usr/src/libcap
make
make install

NameSpace を使って隔離したネットワークからインターネットへ出ていけない

先に紹介した記事 にもある通り,NameSpace 機能で作成した Host1 や Host2 からインターネットに出ていくためには iptables による Nat を設定しなければならないのでホスト Linux 上で以下のように設定した.

iptables -t nat -I POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

この状態で Host1 から ping を打ってみるが,インターネットへ疎通できなかった.

ip netns exec host1 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
...

試しに eth0,veth1,br-veth1,br-host1 のそれぞれで tcpdump をしてみてどこまでパケットが届いているか確認すると br-host1 のキャプチャでしかパケットが表示されなかった.

tcpdump -i eth0 -p icmp -n
...
tcpdump -i veth1 -p icmp -n
...
tcpdump -i br-veth1 -p icmp -n
...
tcpdump -i br-host1 -p icmp -n
01:49:58.420877 IP 10.0.0.1 > 8.8.8.8: ICMP echo request, id 8735, seq 203, length 64
01:49:59.444910 IP 10.0.0.1 > 8.8.8.8: ICMP echo request, id 8735, seq 204, length 64

f:id:a-mochan:20211123122524p:plain
Nat設定状態でパケットが到達したNIC

どうやら bridge 内の通信がまずうまくいっていないので以下の記事を参考にして bridge 内通信を修正.今回は Netfilter で iptables を呼ばない設定をすることにした.記事の最後にこのカーネルパラメータを 1 の状態で解決できなかった理由も書いておく.

sysctl -w net.bridge.bridge-nf-call-iptables = 0

qiita.com

再度各 NIC に対して tcpdump をしてみたところ veth1 までパケットが届いていることがわかった.

tcpdump -i veth1 -p icmp -n
03:06:17.104353 IP 10.0.0.2 > 8.8.8.8: ICMP echo request, id 9766, seq 1, length 64

f:id:a-mochan:20211123122701p:plain
Netfilter の設定変更後にパケットが到達した NIC

tcpdump より veth1 から eth0 へのパケットが落ちていることが分かるので,iptables で veth1 から eth0 への FORWARD を設定した.

iptables -t filter -I FORWARD -s 10.0.0.0/24 -d 0.0.0.0/0 -j ACCEPT

eth0 の NICtcpdump すると Nat の設定も効いていて,インターネットから eth0 まで通信が返ってきていることを確認できた.

tcpdump -i eth0 -p icmp -n
03:12:11.326420 IP 172.30.20.98 > 8.8.8.8: ICMP echo request, id 9779, seq 1, length 64
03:12:11.328700 IP 8.8.8.8 > 172.30.20.98: ICMP echo reply, id 9779, seq 1, length 64

最後に eht0 から veth1 への戻りの FORWARD 設定をする.セキュリティも考慮してソース IP は 8.8.8.8 だけにしておく.

iptables -t filter -I FORWARD -d 10.0.0.0/24 -s 8.8.8.8/32 -j ACCEPT

そうすると Host1 からインターネットへの接続を確立できる.

ip netns exec host2 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=104 time=2.51 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=104 time=2.36 ms

余談

今回は bridge ネットワーク通信を正常にするために net.bridge.bridge-nf-call-iptables を 0 に設定した対応を取ったが,先の記事 にある iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPTiptables に設定する方法で進めてみた.
うまくいかない箇所は同じで,まずは veth1 から eth0 への通信が失敗するので iptablesiptables -t filter -I FORWARD -s 10.0.0.0/24 -d 0.0.0.0/0 -j ACCEPT を設定する.そうすると eth0 にパケットが到達するようになるが,なぜか Nat が効かない状態となる.この Nat をどうにか効かせようと色々調べて設定も変えてみたがうまくいかなかった.結果として net.bridge.bridge-nf-call-iptables=0 にする方法でうまくいったのでそちらをまとめたのだが,もし誰かこの原因をご存知の方がいたらコメント欄に書いていただけると助かります.

まとめ

  • トラブルシュートしながらコンテナネットワークからインターネットへ出ていく時の設定を見つけた
  • 改めてネットワークトラブルは tcpdump などでパケットを取り続けるしかないことを学んだ
  • net.bridge.bridge-nf-call-iptables の設定をそのままにした状態でうまくいかない原因は明確となっていない

サーチリストを使った名前解決はやめよう

DNS を運用していると名前衝突問題に気を付ける必要がある.名前衝突問題とは,組織が内部的に使う Top Level Domain(TLD)と インターネットで利用できる TLD が重複してしまうことによりDNSの動作が期待するものとは違った動作になることを指す.名前衝突問題で具体的に問題となるのは,例えばインターネット上のドメイン名を検索するつもりが,ローカルネットワークで独自に付けたTLDに対して名前解決を行ってしまうことや,またその反対に,ローカルネットワークのドメイン名を検索するつもりがインターネット上の TLD に対して名前解決してしまうことである.JPNIC のページが詳しいのでリンクを置いておく.

www.nic.ad.jp

今回は後者の問題である「ローカルネットワークのドメイン名を検索するつもりがインターネット上の TLD に対して名前解決してしまう」という事象を再現してみようと思う.上記の記事で紹介されているサーチリストを使ったケースを想定する.

概要

環境として使うのは AWS とし,AWS の各サービスの説明は本質的な部分とは異なるので今回は割愛する.

dig コマンドで名前解決の確認を行うために OS を Ubuntu18.04 とした EC2 を立てておく.EC2 から利用する内部的に使うドメインは Route53 で管理し, example.com という名前でプライベートホストゾーンを作成する.プライベートホストゾーンにインターネット上に存在するサブドメインを登録し,サーチリストを設定した EC2 から名前解決を行うと「ローカルネットワークのドメイン名を検索するつもりがインターネット上の TLD に対して名前解決してしまう」が再現することを確認する.

Route53 の設定

Route53 では example.com というプライベートホストゾーンを作成し,適当なプライベート IP アドレス(172.30.20.86)を指定した 2 つの A レコードを登録しておく.

aws という TLDAWS が取得しているドメインであり, dns1.nic.aws を名前解決すると既に A レコードを正引きすることができる.

Route53 の設定を以下の図に示す.

f:id:a-mochan:20211031174115p:plain
Route53の設定

サーチリストの設定

EC2 でサーチリストを設定する.

サーチリストは、DNS検索サフィックスDNS suffix search listなどとも呼ばれる、 ユーザーがドメイン名を入力する手間を減らせるようにするためのリストです。 具体的にはDNSにおいて、 名前解決の際にドメイン名を最後まで入力しなくても、 サーバやクライアントで補完がされるように、 補完候補となる文字列を順番に並べたものです。 www.nic.ad.jp

Ubuntu18.04 の場合 EC2 上で /etc/netplan/99-manual.yaml というファイル名を以下の内容で作成し再起動すればよい.

network:
    ethernets:
        eth0:
            nameservers:
                search:
                  - example.com
    version: 2

再起動した EC2 にログインし,サーチリストを使うようにオプションをつけた dig を実行すると example.com が補完され instance-dst.example.com を名前解決できていることが確認できる.

root@ip-172-30-20-203:/etc# dig instance-dst +search

; <<>> DiG 9.11.3-1ubuntu1.16-Ubuntu <<>> instance-dst +search
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41197
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;instance-dst.example.com.  IN  A

;; ANSWER SECTION:
instance-dst.example.com. 300   IN  A   172.30.20.86

;; Query time: 2 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sun Oct 31 08:55:09 UTC 2021
;; MSG SIZE  rcvd: 69

では,プライベートホストゾーンに登録してあるもう 1 つのサブドメインdns1.nic.aws を名前解決してみる.

root@ip-172-30-20-203:/etc# dig dns1.nic.aws +search

; <<>> DiG 9.11.3-1ubuntu1.16-Ubuntu <<>> dns1.nic.aws +search
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8193
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;dns1.nic.aws.          IN  A

;; ANSWER SECTION:
dns1.nic.aws.       300 IN  A   213.248.218.53

;; Query time: 2 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sun Oct 31 08:58:35 UTC 2021
;; MSG SIZE  rcvd: 57

名前解決の結果 172.30.20.86 ではなく 213.248.218.53 が返ってきている.これより,プライベートホストゾーンではなくインターネットで公開されているドメインが正引きされていることが分かる.これは,サーチリストの補完が行われる前にまずは dig コマンドで指定されたドメインが名前解決されるため, dns1.nic.aws の正引き結果が返ってきてしまっている状態である.この事象は,プライベートホストゾーンのサブドメインを運用中にそのサブドメインとインターネットに存在するドメインが重複した場合,サーチリストを設定したサーバからの名前解決が失敗する可能性があることを示している.

対策

JPNIC が紹介している対策はシンプルでサーチリストを使わないことである.

名前衝突の問題への根本的な対策は、TLDの重複を避けることです。つまり、原因となっている、内部向けのTLDやサーチリストの使用を止めることです。 www.nic.ad.jp

サーチリストを使わなければ名前解決する際に必ずプライベートホストゾーンの FQDN 指定するので,今回の事象は発生しなくなる.

まとめ

  • DNS の名前衝突による「ローカルネットワークのドメイン名を検索するつもりがインターネット上の TLD に対して名前解決してしまう」問題の再現をした
  • 対策であるサーチリストを使わないということが大事

別AWSアカウントへIAMロールを提供する際は外部IDを思い出す

IAMロールでスイッチロール(マネージメントコンソールで他のAWSアカウントのロールにスイッチすること)を実現する際に「外部ID」というオプションがあったので,何のためにあるのかまとめてみた.先に言っておくとスイッチロールを使うときに外部IDを気にする必要はない

外部IDとは

外部IDとは,第三者用のIAMロールを作成する際にセキュリティ対策用途で設定する値のことである.このセキュリティ対策は「混乱した代理」と呼ばれる問題を解決するために実施される.「混乱した代理」の概要は以下に解説されているが,この記事を通して理解できるかと思う.

docs.aws.amazon.com

以下は実際のIAMロールを作成する画面であり,外部IDを設定できることが分かる.スイッチロールを使うときに外部IDを指定する必要はない旨もかかれている.

f:id:a-mochan:20210605165605p:plain

コンソールでは、ロールの切り替え機能を使用する外部 ID の使用はサポートされていません。 画像にも書いてある通り,スイッチロールさせたいだけなら外部IDは無視して構わない.

ユースケース

スイッチロールで使わないのであれば,どんなユースケースがあるのかを考えてみる.また「混乱した代理」とその対策である外部IDがどのように機能するのか理解する.

異なる AWS アカウントで複数の顧客をサポートするマルチテナント環境では、AWS アカウントごとに 1 つの外部 ID を使用することをお勧めします。

「混乱した代理」の概要より,複数のAWSアカウントをお客様に持つサービスを提供している会社を例にとると理解しやすい.ここでは「a-mochan」という監視系のSaaSサービスがあると仮定してユースケースを説明する.「a-mochan」もAWS環境でサービス提供をしている.

「a-mochan」では,お客様のS3バケットにあるログへ定期的にアクセスし,お客様ごとにログを可視化するダッシュボードを用意している.「a-mochan」のお客様であるClient Aは自身のAWSアカウントのS3に「a-mochan」からのアクセスを許可するためにIAMロールを作成した.その際外部IDは設定しなかった.Client A は作成したIAMロールのAmazon Resource Name(arn)を「a-mochan」上で設定し,S3のデータを「a-mochan」に取り込めるようにしたことでダッシュボードを確認することができた.

f:id:a-mochan:20210617212756j:plain
クライアントとサービスの関係

ここで,悪意のあるユーザが「a-mochan」を利用し始めた.悪意のあるユーザは,IAMロールのarnが arn:aws:iam::[AWS AccountID]:role/[Role Name] のような形で推測が容易なこともあり,Client A が作成したIAMロールのarnを推測し「a-mochan」上で設定することができた.そうすると悪意のあるユーザのダッシュボードにも Client A のS3の情報が表示され,悪意のあるユーザは Client A のダッシュボードを閲覧可能な状態になる.この状態のことを「混乱した代理」と呼ぶ.

f:id:a-mochan:20210617210024j:plain
混乱した代理が発生

では「混乱した代理」対策のために外部IDを利用する流れをみていく.「a-mochan」はお客様ごとに一意のIDを発行し,お客様は作成するIAMロールの外部IDとして「a-mochan」が発行したIDを指定する.そうするとClient AのAWSアカウントでは「a-mochan」からのアクセス時に外部IDが一致しているかどうかチェックするようになるので,「a-mochan」ではClient AのAWSアカウントへのリクエスト時に外部IDを付与する仕様に変更する.外部IDは「a-mochan」のシステムで発行された利用者ごとに一意なIDで,利用者はコントロール不可能な値である.そのため,悪意のあるユーザが Client A のIAMロールのarnを推測できたとしても,「a-mochan」から発行されたClient A用のID(948833852)と悪意のあるユーザ用のID(749678483) が違うので「混乱した代理」は起こらない.

f:id:a-mochan:20210617210030j:plain
外部IDにより混乱した代理が起こらない

このように外部IDは「混乱した代理」を引き起こさないために設定する値のことであることがわかった.

感想

AWSアカウントへIAMロールを用いてアクセスさせるようなサービスを利用する時は,外部IDの発行が実装されているか確認すべきだと感じた.外部IDを考慮していないサービスはどのような方法で「混乱した代理」を回避しているのか確認して利用するサービスを選定すべきなのかなと思う.また逆も然りで,そのようなサービスを提供する時は外部IDを考慮した実装にすべきである.

まとめ

  • 外部IDの意図を理解した
  • 外部IDが必要となる「混乱した代理」についてユースケースを用いて理解した