Logo

quadroid

Home
Résume
Tech Blog

AWS Infrastructure without Jump Hosts

bastionless intro

The Jump or Bastion Host is a very popular construct in AWS. The concept is rather simple. You have a dedicated host which can be accessed from the outside of the network to connect to the internal resources. This is done mainly for security purposes, you try to reduce the attack surface of your infrastructure by making only one instance available to the outside. This does as well create the opportunity to install additional security measurements and logging on this one instance.

In reality most Bastion Hosts I came across are very basic Linux machines that do not have any additional security measurements in place. Even worse, sometimes they are abused as remote work station inside your infrastructure. This does neglect the idea of a Bastion Host by widening the attack surface of the machine, due to all the development tools installed on that machine.

A Jump Host less alternative

AWS SSM Session Manager provides an alternative to access your infrastructure without exposing any instance to the public. To connect to the instances you use the aws cli. Which grants access based on your aws credentials, which may (should!) include MFA.

There are a view things Required to make this possible. You need to:

1) Allow your Instances to access the aws ssm services

You need to modify the instance role of the instances you want to access to include the required permissions. AWS Provides a managed IAM Policy called AmazonSSMManagedInstanceCore which allows all required actions. Just add this policy to your instance role, and you are good to go.

role permissions

2) Install the aws ssm plugin on your local machine

The aws cli does not include the ssm session manager capabilities from scratch. They need to be added by installing a plugin provided by AWS. You can find the installation guide here.

3) Make sure your Instances can reach the AWS SSM Endpoints

If your instances are behind a NAT-Gateway or have any other way to reach out the public Internet, you are good to go. If they are in an Isolated Network that is unable to reach the internet, you need to add VPC Endpoints for multiple services:

  • com.amazonaws.region.ssm:

  • com.amazonaws.region.ec2messages:

  • com.amazonaws.region.ssmmessages:

You can find more details about the required endpoints here.

4) Make sure your Instance has the ssm-agent installed

The Amazon Linux and Windows Images already have the agent installed and running. Other or older Images may need manual installation. AWS provides a detailed installation Guide for windows and for linux instances.

It is also worth noting that some capabilities depend on a minimum version of the ssm-agent. You can see the versions at the aws console if you navigate to Services -> Systems Manager -> Managed Instances.

agent version

To automatically keep all agents up to date aws provides a predefined association which takes care of this. It can be activated on the same page:

agent auto update

How to interact with my instances?

Now that all prerequisites are in place let’s connect to the instances. There are multiple features of AWS SSM that may be useful for your case.

Connect to a shell

The simplest thing to do is to start a session manager session. This can be done using the following command:

aws ssm start-session --target INSTANCE_ID

This opens a terminal session on the target instance. Powershell for windows instances and sh for linux machines.

shell demo

Forward Ports

A recent update of SSM adds the capability to tunnel Ports from an aws instance to your local machine (Like ssh -L, or kubectl port-forward). The following command does create a tunnel between your machine and the target instance. In this case used to connect over RDP to a windows instance

    aws ssm start-session --target INSTANCE_ID \
      --document-name AWS-StartPortForwardingSession \
      --parameters '{"portNumber":["'3389'"], "localPortNumber":["'50001'"]}'

You can also add the following helper functions to your .bashrc or .zshrc file:

    alias instances='aws ec2 describe-instances \
        --query "Reservations[*].Instances[*]. \
        { \
            name: Tags[?Key=='\''Name'\''] |[0].Value, \
            instance_id: InstanceId, \
            ip_address: PrivateIpAddress, \
            state: State.Name \
        }" \
        --output table'
    
    beam() {
        aws ssm start-session --target $1
    }
    
    tunnel() {
        aws ssm start-session --target $1 
           --document-name AWS-StartPortForwardingSession 
           --parameters '{"portNumber":["'$2'"], "localPortNumber":["'$3'"]}'
    }

This allows some cool things:

instances demo tunnel demo

What about my Ansible Playbooks?

Managing Instances over PowerShell, sh or RDP may be fun and all, but when you want to create some automation, ansible is one of the most popular approaches to handle complex configuration and update process on instances.

Ansible connects to the Instances using SSH or winRM, which is no longer possible if your instances are not reachable from the outside. To get Ansible to work with your instances you would need to tunnel port 22 of your linux machines or 5986 for your windows machines, which is painful.

Luckily there is also a connector plugin in development that would allow ansible to natively work with aws-ssm which may be integrated into a future version of ansible. You can follow the progress on this pull request.

If you rely on ansible for configuration management or whatever you do with ansible, you may be better off using a classic bastion host until the ssm connector plugin is available as part of ansible. If you do not rely on ansible, and just want to access instances for debugging or development purposes, ssm-session-manager is a great alternative to a bastion host.