「Amazon EKSベストプラクティスガイド (セキュリティ編)」を読み解く (1)アイデンティティとアクセス管理

2020.06.01

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

先月 (5/18) のことになりますが、AWSから「Amazon EKS Best Practices Guide for Security」という文書が公開されました。

この文書は文字通り「EKSを構築・運用する際のセキュリティ面におけるベストプラクティス」をまとめたガイダンスとなっています。
今回公開されたのはセキュリティに関するベストプラクティスですが、今後「パフォーマンス」「オペレーショナルエクセレンス (運用改善)」「コスト最適化」「信頼性」に関するものも公開される予定だそうです。

今回公開された「セキュリティ」に関するベストプラクティスガイドは、以下の10の章で構成されています。

  1. アイデンティティとアクセス管理 (Identity and Access Management)
  2. ポッドセキュリティ (Pod Security)
  3. ランタイムセキュリティ (Runtime Security)
  4. ネットワークセキュリティー (Network Security)
  5. マルチテナンシー (Multi-tenancy)
  6. 発見的統制 (Detective Controls)
  7. インフラストラクチャセキュリティ (Infrastructure Security)
  8. データ暗号化とシークレット管理 (Data Encryption and Secrets Management)
  9. 規約順守 (Regulatory Compliance)
  10. インシデント対応 (Incident Response)

全てを一気に読もうとすると、なかなかのボリュームです。
そこで、各章ごとに読み解いていき、内容のポイントを理解したいと思います。

アイデンティティとアクセス管理

Kubernetesには、セキュリティを担保するためのアイデンティティ管理とアクセス管理の仕組みが用意されています。

一方、AWSには「Identity and Access Management (IAM)」というアイデンティティ・アクセス管理のサービスが存在します。

この章では、Kubernetesのアイデンティティ・アクセス管理のベストプラクティスや、AWS IAMを使ってEKS/Kubernetesのアイデンティティ・アクセス管理をどのように行えば良いのか、などについて記述されています。

ここでは大きく「EKSクラスターに対するアクセスの管理」「ポッドがクラスターアクセスを行う際のアイデンティティ管理」という2つのトピックが取り上げられています。

EKSクラスターに対するアクセスの管理

Kubernetesでは、クラスターに対する操作はKubernetes APIサーバー (kube-apiserver) を通して行われます。

EKSはマネージドKubernetesサービスですので、Kubernetes APIサーバーはAWSが用意したコントロールプレーンに含まれます。

「ユーザー」と「サービスアカウント」

Kubernetes APIサーバーに対するアクセスを行う主体としては、「ユーザー」と「サービスアカウント」の2種類があります。

  • ユーザー (User)
    「人」がkubectlコマンドなどを使ってAPIサーバーへアクセスする場合に使われるもの
  • サービスアカウント (ServiceAccount)
    「人」以外のプロセス/アプリケーション/サービスなどがAPIサーバーへアクセスする場合に使われるもの

ユーザー

Kubernetesでは、ユーザーがKubernetes APIサーバーへアクセスする際の「認証」の方法として「クライアント証明書を使った認証」「パスワードによる認証」などいくつかの方法が用意されています。

EKSでは「Webhookトークンを使った認証」の方式が採用されており、これにより、AWS側で認証を行った「IAMユーザー」がKubernetes側で「ユーザー」として取り扱われます。

EKSにおけるユーザー認証の詳しい仕組み・設定方法については、こちらのブログ記事で解説しています。

サービスアカウント

ユーザーとは異なり、サービスアカウントはKubernetes上で予め定義・作成しておく必要があります。

サービスアカウントを作成すると、アカウントに対応した「サービスアカウントトークン」が自動的に生成されます。
このトークンは、Kubernetesにおいて秘匿情報を取り扱うためのリソースである「シークレット」(Secret) に格納されます。

Kubernetes上の「ポッド」(Pod) などのプロセス/アプリケーション/サービスがKubernetes APIサーバーへアクセスする際、サービスアカウントトークンをHTTPヘッダーに指定してリクエストを行うことで、Kubernetes APIから認証を受けることができます。

推奨事項

EKSクラスターアクセスのアイデンティティ・アクセス管理に関して、いくつかの推奨事項が提示されています。

認証にサービスアカウントトークンを使用しない

さきほど「サービスアカウントはサービスアカウントトークンを使ってKubernetes APIから認証を受ける」と説明しておきながら、いきなり「使用しない」とはおかしな話ですが、これは正確には「Kubernetesクラスターの外部からアクセスする場合は」という条件が付きます。

Kubernetesクラスターの内部からKubernetes APIサーバーへアクセスする場合は、シークレットからサービスアカウントトークンを入手して使用します。
シークレットに対するアクセス権限を適切に管理することによって、不適切なアクセスが行われることを防ぎます。

一方、Kubernetesクラスターの外部からKuberntes APIサーバーへアクセスする場合は、外部からシークレットにアクセスすることができませんので、サービスアカウントトークンを入手することができません。

そこで、シークレットへのアクセス権限を持つユーザーがkubectlコマンドでシークレットの内容を参照して、トークン文字列をファイルに保存したり外部プログラムのコードに埋め込むという方法が考えられます。

しかし、そのような方法は、トークンを盗まれたり外部に漏れたりする危険性があるため推奨されていません

クラスターの外部からAPIサーバーへアクセスするには、以下の方法を検討してください。

  • EC2インスタンス上のプロセスからのアクセス: EC2インスタンスにIAMインスタンスプロファイルを適用することで、IAMロールによる認証を行います。
  • AWS外部のプロセスからのアクセス: 例えば「client-go」などのKubernetesアクセスライブラリでは、kubeconfigファイルを参照して認証情報を取得するようになっています。

いずれも、サービスアカウントとしてではなくユーザーとしてのアクセスとなるため、「aws-auth」コンフィグマップにユーザーのマッピングを記述する必要があります。

ユーザーのAWSリソースに対する特権アクセスを最小限にする

開発者ユーザーなど、Kubernetesクラスターへのアクセスを目的として作成されたIAMユーザーに対して、AWSリソースへのアクセス権限をむやみに与えるべきではありません。

IAMユーザーがkubectlコマンド等を使ってKubernetes APIサーバーへアクセスを行う際、AWS上でEKSやワーカーノード (EC2) に対するアクセス権限は不要です

例えば「開発の一環で特定のS3バケットを参照する必要があるため、当該バケットへの読み取りアクセス権限のみを付与する」など、必要最低限のアクセス権限を与えるようにしましょう。

複数のユーザーがクラスタに対して同じ権限のアクセスを必要とする場合はIAMロールを使用する

IAMユーザー単位でKubernetesクラスターへのアクセス権限を管理しようとすると、ユーザーの数が増えるにつれて「aws-auth」コンフィグマップでのマッピングの管理が煩雑になります。

「aws-auth」コンフィグマップでIAMユーザーの代わりにIAMロールとKubernetesユーザーをマッピングすることによって管理が簡潔になります。
その場合は、IAMユーザーはIAMロールの切り替え (ロールを引き受け) を行ってから、Kubernetesクラスターへアクセスすることになります。

(IAMロールを使ったKubernetesクラスターへのアクセス制御の方法については、別の機会に解説したいと思います)

RoleBindingやClusterRoleBindingを作成する際は特権アクセスを最小限にする

Kubernetesで役割ベースのアクセス制御 (RBAC) を使ってアクセスの「認可 (Authorization)」を行う際、Kubernetesの各リソースに対するアクセス権限を「Role」「ClusterRole」へ記述します。(AWS IAMにおけるIAMポリシーのようなものですね)

アクセス管理の鉄則として、Role/ClusterRoleに記述するアクセス権限は必要最低限にすべきです。
対象リソースや操作を記述する際にワイルドカード(「*」)を使用することができますが、必要でない限りは、むやみに使うべきではありません。

Role/ClusterRoleを記述する際に、具体的なアクセス権限の記述内容が分からない場合もあると思います。
そのような場合は、Kubernetes監査ログに記録されたAPI呼び出しを精査して必要なアクセス権限を確認する方法があります。
もしくは、audit2rbac などのサードパーティ製ツールを使うことでRole/ClousterRoleやRoleBinding/ClusterRoleBindingを自動生成する方法もあります。

EKSクラスターエンドポイントをプライベートにする

EKSクラスターエンドポイントとは、すなわちKubernete APIサーバーへアクセスする際のエンドポイントを意味します。

デフォルトでは、エンドポイントは「パブリック」の設定になっており、エンドポイントはインターネットに向けて開かれます。
インターネットに向けられていますが、IAMによる認証とKubenetes RBACによる認可が行われるため、直ちにセキュリティの問題になることはありません。

よりセキュリティを強固にしたい場合や、企業のセキュリティポリシーで規定されている場合などは、以下の方法を取ることで対応することができます。

  • エンドポイントを「プライベート」に設定する
  • エンドポイントを「パブリック」に設定するが、アクセス元のIPアドレスに制限をかける

エンドポイントの「パブリック」と「プライベート」の違い、パブリックエンドポイントにおいてIPアドレスによるアクセス制限を行う方法などについては、こちらのブログ記事を参照してください。

クラスターへのアクセスを定期的に監査する

クラスターへのアクセスを必要とするユーザーは、時間の経過に従って変化します。

どのユーザーにアクセスが許可されているか、どの権限が割り当てられているかを確認するために、「aws-auth」コンフィグマップの定期的な監査を計画してください。

特定のサービスアカウント、ユーザー、グループにバインドされている役割を検査するために、kubectl-who-canrbac-lookup などのオープンソースツールを使用することもできます。

ポッドがクラスターアクセスを行う際のアイデンティティ管理

ポッドがEKSクラスターに対してアクセスを行う必要があるのは、以下の2通りのシチュエーションです。

  1. ポッドがKubernetes APIサーバーへアクセスして何らかの処理を行う必要がある場合
  2. IAM Roles for Service Accounts (IRSA) を使用する場合

ポッドによるKubernetes APIサーバーへのアクセス

ポッドがKubernees APIサーバーへのアクセスを必要とする典型的な例は、「AWS ALB Ingress Controller」などのKubernetesシステムへ大きく依存するポッド (コントローラー) をデプロイする場合です。

また、場合によってはユーザーアプリケーションであってもKubernetes APIサーバーへアクセスする要件が発生するかもしれません。

いずれの場合も、ポッドがKubernees APIサーバーへアクセスを行うには、「EKSクラスターに対するアクセスの管理」で説明した「サービスアカウント」を作成してポッドへ割り当てを行い、Kubernetes RBACによってサービスアカウントへ適切なアクセス権限を付与する必要があります。

IAM Roles for Service Accounts (IRSA)

ポッドがAWSのリソース (例えばS3バケットなど) へアクセスを行う場合、IAMポリシーによるAWSリソースへのアクセス権限を適用する必要があります。

かつては、ポッドに対してIAMポリシーを直接適用することはできず、ワーカーノードのインスタンスプロファイルを介してワーカーノード上で動作するポッドへIAMポリシーによるアクセス権限を与える方法しかありませんでした。
しかし、この方法では、ワーカーノード単位でアクセス権限が同じになってしまい、個別のポッドに対するきめ細やかなアクセス制御を行うことは不可能でした。

これを改善するために、まず、「kube2iam」や「kiam」などのオープンソースツールが登場して、昨年にはAWS公式の方法として「IAM Roles for Service Accounts」(IRSA) がリリースされました。

IRSAは、IAMロールをKubernetesのサービスアカウントおよびRBAC (役割ベースのアクセス制御) と連係させることで、ポッド単位でIAMポリシーによるアクセス制御を実現することができる仕組みです。

IRSAについての詳細は、こちらのブログ記事で紹介しています。

推奨事項

ポットによるクラスターアクセスのアイデンティティ・アクセス管理に関しても、いくつかの推奨事項が提示されています。

サービスアカウントトークンの自動マウントを無効にする

ポッドを作成する際にサービスアカウントを明示的に指定しなかった場合、自動的に「default」サービスアカウントが割り当てられます。

ポッドに対してサービスアカウントを割り当てる必要があるのは、以下のような場合です。

  • ポッドがKubernetes APIサーバーへアクセスして何らかの処理を行う必要がある場合
  • IAM Roles for Service Accounts (IRSA) を使用する場合

逆に言えば、これらに当てはまらない場合にはサービスアカウントを割り当ててKubernetes APIサーバーへのアクセス権限を得る必要は無いということになります。

ポット作成時にサービスアカウントが自動的に割り当てられないようにするには、以下のいずれかを行います。

  1. ポッドを作成する際、spec:automountServiceAccountToken: falseを記述する
  2. 「default」サービスアカウントにautomountServiceAccountToken: falseを設定する

(正確には、「default」サービスアカウントの割り当ては行われますが、シークレットからサービスアカウントトークンのコピーが行われないため、Kubernetes APIサーバーへのアクセスが不可能になります)

アプリケーション毎に専用のサービスアカウントを使用する

用途やアクセスすべきリソースが異なるアプリケーションでは、単一のサービスアカウントを使い回すのではなく、専用のサービスアカウントを使用するようにします。

ワーカーノードに割り当てられたインスタンスプロファイルへのアクセスを制限する

IAM Roles for Service Accounts (IRSA) を使用することにより、ワーカーノードに割り当てられたインスタンスプロファイルによって付与された権限はポッドへ継承されなくなります。

全てのポッドでIRSAを使用する運用を行う場合、IRSAを使用しないポッドがワーカーノードのインスタンスプロファイルから権限を継承してしまう「抜け道」を防ぐ方法があります。
Amazon EC2 インスタンスプロファイルの認証情報へのアクセスの制限 - Amazon EKS

非rootユーザーとしてアプリケーションを実行する

コンテナーは、デフォルトではrootユーザーとして実行されますが、これはベストプラクティスとは考えられていません。

コンテナーをroot以外のユーザーで実行するには、以下の例のようにspec.securityContext.runAsUser属性を設定します。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]

IRSAのIAMロール信頼ポリシーをサービスアカウント名にスコープする

IAM Roles for Service Accounts (IRSA) を使用する際は、IAMロールの信頼ポリシーにロールを引き受ける条件としてsystem:serviceaccount:(ネームスペース名):(サービスアカウント名)を記述します。

サービスアカウント名まで明示して指定することにより、同じネームスペースに所属する他のポッドが意図せずアクセス権限を持つことを防ぐことができます。

なお、eksctlを使用してeksctl create iamserviceaccountコマンドでIRSAを設定する場合は、この記述が自動的に行われます。

おわりに

今回は「アイデンティティとアクセス管理」について読み解いてみました。

AWS IAMを使った認証、IRSAによるポッドのアクセス制御についてはAWS (EKS) 特有の内容ですが、一方、Kubernetesの一般的な方針が適用される内容についても記載されていました。
両方の観点からEKSのアイデンティティ管理・アクセス管理を考えて行きたいですね。

次回は、2番目の章「ポッドセキュリティ (Pod Security)」を読み解きたいと思います。