The open blogging platform. Say no to algorithms and paywalls.

Connect to a Private Instance Using a Bastion Host Within a Custom VPC


Use Case

You are a cloud engineer tasked with setting up the security and network architecture for your organization’s environment. For extra security you have a server that needs to be in a private subnet and should only be accessible through a Bastion Host. Your private server will also need to have the ability to update software packages without being publicly accessible to the internet.

Create VPC

A Virtual Private Cloud (VPC) is a virtual network in the cloud that you define, where you can launch AWS resources. In a VPC you have the ability to customize the network configuration and add multiple layers of security.

  1. Navigate to VPC.

  2. Click Your VPC.


  1. Click Create VPC.


  1. Enter VPC settings and click Create VPC
  • Name tag: ABC Company Dev

  • IPv4 CIDR Block:

  • IPv6 CIDR block: No IPv6 CIDR block


Create Subnets

  1. Click Subnets.


  1. Click Create Subnet.


  1. Select the newly created vpc from the drop down.

  2. Enter the Subnet settings for our public subnet.

  • Subnet name: DevPublic

  • Availability Zone: us-east-1a

  • IPv4 CIDR block:


  1. Click Add new subnet.


  1. Enter the Subnet settings for our private subnet.
  • Subnet name: DevPrivate

  • Availability Zone: us-east-1a

  • IPv4 CIDR block:

  1. Click Create subnet.


  1. You should now see two new available subnets.


Making Our Public Subnet Truly Public

Just because we name our subnet “Public” doesn’t make it so. Currently our VPC has no way to connect, so we need to attach an Internet Gateway. That alone won’t do the trick. Next we will need to create a route table and create a route to our Internet Gateway.

Internet Gateway

  1. Click Internet Gateways.


  1. Click Create Internet Gateway.

  2. Name your Internet Gateway and click Create internet gateway.


  1. Click Attach to VPC from the Actions drop down.


  1. Select the VPC from **Available VPCs **and click Attach Internet gateway.


Configure Route Table

  1. Click Route Tables.


  1. Click Create route table.

  2. Enter Name tag: ABCDevPublicTR and VPC: ABC Company Dev.


  1. Select the newly created ABCDevPublicTR route table and select the Routes tab.


  1. Click Add route. For Destination type and fo rTarget select the ABCDevIGW Internet Gateway we created. Then click Save routes.


  1. Select the Subnet Associations tab. Currently our two subnets are both associated with the main route table.

  2. Click Edit subnet associations.


  1. Select the DevPublic subnet and click Save. This is a great example of why it’s important to use Name tags. If I didn’t have my Name tag set up then I’d have to go off of the IPv4 sider or Subnet ID to determine which was my public subnet.


Our public subnet is now associated with our public route table.


Modify Auto-Assign IP Settings

  1. Navigate back to Subnets using the left navigation.

  2. Select our public subnet.

  3. Click Actions and click Modify auto-assign IP settings.


  1. Select Enable auto-assign public IPv4 address. Click Save.


Bastion Host

A Bastion Host is an instance that is in a public subnet with hardened security, who’s primary purpose is connecting to instances in a private subnet.

  1. Navigate to EC2 > Instances and click Launch instances.


  1. For Choose AMI select Amazon Linux 2 AMI (HVM), SSD Volume Type.


  1. For Instance Type select t2.micro. Click Next.


  1. On the Configurate Instance settings change the Network from the default to the ABC Company Dev VPC.


  1. For Subnet select the DevPublic Subnet. Check to make sure the Auto-assign Public IP displays Use subnet setting (Enable). If it doesn’t then you most likely forgot to modify the auto-assign ip settings. You can select Auto-assign Public IP and manually enable.


  1. Click Next. Keep Storage defaults and click Next.

  2. For Add Tags click Add another tag. Key: Name and Value: BastionHost. Click Next.

  3. To Configure Security Group select Create a new security group and name the Security group.

  4. Change Source to My IP for added security so that only your IP can SSH into your Bastion Host. Then click Review and Launch.


  1. Click Launch.

  2. Create a new key pair and click Download Key Pair. Then click Launch Instances.


Private Instance

  1. Click Launch instances.

  2. Select Amazon Linux 2 AMI (HVM), SSD Volume Type

  3. Select t2.micro. Click Next.

  4. On Configure Instance Details settings for Network select ABC Company Dev VPC and for Subnet select DevPrivate. Click Next.

  5. Keep the default storage. Click Next.

  6. Add Tag: Key: Name, Value: PrivateServer. Click Next.

  7. For **Configure Security Group **create a new security group.

  8. Keep the existing SSH Rule, but modify the source to be our BastionHost Security Group. Select Custom and in the text box type sg, this will display a list of created security groups. Select BastionSG.

Note: I should have probably done a better job naming the security group or I should have referenced the name before selecting. I accidently selected the BastionHostSG and received an error when trying to launch. The naming was too similar to BastionSG. In the future I’ll need to keep this in mind.


  1. Click Review and Launch. Click Launch.

  2. Select the existing key pair created for the Bastion Host. Select the acknowledgement and click Launch instances.

Now while in the EC2 Dashboard, select the BastionHost and review the details. Notice that there should be a Public IPv4 address.


So I have my Public IPv4 address, but I noticed that I’m missing the Public IPv4 DNS. Although it’s not necessarily needed for this project, I’d still like to have it available. Let’s go back and enable auto assign.

  1. Navigate to VPC > Your VPCs.

  2. Select ABC Company Dev. Click Actions and click Edit DNS hostnames.


  1. Select Enable and click Save changes.

  2. Navigate back to EC2 > Instances. Click the refresh button and the Public IPv4 should populate.


Using our Bastion Host to SSH into our Private Server

I’ll be using PuTTY, PuTTYgen and Pageant to SSH into my Bastion Host and then to SSH into my Private Instance. If you are not using a Windows machine please see Securely Connect to Linux Instances Running in a Private VPC.

First I’ll need to use PuttyGen to save my abcdevkp.pem file that I downloaded earlier to a .ppk file.

  1. Open PuttyGen.

  2. Click Load.


  1. Select your abcdevkp key that was downloaded when we launched our instances. If you aren’t seeing it in your downloads folder make sure All Files are displayed and not just .ppk files.

  2. Click Save private key. Click Yes to acknowledge. I use the same name as it will be saved as a .ppk file so won’t overwrite the .pem file.

Next to keep us from having to provide access keys manually when SSHing into our Private Instance from our Bastion Host I’ll be using Pageant for agent forwarding.

  1. Launch Pageant.

  2. Pageant doesn’t open in a pop up so you’ll need to find it on your task bar.


  1. Double click to open and click Add Key.


  1. Select your .ppk key and close.

Now we can SSH into our Bastion Host.

  1. Launch PuTTY.

  2. Enter the Public IPv4 address from the BastionHost.


  1. Select Auth. Connection > SSH > Auth

  2. Select Allow agent forwarding.

  3. Click Browse and select the .ppk.


  1. Click Open.

  2. This will open up your terminal. For Login as type ec2-user.

We have successfully SSHed into our Bastion Host. Now let’s SSH into our Private Instance.

  1. Run the following (substituting your own private ip address):

    ssh ec2-user@<private ip address>
  2. Type yes

  3. SUCCESS! You should now see that the CLI has updated to your Private Instance ip.

  4. Try and run the following commands to verify everything is working.

    echo "Test 1 was a success" > file.txt
    cat file.txt
  5. Now that we have verified that we are able to type commands successfully to our Private Instance from our Bastion Host let’s run a yum update.

    sudo su
    yum update

After some time this command should fail. This is because although we can interact with our Private Instance through our Bastion Host, it is still cut off from the internet. We don’t want to just open it up to the internet now because that would defeat all the work we just put in to secure our network. Instead we will add a NAT Gateway to our public subnet and update our routing table to allow our private instance to connect to our Internet Gateway securely.

NAT Gateway

  1. Navigate to VPC.

  2. Click NAT Gateways.


  1. Click Create NAT gateway.

  2. Name your NAT gateway. I chose ABC-Dev-NATGW.

  3. For Subnet select the DevPublic subnet.

  4. Click Allocate Elastic IP. Click Create NAT gateway.


Update Route Table

  1. Navigate to Route Tables.

  2. Click Create route table. Right now our private subnet is associated with our default route table. It’s a best practice to create a separate route table if there is any routes that are not for local traffic. So we are going to create a new private route table.

  3. Add a Name tag and select the ABC Company Dev VPC.

  4. Click Create.

  5. Select the private route table and click the Routes tab.


  1. Click Edit routes. Then click Add route.

  2. For Destination: and for Target select NAT Gateway and then select the NAT Gateway we just created.

  3. Click Save routes.

  4. Now we need to associate the private subnet with our new private route table. With the private route table selected click the Subnet Associations tab and then click the Edit subnet associations button.


  1. Select the DevPrivate subnet and click Save.


  1. You should now see the subnet is associated with the private route table.


  1. SSH into your private instance with your Bastion Host using the same steps as before.

  2. Once successfully connected to your private instance run:

    sudo yum update
  3. The process should begin immediately and after some time the yum update will successfully complete.


Congratulations! You created and configured a custom VPC, used a bastion host to connect to our private server to receive updates. Now there is only one thing left to do. Tear it all down. When you are emotionally ready please see the clean up steps.

Clean Up

Your NAT Gateway and EC2 Instances are really the only things costing you currently.

  1. Terminate your EC2 Instances. If you want to do some more testing feel free to keep them but stop them when not in use to avoid unnecessary charges.

  2. Delete your NAT Gateway. Navigate to NAT Gateways > Actions > Delete NAT gateway. Type delete to confirm.


Important: Once your NAT Gateway is deleted you need to release your Elastic IP that was associated with your NAT Gateway.

  1. Navigate to Elastic IPs.


  1. Select the Elastic IP. Click Actions and click Release Elastic IP addresses.


  1. Lastly if you have no more need for the VPC you’ll want to detach and delete the Internet Gateway, delete the Route Tables, Subnets, Security Groups and VPC created.

Continue Learning