Series – Get your database running with Terraform part 6: Security Lists

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!

Simo

View Comments

Recent Posts

Connecting to Autonomous Database Running on Google Cloud

Last time I showed how to provision Autonomous Database Serverless (ADB-S) on Google Cloud. This…

1 month ago

Can you believe it? Provisioning Autonomous Database in GCP!

I bet few years back folks didn't expect that by 2024 we would be able…

1 month ago

IP Address Insights with CLI

My previous post on IP Address Insights I mentioned it wasn't yet available with CLI…

6 months ago

Thoughts on Oracle Database@Azure

This will NOT be a technical walkthrough on Oracle Database@Azure but rather my opinions and…

6 months ago

OCI Vulnerability Scanning Setup

Many times when you work for someone, they already have their own vulnerability scanning throughout…

6 months ago

OCI IP Address Insights

Recently OCI announced small but VERY useful service, IP Address Insights. Why this matters? I've…

6 months ago