From 2a17795d8b0f2c3e7c8e62600017683c636a9302 Mon Sep 17 00:00:00 2001 From: jamesthompson26-nhs Date: Fri, 16 Aug 2024 13:14:17 +0100 Subject: [PATCH] CCM-6245: TFSec Scanning --- .github/actions/setup/action.yaml | 22 ++++++ .github/actions/tfsec/action.yaml | 17 +++++ .github/workflows/stage-1-commit.yaml | 40 ++++++++++ .tool-versions | 1 + .vscode/settings.json | 1 - scripts/config/tfsec.yaml | 1 + scripts/setup/setup.sh | 43 +++++++++++ scripts/terraform/tfsec.sh | 101 ++++++++++++++++++++++++++ 8 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 .github/actions/setup/action.yaml create mode 100644 .github/actions/tfsec/action.yaml create mode 100644 scripts/config/tfsec.yaml create mode 100755 scripts/setup/setup.sh create mode 100755 scripts/terraform/tfsec.sh diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml new file mode 100644 index 00000000..a7e8b3b5 --- /dev/null +++ b/.github/actions/setup/action.yaml @@ -0,0 +1,22 @@ +name: Make Config Action +description: Install dependencies and execute make config + +runs: + using: composite + steps: + - name: Install dependencies and execute make config + shell: bash + run: |- + echo -n " + ====================================================================\n + ==== Starting setup for ${{ github.job }} ==== @ $(date) \n + ====================================================================\n + " + + scripts/setup/setup.sh + + echo -n " + ====================================================================\n + ==== Setup done ${{ github.job }} ==== @ $(date) \n + ====================================================================\n + " diff --git a/.github/actions/tfsec/action.yaml b/.github/actions/tfsec/action.yaml new file mode 100644 index 00000000..e63b99cf --- /dev/null +++ b/.github/actions/tfsec/action.yaml @@ -0,0 +1,17 @@ +name: "TFSec Scan" +description: "Scan HCL using TFSec" +runs: + using: "composite" + steps: + - name: "TFSec Scan - Components" + shell: bash + run: | + for component in $(find infrastructure/terraform/components -mindepth 1 -type d); do + scripts/terraform/tfsec.sh $component + done + - name: "TFSec Scan - Modules" + shell: bash + run: | + for module in $(find infrastructure/terraform/modules -mindepth 1 -type d); do + scripts/terraform/tfsec.sh $module + done diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index a516b786..ca6fd41b 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -77,15 +77,55 @@ jobs: fetch-depth: 0 # Full history is needed to compare branches - name: "Check English usage" uses: ./.github/actions/check-english-usage + detect-terraform-changes: + name: "Detect Terraform Changes" + runs-on: ubuntu-latest + outputs: + terraform_changed: ${{ steps.check.outputs.terraform_changed }} + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Check for Terraform changes" + id: check + run: | + git fetch origin main || true # Ensure you have the latest main branch + CHANGED_FILES=$(git diff --name-only HEAD origin/main) + echo "Changed files: $CHANGED_FILES" + + if echo "$CHANGED_FILES" | grep -qE '\.tf$'; then + echo "Terraform files have changed." + echo "terraform_changed=true" >> $GITHUB_OUTPUT + else + echo "No Terraform changes detected." + echo "terraform_changed=false" >> $GITHUB_OUTPUT + fi lint-terraform: name: "Lint Terraform" runs-on: ubuntu-latest timeout-minutes: 2 + needs: detect-terraform-changes + if: needs.detect-terraform-changes.outputs.terraform_changed == 'true' steps: - name: "Checkout code" uses: actions/checkout@v4 - name: "Lint Terraform" uses: ./.github/actions/lint-terraform + tfsec: + name: "TFSec Scan" + runs-on: ubuntu-latest + timeout-minutes: 2 + needs: detect-terraform-changes + if: needs.detect-terraform-changes.outputs.terraform_changed == 'true' + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + - name: "Setup ASDF" + uses: asdf-vm/actions/setup@v3 + - name: "Perform Setup" + uses: ./.github/actions/setup + - name: "TFSec Scan" + uses: ./.github/actions/tfsec count-lines-of-code: name: "Count lines of code" runs-on: ubuntu-latest diff --git a/.tool-versions b/.tool-versions index cd5707c6..5550ad2f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -4,6 +4,7 @@ terraform 1.9.1 pre-commit 3.6.0 nodejs 18.18.2 gitleaks 8.18.4 +tfsec 1.28.10 # ============================================================================== # The section below is reserved for Docker image versions. diff --git a/.vscode/settings.json b/.vscode/settings.json index 5c7b4134..f2454a40 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,6 @@ "**/CVS": true, "**/Thumbs.db": true, ".devcontainer": true, - ".github": true, ".vscode": false } } diff --git a/scripts/config/tfsec.yaml b/scripts/config/tfsec.yaml new file mode 100644 index 00000000..623796be --- /dev/null +++ b/scripts/config/tfsec.yaml @@ -0,0 +1 @@ +minimum_severity: MEDIUM diff --git a/scripts/setup/setup.sh b/scripts/setup/setup.sh new file mode 100755 index 00000000..b459f3b5 --- /dev/null +++ b/scripts/setup/setup.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/nhs-england-tools/repository-template). Raise a PR instead. + +set -euo pipefail + +# Pre-Install dependencies and run make config on Github Runner. +# +# Usage: +# $ ./setup.sh +# ============================================================================== + +function main() { + + cd "$(git rev-parse --show-toplevel)" + + run-setup +} + +function run-setup() { + + sudo apt install bundler -y + time make config + + check-setup-status +} + +# Check the exit status of tfsec. +function check-setup-status() { + + if [ $? -eq 0 ]; then + echo "Setup completed successfully." + else + echo "Setup was unsuccessful." + exit 1 + fi +} + +# ============================================================================== + +main "$@" + +exit 0 diff --git a/scripts/terraform/tfsec.sh b/scripts/terraform/tfsec.sh new file mode 100755 index 00000000..9dcaf6f0 --- /dev/null +++ b/scripts/terraform/tfsec.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/nhs-england-tools/repository-template). Raise a PR instead. + +set -euo pipefail + +# TFSec command wrapper. It will run the command natively if TFSec is +# installed, otherwise it will run it in a Docker container. +# Run tfsec for security checks on Terraform code. +# +# Usage: +# $ ./tfsec.sh [directory] +# ============================================================================== + +function main() { + + cd "$(git rev-parse --show-toplevel)" + + local dir_to_scan=${1:-.} + + if command -v tfsec > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then + # shellcheck disable=SC2154 + run-tfsec-natively "$dir_to_scan" + else + run-tfsec-in-docker "$dir_to_scan" + fi +} + +# Run tfsec on the specified directory. +# Arguments: +# $1 - Directory to scan +function run-tfsec-natively() { + + local dir_to_scan="$1" + + echo "TFSec found locally, running natively" + + echo "Running TFSec on directory: $dir_to_scan" + tfsec \ + --concise-output \ + --force-all-dirs \ + --exclude-downloaded-modules \ + --config-file ../config/tfsec.yaml \ + --format text \ + "$dir_to_scan" + + check-tfsec-status +} + +# Check the exit status of tfsec. +function check-tfsec-status() { + + if [ $? -eq 0 ]; then + echo "TFSec completed successfully." + else + echo "TFSec found issues." + exit 1 + fi +} + +function run-tfsec-in-docker() { + + # shellcheck disable=SC1091 + source ./scripts/docker/docker.lib.sh + local dir_to_scan="$1" + + # shellcheck disable=SC2155 + local image=$(name=aquasec/tfsec docker-get-image-version-and-pull) + # shellcheck disable=SC2086 + echo "TFSec not found locally, running in Docker Container" + echo "Running TFSec on directory: $dir_to_scan" + docker run --rm --platform linux/amd64 \ + --volume "$PWD":/workdir \ + --workdir /workdir \ + "$image" \ + --concise-output \ + --force-all-dirs \ + --exclude-downloaded-modules \ + --config-file ../config/tfsec.yaml \ + --format text \ + "$dir_to_scan" + check-tfsec-status +} +# ============================================================================== + +function is-arg-true() { + + if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then + return 0 + else + return 1 + fi +} + +# ============================================================================== + +is-arg-true "${VERBOSE:-false}" && set -x + +main "$@" + +exit 0