Terraform: Created NAT gateway does not have any security groups

Created on 26 Oct 2016  ·  11Comments  ·  Source: hashicorp/terraform

I'm trying to setup a NAT gateway on one of my subnets but "it" is not working (whatever "it" is :).

The GW is created just fine, but I can't access the 'Net from an instance on the same subnet..

Looking at https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html, the very last lines talks about adding a security group to the network interface.

Looking at _my_ gw, it doesn't have _any_ security groups.

Trying to add one manually, I get "You do not have permission to access the specified resource.".

Terraform Version

0.7.5

Affected Resource(s)

  • aws_nat_gateway

    Expected Behavior

Not quite sure, but shouldn't there be a way to attach a security group to the network interface that's created by aws_nat_gateway?
Or is this expected behaviour? But if so, how do you control what the NAT let's through (and from where)?

Actual Behavior

NAT gw doesn't work, won't let traffic out.

bug documentation

Most helpful comment

Yes, that what it turned out as. But I didn't understand that sentence to begin with, it took _A LOT_ of trial and errors before i understood it (properly).

But I guess your sentence is correct. But can we add a simple example? The biggest problem I think I had was that I didn't have (or need nor want) two networks - a public and a private. I only have private services in that environment, so creating a second didn't make much sense to me.

But it turns out you absolutly have to (!!), because one (sub)network needs to have the route to the IGW and the other(s) need a route to the NAT, and the NAT needs to be on the one with the route to the IGW..

I just decided not to put any instances on the __public_ one..

How about an example like this:

resource "aws_vpc" "test" {
  cidr_block             = "10.0.0.0/16"
  // VPC options
}

# --- P U B L I C

# The subnet to route to the IGW and attach the NAT on.
# => This will get a route to 0/0 to the IGW gateway below.
resource "aws_subnet" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  cidr_block             = "10.0.2.0/24"
  // SUBNET options
}

# The IGW - attached to the VPC and with the 'aws_main_route_table_association'
# below, will be the default exit/entry point.
# Technically, this neither _public_ nor _private_, but because of the default route,
# it's in the _public_ section. 
resource "aws_internet_gateway" "test" {
  vpc_id                 = "${aws_vpc.test.id}"
  // IGW options
}

# Create a route table that will be attached to the _public_ network.
resource "aws_route_table" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  // ROUTE TABLE options - don't use the "route { }" block! See the 'notes' above.
}

# Attach a route to 0/0 to the public route table going to the IGW.
resource "aws_route" "test_public" {
  route_table_id         = "${aws_route_table.test_public.id}"
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = "${aws_internet_gateway.test.id}"
}

# Associate the _public_ route table with the _public_ network.
resource "aws_route_table_association" "test_public" {
  subnet_id              = "${aws_subnet.test_public.id}"
  route_table_id         = "${aws_route_table.test_public.id}"
}

# The public needs to be the default one (so that the default
# route on the VPC goes to the IGW).
resource "aws_main_route_table_association" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  route_table_id         = "${aws_route_table.test_public.id}"
}

# A EIP for the NAT gateway.
resource "aws_eip" "test" {
  vpc                    = "true"
  // NAT options
}

# The NAT gateway, attached to the _public_ network.
resource "aws_nat_gateway" "test" {
  allocation_id          = "${aws_eip.test.id}"
  subnet_id              = "${aws_subnet.test_public.id}" # MUST be the public one!
  depends_on             = ["aws_internet_gateway.test"]
}

# --- P R I V A T E

# The subnet which need NAT access
# => This will get a route to 0/0 to the NAT gateway created above.
resource "aws_subnet" "test_private" {
  vpc_id                 = "${aws_vpc.test.id}"
  cidr_block             = "10.0.1.0/24"
  // SUBNET options
}

# Create a route table that will be attached to the _private_ network.
resource "aws_route_table" "test_private" {
  vpc_id                 = "${aws_vpc.test.id}"
  // ROUTE TABLE options - don't use the "route { }" block! See the 'notes' above.
}

# Attach a route to 0/0 to the _private_ route table going to the NAT gateway.
resource "aws_route" "test_private" {
  route_table_id         = "${aws_route_table.test_private.id}"
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = "${aws_nat_gateway.test.id}"
}


# Associate the _private_ route table with the _private_ network.
resource "aws_route_table_association" "test_private" {
  subnet_id              = "${aws_subnet.test_private.id}"
  route_table_id         = "${aws_route_table.test_private.id}"
}

Actually, this turned out to be anything but simple! But illustrate how darn difficult it is, and why I had so much problem with it!

This example is paraphrased from my own setup, but I think that's it..

I asked about this on the AWS forum as well (https://forums.aws.amazon.com/thread.jspa?messageID=748601&tstart=0#748601) and my conclusion was (I already had three private networks and a IGW):

  1. Create a fourth network (10.200.0.192/26) - Calling this the "public" ones (won't have any instances or anything in it, just a means to attach the NAT and IGW to).
  2. Make this network the default network.
  3. Create a new route table.
  4. Associate this route table with the network from point 1.
  5. Create a route "0/0 -> igw-blabla".
  6. Create a new NAT gateway.
  7. Associate this NAT with the network from point 1.
  8. Create a route "0/0 -> nat-blabla".
  9. Associate that last route entry with the route tables for my other subnets (i.e., the private ones).

The 'secret' (which isn't documented anywhere that I know of) was point two here - the 0/0 to IGW route needs to be the _default_ network..

All 11 comments

@FransUrbo are you creating a aws_eip resource and then assigning this to the aws_nat_gateway resource using the allocation_id parameter (https://www.terraform.io/docs/providers/aws/r/nat_gateway.html#allocation_id)?

Yes.

Discovered the problem. I had added the wrong subnet for the aws_nat_gateway. I put one of the _private_ ones there. Should have been the _public_!

Maybe reopen this and retagg it as documentation?

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

Something like:

subnet_id - (Required) The Subnet ID of the subnet in which to place the gateway. This needs to be a subnet where a aws_internet_gateway resides.

@FransUrbo an Internet Gateway doesn't reside in a subnet. Do you mean a subnet that has a routetable entry that points directly to the Internet Gateway?

P.

Yes, that what it turned out as. But I didn't understand that sentence to begin with, it took _A LOT_ of trial and errors before i understood it (properly).

But I guess your sentence is correct. But can we add a simple example? The biggest problem I think I had was that I didn't have (or need nor want) two networks - a public and a private. I only have private services in that environment, so creating a second didn't make much sense to me.

But it turns out you absolutly have to (!!), because one (sub)network needs to have the route to the IGW and the other(s) need a route to the NAT, and the NAT needs to be on the one with the route to the IGW..

I just decided not to put any instances on the __public_ one..

How about an example like this:

resource "aws_vpc" "test" {
  cidr_block             = "10.0.0.0/16"
  // VPC options
}

# --- P U B L I C

# The subnet to route to the IGW and attach the NAT on.
# => This will get a route to 0/0 to the IGW gateway below.
resource "aws_subnet" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  cidr_block             = "10.0.2.0/24"
  // SUBNET options
}

# The IGW - attached to the VPC and with the 'aws_main_route_table_association'
# below, will be the default exit/entry point.
# Technically, this neither _public_ nor _private_, but because of the default route,
# it's in the _public_ section. 
resource "aws_internet_gateway" "test" {
  vpc_id                 = "${aws_vpc.test.id}"
  // IGW options
}

# Create a route table that will be attached to the _public_ network.
resource "aws_route_table" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  // ROUTE TABLE options - don't use the "route { }" block! See the 'notes' above.
}

# Attach a route to 0/0 to the public route table going to the IGW.
resource "aws_route" "test_public" {
  route_table_id         = "${aws_route_table.test_public.id}"
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = "${aws_internet_gateway.test.id}"
}

# Associate the _public_ route table with the _public_ network.
resource "aws_route_table_association" "test_public" {
  subnet_id              = "${aws_subnet.test_public.id}"
  route_table_id         = "${aws_route_table.test_public.id}"
}

# The public needs to be the default one (so that the default
# route on the VPC goes to the IGW).
resource "aws_main_route_table_association" "test_public" {
  vpc_id                 = "${aws_vpc.test.id}"
  route_table_id         = "${aws_route_table.test_public.id}"
}

# A EIP for the NAT gateway.
resource "aws_eip" "test" {
  vpc                    = "true"
  // NAT options
}

# The NAT gateway, attached to the _public_ network.
resource "aws_nat_gateway" "test" {
  allocation_id          = "${aws_eip.test.id}"
  subnet_id              = "${aws_subnet.test_public.id}" # MUST be the public one!
  depends_on             = ["aws_internet_gateway.test"]
}

# --- P R I V A T E

# The subnet which need NAT access
# => This will get a route to 0/0 to the NAT gateway created above.
resource "aws_subnet" "test_private" {
  vpc_id                 = "${aws_vpc.test.id}"
  cidr_block             = "10.0.1.0/24"
  // SUBNET options
}

# Create a route table that will be attached to the _private_ network.
resource "aws_route_table" "test_private" {
  vpc_id                 = "${aws_vpc.test.id}"
  // ROUTE TABLE options - don't use the "route { }" block! See the 'notes' above.
}

# Attach a route to 0/0 to the _private_ route table going to the NAT gateway.
resource "aws_route" "test_private" {
  route_table_id         = "${aws_route_table.test_private.id}"
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = "${aws_nat_gateway.test.id}"
}


# Associate the _private_ route table with the _private_ network.
resource "aws_route_table_association" "test_private" {
  subnet_id              = "${aws_subnet.test_private.id}"
  route_table_id         = "${aws_route_table.test_private.id}"
}

Actually, this turned out to be anything but simple! But illustrate how darn difficult it is, and why I had so much problem with it!

This example is paraphrased from my own setup, but I think that's it..

I asked about this on the AWS forum as well (https://forums.aws.amazon.com/thread.jspa?messageID=748601&tstart=0#748601) and my conclusion was (I already had three private networks and a IGW):

  1. Create a fourth network (10.200.0.192/26) - Calling this the "public" ones (won't have any instances or anything in it, just a means to attach the NAT and IGW to).
  2. Make this network the default network.
  3. Create a new route table.
  4. Associate this route table with the network from point 1.
  5. Create a route "0/0 -> igw-blabla".
  6. Create a new NAT gateway.
  7. Associate this NAT with the network from point 1.
  8. Create a route "0/0 -> nat-blabla".
  9. Associate that last route entry with the route tables for my other subnets (i.e., the private ones).

The 'secret' (which isn't documented anywhere that I know of) was point two here - the 0/0 to IGW route needs to be the _default_ network..

Could you remove the bug and provider/aws labels? It turned out that the NAT don't _need_ any SGs.

Instead, I recommend updating the documentation (i.e., add the label documentation) instead.

@FransUrbo you're a legend for providing that example above, thanks 👍

@stack72 Could you please update the labels on this? See https://github.com/hashicorp/terraform/issues/9625#issuecomment-256640877

@FransUrbo Those 10 steps should be saved in the paradise. Finally it is now working.
Thanks so much.

Hi folks, thanks for reporting this and including your findings! The provider documentation now lives in the aws provider repository, so if there is more work needed to update this document, please open an issue there.

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings