SSH over AWS SSM
There is a saying that something is more than sum of it’s parts, meaning it is the specific combination of things that makes it useful or valuable. But it can also be that one those parts is especially useful on it’s own too.
I did post earlier how you can (and should) get rid of bad habbit of using bastion hosts.
The Essense of those can be summarized into one-liner in .ssh/config
Host i-*.* mi-*.*
ProxyCommand bash -c "aws ssm start-session --target $(echo %h|/usr/bin/cut -d'.' -f1) --region $(echo %h|/usr/bin/cut -d'.' -f2) --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
Let’s dissect above letter by letter
Host i-*.* mi-*.*
This applies following configuration when remote hostname matches one of the patterns. i-*
is for regular
EC2 instances and mi-*
will match for any managed instances if you have some.
Remember you can manage also non-EC2 VMs from other clouds or data center with SSM, and the same trick works for them too.
ProxyCommand bash -c "aws ssm start-session --target $(echo %h|cut -d'.' -f1) --region $(echo %h|/usr/bin/cut -d'.' -f2) --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
ProxyCommand is what ssh will run to open a connection to target host. Command can be almost anything as
long as it reads from stdin and writes to stdout. Here you will be using aws ssm start-session
to
open a pipe to remote host. Interesting part is how arguments of start-session
are constructed.
---target $(echo %h|/usr/bin/cut -d'.' -f1)
Here echo %h
will send the remote hostname you specified on command line, and it will be cut
at
the first dot (.) leaving just the instance ID of it.
--region $(echo %h|/usr/bin/cut -d'.' -f2)
Using the same way the remaining part after the first dot (.) is extracted and passed as region to aws-cli.
Calling your hosts by InstanceID.Region
is just a clever way to make this work for multiple regions
without you having to change AWS_DEFAULT_REGION
environment variable to jump from one region to another.
Another small but important detail is calling cut
with full path. Leaving the path off could cause
problems when ProxyCommand is run with a shell that doesn’t it’s PATH set properly. This is also a bit
of added security so you can be sure what commands are run.
---parameters 'portNumber=%p'
And finally just another parameter substitution where %p
is replaced with port number of remote host.
Please refer to manual for complete list of configuration options in .ssh/config
and tokens
available for ProxyCommand
Now you can, not just ssh
to instance without having a network connectivity between your laptop and VPC, but also use scp
and other ssh-tools. This has proven itself extermely useful during ad-hoc troubleshooting for clients I don’t
work with on daily basis. All I need is access to AWS API with key/secret/token, instance-id and region.
% ssh username@i-abcdef01234567890.eu-central-1
Last login: Wed Dec 30 08:23:41 2020 from localhost
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
[username@ip-10-0-0-91 ~]$
I learned this valuable one-liner from Jim Lamb’s blog.
Edit 19/10/2022; AWS blog has a more visual version of the same use-case with EC2 Instance Connect deploying temporary SSH keys