AWS EC2 Key Pairs versus EC2-Instance-Connect
Push SSH server access and RBAC into IAM and get rid of Key Management tooling
When it comes to managing secure shell (SSH) access to Amazon Elastic Compute Cloud (EC2) instances, there are two primary methods:
AWS EC2 Key Pairs SSH
AWS Instance Connect.
Both methods provide secure and convenient ways to access EC2 instances, but they have different pros and cons that make them suitable for different use cases. In this blog post, I'll briefly explain, compare and contrast between the two options highlighting which may suit your needs best.
EC2 Key Pairs
AWS EC2 Key Pairs SSH is the conventional method of SSH access to servers from times immemorial. With this method, users generate a key pair consisting of a public key that is uploaded to AWS, and a private key that must be kept safe, in a Key Management tool for example.
How it works
You either upload the SSH key to AWS as a Key Pair and reference it during instance creation for AWS to set it up for you or you leverage the User data
property at AMI boot (if plain EC2) to download and configure it yourself.
If you are using ECS Fargate / Kubernetes, the process varies according to preferences.
Pros:
Wide usage and familiarity among users spanning back decades meaning there are many resources available to help with key pair management.
Plentiful of SSH clients and tooling around it.
Cons:
Keys must be stored securely
Distribution to authorized users is an operationally cumbersome process
A compromised key requires updating all associated instances which can be quite an involved and lengthy process
Keys must be uploaded into AWS EC2 Key Pairs before starting an instance
Example
Step 1: Initialize the CDK App
First, we need to initialize a new CDK app in our preferred language, which for me is currently TypeScript. Let’s open up a terminal and run the following commands to get started:
mkdir -p ec2-access-ssh
cd ec2-access-ssh
npx cdk init app --language=typescript
Step 2: Define the VPC Stack
Next, we need to define the VPC stack that our EC2 server will be a part of. While this can all be done in one stack, it is best practice to separate the constructs as the layers of an onion, each layer building on top of the previous one, reducing potential blast radius for any change and reducing the number of resources that need to be touched for any one change (blast radius for lower layers is greater than that of higher layers, but you still get reduces deployment time).
Open up the lib
folder and create a new file called vpc-stack.ts
. Add the following code to create a new VPC with a public subnet:
Step 3: Define the EC2 Stack
Now create a file named ec2-stack.ts
with the following contents:
Note
The SSH key found in the example source code has been created using
> ssh-keygen -t rsa -f ec2_rsa_key
and the key is committed into source to have a fully running example. You should use the command above to generate your own secure key and keep it safe. If you are within an enterprise organization, there surely are established processes for keeping the key safe.
Step 4: Define the App Stack
Which you can now run with
> npx cdk deploy --all
followed by the ssh command provided in the output of the command above, that is contained in the Ec2Stack
class, as can be seen below:
Step 5: Cleanup
Now that we have created all the needed resources and have gained SSH access, it is time to delete all the resources to avoid accruing unwanted charges to our account:
> npx cdk destroy --all
EC2 Instance Connect
AWS Instance Connect provides a more streamlined approach to SSH access management. With Instance Connect, you don't need to manage SSH keys at all or configure the SSH daemon yourself. Instead you can use the AWS Management Console, AWS CLI, or SDKs to initiate a one-time use SSH connection to an instance using the Instance Connect client installed on the server. The client comes already installed on AWS AMI’s based on
Amazon Linux 2
2.0.20190618 or laterUbuntu 20.04
or later
How it works
This method relies on you creating an ephemeral 60 seconds short-lived SSH key and pushing it to the EC2 Metadata Service.
As the Instance Connect client is registered in the SSH daemon config file located in /etc/ssh/sshd_config
for the:
AuthorizedKeysCommand
AuthorizedKeysCommandUser
hooks, when you initiate a SSH connection it will leverage the EC2 Metadata service to verify your ephemeral key.
To achieve this simplified secure connection you can either use:
mssh
This is the AWS provided Instance Connect CLI utility that automatically generates the SSH key and publishes it to the EC2 Metadata service to be used for the next 60 seconds abstracting away the entire process from you. Install process can be found here.
aws
cliUse the standard aws cli to perform the connection. This is a two step process as you first need to create a key to be pushed. Herein lies part of the abstraction performed by the
mssh
option above.
How to do it
In order to limit who can connect to a specific instance, here is an example IAM policy that would grant access to exampleuser
to a specific instance:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2-instance-connect:SendSSHPublicKey",
"Resource": "arn:aws:ec2:eu-west-1:111122223333:instance/i-0123456789abcdefg",
"Condition": {
"StringEquals": {
"ec2:osuser": "exampleuser"
}
}
}
]
}
In order to connect, you can either use
mssh <instance-id>
or
ssh-keygen -t rsa -f ephemeral_key
aws ec2-instance-connect send-ssh-public-key \
--region <region> \
--availability-zone <az> \
--instance-id <instance-id> \
--instance-os-user <ec2-user/ubuntu> \
--ssh-public-key file://ephemeral_key.pub
ssh -o "IdentitiesOnly=yes" -i ephemeral_key <user>@<instance-dns>
Pros:
Removes need for a Key Management infrastructure and run-book process for creating, distributing, maintaining, and rotating.
User does not need to download key to their local machine and removes risk of it being extracted if user machine is compromised.
Granular RBAC to instances is pushed into the already existing IAM role distribution policy of the company. To provide access you need to grant
ec2-instance-connect:SendSSHPublicKey
ec2:DescribeInstances
Generates audit trail in CloudTrail for each key push performed
You do not need to contact somebody to see which key is compatible, you generate it on the spot
Cons:
Requires additional software package to be installed on servers unless running AMI which already contains it. This may entail generating a new AMI golden image through Packer or whichever may be the company’s standard golden image generating flow.
Users need to be educated on this new process flow ( should be straightforward though).
Vendor specific access flow
Example
In order to get a basic EC2 configured for access, you can follow the example code from the EC2 Key Pairs section above, with the following to the ec2-stack.ts
file so we do not have any SSH key loaded by AWS into the instance at boot time:
Once you have the EC2 instance running, you can use the commands in the ‘How to do it’ section above to connect, conveniently outputted by the deploy command:
Note
This assumes you are using a recent, compatible AMI that has the EC2 Instance Connect agent already installed. For images that do not contain it, you can find the installation steps here.
Conclusion
Ultimately, the decision between EC2 key pairs and AWS Instance Connect will depend on your specific use case and requirements. It is up to you to decide what best fits your organization’s needs but if your conditions allow, the standardized RBAC and greatly simplified key maintenance and connection process can help in keeping a team nimble and productive.