入門 AWS CloudFormation #2（EC2のセキュリティグループ編）

インフラやクラウドの専門というわけではないのですがそろそろIaC周りもちゃんと勉強しておきたい・・・という感じなのでAWSのCloudFormationについて入門しつつ復習として記事にまとめておきます。

※とりあえず最初ということでEC2を絡めたごく基本的なところの記事を書いていきます。将来もしかしたら3記事目以降を執筆して追加のEC2関係や他の様々なサービスや機能などにも触れていくかもしれません。

# 注意事項と記事までの振り返り

本記事の処理を動かすとEC2関係などで色々と追加になったり起動したりします。その辺はリソースの停止や削除などをしないとお金がかかったりしてくる可能性があるためご注意ください。

また、本記事は2記事目となります。前回までの内容を踏襲していく形となっているのでご注意ください。

1記事目:

https://qiita.com/simonritchie/items/330391e741f394897550

前回までに設定した主な内容:

- `cloudformation-test-1`と`cloudformation-test-2`という名前でのEC2のキーペアを追加しました。
- 以下のような形のYAMLテンプレートで`my-first-ec2-instance`という名前でスタックが作成済みとなります（ただし最後に変更セット失敗時の挙動を試したので最終的なステータスは`UPDATE_ROLLBACK_COMPLETE`となっています。変更セットなどによる更新は効くのでそのまま使っていきます）。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7f06a264-ad7c-876d-6bc5-4917c4e1006b.png)

また、前回の記事中で追加したインスタンスリソースを（コストなどの面を加味して）停止状態などにしていた場合には再開前に起動しておきます（起動していないとCloudFormationの操作でエラーになったりします）。

停止済みになっている場合にはEC2のインスタンスのページに遷移し、runningのフィルターを外して表示されるインスタンスを選択しインスタンスの状態 → インスタンスを開始のメニューを選択して起動しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/aab8e3a1-d9c0-1e3c-3d54-e24587449bef.png)

# 触れること

- EC2インスタンスリソースでのCloudFormation上でのセキュリティグループの設定
- セキュリティグループの一部説明
- EC2インスタンス以外のリソースとなるセキュリティグループのリソース追加設定
- 追加したセキュリティグループのEC2インスタンスリソースへの組み込み関数を使用した設定

# 既存のセキュリティグループのEC2インスタンスへの設定方法

既存のセキュリティグループをEC2インスタンスのリソースへと追加していきます。

セキュリティグループなど自体に関しては引用程度に留めてこの記事では触れません。必要に応じて以下の記事などをご確認ください。

> 1.セキュリティグループとは？
> 
> ・Amazon EC2 インスタンスに適用可能なAWS標準のファイアウォール機能であり、EC2インスタンスへのアクセスを"許可"したり、トラフィックを"制御"することができます。

https://qiita.com/leomaro7/items/42967c080814dfc82139

まずは既存の存在するセキュリティグループを確認します。EC2関係のページに遷移し、左のメニューのネットワーク＆セキュリティの部分からセキュリティグループのメニューをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b42dda54-59eb-f3a5-4d28-f822aad71311.png)

そうすると作成済みのセキュリティグループ一覧が表示されます。今回は勉強用の個人のAWSアカウントなのでセキュリティグループ名がdefault（最初から追加されているもの）のみ存在しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/13cd2997-fa4d-ab2a-aaaf-970b72ac5f4e.png)

今回はこのdefaultのセキュリティグループをCloudFormation上で設定していきます。

CloudFormation上ではEC2の`Properties`部分に`SecurityGroups`キー名を追加して、設定するセキュリティグループ名をリストで指定します。以下のような感じになります。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
      SecurityGroups:
        - default
```

さっそくこのYAMLテンプレートをアップロードしていきます。スタックのページに移動して既存のスタックを選択 → スタックアクション → 既存スタックの変更セットを作成メニューをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/24bbceda-0bf8-574c-1cc2-a91d8a22c3e8.png)

既存テンプレートを置き換える → テンプレートファイルのアップロードを選択して対象のYAMLをアップロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6ccef6fd-7ccc-ef15-21fd-0ce6e92c7073.png)

後はパラメータやタグ設定などはスキップして次へボタンを何回か押して進めます。

レビュー画面で変更セットの作成ボタンをクリックします。変更セット名は`add-default-security-group`としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fe66ab0c-d873-97ae-7f43-d33471dbd2e0.png)

変更セットの作成画面に移ったらしばらく待ってリロードし、ステータスが`CREATE_COMPLETE`になるまで待ちます。

今回は置換がTrueとなっているので既存のインスタンスの破棄と作り直しが実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/207505db-f65e-0dbe-fe8d-b0abf1c873d0.png)

特に今回は置換が走っても問題ないためそのまま変更セットを実行ボタンをクリックします。

イベントの画面になるためステータスが`UPDATE_COMPLETE`になるまでしばらく待ちます。リロードして`UPDATE_COMPLETE`と出れば追加したセキュリティグループの設定が問題なく通っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/217ca491-f68d-2e35-1018-6ac03edb4b64.png)

EC2インスタンスのページに遷移して作成されたインスタンスを選択 → セキュリティのタブを表示するとdefaultのセキュリティグループが設定されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/686fa92b-e048-c69e-688a-49f9fb8c293a.png)

※defaultのセキュリティグループはCloudFormation上で指定していなくともEC2インスタンス作成時に付与されるためdefaultのセキュリティグループしか使わない場合にはCloudFormation側は省略しても結果は変わりません。この節ではCloudFormation上でのセキュリティグループの設定とその設定がエラーにならないことを確認しています。

# セキュリティグループの追加時の設定項目の確認と各項目の説明

defaultのセキュリティグループ設定だけだと微妙なので追加でセキュリティグループの追加をCloudFormation上で対応してみます。

まずはセキュリティグループの設定でどんな項目が存在しているのかをwebコンソールで確認します。

EC2のセキュリティグループのページを開いて右上のセキュリティグループを作成ボタンをクリックします。すると以下のような設定項目が画面に表示されます。

- セキュリティグループ名
- 説明
- VPC
- インバウンドルール
- アウトバウンドルール
- タグ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3d3f8746-ff05-4811-f926-9085e73d0f9c.png)

また、インバウンドルールでルールを追加をクリックしてみると以下のような項目があることが確認できます。

- タイプ
- プロトコル
- ポート範囲
- ソース
- 検索フォーム
- 説明

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7e60fbcc-ef5e-5256-677f-9216b14ac041.png)

検索フォーム部分はクリックしてみるとCIDR ブロックを選択する形となっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/051de902-635f-648e-1c66-bc6e67125fad.png)

タイプやポート範囲、説明などの項目は分かりやすいので説明は本記事では割愛しますが、インバウンドルールやアウトバウンドルール、CIDR ブロックに関しては少し触れておきます。

AWSのセキュリティグループのインバウンドルールは外部から対象のEC2インスタンスへの通信の許可設定となります。

外部からのアクセス設定となるため、基本的に最低限のみ開放する形が好ましくなります（webサーバーであればweb関係のポートのみ開放するなど）。

デフォルトでは設定を追加するまで外部からのトラフィックは弾かれるようになっているようです。

> セキュリティグループを作成するときには、インバウンドルールはありません。インバウンドルールをセキュリティグループに追加するまでは、別のホストからインスタンスに送信されるインバウンドトラフィックは許可されません。

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/vpc-security-groups.html

> インバウンドルールというのは、そのEC2インスタンスにアクセスできるトラフィックに制限をするためのルールです。AWSでEC2インスタンスを起動する場合にセキュリティグループをEC2インスタンスに対して設定しますが、インバウンドルールを最低限必要なIPアドレス、ポートのみに限定することでEC2インスタンスを安全にすることができます。
> ...
> EC2インスタンスを安全に使うために、セキュリティグループのインバウンドルールをできる限り最小限に限定することは、一番重要な対策です。
> 
> ただ、一般ユーザー向けにWebサーバを公開する場合など「任意の場所」に対してアクセスを許可しなければいけない場合もあるかと思います。

https://public-cloud-pci-dss.jp/aws/418/

アウトバウンドルールは逆にEC2インスタンス側から外部へと通信する際の制限設定です。例えばDBを設置してあるインスタンスで、クエリ結果のデータを別のホストへと送信する・・・みたいなものもアウトバウンドのものに該当します。

インバウンドルールと比べるとこれに起因して外部から攻撃を受けやすい・・・という感じではないのでデフォルトでは全ての別のホストへのアウトバウンドトラフィックは許可されているようです（ただしドキュメント上ではこの全てのトラフィックを許可する設定は削除して特定のもののみに制限するといったことが推奨されています）。

> デフォルトでは、セキュリティグループにはすべてのアウトバウンドトラフィックを許可するアウトバウンドルールが含まれています。このデフォルトのルールを削除し、特定のアウトバウンドトラフィックのみを許可するアウトバウンドルールを追加することをお勧めします。

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/vpc-security-groups.html

CIDR（サイダー）はIPアドレスのネットワーク部分とホスト部分のサブネットマスク関係などを柔軟に扱う仕組みです。

`xxx.xx.xxx.xxx/yy`みたいなスラッシュとその後の桁数指定を含んだIPアドレスが該当します。

この辺は以下の記事などがとても分かりやすいので必要に応じてご確認ください。

https://wa3.i-3-i.info/word11989.html

ちなみに後述の節で`0.0.0.0/0`というCIDRの指定が出てきます。これは全てのIPアドレスを対象とする、という指定に該当するようです。

> 0.0.0.0/0 means from the whole Internet

https://stackoverflow.com/a/51622014

# 追加のセキュリティグループをCloudFormation上で用意する

セキュリティグループの追加をCloudFormation上で行いたい場合にはEC2インスタンスのリソース部分とは別のリソースとしてYAML上で記述します。

Typeのキーには`AWS::EC2::SecurityGroup`を指定します。

※EC2関係のTypeの一覧は以下のドキュメントをご確認ください。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_EC2.html

リソース名（論理ID）部分は任意の文字列を指定できます。今回は`MySecurityGroup`としました。

セキュリティグループの説明は`Properties`の部分に`GroupDescription`キーを追加することで設定ができます。

セキュリティグループ追加のリソース設定部分だけ抽出すると以下のような感じになります。

```yaml
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: My new security group
```

過去に扱ったEC2インスタンスリソースも含めるとYAMLは以下のようになります。なお、この時点では新しいセキュリティグループは追加されるものの追加されたセキュリティグループがEC2インスタンスは設定される・・・といった挙動はしません（追加のみとなります）。その辺のEC2インスタンスへの設定関係は後々触れます。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
      SecurityGroups:
        - default
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: My new security group
```

一旦このYAMLをアップロードしていきます。前節までと同様なのでスクショは割愛しますが、CloudFormationのスタックのページで既存のスタックを選択し、スタックアクション → 既存スタックの変更セットを作成 → 既存テンプレートを置き換える → テンプレートファイルのアップロードを選択してYAMLのアップロードを対応します。パラメータやタグ設定などはそのまま変更せずに進めます。

変更セット名は`add-my-new-security-group`としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/00b2b419-3d56-2f2e-8628-9ebf38261e94.png)

変更セットを作成してステータスが`CREATE_COMPLETE`になるまでリロードしつつ待ちます。

`CREATE_COMPLETE`になってから変更部分を確認すると、EC2インスタンス関係は表示されず、セキュリティグループ関係のみの追加となっていることが分かります。既存のものには影響しないので置換に関してもハイフン表示になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e03bf7fd-f697-3068-462d-e3eeb136a1c1.png)

変更セットの実行をクリックして反映処理をスタートさせます。今回はセキュリティグループの追加のみなので10秒強くらいでさくっとステータスが`CREATE_COMPLETE`と`UPDATE_COMPLETE`になりました。セキュリティグループ側は`CREATE_COMPLETE`となっていることから分かるように、正常に作成処理が動いてくれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0477079e-83e1-683d-ba04-802c3d2e1cb5.png)

EC2のセキュリティグループのページにアクセスしてみると、default以外のCloudFormation経由で作成されたセキュリティグループが追加になっていることを確認できます。説明部分も設定した`GroupDescription`の値が反映されていることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0d91cb95-39f1-cc10-f1d7-0b660b7d282e.png)

ただしEC2インスタンスリソース側へは追加したセキュリティグループの指定などはまだしていないため、EC2のページのセキュリティタブを見てみるとdefaultのセキュリティグループのみ設定されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/55f1d1f2-2c47-480f-78fb-512e8040a915.png)

次節で追加したセキュリティグループのEC2インスタンスへの反映について触れていきます。

# CloudFormation上で追加したセキュリティグループをCloudFormation上でEC2インスタンス側に設定する

追加したセキュリティグループのリソースがEC2インスタンスリソース側に設定されるように調整してみます。

CloudFormation上で他のリソースへアクセスする（例 : EC2インスタンスリソースからセキュリティグループのリソースへのアクセス等）場合にはCloudFormationのRef関数が必要になります。

CloudFormationには数はそこまで多くありませんが、YAML上で使用できる組み込み関数が以下のドキュメントに記載されているようにいくつか用意されています。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

Ref関数はその組み込み関数の1つであり、Ref関数によって指定されたパラメータもしくはリソースを参照することができます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html

YAMLでの書き方は上記のドキュメントには`Ref: logicalName`といったように書かれています。`logicalName`のところはCloudFormation上で指定したリソースのリソース名（論理ID）を指定します。今回は追加したセキュリティグループのものを指定したいため`MySecurityGroup`を指定する形となるため、`Ref: MySecurityGroup`といった形になります。

上記の記述をEC2インスタンスのリソースの`SecurityGroups`のリスト部分に追加します。YAMLの記述は以下のようになりました。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
      SecurityGroups:
        - default
        - Ref: MySecurityGroup
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: My new security group
```

上記YAMLをCloudFormationでアップしてみます。

スタックのページで既存のスタックを選択してスタックアクション → 既存スタックの変更セットの作成 → 既存テンプレートを置き換える → テンプレートファイルのアップロードを選択してYAMLをアップし、タグやパラメータなどは変えずに進めます。

変更セット名は`add-security-group-reference-from-ec2-instance`としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7e96eaa6-f7e4-db95-5cc7-92dd71b66491.png)

作成された変更セットでステータスが`CREATE_COMPLETE`になるまでリロードしつつ待ちます。

今回はEC2インスタンス側が変更のリストに表示されています。セキュリティグループの追加の場合も置換がTrueとなるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5ab04d1d-1c5b-dd1b-9b31-ce0cdc1aa17a.png)

変更セットを実行ボタンを押して反映します。発行されるイベントでステータスが`UPDATE_COMPLETE`などになるまでリロードしつつ待ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/41e23936-27a2-5a2a-3370-997feb835aaa.png)

ステータスが`UPDATE_COMPLETE`などになったらEC2のページにアクセスして、置換されたインスタンスを選択してセキュリティのタブを表示するとdefault以外のRef関数で設定したセキュリティグループが対象のインスタンスに設定されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/090016b6-2fd7-7a3a-f669-a117169e6c64.png)

# 組み込み関数の短縮系での書き方について

CloudFormationの組み込み関数には!の記号を付けることで短縮系で記述することも可能です。

前述のRef関数だと`Ref: logicalName`という書き方の代わりに`!Ref logicalName`といったように書いても同様の挙動になります。

正直Ref関数に関しては短縮系でも記述が短くならないので使わなくとも良いという所感ですが他の組み込み関数に関してはぼちぼち短く書くことができます。

例えば`Fn::Cidr`関数であれば短縮系になると`!Cidr`となります。

どちらを使用しても挙動は変わりませんのでお好きな方をお使いください。

# セキュリティグループにCloudFormation上からインバウンドルールを追加してみる

今度は追加したセキュリティグループのリソースにCloudFormation上でインバウンドルールを追加してみます。

インバウンドルールを追加するには対象のセキュリティグループの`Properties`以下に`SecurityGroupIngress`というキー名で設定を追加します（ちなみにアウトバウンドルールの場合には`SecurityGroupEgress`というキー名になります）。

`SecurityGroupIngress`内の設定としてはプロトコルタイプの`IpProtocol`、ポート範囲のための`FromPort`と`ToPort`、CIDRの指定用の`CidrIp`などを設定します。

今回は22のポートのみを開放する形で`IpProtocol`にtcp、`FromPort`に22、`ToPort`にも22、`CidrIp`には0.0.0.0/0を指定する形にしてみます。

以下のような記述になります。

```yaml
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
```

YAML全体だと以下のようになります。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
      SecurityGroups:
        - default
        - Ref: MySecurityGroup
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: My new security group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
```

前節までと同様にYAMLをアップロードする形で変更セットを作成して反映します。変更セット名は`add-ingress-rule-to-security-group`としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/62750bc4-acc1-d12a-063e-1abd7e689337.png)

作成された変更セットのステータスが`CREATE_COMPLETE`になってから変更内容を確認してみるとセキュリティグループのみの更新且つ置換がFalseとなっていることが確認できます。インバウンドルールの追加だけであればどうやら既にEC2インスタンスに設定されているセキュリティグループの変更の場合でも置換は走らないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9796e3d5-b020-f816-7992-dbc0a9697915.png)

変更セットの実行をクリックして反映処理をスタートさせます。リロードしつつ待っていると、20秒強程度でステータスが`UPDATE_COMPLETE`となったようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cb7fdd3e-7be7-a234-3b44-34c891341913.png)

EC2インスタンスのページに遷移して対象のインスタンスを選択しセキュリティタブを表示すると、インバウンドルール部分でポート範囲に22、プロトコルにTCP、ソース部分にはCIDRの0.0.0.0/0が反映されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2cd99e54-092e-44ca-869d-59b67731f35e.png)

大分長くなってきたため本記事は一旦ここで区切ります。残りは3記事目以降で触れていこうと思います。

# 参考サイト・講座・文献まとめ

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html

https://www.udemy.com/course/aws-cloudformation-simplified-hands-on-learning/

https://qiita.com/leomaro7/items/42967c080814dfc82139

https://qiita.com/tsukamoto/items/fff3d7c58d24eb08034c

https://stackoverflow.com/questions/51621586/what-is-the-cidrip-in-security-groups

https://wa3.i-3-i.info/word11989.html

https://public-cloud-pci-dss.jp/aws/418/

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/vpc-security-groups.html

https://stackoverflow.com/a/51622014

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_EC2.html

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-security-group-egress.html

https://qiita.com/yz2cm/items/2448f1545fef406ce8e3