Keystone v3 APIを使ってOpenStackにテナント(プロジェクト)の管理者の概念を導入する

Keystone v3 APIは、Havanaから導入されたAPIのバージョンである。`docs.openstack.org`にある公式のインストールマニュアルでは未だにv2 APIを使うように記述されているがv3 APIを併用して使うことも可能である。使うと言っても、python-keystoneclientはv2 APIのみサポートしているので、使うためには新しく導入されたpython-openstackclientを利用する必要がある。ちなみに、Ubuntu14.04のリポジトリにあるpython-openstackclientは古すぎる、かつjunoリリースのリポジトリからは削除されているという状況のため、使うにはkiloリリースのリポジトリを導入するか、ソースから入れる必要がある。当然であるが、v3のエンドポイントをkeystoneに追加する必要もある。更に加えて言うと、パブリッククラウドやOpenStackのようなプライベートクラウドを構築するソフトウェアでは、マルチテナントという言葉がよく使われる。OpenStackもテナントという言葉を使っていたのだが、Havanaリリースから正式にテナントの事をプロジェクトと表記することになっている。が、周りでは未だにテナントと言う人が大半である。

# v3 APIの特徴
v3 APIを使うとv2までと何が違うのかについて説明しだすと日が暮れてしまうので割愛する。一言で言うと以下の2つの概念が導入されたのである。

* Domains
* Groups

残念ながら、どちらもこの記事の趣旨とは外れるため説明はしない。

# テナント(プロジェクト)の管理者の導入
v2 APIしか使わない場合、作成されたテナントに対してどのユーザを紐付けるのかについて管理できるのは、OpenStackそのものの管理者(いわゆるadmin roleの人)だけだった。v3 APIを使うことで、各テナントごとに管理者(ここではProject Adminと呼ぶ)を定義して、Project Adminが自分のテナントにどのユーザを紐付けるかを決定することが可能である。他にもいろいろ権限を譲渡することは可能であるが、本記事ではひとまずユーザヒモ付を行うところまでやってみようと思う。

## Keystoneにv3 API用の設定を入れる
使用するOpenStackのリリースはjunoである。kiloでもし仕様が変わっていたら申し訳ない。まずは、keystoneにv3 API用エンドポイントを追加する。

```bash:まずserviceを作成する
keystone --os-token ${OS_SERVICE_TOKEN} --os-endpoint ${OS_SERVICE_ENDPOINT} service-create --name keystonev3 --type identityv3 --description "OpenStack Identity v3"

```

```bash:エンドポイント追加
keystone --os-token ${OS_SERVICE_TOKEN} --os-endpoint ${OS_SERVICE_ENDPOINT} endpoint-create --region=${REGION} --service-id=$(keystone --os-token ${OS_SERVICE_TOKEN} --os-endpoint ${OS_SERVICE_ENDPOINT} service-list | awk '/ identityv3 / {print $2}') --publicurl=http://${KEYSTONE_ENDPOINT}:5000/v3 --internalurl=http://${KEYSTONE_ENDPOINT}:5000/v3 --adminurl=http://${KEYSTONE_ENDPOINT}:35357/v3
```

次に、テナント管理者用のroleを作成する。ここではproject_adminという名称にする。

```bash
keystone --os-token ${OS_SERVICE_TOKEN} --os-endpoint ${OS_SERVICE_ENDPOINT} role-create --name project_admin
```

なお、ここで使用している環境変数などは自分で事前にセットしといてくれ。公式マニュアル見れば分かるはず!

## python-openstackclientを導入する

今回はソースから入れてみようと思う。なお、使用するOSはUbuntu14.04である。CentOSを使う場合もほぼ同じはずだが試してはいない。OpenStack Clientだけはkiloリリースを利用するため、`stable/kilo`ブランチをcheckoutする。

```bash
sudo su -
apt-get install python-dev libffi-dev
cd /opt
git clone https://github.com/openstack/python-openstackclient.git
cd python-openstackclient
git checkout stable/kilo
python ./tools/install_venv.py
./tools/with_venv.sh python setup.py install
```

.bashrcに以下を追加しておくと便利である。

```bash
echo "alias openstack='/opt/python-openstackclient/tools/with_venv.sh openstack'" >> ~/.bashrc
source ~/.bashrc
```

## Keystone policy.jsonの編集
`http://ubuntu-cloud.archive.canonical.com/ubuntu`にあるjunoリリースをUbuntu14.04に導入すると、デフォルトでv2 APIに適したpolicy.jsonがインストールされる。そこで、このjsonファイルを編集してテナント管理者としての権限をproject_admin roleに与える。ファイルを全部貼っつけると、かなりの量になるので、ここではオリジナル(`policy.json.orig`)とのdiffの結果のみ記す。

```bash
diff policy.json policy.json.orig
```

```text
28c28
<     "identity:get_domain": "rule:admin_required or role:project_admin",
---
>     "identity:get_domain": "rule:admin_required",
34,36c34,36
<     "identity:get_project": "rule:admin_required or role:project_admin",
<     "identity:list_projects": "rule:admin_required or role:project_admin",
<     "identity:list_user_projects": "rule:admin_or_owner or role:project_admin",
---
>     "identity:get_project": "rule:admin_required",
>     "identity:list_projects": "rule:admin_required",
>     "identity:list_user_projects": "rule:admin_or_owner",
38c38
<     "identity:update_project": "rule:admin_required or (project_id:%(target.project.id)s and role:project_admin)",
---
>     "identity:update_project": "rule:admin_required",
41,42c41,42
<     "identity:get_user": "rule:admin_required or role:project_admin",
<     "identity:list_users": "rule:admin_required or role:project_admin",
---
>     "identity:get_user": "rule:admin_required",
>     "identity:list_users": "rule:admin_required",
70,71c70,71
<     "identity:get_role": "rule:admin_required or role:project_admin",
<     "identity:list_roles": "rule:admin_required or role:project_admin",
---
>     "identity:get_role": "rule:admin_required",
>     "identity:list_roles": "rule:admin_required",
77,79c77,79
<     "identity:list_grants": "rule:admin_required or role:project_admin",
<     "identity:create_grant": "rule:admin_required or (project_id:%(target.project.id)s and role:project_admin)",
<     "identity:revoke_grant": "rule:admin_required or (project_id:%(target.project.id)s and role:project_admin)",
---
>     "identity:list_grants": "rule:admin_required",
>     "identity:create_grant": "rule:admin_required",
>     "identity:revoke_grant": "rule:admin_required",
81c81
<     "identity:list_role_assignments": "rule:admin_required or role:project_admin",
---
>     "identity:list_role_assignments": "rule:admin_required",
```

このjsonファイルで肝となるのは、この1行である。

```text
"identity:update_project": "rule:admin_required or (project_id:%(target.project.id)s and role:project_admin)",
```

jsonファイルを編集したらKeystoneを再起動する。

```bash
service keystone restart
```

## 実際にテナント管理者でユーザ紐付けをやってみる

当然であるが、project_admin roleを持つユーザはadmin roleを持つユーザで付与してあげる必要がある。また、前提として`demo`というテナント(プロジェクト)を事前に作成しておく。ここから先はproject_adminのroleを持つユーザの作業を想定する。そして、紐付けをする対象ユーザは`demo_user`という名称にする。


v3 API用の環境変数をセットする。

```bash
export OS_REGION_NAME=<Region Name>
export OS_TENANT_NAME=demo
export OS_USERNAME=<User Name>
export OS_PASSWORD=<User Password>
export OS_AUTH_URL=http://<Keystone Endpoint>:5000/v3
export OS_IDENTITY_API_VERSION=3
export OS_PROJECT_DOMAIN_ID=default
export OS_USER_DOMAIN_ID=default
```

存在するroleの種別を確認する。

```bash
openstack role list
```

```text
+----------------------------------+---------------+
| ID                               | Name          |
+----------------------------------+---------------+
| 69a12bb20a6b4d40a6aa3976341e94b8 | _member_      |
| e119fa409edb46738b3832430122a6fe | project_admin |
| ebf755abf62641d386335070d561ac5b | admin         |
+----------------------------------+---------------+
```

次にプロジェクトの確認をする。

```bash
openstack project list --long
```

```text
+----------------------------------+---------+-----------+----------------+---------+
| ID                               | Name    | Domain ID | Description    | Enabled |
+----------------------------------+---------+-----------+----------------+---------+
| 5e312dd75687441ca66afd1928994a28 | admin   | default   | Admin Tenant   | True    |
| cadcece515464528baedcb84efab7ba7 | demo    | default   |                | True    |
| d91fb8307105471da67128f5bfcff118 | service | default   | Service Tenant | True    |
+----------------------------------+---------+-----------+----------------+---------+
```

`demo`プロジェクトに対して`demo_user`を`_member_`というroleで紐付ける。

```bash
openstack role add --project=demo --user=demo_user _member_
```

### 以下、確認作業

userのUUIDを確認する。

```bash
openstack user list --domain=default | grep demo_user
```

```text
| 84790aef5e6a702c0ec31b0f9d4a6eee03e79b366c163be412cd99ac1ac6712f | demo_user     |
```

roleのアサイン情報をUUIDでgrepして確認する。

```bash
openstack role assignment list | grep 84790aef5e6a702c0ec31b0f9d4a6eee03e79b366c163be412cd99ac1ac6712f
```

```text
| 69a12bb20a6b4d40a6aa3976341e94b8 | 84790aef5e6a702c0ec31b0f9d4a6eee03e79b366c163be412cd99ac1ac6712f |       | cadcece515464528baedcb84efab7ba7 |        |
```

grepしてるので見づらいが、Role、User、Group、Project、Domainの順に出力されている。よって、`demo_user` (`84790aef5e6a702c0ec31b0f9d4a6eee03e79b366c163be412cd99ac1ac6712f`)が、`demo`(`cadcece515464528baedcb84efab7ba7`)プロジェクトに対して、`_member_`(`69a12bb20a6b4d40a6aa3976341e94b8`)というroleで紐付けられていることが確認できた。

補足であるが、現行のkiloリリース時点では同様の作業をGUIから行うためのHorizonの実装はまだなされていない。ゆえに、GUIから同様の作業をしたい場合には、その次のlibertyリリースまで待つか、独自でGUIを実装する必要がある。