Skip to main content

Configure AWS EFS Storage in a Hybrid Architecture with AWS Client VPN

Hands-On Lab


Photo of Tia  Williams

Tia Williams

AWS Training Architect II in Content





AWS has added Client VPN connectivity, where OpenVPN based clients may be used to connect to AWS services. The connectivity may be extended to other connected networks as well. In this hands-on lab, we will simulate a remotely connected environment, connecting where to a VPC using a Client VPN Endpoint. The VPC will be peered with another VPC in which we will setup EFS. We will configure the appropriate routing to allow the VPN client to connect to EFS in the VPC Peer.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Configure Transit Gateway for a Multi-VPC Environment


AWS has added Client VPN connectivity, where OpenVPN based clients may be used to connect to AWS services. The connectivity may be extended to other connected networks as well. In this hands-on lab, we will simulate a remotely connected environment, connecting where to a VPC using a Client VPN Endpoint. The VPC will be peered with another VPC in which we will setup EFS. We will configure the appropriate routing to allow the VPN client to connect to EFS in the VPC Peer.

Get logged in

Use the credentials and server IP in the hands-on lab overview page to log into AWS Console. Once we're in, let's make sure we're in the us-east-1 region. This text file contains the CLI commands. Download it and keep it handy, for copying and pasting.

Setting the table

We're going to be all over the place in this lab, so we'd best get some browser tabs open to keep things separate and manageable. Once we're logged in, let's open up a browser tab each for the EC2, VPC, and EFS dashboards. Now we can roll up our sleeves and get to work.

Create a peer connection between two AWS VPCs

In our VPC tab, we need to highlight VPC1 in the table, and copy the last few characters of its VPC ID in the Description tab, down in the lower part of the screen. Paste that into a text document somewhere, and navigate to Subnets, in the left-hand menu. Let's grab the last few characters here for the PublicSubnet1 Subnet ID.

The Peering Connection

Now let's head into Peering Connections (again, in the left-hand menu) and click Create Peering Connection. We'll set the Peering connection name tag to MyVPCPeers. Select VPC1 from the VPC (Requester) dropdown, and VPC3 from the VPC (Accepter) dropdown. Now click Create Peering Connections, and OK.

The Route

In Route Tables (again, in the left-hand menu), highlight Public-RT1, get into the Routes tab below, and click Edit routes. In here, we need to click Add Route. Use as a Destination, and set the Target to the peering connection we just set up. Then click Save and Close.

Now do the same thing for Public-RT2, but set the Destination to


Let's log into the public IP of INSTANCE1 (as cloud_user, with SSH), and see whether or not we can ping the private IP of INSTANCE2:

[cloud_user@INSTANCE1]$ ping &ltINSTANCE2 PRIVATE IP>

It should work. Let us march forth, to the next task.

Create the Certificates

We need to be in INSTANCE4, so log in (as cloud_user, with SSH) to INSTANCE3's public IP, then hop over:

[cloud_user@INSTANCE3]$ ssh &ltINSTANCE4 PRIVATE IP>

Once we're in, run aws configure and we'll set these when we're prompted:

  • AWS Access KEY ID: Leave it blank
  • AWS Secret Access KEY: Leave it blank
  • Default region name: us-east-1
  • Default output format: Leave it blank

Now run an update on the system:

[cloud_user@INSTANCE4]$ sudo yum -y update

Once that's done, we need to install Git:

[cloud_user@INSTANCE4]$ sudo yum -y install git

And now we have to clone a Git repository:

cloud_user@INSTANCE4]$ git clone

Let's get into the newly-created directory, then create the certificates we need:

cloud_user@INSTANCE4]$ cd easy-rsa/easyrsa3
cloud_user@INSTANCE4]$ ./easyrsa init-pki

This builds the CA:

cloud_user@INSTANCE4]$ ./easyrsa build-ca nopass

When prompted, set the common Name to something simple, like cans.local.

Now we've got to build the server, and then the client, certificates:

cloud_user@INSTANCE4]$ ./easyrsa build-server-full server nopass
cloud_user@INSTANCE4]$ ./easyrsa build-client-full client1.domain.tld nopass

Now make a new directory, and copy all of the certificates and keys into it (remember, we should still be sitting in the /home/cloud_user/easy-rsa/easyrsa3 directory):

cloud_user@INSTANCE4]$ mkdir /home/cloud_user/certificates
cloud_user@INSTANCE4]$ cp pki/ca.crt /home/cloud_user/certificates
cloud_user@INSTANCE4]$ cp pki/issued/server.crt /home/cloud_user/certificates
cloud_user@INSTANCE4]$ cp pki/private/server.key /home/cloud_user/certificates
cloud_user@INSTANCE4]$ cp pki/issued/client1.domain.tld.crt /home/cloud_user/certificates
cloud_user@INSTANCE4]$ cp pki/private/client1.domain.tld.key /home/cloud_user/certificates

We've got to get into that directory, then import them into AWS's certificate manager:

cloud_user@INSTANCE4]$ cd /home/cloud_user/certificates
cloud_user@INSTANCE4]$ sudo aws acm import-certificate --certificate file://server.crt --private-key file://server.key --certificate-chain file://ca.crt --region us-east-1
cloud_user@INSTANCE4]$ sudo aws acm import-certificate --certificate file://client1.domain.tld.crt --private-key file://client1.domain.tld.key --certificate-chain file://ca.crt --region us-east-1

Make note, somewhere, of each key's last few characters. We're going to need these later for distinguishing between the server and client keys.

Create the Client VPN Endpoint

In our VPC browser tab, click on Client VPN Endpoints in the left-hand menu, then the Client VPN Endpoint button in the page we land on. Set these values in the web form:

  • Name Tag: MyClientVPN
  • Description:
  • Client IPv4 CIDR:
  • Server certificate ARN: Pick the one here with the same last few characters as the server certificate we created earlier
  • Authentication Options: Use mutual authentication
    • Choose our client certificate in the drop that appears here.
  • Do you want to log... : No
  • Transport Protocol: TCP

We can leave everything else alone, and click Create Client VPN Endpoint, then Close. Get into the Associations tab, once we land back out in the Client VPN Endpoints screen, and click the Associate button.

On this screen, in the VPC dropdown, we want to select VPC1. There's no such label here, so we've got to look in the file we're keeping notes in for the last few characters of its ID. Select our subnet in the Choose a subnet to associate dropdown, then click Associate, and Close. We'll get dumped back out into the Client VPN Endpoints screen, and we've got to wait a few minutes while our endpoint associates with the VPC.

If we hit the refresh button every so often, this will eventually get a status of Available in 5-10 minutes.

Once it's done, head down tot he Associations tab, and click Associate. Then, in the Security Groups tab, we'll select the VPC we see there, and click Apply Security Groups. In the next screen, we'll select our security group, the one that doesn't have a group name of default. Now we can click Apply Security Groups.

Back out on the Client VPN Endpoints scree, get into the Authorization tab, and let's click Authorize Ingress. In the next screen, we want to use as a Destination network to enable, and click Add authorization rule, then Close. This is another spot that takes a couple minutes. Hitting the refresh every so often will make sure we see things right when the state of the endpoint goes from Authorizing to Active.

Before we move on, we need to snag out Client VPN Endpoint ID. It's back over in the Summary tab. Just copy it and paste it into that same text document we've been using for stuff like this.

Create the VPN Client

We had a terminal window open earlier. We were cloud_user, logged into INSTANCE4, and sitting in the /home/cloud_user/certificates directory.

To export the client configuration, run this, placing our endpoint ID that we copied/pasted in the last section:

[cloud_user@INSTANCE4]$ aws ec2 export-client-vpn-client-configuration --client-vpn-endpoint-id <OUR VPN ENDPOINT ID> --output text>client-config.ovpn

Now we can append the client certificate and key to the OpenVPN config file. Run the cat command:

[cloud_user@INSTANCE4]$ cat >> client-config.ovpn

Then enter the two following lines:

cert /path/client1.domain.tld.crt
key /path/client1.domain.tld.key

To get out of the cat command, just hit Ctrl + C.

Install OpenVPN client

This is easy:

[cloud_user@INSTANCE4]$  sudo yum install -y openvpn

Connect the Client to the VPN

Now let's try to actually launch the OpenVPN client using the configuration file:

[cloud_user@INSTANCE4]$ sudo openvpn --config client-config.ovpn

To verify whether we're connected or not, we can flip back over to our web browser, and peek at the Connections tab in Client VPN Endpoints. We were just in the Summary tab when we grabbed that endpoint ID, so it shouldn't be too hard to find.

Ok, we're going to need another terminal. If we use the one we've already got open, the VPN connection will die. Fire one up, then log into INSTANCE4 (hopping through INSTANCE3 like we did before) so we can continue.

Once we're in, let's test our connections. Try pinging INSTANCE1 and INSTANCE2, using their private IP addresses. You'll notice that one works and the other doesn't. We need to get back into our endpoint and set up some more routing. Don't worry though, it's a pretty quick fix. Leave that INSTANCE2 ping going while we work in the AWS console.

So, back over in our web browser, get into the Authorization tab of our endpoint screen, and click Authorize Ingress. For a Destination network to enable, use Click Add authorization rule, and Close. Go in and add another one for Remember, once these are done, we have to wait a minute or two before they are actually active.

Now, in the Route Table tab, click Create Route and use these values in the web form:

_ Route destination: _ Target VPC Subnet ID: Select our VPC1 subnet here (it should be the only one in the list)

Click Create Route, and Close. Go through this same process, but use the Route destination of

If we go back and watch that ping command we had running, we'll see it start to work once these two routes are associated.

Ok, we're almost done. Now let's play with EFS and data.

Create the EFS Mount

Create the EFS File System

Remember, a long time ago, when we opened up a few different browser tabs? There should be one sitting open to the EFS dashboard. Maybe it's the welcome page. Whichever one we've got open, let's click Create file system to get the ball rolling.

In the VPC dropdown, select VPC2. The PublicSubnet2 should show up automatically in the Create mount targets section. In the Security groups column, delete the default on that's in there and choose our group that has SecurityGroupPublic2 in the name.Once that's done, we can click Next Step. We're not changing anything on the next screen, so we can just click Create File System.

We've got to wait here a bit. Keep hitting the refresh button, and watch the Mount targets section for the Security groups column to change from Creating to displaying the security group ID of our SecurityGroupPublic2. In that same table, there's an IP address. We need that later when we mount this file system up, so we should copy it and paste it into that same text document where we've been sticking everything else like this.

Mounting EFS

Before we can mount an EFS file system, we need a place to mount it to. Let's make one:

[cloud_user@INSTANCE4]$ sudo mkdir /mnt/efs

And now, lets actually mount it up:

[cloud_user@INSTANCE4]$ sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport %ltEFS MOUNT IP>:/   /mnt/efs

If that all goes well (as in, no errors), then we can get into the directory and play with files and folders. Get in:

[cloud_user@INSTANCE4]$ cd /mnt/efs

Create an empty file:

[cloud_user@INSTANCE4]$ touch linuxacademy

Oops, we'll get an error. We don't have permissions, so let's up our game:

[cloud_user@INSTANCE4]$ sudo touch linuxacademy

Now it works. Let's create a new directory and give it to cloud_user:

[cloud_user@INSTANCE4]$ sudo mkdir mystorage
[cloud_user@INSTANCE4]$ sudo chown cloud_user mystorage

Now we can get in and make a file without using sudo:

[cloud_user@INSTANCE4]$ cd mystorage
[cloud_user@INSTANCE4]$ touch linuxacademy2

If we run an ls -l, we'll see that cloud_user does in fact own the empty linuxacademy2 file.


Well, we've done it. We laid a bunch of groundwork, then create an EFS file system, and mounted it so that we could create files and directories in it. Congratulations!