EKSでAmazon VPC CNI PluginとCalicoを同居させてIMDSとk8s APIへのアクセスを制限する
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つの課題があります。
- デフォルトのCNI PluginであるAmazon VPC CNI PluginではNetwork Policyを利用できない。
- (追記: 2023/08/29 からできるようになりました。ブログの日本語化もすでにされています Amazon VPC CNI による Kubernetes NetworkPolicy のサポート | Amazon Web Services ブログ)
- 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をインストールします。
- OIDC Provierを作成します。
- クラスター用の IAM OIDC プロバイダーの作成 - Amazon EKS の通りに実施します。
- OIDC URLを確認する (コンソールからも見れます)
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
- CSI Driver の動作確認を行います。
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
を使うのが無難かと思われます)
- (実体はCRD (
- k8sで稼働中のCalicoと
calicoctl
のpatch versionまで一致させないとオプション (--allow-version-mismatch
) をつけないとcalicoctl
を実行することができません。
- Calico Network Policyは
-
任意の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
を追加してください。
ルールは定義順に適用されます。
- 例えば、一部の通信のみDenyしたい場合はDenyのルールを書いた後、最後に
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に到達してしまいます。-
quay.io
にも同一イメージがPushされていたので、以下のようにして回避しています[4]。
-
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クラスタ内部の通信やクラスタから外に出る通信において制限したいシチュエーションが出てきたら、ぜひ試してみてください。
参考資料
- Amazon Elastic Kubernetes Service (EKS) | Calico Documentation
- Installing the Calico network policy engine add-on - Amazon EKS
- Network Security - EKS Best Practices Guides
- EKSでCalicoを使ってみた[1.インストール編]|SHIFT Group 技術ブログ
-
Identity and Access Management - EKS Best Practices Guides ↩︎
-
GKEやAKSだとどうかは調査できていません。 ↩︎
-
https://github.com/projectcalico/calico/issues/6491#issuecomment-1253971522 ↩︎
-
実際にRate Limitに引っかかって応急対応的にこのワークアラウンドを入れました。Installationの
spec.registry=quay.io
でも指定できそうですが、未検証です。 ↩︎
Discussion