Fully automating certificate based AWS Client VPN deployments
Remove any manual step from the rollout of a Client VPN setup with the help of a script to automate the laborious tasks and include it in your AWS CDK stack
As engineers we constantly strive to automate and simplify the dull and repetitive tasks as much as possible, especially the laborious and long-winded ones that are more of an annoyance than anything else. And as I rollout many small proof of concept projects both for myself and for clients, it becomes necessary for my mental sanity to automate even those steps that for most is a once in a blue moon nuisance, such as the configuration of a self-signed certificate based AWS VPC Client VPN setup.
Advantages
Running “
npx cdk deploy”
is truly all that is needed to get the entire environment deployed.No need to constantly reference documentation for all the commands needed to rollout a self-signed Certificate Authority (CA), client certificates, upload them to AWS Certificate Manager (ACM), copy the newly imported certificate ID’s into my CDK code and deploy.
Becomes a blueprint for all projects.
The overall architecture of what we will be building is identical to what we covered in this post where I highlighted the benefits of the AWS Client VPN in more detail:
The architectural diagram is nearly identical to the one in the above post, with the added focus on the appearance of the S3 bucket for storing the raw certificate artifacts created during the first run of our VPC stack and the ACM being used by the Client VPN for the server certificate.
While the diagram bears significant similarities to the previous post, the CDK code has several modifications.
Step 1: The S3 Stack
In order to deterministically assess during each run, regardless of the machine the command is being run on, that we have the certificates generated, we need to leverage a common shared file store, such as S3:
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a79d6d-15bd-4a52-aef3-b64f71c942ee_1558x1254.png)
Step 2: The VPC Stack
This bears significant semblance to the previous stack with minor cosmetic changes:
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e65b4d-6f1c-444e-88fc-518b0adb41fe_1558x2468.png)
Step 3: The VPN Stack
This is where the whole magic happens, we leverage the fact that while the CloudFormation JSON/YAML that CDK synthesizes our code into is declarative, the TypeScript that generates the declarative code is imperative, so we can add some if
statements to assess with the help of the aws-sdk
for our language of choice, that the conditions hold true (the certificates exist), otherwise we create the certificates, upload them to the S3 bucket and create the SSM parameters, so that the AWS CDK code can find the parameters when the stack is applied.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadb86593-8053-4dde-8123-a381c1c1ebbe_1840x8544.png)
Step 4: The Application Stack
Now the final element to make this all work together is the entry point for the application, which allows us to guard for the very first deployment against the synthesis of the VPNStack
.
Although we specify the name of the stack to be deployed, there is no way to control through CDK that only the specified stack gets synthesized as well, only way to achieve that would be by creating separate entry points for each stack and invoking them individually. As such, I am adding a latch that is by default open, allowing the creation of the VPN stack as well, but for the first run, it will be set to false, so that the S3 bucket and the VPC are created first, ensuring the VPC.fromLookup
will succeed along with the SSM Parameter.valueFromLookup
.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51c343a6-8d57-4d67-9d05-87f70f5631aa_1666x2468.png)
Step 5: The certificate creation script
By reading the AWS documentation found here, I have created the following shell script to automate the process, allowing the above CDK code to create the certificates needed for the mutual authentication automatically:
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13dfe50c-a5c3-4fbe-b6a0-e42a4b9f3cbb_1840x4404.png)
With this final piece of the lego, we can now see how the VPNStack
generateCertificates
method is invoking the shell script, and capturing the output, filtering for the “SERVER_CERT:” and “CLIENT_CERT:” rows to obtain the ACM certificate ARN’s that are subsequently stored within SSM.
Step 6: The two step deployment
As mentioned previously, we need to deploy the stack twice, once to create the required S3 & VPC resources and a second time to create the Client VPN leveraging the dependencies:
npx cdk deploy --all -c deployVpn=false
This first run, with deployVpn=false
prevents the VPNStack from being synthesized, allowing the dependencies to be created first, followed by a second run with:
npx cdk deploy --all
At this point we are finally complete, with a fully functional VPN service, for which we can download the Client VPN configuration, along with the certificates we now have in the “certificates” folder can be used to connect to the VPC network.
Conclusion
In conclusion, the journey through automating the setup of a self-signed certificate-based AWS VPC Client VPN has not only been a testament to the power of automation but also a deep dive into the intricacies of cloud infrastructure management.
The advantages of this approach are clear: from the ease of deployment with a single command to the creation of a reusable blueprint that can be adapted across various projects, the methodology outlined in this post offers a robust framework for managing VPN configurations.
As engineers, our goal is to tackle challenges head-on, leveraging technology to our advantage. This blog post serves as a blueprint for those looking to automate their AWS VPC Client VPN setup, offering a comprehensive guide that marries the imperative power of scripting with the declarative nature of infrastructure as code. By embracing these practices, we not only streamline our workflows but also pave the way for more innovative and efficient solutions in the future.
I hope this post inspires you to explore the possibilities of automation in your own projects and that it encourages a mindset of continuous improvement and innovation.
As always, you can find the full source code for this project on Github here.