Skip to content

Instantly share code, notes, and snippets.

@sayadi
Last active January 30, 2023 16:34
Show Gist options
  • Save sayadi/cec17d17b5c7953e9d50f551770a011d to your computer and use it in GitHub Desktop.
Save sayadi/cec17d17b5c7953e9d50f551770a011d to your computer and use it in GitHub Desktop.
How to SSH to an AWS EC2 Instance Running Linux from a CircleCI 2 Build

How to SSH to an AWS EC2 Instance Running Linux from a CircleCI 2 Build

The Problem

If you use EC2 to host your application, and a tool like CircleCI to automate the deployment of new versions of your application, chances are, you've run into the problem of Circle not being able to SSH into your servers.

The problem is Circle could change the public IP of your runner for every new Build which prevents it from being able to access (SSH) the EC2 Instance. Unfortunately, Circle is not able to provide a list of public IP addresses to whitelist on a Security Group attached to the EC2 Instance. More on this here: https://discuss.circleci.com/t/circleci-source-ip/1202

Assuming that, of course, you haven't allowed public access to port 22 on a production server, right?

Solution's Steps

The solution is to, temporarily, allow the current Circle runner access to the server in the config.yml of Circle. The steps for doing so can be summarised by the following:

1- Install the AWS CLI

2- Get the public IP of the current CircleCI runner

3- Get the AWS region in which the EC2 Instance is running

4- Get the ID of a Security Group attached to the EC2 Instance This can be specified as an Environment Variable in Circle Project Settings if you wish to avoid committing it to the source code.

5- Add an ingress rule to the security group

6- Give the ingress rule some time to propagate This step can certainly be improved, but waiting for 5 seconds seems to be working fine for now.

7- SSH to the server to deploy

8- Remove the ingress rule

Solution's Details

Below is a snippet to implement the above steps:

    # 1- Install AWS CLI
    curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
    unzip awscli-bundle.zip
    ./awscli-bundle/install -b ~/bin/aws
    
    # 2- Get the public IP of the current CircleCI runner
    PUBLIC_IP=$(curl ipinfo.io/ip)
    
    # 3- Get AWS Region
    # TODO Don't forget to replcae with your own Region
    AWS_REGION=us-east-2
    
    # 4- Get SG ID
    # TODO Don't forget to replace with your own SG ID
    SG_ID=sg-XXXXXXXX
    
    # 5- Add an ingress rule to the security group
    ~/bin/aws ec2 authorize-security-group-ingress --region $AWS_REGION --group-id $SG_ID \
      --protocol tcp --port 22 --cidr $PUBLIC_IP/24
    
    # 6- Give the ingress rule some time to propogate
    sleep 5
    
    # 7- SSH to the server to deploy
    ssh -o StrictHostKeyChecking=no ubuntu@EC2_public_IP \
        # other commands
    # TODO Perform steps to deploy
    # .
    # .
    # .
    
    # 8- Remove the ingress rule
    ~/bin/aws ec2 revoke-security-group-ingress --region $AWS_REGION --group-id $SG_ID \
      --protocol tcp --port 22 --cidr $PUBLIC_IP/24

IMPORTANT Do not forget to add the SSH key file (.pem) in the Project Settings in Circle.

The file below is a complete config.yml that you can use as a starting point.

.idea
.DS_Store
version: 2
general:
branches:
only:
- dev
- staging
- prod
jobs:
build:
docker:
- image: circleci/openjdk:8-jdk
steps:
- checkout
- run:
name: Deploy
command: |
# 1- Install AWS CLI
curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
./awscli-bundle/install -b ~/bin/aws
# 2- Get the public IP of the current CircleCI runner
PUBLIC_IP=$(curl ipinfo.io/ip)
# 3- Get AWS Region
# TODO Don't forget to replcae by your own Region
AWS_REGION=us-east-2
# 4- Get SG ID
# TODO Don't forget to replace by your own SG ID
SG_ID=sg-XXXXXXXX
# 5- Add an ingress rule to the security group
~/bin/aws ec2 authorize-security-group-ingress --region $AWS_REGION --group-id $SG_ID \
--protocol tcp --port 22 --cidr $PUBLIC_IP/24
# 6- Give the ingress rule some time to propogate
sleep 5
# 7- SSH to the server to deploy
ssh -o StrictHostKeyChecking=no ubuntu@EC2_publice_IP \
# other commands
# TODO Perform steps to deploy
# .
# .
# .
# 8- Remove the ingress rule
~/bin/aws ec2 revoke-security-group-ingress --region $AWS_REGION --group-id $SG_ID \
--protocol tcp --port 22 --cidr $PUBLIC_IP/24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment