diff --git a/.github/workflows/_build-and-push.yml b/.github/workflows/_build-and-push.yml index 5b17a750f6..41949f940f 100644 --- a/.github/workflows/_build-and-push.yml +++ b/.github/workflows/_build-and-push.yml @@ -250,7 +250,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab id: trivy_upload_sarif - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 if: | (steps.trivy_scan.outcome == 'success' || steps.trivy_scan.outcome == 'failure') && (inputs.specific_path == 'all' || inputs.specific_path == matrix.svc_prefix) diff --git a/.github/workflows/path-to-live.yml b/.github/workflows/path-to-live.yml index 51f0a2f0cb..3844876f53 100644 --- a/.github/workflows/path-to-live.yml +++ b/.github/workflows/path-to-live.yml @@ -28,14 +28,26 @@ jobs: workflow_variables: runs-on: ubuntu-latest name: output workflow variables + permissions: + contents: write outputs: short_sha: ${{ steps.variables.outputs.short_sha }} + semver_tag: ${{ steps.semver_tag.outputs.new_tag }} steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v3 - name: extract variables for workflow id: variables run: | echo "short_sha=$(echo ${GITHUB_SHA:0:7})" >> $GITHUB_OUTPUT + + - name: Bump version and push tag + uses: anothrNick/github-tag-action@1.64.0 + id: semver_tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WITH_V: true + INITIAL_VERSION: 1.0.0 + DEFAULT_BUMP: minor terraform_lint: name: lint terraform code @@ -66,7 +78,7 @@ jobs: - node_test - node_build with: - tag: main-${{ needs.workflow_variables.outputs.short_sha }} + tag: main-${{ needs.workflow_variables.outputs.semver_tag }} branch_name: main push_to_ecr: true specific_path: all @@ -133,7 +145,7 @@ jobs: with: workspace: preproduction terraform_path: environment - container_version: main-${{ needs.workflow_variables.outputs.short_sha }} + container_version: main-${{ needs.workflow_variables.outputs.semver_tag }} apply: true specific_path: all secrets: inherit @@ -175,7 +187,7 @@ jobs: with: workspace: production terraform_path: environment - container_version: main-${{ needs.workflow_variables.outputs.short_sha }} + container_version: main-${{ needs.workflow_variables.outputs.semver_tag }} apply: true specific_path: all extra_vars: "-var public_access_enabled=true" @@ -230,5 +242,5 @@ jobs: - name: workflow has ended without issue run: | echo "Deployment to production successful" - echo "Tag Used: main-${{ needs.workflow_variables.outputs.short_sha }}" + echo "Tag Used: main-${{ needs.workflow_variables.outputs.semver_tag }}" echo "URL: https://use-lasting-power-of-attorney.service.gov.uk" diff --git a/.github/workflows/psalm-static-analysis-api.yml b/.github/workflows/psalm-static-analysis-api.yml index 05279e2c0b..1158611704 100644 --- a/.github/workflows/psalm-static-analysis-api.yml +++ b/.github/workflows/psalm-static-analysis-api.yml @@ -46,6 +46,6 @@ jobs: run: psalm --output-format=github --taint-analysis --report=results.sarif - name: Upload Security Analysis results to GitHub - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ./${{ env.SERVICE-PATH }}/app/results.sarif diff --git a/.github/workflows/psalm-static-analysis-front.yml b/.github/workflows/psalm-static-analysis-front.yml index c15d55e589..d4e3381f6b 100644 --- a/.github/workflows/psalm-static-analysis-front.yml +++ b/.github/workflows/psalm-static-analysis-front.yml @@ -46,6 +46,6 @@ jobs: run: psalm --output-format=github --taint-analysis --report=results.sarif - name: Upload Security Analysis results to GitHub - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ./${{ env.SERVICE-PATH }}/app/results.sarif diff --git a/.github/workflows/scheduled-codeql-analysis.yml b/.github/workflows/scheduled-codeql-analysis.yml index 5e43fedd29..b226d3ee30 100644 --- a/.github/workflows/scheduled-codeql-analysis.yml +++ b/.github/workflows/scheduled-codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -45,7 +45,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -59,4 +59,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/scheduled-tfsec.yml b/.github/workflows/scheduled-tfsec.yml index b95460c49f..4dfd037cc0 100644 --- a/.github/workflows/scheduled-tfsec.yml +++ b/.github/workflows/scheduled-tfsec.yml @@ -23,7 +23,7 @@ jobs: sarif_file: tfsec.sarif - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: # Path to SARIF file relative to the root of the repository sarif_file: tfsec.sarif diff --git a/docker-compose.dependencies.yml b/docker-compose.dependencies.yml index b5cf3b9633..499fc5b992 100644 --- a/docker-compose.dependencies.yml +++ b/docker-compose.dependencies.yml @@ -104,11 +104,11 @@ services: mock-one-login: container_name: mock-one-login - image: 311462405659.dkr.ecr.eu-west-1.amazonaws.com/use_an_lpa/mock_onelogin_app:v0.46.0 + image: 311462405659.dkr.ecr.eu-west-1.amazonaws.com/use_an_lpa/mock_onelogin_app:v0.58.0 ports: - "4013:8080" environment: PUBLIC_URL: http://localhost:4013 INTERNAL_URL: http://mock-one-login:8080 - REDIRECT_URL: http://localhost:9002/home/login + REDIRECT_URL: https://localhost:9042/home/login CLIENT_ID: client-id diff --git a/terraform/account/kms.tf b/terraform/account/kms.tf index 374e332e29..0ba2191fa6 100644 --- a/terraform/account/kms.tf +++ b/terraform/account/kms.tf @@ -94,6 +94,7 @@ data "aws_iam_policy_document" "cloudwatch_kms" { type = "Service" identifiers = [ "logs.${data.aws_region.current.name}.amazonaws.com", + "logs.eu-west-2.amazonaws.com", "events.amazonaws.com" ] } diff --git a/terraform/account/region.tf b/terraform/account/region.tf index a95ec3a635..a9e2c7e6a9 100644 --- a/terraform/account/region.tf +++ b/terraform/account/region.tf @@ -13,7 +13,28 @@ module "eu_west_1" { providers = { aws.region = aws.eu_west_1 - aws.management = aws.management + aws.management = aws.management_eu_west_1 aws.shared = aws.shared } } + +module "eu_west_2" { + count = local.environment == "development" ? 1 : 0 + source = "./region" + + account = local.account + account_name = local.account_name + environment_name = local.environment + lambda_container_version = var.lambda_container_version + vpc_flow_logs_iam_role = aws_iam_role.vpc_flow_logs + + depends_on = [ + module.cloudwatch_mrk, + ] + + providers = { + aws.region = aws.eu_west_2 + aws.management = aws.management_eu_west_2 + aws.shared = aws.shared + } +} \ No newline at end of file diff --git a/terraform/account/region/cloudwatch_alarms.tf b/terraform/account/region/cloudwatch_alarms.tf index 9ac4801ada..f5bc9e1e3d 100644 --- a/terraform/account/region/cloudwatch_alarms.tf +++ b/terraform/account/region/cloudwatch_alarms.tf @@ -18,6 +18,8 @@ resource "aws_cloudwatch_metric_alarm" "elasticache_high_cpu_utilization" { dimensions = { CacheClusterId = each.value } + + provider = aws.region } resource "aws_cloudwatch_metric_alarm" "elasticache_high_swap_utilization" { @@ -40,4 +42,6 @@ resource "aws_cloudwatch_metric_alarm" "elasticache_high_swap_utilization" { dimensions = { CacheClusterId = each.value } + + provider = aws.region } diff --git a/terraform/account/region/lambda_functions.tf b/terraform/account/region/lambda_functions.tf index 1044c490bd..fc20c078c8 100644 --- a/terraform/account/region/lambda_functions.tf +++ b/terraform/account/region/lambda_functions.tf @@ -11,7 +11,7 @@ data "aws_ecr_repository" "ship_to_opg_metrics" { module "clsf_to_sqs" { source = "./modules/lambda_function" count = var.account.opg_metrics.enabled ? 1 : 0 - lambda_name = "clsf-to-sqs" + lambda_name = "clsf-to-sqs-${data.aws_region.current.name}" description = "Function to take Cloudwatch Logs Subscription Filters and send them to SQS" working_directory = "/var/task" environment_variables = { @@ -25,6 +25,10 @@ module "clsf_to_sqs" { ecr_arn = data.aws_ecr_repository.clsf_to_sqs.arn lambda_role_policy_document = data.aws_iam_policy_document.clsf_to_sqs_lambda_function_policy[0].json aws_cloudwatch_log_group_kms_key_id = data.aws_kms_alias.cloudwatch_mrk.arn + + providers = { + aws = aws.region + } } data "aws_iam_policy_document" "clsf_to_sqs_lambda_function_policy" { @@ -62,7 +66,7 @@ data "aws_kms_alias" "opg_metrics_api_key_encryption" { module "ship_to_opg_metrics" { source = "./modules/lambda_function" count = var.account.opg_metrics.enabled ? 1 : 0 - lambda_name = "ship-to-opg-metrics" + lambda_name = "ship-to-opg-metrics-${data.aws_region.current.name}" description = "Function to take metrics from SQS and PUT them to OPG Metrics" working_directory = "/var/task" environment_variables = { @@ -73,6 +77,10 @@ module "ship_to_opg_metrics" { ecr_arn = data.aws_ecr_repository.ship_to_opg_metrics.arn lambda_role_policy_document = data.aws_iam_policy_document.ship_to_opg_metrics_lambda_function_policy[0].json aws_cloudwatch_log_group_kms_key_id = data.aws_kms_alias.cloudwatch_mrk.arn + + providers = { + aws = aws.region + } } data "aws_iam_policy_document" "ship_to_opg_metrics_lambda_function_policy" { diff --git a/terraform/account/region/modules/lambda_function/terraform.tf b/terraform/account/region/modules/lambda_function/terraform.tf new file mode 100644 index 0000000000..db0045574e --- /dev/null +++ b/terraform/account/region/modules/lambda_function/terraform.tf @@ -0,0 +1,13 @@ +terraform { + required_version = "<= 1.6.3" + + required_providers { + aws = { + source = "hashicorp/aws" + } + pagerduty = { + source = "PagerDuty/pagerduty" + version = ">= 2.16.0" + } + } +} diff --git a/terraform/account/region/modules/s3_bucket/main.tf b/terraform/account/region/modules/s3_bucket/main.tf index 48b63f6f33..150d05077a 100644 --- a/terraform/account/region/modules/s3_bucket/main.tf +++ b/terraform/account/region/modules/s3_bucket/main.tf @@ -6,6 +6,8 @@ resource "aws_s3_bucket" "bucket" { resource "aws_s3_bucket_acl" "bucket_acl" { bucket = aws_s3_bucket.bucket.id acl = var.acl + + depends_on = [aws_s3_bucket_ownership_controls.main] } resource "aws_s3_bucket_versioning" "bucket_versioning" { @@ -71,6 +73,14 @@ resource "aws_s3_bucket_logging" "bucket" { target_prefix = "log/${aws_s3_bucket.bucket.id}/" } +resource "aws_s3_bucket_ownership_controls" "main" { + bucket = aws_s3_bucket.bucket.id + + rule { + object_ownership = var.object_ownership + } +} + data "aws_iam_policy_document" "bucket" { policy_id = "PutObjPolicy" @@ -113,3 +123,4 @@ data "aws_iam_policy_document" "bucket" { } } } + diff --git a/terraform/account/region/modules/s3_bucket/variables.tf b/terraform/account/region/modules/s3_bucket/variables.tf index a1151b9f7a..9330662978 100644 --- a/terraform/account/region/modules/s3_bucket/variables.tf +++ b/terraform/account/region/modules/s3_bucket/variables.tf @@ -56,6 +56,16 @@ variable "versioning_enabled" { default = false } +variable "object_ownership" { + description = "The object ownership setting. Valid values are BucketOwnerPreferred and ObjectWriter." + default = "ObjectWriter" + + validation { + condition = can(regex("BucketOwnerPreferred|ObjectWriter", var.object_ownership)) + error_message = "object_ownership must be either BucketOwnerPreferred or ObjectWriter" + } +} + locals { environment = split("_", terraform.workspace)[0] } diff --git a/terraform/account/region/s3_lb_access_logs.tf b/terraform/account/region/s3_lb_access_logs.tf index 02349c5192..efcb498c68 100644 --- a/terraform/account/region/s3_lb_access_logs.tf +++ b/terraform/account/region/s3_lb_access_logs.tf @@ -1,13 +1,31 @@ -resource "aws_s3_bucket" "access_log" { +# Old version of the access log bucket. The new version is suffixed with the region name. We're keeping this around for a while to ensure we don't lose any logs. +resource "aws_s3_bucket" "old_access_log" { + count = data.aws_region.current.name == "eu-west-1" ? 1 : 0 bucket = "opg-ual-${var.environment_name}-lb-access-logs" provider = aws.region } +resource "null_resource" "old_access_log" { + provisioner "local-exec" { + command = "echo 'Reading ${data.aws_region.current.name}'" + } +} + +resource "aws_s3_bucket" "access_log" { + bucket = "opg-ual-${var.environment_name}-lb-access-logs-${data.aws_region.current.name}" + + provider = aws.region +} + resource "aws_s3_bucket_acl" "access_log" { bucket = aws_s3_bucket.access_log.id acl = "private" + depends_on = [ + aws_s3_bucket_ownership_controls.access_log + ] + provider = aws.region } @@ -126,6 +144,16 @@ data "aws_iam_policy_document" "access_log" { } } +resource "aws_s3_bucket_ownership_controls" "access_log" { + bucket = aws_s3_bucket.access_log.id + + rule { + object_ownership = "ObjectWriter" + } + + provider = aws.region +} + resource "aws_s3_bucket_public_access_block" "access_log" { bucket = aws_s3_bucket.access_log.id block_public_acls = true diff --git a/terraform/account/region/vpc_flowlogs.tf b/terraform/account/region/vpc_flowlogs.tf index 50421e765d..8997a937ff 100644 --- a/terraform/account/region/vpc_flowlogs.tf +++ b/terraform/account/region/vpc_flowlogs.tf @@ -8,6 +8,17 @@ resource "aws_flow_log" "vpc_flow_logs" { } resource "aws_cloudwatch_log_group" "vpc_flow_logs" { + name = "vpc_flow_logs-${data.aws_region.current.name}" + retention_in_days = 400 + kms_key_id = data.aws_kms_alias.cloudwatch_mrk.arn + + provider = aws.region +} + +# Kept around to avoid losing logs after switching to region-specific flow logs group. +# This can be deleted 400 days after the creation of aws_cloudwatch_log_group.vpc_flow_logs. +resource "aws_cloudwatch_log_group" "old_vpc_flow_logs" { + count = data.aws_region.current.name == "eu-west-1" ? 1 : 0 name = "vpc_flow_logs" retention_in_days = 400 kms_key_id = data.aws_kms_alias.cloudwatch_mrk.arn diff --git a/terraform/account/terraform.tf b/terraform/account/terraform.tf index be3b9d9ea3..2b6757ed4f 100644 --- a/terraform/account/terraform.tf +++ b/terraform/account/terraform.tf @@ -88,6 +88,34 @@ provider "aws" { } } +provider "aws" { + region = "eu-west-2" + alias = "management_eu_west_2" + default_tags { + tags = local.default_tags + } + + assume_role { + role_arn = "arn:aws:iam::311462405659:role/${var.default_role}" + session_name = "terraform-session" + } +} + +provider "aws" { + region = "eu-west-1" + alias = "management_eu_west_1" + default_tags { + tags = local.default_tags + } + + assume_role { + role_arn = "arn:aws:iam::311462405659:role/${var.default_role}" + session_name = "terraform-session" + } +} + + + provider "aws" { region = "eu-west-1" alias = "shared" diff --git a/terraform/environment/region/data_sources.tf b/terraform/environment/region/data_sources.tf index 0c9d224d42..cd6419eef8 100644 --- a/terraform/environment/region/data_sources.tf +++ b/terraform/environment/region/data_sources.tf @@ -153,7 +153,7 @@ data "aws_iam_role" "ecs_autoscaling_service_role" { } data "aws_s3_bucket" "access_log" { - bucket = "opg-ual-${var.account_name}-lb-access-logs" + bucket = "opg-ual-${var.account_name}-lb-access-logs-${data.aws_region.current.name}" provider = aws.region } diff --git a/terraform/environment/region/ship_to_metrics.tf b/terraform/environment/region/ship_to_metrics.tf index f34861969b..dec38c9f91 100644 --- a/terraform/environment/region/ship_to_metrics.tf +++ b/terraform/environment/region/ship_to_metrics.tf @@ -5,12 +5,12 @@ data "aws_sqs_queue" "ship_to_opg_metrics" { data "aws_lambda_function" "clsf_to_sqs" { count = var.ship_metrics_queue_enabled ? 1 : 0 - function_name = "clsf-to-sqs" + function_name = "clsf-to-sqs-${data.aws_region.current.name}" } data "aws_lambda_function" "ship_to_opg_metrics" { count = var.ship_metrics_queue_enabled ? 1 : 0 - function_name = "ship-to-opg-metrics" + function_name = "ship-to-opg-metrics-${data.aws_region.current.name}" } resource "aws_cloudwatch_log_subscription_filter" "events" {