Kibanaのセキュリティ設定について改めて考えてみた #Elasitcsearch #Kibana
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
皆さま、Elasticの通信暗号化やユーザ認証が今や無償で利用できることをご存知でしたでしょうか?
すでに1年以上前のことになりますが、バージョン6.8または7.1以降では
以下のセキュリティ機能が無償のベーシックで利用することができるようになっています。
・TLSによる通信暗号化
・ユーザー作成と管理にファイルおよびネイティブのレルム認証を使用可能
・クラスターAPIとインデックスに対するユーザーアクセスの管理にロールベースのアクセス制御を使用可能
・Spaces機能でKibanaのマルチテナンシーの安全性を向上
【参考】
・Elasticsearchのセキュリティの主要な機能が無料に
Elasticのセキュリティ機能とは
- 2020年11月時点における主なセキュリティ機能について、表にまとめてみました。
- 色々と試行錯誤した結果、今回実際に利用した機能はユーザ認証(パスワード)のみでした。
主なセキュリティ機能 | 無償/有償 | 本件で利用 |
---|---|---|
通信暗号化 (TLS) | 無償 | × (Cloud LoadBalancingで実装) |
ロールベースアクセス制御 (RBAC) | 無償 | × |
Kibana Space (マルチテナント) | 無償 | × |
ユーザ認証 (パスワード) | 無償 | ● |
ユーザ認証 (LDAP/AD) | 有償 | × |
ユーザ認証 (SSO) | 有償 | × |
監査ログ | 有償 | × |
IPフィルタ | 有償 | × (Cloud Armorで実装) |
高度なアクセス制御 (Field&Document) | 有償 | × |
データ暗号化 (dmcryptサポート) | 有償 | × |
※ 有償はゴールド以上のサブスクリプションが必要な機能になります。(詳細は以下参照)
【参考】
・Elastic Stackサブスクリプション
前提としたこと
実装方法は多々あると思いますが、今回は以下を前提としました。
- AWSではなく、Google Cloud Platform (以下、GCP)で構築すること。
- Elasticのセキュリティ機能は無償範囲内で実装すること。
- インターネット公開するため、通信暗号化とユーザ認証は最低限実装すること。
- 送信元を特定のIPアドレスからに限定できること。 (ElasticのIPフィルタ機能を使わずに...)
試行錯誤してみた
改めて本番環境をクラウド上に構築して、Kibanaをインターネット公開する場合に
どこまでセキュリティ機能を活用して、実装するのがバランスの良い構成となり得るのか考えてみました。
まず、インターネット経由でのKibanaアクセスに対して、以下の機能は必須と考えました。
・通信暗号化(TLS)
・ユーザ認証(パスワード)
・IPフィルタ
しかし、上2つは無償範囲で実装ができますが、IPフィルタは有償となるため別の方法を検討しました。
また、通信暗号化(TLS)についても対象区間によって使う機能が異なるため、改めて整理してみました。
Elasticの通信暗号化
- 通信暗号化(TLS)ですが、Elasticにおける通信暗号化にはいくつか暗号化の対象区間が存在します。
① ブラウザ-Kibana間
・インターネット公開の区間であり、今回暗号化したい区間です。(セキュリティ機能は不要)
② Kibana-Elasticsearch間
・HTTPレイヤーの暗号化という表現をします。(セキュリティ機能のオプション)
③ Elasticsearch間(クラスタ構成)
・トランスポートレイヤーの暗号化という表現をします。(セキュリティ機能の必須)
- 今回必要としている通信暗号化(TLS)は、ベーシックのセキュリティ機能が必要ではありませんでした。
- ベーシックのセキュリティ機能を有効化した場合、Elasticsearch間のトランスポートレイヤーの暗号化は必須となります。
【参考】
・Encrypt communications in Kibana
・Encrypting communications in Elasticsearch
色々と考えた結果、、
- 本番運用を考えるとLB配下にKibanaを配置した方式が良いため、案2を採用しました。
構成案 | 通信暗号化(TLS) | ユーザ認証(パスワード) | IPアドレス制御 | メリット | デメリット |
---|---|---|---|---|---|
案1 | kibanaのTLS機能(Elastic) | ベーシック機能(Elastic) | クラウドファイアウォール | 最低限のクラウドサービスで組める | 証明書管理が意外と手間 |
案2 | クラウド負荷分散によるTLS化(Cloud Load Balancing) | ベーシック機能(Elastic) | クラウドWAF(Cloud Armor) ※1 | クラウドサービスにオフロードできる | 意外と大掛かりになる |
※1 Cloud Load Balancingにファイアウォールを適用できないため、Cloud ArmorでIPアドレス制御を実装します。
- 個人のちょっとした検証で利用するには、Kibanaが生成する自己証明書を利用することも可能です。
- 企業内で利用する場合は、3rdパーティの発行する証明書をインポートすることをお勧めします。
(以下、Let's Encryptでの導入例になります)
【参考】
・Elasticsearch & KibanaをLet's EncryptでTLS対応させる
以降の内容は、実際に検証した手順の紹介になります。
利用環境
- GCP上に現時点における最新のElasticバージョンで環境構築しています。
項目 | パラメータ |
---|---|
Kibana version | 7.10.0 |
Elasticsearch version | 7.10.0 |
VM type | e2-standard-2 (vCPU2個、メモリ8GB) |
VM image | centos-7-v20201112 |
Region | us-central1 (アイオワ) |
【構成図】
- Elasticsearch/KibanaがインストールされたVMインスタンスは、elasticstack01になります。
- GCPのVPCネットワークは、defaultネットワークを利用しています。
実施手順
- 以下の手順で設定を実施しました。
- インスタンスグループの作成 (GCP)
- ヘルスチェックの作成 (GCP)
- ファイアウォールルールの作成 (GCP)
- GoogleマネージドSSL証明書の取得 (GCP)
- 外部IPアドレスの予約 (GCP)
- Cloud Load Balancingの作成 (GCP)
- DNSサーバの設定 (GCP)
- セキュリティポリシーの作成 (GCP)
- ユーザ認証(パスワード)の有効化 (Elastic)
- Kibana接続 (Elastic)
【補足事項】
・本投稿では、VM作成およびElasticsearch/Kibanaのインストール手順は省略しています。
・原則、GCPでの操作はCloudShellによるgcloudコマンドで実施しています。
・SSL証明書に利用できるDNSドメイン名を保有していることを前提としています。
1. インスタンスグループの作成 (GCP)
- VMをCloud Load Balancing配下に配置するため、非マネージドインスタンスグループを作成します。
$ gcloud compute instance-groups unmanaged create kibana-group \ --named-ports "kibana:5601" \ --zone "us-central1-a"
- インスタンスグループ (kibana-group)に名前付きポート (kibana:5601)を追加させます。
$ gcloud compute instance-groups unmanaged set-named-ports kibana-group \ --named-ports "kibana:5601" \ --zone "us-central1-a"
- インスタンスグループ (kibana-group)にVMインスタンス (elasticstack01)を所属させます。
$ gcloud compute instance-groups unmanaged add-instances kibana-group \ --instances "elasticstack01" \ --zone "us-central1-a"
【参考】
・非マネージドインスタンスグループの作成
2. ヘルスチェックの作成 (GCP)
- Cloud Load BalancingからKibanaに対するヘルスチェックの設定を作成します。
- 手順6でKibanaのユーザ認証を有効化しますが、ヘルスチェックは認証無しで通るに設定を行います。
- プロトコル: HTTP、ポート: 5601(Kibanaのデフォルト)、リクエストパス: /statusとします。
$ gcloud compute health-checks create http kibana-healthcheck \ --port "5601" \ --request-path "/status" \ --global
【参考】
・ヘルスチェックの作成
3. ファイアウォールルールの作成 (GCP)
- Cloud Load BalancingからKibanaへのヘルスチェック通信を許可する必要があります。
- Cloud Load Balancingが利用する35.191.0.0/16と130.211.0.0/22からのTCP5601のみ許可します。
- VMインスタンスに紐付けできるようなターゲットタグを設定しましょう。 (今回は、kibanaとしています)
$ gcloud compute firewall-rules create kibana-firewall \ --direction INGRESS \ --source-ranges 35.191.0.0/16,130.211.0.0/22 \ --allow "tcp:5601" \ --target-tags "kibana"
【参考】
・ヘルスチェックのファイアウォールルール
4. GoogleマネージドSSL証明書の取得 (GCP)
- Googleが証明書管理してくれるマネージドSSL証明書を利用します。
- GoogleマネージドSSL証明書は、取得および更新(自動)をGoogleが実施し、費用もかかりません。
- 今回は、ドメイン名: kibana01.elasticdev.ninjaというSSL証明書を作成します。
$ gcloud compute ssl-certificates create kibana-tls \ --domains "kibana01.elasticdev.ninja" \ --global
【参考】
・GoogleマネージドSSL証明書の使用
5. 外部IPアドレスの予約 (GCP)
- Cloud Load Balancingに割り当てる固定グローバルIPアドレスを予約します。
$ gcloud compute addresses create kibana-lb-ipv4 \ --ip-version IPV4 \ --global
【参考】
・静的外部IPアドレス
6. Cloud Load Balancingの作成 (GCP)
- Cloud Load Balancingには、HTTP(S)負荷分散、TCP負荷分散、UDP負荷分散の3種類が存在します。
-
実際には、ケースに応じて細かく複雑になっていますが、今回はHTTP(S)負荷分散を利用します。
-
Cloud Load Balancingが負荷分散する先のインスタンスグループを認識できるようにバックエンドサービスを作成します。
- 手順1で作成したKibanaが含まれるインスタンスグループ (kibana-group) を利用します。
- 手順2で作成したヘルスチェック (kibana-healthcheck) でKibanaインスタンスの正常性監視を行います。
$ gcloud compute backend-services create kibana-back \ --protocol "HTTP" \ --port-name "kibana" \ --health-checks "kibana-healthcheck" \ --global $ gcloud compute backend-services add-backend kibana-back \ --instance-group "kibana-group" \ --instance-group-zone "us-central1-a" \ --global
- デフォルトのバックエンドサービスに受信リクエストをルーティングするURLマップを作成します。
$ gcloud compute url-maps create kibana-lb \ --default-service "kibana-back" $ gcloud compute url-maps add-path-matcher kibana-lb \ --path-matcher-name "kibana-matcher" \ --default-service "kibana-back" \ --path-rules "/app/kibana"="kibana-back" \ --new-hosts "kibana01.elasticdev.ninja"
- URLマップにリクエストをルーティングするターゲットHTTPSプロキシを作成します。
$ gcloud compute target-https-proxies create kibana-lb-target-proxy \ --url-map "kibana-lb" \ --ssl-certificates "kibana-tls"
- 受信リクエストをプロキシにルーティングするグローバル転送ルールを作成します。
- kibana-frontというフロントエンドサービスが作成されます。
$ gcloud compute forwarding-rules create kibana-front \ --address "kibana-lb-ipv4" \ --target-https-proxy "kibana-lb-target-proxy" \ --ports "443" \ --global
【参考】
・シンプルな外部HTTPSロードバランサの設定
7. DNSサーバの設定 (GCP)
- 保有しているドメイン名でDNSゾーンを作成します。
$ gcloud dns managed-zones create "elasticdev" \ --description "elasticdev" \ --dns-name "elasticdev.ninja"
- 手順5で予約した外部IPアドレスをAレコード (今回はkibana01.elasticdev.ninja)として登録します。
- トランザクションを開始し、レコードを追加後、トランザクションを終了します。
$ gcloud dns record-sets transaction start \ --zone "elasticdev" $ gcloud dns record-sets transaction add "" \ --zone "elasticdev" \ --name "kibana01.elasticdev.ninja." \ --type A \ --ttl 300 $ gcloud dns record-sets transaction execute \ --zone "elasticdev"
- Google Domainsでドメインを保有している場合は、以下URLを参考にCloud DNSのネームサーバにGoogle Domainsを置き換えてください。
【参考】
・Google Domainsで取得したドメインにCloud DNSでIPを関連つける
・一般公開マネージド ゾーンを作成する
8. セキュリティポリシーの作成 (GCP)
- Cloud Armorで特定の送信元IPアドレス以外から通信許可しないようポリシー作成します。
- 作成したポリシーは、手順6で作成したバックエンドサービスに割り当てます。
$ gcloud compute security-policies create kibana-waf $ gcloud compute security-policies rules update 2147483647 \ --security-policy "kibana-waf" \ --action "deny-403" $ gcloud compute security-policies rules create 0 \ --security-policy "kibana-waf" \ --src-ip-ranges "" \ --action "allow" $ gcloud compute backend-services update kibana-back \ --security-policy "kibana-waf" \ --global
【参考】
・HTTP(S)負荷分散のセキュリティポリシーを構成する
9. ユーザ認証(パスワード)の有効化 (Elastic)
- Elasticsearchでセキュリティ機能を有効化します。
$ sudo vi /etc/elasticsearch/elasticsearch.yml xpack.security.enabled: true
- Elasticsearchのプロセスを再起動します。
$ sudo systemctl restart elasticsearch
- ビルドインユーザのパスワードを初期化します。 (パスワードは自社の規約に沿ったものを設定ください)
$ sudo usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive Initiating the setup of passwords for reserved users elastic,apm_system,kibana,logstash_system,beats_system,remote_monitoring_user. You will be prompted to enter passwords as the process progresses. Please confirm that you would like to continue [y/N] y Enter password for [elastic]: xxxxxxxxxxx (パスワードを入力) Reenter password for [elastic]: xxxxxxxxxxx (パスワードを入力) Enter password for [apm_system]: xxxxxxxxxxx (パスワードを入力) Reenter password for [apm_system]: xxxxxxxxxxx (パスワードを入力) Enter password for [kibana]: xxxxxxxxxxx (パスワードを入力) Reenter password for [kibana]: xxxxxxxxxxx (パスワードを入力) Enter password for [logstash_system]: xxxxxxxxxxx (パスワードを入力) Reenter password for [logstash_system]: xxxxxxxxxxx (パスワードを入力) Enter password for [beats_system]: xxxxxxxxxxx (パスワードを入力) Reenter password for [beats_system]: xxxxxxxxxxx (パスワードを入力) Enter password for [remote_monitoring_user]: xxxxxxxxxxx (パスワードを入力) Reenter password for [remote_monitoring_user]: xxxxxxxxxxx (パスワードを入力)
- Kibanaで以下の設定を行います。
- Elasticsearchへの接続に必要なユーザ認証設定を行います。
- status.allowAnonymousは、LBヘルスチェック時にユーザ認証を無視する設定になります。
$ sudo vi /etc/kibana/kibana.yml 部分抜粋 elasticsearch.username: "kibana_system" elasticsearch.password: "" status.allowAnonymous: true
- Kibanaのプロセスを再起動します。
$ sudo systemctl restart kibana
【参考】
・ビルドインユーザのパスワード初期化
10. Kibana接続 (Elastic)
- Webブラウザから "https://kibana01.elasticdev.ninja" にアクセスします。
- 許可された送信元IPアドレスからアクセスした場合とそうでない場合で画面が異なることが確認できれば完了です。
【許可されたIPアドレスからのアクセス】
【許可されていないIPアドレスからのアクセス】
まとめ
意外とかなりの長文となってしまいましたが、いかがでしたでしたでしょうか?
やはり自己証明書ではなくちゃんとしたSSL証明書を使って実装しようとすると
大掛かりになってしまう感じは否めませんね。
社内勉強会など、ちょっとしたelastic環境をクラウド上に構築するような場合は
色々と悩ましい課題がありますね。
DNSドメインを保有しておく必要がありますので、その辺りも1つハードルになりますが
本内容を抑えておけると最低限必要なセキュリティは確保できるのではないかと思います^^
ぜひ、時間あれば試してみて頂けると幸いです!