On-demand Opening of SSH Ports on AWS

Posted on 10 December 2016

Last updated on

Image of BBEdit running on a MacBook Pro with the article's script loaded
Scripting helps automate mundane things and make your life wonderful.

Update: The script has been amended to support IPv6 as well and will allow access coming from your public IPv4 and IPv6 address simultaneously. I will also use CIDR notation for letting both addresses in, which is optional for IPv4 but mandatory for IPv6. Also note that I’m no longer using AWS myself for tobyx.com.


While other cloud providers usually force you to leave SSH open, restrict access with your own firewalls, or set up IP ranges from which access is possible, AWS has the major advantage of security groups1. You can stop any traffic dead in its tracks before your server has to deal with it.

You can configure Security Groups in a few ways:

  • Through the AWS web interface.
  • With the AWS CLI command-line interface.
  • Not at all.

Some people just leave their SSH port open. This is arguably ok with public key authentication in place, but being able to prevent automated login attempts from around the world gives you peace of mind. You can also restrict access to your static IP or IP ranges, but people on ISPs with dynamically changing IP addresses are out of luck.

Pre-requisite: Install the AWS CLI

The AWS-CLI website has information on how to install the CLI, with most Linux distros having packages for it for an easy install. On macOS, you can go the Homebrew route.

If you haven’t already done so, install Homebrew. Homebrew is a package manager for all the developey things your Mac lacks—which is usually not much compared to Windows, but still a lot for a developer to be sad. Use Homebrew and its version of Python.

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Make sure you know what this command does, refer to the Homebrew website.

Now we install Python and the AWS CLI.

brew install python
pip install --upgrade --user awscli

Scripting

I’m using a script to connect to my AWS instances. The script will open an SSH port in the associated Security Group, connect me via SSH, and when the connection ends or disconnects, remove that rule again, closing access to the instance off.

Here’s the script connect-aws.sh:

#!/bin/bash

# -----------------------------------------------------------
# connect-aws.sh
# -----------------------------------------------------------

# Variables

instancehost="yourhost.example.com"
sshuser="admin"
sshport="22"
sshidentity="~/.ssh/yourprivatekey.key"
securitygroup="sg-1234a12345b1cd12e"
region="eu-central-1"

# Execution

ip=`curl -s https://api.ipify.org`
ip6=`curl -s https://api6.ipify.org`

if [ "$ip" != "$ip6" ]; then
  ip6avail=1
fi

echo "Current IP address: $ip"

if [ $ip6avail ]; then
  echo "Current IPv6 address: $ip6"
else
  echo "IPv6 is not available."
fi

echo "Instance Host: $instancehost"
echo "AWS Security Group ID: $securitygroup"

echo "Adding IP $ip to AWS Security Group $securitygroup in region \
  $region ..."
aws ec2 authorize-security-group-ingress --group-id $securitygroup \
  --region $region --ip-permissions \
  IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=$ip/32}]

if [ $ip6avail ]; then
  echo "Adding IP $ip6 to AWS Security Group $securitygroup in region \
  $region ..."
  aws ec2 authorize-security-group-ingress --group-id $securitygroup \
  --region $region --ip-permissions \
  IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges=[{CidrIpv6=$ip6/128}]
fi

echo "Connecting via SSH to $instancehost ..."

ssh $instancehost -l $sshuser -p $sshport -i $sshidentity

if [ $ip6avail ]; then
  echo "SSH connection closed. Removing IP $ip and $ip6 from AWS \
    Security Group $securitygroup ..."
  aws ec2 revoke-security-group-ingress --group-id $securitygroup \
    --region $region --ip-permissions \
    IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=$ip/32}]
  aws ec2 revoke-security-group-ingress --group-id $securitygroup \
    --region $region --ip-permissions \
    IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges=[{CidrIpv6=$ip6/128}]
else
  echo "SSH connection closed. Removing IP $ip from AWS Security \
    Group $securitygroup ..."
  aws ec2 revoke-security-group-ingress --group-id $securitygroup \
    --region $region --ip-permissions \
    IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=$ip/32}]
fi

echo "Done."

You only need to modify the # Variables:

instancehost: Hostname or IP address of your AWS instance.
sshuser: The local user name you’re using to login via SSH.
sshport: The SSH port, default is 22.
sshidentity: The path to the private key used for connecting to your AWS instance.
securitygroup: The Security Group identifier.
region: Your AWS region (since you can have instances in multiple regions).

Note: To get the current IP address programmatically, I’m using https://www.ipify.org. By default, they will simply output your current IP address as plain text. There are more options, so please have a look at their site. Their traffic is encrypted.

Make the script executable:

$ chmod u+x connect-aws.sh

Now, simply execute the script to open a SSH shell to your instance:

$ ./connect-aws.sh
  1. AWS Security Groups are virtual firewalls, not only giving you fine-grained control over external access to your instances, but also isolating your instances from each other if needed. Great great great. ↩︎