Before we can create the subnets we still need to create security lists. They will control what ingress and egress traffic will be allowed within subnets.

I will create two security lists for this project, one for private subnet and another one for public subnet. Security lists can have have multiple rules in them and you can assign multiple security lists to a subnet.

Usually if you are having connection issues first thing I would look is the security list and it’s rules.

By default the rules are stateful so if you have allowed ingress traffic on port 80 you don’t need to open port 80 for the egress traffic. With stateless rules you need to keep it updated both ways.

I will make public security list which allows ingress traffic from anywhere to port 22 (SSH). Usually if I need to access it from outside I restrict it to my corporate CIDR block or then just use my own /32 IP so port is blocked from most of locations.

Traffic will be also allowed from public subnet to private subnet port 22 (SSH) and port 1521 (DB listener) to demonstrate how you can access the database and add multiple rules to security list. Finally all traffic from private subnet to public subnet will be allowed.

Terraform

This time there are more variables to be added into main.tf and variables.tf than just names. We will need to define multiple rules for security list and each rule will contain:

  • Rule stateful/stateleness
  • Source type: CIDR block or Service (for example traffic from service gateway)
  • Source CIDR: Where traffic is coming from
  • Source Service: Only if you are using Source type Service
  • IP protocol: IPv4 or all
  • Source port: Port from where traffic originates from (
    single, range or all ports)
  • Destination port: Where traffic is going to (single, range or all ports)
  • ICMP type and code

Depending on what you are implementing check if you need to have ICMP traffic allowed. We got recommendation that with our VPN connection we should allow all ICMP type traffic for optimal network performance.

// PUBLIC AND PRIVATE SECURITYLIST VARIABLES

variable "public_sl_display_name" {
  default = "PublicSL"
} // Name for the public securitylist

variable "private_sl_display_name" {
  default = "PrivateSL"
} // Name for the private securitylist

variable "egress_destination" {
  default = "0.0.0.0/0"
} // Outside traffic is allowed

variable "tcp_protocol" {
  default = "6"
} // 6 for TCP traffic

variable "public_ssh_sl_source" {
  default = "0.0.0.0/0"
} 

variable "rule_stateless" {
  default = "false"
} // All rules are stateful by default so no need to define rules both ways

variable "public_sl_ssh_tcp_port" {
  default = "22"
} // Open port 22 for SSH access

variable "private_sl_ssh_tcp_port" {
  default = "22"
} // Open port 22 for SSH access

variable "private_sl_db_tcp_port" {
  default = "1521"
} // Open port 1521 for DB listener

And here are above variables used in main.tf. Really nothing complicated but just passing all necessary variables here into use.

resource "oci_core_security_list" "CreatePublicSecurityList" {
  compartment_id = "${oci_identity_compartment.CreateCompartment.id}"
  vcn_id         = "${oci_core_virtual_network.CreateVCN.id}"
  display_name   = "${var.public_sl_display_name}"

  // Allow outbound tcp traffic on all ports
  egress_security_rules {
    destination = "${var.egress_destination}"
    protocol    = "${var.tcp_protocol}"
  }

  // allow inbound ssh traffic from a specific port
  ingress_security_rules = [{
    protocol  = "${var.tcp_protocol}"     // tcp = 6
    source    = "${var.public_ssh_sl_source}" // Can be restricted for specific IP address
    stateless = "${var.rule_stateless}"

    tcp_options {
      // These values correspond to the destination port range.
      "min" = "${var.public_sl_ssh_tcp_port}"
      "max" = "${var.public_sl_ssh_tcp_port}"
    }
  },
    {
      protocol  = "${var.tcp_protocol}"   // tcp = 6
      source    = "${var.vcn_cidr_block}" // open all ports for VCN CIDR and do not block subnet traffic 
      stateless = "${var.rule_stateless}"
    },
  ]
}

resource "oci_core_security_list" "CreatePrivateSecurityList" {
  compartment_id = "${oci_identity_compartment.CreateCompartment.id}"
  vcn_id         = "${oci_core_virtual_network.CreateVCN.id}"
  display_name   = "${var.private_sl_display_name}"

  // Allow outbound tcp traffic on all ports
  egress_security_rules {
    destination = "${var.egress_destination}"
    protocol    = "${var.tcp_protocol}"
  }

  // allow inbound traffic from VCN
  ingress_security_rules = [
    {
      protocol  = "${var.tcp_protocol}"   // tcp = 6
      source    = "${var.vcn_cidr_block}" // open all ports for VCN CIDR and do not block subnet traffic 
      stateless = "${var.rule_stateless}"
      
      tcp_options {
      // These values correspond to the destination port range.
      "min" = "${var.private_sl_ssh_tcp_port}"
      "max" = "${var.private_sl_ssh_tcp_port}"
    }
    },
     {
      protocol  = "${var.tcp_protocol}"   // tcp = 6
      source    = "${var.vcn_cidr_block}" // open all ports for VCN CIDR and do not block subnet traffic 
      stateless = "${var.rule_stateless}"
      
      tcp_options {
      // These values correspond to the destination port range.
      "min" = "${var.private_sl_db_tcp_port}"
      "max" = "${var.private_sl_db_tcp_port}"
    }
    }
  ]
}

I’ve run Terraform plan and apply. Output can be seen below and when I go to verify everything from console I see same rules exist now. You can see from Terraform output that there are multiple rules created.

oci_core_security_list.CreatePrivateSecurityList: Creating...
  compartment_id:                                                      "" => "ocid1.compartment.oc1..xxxxx"
  display_name:                                                        "" => "PrivateSL"
  egress_security_rules.#:                                             "" => "1"
  egress_security_rules.1420396200.destination:                        "" => "0.0.0.0/0"
  egress_security_rules.1420396200.destination_type:                   "" => "<computed>"
  egress_security_rules.1420396200.icmp_options.#:                     "" => "0"
  egress_security_rules.1420396200.protocol:                           "" => "6"
  egress_security_rules.1420396200.stateless:                          "" => "<computed>"
  egress_security_rules.1420396200.tcp_options.#:                      "" => "0"
  egress_security_rules.1420396200.udp_options.#:                      "" => "0"
  freeform_tags.%:                                                     "" => "<computed>"
  ingress_security_rules.#:                                            "" => "2"
  ingress_security_rules.2353706552.icmp_options.#:                    "" => "0"
  ingress_security_rules.2353706552.protocol:                          "" => "6"
  ingress_security_rules.2353706552.source:                            "" => "172.16.0.0/16"
  ingress_security_rules.2353706552.source_type:                       "" => "<computed>"
  ingress_security_rules.2353706552.stateless:                         "" => "false"
  ingress_security_rules.2353706552.tcp_options.#:                     "" => "1"
  ingress_security_rules.2353706552.tcp_options.0.max:                 "" => "1521"
  ingress_security_rules.2353706552.tcp_options.0.min:                 "" => "1521"
  ingress_security_rules.2353706552.tcp_options.0.source_port_range.#: "" => "0"
  ingress_security_rules.2353706552.udp_options.#:                     "" => "0"
  ingress_security_rules.3291565597.icmp_options.#:                    "" => "0"
  ingress_security_rules.3291565597.protocol:                          "" => "6"
  ingress_security_rules.3291565597.source:                            "" => "172.16.0.0/16"
  ingress_security_rules.3291565597.source_type:                       "" => "<computed>"
  ingress_security_rules.3291565597.stateless:                         "" => "false"
  ingress_security_rules.3291565597.tcp_options.#:                     "" => "1"
  ingress_security_rules.3291565597.tcp_options.0.max:                 "" => "22"
  ingress_security_rules.3291565597.tcp_options.0.min:                 "" => "22"
  ingress_security_rules.3291565597.tcp_options.0.source_port_range.#: "" => "0"
  ingress_security_rules.3291565597.udp_options.#:                     "" => "0"
  state:                                                               "" => "<computed>"
  time_created:                                                        "" => "<computed>"
  vcn_id:                                                              "" => "ocid1.vcn.oc1.eu-frankfurt-1.xxxxx"
oci_core_security_list.CreatePublicSecurityList: Creating...
  compartment_id:                                                    "" => "ocid1.compartment.oc1..xxxxx"
  display_name:                                                      "" => "PublicSL"
  egress_security_rules.#:                                           "" => "1"
  egress_security_rules.1420396200.destination:                      "" => "0.0.0.0/0"
  egress_security_rules.1420396200.destination_type:                 "" => "<computed>"
  egress_security_rules.1420396200.icmp_options.#:                   "" => "0"
  egress_security_rules.1420396200.protocol:                         "" => "6"
  egress_security_rules.1420396200.stateless:                        "" => "<computed>"
  egress_security_rules.1420396200.tcp_options.#:                    "" => "0"
  egress_security_rules.1420396200.udp_options.#:                    "" => "0"
  freeform_tags.%:                                                   "" => "<computed>"
  ingress_security_rules.#:                                          "" => "2"
  ingress_security_rules.47193274.icmp_options.#:                    "" => "0"
  ingress_security_rules.47193274.protocol:                          "" => "6"
  ingress_security_rules.47193274.source:                            "" => "0.0.0.0/0"
  ingress_security_rules.47193274.source_type:                       "" => "<computed>"
  ingress_security_rules.47193274.stateless:                         "" => "false"
  ingress_security_rules.47193274.tcp_options.#:                     "" => "1"
  ingress_security_rules.47193274.tcp_options.0.max:                 "" => "22"
  ingress_security_rules.47193274.tcp_options.0.min:                 "" => "22"
  ingress_security_rules.47193274.tcp_options.0.source_port_range.#: "" => "0"
  ingress_security_rules.47193274.udp_options.#:                     "" => "0"
  ingress_security_rules.613850372.icmp_options.#:                   "" => "0"
  ingress_security_rules.613850372.protocol:                         "" => "6"
  ingress_security_rules.613850372.source:                           "" => "172.16.0.0/16"
  ingress_security_rules.613850372.source_type:                      "" => "<computed>"
  ingress_security_rules.613850372.stateless:                        "" => "false"
  ingress_security_rules.613850372.tcp_options.#:                    "" => "0"
  ingress_security_rules.613850372.udp_options.#:                    "" => "0"
  state:                                                             "" => "<computed>"
  time_created:                                                      "" => "<computed>"
  vcn_id:                                                            "" => "ocid1.vcn.oc1.eu-frankfurt-1.xxxxx"
oci_core_security_list.CreatePrivateSecurityList: Creation complete after 1s (ID: ocid1.securitylist.oc1.eu-frankfurt-1.a...xxxxx)
oci_core_security_list.CreatePublicSecurityList: Creation complete after 2s (ID: ocid1.securitylist.oc1.eu-frankfurt-1.a...xxxxx)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Understanding security lists and how they work is essential to create your network configuration so it’s good to put some thought into it so you get everything sorted out properly.

One thing I’d like to point out is that as you can see providing multiple rules to security lists is passed as a list (hence the usage of [] ).

But you can’t pass these through a Terraform module so you end up manually adding necessary rows for new list items. This is one of the biggest things creating unnecessary manual work for us at the moment as hacking the modules when you need to open 4-5 ports comes increasingly difficult and the whole concept of using reusable modules becomes obsolete.

Now since we have security lists completed we are ready to create the subnets next!

One thought on “Series – Get your database running with Terraform part 6: Security Lists”

Leave a Reply

Your email address will not be published. Required fields are marked *