Google Cloud Armorのセキュリティーポリシーの許可ルールにGithub ActionsのIPを一時的に追加する方法

# はじめに
[Google Cloud Armor](https://cloud.google.com/armor)はDDoS攻撃などの様々な脅威からアプリケーションを保護するためのGCPのサービスです。

Google Cloud Armorでは**セキュリティーポリシー**というものを設定できます。このセキュリティーポリシーには IPアドレスやリクエストヘッダーによる条件によってトラフィックを制限するルールを設定することができます。

今回、ワークフローエンジンであるDigdagに対して、Github Actionsで`digdag push`をするワークフローを作成する必要がありました。このDigdagサーバーはGoogle Cloud Armorで保護されています。Github Actionsは実行のたびにIPが変わるため、単一のIPをGoogle Cloud Armorのセキュリティーポリシールールに設定することはできません。
もし仮にGithub ActionsのIPを固定にすることができ、セキュリティールールに設定することが出来ても、特定のIPが常に許可された状態になるため、セキュリティー的によくありません。

そこで、Github Actionsを実行するタイミングにGoogle Cloud Armorのセキュリティーポリシーの許可ルールにGithub ActionsのIPを一時的に追加することで、この問題を解決しました。この方法について紹介します。

# 実装
Github Acitonsのワークフロー定義です。
Google Cloud Armorのセキュリティーポリシー変更に関わるstep部分のみを記述しています。

事前準備として、Github　Actions用GCPサービスアカウントを作成します。このサービスアカウントには**Compute セキュリティ管理者（roles/compute.securityAdmin）**のロールが必要です。
Github Actionsのsecretsに`GCP_SERVICE_ACCOUNT_KEY`というキー名でGithub　Actions用GCPサービスアカウントの秘密鍵が設定されている想定です。

`allow-policy`という名前のGoogle Cloud Armorセキュリティーポリシーに対してルールを一時的に追加します。

```yaml
jobs:
  digdag-push:
    steps:
      - name: Setup gcloud
        uses: google-github-actions/setup-gcloud@master
        with:
          project_id: gcp-sample-project
          service_account_key: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}
          export_default_credentials: true

      - name: Digdag push
        run: |
          IP=$(curl -f -s http://whatismyip.akamai.com)
          trap "gcloud compute security-policies rules delete 1000 --security-policy allow-policy --quiet" 0 1 2 3 15
          gcloud compute security-policies rules create 1000 --action allow --security-policy allow-policy --description "GithubActions IP" --src-ip-ranges ${IP}
          (digdag pushの処理)
         
```

# 解説
`Setup gcloud`　というステップでgcloudコマンドが使えるように設定をします。

そして今回の肝となる、Google Cloud Armorのセキュリティーポリシーの許可ルールにGithub ActionsのIPを一時的に追加するための実装は以下の部分です。

```sh
IP=$(curl -f -s http://whatismyip.akamai.com)
trap "gcloud compute security-policies rules delete 1000 --security-policy allow-policy --quiet" 0 1 2 3 15
gcloud compute security-policies rules create 1000 --action allow --security-policy allow-policy --description "GithubActions IP" --src-ip-ranges ${IP}
```
一行ずつ解説します。

## IPアドレスの取得
```
IP=$(curl -f -s http://whatismyip.akamai.com)
```

1行目では環境変数IPにGithub Actionsが実行されているインスタンスのIPを設定します。
http://whatismyip.akamai.com にGETリクエストを送ることで自身のIPがわかります。curlコマンドには`-f(--fail)`と`-s(--silent)`のオプションを指定しています。`-f(--fail)`はリクエスト失敗時に異常終了します。`-s(--silent)`はリクエストの進捗状況を非表示にするためです。

## セキュリティポリシールールの追加
```
gcloud compute security-policies rules create 1000 --action allow --security-policy allow-policy --description "GithubActions IP" --src-ip-ranges ${IP}
```

先に3行目について説明します。ここではgcloudコマンドを使って、セキュリティポリシールールの追加をしています。
ここでは、`allow-policy`という名前のGoogle Cloud AromorセキュリティポリシーにGihub Actionsからのアクセスを許可するルールを追加しています。

気をつけてほしいのは、crateのあとの1000という数字です。これはルールの優先度を指定しています。優先度は低いものが優先されます。今回は優先度1000にしていますが、ここは環境にあった優先度を設定してください。

## セキュリティポリシールールの削除
```
trap "gcloud compute security-policies rules delete 1000 --security-policy allow-policy --quiet" 0 1 2 3 15
```

2行目では、セキュリティポリシールールを削除しています。

まず先にこの行で使っているtrapコマンドについて説明します。
trapコマンドとは、シグナルを受け取った際の動作を指定するためのコマンドです。 `trap 'コマンド' シグナル1 シグナル2 ...`というフォーマットで記述します
シグナルとは実行中のプロセスに対するイベント通知に利用されるものです。具体例をあげると、プロセス実行中に`Ctrl + c`を押すと、プロセスに対して`SIGINT`という割り込みイベントを通知します。
その他にも様々なイベントがあります。また、それぞれのイベントには番号が割り当てられています。

以上、trapコマンドについて説明した上で、この行でやっていることは、EXIT(0)・HUP(1)・INT(2)・QUIT（3）・TERM(15)というシグナルを受け取ったときに、優先度1000のセキュリティポリシーを削除するというものです。

指定しているシグナルについての説明は省略しますが、EXIT（0）・SIGHUP(1)・SIGINT(2)・SIGQUIT（3）・SIGTERM(15)をtrapコマンドのシグナルに指定することで、step内の処理が正常終了または途中終了した場合でもセキュリティポリシールール削除の処理が実施されます。

stepの最後でそのまま`gcloud compute security-policies rules delete`を実行してしまうと、それ以前に処理が異常終了してしまった場合に、セキュリティポリシーの許可ルールが設定されたままになってしまいます。これを防ぐためにtrapコマンドを使っています

# さいごに
Google Cloud Armorのセキュリティーポリシーの許可ルールにGithub ActionsのIPを一時的に追加する方法について紹介しました。
外部からGoogle Cloud Armorで保護されたリソースへのアクセスを許可する際に参考になれば幸いです。

# 参考資料
https://cloud.google.com/armor/docs/configure-security-policies
https://shellscript.sunone.me/signal_and_trap.html