Table of Contents
「Kubernetes1.8のクラスタを構築する。kubeadmで。」で、Dashboardがうまく動かない問題が発生したんだけど、それを解決した話。
問題の現象
kubeadmでKubernetesクラスタを組んで、自前のアプリ(Goslings)のデプロイまではうまくできたんだけど、Dashboardをデプロイしたら動かず、Web UIにkubectl proxy
経由でつないでもタイムアウトしてしまった。
対策
なんとなく、クラスタ内部での名前解決にはkube-dnsによるDNSサービスが使われているっぽいので、/etc/hosts
に余計な事書いたのがいけなかったと思った。
ので、/etc/hosts
からk8s-masterとk8s-nodeのエントリを削除してから、kubeadm init
からやり直してみた。
結果
したらちゃんと動いた。
VMのホストでkubectl proxy
して、
C:\Users\kaitoy\Desktop>kubectl proxy
Starting to serve on 127.0.0.1:8001
http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
にブラウザでつないだらサインイン画面が表示された。
Dashboardのサインイン処理はKubernetes(というかkube-apiserver)のそれに移譲している。 Dashboardはそこで認証されたユーザでクラスタのリソースにアクセスし、情報を取得して表示する。多分。
Dashboardへのサインイン方法はいくつかあるが、それらを理解するにはKubernetesのアクセス制御について学ぶことを推奨とあったのでちょっとKubernetesのドキュメントを読んだ。
Kubernetesのアクセス制御
Kubernetesクラスタのエンドポイントはkube-apiserverであり、クラスタのリソースへのアクセス制御もkube-apiserverがやる。 クライアントとkube-apiserverとのTLSセッションが確立した後、HTTP層のデータを見てアクセス制御をするんだけど、その処理はAuthentication(認証)、Authorization(認可)、Admission(許可)の三段階からなる。
Authentication
第一段階がAuthentication。 ここでは、kube-apiserverに仕込まれたAuthenticatorモジュールがユーザ認証をする。
Kubernetesが認証するユーザには、Kubernetesが管理するService Accountと、クラスタ外部で管理される通常ユーザの二通りがある。 Service AccountはPodがkube-apiserverと話すためのユーザで、通常ユーザは主に人がkubectlとかでkube-apiserverと話すためのユーザ。(匿名で話すこともできる。) 前者はServiceAccountオブジェクトで定義されるけど、後者用のオブジェクトはない。
ServiceAccountはNamespaceと関連付き(つまりnamespace毎にユニーク)、Secretに紐づく。 Secretオブジェクトはクレデンシャルのセットを定義し、Podにマウントされる。 ServiceAccountとSecretは、ふつうは自動で作られ、Podに割り当てられる。
kube-apiserverには一つ以上のAuthenticatorモジュールを設定できて、どれかで認証できれば次の段階に進める。 認証失敗するとHTTPステータスコード401が返る。
Authenticatorモジュールには以下のようなものがある。
- クライアント証明書: X.509のディジタル証明書を使うモジュール。kube-apiserver起動時に
--client-ca-file
オプションで証明書ファイルを渡してやると有効になる。証明書のCommon Nameがユーザ名になり、Organizationがグループになる。クライアント側は、その証明書と対応する秘密鍵をクレデンシャルとして指定する。 - Bearer Token: 無記名トークンを使うモジュール。kube-apiserver起動時に
--token-auth-file
オプションでトークン情報を渡してやると有効になる。トークン情報はCSVで、「token,user,uid,"group1,group2,group3"
」という形式で書く。クライアント側は、トークン文字列をクレデンシャルとして指定する。 - ベーシック認証: ユーザ名とパスワードで認証するモジュール。kube-apiserver起動時に
--basic-auth-file
オプションでユーザ名とパスワードのリストを渡してやると有効になる。このリストはCSVで、「password,user,uid,"group1,group2,group3"
」という形式で書く。クライアント側は、ユーザ名とパスワードをクレデンシャルとして指定する。HTTPクライアントの時はAuthorizationヘッダが使える。 - Service Account Token: Service Accountを署名付きBearer Tokenで認証するモジュール。デフォルトで有効になる。
このあたり、Qiitaの「kubernetesがサポートする認証方法の全パターンを動かす」という記事をみると理解が深まる。
Authorization
Authenticationをパスすると、クライアントのユーザ(とグループ)が認証され、第二段階のAuthorizationモジュールの処理に移る。 ここでは、リクエストの内容(操作対象、操作種別(メソッド)等)を見て、それがユーザに許されたものなら認可する。 何を許すかは事前にクラスタにポリシーを定義しておく。
kube-apiserver起動時に--authorization-mode
オプションで一つ以上のAuthenticatorモジュールを指定できて、どれかで認可されれば次の段階に進める。
さもなくばHTTPステータスコード403が返る。
Authorizationモジュールには以下のようなものがある。
- Node: kubeletからのリクエストを認可する。
- ABAC Mode: Attribute-based Access Control。リクエストに含まれる属性とPolicyオブジェクトを比較して、マッチするものがあれば認可。
- RBAC Mode: Role-Based Access Control。RoleオブジェクトやClusterRoleオブジェクトでロールを作成し、アクセスできるリソースや許可する操作を定義して、RoleBindingオブジェクトやClusterRoleBindingオブジェクトでユーザ名やグループと紐づける。
- Webhook Mode: リクエストの内容を示すSubjectAccessReviewオブジェクトをシリアライズしたJSONデータをHTTPでPOSTして、そのレスポンスによって認可可否を決める。
Admission Control
Authorizationをパスすると、第三段階のAdmission Controlモジュールの処理に移る。 ここでは、オブジェクトの作成、削除、更新などのリクエストをインターセプトして、オブジェクトの永続化前にそのオブジェクトを確認して、永続化を許可するかを決める。 リクエストされたオブジェクトやそれに関連するオブジェクトを永続化前にいじって、デフォルト値を設定したりもできる。 読み取りリクエストの場合は実行されない。
kube-apiserver起動時に--admission-control
オプションで複数のAdmission Controlモジュールを指定できて、全てが許可しないとリクエストが却下される。
Admission Controlモジュールは色々あるんだけど、Kubernetes 1.6以降では--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
と指定するのが強く推奨されている。
ここで指定しているServiceAccountモジュールは、kube-controller-managerに含まれるServiceAccountControllerとTokenControllerと協調し、Service Account周りの処理を自動化してくれるもの。
ServiceAccountControllerは、各Namespaceにdefault
という名前のService Accountを作る。
ServiceAccountが作成されるとTokenControllerが動き、対応したSecretとトークンを生成して紐づける。
ServiceAccountモジュールは、Podの作成や更新時に動き、以下の処理をする。
- PodにServiceAccountが設定されていなければ、
default
を設定する。 - Podに設定されたServiceAccountが存在していることを確認し、存在していなければリクエストを却下する。
- PodがImagePullSecretsを含んでいなければ、ServiceAccountのImagePullSecretsをPodに追加する。
- トークンを含んだVolumeをPodに追加する。
- Pod内の各コンテナの
/var/run/secrets/kubernetes.io/serviceaccount
にそのVolumeをマウントさせる。
DashboardへBearer Tokenでサインイン
Dashboardの話に戻る。 とりあえずBearer Tokenでのサインインを試す。
クラスタにはデフォルトで色んなService Accountが作られていて、異なる権限を持っている。 そのいずれかのSecretのTokenを使ってDashboardへサインインできるらしい。
以下のコマンドでkube-system
というNamespaceのSecretを一覧できる。
C:\Users\kaitoy>kubectl -n kube-system get secret
NAME TYPE DATA AGE
attachdetach-controller-token-skzmj kubernetes.io/service-account-token 3 18m
bootstrap-signer-token-mhqfh kubernetes.io/service-account-token 3 18m
bootstrap-token-2964e0 bootstrap.kubernetes.io/token 7 18m
certificate-controller-token-fvrgm kubernetes.io/service-account-token 3 18m
cronjob-controller-token-hmrdm kubernetes.io/service-account-token 3 18m
daemon-set-controller-token-vqz85 kubernetes.io/service-account-token 3 18m
default-token-h987g kubernetes.io/service-account-token 3 18m
deployment-controller-token-86bp9 kubernetes.io/service-account-token 3 18m
disruption-controller-token-6mskg kubernetes.io/service-account-token 3 18m
endpoint-controller-token-d4wz6 kubernetes.io/service-account-token 3 18m
generic-garbage-collector-token-smfgq kubernetes.io/service-account-token 3 18m
horizontal-pod-autoscaler-token-wsbn9 kubernetes.io/service-account-token 3 18m
job-controller-token-fttt2 kubernetes.io/service-account-token 3 18m
kube-dns-token-sn5qq kubernetes.io/service-account-token 3 18m
kube-proxy-token-w96xd kubernetes.io/service-account-token 3 18m
kubernetes-dashboard-certs Opaque 2 7m
kubernetes-dashboard-key-holder Opaque 2 6m
kubernetes-dashboard-token-gtppc kubernetes.io/service-account-token 3 7m
namespace-controller-token-5kksd kubernetes.io/service-account-token 3 18m
node-controller-token-chpwt kubernetes.io/service-account-token 3 18m
persistent-volume-binder-token-d5x49 kubernetes.io/service-account-token 3 18m
pod-garbage-collector-token-l8sct kubernetes.io/service-account-token 3 18m
replicaset-controller-token-njjwr kubernetes.io/service-account-token 3 18m
replication-controller-token-qrr5h kubernetes.io/service-account-token 3 18m
resourcequota-controller-token-dznjm kubernetes.io/service-account-token 3 18m
service-account-controller-token-99nh8 kubernetes.io/service-account-token 3 18m
service-controller-token-9cw7k kubernetes.io/service-account-token 3 18m
statefulset-controller-token-8z8w9 kubernetes.io/service-account-token 3 18m
token-cleaner-token-cxbkc kubernetes.io/service-account-token 3 18m
ttl-controller-token-k7gh7 kubernetes.io/service-account-token 3 18m
weave-net-token-lqdgm kubernetes.io/service-account-token 3 17m
で、適当にそれっぽいSecret、deployment-controller-token-86bp9
を選んで、kubectl describe
したらTokenが見れた。
(Dataセクションのtoken
のとこ。)
C:\Users\kaitoy>kubectl -n kube-system describe secret deployment-controller-token-86bp9
Name: deployment-controller-token-86bp9
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name=deployment-controller
kubernetes.io/service-account.uid=17fc5207-b627-11e7-9867-000c2938deae
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZXBsb3ltZW50LWNvbnRyb2xsZXItdG9rZW4tODZicDkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVwbG95bWVudC1jb250cm9sbGVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMTdmYzUyMDctYjYyNy0xMWU3LTk4NjctMDAwYzI5MzhkZWFlIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRlcGxveW1lbnQtY29udHJvbGxlciJ9.ZGV9XDd-GQjAwRuLKpdsWL_dTeF0Mr_2gF117OW4BhEuLwPujnsfOuysAQ-DUtNOp1NHKGitlfxjh6fKo4tFsdwLVJWrRK6i4YH1Mm2No7Sheks7IQn1FnwSmr7yCuvjlHD2e4RpZH0wupOFoY7FHntilhOWbXTJzJzi7TozLX02EKbkVGAsvch3LZ6p8jmUH5hr8DdKc4jbmTRp86SOiFS4_-TJ3RtAHCxiioAuKzXm3-rAWdeGLLcKrM2pAFSAGaBNu8MO5BZlAi6h3Xt4x-8-1ZXs4mudtJiECvjB-XIwiwzhpq8wIPZvvQQ-f1khixOyk1RfIXRJhIE5Gqvi8g
サインイン画面でTokenを選択し、
この、eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZXBsb3ltZW50LWNvbnRyb2xsZXItdG9rZW4tODZicDkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVwbG95bWVudC1jb250cm9sbGVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMTdmYzUyMDctYjYyNy0xMWU3LTk4NjctMDAwYzI5MzhkZWFlIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRlcGxveW1lbnQtY29udHJvbGxlciJ9.ZGV9XDd-GQjAwRuLKpdsWL_dTeF0Mr_2gF117OW4BhEuLwPujnsfOuysAQ-DUtNOp1NHKGitlfxjh6fKo4tFsdwLVJWrRK6i4YH1Mm2No7Sheks7IQn1FnwSmr7yCuvjlHD2e4RpZH0wupOFoY7FHntilhOWbXTJzJzi7TozLX02EKbkVGAsvch3LZ6p8jmUH5hr8DdKc4jbmTRp86SOiFS4_-TJ3RtAHCxiioAuKzXm3-rAWdeGLLcKrM2pAFSAGaBNu8MO5BZlAi6h3Xt4x-8-1ZXs4mudtJiECvjB-XIwiwzhpq8wIPZvvQQ-f1khixOyk1RfIXRJhIE5Gqvi8g
を入力したらサインインできて、GoslingsのDeploymentの情報が見れた。
Podも見れる。
けどServiceは見れない。
各画面でオレンジ色のワーニングも出ていて、deployment-controller
ユーザで見れる範囲はあまり広くないことが分かる。
DashboardへAdmin権限でサインイン
DashboardのPodのService Accountであるkubernetes-dashboard
にAdmin権限を付けてやって、サインイン画面でSKIPを押すとなんでも見れるようになる。セキュリティリスクがあるので本番ではNG設定だけど。
cluster-admin
というClusterRoleがあって、これをkubernetes-dashboard
にバインドするClusterRoleBindingを作ってやればいい。
ので、以下のようなYAMLファイルを書いて、
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
labels:
k8s-app: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kube-system
kubectl
で投げる。
C:\Users\kaitoy\Desktop>kubectl create -f dashboard-admin.yml
clusterrolebinding "kubernetes-dashboard" created
したらServiceも見えるようになった。
ついでにHWリソース情報も見れた。
満足した。