🐈

EKSでAmazon VPC CNI PluginとCalicoを同居させてIMDSとk8s APIへのアクセスを制限する

2023/08/25に公開

TL;DR

  • Amazon VPC CNI Pluginを利用したまま、Calicoをインストールすることで、k8sのNetwork Policyと拡張されたCalico Network Policyを利用できます。

    Amazon Elastic Kubernetes Service (EKS) | Calico Documentation より
  • Calico Network Policyを利用するとServiceを利用したポリシー設定が可能となります。
    • そのため、ユーザー側で制御できないタイミングでIPが更新されてしまうEKSのk8s APIに対しても制限ができます。
  • k8s Network Policyと異なる点がいくつかあり、例えばデフォルトの挙動が Deny となっていることに注意が必要です。

背景

EKSにおいて、セキュリティの観点からEC2のIMDSへのアクセスと、K8s APIへのアクセスを制限しておきたいケースがあると思います。

IMDSへのアクセスに関しては、IMDSv2の必須化(metadata_http_tokens=required) + put_response_hop_limit=1 にしてコンテナからのIMDSへのアクセスを制限する[1]ことでも対応できます。

一方で、クラスタ内からk8s APIへのアクセスを制限するにはNetWork Policyを利用する必要があります。
しかし、EKSの仕様上[2]、以下の2つの課題があります。

  1. デフォルトのCNI PluginであるAmazon VPC CNI PluginではNetwork Policyを利用できない。
  2. EKSのControl PlaneはIPが固定ではなく時間経過で変わってしまうため、IP指定による制御ができない。

1.についてはCNI PluginをCalicoに切り替えるか、Amazon VPC CNI PluginとCalicoを同居させてNetwork Policy機能のみCalicoを利用する必要があります。この記事では後者の導入手順をお伝えします。

2.については、Kubernetes APIのServiceに対してポリシーが設定できればよいため、Serviceに対してポリシー設定ができるCalico Network Policyを利用します。

Caico導入手順

  • 前提として Amazon VPC CNI Plugin がインストールされたEKSクラスタが存在することとします。
  • Amazon Elastic Kubernetes Service (EKS) | Calico Documentation にはHelm Chartの手順もありますが、今回はOperatorを利用してインストールする手順を紹介します。

Amazon EBS CSI Driverのインストール

CalicoのドキュメントのBefore you begin...に書かれているように、Amazon EBS CSI Driverをインストールします。

aws eks describe-cluster --name eks-cluster  --query "cluster.identity.oidc.issuer" --output text
# https://oidc.eks.ap-northeast-1.amazonaws.com/id/<oidc_id> のようなアウトプットが得られる
  • assume role用のIAM Policyを用意します。
cat <<EOF > aws-ebs-csi-driver-trust-policy.json
{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Effect": "Allow",
   "Principal": {
    "Federated": "arn:aws:iam::<account_id>:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/<oidc_id>"
   },
   "Action": "sts:AssumeRoleWithWebIdentity",
   "Condition": {
    "StringEquals": {
     "oidc.eks.ap-northeast-1.amazonaws.com/id/<oidc_id>:aud": "sts.amazonaws.com",
     "oidc.eks.ap-northeast-1.amazonaws.com/id/<oidc_id>:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"
    }
   }
  }
 ]
}
EOF
  • IAM Roleを作成します。
aws iam create-role \
  --role-name EKS_EBS_CSI_DriverRole \
  --assume-role-policy-document file://"aws-ebs-csi-driver-trust-policy.json"
  • AWS Managed PolicyをRoleにアタッチします。
aws iam attach-role-policy \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
  --role-name EKS_EBS_CSI_DriverRole
  • EKS addonとしてCSI Driverをクラスタにインストールします。
aws eks create-addon \
   --cluster-name eks-cluster --addon-name aws-ebs-csi-driver \
  --service-account-role-arn arn:aws:iam::<account_id>:role/EKS_EBS_CSI_DriverRole

Calicoのインストール (tigera-operatorの導入とCalico installationの設定)

Amazon EBS CSI Driverと比べるとサクッと入れられる印象です。

  • tigera-operatorをインストールする。
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml
  • Calico installationの設定を行います。
kubectl create -f - <<EOF
kind: Installation
apiVersion: operator.tigera.io/v1
metadata:
  name: default
spec:
  kubernetesProvider: EKS
  # Configures Calico policy configured to work with AmazonVPC CNI networking.
  cni:
    type: AmazonVPC
EOF
  • Stars Policy デモでNetwork Policyが利用できることの確認を行います。

  • calicoctlをインストールする。

    • Calico Network Policyはcalicoctlを利用して操作します。
      • (実体はCRD (networkpolicies.crd.projectcalico.org) のようなので、kubectlでも操作できなくはなさそうなのですが、apiVersion の表示が違うなどがあるため、大人しく calicoctl を使うのが無難かと思われます)
    • k8sで稼働中のCalicoとcalicoctlのpatch versionまで一致させないとオプション (--allow-version-mismatch) をつけないとcalicoctlを実行することができません。
  • 任意のCalico Network Policyをapplyします。

calicoctl apply -f super-calio-network-policy.yaml

k8s APIとIMDSへのアクセスを禁止するCalico Network Policyの例

  • ルール適用範囲 (spec.selector にマッチするリソース) に対するデフォルトの挙動は Deny です。
    • 例えば、一部の通信のみDenyしたい場合はDenyのルールを書いた後、最後にaction: Allowを追加してください。
      ルールは定義順に適用されます。
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: deny-k8s-api-and-imds
  namespace: default
spec:
  # 制限したいPodにマッチするラベル
  selector: (app.kubernetes.io/managed-by == 'manager') && (app.kubernetes.io/part-of == 'super-feature')
  types:
    - Egress
  egress:
    # IMDSへのアクセスを禁止するルール
    - action: Deny
      destination:
        nets:
          - 169.254.169.254/32
    # k8s APIへのアクセスを禁止するルール
    - action: Deny
      destination:
        services:
          name: kubernetes
          namespace: default
    # defaultがDenyなので、その他の通信を許可するルール
    - action: Allow

運用Tips

Calicoに関しては、おそらくHelmを利用するのが楽なのですが、Helm未導入の環境での管理方法の一例としていくつかTips的なものを書いておきます。

  • tigera-operator関連のマニフェストファイルは18000行超え(v3.25.0時点)と大きく、またkubectl applyを利用するデプロイフローに混ぜるとエラーになる[3]ため、どのバージョンをデプロイしているかの参考目的に留め、コメントアウトしています。
  • デフォルトではDocker Hubからイメージを取得するようになっていますが、DaemonSetsで calico-node がデプロイされる都合上、頻繁にノードがスケールする環境であれば、すぐにImage Pull Rate Limitに到達してしまいます。
kubectl -n calico-system set image daemonset calico-node calico-node=quay.io/calico/node:v3.25.0 flexvol-driver=quay.io/calico/pod2daemon-flexvol:v3.25.0
kubectl -n calico-system set image daemonset csi-node-driver csi-node-driver-registrar=quay.io/calico/node-driver-registrar:v3.25.0 calico-csi=quay.io/calico/csi:v3.25.0
kubectl -n calico-system set image deployment calico-kube-controllers calico-kube-controllers=quay.io/calico/kube-controllers:v3.25.0
kubectl -n calico-system set image deployment calico-typha calico-typha=quay.io/calico/typha:v3.25.0

まとめ

EKSへのCalico導入手順と、IMDSとk8s APIへのアクセスを禁止するCalico Network Policyの例を紹介しました。また、Calico運用時のTipsも紹介しました。

k8sクラスタ内部の通信やクラスタから外に出る通信において制限したいシチュエーションが出てきたら、ぜひ試してみてください。

参考資料


脚注
  1. Identity and Access Management - EKS Best Practices Guides ↩︎

  2. GKEやAKSだとどうかは調査できていません。 ↩︎

  3. https://github.com/projectcalico/calico/issues/6491#issuecomment-1253971522 ↩︎

  4. 実際にRate Limitに引っかかって応急対応的にこのワークアラウンドを入れました。Installationspec.registry=quay.ioでも指定できそうですが、未検証です。 ↩︎

株式会社primeNumber

Discussion