diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ed140a..70835ba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,12 @@ jobs: REPORT_TO_HEIMDALL: true HEIMDALL_URL: https://heimdall-demo.mitre.org/evaluations HEIMDALL_API_KEY: ${{ secrets.SAF_HEIMDALL_UPLOAD_KEY }} + + REPORT_DIR: reports + INSPEC_REPORT_FILENAME: inspec_results.json + ATTESTATION_FILENAME: ${{ secrets.ATTESTATION_FILE}} + ATTESTED_INSPEC_FILE_NAME: inspec_results.json + CA_FILE_BASE64_AA: ${{ secrets.CA_FILE_BASE64_AA }} CA_FILE_BASE64_AB: ${{ secrets.CA_FILE_BASE64_AB }} CERTIFICATE_KEY_FILE: ${{ secrets.CERTIFICATE_KEY_FILE }} @@ -42,6 +48,22 @@ jobs: packer init mongo-hardening.pkr.hcl packer build mongo-hardening.pkr.hcl + - name: Run Packer Validation + run: | + packer build -var 'report={"report_to_heimdall":"${{ env.REPORT_TO_HEIMDALL }}","heimdall_url":"${{ env.HEIMDALL_URL }}","heimdall_api_key":"${{ env.HEIMDALL_API_KEY }}"}' -var 'attestation={"report_dir":"${{ env.REPORT_DIR }}","inspec_report_filename":"${{ env.INSPEC_REPORT_FILENAME }}","attestation_filename":"${{ env.ATTESTATION_FILENAME }}","attested_inspec_filename":"${{ env.ATTESTED_INSPEC_FILE_NAME }}"}' mongo-validate.pkr.hcl + docker ps -a + + - name: Get Docker Image Tag + run: | + if docker images | grep -q 'passed'; then + echo "image_tag=passed" >> $GITHUB_ENV + elif docker images | grep -q 'failed'; then + echo "image_tag=failed" >> $GITHUB_ENV + else + echo "No suitable image found" + exit 1 + fi + - name: Log in to GitHub Container Registry uses: docker/login-action@v2 with: @@ -49,15 +71,16 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Tag and Push Docker Image + - name: Tag and Push Docker Image to GHCR run: | docker tag mongo-hardened:latest ghcr.io/${{ github.repository_owner }}/mongo-hardened:latest docker push ghcr.io/${{ github.repository_owner }}/mongo-hardened:latest - - name: Run Packer Validation + - name: Tag and Push Docker Image to GHCR run: | - packer build -var 'report={"report_to_heimdall":"${{ env.REPORT_TO_HEIMDALL }}","heimdall_url":"${{ env.HEIMDALL_URL }}","heimdall_api_key":"${{ env.HEIMDALL_API_KEY }}"}' mongo-validate.pkr.hcl - docker ps -a + docker tag mongo-hardened:${{ env.image_tag }} ghcr.io/${{ github.repository_owner }}/mongo-hardened:${{ env.image_tag }} + docker push ghcr.io/${{ github.repository_owner }}/mongo-hardened:${{ env.image_tag }} + if: env.image_tag != '' - name: Save Scan Artifacts uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 222b312..66d239f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ reports/** +!reports/attestation_template.json inputs.yml inspec.lock spec/ansible/roles/mitre.mongo-stig diff --git a/README.md b/README.md index 045b87e..314c87f 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,12 @@ mongo_superusers: cp variables_template.pkrvar.hcl variables.pkrvar.hcl ``` + 8.1 **Optional: Update the `attestation_template.json` Now if Using a STIG Viewer** + + If you have a STIG Viewer available, you can update the `attestation_template.json` now to avoid rerunning the validation Packer file. This allows you to look up the control IDs beforehand and check for compliance in advance. + + Follow the instructions [here](#inspec-report) to proceed, and then return to this step once done. + 9. **Build the Hardened Image** Execute the following command to build and save the hardened Mongo image: @@ -136,6 +142,44 @@ mongo_superusers: mongod --config /etc/mongod.conf ``` +## Inspec Report + +After running the hardening and validation packer files, a report will be generated in `reports/inspec_results.json`. + +1. **Upload the Results**: + + Upload the `inspec_results.json` file to [Heimdall](https://heimdall-lite.mitre.org/). There should be 19 _Not Reviewed_ controls. These controls need to be attested to. + +2. **Edit the Attestation Template**: + + Execute the following command to create the `attestation.json` file under `reports` by copying `attestation_template.json` and renaming it to `attestation.json`. + + ```sh + cp spec/mongo-inspec-profile/inputs_template.yml spec/mongo-inspec-profile/inputs.yml + ``` + + Alternatively, you can also have the SAF CLI guide you through the creation of the attestation file. For instructions on how to use the CLI to create an attestation, please refer to the [SAF CLI documentation](https://saf-cli.mitre.org/#create-attestations). + +3. **Review and Provide Explanations**: + + Manually review each control and provide an explanation on whether it `passed` or `failed`. + +4. **Update `variables.pkrvar.hcl`**: + + Update `variables.pkrvar.hcl` with your new attestation file values. + +5. **Re-run the Packer Validation to Apply Your Attestations** + + Execute the following command to test the hardened Mongo image, it should now produce a docker image tagged with `passed`: + + ```sh + packer build -var-file="variables.pkrvar.hcl" mongo-validate.pkr.hcl + ``` + +6. **Re-upload to Heimdall**: + + Upload the new `inspec_results.json` file back into [Heimdall](https://heimdall-lite.mitre.org/) see your compliance level. + ## Notes ### Certificates diff --git a/mongo-validate.pkr.hcl b/mongo-validate.pkr.hcl index c4c656b..74fd1f6 100644 --- a/mongo-validate.pkr.hcl +++ b/mongo-validate.pkr.hcl @@ -35,6 +35,11 @@ variable "report" { description = "Configuration for reporting to Heimdall" } +variable "attestation" { + type = map(string) + description = "Configuration for attesting inspec results" +} + # Hardened docker container to be validated source "docker" "hardened" { image = "${var.input_hardened_image.name}:${var.input_hardened_image.tag}" @@ -67,12 +72,23 @@ build { "REPORT_DIR=${var.scan.report_dir}", "REPORT_FILE=${var.scan.inspec_report_filename}", "INPUT_FILE=${var.scan.inspec_input_file}", - "TARGET_IMAGE=${var.input_hardened_image.name}", + "TARGET_IMAGE=${var.input_hardened_image.name}" ] valid_exit_codes = [0, 100, 101] # inspec has multiple valid exit codes script = "spec/scripts/scan.sh" } + ### ATTEST + provisioner "shell-local" { + environment_vars = [ + "INSPEC_FILE=${var.attestation.inspec_report_filename}", + "REPORT_DIR=${var.attestation.report_dir}", + "ATTESTATION_FILE=${var.attestation.attestation_filename}", + "ATTESTED_FILE=${var.attestation.attested_inspec_filename}" + ] + script = "spec/scripts/attestation.sh" + } + ### REPORT provisioner "shell-local" { environment_vars = [ @@ -88,7 +104,8 @@ build { provisioner "shell-local" { environment_vars = [ "TARGET_IMAGE=${var.input_hardened_image.name}", - "REPORT_DIR=${var.scan.report_dir}" + "REPORT_DIR=${var.scan.report_dir}", + "ATTESTED_FILE=${var.attestation.attested_inspec_filename}" ] valid_exit_codes = [0, 1] # the threshold checks return 1 if the thresholds aren't met # this does not mean we want to halt the run diff --git a/reports/attestation_template.json b/reports/attestation_template.json new file mode 100644 index 0000000..7960557 --- /dev/null +++ b/reports/attestation_template.json @@ -0,0 +1,154 @@ +[ + { + "control_id": "SV-252144", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252147", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252150", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252151", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252152", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252153", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252158", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252161", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252162", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252165", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252166", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252170", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252172", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252173", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252177", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252181", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252183", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252184", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + }, + { + "control_id": "SV-252185", + "explanation": "This attestation is provided as a template.", + "frequency": "1y", + "status": "not_reviewed", + "updated": "2024-07-11T17:30:33.627Z", + "updated_by": "MITRE SAF" + } +] diff --git a/spec/scripts/attestation.sh b/spec/scripts/attestation.sh new file mode 100755 index 0000000..e021b9b --- /dev/null +++ b/spec/scripts/attestation.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -uo pipefail + +### Apply attestion to inspec results ### +# Note - Packer has an InSpec provisioner plugin, but it doesn't work well with Docker containers +echo "--- Applying Attestation Against InSpec Profile ---" + +saf attest apply -i $REPORT_DIR/$ATTESTATION_FILE $REPORT_DIR/$INSPEC_FILE -o $REPORT_DIR/$ATTESTED_FILE \ No newline at end of file diff --git a/spec/scripts/verify_threshold.sh b/spec/scripts/verify_threshold.sh index cc27a91..39de65b 100755 --- a/spec/scripts/verify_threshold.sh +++ b/spec/scripts/verify_threshold.sh @@ -2,13 +2,17 @@ set -uo pipefail ### Validate the compliance and status counts of the HDF file ### -saf validate threshold -F inspec.threshold.yml -i "$REPORT_DIR/inspec_results.json" +echo "--- Validating the compliance and status counts of the HDF file ---" + +saf validate threshold -F inspec.threshold.yml -i $REPORT_DIR/$ATTESTED_FILE INSPEC_THRESHOLD_CHECK=$? if [ $INSPEC_THRESHOLD_CHECK -eq 0 ]; then docker tag $TARGET_IMAGE:latest $TARGET_IMAGE:passed + echo "$TARGET_IMAGE:passed created" exit 0 else docker tag $TARGET_IMAGE:latest $TARGET_IMAGE:failed + echo "$TARGET_IMAGE:failed created" exit 1 fi \ No newline at end of file diff --git a/variables_template.pkrvar.hcl b/variables_template.pkrvar.hcl index 60c7ccd..e7ed4a9 100644 --- a/variables_template.pkrvar.hcl +++ b/variables_template.pkrvar.hcl @@ -2,4 +2,11 @@ report = { "report_to_heimdall" = true "heimdall_url" = "https://heimdall-demo.mitre.org/evaluations" "heimdall_api_key" = "your_actual_api_key_here" +} + +attestation = { + "report_dir" = "reports", + "inspec_report_filename" = "inspec_results.json", + "attestation_filename" = "attestation_template.json" + "attested_inspec_filename" = "inspec_results_attested.json" } \ No newline at end of file