経緯
GitHubActionsのワーカーから、AWSのEC2にSSHで接続したい!
↓
GitHubActionsのワーカーのグローバルIPアドレスでのSSH接続をEC2のセキュリティグループで許可しよう
↓
GitHub無料利用だと、クラウド上のワーカー(AzureのVMらしい)となり、ワーカーが作成されるたびに異なるIPアドレスが割り当てられることを知る
↓
セキュリティグループの設定に必要な固定のIPアドレスがわからない!
↓
割り当てられるIPアドレスのリストは公開されているが、実際に使用するIPアドレスだけ許可したい...
↓
今に至る
解決策
下記の3点で、対応可能
詳細を事項で解説
① 作成されたワーカー内で、現在のグローバルIPアドレスを取得する
② AWS CLIでセキュリティグループにポート22, 取得したIPアドレスでルールを追加
③ ジョブの最後でルールを削除
必要作業
大まかに必要な作業を列挙する
① GitHubとAWSをOIDCで認証するように設定 (GitHubドキュメント)
アクセスキー・シークレットキーでも可能だが、キーの運用の手間やセキュリティの観点からOIDCを使うことにする
AWSのIAMコンソールからIDプロバイダを設定する
下記項目を選択・入力してプロバイダを追加
プロパティのタイプ:OpenID Connect
プロバイダのURL:https://token.actions.githubusercontent.com
対象者:sts.amazonaws.com
② 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