Secure GitHub Actions SSH Access With Firewall Restrictions
Are you trying to set up automatic CI/CD deployment of your frontend using GitHub Actions but running into firewall issues when trying to SSH into your virtual machine? You're not alone! Many developers face this challenge when trying to secure their VMs while still enabling automated deployments. In this comprehensive guide, we'll walk you through the steps to grant GitHub Actions SSH access to your VM, even with a restricted firewall on port 22. We will use a Hetzner Cloud VM as an example, but the principles apply to any VM or server.
Understanding the Challenge
So, GitHub Actions SSH access can be tricky, especially when you're serious about security. You've got your firewall locked down, which is great, but now your automated deployments are hitting a wall. The core issue is that SSH, by default, uses port 22, and firewalls often block this port to prevent unauthorized access. But don't worry, guys, we've got a solution! The goal here is to enable your GitHub Actions workflow to securely connect to your VM and deploy your code without compromising your server's security. This involves a few key steps: generating SSH keys, securely storing them, configuring your VM's firewall, and setting up your GitHub Actions workflow.
Why Restricting Port 22 is Important
Before we dive into the solution, let's quickly touch on why restricting port 22 is a best practice. Port 22 is the default port for SSH, making it a common target for attackers. By blocking or restricting access to this port, you significantly reduce the risk of brute-force attacks and other unauthorized access attempts. However, this also means you need a secure way to allow legitimate SSH connections, such as those from your GitHub Actions workflow. This is where key-based authentication and firewall rules come into play, allowing you to control exactly who can access your server and how.
Overview of the Solution
We're going to walk through a method that uses SSH keys for authentication and carefully configured firewall rules to allow access only from GitHub Actions. This approach ensures that your VM remains secure while still enabling automated deployments. Here's a quick rundown of the steps we'll cover:
- Generate an SSH Key Pair: We'll create a new SSH key pair specifically for GitHub Actions to use.
- Add the Public Key to Your VM: We'll add the public key to the
~/.ssh/authorized_keys
file on your VM, allowing the corresponding private key to authenticate. - Securely Store the Private Key: We'll store the private key as a GitHub Actions secret, ensuring it's protected.
- Configure Your VM's Firewall: We'll set up firewall rules to allow SSH access only from GitHub Actions IP ranges.
- Set Up Your GitHub Actions Workflow: We'll create a workflow that uses the private key to SSH into your VM and deploy your code.
Step 1: Generate an SSH Key Pair
First things first, we need to generate an SSH key pair. This key pair will consist of a private key, which we'll keep secret and use in our GitHub Actions workflow, and a public key, which we'll add to our VM. This SSH key pair allows secure authentication without the need for passwords, which is crucial for automation. To generate the key pair, you can use the ssh-keygen
command in your terminal. Make sure you generate a strong key, preferably using the Ed25519 algorithm for enhanced security. This is where you'll create the keys that unlock the door for GitHub Actions to your VM, so let's do it right.
ssh-keygen -t ed25519 -N "" -f ~/.ssh/github-actions-deploy-key
-t ed25519
: Specifies the Ed25519 algorithm for stronger security.-N ""
: Sets an empty passphrase (we want passwordless authentication for automation).-f ~/.ssh/github-actions-deploy-key
: Specifies the file name for the key pair. You can choose a different name if you like.
This command will generate two files in your ~/.ssh/
directory: github-actions-deploy-key
(the private key) and github-actions-deploy-key.pub
(the public key). Itβs absolutely crucial to protect the private key. If someone gains access to it, they can access your VM. The public key, on the other hand, is safe to share.
Step 2: Add the Public Key to Your VM
Now that we have our key pair, we need to add the public key to the ~/.ssh/authorized_keys
file on your VM. This file tells your VM which public keys are authorized to connect using SSH. Think of it as a list of approved keys that can unlock your server. This step essentially grants your GitHub Actions workflow permission to access your VM. You can do this by copying the contents of the github-actions-deploy-key.pub
file to the ~/.ssh/authorized_keys
file on your VM. There are a few ways to accomplish this, but one of the easiest is to use the ssh-copy-id
command.
If you have ssh-copy-id
available (it's often included in SSH client packages), you can use it like this:
ssh-copy-id -i ~/.ssh/github-actions-deploy-key.pub <your_user>@<your_vm_ip>
Replace <your_user>
with the username you use to SSH into your VM and <your_vm_ip>
with the IP address of your VM. If you don't have ssh-copy-id
, you can manually copy the public key using ssh
and cat
:
ssh <your_user>@<your_vm_ip> "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" < ~/.ssh/github-actions-deploy-key.pub
This command connects to your VM via SSH and appends the contents of your public key file to the ~/.ssh/authorized_keys
file. Make sure to replace <your_user>
and <your_vm_ip>
with your actual username and IP address. After running this command, you should be able to SSH into your VM using the private key without being prompted for a password. This is a big step forward, but we're not done yet β security is a layered approach!
Step 3: Securely Store the Private Key
Okay, we've got our SSH key pair and added the public key to our VM. Now comes the crucial part of securely storing the private key. Remember, this key is the key to the kingdom, so we need to treat it with the utmost care. We're going to store it as a GitHub Actions secret. GitHub Actions secrets are encrypted and securely stored, making them a safe place to keep sensitive information like our private key. Never, ever commit your private key directly to your repository! That's a recipe for disaster.
To store the private key as a secret, go to your GitHub repository, then navigate to "Settings" > "Secrets" > "Actions". Click on "New repository secret" and create a new secret. Give it a meaningful name, like SSH_PRIVATE_KEY
, and paste the contents of your ~/.ssh/github-actions-deploy-key
file into the "Value" field. Make sure you copy the entire content of the file, including the -----BEGIN OPENSSH PRIVATE KEY-----
and -----END OPENSSH PRIVATE KEY-----
lines. Once you've pasted the content, click "Add secret". Now your private key is securely stored and can be used in your GitHub Actions workflows.
Step 4: Configure Your VM's Firewall
Now, let's talk about the VM's firewall configuration. This is where we'll tighten security while still allowing our GitHub Actions workflow to connect. We want to restrict SSH access to only the IP ranges used by GitHub Actions. This means that only connections originating from GitHub Actions will be allowed to SSH into your VM. This significantly reduces the attack surface of your server. GitHub publishes a list of IP ranges that their Actions runners use. We'll use these IP ranges to configure our firewall rules.
You can find the current list of GitHub Actions IP ranges in their API documentation or meta API. You can fetch the list using curl
:
curl -s https://api.github.com/meta | jq .actions
This command fetches the metadata from GitHub's API and uses jq
(a JSON processor) to extract the actions
IP ranges. You'll get a list of IP addresses and CIDR blocks. Now, you need to configure your VM's firewall to allow SSH access from these IP ranges. The exact commands will vary depending on the firewall software you're using (e.g., iptables
, ufw
, or your cloud provider's firewall). Here's an example using ufw
(Uncomplicated Firewall), a common firewall on Ubuntu systems:
sudo ufw allow from <IP_RANGE_1> to any port 22 proto tcp
sudo ufw allow from <IP_RANGE_2> to any port 22 proto tcp
# ... add rules for all GitHub Actions IP ranges
sudo ufw enable
Replace <IP_RANGE_1>
, <IP_RANGE_2>
, etc., with the IP ranges you obtained from the GitHub API. Remember to add rules for all the IP ranges. After adding the rules, enable the firewall if it's not already enabled. This step is crucial for ensuring that only authorized connections can reach your VM.
Step 5: Set Up Your GitHub Actions Workflow
Alright, we've done the groundwork. Now for the grand finale: setting up your GitHub Actions workflow. This is where we'll put everything together and automate the deployment process. We'll create a workflow file that uses the private key we stored as a secret to SSH into our VM and deploy our code. This workflow will be triggered by events like pushes to your repository, ensuring that your deployments are automated and seamless.
Create a new file in your repository at .github/workflows/deploy.yml
(or whatever name you prefer). This file will define your workflow. Here's a basic example of a workflow that checks out your code, connects to your VM via SSH, and executes a deployment script:
name: Deploy to VM
on:
push:
branches:
- main # or your main branch
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: SSH Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VM_IP }}
username: ${{ secrets.VM_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 22
script:
# Your deployment script here
- echo "Deploying..."
- cd /var/www/your-app # Replace with your deployment directory
- git pull origin main
- # Add your deployment commands here (e.g., npm install, npm build, etc.)
- echo "Deployment complete!"
Let's break down this workflow:
name
: The name of the workflow.on
: Specifies the trigger for the workflow (in this case, pushes to themain
branch).jobs
: Defines the jobs to run.deploy
: The name of the deployment job.runs-on
: Specifies the runner environment (in this case, the latest Ubuntu runner).steps
: Defines the steps to execute.Checkout code
: Checks out your code from the repository.SSH Deploy
: Uses theappleboy/ssh-action
action to connect to your VM via SSH and execute a script.host
: The IP address of your VM (stored as a secret namedVM_IP
).username
: The username to use for SSH (stored as a secret namedVM_USER
).key
: The private key we stored earlier (stored as a secret namedSSH_PRIVATE_KEY
).port
: The SSH port (22 in this case).script
: The script to execute on your VM. This is where you'll put your deployment commands.
Make sure to replace {{ secrets.VM_IP }}
and {{ secrets.VM_USER }}
with the names of the secrets you create for your VM's IP address and username, respectively. Also, replace the example deployment commands in the script
section with your actual deployment commands. This is where the magic happens β your workflow will automatically connect to your VM and deploy your code whenever you push changes to your main branch! Make sure your deployment script handles everything from pulling the latest code to installing dependencies and restarting your application.
Conclusion
And there you have it! You've successfully set up GitHub Actions SSH access to your VM with a restricted firewall on port 22. By using SSH keys, securely storing your private key, and configuring your firewall to allow access only from GitHub Actions IP ranges, you've created a secure and automated deployment pipeline. This approach not only enhances your security posture but also streamlines your development workflow, allowing you to focus on building awesome things. Remember, security is an ongoing process, so be sure to regularly review and update your configurations as needed. Happy deploying, guys!