How To Secure Access To Your Infrastructure Using AWS Session Manager And A Bastion Host

Securing cloud infrastructure is essential, but traditional bastion host setups often come with challenges. What if I told you that you could still leverage bastion hosts without the hassles? Dive in to find out how!

Sharing is caring!

by Ulises Jeremias Cornejo Fandos

12/02/2024

In this guide, I’ll show you how to leverage Terraform to effortlessly build a bastion host in AWS, using AWS Systems Manager Session Manager and Amazon EC2 Instance Connect for secure, streamlined access.

From setup and configuration to automation and troubleshooting, you’ll learn the NaNLABS way of building a secure, scalable, and easy-to-manage bastion host.

Ready to harness the power of AWS and Terraform to elevate your infrastructure security? Let’s dive in.

Table of Contents 

How can a bastion host contribute to securing your infrastructure?

A bastion host acts as a secure gateway to your private network resources. However, traditional bastion hosts often require open inbound ports and complex SSH key management. At NaNLABS, we’ve come up with a solution that allows you to leverage bastion hosts without weakening your security posture. 

This solution leverages AWS Systems Session Manager and Amazon EC2 Instance Connect to: 

  • Provides a safer way to handle inbound port traffic

  • Eliminate long-lived SSH key management 

  • Secure access to AWS network resources 

  • Streamline operational processes

By combining these tools, we can enhance your infrastructure security, while simplifying access management. 

Let’s dive so you can see how to implement our solution to your infrastructure! 

Need extra help to enhance your infrastructure security? We’ve got you! Reach out to enhance your infrastructure security!

NaNLABS’ approach to bastion hosts in AWS environments

Terraform module roadmap to bootstrap a bastion host in AWS

A bastion host, sometimes called a jump box, is a server that provides a single point of access from an external network to resources located in a private network. This module uses AWS Systems Manager Session Manager and Amazon EC2 Instance Connect to securely connect to an Amazon EC2 bastion host without exposing any inbound ports or managing long-lived SSH keys.

Usage example 

Target technology stack

  • VPC with at least one private subnet

  • 3 VPC (Interface) Endpoints with corresponding security groups and security group rules

  • EC2 Instance (t3.nano) with Ubuntu 24.04 installed with associated:

    • IAM role

    • IAM instance profile

    • security group

Target architecture

Let me guide you through how the Bastion host interacts with AWS services:

  1. Deployment of the EC2 instance: First, we deploy a single isolated EC2 instance (t3.nano) with Ubuntu 24.04, installed inside the private subnet in the created VPC. The instance doesn’t expose any ports or have a public IP assigned. 

  2. Communication via VPC Endpoints: The EC2 instance uses VPC Interface Endpoints, which are private connections to AWS services (like Systems Manager and EC2), so it remains isolated while allowing secure interactions with AWS. 

  3. Role-based Access Control: You assume an IAM role with policies granting specific permissions for authenticating, authorizing, and connecting to the EC2 instance, following the least privilege principle. Separately, the EC2 instance assumes an IAM role, which defines its permissions for interacting with other AWS resources. This setup ensures minimal, controlled access, requiring any additional access to be explicitly granted by assigning policies to the respective IAM roles for both users and the EC2 instance. 

This setup establishes a secure way to manage and interact with resources within an AWS VPC without direct internet exposure, leveraging IAM for secure, role-based access.

EC2 Instance Provisioning 

The created EC2 instance is provisioned using cloud-init. You can customize the instance provisioning by modifying the content of the cloud_init template file at templates/cloud_init.tpl.

Connecting to the Bastion Host Using Session Manager

Now let’s walk through the steps to securely connect to the bastion host using AWS Session Manager, IAM roles, and SSH keys.

Prerequisites

Setup Local Environment 

  1. Install tools
    Follow the prerequisites section to install AWS CLI and Session Manager plugin.

  2. Configure SSH connection
    Follow the documentation to update the SSH configuration file to allow SSH connections through Session Manager. It enables a proxy command to initiate a Session Manager session, seamlessly transferring all data through the established connection.

  3. Generate SSH keys
    Generate local SSH private and public keys. For example, use the following command: ssh-keygen -t rsa -f my_key It will generate a pair of private and public SSH keys, which will be used to connect to the bastion host. Setting a password to protect access to the private key and storing both keys in a secure location is recommended.

Connect To the Bastion Host

Follow these 4 steps to connect to the bastion host:

  1. Assume IAM Role
    Assume an IAM role that has permissions to authenticate, authorize, and connect to the EC2 instance.

  2. Obtain the instance ID
    You must obtain the EC2 instance ID to connect to the deployed bastion host. You can retrieve it in several ways, such as by navigating to the EC2 dashboard in the AWS Console or by using the AWS CLI command: aws ec2 describe-instances

    To further filter results you can use the following command:

    Replace $BASTION_HOST_TAG with the tag used to mark the bastion host. Copy obtained EC2 instance ID for later use.

  3. Upload SSH keys
    To connect to the bastion host, first, upload your public SSH key to the host using EC2 Instance Connect. Run the following command, replacing:

    • $INSTANCE_ID with the EC2 instance ID obtained in the previous step

    • $PUBLIC_KEY_FILE with the path to your public key file (e.g., my_key.pub).

      Be sure to use the public key and NOT the private key.
      <script src="https://gist.github.com/sarahjdn/8db85bd0fc21a0ab95fe129d92470b9a.js"></script>You should see a message confirming the successful upload of the SSH key. The key is temporarily stored in the EC2 instance metadata for 60 seconds. After this period, it is automatically removed, and any attempts to connect will result in a 'Permission denied' error. If that happens, you can resend the key using the same command. 
      This means you have 60 seconds to establish the SSH connection after uploading the key. Follow the next step to proceed.

  4. Connect via SSH through Session Manager
    In this final step, you'll connect to the bastion host using SSH. Use the following command, replacing: 

  • $PRIVATE_KEY_FILE with the path to your private key (e.g., my_key)

  • $INSTANCE_ID with the EC2 instance ID obtained earlier.

Confirm the connection by typing 'yes'. This will establish the SSH connection using the previously configured Session Manager. 

You’re in!

Additional Useful Commands

List Active Sessions

To list all active sessions:

Terminate a Session

To terminate a specific session, you need the session ID. First, list the active sessions to get the session ID:

Then terminate the session using the session ID:

Example Script For Complete Workflow

Here’s a full example script that retrieves the Bastion instance ID, uploads the SSH public key, and initiates a Session Manager session: 

Optional Steps

The following steps are optional and can help enhance the security of the Bastion host or simplify the connection process.

  1. Quick Connection Using Script

    To simplify the process of connecting to the Bastion host, you can use the connect.sh script. This script automates the tasks of generating an SSH key pair, uploading the public key to the Bastion host, and establishing an SSH connection through AWS Systems Manager.
    Run the script with the following command: <script src="https://gist.github.com/sarahjdn/bdfe5d4a16e6cb3d2be5815077b4a48a.js"></script>

    Replace <instance-id> with the ID of the Bastion host instance. The script will automatically:

    • Generate an SSH key pair.

    • Upload the public key to the Bastion host.

    • Establish an SSH connection using the private key.

    Use the -h or --help flag to see the available options:

    Use Other SSH Options to Open Connection
    There are various options available for connecting to the Bastion host. For example, you can use the -D 8888 option to establish an SSH connection with local 'dynamic' application-level port forwarding on port 8888. Refer to this link for a detailed explanation. 

    https://gist.github.com/sarahjdn/cf3f6725d241600dc765e16c199e62da

This type of connection sets up a SOCKS proxy, allowing you to forward traffic from your local browser through the bastion host, for example. Refer to the main pages of ssh command to see all options.

How to automate and scale the bastion host in larger infrastructure setups?

The previous pattern can be adopted as part of a larger infrastructure code and deployed in an automated way using CI/CD tools. You can modify the code to change the type of deployed EC2 instance to adjust its parameters to your specific needs.

Automating and scaling the bastion host as part of a larger infrastructure setup offers several key benefits such as:

  • Improved security: Automation ensures that security practices, such as key management and access controls, are consistently applied. It reduces the risk of human error, ensuring that security settings are always up-to-date.

  • Consistency: By automating the deployment and configuration of bastion hosts, you ensure that each instance is set up consistently across your environment, reducing configuration drift and minimizing the risk of misconfigurations.  

  • Cost efficiency: Automation allows you to scale bastion hosts up or down based on demand, ensuring you’re only paying for the resources you need at any given time. 

  • Operational efficiency: Automating the creation and management of bastion hosts can free up time for your teams to focus on other critical tasks. It also streamlines the process of rotating keys, applying updates, and scaling infrastructure.  

  • Easier disaster recovery: If a bastion host fails, automation allows you to quickly replace it or spin up new instances, reducing downtime and ensuring that access remains uninterrupted. 

Troubleshooting

Occasionally, you may encounter a TargetNotConnected error when attempting to connect to the Bastion host.

Here’s how to resolve the issue:

  1. Follow the Reboot your instance documentation from AWS to reboot the deployed bastion host.

  2. Refer to the Connect to the Bastion Host section of this article and try to reconnect. 

Additional Resources

Here are some additional resources to help you optimize your setup: 

Conclusion: Combine AWS and Host Bastion powers to secure your infrastructure. 

Security doesn't have to be a hurdle—it can be a catalyst for better software and smoother operations. By embracing regular code scanning, you're not just preventing problems; you're empowering your team to innovate confidently.

At NaNLABS, we're passionate about helping you succeed. We're your friendly sidekick, bringing expertise and a top-class partnership to amplify your strengths.

Ready to elevate your security game? Let's connect and co-create extraordinary solutions together.



















More articles to read

Previous blog post

Web Technologies

11/18/2013

Avoiding huge controllers in AngularJS

Read the complete article

Next blog post

Web Technologies

11/29/2024

8 Auto Industry Challenges You Can Solve With AWS Automotive Solutions [+ Real Examples]

Read the complete article