aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.35k stars 3.77k forks source link

šŸ“ŠTracking: AWS Network Firewall #11925

Open SomayaB opened 3 years ago

SomayaB commented 3 years ago

Add your +1 šŸ‘ to help us prioritize high-level constructs for this service


Overview:

AWS Network Firewall is a stateful, managed, network firewall and intrusion detection and prevention service for your virtual private cloud (VPC) that you created in Amazon Virtual Private Cloud (Amazon VPC).

AWS Docs

Maturity: CloudFormation Resources Only

See the AWS Construct Library Module Lifecycle doc for more information about maturity levels.

Implementation:

See the CDK API Reference for more implementation details.

Issue list:


This is a šŸ“ŠTracking Issue

bf-sodle commented 3 years ago

I'm not sure if this is the right place to say this, or if I need to start a new issue. But the CfnFirewallPolicy construct is not deployable in its current state. It synthesizes a template that the CloudFormation service will not accept, likely due to an inconsistency in the CloudFormation docs for this resource.

nicoaws commented 3 years ago

it's also very hard to extract the EndpointId List (with correspondent AZs) out of the CfnFirewall construct

vennemp commented 2 years ago

@nicoaws I had to write custom resource to help get around that - it takes the public subnet, looks up az and matches it with the vpc endpoint created by the firewall - this is so you can reference it properly in the route tables. Just invoke it once per subnet/az and save the output for later reference.

My problem at the moment is the creation of the nat gateways in those public subnets.

I initially created 3 subnet groups of type PRIVATE_ISOLATED and would just create the routes manually:

  1. Firewall
  2. Public
  3. TGW-Attach (this firewall will be centralized ingress/egress - opted for this method instead of firewall endpoints in multiple vpcs since that would be more expensive imo).

And then tried to add NAT gateway to the Public group. BUT for some reason the SubnetSelector is not iterable. Forgive me i'm new to TS, JS etc so seemed like a deal-breaker in this method - though it was odd. So I am currently going another route.

    const pubsubnets = vpc.selectSubnets({
      subnetGroupName: 'public'
    });

  for (let subnet of pubsubnets)
  {
    new CfnNatGateway(this, 'nat '+subnet.subnetId , {
      subnetId: subnet.subnetId
    })
  }
Type 'SelectedSubnets' must have a '[Symbol.iterator]()' method that returns an iterator.

My current path is to make the public and tgw-attach subnet groups PUBLIC and PRIVATE_WITH_NAT respectively.

However, this requires me to update the default route of the "public" route tables to point to their respective vpce not the igw. I thought this out before going thru the above method and thought this second way would be more involved. Looks like another custom resource. šŸ˜‘

Edit: Just remembered that CDK offers a construct that allows you to write simple AWS API call custom resources natively - without having to worry about to creating and testing the lambda. This will be great for latter - since i am just doing deleteroute to igw and addroute to vpce.

bmorrissirromb commented 2 years ago

Edit: Just remembered that CDK offers a construct that allows you to write simple AWS API call custom resources natively - without having to worry about to creating and testing the lambda. This will be great for latter - since i am just doing deleteroute to igw and addroute to vpce.

@vennemp Any chance you could share the CDK code you created to natively create the custom resource?

vennemp commented 2 years ago

I published it on construct hub

https://constructs.dev/packages/cdk-nwfirewall/v/0.0.7?lang=typescript

roberthutto commented 2 years ago

And then tried to add NAT gateway to the Public group. BUT for some reason the SubnetSelector is not iterable. Forgive me i'm new to TS, JS etc so seemed like a deal-breaker in this method - though it was odd. So I am currently going another route.

    const pubsubnets = vpc.selectSubnets({
      subnetGroupName: 'public'
    });

  for (let subnet of pubsubnets)
  {
    new CfnNatGateway(this, 'nat '+subnet.subnetId , {
      subnetId: subnet.subnetId
    })
  }
Type 'SelectedSubnets' must have a '[Symbol.iterator]()' method that returns an iterator.

SelectedSubnets is not iterable but it has 2 members that are

readonly subnetIds: string[];
readonly subnets: ISubnet[];
vennemp commented 2 years ago

Thank you!

prli commented 6 months ago

trying to get endpointId with its AZ was a pain to work with. I posted in this related issue earlier.

for anyone looking to sort endpointIds by AZ, and not involve a CustomResource lambda, check out this stackoverflow for details. the idea is to use Fn.join and Fn.split to do some sorting.

posting the sorting function below as well.

/** this is actually some voodoo magic here...
 * we select firewall subnet from vpc, assuming (hoping...) vpc.availabilityZones are in order of AZs
 * this means we require `firewall.attrEndpointIds` to be in order.
 * however, `firewall.attrEndpointIds` is not in order for some inexplicable reasons.
 * https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-networkfirewall/issues/15
 * `firewall.attrEndpointIds` is a List-encoded tokens - https://docs.aws.amazon.com/cdk/v2/guide/tokens.html#tokens_list
 * in order to sort `firewall.attrEndpointIds` in order of AZs, we need to use a combination of splits and joins.
 * AWS AZs are ordered lexicographically starting at `a`, and "assuming" vpc.availabilityZones are returned in order of AZs
 * to sort `firewall.attrEndpointIds` as a List-encoded tokens, the following steps are taken:
 * 1. join `firewall.attrEndpointIds` as a string token with `,` as delimiter.
 *   -> "us-west-2c:vpce-111122223333,us-west-2a:vpce-987654321098,us-west-2b:vpce-012345678901"
 * 2. split the resulting string token `azWithFirewallVpcEndpointIds` with `a:` for the first AZ (`b:` and `c:` subsequently).
 *   -> [ "us-west-2c:vpce-111122223333,us-west-2", "vpce-987654321098,us-west-2b:vpce-012345678901" ]
 * 3. select 2nd element as it contains the endpoint ID, along with some extra garbage and store it into var `temp`
 *   -> "vpce-987654321098,us-west-2b:vpce-012345678901"
 * 4. split `temp` by `,` as that was the joined list delimiter
 *   -> [ "vpce-987654321098", "us-west-2b:vpce-012345678901" ]
 * 5. select 1st element as it contains the endpoint ID without any extra garbage now
 *   -> "vpce-987654321098"
 * https://carriagereturn.nl/aws/cloudformation/firewall/endpoint/routing/2022/05/22/firewall-endpoints.html
 */
getFirewallEndpointsInOrder(firewall: fw.CfnFirewall): string[] {
    const firewallVpcEndpointIds: string[] = [];
    const azWithFirewallVpcEndpointIds = cdk.Fn.join(',', firewall.attrEndpointIds);
    const aZs = ['a', 'b', 'c', 'd', 'e', 'f']; // max amount of AZs in AWS (us-east-1)
    for (let i = 0; i < this.vpc.availabilityZones.length; i++) {
        const temp = cdk.Fn.split(`${aZs[i]}:`, azWithFirewallVpcEndpointIds, 2)[1];
        const firewallVpcEndpointId = cdk.Fn.split(',', temp, 2)[0];
        firewallVpcEndpointIds.push(firewallVpcEndpointId);
    }
    return firewallVpcEndpointIds;
}