NginxコンテナにGremlinでシャットダウン攻撃を仕掛けてみた
こんにちは。AWS事業本部のKyoです。
カオスエンジニアリングプラットフォームのGremlinを使って、Nginxコンテナにシャットダウン攻撃を仕掛けてみました。 また、今回はDockerの学習も兼ねてDockerのオプションの解説もしてみました。
シャットダウン攻撃は無料版のGremlinでも利用可能ですので、もしよろしければお試しください。
概要
参考にしたのは以下のチュートリアルです(名前の通り、Mac向けです)。これを元に一部追加・改変を行っています。
このチュートリアルは、4つのStepから構成されています。
- Step 1.0 – Install Docker For Mac
- Step 2.0 – Installing Gremlin
- Step 3.0 – Create an NGINX container to attack
- Step 4.0 - Run A Gremlin Shutdown Attack
また、GremliアプリへのサインアップとTeam IDとSecret Keyが必要になります。
確認方法はこちらの「Team IDとSecret Keyの確認」をご覧ください。
Dockerのオプションについては以下のページを参照しています。
Step 1.0 – Install Docker For Mac
このステップではDockerのインストールを行います。インストール済みの場合はスキップしてください。
インストールの方法は以下です。
インストールできているか、確認します。
docker --version
私の環境では以下が表示されました。
Docker version 19.03.5, build 633a0ea
Step 2.0 – Installing Gremlin
このステップではGremlinコンテナを起動し、利用できる攻撃の種類を確認します。
まず、作業用のディレクトリを作成します。
mkdir ~/gremlin-nginx cd ~/gremlin-nginx mkdir gremlin_log mkdir gremlin_lib
先ほど確認したTeam IDとSecret KeyをGREMLIN_TEAM_ID
とGREMLIN_TEAM_SECRET
としてエクスポートします。
export GREMLIN_TEAM_ID=【確認したTeam ID】 export GREMLIN_TEAM_SECRET=【確認したSecret Key】
Gremlinコンテナを起動します。 初めて利用する場合はDocker HubからイメージがPullされます。
docker run \ -d \ --net=host \ --pid=host \ --cap-add=NET_ADMIN \ --cap-add=SYS_BOOT \ --cap-add=SYS_TIME \ --cap-add=KILL \ -e GREMLIN_TEAM_ID="${GREMLIN_TEAM_ID}" \ -e GREMLIN_TEAM_SECRET="${GREMLIN_TEAM_SECRET}" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v 【作成したgremlin_logのフルパス】:/var/log/gremlin \ -v 【作成したgremlin_libのフルパス】::/var/lib/gremlin \ gremlin/gremlin daemon
オプションが大量についてますね。それぞれのオプションの意味は以下です。
オプション | 意味 |
---|---|
-d, --detach | コンテナをバックグラウンドで実行し、コンテナ ID を表示 |
--net="bridge" | コンテナをネットワークに接続 |
--pid="" | 使用する PID 名前空間 |
--cap-add=[] | Linux ケーパビリティの追加 |
-e, --env=[] | 環境変数を指定 |
-v, --volume=[ホスト側ソース:]コンテナ側送信先[:<オプション>] | ボリュームをバインドマウント。カンマ区切りで指定。オプション は [rw|ro], [z|Z], [[r]shared|[r]slave|[r]private], [nocopy] 'ホスト側ソース' は絶対パスまたは名前の値 |
参考: バインドマウント
ざっくり言うと以下のことをやっています。
- コンテナをバックグラウンド実行
- ホスト(Mac)のネットワークインターフェースを利用
- ホスト(Mac)のプロセスIDをコンテナ内から認識可能に
NET_ADMIN
,SYS_BOOT
,SYS_TIME
のケーパビリティを付与- 環境変数にTeam IDとSecret Keyをセット
- 先ほど作成した(Macの)
gremlin_log
,gremlin_log
ディレクトリをそれぞれGremlinコンテナのvar/log/gremlin
とvar/log/gremlin
にバインドマウント - gremlin daemonを実行
次にコンテナが起動しているの確認してみます。
docker ps
以下のような結果が得られました。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ede815e12c7a gremlin/gremlin "/entrypoint.sh daem…" 7 seconds ago Up 6 seconds sad_pare
Gremlinコンテナに入ってみます。
docker exec -it 【gremlinコンテナのID】 /bin/bash
Gremlinコンテナで何ができるのかをヘルプから確認してみます。
gremlin help attack-container
Type "gremlin help attack-container TYPE" for more details: blackhole # An attack which drops all matching network traffic cpu # An attack which consumes CPU resources io # An attack which consumes IO resources latency # An attack which adds latency to all matching network traffic memory # An attack which consumes memory packet_loss # An attack which introduces packet loss to all matching network traffic shutdown # An attack which forces the target to shutdown dns # An attack which blocks access to DNS servers time_travel # An attack which changes the system time. disk # An attack which consumes disk resources process_killer # An attack which kills the specified process
exit
でGremlinコンテナから出ます。このコンテナは確認用なので、停止しておきます。
docker stop 【GremlinコンテナのID】
Step 3.0 – Create an NGINX container to attack
このステップでは攻撃対象となるNginxコンテナを起動します。
事前準備として、作業ディレクトリを作成して移動します(Step 2.0で作ったディレクトリの下に作成します)。
cd ~/gremlin-nginx mkdir -p ~/docker-nginx/html cd ~/docker-nginx/html
表示用のHTMLファイルを作成します。
vim index.html
HTMLの中身として以下を貼り付けます。
<html> <head> <title>Docker nginx tutorial</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> </head> <body> <div class="container"> <h1>Hello it is your container speaking</h1> <p>This nginx page was created by your Docker container.</p> <p>Now it's time to create a Gremlin attack.</p> </div> </body> </html>
以下のコマンドでNginxコンテナを起動します。
docker run \ -l service=nginx \ --name docker-nginx \ -p 80:80 \ -d \ -v 【作成したhtmlディレクトリのフルパス】:/usr/share/nginx/html \ nginx
オプション | 意味 |
---|---|
-l, --label=[] | コンテナにメタデータを指定 (例: --label=com.example.key=value) |
--name="" | コンテナに名前を割り当て |
-p, --publish=[] | コンテナのポートをホスト側に公開 |
-d, --detach | コンテナをバックグラウンドで実行し、コンテナ ID を表示 |
-v, --volume=[ホスト側ソース:]コンテナ側送信先[:<オプション>] | ボリュームをバインドマウント。カンマ区切りで指定。オプション は [rw|ro], [z|Z], [[r]shared|[r]slave|[r]private], [nocopy] 'ホスト側ソース' は絶対パスまたは名前の値 |
ざっくり言うと以下を実行しています。
- メタデータとして、
service=nginx
を付与 - コンテナの名前として
docker-nginx
を付与 - コンテナのポート
80
をホスト(Mac)のポート80
にバインド - コンテナをバックグラウンド実行
- 先ほど作成した(Macの)
html
ディレクトリをNginxコンテナの/usr/share/nginx/html
にバインドマウント - Nginxを起動
ブラウザからlocalhost
でアクセスすると以下のページが見えると思います。
Step 4.0 - Run A Gremlin Shutdown Attack
本ステップでは、GremlinコンテナでNginxコンテナをシャットダウンします。これによって、先ほどブラウザで表示したページが表示されなくなることが予想されます。
お待ちかねの攻撃タイムです。
Gremlinコンテナを使って、シャットダウン攻撃を実施します。
sudo docker run -i \ --cap-add=NET_ADMIN \ -e GREMLIN_TEAM_ID="${GREMLIN_TEAM_ID}" \ -e GREMLIN_TEAM_SECRET="${GREMLIN_TEAM_SECRET}" \ -v /var/run/docker.sock:/var/run/docker.sock \ gremlin/gremlin attack-container docker-nginx shutdown
オプション | 意味 |
---|---|
--cap-add=[] | Linux ケーパビリティの追加 |
-e, --env=[] | 環境変数を指定 |
-v, --volume=[ホスト側ソース:]コンテナ側送信先[:<オプション>] | ボリュームをバインドマウント。カンマ区切りで指定。オプション は [rw|ro], [z|Z], [[r]shared|[r]slave|[r]private], [nocopy] 'ホスト側ソース' は絶対パスまたは名前の値 |
ざっくり言うと以下を実行しています。
NET_ADMIN
のケーパビリティを付与- 環境変数にTeam IDとSecret Keyをセット
- ホスト(Mac)のDockerソケットパスとGremlinコンテナのDockerソケットパスをバインドマウント
- Gremlinの
attack-container
でdocker-nginx
を対象にシャットダウン攻撃を実行
ブラウザをリロードしてアクセスを確認してみます。
予想通りページがダウンしましたね。
ちなみにGremlinをSlackに連携していれば通知が飛んできます。
おわりに
今回はGremlinで、ローカルのNginxコンテナにシャットダウン攻撃を実施し、ダウンを確認できました。
ECS等でスケールするコンテナに対してもやってみたくなりますね。
簡単な構成でしたが、壊してみることは理解のために重要だと改めて感じました。
以上、何かのお役に立てれば幸いです。