kube2iam で Kubernetes の Pod に IAM role を割り当てる

シェアする

  • このエントリーをはてなブックマークに追加

概要

  • kube2iam で Kubernetes Pod に対して IAM role を割り当て、動作を確認する
  • Kubernetes cluster は kops で AWS 上に構築したものを使う
  • 検証用の S3 bucket を使い Pod に割り当てた role の有無で参照可能/不可能になることを確認する

kube2iam とは

AWS 環境では EC2 インスタンスに IAM role を割り当て、サービスの呼び出し時に資格情報を使用することができます。この仕組を、Kubernetes のように1つの EC2 インスタンスで複数のコンテナが稼働する状況で利用すると、セキュリティ上の問題が生じる可能性があります。

  • EC2 には各コンテナで必要な権限を集約した role を割り当てる必要がある
    • 神 role が誕生してしまう
  • コンテナ上からそのコンテナでは必要としない role の資格情報まで見えてしまう
kube2iam provides different AWS IAM roles for pods running on Kubernetes

Kubernetes クラスタを AWS 上に構築しているケースでは、 kube2iam を使うと IAM role を Kubernetes の Pod に対して割り当てることができるようになります。

EC2 メタデータ API に送られるトラフィックは kube2iam のコンテナにリダイレクトされ、 AWS API を呼び出して一時的な資格情報を取得し、呼び出し元のコンテナに返すという仕組みで実現しているようです。

検証

kube2iam のインストール

kube2iam.yml を作成し、 kubectl apply -f kube2iam.yaml でインストールする。

以下の yaml では k8s node に設定する iptables ルールを自動設定するために、使用するネットワークインターフェース名などのオプション指定を追加している。

参考: Updated to README.md for RBAC #99

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube2iam
  namespace: kube-system
---
apiVersion: v1
items:
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRole
    metadata:
      name: kube2iam
    rules:
      - apiGroups: [""]
        resources: ["namespaces","pods"]
        verbs: ["get","watch","list"]
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: kube2iam
    subjects:
    - kind: ServiceAccount
      name: kube2iam
      namespace: kube-system
    roleRef:
      kind: ClusterRole
      name: kube2iam
      apiGroup: rbac.authorization.k8s.io
kind: List
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kube2iam
  namespace: kube-system
  labels:
    app: kube2iam
spec:
  template:
    metadata:
      labels:
        name: kube2iam
    spec:
      serviceAccountName: kube2iam
      hostNetwork: true
      containers:
        - image: jtblin/kube2iam:latest
          imagePullPolicy: Always
          name: kube2iam
          args:
            - "--base-role-arn=arn:aws:iam::111111111111:role/"
            - "--iptables=true"
            - "--host-ip=$(HOST_IP)"
            - "--host-interface=cni0"
            - "--verbose"
          env:
            - name: HOST_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          ports:
            - containerPort: 8181
              hostPort: 8181
              name: http
          securityContext:
            privileged: true

EC2 メタデータ API へのアクセス制限

Pod が kube2iam をバイパスして EC2 メタデータ API にアクセスすることを防ぐために、 k8s node の iptables にルール追加をする。
このルールは DaemonSet の配置時に自動追加することも可能。(上記の yaml ではそうしている)

以下のようなルールを追加することで、 EC2 メタデータ API へのアクセスを kube2iam コンテナに転送する。

iptables \
  --append PREROUTING \
  --protocol tcp \
  --destination 169.254.169.254 \
  --dport 80 \
  --in-interface docker0 \
  --jump DNAT \
  --table nat \
  --to-destination `curl 169.254.169.254/latest/meta-data/local-ipv4`:8181

Pod から S3 bucket へのアクセスを許可する例

kube2iam は EC2 に割り当てられた中間的な Role を使い、Pod に割り当てる Role を引き受け (AssumeRole) 一時的なクレデンシャルを取得するために使われる。

Pod に割り当てる Application Role には、その Role を実際に引き受ける中間 Role を信頼するための AssumeRolePolicyDocument を設定する必要がある。

Role を引き受けるための AssumeRole 許可を行う記述を k8s node に割り当てる。
kops edit cluster で編集できる yaml に以下のポリシーを追加した。

Kubernetes Operations (kops) - Production Grade K8s Installation, Upgrades, and Management

編集が完了したら kops cluster update --yes で反映する。

spec:
  additionalPolicies:
    node: |
      [
        {
          "Effect": "Allow",
          "Action": ["sts:AssumeRole"],
          "Resource": ["*"]
        }
      ]

今回は Application Role として特定の S3 バケットを ListBucket できる Role を Terraform で作った。
kops が作った k8s node に割り当てられている Role の ARN を、 variable で持たせておき Principal の指定に使っている。

provider "aws" {
  region = "ap-northeast-1"
}

#
# variables
#
variable "test-kube2iam-bucket-name" {
  type    = "string"
  default = "test-kube2iam-bucket"
}

variable "iam-role-arn_test-k8s" {
  type    = "string"
  default = "arn:aws:iam::111111111111:role/nodes.test.k8s.example.com"
}

#
# test-kube2iam-bucket role & policy
#
## role
resource "aws_iam_role" "test-kube2iam-bucket" {
  name = "${var.test-kube2iam-bucket-name}"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "${var.iam-role-arn_test-k8s}"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

## policy
resource "aws_iam_role_policy" "test-kube2iam-bucket" {
  name = "test-kube2iam-bucket"
  role = "${aws_iam_role.test-kube2iam-bucket.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::test-kube2iam-bucket"
      ]
    }
  ]
}
EOF
}

Pod への IAM ロールの割り当て

Pod マニフェストに annotation をつけ、 割り当てる role の ARN を書く。

apiVersion: v1
kind: Pod
metadata:
  annotations: iam.amazonaws.com/role: arn:aws:iam::ACCOUNTID:role/S3-BUCKET-NAME

kubectl でつけるなら、こんな感じ

kubectl annotate pods <PodName> iam.amazonaws.com/role='arn:aws:iam::111111111111:role/test-kube2iam-bucket'

annotation を外すときは - をつけると消せる

kubectl annotate pods <PodName> iam.amazonaws.com/role-

動作確認

aws-cli の使える適当な Pod を配置して、 Pod から EC2 メタデータ API へアクセスしたときや aws cli を実行したときの挙動の違いについて確認する。

a. kube2iam を使う場合/使わない場合
b. kube2iam で Pod に IAM ロールを割り当てた場合/割り当てない場合

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  labels:
    name: mypod 
spec:
  containers:
  - image: debian
    command: [ "/bin/bash", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
    name: mypod
$ kubectl create -f mypod.yml
pod "mypod" created

## aws-cli のインストール
$ kubectl exec -it mypod /bin/bash
root@mypod:/# apt-get update && apt-get install -y curl
root@mypod:/# apt-get install -y python && curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" && python get-pip.py && pip install awscli
root@mypod:/# cat > ~/.aws/config <<EOF
> [default]
> region = ap-northeast-1
> EOF

EC2 メタデータ API への Pod からのアクセス

kube2iam インストール前
## EC2 に割り当てられた IAM Role が見えている
root@mypod:/# curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
nodes.test.k8s.example.com

## クレデンシャル情報以外の API アクセスも可能
root@mypod:/# curl http://169.254.169.254/latest/meta-data/local-ipv4
10.xxx.xxx.xxx
kube2iam インストール後

インストール後、 k8s の worker node には iptables のルールが追加された。

# iptables-save | grep 169.254
-A PREROUTING -d 169.254.169.254/32 -i cni0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.xxx.xxx.xxx:8181
## security-credentials の中身は空になり見えなくなった
root@mypod:/# curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
root@mypod:/#

## クレデンシャル情報以外の API アクセスも可能
root@mypod:/# curl http://169.254.169.254/latest/meta-data/local-ipv4
10.xxx.xxx.xxx

Pod に IAM ロールを付与し S3 バケットの内容が取得できるか確認

IAM ロールは Pod に annotation をつけることで付与できる

annotation 付与前
## 必要なクレデンシャルを取得できない状況なので S3 バケットの中身を見ることはできない。
root@mypod:/# aws s3 ls s3://test-kube2iam-bucket
Unable to locate credentials. You can configure credentials by running "aws configure".
annotation 付与後
## Pod に対して annotation を付与し IAM Role を割り当てる
% kubectl annotate pods mypod iam.amazonaws.com/role='arn:aws:iam::111111111111:role/test-kube2iam-bucket'
pod "mypod" annotated

## S3 バケットの中身が見えるようになった
root@mypod:/# aws s3 ls s3://test-kube2iam-bucket
2018-02-26 12:31:57        306 chart.txt
## 先ほど付与した annotation を外す
% kubectl annotate pods mypod iam.amazonaws.com/role-
pod "mypod" annotated

## S3 バケットの中身は見えなくなった
root@mypod:/# aws s3 ls s3://test-kube2iam-bucket
Unable to locate credentials. You can configure credentials by running "aws configure".

まとめ

  • Pod に対して IAM role を付与することで S3 bucket の参照が可能になることを確認した
  • 各 Pod のための IAM role をどう準備するかは何かうまい方法を考えたい
    • kops と Terraform で二重管理のようになってしまうのをどうにかしたい
  • 安定性とセキュリティ面で kiam のほうが良いらしいのでコチラも試してみる
    • https://twitter.com/mumoshu/status/934018256561586176

参考

Using Kube2IAM to provide fine grained IAM roles to individual Kubernetes pods
## TL;DR; ## EC2におけるインスタンスプロフィールによる権限付与の仕組み EC2インスタンスにインスタンス...

シェアする

  • このエントリーをはてなブックマークに追加

フォローする