AWS Configを用いて、予め設定したコンプライアンスに非準拠となった場合、SSM Automationを用いて自動修復する

パブリッククラウドは、操作が簡単なゆえに、想定外の設定をされることを防ぐ（直す）仕組みが必要になってきます。

今回は、**セキュリティグループの誤った設定から守る**というセキュリティ対策の一環として、**AWS Configを用いて、予め設定したコンプライアンス(※)に非準拠となった場合、SSM Automationを用いて自動修復する**という対策についてご説明します。

# はじめに
万が一、ネットワーク的にフルオープンなルールを設定してしまうと。。
全世界に向けて開放されたネットワーク通信を設定したら、AWSはパブリックなネットワークですので、攻撃者のポートスキャンに常に晒されているということになってしまいます。

そこで、今回は**AWS Config**と**SSM Automation**を利用して、「ポート番号22（SSH）、80（HTTP）、443（HTTPS）、3389（UDP）が全開放になっているセキュリティグループのインバウンドルールは削除する」という設定を行ってみます。

流れとしては、①AWS Config のルールで違反を検知して、②SSM Automationに連携し、③違反しているルールを自動修復する、といった設定を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/f2cd6937-5e7f-9e8c-8975-5b23e645dd53.png)

設定した手順は以下の通りです。
[1.ロールの作成](#1.ロールの作成)
[2.SSM Automationの作成](#2.SSMAutomationの作成)
[3.Configの設定](#3.Configの設定)

# 1.ロールの作成
まずは、IAM Roleの作成を行います。指定されたセキュリティグループのインバウンドルールおよびアウトバウンドルールを削除する権限を与えるために、ポリシーを作成してアタッチします。
以下の設定で、SSMを実行するロールを作成します。
| 項目 | 値 |
|:-:|:-:|
| ユースケースの選択 | Systems Manager |
| Attachアクセス権限ポリシー  | 新規作成（以下コード参照） |
| タグ | なし |

ポリシーのコードは以下のようになります。
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:RevokeSecurityGroupIngress",
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        }
    ]
}
```
```RevokeSecurityGroupIngress```を使用することで、インバウンドルールを変更または削除することができます。後ほど、このロールのARNを使用します。
# 2.SSMAutomationの作成
続いて、SSM Automationの作成です。
SSM（AWS Systems Manager）とは、AWSの運用管理をスケーラブルかつコスト効率よく行うサービス群のことを指します。そのうちのひとつに、AWS環境全体に対する自動化処理の実行を行うAutomationというコンポーネントがあります。

今回は、このサービスを利用して、対象のポート（22,80,443,3389）のルールがフルオープンになっていたら自動修復するというルールを作成します。

まずは、AWS Systems Managerのドキュメントのページより、[Create document」を選択します。続いて、以下の画面より、「エディタ」をクリックし、以下のコードを添付して「オートメーションを作成する」をクリックします。

AWS側で既に準備されているものもたくさんありますが、今回は指定した4つのポートのみを対象にしたいので、自分でカスタマイズしました。
```json
{
    "schemaVersion": "0.3",
    "description": "Disable SSH and RDP ports opened to IP address specified, or to all addresses if no address 
   is specified. Similar to the [RevokeSecurityGroupIngress
    ](https: //docs.aws.amazon.com/AWSEC2/latest/APIReference
   /API_RevokeSecurityGroupIngress.html) API, the security group must have existing rules specifically on the SSH 
   and RDP ports in order for ingress to be disabled.","assumeRole": "{{ AutomationAssumeRole }}",
    "parameters": {
        "AutomationAssumeRole": {
            "default": "",
            "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your 
   behalf.","type": "String"
        },
        "GroupId": {
            "description": "(Required) Security Group ID",
            "type": "String",
            "allowedPattern": "^([s][g]\\-)([0-9a-f]){1,}$"
        },
        "IpAddressToBlock": {
            "default": "",
            "description": "(Optional) Additional Ipv4 or Ipv6 address to block access from (ex:1.2.3.4/32)",
            "type": "String",
            "allowedPattern": "(^$)|^((25[0-5]|(2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|(^
   ((?: [
                0-9A-Fa-f
            ]{
                1,
                4
            }(?: : [
                0-9A-Fa-f
            ]{
                1,
                4
            })*)?): :((?: [
                0-9A-Fa-f
            ]{
                1,
                4
            }(?: : [
                0-9A-Fa-f
            ]{
                1,
                4
            })*)?))|(^(?: [
                0-9a-fA-F
            ]
   {
                1,
                4
            }:){
                7
            }[
                0-9a-fA-F
            ]{
                1,
                4
            }))\\/(25[
                0-5
            ]|2[
                0-4
            ]\\d|[
                0-1
            ]?\\d?\\d)$"
        }
    },
    "mainSteps": [
        {
            "inputs": {
                "Choices": [
                    {
                        "And": [
                            {
                                "Not": {
                                    "StringEquals": "",
                                    "Variable": "{{IpAddressToBlock}}"
                                }
                            },
                            {
                                "Not": {
                                    "Variable": "{{ IpAddressToBlock }}",
                                    "Contains": ":"
                                }
                            }
                        ],
                        "NextStep": "DisableSSHFromCustomIpV4"
                    }
                ],
                "Default": "DisableSSHFromIpV4"
            },
            "name": "CustomIpCheck",
            "action": "aws:branch"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 22,
                        "ToPort": 22,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "0.0.0.0/0"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableSSHFromIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 80,
                        "ToPort": 80,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "0.0.0.0/0"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableHTTPFromIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 443,
                        "ToPort": 443,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "0.0.0.0/0"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableHTTPSFromIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 3389,
                        "ToPort": 3389,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "0.0.0.0/0"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableRDPFromIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 22,
                        "ToPort": 22,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "{{ IpAddressToBlock }}"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableSSHFromCustomIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 80,
                        "ToPort": 80,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "{{ IpAddressToBlock }}"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableHTTPFromCustomIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 443,
                        "ToPort": 443,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "{{ IpAddressToBlock }}"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableHTTPSFromCustomIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue"
        },
        {
            "inputs": {
                "Service": "ec2",
                "Api": "RevokeSecurityGroupIngress",
                "IpPermissions": [
                    {
                        "FromPort": 3389,
                        "ToPort": 3389,
                        "IpProtocol": "tcp",
                        "IpRanges": [
                            {
                                "CidrIp": "{{ IpAddressToBlock }}"
                            }
                        ]
                    }
                ],
                "GroupId": "{{GroupId}}"
            },
            "name": "DisableRDPFromCustomIpV4",
            "action": "aws:executeAwsApi",
            "onFailure": "Continue",
            "isEnd": true
        }
    ]
}
```
なお、作成したオートメーションは、「自己所有」の中で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/89c90b43-b7a1-5654-bf75-4de1fd057e02.png)
これで、自動修復アクションを実行するSSM Automationが完成です。
# 3.Configの設定
AWS Configでは、設定したポリシーに準拠しているかどうか、定期的なチェックを実行できます。では、設定してきましょう。

AWS Configより、「ルールを追加」を選択します。今回は、予め用意されている、**vpc-default-security-group-closed**というルールを使用します。これは、セキュリティグループのインバウンドルールとアウトバウンドルールのルールを確認し、許可されていない場合はNOT_APPLICABLEを返します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/b7c5139e-fabf-7560-c48b-ab51965d176e.png)

続いて、Step 2のルールの設定にて、パラメータの設定を行います。今回はauthorizedTcpPortsとauthorizedUdpPortsに、1-21,23-79,81-442,444-3388,3390-65535と指定します。これは、TCP・UDPによる通信で、0.0.0.0/0のフルオープンであることを許可するポート番号を指定しています。

つまり、**指定した以外の22,80,443,3389ポートは、0.0.0.0/0だったらダメ**というルールを設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/b7cf3cdd-860e-1cad-6613-fceafc291728.png)
この設定を実施し、ルールを追加します。

続いて、作成したルールを選択し、「アクション」から「修復の管理」を選択します。ここで、修復アクションの設定を行えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/5eebae34-8434-c6b6-28b8-9c9c4d09b52c.png)
今回は、以下の設定で修復方法を設定します。
| 項目 | 値 |
|:-:|:-:|
| 修復方法 | 自動修復 |
| 再試行まで | 1 |
| 秒 | 20 |
| 修復アクションを選択 | 先ほど作成したSSM Automationを選択 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/19a02e96-6872-a95d-0b34-1fb180b74188.png)

これで、設定は完了です。最後に、正しく機能するかチェックしてみましょう。

# セキュリティグループを作成して確かめてみる
試しに、以下のような2つフルオープンになっているインバウンドルールが設定されているセキュリティグループを作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/1a59ad46-c1db-7b95-1e52-d7bda79c9198.png)
1分弱待ったところで、AWS Configで作成したルールを見にいってみると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/3944105a-5753-3aff-bcf9-aa0b3ab0230b.png)

修復アクションが実行されたことが確認できますね。「注釈」にてアクションが実行された理由も書かれていることが分かります！最後に、先ほど作成したセキュリティグループを見ると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3281098/6d49ba28-e67d-e1fd-716c-f9f4dacb5c6b.png)
フルオープンになっていたルールは削除されています。

# さいごに
AWS Configを用いてセキュリティグループのコンプライアンス対策を実施することで、簡単に自動修復もできちゃいます。あらかじめ準備されている修復アクションも豊富なうえ、コードを少し書き換えることで独自のルールを簡単に加えられることも魅力的です。