Sample React application for Trying to Use DevSecOps tools.
- AWS: ECS on FargateにCode Pipeline経由でデプロイする。サンプルではdevとprod環境を用意し、dev環境で動作確認後に承認ボタンを押すとprod環境のデプロイが進む形になっている。
- GitHub Actions
- application: create-react-appで作られるデフォルトそのまま。
See ./doc/tools_doc
- pre-commit,git-secret
- semgrep
- jest
- trivy(dependency check)
- trivy(image scan)
copilot cliを使って環境構築を行う。
- 名前は任意だが,自分は
react-app
とした - ここで必用なIAMロールの一部やKMSのキーやCodePipelineに使うS3やそのポリシーが作成されている。
copilot app init
cat ./copilot/.workspace
application: react-app
- amd64を指定しないとビルドエラーになった。TODO
- 名前は任意だが,dev-env,dev-svcとprod-env,prod-svcとした。
- TypeはLoad Balancerを選択した。
- 新しいVPCや,ECSのCluster,Load Balancerや権限周りが作成される。
DOCKER_DEFAULT_PLATFORM=linux/amd64 copilot init
Important
copilot/以下のファイルを編集することで設定を変更できる。
Note
production と development で共通の VPC を使う場合には以下のようにしてcopilot env init
単体で作成する。
共通のVPCを使う場合の詳細
copilot env init
Environment name: prod-env
Which credentials would you like to use to create hoge? [Use arrows to move, type to filter, ? for more help]
Enter temporary credentials
> [profile default]
Environment name: prod-env
Credential source: [profile default]
Would you like to use the default configuration for a new environment?
- A new VPC with 2 AZs, 2 public subnets and 2 private subnets
- A new ECS Cluster
- New IAM Roles to manage services and jobs in your environment
[Use arrows to move, type to filter]
Yes, use default.
Yes, but I'd like configure the default resources (CIDR ranges, AZs).
> No, I'd like to import existing resources (VPC, subnets).
Environment name: prod-env
Credential source: [profile default]
Default environment configuration? No, I'd like to import existing resources
Which VPC would you like to use? [Use arrows to move, type to filter]
> vpc-xxxxxxxxxxxxxxxxx (copilot-react-app-dev-env)
- environmentをデプロイ
copilot env deploy
- serviceをデプロイする。
[!WARNING] この際に間違えて dev-svc や dev-env を選ばないように注意する。
DOCKER_DEFAULT_PLATFORM=linux/amd64 copilot svc init # サービス作成済みなら実行しない。
copilot svc deploy
- ブラウザからアクセスできるか試してみる。
copilot svc show # urlが出てくるのでそこにアクセスする
- 名前は任意だが,自分はreact-app-pipelineとした。
copilot pipeline init
- manifest.ymlを編集してdevelopmentでサービス開始後にユーザが承認した後にproductionにデプロイされるようにする。
requires_approval: true
- 先に github に設定ファイルをアップロードしてから pipeline をデプロイする
git add .
git commit -m "add pipeline"
git push
copilot pipeline deploy
- ACTION REQUIRED が出るので URL にアクセスし,pending になっている pipeline と GitHub を接続する設定を追加する。
- 一度 pipeline をデプロイすると以後,指定した GitHub のブランチにマージされるたびに Code Pipeline を通してデプロイが進むようになる。
-
./copilot/pipelines/react-app-pipeline/buildspec.ymlを編集して trivy による image scan を追加する。
ECR のイメージスキャン機能はデフォルトであるので併用してもよいかも。
install:
commands:
- echo "install trivy"
- rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v0.48.0/trivy_0.48.0_Linux-64bit.rpm
# Run trivy scan on the docker images.
- trivy image --vuln-type os --no-progress --format table -o container-scanning-report.txt --severity CRITICAL,HIGH $(jq -r '.Parameters.ContainerImage' ./infrastructure/dev-svc-dev-env.params.json)
- cat container-scanning-report.txt
buildspec.ymlの解説
cat ./infrastructure/dev-svc-dev-env.params.json
{
"Parameters": {
"AddonsTemplateURL": "",
"AppName": "react-app",
"ContainerImage": "xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/react-app/dev-svc:xxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxxxx-dev-env",
}
}
jq -r '.Parameters.ContainerImage' ./infrastructure/dev-svc-dev-env.params.json
xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/react-app/dev-svc:xxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxxxx-dev-env
- ローカルでのセットアップが必用なのは git-secrets と pre-commit くらい
- pre-commit のドキュメント
- git-secrets のドキュメント
cd devsecops-demo-aws-ecs
pre-commit install
git secrets --install
git secrets --register-aws # awsのクレデンシャル検知ルールを登録
-
VSCodeのExtensionsもお好みで。Dockerのhadolintはおすすめ。
-
GitHub Actionsがスキャン結果のファイルをアップロードできるように権限をつける。詳細はsemgrepのyamlを参照。
- PREPARINGの設定を先にやる。
- commit時にはpre-commitとgit-secretが作動
- push時にはGitHub ActionsによりSAST(semgrep),UnitTest(jest),Dependency Check(trivy)が実行される。
- masterブランチにマージしたりmasterにpushした時にCodePipelineによってAWSへリポジトリがクローンされ,ビルド(image scanを含む),developmentへのデプロイが始まる。
- developmentで問題がなければCodePipeline上で承認し,productionへデプロイ
以下コマンドでログが見られる。ブラウザのAWS Code Deploy
copilot svc logs --previous
- ECS の仕様で非特権ユーザを使用したコンテナでは 80 番ポートが使えないっぽい --> つまり,localのdockerで80でサービスが起動できてもECSだと権限エラーになる。このため,コンテナで開放するportは8080としている(ALBに対して8080がマッピングされているためブラウザからは80でアクセスできる)。
toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit
- Docker Hubに短期間にアクセスしすぎているだけなので放置でOK
- DockerfileのRUNをヒアドキュメントで書いていたら怒られた(ローカルでは動いてたのに...)
# 修正前Dockerfile
RUN <<EOF
mkdir -p /var/log/nginx
chown -R nginx:nginx /var/log/nginx
touch /run/nginx.pid
chown -R nginx:nginx /run/nginx.pid
EOF
# 修正後
RUN mkdir -p /var/log/nginx \
&& chown -R nginx:nginx /var/log/nginx \
&& touch /run/nginx.pid \
&& chown -R nginx:nginx /run/nginx.pid
Resource handler returned message: "Error occurred during operation 'ECS Deployment Circuit Breaker was triggered'
コンテナが正常に起動していない。amd64を指定したら動いた。
DOCKER_DEFAULT_PLATFORM=linux/amd64 copilot deploy
copilot app show
はParameter Storeを見ているのでそこを消す。