
はじめに
こんにちは!今回は、手元のPC(ローカル環境)に本格的なセキュリティ・テストラボを構築する方法をご紹介します。
セキュリティの勉強をしようと思っても、攻撃を試したり防御のログを見たりする環境を用意するのは一苦労ですよね。そこで今回は、Docker Composeを使って「やられサイト」「攻撃ツール」「防御・監視ツール(SIEM/IDS)」をコマンド一発で立ち上げる環境を作ります。
さらに、構築した環境を使って、ネットワーク侵入検知システム(Suricata)のログを、統合ログ管理ツール(Splunk)に取り込んでグラフ化するところまでをステップ・バイ・ステップで解説します!
今回構築するラボの構成
今回作成するラボ環境には、大きく分けて3つの役割を持つコンテナ群が含まれています。
- 守る側(防御・監視):
- Splunk: ログを集約・分析するSIEM(今回の主役!)
- Suricata: ネットワークのパケットを監視する次世代IDS/IPS
- やられる側(攻撃対象):
- Nginx / Postgres: 一般的なWebサーバーとデータベース
- Juice Shop: 意図的に脆弱性が作り込まれた学習用のWebアプリ
- 高度な管理・演習ツール:
- Vault: パスワードなどを暗号化して管理するシークレット管理
- Keycloak: 認証基盤(SSO)
- Gophish: フィッシングメール演習ツール
これらが1つの仮想ネットワーク内で連携して動きます。
Docker Composeファイルの準備
任意のディレクトリ(例:sec-lab)を作成し、その中に以下の docker-compose.yml を作成します。
YAML
name: sec-lab
services:
# ==========================================
# 1. 監視対象・テスト環境(やられ側)
# ==========================================
target-frontend:
image: nginx:alpine
container_name: target-frontend
ports:
- "4300:80"
volumes:
- ./logs/nginx:/var/log/nginx
target-db:
image: postgres:15-alpine
container_name: target-db
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: StrongPassword123!
POSTGRES_DB: target_app_db
ports:
- "4301:5432"
juice-shop:
image: bkimminich/juice-shop:latest
container_name: juice-shop
ports:
- "4302:3000"
# ==========================================
# 2. セキュリティ管理・診断環境(守る・攻める側)
# ==========================================
metabase:
image: metabase/metabase:latest
container_name: metabase
ports:
- "4305:3000"
owasp-zap:
image: zaproxy/zap-stable:latest
container_name: owasp-zap
command: tail -f /dev/null
ports:
- "4306:8080"
volumes:
- ./zap_wrk:/zap/wrk
splunk:
image: splunk/splunk:latest
container_name: splunk
environment:
- SPLUNK_START_ARGS=--accept-license
- SPLUNK_GENERAL_TERMS=--accept-sgt-current-at-splunk-com
- SPLUNK_PASSWORD=StrongPassword123!
ports:
- "4307:8000"
volumes:
# ★ここがポイント!ホスト側のログをSplunkに読み取り専用で共有
- ./logs/nginx:/var/log/nginx_logs:ro
- ./logs/suricata:/var/log/suricata_logs:ro
# ==========================================
# 3. 高度なセキュリティ・コンポーネント
# ==========================================
gophish:
image: gophish/gophish:latest
container_name: gophish
ports:
- "4308:3333"
- "4309:80"
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
command: start-dev
ports:
- "4310:8080"
vault:
image: hashicorp/vault:latest
container_name: vault
environment:
- VAULT_DEV_ROOT_TOKEN_ID=myroot
cap_add:
- IPC_LOCK
ports:
- "4311:8200"
suricata:
image: jasonish/suricata:latest
container_name: suricata
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_NICE
network_mode: "host"
command: -i eth0
volumes:
# ★Suricataが検知したログをホスト側に書き出す
- ./logs/suricata:/var/log/suricata
💡 連携の仕組み(Volumes)
ここでのポイントは、Suricata(検知側)とSplunk(分析側)がどうやってログを受け渡しているかです。 SuricataコンテナがホストPCの ./logs/suricata フォルダにログ(eve.json)を書き出し、それをSplunkコンテナが /var/log/suricata_logs として読み込むという「バケツリレー」方式をとっています。
起動してみよう
ファイルが保存できたら、以下のコマンドで一斉に起動します!
docker compose up -d
少し待って docker ps で全てのコンテナが Up になっていれば準備完了です。
SplunkへのSuricataログ取り込み設定
それでは、本題のSplunkでのログ分析に入りましょう。
- ブラウザを開き、
http://localhost:4307にアクセスします。 - ユーザー名
admin、パスワードStrongPassword123!でログインします。
こんな感じの画面になるはずです。

ログの入力設定
初期状態のSplunkは「ログの入ったフォルダ」を知っているだけで、まだ中身を読み込んでいません。以下の手順で読み込みを指示します。
- 画面右上の [Settings (設定)] > [Data Inputs (データの入力)] をクリック。

- [Files & Directories (ファイルとディレクトリ)] の行にある 「+ Add new (新規追加)」 をクリック。

- File or Directory: に
/var/log/suricata_logs/eve.jsonと入力し、Nextへ。

- Source Type: 左側のリストから
Structured>_jsonを選択し、Nextへ。(SuricataのログはJSON形式のため)

- Index: デフォルトの
mainのままで Review に進み、Submit(保存)します。

これで、Suricataが監視しているネットワークのログがリアルタイムにSplunkへ流れ込むようになりました!

脅威ログを検索してグラフ化してみよう!
ログが取り込めたら、さっそく可視化(グラフ化)してみましょう。トップ画面の左上の「Splunk Enterprise」ロゴをクリックしてホームに戻り、**「Search & Reporting」**を開きます。
STEP 1: ログを検索する
検索窓に以下のSPL(Splunk専用の検索言語)を入力して、右側の虫眼鏡(検索)ボタンを押します。

index="main" sourcetype="_json"
index="main"
Splunkの中でデータが保存されている「本棚(データベース)」を指定しています。今回はデフォルト設定である main という本棚を探しに行け、という指示です。
sourcetype="_json"
その本棚の中から、さらに「JSON形式で書かれたログ」だけを引っ張り出してこい、という条件です。Suricataのログ(eve.json)がこの形式に該当します。
すると、Suricataが記録した通信の生データ(JSON)がズラッと表示されるはずです。

イベントタイプを円グラフにする
Suricataのログには「これはアラートだ」「これはただのHTTP通信だ」「これはDNSの名前解決だ」といった通信の種類(event_type)が記録されています。これを集計してグラフにしてみましょう。
検索窓に以下のコマンドを入力します。
index="main" sourcetype="_json" | stats count by event_type
|(パイプ)
Splunkで最も重要な記号です。左側で絞り込んだ検索結果(生データ)を、右側のコマンドに「はい、次よろしく!」とバトンタッチする役割を持ちます。
stats
統計(Statistics)を計算するためのコマンドです。
count by event_type
「通信の種類(event_type)ごとに(by)、何件あったかを数えろ(count)」という指示です。これにより、「http通信が100件、dns通信が50件、alert(攻撃)が3件…」といった集計表が作られ、円グラフ化できるようになります。
(※ 「| (パイプ)」を使って、検索結果を stats コマンドに渡し、event_type ごとにカウントしてね、という指示です)
検索を実行したら、検索窓のすぐ下にある 「視覚エフェクト」 タブをクリックします。 左上のグラフの種類を選ぶボタンから 「Pie (円グラフ)」 を選択してみてください。

通信の割合が美しい円グラフで表示されましたか?これがSIEMの基本的な使い方です!

こんどは攻撃をしてみよう
今度は攻撃をしてみましょう。下記の構成で行ってみます。
成功したルート:コンテナを「踏み台」にした攻撃(図の太い黒線 ②〜④)
裏道を通らせないために、「Dockerコンテナ(Nginx)の中から攻撃する」という作戦に変更したルートです。
- コンテナへ潜入(②): まず、
docker execコマンドを使って、仮想ネットワーク内にいるNginxコンテナに入り込みます。 - 外部へ向けて攻撃(③〜④): コンテナの中から、外部のインターネット(testmynids.org)に向けてマルウェアのテスト通信を発射します。
- 必ずメインゲートを通る: コンテナから外部のインターネットへ出るためには、必ずPCのメインゲート(
eth0)を通過しなければならないというネットワークのルールがあります。
検知とグラフ化の流れ(図の緑色とオレンジ色の線 ⑤〜⑥)
攻撃がメインゲートを通ったことで、ついに監視員と分析システムが活躍します。
- Suricataが発見(👁️): メインゲート(
eth0)を通過する怪しい通信を、Suricataがバッチリ捉えます。 - ログファイルに記録(⑤): Suricataは「攻撃を発見!」というアラートを、共有フォルダにある
eve.jsonというファイル(記録帳)に書き出します。 - Splunkで可視化(⑥): 統合ログ管理ツールのSplunkが、この
eve.jsonを常に監視して読み込み続けているため、最終的にあの美しいグラフとして画面に表示されました。

ルールの設定
DockerでSuricataを立ち上げた直後、実は肝心の**「どんな通信が危険なのか」というルール(ブラックリスト)を一つも持っていません。** まずはコンテナの中に最新のルールセットをダウンロードさせる必要があります。
docker exec suricata suricata-update
8/3/2026 -- 07:06:01 - <Info> -- Loading /etc/suricata/update.yaml
8/3/2026 -- 07:06:01 - <Info> -- Using data-directory /var/lib/suricata.
...(中略)...
8/3/2026 -- 07:06:04 - <Info> -- Loaded 64774 rules.
8/3/2026 -- 07:06:08 - <Info> -- Writing rules to /var/lib/suricata/rules/suricata.rules: total: 64774; enabled: 48919; added: 0; removed 0; modified: 0
8/3/2026 -- 07:06:09 - <Info> -- No changes detected, exiting.
このように Loaded XXXX rules. と表示されれば成功です。その後、docker restart suricata で再起動してルールを読み込ませます。
Dockerコンテナ再起動でデータが消滅する罠
よし、これで検知できるぞ!と、設定ファイル(docker-compose.yml)をいじって再度 docker compose up -d を実行したとします。
docker compose up -d
WARN[0000] Found orphan containers ([grafana sonarqube]) for this project.
[+] up 10/10
✔ Container target-frontend Running 0.0s
✔ Container keycloak Running 0.0s
✔ Container suricata Recreated 2.2s
...
ここで suricata Recreated と出た場合、大ピンチです! Dockerは設定を変えてコンテナを作り直すと、中身が初期化されます。つまり、先ほど罠1で苦労してダウンロードした6万件のルールが綺麗さっぱり消滅してしまったのです。 コンテナを作り直した際は、必ずもう一度 suricata-update を実行するのを忘れないようにしましょう。
同じPCからの攻撃は見えない?「チェックサムとルーティング」の壁
ルールも入れた。さあ、定番のテスト用マルウェア通信(testmynids.org)を実行してアラートを確認してみます。
【実行コマンドと実際の出力】
curl <URL>(カール)
- 解説: ターミナルからWebサイトにアクセスしたり、データを送信したりするコマンドです。ブラウザを使わずに通信を発生させたい時によく使います。
- オプション:
-s(サイレントモード): 通信の余計な進捗バーを非表示にします。-A "名前": アクセスする際の自分の名前(User-Agent)を偽装します。今回は"BlackSun"という悪意あるツール名に偽装して攻撃しました。
curl http://testmynids.org/uid/index.html
uid=0(root) gid=0(root) groups=0(root)
grep '"event_type":"alert"' ./logs/suricata/eve.json
攻撃のレスポンス(uid=0(root)...)は確実に返ってきているのに、Suricataのログをgrep(検索)しても何も出ません(無反応)。
このからくりは**「Linuxカーネルの過剰な最適化」**にあります。
- チェックサム・オフロード: PCは計算をサボるため、パケットの確認(チェックサム)をネットワークカードに後回しにします。Suricataは「壊れたパケットだ」と勘違いして無視してしまいます。(対策:
command: -i eth0 -k noneでチェックサム無視を設定) - ローカルルーティング: ホストPC(自分自身)から攻撃を打つと、Linuxは「外のドア(eth0の監視ポイント)」を通らずに、内部の裏道を通って通信を処理してしまうことがあります。
つまり、ホストPCから直接curlを打っても、ガードマン(Suricata)の目の前を通らないのです!
突破口は「コンテナ内部からの踏み台攻撃」!
これを解決するには、別のコンテナの内部に入り込み、そこから攻撃通信を発生させます。コンテナからの通信であれば、必ず仮想ルーターを通ってeth0から出ていくため、Suricataが確実に捕捉できます。
Nginxコンテナ(target-frontend)を踏み台にして、マルウェアテスト通信と、やられサイト(Juice Shop)への攻撃を実行してみましょう。
【実行コマンドと感動の出力】
# Nginxコンテナを踏み台にして外部のテストサイトへ通信
docker exec target-frontend curl -s http://testmynids.org/uid/index.html > /dev/null
##
####> /dev/null(リダイレクト)
##解説: コマンドの出力結果を「ブラックホール」に捨てて表示させないようにする、Linuxの便利な裏技で##す。画面をスッキリさせたい時に使います。
# Nginxコンテナを踏み台にしてJuice Shopへ攻撃(BlackSunという怪しいユーザーエージェントを使用)
docker exec target-frontend curl -s -A "BlackSun" http://juice-shop:3000/etc/passwd > /dev/null
# いざ、ログを確認!
grep '"event_type":"alert"' ./logs/suricata/eve.json
{"timestamp":"2026-03-08T07:07:47.555422+0000","flow_id":879198135428343,"in_iface":"eth0","event_type":"alert","src_ip":"18.64.122.67","src_port":80,"dest_ip":"192.168.65.3","dest_port":45658,"proto":"TCP","ip_v":4,"pkt_src":"wire/pcap","metadata":{"flowints":{"tcp.retransmission.count":2}},"alert":{"action":"allowed","gid":1,"signature_id":2100498,"rev":7,"signature":"GPL ATTACK_RESPONSE id check returned root","category":"Potentially Bad Traffic","severity":2,"metadata":{"confidence":["Medium"],"created_at":["2010_09_23"],"signature_severity":["Informational"],"updated_at":["2019_07_26"]}},"app_proto":"http","direction":"to_client","flow":{"pkts_toserver":6,"pkts_toclient":5,"bytes_toserver":496,"bytes_toclient":914,"start":"2026-03-08T07:07:47.532384+0000","src_ip":"192.168.65.3","dest_ip":"18.64.122.67","src_port":45658,"dest_port":80}}
出ました!!! "event_type":"alert" とともに、"signature":"GPL ATTACK_RESPONSE id check returned root" というアラート名がしっかりと記録されています。これがSuricataが脅威を検知した「生きた証」です!
最後の砦、Splunkのパーミッション(権限)の壁
生ログは出た!あとはSplunkの画面で見るだけ!……と思いきや、まだ出ないことがあります。 理由は単純、Suricataが作ったログファイルを、Splunk(別のユーザー)が読み取る権限がないからです。
【実行コマンドと実際の出力】
chmod -R 777 ./logs/suricata
chmod: changing permissions of './logs/suricata': Operation not permitted
chmod: changing permissions of './logs/suricata/eve.json': Operation not permitted
# sudoをつけて強制的に全開放する!
sudo chmod -R 777 ./logs/suricata
[sudo] password for david:
このように sudo を使ってログフォルダの権限をフルオープン(777)にしてあげます。
そして最後に、SplunkのGUI画面から [設定] > [データ入力] > [ファイルとディレクトリ] に進み、手動で /var/log/suricata_logs/eve.json を 「監視 (Monitor)」 として登録します(※これをやらないとSplunkは一生ファイルを読み込みません)。
完了!美しいグラフの生成へ
数々の罠を乗り越え、ついにすべてのからくりが繋がりました。 Splunkの「Search & Reporting」画面を開き、以下のコマンドを打ち込んでみましょう。
index="main" sourcetype="_json" event_type="alert" | timechart count by alert.category
event_type="alert"
パイプの左側に条件を追加し、Suricataが記録した全通信の中から「これは攻撃だ(alert)」と判定されたログだけを、あらかじめ抽出しています。
timechart
時間を横軸にしたグラフ(Time Chart)を作るための強力な専用コマンドです。
count by alert.category
「アラートのカテゴリ(alert.category)ごとに、時間経過に伴う件数の推移を数えろ(count)」という指示です。これにより、「10時15分にWeb攻撃が5件、10時20分に不正ログイン試行が2件…」といった時系列データが生成され、美しい折れ線グラフや棒グラフを描画できます。
下記のようにデータが取れていれば完成です

おわりに
今回はDocker Composeを使ったセキュリティラボの構築と、Splunk×Suricataの基本的な連携・グラフ化について解説しました。
自宅のPCだけでこれだけの環境が動かせるのは、Dockerの素晴らしいところですね。次は「OWASP ZAPを使ってやられサイト(Juice Shop)を攻撃し、そのアラートをSplunkで観察する」といった攻防演習にもチャレンジしてみてください!
