EKSでのkubectlコンフィグ設定について

kubectlとは、kubernetes cluster向けのコマンドラインツールです。

Kubectl is a command line interface for running commands against Kubernetes clusters.

Overview of kubectl

kubectlを利用して、cluster上にworkloadsやserviceを作成することになります。当然、kubenetes clusterまで通信できなければ処理指示をできないので、kubectlには、kubernetes clusterへの接続定義を記したコンフィグファイルを用意する必要があります。その設定方法について、AWS EKSを例にとり確認します。

環境

kubectlのインストール

サポートされるkubectlのバージョンは、kubernetes cluster側のバージョンに以下依存します。

You must use a kubectl version that is within one minor version difference of your cluster. For example, a v1.2 client should work with v1.1, v1.2, and v1.3 master.

なお、現在のEKSにてサポートされているkubernetesバージョンは下記です。

The following Kubernetes versions are currently available for new clusters in Amazon EKS:

・1.14.9

・1.13.12

・1.12.10

Amazon EKS Kubernetes Versions

そのため、EKSのKubernetes Cluterのバージョンと同じ、v1.14.9 をインストールします。ダウンロードしているモジュールの v1.14.9 を変えれば、指定バージョンをダウンロードできます。

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.9/bin/linux/amd64/kubectl
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.9", GitCommit:"500f5aba80d71253cc01ac6a8622b8377f4a7ef9", GitTreeState:"clean", BuildDate:"2019-11-13T11:21:43Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}

シェルの補完機能を設定。

$ cat ~/.bash_profile
# kubectl completion
eval "$(kubectl completion bash)"

kubectlのインストールおよびセットアップ

kubectlの接続設定確認

kubectlにおける現在の設定情報は、以下のコマンドより確認できます。下記は何も設定がされていないコンフィグの状態です。

$ kubectl config view
apiVersion: v1
kind: Config
preferences: {}
clusters: []
users: []
contexts: []
current-context: ""

コンフィグファイルでは、 clustersuserscontexts を設定します。それぞれ項目の具体的パラメータは、kubectl configを対話モードで実行したときのパラメータが参考になります。

コンフィグファイルのデフォルトパスは、各OSユーザーのホームディレクトリ配下にある .kube/config となります。

kubectl configの設定

EKSのKubernetes Cluster向けの、kubectl configの設定方法について。

利用するAWS IAMユーザーについて

EKSにおけるClusterへの認証方法は、 aws eks get-token コマンドで取得されるtokenによりなされます。以前は AWS IAM Authenticator for Kubernetes というモジュールでtokenを取得していたようですが、バージョン1.16.308以降のAWS CLIを利用する場合、kubectl実行時の内部でこのコマンドが処理されることでtokenを取得し、接続の認証として利用しています。

コマンド実行例。tokenが払い出されています。

> aws eks get-token --cluster-name <CLUSTER_NAME> --profile xxx
{
  kind:ExecCredential
  apiVersion:client.authentication.k8s.io/v1alpha1
  spec:{}
  status:{
    expirationTimestamp:2020-02-02T22:24:09Z
    token:k8s-aws-v1.xxxxxxxxxxxxxxxx
  }
}

aws eks get-token

なお、 aws eks get-token コマンドを実行するに必要なIAM Policyはありません。何のIAM Policyを持っていないIAM Userでも、このコマンドからtokenを取得できます。この点は、以前の AWS IAM Authenticator for Kubernetes モジュールで利用されていた aws sts get-caller-identity APIの考えと同じです。

No permissions are required to perform this operation. If an administrator adds a policy to your IAM user or role that explicitly denies access to the sts:GetCallerIdentity action, you can still perform this operation.

aws sts get-caller-identity

これは、本コマンドで取得されるtokenは、あくまでKubernetes Clusterへの認証時に利用されるものであり、実際のKubernetesの操作権限の管理(認可の処理)は、KubernetesのRBACが担っているためとなります。

設定方法1 eks update-kubeconfig コマンドを利用する方法

そもそもkubectl configを作成してくれるコマンドがAWS CLI側に用意されているため、そのコマンドを実行することで、コンフィグ情報を設定してくれます。

> aws eks update-kubeconfig --name CLUSTER_NAME --profile xxxx

これにて、kubectlに必要設定情報を登録してくれます。

aws eks update-kubeconfig

設定方法2 手動で編集する方法

eks update-kubeconfig コマンドを利用しない場合について。公式ドキュメントがあるので、それを参考に。

kubeconfig を Amazon EKS 用に作成します

サンプルとなるyamlファイルが下記です。

apiVersion: v1
kind: Config
preferences: {}
clusters:
- name: eks/cluster-sample
  cluster:
    server: https://xxxxx.sk1.ap-northeast-1.eks.amazonaws.com
    certificate-authority-data: LS0tLXXXXXXXXXXX
users:
- name: test-user
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      command: aws
      args:
        - eks
        - get-token
        - --cluster-name
        - <cluster-name>
        - --region
        - <region-name>
        # - --role
        # - <role-arn>
      env:
        - name: AWS_PROFILE
          value: <profile-name>
contexts:
- name: eks/cluster-sample
  context:
    cluster: eks/cluster-sample
    user: test-user
current-context:

EKS Clusterの設定情報を取得するコマンドです。servercertificate-authority-data の値を確認できます。

$ aws eks describe-cluster --name <cluster-name> --region ap-northeast-1 --profile xxx | jq '.cluster | { server: .endpoint, cert_data: .certificateAuthority.data }'
{
  "server": "https://xxxxx.sk1.ap-northeast-1.eks.amazonaws.com",
  "cert_data": "LS0tLXXXXXXXXXXX"
}

aws eks describe-cluster

kubectl config側の設定は、これで完了です。

接続してみる

では、kubectlを利用して、kubernetetesに接続してみます。

定義したコンフィグ情報から、現在利用されるcontextを確認します。コンフィグファイル内の current-context で設定したものが、この値となります。以下は空白とした場合。

$ kubectl config current-context
error: current-context is not set

設定可能なcontext情報を表示。

$ kubectl config get-clusters
NAME
eks/cluster-sample

以下のコマンドを利用することで、接続時に利用するcontextを指定できます。

$ kubectl config use-context eks/cluster-sample
Switched to context "eks/cluster-sample".

改めて接続を試してみます。

$ kubectl get service
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   172.20.0.1   <none>        443/TCP   60m

一方で、以下の Unauthorized と表示される場合、Kubernetes側での権限が不足しています。これはKubernetesにおける認可の処理は、あくまでもKubernetes側のRBAC(Role Base Access Contorl)の設定で管理されており、その設定により起こるためとなります。

$ kubectl get service
error: You must be logged in to the server (Unauthorized)

EKSのKubernetes Clusterを作成した場合、作成したIAM Userの情報は自動的にRBACに登録されるため、Cluster作成ユーザーは認可が通るものの、それ以外のIAMユーザーはRBACの設定はされないので、Unauthorizedとなってしまう訳です。

クラスター認証の管理

kubernetes側の認可設定

KubernetesのRBACについて

Unauthorizedとなってしまうのを解決する前に、KubernetesのRBACの概念について確認します。以下ようなイメージとなっています。

f:id:goodbyegangster:20200214222433p:plain


RoleClusterRole は、Kubernetesにおいて権限のポリシーを定義したもの(例えば、podsのget権限やsecretsのlist権限のような)となります。

In the RBAC API, a role contains rules that represent a set of permissions. Permissions are purely additive (there are no “deny” rules). A role can be defined within a namespace with a Role , or cluster-wide with a ClusterRole .

Role and ClusterRole

2つの違いは、上記の引用の通り、Roleがnamepaceに縛られるのに対して、ClusterRoleはnamespaceに縛られず(その名前の通り)Cluster全体に適用されるものになります。

またClusterRoleは、他のClusterRoleを継承してClusterRoleを定義することのできる aggregationRule という機能を有します。

As of 1.9, ClusterRoles can be created by combining other ClusterRoles using an aggregationRule . The permissions of aggregated ClusterRoles are controller-managed, and filled in by unioning the rules of any ClusterRole that matches the provided label selector.

Aggregated ClusterRole

作成済みClusterRoleの一覧を表示するコマンド。

$ kubectl get clusterrole
NAME                                                                   AGE
admin                                                                  24m
aws-node                                                               24m
cluster-admin                                                          24m
edit                                                                   24m
...

定義されているポリシーを確認するコマンドはこちらです。editというClusterRoleのポリシーを表示。

$ kubectl describe clusterrole edit
Name:         edit
Labels:       kubernetes.io/bootstrapping=rbac-defaults
              rbac.authorization.k8s.io/aggregate-to-admin=true
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources                                Non-Resource URLs  Resource Names  Verbs
  ---------                                -----------------  --------------  -----
  configmaps                               []                 []              [create delete deletecollection patch update get list watch]
  endpoints                                []                 []              [create delete deletecollection patch update get list watch]
  persistentvolumeclaims                   []                 []              [create delete deletecollection patch update get list watch]
  pods                                     []                 []              [create delete deletecollection patch update get list watch]
...

RoleBindingClusterRoleBinding は、前述のRoleやClusterRoleと、後述のServiceAccountやUser、Groupを紐付けるものとなります。

A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted. Permissions can be granted within a namespace with a RoleBinding , or cluster-wide with a ClusterRoleBinding .

RoleBinding and ClusterRoleBinding

RoleBindingがnamespaceに縛られ、同一namespaceにあるRoleにしか紐付けられないのに対して、ClusterRoleBindingはnamespaceによる制限を受けません。また、Role/CluserRoleとRoleBinding/ClusterRoleBindingは、1対1の関係になります。RoleBinding/ClusterRoleBindingとUserは、1対Nの関係になります。

定義されているClusterRoleBindingの一覧を表示するコマンドはこちらです。

$ kubectl get clusterrolebinding
NAME                                                   AGE
aws-node                                               71m
cluster-admin                                          71m
eks:fargate-manager                                    71m
eks:kube-proxy                                         71m
...

ClusterRoleBindingの詳細を確認するコマンド。以下はcluster-adminというClusterRoleBindingの詳細を確認しています。

$ kubectl describe clusterrolebinding cluster-admin
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind   Name            Namespace
  ----   ----            ---------
  Group  system:masters

ServiceAccournt は、起動したpodに対して、アイデンティティ情報(認証情報)を付与するものとなります。

When you (a human) access the cluster (for example, using kubectl), you are authenticated by the apiserver as a particular User Account (currently this is usually admin, unless your cluster administrator has customized your cluster). Processes in containers inside pods can also contact the apiserver. When they do, they are authenticated as a particular Service Account (for example, default).

Configure Service Accounts for Pods

AWSにおけるIAM Roleのようなもの、と理解しています。以下の引用は、ServiceAccountとUsersに違いについての説明文です。

Kubernetes distinguishes between the concept of a user account and a service account for a number of reasons:

・User accounts are for humans. Service accounts are for processes, which run in pods.

・User accounts are intended to be global. Names must be unique across all namespaces of a cluster, future user resource will not be namespaced. Service accounts are namespaced.

・Typically, a cluster’s User accounts might be synced from a corporate database, where new user account creation requires special privileges and is tied to complex business processes. Service account creation is intended to be more lightweight, allowing cluster users to create service accounts for specific tasks (i.e. principle of least privilege).

・Auditing considerations for humans and service accounts may differ.

・A config bundle for a complex system may include definition of various service accounts for components of that system. Because service accounts can be created ad-hoc and have namespaced names, such config is portable.

User accounts vs service accounts

定義されているServiceAccountの一覧を表示するコマンドはこちらです。

$ kubectl get serviceaccount --all-namespaces
NAMESPACE         NAME                                 SECRETS   AGE
default           default                              1         76m
kube-node-lease   default                              1         76m
kube-public       default                              1         76m
kube-system       attachdetach-controller              1         76m
kube-system       aws-cloud-provider                   1         76m
...

ServiceAccountの詳細を確認するコマンドです。aws-cloud-providerというServiceAccountの詳細を表示しています。

$ kubectl get serviceaccount --all-namespaces --output yaml --field-selector metadata.name=aws-cloud-provider
apiVersion: v1
items:
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2020-02-14T07:08:37Z"
    name: aws-cloud-provider
    namespace: kube-system
    resourceVersion: "197"
    selfLink: /api/v1/namespaces/kube-system/serviceaccounts/aws-cloud-provider
    uid: d20ff33e-4ef8-11ea-8328-0e79f2b1f57c
  secrets:
  - name: aws-cloud-provider-token-h9rdd
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

kubectl describe serviceaccount <sa名> というコマンドでは、 --all-namespaces オプションをつけると怒られてしまうため、 --output yaml オプションを指定して(これを付けると、設定の詳細まで出力してくれます)、 --field-selector オプションで表示範囲を絞っています。

フィールドセレクター(Field Selectors)

また、ServiceAccountは、アイデンティティ情報を付与されたものでありますから、該当の情報が Secretリソース に格納されています。Secretリソースとは、kube-apiserverへの認証情報やコンテナが接続するDBの認証情報を格納するための仕組みです。AWSにおける、Parameter StoreやSecrets Managerみたいなもの、という理解をしています。

定義されているSecretsリソースの一覧を表示するコマンド。

$ kubectl get secrets --all-namespaces
NAMESPACE         NAME                                   TYPE                                  DATA   AGE
default           default-token-8fkhr                    kubernetes.io/service-account-token   3      95m
kube-node-lease   default-token-klmbj                    kubernetes.io/service-account-token   3      95m
kube-public       default-token-ql9xl                    kubernetes.io/service-account-token   3      95m
kube-system       attachdetach-controller-token-2gmzl    kubernetes.io/service-account-token   3      95m
kube-system       aws-cloud-provider-token-h9rdd         kubernetes.io/service-account-token   3      95m

詳細を確認するコマンド。

$ kubectl get secrets --all-namespaces --output yaml --field-selector metadata.name=aws-cloud-provider-token-h9rdd
apiVersion: v1
items:
- apiVersion: v1
  data:
    ca.crt: LS0tLS1C....
    namespace: a3ViZS1zeXN0ZW0=
    token: ZXlKaGJHY....
  kind: Secret
  metadata:
    annotations:
      kubernetes.io/service-account.name: aws-cloud-provider
      kubernetes.io/service-account.uid: d20ff33e-4ef8-11ea-8328-0e79f2b1f57c
    creationTimestamp: "2020-02-14T07:08:37Z"
    name: aws-cloud-provider-token-h9rdd
    namespace: kube-system
    resourceVersion: "193"
    selfLink: /api/v1/namespaces/kube-system/secrets/aws-cloud-provider-token-h9rdd
    uid: d21f13b7-4ef8-11ea-8328-0e79f2b1f57c
  type: kubernetes.io/service-account-token
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

tokenの情報が格納されていることが分かります。ServiceAccout(を付与されたpod)は、このtokenを利用することでkube-apiserverと認証処理をしている訳ですね。


user は、その通りKubernetes Clusterに接続する利用者であり、 group はそのuserをまとめたものです。Kubernetesにおけるユーザーやグループを作成/管理する考え方に関しては、以下のフォーラムの投稿が参考になります。

In kubernetes you don't need to create the user/groups. They are determined by an authenticator and just used by kubernetes, so any user/groups returned by your mappings will be 'created' and kubernetes will use those values.

QUESTION: How to create the k8s users and groups to map to?

OSやRDBでのユーザーの考え方と違い、Kubernetesではユーザーを管理する機能を持っていません。それは、他のID Provider (EKSであればIAM)が担っている部分で、KubernetesのRBACでは、そのIDとKubernetes内のRoleのマッピング定義を管理するのみとなります。またグループとは、RoleBinding/ClusterRoleBindingになります。(ユーザーを束ねるものという機能は一緒であるため)

RBCAのマッピング定義を確認するには(つまり、Kubernetesに定義されているユーザーやグループを確認するには)、以下のコマンドからとなります。

$ kubectl get rolebinding,clusterrolebinding --all-namespaces --output json |
jq -r '.items[] |
{ RoleKind:.roleRef.kind, RoleName:.roleRef.name, ClusterRoleBindingKind:.kind, ClusterRoleBindingName:.metadata.name, SubjectKind:.subjects[]?.kind, SubjectName:.subjects[]?.name } |
[ .RoleKind, .RoleName, .ClusterRoleBindingKind,  .ClusterRoleBindingName, .SubjectKind, .SubjectName ] |
@tsv' | column -t
Role         system:controller:bootstrap-signer                    RoleBinding         system:controller:bootstrap-signer                    ServiceAccount  bootstrap-signer
Role         eks:fargate-manager                                   RoleBinding         eks:fargate-manager                                   User            eks:fargate-manager
Role         eks:node-manager                                      RoleBinding         eks:node-manager                                      User            eks:node-manager
Role         extension-apiserver-authentication-reader             RoleBinding         system::extension-apiserver-authentication-reader     User            system:kube-controller-manager
Role         extension-apiserver-authentication-reader             RoleBinding         system::extension-apiserver-authentication-reader     User            system:kube-scheduler
Role         extension-apiserver-authentication-reader             RoleBinding         system::extension-apiserver-authentication-reader     User            system:kube-controller-manager
...

--output オプションでjsonを指定すると、作成済のClusterBindingとClusterRoleBindingの定義情報をJSONで一気に出力してくれます。出力情報はあまりに膨大で、そのままではとても確認できないので、 jq コマンドと column コマンドで整形しています。kubectlには JSONPath というJSONを整形してくれるオプションがあるのですが、僕には分かりにくく、jqで整形しています。

JSONPath Support

EKSでのRBAC設定方法

EKSの場合、上記で説明したRBCAの仕組みの中に、AWSのIAMユーザー、またはIAMロールの情報を加えてあげれば良い訳ですが、そのための方法が aws-auth というConfigMapを利用する方法となります。

ConfigMapとは、Kubernetesで用意されているKVSぽい機能で、AWSにおけるParameter Storeみたいなものです。ConfigMapと上で出てきたSecretとの違いは、機密情報を保管するかどうかという話みたいで、これにはkubernetesコンポーネントetcd という存在に関係があるみたいです。(etcdについては、勉強不足でまだ理解できてないです)

いずれにしろ、 aws-auth というConfigMapに利用したいIAM情報を登録することで、Kubernetesへの認可設定ができるとのことです。

最初は、Amazon EKS クラスターの作成者のみがクラスターを設定するための system:masters 権限を持っています。system:masters 権限を他のユーザーとロールに拡張するには、aws-auth ConfigMap を Amazon EKS クラスターの設定に追加する必要があります。ConfigMap により、ユーザーやロールなどの他の IAM エンティティに Amazon EKS クラスターへのアクセス権限が付与されます。

Amazon EKS でクラスターを作成した後、他のユーザーやロールにアクセス権限を付与する方法について教えてください。

AWS上の公式ドキュメントを頼りに設定してみます。

クラスターのユーザーまたは IAM ロールの管理

現在のaws-auth登録内容を確認します。

$ kubectl describe configmap -n kube-system aws-auth
Name:         aws-auth
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
mapRoles:
----
- groups:
  - system:bootstrappers
  - system:nodes
  rolearn: arn:aws:iam::123456789012:role/String
  username: system:node:{{EC2PrivateDNSName}}

ここでaws-authが見つからないようなエラーメッセージが返る場合、公式ドキュメントにあるaws-auth適用手順を参照してください。通常にEKS Cluster作成時に、一緒にデプロイされるとのこと。

現行設定情報をyamlファイルとして抽出します。以下コマンドを実行して現行設定情報を表示し、そいつをファイルに貼り付けます。 kubectl apply するには不要な項目があるので、それら項目を削除します。(恐らくもっと良い方法がある気がするのですが...)

$ kubectl edit -n kube-system configmap/aws-auth

初期のEKS Cluster状態であれば、下記のような定義になるはずです。

aws-auth.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::123456789012:role/String
      username: system:node:{{EC2PrivateDNSName}}

公式ドキュメントにある通り、 data: セクションにIAMユーザー情報を追加します。今回は、デフォルトで用意されているClusterRole view という権限を、追加IAMユーザーに付与したいと思います。

まず、ClusterRole view に紐づくClusterRoleBindingが存在しないため、ClusterRoleBindingを作成します。デフォルトで用意されているRoleとRoleBindingは、Kubernetes公式ページで確認できます。

f:id:goodbyegangster:20200214222154p:plain

Default Roles and Role Bindings

custom:view というClusterRoleBindingを作成するマニフェストファイルを用意します。紐付けるSubjectとして custom:viewers というグループを定義しています。

clusterrolebinding-view.yml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: custom:view
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io
subjects:
  - kind: Group
    name: custom:viewers
    apiGroup: rbac.authorization.k8s.io

適用します。

$ kubectl apply -f ./clusterrolebinding-view.yml
clusterrolebinding.rbac.authorization.k8s.io/custom:view created

aws-auth.ymlファイルに、追加したいIAMユーザー情報を追加します。IAMユーザー no-policy-user を、グループ custom:viewers に紐づけています。

aws-auth.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::123456789012:role/String
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - userarn: arn:aws:iam::123456789012:user/no-policy-user
      username: no-policy-user
      groups:
        - custom:viewers

適用します。

$ kubectl apply -f /mnt/c/tmp/aws-auth.yml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
configmap/aws-auth configured

確認

実際に確認してみます。

kubectlのconfigファイルにて、IAMユーザー no-policy-user を利用するように変更してみます。

$ kubectl config view
apiVersion: v1
clusters:
()
      command: aws
      env:
      - name: AWS_PROFILE
        value: no-policy-user

podの取得をしてみます。

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-ww2np             1/1     Running   0          69m
kube-system   coredns-58986cd576-dkljh   1/1     Running   0          70m
kube-system   coredns-58986cd576-mkrwj   1/1     Running   0          70m
kube-system   kube-proxy-828qm           1/1     Running   0          69m

取得できます。

podを作成してみます。

$ kubectl run nginx --image=nginx
Error from server (Forbidden): deployments.apps is forbidden: User "no-policy-user" cannot create resource "deployments" in API group "apps" in the namespace "default"

view の権限しかないため、 Forbidden と怒られます。