GitHub ActionsでSrc IP制限のかかったサービス(AWS RDS)につなぐ

## はじめに

GitHub Actionsは、GitHub上のコードに対して、PushやPullRequestをフックして、処理を実行できる処理基盤です。


このGitHub Actionsを使って自動テストや自動デプロイをするのですが、GitHub ActionsのワーカーのグローバルIPアドレスは毎回変わるため、Src IP制限のかかったサービスを使おうとするとうまくいきません。

そこで、この記事では、GitHub ActionsからSrc IP制限のかかったサービスを使う方法を、AWSのRDSを例に上げて説明します。

## 概要

処理の概要は以下のとおりです

1. GitHub ActionsのワーカーのグローバルIPを、`haythem/public-ip@v1.2` を使って取得する
2. RDSのSecurity Groupに対して、取得したグローバルIPからの通信を一時的に許可する
3. サービスにつないでやりたいことをやる
4. RDSのSecurity Groupに対して、取得したグローバルIPからの通信を一時的に遮断する

## 作り方

#### 1. RDSのセキュリティグループを開け締めするプログラムを作る

まず、Security Groupを操作するためのプログラムをGo言語で作ります。

`sgutil.go`


```golang
package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/rds"
	"os"
	"syscall"
)

const RDS_ID = "myrds" // RDSのID
const RDS_PORT = 5432 // RDSのポート

// cidrからの通信を許可するようにRDSのセキュリティグループを変更する。
// @param : ipAddr :通信を許可したいIPアドレス。
// @param : op : 操作。op=openで開ける。 op=closeで閉じる。
func securityGroupIngressControl(ipAddr string, op string) {
	sess, err := session.NewSession(&aws.Config{Region: aws.String("ap-northeast-1")})
	if err != nil {panic(err)}
	rdsService := rds.New(sess)

	// RDSのセキュリティグループの取得
	input := &rds.DescribeDBClustersInput{DBClusterIdentifier: aws.String(RDS_ID)}
	result, err := rdsService.DescribeDBClusters(input)
	if err != nil {panic(err)}
	securityGroupId := *(result.DBClusters[0].VpcSecurityGroups[0].VpcSecurityGroupId)

	// security groupの操作
	ec2Service := ec2.New(sess)
	ipPermissions := []*ec2.IpPermission{
		{
			FromPort:   aws.Int64(int64(RDS_PORT)),
			ToPort:     aws.Int64(int64(RDS_PORT)),
			IpProtocol: aws.String("tcp"),
			IpRanges:   []*ec2.IpRange{{CidrIp: aws.String(ipAddr + "/32")}},
		},
	}
	if op == "open" {
		// security groupを開ける
		input := &ec2.AuthorizeSecurityGroupIngressInput{
			GroupId:       aws.String(securityGroupId),
			IpPermissions: ipPermissions,
		}
		_, err := ec2Service.AuthorizeSecurityGroupIngress(input)
		if err != nil {panic(err)}
	} else if op == "close" {
		// security groupを閉じる
		input := &ec2.RevokeSecurityGroupIngressInput{
			GroupId:       aws.String(securityGroupId),
			IpPermissions: ipPermissions,
		}
		_, err := ec2Service.RevokeSecurityGroupIngress(input)
		if err != nil {panic(err)}
	} else {
		panic("invalid op")
	}
}

func printUsage() {
	fmt.Printf("Argument ERROR. \n\nUsage: go run %s [open|close] IpAddress \n", os.Args[0])
}

func main() {
	if len(os.Args) != 3 {
		printUsage()
		syscall.Exit(1)
	}
	op := os.Args[1] // 操作 [open|close]
	ipAddr := os.Args[2]
	securityGroupIngressControl(ipAddr, op)
}

```

※Go言語を用いなくても、bashとaws cliを駆使して実現することも可能ですが、少し複雑なプログラムのため、プログラミング言語で作りました。

#### 2. GitHubのsecretsにAWSのクレデンシャル情報を追加する

今回は、`AWS_ACCESS_KEY_ID`と `AWS_SECRET_ACCESS_KEY` という名前に、それぞれAWSのシークレットを登録します。

#### 3. Github Actionsのワークフローを作る

`./github/workflows/my_action.yml`

```yml

name: MyAction

on:
  push:
    # developブランチにpushされたら動き出す。
    branches: [ develop ]

jobs:
  my_action:
    name: MyJob
    runs-on: ubuntu-latest
    steps:

      # AWSのクレデンシャルをセット
      - name: Create AWS credential Environment Variables
        uses: aws-actions/configure-aws-credentials@v1
        with:
          # GitHubのSecretsに登録しておくと、secrets.XXXXで参照できる
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      # GitHub ActionsのワーカーのGlobalIPアドレスを取得するためのステップ
      - name: Install Public IP Lib
        id: ip
        uses: haythem/public-ip@v1.2

      # Go言語のセットアップ
      - name: Setup Go
        uses: actions/setup-go@v2
        with:
          go-version:  1.15

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

      # RDSのセキュリティグループに対して、GitHub Actionsのワーカーからの通信を許可する
      - name: Open RDS security group ingress from this IP
        run: go run sgctl.go open ${{ steps.ip.outputs.ipv4 }} # steps.ip.outputs.ipv4でIPを参照できる

      # DBに接続していろいろする
      - name: Run DB Ops
        run: xxxxxxxxx

      # RDSのセキュリティグループに対して、GitHub Actionsのワーカーからの通信を閉じる
      - name: Close RDS security group ingress from this IP
        if: always() # alwaysをつけることにより、アクションが失敗しても必ず実行されるようにする
        run: go run sgctl.go close ${{ steps.ip.outputs.ipv4 }}


```