EKSクラスターへ「kubectl」コマンドでアクセスする際の認証・認可の仕組みと設定
みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。
今回は、Amazon EKS における「クラスターへアクセスする際の認証・認可」について解説したいと思います。
はじめに
みなさんは、EKSクラスターを構築して「kubectl」コマンドで最初にアクセスする際に、どのような手順を実施しているでしょうか?
eksctl
コマンドを使用してクラスターを構築する場合は、特に何もすることなく、すぐにkubectl get nodes
等のコマンドを使ってクラスターへアクセスできると思います。
また、マネジメントコンソールやAWS CLIを使用する場合は、クラスター構築を行ったIAMユーザーでaws eks update-kubeconfig
コマンドを実行することで、kubectl
によるクラスターへのアクセスが行えるようになると思います。
いずれの場合でも、kubectl
コマンドを使ったクラスターへのアクセスは「フルアクセス権限」で行われます。
そのため、アクセス認証やアクセス権限の設定や仕組みについて特に意識をしなくても、支障なく利用することができます。
しかし、EKSクラスターを本番運用することを考えた場合は、クラスター構築ユーザーによる「フルアクセス権限」でのアクセスのみで全てを運用する訳にはいかないと思います。
「開発担当者」や「運用担当者」など、ユーザーの種類に応じたアクセス権限を使い分ける必要が出てきますよね。
クラスターアクセスの認証・認可の仕組み
Kubernetesでは、ユーザーがクラスターへアクセスする際の「認証」の方法がいくつか用意されています。
例を挙げると以下のようなものがあります。
- クライアント証明書による認証
- パスワードによる認証
- OpenID Connectを使った認証
- 認証Webhookを使った外部認証サービスとの連係
EKSでは「認証Webhook」を使った認証がサポートされています。
(https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/managing-auth.html に掲載の図版をアレンジしました)
(1) kubectl
コマンドは、現在のIAMユーザーのセッションに基いたトークンを生成して、Kubernetes APIへ渡します。
(2) Kubernetes APIは、渡されたトークンが正しいIAMユーザーのものであるのか、(Kubernetesから見ると外部サービスである) IAMに対して検証を依頼します。
この(1)~(2)の流れが「認証Webhook」の仕組みです。
認証が完了したIAMユーザーはKubernetes上の「ユーザー」として取り扱われ、続けてRBACによる「認可」が行われます。
(3) Kubernetesに備わるRBACの仕組みを使って、「ユーザー」に対するKubernetes上の権限を決定します。
(4) kubectl
コマンドから要求されたKuberenesに対するアクセス要求が、「認可」によって決定した権限で許可されるものか否かを返答します。
ざっくりと仕組みを説明したところで、具体的な設定を行ってみましょう。
設定方法
準備
EKSクラスターを作成する
サクっとeksctl
コマンドで作成します。
オプションパラメーターは適宜設定してください。
$ eksctl create cluster \ --name example \ --version 1.16 \ --managed \ --nodegroup-name ng-example \ --node-type t3.micro \ --nodes 2 \ --nodes-min 2 \ --nodes-max 2
クラスターが作成されましたら、kubeconfigファイル (~/.kube/config
) が作成されていること、および、クラスターへアクセスできることを確認しておきます。
$ kubectl get nodes NAME STATUS ROLES AGE VERSION ip-192-168-24-200.ap-northeast-1.compute.internal Ready <none> 14m v1.16.8-eks-e16311 ip-192-168-83-148.ap-northeast-1.compute.internal Ready <none> 14m v1.16.8-eks-e16311
IAMユーザーを作成する
EKSクラスターへのアクセスに用いるIAMユーザーを、何パターンか用意します。
例として、user01
、user02
、user03
の3ユーザーを作成します。
「プログラムによるアクセス」にチェックを入れて、アクセスキーIDとシークレットアクセスキーを発行してください。
ユーザーを作成しましたら、可能であればクライアント側 (Linux、MacOSなど) にもテスト用のユーザー (例えばpcuser1
、pcuser2
、pcuser3
) を作成して、各OSユーザー毎にIAMユーザーのアクセスキーID・シークレットアクセスキーを設定してください。
pcuser1@mypc:~$ aws configure AWS Access Key ID [None]: (IAMユーザーuser01のアクセスキーID) AWS Secret Access Key [None]: (IAMユーザーuser01のシークレットアクセスキー) Default region name [None]: ap-northeast-1 Default output format [None]: (省略)
pcuser2@mypc:~$ aws configure AWS Access Key ID [None]: (IAMユーザーuser02のアクセスキーID) AWS Secret Access Key [None]: (IAMユーザーuser02のシークレットアクセスキー) Default region name [None]: ap-northeast-1 Default output format [None]: (省略)
pcuser3@mypc:~$ aws configure AWS Access Key ID [None]: (IAMユーザーuser03のアクセスキーID) AWS Secret Access Key [None]: (IAMユーザーuser03のシークレットアクセスキー) Default region name [None]: ap-northeast-1 Default output format [None]: (省略)
OSユーザーの作成が難しければ、単一のOSユーザーに対して3つのIAMユーザーを設定しても構いません。
IAMユーザーへEKSクラスターの参照権限を付与する
EKSクラスターの参照権限 (eks:DescribeCluster
) を設定したIAMポリシーを作成して、各IAMユーザーへアタッチしてください。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "eks:DescribeCluster", "Resource": "*" } ] }
この権限は、後述するaws eks update-kubeconfig
コマンドを実行するために必要です。
kubeconfigファイルを構成する
では、設定を始めていきます。
まず、OSユーザーpcuser1
でログインします。
このユーザーはまだkubeconfigファイル (~/.kube/config
) が作成されていないはずですので、これから新規に作成します。
(もし1つのOSユーザーのみで進めているのであれば、既に作成されているkubeconfigファイルをリネーム等で退避しておいてください)
kubeconfigファイルを作成するには、aws eks update-kubeconfig
コマンドを実行します。
指定するオプションは「対象のクラスター名」のみです。
$ aws eks update-kubeconfig --name example Added new context arn:aws:eks:ap-northeast-1:123456789012:cluster/example to /home/pcuser1/.kube/config
これでkubeconfigファイルが作成されましたので、中身を見てみましょう。
(見易いように行の順番の並び替えなどを行っています)
$ cat ~/.kube/config apiVersion: v1 kind: Config preferences: {} clusters: - name: arn:aws:eks:ap-northeast-1:123456789012:cluster/example cluster: certificate-authority-data: (クラスターのCA証明書) server: (クラスターのAPIエンドポイントURI) users: - name: arn:aws:eks:ap-northeast-1:123456789012:cluster/example user: exec: apiVersion: client.authentication.k8s.io/v1alpha1 command: aws args: - --region - ap-northeast-1 - eks - get-token - --cluster-name - example contexts: - name: arn:aws:eks:ap-northeast-1:123456789012:cluster/example context: cluster: arn:aws:eks:ap-northeast-1:123456789012:cluster/example user: arn:aws:eks:ap-northeast-1:123456789012:cluster/example current-context: arn:aws:eks:ap-northeast-1:123456789012:cluster/example
ファイルは4つのブロックで構成されています。
「clusters」
接続先クラスターの情報です。
この情報をEKSから取得するためにeks:DescribeCluster
権限が必要だったという訳です。
「users」
クラスターへ接続するユーザーの情報です。
「仕組み」で説明した通り、EKSでは「認証Webhook」を使ってユーザーの認証を行います。
ここには、認証Webhookで用いる「トークン」を生成する手続きについて記述されています。
exec:
の配下に記述されているcommand:
やargs:
の記述内容を合わせてみると、以下のコマンドラインになっていることが分かります。
aws eks get-token --region ap-northeast-1 --cluster-name example
試しに実行してみると、以下のような結果が返ってくると思います。
(結果は見易いように整形しています)
$ aws eks get-token --region ap-northeast-1 --cluster-name example { "kind": "ExecCredential", "apiVersion": "client.authentication.k8s.io/v1alpha1", "spec": {}, "status": { "expirationTimestamp": "2020-05-27T06:26:55Z", "token": "k8s-aws-v1.(以下トークン文字列が並びます)" } }
「contexts」
「コンテキスト」とは、「接続先クラスター」と「接続するユーザー」をセットにした情報です。
同じクラスターへ接続する場合でも、使用するユーザーによって権限などが異なりますので、kubectlでは「コンテキスト」単位で接続を取り扱います。
「current-context」
コンテキストが複数定義されている場合、現在選択されているコンテキストがどれであるのかを示す情報です。
コンフィグマップ「aws-auth」を編集する
「仕組み」の説明で「認証が完了したIAMユーザーはKubernetes上の『ユーザー』として取り扱われる」と書きましたが、そのためには、IAMユーザーとKubernetes上のユーザーの「対応付け」(マッピング) が必要になります。
マッピングの設定は「aws-auth」コンフィグマップへ記述します。
(「コンフィグマップ」(ConfigMap) とは、Kubernetesにおいて主に各種設定情報を格納するために使われるリソースです)
※ ここからの手順は、user01
ではなく、EKSクラスターを作成した時のIAMユーザー権限で実施してください。(user01
にはまだ何の権限もありません)
クラスターに定義されているコンフィグマップの一覧を確認します。
$ kubectl get --all-namespaces configmaps NAMESPACE NAME DATA AGE kube-system aws-auth 1 4h40m kube-system coredns 1 4h44m kube-system eks-certificates-controller 0 4h44m kube-system extension-apiserver-authentication 6 4h44m kube-system kube-proxy 1 4h44m kube-system kube-proxy-config 1 4h44m
既に「aws-auth」コンフィグマップは存在しますね。
kubectl edit
コマンドを使って「aws-auth」コンフィグマップの内容を編集します。
$ kubectl edit -n kube-system configmap/aws-auth
viエディタが開いて、現在の「aws-auth」コンフィグマップの内容が表示されます。
ハイライト表示されているmapUsers:
以降の行を追記します。
※ 既存の記述行は、EKSクラスターがワーカーノードを管理するために必要となる重要な記述です。決して削除したり変更したりしないでください。
apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: arn:aws:iam::123456789012:role/eksctl-example-nodegroup-ng-examp-NodeInstanceRole-6U5725EVEOU8 username: system:node:{{EC2PrivateDNSName}} groups: - system:bootstrappers - system:nodes mapUsers: | - userarn: arn:aws:iam::123456789012:user/user01 username: user01 groups: - dev-group
mapUsers:
には、次の要素を記述します。
userarn
: マッピング対象となるIAMユーザーのARNを記述しますusername
: Kubernetes上で「ユーザー」として識別される際の名前を指定しますgroups
: ユーザーを所属させるKubernetes上の「グループ」を指定します
コンフィグマップの編集を終えて保存に成功すると、以下のように表示されます。
$ kubectl edit -n kube-system configmap/aws-auth configmap/aws-auth edited
なお、Kubernetesにおいて「ユーザー」「グループ」は概念として存在しますが、マニフェスト等で定義可能な「リソース」には含まれません。
つまり、Kubernetes上で予め「ユーザー」「グループ」を定義しておくのではなく、アクセス要求元が認証されることによって「ユーザー」(およびユーザーが所属する「グループ」) として取り扱われます。
(OSやAWSのアイデンティティ管理の仕組みとは少し違いますので、気に留めておいてください)
RBACの設定:「ロール」と「ロールバインディング」を定義する
ここまでの手順で「認証」のための設定が終わりましたので、次は「認可」の設定を行いましょう。
「仕組み」で説明した通り、「認可」はKubernetesのRBACの仕組みを使って行われます。
RBACの設定に必要なリソースには、以下のものがあります。
- 「ロール」: Kuberntesの各種リソースに対するアクセス権限を定義するもの
- 「ロールバインディング」: アクセス要求元である「ユーザー」や「グループ」に対して「ロール」の紐付け (バインド) を定義するもの
「ロール」の定義
まずは「ロール」の方から見て行きましょう。
ロールは、一から定義してもよいのですが、良く使われるアクセス権限を定義したデフォルトのロールがいくつか用意されています。
ロール | 説明 |
---|---|
cluster-admin | クラスターに対する全てのアクセス権限が与えられます。 いわゆる「rootユーザー」の位置付けです。 |
admin | ほぼ全てのアクセス権限が与えられますが、対象範囲がクラスター全体に及ぶ一部の権限を持っていません。 通常運用における「管理者」に割り当てる場合はこちらを使います。 |
edit | 多くのリソースに対する「編集」(書き込み/読み取り) を行える権限が与えられます。 ロールやロールバインディングを変更する権限は持っていませんので、自分や他人の権限を変更することはできません。 |
view | 多くのリソースに対する「参照」(読み取りのみ) を行える権限が与えられます。 |
上記のデフォルトロールに当てはまらないアクセス権限を定義したい場合は、新たなロールを定義することになります。
例えば、ポッドの参照のみ行えるアクセス権限は、以下のロールで定義できます。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]
$ kubectl apply -f pod-reader.yaml clusterrole.rbac.authorization.k8s.io/pod-reader created
ただし、実際には「ポッドの参照のみ」のアクセス権限ではできることが限られますので、実運用で利用するロールでは、いろいろなリソース種類 (resources) に対するアクション (verbs) の組み合わせを細かく定義することになるかと思います。
「ロールバインディング」の定義
次に、「ロールバインディング」を定義します。
ここでは、デフォルトのロール「edit」を、「aws-auth」コンフィグマップの編集で記述したグループ「dev-group」にバインドしてみます。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: cluster-editor roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: edit subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: dev-group
roleRef:
配下には、バインド対象のロールを指定します。
subjects:
配下には、バインド対象のユーザーまたはグループを指定します。
※ なお、ロールをユーザーに対してバインドすることも可能ですが、管理が煩雑になるためグループ単位でのバインドをお勧めします。
マニフェストを適用してロールバインディングを作成します。
$ kubectl apply -f cluster-editor.yaml clusterrolebinding.rbac.authorization.k8s.io/cluster-editor created
これで、全ての設定が終わりました。
設定したユーザーでクラスターへアクセスできることを確認する
OSユーザーpcuser1
でログインして、IAMユーザーuser01
の権限でEKSクラスターへのアクセスを試みます。
ポッドの一覧を参照してみましょう。
(まだポッドを作成していないので、デフォルトで動作しているシステム関連ポッドを参照します)
$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-s8gsn 1/1 Running 0 7h40m kube-system aws-node-s9b55 1/1 Running 0 7h40m kube-system coredns-cdd78ff87-h2wtf 1/1 Running 0 7h44m kube-system coredns-cdd78ff87-hr6p7 1/1 Running 0 7h44m kube-system kube-proxy-gsnmd 1/1 Running 0 7h40m kube-system kube-proxy-z7dtd 1/1 Running 0 7h40m
無事、クラスターへアクセスしてポッド一覧を参照できました。
では、こちらはどうでしょう?
$ kubectl get nodes Error from server (Forbidden): nodes is forbidden: User "user01" cannot list resource "nodes" in API group "" at the cluster scope
「アクセス権限が無い」とエラーになってしまいました。
デフォルトロール「edit」には、ポッドの参照権限はありますが、ワーカーノードに関する権限は付与されていないためです。
今回設定したRBACの構成を図で表すと、下図のようになります。
「名前空間」単位でアクセス制御を行う
Kubernetesには「ネームスペース」(Namespace) という概念があります。
ネームスペースは文字通り、クラスター内のリソースを「名前空間」によって識別・管理するためのものです。
Kubernetesでクラスターを構築した直後は「default」という名前のネームスペースが用意されていますが、本番業務で利用するクラスターであれば、アプリケーション・サービス・プロジェクトなどの単位でネームスペースを分割するのが普通だと思います。
ここでは、クラスターが複数のプロジェクトで利用されている前提で、各プロジェクトに所属するユーザーが自分のプロジェクトのネームスペースのみにアクセスできる設定を行ってみたいと思います。
kubeconfigファイルを構成する
今回は、IAMユーザーuser02
とuser03
を使います。
OSユーザーpcuser2
でログインして、 以下のコマンドを実行します。
$ aws eks update-kubeconfig --name example Added new context arn:aws:eks:ap-northeast-1:123456789012:cluster/example to /home/pcuser2/.kube/config
これでkubeconfigファイルが作成されました。
中身はuser01
の時と同じになっていると思います。
同様にして、OSユーザーpcuser3
でもコマンドを実行してkubeconfigファイルを作成します。
コンフィグマップ「aws-auth」を編集する
ここからは、また、EKSクラスターを作成した時のIAMユーザー権限で作業を実施してください。
kubectl edit
コマンドを使って「aws-auth」コンフィグマップにuser02
、user03
の情報を追加します。
$ kubectl edit -n kube-system configmap/aws-auth
apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: arn:aws:iam::123456789012:role/eksctl-example-nodegroup-ng-examp-NodeInstanceRole-6U5725EVEOU8 username: system:node:{{EC2PrivateDNSName}} groups: - system:bootstrappers - system:nodes mapUsers: | - userarn: arn:aws:iam::123456789012:user/user01 username: user01 groups: - dev-group - userarn: arn:aws:iam::123456789012:user/user02 username: user02 groups: - project-a-group - userarn: arn:aws:iam::123456789012:user/user03 username: user03 groups: - project-b-group
user02
はproject-a-group
というグループ、user03
はproject-b-group
というグループにそれぞれ参加させます。
ネームスペースの作成
次に、RBACによる認可の設定を行いたいところですが、、、その前に、各プロジェクトのネームスペースを作成します。
$ kubectl create namespace project-a namespace/project-a created $ kubectl create namespace project-b namespace/project-b created
RBACの設定:「ロール」と「ロールバインディング」を定義する
今回は、ロールとしてデフォルトロール「admin」を使います。
「各プロジェクトのメンバーに対して、自分のプロジェクトのネームスペースに対するフル権限を与える」という前提です。
プロジェクト毎のロールバインディングを作成していきます。
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: project-a-admin namespace: project-a roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: project-a-group
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: project-b-admin namespace: project-b roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: project-b-group
$ kubectl apply -f project-a-admin.yaml rolebinding.rbac.authorization.k8s.io/project-a-admin created $ kubectl apply -f project-b-admin.yaml rolebinding.rbac.authorization.k8s.io/project-b-admin created
さきほどのロールバインディングの定義とは、2つ異なる点があります。
- 2行目が
kind: ClusterRoleBinding
ではなくkind: RoleBinding
になっている - 5行目に
namespace: project-a
(あるいはnamespace: project-b
) の記述が追加されている
「ClusterRole」「ClusterRoleBinding」と「Role」「RoleBinding」の違い
ClusterRole
/ClusterRoleBinding
は「スコープがクラスター全体」であるのに対してRole
/RoleBinding
は「スコープがネームスペース単位」であるという違いがあります。
ClusterRole
: クラスター全体で共有できるロールを定義するRole
: 特定のネームスペースのみで使用できるロールを定義する-
ClusterRoleBinding
: クラスター全体にロールを適用する RoleBinding
: 特定のネームスペースのみにロールを適用する
Role
、RoleBinding
の定義では、ネームスペースを指定する必要があるためmetadata:
直下にnamespace:
の記述が必要です。
ロールとロールバインディングの関係は以下の通りです。
ロール | ロールバインディング | 組み合わせ可否 |
---|---|---|
ClusterRole |
ClusterRoleBinding |
可 |
ClusterRole |
RoleBinding |
可 |
Role |
ClusterRoleBinding |
不可 |
Role |
RoleBinding |
可 |
今回は、クラスター全体で定義されているデフォルトロールadmin
を使って、特定のネームスペースproject-a
とproject-b
をスコープとして、グループproject-a-group
とproject-b-group
にバインドしているということになります。
ユーザー毎にアクセス権限が意図した通りに適用されていることを確認する
まず、OSユーザーpcuser2
でログインして、IAMユーザーuser02
の権限でEKSクラスターへのアクセスを試みます。
ポッドを定義するマニフェストを記述します。
apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
ネームスペースproject-a
を指定して、ポッドの作成を試みます。
$ kubectl apply -n project-a -f myapp-pod.yaml pod/myapp-pod created
ポッドが作成されました。状態を見てみましょう。
$ kubectl get -n project-a pods NAME READY STATUS RESTARTS AGE myapp-pod 1/1 Running 0 42s
ちゃんと起動しているようです。
念のために、正常に動作しているか、ログを確認しましょう。
$ kubectl logs -n project-a pod/myapp-pod Hello Kubernetes!
問題無いですね!
次に、ネームスペースproject-b
を指定して、ポッドの作成を試みます。
$ kubectl apply -n project-b -f myapp-pod.yaml Error from server (Forbidden): error when retrieving current configuration of: Resource: "/v1, Resource=pods", GroupVersionKind: "/v1, Kind=Pod" Name: "myapp-pod", Namespace: "project-b" from server for: "myapp-pod.yaml": pods "myapp-pod" is forbidden: User "user02" cannot get resource "pods" in API group "" in the namespace "project-b"
「ネームスペースproject-b
でポッドを操作する権限が無い」とエラーになりました。
IAMユーザーuser03
の権限で同様にテストを行うと、逆の結果になります。
(ネームスペースproject-b
へはアクセスが行え、ネームスペースproject-a
へはアクセスできない)
「名前空間」単位でのアクセス制御が行えることが確認できました。
今回設定したRBACの構成を図で表すと、下図のようになります。
デフォルトのネームスペースを設定する
ユーザー毎にアクセスできるネームスペースが限定されているにも関わらず、わざわざ-n project-a
というようにネームスペースを指定しなければならないのは面倒ですよね。
kubeconfigファイルの「コンテキスト」を修正することで、デフォルトのネームスペースを設定することができます。
以下のコマンドを実行します。
$ kubectl config set-context --current --namespace project-a Context "arn:aws:eks:ap-northeast-1:123456789012:cluster/example" modified.
kubeconfigファイルのcontexts:
の部分が以下のように修正されていると思います。
contexts: - name: arn:aws:eks:ap-northeast-1:123456789012:cluster/example context: cluster: arn:aws:eks:ap-northeast-1:123456789012:cluster/example user: arn:aws:eks:ap-northeast-1:123456789012:cluster/example namespace: project-a
設定後は、以下のようにネームスペース指定を省略しても、project-a
のリソースが表示されるようになります。
$ kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 1/1 Running 0 5m
おわりに
今回は、IAMユーザーに対してKubernetesリソースへのアクセス権限を制御したり、ネームスペース単位でのアクセス許可を与える方法についてご紹介しました。
なお、ユーザー数が多い場合は、個々のユーザー毎にコンフィグマップ「aws-auth」のマッピングを設定したり、RBACの設定を行うことは大変になってきます。
そのような場合は、IAMロール単位でアクセス認証・認可を行う方法があります。
次回はそのあたりを解説したいと思います。