Skip to main content
  1. Posts/

Secure Terraform - Part 2 - tfsec Customization

·1215 words·6 mins
Chris Ayers
Author
Chris Ayers
I am a father, nerd, gamer, and speaker.

This is part 2 of the Secure Terraform series. You can read the series of articles here:

Introduction
#

In the previous article, we discussed tfsec, a static code analysis tool for Terraform. We also learned how to use it in VSCode and GitHub Actions to scan our Terraform code. We learned how to override the severity of rules. In this article, we will learn how to customize the rules and add our own rules.

tfsec logo

Customizing tfsec Rules
#

Tfsec allows you to customize the rules that are used to scan your Terraform code. You can do this by creating a file ending in _tfchecks.json or _tfchecks.yaml in the .tfsec folder in the root of project. You can also put these files in a different folder and pass the option --custom-check-dir or --custom-check-url to the tfsec command. This is covered in the documentation: https://aquasecurity.github.io/tfsec/v1.28.1/guides/configuration/custom-checks/.

The documentation references a tool called tfsec-checkgen that you can install. This tool will validate your check file or help perform tests to ensure that it is valid for use with tfsec. I found that the tool helped me create and validate checks but not run the test-check action.

In this post, we will take a look at how to create a few different custom rules. The first rule we’ll work on is for a required tag for our Azure resources. There is an example of this in the tfsec documentation, but its for AWS.

---
checks:
  - code: CUS001
    description: Custom check to ensure the CostCentre tag is applied to EC2 instances
    impact: By not having CostCentre we can't keep track of billing
    resolution: Add the CostCentre tag
    requiredTypes:
      - resource
    requiredLabels:
      - aws_instance
    severity: ERROR
    matchSpec:
      name: tags
      action: contains
      value: CostCentre
    errorMessage: The required CostCentre tag was missing
    relatedLinks:
      - http://internal.acmecorp.com/standards/aws/tagging.html

Lets look at my Azure example and discuss the tweaks and how to use it.

---
checks:
  - code: tags-resources
    description: Custom check to ensure the CostCenter tag is applied to Azure Resources
    impact: By not having CostCenter we can't keep track of billing
    resolution: Add the CostCenter tag
    requiredTypes:
      - resource
    requiredLabels:
      - azurerm_subscription
      - azurerm_resource_group
      - azurerm_linux_web_app
      - azurerm_windows_web_app
      - azurerm_storage_account
      - azurerm_service_plan
      - azurerm_app_service
    severity: HIGH
    matchSpec:
      name: tags
      action: contains
      value: CostCenter
    errorMessage: The required CostCenter tag was missing
    relatedLinks:
      - https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-tagging

I’ve changed the name to tags-resources to make it more descriptive. I’ve also changed the requiredLabels to include the resources I want to check for the tag. This rule will only trigger on the resource types listed under requiredLabels. I’ve also changed the severity to HIGH. I’ve also added a link to the Azure Best Practices for Resource Tagging.

This is saved to .tfsec/custom_tfchecks.yaml. The tfsec vscode extension we installed before will automatically pick up the new rule. We can see it highlighting the code with an issue and showing up in the results screen.

Issues also show up as problems at the bottom of the screen for you to see, click on, and navigate to the right code section.

A Custom Rule for Naming
#

Let’s try something a little more complex. We can try to enforce a naming scheme for our resources. I want to enforce a naming scheme that all resource groups must follow a pattern of rg-app-env-region. This will help us identify which resources belong to which applications or environments. This is a great way to enforce a naming scheme and keep things organized.

- code: rg-naming-pattern
  description: Custom check to check resource group naming
  impact: resource groups should be named consistently
  resolution: use the pattern rg-app-env-region
  requiredTypes:
    - resource
  requiredLabels:
    - azurerm_resource_group
  severity: HIGH
  matchSpec:
    name: name
    action: regexMatches
    value: '^rg-[a-zA-Z]+-[a-zA-Z]+-[a-zA-Z]+'
  errorMessage: improperly named resource group
  relatedLinks:
    - https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming

This rule, only checks against resource groups as identified by the requiredLabels property containing azurerm_resource_group. The matchSpec uses the regexMatches action. I am able to provide my regular expression and the error message. There are a bunch of provided check actions that you can use to develop your custom checks.

Custom Checks for Deprecated Resources
#

Tfsec also allows you to create custom checks for deprecated resources. This is a great way to keep up with the latest changes in Terraform and Azure. I’ve created a custom check for the deprecated azurerm_app_service resource. This resource has been deprecated in favor of azurerm_linux_web_app and azurerm_windows_web_app. I’ve created a custom check to warn us when we use the deprecated resource.

- code: app-service-deprecated
  description: Custom check to warn on deprecated app service
  impact: using deprecated app service resource instead of azurerm_linux_web_app or azurerm_windows_web_app
  resolution: Use azurerm_linux_web_app or azurerm_windows_web_app
  requiredTypes:
    - resource
  requiredLabels:
    - azurerm_app_service
  severity: WARN
  matchSpec:
    name: azurerm_app_service
    action: isPresent
  errorMessage: Using a deprecated resource - azurerm_app_service
  relatedLinks:
    - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service

This time I’m using the isPresent action to check if the resource is present. I’ve also added a link to the documentation for the resource.

Rego Policies
#

What about Rego policies?

We can create a folder to hold all of our rego policies. I created a file called keyvault_softdeleteretentiondays.rego and added the following code.

package custom.azure.keyvault.softdeleteretentiondays

deny[msg] {
    kv := input.azure.keyvault.vaults[_]
    kv.softdeleteretentiondays.value < 14
    msg := "Key Vault Soft Delete Retention Days is less than 14 days"
}

To understand the json input going into the rego policy, you can use the following command:

tfsec --print-rego-input

This will give you a ton of output that you can filter with jq to find the specific resource you are looking for. For example, to find the keyvault resource, you can use the following command:

vscode ➜ /workspaces/secure-terraform-on-azure/ (main) $ tfsec --print-rego-input | jq '.azure.keyvault'{
  "vaults": [
    {
      "__defsec_metadata": {
        "endline": 52,
        "explicit": false,
        "filepath": "workspaces/secure-terraform-on-azure/custom_checks_examples/keyvault/fail/main.tf",
        "managed": true,
        "resource": "azurerm_key_vault.example",
        "startline": 20
      },
      "enablepurgeprotection": {
        "endline": 27,
        "explicit": true,
        "filepath": "workspaces/secure-terraform-on-azure/custom_checks_examples/keyvault/fail/main.tf",
        "managed": true,
        "resource": "azurerm_key_vault.example.purge_protection_enabled",
        "startline": 27,
        "value": true
      },
      "networkacls": {
        "__defsec_metadata": {
          "endline": 34,
          "explicit": false,
          "filepath": "workspaces/secure-terraform-on-azure/custom_checks_examples/keyvault/fail/main.tf",
          "managed": true,
          "resource": "network_acls",
          "startline": 31
        },
        "defaultaction": {
          "endline": 33,
          "explicit": true,
          "filepath": "workspaces/secure-terraform-on-azure/custom_checks_examples/keyvault/fail/main.tf",
          "managed": true,
          "resource": "network_acls.default_action",
          "startline": 33,
          "value": "Deny"
        }
      },
      "softdeleteretentiondays": {
        "endline": 26,
        "explicit": true,
        "filepath": "workspaces/secure-terraform-on-azure/custom_checks_examples/keyvault/fail/main.tf",
        "managed": true,
        "resource": "azurerm_key_vault.example.soft_delete_retention_days",
        "startline": 26,
        "value": 7
      }
    }
  ]
}

I used this output to develop the policy. I had a few issues with the samples from the docs, and there is an open GitHub issue. To run the rego policies with tfsec, you have to pass the --rego-policy-dir command like this:

vscode ➜ /workspaces/secure-terraform-on-azure (main) $ tfsec --rego-policy-dir ./tfsec_rego_policies/ ./custom_checks_examples/keyvault/

Result #1  Key Vault Soft Delete Retention Days is less than 14 days
───────────────────────────────────────────────────────────────────────────────────────────────
───────────────────────────────────────────────────────────────────────────────────────────────
  fail  Rego Package custom.azure.keyvault.softdeleteretentiondays
     Rego Rule deny
───────────────────────────────────────────────────────────────────────────────────────────────

You can see the results of the rego policy in the output.

Conclusion
#

While rego policies support are nice, I think the yaml policies are more flexible and easier to use. Having the ability to use a URL for custom checks allows you to share your checks with others.

I wanted to show how to do checks in Azure because I didn’t see a lot of examples or docs on Azure resources specifically.

I hope this deeper dive into custom checks was helpful.

Related

Secure Terraform - Part 1 - tfsec

This blog was posted as part of the Festive Tech Calendar 2022. I really want to thank the organizers for helping set this up! Gregor Suttie Richard Hooper Keith Atherton Simon Lee Lisa Hoving Look for the hashtag #FestiveTechCalendar2022 on social media! Make sure to check out everyone else’s work when you’re done here This is part 1 of the Secure Terraform series. You can read the series of articles here:

Multiple Domains on GitHub Pages

Something I found out after moving from WordPress to GitHub Pages is that out of the box you can only host a single domain for a repository with GitHub Pages. This is a problem for me because I have a number of domains I was hosting at WordPress that I wanted to point at my GitHub Pages. Official Docs and the limitation # So officially, GitHub pages doesn’t support multiple domains. The docs here https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/troubleshooting-custom-domains-and-github-pages#custom-domain-names-that-are-unsupported state:

Customizing the Jekyll Theme

I haven’t done a lot with jekyll in the past, but I’m a big fan of Markdown everything. For me that usually means I’m taking notes in Markdown Obsidian, doing diagrams in mermaid in Azure DevOps or https://mermaid.live/. I’ve even started turning my talk slides into Markdown with a tool called MARP. Understanding when I use standard Markdown or some sort of templating language (jekyll uses Liquid) has been fun. I’ll do something in HTML or Markdown, then find out that Jekyll or my theme already has helpers to render that (like gists, videos, and figures). Sometimes rendering more advanced things takes a little tweaking of Jekyll and the theme.

Migrating from WordPress to GitHub Pages

I’ve been hosting on WordPress for a while. I wanted something that worked pretty well and was easy to work with. I picked a decent theme, added some plugins, pointed my domains and was up and running. I would work on blogs in Markdown, and then paste the txt into a Markdown. I could upload a few images and move them around in a wysiwyg. Lately, I’ve been doing a lot more in Markdown. All my conference talks were in PowerPoint but I’ve started switching over to Markdown slides using MARP. I should probably do a post on MARP sometime (I did :-) ). I wanted to reduce my overhead of WordPress Hosting and get back into more direct styling and coding of my theme. I decided to switch my hosting to Jekyll on GitHub Pages.

Tools for working with Kubernetes

·226 words·2 mins
I’ve been in a number of internal and external calls where tooling to help work with Kubernetes keeps coming up. I thought I would share some of these cool tools in case you weren’t aware of them. Tools # K9S kubectx and kubens fzf K9S # K9S is a terminal based UI for interacting and managing Kubernetes Clusters. You can find k9s at https://github.com/derailed/k9s or their site https://k9scli.io/.