Featured image of post AWS Client VPN Endpoint Setup

AWS Client VPN Endpoint Setup

Introduction

Some enterprise customers do not allow SSH access to their AWS EC2 instances for security reasons. However, they still need to access their EC2 instances from their corporate network or other remote locations for debugging purposes. Setting up a bastion host inside a public subnet is unfortunately not an option in this case.

One approach is using AWS Systems Manager (SSM) Session Manager to establish a secure connection between the EC2 instance and the developer’s client. Another approach is using AWS Client VPN to establish a VPN tunnel between the private subnets where the instance is running and any developer machine. One advantage of using AWS Client VPN is that it allows you to control access to the instance based on the identity of the developer using Entra ID for authentication, for example - including MFA. Another advantage is the target network association on the subnet level with and the authorization rules that would drop any traffic that does not match those predefined firewall rules.

In this post, we will see how to set up an AWS Client VPN endpoint using Terraform and how to manage access using Entra ID. We will also demonstrate how to use an internal bastion host that is located inside the private subnets without having a public IP address as a secure gateway to access other internal EC2 instances.

Prerequisites

We will be using Entra ID for authentication and user management. The first step would be to create a new enterprise application inside the Azure Portal Microsoft Entra admin center.

IAM SAML Identity Provider

The details are described in an official Microsoft Entra ID article here. Here is a high-level summary of the necessary steps:

  • New enterprise application (AWS ClientVPN)
  • Setup Single Sing-On (SSO) with SAML
    • Identifier (Entity ID): urn:amazon:webservices:clientvpn
    • Reply URL (Assertion Consumer Service URL): http://127.0.0.1:35001
    • Attributes & Claims:
      • FirstName: user.givenname
      • LastName: user.surname
      • memberOf: user.groups (important for using authorization rules using groups and their ids)
      • Unique User Identifier: user.userprincipalname
    • SAML Signing Certificate / Signing Options: Sign SAML response and assertion (otherwise authentication will be rejected by AWS)

AWS mentions how to set up federated authentication here including the details needed for the user attributes and claims. After creating the enterprise application, you will need to download Federation Metadata XML file and then create a new identity provider in the AWS IAM section of the AWS console. The downloaded metadata XML file should be uploaded to the newly created identity provider.

AWS only allows one SAML identity provider per Client VPN endpoint, and Entra ID only allows one enterprise application with the same Unique User Identifier per Azure tenant. This means you cannot create multiple SAML identity providers in the same Azure tenant if you have multiple AWS Client VPN endpoints. If multiple AWS Client VPN endpoints are planned, then you will need to create multiple Entra ID groups in the same enterprise application and use the group IDs in the authorization rules to have separate access to the different private subnets. More on this topic in the Authorization Rules section.

Server Certificate

The “Authentication Info” section requires a “Server certificate ARN” when creating the client VPN endpoint. The certificate could be a self-signed certificate for our use case since we will not be using certificate-based authentication but federated user-based authentication. Make sure the following required X509v3 extensions are present in the server certificate:

  • X509v3 Extended Key Usage: TLS Web Server Authentication
  • X509v3 Key Usage: Digital Signature, Key Encipherment

Here is an example of certificate generation using easy-rsa:

1
2
3
4
5
6
7
8
9
git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3/
./easyrsa init-pki
# create a new CA
./easyrsa build-ca nopass
# create a new server certificate
./easyrsa build-server-full ec2vpn.myawesomecompany.internal nopass
# will not be needed for this case but is mentioned here for completeness 
./easyrsa build-client-full ec2vpnclient1.myawesomecompany.internal nopass

This can now be added inside AWS Certificate Manager (ACM), and the ARN can be used as the Server certificate ARN.

Client VPN Endpoint Setup

Create a client VPN endpoint with the following main settings:

  • Association type: VPC and then choose the VPC ID.
  • client IPv4 CIDR block (The address range cannot overlap with the target network address range, the VPC address range, or any of the routes that will be associated with the client VPN endpoint): For example, use 172.20.0.0/22 if your VPC CIDR block is 10.0.0.0/16.
  • Authentication / Server certificate ARN: previously created server certificate ARN.
  • Authentication / Use user-based authentication / Federated authentication / SAML provider ARN: previously created inside the IAM identity provider.
  • Connection protocol / Tunnel mode: Full-tunnel if all client traffic should be routed through the VPN tunnel or use Split-tunnel if only traffic matching client VPN endpoint routes should be routed through the VPN tunnel.

Target Network Associations

After creating the client VPN endpoint, you will need to associate it with one or more subnets that should be accessible from the client VPN endpoint. There is one limitation here, however: only one subnet association per availability zone is allowed per client VPN endpoint.

If you have multiple private subnets in the same availability zone, or separate private and database subnets in the same availability zone, for example, then you need to use a private subnet as a central bastion host to reach the other subnets or to connect to the database.

Authorization Rules

Authorization rules act as firewall rules to grant specific clients access to the specified network. You should have an authorization rule for each network you want to grant access to. Since we have set up Entra ID for authentication and added the SAML identity provider, we can use the Entra ID group IDs as the access group IDs in the authorization rules.

Let’s add an authorization rule to access the private subnet that hosts our EC2 instance:

  • Destination network to enable access: 10.0.2.0/24
  • Grant access to: Allow access to users in a specific access group
    • Access group ID: 07fc77bc-fa54-4b61-931e-2be27857671b

Only users in the specified access group will be able to access the private subnet. All other traffic coming from other users connected to the client VPN endpoint will be dropped. This way, we can control access to the private subnets based on the identity of the user inside Entra ID.

Route Table

Depending on whether you are using full-tunnel or split-tunnel, you will need to add an extra route to enable internet access on your local machine while connected to the client VPN endpoint. If you are using split-tunnel, then you can skip this step. If you are using full-tunnel, then you need to add an extra route where the destination is 0.0.0.0/0.

Create route:

  • Route destination: 0.0.0.0/0
  • Subnet ID for target network association: select any associated subnet that has outgoing internet connectivity using a NAT gateway

Architecture Diagram

We have explained all the steps above in detail. Here is a high-level architecture diagram of the proposed solution:

  • VPN users are managed using Entra ID: users in the allowed access group will be able to access the private subnets.
  • there is one bastion host inside the private subnets that can be reached when connected using the AWS Client VPN.
  • the private subnets have outgoing internet connectivity using a NAT gateway.
  • the bastion host can reach other internal EC2 instances such as the backend VM or the DB using the private IP address as long as the security group allows it.
  • EC2 instances in the public subnet such as the load balancer VM do not need to be accessible from the internet via ssh.
  • No ssh port is open to the public internet, and all traffic is routed through the VPN tunnel and then internally via the bastion host (jumphost).

Automation with Terraform

The GitHub repository for this blog post is available here. We will go over the main resources that were used in the module. The aws_ec2_client_vpn_endpoint resource is the main resource that we will use to create the client VPN endpoint. The network associations, authorization rules, and routes are created using the following resources:

  • aws_ec2_client_vpn_network_association
  • aws_ec2_client_vpn_authorization_rule
  • aws_ec2_client_vpn_route

The examples folder contains an example of a new vpc with a new client VPN endpoint. The VPC contains three private subnets that are associated with the client VPN endpoint. The user has to provide a public ssh key, the server certificate ARN, and the SAML identity provider ARN. The Entra ID group ID is also used inside the authorization rules section.

Conclusion

In this post, we have seen how to set up an AWS Client VPN endpoint and how to manage access using Entra ID. Federated user-based authentication is an important requirement in most enterprises for compliance reasons. The solution to use an internal bastion host that is located inside the private subnets without having a public IP address that can be reached from the client VPN endpoint was discussed. From that point on, using the bastion host as a secure gateway to access other internal EC2 instances meets the requirements of most organizations.

References: