CloudFormation GuardをGitHub Actionsに組み込んでみた
CloudFormation Guard を使うことで、CloudFormation をデプロイ前にコンプライアンスチェックできます。
前回の記事は、CloudFormation Guard をローカルで実行しました。
CloudFormation Guard を CI/CD パイプラインに組み込むことによって、デプロイ前にチェックし、コンプライアンスに準拠していないリソースがデプロイされることを防げます。
本記事では、CI/CD パイプラインに GitHub Actions を使ったパターンを試してみます。
なお、CodeCommit / CodePipeline / CodeBuild を使用するパターンについては、以下の公式ブログで紹介されています。
やってみた
CloudFormation Guard で「SSHポートが全公開されているセキュリティグループを許可しない」ルールを作り、該当するセキュリティグループがあった場合にデプロイを中止させます。
実装は、以下の手順で進めます。
- CloudFormation テンプレートを作成
- GitHub Actions のワークフローを作成
- GitHub Secrets を設定
- 実行
1. CloudFormation テンプレートを作成
セキュリティグループのテンプレートを抜粋します。
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${ProjectPrefix}-alb-sg
GroupDescription: !Sub ${ProjectPrefix}-alb-sg
Tags:
- Key: Name
Value: !Sub ${ProjectPrefix}-alb-sg
VpcId:
Fn::ImportValue: !Sub ${NetworkStackNamePrefix}-VpcId
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 443
ToPort: 443
CidrIp: "0.0.0.0/0"
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${ProjectPrefix}-web-sg
GroupDescription: !Sub ${ProjectPrefix}-web-sg
Tags:
- Key: Name
Value: !Sub ${ProjectPrefix}-web-sg
VpcId:
Fn::ImportValue: !Sub ${NetworkStackNamePrefix}-VpcId
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
よくある ALB と EC2 のセキュリティグループを想定しており、ALB は 443 番ポートで全許可し、EC2 は 22 番ポートを全許可しています。
2. GitHub Actions のワークフローを作成
ワークフローでは以下を行います。
- GitHub Secrets に登録した認証情報をセット
- CloudFormation Guard をインストール
- CloudFormation Guard を実行
- CloudFormation デプロイ
name: Compliance check with CloudFormation Guard
on:
push
defaults:
run:
shell: bash
jobs:
check-cfn-templates:
name: Check AWS CloudFormation templates
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: install CloudFormation Guard
env:
CFN_GUARD_VERSION: 1.0.0
run: |
wget https://github.com/aws-cloudformation/cloudformation-guard/releases/download/${CFN_GUARD_VERSION}/cfn-guard-linux-${CFN_GUARD_VERSION}.tar.gz
tar -xvf cfn-guard-linux-${CFN_GUARD_VERSION}.tar.gz
- name: check network template with CloudFormation Guard
env:
TEMPLATE: network
run: |
./cfn-guard-linux/cfn-guard check --template ./templates/${TEMPLATE}.yml --rule_set ./cfn-guard-rules/${TEMPLATE}_ruleset
- name: check security group template with CloudFormation Guard
env:
TEMPLATE: sg
run: |
./cfn-guard-linux/cfn-guard check --template ./templates/${TEMPLATE}.yml --rule_set ./cfn-guard-rules/${TEMPLATE}_ruleset
- name: Deploy network stack
env:
STACK_NAME: network
run: |
aws cloudformation deploy \
--stack-name ${STACK_NAME} \
--template-file ./templates/${STACK_NAME}.yml \
--no-fail-on-empty-changeset
- name: Deploy security group stack
env:
STACK_NAME: sg
run: |
aws cloudformation deploy \
--stack-name ${STACK_NAME} \
--template-file ./templates/${STACK_NAME}.yml \
--no-fail-on-empty-changeset
41 行目で CloudFormation Guard でセキュリティグループのチェックを行っています。ルールセットの中身は以下になります。
let disallowed_ingress_rule =[{"CidrIp":"0.0.0.0/0","FromPort":22,"IpProtocol":"tcp","ToPort":22}]
AWS::EC2::SecurityGroup SecurityGroupIngress.* NOT_IN %disallowed_ingress_rule
SecurityGroup のプロパティ SecurityGroupIngress に、変数 disallowed_ingress_rule のルールが含まれないことを定義しています。
ルールセットの書き方の詳細については、こちらを参照ください。
3. GitHub Secrets を設定
GitHub リポジトリから Settings > Secrets を開き、以下のシークレットを登録します。
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
4. 実行
準備が完了しました。最終的なディレクトリ構成は以下となります。
.
├── README
├── .github
│ └── workflows
│ └── check_cfn_guard.yml
├── cfn-guard-rules
│ ├── network_ruleset
│ └── sg_ruleset
└── templates
├── network.yml
└── sg.yml
GitHub にプッシュします。
$ git add -A
$ git commit -m 'initial commit'
$ git push
GitHub Actions のワークフローを確認すると、cfn-guard でルールセットに準拠していないため、エラーが発生しています。
エラーの原因となっている、45 行目を以下の通り変更し、再度 GitHub にプッシュします。
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 22
ToPort: 22
CidrIp: "123.45.67.89/32"
ワークフローを確認すると、エラーが消えデプロイで来ていることを確認できました。
さいごに
CloudFormation Guard を GitHub Actions に組み込み、デプロイ前に自動でテンプレートがコンプライアンスに準拠していることをチェックさせてみました。
今回は行いませんでしたが、cfn-lint もワークフローに入れることで、より安全なデプロイができると思います。
本ブログで作成したリソースはこちらになります。