vpclattice – the network* when you’re not having a network. with aws cdk.

Preamble and Waffle:

This is an early peek at using vpclattice with cdk, while we do the dance with getting a contruct into the aws-cdk-lib project
. Its a very much in progress piece of work, and there is a 99.99% chance that there will be breaking changes before this goes GA. Please be aware of this, when you use it. It is also an opportunity to provide feedback while the API is solidified.

Many modern ( and not so modern ) applications are built using modular and distributed components. Each implementing a set of functionalities. In order to make those components do useful things you need to allow them to communicate with each other. That normally means building out a network, which is a whole set of skills and expertise over the top of building the application. At re-invent ’22 Aws announced vpc-lattice, which is now in GA status.

In a nutshell VPC Lattice, can provide a way to connect, secure and monitor the connections between services that you’ve created in AWS, without having to create a lot of complex network interconnects. Its not quite no network, but its getting close.

It can cross account and vpc boundaries, but remember it is a regional only service. And services is highlighted because, this is not intended for human to service communications, its service to service communications. Also its only for things that work using http, https or gprc. If you can live inside those restrictions, then vpc-lattice might be useful for you.

vpc-lattice is application networking, operating at layer 7. It lets you define policy for access with IAM, it deals with the complexity of Ipv4 and ipv6 translations and overlapping address spaces. It lets you set up quite complex traffic routing based on request characteristics, with weighted groups for blue/green or canary deployments, and you can mix up different types of sources such as lambda and container clusters. Its flexible. It also is quite possible to have it sitting side by side with an existing network, so staged migrations could be quite feasible. Because VPC Lattice provides its own transport fabric so, you dont’ need to peer vpcs, create transitgateways or other network trickery.

How Amazon VPC Lattice Works ( the very short version )
At the top ‘layer’ of VPC Lattice, you have a  service network. This is the logical construct that connects consumers and producers across different VPCs and accounts, using the vpclattice fabric. Logging is configured here, if required, along with the possiblity of setting overarching policies. The service network can be shared, using RAM to accounts as required.

To consume a service that is available on the service network, a vpc needs to ‘associate’ with the service network. When the association is established a vpclattice end point is placed in the vpc, and dns resolution is provided for the service network. security groups can be established on the endpoints as required. It is possible to use security groups on the endpoints. Of interest is that the service endpoint will have a 169.254.x.x address. And a dns query will reply with this endpoint.

Services are independent units of software. In VPC Lattice, a service is a logical component that can live in any VPC or account and can run on a mixture of compute types (virtual machines, containers, and serverless functions). A service configuration consists of:

  •  listener(s) that define ports and protocols that the service will operated on. Supported protocols are HTTP/1.1, HTTP/2, and gRPC, including HTTPS for TLS-enabled services. ports can be any valid tcp port
  • rules that have (a) prioritys, which specify the order in which rules should be processed. (b) conditions that define when to apply the rule, for example a path, and (c) actions that forward traffic to target groups.
    Each listener has to have a default rule that takes effect no conditions are met.
  • Target groups are a collection of targets, (compute resources), that you route traffic toward. The target groups can be weighted as well.

Access policies are IAM resource policies that can be associated with individual services. This lets you enforce context-specific access controls for services, which when carefully created can provide very granular control of who can access services.

Please check the AWS documentation for more details. I’d also highly recommend the last half of this talk from re:inforce
( AWS re:Inforce 2023 – Achieving Zero Trust with AWS application networking (NIS307) – YouTube )


Deploying VPC lattice with CDK
using Constructs.

AWS CDK (cloud development kit), is a programmatic approach to deploying infrastructure and applications. CDK works by synthesizing CloudFormation templates, using general purpose programming languages such as Typescript, Python, Go, DotNet and Java. CloudFormation provides us with definitions for a wide range (most) of AWS resources. They mostly map 1:1 to API calls. With CDK, you could write some ‘code’ that uses those definitions directly. ( in cdk these are called L1 constructs), but there is a better way, which leverages the ability of general-purpose code to abstract the complexity away. In this example we are going to use aws-vpclattice-prealpha contructs to build this example ( this is based on the ‘Centralised Single Service Network‘ reference architecture from AWS ). Using this construct will mean we do not need to directly create all the required configuration for lattice directly. We let the code do the heavy lifting for us. In the context of this construct, this makes the creation of the policys so much

A central account (account ‘S’) creates and ‘owns’ a service network. That service network is shared (using AWS Resource Access Manager) to the other accounts, (A, B, X, Y and Z). This will allow the consumers (in accounts a, and b) to associate their VPCs, and the providers (in accounts x,y and z) to register their services.

In this example the consumer application that is installed in on Host-A, and Host-B will be curl to allow us to visualise the responses. In a real-world example this would be an application making API calls. The consumers, query the VPC DNS resolver to get dns resolution of the services, which will resolve to the vpc endpoint that was created. The services will be a simple ‘hello world’ lambda.


In our example, we will require

  • All requests are authenticated
  • Only requests from within our AWS organization are permitted.
  • Host A can access the services X, and Y
  • Host B can access the services X and Z


Implementation.


It is assumed that the reader has a working knowledge of cdk, cdk installed and accounts bootstraped.
The code for this example is available in at github.com/raindancers/xxxxx. While this blog is in typescript we could equally well as code this any of the other cdk supported languages. It is recommended that the repo is cloned. It is possible and encouraged modify this example. ( please provide PR’s if you do create something new and interesting using the stacks )

The quick installation.


git clone https://github.com/raindancers/vpclattice-prealpha-demo
cd vpclatticealpha
edit environments.ts to include the account numbers of your accounts.
cdk ls
cdk deploy VpclatticealphaStack --profile <accountS>
cdk deploy ConsumerA --profile <accountA>
cdk deploy ConsumerB --profile <accountB>
cdk deploy ProviderX --profile <accountX>
cdk deploy ProviderY --profile <accountY>
cdk deploy ProviderZ --profile <accountZ>



Explanation of code

(1) Start with creating a stack for the service network. ( lib/servicenetwork.ts )

https://gist.github.com/mrpackethead/296c064db541d5dabf82c723b286c9d3

the Construct ServiceNetwork (line 16), takes an optional accessmode as a property. Choosing an access mode, results in a policy being created for the service network. You don’t’ need separately create the policy and apply it. In this case the mode is ORG_ONLY. This will create a policy that only allows authenticated requests, and from principals that are within this accounts organization. The construct will find the Org Id for us. ( For policy that is not covered by the included access modes, it is possible to add your own iam statements to the policy, or to not have any auth policy )

The service network, is shared with the other accounts. (Line 29) Using the method share on the this.serviceNetwork, we pass an array of accounts, and a share will be created.

The AuthPolicy that has been created needs to be applied to the servicenetwork ( line 35 )

(2) Create a stack for the consumers ( /lib/consumers.ts )

https://gist.github.com/mrpackethead/956a6eb3b3e6ac77c264739b705b8b55


This stack will get reused for both Account A and Account B.

A vpc is created (line27). For the purpose of demonstrating that overlapping vpc’s work with vpclattice, the vpc in account A and account will be identical. SSM endpoints are added to make it easy to connect to the EC2 instance we will create

On line 43, we import (to the stack) the service network that we shared to this account, which allows the vpc to be assocaited with the servicenetwork ( line 47)

lines 50 through 72, create an ec2 instance, place it on the vpc, and give the instance role, permission to access lattice service networks.

Most of this stack is not vpc-lattice related but scaffolding for the demonstration.



(3) Create a stack for the producers. ( /lib/producers )

We will create three producer stacks that are similar to each other, in the accounts X, Y, Z

https://gist.github.com/mrpackethead/b376424e6f377d648529693db3612aa3

The code at line 26, create a python lambda function. It will return a 200 OK, with a ‘hello world’ payload. This lambda does not need any additional permissions as it is just doing a simple hello world.

A lattice service is created at line 36. It takes a property, the shared service network. A listener is created at line 42. the listener construct takes the lattice service as a parameter. The service will be associated with the servicenetwork.

At line 48, we use the .addListenerRule() method. The .addListenerRule method will create a traffic routing rule, as well as adding iam statements to the service network policy. This will allow access to the target that is associated with this rule. This is an example of the power of a programmatic approach to deploying infrastructure. Two different resources are created with one method, as there is a significant overlaps in properties, and the complexity and opportunity of errors reduced.

(4) Now bring this all together in a cdk application.

https://gist.github.com/mrpackethead/21af352909de58bc6e96e93863755983

At line 10, an instance of the service network stack is created. It takes as a property an array of environments. This is used to determine which accounts to share the service network to.

At line 23 and 29, instances of the consumer stacks are created. The name of the service network created in the service network is passed as a property, so, the service network can be imported to this stack. Note that this is a cross account reference, so these references must be concrete and not tokens, resolved at deployment.

At lines 35-54, instances of the producer stacks are created. An array of roles is passed into the stack, which is used to created the access policy for the service network. Note the roles passed into the accounts are different.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.