Amazon Web Services ブログ

高度な防御のために EKS 暗号化プロバイダーのサポートを利用する

Gyuho Lee、Rashmi Dwaraka、Michael Hausenblas

Amazon EKS で AWS の暗号化プロバイダーをネイティブにサポートする計画を発表した際、皆さまから頂いたご意見の大半は「すぐ入手したいのですが」というものでした。 高度な防御のための重要なセキュリティ機能である、暗号化プロバイダー向けの EKS サポートを開始しました。これで、独自のマスターキーを使用し、EKS で Kubernetes シークレットのエンベロープ暗号化を使用できるようになりました。この投稿では、その背景と開始方法について解説します。

背景

Kubernetes のシークレットで、パスワードや API キーなどの機密情報を Kubernetes 固有の方法で管理できます。たとえば、kubectl create secret を使用してシークレットリソースを作成すると、Kubernetes API サーバーは base64 エンコード形式で etcd に保存します。EKS では、AWS が管理する暗号化キーを使用してディスクレベルで暗号化された etcd ボリュームを処理します。

エンベロープ暗号化とは、キーを別のキーで暗号化することです。なぜこれが必要なのでしょうか? これは、機密データを保存するアプリケーションに対するセキュリティのベストプラクティスで、高度な防御のためのセキュリティ戦略の一部でもあるからです。つまり、AWS KMS にマスターキーを (長期) 保存し、Kubernetes API サーバーでのデータキー生成に使用します。このキーは Kubernetes シークレットに保存した機密データの暗号化/復号化に使用します。

これまで、エンベロープ暗号化のために EKS で独自のマスターキーを使用するネイティブな方法はありませんでした。今回のリリースで、GoDaddy の外部シークレットや HashiCorp Vault などの外部ソリューションを操作して、Kubernetes シークレットを独自のキーで暗号化する必要がなくなりました。AWS KMS を使用すれば、EKS クラスター内に保存したシークレットを暗号化するためのキーを生成できます。または、追加のソフトウェアをインストールまたは操作することなく、別のシステム (オンプレミスソリューションなど) から生成したキーを KMS にインポートし、EKS クラスターで使用できます。

しくみ

EKS クラスターを作成する場合、AWS CLI、コンソール、または eksctl を使用して「KMS Key ARN」を設定することにより、暗号化プロバイダーのサポートを有効にできます。これは、設定ファイルを介したキー ARN の設定をサポートするものです。

設定が完了し、開発者の 1 人が Kubernetes シークレットを作成すると、暗号化プロバイダーが Kubernetes が生成したデータ暗号化キーで自動的にシークレットを暗号化します。そこで提供された KMS マスターキーを使って暗号化します。

詳細を説明する前に、以下で使用する 2 つの主要な用語について、同じページで簡単に見てみましょう。

  1. CMKCustomer Master Key の略です。これを王国のキーと考えてみましょう。これを使って、機密情報を保護するために実際に使用されるキーを暗号化および復号化します。
  2. このキーが私たちを DEK へと導きます。これは Data Encryption Key の略で、データを暗号化および復号化するためにシークレットごとに使用します。

これで明確になったでしょうか。では、EKS が高いレベルで暗号化プロバイダーをサポートする方法、およびこの機能が有効になっている Kubernetes シークレットの書き込みおよび読み込みパスがどのようなものかを見てみましょう。

詳細な手順は次のとおりです。

  1. ユーザー (通常は管理者ロール) が、たとえば kubectl または GitOps スタイルを使って、シークレットを作成することが最初のステップです。
  2. コントロールプレーンの Kubernetes API サーバーはローカルで DEK を生成し、これを使用してシークレットのプレーンテキストペイロードを暗号化します。書き込みごとに一意の DEK を生成すること、また、プレーンテキスト DEK は決してディスクに保存されないことにご注意ください。
  3. Kubernetes API サーバーは kms:Encrypt を呼び出して、CMK を使って DEK を暗号化します。このキーはキー階層のルートで、KMS の場合、ハードウェアセキュリティモジュール (HSM) に CMK を作成します。このステップでは、API サーバーは CMK を使用して DEK を暗号化し、さらに、暗号化された DEK の base64 キャッシュします。
  4. 最後に、write-path に関して、API サーバーは DEK で暗号化されたシークレットを etcd に保存します。
  5. ボリューム (read-path) を介してポッドなどでシークレットを使用する場合、逆のプロセスが行われます。つまり、API サーバーは etcd から暗号化されたシークレットを読み込み、DEK でシークレットを復号化します。
  6. EC2 または Fargate のポッドのいずれかで実行中のアプリケーションは、この時点でいつもどおりシークレットを使用できます。

暗号化プロバイダーへの EKS サポートは、Kubernetes バージョン 1.13 (プラットフォームバージョン eks.8) および 1.14 (eks.9) を実行しているクラスターで利用できることに注意してください。シークレットの使用方法を変更する必要はありません。必要なのは、クラスター作成時に暗号化プロバイダーのサポートを有効にすることだけです。

これらの基本事項を理解できたら、実際にどのようになるか見てみましょう。

チュートリアル

まず、暗号化に使用するクラスターと同じリージョンで、KMS キーが必要になります。KMS キーをまだ作成していない場合は、AWS CLI を使用して作成できます。

$ MASTER_KEY_ARN=$(aws kms create-key --query KeyMetadata.Arn --output text)

ここで、AWS コンソールを使って EKS 1.14 クラスターを作成します。気をつけていただきたいこと が 1 つあります。ネットワーク設定のすぐ下でシークレット暗号化を有効にしてください。また、現時点では、これはクラスター作成時にのみ設定できます (つまり、クラスター設定の更新ではサポートされません)。

ここで、マネージドノードグループを追加するか、サーバーレスデータプレーンの Fargate プロファイルを定義します。

次に、ローカル Kubernetes 設定ファイルを更新し、コマンドラインからクラスターにアクセスできるようにします。

$ aws eks update-kubeconfig --name ekseprovidercon
Added new context arn:aws:eks:us-west-2:123456789012:cluster/ekseprovidercon to /Users/example/.kube/config

これで、暗号化をエンベロープ化したシークレットを使用するための準備が整いました。たとえば、test-creds というシークレットを名前空間 encprovtest に作成し、それを使用します。

まず、シークレット値とターゲット名前空間を準備します。

$ echo -n "am i safe?" > ./test-creds
$ cat ./test-creds
am i safe?

$ kubectl create ns encprovtest

シークレットの値とネームスペースを設定したら、それを見てみましょう。

$ kubectl create secret \
          generic test-creds \
          --from-file=test-creds=./test-creds \
          --namespace encprovtest
secret/test-creds created

この時点で、シークレットは etcd に到達し、DEK を使って暗号化されました。

開発者はポッドのボリュームマウントなどを介して、このシークレットを使用できます。デモ用に、次のように CLI 経由で読み返そうとしています。

$ kubectl get secret test-creds \
  -o jsonpath="{.data.test-creds}" \
  --namespace encprovtest | \
  base64 —decode
am i safe?

うまくいきました。 でも、シークレットの作成時に実際にシークレットが暗号化され、読み込んだ際に解読されていることが、どうやってわかるのでしょうか? では、AWS CloudTrail を使用して、何が起こったのか見てみましょう。Decrypt イベントを検索すると、次のように表示されます。

 

これは思った通りに機能したので、今度はポッドからシークレットを使用してみましょう。さらに、面白くするために、弊社のサーバーレスサービスである Fargate で EKS を使用します。

まず、Fargate プロファイルが設定されていることを確認します。Kubernetes 名前空間のサーバーレスで起動したポッドをサポートする Fargate プロファイルを使用しているので、出力が次のようになっていることを確認します。

$ aws eks describe-fargate-profile \
      --cluster-name envencdemo 
      --fargate-profile-name fgp0
{
    "fargateProfile": {
        "fargateProfileName": "fgp0",
        "fargateProfileArn": "arn:aws:eks:us-west-2:123456789012:fargateprofile/envencdemo/fgp0/b4b84077-0074-34d0-eaab-a5e81c043ebb",
        "clusterName": "envencdemo",
        "createdAt": 1582711047.622,
        "podExecutionRoleArn": "arn:aws:iam::123456789012:role/fg-cluster-FargatePodExecutionRole-T0V7YEE2PZCM",
        "subnets": [
            "subnet-05286e168dbafbdc6"
        ],
        "selectors": [
            {
                "namespace": "serverless",
                "labels": {}
            }
        ],
        "status": "ACTIVE",
        "tags": {}
    }
}

次に、Fargate プロファイルが選択するターゲット環境である Kubernetes 名前空間を作成します。

kubectl create ns serverless

現在、私たちは上記の名前空間にシークレットを作成する立場にあります。

kubectl --namespace serverless \
        create secret generic test-creds \
        --from-file=test-creds=./test-creds 

次のマニフェストを使用して、consumesecret というポッドでシークレットを使用します。

apiVersion: v1
kind: Pod
metadata:
  name: consumesecret
spec:
  containers:
  - name: shell
    image: amazonlinux:2018.03
    command:
      - "bin/bash"
      - "-c"
      - "cat /tmp/test-creds && sleep 10000"
    volumeMounts:
      - name: sec
        mountPath: "/tmp"
        readOnly: true
  volumes:
  - name: sec
    secret:
      secretName: test-creds

では、すべての準備が整ったので、ポッドを起動しましょう。

kubectl --namespace serverless \
        apply -f podconsumingsecret.yaml

すべて成功した場合、ポッドに見えるもの、つまりコンテナのファイルシステム内の /tmp/test-creds にあるシークレットを確認できるはずです。

$ kubectl --namespace serverless exec -it consumesecret -- cat /tmp/test-creds
am i safe?

うまくできました! EKS クラスターでエンベロープ暗号化を有効にしてシークレットを使用する方法がわかったところで、基礎となっているオープンソースプロジェクトと使用コストを詳しく見てみましょう。

負担料金と使用方法

EKS では、オープンソースの AWS 暗号化プロバイダー を使用して、KMS でシークレットのエンベロープ暗号化を行います。このプロジェクトは、Kubernetes コミュニティと Kubernetes SIG 組織の一部が支援しています。AWS暗号化プロバイダーは、Kubernetes コントロールプレーンで実行する必要があります。これは、以前は AWS のセルフマネージド Kubernetes クラスターでは可能でしたが、EKS クラスターではできませんでした。AWS は EKS クラスターの暗号化プロバイダーを全面的にサポートしています。さらに、EKS エンジニアが率いるプロジェクト管理担当者とともに、今後もオープンソースプロジェクトの改善と管理に投資を行っていく予定です。

コストに関しては、作成するキーまたは KMS にインポートするキーの保存に月額 1 USD がかかります。KMS では、アカウントごとに 1 か月あたり 20,000 件のリクエストの 無料利用枠で、暗号化および復号化リクエストに課金します。また、1 か月あたり無料利用枠を超える 10,000 件のリクエストあたり 0.03 USD をお支払いいただきます。ただし、AWS 暗号化プロバイダーのビルトインのキャッシング機能により、すべての読み込み操作が KMS への実際のリクエストを引き起こすわけではなく、そのため全体の料金が下がることに注意してください。これはアカウントのすべての KMS 使用に適用されるため、クラスターで KMS を使用する際のコストは、アカウント内の他のクラスターまたは AWS リソースでの KMS の使用により変わる可能性があります。

ワクワクする新しいセキュリティ機能に関して、皆さまのご感想と使用方法について教えてください。さらに、オープンソースプロジェクト kubernetes-sigs/aws-encryption-provider にもぜひご参加ください。

Gyuho Lee

Gyuho Lee

Gyuho は EKS チームの SDE として、Kubernetes やその他の OSS プロジェクトで活躍しています。

Rashmi Dwaraka

Rashmi Dwaraka

Rashmi は、EKS マネージドクラスターに携わる EKS チームの SDE です。