GCPのFireWallルールを設計する

#  はじめに
[フューチャー Advent Calendar 2019（2）](https://qiita.com/advent-calendar/2019/future2)　の9日目です。
[以前技術Blogで書いたGCP記事](https://future-architect.github.io/articles/20190820/)　のFireWallルールについての記事になります。
GCPで共有VPC環境を構築していると通常のGCP環境構築に比べて考慮する点が多く、FireWallは特に苦労したポイントの一つです。
そんなFireWallの設計について実例ベースに書いていきたいと思います。

# ネットワーク構成
前述の通り、構築する環境はGCPで共有VPCを利用した環境になります。
仮に10.14.0.0/16のアドレス範囲を利用するとして、以下のようにサブネットとプロジェクトを構築しています。
※詳細は[以前の記事](https://qiita.com/advent-calendar/2019/future2)を参照。

- 各プロジェクト（環境）毎にサブネットを割り当てている
- CloudSQLインスタンスはすべてのプロジェクトで同じアドレス範囲を利用する<br>
※CloudSQLインタンスを作成するインタンスのアドレス範囲はVPCに紐づくため共有VPCで1つのため<br>
　https://cloud.google.com/sql/docs/mysql/private-ip#network_requirements

![スライド1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/9f2a5c95-47fa-a382-eab7-258a0ecc6b03.png)


# GCPのFireWallについて
実際の設計をする前にFireWallの設定項目について説明します。
https://cloud.google.com/vpc/docs/firewalls

###  1. 一致したときのアクション（allowとdeny）
FireWallのルールは特定の通信をallow（許可）/deny（拒否）する設定になります。

###  2. 優先度
前項のallow/denyしたルールの優先度を設定します。
これにより「全体に対しての通信は拒否するが一部のみ許可」のような設定が可能になります。

###  3. トラフィックの方向
「上り」か「下り」が設定できます。
FireWallのルールを設定するVMインスタンスに対しFromの通信なのかToの通信なのかを定義できます。

###  4. ターゲット
対象が「ネットワーク内のすべてのインスタンス」、「ターゲットタグによるインスタンス」、「ターゲット サービス アカウントによるインスタンス」で指定できます。
後述しますが、サブネットでの指定はできません。
###  5. 暗黙ルール
前項の上りに関しては暗黙（最低優先度）でdenyの設定が、下りに関してはallowの設定が入っています。

#  実際に設計してみる
では実際に設計してみます。
今回は以下の2観点で分けて設計します。
![スライド2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/4f193fb5-1ef3-b4ad-1e88-56118d474338.png)

## VMインスタンス
VMインタンスについては以下の２つを実現する必要がありました。

- 各プロジェクト（環境）を跨いだ通信ができないようにする。
- VPC外のアドレス（インターネットVPN経由でのオンプレ環境）からの通信は全て可能とする。

では順を追って設計してみましょう。

### 1. ブラックリストかホワイトリストか
まずは考え方の方針として、ブラックリストかホワイトリストかを決めます。
今回はホワイトリストを意識させる方式にしました。
これはブラックリストにすると、とあるプロジェクトにインスタンス追加したときに他プロジェクトすべてに対してそのインスタンスを意識する必要が出てきてしまうためです。
ホワイトリストにすることで、自プロジェクトのみ意識すればよくなります。

### 2. 全てのアドレスからの通信を許可する
では次に実際の設定を入れていきます。
前述の通り、上りに関しては暗黙（最低優先度）でdenyの設定が入っているため、VMインスタンスは基本的にどことも通信できないです。
ここでVPC以外のアドレス（10.14.0.0/16以外）と通信可能にする必要があるのですが、「10.14.0.0/16 **以外**」のような設定ができないため、まずはすべての上り通信に対して許可を入れます

```
ターゲット：すべてのインスタンス
方向：上り
ソースフィルタ：IP範囲（0.0.0.0/0）
可否：許可
優先度：1000
```
これでまずは外部、内部の全ての通信が可能となります。
![スライド3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/b0db2617-46aa-f4cd-1258-2c7a1e4e9dfe.png)


### 3. インスタンス間の通信をすべてNGとする
次にVMインスタンス間の通信を全て拒否します。
今の状態だとプロジェクトも跨いだ（ProductionとStaging間等）通信が可能な状態です。
ここでもProduction環境に対してがProduction環境のサブネット以外を拒否が出来れば良いのですが、前述の通り「以外」
の設定ができないため、まずはホストのVPC（10.14.0.0/16）に対して全ての通信を拒否します。

```
ターゲット：すべてのインスタンス
方向：上り
ソースフィルタ：IP範囲（10.14.0.0/16）※VPCで使っているアドレス範囲
可否：拒否
優先度：900
```
ここでは前述の設定よりも優先度を高くすることで以下のようになります。
![スライド4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/01317768-bcd3-fe82-24f7-82a79c15b588.png)


### 4. 同じプロジェクトのインスタンスの通信を許可する
最後に同一のプロジェクトのインスタンス間の通信を許可します。
これを実現するため、各インスタンスにはプロジェクトごとに共通のネットワークタグを設定します。
※仮にProduction環境のインスタンスについて「prd-instance」とタグをつけるとします。

```
ターゲット：対象のネットワークタグ（prd-instance）
方向：上り
ソースフィルタ：IP範囲（10.14.16.0/20）※各プロジェクトで使っているアドレス範囲
可否：許可
優先度：500
```
これで各プロジェクト内での通信が可能となります
![スライド5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/237f2082-f5c7-b942-8de9-67d2154c7aa7.png)

これをStagingなど他環境にも設定することで各プロジェクト内での通信が可能となります。

# CloudSQLインスタンス
次にCloudSQLインスタンスへのアクセスを設定したいと思います。
前述の通り、CloudSQLインスタンスはすべてのプロジェクトで同じアドレス範囲を利用するため、
アドレス範囲を指定してのキレイな指定はできません。

## 1. 何対してFireWallルールを設定するか
SQLインスタンスにはネットワークタグは付けれないため、FireWallルールはVMインスタンスについて設定することになります。
また、先ほどは上りに対しての設定でしたが、VMインスタンスからCloudSQLの通信を制御するため、こちらでは下りの制御を入れます。

## 2. 全てのSQLインスタンスに対して通信を拒否する
まず、下りの通信は暗黙のルールでallowになっているため、どのVMからでもすべてのSQLインスタンスに接続できます。
このため、まずはすべてのVMインスタに対してSQLインスタンスに割り当てられるアドレス範囲の通信を拒否します。

```
ターゲット：すべてのインスタンス
方向：下り
ターゲットフィルタ：IP範囲（10.14.240.0/20）※CloudSQLの生成されるアドレス範囲
一致したときのアクション：拒否
優先度：500
```

これで全てのVMインスタンスとSQLインスタンスで通信が出来なくなります。
![スライド6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/ec3bcdba-c999-3d5a-8b83-ccc667249956.png)


## 特定のSQLインスタンスに対して通信を許可する
次に同一プロジェクトのVMインスタンスとSQLインスタンスの通信を許可します。
この際、SQLインタンスはアドレス範囲内でランダムのため、アドレス範囲の指定ではなく、インスタンスのIPそのものを指定する必要があります。
Production環境を例にとると以下の通りです。

```
ターゲット：対象のネットワークタグ（prd-instance）
方向：下り
ターゲットフィルタ：IP範囲（10.14.240.1, 10.14.240.46, 10.14.240.76 ・・・）
　　　　　　　　　　　※対象のSQLインスタンスのアドレス
一致したときのアクション：許可
優先度：100
```

これで同一プロジェクトのVMインスタンスとSQLインスタンスのみで通信が可能となります。
![スライド7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133368/7f9f7840-5051-74bc-abe1-eddadd253a93.png)


この際、SQLインスタンスのアドレスはインタンス名から取得するようにTerraformやスクリプトなど組んでおくように注意しましょう。
IPアドレスはインタンスの再作成で変わるため定義としての再現性はありません。

# まとめ
今回はネットワーク構成を起点にFireWallルールを設定するというオンプレ的な設計をしてみました。
サービスアカウントなどをうまく使えばもっとシンプルな設計も出来たかもしれませんが、そのあたりの設計が固まっていなくても下回りで保証する一例として見てもらえると幸いです。