Skip to content

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.

img/simple-diagram-about-slp_tf-internal-files-usage.png

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

resource "aws_vpc" "main" {
  cidr_block = var.base_cidr_block
}

<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
  # Block body
  <IDENTIFIER> = <EXPRESSION> # Argument
}

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)!

  1. trustzones section defines the TrustZone mapping behavior. At least one TrustZone is needed to be defined
  2. set trustzone[id] value, which also can be used as a reference when setting the parent of a component parent: public-cloud. The id field uniquely identifies a trustzone, and differentiates it from other trustzones of the same type.
  3. set trustzone[name] value
  4. Optional: default trustzone to be used if a component does not define its parent
  5. 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 the id 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)!
    }
  1. special mapping fields $source this attribute is used to configure the mapping behavior which will create a TrustZone
  2. 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
  3. mapping functions $type performs a search along the entire resource list to return the element with the matching type
  4. 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:

  1. The Public Cloud is created as the default TrustZone.
  2. The Internet is created because a resource with resource_type == aws_security_group with resource_properties contains egress[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
[...] Reduced for simplicity
resource "aws_security_group" "VPCssmSecurityGroup" {
  vpc_id        = aws_vpc.CustomVPC.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow all outbound traffic by default"
  }
}
[...] Reduced for simplicity
trustZones:
- id: public-cloud-01
  name: Public Cloud
  type: b61d6911-338d-46a8-9f39-8dcd24abfe91
  risk:
    trustRating: 10

- id: internet-01
  name: Internet
  type: f0ba7722-39b6-4c81-8290-a30a248bb8d9
  risk:
    trustRating: 10
[...] 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})"}}}

  1. set the component[name] value
  2. returns the value multipleSource in case ($singleton && exists more than 1 resource), returns oneSource otherwise.
  3. set the component[parent] as the default configured TrustZone
  4. set the component[tags] value
  5. returns the value multipleSource in case ($singleton && exists more than 1 resource), returns oneSource otherwise.

The values provided in the mapping file have always priority over the template’s ones.

Example

The following configuration:

  - type:        CD-ACM
    $source:     {$singleton: {$type: "aws_acm_certificate"}}
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
    $source:     {$type: "aws_vpc"}
dataflows: []
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: []
resource "aws_vpc" "CustomVPC" {
  cidr_block  = var.vpcCidrblock
}
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)!

  1. special mapping fields specifies an alternative mapping when $source returns no object
  2. special mapping fields set the selected resources as the object to be mapped
  3. special mapping fields specifies the attribute over the one the $mappingLookups logic will be executed
  4. 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
  5. 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)! 
  1. set the component[name] value by type inheritance with the suffix from atlsource.
  2. set the component[type] value by type inheritance.
  3. set the component[tags] value
  4. returns the value multipleSource in case ($singleton && exists more than 1 resource), returns oneSource otherwise.
  5. returns the resource_type as oneSource attribute
  6. returns the "resource_name (resource_type)" as multipleSource 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: []
resource "aws_vpc_endpoint" "s3" {
  vpc_id            = data.aws_vpc.selected.id
  service_name      = "com.amazonaws.${var.region}.s3"
  vpc_endpoint_type = "Interface"

  private_dns_enabled = true
}
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 or aws_elb and sets its parent by its resource_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: []
resource "aws_ecs_task_definition" "service" { }
resource "aws_ecs_service" "mongo" {
  task_definition = aws_ecs_task_definition.service.arn
}
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: []
module "db" {
  source  = "terraform-aws-modules/rds/aws"
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
}

module "alb" {
  source  = "terraform-aws-modules/alb/aws"
}
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}"

  1. dataflows section defines the Dataflows mapping behavior.
  2. set dataflow[name] value
  3. special mapping fields $source this attribute is used to configure the mapping behavior which will create a dataflow
  4. set dataflow[source] value using any attribute available at the $source
  5. set dataflow[destination] value using any attribute available at the $source
  6. 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}"
resource "aws_s3_bucket_logging" "logging" {
  bucket = aws_s3_bucket.bucket.id 
  target_bucket = aws_s3_bucket.log_bucket.id
}
resource "aws_s3_bucket" "bucket" { }
resource "aws_s3_bucket" "log_bucket" { }
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.