2026年6月30日
2026年6月30日
ELKスタック(Elasticsearch・Logstash・Kibana)のセットアップ手順|セキュリティ有効のまま構築する方法
ELKスタックは、Elasticsearch(保存・検索)、Logstash(加工)、Kibana(可視化)を組み合わせたログ基盤です。現行の8.x系はインストール直後からTLS通信とユーザー認証が有効になっており、その仕組みに沿って進めれば、セキュリティを切らずに安全な構成を作れます。この記事では、Ubuntu/Debianの1台のサーバーに3つをインストールし、Filebeatで集めたNginxのアクセスログをKibanaで検索できるところまでを順に説明します。
構成と事前に決めておくこと
ログの流れは「Filebeat → Logstash → Elasticsearch → Kibana」です。Filebeatは各サーバーでログファイルを読み取って送る軽量な収集役で、Logstashはそれを受け取り、アクセスログの1行をIPアドレス・リクエスト・ステータスコードなどの項目に分解します。分解されたデータをElasticsearchが保存し、Kibanaがブラウザから検索・グラフ化します。
小規模なら1台に同居させても動きますが、Elasticsearchはメモリを多く使います。3つを同居させる場合は、メモリ8GB程度を目安にしてください。4GBでも起動はしますが、ログ量が増えると検索が遅くなったり、メモリ不足でプロセスが強制終了されたりしやすくなります。
バージョンは3製品で揃えるのが原則です。Elasticsearch 8.15にKibana 8.12を組み合わせる、といった混在は動作保証外になります。(2026年10月現在、9.x系も提供されています。手順の流れは8.x系とほぼ同じで、リポジトリURLの「8.x」を「9.x」に変えて読み替えられます。)
Elasticsearchのインストール
Elastic公式のAPTリポジトリを登録してインストールします。
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
sudo apt-get install -y apt-transport-https
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt-get update && sudo apt-get install -y elasticsearch
インストールの途中で「Security autoconfiguration information」という出力が表示され、そこに組み込みの管理ユーザー elastic のパスワードが一度だけ出ます。この時点で、HTTP通信用とノード間通信用のTLS証明書も自動生成され、/etc/elasticsearch/certs/ に置かれます。パスワードを控え損ねた場合は、次のコマンドで再発行できます。
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
起動前に、ヒープサイズを決めておきます。8.x系は搭載メモリから自動で決めますが、同居構成ではLogstashやKibanaの分を残すため、明示したほうが安定します。目安は搭載メモリの半分以下で、最小値と最大値を同じにします。
# /etc/elasticsearch/jvm.options.d/heap.options
-Xms2g
-Xmx2g
あわせて sysctl vm.max_map_count を実行し、値が262144以上かを確かめてください。足りないと起動時のチェックで止まる場合があります。そのうえで起動し、HTTPSと認証付きで応答するかを確認します。
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
sudo curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic https://localhost:9200
パスワードを入力し、クラスター名やバージョン番号を含むJSONが返れば成功です。ここで「証明書エラーが出るので -k を付ける」「面倒なので xpack.security.enabled: false にする」といった回避は避けてください。前者は通信相手の検証を捨てることになり、後者はポート9200に到達できる人なら誰でもデータを読み書き・削除できる状態になります。Elasticsearchが認証なしで公開され、データを消されて身代金を要求される被害は実際に繰り返し起きています。
なお、自動設定は /etc/elasticsearch/elasticsearch.yml に http.host: 0.0.0.0 を書き込むため、HTTP API(9200番)はすべてのネットワークインターフェースで待ち受けます。通信は暗号化され認証も必要ですが、同じサーバー内でしか使わない構成なら、この値を localhost に変えるか、ファイアウォールで9200番への外部からの接続を閉じておくと、攻撃を受ける入口を減らせます。
Kibanaのインストールと接続
Kibanaは同じリポジトリから入れます。Elasticsearchへの接続設定は、エンロールメントトークンという一時的な合言葉を使うと、証明書の配置まで自動で行われます。
sudo apt-get install -y kibana
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana
sudo /usr/share/kibana/bin/kibana-setup --enrollment-token <表示されたトークン>
sudo systemctl enable --now kibana
トークンの有効期限は30分です。時間が経って失敗した場合は、トークンを作り直してください。
Kibanaは既定で localhost:5601 だけで待ち受けます。ブラウザから使うには、SSHのポートフォワードで手元のPCから接続するか、NginxをHTTPSのリバースプロキシとして前に置き、アクセス元IPの制限をかける方法が一般的です。5601番をインターネットに直接開けるのは避けてください。
アラート機能などで警告が出る場合は、kibana-encryption-keys generate で暗号化キーを生成し、表示された設定行を /etc/kibana/kibana.yml に追記して再起動します。
ログイン画面が出たら、elastic ユーザーで入ります。ただし elastic はすべての権限を持つため、日常の閲覧用には「Stack Management」の「Users」から権限を絞ったユーザーを別に作ると安全です。
Logstash用の書き込みユーザーを作る
Logstashに elastic のパスワードを持たせると、設定ファイルが漏れたときの被害が大きくなります。書き込み先のインデックスだけを操作できるロールとユーザーを作ります。
CA=/etc/elasticsearch/certs/http_ca.crt
sudo curl --cacert $CA -u elastic -X POST "https://localhost:9200/_security/role/logstash_writer" \
-H 'Content-Type: application/json' -d '{
"cluster": ["monitor", "manage_index_templates", "manage_ilm"],
"indices": [{
"names": ["nginx-access-*"],
"privileges": ["write", "create", "create_index", "manage", "manage_ilm"]
}]
}'
sudo curl --cacert $CA -u elastic -X POST "https://localhost:9200/_security/user/logstash_internal" \
-H 'Content-Type: application/json' -d '{
"password": "your-strong-password",
"roles": ["logstash_writer"]
}'
同じ操作はKibanaの「Stack Management」→「Roles」「Users」からも行えます。
Logstashのパイプライン設定
Logstashをインストールし、Elasticsearchの証明書をLogstashから読める場所に複製します。
sudo apt-get install -y logstash
sudo mkdir -p /etc/logstash/certs
sudo cp /etc/elasticsearch/certs/http_ca.crt /etc/logstash/certs/
sudo chown -R root:logstash /etc/logstash/certs && sudo chmod 640 /etc/logstash/certs/http_ca.crt
パスワードは設定ファイルに平文で書かず、Logstashのキーストアに入れます。
sudo /usr/share/logstash/bin/logstash-keystore --path.settings /etc/logstash create
sudo /usr/share/logstash/bin/logstash-keystore --path.settings /etc/logstash add ES_PW
パイプラインは /etc/logstash/conf.d/ に置きます。Beatsからの受信、Nginxの標準形式(combined)の分解、Elasticsearchへの出力という3段構成です。
# /etc/logstash/conf.d/nginx.conf
input {
beats {
port => 5044
}
}
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
}
}
output {
elasticsearch {
hosts => ["https://localhost:9200"]
ssl_certificate_authorities => ["/etc/logstash/certs/http_ca.crt"]
user => "logstash_internal"
password => "${ES_PW}"
index => "nginx-access-%{+YYYY.MM.dd}"
}
}
date フィルターは、ログ行に書かれた時刻をイベントの時刻(@timestamp)に使うための設定です。これがないと「Logstashが受け取った時刻」で記録されるため、遅れて送られたログや過去ログの取り込みで時系列がずれます。
起動前に構文チェックを通しておくと、原因の分かりにくい起動失敗を避けられます。
sudo -u logstash /usr/share/logstash/bin/logstash --path.settings /etc/logstash -t
sudo systemctl enable --now logstash
「Configuration OK」と出れば構文は問題ありません。起動後のエラーは /var/log/logstash/logstash-plain.log に出ます。認証エラー(401)ならユーザー名とキーストアの値、証明書エラーなら ssl_certificate_authorities のパスと読み取り権限を見直します。
Filebeatでログを送る
ログを出しているサーバーにFilebeatを入れます。同じサーバーなら、そのままインストールできます。
sudo apt-get install -y filebeat
/etc/filebeat/filebeat.yml の入力と出力を次のように設定します。初期状態では output.elasticsearch が有効になっているので、そちらはコメントアウトし、出力先を1つにします。
filebeat.inputs:
- type: filestream
id: nginx-access
paths:
- /var/log/nginx/access.log
output.logstash:
hosts: ["localhost:5044"]
sudo filebeat test config
sudo filebeat test output
sudo systemctl enable --now filebeat
別のサーバーから5044番へ送る場合は、ファイアウォールで送信元IPを限定したうえで、FilebeatとLogstashの間もTLSで暗号化してください。ログにはIPアドレスやURLなどの個人に関わる情報が含まれるため、平文で外部ネットワークを流すのは避けたほうが安全です。
Kibanaでログを確認する
Kibanaの「Stack Management」→「Data Views」で、インデックスパターン nginx-access-*、時刻フィールド @timestamp のデータビューを作ります。その後「Discover」を開くと、取り込まれたログが時系列で並びます。
データが出ない場合は、上流から順に切り分けます。
- Elasticsearchにインデックスがあるか:
curl --cacert ... -u elastic "https://localhost:9200/_cat/indices/nginx-access-*?v" - Logstashのログに401や証明書のエラーがないか
- Filebeatのログ(
journalctl -u filebeat)に接続エラーがないか - Discoverの表示期間が、ログの時刻を含んでいるか
grokで分解できなかった行には _grokparsefailure というタグが付きます。Nginxの log_format を独自に変えている場合はこのタグが大量に出るので、パターンを実際の形式に合わせて調整してください。
運用で気をつけること
ログは放っておくとディスクを使い切ります。Elasticsearchはディスク使用率が一定の水準を超えると新しい書き込みを止めるため、「ある日から急にログが入らなくなった」という形で表面化します。Kibanaの「Index Lifecycle Policies」で、例えば30日を過ぎたインデックスを削除するポリシーを作り、対象のインデックスに適用しておくと安心です。
アップデートは、Elasticsearch → Kibana → Logstash → Filebeat の順に行い、バージョンを揃えます。apt upgrade で一部だけが先に上がると不整合が起きるため、apt-mark hold でバージョンを固定し、計画的に上げる運用も検討してください。
まとめ
- 8.x系はTLSと認証が初期状態で有効。証明書と自動生成パスワードを使って接続し、無効化はしない
- Elasticsearchのヒープは最小・最大を同じ値にし、同居構成では搭載メモリの半分以下に抑える
- KibanaはエンロールメントトークンでElasticsearchとつなぎ、5601番は直接公開しない
- Logstashには書き込み専用ユーザーを作り、パスワードはキーストアに入れる
- 取り込めないときはElasticsearch → Logstash → Filebeatの順にログを見て切り分ける
- ライフサイクルポリシーで古いインデックスを削除し、ディスク不足を防ぐ