CloudFormationテンプレートのセキュリティ的な問題を開発プロセスの初期段階から警告しよう

CloudFormation を使用することで、AWS のリソースを素早くプロビジョニングできます。また、yaml 形式のテンプレートファイルで記述することにより、インフラ開発者はどのリソースが作成されるのかを宣言的に管理できます。
開発者は AWS リソースをすばやく簡単に作成できますが、安全でないリソースもすばやく作成できてしまいます。「安全でない」とは、TCP ポートを世界中に公開したり、特定の IAM ユーザーにフル権限を与えてしまうことでセキュリティ的に問題があるリソースを作り出してしまうことを意味しています。

以前、CircleCI MeetUp で [CloudFormation を静的構文解析する話](https://speakerdeck.com/gawa/aws-testing-techniques-and-policy-as-a-code?slide=17)をしました。
CloudFormation を静的構文解析することにより、セキュリティ的に問題があったり、命名規則に従っていないリソースに対して警告できます。このようにポリシー違反を検知する仕組みを作ることで、AWS リソースを構築する前にセキュリティ的な問題を対応できるようになります。

<a href="https://speakerdeck.com/gawa/aws-testing-techniques-and-policy-as-a-code"><img src="https://speakerd.s3.amazonaws.com/presentations/442ec2e5415b47a8b11bfdeed1270bda/preview_slide_0.jpg?13755233"></a>

ただしルールを全て自作したり、セキュリティ的に問題がある箇所を洗い出す作業は非常に面倒です。

今回はそんなケースに有効な [cfn-nag](https://github.com/stelligent/cfn_nag) というツールを紹介します。

cfn-nag はセキュリティ的に問題がある CloudFormation のテンプレートを警告できるツールです。cfn-Python-lint と並んで CI のプロセスに組み込むことができます。

<img src="https://github.com/stelligent/cfn_nag/raw/master/logo.png?raw=true" width=30%>

# cfn-nag のインストール

cfn-nag は Ruby 製のツールです。Gem としてインストールしましょう。

```bash
gem install cfn-nag
```

# cfn-nag の使用方法

以下のようなテンプレートを例に取り上げます。
外部から 80 番ポートで TCP 通信することを許可するセキュリティグループを作成しようとしています。

```template.yaml
  SecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VpcId
      GroupDescription: security group
      SecurityGroupIngress:
        - IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: "All"
```

このテンプレートに対して cfn-nag を実行してみましょう。

```bash
$ cfn_nag template.yaml
------------------------------------------------------------
template.yaml
------------------------------------------------------------------------------------------------------------------------
| WARN W9
|
| Resources: ["SecurityGroup"]
| Line Numbers: [9]
|
| Security Groups found with ingress cidr that is not /32
------------------------------------------------------------
| WARN W2
|
| Resources: ["SecurityGroup"]
| Line Numbers: [9]
|
| Security Groups found with cidr open to world on ingress.  This should never be true on instance.  Permissible on ELB
------------------------------------------------------------
| FAIL F1000
|
| Resources: ["SecurityGroup"]
| Line Numbers: [9]
|
| Missing egress rule means all traffic is allowed outbound.  Make this explicit if it is desired configuration

Failures count: 1
Warnings count: 2
```

２つの Warnings, 1 つの Failure, 合計 3 つの警告がでました。

- **Warnings**: ingress ルールで/32 からのアクセスを許可していること
- **Warnings**: ingress ルールが全世界に向けて全開放していること
- **Failures**: egress ルールがついていないこと

# cfn-nag のルールを許容する

cfn-nag の警告を確認すると確かにセキュリティ的にはあまりよくないルールになっていますね。

ただし、実際にこのようなセキュリティグループを使用することはよくあります。たとえばチームとして egress ルールは全許可にしていても良いなどのルールを決めている。あるいはパブリックユーザ向けの Web サービスの場合、特定のポート番号（例えば 443）で ingress ルールを全世界に向けて全開放することは一般的です。

このような場合は、許容するルールを `Metadata` に記載しておきましょう。

```template.yaml
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !Ref VpcId
      GroupDescription: security group
      SecurityGroupIngress:
        - IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: "All"
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W9
            reason: "このセキュリティグループはEC2には付与しない。ELBに付与して使用する"
          - id: W2
            reason: "このセキュリティグループはEC2には付与しない。ELBに付与して使用する"
          - id: F1000
            reason: "egressルールには制限を課しません"
```

このようにしておくことで警告を無視できます。

```bash
$ cfn_nag template.yaml
------------------------------------------------------------
template.yaml
------------------------------------------------------------
Failures count: 0
Warnings count: 0
```


# 開発環境に組み込む（VSCode の場合）

VSCode には cfn-nag の拡張機能（プラグイン）が提供されています。
[こちら](https://marketplace.visualstudio.com/items?itemName=eastman.vscode-cfn-nag)からインストールしておきましょう。

`Format On Save` の項目にチェックを入れておくと、ファイルを保存した時に自動的に cfn-nag が実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/4a419e37-31fd-b65b-d60f-6dca650487ea.png)

このようにすることで早い段階でセキュリティ的に問題があるテンプレートを見つけることができるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/509490ff-61ac-8f18-2968-ee569c00ea74.png)


# CI に組み込む（CircleCI の場合）

さらにCIのプロセスにも組み込んでおくと安心です。CircleCI の場合は以下のような感じでしょうか。
実際のユースケースに合わせて `bundler` で管理することもあるかと思います。

```.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.6.0-node-browsers-legacy
    working_directory: ~/repo
    steps:
      - checkout
      - run: gem install cfn-nag
      - run: cfn_nag template.yaml
```