NginxコンテナにGremlinでシャットダウン攻撃を仕掛けてみた

2020.03.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。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 IDSecret Keyが必要になります。

確認方法はこちらの「Team IDとSecret Keyの確認」をご覧ください。

Dockerのオプションについては以下のページを参照しています。

Step 1.0 – Install Docker For Mac

このステップではDockerのインストールを行います。インストール済みの場合はスキップしてください。

インストールの方法は以下です。

Install Docker Desktop on Mac

インストールできているか、確認します。

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_IDGREMLIN_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/gremlinvar/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-containerdocker-nginxを対象にシャットダウン攻撃を実行

ブラウザをリロードしてアクセスを確認してみます。

予想通りページがダウンしましたね。

ちなみにGremlinをSlackに連携していれば通知が飛んできます。

おわりに

今回はGremlinで、ローカルのNginxコンテナにシャットダウン攻撃を実施し、ダウンを確認できました。

ECS等でスケールするコンテナに対してもやってみたくなりますね。

簡単な構成でしたが、壊してみることは理解のために重要だと改めて感じました。

以上、何かのお役に立てれば幸いです。