【Elasticsearch & Kibana】最低限のセキュリティを踏まえたElasticsearch with Docker

こんにちは。

付録に記載したセキュリティ要件を満たしつつ、DockerでElasticsearchとKibanaを起動することを目的としたページです。

記載内容に間違いがあったり、改善点があったりしたら是非ご指摘をいただきたいです。

よろしくお願いいたします。

## 前提

Elasticsearchの設定ファイル　`elasticsearch.yml`

Kibanaの設定ファイル `kibana.yml`

上記が入っている場所を設定ディレクトリという

Elasticsearch側 設定ディレクトリe (e.g. `$ES_PATH_CONF`)

Kibana側 設定ディレクトリ (e.g. `$KBN_PATH_CONF`)

ブラウザからElasticsearchにログインするユーザー: `elastic` 

→これはsuperuser

KibanaからElasticsearchにログインするユーザー: `kibana_system`

Elasticsearchはシングルノードで作成する

### ⚠️マルチノードの場合本番環境では推奨されていないので注意

transport層のセキュリティも気にしないといけない(ノード間通信)

## 構成

今回は前段として、⓪証明書系の生成(ここは本番では使わないはず？)を行ったのち

①  Elasticsearchの起動

①’ `kibana_system`のパスワード変更

②  Kibanaの起動

を行います。

パスワードを変更してKibanaを再起動する必要があるようなので、後から起動してしまう作戦です。

ではみていきます。

### ネットワーク作成

```bash
docker network create elastic
```

## 証明書作成

```bash
docker run --rm --user=root --network=elastic -v certs:$ES_PATH_CONF/certs docker.elastic.co/elasticsearch/elasticsearch:$VERSION \
  /bin/bash -c "elasticsearch-certutil ca --silent --pem";
```

docker runを使ってコンテナ内で`elasticsearch-certutil` を実行する形です。

証明書はVolumeで管理してるので、docker runでやってしまいます。

## composeファイル

```docker
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:${VERSION}
    container_name: es01
    environment:
      - node.name=es01
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms${ES_HEAP_MEMORY_SIZE_MIN} -Xmx${ES_HEAP_MEMORY_SIZE_MAX}"
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=${ES_PATH_CONF}/certs/es01/es01.key
      - xpack.security.http.ssl.certificate=${ES_PATH_CONF}/certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=${ES_PATH_CONF}/certs/ca/ca.crt
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data01:/usr/share/elasticsearch/data
      - ${HOME}/certs:${ES_PATH_CONF}/certs:ro
    ports:
      - 9200:9200
    networks:
      - elastic
    healthcheck:
      test: curl --cacert ${ES_PATH_CONF}/certs/ca/ca.crt -s https://${ES_HOST}:9200 >/dev/null || exit 1
      interval: 30s
      timeout: 10s
      retries: 5

  kib01:
    image: docker.elastic.co/kibana/kibana:${VERSION}
    container_name: kib01
    depends_on:
      - es01
    environment:
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${ES_PASSWORD}
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=${KIB_PATH_CONF}/certs/ca/ca.crt
      - ELASTICSEARCH_SSL_VERIFICATIONMODE=certificate
      - csp.strict=true
      - SERVER_HOST=0.0.0.0
      - SERVER_SSL_ENABLED=true
      - SERVER_SSL_CERTIFICATE=${KIB_PATH_CONF}/certs/es01/es01.crt
      - SERVER_SSL_KEY=${KIB_PATH_CONF}/certs/es01/es01.key
      - server.securityResponseHeaders.disableEmbedding=true
      - server.securityResponseHeaders.strictTransportSecurity="max-age=31536000"

    volumes:
      - kibanadata:/usr/share/kibana/data
      - ${HOME}/certs:${KIB_PATH_CONF}/certs:ro
    ports:
      - 5601:5601
    networks:
      - elastic
    healthcheck:
      test: curl -s https://${ES_HOST}:5601 >/dev/null || exit 1
      interval: 30s
      timeout: 10s
      retries: 5

volumes:
  certs:
    driver: local
  data01:
    driver: local
  kibanadata:
    driver: local

networks:
  elastic:
    driver: bridge
```

KibanaのEnvironmentについては以下でも書いた通り大文字で設定する必要があります。

https://qiita.com/tamaloon/items/0cb52d5f956e7564d38f

https://www.elastic.co/guide/en/kibana/current/docker.html#environment-variable-config

設定したcomposeファイルを使って次のような順序でサービスを起動します。

```bash
docker-compose up es01 -d
bash set_password.sh # 下で触れます
docker-compose up kib01 -d
```

## パスワード生成

KibanaがElasticsearchにアクセスするためのパスワードを生成します。

前提でも書いたように、アクセス用のアカウントとして`kibana_system` が用意されています。

パスワードのリセットにはElasticsearchのコンテナが必要で、特に再起動の予定もないので先に起動してます。

```bash
echo "Start Creating Containers...";

echo "Waiting for Elasticsearch availability";
until curl --cacert certs/ca/ca.crt -s https://$ES_HOST:9200 >/dev/null; do
sleep 5;
# certs/ca/ca.crtはローカルにある証明書
# なんらかの返答があればサービスの起動が確認できたことになるのでパスワードのリセット処理に入る

echo "Waiting for Elasticsearch...";
done;

# パスワードリセットして出力を保存
docker exec -it es01 elasticsearch-reset-password -u kibana_system --url https://$ES_HOST:9200 --batch  > pwd_output.txt

# 出力からパスワードを抽出して.envに追加
# ここはあんまいけてない
NEW_PWD=$(grep 'New value:' pwd_output.txt | sed 's/New value: //'| tr -d '\n\r')
if [ ! -z "$NEW_PWD" ]; then
  echo -e "ES_PASSWORD=\"$NEW_PWD\"" >> .env
  echo "パスワードを.envに追加しました"
fi

echo "Finish Creating Containers...";
```

これでKibanaがElasticsearchに接続できるようになったので、満を持してKibanaのサービスを起動します。

しばらく待ったら、
https://localhost:5601
などでアクセスできるはずです。

※localhostの箇所は設定したホストによって変わります。

ログインするときは`elastic` ユーザーを使うと良いです。

パスワードのリセットはローカルで`kibana_system` を参考にしてやってください。

以上です！

## 感想

とりあえず動かすことはできました。

現状の課題としては、内部で作成された鍵を用いている点とそれらをvolumeで共有していることです。

外部で生成した鍵をそれぞれで保存する形にすれば問題ないように思います。

問題があればぜひ教えてください。

## 付録)最低限のセキュリティ ざっくりまとめ

これらドキュメントをまとめただけ

https://www.elastic.co/guide/en/elasticsearch/reference/current/security-minimal-setup.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/security-basic-setup-https.html

### `xpack.security.enabled: true` を `elasticsearch.yml` に設定

この設定によってユーザー名とパスワードによるbasic認証が通る

## パスワードのリセット

```bash
./bin/elasticsearch-reset-password -u kibana_system
```

このコマンドでユーザー`kibana_system`のパスワードをリセットできる　

`elastic` についても同コマンドでパスワードをリセットできる　

## KibanaからElasticsearchへのパスワードによる接続

KibanaからElasticsearchに接続するうえで必要な設定

### `elasticsearch.username: "kibana_system"` を`kibana.yml` に設定

### Kibana内のセキュアな情報を保護するための設定

キーストアの作成

```bash
./bin/kibana-keystore create
```

Elasticsearchのパスワードをキーストアに追加

```bash
./bin/kibana-keystore add elasticsearch.password
```

Kibanaをリスタートする

## For HTTPs ElasticSearch & Client 編

動作を止める

以下コマンドを実行し、CAのパス等を指定

```bash
./bin/elasticsearch-certutil http
```

　

生成される`elasticsearch-ssl-http.zip` を解凍する

`http.p12` を設定ディレクトリeに移動

`xpack.security.http.ssl.enabled: true`を`elasticsearch.yml` に設定

`xpack.security.http.ssl.keystore.path: http.p12`を`elasticsearch.yml` に設定

キーストアに追加

```bash
./bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password
```

## For HTTPs Kibana & ElasticSearch 編

`elasticsearch-ca.pem` を設定ディレクトリkに移動

`elasticsearch.ssl.certificateAuthorities: $KBN_PATH_CONF/elasticsearch-ca.pem` を`kibana.yml` に設定
`elasticsearch.hosts: https://<your_elasticsearch_host>:9200` を`kibana.yml` に設定

Kibanaをリスタート

## For HTTPs Kibana & Client 編

サーバーの証明書と秘密鍵を生成

```bash
./bin/elasticsearch-certutil csr -name kibana-server -dns example.com,www.example.com
```

生成される`csr-bundle.zip`を解凍

`server.ssl.certificate: $KBN_PATH_CONF/kibana-server.crt`を`kibana.yml` に設定
`server.ssl.key: $KBN_PATH_CONF/kibana-server.key`を`kibana.yml` に設定
`server.ssl.enabled: true`を`kibana.yml` に設定

## 仲間を募集中！
株式会社ホープでは、福岡で働くエンジニアを募集中です。
ぜひ、求人を見てみてください！
▼ Wantedly求人
https://www.wantedly.com/projects/1684027
▼ コーポレートサイト
https://www.zaigenkakuho.com/recruit/
「自治体を通じて人々に新たな価値を提供し、会社及び従業員の成長を追求する」
この理念の実現に向けて、今後も自治体の課題解決に取り組んでいきます。
ご応募お待ちしております！