GitHubActionsでAWSのセキュリティグループのルールを一時的に変更する

## 経緯
GitHubActionsのワーカーから、AWSのEC2にSSHで接続したい！
↓
GitHubActionsのワーカーのグローバルIPアドレスでのSSH接続をEC2のセキュリティグループで許可しよう
↓
GitHub無料利用だと、クラウド上のワーカー（AzureのVMらしい）となり、ワーカーが作成されるたびに異なるIPアドレスが割り当てられることを知る
↓
セキュリティグループの設定に必要な固定のIPアドレスがわからない！
↓
割り当てられるIPアドレスのリストは[公開](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses)されているが、実際に使用するIPアドレスだけ許可したい...
↓
今に至る

## 解決策

下記の３点で、対応可能
詳細を事項で解説

#### ① 作成されたワーカー内で、現在のグローバルIPアドレスを取得する
#### ② AWS CLIでセキュリティグループにポート22, 取得したIPアドレスでルールを追加
#### ③ ジョブの最後でルールを削除

## 必要作業

大まかに必要な作業を列挙する

### ① GitHubとAWSをOIDCで認証するように設定 ([GitHubドキュメント](https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services))
アクセスキー・シークレットキーでも可能だが、キーの運用の手間やセキュリティの観点からOIDCを使うことにする


#### AWSのIAMコンソールからIDプロバイダを設定する

下記項目を選択・入力してプロバイダを追加
プロパティのタイプ：OpenID Connect
プロバイダのURL：https://token.actions.githubusercontent.com
対象者：sts.amazonaws.com
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3418025/3017ad40-ff87-4763-ae6c-5685ac90d589.png)



### ② OIDC用のIAMロール作成とポリシーの追加
OIDC用のロールを作成し、ワーカーが実際にセキュリティグループに対してアクションが可能なポリシーを追加する

#### ロールの作成
AWSアカウントID、GitHubのユーザ名、リポジトリ名を適宜設定
ちなみに、今回はリポジトリ名の後はワイルドカードで指定しているがブランチも指定が可能
````
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:<GitHubユーザ名>/<リポジトリ名>:*"
                }
            }
        }
    ]
}
````

#### ポリシーの作成・アタッチ
※ロールの詳細画面からインラインポリシーを作成してもよし、作成したポリシーをロールにアタッチするもよし

ルールの作成、削除とルール一覧を表示するポリシーを付与
リージョン、AWSアカウントID、セキュリティグループIDを適宜入力
````
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:RevokeSecurityGroupIngress",
                "ec2:AuthorizeSecurityGroupIngress"
            ],
            "Resource": "arn:aws:ec2:<リージョン>:<AWSアカウントID>:security-group/<セキュリティグループID>"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:DescribeSecurityGroupRules",
            "Resource": "*"
        }
    ]
}
````
### ③ ジョブ内容（ymlファイル）作成
今回はSSH接続方法までは解説せず、あくまでセキュリティグループへのルールの追加・削除方法を記載する
ちなみに今回はAWS CLIを使うが、SDKを利用したプログラミング言語でも可能

最終的なコードは最後に表示するとして、要点の部分を記載していく

#### アクセス許可設定
````
permissions:
  id-token: write   # id-tokenは「write」を指定（これ絶対）
  contents: read    
````

#### アクセストークンの要求
````
 - name: AWS CLI set up
        uses: actions/checkout@v4
 - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-session-name: Hoge
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }} # ②で作成したロールのARNを指定
          aws-region: <リージョン>
````
#### IPアドレスを取得してセキュリティグループにルールを追加

````
 - name: add tempolary securitygroup rule
   run: | 
          WORKER_IP=$(curl -s https://checkip.amazonaws.com/)  # グローバルIPアドレスを返すAPIエンドポイント
          aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr $WORKER_IP/32
````

#### 追加したルールの削除
ルール追加時に取得したWORKER_IPのスコープは狭いため、削除時には再度取得する必要アリ
````
 - name: revoke tempolary securitygroup rule
        run: |
          WORKER_IP=$(curl -s https://checkip.amazonaws.com/)
          aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr $WORKER_IP/32
````
#### 全体のコード
※一部マスクしてます
````
name: Build Test

on:
  push:
    branches:
      - deploy  # 実行対象のブランチを指定
  workflow_dispatch:
  
permissions:
  id-token: write   
  contents: read    
  
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # コードのチェックアウト
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: AWS CLI set up
        uses: actions/checkout@v4
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-session-name: ******
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
          aws-region: *******
      

      - name: update tempolary securitygroup rule
        run: | 
          WORKER_IP=$(curl -s https://checkip.amazonaws.com/)
          aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr $WORKER_IP/32

      - name: SSH and clone app on EC2
        run: |
          sudo apt update -y && sudo apt install -y openssh-client
          sudo mkdir -p /home/runner/.ssh
          sudo chmod 700 /home/runner/.ssh
          sudo echo "${{ secrets.AWS_EC2_PRIVATE_KEY }}" | sudo tee /home/runner/.ssh/id_rsa > /dev/null
          sudo chmod 600 /home/runner/.ssh/id_rsa
          sudo ssh -i /home/runner/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@****** "sudo rm -rf /var/next-arc"
          sudo ssh -i /home/runner/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@****** 'sudo GIT_SSH_COMMAND="ssh -i /root/.ssh/id_rsa" git clone git@github.com:******/******.git /var/next-arc'
          sudo ssh -i /home/runner/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@****** 'sudo touch /var/next-arc/.env && sudo chmod 666 /var/next-arc/.env && \
          sudo echo "${{ secrets.NEXT_PUBLIC_BACKEND_URL }}" >> /var/next-arc/.env && \
          sudo echo "${{ secrets.THUMBNAIL_IMAGE_PATH }}" >> /var/next-arc/.env && \
          sudo echo "${{ secrets.BACKEND_APP_PATH }}" >> /var/next-arc/.env'
          
      - name: build and start app
        run: |
          sudo ssh -i /home/runner/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@****** "cd /var/next-arc"
          sudo ssh -i /home/runner/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@****** 'sudo bash -c "cd /var/next-arc && \
          npm install && \
          npm run build && \
          pm2 delete all && \
          pm2 start npm --name \"next\" -- start && \
          pm2 save"'
          
      - name: revoke tempolary securitygroup rule
        run: |
          WORKER_IP=$(curl -s https://checkip.amazonaws.com/)
          aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr $WORKER_IP/32
````