Home EC2 Instance Connect.
Post
Cancel

EC2 Instance Connect.

I used to be a Linux Engineer, and it will come as no surprise that one of my most used tools was/is SSH. I love SSH, whether its for my homelab environment, AWS network access, or copying stuff using rsync, the possibilities of SSH are endless. (foreshadowing) SSH is like the VIP all access pass to your servers, but with top-notch security and encryption so tight, it’s like wrapping your data in a bulletproof vest. And setting it up? Piece of cake once you get the hang of it. So yeah, I am a big fan of SSH.

Bastion hosts though, not so much.

Those of us that use SSH will inevitably have wrestled with “Bastion Hosts” or “Gateway servers” or “Jump Boxes”. Whatever you call them, they are a permanent exposure to the unwashed masses of the internet, or if you are using VPN’s VLAN’s they are still exposed to Tommy the intern who will think he is on his test machine and kill the entire thing…

Bastion Hosts come with administrative overhead, they need to be secure, up-to-date with patching and run pretty much continuously, so as with all things, there is a cost involved with running them.

What’s the alternative?

Well, for your AWS environment now there is Amazon EC2 Instance Connect

Setting it up

Assuming you have a VPC with a bastion host set up already, here are the steps for implementing Amazon EC2 Instance Connect.

Security Group

In order for the EC2 Instance Connect endpoint to be allowed to connect to the EC2 instances we need to configure security groups that will allow this to happen. I like to create a single “self referencing” security group that allows communication between the endpoint and the instances on all ports.

Create a new security group called ec2-instance-connect Create Security Group Do not define any inbound rules, after its creation edit the security group and add the following inbound rule:

1
2
3
Type: All
Ports: ALl
Source: SELF

Modify Security Group This is why we have to create it first and then modify it after creation, in order to reference itself, the security group needs a security group ID, you can see it at the top in the context menu:

EC2 > Security Groups > sg-0ea2b9bb1054dcc1f - ec2-instance-connect

The security group ID is the bit starting with sg-

In the creation page of the security group it does not yet have an ID, so it can not reference itself quite yet.

What this does is very cool, any network entity in AWS that has this security group applied to it will be allowed to communicate on all ports with any other network entity that is in this security group.

Endpoint

Go to your VPC management page and go to the endpoints section, create a new endpoint and choose the following settings: Create Endpoint Make sure you select the security group we created earlier.

Its worth pointing out that the endpoint is created in one subnet only, i.e. one Availability Zone. If EC2-Instance-Connect is critical for your business, there is a single point of failure. I’ve tried creating more than one, but at this time AWS will only allow you to have one per VPC: Quota Current quotas are:

Description Quota
Maximum number of EC2 Instance Connect Endpoints per AWS account per AWS Region 5
Maximum number of EC2 Instance Connect Endpoints per VPC 1
Maximum number of EC2 Instance Connect Endpoints per subnet 1
Maximum number of concurrent connections per EC2 Instance Connect Endpoint 20

I had a look in the Service Quotas Console, but I could not find an entry for EC2 instance connect endpoints.

Service Quotas Console

Instance

EC2 Instance Connect comes preinstalled on the following AMIs:

  • AL2023
  • Amazon Linux 2 2.0.20190618 or later
  • macOS Sonoma 14.2.1 or later
  • macOS Ventura 13.6.3 or later
  • macOS Monterey 12.7.2 or later
  • Ubuntu 20.04 or later

EC2 Instance Connect is not preinstalled on the following AMIs, but you can install it:

  • Amazon Linux 2 prior to version 2.0.20190618
  • CentOS Stream 8 and 9
  • macOS Sonoma prior to 14.2.1, Ventura prior to 13.6.3, and Monterey prior to 12.7.2
  • Red Hat Enterprise Linux (RHEL) 8 and 9
  • Ubuntu 16.04 or 18.04

I’m going to assume your instance is running one of the AMI’s that has it preinstalled.

Simply add the security group we created earlier to the instance security configuration.

Key Pair

Having the PKI setup is not strictly nescesary as long as some key exists. I have done some testing and the results are interesting.

Instance Configuration AWS Console AWS CLI SSH client
No keypair ❌ Can not connect ❌ Can not connect ❌ Can not connect
Random Keypair (deleted after creation) ✅ Can connect ✅ Can connect ❌ Can not connect
Owned Keypair (uploaded RSA) ✅ Can connect ✅ Can connect ✅ Can connect

What I think the agent is doing, is doing something clever to allow the EC2 Instance Connect endpoint to connect, but for that to work there needs to be an existing ~/.ssh/authorized_keys file. With the random keyfile I simply generated a keypair, assigned it to the instance and immediately deleted it after the instance was created (to ensure the console didnt have access to the private key). that worked perfectly fine for AWS console and AWS CLI, but obviously not for pure SSH as it needs to negotiate encryption using PKI.

Testing it out

AWS Console

The simplest way to test it is using the AWS console. Console Connect Console Connect2

CLI

Cool kids use the CLI, first I will need to authenticate with AWS on my shell, I will not go into details on how to do that, look here if you need help.

I then run aws ec2-instance-connect ssh --instance-id i-0e0ca76444bbafdc3 where i-0e0ca76444bbafdc3 is my instance ID.

CLI Connect

SSH

This is where its at, pure SSH, accept no substitutes! text To get SSH to play nice with EC2-Instance-Connect we will have to use the ProxyCommand option flag, wich uses the AWS-CLI tool to connect to and authenticate with the EC2-Instance-Connect endpoint.

I run the following command: ssh ec2-user@i-03359a6310bdecd35 -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-03359a6310bdecd35'

SSH Connect

Now we are in business, being able to connect using pure SSH allows for some very interesting use cases.

SSH Magic

SSH Config

That command I ran earlier with the -o ProxyCommand= stuff was neat, but nobody wants to type that all the time, this is where the SSH Config comes in. In your SSH config add the following:

1
2
3
4
Host ec2
  HostName i-03359a6310bdecd35
  User ec2-user
  ProxyCommand aws ec2-instance-connect open-tunnel --instance-id i-03359a6310bdecd35

Now all I have to do is run ssh ec2 and im in.

Tunnels

The internet is a series of tubes, as you well know, and tunnels are a bit like tubes, so lets set some up.

Tunnel to Target

You can tell SSH to create a tunnel to a target server using the SSH server as a stepping stone. the command will look something like this: ssh -L <local_port>:<target_server>:<target_port> <ssh_server> SSH Tunnel to Target

This will expose a port on your local machine, which you can then connect to by going to localhost:<local_port> and that will tunnel to the target server on the <target_port>. Using the example image above, after running the command ssh -L 8080:web.mycompany.com:80 ssh.mycompany.com it would expose port 8080 on my local machine, which I could open by directing my webbrowser to http://localhost:8080 which would show me the webserver exposed on the server web.mycompany.com on port 80

Using EC2-Instance-Connect that would look like this:

ssh -L <local_port>:<target_server>:<target_port> <ssh_server> -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id <ssh_server_ID>'

Tunnel to Self

Like the example before, you can have the SSH server and the target server be the same, that would result in the following: SSH Tunnel to Self This is useful if you want to expose a server’s port locally without using an intermediate server. Doing this with EC2-Instance-Connect it would look like this:

ssh -L <local_port>:localhost:<target_port> <target_server> -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id <ssh_server_ID>'

Socks Proxy

If you want complete SSH tunneling freedom, a “Socks Proxy” might be your answer. What this does is, it sets up a SSH tunnel to a target server, exposing a local port as a proxy which you can use with your web browser (among other things). this will give you the freedom to try other ports as you need them. SSH Socks Proxy Using EC2-Instance-Connect you would run the command like this:

ssh -D <local_port> <target_server> -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id <ssh_server_ID>

Then in your web browser (or other application that can leverage socks proxies) simply add a proxy, like this: Browser Config

In your browser you can now enter anything that the SSH server would be able to connect to. Pretend like you are running the browser from the SSH server. Web Page

Rsync

With the EC2 instance added to the SSH Config described earlier, you can now use rsync to synchronise files from your local machine to the server or vice versa:

As a test I’ve created 5 files of 1MiB each, Web Page I then rsync them to the server designated in my SSH config as ec2 Web Page And that synchronises the files up to the EC2 instances just as expected: Web Page

⚠️ Please keep in mind cross AZ charges apply.

If your endpoint is in another Availability Zone than the server you are transferring files to, you will incur a cost for moving data from AZ1 to AZ2.

Remote Docker Host

Now that we know how to set up tunnels, we can connect to unix sockets over SSH, this will allow us to connect to a docker daemon hosted on an AWS EC2 instance while running the docker commands on our local machine. to set it up, install docker on an EC2 instance by executing the following commands:

1
2
3
 sudo yum install docker
 systemctl enable docker
 systemctl start docker

We will also need to allow the ec2-user to connect to the docker socket by making it a member of the docker group. issue the following commands:

1
2
 usermod -aG docker ec2-user
 id ec2-user

that id command should show that the user is a member of the docker group.

On your local machine ensure that the docker client is installed but that there is no docker daemon running. Create a tunnel to the instance connecting to the docker socket using the following command:

ssh -L localhost:2377:/var/run/docker.sock <ssh_server> -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id <ssh_server_ID>'

or if you have a working SSH config rule for your server:

ssh -L localhost:2377:/var/run/docker.sock ec2

Then create the following environment variable:

export DOCKER_HOST="localhost:2377"

That should be it, now when you run docker commands locally, it should behave normally, but the containers are actually running on the EC2 instance.

Docker

Other Cool Tricks

Let your imagination run wild, VS-code has this cool thing where you can run it remotely, ive set it up and its very interesting, link here.

Background Running

The commands I showed earlier are for demonstration purposes only, if you want to do this in earnest you will probably want the tunnels to run in the background. Use the following flags in your SSH command to get the best results:

1
2
-f Requests to go to background just before command execution.
-N Do not execute a remote command.

For further flags/options look here Don’t forget to kill the tunnel process if you do not need it anymore.

References

Amazon EC2 Instance Connect News

Instance Connect Documentation

Amazon Blog Post

EC2 Instance Connect Endpoint Quotas

SSH man page

Docker Deamon Socket Options

This post is licensed under CC BY 4.0 by the author.