kubernetesのapiserverはauthentication(認証), authorization(認可)の機能が実装されており、これを利用することでユーザとリソースが管理出来るようになっています。

今回はService Account Tokens方式で認証を行い、ABAC方式で認可を行います。

authenticationについて

kubernetesではいくつかの認証方式が用意されています。
https://kubernetes.io/docs/admin/authentication/#authentication-strategies

それぞれざっと見た感じで説明すると。

  • X509 Client Certs
    SSL証明書のcommon nameでユーザを定義する。SSLクライアント証明書の管理めんどそう。

  • Static Token File / Static Password File
    ユーザの認証情報が書かれたファイルを用意する方式。ファイルの再読込はapiserverの再起動が必要。クラスタ構成にしているときのファイルの管理とか大変そう。

  • Service Account Tokens
    デフォルトで有効になっている認証方式。kubernetesで管理されるservice accountのNameとTokenを使って認証をする。今回はこれを取り扱う。

  • OpenID Connect Tokens
    OpenID providerに認証を委譲してTokenを使って認証する方法。OpenIDのproviderがそもそもないので使えない。

  • Webhook Token Authentication
    名前の通り。webhookで認証をする方法。

  • Authenticating Proxy
    Proxyした時に認証してproxy後のheaderに認証情報を付与する方法。

  • Keystone Password
    OpenStackの認証、ポリシー管理をする機能に連携させて認証する。

今回はデフォルトで有効になっているService Account Tokens方式を使って認証をしてみます。

authorizationについて

認可の方式もいくつかあります。
https://kubernetes.io/docs/admin/authorization/

認証方式はapiserverに--authorization-mode=で指定します。カンマ区切りで複数指定でき、1つでもOKになれば認可されます。

  • AlwaysDeny / AlwaysAllow
    名称のままです。常に拒否と常に許可します。

  • ABAC
    認可policyを書いたファイルを用意してファイルに従い認可を実行します。今回はこの方式を試してみます。

  • RBAC
    ABACと設定出来る内容は同じです。こちらはファイルを使わず、policyをkubectl createするので状態はetcdの中で保持します。ABACを使うのであればRBACを使ったほうが良いです。

  • Webhook
    webhookでやり取りした値で認可を行います。どこにwebhookするのかを記述する設定ファイルを用意する必要があります。

apiserverの設定

前に書いたKubernetesのClusterをインストールするでKubernetesのClusterがインストールされている前提で記述します。

authenticationの設定

まず、--insecure-bind-addressを使っている場合は認証が行われないので--bind-addressの設定を追加して、対応するportの設定も追加します。

/etc/kubernetes/apiserverに以下の設定を追加します。

# The address on the local server to listen to.
KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0 --bind-address=0.0.0.0"

# The port on the local server to listen on.
KUBE_API_PORT="--secure-port=6443 --insecure-port=8080"

--insecureな設定は認証が行われないので注意が必要です。minionのkubeletなど認証が不要なものはinsecureに接続して、認証が必要なユーザからの接続はbind-addressで指定したhttps://0.0.0.0:6443に接続させるように制御が必要です。ここでは特に設定しませんがFWで適切に設定する必要があります。

service accountにsecret(token)が付与されるように設定します。
https://kubernetes.io/docs/admin/authentication/#service-account-tokens

apiserverの--service-account-key-fileに設定する鍵を生成します。

$ mkdir /etc/kubernetes/ssl
$ openssl req -newkey rsa:2048 -nodes -sha256 \
-keyout /etc/kubernetes/ssl/apiserver.key \
-x509 -days 356 \
-out /etc/kubernetes/ssl/apiserver.crt \
-subj "/CN=192.168.33.21"

apiserverだけでなく、controller-managerに含まれるtoken-manegerが使う鍵の設定も必要なので合わせて設定します。controller-managerのtoken-managerがsecretsのTokenを割り当てているようです。
https://kubernetes.io/docs/admin/service-accounts-admin/

$ cat /etc/kubernetes/apiserver
(snip)
# default admission control policies
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"

# Add your own!
KUBE_API_ARGS="--service_account_key_file=/etc/kubernetes/ssl/apiserver.key"

$ cat /etc/kubernetes/controller-manager
(snip)
# Add your own!
KUBE_CONTROLLER_MANAGER_ARGS="--root-ca-file=/etc/kubernetes/ssl/apiserver.crt --service_account_private_key_file=/etc/kubernetes/ssl/apiserver.key"

再起動して設定を適用します。

$ systemctl restart kube-apiserver.service
$ systemctl restart kube-controller-manager.service

適当なServiceAccountを作成してTokenを確認します。

$ kubectl create sa test
serviceaccount "test" created

$ kubectl describe sa test
Name: test
Namespace: default
Labels: <none>

Image pull secrets: <none>

Mountable secrets: test-token-kv95u

Tokens: test-token-kv95u

$ kubectl describe secrets test-token-kv95u
Name: test-token-kv95u
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name=test
kubernetes.io/service-account.uid=57b0a1f3-dffa-11e6-b11d-525400225b53

Type: kubernetes.io/service-account-token

Data
====
namespace: 7 bytes
token: TOKEN_STRING

正しく動作している場合はTOKENが表示されます。

また、この時点でToken無しでapiserverに接続すると認証情報を求められるようになります。apiserverが自動生成した自己署名証明書を使っているので--insecure-skip-tls-verifyの設定が必要です。設定しない場合は以下の様にエラーが表示されます。

Unable to connect to the server: x509: certificate is valid for kubernetes.default.svc, kubernetes.default, kubernetes, not centos-master-1

$ kubectl get sa --server=https://centos-master-1:6443 \
--insecure-skip-tls-verify=true
Please enter Username:

testユーザのtokenを設定すると認証が行われて内容を表示することができます。

$ kubectl get sa --server=https://centos-master-1:6443 \
--insecure-skip-tls-verify=true
--token=TOKEN_STRING
NAME SECRETS AGE
default 1 6m
test 1 1m

authorizationの設定

次にapiserverにABACの設定を追加します。

https://kubernetes.io/docs/admin/authorization/#abac-mode

$ cat /etc/kubernetes/apiserver
(snip)
# Add your own!
KUBE_API_ARGS="--service_account_key_file=/etc/kubernetes/ssl/apiserver.key --authorization-mode=ABAC --authorization-policy-file=/etc/kubernetes/abac-policy.jsonl"

Policyファイルを追加します。

$ cat /etc/kubernetes/abac-policy.jsonl
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"system:serviceaccount:ishiis-ns:ishiis", "namespace": "ishiis-ns", "resource": "*", "apiGroup": "*"}}

“誰が”、”何を”といったPolicyを記述します。kubernetesのsaを指定する場合はsystem:serviceaccount:(namespace):(username)という形式で設定する必要があります。上記の設定では、ishiisユーザはnamespace:ishiis-nsの全てを操作出来るようになります。

再起動して設定を適用させます。

$ systemctl restart kube-apiserver.service

試しに上で書いたユーザを作成してリソースの表示が出来るか確認してみます。

$ kubectl create ns ishiis-ns
namespace "ishiis-ns" created
$ kubectl create sa ishiis --namespace=ishiis-ns
serviceaccount "ishiis" created

作成したユーザのTokenを確認します。

$ kubectl describe secret ishiis --namespace=ishiis-ns
Name: ishiis-token-5v552
Namespace: ishiis-ns
Labels: <none>
Annotations: kubernetes.io/service-account.name=ishiis
kubernetes.io/service-account.uid=c934761c-e000-11e6-95be-525400225b53

Type: kubernetes.io/service-account-token

Data
====
namespace: 9 bytes
token: ......

上記のtokenを設定してservice accountを確認します。

$ kubectl get sa --server=https://centos-master-1:6443 \
--insecure-skip-tls-verify=true \
--token=......
Error from server: the server does not allow access to the requested resource (get serviceaccounts)

namespaceを指定していないのでdefaultのservice accountを表示しようとしていますが拒否されていることが確認できます。

次にnamespace=ishiis-nsとして確認してみます。

$ kubectl get sa --server=https://centos-master-1:6443 \
--insecure-skip-tls-verify=true \
--namespace=ishiis-ns \
--token=......
NAME SECRETS AGE
default 1 7m
ishiis 1 7m

ishiis-nsはpolicyで許可されているのでアクセス出来ることが確認できます。

最後に、毎回コマンドに設定するのは面倒なのでkubectl configで割り当てられたtokenを使うように設定します。

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

$ kubectl config set-credentials ishiis --token=...
user "ishiis" set.
$ kubectl config set-cluster ishiis-cluster --server=https://centos-master-1:6443 --insecure-skip-tls-verify=true
cluster "ishiis-cluster" set.
$ kubectl config set-context ishiis-context --cluster=ishiis-cluster --user=ishiis --namespace=ishiis-ns
$ kubectl config use-context ishiis-context
$ kubectl get sa
NAME SECRETS AGE
default 1 13m
ishiis 1 13m

いい感じですね。

おわり。

参考

  1. https://kubernetes.io/docs/admin/authentication/
  2. https://kubernetes.io/docs/admin/kube-apiserver/
  3. https://kubernetes.io/docs/admin/service-accounts-admin/
  4. https://kubernetes.io/docs/admin/authorization/