yamamototis1105’s tech blog

ネットワークを中心とした技術ブログです

Amazon CloudFrontの主要機能と非機能項目のまとめ

はじめに

 Amazon CloudFrontは、Webコンテンツをスピーディかつ効率的にデリバリすることを可能にする、CDN (Contents Delivery Network) のマネージドサービスです。
 このようなCloudFrontですが、本記事は構成、機能、性能、セキュリティ、信頼性、運用性の観点で整理してみました。

構成

 ユーザがディストリビューション (=エッジ) が持つドメインへリクエストし、キャッシュヒットした場合はそのままレスポンスしますが、キャッシュミスした場合はビヘイビア (=ルール) に従いオリジンにリクエストし、データをキャッシングのうえ、ユーザにレスポンスする、というフローとなります。

 構成は1つのドメインにつき1つのディストリビューションを作成し、1つのディストリビューションにつき複数のオリジンとビヘイビアを定義します。オリジンはAPI gatewayMedia PackageS3Elastic Load BalancerMediaStoreが選択可能です。その他、ディストリビューションWAF、ビヘイビアはCloudFlontの前後でCloudFront FunctionもしくはLambda@Edgeの処理を実行するオプションが選択可能です。

 下記の例では、xxxxxxxxxxxxxx.cloudfront.netというドメインディストリビューションを作成し、API GatewayApplication Load BalancerS3のオリジンを定義し、/api/*パスの場合はAPI Gatewayへ、/alb/*パスの場合はALBへ、それ以外はS3へリクエストするビヘイビアを定義した構成です。

機能

 キャッシングは、キャッシュポリシー、オリジンリクエストポリシー、レスポンスヘッダーポリシーの組み合わせで制御します。ユーザはカスタムポリシーも作成できますが、特殊な要件が無いようならAWSマネージドポリシーから選択すればOKかと思います。

ポリシー 説明
キャッシュポリシー キャッシュ条件を定義
  • キャッシュTTL (最小TTL/最大TTL/デフォルトTTL)
  • キャッシュキー (ヘッダ/クエリ文字列/cookie)
  • 圧縮サポート (Gzip/Brotli)
オリジンリクエストポリシー オリジンへのリクエストに追加する情報を定義
  • リクエスト情報 (ヘッダ(注1)/クエリ文字列/cookie)
(注1) X-Forwarded-ForヘッダでユーザIP確認など
レスポンスヘッダーポリシー ユーザへのレスポンスに追加する情報を定義
  • レスポンス情報 (ヘッダ(注2))
(注2) CORSヘッダでリソース共有許可など

 キャッシュTTLは、オリジンとCloudFrontで制御することが可能です。
 オリジンで制御する場合、オリジン環境次第で変わりますが、例えばS3の場合はオブジェクトのメタデータにcache-control: max-age=Xを設定します。CloudFrontで制御する場合、上記の通りキャッシュポリシーで最大TTL、最小TTL、デフォルトTTLを設定します。
 両方とも設定した場合、オリジンよりCloudFrontの方が優先されるためご注意下さい。

性能

 エッジ (CloudFront)・オリジン (S3/API Gateway) の静的・動的コンテンツにcurlコマンドを実行し、全体処理時間 (time_total) と最初の転送までの時間 (time_starttransfer) を計測した結果は以下の通り。動的コンテンツより静的コンテンツの方が短縮される時間は大きいことが分かります。

for i in `seq 1 10`;\
do curl -s -o /dev/null -w\
 "time_tolal: %{time_total} / time_starttransfer: %{time_starttransfer}\n"\
 https://xxxxxxxxxxxxxx/;\
done
静的なコンテンツ 動的なコンテンツ
CloudFront
(time_tolal / time_starttransfer)
1回目: 1.213037 / 0.349783 s
2回目: 0.030402 / 0.015279 s
3回目: 0.024549 / 0.009559 s
4回目: 0.019939 / 0.008136 s
5回目: 0.019447 / 0.010789 s
6回目: 0.042804 / 0.008992 s
7回目: 0.019219 / 0.008589 s
8回目: 0.026871 / 0.011357 s
9回目: 0.018739 / 0.007598 s
10回目: 0.030951 / 0.020316 s
1回目: 0.998139 / 0.998066 s
2回目: 0.667226 / 0.667149 s
3回目: 0.645744 / 0.645671 s
4回目: 0.635403 / 0.635334 s
5回目: 0.213461 / 0.213381 s
6回目: 0.660867 / 0.660790 s
7回目: 0.646030 / 0.645955 s
8回目: 0.652421 / 0.652343 s
9回目: 0.212897 / 0.212818 s
10回目: 0.213340 / 0.213256 s
オリジン
(time_tolal / time_starttransfer)
1回目: 1.235794 / 0.373417 s
2回目: 1.194972 / 0.318534 s
3回目: 1.188544 / 0.319619 s
4回目: 1.155201 / 0.310591 s
5回目: 1.207720 / 0.317577 s
6回目: 1.353181 / 0.326765 s
7回目: 1.206274 / 0.320271 s
8回目: 1.248435 / 0.319104 s
9回目: 1.200117 / 0.318902 s
10回目: 1.178259 / 0.322315 s
1回目: 0.445774 / 0.445713 s
2回目: 0.443157 / 0.443091 s
3回目: 0.449035 / 0.448966 s
4回目: 0.446315 / 0.446255 s
5回目: 0.435737 / 0.435677 s
6回目: 0.442605 / 0.442538 s
7回目: 0.443052 / 0.442987 s
8回目: 0.439792 / 0.439723 s
9回目: 0.429575 / 0.429508 s
10回目: 0.441894 / 0.441828 s

 エッジ (CloudFront) のコンテンツに圧縮オプションを付与したcurlコマンドを実行し、ダウンロードサイズ (size_download) を計測した結果は以下の通り。非圧縮よりGzip圧縮、Gzip圧縮よりBrotli圧縮の方がサイズは小さいことが分かります。

curl -s -o /dev/null -w "size_download: %{size_download}"\
 https://xxxxxxxxxxxxxx/compress.txt
curl -s -o /dev/null -H 'Accept-Encoding:gzip' -w "size_download: %{size_download}"\
 https://xxxxxxxxxxxxxx/compress.txt
curl -s -o /dev/null -H 'Accept-Encoding:br' -w "size_download: %{size_download}"\
 https://xxxxxxxxxxxxxx/compress.txt
非圧縮 Gzip圧縮 Boltri圧縮
CloudFront
(size_download)
179,079 byte 1,018 byte 339 byte

セキュリティ

 ユーザからのリクエストやオリジンへのリクエストに対して、プロトコル、ポート、バージョンで制御することが可能です。
オリジン ビヘイビア
ユーザからのリクエストを制御 (注3) ユーザーからのリクエストと
    オリジンへのリクエストで合わせる
オリジンへのリクエストを制御
オリジンアクセス制御
 オリジンへのリクエストに対して、ソースをCloudFrontだけに制御することが可能です。
  • S3の場合、CloudFrontのオリジンでOAC (Origin Access Control) を作成・設定
    S3のバケットポリシーでOAC設定時にコピー可能なポリシーを設定

  • API Gatewayの場合、API Gatewayのリソースでメソッドを選択のうえAPIキー必須を設定
    CloudFrontのオリジンでカスタムヘッダとしてX-API-Keyを設定

地域制限
 ユーザからのリクエストに対して、アクセスできる・できない国を制御することが可能です。
署名付きCookie
 ユーザからのリクエストに対して、署名付きCookieで制御することが可能です。
  • CloudFrontのパブリックキーで公開鍵(注4)を設定のうえキーグループを作成
  • CloudFrontのビヘイビアでビューワアクセス制限のためのキーグループを選択
  • オリジンで秘密鍵(注4)により署名付きCookieを作成するように設定
    (注4) 公開鍵・秘密鍵はOpenSSHなどで別途作成しておく
フィールドレベル暗号化
 オリジンへのリクエストに対して、フィールドレベル暗号化で暗号化することが可能です。
  • CloudFrontのフィールドレベル暗号化でプロファイルを作成・設定
  • CloudFrontのビヘイビアでフィールドレベル暗号化の設定IDを入力。
  • オリジンでデータを複合するように設定

信頼性

ステールコンテンツ
 オリジンがダウンしたりアクセスできない場合でも、CloudFrontでキャッシュされたコンテンツをデリバリし、ユーザに対して正常な状態を振る舞います。
  • 特筆すべき設定はなく、キャッシュ設定のみでOK
オリジン冗長化
 プライマリオリジンがダウンしたりアクセスできない場合でも、セカンダリオリジンへリクエストのうえコンテンツをデリバリし、サービスの可用性を向上させます。
  • CloudFrontのオリジンでプライマリオリジンとセカンダリオリジンを作成
  • CloudFrontのオリジンでプライマリオリジン、セカンダリオリジン、フェイルオーバー基準としてエラーコードを選択のうえオリジングループを作成
  • CloudFrontのビヘイビアでオリジングループを設定
エラーページ
 オリジンがダウンしたりアクセスできない場合、予め準備されたエラーページを表示することで、ユーザに不用意な情報を提供することを防ぎます。
  • CloudFrontのエラーページでエラーコード、レスポンスファイルパス、レスポンスコードを選択のうえカスタムエラーレスポンスを作成

運用性

モニタリング
 CloudFrontのメトリクスをモニタリングすることが可能です。
  • CloudFrontのモニタリングでリクエスト数、データ転送サイズ、エラーレート、キャッシュヒットレート、オリジンレイテンシーなどのメトリクスを表示
レポート
 CloudFrontのレポートを表示・ダウンロードすることが可能です。
  • CloudFrontのレポートでキャッシュ統計、人気オブジェクト、トップリファラ、使用状況、ユーザなどのレポートを表示
カテゴリ レポート
キャッシュ統計
  • リクエストの合計数
  • 結果タイプ別(ヒット/ミス/エラー)のビューワーリクエストの割合
  • ビューワーに転送されたバイト数
  • HTTP ステータスコード
人気オブジェクト
  • 人気オブジェクト
トップリファラ
使用状況
ビューワー

まとめ

 CloudFrontについて構成、機能、性能、セキュリティ、信頼性、運用性の観点で紹介しました。
本記事内容は、AWS workshop studioでも体験できますので是非お試し下さい。

catalog.us-east-1.prod.workshops.aws

おわりに

 CloudFrontを設計・構築される方にとって、本記事内容がご参考になりますと幸いです。

AWS Network Firewallの構成パターンとフィルタ機能

はじめに

 AWS Network Firewallは、トラフィックを制御するファイアウォールのマネージドサービスですが、どのような構成やフィルタがあって、どのように使い分けをすればよいか、と思うことはないですか?
 本記事では、このようなAWS Network Firewallの構成パターンとフィルタ機能を整理してみました。

構成パターン

分散型デプロイモデル

 ワークロードを割り当てたVPC単位でファイアウォールをデプロイするモデルです。VPC単位でファイアウォールポリシーを定義することにより影響範囲を局所化します。

IGW〜NAT GatewayもしくはALB間へデプロイする場合
 IGWの全入出力トラフィックに対して、 同じファイアウォールポリシーを適用したい場合に有効です。インターネットからALBへSSL/TLS通信でアクセスされる場合、ファイアウォールルール次第でNetwork Firewallでインスペクションする必要がある点は要注意です。
 例として、Server1からNAT Gateway経由のインターネットへの通信の流れを以下に示します。
  1. Server1がインターネットへのリクエストパケットを送信する
    ルートテーブルに従ってNATGW1に転送する
  2. NATGW1は送信元アドレスを10.1.1.xから10.1.3.xに変換する
    ルートテーブルに従いEndpoint1に転送する
  3. Endpoint1はNetwork Firewallに転送する
  4. Network Firewallはパケットをフィルタし、Endpoint1に転送する
  5. Endpoint1はルートテーブルに従ってIGWに転送する
  6. IGWは送信元アドレスを10.1.3.xからグローバルアドレスに変換する
    IGWはインターネットに転送する
  7. インターネットからIGWにレスポンスパケットを送信する
  8. IGWは送信先アドレスをグローバルアドレスから10.1.3.xに変換する
    イングレスルートテーブルに従いEndpoint1に転送する
  9. Endpoint1はNetwork Firewallに転送する
  10. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  11. Endpoint1はルートテーブルに従いLacalのNATGW1に転送する
  12. NATGW1は送信先アドレスを10.1.3.xから10.1.1.xに変換する
    ルートテーブルに従いLocalにあるServer1に転送する
NAT GatewayもしくはALB〜Server間へデプロイする場合
 特定Serverの入出力トラフィックに対しのみ、ファイアウォールポリシーを適用したい場合に有効です。ServerからNAT Gateway経由でインターネットへアクセスされる場合、変換前の各Serverのアドレスで検査可能となります。
 例として、Server1からNAT Gateway経由のインターネットへの通信の流れを以下に示します。
  1. Server1がインターネットへのリクエストパケットを送信する
    ルートテーブルに従ってEndpoint1に転送する
  2. Endpoint1はNetwork Firewallに転送する
  3. Network Firewallはパケットをフィルタし、Endpoint1に転送する
  4. Endpoint1はルートテーブルに従ってNATGW1に転送する
  5. NATGW1は送信元アドレスを10.1.1.xから10.1.5.xに変換する
    ルートテーブルに従いIGWに転送する
  6. IGWは送信元アドレスを10.1.5.xからグローバルアドレスに変換する
    IGWはインターネットに転送する
  7. インターネットからIGWにレスポンスパケットを送信する
  8. IGWは送信先アドレスをグローバルアドレスから10.1.5.xに変換する
    デフォルトルートテーブルに従いLocalにあるNATGW1に転送する
  9. NATGW1は送信先アドレスを10.1.5.xから10.1.1.xに変換する
    ルートテーブルに従いEndpoint1に転送する
  10. Endpoint1はNetwork Firewallに転送する
  11. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  12. Endpoint1はルートテーブルに従いLacalにあるServer1に転送する

集約型デプロイモデル

 TGWへ接続された検査用VPCファイアウォールをデプロイするモデルです。TGW経由の全トラフィックを検査用VPCにルーティングすることによりファイアウォールポリシーを統一できます。

VPC間のトラフィックを検査する場合
 VPC間の全トラフィックが、AWS Network Firewallがデプロイされた検査用VPC経由でルーティングされます。
 例として、Sever1~Server2間通信の流れを以下に示します。
  1. Server1がServer2へのリクエストパケットを送信する
    ルートテーブルに従いTGW ENI1に転送する
  2. TGW ENI1はTGWに転送する
  3. TGWはVPC2がアソシエートされたルートテーブルに従いVPC1のアタッチメントに転送する
  4. TGW ENI3はルーティテーブルに従いEndpoint1に転送する
  5. Endpoint1はNetwork Firewallに転送する
  6. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  7. Endpoint1はルートテーブルに従いTGW ENI3に転送する
  8. TGW ENI3はTGWに転送する
  9. TGWはVPC1がアソシエートされたルートテーブルに従いVPC3のアタッチメントに転送する
  10. TGW ENI2はルーティテーブルに従いLocalにあるServer2に転送する
  11. Server2がServer1へのレスポンスパケットを送信する
    ルートテーブルに従いTGW ENI2に転送する
  12. TGW ENI2はTGWに転送する
  13. TGWはVPC3がアソシエートされたルートテーブルに従いVPC1のアタッチメントに転送する
  14. TGW ENI3はルーティテーブルに従いEndpoint1に転送する
  15. Endpoint1はNetwork Firewallに転送する
  16. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  17. Endpoint1はルートテーブルに従いTGW ENI3に転送する
  18. TGW ENI3はTGWに転送する
  19. TGWはVPC1がアソシエートされたルートテーブルに従いVPC2のアタッチメントに転送する
  20. TGW ENI1はルーティテーブルに従いLocalにあるServer1に転送する
VPC〜インターネット間のトラフィックを検査する場合
 VPC〜インターネット間の全トラフィックが、AWS Network Firewallがデプロイされた検査用VPC経由でルーティングされます。
 例として、Sever1からNAT Gateway経由のインターネットへの通信の流れを以下に示します。
  1. Server1がインターネットへのリクエストパケットを送信する
    ルートテーブルに従いTGW ENI1に転送する
  2. TGW ENI1はTGWに転送する
  3. TGWはVPC2がアソシエートされたルートテーブルに従いVPC1のアタッチメントに転送する
  4. TGW ENI3はルーティテーブルに従いEndpoint1に転送する
  5. Endpoint1はNetwork Firewallに転送する
  6. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  7. Endpoint1はルートテーブルに従いNATGW1に転送する
  8. NATGW1は送信元アドレスを10.2.1.xから10.1.5.xに変換する
    ルートテーブルに従いIGWに転送する
  9. IGWは送信元アドレスを10.1.5.xからグローバルアドレスに変換する
    IGWはインターネットに転送する
  10. インターネットからIGWにレスポンスパケットを送信する
  11. IGWは送信先アドレスをグローバルアドレスから10.1.5.xに変換する
    デフォルトルートテーブルに従いLocalにあるNATGW1に転送する
  12. NATGW1は送信先アドレスを10.1.5.xから10.2.1.xに変換する
    ルートテーブルに従いEndpoint1に転送する
  13. Endpoint1はNetwork Firewallに転送する
  14. Network Firewallはパケットをフィルタし、Network FirewallはEndpoint1に転送する
  15. Endpoint1はルートテーブルに従いTGW ENI3に転送する
  16. TGW ENI3はTGWに転送する
  17. TGWはVPC1がアソシエートされたルートテーブルに従いVPC2のアタッチメントに転送する
  18. TGW ENI1はルーティテーブルに従いLocalにあるServer1に転送する

フィルタ機能

 Network Firewallは一つのポリシーを関連付け、ポリシーに複数のルールグループを関連付けます。ルールグループはタイプによって様々なフィルタ機能が提供されています。

ステートフル

標準ステートフルルール
 プロトコル、送信元アドレス/ポート、送信先アドレス/ポート 、方向、アクションを定義します。
 IPセット変数、ポート変数、IPセット参照などの機能を用いたオブジェクトベースのファイアウォールになっており、再利用性や可読性が高いです。
 Network Filrewallの標準ステートフルルールとセキュリティグループは似てますが、条件、方向、アクションの観点で以下の違いがあります。
Network Firewall Security Group
条件 5タプル ※ アドレスはIPセット変数や
  IPセット参照も指定可能
※ ポートはポート変数も指定可能
3タプル ※ アドレスはセキュリティグループや
  プレフィクスリストも指定可能
方向 転送/すべて
※ すべて…送信先アドレス/ポート発、
  送信元アドレス/ポート着の
  反対方向パケットも対象とする意
IN/OUT
アクション パス/ドロップ/アラート パスのみ
ロギング Network Firewallのログ記録 VPCフローログ
ドメインリストルール
 ドメイン名、CIDR 、プロトコル、アクションを定義します。
 CIDR (=送信元アドレス) はデフォルトでVPC CIDRとなっていますが、カスタムで特定Serverのみドメインフィルタを適用するなども可能です。
 AWS Network Firewallドメインリストルールはhttp/httpsでアクセスする時点でフィルタしますが、dnsで名前解決する時点でフィルタしたい場合にはDNS Firewallを使います。
Network Firewall DNS Firewall
タイミング WebサーバへのHTTP/HTTPSリクエスト時
(Network Firewall透過時)
DNSサーバへのDNSクエリ時
(Route53 Resolverクエリ時)
条件 ※ CIDRは送信元アドレスが指定可能
プロトコルはhttp/httpsが指定可能
ドメインリダイレクト設定は
  全て/最初のドメインのみが指定可能
※ クエリの種類は
  リソースレコードタイプが指定可能
アクション ALLOW/BLOCK/ALERT パスのみ
ロギング Network Firewallのログ記録 Route53のクエリのログ記録
Suricata互換ルール
 オープンソースIDS/IPSであるSuricata互換ルールに従い、アクション 、プロトコル、アドレス、ポート、方向、オプションが定義できます。
 Suricata互換ルールは汎用性が極めて高く、一例としては「TCP/443であるが非TLSである通信は拒否する」という指定も可能です。
 Sricata互換ルールの記述方法については、Sricataユーザガイドをご参照下さい。ただし、あくまでも互換のため、AWS Network Firewall開発者ガイドも併せてご参照下さい。
docs.suricata.io
docs.aws.amazon.com

ステートレス

 ステートフルルールとほぼ同様、優先度、プロトコル、送信元アドレス/ポート、送信先アドレス/ポート、アクションが定義できます。
 ただし、定義された優先順で処理される、アドレス/ポートの変数が定義/参照できない、カスタムメトリクス/TCPフラグのオプションがあるなどの違いがあります。

まとめ

  • AWS Network Firewallの構成パターンは、分散型/集約型デプロイモデルがある。
  • AWS Network Firewallのフィルタ機能はステートフル/ステートレスルールがあり、ステートフルルールは標準ステートフル/ドメインリスト/Suricata互換ルールがある。

おわりに

 AWS Network Firewallを設計される方々にとって、本記事がご参考になりますと幸いです。

AWS ALBのサーバ/クライアント認証の処理および実装フロー

はじめに

 証明書は自分で触れる機会が少ないため、勉強がてらAWS ALBのサーバ/クライアント認証について検証してみました。本記事は、検証で整理できた処理および実装フローを記載してます。

処理フロー

 下記のSSLハンドシェイクの流れで、サーバおよびクライアントの真正性を証明します。
その他、暗号化による盗聴の防止や、電子署名による改ざん防止にも有効です。

  1. クライアントからサーバへSSL/TLS通信をリクエスト。
  2. サーバからクライアントへサーバ証明書を送付。
  3. クライアントはルート証明書を用いてサーバ証明書を確認 (サーバ認証)。
  4. クライアントからサーバへクライアント証明書を送付。
  5. サーバはルート証明書を用いてクライアント証明書を確認 (クライアント認証)。
  6. クライアントとサーバ間で共通鍵の鍵交換を実施。
  7. クライアントとサーバ間でSSL/TLS通信を実施。

実装フロー

 下記の構成をベースに、ALBのサーバ認証/クライアント認証を実装するフローを説明します。
処理フローで言うところのサーバはALB、クライアントはUserが該当します。

事前準備

 サーバ認証/クライアント認証の成否を確認するためのWebサーバを構築します。

  1. VPCを作成
    • VPC×1、Private Subnet×2、Public Subnet×2、Route Table×2を作成。
    • Route Tableの1つはPrivate Subnet×2、もう1つはPublic Subnet×2へ関連付け。
  2. EC2 Instanceを作成
    • 下記のユーザデータが設定されたEC2 Instance×2、Security Group×1を作成。
      (NATGWを作成する等し、一時的にインターネットに接続する必要あり。)
#!/bin/bash
yum install httpd -y
systemctl start httpd.service
systemctl enable httpd.service
touch /var/www/html/index.html
echo "web1" | tee -a /var/www/html/index.html

 ※最後の一文は、2つ目のEC2 Instanceでは"web1"を"web2"に変更して下さい。

サーバ認証

 サーバ認証で必要となるドメインや証明書を取得・作成のうえデプロイします。

  1. Route53でホストゾーンを作成
Route53 画面
ドメイン管理サービス画面


  1. ACMサーバ証明書を作成
    • 証明書をインポートする場合
      • Linux上で下記のコマンドを実行し、秘密鍵/証明書、CSRファイルを作成。作成中に求められるCommon Nameは取得済みドメイン、それ以外は初期値でOK。
      • 証明書本文は、証明書の中身を転記。
      • 証明書のプライベートキーは、秘密鍵の中身を転記。
      • その他は、デフォルトのままでOK。
# 秘密鍵を作成
openssl genrsa -out private.key
# CSRファイルを作成
openssl req -new -key private.key -out server.csr
# 証明書を作成
openssl x509 -req -days 365 -in server.csr -signkey private.key -out server.crt
  • ブラウザがGoogle Chromeの場合、ERR_CERT_AUTHORITY_INVALIDメッセージで認証が失敗しますが、Subject Alternative Nameパラメータで解決します。
# 秘密鍵を作成
openssl genrsa -out private.key
# CSRファイルを作成
openssl req -new -key private.key -out server.csr
# EXTファイルを作成
echo "subjectAltName = DNS:<取得済みドメイン>" > san.txt
# 証明書を作成
openssl x509 -req -days 365 -in server.csr -signkey private.key -out server.crt -extfile san.txt
  • 証明書をリクエストする場合
    • 証明書タイプは、パブリック証明書を選択。
    • ドメイン名は、取得済みドメインを入力。
    • 検証方法は、DNS検証を選択。
    • その他は、デフォルトのままでOK。
    • サーバ証明書が作成できたら、ドメイン所有者を確認するためRoute53にCNAMEレコードを登録。
ACM 画面
Route53 画面


  1. ALBを作成
    • ターゲットグループを作成。
      • ターゲットタイプは、インスタンスを選択。
      • ターゲットグループ名は、任意の文字列を入力。
      • ターゲットのプロトコルはHTTPを選択し、ポートは80を入力。
      • VPCは、作成済みVPCを選択。
      • ターゲットは、作成済みEC2 Instance×2を選択。
      • その他は、デフォルトのままでOK。
    • ロードバランサ―名は、任意の文字列を入力。
    • スキームは、インターネット向けを選択。
    • VPCは、作成済みVPCを選択。
    • サブネットは、作成済みPublic Subnetを選択。
    • セキュリティグループは、作成済みSecurity Groupを選択。
    • リスナーのプロトコルHTTPSを選択し、ポートは443を入力。
    • デフォルトアクションは作成済みターゲットグループを選択。
    • デフォルトSSL/TLSサーバ証明書は、作成済みサーバ証明書を選択。
    • その他は、デフォルトのままでOK。
    • ALBが作成できたら、ALBのDNSをALIASレコードとして、Route53にを登録。
ALB 画面
Route53 画面


  1. IGWを作成
    • IGWを作成し、作成済みVPCにアタッチ。
    • Public Subnetへ関連付けたRoute Tableで0.0.0.0/0のターゲットとしてIGWを登録。
    • Private Subnetへ関連付けたRoute Tableで0.0.0.0/0のターゲットとしてALBを登録。
  1. サーバ認証を確認
    • 証明書をリクエストした場合
      • ブラウザで「https://<取得済みドメイン>/」へアクセス。
      • 保護された通信で「web1」もしくは「web2」を表示。
  • 今回のような自己証明書の場合、下記のような画面が表示されることがありますが、詳細情報を表示のうえ、取得済みドメインにアクセスするを選択。

クライアント認証

 クライアント認証で必要となる証明書を作成のうえデプロイします。

  1. S3 Bucketを作成
    • Linux上で下記のコマンドを実行し、ルート/クライアントの秘密鍵/証明書、CSRファイルを作成。作成中に求められるCommon Nameは取得済みドメイン、それ以外は初期値でOK。
    • バケット名は、任意の文字列を入力。
    • バケットのバージョニングは、有効を選択。
    • S3 Bucketが作成できたら、CAバンドル (複数のルート/中間証明書を含むファイルですが、今回はルート証明書のみ) をアップロード。
# ルート秘密鍵を作成
openssl genrsa -out root.key
# クライアント秘密鍵を作成
openssl genrsa -out client.key
# CSRファイルを作成
openssl req -new -key client.key -out client.csr
# ルート証明書を作成
openssl req -new -x509 -days 365 -key root.key -out root.pem
# クライアント証明書を作成
openssl x509 -req -days 365 -in client.csr -CA root.pem -CAkey root.key -set_serial 01 -out client.pem
  1. ALBを修正
    • トラストストアを作成。
      • トラストストアの名前は、任意の文字列を入力。
      • 認証局バンドルは、作成済みS3 Bucketにアップロード済みCAバンドルを選択。
      • その他は、デフォルトのままでOK。
    • 相互認証 (mTLS) は、チェックを付けたうえでトラストストアで検証を選択。
    • トラストストアは、作成済みトラストストアを選択。
    • その他は、デフォルトのままでOK。
  1. クライアント認証を確認
    • Linux上で下記のコマンドを実行し、「web1」もしくは「web2」を表示。
curl --key client.key --cert client.pem https://<取得済みドメイン>.com/
web1

まとめ

 AWS ALBのサーバ認証/クライアント認証の処理および実装フローを紹介しました。

おわりに

 AWS ALBのサーバ認証/クライアント認証を行われる方にとって参考になりますと幸いです。

AWS Cloud WANを用いたハイブリッド接続のBGPルーティング

はじめに

 前回記事の「AWS ハイブリッド接続のBGPパラメータ設計のポイント」を投稿後、公式ブログでも「Advanced hybrid routing scenarios with AWS Cloud WAN and AWS Direct Connect」のタイトルで、Cloud WAN+ハイブリッド接続のネタが投稿されたようです。

 Cloud WANはTransitGatewayで事足りるため、馴染みが薄いという方も多いのではないでしょうか。本記事では、前回記事のポイントを押さえつつ「AWS Cloud WANを用いたハイブリッドクラウド接続のBGPルーティング」について説明していきたいと思います。

yamamototis1105.hatenablog.com aws.amazon.com

Cloud WANとは?

 Cloud WANとは「各リージョンのVPCやオンプレミスを相互接続するためのマネージドなグローバルネットワークサービス」と言い表せるかと思います。
 冒頭で記載の通り、TransitGatewayで事足りることも多いと思いますが、個人的に気になった観点を取り入れたメリデメ表を作成してみました。

  • コストは、東京リージョンの1リソースあたりの料金であり、データ伝送の料金を含みません。
  • ロンチ時間は、コンソールでの実測値ですが、参考値としてお捉え下さい。
TransitGateway Cloud WAN
メリット
  • コストがCloud WANよりは安い
    • TGWアタッチメント:$0.07 / 時間
  • ロンチがCloud WANよりは早い
    • TGW作成:1分未満
    • Peeringアタッチメント作成:約1~2分
    • VPCアタッチメント作成:約1~2分
  • Connectアタッチメントがトンネルレス可
    • トンネルを作成する必要がなく、対向のデバイス設定がシンプル
  • すべてのアタッチメントがルート伝播可
    • VPNVPC、Conneect、TGWアタッチメントともルート伝播可
デメリット
  • Peeringアタッチメントがルート伝播不可
    • VPNVPC、Connect、DXGWアタッチメントはルート伝播可
    • 静的ルートは設定できるが、障害発生時に切り替える信頼性設計が複雑となる
  • DXGWアタッチメントがない
    • TGW経由で接続する必要があり、構成が複雑化かつコストが高い
  • コストが高い
    • コアネットワークエッジ(CNE):$0.50 / 時間
    • アタッチメント:$0.09 / 時間
  • ロンチが遅い
    • グローバルネットワーク作成:1分未満
    • コアネットワーク作成:約13~14分
    • ポリシー編集:1分未満
    • TGW作成:1分未満
    • ピアリング作成:約10~11分
    • アタッチメント作成:約5~6分

 Cloud WANは各リージョンのCNE間でルート伝播できたり、ConnectアタッチメントでトンネルレスでBGP接続できるシンプルさが売りだと思います。
 ただし、複数セグメントでアクセス制御したり、複雑な条件のアタッチメントポリシーを適用するとそのシンプルさが失われてしまうのでご注意下さい。

 個人的には、2リージョン間接続であればTransitGateway、数リージョン間接続であればCloudWANを検討するなどの使い分けかなと思います。

BGPルーティング

 以下の構成をベースに、Cloud WANを用いたハイブリッド接続のBGPルーティングを説明します。
 構成のポイントとしては、AWS側のサーバは東京・大阪リージョンのVPC間で冗長化されており、各VPCとオンプレミスがCloud WANで接続されています。

1. BGPトポロジ

 BGPスピーカーにASNを設定し、BGPピアリングを構成します。

  • ASNは64512~65535の範囲より、CNE、TGW、DXGW、ユーザ機器に異なる値を設定してeBGPを構成し、冗長化されたユーザ機器に同じ値を設定してiBGPを構成します。
  • 東京・大阪リージョンのCNEのASNは、ポリシーにより動的に割り当てられます。
  • 下図のサンプル設定では、ASNは東京リージョンのCNEに65001、大阪リージョンのCNEに65002、TGWに64512、DXGWに64513、ユーザ機器に65000を割り当てています。

2. BGPメトリック

 BGPピアリングごとのメトリックを設定し、BGPルーティングを制御します。

AWSからオンプレミスへの通信
  • CNE~DXGW間はAWSリソースへ設定されたAS Pathでルート制御し、DXGW~ユーザ機器間はユーザ機器へ設定されたAS Pathでルート制御します。
  • 下図のサンプル設定では、CNE~DXGW間のAS Pathはメインルートが64513 64512 65001の合計3パス、バックアップルートが64513 64512 65002 65001の合計4パスとなります。
  • 下図のサンプル設定では、DXGW~ユーザ機器間のAS Pathはメインルートが65000 64513の合計2パス、バックアップルートが65000 65000 64513の合計3パスとなります。
    (バックアップルートの65000 65000は、バックアップルートのユーザ機器で2パス追加するように設定します。ちなみに、メインルートとバックアップルートのユーザ機器間はiBGP接続のため、各ユーザ機器で1パスずつ追加することはできません。)
オンプレミスからAWSへの通信
  • ユーザ機器~DXGW間はユーザ機器へ設定されたLocal Preferenceでルート制御し、DXGW~CNE間はAWSリソースへ設定されたAS Pathでルート制御します。
  • 下図のサンプル設定では、ユーザ機器~DXGW間のLocal Preferenceはメインルート200、バックアップルート100となります。
  • 下図のサンプル設定では、DXGW~CNE間のAS Pathはメインルートが65001 64512 64513の合計3パス、バックアップルートが65001 65002 64512 64513の合計4パスとなります。

3. BGPタイマー

 BGPピアリングごとのタイマーなどを設定し、BGPフェイルオーバータイムを制御します。

  • DXGW~ユーザ機器間はユーザ機器へ設定されたタイマーでフェイルオーバータイムを制御し、CNE~TGW~DXGW間はAWS内のタイマーでフェイルオーバータイムを制御します。
  • AWS内のタイマーは開示されていないため、フェイルオーバータイムは確認できません。
  • 下図のサンプル設定では、BGP keepalive/hold timeは10s/30s、BFD interval/multiplierは300ms/3をユーザ機器に設定しています。




2021年9月2日 AWS 東京リージョン DirectConnect障害への有効性

 だいぶ風化してそうですが、2021年9月2日 AWS 東京リージョン DirectConnect障害[1] に対しても、本構成でバックアップのDirectConnectへフェイルオーバーすることが有効策となりえます。
 ただし、本障害は完全な切断へ至らずバタつくため、バックアップのDirectConnectへ手動フェイルオーバーする必要があります。

 [1] 障害の詳細については、ServerWorksさんのブログが大変分かり易いため、そちらをご参照下さい。
   ServerWorks - ENGINEER BLOG
     https://blog.serverworks.co.jp/dx-manual-failover-to-vpn

まとめ

 Cloud WANを用いたAWSハイブリッド接続のBGPパラメータも、以下の切り口で考えると理解しやすいです。

  • BGPスピーカーにASNを設定し、BGPピアリングを構成します。
  • BGPピアリングごとのメトリックを設定し、BGPルーティングを制御します。
  • BGPピアリングごとのタイマーなどを設定し、BGPフェイルオーバータイムを制御します。

おわりに

 Cloud WANを用いたハイブリッド接続を構築される方々にとって、本記事がご参考になりますと幸いです。

AWS ハイブリッド接続のBGPパラメータ設計のポイント

はじめに

 昨年ご紹介したAWS DirectConnectパートナーのチェックポイントですが、BGPパラメータあたりがフワッとした書きっぷりでしたので、実際どう設計するか整理してみました。
 本記事は、AWSハイブリッド接続のBGPパラメータ設計で必要な情報を纏めています。

  • 本記事で説明すること
    • DirectConnect(PrivateVIF/TransitVIF)上のBGPパラメータ設計
    • Site-to-Site VPN上のBGPパラメータ設計
      ※ Site-to-Site VPN over DirectConnect(TransitVIF/PublicVIF)もコチラへ含まれます。
  • 本記事で説明しないこと
    • DirectConnect(PublicVIF)上のBGPパラメータ設計
    • TGW Connectアタッチメント上のBGPパラメータ設計
    • 仮想ルータ~オンプレミスルータ間のBGPパラメータ設計
    • インターネット上のBGPパラメータ設計
      ※ DirectConnect(PublicVIF)上のBGPパラメータ設計は私が利用しないので割愛します汗
yamamototis1105.hatenablog.com

BGPとは

 「IPネットワーク上で通信するために必要なルート情報を動的に更新するプロトコルの一つ」とだけ覚えとけば、AWS利用上で困らないと思います(NEの風上にも置けぬ奴と言われそうですが...汗)。
公式サイトで詳しくは説明されていますので、ご興味がある方は是非ご覧下さいませ。

aws.amazon.com

BGPパラメータ設計

 BGPトポロジ、BGPメトリック、BGPタイマーの切り口で考えると理解しやすいかなと思います。こちらの切り口で、DirectConnect冗長構成(パートナー機器なし)、DirectConnect冗長構成(パートナー機器あり)、DirectConnect/Site-to-Site VPN冗長構成の各パターンを見ていきたいと思います。

1. BGPトポロジ

 BGPスピーカーにASNを設定し、BGPピアリングを構成します。

DirectConnect冗長構成 (パートナー機器なし)
 TGWのASNは64512、DXGWのASNは64513、ユーザ機器のASNは65000を割り当てています。
DirectConnect冗長構成 (パートナー機器あり)
 パートナー機器のASNはパートナー側で固定化されています。
単一ASNだけでなく複数ASNを経由する場合もあり、パートナーへ確認する必要があります。
DirectConnect/Site-to-Site VPN冗長構成
 Site-to-Site VPNはデフォルトでBGPピアリング over VPNトンネルが冗長化されています。
それ以外、ASNの割り当て方はDirectConnectと変わりありません。

2. BGPメトリック

 BGPピアリングごとのメトリックを設定し、BGPルーティングを制御します。

DirectConnect冗長構成 (パートナー機器なし)
 AS Pathは、メインのユーザ機器に65000、バックアップのユーザ機器に65000 65000を設定し、メインが64513、65000の合計2パス、バックアップが64513、65000、65000の合計3パスになり、AWS→オンプレミス通信はメインパスが優先されます。
 Local Preferenceは、メインのユーザ機器に200、バックアップのユーザ機器に100を設定し、オンプレミス→AWS通信はメインパスが優先されます。
DirectConnect冗長構成 (パートナー機器あり)
 AS Pathは、メインのユーザ機器に65000、バックアップのユーザ機器に65000 65000を設定し、メインが64513、XXXXX、65000の合計3パス、バックアップが64513、YYYYY、65000、65000の合計4パスになり、AWS→オンプレミス通信はメインパスが優先されます。
 Local Preferenceは、メインのユーザ機器に200、バックアップのユーザ機器に100を設定し、オンプレミス→AWS通信はメインパスが優先されます。
DirectConnect/Site-to-Site VPN冗長構成
 Site-to-Site VPN向けルートよりDirect Connect向けルートの方が優先されるため、AS Pathは設定しなくてもAWS→オンプレミス通信はメインパスが優先されます。
 Site-to-Site VPNよりDirect Connectの方がデフォルトMEDが小さいため、Local Preferenceは設定しなくてもオンプレミス→AWS通信はメインパスが優先されます。

3. BGPタイマー

 BGPピアリングごとのタイマーなどを設定し、BGPフェイルオーバータイムを制御します。

DirectConnect冗長構成 (パートナー機器なし)
 タイマー10s/30s、BFD300ms/3回をユーザ機器に設定します。
DirectConnect冗長構成 (パートナー機器あり)
 AWS~パートナー機器間のBGPフェイルオーバータイムは、パートナー機器のタイマーBFDの設定次第のため、パートナーに確認する必要があります。
DirectConnect/Site-to-Site VPN冗長構成
 BFDは回線品質が安定しないSite-to-Site VPNに設定することを推奨しません。
それ以外、タイマーの設定方法はDirectConnectと変わりありません。

まとめ

 AWSハイブリッド接続のBGPパラメータは、以下の切り口で考えると理解しやすいです。

  • BGPスピーカーにASNを設定し、BGPピアリングを構成します。
  • BGPピアリングごとのメトリックを設定し、BGPルーティングを制御します。
  • BGPピアリングごとのタイマーなどを設定し、BGPフェイルオーバータイムを制御します。

おわりに

 AWS ハイブリッド接続のBGPパラメータを設計される方々にとって、本記事がご参考になりますと幸いです。

AWS Site-to-Site VPNトンネルエンドポイントのライフサイクル制御機能によるメンテナンステスト

はじめに

 昨年度リリースされたSite-to-Site VPNトンネルエンドポイントのライフサイクル制御機能ですが、単なるメンテナンス通知・表示・実行の機能でなく、メンテナンステスト機能としての有効性を伝えるために記事を書きました。
(JAWSUG発表内容と被りますが、検証だけでなく事例も書き加えてます。)

aws.amazon.com

機能概要

 2023/3/30に発表された機能で、Site-to-Site VPN のメンテナンス情報が可視化されて制御できるようになりました。主要な機能は以下1~3です。

  1. メンテナンスの対象VPN接続や日時をユーザにメールで通知
  2. 保留中のメンテナンス有無、自動適用日、最終適用日を表示
  3. ユーザの好きなタイミングでメンテナンスを実行 (VPNトンネル置き換え)

機能詳細

 メンテナンスが計画された状態で、(1)「ルーティングされていないVPNトンネルから置き換え」(2)「ルーティングされているVPNトンネルから置き換え」を実施した結果をまとめてみました。

(1) ルーティングされていないVPNトンネルから置き換え

 メンテナンス済みのトンネル向けのMED値を下げ、メンテナンス済みのトンネルにルーティングするため、通信影響を与えません。

(2) ルーティングされているVPNトンネルから置き換え

 ルーティングされているトンネルを置き換えようとしても、他トンネルにルーティングしてからトンネルを置き換えるため、通信影響を与えません。

注意事項

 VGW(TGW)でIN方向ルーティング制御のためMED値が設定され、CGWでOUT方向ルーティング制御のためWeight値やLocalPreference値を設定した場合、前者より後者の方が優先されます。
 そのため、Weight値やLocalPreference値を設定しない、もしくは同じ値を設定しなければ、メンテナンス時におけるルーティング自動変更が機能せず、通信影響を与えることになります。

 公式サイトのルーティング注記でも、「MED の低いアップトンネルが優先されるようにするには、カスタマーゲートウェイバイスで、両方のトンネルに対して同じ重みおよびローカル優先設定の値が使用されていることを確認します」と記載されています。

docs.aws.amazon.com

ユースケース

 一見、通信影響を与えないようにメンテナンスできるからありがたい!と思われるかもしれません。
 実は、メンテナンスの自動適用日まで放ったらかしでも、本機能と同様の動作で通信影響を与えずに自動的にメンテナンスされるため、敢えてユーザがメンテナンスしたいケースは絞られると思います。

 具体的なユースケースとしては、(1)「ユーザの好きなタイミングでメンテナンスしたいケース」(2)「Site-to-Site VPNの疑似メンテナンステストを事前に実施したいケース」などが考えられます。
 個人的には、(2) の使い方が大変有用だと思っています。

(1) ユーザの好きなタイミングでメンテナンスしたいケース

 前述の通り、メンテナンスが通信影響を与えないため、敢えてユーザがメンテナンスのタイミングを制御したいケースは少ないかと思いますが、以下1~3が挙げられます。

  1. VPNトンネルが単一構成で、メンテナンス時に通信断が発生するため、通信断が許容できるタイミングでメンテナンスしたいケース(公式サイト記載ユースケース
  2. VPNトンネルの状態変化によって、監視アラームが鳴動するなどの影響を受けるため、VPNトンネルの状態変化のタイミングを指定したいケース(公式サイト記載ユースケース
  3. VPNトンネルは冗長構成だが、万が一ルーティング自動変更が失敗する可能性を考慮し、通信断が許容できるタイミングでメンテナンスしたいケース

(2) 「Site-to-Site VPNのメンテナンステストを実施したいケース」

 メンテナンス時におけるルーティング自動変更は、Site-to-Site VPNを作成するだけで動作するわけでなく、CGWのBGPパラメータによっては上手く動作しない可能性があります。
 今まで疑似的にメンテナンスを発生させられず、パラメータの妥当性をテストできませんでしたが、本機能によってテストできるようになりました(本機能はメンテナンス有無問わず実行可)
 障害時に備えたテストは大切ですが、その他の運用中に起こりうる動作・状態を確認する意味では、こうしたメンテナンス時に備えたテストも大切かと思います。

 有効性が確認できた一例として、メンテナンスの通信影響を与えないようにするため、冗長化されたトンネルのWeight値を違う値から同じ値に変更した場合における事例を紹介します。
 Weight値を変更すること自体は難しいことではありませんが、「本当にメンテナンスが生じた場合に通信影響を与えないだろうか?」といった不安の声もありました。
 その際には、以下1~3の流れで進めることでWeight値の変更妥当性を説明することができました。

  1. Weight値が違う状態でトンネルを置き換え、通信影響を与えることを確認する。
  2. Weight値を違う値から同じ値に変更する。
  3. Weightが同じ状態でトンネルを置き換え、通信影響を与えないことを確認する。

利用手順

 詳細な手順は公式サイトをご覧いただければと思います。本記事ではスクリーンショットと合わせて簡易な手順のみ載せていますので雰囲気を感じる程度にご覧ください。

docs.aws.amazon.com

トンネルエンドポイントのライフサイクル制御の有効化

 Site-to-Site VPN画面にて「アクション」から「VPNトンネルオプションを変更」を選択のうえ、 「トンネルエンドポイントのライフサイクル制御」の「有効にする」をチェックしてください。
※有効化・無効化ともトンネル切断が発生するため注意してください。

メンテナンス情報の表示

 Site-to-Site VPN画面にて保留中のメンテナンス有無、自動適用日、最終適用日など確認できます。 保留中のメンテナンスがある場合、「利用可能」と表示されています。

VPNトンネルの置き換え

 Site-to-Site VPN画面にて「アクション」もしくは「利用可能」から「VPNCトンネルを置き換え」を選択のうえ、 「VPNトンネル外部IPアドレス」から対象トンネルを選択してください。
※トンネル置き換え時はトンネル切断が発生するため注意してください。

まとめ

  • Site to Site VPNトンネルエンドポイントのライフサイクル制御機能を有効化することによって、メンテナンスを通知・表示・実行することができます。
  • 注意事項としては、メンテナンスの通信影響を与えないようにするため、CGWでWeight値やLocalPreferencwe値は設定しない、もしくは同じ値にする必要があります。
  • ユースケースとしては、ユーザが好きなタイミングでメンテナンスしたいケースのほか、メンテナンステストを実施したいケースで有効だと考えられます。

おわりに

 Site-to-Site VPNトンネルエンドポイントのライフサイクル制御機能をご利用される方々にとって、本記事の機能・注意事項・ユースケースがご参考になりますと幸いです。

AWS TGW Connectアタッチメント接続のCFnカスタムリソース作成

はじめに

 ここ最近、TGW Connectアタッチメント接続するための検証環境を構築することが多々ありますが、この度CFn提供待ちに痺れを切らして、カスタムリソースを作ったので本記事を書きました。
 カスタムリソースは可読性や保守性を考えると使いどころが難しいですが、もし作成される場合には本記事を一例としてご参考にしていただけますと幸いです。

TGW Connectアタッチメント接続構成

 TGW~仮想アプライアンスをBGP over GRE接続する場合、Connectアタッチメントを利用します。Connectアタッチメントは、以下の流れで作成します。

  1. TGW~VPC(仮想アプライアンスあり)をVPCアタッチメントで接続する
  2. トランスポートアタッチメント(GREトンネルアドレスをルーティングするため)としてVPCアタッチメントを指定し、Connectアタッチメントを作成する
  3. さらにConnectアタッチメント内にConnectピアを作成し、GREおよびVPNを定義する

 以下の構成を作成する場合、TGW、TGWルートテーブル、VPCアタッチメント、ConnectアタッチメントまでCFnカバーされていますが、ConnectピアのみCFnカバーされていない状況です。
 ちなみに、VPCアタッチメントはGREトンネルアドレスをルーティングするためにAttachする必要がありますが、Association & Propagationする必要はありません。



 CFnロードマップ上、2022.06にカバレッジラベル付与、2023.03にResearching遷移、2023.08にComming Soon遷移、と間も無くカバーされる気もしますが、コレばかりは何とも言えないですね...

github.com github.com

カスタムリソースの概要

 それでは、どのようにCFnカバーされていないリソースを構築自動化するのでしょうか。
 CFnはLambdaやSNSを呼び出すカスタムリソースという機能があり、呼び出されたLambdaやSNS自由度の高いロジックを実装することが可能となっています。
 例えば、CFnカバーされていないConnectピアを作成するLambdaを作成し、カスタムリソースからLambdaを呼び出すといった使い方も考えられます。


 一見すると便利そうですが、IaCの優位点である可読性や保守性が下げてしまう可能性もあります。個人的には、カスタムリソース検討時に以下のチェックポイントで確認するように心掛けてます。

  • 更新頻度はほぼなく繰り返し利用するか
  • サービス提供するため利用者の作業負担を抑えたいか
  • 部分的手動やshell+aws cliで代替できないか

 その他のカスタムリソースに関する詳細な情報は、CloudFormationのユーザガイドをご参照下さい。個人的には、MarketplaceでAMIと一緒に提供されているテンプレートを読む等も勉強になりました。

docs.aws.amazon.com

カスタムリソースの作成

 それでは、以下のミニマム構成(VPC、TGW、EC2、カスタムリソース)を作成していきます。
カスタムリソースは作成時のみ実装し、更新時・削除時が実装できていないためご留意下さい。

VPC

 VPC、Subnet、RouteTableなどを作成します。Route宛先のVPCアタッチメント作成は時間がかかるため、DependsOnにVPCアタッチメントを設定し待機させます。

{
  "Resources" : {
    "Vpc" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : "10.0.0.0/16",
        "Tags" : [ { "Key" : "Name", "Value" : "vpc" } ]
      }
    },
    "VpcSubnet" : {
      "Type" : "AWS::EC2::Subnet",
      "Properties" : {
        "VpcId" : { "Ref" : "Vpc" },
        "CidrBlock" : "10.0.1.0/24",
        "AvailabilityZone" : { "Fn::Select": [ 0, { "Fn::GetAZs": "" } ] },
        "Tags" : [ { "Key" : "Name", "Value" : "vpc-subnet" } ]
      }
    },
    "VpcRtb" : {
      "Type" : "AWS::EC2::RouteTable",
      "Properties" : {
        "VpcId" : { "Ref" : "Vpc" },
        "Tags" : [ { "Key" : "Name", "Value" : "vpc-rtb" } ]
      }
    },
    "VpcSubnetRouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "VpcSubnet" },
        "RouteTableId" : { "Ref" : "VpcRtb" }
      }
    },
    "VpcRtbRoute" : {
      "Type": "AWS::EC2::Route",
      "Properties": {
        "RouteTableId" : { "Ref" : "VpcRtb" },
        "DestinationCidrBlock" : "192.168.1.0/24",
        "TransitGatewayId" : { "Ref" : "Tgw" }
      },
      "DependsOn" : "TgwVpcAttach"
    }
  }
}

TGW

 TGW、RouteTable、VPCアタッチメント、Connectアタッチメントなどを作成します。前述の通り、VPCアタッチメントはAssociation & Propagationしないようにします。

{
  "Resources" : {
    "Tgw" : {
      "Type": "AWS::EC2::TransitGateway",
      "Properties": {
        "AmazonSideAsn" : 64512,
        "DefaultRouteTableAssociation" : "disable",
        "DefaultRouteTablePropagation" : "disable",
        "TransitGatewayCidrBlocks" : [ "192.168.1.0/24" ],
        "Tags" : [ { "Key" : "Name", "Value" : "tgw" } ]
      }
    },
    "TgwRtb" : {
      "Type" : "AWS::EC2::TransitGatewayRouteTable",
      "Properties" : {
        "TransitGatewayId" : { "Ref" : "Tgw" },
        "Tags" : [ { "Key" : "Name", "Value" : "tgw-rtb" } ]
      }
    },
    "TgwVpcAttach" : {
      "Type": "AWS::EC2::TransitGatewayAttachment",
      "Properties": {
        "SubnetIds": [ { "Ref" : "VpcSubnet" } ],
        "TransitGatewayId": { "Ref" : "Tgw" },
        "VpcId": { "Ref" : "Vpc" },
        "Tags" : [ { "Key" : "Name", "Value" : "tgw-vpc-attach" } ]
      }
    },
    "TgwConnectAttach" : {
      "Type": "AWS::EC2::TransitGatewayConnect",
      "Properties": {
        "Options" : { "Protocol" : "gre" },
        "TransportTransitGatewayAttachmentId": { "Ref" : "TgwVpcAttach" },
        "Tags" : [ { "Key" : "Name", "Value" : "tgw-connect-attach" } ]
      }
    },
    "TransitGatewayRouteTableAssociation" : {
      "Type": "AWS::EC2::TransitGatewayRouteTableAssociation",
      "Properties": {
        "TransitGatewayAttachmentId" : { "Ref" : "TgwConnectAttach" }, 
        "TransitGatewayRouteTableId" : { "Ref" : "TgwRtb" }
      }
    },
    "TransitGatewayRouteTablePropagation" : {
      "Type": "AWS::EC2::TransitGatewayRouteTablePropagation",
      "Properties": {
        "TransitGatewayAttachmentId" : { "Ref" : "TgwConnectAttach" }, 
        "TransitGatewayRouteTableId" : { "Ref" : "TgwRtb" }
      }
    }
  }
}

EC2

 EC2、ENI、SecurityGatewayなどを作成します。EC2はMarketplaceからCatalyst 8000Vを起動し、ユーザーデータでGREおよびBGP設定を行います。

{
  "Resources" : {
    "RouterSg" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupName" : "router-sg",
        "GroupDescription" : "router-sg",
        "VpcId" : { "Ref" : "Vpc" },
        "SecurityGroupIngress" : [ { "IpProtocol" : "-1", "FromPort" : "-1",  "ToPort" : "-1",  "CidrIp" : "10.0.0.0/8" } ],
        "SecurityGroupEgress" : [ { "IpProtocol" : "-1", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : "0.0.0.0/0" } ],
        "Tags" : [ { "Key" : "Name", "Value" : "router-sg" } ]
      }
    },
    "RouterEni" : {
      "Type" : "AWS::EC2::NetworkInterface",
      "Properties" : {
        "GroupSet" : [ { "Ref" : "RouterSg" } ],
        "SubnetId": { "Ref" : "VpcSubnet" },
        "PrivateIpAddress" : "10.0.1.254",
        "SourceDestCheck" : "false",
        "Tags" : [ { "Key" : "Name", "Value" : "router-eni" } ]
      }
    },
    "Router" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "InstanceType" : "t3.medium",
        "ImageId" : { "Fn::FindInMap" : [ "RouterAmi", { "Ref" : "AWS::Region" }, "AmiId" ] },
        "NetworkInterfaces": [ { "DeviceIndex": "0", "NetworkInterfaceId": { "Ref" : "RouterEni" } } ],
        "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
          "ios-config-1=\"interface GigabitEthernet1\"\n",
          "ios-config-2=\" ip address 10.0.1.254 255.255.255.0\"\n",
          "ios-config-3=\"interface Tunnel1\"\n",
          "ios-config-4=\" ip address 169.254.6.1 255.255.255.248\"\n",
          "ios-config-5=\" tunnel source GigabitEthernet1\"\n",
          "ios-config-6=\" tunnel destination 192.168.1.1\"\n",
          "ios-config-7=\"ip route 192.168.1.0 255.255.255.0 10.0.1.1\"\n",
          "ios-config-8=\"router bgp 65000\"\n",
          "ios-config-9=\" timer bgp 10 30\"\n",
          "ios-config-10=\" neighbor 169.254.6.2 remote-as 64512\"\n",
          "ios-config-11=\" neighbor 169.254.6.2 ebgp-multihop\"\n",
          "ios-config-12=\" neighbor 169.254.6.3 remote-as 64512\"\n",
          "ios-config-13=\" neighbor 169.254.6.3 ebgp-multihop\"\n"
        ] ] } },
        "Tags" : [ { "Key" : "Name", "Value" : "router" } ]
      }
    }
  }
}

カスタムリソース

 Lambda、Role、カスタムリソースなどを作成します。LambdaはBoto3でConnectピアを作成し、Roleは最小権限の原則よりCloudWatchログ出力、Connectピア作成のみ認可します。

{
  "Resources" : {
    "TgwConnectPeerCreatorRole" : {
      "Type" : "AWS::IAM::Role",
      "Properties" : {
        "AssumeRolePolicyDocument" : {
          "Version": "2012-10-17",
          "Statement": [ {
            "Effect": "Allow",
            "Principal": { "Service" : [ "lambda.amazonaws.com" ] },
            "Action": [ "sts:AssumeRole" ]
          } ]
        },
        "Path" : "/",
        "ManagedPolicyArns" : [ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ],
        "Policies" : [ {
          "PolicyName": "TgwConnectPeerCreatorPolicy",
          "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [ {
              "Effect": "Allow",
              "Action": [ "ec2:CreateTransitGatewayConnectPeer" , "ec2:CreateTags" ],
              "Resource": "*"
            } ]
          }
        } ]
      }
    },
    "FunctionCreateTgwConnectPeer" : {
      "Type" : "AWS::Lambda::Function",
      "Properties" : {
        "Code" : {
          "ZipFile" : { "Fn::Join": [ "", [
            "import json\n",
            "import boto3\n",
            "import cfnresponse\n",
            "def lambda_handler(event, context):\n",
            "  try:\n",
            "    client = boto3.client('ec2')\n",
            "    response = client.create_transit_gateway_connect_peer(\n",
            "      TransitGatewayAttachmentId='" , { "Ref" : "TgwConnectAttach" } , "',\n",
            "      TransitGatewayAddress='192.168.1.1',\n",
            "      PeerAddress='10.0.1.254',\n",
            "      BgpOptions={'PeerAsn':65000},\n",
            "      InsideCidrBlocks=['169.254.6.0/29'],\n",
            "      TagSpecifications=[{\n",
            "        'ResourceType':'transit-gateway-connect-peer',\n",
            "        'Tags':[{'Key':'Name','Value':'tgw-connect-attach-peer'}],\n",
            "      }],\n",
            "      DryRun=False\n",
            "    )\n",
            "    cfnresponse.send(event, context, cfnresponse.SUCCESS, {})\n",
            "  except Exception as e:\n",
            "    cfnresponse.send(event, context, cfnresponse.FAILED, {})"
          ] ] }
        },
        "Handler": "index.lambda_handler",
        "Role": { "Fn::GetAtt" : [ "TgwConnectPeerCreatorRole", "Arn" ] },
        "Runtime": "python3.9",
        "Timeout": 60
      }
    },
    "CustomCreateTgwConnectPeer" : {
      "Type" : "Custom::FunctionCreateTgwConnectPeer",
      "Properties" : {
        "ServiceToken" : { "Fn::GetAtt" : [ "FunctionCreateTgwConnectPeer", "Arn" ] }
      }
    }
  }
}

つまづきポイントと解決方法

 テンプレートは直ぐに作成できたわけでなく、スタック作成・更新・削除を繰り返し試行することで作成できました。ここでは自らのつまづきポイントと解決方法をご紹介します。

CloudFormation/Lambdaのコーディング

 今日びググったら多くのサンプルを手に入れることは容易いですが、マイナーな設定と遭遇した場合は王道な調査方法が分かってないと立往生します。
 苦労したポイントは、CFnのリソースの構造、IAMポリシーのアクション、Boto3のメソッドの引数あたりですが、下記サイトで解決することができました。

CloudFormation/Lambdaのトラブルシュート

 大抵CloudFormationはCloudFormationイベント、LambdaはCloudWatchログを確認することにより問題判明できてましたが、それでも理解不能なエラーメッセージは出てきます。
 その時に解決の糸口になったのがCloudTrailイベント履歴でした。一つ一つのAPIログが確認でき、CloudWatchログより有益な情報が得られる場合がありました。

まとめ

 Connectピアのカスタムリソースを作成し、Connectアタッチメント接続がフル自動化できました。 カスタムリソースは可読性や運用性が失われる可能性もあるため十分検討したうえでご利用下さい。

 今回ご紹介したコードは下記ページをご参照下さいませ。

github.com

おわりに

 AWS TGW Connectアタッチメント接続に限らずカスタムリソースを利用される方にとって、本記事がご参考になりますと幸いです。