Monday, April 25, 2016

How to route between two subnets in an AWS VPC w/ Terraform?

Leave a Comment

UPDATE: Been working on this off and on among other things. Cannot seem to get a working config w/ two subnets and an SSH bastion. Placing bounty for a full .tf file config that: * creates two private subnets * creates a bastion * spins an ec2 instance on each subnet configured via the bastion (run some arbitrary shell command via the bastion) * has an internet gateway configured * has a nat gateway for the hosts on the private subnets * has routes and security groups configured accordingly

Original post: I am trying to learn Terraform and build a prototype. I have an AWS VPC configured via Terraform. In addition to a DMZ subnet, I have a public subnet 'web' that receives traffic from the internet. I have a private subnet 'app' that is not accessible from the internet. I am trying to configure a bastion host so that terraform can provision instances on the private 'app' subnet. I have not yet been able to get this to work.

When I ssh in to the bastion, I cannot SSH from the bastion host to any instances within the private subnet. I suspect there is a routing problem. I have been building this prototype via several available examples and the documentation. Many of the examples use slightly different techniques and terraform routing definitions via the aws provider.

Can someone please provide the ideal or proper way to define these three subnets (public 'web', public 'dmz' w/ a bastion, and private 'app') so that instances on the 'web' subnet can access the 'app' subnet and that the bastion host in the DMZ can provision instances in the private 'app' subnet?

A snip of my configs are below:

resource "aws_subnet" "dmz" {     vpc_id = "${aws_vpc.vpc-poc.id}"     cidr_block = "${var.cidr_block_dmz}" }  resource "aws_route_table" "dmz" {     vpc_id = "${aws_vpc.vpc-poc.id}"     route {         cidr_block = "0.0.0.0/0"         gateway_id = "${aws_internet_gateway.gateway.id}"     } }  resource "aws_route_table_association" "dmz" {     subnet_id = "${aws_subnet.dmz.id}"     route_table_id = "${aws_route_table.dmz.id}" }  resource "aws_subnet" "web" {     vpc_id = "${aws_vpc.vpc-poc.id}"     cidr_block = "10.200.2.0/24" }  resource "aws_route_table" "web" {     vpc_id = "${aws_vpc.vpc-poc.id}"     route {         cidr_block = "0.0.0.0/0"         instance_id = "${aws_instance.bastion.id}"     } }  resource "aws_route_table_association" "web" {     subnet_id = "${aws_subnet.web.id}"     route_table_id = "${aws_route_table.web.id}" }  resource "aws_subnet" "app" {     vpc_id = "${aws_vpc.vpc-poc.id}"     cidr_block = "10.200.3.0/24" }  resource "aws_route_table" "app" {     vpc_id = "${aws_vpc.vpc-poc.id}"     route {         cidr_block = "0.0.0.0/0"         instance_id = "${aws_instance.bastion.id}"     } }  resource "aws_route_table_association" "app" {     subnet_id = "${aws_subnet.app.id}"     route_table_id = "${aws_route_table.app.id}" } 

4 Answers

Answers 1

Unless that bastion host is also acting as a NAT (I wouldn't advise you to combine roles on the same instance) then the web and app subnets will not have any outbound web access but otherwise that looks fine routing wise as TF will automatically add a local routing record for the VPC.

As long as you have that local routing record that covers your VPC range then routing should be fine. Taking your Terraform configuration file (and adding the minimum necessary resources) allows me to create some basic instances in all 3 subnets and route between them successfully so you're likely missing something else such as security groups or NACLs.

Answers 2

You haven't given your full Terraform, but you'll need to allow SSH into your 'app' VPC instances from either the bastion IP or the CIDR block of your bastion host, so something like this:

resource "aws_security_group" "allow_ssh" {   name = "allow_ssh"   description = "Allow inbound SSH traffic"    ingress {       from_port = 22       to_port = 22       protocol = "tcp"       cidr_blocks = ["${aws_instance.bastion.private_ip}/32"]   } } 

Then in your 'app' instance resources, you'll want to add the security group:

... vpc_security_group_ids = ["${aws_security_group.allow_ssh.id}"] ... 

https://www.terraform.io/docs/providers/aws/r/security_group_rule.html

Answers 3

You should check the network issues with tcpdump and other debug tools. Please check that:

  1. Ips are reacheble and the network setting are right (e.g. 10.200.2.X can reach ip of the bastion host)
  2. That iptables/another firewall doesn't block your traffic
  3. a ssh server is listen (ssh to an ip of those host from those host)
  4. You have right security groups for hosts (you can see this in proterties of EC2 instances)
  5. Try to sniff the traffic with tcpdump

Answers 4

I don't see a reason for Bastion host.

I have something similar using saltstack, I just control the rest using a master server inside a VPC, assign specific security group to it to allow access.

CIDR X/24 subnetX.0/26- subnet for control server. <aster server ip EC2-subnet1/32 subnetX.64/26 - private minions  subentX.128/26 - public minions subnetX.192/26- private minions  

then create one route table for EACH subnet for your love of isolation Attach each route to individual subnet. E.g.

rt-1  - subnetX.0/26 rt-2  - subnetX.64/26 rt-3  - subnetX.128/26 rt-4  - subnetX.192/26 

make sure your route table has something such this so the route is possible for rt-1 instance connect to everyone

destination: CIDR X/24  Target: local 

Then restrict the connectivity through security group inbound.e.g. allow SSH from EC2-subnet1/32

Once I am done all the work with the control server, I can delete the specific route that say CIDR X/24 Target: local in my public subnet, so it no longer able route traffic to my local CIDR.

There is no reason for me to create complicate bastion since I already given the power to remove route in the control server.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment