From 6068658707bd7a462756d088e9e1f306251442a7 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 11:36:36 +0100
Subject: [PATCH 01/15] can we enable docker content trust?

---
 .github/workflows/docker_job.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/docker_job.yml b/.github/workflows/docker_job.yml
index d479ba7d42..6df152a76c 100644
--- a/.github/workflows/docker_job.yml
+++ b/.github/workflows/docker_job.yml
@@ -127,6 +127,7 @@ jobs:
             TAG=${{inputs.tag}}
         env:
           SOURCE_DATE_EPOCH: 0
+          DOCKER_CONTENT_TRUST: 1
 
       - name: Trivy Image Vulnerability Scanner for ${{ matrix.ecr_repository }}
         id: trivy_scan

From 8c21c264fb4153d8088bfc2f38c7746d94e37fc8 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 11:41:48 +0100
Subject: [PATCH 02/15] turn off rest of workflow

---
 .github/workflows/workflow_pr.yml | 281 +++++++++++++++---------------
 1 file changed, 141 insertions(+), 140 deletions(-)

diff --git a/.github/workflows/workflow_pr.yml b/.github/workflows/workflow_pr.yml
index fd27f3930c..d94f82de70 100644
--- a/.github/workflows/workflow_pr.yml
+++ b/.github/workflows/workflow_pr.yml
@@ -34,154 +34,155 @@ jobs:
     with:
       changes_detected: ${{ needs.detect_changes.outputs.changes_detected }}
 
-  go_unit_tests:
-    name: Run Go unit tests
-    if: needs.detect_changes.outputs.changes_detected == 'true'
-    needs: create_tags
-    uses: ./.github/workflows/go-unit-tests.yml
-    with:
-      tag: ${{ needs.create_tags.outputs.version_tag }}
-      commit_sha: ${{ github.event.pull_request.head.sha }}
-      branch: ${{ github.head_ref }}
-    secrets:
-      pact_broker_password: ${{ secrets.PACT_BROKER_PASSWORD }}
-      codecov_token: ${{ secrets.CODECOV_TOKEN }}
+  # go_unit_tests:
+  #   name: Run Go unit tests
+  #   if: needs.detect_changes.outputs.changes_detected == 'true'
+  #   needs: create_tags
+  #   uses: ./.github/workflows/go-unit-tests.yml
+  #   with:
+  #     tag: ${{ needs.create_tags.outputs.version_tag }}
+  #     commit_sha: ${{ github.event.pull_request.head.sha }}
+  #     branch: ${{ github.head_ref }}
+  #   secrets:
+  #     pact_broker_password: ${{ secrets.PACT_BROKER_PASSWORD }}
+  #     codecov_token: ${{ secrets.CODECOV_TOKEN }}
 
   docker_build_scan_push:
     name: Docker Build, Scan and Push
-    if: needs.detect_changes.outputs.changes_detected == 'true' &&
-      (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped')
+    if: always()
+    # if: needs.detect_changes.outputs.changes_detected == 'true' &&
+    #   (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped')
     uses: ./.github/workflows/docker_job.yml
     needs: [
-      go_unit_tests,
+      # go_unit_tests,
       create_tags
       ]
     with:
       tag: ${{ needs.create_tags.outputs.version_tag }}
       branch_name: ${{ github.head_ref }}
 
-  terraform_account_workflow_development:
-    name: TF Plan Dev Account
-    uses: ./.github/workflows/terraform_account_job.yml
-    with:
-      workspace_name: development
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-      pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
-
-  terraform_account_workflow_preproduction:
-    name: TF Plan Preprod Account
-    needs: terraform_account_workflow_development
-    uses: ./.github/workflows/terraform_account_job.yml
-    with:
-      workspace_name: preproduction
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-      pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
-
-  terraform_account_workflow_production:
-    name: TF Plan Prod Account
-    needs: terraform_account_workflow_development
-    uses: ./.github/workflows/terraform_account_job.yml
-    with:
-      workspace_name: production
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-      pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
-
-  ui_tests_image:
-    name: Run Cypress UI Tests On Images
-    if: needs.detect_changes.outputs.changes_detected == 'true' &&
-      (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped')
-    uses: ./.github/workflows/ui_test_job.yml
-    needs: [docker_build_scan_push, create_tags]
-    with:
-      run_against_image: true
-      tag: ${{ needs.create_tags.outputs.version_tag }}
-      specs: 'cypress/e2e/**/*.cy.js'
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-      cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
-      github_access_token: ${{ secrets.GITHUB_TOKEN }}
-
-  pr_deploy:
-      name: PR Environment Deploy
-      if: always() &&
-        (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped') &&
-        (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped') &&
-        (needs.ui_tests_image.result == 'success' || needs.ui_tests_image.result == 'skipped')
-      needs: [
-        create_tags,
-        go_unit_tests,
-        docker_build_scan_push,
-        ui_tests_image
-      ]
-      uses: ./.github/workflows/terraform_environment_job.yml
-      with:
-        workspace_name: ${{ needs.create_tags.outputs.environment_workspace_name }}
-        version_tag: ${{ needs.create_tags.outputs.version_tag }}
-        s3_av_scanner_zip_tag: ${{ needs.create_tags.outputs.s3_av_scanner_zip_tag }}
-      secrets:
-        aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-        aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-        ssh_deploy_key: ${{ secrets.OPG_MODERNISING_LPA_DEPLOY_KEY_PRIVATE_KEY }}
-        github_access_token: ${{ secrets.GITHUB_TOKEN }}
-        pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
-
-
-  ui_tests_pr_env:
-    name: Run Cypress UI Tests On PR Environment
-    if: always() &&
-      needs.pr_deploy.result == 'success'
-    uses: ./.github/workflows/ui_test_job.yml
-    needs: [pr_deploy, create_tags]
-    with:
-      run_against_image: false
-      base_url: "https://${{ needs.pr_deploy.outputs.url }}"
-      tag: ${{ needs.create_tags.outputs.version_tag }}
-      environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
-      specs: 'cypress/smoke/*.cy.js'
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-      cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
-      github_access_token: ${{ secrets.GITHUB_TOKEN }}
-
-  always_remove_ingress:
-    name: Remove CI ingress from environment
-    if: always()
-    uses: ./.github/workflows/remove_ingress_job.yml
-    needs: [ui_tests_pr_env, pr_deploy]
-    with:
-      environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
-    secrets:
-      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-
-  end_of_pr_workflow:
-    name: End of PR Workflow
-    runs-on: ubuntu-latest
-    if: always()
-    environment:
-      name: "dev_${{ needs.create_tags.outputs.environment_workspace_name }}"
-      url: "https://${{ needs.pr_deploy.outputs.url }}"
-    needs: [pr_deploy, create_tags, ui_tests_pr_env]
-    steps:
-      - name: End of PR Workflow
-        run: |
-          echo "${{ needs.pr_deploy.outputs.terraform_workspace_name }} PR environment tested, built and deployed"
-          echo "Tag Deployed: ${{ needs.pr_deploy.outputs.terraform_container_version }}"
-          echo "URL: https://${{ needs.pr_deploy.outputs.url }}"
-
-          if ${{ contains(needs.ui_tests_pr_env.result,'success') }}
-          then
-            echo "PR environment tested, built and deployed"
-            exit 0
-          else
-            echo "PR environment tested, built and deployed but UI tests failed"
-            exit 1
-          fi
+  # terraform_account_workflow_development:
+  #   name: TF Plan Dev Account
+  #   uses: ./.github/workflows/terraform_account_job.yml
+  #   with:
+  #     workspace_name: development
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #     pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
+
+  # terraform_account_workflow_preproduction:
+  #   name: TF Plan Preprod Account
+  #   needs: terraform_account_workflow_development
+  #   uses: ./.github/workflows/terraform_account_job.yml
+  #   with:
+  #     workspace_name: preproduction
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #     pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
+
+  # terraform_account_workflow_production:
+  #   name: TF Plan Prod Account
+  #   needs: terraform_account_workflow_development
+  #   uses: ./.github/workflows/terraform_account_job.yml
+  #   with:
+  #     workspace_name: production
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #     pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
+
+  # ui_tests_image:
+  #   name: Run Cypress UI Tests On Images
+  #   if: needs.detect_changes.outputs.changes_detected == 'true' &&
+  #     (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped')
+  #   uses: ./.github/workflows/ui_test_job.yml
+  #   needs: [docker_build_scan_push, create_tags]
+  #   with:
+  #     run_against_image: true
+  #     tag: ${{ needs.create_tags.outputs.version_tag }}
+  #     specs: 'cypress/e2e/**/*.cy.js'
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #     cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
+  #     github_access_token: ${{ secrets.GITHUB_TOKEN }}
+
+  # pr_deploy:
+  #     name: PR Environment Deploy
+  #     if: always() &&
+  #       (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped') &&
+  #       (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped') &&
+  #       (needs.ui_tests_image.result == 'success' || needs.ui_tests_image.result == 'skipped')
+  #     needs: [
+  #       create_tags,
+  #       go_unit_tests,
+  #       docker_build_scan_push,
+  #       ui_tests_image
+  #     ]
+  #     uses: ./.github/workflows/terraform_environment_job.yml
+  #     with:
+  #       workspace_name: ${{ needs.create_tags.outputs.environment_workspace_name }}
+  #       version_tag: ${{ needs.create_tags.outputs.version_tag }}
+  #       s3_av_scanner_zip_tag: ${{ needs.create_tags.outputs.s3_av_scanner_zip_tag }}
+  #     secrets:
+  #       aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #       aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #       ssh_deploy_key: ${{ secrets.OPG_MODERNISING_LPA_DEPLOY_KEY_PRIVATE_KEY }}
+  #       github_access_token: ${{ secrets.GITHUB_TOKEN }}
+  #       pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
+
+
+  # ui_tests_pr_env:
+  #   name: Run Cypress UI Tests On PR Environment
+  #   if: always() &&
+  #     needs.pr_deploy.result == 'success'
+  #   uses: ./.github/workflows/ui_test_job.yml
+  #   needs: [pr_deploy, create_tags]
+  #   with:
+  #     run_against_image: false
+  #     base_url: "https://${{ needs.pr_deploy.outputs.url }}"
+  #     tag: ${{ needs.create_tags.outputs.version_tag }}
+  #     environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
+  #     specs: 'cypress/smoke/*.cy.js'
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  #     cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
+  #     github_access_token: ${{ secrets.GITHUB_TOKEN }}
+
+  # always_remove_ingress:
+  #   name: Remove CI ingress from environment
+  #   if: always()
+  #   uses: ./.github/workflows/remove_ingress_job.yml
+  #   needs: [ui_tests_pr_env, pr_deploy]
+  #   with:
+  #     environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
+  #   secrets:
+  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+
+  # end_of_pr_workflow:
+  #   name: End of PR Workflow
+  #   runs-on: ubuntu-latest
+  #   if: always()
+  #   environment:
+  #     name: "dev_${{ needs.create_tags.outputs.environment_workspace_name }}"
+  #     url: "https://${{ needs.pr_deploy.outputs.url }}"
+  #   needs: [pr_deploy, create_tags, ui_tests_pr_env]
+  #   steps:
+  #     - name: End of PR Workflow
+  #       run: |
+  #         echo "${{ needs.pr_deploy.outputs.terraform_workspace_name }} PR environment tested, built and deployed"
+  #         echo "Tag Deployed: ${{ needs.pr_deploy.outputs.terraform_container_version }}"
+  #         echo "URL: https://${{ needs.pr_deploy.outputs.url }}"
+
+  #         if ${{ contains(needs.ui_tests_pr_env.result,'success') }}
+  #         then
+  #           echo "PR environment tested, built and deployed"
+  #           exit 0
+  #         else
+  #           echo "PR environment tested, built and deployed but UI tests failed"
+  #           exit 1
+  #         fi

From a0345ea23453f7c5dd6d18c9e6208d6fa5c31194 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 11:43:31 +0100
Subject: [PATCH 03/15] create tags

---
 .github/workflows/workflow_pr.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/workflow_pr.yml b/.github/workflows/workflow_pr.yml
index d94f82de70..418e50cf7d 100644
--- a/.github/workflows/workflow_pr.yml
+++ b/.github/workflows/workflow_pr.yml
@@ -32,7 +32,8 @@ jobs:
     needs: detect_changes
     uses: ./.github/workflows/tags_job.yml
     with:
-      changes_detected: ${{ needs.detect_changes.outputs.changes_detected }}
+      # changes_detected: ${{ needs.detect_changes.outputs.changes_detected }}
+      changes_detected: true
 
   # go_unit_tests:
   #   name: Run Go unit tests

From 82ebe2fa85a7cf1038b1b335833598b12a943a30 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 11:48:29 +0100
Subject: [PATCH 04/15] enable on pushes too

---
 .github/workflows/docker_job.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/docker_job.yml b/.github/workflows/docker_job.yml
index 6df152a76c..ef78dae5fe 100644
--- a/.github/workflows/docker_job.yml
+++ b/.github/workflows/docker_job.yml
@@ -186,6 +186,7 @@ jobs:
           provenance: false
         env:
           SOURCE_DATE_EPOCH: 0
+          DOCKER_CONTENT_TRUST: 1
 
       - name: Push ${{ matrix.ecr_repository }} Image to ECR for Path to Live
         if: ${{ github.workflow == 'Path To Live' }}
@@ -207,3 +208,4 @@ jobs:
           provenance: false
         env:
           SOURCE_DATE_EPOCH: 0
+          DOCKER_CONTENT_TRUST: 1

From c2d5acc2abdfc2a5be157aeb743240bf5d17fc06 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 11:52:48 +0100
Subject: [PATCH 05/15] test an untrusted image

---
 docker/mlpa/Dockerfile | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index c78855f9aa..7d9fcd0fb1 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -44,6 +44,9 @@ COPY --link internal ./internal
 
 RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.Tag=${TAG}" -o /go/bin/mlpab ./cmd/mlpa
 
+FROM docker/trusttest:testing AS testingcontenttrustfailure
+RUN echo
+
 FROM alpine:3.20.3 AS production
 
 WORKDIR /go/bin

From 48461ca5e500f488e2f75bd4653d4bb35936d9ab Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 12:02:44 +0100
Subject: [PATCH 06/15] revert

---
 docker/mlpa/Dockerfile | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index 7d9fcd0fb1..c78855f9aa 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -44,9 +44,6 @@ COPY --link internal ./internal
 
 RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.Tag=${TAG}" -o /go/bin/mlpab ./cmd/mlpa
 
-FROM docker/trusttest:testing AS testingcontenttrustfailure
-RUN echo
-
 FROM alpine:3.20.3 AS production
 
 WORKDIR /go/bin

From 1088e4b903dbc95c301ce947da3d926975a48427 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 12:04:17 +0100
Subject: [PATCH 07/15] returns no tag or not trust?

---
 docker/mlpa/Dockerfile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index c78855f9aa..bbd4f68b5d 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -44,7 +44,8 @@ COPY --link internal ./internal
 
 RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.Tag=${TAG}" -o /go/bin/mlpab ./cmd/mlpa
 
-FROM alpine:3.20.3 AS production
+
+FROM docker/trusttest:testing AS production
 
 WORKDIR /go/bin
 

From d98270651f07926f17b4e2dbe008b6d2f885a188 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Wed, 16 Oct 2024 12:06:18 +0100
Subject: [PATCH 08/15] revert

---
 docker/mlpa/Dockerfile | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index bbd4f68b5d..c78855f9aa 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -44,8 +44,7 @@ COPY --link internal ./internal
 
 RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.Tag=${TAG}" -o /go/bin/mlpab ./cmd/mlpa
 
-
-FROM docker/trusttest:testing AS production
+FROM alpine:3.20.3 AS production
 
 WORKDIR /go/bin
 

From bd14d7524d171d6933131bc6ffa325ebde262a34 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 13:18:11 +0100
Subject: [PATCH 09/15] ad ual hardening script for alpine

---
 docker/mlpa/Dockerfile                        | 12 ++-
 .../alpine_image_hardening.sh                 | 98 +++++++++++++++++++
 2 files changed, 106 insertions(+), 4 deletions(-)
 create mode 100755 scripts/docker_hardening/alpine_image_hardening.sh

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index c78855f9aa..3916738015 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -54,9 +54,13 @@ COPY --from=build-env /go/bin/mlpab mlpab
 COPY --link web/template web/template
 COPY --link lang lang
 
-RUN addgroup -S app && \
-  adduser -S -g app app && \
-  chown -R app:app mlpab web/template web/static web/robots.txt
-USER app
 
+RUN addgroup -S user && \
+  adduser -S -g user user && \
+  chown -R user:user mlpab web/template web/static web/robots.txt
+
+COPY scripts/docker_hardening/alpine_image_hardening.sh /harden.sh
+RUN /harden.sh && rm /harden.sh
+
+USER user
 ENTRYPOINT ["./mlpab"]
diff --git a/scripts/docker_hardening/alpine_image_hardening.sh b/scripts/docker_hardening/alpine_image_hardening.sh
new file mode 100755
index 0000000000..4124c0edbf
--- /dev/null
+++ b/scripts/docker_hardening/alpine_image_hardening.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+echo "=== Starting Alpine Hardening Script ==="
+
+echo "add default user"
+adduser -D -s /bin/sh -u 1000 user && \
+    sed -i -r 's/^user:!:/user:x:/' /etc/shadow && \
+    chmod u-s /usr/sbin/login_duo
+
+echo "/etc/duo/login_duo.conf must be readable only by user 'user'."
+chown user:user /etc/duo/login_duo.conf && \
+chmod 0400 /etc/duo/login_duo.conf
+
+echo "Ensure strict ownership and perms."
+chown root:root /usr/bin/github_pubkeys && \
+    chmod 0555 /usr/bin/github_pubkeys && \
+    echo -e "\n\nApp container image built on $(date)." > /etc/motd
+
+echo "Remove world-writeable permissions except for /tmp/"
+find / -xdev -type d -perm +0002 -exec chmod o-w {} + \
+  && find / -xdev -type f -perm +0002 -exec chmod o-w {} + \
+  && chmod 777 /tmp/ \
+  && chown www-data:root /tmp/
+
+echo "Remove unnecessary user accounts."
+sed -i -r '/^(user|root|sshd|www-data|nobody)/!d' /etc/group
+sed -i -r '/^(user|root|sshd|www-data|nobody)/!d' /etc/passwd
+
+echo "Remove existing crontabs, if any."
+rm -fr /var/spool/cron \
+  && rm -fr /etc/crontabs \
+  && rm -fr /etc/periodic
+
+echo "Remove interactive login shell for everybody but user."
+sed -i -r '/^user:/! s#^(.*):[^:]*$#\1:/sbin/nologin#' /etc/passwd
+
+sysdirs="
+  /bin
+  /etc
+  /lib
+  /sbin
+  /usr
+"
+echo "Remove apk configs."
+find $sysdirs -xdev -regex '.*apk.*' -exec rm -fr {} +
+find $sysdirs -xdev -type f -regex '.*-$' -exec rm -f {} +
+
+echo "Ensure system dirs are owned by root and not writable by anybody else."
+find $sysdirs -xdev -type d \
+  -exec chown root:root {} \; \
+  -exec chmod 0755 {} \;
+
+echo "Remove all suid files."
+find $sysdirs -xdev -type f -a -perm +4000 -delete
+find $sysdirs -xdev -type f -a \( -perm +4000 -o -perm +2000 \) -delete
+
+echo "Remove other programs that could be dangerous."
+find $sysdirs -xdev \( \
+  -name hexdump -o \
+  -name chgrp -o \
+  -name chmod -o \
+  -name chown -o \
+  -name ln -o \
+  -name od -o \
+  -name strings -o \
+  -name su \
+  -name sudo \
+  \) -delete
+
+echo "Remove init scripts since we do not use them."
+rm -fr /etc/init.d
+rm -fr /lib/rc
+rm -fr /etc/conf.d
+rm -fr /etc/inittab
+rm -fr /etc/runlevels
+rm -fr /etc/rc.conf
+rm -fr /etc/logrotate.d
+
+echo "Remove kernel tunables since we do not need them."
+rm -fr /etc/sysctl*
+rm -fr /etc/modprobe.d
+rm -fr /etc/modules
+rm -fr /etc/mdev.conf
+rm -fr /etc/acpi
+
+echo "Remove root homedir since we do not need it."
+rm -fr /root
+
+echo "Remove fstab since we do not need it."
+rm -f /etc/fstab
+
+echo "Remove all but a handful of admin commands."
+find /sbin /usr/sbin ! -type d -a ! -name apk -a ! -name ln -delete
+
+echo "Remove broken symlinks (because we removed the targets above)."
+find $sysdirs -xdev -type l -exec test ! -e {} \; -delete
+
+echo "Disable password login for everybody"
+while IFS=: read -r username _; do passwd -l "$username"; done < /etc/passwd || true

From f479db195dff87d72730ae309b9953bbe92992ce Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 13:29:33 +0100
Subject: [PATCH 10/15] run ui tests on images

---
 .github/workflows/workflow_pr.yml | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/.github/workflows/workflow_pr.yml b/.github/workflows/workflow_pr.yml
index 418e50cf7d..4d4f8d345c 100644
--- a/.github/workflows/workflow_pr.yml
+++ b/.github/workflows/workflow_pr.yml
@@ -94,21 +94,22 @@ jobs:
   #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
   #     pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
 
-  # ui_tests_image:
-  #   name: Run Cypress UI Tests On Images
-  #   if: needs.detect_changes.outputs.changes_detected == 'true' &&
-  #     (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped')
-  #   uses: ./.github/workflows/ui_test_job.yml
-  #   needs: [docker_build_scan_push, create_tags]
-  #   with:
-  #     run_against_image: true
-  #     tag: ${{ needs.create_tags.outputs.version_tag }}
-  #     specs: 'cypress/e2e/**/*.cy.js'
-  #   secrets:
-  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-  #     cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
-  #     github_access_token: ${{ secrets.GITHUB_TOKEN }}
+  ui_tests_image:
+    name: Run Cypress UI Tests On Images
+    if: always()
+    # if: needs.detect_changes.outputs.changes_detected == 'true' &&
+    #   (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped')
+    uses: ./.github/workflows/ui_test_job.yml
+    needs: [docker_build_scan_push, create_tags]
+    with:
+      run_against_image: true
+      tag: ${{ needs.create_tags.outputs.version_tag }}
+      specs: 'cypress/e2e/**/*.cy.js'
+    secrets:
+      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+      cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
+      github_access_token: ${{ secrets.GITHUB_TOKEN }}
 
   # pr_deploy:
   #     name: PR Environment Deploy

From a0042806a430070d8531d40b6c8150e1f081d8ae Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 14:57:25 +0100
Subject: [PATCH 11/15] expose only needed ports

---
 docker/mlpa/Dockerfile     | 4 ++++
 docker/mock-pay/Dockerfile | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/docker/mlpa/Dockerfile b/docker/mlpa/Dockerfile
index 3916738015..b23d8feaa5 100644
--- a/docker/mlpa/Dockerfile
+++ b/docker/mlpa/Dockerfile
@@ -60,7 +60,11 @@ RUN addgroup -S user && \
   chown -R user:user mlpab web/template web/static web/robots.txt
 
 COPY scripts/docker_hardening/alpine_image_hardening.sh /harden.sh
+
 RUN /harden.sh && rm /harden.sh
 
 USER user
+
+EXPOSE 8080
+
 ENTRYPOINT ["./mlpab"]
diff --git a/docker/mock-pay/Dockerfile b/docker/mock-pay/Dockerfile
index daef3782ad..a04a0ca689 100644
--- a/docker/mock-pay/Dockerfile
+++ b/docker/mock-pay/Dockerfile
@@ -3,3 +3,5 @@ FROM outofcoffee/imposter:4.0.5
 COPY ./docker/mock-pay /opt/imposter/config/
 
 USER imposter
+
+EXPOSE 8080

From 234116226e4c1ba3e764c3c68d372d1f7e1c57be Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 15:10:24 +0100
Subject: [PATCH 12/15] deploy images and test

---
 .github/workflows/workflow_pr.yml | 106 +++++++++++++++---------------
 1 file changed, 54 insertions(+), 52 deletions(-)

diff --git a/.github/workflows/workflow_pr.yml b/.github/workflows/workflow_pr.yml
index 4d4f8d345c..e1473432db 100644
--- a/.github/workflows/workflow_pr.yml
+++ b/.github/workflows/workflow_pr.yml
@@ -111,59 +111,61 @@ jobs:
       cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
       github_access_token: ${{ secrets.GITHUB_TOKEN }}
 
-  # pr_deploy:
-  #     name: PR Environment Deploy
-  #     if: always() &&
-  #       (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped') &&
-  #       (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped') &&
-  #       (needs.ui_tests_image.result == 'success' || needs.ui_tests_image.result == 'skipped')
-  #     needs: [
-  #       create_tags,
-  #       go_unit_tests,
-  #       docker_build_scan_push,
-  #       ui_tests_image
-  #     ]
-  #     uses: ./.github/workflows/terraform_environment_job.yml
-  #     with:
-  #       workspace_name: ${{ needs.create_tags.outputs.environment_workspace_name }}
-  #       version_tag: ${{ needs.create_tags.outputs.version_tag }}
-  #       s3_av_scanner_zip_tag: ${{ needs.create_tags.outputs.s3_av_scanner_zip_tag }}
-  #     secrets:
-  #       aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-  #       aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-  #       ssh_deploy_key: ${{ secrets.OPG_MODERNISING_LPA_DEPLOY_KEY_PRIVATE_KEY }}
-  #       github_access_token: ${{ secrets.GITHUB_TOKEN }}
-  #       pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
-
-
-  # ui_tests_pr_env:
-  #   name: Run Cypress UI Tests On PR Environment
-  #   if: always() &&
-  #     needs.pr_deploy.result == 'success'
-  #   uses: ./.github/workflows/ui_test_job.yml
-  #   needs: [pr_deploy, create_tags]
-  #   with:
-  #     run_against_image: false
-  #     base_url: "https://${{ needs.pr_deploy.outputs.url }}"
-  #     tag: ${{ needs.create_tags.outputs.version_tag }}
-  #     environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
-  #     specs: 'cypress/smoke/*.cy.js'
-  #   secrets:
-  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
-  #     cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
-  #     github_access_token: ${{ secrets.GITHUB_TOKEN }}
+  pr_deploy:
+      name: PR Environment Deploy
+      if: always()
+      # if: always() &&
+      #   (needs.go_unit_tests.result == 'success' || needs.go_unit_tests.result == 'skipped') &&
+      #   (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped') &&
+      #   (needs.ui_tests_image.result == 'success' || needs.ui_tests_image.result == 'skipped')
+      needs: [
+        create_tags,
+        go_unit_tests,
+        docker_build_scan_push,
+        ui_tests_image
+      ]
+      uses: ./.github/workflows/terraform_environment_job.yml
+      with:
+        workspace_name: ${{ needs.create_tags.outputs.environment_workspace_name }}
+        version_tag: ${{ needs.create_tags.outputs.version_tag }}
+        s3_av_scanner_zip_tag: ${{ needs.create_tags.outputs.s3_av_scanner_zip_tag }}
+      secrets:
+        aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+        aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+        ssh_deploy_key: ${{ secrets.OPG_MODERNISING_LPA_DEPLOY_KEY_PRIVATE_KEY }}
+        github_access_token: ${{ secrets.GITHUB_TOKEN }}
+        pagerduty_api_key: ${{ secrets.PAGERDUTY_API_KEY }}
+
+
+  ui_tests_pr_env:
+    name: Run Cypress UI Tests On PR Environment
+    if: always()
+    # if: always() &&
+    #   needs.pr_deploy.result == 'success'
+    uses: ./.github/workflows/ui_test_job.yml
+    needs: [pr_deploy, create_tags]
+    with:
+      run_against_image: false
+      base_url: "https://${{ needs.pr_deploy.outputs.url }}"
+      tag: ${{ needs.create_tags.outputs.version_tag }}
+      environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
+      specs: 'cypress/smoke/*.cy.js'
+    secrets:
+      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+      cypress_record_key: ${{ secrets.CYPRESS_RECORD_KEY }}
+      github_access_token: ${{ secrets.GITHUB_TOKEN }}
 
-  # always_remove_ingress:
-  #   name: Remove CI ingress from environment
-  #   if: always()
-  #   uses: ./.github/workflows/remove_ingress_job.yml
-  #   needs: [ui_tests_pr_env, pr_deploy]
-  #   with:
-  #     environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
-  #   secrets:
-  #     aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
-  #     aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
+  always_remove_ingress:
+    name: Remove CI ingress from environment
+    if: always()
+    uses: ./.github/workflows/remove_ingress_job.yml
+    needs: [ui_tests_pr_env, pr_deploy]
+    with:
+      environment_config_json: ${{ needs.pr_deploy.outputs.environment_config_json }}
+    secrets:
+      aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_ACTIONS }}
+      aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ACTIONS }}
 
   # end_of_pr_workflow:
   #   name: End of PR Workflow

From f466dc5460873772a486008fe019dd8bb3fafdb4 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 15:14:50 +0100
Subject: [PATCH 13/15] fix dependson

---
 .github/workflows/workflow_pr.yml | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/workflow_pr.yml b/.github/workflows/workflow_pr.yml
index e1473432db..89775580d3 100644
--- a/.github/workflows/workflow_pr.yml
+++ b/.github/workflows/workflow_pr.yml
@@ -57,7 +57,7 @@ jobs:
     needs: [
       # go_unit_tests,
       create_tags
-      ]
+    ]
     with:
       tag: ${{ needs.create_tags.outputs.version_tag }}
       branch_name: ${{ github.head_ref }}
@@ -100,7 +100,10 @@ jobs:
     # if: needs.detect_changes.outputs.changes_detected == 'true' &&
     #   (needs.docker_build_scan_push.result == 'success' || needs.docker_build_scan_push.result == 'skipped')
     uses: ./.github/workflows/ui_test_job.yml
-    needs: [docker_build_scan_push, create_tags]
+    needs: [
+      docker_build_scan_push,
+      create_tags
+    ]
     with:
       run_against_image: true
       tag: ${{ needs.create_tags.outputs.version_tag }}
@@ -120,7 +123,7 @@ jobs:
       #   (needs.ui_tests_image.result == 'success' || needs.ui_tests_image.result == 'skipped')
       needs: [
         create_tags,
-        go_unit_tests,
+        # go_unit_tests,
         docker_build_scan_push,
         ui_tests_image
       ]
@@ -143,7 +146,10 @@ jobs:
     # if: always() &&
     #   needs.pr_deploy.result == 'success'
     uses: ./.github/workflows/ui_test_job.yml
-    needs: [pr_deploy, create_tags]
+    needs: [
+      pr_deploy,
+      create_tags
+    ]
     with:
       run_against_image: false
       base_url: "https://${{ needs.pr_deploy.outputs.url }}"

From f2cf4b9651d0d8c4072e24938ff6946ab2b95420 Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Thu, 17 Oct 2024 16:08:47 +0100
Subject: [PATCH 14/15] 5.13 Ensure that the container's root filesystem is
 mounted as read-only

---
 terraform/environment/region/modules/app/ecs.tf      | 2 +-
 terraform/environment/region/modules/mock_pay/ecs.tf | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/terraform/environment/region/modules/app/ecs.tf b/terraform/environment/region/modules/app/ecs.tf
index 7451f2ebd3..6dba06f0f2 100644
--- a/terraform/environment/region/modules/app/ecs.tf
+++ b/terraform/environment/region/modules/app/ecs.tf
@@ -534,7 +534,7 @@ locals {
       portMappings           = [],
       essential              = false,
       entryPoint             = [],
-      readonlyRootFilesystem = false
+      readonlyRootFilesystem = true
       command = [
         "/bin/bash",
         "-c",
diff --git a/terraform/environment/region/modules/mock_pay/ecs.tf b/terraform/environment/region/modules/mock_pay/ecs.tf
index 0a86a26896..9572c87721 100644
--- a/terraform/environment/region/modules/mock_pay/ecs.tf
+++ b/terraform/environment/region/modules/mock_pay/ecs.tf
@@ -133,7 +133,7 @@ locals {
       essential              = true,
       image                  = "${var.repository_url}:${var.container_version}",
       mountPoints            = [],
-      readonlyRootFilesystem = false,
+      readonlyRootFilesystem = true,
       name                   = "mock_pay",
       portMappings = [
         {

From e89c7dc5858775d206f6dd94c4958d09279a800e Mon Sep 17 00:00:00 2001
From: Andrew Pearce <andrew.pearce@digital.justice.gov.uk>
Date: Mon, 21 Oct 2024 10:20:25 +0100
Subject: [PATCH 15/15] revert readonlyRootFilesystem for pay

---
 terraform/environment/region/modules/mock_pay/ecs.tf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/terraform/environment/region/modules/mock_pay/ecs.tf b/terraform/environment/region/modules/mock_pay/ecs.tf
index 9572c87721..0a86a26896 100644
--- a/terraform/environment/region/modules/mock_pay/ecs.tf
+++ b/terraform/environment/region/modules/mock_pay/ecs.tf
@@ -133,7 +133,7 @@ locals {
       essential              = true,
       image                  = "${var.repository_url}:${var.container_version}",
       mountPoints            = [],
-      readonlyRootFilesystem = true,
+      readonlyRootFilesystem = false,
       name                   = "mock_pay",
       portMappings = [
         {