Leaving inbound EC2 SSH ports open greatly increases the risk of unauthorized entities running commands on the server. In the perfect world, each developer with access rights would use only a single static IP address. You could whitelist it in an AWS security group firewall in addition to using standard SSH keys based authentication.
In practice, distributed remote dev teams often need to SSH into the servers from constantly changing IP addresses. In this tutorial, I’ll describe how to grant temporary SSH access for dynamic IP addresses using simple AWS CLI bash scripts.
Your EC2 security group firewall currently whitelists
0.0.0.0/0 for port
22? You’d like to improve the security of this setup without getting lost in AWS complexities? Than this tutorial is for you.
Why not AWS Session Manager?
AWS offers an excellent tool for solving exactly this issue, e.i. granting temporary SSH access rights without opening the SSH ports in security groups.
Unfortunately, it requires you to install additonal software on both server and client, so for some use cases it might not be acceptable. The solution I’d like to propose requires only a standard AWS CLI with minimal permission installed on the client.
Let’s get started.
Check SSH access logs
Before you continue, you might want to check if uninvited guests are knocking on your server’s doors.
Every server instance with publicly facing IP address and opened ports is constantly targeted by malicious network scanning bots. Those bots are usually harmless but they can always start a DDoS attack or discover a vulnerability.
You can check how often your server is targeted by scanner bots by SSHing into it and running these commands:
/var/log/auth.logis the default SSH log file for Ubuntu systems
you should see the similar output:
Bot login attempts are failing in the
preauth phase because they are using passwords, and your server is configured only to accept public/private key based logins.
If you’ve enabled password authentication to your server by adding this line to
you could also see a similar output:
Unless absolutely necessary enabling password-based login should be avoided.
If you don’t see any output, looks like your server if currently not targeted. It was not the case for my AWS EC2 instances. All of them started receiving unwanted visitors just minutes after provisioning.
Completing this tutorial will permanently cut off all the scanner bots, and other unauthorized access attempts to your server.
The rest of this tutorial assumes that you already have a running EC2 instance on public IP with SSH access for your user.
Also make sure to install the AWS CLI before continuing.
Now you should remove all the security groups rules that allow connection to your EC2 instance with SSH port 22. Confirm that you can no longer connect by running this command:
18.104.22.168with the public IP and user of your EC2 server.
It should fail with the following output:
If you managed to connect, it means that there’s still some security group whitelisting inbound connections on port 22, so make sure to remove it before continuing.
Setup ephemeral security group firewall and IAM user
In EC2 section of AWS console go to Security groups and click Create security group. You can name the new group
ephemeral-ec2-ssh-access, and assign it to the same VPC as your EC2 instance. Don’t add any inbound rules.
Now go to Instances, select your EC2 instance, click Actions > Networking > Change Security Groups and assign your newly created
ephemeral-ec2-ssh-access security group.
Let’s move on to creating an IAM user now. Go to IAM section and click Users > Add user.
You can call it whatever you want, e.g.,
Complete the user creating wizard without granting him any policies and write down
Access key ID and
Secret access key .
Now go to the newly created user details and click Add inline policy. Select JSON policy format and paste the following content:
You have to replace:
REGION with the code of your AWS region, e.g.,
us-west-1 for North California.
ACCOUNT_NUMBER is your AWS account ID. You can find it in My account tab e.g.
SECURITY_GROUP_ID is the ID of the
ephemeral-ec2-ssh-access security group you just created e.g.
If your JSON correct in the policy review screen you should see the Summary mentioning your security group displayed:
If you can see a warning that policy does not grant any permissions than double-check your region, account number, and security group ID.
So much for clicking in the AWS UI. The rest of the setup can be done straight from the terminal.
Configure AWS CLI scripts
One more thing we need to do is to write two simple bash scripts, literally:
Running them will toggle the inbound rule in
ephemeral-ec2-ssh-access security group for your current IP address and port 22.
I usually add those scripts in
bin directory of each of my projects. Every project has a separate security group and IAM user assigned.
Let’s start by configuring your AWS CLI with the profile:
You need to provide correct access credentials for your new
SSHEphemeralEC2AccessGroupManager IAM user.
Now add the scripts doing the actual opening and closing of SSH ports:
us-west-1with the code of your AWS region
That’s it! Now execute your script and confirm that you can SSH into your server:
If you have trouble getting it to work, you can remove silencing the output of
aws command (
2> /dev/null) to help you debug. AWS CLI error messages are usually quite descriptive.
Inbound SSH port is now opened for your IP. Even if you forget to run
bin/close_ssh, no other IP address can access your EC2. It’s an additional security factor, so the correct SSH keys are still necessary. The risk of sometimes leaving the port open for a single IP is negligible. The potential attacker would first have to access the same IP as you. You should be safe, unless your next-door neighbor is up to something.
Automate toggling SSH access
I use this approach as a one-man team, but it should scale for more developers working on the same project. Each team member could use his ephemeral security group managed by a separate IAM user.
The additional security of constantly closed SSH ports hardly affects my workflow. I don’t have to use VPN, 2FA, or login into a special bastion host to access the production system securely. It’s just the good ol’ SSH and two simple scripts. Once configured, they are barely noticeable but guarantee to keep the bad guys out.
This approach might not grant your project the PCI compliance certificate, but should be good enough for many use cases.