tfsec
IAC scanning tool
What is TFSec?
TFSec is an open-source project from Aqua Security
However, the team is consolidating tfsec into their other solution, Trivy which offers more comprehensive capabilities
Installation
brew install tfsec
Other methods can be found in the official docs
Scanning
Local Scanning
With tfsec installed, we can easily run it to scan a directory of terraform code
tfsec /terraform-directory
# Partial output of results
Result #1 HIGH Bucket does not encrypt data with a customer managed key.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
s3-cis-req.tf:3-15
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
3 β resource "aws_s3_bucket_server_side_encryption_configuration" "s3-encryption-config" {
4 β bucket = aws_s3_bucket.s3-bucket-1.bucket # defines bucket name
5 β
6 β rule {
7 β apply_server_side_encryption_by_default {
8 β sse_algorithm = var.sse-algorithm # defines encryption type
9 β }
10 β }
11 β
..
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ID aws-s3-encryption-customer-key
Impact Using AWS managed keys does not allow for fine grained control
Resolution Enable encryption using customer managed keys
More Information
- https://aquasecurity.github.io/tfsec/v1.28.4/checks/aws/s3/encryption-customer-key/
- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#enable-default-server-side-encryption
[SNIP]
results
ββββββββββββββββββββββββββββββββββββββββββ
passed 9
ignored 0
critical 0
high 7
medium 3
low 1
9 passed, 11 potential problem(s) detected.
CI Job
tfsec can be added as a job to a CI pipeline. For example, the code below can be used for GitLab. Just add it to .gitlab-ci.yml
.
# example leveraging the docker image
stages:
- iac_scanning
- prep
- plan
- deploy
code_scanning:
stage: iac_scanning
image:
name: aquasec/tfsec-ci:latest
script: tfsec . -f markdown --out $CI_PROJECT_DIR/tfsec_findings.md
allow_failure: true
artifacts:
when: always
expire_in: 1 day
paths:
- tfsec_findings.md
# example downloading the tool
stages:
- iac_scanning
- prep
- plan
- deploy
code_scanning:
stage: iac_scanning
image:
name: ubuntu:latest
script:
- apt-get update && apt-get install -y curl
- curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
- tfsec . -f markdown --out $CI_PROJECT_DIR/tfsec_findings.md
allow_failure: true
artifacts:
when: always
expire_in: 1 day
paths:
- tfsec_findings.md
Creating Exceptions
We can easily make exceptions by adding this line of code directly above the resource. Hint, if you use the VS Code extension, you can just click a button to do this for you. When the scan runs again, this finding will be ignored.
#tfsec:ignore:aws-s3-encryption-customer-key
resource "aws_s3_bucket_server_side_encryption_configuration" "tpcrc" {
Additionally, we can set an expiration date to suppress the finding until after the date e.g., Mar 2, 2024.
#tfsec:ignore:aws-s3-encryption-customer-key:exp:2024-03-02
resource "aws_s3_bucket_server_side_encryption_configuration" "tpcrc" {
Config File
The tfsec config file enables us to modify a rule's severity level or exclude a rule from a scan completely. This is useful when a rule is not applicable or the severity should be lowered for a dev environment vs a prod environment.
Overriding the Severity
This rule below is being modified from the original "HIGH" finding to a "LOW" finding.
# tfsec.json
{
"severity_overrides": {
"AWS001": "ERROR",
"aws-s3-encryption-customer-key": "LOW"
}
}
The custom config file can be leveraged during a scan. The next time this finding is discovered, it will be reported with a LOW severity.
tfsec --config-file tfsec.json
Excluding a Rule from a Scan
Sometimes you may want to exclude a rule from a scan completely.
#tfsec.json
{
"severity_overrides": {
"AWS001": "ERROR",
"aws-s3-encryption-customer-key": "LOW"
},
"exclude": [
"AWS002",
"aws-s3-enable-bucket-encryption"
]
}
Again, leveraging the custom config file will result in the rule aws-s3-enable-bucket-encryption
being excluded from the findings.
Custom tfsec Rules
tfsec supports custom rules written in JSON, YAML, or Rego. Custom rules are useful when you want to enforce security policies specific to your organization or when out-of-the-box rules don't meet your requirements.
JSON Format
This rule ensures an S3 bucket has a business owner tag.
# _tfchecks.json
{
"checks": [
{
"code": "No Business Owner Tag - CUS001",
"description": "Custom check to ensure buckets are tagged with a business owner.",
"impact": "By not having a business owner tag, it is difficult to determine who is responsible for the bucket.",
"resolution": "Add the businessOwner tag.",
"requiredTypes": [
"resource"
],
"requiredLabels": [
"aws_s3_bucket"
],
"severity": "CRITICAL",
"matchSpec": {
"name": "tags",
"action": "contains",
"value": "businessOwner"
},
"errorMessage": "The required businessOwner tag was missing."
}
]
}
The custom rule can be leveraged during a scan. This assumes the terraform code and custom rule are in the same directory but this doesn't have to be true.
tfsec --custom-check-dir .
YAML Format
The same rule as above but in YAML.
---
checks:
- code: No Business Owner Tag - CUS001
description: Custom check to ensure buckets are tagged with a business owner.
impact: By not having a business owner tag, it is difficult to determine who is responsible for the bucket.
resolution: Add the businessOwner tag.
requiredTypes:
- resource
requiredLabels:
- aws_s3_bucket
severity: CRITICAL
matchSpec:
name: tags
action: contains
value: businessOwner
errorMessage: The required businessOwner tag was missing.
Rego Format
tfsec allows for writing policies in Rego format but not all arguments are supported. In this case, we cannot write a tfsec rego policy that checks for tags. Under the hood, tfsec is utilizing the open-source project defsec and tags are not supported for an S3 bucket. You can view what is supported here. Additionally, you can see all the values and their current configuration by running this command, tfsec --print-rego-input | jq '.aws.s3.buckets[0]'
which will return an output similar to below.

An example of a supported check would be a Rego policy that checks S3 bucket versioning is enabled.
package custom.aws.s3.versioning_enabled
deny[res] {
bucket := input.aws.s3.buckets[_]
bucket.versioning.enabled.value == false
msg := "Bucket should have versioning enabled."
res := result.new(msg, bucket.versioning.enabled)
}
Useful Resources
Last updated
Was this helpful?