AWS上に構築したKubernetesクラスタ内のPodが,AWSのリソースにアクセスしようとしたときには,もちろんAWS IAM Roleか,AWSの認証情報を使ってAWSのAPIを叩く必要がある.
しかし,Kubernetesはクラスタだ.もちろんノードはいっぱいあるし,その上では様々なPodが動くことになる.そういうときに,ノード全体に強い権限を持ったIAM Roleを付与するのは,あまり嬉しくない. 意図せず別のPodが,アクセスしてほしくないリソースにアクセスできることになってしまう.
というわけで,Podごとに個別にIAM Roleを付与したいという要求があるのだが,それを満たすのがkube2iamやkiamというOSSだ.
で,中身の仕組みの話を詳しくここではしないのだが,基本的にkube2iamの方が仕組みが単純である. ただし,たまにバグを拾うことがあって,正しくAWSのCredentialを引けない場合があった.
というわけで今回kiamに移行した.
ちなみにKubernetesクラスタはkopsで構築している. EKSを使っている場合は,つい最近発表されたIAM Role for Service Account を使うと良いと思うよ.
前提条件
kiamは,serverとagentの2つのプロセスに分かれているのだが,このうちserver側のプロセスはkubernetesクラスタのmasterノードで動作することを前提にしている. そのためmasterノードにpodを配置できない,EKSのような環境の場合,kiamは選択できないことになる.
今回は,kopsで構築したクラスタなので,問題なくmasterノードに配置することができる.
また,kube2iamはhelmでインストールしている.これをアンインストールし,kiamもhelmでインストールすることにする. ただし,一度kube2iamをアンインストールしてしまうので,一時的にIAMの認証が通らない時間が発生してしまうが,これは許容している.
kube2iamの削除
実際に移行する際は,kiamのインストール直前でやることをおすすめする.
$ helm delete --purge kube2iam
Podはこれで全て消える.
iptablesを削除する
一応iptablesを編集するような設定を書いていた場合,それを削除したほうが良い.
workerのnodeにsshで入り,
$ sudo iptables -t nat -n -L PREROUTING --line-numbers Chain PREROUTING (policy ACCEPT) num target prot opt source destination 1 cali-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* cali:6gwbT8clXdHdC1b1 */ 2 KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */ 3 DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL 4 DNAT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 to:10.192.102.212:8181 5 DNAT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 to:10.192.102.212:8881
のようにしてiptablesを確認する.
この場合,8181
ポートを使っているルールがkube2iamなので,これを削除する.
$ sudo iptables -t nat -D PREROUTING 4
これでkube2iamの削除は完了である.
kiamを入れる
kiamが使うIAM Roleを準備する
必要なRoleについては,
に記述されている.
k8sクラスタのmaster nodeに付与するRole
master nodeのIAM Roleは既に用意されていることが多い.kopsだと自動作成されるしね. なので,新規作成ではなく修正になる可能性が高いが,以下のようなIAM Roleを作成する.
resource "aws_iam_role" "k8s_master_role" { name = "k8s-master-role" path = "/" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF }
そして,このRoleはEC2インスタンスに付与されるので,Instance Profileになっている必要がある.
resource "aws_iam_instance_profile" "k8s_master_profile" { name = "k8s-master-profile" role = aws_iam_role.k8s_master_role.name }
kiamのserverに付与するRole
masterインスタンス内で動くkiam serverプロセスに,Roleを渡す必要がある.
こいつは,先程作成したk8sのmaster roleにsts:Assume
できる必要がある.
resource "aws_iam_role" "kiam_master_role" { name = "kiam-master-role" path = "/" assume_role_policy = data.template_file.k8s_master_assume_role_policy.rendered } data "template_file" "k8s_master_assume_role_policy" { template = file( "${path.module}/aws_iam_role_policies/arn_assume_role_policy.json.tpl" ) vars = { arn = aws_iam_role.k8s_master_role.arn } }
arn_assume_role_policy.json.tpl
の中身がこちら.
{ "Version":"2012-10-17", "Statement": [ { "Effect":"Allow", "Principal": { "AWS": "${arn}" }, "Action": "sts:AssumeRole" } ] }
k8sのmaster roleに付与するpolicy
kubernetesのmasterノードに付与されるIAM Roleには,以下のようなpolicyをくっつける.
resource "aws_iam_policy_attachment" "sts_assume_for_kiam_role" { name = "sts-assume-role-for-kiam-role" roles = [ aws_iam_role.k8s_master_role.name ] policy_arn = aws_iam_policy.sts_assume_for_kiam_role_policy.arn } resource "aws_iam_policy" "sts_assume_for_kiam_role_policy" { name = "sts-assume-for-kiam-role-policy" path = "/" description = "" policy = data.template_file.sts_assume_for_kiam_role_policy.rendered } data "template_file" "sts_assume_for_kiam_role_policy" { template = file( "${path.module}/aws_iam_policies/sts_assume_role_for_role_policy.json.tpl" ) vars = { role = aws_iam_role.kiam_master_role.arn } }
sts_assume_role_for_role_policy.json.tpl
はこちら.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sts:AssumeRole" ], "Resource": "${role}" } ] }
kiamのserverプロセスに付与するPolicy
kiamのserverプロセスが利用するIAM Roleには以下のようなpolicyを付与する.
resource "aws_iam_policy_attachment" "sts_assume_role_for_all" { name = "sts-assume-role-for-all" roles = [ aws_iam_role.kiam_master_role.name ] policy_arn = aws_iam_policy.sts_assume_role_for_all_policy.arn } resource "aws_iam_policy" "sts_assume_role_for_all_policy" { name = "sts-assume-role-for-all-policy" path = "/" description = "" policy =<<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "*" } ] } EOF }
これで準備はできた.
kaimのhelm chartのオプションについて
chartはここにあるものを使う.
なお,オプションの指定は全てhelmfile の形式で記述している.
hostの設定
まず,kube2iamと同じくnodeのiptablesを追記する必要がある.そのため,
- agent: host: iptables: true interface: "!eth0"
と指定する.
俺が使っているkopsのkubernetesクラスタはnetworking modeとしてamazon-vpc-routed-eniを利用している.そのため !eth0
を指定しているが,ここは使っているクラスタのnetworking modeに合わせたものを設定すると良い.
ちなみにkiam側でサポートしている値は以下の通り.
ssl-certsのボリュームをマウントする
kiamのhelm chartはssl-certsのボリュームマウントが定義されていない.しかし,
この通り,ホスト側のcertsをマウントする必要があり,それを追記しなければならない.
kopsはubuntuをベースのノードとしているため, /etc/ssl/certs
を指定すれば良い.
なお,この設定はserverとagent両方に入れる必要がある.
- agent: extraHostPathMounts: - name: ssl-certs hostPath: /etc/ssl/certs mountPath: /etc/ssl/certs readOnly: true - server: extraHostPathMounts: - name: ssl-certs hostPath: /etc/ssl/certs mountPath: /etc/ssl/certs readOnly: true
tolerationsを付与する
の通りに,こちらも指定する必要がある.
- server: tolerations: - key: "node-role.kubernetes.io/master" effect: "NoSchedule" operator: "Exists"
nodeSelectorを入れる
kiamは,agentとserverに分かれているという話をしたが,agentはnodeで,serverはmasterで動くことになる. これが,serverをnodeで動かしたりするとエラーになるため,serverに関しては実行するnodeをmasterに限定する必要がある.
そのためにこのような制約を入れる.
- server: nodeSelector: kubernetes.io/role: master
assumeRoleArnを指定する
先程作成した,kiamのserverが利用するIAM Roleをkiam serverのプロセスに教えてやる必要がある.これは自動探索されたりはしない.
- server: assumeRoleArn: arn:aws:iam::123456789:role/kiam-master-role
これで,あとは helmfile sync
すればこの通りのkiamがインストールされる.
PodにIAM Roleを付与する
IAM Roleの準備
各Podが使うIAM Roleを作る.
resource "aws_iam_role" "fascia_prd_pod_role" { name = "fascia-prd-pod-role" path = "/" assume_role_policy = data.template_file.k8s_pod_assume_role_policy.rendered } data "template_file" "k8s_pod_assume_role_policy" { template = file( "${path.module}/aws_iam_role_policies/k8s_pod_assume_role_policy.json.tpl", ) vars = { kiam_master_role_arn = aws_iam_role.kiam_master_role.arn } }
k8s_pod_assume_role_policy.json.tpl
はこちら.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "${kiam_master_role_arn}" }, "Action": "sts:AssumeRole" } ] }
Podにannotationを追加
ここで追加するannotationはkube2iamで使っていたものとまったく同じである.
apiVersion: apps/v1 kind: Deployment metadata: name: fascia-deployment labels: app: fascia spec: replicas: 2 template: metadata: annotations: iam.amazonaws.com/role: fascia-prd-pod-role
Namespaceにannotationを追加
ここはkube2iamと違って,namespaceにもannotationを追加する必要がある. namespaceのannotationでは,namespace内で使えるIAM Roleを正規表現で絞ることができる.
ここで下手な指定をすると後でエラーになるのだが,これについては後述.
apiVersion: v1 kind: Namespace metadata: name: my-namespace labels: name: my-namespace annotations: iam.amazonaws.com/permitted: ".*"
出くわしたエラーたち
Kubernetesのmaster node,もしくはkiam server processのIAM Roleが正しく設定されていない場合
そもそもいかなるRoleを引くこともできないので,kiam serverを起動した段階で,
{ "level":"info", "msg":"started namespace cache controller", "time":"2019-09-07T13:39:47Z" } { "level":"error", "msg":"error requesting credentials: AccessDenied: Access denied\n\tstatus code: 403, request id: f5ad7de6-d174-11e9-9914-65d5429e551b", "pod.iam.role":"fascia-prd-pod-role", "time":"2019-09-07T13:39:48Z" } { "generation.metadata":0, "level":"error", "msg":"error warming credentials: AccessDenied: Access denied\n\tstatus code: 403, request id: f5ad7de6-d174-11e9-9914-65d5429e551b", "pod.iam.role":"fascia-prd-pod-role", "pod.name":"fascia-deployment-d64b565c-psbvk", "pod.namespace":"web-public", "pod.status.ip":"10.0.12.70", "pod.status.phase":"Running", "resource.version":"36307952", "time":"2019-09-07T13:39:48Z" }
このような出力が出る. これが出ている場合は,そもそもIAM Roleを正しく取得できていないので,以下の点を確認する.
- kiamのdocの通りのIAMが設定されているかどうか
- 上のIAM内の,
kiam-server
(※server_node
ではないので注意) が,kiam server起動時の--assume-role-arn
に渡っているかどうか.
を確認すると良い. だいたいこの2つが合致した時点で,先のエラーが消え,
{ "credentials.access.key":"xxxx", "credentials.expiration":"2019-09-07T14:28:23Z", "credentials.role":"fascia-prd-pod-role", "level":"info", "msg":"requested new credentials", "time":"2019-09-07T14:13:23Z" } { "credentials.access.key":"xxxx", "credentials.expiration":"2019-09-07T14:28:23Z", "credentials.role":"fascia-prd-pod-role", "generation.metadata":0, "level":"info", "msg":"fetched credentials", "pod.iam.role":"fascia-prd-pod-role", "pod.name":"fascia-deployment-d64b565c-psbvk", "pod.namespace":"web-public", "pod.status.ip":"10.0.12.70", "pod.status.phase":"Running", "resource.version":"36307952", "time":"2019-09-07T14:13:23Z" }
このようなメッセージに変更される.
annotationが正しく設定されていない場合
これはハマった. kiamは,podのannotation自体はkube2iamとまったく同じではあるのだが, namespace側でもannotationを追記する必要がある.
ちなみにこれを見落とし,podにのみannotationを付けていた場合,
{ "generation.metadata":0, "level":"error", "msg":"pod denied by policy", "pod.iam.requestedRole":"whalebirdorg-prd-pod-role", "pod.iam.role":"whalebirdorg-prd-pod-role", "pod.name":"whalebirdorg-deployment-5b94dc6d94-9b4ms", "pod.namespace":"web-public", "pod.status.ip":"10.0.42.68", "pod.status.phase":"Running", "policy.explanation":"namespace policy expression (empty) forbids role whalebirdorg-prd-pod-role", "resource.version":"36299056", "time":"2019-09-07T14:50:51Z" }
このようなエラーが出続けることになった.
namespaceには先に示したように
apiVersion: v1 kind: Namespace metadata: name: my-namespace labels: name: my-namespace annotations: iam.amazonaws.com/permitted: ".*"
このようなannotationをつけることで,無事認証が通るようになる.
ちなみに,ここで指定しているのは正規表現である.間違った正規表現を指定すると,
{ "generation.metadata":0, "level":"error", "msg":"error checking policy: error parsing regexp: missing argument to repetition operator: `*`", "pod.iam.requestedRole":"whalebirdorg-prd-pod-role", "pod.iam.role":"whalebirdorg-prd-pod-role", "pod.name":"whalebirdorg-deployment-5b94dc6d94-9b4ms", "pod.namespace":"web-public", "pod.status.ip":"10.0.42.68", "pod.status.phase":"Running", "resource.version":"36299056", "time":"2019-09-07T14:55:52Z" }
このようにしっかり怒られることになる.
そして,例えばIAM認証を使うPodがkube-system
に立ち上がる場合,もちろん既存のnamespaceであるkube-system
にもnamespaceを付与する必要がある.
apiVersion: v1 kind: Namespace metadata: name: kube-system labels: name: kube-system annotations: iam.amazonaws.com/permitted: ".*"
まとめ
kube2iamよりは安定して動作している.
ただし,kube2iamにしろkiamにしろ,daemonsetでpodを動かして,IAMへの認証をそちらに捻じ曲げて処理しているに過ぎない.意図せずdaemonsetのpodが死んだり,疎通できなくなった場合には,もちろん認証が通らなくなる. この点では,やはり将来的にはIRSAに移行したほうが安全なのではないだろうか.