-
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
Changes from 8 commits
3b31922
272126c
f4e1924
2153103
d55e992
0fda371
358dc0c
3ad3203
5fb9a3b
41eca6d
2c460bd
afa6788
a94fdc0
a383695
c9d8c2a
4e61c0c
cbba2ef
9bf8b61
a78a48f
547344d
6e8cba3
59b54ca
f888da3
05685f5
de5413b
339738e
a632668
153f63c
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 |
|---|---|---|
|
|
@@ -46,15 +46,17 @@ jobs: | |
| echo "name=$TAG" >> $GITHUB_OUTPUT | ||
| echo "Resolved tag: $TAG" | ||
|
|
||
| deploy: | ||
| name: "Deploy to TEST (approval required)" | ||
| sign-lambda-artifact: | ||
| name: "Sign lambda artifact for TEST" | ||
| runs-on: ubuntu-latest | ||
| needs: [metadata] | ||
| environment: test | ||
| timeout-minutes: 10080 | ||
| 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 | ||
|
|
@@ -80,6 +82,124 @@ jobs: | |
| 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 | ||
|
|
||
| - 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" | ||
| 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 | ||
|
|
@@ -92,7 +212,6 @@ jobs: | |
| 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 }}" | ||
|
|
@@ -109,17 +228,10 @@ jobs: | |
| pip install boto3 | ||
| python scripts/feature_toggle/validate_toggles.py | ||
|
|
||
| - name: "Extract S3 bucket name from Terraform output" | ||
| id: tf_output | ||
| run: | | ||
| BUCKET=$(terraform output -raw lambda_artifact_bucket) | ||
| echo "bucket_name=$BUCKET" >> $GITHUB_OUTPUT | ||
| working-directory: ./infrastructure/stacks/api-layer | ||
|
|
||
| - name: "Upload lambda artifact to S3" | ||
| - name: "Upload signed lambda artifact to S3" | ||
| run: | | ||
| aws s3 cp ./dist/lambda.zip \ | ||
| s3://${{ steps.tf_output.outputs.bucket_name }}/artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip \ | ||
| s3://${{ needs.sign-lambda-artifact.outputs.bucket_name }}/artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip \ | ||
| --region eu-west-2 | ||
|
|
||
| regression-tests: | ||
|
|
@@ -130,4 +242,3 @@ jobs: | |
| ENVIRONMENT: "test" | ||
| VERSION_NUMBER: "main" | ||
| secrets: inherit | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| locals { | ||
| enable_lambda_code_signing = contains(["test", "preprod", "prod"], var.environment) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| resource "aws_signer_signing_profile" "lambda_signing" { | ||
| name = "eligibilityapi${var.environment}lambdasigningprofile" | ||
|
TOEL2 marked this conversation as resolved.
Outdated
|
||
| #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.
|
||
| 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 |
|---|---|---|
|
|
@@ -709,6 +709,60 @@ resource "aws_iam_policy" "kinesis_management" { | |
| tags = merge(local.tags, { Name = "kinesis-management" }) | ||
| } | ||
|
|
||
| resource "aws_iam_policy" "code_signing_management" { | ||
| 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 = "arn:aws:lambda:*:${data.aws_caller_identity.current.account_id}:function:eligibility_signposting_api:*", | ||
|
TOEL2 marked this conversation as resolved.
Outdated
|
||
| }, | ||
| { | ||
| 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.
Outdated
|
||
| { | ||
| 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.
Outdated
|
||
| ] | ||
| }) | ||
|
|
||
| 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 | ||
|
|
@@ -828,3 +882,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.
The signing job depends on a signing profile that must already exist in the target AWS account/state before this workflow runs (it only does
init+output). On the first deployment after introducing these Terraform resources, the profile/config won’t exist yet, so signing will fail before the workflow reaches the laterterraform applythat would create them. Consider a bootstrap path (e.g. create signing profile/config in a prior step/stack, or handle missing outputs by applying the signing resources first).