Amazon Web Services ブログ

AWS SAM を使用した Kubernetes のサーバーレスアドミッションウェブフックの構築

著者:

Simon Woldemichael、アソシエイトソリューションアーキテクト、WWPS ソリューションアーキテクチャ

Josh Jiang、アソシエイトクラウド開発者、プロフェッショナルサービス共有配信チーム

学習レベル: 300

Kubernete クラスターでリソースデプロイを制御すると、難しい課題に直面することがあります。たとえば、本番環境に変更をプッシュすると、互換性のないパッケージやサービスをクラッシュさせる脆弱な依存関係をインストールする危険性があります。 Kubernetes のカスタムアドミッションウェブフックを作成すると、規制を厳格に定義し、承認済みリソースをクラスターでのみ起動できます。

次の図は、サンプルのウェブフックのアーキテクチャを示しています。

このブログでは、Kubernetes の開発者とクラスター管理者に向けて、AWS サーバーレスアプリケーションモデル (SAM) を使用してサーバーレスアドミッションウェブフックを作成する方法を解説します。ウェブフックの有用性を示すために、Amazon Elastic Kubernetes Service (EKS) のデプロイを、Amazon Elastic Container Registry (ECR) のイメージに対して検証するようにウェブフックを設定します。

サーバーレスアドミッションウェブフックは、このユースケースに適しています。ですが、Kubernetes リソースの作成、削除、更新に機能を拡張することもできます (Pod など)。最初に、Kubernetes で作成できるウェブフックのタイプについて説明します。次に、構築済みウェブフックをデプロイします。最後に、カスタムウェブフックを作成する方法について説明します。

バックグラウンド

Kubernetes クラスターの動的受付制御

Kubernetes がどのように新しいクラスターリソースを内部で調整するかを理解することは、独自のルールを構築するために重要です。Kubernetes ではいくつかのアドミッションコントローラーを使用して、クラスター内のリソースが特定の予想に一致するようにします。これらのアドミッションコントローラーは、有効な作成、更新、削除操作のみを実行できることを保証します。たとえば、存在しないクラスター名前空間に Deployment を作成しようとすると、NamespaceExists アドミッションコントローラーは作成を拒否します。

Kubernetes バージョン 1.9 以降、カスタムプラグインを作成できる 2 つのコードパッケージ (ValidatingAdmissionWebhookMutatingAdmissionWebhook) が導入されました。これらのプラグインを使用すると、リソースの受付処理に直接統合できます。

ValidatingAdmissionWebhook で、リソースが予想基準に一致するかどうかを検証 できます。たとえば、作成中の Pod には正しいラベルがあり、制限された量の CPU とメモリをリクエストしていますか? そうでない場合は、クラスターへの Pod の受付を拒否することができ、ポッドは作成されません。

MutatingAdmissionWebhook はリソースが特定の基準を満たしているかどうかを検証できるだけでなく、そのリソースを変換または変更して、リソースがクラスターのデータプレーンに入る前に基準を満たすようにすることもできます。たとえば、作成中のIngress リソースは HTTPS のみを適用していますか? そうでない場合、ウェブフックはリソースを変更して変更できるようにします。 詳しく言うと、EKS 用 AWS App Mesh サイドカーインジェクターは、AWS App Mesh を使用して envoy プロキシコンテナを EKS ポッドに挿入して観測可能性とアプリケーションレベルのネットワーキングを行う変換ウェブフックです。EKS のマネージド型 kube-apiserver では、これらのアドミッションコントローラーの両方がデフォルトで有効になっています。

どちらの場合も、Kubernetes API は、本文に AdmissionReview を含むウェブフックに POST リクエストを送信し、ウェブフックも AdmissionReview で応答します。AdmissionReview のタイプにはリクエストフィールドと応答フィールドがあります。受信した AdmissionReview を処理する際に、リクエストを読み取ります。AdmissionReview で応答する場合は、応答を入力し、クラスターが作成した一意の識別子 (UID) を含めます。クラスターはこの UID をバージョン管理メカニズムとして使用します。このクラスターはリクエストに含まれています。

受信レビューの本文には、作成、更新、または削除するオブジェクトの未処理の JSON 仕様が含まれます。結果として、リクエストした API アクションを実行する際に、Kubernetes API が表示するものと同じデータを表示できるようになります。これらの詳細は、ウェブフックを作成する際に重要となります。

ソリューションの概要

ここまで、アドミッションコントローラーの機能、利用可能なウェブフックのタイプ、ValidatingAdmissionWebhookMutatingAdmissionWebhook アドミッションコントローラーがカスタムウェブフックとどのように相互作用を行うかについて説明しました。次に、それらがどのように役立つかを示す例を見てみましょう。

このブログでは、EKS が管理するクラスターを使用しますが、ValidatingAdmissionWebhook アドミッションコントローラーが有効になっている Kubernetes クラスターバージョン 1.9 以降であればどれでも使用可能です。このコントローラーがクラスターで有効になっているかどうかを確認するには、こちらにある公式の Kubernetes ドキュメントにアクセスしてください。

ウェブフックは、Go プログラミング言語で AWS Lambda 関数として実装されています。この関数は ValidatingAdmissionWebhook を実行し、EKS クラスターで作成したすべてのポッドが ECR の有効なコンテナリポジトリからのものであることを確認します。Pods のベースラインのセキュリティ状態が良好かどうかもチェックします。Kubernetes クラスターは、Amazon API Gateway エンドポイントを介してこの関数に接続します。ウェブフックのインフラストラクチャとアクセス許可はすべて、AWS SAM テンプレートで定義されています。

チュートリアル

ここでは、AWS Serverless Application Repository (SAR) から AWS SAM テンプレートを起動して、ウェブフックのサービスをデプロイします。

前提条件

このアーキテクチャを実装するには、以下が必要です。

  • Amazon ECR、AWS Serverless Application Repository、AWS CloudFormation、AWS Lambda、Amazon API Gateway にアクセスできる AWS アカウント
  • Kubernetes クラスター。クラスターのデプロイについては、EKS ワークショップの手順をご参照ください。
  • ウェブフックの設定をデプロイし、Kubernetes クラスターにいくつかのテストアプリケーションを追加するための kubectl
  • サンプル GitHub リポジトリを複製するための git CLI

サーバーレスアドミッションウェブフックのデプロイとテスト

ウェブフックの実装に入る前に、準備したサンプルを AWS SAR から Kubernetes クラスターにデプロイしましょう。

まず、検証ウェブフックを AWS アカウントにデプロイします。これを行うには、下の [スタックの起動] ボタンをクリックして、us-east-2 リージョンの AWS SAR マネジメントコンソールに移動します。

SAR アプリケーションデプロイのリンク

デプロイが完了したら、[CloudFormation スタックの表示] ボタンをクリックして CloudFormation コンソールに移動し、「出力」セクションから WebhookURL スタックの出力値をコピーします。

先ほど起動した Lambda 関数で定義したセキュリティルールが、4 つの点を検証します。これらの基準のいずれかでも満たされない場合、作成するポッドはクラスターへの受付が拒否されます。コンテナイメージは次の条件を満たす必要があります。

  1. ECR から取得している
  2. イメージタグの不変性が有効になっている
  3. プッシュ時のイメージスキャンが有効になっている
  4. ECR 画像スキャンで報告されているような重大なセキュリティの脆弱性がない

次に、API Gateway と Lambda が通信するようにクラスター内の検証ウェブフックアドミッションコントローラーを設定します。

  1. サンプルのウェブフックをローカルマシンに複製し、作業ディレクトリをそこに変更します。
git clone https://github.com/aws-samples/amazon-ecr-repository-compliance-webhook.git && cd amazon-ecr-repository-compliance-webhook
  1. deploy/validatingwebhook.yaml を編集します (API Gateway エンドポイントで webhooks[0].clientConfig.url のキーを更新する)。HTTPS エンドポイントを使用するには、すべてのウェブフックが必要です。デプロイするリソースの名前空間とラベルと一致するように、必要な追加を行います。次に、次のコマンドを実行して ValidatingWebhookConfiguration をデプロイします。
kubectl apply -f deploy/validatingwebhook.yaml

ウェブフックで使用する caBundle は、アマゾン ウェブ サービスが作成および運用する認証局の Amazon Trust Services (ATS) からのものです。これは、PEM 形式のルート証明書の Base-64 エンコーディングで、こちらにあります。この値は変更しないでください。ATS で署名された証明書を信頼していない場合でも、クラスターで API Gateway への安全な接続を確立できます。たとえば、EC2 でセルフマネージドクラスターを操作する場合などです。

次に、ECR の外部 (DockerHub) からコンテナを参照するデプロイ例を使用して、先ほどデプロイしたウェブフックをテストします。

  1. サンプルのデプロイを Kubernetes クラスターに適用します。テストするイメージを選択します。AWS および構成済みリージョンの両方で使用可能な ECR イメージをテストする場合は、ECR リポジトリからノードにプルする権限があることを確認してください。必要に応じて、デプロイの名前空間を変更します。デプロイは deploy/mydeployment.yaml にあります。
kubectl create ns test-namespace && kubectl apply -f deploy/mydeployment.yaml
  1. このデプロイでは、nginx:latest を DockerHub (mydeployment.yaml にある) からプルしようとします。デプロイを AWS アカウント内に存在する ECR イメージに変更し、コンプライアンスもテストします。
  1. デプロイが Kubernetes API に送信されるとすぐに、検証ウェブフックアドミッションコントローラーがウェブフックを呼び出します。ウェブフックはデプロイの本文を解析し、リクエストに含まれるコンテナイメージが指定された 4 つの要件を満たしていることを確認します。イベントがポッドの参加を許可または拒否していることを確認しましょう。
kubectl get events -n test-namespace

作成中の両方の Pods は、デフォルトの nginx:latest 値の場合、ECR ではなく DockerHub から取得されるため、どちらも拒否されます。次のようなイベントが表示されます。

Error creating: admission webhook "admission.ecr.amazonaws.com" denied the request: webhook: no ecr images found in pod specification

ecr-repository-compliance-webhook Lambda 関数が CheckRepositoryCompliance 関数の ECR ではないイメージを拒否するため、デプロイが失敗します。

カスタムウェブフックを作成する予定の場合は、次のコマンドを実行して名前の衝突を回避する前に、この CloudFormation スタックを削除します。

aws --region us-east-2 cloudformation delete-stack --stack-name serverlessrepo-amazon-ecr-repository-compliance-webhook

最後に、Lambda 関数が何を行ってこれを実現するのかを見てみましょう。

実装: ウェブフックの作成

このセクションでは、ウェブフックを開発する方法について説明します。ここでは Go プログラミング言語を使用しますが、Lambda でサポートされている任意の言語やカスタムランタイムも使用できます。開発の一般的な流れは次のとおりです。

  1. ウェブフックのビジネスロジックを記述する
  2. Lambda ハンドラーをセットアップする
  3. AWS SAM で使用するウェブフックをデプロイする

GitHub リポジトリからサンプルのウェブフックを参照することで、これに沿って追うことができます。

1.ウェブフックのビジネスロジックを記述する

Lambda 関数のハンドラーの詳細を説明する前に、そのビジネスロジックを駆動する主なコンポーネントについて解説します。ロジックを 2 つの Go パッケージに分割します。まず、pkg/webhook にはポッド情報の抽出に役立つメソッドとタイプが含まれています。次に、pkg/function は、コンプライアンスのためにポッドのコンテンツを処理し、Lambda 関数のハンドラーを定義します。

この処理を行う際に、pkg/webhook/request.go からデータを追跡します。Lambda 関数が API Gateway によってトリガーされると、AdmissionReviewValidatingWebhookConfiguration アドミッションコントローラーから受け取ります。このため、その AdmissionReview をネイティブの Go タイプ (unmarshalling 呼ばれる処理) に変換することが、この関数が最初に行うことになります。 pkg/webhook/request.go に移動して、実装の詳細を確認しましょう。

pkg/webhook/request.go は、リクエストからデータを処理、検証、抽出するためのエントリポイントです。

デプロイからポッド仕様データを読み取ることができるようになったので、そのコンテナイメージが予想通りかどうかを確認できます。この関数は、ポッド内のコンテナイメージの 4 つの特定の側面を確認することに関連します。しかしながら、ウェブフックはデプロイのあらゆる側面を検査できます。この場合、pkg/function/ecr.go にあるヘルパーを使用して、4 つの要件が満たされているかどうかを確認します。pkg/function/ecr.go 内の関数は ECR API にリクエストを送信して、ECR イメージが存在すること、イメージタグの不変性が有効になっていること、重大なセキュリティの脆弱性が含まれていないことを確認します。

このような関数による確認が完了すると、Lambda 関数は別の AdmissionReview を形成しますが、今回は空の AdmissionRequest と入力済みの AdmissionResponse が含まれています。存在するコンテナイメージが要件を満たしていない場合は、クラスターからのポッドの受付を拒否します。Lambda は pkg/function/middleware.go の WithProxiedResponse ミドルウェア関数を使用して、API Gateway に応答します。

pkg/webhook/response.go は、正しい AdmissionResponse を構造化してクラスターに送り返すために使用されます。

2.Lambda ハンドラーをセットアップする

構成要素を説明したので、それらを使用してハンドラーを作成しましょう。pkg/function/container.go で、タイプコンテナを定義して、Lambda 関数のハンドラーをカプセル化します。これを行うと、main_test.go にあるシンプルなユニットテスト中に、外部の依存関係 (ECR API クライアントなど) のモックを作成しやすくなります。

まず、Lambda 関数のハンドラーが API Gateway リクエストをダイジェストして、前のセクションで説明したメソッドとタイプを使用して AdmissionReview からポッド情報を抽出します。AdmissionRequest (AdmissionReview に埋め込まれた) からポッドのコンテンツを解析し、レビュー内に存在するポッドのコンテナイメージが準拠しているかどうかを判断します。最後に、その判断を API Gateway に返し、ポッドをクラスターに承認または拒否する応答を返します。ハンドラーの実行フローの詳細は、関数の上のドキュメント文字列にあります。

3.AWS SAM で使用するウェブフックをデプロイする

ウェブフックをデプロイする前に、最終的なコマンドをいくつか実行して、作業を簡単にします。開始する前に、以下のことを実行したと想定します。

  • AWS アカウントのリソースにアクセスするために AWS CLI を使用してターミナルで AWS の認証情報を設定済みで、CloudFormation、S3、IAM にアクセスする権限を持っている
  • AWS SAM がパッケージ化されたテンプレートを配置するための S3 バケットを作成済み
  • DEFAULT_REGIONS3_BUCKET 変数 (Makefile の 1 行目と 2 行目にある) を、作成した S3 バケットと一致するように更新済み

ウェブフックをデプロイするには、ターミナルで次のコマンドを実行します。make をインストールしていない場合は、任意のパッケージマネージャーを使用してインストールします。最初のコマンドは、Python を使用して SAM CLI をインストールするものですが、SAM をインストールして使用するようにコマンドを変更できます。Go プログラミング言語もインストールする必要があります。

make install-tools
make

これらのコマンドは、コードのリンティング、テスト、コンパイルを自動化するものです。Makefile で使用する特定のコマンドを確認できます。

次に、AWS SAM テンプレートを使用して、ウェブフックの各コンポーネントを起動します。AWS SAM は、サーバーレスアプリケーションを構成する関数、API、アクセス許可、設定、イベントを記述するためのシンプルかつクリーンな構文を提供します。CloudFormation の機能を拡張するために使用します。指定したリソースは、Lambda 関数のコードをパッケージ化し、API Gateway をプロビジョニングして、対応するロールとアクセス許可を確立します。

CloudFormation and AWS SAM-specific resources present in template.yaml; 4 total.

最後に、次のコマンドを実行して、このテンプレートをリージョンにデプロイします。

make sam-deploy

上記の「サーバーレスアドミッションウェブフックのデプロイとテスト」セクションと同じ手順に従って、ウェブフックが正しく機能していることを確認します。

セキュリティに関する考慮事項

このソリューションを運用環境にデプロイする前に、このアーキテクチャが環境のセキュリティにどのように影響するかを知っておくことが重要です。現在、ウェブフックへのリクエストが Kubernetes クラスターからのものであることを確認するための方法はありません。この結果、悪意のあるユーザーが API Gateway へのリクエストを偽装し、アカウントに使用分を請求する可能性があります。これに対処するために、デプロイを保護するための手順をいくつか見てみましょう。

マネージドクラスター

EKS などのマネージド型 Kubernetes サービスプロバイダーを使用する場合、コントロールプレーンは Kubernetes クラスターのマネージドコンポーネントです。クラスターのデータプレーンのワークロードのみを対象とする必要があります。この設定は、ユーザーがコントロールプレーンを編集することによってのみセキュリティ設定を変更できることも意味します。つまりこれは、2020 年 5 月の時点で、API Gateway へのリクエストがクラスターからのものであることをユーザーは保証できません。したがって、このソリューションは現在、リファレンス実装としてのみお勧めしています。

セルフマネージドクラスター

クラスターのコントロールプレーン (kube-apiserver、etcd ノード、ネットワーキングなど) の管理を担当している場合、開始前に、Kubernetes API サーバーに特定のフラグを設定し、ウェブフックへのリクエストを認証できます。AdmissionConfiguration  オブジェクトを使用すると、ValidatingAdmissionWebhook アドミッションコントローラーがリクエストを認証する方法のプラグイン設定値を設定できます。さまざまな Kubernetes クラスターに kubectl アクセスとコンテキストを付与する方法と同じように、kubeconfig を渡すことができます。これにより、トークンまたは API Gateway の API キーを渡すことができます。

クリーンアップ

今後、請求が発生しないようにするには、make destroy-stack または AWS CLI を使用して次のコマンドを実行し、CloudFormation スタックを削除します。

aws --region us-east-2 cloudformation delete-stack --stack-name amazon-ecr-repository-compliance-webhook

まとめ

API Gateway と Lambda を使用してサーバーレスアドミッションウェブフックを作成し、クラスターのセキュリティ制御を向上しました。これで、スケーラブルで可用性の高い、セキュアなアーキテクチャでの準拠しないデプロイを拒否できます。ValidatingAdmissionWebhookMutatingAdmissionWebhook リソースを使用して Kubernetes オブジェクトを操作することで、さらに多くの問題を解決できます。基本的な事柄を扱ったこの記事が、EKS プロジェクトの機能を構築する出発点となることを願っています。

著者について


Simon Woldemichael は、公共部門の AWS のお客様と協力するソリューションアーキテクトです。彼は音楽を聴いたり、ビデオゲームをプレイしたり、コンテナテクノロジーに取り組んだり、ソフトウェアを開発したりして楽しんでいます。
Josh Jiang は、AWS のアプリケーション開発を専門とするプロフェッショナルサービスコンサルタントです。彼は文学作品を評論し、ニンテンドー GameCube™ の大乱闘スマッシュブラザーズDX をプレイするのに熱中です。