Kubernetes の認証と dex

Kubernetes v1.2 で提供されている認証と OpenID Connect ID トークンを使った認証で連携できる dex についてまとめました。

## TL;DR

- Kubernetes の認証には、クライアント証明書、 トークンファイル、 OpenID Connect ID トークン、 Basic 認証, Keystone 認証が提供されている
- OpenID Connect ID トークンを使った認証をするには、coreos/dex が選択肢のひとつになる

## 認証 (Authentication)

Kubernetes の認証は、 **クライアント証明書**、 **トークンファイル**、 **OpenID Connect ID トークン**、 **Basic 認証**, **Keystone 認証** が提供されています。

Kubernetes は、認証の処理を [kube-apiserver](http://qiita.com/tkusumi/items/c2a92cd52bfdb9edd613#kube-apiserver) で行います。そのため、認証の設定は全て kube-apiserver に対して行います。

### クライアント証明書

クライアント証明書を使った認証は、`--client-ca-file=SOMEFILE` を設定することで有効になります。引き渡すファイルには、ひとつ以上の証明書が含まれている必要があります。正しく検証された際は、クライアント証明書の Common Name (CN) がユーザ名として使われます。

### トークンファイル

トークンファイルは、`--token-auth-file=SOMEFILE` を設定することで有効になります。トークンファイルを更新するためには、apiserver を再起動しなければなりません。

トークンファイルのフォーマットは、最低限必要な `token`, `user name`, `user uid` の3カラムと、それに続くオプションのグループ名が含まれた CSV ファイルです。二つ以上のグループ名を指定する場合は、ダブルクオーテーションで囲う必要があります。

```
token,user name, user uid, "group1,group2,group3"
```

HTTP クライアントからトークンファイルを使った認証を使うときは、kube-apiserver に対してのリクエストの `Authorization` ヘッダに `Bearer SOMETOKEN` としてトークンを設定します。

### OpenID Connect ID トークン

OpenID Connect ID トークンを使った認証は、上記の二つと比べ少し複雑で、以下のオプションを設定する必要があります。

- `--oidc-issuer-url` (required): 接続する OpenID Connect Identity Provider を設定します。このプロバイダは、OpenID Connect discovery に対応していなければなりません。スキームは、HTTPS のみを受け付けます。
- `--oidc-client-id` (require): ID トークンには、トークンを発行した先の識別子が `aud` クレームに含まれています。apiserver は、渡されたトークンが正しい発行先に向けて発行されたかを検証するために、client-id を必要とします。これは、OpenID Connect の仕様になるため、詳しくは、[Draft: OpenID Connect Core 1.0 - draft 17](http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#IDToken) を参照してください。
- `--oidc-ca-file` (optional): Identity Provider にセキュアに接続するために使う中間CAファイルを設定できます。
- `--oidc-username-claim`（optional、experimental）: ユーザ名として使うための OpenID クレームを指定できます。デフォルトでは、発行者の元でユニークで不変である `sub` クレームが使われます。`email` と設定することで、`email` クレームをユーザ名として使うことができますが、ユニークと不変が保証されません。
- `--oidc-groups-claim` (optional, experimental): グループ名として使うための OpenID クレームを指定できます。

HTTP クライアントから ID トークンファイルを使った認証を使うときは、トークンファイルと同様に kube-apiserver に対してのリクエストの `Authorization` ヘッダに `Bearer SOMETOKEN` としてトークンを設定します。

### Basic 認証

Basic 認証は、`--basic-auth-file=SOMEFILE` を設定することで有効になります。Basic 認証のクレデンシャルは無期限であり、パスワードを更新するためには、apiserver を再起動しなければなりません。Basic 認証は使いやすく利便性がよいため、提供されていますが、セキュアではないため、検証目的以外では利用すべきではありません。

Basic 認証で使われるファイルのフォーマットは、パスワード、ユーザ名、ユーザ ID の3カラムからなる CSV ファイルです。

HTTP クライアントから Basic 認証を使うときは、kube-apiserver に対してのリクエストの `Authorization` ヘッダに `Basic BASE64ENCODED(USER:PASSWORD)` として設定します。

### Keystone 認証

Keystone 認証は、`--experimental-keystone-url=<AuthURL>` を設定することで有効になります。Keystone は、オープンソースの IaaS プロジェクトである OpenStack の認証機能を提供するコンポーネントです。プロジェクトとユーザを管理するためにどのように Keystone を利用するかは、[Keystone のドキュメント](http://docs.openstack.org/developer/keystone/) を参照してください。

Keystone 認証は、まだ実験的であり、今後仕様が変更される可能性があることに注意する必要があります。

### 認証プラグインの開発

v1.2 で使える認証機能について紹介しましたが、Kubernetes では、今後外部の認証プロバイダでユーザを認証したのち、独自のトークンを発行する機能を kube-apiserver に持たせる計画があるそうです。この機能が実装されると、Google や GitHub、LDAP などと直接認証を行うことができるようになり、非常に簡単に認証プラグインの開発が行えるようになります。

### その他、LDAP 認証

v1.2 時点では認証はプラグインとして開発することができないため、独自の認証機能を持たせるには、kube-apiserver にパッチを当てる必要があります。パッチを当てる運用は苦行になることが想定されるため、最終手段にしたいと考えるはずですが、この方法で使える認証として Kismatic, Inc がオープンソースで提供している [kubernetes-ldap](https://github.com/kismatic/kubernetes-ldap) を使うことで apiserver から直接 LDAP 認証を使うことができるようになります。Kismatic, Inc は Kubernetes の商用サポートを提供している企業です。

ただドキュメントが全くなく数ヶ月更新がないため、正しく使えるかどうかは分かりません。もし LDAP を使いたいなら OpenID Connect Identity Provider として機能しながら認証自体に LDAP を使うことができる dex を検討してみてください。この後に紹介しています。

## OpenID Connect ID トークンを使った認証を行う

Kubernetes v1.2 でここまでで書いたようにいくつかの認証が提供されています。その中で Kubernetes クラスタを構築し動的にユーザを追加、削除を行うのに現実的なのは、OpenID ID トークンを使うか、OpenStack Keystone 認証を使うかです。
OpenStack Keystone 認証は、まだ experimental であり、そもそも OpenStack を使っていないと選択肢になりえません。そのため、OpenID Connect ID トークンを使う方法のひとつとして coreos/dex を紹介します。

dex を使った認証は、今後 kube-apiserver に実装されるらしい認証プラグインの機構と近いと考えられます。

### coreos/dex

dex は、CoreOS を開発している CoreOS, Inc が開発する OpenID Connect Identity Provider (OIDC IdP) として機能するソフトウェアです。OpenID Connect discovery に対応していて、Kubernetes の OpenID Connect ID トークンを使った認証に使うことができます。特徴的な機能として、OIDC IdP として機能しながら、認証自体をコネクタとして外部の認証プロバイダを使うことができます。ここまで知ると分かるとおり、これは Kubernetes で今後予定されている認証プラグインの機能に近いと考えられます。外部の認証プロバイダとして主なモノで Google などが提供する OpenID Connect や、OAuth2、GitHub、BitBucket、LDAP が提供されています。コネクタの実装は非常に単純なため、必要なら独自に開発することも容易です。

Kubernetes の認証として利用するときは、まず dex を構築し、なんらかの外部の認証プロバイダと連携させます。構築の詳細な手順は、[Get Started](https://github.com/coreos/dex/blob/master/Documentation/getting-started.md) を参照してください。

次に、OpenID Connect Relying Party (OIDC RP) を構築します。RP で使う client-id と client-secret は `dexctl` を使って発行することができます。

```
$ dexctl --db-url=$DEX_DB_URL new-client http://127.0.0.1:5555/callback
```

Kubernetes の設定は、kube-apiserver の `--oidc-issuer-url` に dex によって構築した IdP の URL を設定し、`--oidc-client-id` に、RP で使った client-id を設定します。あとは、構築した RP で発行された ID トークンを `kubectl config set-credentials` で設定することで認証することができます。

apiserver は、`--oidc-issuer-url` に設定された IdP の URL から OpenID Connect Discovery として公開されている `/.well-known/openid-configuration` にアクセスして ID トークンの署名に使われている公開鍵の URL を知ります。この公開鍵を使って渡された ID トークンの署名を検証します。次に、ID トークンに含まれる `aud` クレームの値が、`--oidc-client-id` に設定されている client-id と等しいかを確認します。ここで client-id が等しければ、この RP に向けて発行されたトークンであると判断され、認証に成功します。

以上が、dex で OpenID Connect ID トークンを使った認証を行うときの流れです。dex はまだまだ機能が貧弱で、複数の外部の認証プロバイダを同時に利用できなかったり、認可の機能がなかったりしますが、今後対応する予定なようです。ちなみに dex に類似するソフトウェアの JBoss プロジェクト下で開発されている Keycloak にはそれらの機能が既に備わっています、が Java です。dex は Go で実装されています。 :laughing:

最後に、dex で外部の OpenID Connect IdP のコネクタを使ったときのフローを図にしたものを置いておきます。OpenID Connect に詳しくないため誤っているかもしれません。そのときはコメントで指摘いただけるとありがたいです。

![dex with OIDC Connector 認証フロー](https://qiita-image-store.s3.amazonaws.com/0/3780/041b91fb-74f9-1395-670a-0b47a1674230.png)

## まとめ

v1.2 時点でユーザを動的に管理するには、OpenID Connect ID トークンを使うのが良さそうです。そしてそれを実現する OSS として dex を紹介しました。dex は機能は貧弱ですが、今のところ実装がシンプルで素敵です。将来的に Kubernetes に認証プラグインの機構が実装されたら、不要になるかもしれませんが、それまでは十分に機能しそうです。

## 参考

- [Kubernetes - Authenticating](http://kubernetes.io/docs/admin/authentication/)
- [kismatic/kubernetes-ldap: Lightweight Directory Access Protocol (LDAP) plug-in for Kubernetes™](https://github.com/kismatic/kubernetes-ldap)
- [coreos/dex: OpenID Connect Identity (OIDC) and OAuth 2.0 Provider with Pluggable Connectors](https://github.com/coreos/dex)
- [Keycloak - JBoss Community](http://keycloak.jboss.org/)
- [The Universal Kubernetes Solution: Deploy, Manage and Secure your Containers Anywhere](https://tectonic.com/)