AWS Systems Manager Session Manager: bye bye bastion hosts!

There are customers where public internet access is no go. But at the same time you need to access your EC2 instances, right? How would you approach this? Usually, we use bastion hosts, that's basically a different name for jump host you can use to access internal resources. In AWS, you don't need to manage such extra instances and take care of all the low-level configuration. Instead, you can leverage fully managed Session Manager from the AWS Systems Manager suite!

Before you start

First, you need to make sure that your systems have SSM Agent installed. There's decent documentation available but if you are using official AMI, you can skip this since SSM Agent is already pre-installed.

Also, your instances need proper IAM permissions. And that's actually pretty simple task, just attach AWS managed policy AmazonSSMManagedInstanceCore to your instances and that's it! Alternatively, we can just copy&paste the content of this managed role to the existing user-managed role. Both scenarios work well.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeAssociation",
                "ssm:GetDeployablePatchSnapshotForInstance",
                "ssm:GetDocument",
                "ssm:DescribeDocument",
                "ssm:GetManifest",
                "ssm:GetParameter",
                "ssm:GetParameters",
                "ssm:ListAssociations",
                "ssm:ListInstanceAssociations",
                "ssm:PutInventory",
                "ssm:PutComplianceItems",
                "ssm:PutConfigurePackageResult",
                "ssm:UpdateAssociationStatus",
                "ssm:UpdateInstanceAssociationStatus",
                "ssm:UpdateInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2messages:AcknowledgeMessage",
                "ec2messages:DeleteMessage",
                "ec2messages:FailMessage",
                "ec2messages:GetEndpoint",
                "ec2messages:GetMessages",
                "ec2messages:SendReply"
            ],
            "Resource": "*"
        }
    ]
}

Viewing available sessions

When the instances are ready, we can view all the available session in the System Manager.

And you can even start a new session from here. It'll open a new window with web-based terminal.

However, it does not feel right for day-to-day operations 😂. Let's connect there directly from the workstation.

Just one more step …

It's just ssh, right? So we're almost ready, we just need to quickly prepare our local workstation by installing Session Manager plugin. This plugin is available for all major platforms, just copy&paste a few lines from the documentation and that's pretty much it.

curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb"
sudo dpkg -i session-manager-plugin.deb

Now, we can just add following lines to ~/.ssh/config so shh command knows how to handle hosts starting with i-* or mi-*.

host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

Good old SSH

From now it's really straightforward. Get your instance id and open the SSH session.

ssh ubuntu@i-0182d91e85d69cf00

Yeah, it's that simple. We can treat it as good old SSH from here. No strings attached.

Key advantages of Session Manager service

The obvious was already mentioned: no need to manage own EC2 instances for bastion hosts.

But there's more of it. You can even control access to the instances with IAM! User with the following policy attached can access all three instances mentioned in the Resource property. As always, everything is documented so suit yourself.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:us-east-2:123456789012:instance/i-1234567890EXAMPLE",
                "arn:aws:ec2:us-east-2:123456789012:instance/i-abcdefghijEXAMPLE",
                "arn:aws:ec2:us-east-2:123456789012:instance/i-0e9d8c7b6aEXAMPLE"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}

And last but not least, you can review all the open session and you can even inspect the history. This will come handy in case of any audit 😏. From this perspective, AWS Systems Manager Session Manager is even more secure than traditional setups with bastion hosts (of course, I'm not talking about over-engineered configurations).

Wrap

Apparently, there's no need to stick with traditional bastion hosts, AWS System Manager Session Manager can do all the job. Moreover, you can granularly control SSH access via IAM. That's actually, at least from my point of view, the most powerful feature.

Say bye-bye to your jump hosts!