Elasticsearchのデータ暗号化

## はじめに
Elasticsearchのデータ暗号化について、検討する機会があったので、記事にまとめてみました^^:

## 利用した環境
| product | version |
|---------|---------|
| cryptsetup | 1.7.4 |
| Elasticsearch | 6.6.0 |
| OS | Amazon Linux2 (4.14.88) |
| Instance Type | m5.2xlarge |
| EBS | ルート(8GB)、追加(30GB) |
| AMI | ami-04677bdaa3c2b6e24 |

**【ディスクの状態】**
`nvme0n1`が8GBのルートボリューム、`nvme1n1`が追加の30GBボリュームです。
※m5はNitro世代のEC2インスタンスでEBS NVMeボリュームのため、`/dev/nvmeXnX`という名称

```
[root@ip-172-31-34-49 ~]# lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk 
nvme0n1       259:1    0   8G  0 disk 
├─nvme0n1p1   259:2    0   8G  0 part /
└─nvme0n1p128 259:3    0   1M  0 part 

[root@ip-172-31-34-49 ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs         16G     0   16G   0% /dev
tmpfs            16G     0   16G   0% /dev/shm
tmpfs            16G  388K   16G   1% /run
tmpfs            16G     0   16G   0% /sys/fs/cgroup
/dev/nvme0n1p1  8.0G  1.2G  6.9G  15% /
tmpfs           3.1G     0  3.1G   0% /run/user/1000
```

## 基本的な考え方
Elasticsearchのデータ(=Index)暗号化では、日立ソリューションズ社の[Credeon Secure Full-text Search][01]がありますが、あくまで全文検索のためのIndex暗号化であって、Kibanaが利用する`aggregation`機能には対応していません。

Elastic Stackでは[5.3.0][02]から保存データの暗号化に対応しています。
[サブスクリプション][03]のPlatinum Editionで対応していますが、あくまで`dm-crypt`を用いたボリュームの暗号化を利用した場合でもElasticsearchとしてサポートするというものです。暗号化機能が付いている訳ではありません。

## dm-cryptとは
Device-Mapperの機能の1つでLinuxにおけるブロックデバイス暗号の仕組みです。
Kernel 2.6以降のdevice-mapperモジュールに標準で組み込まれていて、`cyptsetup`という暗号化管理ツールを使ってストレージデバイスの暗号化を行います。
<img width="492" alt="dmcrypt.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/211371/3825647b-c688-eb9c-9bfd-db4d6d338ec9.png">

**【参考】**
・[LUKSとは？][04]

## 設定手順
1. Java 1.8.0 インストール
2. Elasticsearch 6.6.0 インストール
3. JVMヒープサイズ設定
4. Elasticsearch設定
5. 暗号化ボリュームの設定
6. Index保存領域の作成
7. 設定確認

#### 1. Java 1.8.0 インストール
Javaがインストールされていないことを確認した上でJava 1.8.0をインストールします。

```
[root@ip-172-31-34-49 ~]# java -version
-bash: java: command not found

[root@ip-172-31-34-49 ~]# yum install -y java-1.8.0-openjdk
<インストールの経過は省略>

[root@ip-172-31-34-49 ~]# java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
```

#### 2. Elasticsearch 6.6.0 インストール
Elastic社公式のリポジトリを登録し、Elasticsearch 6.6.0をインストールします。

```
[root@ip-172-31-34-49 ~]# vi /etc/yum.repos.d/elastic.repo
[root@ip-172-31-34-49 ~]# cat /etc/yum.repos.d/elastic.repo
[elasticsearch-6.x]
name=Elasticsearch repository for 6.x packages
baseurl=https://artifacts.elastic.co/packages/6.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

[root@ip-172-31-34-49 ~]# yum install -y elasticsearch
<インストールの経過は省略>

[root@ip-172-31-34-49 ~]# systemctl status elasticsearch
● elasticsearch.service - Elasticsearch
   Loaded: loaded (/usr/lib/systemd/system/elasticsearch.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: http://www.elastic.co
```

#### 3. JVMヒープサイズ設定
Elasticsearchには物理メモリの半分を上限にJVMヒープを割り当てます。
m5.2xlargeのメモリは32GBなので、今回は15GBのメモリを割り当てます。

```
[root@ip-172-31-34-49 ~]# vi /etc/elasticsearch/jvm.options
-Xms15g
-Xmx15g
# 以下、設定の確認です。
[root@ip-172-31-34-49 ~]# cat /etc/elasticsearch/jvm.options | grep 15
```

#### 4. Elasticsearch設定
ElasticsearchのIndex保存領域を`path.data`で指定します。今回は`/mnt/es/elasticsearch`とします。

```
[root@ip-172-31-34-49 ~]# vi /etc/elasticsearch/elasticsearch.yml
path.data: /mnt/es/elasticsearch
# 以下、設定の確認です。
[root@ip-172-31-34-49 ~]# cat /etc/elasticsearch/elasticsearch.yml | grep path.data
```

#### 5. 暗号化ボリュームの設定
cryptsetupコマンドで`/dev/nvme1n1`にAES256/SHA1暗号化ボリューム(`esvol`)を作成し、`/mnt/es`にマウントします。

```
[root@ip-172-31-34-49 ~]# cryptsetup --key-size 512 --hash sha512 luksFormat /dev/nvme1n1

WARNING!
========
This will overwrite data on /dev/nvme1n1 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase: #パスワードを設定
Verify passphrase: #パスワードの確認

[root@ip-172-31-34-49 ~]# cryptsetup luksOpen /dev/nvme1n1 esvol
Enter passphrase for /dev/nvme1n1: #上記で設定したパスワードを入力

[root@ip-172-31-34-49 ~]# ls /dev/mapper
control  esvol

[root@ip-172-31-34-49 ~]# mkfs.ext4 /dev/mapper/esvol
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1966080 inodes, 7863808 blocks
393190 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2155872256
240 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
        4096000

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

[root@ip-172-31-34-49 ~]# mount /dev/mapper/esvol /mnt/es
[root@ip-172-31-34-49 ~]# ll /mnt
total 4
drwxr-xr-x 3 root root 4096 Feb 10 18:29 es
```

暗号化ボリュームが作成されていることを確認します。

```
[root@ip-172-31-34-49 ~]# lsblk
NAME          MAJ:MIN RM SIZE RO TYPE  MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk  
└─esvol       253:0    0  30G  0 crypt /mnt/es
nvme0n1       259:1    0   8G  0 disk  
├─nvme0n1p1   259:2    0   8G  0 part  /
└─nvme0n1p128 259:3    0   1M  0 part

[root@ip-172-31-34-49 ~]# df -h
Filesystem         Size  Used Avail Use% Mounted on
devtmpfs            16G     0   16G   0% /dev
tmpfs               16G     0   16G   0% /dev/shm
tmpfs               16G  392K   16G   1% /run
tmpfs               16G     0   16G   0% /sys/fs/cgroup
/dev/nvme0n1p1     8.0G  1.5G  6.6G  19% /
/dev/mapper/esvol   30G   45M   28G   1% /mnt/es
tmpfs              3.1G     0  3.1G   0% /run/user/1000
```

#### 6. Index保存領域の作成
/mnt/es配下に`elasticsearch`ディレクトリを作成し、適切な権限を付与します。

```
[root@ip-172-31-34-49 elasticsearch]# cd /mnt/es
[root@ip-172-31-34-49 es]# mkdir elasticsearch
[root@ip-172-31-34-49 es]# chown elasticsearch:elasticsearch elasticsearch/
[root@ip-172-31-34-49 es]# chmod 750 elasticsearch/
[root@ip-172-31-34-49 es]# ll
total 20
drwxr-x--- 2 elasticsearch elasticsearch  4096 Feb 10 21:47 elasticsearch
drwx------ 2 root          root          16384 Feb 10 18:29 lost+found
```

Elasticsearchをサービス起動し、`path.data`配下にNodeIDファイル(`node-0.st`)が生成されていることを確認します。

```
[root@ip-172-31-34-49 es]# systemctl start elasticsearch
[root@ip-172-31-34-49 es]# cat elasticsearch/nodes/0/_state/node-0.st
?lstate:)
node_idUWTgg6zqmS2Cq72YdCOdCpA(
```

#### 7. 設定確認
- 暗号化した30GBの追加EBSボリュームのSnapshotを作成します。
- 作成したSnapshotから別途EBSボリュームを作成し、別EC2にアタッチします。

EBSをアタッチしたEC2にログインし、追加EBSとして`nvme1n1`が存在していることを確認します。

```
[root@ip-172-31-6-22 ~]# lsblk
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1       259:0    0  100G  0 disk 
├─nvme0n1p1   259:1    0  100G  0 part /
└─nvme0n1p128 259:2    0    1M  0 part 
nvme1n1       259:3    0   30G  0 disk 

[root@ip-172-31-6-22 ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        3.8G     0  3.8G   0% /dev
tmpfs           3.8G     0  3.8G   0% /dev/shm
tmpfs           3.8G  380K  3.8G   1% /run
tmpfs           3.8G     0  3.8G   0% /sys/fs/cgroup
/dev/nvme0n1p1  100G  2.2G   98G   3% /
tmpfs           763M     0  763M   0% /run/user/1000
```

/mnt/esにマウントし、内容を確認してみます。

```
[root@ip-172-31-6-22 ~]# mkdir /mnt/es
[root@ip-172-31-6-22 ~]# mount -o discard /dev/nvme1n1 /mnt/es
mount: /mnt/es: unknown filesystem type 'crypto_LUKS'.
```

## まとめ
LUKSで暗号化したボリュームを他マシンにマウントしても認識しませんでした。
これでElasticsearchノードにログインされない限り、OSレベルでのIndexデータの機密性は確保できました。

ElasticsearchのData Nodeをi3系インスタンスで構築し、[NVMe SSD][05]にIndexを配置することで暗号化することも可能です。

>NVMe インスタンスストレージのデータは、インスタンスのハードウェアモジュールに実装されている XTS-AES-256 ブロック暗号を使用して暗号化されます。暗号化キーは、ハードウェアモジュールで作成され、NVMe インスタンスストレージデバイスごとに固有です。すべての暗号化キーは、インスタンスが停止または終了して復元できないときに破棄されます。この暗号化を無効にしたり、独自の暗号キーを指定したりすることはできません。

以上、いかがでしたでしょうか。
不明な点、誤植などありましたら、コメントをお願いします！！

[01]: https://msdnshared.blob.core.windows.net/media/2018/01/Secure_Fulltext_Search_Brochure_r17_jp_20160204.pdf "Credeon Secure Full-text Search"
[02]:https://www.elastic.co/jp/blog/elastic-stack-5-3-0-released "Elastic Stack 5.3.0リリース"
[03]:https://www.elastic.co/jp/subscriptions "サブスクリプション"
[04]:http://nekophylline.tea-nifty.com/blog/2016/04/luks-5c07.html "LUKSとは？"
[05]:https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ssd-instance-store.html "SSD インスタンスストアボリューム"