How to prevent Man-in-the-Middle (MITM) attacks by adding EC2 instance’s SSH Host Key to GitHub Workflow

Henry Wu
4 min readDec 30, 2023

--

To enhance the security of your GitHub Actions workflow, you can add the EC2 instance’s SSH host key to the known_hosts file. This process will help in verifying the identity of the EC2 instance when connecting via SSH, thereby preventing Man-in-the-Middle (MITM) attacks.

Step 1: Obtain Your EC2 SSH Host Key:

  • SSH into your EC2 instance.
  • Run ssh-keyscan -t rsa [your-ec2-ip-or-domain] to get the SSH host key.

ops, it appears that ssh-keyscan is not returning the full SSH key, but only the SSH server version. To correctly capture the SSH host key, you might need to run ssh-keyscan without specifying the key type, or try a different approach.

Copy the output, which is your EC2 instance’s SSH host key.

35.164.114.135 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAdqwffsdfas

Step 2: Add EC2 Host Key to GitHub Secrets:

In your GitHub repository, go to “Settings” -> “Secrets”. Click “New repository secret”. Name it EC2_HOST_KEY. Paste the SSH host key you copied into the value field.

STEP 3: Update Your GitHub Actions Workflow

Modify your .github/workflows/deploy.yml file to include the host key in the known_hosts file. Here's how you can do it:

name: Deploy to AWS EC2

on:
push:
branches:
- main # Set the branch to trigger the deployment


jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Deploy to AWS EC2
run: |
# Setting up SSH Key
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa

# Add EC2 host key to known_hosts
echo "${{ secrets.EC2_HOST_KEY }}" >> ~/.ssh/known_hosts

# Rsync to transfer the project to EC2
rsync -avz --exclude='.git/' --exclude='.DS_Store' --exclude='venv_winjob' --exclude='__pycache__' --exclude='.gitattributes' --exclude='.gitignore' --exclude='history templates' --exclude='history version' --exclude='.env' -e "ssh -i ~/.ssh/id_rsa" $GITHUB_WORKSPACE/ ec2-user@${{ secrets.EC2_HOST }}:/home/ec2-user/winjob/

# SSH into EC2 and set up the virtual environment and install dependencies
ssh -i ~/.ssh/id_rsa ec2-user@${{ secrets.EC2_HOST }} "
# Remove the existing virtual environment (if necessary)
# rm -rf /home/ec2-user/winjob/venv_winjob
# Create a new virtual environment
# python3 -m venv /home/ec2-user/winjob/venv_winjob
# Activate the virtual environment
source /home/ec2-user/winjob/venv_winjob/bin/activate
# Install the required packages
pip3 install -r /home/ec2-user/winjob/requirements.txt
# Additional commands to restart your application (if necessary)
sudo systemctl restart gunicorn
"

env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
EC2_HOST: ${{ secrets.EC2_HOST }}
EC2_HOST_KEY: ${{ secrets.EC2_HOST_KEY }}

ops, error:

After getting advises from ChatGPT and testing a lot of time, I found that we can use

ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts

to replce

echo "${{ secrets.EC2_HOST_KEY }}" >> ~/.ssh/known_hosts

in other words, no need to use EC2_HOST_KEY :(

The updated file is:

name: Deploy to AWS EC2

on:
push:
branches:
- main # Set the branch to trigger the deployment

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Deploy to AWS EC2
run: |
# Setting up SSH Key
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa

# Add EC2 host key to known_hosts
# echo "${{ secrets.EC2_HOST_KEY }}" >> ~/.ssh/known_hosts
# Dynamically add EC2 host key to known_hosts
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts

# Rsync to transfer the project to EC2
rsync -avz --exclude='.git/' --exclude='.DS_Store' --exclude='venv_winjob' --exclude='__pycache__' --exclude='.gitattributes' --exclude='.gitignore' --exclude='history templates' --exclude='history version' --exclude='.env' -e "ssh -i ~/.ssh/id_rsa" $GITHUB_WORKSPACE/ ec2-user@${{ secrets.EC2_HOST }}:/home/ec2-user/winjob/

# SSH into EC2 and set up the virtual environment and install dependencies
ssh -i ~/.ssh/id_rsa ec2-user@${{ secrets.EC2_HOST }} "
# Remove the existing virtual environment (if necessary)
# rm -rf /home/ec2-user/winjob/venv_winjob
# Create a new virtual environment
# python3 -m venv /home/ec2-user/winjob/venv_winjob
# Activate the virtual environment
source /home/ec2-user/winjob/venv_winjob/bin/activate
# Install the required packages
pip3 install -r /home/ec2-user/winjob/requirements.txt
# Additional commands to restart your application (if necessary)
sudo systemctl restart gunicorn
"

env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
EC2_HOST: ${{ secrets.EC2_HOST }}
EC2_HOST_KEY: ${{ secrets.EC2_HOST_KEY }}

It works! At least on my computer. So for now, every time I update my GitHub repo, the EC2 instance will update too.

--

--

Henry Wu
Henry Wu

Written by Henry Wu

Indie Developer/ Business Analyst/ Python/ AI/ Former Journalist/ Codewriter & Copywriter

No responses yet