この記事は DENSOアドベントカレンダー2024 の4日目の記事です。
はじめに
本記事の要点を3行で
Webアプリケーションのセキュリティ向上のためにWAF(Web Application Firewall)を利用中
WAFの地域制限が原因でGitHub ActionsからのAPIテストに失敗する課題があり
この問題を解決する方法の一例を紹介
課題の詳細
WAFと地域制限の概要
-
WAF(Web Application Firewall)の役割
- WAFは、Webアプリケーションに対する攻撃を検知・防御するセキュリティ対策ツール
- 代表的な攻撃例:
- SQLインジェクション
- クロスサイトスクリプティング(XSS)
- ボットによる不正アクセス
- 主にトラフィックを監視し、設定したルールに基づいてアクセスを制御する
-
地域制限の目的と動作
- WAFでは、特定の地域からのアクセスを制限する機能を持つ場合が多い
- この制限は、次の理由で有効:
- 攻撃元となる地域をブロックすることでセキュリティリスクを軽減
- 特定地域内のユーザーに限定したサービス提供
- 制限の実装例:
- IPアドレスの地域情報に基づきアクセスを許可または拒否
GitHub Actionsの制限
-
GitHub Actionsの動作環境
- ワークフローの実行にはGitHubが提供する共有ランナーを利用する
- 共有ランナーでは、実行ごとに異なる動的なIPアドレスが使用される
発生した課題
-
地域制限との衝突
- WAFの地域制限が、GitHub Actionsからのリクエストをブロックする可能性
- 例えば、共有ランナーが許可地域外のIPを使用した場合、APIアクセスが遮断される
- この結果、テストやデプロイメントが失敗する事態が発生
解決方法の概要
APIテストを実行するAWS Lambda関数を許可地域にデプロイし、Github Actionsから該当のLambda関数を実行する
手順
- AWS側にIdPを設定しRoleを引き受けるよう設定(参考リンク)
RoleはLambda関数を実行できる許可ポリシーが必要 - 許可地域のリージョンでAPIテスト実行用のLambda関数を準備
- Github Actionsで2.のLambda関数を実行し、結果を取得する
ポイントは2.でLambda関数をデプロイするリージョンを許可地域内にしているところです。
これにより、WAFの地域制限を突破することができます。
GitHub Actionsのワークフロー例は以下の通りです。
こちらのページを参考にさせていただきました。
.github/workflows/lambda-invoke.yml
name: lambda-invoke
on: pull_request
permissions:
id-token: write
contents: read
jobs:
api_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/lambda-invoke
with:
lambda-function-name: hogehoge
aws-account-id: ${{ secrets.AWS_ACCOUNT_ID }}
iam-role-name: ${{ secrets.IAM_ROLE_NAME }}
.github/actions/lambda-invoke/action.yml
name: Lambda Invoke
description: Lambda関数を同期実行し、レスポンスがfalseの場合Github Actionsを失敗させる
inputs:
lambda-function-name:
required: true
description: 実行する Lambda の名前
aws-region:
default: ap-northeast-1
required: true
description: AWSリージョン
aws-account-id:
required: true
description: AWSアカウントID
iam-role-name:
required: true
description: IAMロール名
read-timeout-second:
default: 300
required: true
description: 最大ソケット読み取り時間(秒)
outputs:
result:
value: ${{ steps.execute-lambda.outputs.result }}
description: Lambda の実行結果 (OK / NG)
response:
value: ${{ steps.execute-lambda.outputs.response }}
description: Lambda function のレスポンス
runs:
using: composite
steps:
# IAMロールを引き受ける
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ inputs.aws-region }}
role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.iam-role-name }}
role-session-name: gh-action-${{ github.run_id }}
# AWS CLIでLambda関数を実行し、レスポンスがfalseの場合Github Actionsを失敗させる
- name: Execute Lambda
id: execute-lambda
shell: bash
env:
PAYLOAD_JSON: ${{ steps.check-json-format.outputs.payload-json }}
LAMBDA_FUNCTION_NAME: ${{ inputs.lambda-function-name }}
READ_TIMEOUT_SECOND: ${{ inputs.read-timeout-second }}
run: |
output=$(
aws lambda invoke \
--function-name "${LAMBDA_FUNCTION_NAME}" \
--cli-binary-format raw-in-base64-out \
--cli-read-timeout "${READ_TIMEOUT_SECOND}" \
/dev/stdout \
| jq -s
)
echo "${output}"
if echo "${output}" | jq -e -r '.[1].FunctionError' > /dev/null; then
echo "ERROR: Lambda execution failed."
result="NG"
else
echo "INFO: Lambda execution success."
result="OK"
fi
echo "result=${result}" >> "${GITHUB_OUTPUT}"
response=$(echo "${output}" | jq .[0])
echo "INFO: Lambda function response: "${response}
response="${response//'%'/'%25'}"
response="${response//$'\n'/'%0A'}"
response="${response//$'\r'/'%0D'}"
echo "response=${response}" >> "${GITHUB_OUTPUT}"
if [ "${response}" = "false" ]; then
exit 1
fi
考慮した前提条件
- サービスインフラとしてAWSを使用しているので、可能であればAWSを利用したい
- 管理の手間がかかるので可能な限りマネージドサービスを使いたい
その他の実現方法例
他にも下記方法などがあると思いますが、今回は着手の容易性などからLambda関数で実装する方式をとっています。
- 制限対象地域のリージョンにGitHub Actionsのセルフホステッドランナーを構築して実行する
- PostmanなどのSaaS製品を使い、リクエスト元のリージョンを指定する