AWS Site-to-Site VPN: Customer Gateway IP address dynamic update
How to programmatically update the IP address of a Customer Gateway logical device for your AWS VPN Connection in the absence of a static IP on your premises.
When you need to access your on-prems resources from an AWS VPC, or the other way round, the first option you have in your hands is to create a AWS Site-to-Site VPN connection, and configure routing to forward traffic via this connection bilaterally or unilaterally.
From the AWS point of view, every Site-to-Site (from now on will be referred as S2S) VPN connection has two tunnels, that each one of them is using its own public IP address — for redundancy reasons.
From the customer’s point of view you need either a physical or a logical device that will act as a physical or software-based router respectively, that lives on the edge of your on-prems infrastructure. That is your Customer Gateway Device. AWS knows nothing though about this device and its configuration. Here comes in play the Customer Gateway, an AWS resource that provides the necessary information to AWS about your Customer Gateway Device.
Configuring the router from your end and establishing a VPN connection is out of the scope of this article.
Customer’s Gateway configuration options is not a big a list, but contains a piece of information that is absolutely crucial for establishing your VPN Connection. The public IP address of your premises, which according to the documentation must be static:
Well, usually a “must” does not bounce nice with me; I always try either to rationalize or find a way around it. Especially in this case not everyone has or can afford a static IP address. Let’s see how we can fix this in an automated programmatic way using the AWS SDK for Go (the example is using v1 but you can easily migrate it to v2).
Configure the client
First thing we want to do is to authenticate against AWS, we are going to do it using an Access Key and Secret Access Key pair, short as AK/SK, (if you don’t have already a pair in place, learn how to create one in this link). Let’s configure our client as it is described here:
- Create under
~/.aws
two filescredentials
andconfig
:
In credentials
file enter the AK/SK under the umbrella of a default
profile. In this example we are going to use shared configuration and not named profiles (for more information refer to the the second link in the documentation provided above)
In config
file it suffices to enter the region
you intend to work with (be sure you put it under the same profile flag, in our case default
):
Create a session and a service client
Before you can create a service client (an object that will take over the requests and the responses to AWS services) you need first to create a session dictating that you intend to use a shared profile (the default
one we configured before):
After creating a session, we are ready to create a service client (in this case we are interested in ec2.EC2
which will give us access to VPN-related resources and services):
Creating a service client has a rich configuration pallete, but here we are just going to enable some more debugging information with WithLogLevel
option and to define a retry strategy with WithMaxRetries
.
Find the Customer Gateway of our VPN
Every VPN Connection has a Customer Gateway and our second task after creating a session and a service client is to find out which is our existing Customer Gateway and what is its current configuration. First we need to get an instance of our VPN connection, for that matter we pass as command line parameter to our tool the VPN Connection ID which will be our entry point in the rabbit hole:
AWS service clients have a standard pattern of requests and responses. You provide an Input object and you get and Output object.
If our request was successfully executed we should get now in hand the metadata of our VPN connection. One of them, is the CustomerGatewayId
which will give help us get a grip to the existing Customer Gateway.
With this in hand, we can now inquire about our Customer Gateway:
and if all went well we can now find out the IP address that is already configured on the AWS as our premises WAN IP address:
Usually ISP providers will refresh your dynamic IP address in a span of 24 hours. Our goal here is to periodically check if our dynamic address has changed and if yes, and only if yes to try change this information on AWS. So checking what was the last IP address configured is pretty important.
Next we need to find out our current WAN IP address. For that matter we are going to make an HTTP request to https://myexternalip.com/raw , which very conveniently returns a page with sole content our IP address:
If the two IP addresses match, we don’t need to do anything:
In the opposite case we need to inform AWS for our configuration settings changes.
Update Customer Gateway with the new IP address
Theoretically you should think, straightforward update the Customer Gateway with the newIpAddress
value and we are ready to go. Well not. AWS do not allow us to update an existing Customer Gateway, so we will go do a workaround. We are going to create a new Customer Gateway, we are going to modify our VPN Connection to use the new one and we are going to delete the old one.
So first let’s create a new Customer Gateway passing the configuration options of our existing Customer Gateway and the new external IP address of our premises:
That will create a clone of the Customer Gateway but with an updated IP address.
Next thing in the pipeline is to update our VPN Connection and replace old Customer Gateway with the new one:
and finally, if no errors we met so far, we are ready to remove the old Customer Gateway:
The VPN connection needs some time to modify itself, unlike in Azure that things are more fast when you update a Local Network Gateway’s IP address. That means that you might experience some downtime in your VPN connections, perhaps on a daily basis.
Summary
That’s a simple and straightforward way to dynamic update the IP address for an AWS VPN connection without resorting to expensive solutions like buying a static IP. You are welcome to refine the code to be more production-ready and let it run periodically as a Linux or Kubernetes CronJob.
You can find the whole code in the repository below: