CloudMapperをDockerで動かして、AWSのネットワーク構成を可視化しよう。

こんにちは。

AWSいいですよね。
でも、AWSって一つ一つのサービスがしっかり役割分担されているので、いざ一つのシステムとしてネットワーク構成図がどうなっているのかをみようとすると頭がこんがらがっちゃうんです、私。

そんなとき[CloudMapper](https://github.com/duo-labs/cloudmapper)にお世話になりたくなります。READMEを読んでいただければですが、現在のAWSのネットワーク構成図を自動生成してくれるやつです。
ただ、何度かチャレンジしているのですが、いつも何かが足りなくてうまく動かないんです、私。最近は何をやるにもDockerでやっちゃっているのでローカル環境もあんまり汚したくなくて...

CloudMapper公式や他の方々もDockerで動くようにソースコードを公開していたりするんですが、なんでかうまく行かなかったりしたので、今回は自分でDockerfileから作ってみようという記事です。

# 前提

- AWSアカウントがあること
- `SecurityAudit`と`ViewOnlyAccess`の権限をもったIAM（以下、`IAM_A`）があること（よくわからなければ最悪`AdministratorAccess`の権限でもひとまずOK）
- `IAM_A`のアクセスキーIDとシークレットアクセスキーを払い出していること
- DockerおよびDocker Composeが利用可能であること

# CloudMapperが動くDockerイメージを作る
CloudMapperはPythonで動いているので[PythonのDockerイメージ](https://hub.docker.com/_/python)をベースに必要なパッケージをインストールしたDockerイメージを作るためのDockerfileを作成します。

```dockerfile:Dockerfile
FROM python:latest

RUN git clone https://github.com/duo-labs/cloudmapper.git && \
    pip install \
      jinja2 \
      netaddr \
      parliament \
      policyuniverse \
      pyjq \ 
      pyyaml && \
    apt update -qq && \
    apt install -q -y vim
```
最初に`git`で`CloudMapper`のソースコードをダウンロードしています。

PythonのDockerイメージを使うと、READMEで必要と言われているパッケージはほとんど入っているようで、実際にCloudMapperを実行しようとしたときに追加で必要になったパッケージを`pip`コマンドで追加してあげています。

`vim`はコンテナ内でファイルを編集したい時などに使いたいのでインストール。

次にDocker Compose用のファイルを作ります。

```yaml:docker-compose.yml
version: "3"

services:
  cm:
    build: .
    working_dir: /cloudmapper
    ports:
      - 8000:8000
    environment:
      - AWS_ACCESS_KEY_ID=[IAM_AのアクセスキーID]
      - AWS_SECRET_ACCESS_KEY=[IAM_Aのシークレットアクセスキー]
    volumes:
      - ./config.json:/cloudmapper/config.json
```
### working_dir
`cloudmapper`を`git clone`しているのでDockerイメージの中には`/cloudmapper`というディレクトリができています。そこをワーキングディレクトリにします。

### ports
CloudMapperのViewを表示するポートです。`8000`がデフォルトなのでそれに合わせて。

### environment
ここで`IAM_A`のアクセスキーIDとシークレットアクセスキーを環境変数に定義します。

### volumes
この後書きますが、CloudMapperが情報を取得しにいくAWS AccountのAccount IDを設定する`config.json`が必要になるのでローカルのファイルをマウントするようにしています。コンテナの中で書き換えてもいいのですが、毎回書くのは面倒なのでローカルのファイルと同期しておきます。

`config.json`も作っておきましょう。CloudMapperのソースコードにある`config.json.demo`を参考に以下のように書きます。

```json:config.json
{ 
  "accounts": [
    {
      "id": "[AWSのAccount ID]",
      "name": "[識別名]",
      "default": true
    }
  ],
  "cidrs": {}
}
```
AWSのAccount IDはコンソールの「マイアカウント」から確認できます。（12桁の数字。もしどこ見ればいいの？って方はこの記事の最後のAppendix2のやり方とかみてみてください。）

ここまで準備ができたらDockerイメージをビルドします。

```
$ docker-compose build
```

# CloudMapperをDockerコンテナで動かす
ビルドが完了したらコンテナの中でCloudMapperを動かしてみましょう！

まずコンテナを起動させて中に入ります。

```
$ docker-compose run --service-ports cm bash
```
`--service-ports`オプションは`docker-compose run`コマンドを実行するときに`docker-compose.yml`の中の`ports`を適用させるためのオプションです。これがないとローカル環境から`http://localhost`経由でCloudMapperのWebページにアクセスできないので注意です。

コンテナの中に入ったらAWSからデータを収集します。

```
# python cloudmapper.py collect
```
このコマンドでAWSアカウントのネットワーク構成情報をめっちゃ取得してくれます。
全てのリージョンの全ての情報を取ってきているみたいで結構時間がかかります。
毎回これをやるのはきついんで何回もコンテナを消して立ち上げ直してとやる場合は、`/cloudmapper/account-data/`をマウントしておけばこの工程を省けます。（このディレクトリにデータを生成している工程なので）

情報を取得できたらレポートを作成します。

```
# python cloudmapper.py prepare
# python cloudmapper.py report --accounts [IAM_Aの識別子]
```

最後に収集してレポートを作ったサイトを公開します。

```
# python cloudmapper.py webserver --public
```
さぁ！これで`http://localhost:8000`にアクセスしてみましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/d7f23c8a-bbe0-cffd-dd92-5a5b12e9d2b3.png)
こちらは公式のイメージですが、こんな感じのページが表示されるはずです！

# まとめ
CloudMapperをDockerで動かすことができました！
READMEを見ると色々とパッケージのインストールなどが大変そうだな（実際つまづいた）という感じだったのですが、Dockerですっきりと動かせました〜。

# Appendix1: CloudMapperのヘルプ
READMEだけだとあんまりわからなかったのですが、`cloudmapper.py`はhelp機能がついてました。

```
# python cloudmapper.py -h

CloudMapper 2.8.2
usage: cloudmapper.py [access_check|amis|api_endpoints|audit|collect|configure|find_admins|find_unused|iam_report|prepare|public|report|sg_ips|stats|weboftrust|webserver] [...]
  access_check: [proof-of-concept] Check who has access to a resource
  amis: Cross-reference EC2 instances with AMI information
  api_endpoints: [Deprecated] Map API Gateway end-points
  audit: Identify potential issues such as public S3 buckets
  collect: Run AWS API calls to collect data from the account
  configure: Add and remove items from the config file
  find_admins: Find privileged users and roles in accounts
  find_unused: Find unused resources in accounts
  iam_report: Create IAM report
  prepare: Generate network connection information file
  public: Find publicly exposed services and their ports
  report: Create report
  sg_ips: Find all IPs are that are given trusted access via Security Groups
  stats: Print counts of resources for accounts
  weboftrust: Create Web Of Trust diagram for accounts
  webserver: Run a webserver to display network or web of trust map
```

```
# python cloudmapper.py collect -h

usage: cloudmapper.py [-h] [--config CONFIG] [--account ACCOUNT_NAME]
                      [--profile PROFILE_NAME] [--clean]
                      [--max-attempts MAX_ATTEMPTS]

optional arguments:
  -h, --help            show this help message and exit
  --config CONFIG       Config file name
  --account ACCOUNT_NAME
                        Account to collect from
  --profile PROFILE_NAME
                        AWS profile name
  --clean               Remove any existing data for the account before
                        gathering
  --max-attempts MAX_ATTEMPTS
                        Override Botocore config max_attempts (default 4)
```
チェックしてみると使い方が良くわかるかもしれないです！（`webserver`の`--public`オプションはこれで見つけられた...）

# Appendix2: AWS Account IDを調べる
AWS Account IDはAWS CLIで取得することもできます。
この記事で作成したコンテナではAWS CLIを使えないので`Dockerfile`を少し更新します。

```diff:Dockerfile
  FROM python:latest

  RUN git clone https://github.com/duo-labs/cloudmapper.git && \
      pip install \
+       awscli \
        jinja2 \
        netaddr \
        parliament \
        policyuniverse \
        pyjq \ 
        pyyaml && \
      apt update -qq && \
      apt install -q -y vim
```
ビルドしてDockerコンテナの中で以下のコマンドを実行します。

```
# aws sts get-caller-identity
{
  "UserId": "XXXXXXXXXXXXXXXXXXXX",
  "Account": "123456789012",
  "Arn": "arn:aws:iam::123456789012:user/xxxxxxxxxx"
}
```
ここの`Account`に表示されている数字列がAccount IDです。
参考：[【小ネタ】AWS CLIでAWS Account IDが取れるようになりました！ | Developers.IO](https://dev.classmethod.jp/articles/get-aws-account-id-with-get-caller-identity/)