How Mapping File works
This mapping configuration only applies to Terraform Processor.
Please refer to another mapping file configuration documentation if needed. You can locate each processor's documentation in the left menu under the "StartLeft Processors (SLP)" section.
This page is an in-depth explanation of how slp_tf
handles mapping and Terraform resource files to generate an OTM.
This diagram shows how mapping and Terraform resource files are pre-processed to simplify and decouple the Terraform parser logic from the input files.
Loading the Terraform Source File
In this step, the Terraform Loader receives a file written in the Terraform configuration language and generates a Terraform source dictionary with the internal data structure used by the Terraform Parser for compose the OTM.
What is a Terraform Configuration
"A Terraform configuration is a complete document in the Terraform language that tells Terraform how to manage a given collection of infrastructure. A configuration can consist of multiple files and directories."
Terraform language Syntax
The syntax of the Terraform language consists of only a few basic elements
Example for Network topology for Amazon Web Services
The following example describes a simple network topology for Amazon Web Services, just to give you a sense of the overall structure and syntax of the Terraform language. Similar configurations can be created for other virtual network services, using resource types defined by other providers, and a practical network configuration will often contain additional elements not shown here.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 1.0.4"
}
}
}
variable "aws_region" {}
variable "base_cidr_block" {
description = "A /16 CIDR range definition, such as 10.1.0.0/16, that the VPC will use"
default = "10.1.0.0/16"
}
variable "availability_zones" {
description = "A list of availability zones in which to create subnets"
type = list(string)
}
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
# Referencing the base_cidr_block variable allows the network address
# to be changed without modifying the configuration.
cidr_block = var.base_cidr_block
}
resource "aws_subnet" "az" {
# Create one subnet for each given availability zone.
count = length(var.availability_zones)
# For each subnet, use one of the specified availability zones.
availability_zone = var.availability_zones[count.index]
# By referencing the aws_vpc.main object, Terraform knows that the subnet
# must be created only after the VPC is created.
vpc_id = aws_vpc.main.id
# Built-in functions and operators can be used for simple transformations of
# values, such as computing a subnet address. Here we create a /20 prefix for
# each subnet, using consecutive addresses for each availability zone,
# such as 10.1.16.0/20 .
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1)
}
Terraform Source Dictionary
The most important data for the
TerraformParser
is placed into the<BLOCK TYPE>
: Resource Blocks and Module Blocks
This TerraformLoader
takes the Terraform Configuration files and generates the Terraform source dictionary
used to build the OTM.
It is important to notice that the mapping file works always against this data structure and not the against raw
Terraform source.
Terraform Source Dictionary Syntax
This data structure is generated by post-processing the Terraform Configuration files
for adding the values resource_type, resource_name and resource_properties
which will be used to configure the mapping behavior.
Some additional data may exist due to its existence in the original loaded file.
The fields
resource_*
is used for matching and generating the OTM components.
# [...] Reduced for simplicity
resource "aws_vpc" "main" {
cidr_block = var.base_cidr_block
}
resource "aws_subnet" "az" {
count = length(var.availability_zones)
availability_zone = var.availability_zones[count.index]
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1)
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
}
# [...] Reduced for simplicity
# [...] Reduced for simplicity
resource:
- resource_type: aws_vpc
resource_name: main
resource_properties:
cidr_block: '${var.base_cidr_block}'
- resource_type: aws_subnet
resource_name: az
resource_properties:
count: '${length(var.availability_zones)}'
availability_zone: '${var.availability_zones[count.index]}'
vpc_id: '${aws_vpc.main.id}'
cidr_block: '${cidrsubnet(aws_vpc.main.cidr_block,4,count.index + 1)}'
module:
- vpc:
source: terraform-aws-modules/vpc/aws
# [...] Reduced for simplicity
Creating a Terraform Mapping File
This section will explain the three different sections into which a Mapping File is divided.
How To Map TrustZones
The TrustZone is a Threat Modelling concept that will never be present as is in the Terraform
source file. Thus, the slp_tf
relies on the configuration defined in the trustzones
section of the Mapping File.
trustzones: # (1)!
- id: public-cloud-01 # (2)!
name: Public Cloud # (3)!
type: b61d6911-338d-46a8-9f39-8dcd24abfe91 # (5)!
$default: true # (4)!
- trustzones section defines the TrustZone mapping behavior. At least one TrustZone is needed to be defined
- set trustzone[id] value, which also can be used as a reference when setting the parent of a component
parent: public-cloud
. Theid
field uniquely identifies a trustzone, and differentiates it from other trustzones of the same type. - set trustzone[name] value
- Optional: default trustzone to be used if a component does not define its parent
- set trustzone[type] value. For mapping trustzones to IriusRisk trustzones,
type
field must take internal IriusRisk values depending on the type of trustzone.
For the purpose of preserving backwards compatibility, StartLeft also accepts the legacy mapping file format. In this format, there is no
type
field and theid
will be used as both, ID and type.It is not possible to have multiple trustzones of the same type when using this format.
The creation of some TrustZone may depend on the existence of some resources in the original file. To configure this
behavior, the $source
attribute is used.
trustzones:
- id: internet-01
name: Internet
type: f0ba7722-39b6-4c81-8290-a30a248bb8d9
$source: { # (1)!
$singleton: # (2)!
{$type: "aws_security_group", # (3)!
$props: "egress[0].cidr_blocks"} # (4)!
}
- special mapping fields
$source
this attribute is used to configure the mapping behavior which will create a TrustZone - All the matching resources will be unified under a single TrustZone which will be created in case the
{$type: "aws_security_group", $props: "egress[0].cidr_blocks"}
query returns any element - mapping functions
$type
performs a search along the entire resource list to return the element with the matching type - mapping functions
$props
performs a search along the entire resource list to return the element with the matching props
Example for TrustZones Terraform Mapping
In this example, two TrustZones are created:
- The
Public Cloud
is created as the default TrustZone. - The
Internet
is created because a resource withresource_type
==aws_security_group
withresource_properties
containsegress[0].cidr_blocks
is present.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
- id: internet-01
name: Internet
type: f0ba7722-39b6-4c81-8290-a30a248bb8d9
$source: {
$singleton:
{$type: "aws_security_group",
$props: "egress[0].cidr_blocks"}
}
[...] Reduced for simplicity
How To Map Components
This section explains several ways of mapping a resource to an OTM component.
Those components are retrieved by the Terraform Domain-Specific Language
available functions configured in special mapping fields $source
.
We will use the $type
Terraform DSL function in the following examples for simplicity.
Mapping a component
The easiest way to map a component is to define the output type
value and the $source
to specify the source of the object type
Component Template Pattern
To reduce the minimal amount of data needed for mapping files, a template pattern is used to set the common attributes.
name: # (1)!
{$numberOfSources: { # (2)!
oneSource: {$path: "resource_name"},
multipleSource: {$format: "{type} (grouped)"}}} #
parent: {$parent: $default_tz} # (3)!
tags: # (4)!
- {$numberOfSources: { # (5)!
oneSource: {$path: "resource_type"},
multipleSource: {$format: "{resource_name} ({resource_type})"}}}
- set the component[name] value
- returns the value
multipleSource
in case ($singleton
&& exists more than 1 resource), returnsoneSource
otherwise. - set the component[parent] as the default configured TrustZone
- set the component[tags] value
- returns the value
multipleSource
in case ($singleton
&& exists more than 1 resource), returnsoneSource
otherwise.
The values provided in the mapping file have always priority over the template’s ones.
Example
The following configuration:
would be internally converted to: - type: CD-ACM
name: {$numberOfSources: {
oneSource: {$path: "resource_name"},
multipleSource: {$format: "{type} (grouped)"}}}
$source: {$singleton: {$type: "aws_acm_certificate"}}
# note: the TZ id is the configured as default
parent: {$parent: public-cloud-01}
tags:
- {$numberOfSources: {
oneSource: {$path: "resource_type"},
multipleSource: {$format: "{resource_name} ({resource_type})"}}}
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: vpc
name: {$numberOfSources: {
oneSource: {$path: "resource_name"},
multipleSource: {$format: "{type} (grouped)"}}}
$source: {$type: "aws_vpc"}
parent: {$parent: public-cloud-01}
tags:
- {$numberOfSources: {
oneSource: {$path: "resource_type"},
multipleSource: {$format: "{resource_name} ({resource_type})"}}}
dataflows: []
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
risk:
trustRating: 10
components:
- id: public-cloud-01.aws_vpc-customvpc
name: CustomVPC
type: vpc
parent:
trustZone: public-cloud-01
tags:
- aws_vpc
dataflows: []
The component identifier is generated internally by the
slp_tf
.
Mapping an AltSource
$altsource is a special mapping field that specifies an alternative mapping when $source returns nothing.
This is the minimal configuration needed to configure an $altsource, the following Example configures an altsource for aws_s3_bucket
- type: s3
$source: {$type: "aws_s3_bucket"}
$altsource: # (1)!
- $mappingType: {$type: "aws_vpc_endpoint"} # (2)!
$mappingPath: {$path: "*.service_name | [0]"} # (3)!
$mappingLookups: # (4)!
- regex: ^(.*)s3$ # (5)!
- special mapping fields specifies an alternative mapping when $source returns no object
- special mapping fields set the selected resources as the object to be mapped
- special mapping fields specifies the attribute over the one the $mappingLookups logic will be executed
- contains a set of regular expressions to search inside the string defined in $mappingType and $mappingPath. This field may be multiple because there may be more than one service to infer
- if regex match with the $mappingPath the component is created
This configuration maps a s3 by the existence of a resource with
resource_type == aws_vpc_endpoint
and*.service_name[0] match regex ^(.*)s3$
but it is only executed in case none resource of aws_s3_bucket type exists.
AltSource Template Pattern
For reducing the minimal amount of data needed for mapping file, a template pattern is used to set the common attributes for the $mappingLookups attribute.
$altsource:
- $mappingLookups:
- name: "{mapping['type']} from altsource" # (1)!
type: "{mapping['type']}" # (2)!
tags: # (3)!
- {$numberOfSources: # (4)!
{oneSource: {$path: "resource_type"}, # (5)!
multipleSource: {$format: "{resource_name} ({resource_type})"}}} # (6)!
- set the component[name] value by type inheritance with the suffix
from atlsource
. - set the component[type] value by type inheritance.
- set the component[tags] value
- returns the value
multipleSource
in case ($singleton
&& exists more than 1 resource), returnsoneSource
otherwise. - returns the
resource_type
asoneSource
attribute - returns the "
resource_name (resource_type)
" asmultipleSource
attribute
The values provided in the mapping file are always preferred over the template’s ones.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: s3
$source: {$type: "aws_s3_bucket"}
$altsource:
- $mappingType: {$type: "aws_vpc_endpoint"}
$mappingPath: {$path: "*.service_name | [0]"}
$mappingLookups:
- regex: ^(.*)s3$
name: S3 from VPCEndpoint
dataflows: []
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: s3
name: {$numberOfSources: {
oneSource: {$path: "resource_name"},
multipleSource: {$format: "{type} (grouped)"}}}
$source: {$type: "aws_s3_bucket"}
$altsource:
- $mappingType: {$type: "aws_vpc_endpoint"}
$mappingPath: {$path: "*.service_name | [0]"}
$mappingLookups:
- regex: ^(.*)s3$
name: S3 from VPCEndpoint
type: s3
tags:
- {$numberOfSources:
{oneSource:
{$path: "resource_type"},
multipleSource:
{$format: "{resource_name} ({resource_type})"}}}
parent: {$parent: public-cloud-01}
tags:
- {$numberOfSources: {
oneSource: {$path: "resource_type"},
multipleSource: {$format: "{resource_name} ({resource_type})"}}}
dataflows: []
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
risk:
trustRating: 10
components:
- id: public-cloud-01.aws_vpc_endpoint-s3-altsource
name: S3 from VPCEndpoint
type: s3
parent:
trustZone: public-cloud-01
tags:
- aws_vpc_endpoint
dataflows: []
Note that the template component name is override for $mappingLookups[].name on the Mapping File.
Mapping a Parent
The order of the components is important because parent components must be defined before child components.
When mapping a component, the default TrustZone is set to the parent component value by the component template pattern,
nevertheless, it can be modified to set other TrustZones or Component with the attribute component['parent']
in the mapping file.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: empty-component
$source: {$type: "aws_subnet"}
- type: load-balancer
$source: {$type: ["aws_lb", "aws_elb"]}
parent: {$path: "resource_properties.subnets"}
dataflows: []
resource "aws_subnet" "PrivateSubnet1" {
vpc_id = aws_vpc.CustomVPC.id
cidr_block = "10.0.2.0/24"
}
resource "aws_subnet" "PrivateSubnet2" {
vpc_id = aws_vpc.CustomVPC.id
cidr_block = "10.0.3.0/24"
}
resource "aws_lb" "ServiceLB" {
subnets = [aws_subnet.PrivateSubnet1.id, aws_subnet.PrivateSubnet2.id]
}
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
risk:
trustRating: 10
components:
- id: public-cloud-01.aws_subnet-privatesubnet1
name: PrivateSubnet1
type: empty-component
parent:
trustZone: public-cloud-01
tags:
- aws_subnet
- id: public-cloud-01.aws_subnet-privatesubnet2
name: PrivateSubnet2
type: empty-component
parent:
trustZone: public-cloud-01
tags:
- aws_subnet
- id: >-
public-cloud-01.aws_subnet-privatesubnet1.aws_lb-servicelb
name: ServiceLB
type: load-balancer
parent:
component: public-cloud-01.aws_subnet-privatesubnet1
tags:
- aws_lb
- id: >-
public-cloud-01.aws_subnet-privatesubnet2.aws_lb-servicelb
name: ServiceLB
type: load-balancer
parent:
component: public-cloud-01.aws_subnet-privatesubnet2
tags:
- aws_lb
dataflows: []
This mapper defines a load-balancer by resources of type
aws_lb
oraws_elb
and sets its parent by itsresource_properties.subnets
value. This component will be created as many times as subnets exist.
Mapping a Children
The order of the components is important because parent components must be defined before child components.
When mapping a component, it can define what components are their children on the OTM. $children will set the parent attribute of those components on the OTM.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: elastic-container-service
$source: {$type: "aws_ecs_service"}
parent: {$path: "resource_properties.network_configuration[0].subnets"}
$children: {$path: "resource_properties.task_definition|split(@, '.')[1]"}
- type: docker-container
$source: {$type: "aws_ecs_task_definition"}
dataflows: []
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
risk:
trustRating: 10
components:
- id: public-cloud-01.aws_ecs_service-mongo
name: mongo
type: elastic-container-service
parent:
trustZone: public-cloud-01
tags:
- aws_ecs_service
- id: public-cloud-01.aws_ecs_service-mongo.aws_ecs_task_definition-service
name: service
type: docker-container
parent:
component: public-cloud-01.aws_ecs_service-mongo
tags:
- aws_ecs_task_definition
dataflows: []
Mapping a Module
The modules imported in a Terraform file can be mapped into OTM components using the special mapping field
$module
to match the module[].source
value.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: rds
$source: {$module: "terraform-aws-modules/rds/aws"}
- type: vpc
$source: {$module: "terraform-aws-modules/vpc/aws"}
- type: load-balancer
$source: {$module: "terraform-aws-modules/alb/aws"}
dataflows: []
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
risk:
trustRating: 10
components:
- id: public-cloud-01.db
name: db
type: rds
parent:
trustZone: public-cloud-01
tags:
- terraform-aws-modules/rds/aws
- id: public-cloud-01.vpc
name: vpc
type: vpc
parent:
trustZone: public-cloud-01
tags:
- terraform-aws-modules/vpc/aws
- id: public-cloud-01.alb
name: alb
type: load-balancer
parent:
trustZone: public-cloud-01
tags:
- terraform-aws-modules/alb/aws
dataflows: []
How To Map Dataflows
The Dataflows is a Threat Modelling concept that will never be present as is in the Terraform
source file. Thus, the slp_tf
relies on the configuration defined in the dataflows
section of the Mapping File.
dataflows: # (1)!
- name: {$format: "S3 dataflow from {resource_type}"} # (2)!
$source: {$type: "aws_s3_bucket_logging"} # (3)!
source: {$path: "resource_properties.bucket" } # (4)!
destination: {$path: "resource_properties.target_bucket"} # (5)!
tags: # (6)!
- $format: "Dataflow created by {resource_type}-{resource_name}"
- dataflows section defines the Dataflows mapping behavior.
- set dataflow[name] value
- special mapping fields
$source
this attribute is used to configure the mapping behavior which will create a dataflow - set dataflow[source] value using any attribute available at the $source
- set dataflow[destination] value using any attribute available at the $source
- Optional: set component[tag] value
The mapping name attribute is optional, resource_name will use as default
The creation of some dataflow may depend on the existence of some resources in the original file. To configure this
behavior, the $source
attribute is used.
trustzones:
- id: public-cloud-01
name: Public Cloud
type: b61d6911-338d-46a8-9f39-8dcd24abfe91
$default: true
components:
- type: s3
$source: {$type: "aws_s3_bucket"}
dataflows:
- name: {$format: "S3 dataflow from {resource_type}"}
$source: {$type: "aws_s3_bucket_logging"}
source: {$path: "resource_properties.bucket" }
destination: {$path: "resource_properties.target_bucket"}
tags:
- $format: "Dataflow created by {resource_type}-{resource_name}"
otmVersion: 0.1.0
project:
name: name
id: id
representations:
- name: Terraform
id: Terraform
type: code
trustZones:
- id: b61d6911-338d-46a8-9f39-8dcd24abfe91
name: Public Cloud
risk:
trustRating: 10
components:
- id: public-cloud-01.aws_s3_bucket-bucket
name: bucket
type: s3
parent:
trustZone: b61d6911-338d-46a8-9f39-8dcd24abfe91
tags:
- aws_s3_bucket
- id: public-cloud-01.aws_s3_bucket-log_bucket
name: log_bucket
type: s3
parent:
trustZone: b61d6911-338d-46a8-9f39-8dcd24abfe91
tags:
- aws_s3_bucket
dataflows:
- id: 591089c3-9dc1-42c0-a574-3674f2d9deaf
name: S3 dataflow from aws_s3_bucket_logging
source: public-cloud-01.aws_s3_bucket-bucket
destination: public-cloud-01.aws_s3_bucket-log_bucket
tags:
- Dataflow created by aws_s3_bucket_logging-logging
The dataflow identifier is generated internally by the
slp_tf
.
As Terraform Dataflows may be a complicated concept, refer to the How Dataflow Mapping works section for deeper explanation.