4 supply chain risks in Terraform and how to prevent them with Checkov

In many ways, infrastructure as code (IaC) is becoming the new crown jewel of cloud-native applications. The reliability of architecture and its ability to adapt to changes and scale is highly dependent on IaC and the continuous delivery (CD) systems that deploy it. In other words, IaC is a vital component of software supply chains and thus business velocity.

Take, for example, Terraform by HashiCorp, an incredibly powerful IaC tool that lets users define cloud resources in a declarative configuration file that can be version controlled, reused, and shared. Terraform is the most popular framework to manage cloud resources such as a database, network, DNS, compute, storage, and other components of an application.

However, it’s just as easy to make mistakes and introduce such risk in infrastructure as code (Terraform, CloudFormation, etc.) as it is in our application code (JavaScript, Python, etc.). And just like in application code, those risks can occur either from bad source code or old misconfigured/vulnerable versions of third-party components. If IaC or CD pipelines are compromised, account takeover or remote code execution can occur and put data or the availability of the application at risk.

To avoid supply chain attacks while reaping the IaC benefits of repeatability and scalability, let’s explore what some of the supply chain risks look like in Terraform and how to find them using Checkov’s built-in policies and custom policies.

Leaking secrets using Terraform HTTP data blocks

Cloud environments often require sensitive information such as database credentials or access keys for a deployment to work. Those credentials can be auto-generated using Terraform, which is better than hard-coding a secret for this use. But if an attacker has access to a code repository or a third-party module imported into that repository, the secrets can be leaked using an HTTP call data block.

For example, applying the following code to a repository would send the secrets to the bad domain:

To prevent the leakage of secrets over HTTP, Checkov policy CKV2_AWS_36: Ensure Terraform is not sending SSM secrets to untrusted domains over HTTP scans a graph connection between secrets and HTTP commands:

If Checkov detects such a connection when scanning the Terraform code before the plan phase (via a pre-commit hook or continuous integration (CI) step), Checkov can provide the CI job a bad exit code, block integrating and applying the code, and prevent the leakage of the secret.

Arbitrary code execution using a Terraform external data source

Originally reported by Hacking the Cloud, remote code execution can occur if a Terraform plan is executed upon pull request and a contributor adds Terraform code that runs an external data block via external providers.

External providers are intended to be used where a first-class Terraform provider does not exist for a third-party software but we need Terraform to interact with it. External providers allow Terraform to run a script to achieve that interaction.

The script runs during the Terraform plan or apply stages. Using external providers is not considered a best practice since there is no guarantee of state persistency and assurance of portability as mentioned on the Terraform official docs.

When running the vulnerable code above in a Terraform plan operation, the code will be executed and can access secrets or call any third-party code library without vetting. So, if in our CI pipeline we have displayed the Terraform plan, malware.py will be executed even without applying any resource to a cloud environment and can cause data leakage or execution of crypto miners on the CD workers.

To review those third-party code snippets, we can create a custom Checkov policy that ensures Terraform external data blocks run only vetted code:

If this code is legit, a skip comment can be added with a description of the reason for using this method over an official verified provider.

Immutable modules

Using pre-made Terraform modules creates a “paved road” to success for cloud deployments by making infrastructure code reproducible, shareable, and helping maintain the DRY principle. Tagging Terraform modules with a version is a key piece of making a reproducible deployment. The right way to have immutability is to reference a git hash when importing a module. Not having an immutable module makes it vulnerable to code updates that the consumer is unaware of, since git tags can be deleted and re-added using a different commit-id. For example:

In the sample above, we have three modules:

  • not_immutable_s3_bucket is a module that uses the tagged version 0.3.4. The version can be updated under the hood with a different commit id, changing the third-party module we rely upon without changing the version value.
  • not_immutable_s3_bucket_always_latest always uses the latest version of the third-party module “example/s3-bucket/aws”.Using the latest version means that if the last update is misconfigured with the current values, our code gets misconfigured too on the next terraform init command
  • immutable_module is referencing a third-party module, with a git reference with a specific commit id. This code is immutable. No changes can occur to the code that is imported on terraform init command without updating the reference hash

To ensure all modules are immutable using a git commit hash reference, we can create a custom policy:

Using old versions of misconfigured modules

Over time, modules change and are enhanced with security configurations and operational best practices. For example, when cloudposse/s3-bucket/aws was in its early versions, S3 buckets did not have the access logs toggle available. Today, in version 0.47.4, the configuration of access logs is available for consumption by its dependent Terraform blocks.

An SRE’s preferred module version of choice might be “use a version that is newer than X” and deprecate the support of old versions over time so all of the running instances will be able to enjoy the latest enhancements around availability, auditing, and security.

For example, The following code snippet contains two versions of a third-party module. The 0.47.4 has access logging available while 0.2.1 does not:

The custom policy below will help ensure that the S3 module used is the approved one with a version newer than the vulnerable version:

···

Some of the supply chain weaknesses seen above are vulnerable even before the Terraform plan phase and can be exploited using a pull request without even merging it. To avoid those, it is highly recommended to run static analysis for IaC at all steps of development—from the IDE and CI (pre-plan) to CD (post-plan) and runtime.

But comprehensive supply chain security doesn’t stop at securing IaC and its underlying delivery pipelines. Application code, open-source packages, images, containers, and workloads are all part of interconnected supply chains, and securing them requires a holistic approach that starts with visibility.

Tell us about your Terraform supply chain Checkov use cases @bridgecrewio and join us at our upcoming Code to Cloud Summit to hear from Bridgecrew Sr. Director, Product Management, Guy Eisenkot on the complexities of supply chain security.