Elasticsearchのプラグイン「Shield」を利用してELKをセキュアに！！Kibanaにおける認証・認可を実現してみた


# 本記事の概要

ログ収集・解析の基盤としてELK（Elasticsearch + Logstash + Kibana）の構成がよく知られていますが、
その中でもログの管理・可視化を担うElasticsearch + Kibanaがメインの記事となります。
このKibanaですが、現状、認証・認可やセキュリティ対策のような機能は実装されておらず、
本格的な運用を考えたときに何とかしたいと思ったのが事の発端です。
認証・認可がないということは、例えば管理者となる人がKibanaにてVisualization（グラフ）やDashboard（グラフの集合）を作成したとしても、Kibanaにアクセス可能な人であれば誰でもそれらを改ざんすることが可能となってしまいます。  
今回はElasticsearchの有償セキュリティプラグインである「Shield」を用いて、Kibanaの認証・認可を実現し、
Visualization、Dashboardをユーザに応じてRead Onlyにしてみようと思います。

※Kibanaの認証機能は当初4.2で導入される予定でしたが、今のところ4.4で導入予定となっています。
https://github.com/elastic/kibana/issues/3904

# 環境情報

* OS：CentOS6.5 on VirtualBox + Vagrant
* Logstash 2.1.1
* Elasticsearch 2.1.0
* Kibana 4.3.0
* Java 1.8.65

# 公式サイト

* Elasticsearch
https://www.elastic.co/products/elasticsearch

* Logstash
https://www.elastic.co/products/logstash

* Kibana
https://www.elastic.co/products/kibana

* Shield
https://www.elastic.co/products/shield

# 事前準備

## ELKインストール

### Logstash

```bash
wget -P /usr/local/src/ https://download.elastic.co/logstash/logstash/packages/centos/logstash-2.1.1-1.noarch.rpm
rpm -ivh /usr/local/src/logstash-2.1.1-1.noarch.rpm
```

### Elasticsearch

```bash
wget -P /usr/local/src/ https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/rpm/elasticsearch/2.1.0/elasticsearch-2.1.0.rpm
rpm -ivh /usr/local/src/elasticsearch-2.1.0.rpm
```

### Kibana

```bash
wget -P /usr/local/src/ https://download.elastic.co/kibana/kibana/kibana-4.3.0-linux-x64.tar.gz
tar xzvf /usr/local/src/kibana-4.3.0-linux-x64.tar.gz -C /opt
```

※ ELKを起動させるためには、Javaがインストール済みであることが前提となります。

## ELK連携

Logstashの[input-twitter-plugin](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-twitter.html)を利用してタイムラインをElasticsearchへ取り込み、Kibanaで可視化できるようにしておきます。
本旨ではないですがクリスマス目前ということで「Xmas」をキーワードにタイムラインを収集してみます。

### Logstash設定

```conf:/etc/logstash/conf.d/twitter.conf
input {
    twitter {
        consumer_key => "<consumer_key>"
        consumer_secret => "<consumer_secret>"
        oauth_token => "<oauth_token>"
        oauth_token_secret => "<oauth_token_secret>"
        keywords => ["Xmas"]
        full_tweet => true
        ignore_retweets => true
    }
}
output {
    elasticsearch {
    }
    stdout {
        codec => rubydebug
    }
}
```

※ `consumer_key`、`consumer_secret`、`oauth_token`、`oauth_token_secret`には、[Twitter Apps](https://apps.twitter.com/)にて登録したアプリケーションのものを使用します。
参考：http://dev.classmethod.jp/cloud/aws/twitter-visualize-using-elastic/

### Elasticsearch起動

```bash
service elasticsearch start
```

### Kibana起動

```bash
/opt/kibana-4.3.0-linux-x64/bin/kibana &
```

### Logstash起動

```bash
service logstash start
```

### ログ連携確認

Kibana上からタイムラインが連携されていることを確認します。
<img width="1440" alt="スクリーンショット 2015-12-13 3.10.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/095c12cb-4b13-7f45-e813-1a180d0eeab6.png">

# セキュリティ設定

下準備ができたところで、いよいよKibanaに対する認証・認可を実現していきます。

## Shieldインストール

まずはElasticsearchのセキュリティプラグイン「Shield」をインストールします。
このプラグインは有償のサブスクリプションとして提供されていますが、30日間は試用ライセンスとして試すことができます。

```bash
/usr/share/elasticsearch/bin/plugin install elasticsearch/license/latest
/usr/share/elasticsearch/bin/plugin install elasticsearch/shield/latest
```

## 認証（Authentication）

### Shieldにおける認証の考え方

Shieldではユーザの資格情報を受け取りチェックする認証メカニズムを「レルム」と定義し、
4タイプの「レルム」をサポートしてます。

* **esusers**
Shieldに組み込まれたネイティブの認証システムとなります。（デフォルト）
ファイルベースのレルムとなり、コマンドラインからユーザの追加・削除等を実行することができます。

* **LDAP**
外部のLDAPサーバでユーザを認証するレルムとなります。

* **Active Directory**
外部のActive Directoryサーバでユーザを認証するレルムとなります。

* **PKI**
公開鍵を利用して認証を行うレルムとなります。

今回はデフォルトのまま`esusers`レルムを利用したいと思います。

### ユーザ追加

`esusers`レルムにおけるユーザの追加は`esusers`コマンドによって実施できますが、
ユーザの追加の前に、先に認可に触れておきたいと思います。

## 認可（Authorization）

### Shieldにおける認可の考え方

Shiledではユーザ毎の権限管理を、ロールベースのアクセスコントロール（RBAC）モデルによって実現します。
端的言えば、各ロールにパーミッションの集合が定義されており、各ユーザはロールの集合に紐付くモデルとなります。

### ロール定義

Shildのロールは `roles.yml` で定義されます。
デフォルトでも管理者用のロール`admin`などいくつかのロールが定義されています。

```yml:/etc/elasticsearch/shield/roles.yml
# All cluster rights
# All operations on all indices
admin:
  cluster: all
  indices:
    '*':
      privileges: all

# Monitoring cluster privileges
# All operations on all indices
power_user:
  cluster: monitor
  indices:
    '*':
      privileges: all

# Only read operations on indices
user:
  indices:
    '*':
      privileges: read

# Only read operations on indices named events_*
events_user:
  indices:
    'events_*':
      privileges: read
```

ShieldではElasticsearchの権限を`cluster`と`indices`という大きく2つのタイプで分類しています。
`cluster`はElasticsearchのクラスタ全体の管理・監視アクションに対する権限を定義します。
`indices`はクラスタ内の特定のインデックスに対する管理・監視アクションに対する権限を定義します。

また`cluster`と各`index`に対する権限は、`all`, `monitor`, `read`, `write`など[予め定義された権限](https://www.elastic.co/guide/en/shield/current/reference.html#privileges-list)を設定することができますが、次のようにより細かく特定のロールに特定のアクションを指定することもできます。

```yml
user01:
  indices:
    '*':
      privileges: indices:admin/create, indices:admin/exists
```

### ロール追加

前置きが長くなりましたが、今回Kibanaでは以下3つのロールを利用しようと思います。

* kibana4_server：Kibanaサーバ用のロール
* kibana4：Kibana管理ロール（Visualization、Dashboardの読取・作成・更新・削除可）
* kibana4_monitor：Kibana監視ロール（Visualization、Dashboardは読取のみ可）

`kibana4_server`ロール、`kibana4`ロールはデフォルトの定義を利用し、`kibana4_monitoring`ロールを新規に作成します。[^1]
`roles.yml`を編集し、以下を追記します。

```yml:/etc/elasticsearch/shield/roles.yml
# The required permissions for the kibana 4 monitor
kibana4_monitoring:
  cluster:
      - cluster:monitor/nodes/info
      - cluster:monitor/health
  indices:
    'logstash-*':
      - indices:admin/get
      - indices:admin/mappings/fields/get
      - indices:admin/validate/query
      - indices:data/read/search
      - indices:data/read/msearch
      - indices:data/read/field_stats
    '.kibana':
      - indices:admin/exists
      - indices:admin/get
      - indices:admin/mapping/put
      - indices:admin/mappings/fields/get
      - indices:admin/refresh
      - indices:admin/validate/query
      - indices:data/read/mget
      - indices:data/read/search
      - indices:data/read/msearch
      - indices:data/read/field_stats
```

`kibana4`ロールの定義と比較するとわかりますが、
`.kibana`インデックスのデータに対するwrite権限が除外されている形になります。

```yml:/etc/elasticsearch/shield/roles.yml
# The required permissions for kibana 4 users.
kibana4:
  cluster:
      - cluster:monitor/nodes/info
      - cluster:monitor/health
  indices:
    '*':
      privileges: indices:admin/mappings/fields/get, indices:admin/validate/query, indices:data/read/search, indices:data/read/msearch, indices:admin/get
    '.kibana':
      privileges: indices:admin/exists, indices:admin/mapping/put, indices:admin/mappings/fields/get, indices:admin/refresh, indices:admin/validate/query, indices:data/read/get, indices:data/read/mget, indices:data/read/search, indices:data/write/delete, indices:data/write/index, indices:data/write/update
```

### 各種ユーザ作成

上記で作成したロールに紐付くユーザをそれぞれ作成します。
`esusers`コマンドの`useradd`オプションに「作成するユーザ名」、`-r`オプションに「紐付けるロール名」を指定します。
コマンド実行後にパスワードの入力を求められます。

```
# Kibanaサーバ用ユーザ
/usr/share/elasticsearch/bin/shield/esusers useradd kibana4_server -r kibana4_server
# Kibana管理用ユーザ
/usr/share/elasticsearch/bin/shield/esusers useradd kibana4 -r kibana4
# Kibana監視用ユーザ
/usr/share/elasticsearch/bin/shield/esusers useradd kibana4_monitor -r kibana4_monitoring
```

## Kibana設定

Kibanaの設定ファイル`/config/kibana.yml`を編集し、Kibanaサーバユーザの定義を追加します。

```yml:/opt/kibana-4.3.0-linux-x64/config/kibana.yml
elasticsearch.username: "kibana4_server"
elasticsearch.password: "password"
```

上記の設定まで完了しましたらElasticsearch、Kibanaともに再起動し、Kibana画面上から確認してみます。[^2]

# 確認

## Kibana管理ユーザでログイン

Kibanaにアクセスすると、ユーザ名・パスワードのの入力を求められますので、Kibana管理者ユーザでログインします。
<img width="1440" alt="スクリーンショット 2015-12-13 2.35.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/b1ec34ac-0b02-a312-96f9-15df78873be7.png">

## Visualization、Dashboard作成

適当にVisualizationを作成してDashboardに設定し、「dashboard_twitter」という名前で保存します。
管理者なので問題なく保存できます。

Kibana4.2から黒Dashboardが再び選択できるようになりましたが、やはり黒の方がカッコいい！！
<img width="1440" alt="スクリーンショット 2015-12-13 2.37.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/a32a49fe-5550-8346-fe40-dd42cd691304.png">

## Kibana監査ユーザでログイン

ブラウザを立ち上げ直し再度Kibanaにアクセスし、今度はKibana監視ユーザでログインします。
<img width="1440" alt="スクリーンショット 2015-12-13 2.29.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/4fee0974-1c41-21eb-da6c-130cd1e69b21.png">

## Visualization、Dashboard読込

先ほど管理ユーザで作成したDashboard「dashboard_twitter」を開きます。
ここまでは問題ないですね。
<img width="1440" alt="スクリーンショット 2015-12-13 2.37.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/a32a49fe-5550-8346-fe40-dd42cd691304.png">

## Visualization、Dashboard編集・保存

ここからが本題です。Dashboardを編集して保存してみましょう。
ここではVisualizationを2つ削ってみました。
<img width="1440" alt="スクリーンショット 2015-12-13 2.32.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/4339c86f-d7c5-43ea-e156-f5cba2a85f16.png">

## 権限エラー

キターーーー(・∀・)ーーー！！ばっちり権限エラーとなってますね。
別途Visualizationの新規作成等も試してみましたがこちらも想定通りエラーとなりました。
<img width="1440" alt="スクリーンショット 2015-12-13 2.32.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/42275/8ba14082-f5fb-9dc1-b708-f96c97121867.png">

# まとめ

いかがでしたでしょうか。
ビジネスでELKを利用するとなると、ここら辺のセキュリティに対する考慮は避けて通れない部分かと思います。
有償プラグインとなりますが利用価値は大きいと考えています。
「Shield」では本日紹介した「認証」「認可」以外にも「監査証跡ログ出力」や「暗号化通信」、「IPフィルタリング」等もサポートしていますので機会があれば試してみようと思います。

また、本日紹介した方法以外のベストプラクティスがあればご教示ください。

# 参考（公式サイト以外）

* http://blog.johtani.info/blog/2015/02/27/you-know-for-security-shield-goes-ga-ja/
* http://dev.classmethod.jp/cloud/aws/using-elasticsearch-plugin-shield/
* http://dev.classmethod.jp/cloud/aws/twitter-visualize-using-elastic/


[^1]: 厳密にはデフォルトの設定に`indices:data/read/field_stats`を追加しています。
[^2]: 当記事では割愛してますがShield設定後に、LogstashからElasticsearchに連携するためにはLogstash用のユーザを設定（`esusers`コマンドでのlogstashユーザ追加、elasticsearch-output-pluginへのユーザ設定追加）する必要があります。