Using Terraform Part 6: Virtual Routers

Introduction

In the last installment of this series, I demonstrated how to use Terraform and the Rapid Access Cloud to create a multi-tier virtual network environment. In this part, I’ll show how Virtual Routers can be used to extend that environment.

Virtual Routers

In the last blog post, we created a virtual machine that was connected to two networks. This allowed us to log into the virtual machine via one network and access a second virtual machine on another network:

In the above illustration, the virtual machine that is connected to both the orange and green network can be thought of as a router.

A network router is a device that "routes" traffic from one network to another. Very simplistically, it is kind of like an on-ramp that connects two highways. A virtual router is just a software representation of a hardware router.

With that in mind, let's jump in.

To start, create a directory on your workstation called "vrouter". Inside that directory, create a file called "main.tf". I have laid out below the Terraform code that you will want to copy into this file.

First, we'll create the same security group as we did in the last part of this series:

resource "openstack_networking_secgroup_v2" "sg_1" {
  name        = "sg_1"
  description = "sg_1"
}

resource "openstack_networking_secgroup_rule_v2" "allow-all-tcp-ipv4" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 1
  port_range_max    = 65535
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = "${openstack_networking_secgroup_v2.sg_1.id}"
}

resource "openstack_networking_secgroup_rule_v2" "allow-all-tcp-ipv6" {
  direction         = "ingress"
  ethertype         = "IPv6"
  protocol          = "tcp"
  port_range_min    = 1
  port_range_max    = 65535
  remote_ip_prefix  = "::/0"
  security_group_id = "${openstack_networking_secgroup_v2.sg_1.id}"
}

resource "openstack_networking_secgroup_rule_v2" "allow-all-icmp-ipv4" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "icmp"
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = "${openstack_networking_secgroup_v2.sg_1.id}"
}

resource "openstack_networking_secgroup_rule_v2" "allow-all-icmp-ipv6" {
  direction         = "ingress"
  ethertype         = "IPv6"
  protocol          = "icmp"
  remote_ip_prefix  = "::/0"
  security_group_id = "${openstack_networking_secgroup_v2.sg_1.id}"
}

Previously, we created just one network. Now we'll create two:

resource "openstack_networking_network_v2" "network_1" {
  name           = "network_1"
  admin_state_up = "true"
}

resource "openstack_networking_subnet_v2" "subnet_1" {
  name        = "subnet_1"
  network_id  = "${openstack_networking_network_v2.network_1.id}"
  cidr        = "192.168.100.0/24"
  gateway_ip  = "192.168.100.1"
  ip_version  = 4
  enable_dhcp = true

  host_routes {
    destination_cidr = "192.168.200.0/24"
    next_hop         = "192.168.100.1"
  }
}

resource "openstack_networking_network_v2" "network_2" {
  name           = "network_2"
  admin_state_up = "true"
}

resource "openstack_networking_subnet_v2" "subnet_2" {
  name        = "subnet_2"
  network_id  = "${openstack_networking_network_v2.network_2.id}"
  cidr        = "192.168.200.0/24"
  gateway_ip  = "192.168.200.1"
  ip_version  = 4
  enable_dhcp = true

  host_routes {
    destination_cidr = "192.168.100.0/24"
    next_hop         = "192.168.200.1"
  }
}

There are two things to note about the above networks, particularly with the subnets:

  1. Each one is a different subnet: the first is 192.168.100.0/24 and the second is 192.168.200.0/24. Keep in mind that, without a router, network traffic can only send and receive on the same subnet. Successful communication will show you that the virtual router is working.
  2. Each subnet has a "host route". This tells each subnet how to reach the other.

Next, we'll create a virtual router, and attach the two subnets to the router:

resource "openstack_networking_router_v2" "router" {
  name = "router"
}

resource "openstack_networking_router_interface_v2" "router_interface_1" {
  router_id = "${openstack_networking_router_v2.router.id}"
  subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
}

resource "openstack_networking_router_interface_v2" "router_interface_2" {
  router_id = "${openstack_networking_router_v2.router.id}"
  subnet_id = "${openstack_networking_subnet_v2.subnet_2.id}"
}

Next, let's create a virtual machine. This will be very similar to Part 5 in that it will be connected to two networks. The reason for this is because it's not possible to attach a router to the Rapid Access Cloud's "default" network.

resource "openstack_compute_instance_v2" "instance_1" {
  depends_on = [
    "openstack_networking_subnet_v2.subnet_1",
    "openstack_networking_subnet_v2.subnet_2",
  ]

  name = "instance_1"
  image_name = "Ubuntu 14.04"
  flavor_name = "m1.small"
  key_pair = "CHANGE"
  security_groups = ["${openstack_networking_secgroup_v2.sg_1.id}"]
  user_data = "#!/bin/bash\nifconfig eth1 up\ndhclient eth1"

  network {
    name = "default"
  }

  network {
    name = "${openstack_networking_network_v2.network_1.name}"
  }
}

And for the second virtual machine:

resource "openstack_compute_instance_v2" "instance_2" {
  depends_on = [
    "openstack_networking_subnet_v2.subnet_1",
    "openstack_networking_subnet_v2.subnet_2",
  ]

  name = "instance_2"
  image_name = "Ubuntu 14.04"
  flavor_name = "m1.small"
  key_pair = "CHANGE"
  security_groups = ["${openstack_networking_secgroup_v2.sg_1.id}"]

  network {
    name = "${openstack_networking_network_v2.network_2.name}"
  }
}

And finally, some "outputs":

output "instance_1" {
  value = "${openstack_compute_instance_v2.instance_1.access_ip_v6}"
}

output "instance_2" {
  value = "${openstack_compute_instance_v2.instance_1.network.1.fixed_ip_v4}"
}

With all of the above in place, apply the configuration:

$ cd terraform-vrouter
$ terraform apply

When Terraform has finished, you should see something similar to the following:

Outputs:

instance_1 = [2605:fd00:4:1001:f816:3eff:fe99:971d]
instance_2 = 192.168.200.4

Using the IPv6 address of instance_1, remotely connect to it via SSH. Again, note that you will need IPv6 connectivity to do this step (but you do have IPv6 connectivity, right?).

$ ssh ubuntu@2605:fd00:4:1001:f816:3eff:fe99:971d

Once logged in, verify that you can communicate with instance_2:

ubuntu@instance-1:~$ ping 192.168.200.4
PING 192.168.200.4 (192.168.200.4) 56(84) bytes of data.
64 bytes from 192.168.200.4: icmp_seq=1 ttl=63 time=1.62 ms

The successful ping shows that instance_1 can communicate with instance_2, which is hosted on a different subnet. The virtual router is correctly routing packets from subnet_1 to subnet_2.

Conclusion

In this part of the Using Terraform series, we created a virtual router to route network traffic between two different virtual networks. This can be an effective approach to building complex, multi-tier network architectures in the Rapid Access Cloud.