Streamlining EC2 Access with AWS Session Manager and Port Forwarding: An In-Depth Guide

Streamlining EC2 Access with AWS Session Manager and Port Forwarding: An In-Depth Guide

EC2 is one of the fundamental services provided by AWS and used by many who come across the platform. This compute service has applications that extend far and wide depending on the use cases.

One of the basic tasks, we do with EC2 is SSH into it, to “do stuff”. This article revolves around the very topic of accessing the EC2 instance via SSH.

Conventional Way

A private Key is required whenever you want to establish a connection between your machine and the EC2 instance. It’s an accepted standard practice that has been used for many years to connect to a remote machine. But using a private key comes with its baggage.

What if:

  • you lose your keys

  • delete your keys

  • someone steals your keys

  • you don’t know who has access to your keys (this bothers me)

Every one of us is susceptible to the above scenarios. And it brings chaos, confusion, and vulnerability when you just want to perform your task.

Access to your keys means access to your machine. If you are distributing the keys to multiple team members or using multiple keys so that they can connect and perform some action, then it immediately becomes a problem. You are entrusting the access and security of your workload that spreads across different machines, regions and god knows where. Also, logging becomes a hassle to get visibility into what users are doing on the system.

Also, SSH-ing in itself is a tedious task:

  • you have to assign an elastic IP address so you are always connecting to the same public IP address.

  • add a port for SSH to the Security Group and expose it to the world or a part of it.

  • enter a long command or configure ssh config

  • make sure private keys are in a secure location on your machine

Example of a command for ssh-ing into an EC2 instance.

ssh -i "private_key.pem" username@<ip-address>.<region>.compute.amazonaws.com

AWS Session Manager: The modern secure way

Well, I am not the only one talking about all of the issues above. It’s faced by the development, DevOps, and security teams across different organizations. When it comes to security, AWS made sure it is providing the right set of tools, mostly an intuitive one to make all of the aforementioned issues go away.

AWS Session Manager is a one-stop service that should always be used with your EC2 instance. It immediately shows its benefits upfront:

  • no need for private keys 🙌

  • provide access directly to IAM users with fine-grained policies 🔐

  • no need to expose SSH ports on Security Groups ✨

  • no need for an elastic IP address 🎉

  • centralized logging with direct access to CloudWatch, S3 🪵

  • and most importantly, an intuitive command to connect to an instance 🌟

Example of a command for connecting to an EC2 instance.

aws ssm start-session --target <instance-id>

Setting up Session Manager with EC2

Setting up Session Manager with EC2 is an effortless process. There is one extra step before we launch any new instances.

Create a new Role that allows access from Session Manager to the EC2 instance.

  1. Go to IAM → Roles

  2. Select the AmazonSSMManagedInstanceCore policy

  3. Name it anything that makes sense, I will name it as SSMInstanceCore.

Now that we have a Role, let’s create a new EC2 instance and attach it to it.

  1. Click on Launch Instances

  2. Give your instance a relatable name.

  3. I will choose the default Amazon Linux 2 AMI and Instance type (it selects the free tier by default).

  4. Finally, in the Key Pair section, choose Proceed without a key pair ⚡️

  5. On Network Settings you can name your security group and keep it empty.

  6. Make sure Auto-assign public IP is set to enabled inside of Network settings, for Session Manager to connect to it.

  7. I will leave the storage as default.

  8. On Advanced Settings → IAM Instance Profile: Select the Role we created in the earlier section. I named it SSMInstanceCore.

  9. Select Launch Instance 🚀

Setting up permissions to access via Session Manager

The minimum policy configuration we’ll for an IAM user to be able to connect to an EC2 instance is as follows:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ConnectToSSMInstance",
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/i-010a4c6d9cb6223e3",
                "arn:aws:ssm:ap-southeast-1:*:session/*"
            ]
        }
    ]
}

You can make it more secure by tightening up this policy. But we'll leave it like above.

Once you attach this policy to a user, they’ll be able to connect to the defined EC2 instance using Session Manager.


NOTE: Installing SSM Agent on your Instance if it's not pre-installed

Before we move further, we need to make sure our EC2 instance has the SSM agent installed. This list provides a list of AMIs that has the agent pre-installed. If you are using an instance that does not have an SSM agent, follow the docs and follow the instructions on manually installing the agent for Linux, Windows or Mac.

Accessing EC2 Instance using Session Manager

You can access EC2 Instance using Session Manager, in 3 different ways:

  1. From the EC2 instance connect option

    This will redirect the user to the Session Manager console and start the session from there, but we’ll see how to connect from the Session Manager console after this.

    1. Select your EC2 instance and click on connect.

    2. Select Session Manager and Click on Connect

    3. You can start using your instance

  2. Connect from Session Manager

    1. Go to Systems Manager and Select Session Manager on the left-hand side menu.

    2. Click on Start Session

    3. Select the name of the instance you want to connect to and select Start Session

    4. We’ll get the same terminal window, we got from the first option.

Connect from Terminal

Probably my favorite way of connecting to the EC2 instance with the Session Manager is via terminal. For this, you need to have AWS CLI installed on our machine and configured. After that, you need to install the Session Manager plugin for the CLI.

After that, anytime we want to connect to our instance, we can run the following command:

aws ssm start-session --target <instance-id>

aws ssm start-session --target i-010a4c6d9cb6223e3

You’ll get a similar screen as below:

Setting Up Logging

One of the benefits of Session Manager is centralized logging. You can have multiple users doing different things on the instance, and you can track all of that in a single log group in CloudWatch or you can upload it to an S3 bucket. In this blog, we'll be sending it to CloudWatch.

Before we dive into logging, we need to update our Role SSMInstanceCore and attach the CloudWatchAgentServerPolicy. This will allow the EC2 instance to send logs to the CloudWatch log group.

Setup Permission to send logs to Cloudwatch

  1. Go to IAM -> Roles, Search for the role we created earlier(if you are following the blog: it is SSMInstanceCore) and select Attach policies

  2. On the search bar, paste CloudWatchAgentServerPolicy and select it and click on Attach Policies.

    Create a new Log Group

    Before we can send any logs, we need to create a new log group to hold our logs. Run the command below to create a new log group:

     aws logs create-log-group --log-group-name SSMInstanceLogs
    

    Command to create a new AWS CloudWatch log group

    Note: that all the session logs for any EC2 instance will be forwarded to this log group. You can't assign a log group per instance. The best thing is the logs will be JSON formatted so they'll be easy to read.

    Send Session Logs to CloudWatch

    Now that our EC2 instance has permission to write to CloudWatch, we can go to Session Manager under Systems Manager.

    1. Select the "Preferences" tab and Click on "Edit"

    2. Enable CloudWatch logging and select "Stream session logs"

    3. Then add the name of the CloudWatch Log group we created earlier.

    4. Our log group is not encrypted with Customer Managed Keys (CMK) so we do not enable the "Enforce encryption" option.
      But a side note, CloudWatch logs are always encrypted using AWS-managed keys.

    5. Click on Save

      Viewing Logs

      Now that we have logs enabled for Session Manager, let's jump into our EC2 instance and run some commands.

      Now let's had to our Log Group and check our log stream:

Port Forwarding

Managing Access from EC2 to private RDS

Another common use case of an EC2 instance is to use it as a bastion host (jump server) to connect to resources on a private subnet securely.

Let's say there's an RDS Postgres instance on a private subnet running on port 5432 that we'd like to connect to. RDS has no access to the internet and is accessible only by the services within that VPC.

To connect to this database service we need to:

  1. create an EC2 instance in that VPC

  2. go to the Security Group attached to the RDS instance and, select "Edit inbound rules" and add a new inbound rule with:

    • type as PostgreSQL

    • Source as the Security Group name of our EC2 instance.

  3. Then Click on Save.

That's it! Now our EC2 instance has access to RDS. Now we can use our EC2 instance as a bridge to connect our local machine to the RDS instance.

Connecting to private RDS

Session manager makes it easy to forward port from our EC2 instance with the use of Documents. Documents are blueprints of action that the Session manager performs on our EC2 instances. AWS manages hundreds of these documents for common actions and they have one for Port Forwarding too.

To view what documents are available. Go to System Managers, and on the left-hand menu you can find "Documents" under "Shared Resources".

The document we are going to use is titled AWS-StartPortForwardingSessionToRemoteHost. You can search for it in the Documents section to get its details. Click on the result.

Go to the Details tab and under Parameters section you can find what parameters we can pass to this document. Parameters are passed as a key-value pair.

To use this document, we need to update the policy we created above for users:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ConnectToSSMInstance",
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/i-010a4c6d9cb6223e3",
                "arn:aws:ssm:ap-southeast-1:*:session/*",
                "arn:aws:ssm:*:*:document/AWS-StartPortForwardingSessionToRemoteHost",
            ]
        }
    ]
}

Now the only thing we need to grab is the database host URL.

You can go to the RDS instance and on the "Connectivity & Security" tab find "Endpoint & port". Now we have the parameters gathered for port forwarding let's see it in action.

aws ssm start-session --target i-010a4c6d9cb6223e3\
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"portNumber":["5432"],"localPortNumber":["5432"],"host":["testdatabase.csrc3be2awyi.ap-southeast-1.rds.amazonaws.com"]}'

I am port forwarding from RDS on a private subnet to my local machine on port 5432. Since I am using Postgres I can connect to it using the following command:

psql -U postgres -h localhost -p 5432

That's it! Now you have a connection to a private RDS instance from your EC2 instance using session manager. ✨

Conclusion

Session Manager is a powerful tool from a security and usability perspective. There are many actions you can do with it on an EC2 instance and having the power of Documents makes it more resourceful.


If you have any suggestions or queries about this article, you can reach out to me on Twitter or add a comment below. 🎉