From 6068658707bd7a462756d088e9e1f306251442a7 Mon Sep 17 00:00:00 2001 From: Andrew Pearce Date: Wed, 16 Oct 2024 11:36:36 +0100 Subject: [PATCH 01/13] 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 Date: Wed, 16 Oct 2024 11:41:48 +0100 Subject: [PATCH 02/13] 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 Date: Wed, 16 Oct 2024 11:43:31 +0100 Subject: [PATCH 03/13] 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 Date: Wed, 16 Oct 2024 11:48:29 +0100 Subject: [PATCH 04/13] 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 Date: Wed, 16 Oct 2024 11:52:48 +0100 Subject: [PATCH 05/13] 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 Date: Wed, 16 Oct 2024 12:02:44 +0100 Subject: [PATCH 06/13] 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 Date: Wed, 16 Oct 2024 12:04:17 +0100 Subject: [PATCH 07/13] 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 Date: Wed, 16 Oct 2024 12:06:18 +0100 Subject: [PATCH 08/13] 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 Date: Thu, 17 Oct 2024 13:18:11 +0100 Subject: [PATCH 09/13] 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 Date: Thu, 17 Oct 2024 13:29:33 +0100 Subject: [PATCH 10/13] 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 Date: Thu, 17 Oct 2024 14:57:25 +0100 Subject: [PATCH 11/13] 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 Date: Thu, 17 Oct 2024 15:10:24 +0100 Subject: [PATCH 12/13] 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 Date: Thu, 17 Oct 2024 15:14:50 +0100 Subject: [PATCH 13/13] 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 }}"