rsync access can be restricted with a custom wrapper script or an official script named “rrsync”. Access to your remote filesystem should always be restricted so you won’t leak any information in case of a security breach.

I’ve used GitHub Actions a lot in the last few months. For web projects I am mostly using a custom script which gets triggered by SSH and does a git pull from the GitHub repository itself. Besides that it does Blue/Green deployment, automatically linking of VHosts and some other stuff.

Recently I had a much simpler requirement: I just wanted to copy the whole GitHub repository to my Uberspace. The GitHub Action definition for this is pretty simple:

name: Deploy to Uberspace

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      
    - name: Synchronize Git repository to uberspace
      uses: AEnterprise/rsync-deploy@v1.0
      env:
        DEPLOY_KEY: ${{ secrets.SSH_DEPLOYMENT_KEY }}
        ARGS: "-e -c -r --delete"
        SERVER_PORT: 22
        FOLDER: "./"
        SERVER_IP: ${{ secrets.SSH_HOST }}
        USERNAME: ${{ secrets.SSH_USERNAME }}
        SERVER_DESTINATION: .

You just have to add the parameters SSH_DEPLOYMENT_KEY, SSH_HOST and SSH_USERNAME in your GitHub project settings.

When running the GitHub Action, it copies the whole Git repository (including the .git directory) to the directory SERVER_DESTINATION on your host.

Restricting rsync access

As you might have already guessed it, there is a catch: I am a paranoid person and I am feeling slightly uncomfortable to grant GitHub access to my whole webspace. Unluckily, with Uberspace you get only one user account. So I am not able to use a specific user for restricted file or directory permissions.

There are two ways known to me how you can restrict the access – without having a dedicated rsync user.

Using a wrapper script for checking the rsync command

When using SSH with public/private keys, you can specify which command has to be executed when a given key authenticates. Inside your .ssh/authorized_keys file you have to prepend the command=”” option before your public key:

command="my-script.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1y...

As soon as the public key AAAAB3NzaC1y… authenticats, the my-script.sh is executed. Inside the given script you can access the initiated command by GitHub Actions through the environment variable $SSH_ORIGINAL_COMMAND. You can go with http://gergap.de/restrict-ssh-to-rsync.html to see how you can implement it this way.

Meet rrsync

Depending upon your needs, a custom wrapper script might be the way to go. But there is also a second way. The rsync developers had also developed a wrapper script written in Perl. It accepts one of two parameters -ro (read-only) or -wo (write-only) and a base directory for which the access is granted.

To set it up, do the following:

cd ~/bin
curl https://ftp.samba.org/pub/unpacked/rsync/support/rrsync -o rrsync
chmod +x rrsync

vim ~/.ssh/authorized_keys
# add the following line or prepend the options before your deployment key
# command="$HOME/bin/rrsync -wo ~/your_project_directory",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-rsa AAAAB3N...

The parameter -wo ~/your_project_directory restricts rsync to be only able to write to the directory ~/your_project_directory. In case of a security breach your GitHub Action’s SSH key would not be able to copy any files, like .env configuration files, from your host.

A minor advantage is that you don’t have to specify the target directory in your GitHub Action itself. You just have to use

SERVER_DESTINATION: .

in the YAML file. All files will be rsynced to ~/your_project_directory.

Post-deployment tasks

After having rsync’ed your deployment, you probably want also to do some post-depolyment tasks like executing a bash script.
When using rrsync you can not concat multiple commands in the authorized_keys file like

command="$HOME/bin/rrsync -wo ~/your_project_directory && my-post-deployment-script.sh" ...

This is because rsync can not determine that the synchronization process has been ended. This leads to an infinite execution of rsync.

To get a post-deployment script working, you can use nohup:

command="$HOME/bin/rrsync -wo ~/your_project_directory && nohup my-post-deployment-script.sh" ...

Summing it up

I showed you how you can rsync your GitHub project to a remote host and restrict access to the remote filesystem. In my case I went with the rrsync solution.

I am asking you for a donation.

You liked the content or this article has helped and reduced the amount of time you have struggled with this issue? Please donate a few bucks so I can keep going with solving challenges.

Categories: CI/CD