-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/eli 702 code signing #617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3b31922
272126c
f4e1924
2153103
d55e992
0fda371
358dc0c
3ad3203
5fb9a3b
41eca6d
2c460bd
afa6788
a94fdc0
a383695
c9d8c2a
4e61c0c
cbba2ef
9bf8b61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,244 @@ | ||
| name: "Updated - 3. CD | Deploy to Test" | ||
|
|
||
| #on: | ||
| # workflow_run: | ||
| # workflows: ["2. CD | Deploy to Dev"] | ||
| # types: [completed] | ||
|
|
||
| concurrency: | ||
| group: test-deployments | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| actions: read | ||
|
|
||
| jobs: | ||
| metadata: | ||
| name: "Resolve metadata from triggering run" | ||
| runs-on: ubuntu-latest | ||
| if: ${{ github.event.workflow_run.conclusion == 'success' }} | ||
| outputs: | ||
| terraform_version: ${{ steps.vars.outputs.terraform_version }} | ||
| tag: ${{ steps.tag.outputs.name }} | ||
| steps: | ||
| - name: "Checkout exact commit from CI/CD publish" | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ github.event.workflow_run.head_sha }} | ||
|
|
||
| - name: "Set CI/CD variables" | ||
| id: vars | ||
| run: | | ||
| echo "terraform_version=$(grep '^terraform' .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Resolve the dev-* tag for this commit" | ||
| id: tag | ||
| run: | | ||
| git fetch --tags --force | ||
| SHA="${{ github.event.workflow_run.head_sha }}" | ||
| TAG=$(git tag --points-at "$SHA" | grep '^dev-' | sort -r | head -n1 || true) | ||
| if [ -z "$TAG" ]; then | ||
| echo "No dev-* tag found on $SHA" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "name=$TAG" >> $GITHUB_OUTPUT | ||
| echo "Resolved tag: $TAG" | ||
|
|
||
| sign-lambda-artifact: | ||
| name: "Sign lambda artifact for TEST" | ||
| runs-on: ubuntu-latest | ||
| needs: [metadata] | ||
| environment: test | ||
| timeout-minutes: 45 | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| outputs: | ||
| bucket_name: ${{ steps.tf_output.outputs.bucket_name }} | ||
| steps: | ||
| - name: "Checkout same commit" | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ github.event.workflow_run.head_sha }} | ||
|
|
||
| - name: "Setup Terraform" | ||
| uses: hashicorp/setup-terraform@v3 | ||
| with: | ||
| terraform_version: ${{ needs.metadata.outputs.terraform_version }} | ||
|
|
||
| - name: "Configure AWS Credentials" | ||
| uses: aws-actions/configure-aws-credentials@v6 | ||
| with: | ||
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role | ||
| aws-region: eu-west-2 | ||
|
|
||
| - name: "Download lambda artefact from dev workflow" | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: lambda-${{ needs.metadata.outputs.tag }} | ||
| path: ./dist | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ github.token }} | ||
|
|
||
| - name: "Terraform Init (TEST api-layer)" | ||
| env: | ||
| ENVIRONMENT: test | ||
| WORKSPACE: "default" | ||
| run: | | ||
| echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=init" | ||
| make terraform env=$ENVIRONMENT stack=api-layer tf-command=init workspace=$WORKSPACE | ||
| working-directory: ./infrastructure | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not clear on intention here - no apply step, which would presumably block, unless the intention is to run the other workflow, to deploy the codesigning infra, then ths to test it out / turn it on?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its to get the outputs from the init so that we can use them in the signing step below (120)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then once we have a signed lambda we can do the next job in this workflow - deploy which does the tf apply |
||
| - name: "Extract S3 bucket name from Terraform output" | ||
| id: tf_output | ||
| run: | | ||
| BUCKET=$(terraform output -raw lambda_artifact_bucket) | ||
| PROFILE=$(terraform output -raw lambda_signing_profile_name) | ||
| echo "bucket_name=$BUCKET" >> $GITHUB_OUTPUT | ||
| echo "signing_profile_name=$PROFILE" >> $GITHUB_OUTPUT | ||
| working-directory: ./infrastructure/stacks/api-layer | ||
|
|
||
| - name: "Upload unsigned lambda artifact to S3" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering now whether each environment will need to sign, as we're using separate accounts (so Prod has no reason to trust PreProd etc....) |
||
| run: | | ||
| aws s3 cp ./dist/lambda.zip \ | ||
| s3://${{ steps.tf_output.outputs.bucket_name }}/unsigned/${{ needs.metadata.outputs.tag }}/lambda.zip \ | ||
| --region eu-west-2 | ||
|
|
||
| - name: "Get uploaded source object version" | ||
| id: source_object | ||
| run: | | ||
| VERSION_ID=$(aws s3api head-object \ | ||
| --bucket "${{ steps.tf_output.outputs.bucket_name }}" \ | ||
| --key "unsigned/${{ needs.metadata.outputs.tag }}/lambda.zip" \ | ||
| --query 'VersionId' \ | ||
| --output text \ | ||
| --region eu-west-2) | ||
| echo "version_id=$VERSION_ID" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Start signing job" | ||
| id: signing | ||
| env: | ||
| SIGNING_PROFILE_NAME: ${{ steps.tf_output.outputs.signing_profile_name }} | ||
| run: | | ||
| JOB_ID=$(aws signer start-signing-job \ | ||
| --source "s3={bucketName=${{ steps.tf_output.outputs.bucket_name }},key=unsigned/${{ needs.metadata.outputs.tag }}/lambda.zip,version=${{ steps.source_object.outputs.version_id }}}" \ | ||
| --destination "s3={bucketName=${{ steps.tf_output.outputs.bucket_name }},prefix=signed/${{ needs.metadata.outputs.tag }}/}" \ | ||
| --profile-name "$SIGNING_PROFILE_NAME" \ | ||
| --query 'jobId' \ | ||
| --output text \ | ||
| --region eu-west-2) | ||
| echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Wait for signing job" | ||
| run: | | ||
| aws signer wait successful-signing-job \ | ||
| --job-id "${{ steps.signing.outputs.job_id }}" \ | ||
| --region eu-west-2 | ||
|
|
||
| - name: "Resolve signed artifact location" | ||
| id: signed_object | ||
| run: | | ||
| SIGNED_BUCKET=$(aws signer describe-signing-job \ | ||
| --job-id "${{ steps.signing.outputs.job_id }}" \ | ||
| --region eu-west-2 \ | ||
| --query 'signedObject.s3.bucketName' \ | ||
| --output text) | ||
|
|
||
| SIGNED_KEY=$(aws signer describe-signing-job \ | ||
| --job-id "${{ steps.signing.outputs.job_id }}" \ | ||
| --region eu-west-2 \ | ||
| --query 'signedObject.s3.key' \ | ||
| --output text) | ||
|
|
||
| echo "bucket_name=$SIGNED_BUCKET" >> $GITHUB_OUTPUT | ||
| echo "object_key=$SIGNED_KEY" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: "Download signed lambda artifact" | ||
| run: | | ||
| aws s3 cp \ | ||
| "s3://${{ steps.signed_object.outputs.bucket_name }}/${{ steps.signed_object.outputs.object_key }}" \ | ||
| ./dist/lambda.zip \ | ||
| --region eu-west-2 | ||
|
|
||
| - name: "Upload signed lambda artifact for current workflow" | ||
| uses: actions/upload-artifact@v6 | ||
| with: | ||
| name: lambda-${{ needs.metadata.outputs.tag }} | ||
| path: ./dist/lambda.zip | ||
|
|
||
| deploy: | ||
| name: "Deploy to TEST (approval required)" | ||
| runs-on: ubuntu-latest | ||
| needs: [metadata, sign-lambda-artifact] | ||
| environment: test | ||
| timeout-minutes: 10080 | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| steps: | ||
| - name: "Checkout same commit" | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ github.event.workflow_run.head_sha }} | ||
|
|
||
| - name: "Setup Terraform" | ||
| uses: hashicorp/setup-terraform@v3 | ||
| with: | ||
| terraform_version: ${{ needs.metadata.outputs.terraform_version }} | ||
|
|
||
| - name: "Download signed lambda artefact" | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: lambda-${{ needs.metadata.outputs.tag }} | ||
| path: ./dist | ||
|
|
||
| - name: "Configure AWS Credentials" | ||
| uses: aws-actions/configure-aws-credentials@v6 | ||
| with: | ||
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role | ||
| aws-region: eu-west-2 | ||
|
|
||
| - name: "Terraform Apply (TEST)" | ||
| env: | ||
| ENVIRONMENT: test | ||
| WORKSPACE: "default" | ||
| TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }} | ||
| TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }} | ||
| TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }} | ||
| TF_VAR_SPLUNK_HEC_TOKEN: ${{ secrets.SPLUNK_HEC_TOKEN }} | ||
| TF_VAR_SPLUNK_HEC_ENDPOINT: ${{ secrets.SPLUNK_HEC_ENDPOINT }} | ||
| TF_VAR_OPERATOR_EMAILS: ${{ vars.SECRET_ROTATION_OPERATOR_EMAILS }} | ||
| TF_VAR_PROXYGEN_PRIVATE_KEY_PTL: ${{ secrets.PROXYGEN_PRIVATE_KEY_PTL }} | ||
| TF_VAR_PROXYGEN_PRIVATE_KEY_PROD: ${{ secrets.PROXYGEN_PRIVATE_KEY_PROD }} | ||
| run: | | ||
| mkdir -p ./build | ||
| echo "Deploying tag: ${{ needs.metadata.outputs.tag }}" | ||
| echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=networking tf-command=apply" | ||
| make terraform env=$ENVIRONMENT stack=networking tf-command=apply workspace=$WORKSPACE | ||
| echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=apply" | ||
| make terraform env=$ENVIRONMENT stack=api-layer tf-command=apply workspace=$WORKSPACE | ||
| working-directory: ./infrastructure | ||
|
|
||
| - name: "Validate Feature Toggles" | ||
| env: | ||
| ENV: test | ||
| run: | | ||
| pip install boto3 | ||
| python scripts/feature_toggle/validate_toggles.py | ||
|
|
||
| - name: "Upload signed lambda artifact to S3" | ||
| run: | | ||
| aws s3 cp ./dist/lambda.zip \ | ||
| s3://${{ needs.sign-lambda-artifact.outputs.bucket_name }}/artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip \ | ||
| --region eu-west-2 | ||
|
|
||
| regression-tests: | ||
| name: "Regression Tests" | ||
| needs: deploy | ||
| uses: ./.github/workflows/regression-tests.yml | ||
| with: | ||
| ENVIRONMENT: "test" | ||
| VERSION_NUMBER: "main" | ||
| secrets: inherit | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,4 +130,3 @@ jobs: | |
| ENVIRONMENT: "test" | ||
| VERSION_NUMBER: "main" | ||
| secrets: inherit | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| locals { | ||
| enable_lambda_code_signing = false | ||
| # enable_lambda_code_signing = contains(["test", "preprod", "prod"], var.environment) | ||
| # For the next deployment ^ | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| resource "aws_signer_signing_profile" "lambda_signing" { | ||
| name = "${terraform.workspace == "default" ? "" : "${terraform.workspace}"}EligibilityApiLambdaSigningProfile" | ||
| #aws signer is strict with names, does not like hyphens or underscores | ||
|
|
||
| platform_id = "AWSLambda-SHA384-ECDSA" | ||
|
|
||
| signature_validity_period { | ||
| value = 365 | ||
| type = "DAYS" | ||
| } | ||
| } | ||
|
|
||
| resource "aws_lambda_code_signing_config" "signing_config" { | ||
| allowed_publishers { | ||
| signing_profile_version_arns = [ | ||
| aws_signer_signing_profile.lambda_signing.version_arn | ||
| ] | ||
| } | ||
|
|
||
| policies { | ||
| untrusted_artifact_on_deployment = "Enforce" | ||
| } | ||
|
|
||
TOEL2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| description = "Only allow Lambda bundles signed by our trusted signer profile" | ||
| } | ||
|
|
||
| output "lambda_signing_profile_name" { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might need to be added to outputs.tf in the module, then potentially re-output in the stack (in infrastructure/stacks/api-layer/lambda.tf ?) |
||
| value = aws_signer_signing_profile.lambda_signing.name | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -740,6 +740,63 @@ resource "aws_iam_policy" "kinesis_management" { | |
| tags = merge(local.tags, { Name = "kinesis-management" }) | ||
| } | ||
|
|
||
| resource "aws_iam_policy" "code_signing_management" { | ||
| #checkov:skip=CKV_AWS_290: Actions require wildcard resource for Lambda code signing configs and Signer jobs | ||
| #checkov:skip=CKV_AWS_235: Actions require wildcard resource for Lambda code signing configs and Signer jobs | ||
| #checkov:skip=CKV_AWS_355: Actions require wildcard resource for Lambda code signing configs and Signer jobs | ||
| name = "code-signing-management" | ||
| description = "Allow GitHub Actions to manage Lambda code signing and start Signer jobs" | ||
| path = "/service-policies/" | ||
|
|
||
| policy = jsonencode({ | ||
| Version = "2012-10-17", | ||
| Statement = [ | ||
| { | ||
| Sid = "LambdaCodeSigningConfigManagement", | ||
| Effect = "Allow", | ||
| Action = [ | ||
| "lambda:CreateCodeSigningConfig", | ||
| "lambda:UpdateCodeSigningConfig", | ||
| "lambda:DeleteCodeSigningConfig", | ||
| "lambda:GetCodeSigningConfig", | ||
| "lambda:ListCodeSigningConfigs", | ||
| "lambda:GetFunctionCodeSigningConfig", | ||
| "lambda:ListTags", | ||
| "lambda:DeleteFunctionCodeSigningConfig", | ||
| "lambda:PutFunctionCodeSigningConfig" | ||
| ], | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| Sid = "SignerJobUsage", | ||
| Effect = "Allow", | ||
| Action = [ | ||
| "signer:StartSigningJob", | ||
| "signer:DescribeSigningJob" | ||
| ], | ||
| Resource = "arn:aws:signer:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:/signing-jobs/*" | ||
| }, | ||
TOEL2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| Sid = "SignerProfileManagement", | ||
| Effect = "Allow", | ||
| Action = [ | ||
| "signer:PutSigningProfile", | ||
| "signer:GetSigningProfile", | ||
| "signer:ListSigningProfiles", | ||
| "signer:ListTagsForResource", | ||
| "signer:TagResource", | ||
| "signer:UntagResource", | ||
| "signer:CancelSigningProfile", | ||
| "signer:RevokeSignature" | ||
| ], | ||
| Resource = "arn:aws:signer:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:/signing-profiles/eligibility-signposting-api-*" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The resource gets a name like 'EligibilityApiLambdaSigningProfile' e.g. line 2 of signing.tf: "${terraform.workspace}"}EligibilityApiLambdaSigningProfile"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah oversight there, changed it in one place and not the other - will fix |
||
| } | ||
TOEL2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ] | ||
| }) | ||
|
|
||
| tags = merge(local.tags, { Name = "code-signing-management" }) | ||
| } | ||
|
||
|
|
||
| resource "aws_iam_policy" "cloudwatch_management" { | ||
| #checkov:skip=CKV_AWS_355: GetMetricWidgetImage requires wildcard resource | ||
| #checkov:skip=CKV_AWS_290: GetMetricWidgetImage requires wildcard resource | ||
|
|
@@ -859,3 +916,8 @@ resource "aws_iam_role_policy_attachment" "kinesis_management_attach" { | |
| role = aws_iam_role.github_actions.name | ||
| policy_arn = aws_iam_policy.kinesis_management.arn | ||
| } | ||
|
|
||
| resource "aws_iam_role_policy_attachment" "code_signing_management" { | ||
| role = aws_iam_role.github_actions.name | ||
| policy_arn = aws_iam_policy.code_signing_management.arn | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise (to below comment), this should still have the iams roles deployment