- Create SSH keys (without passphrase):
cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "[email protected]"
- Add authorized keys:
cd ~/.ssh
cat id_rsa.pub >> authorized_keys
-
Ensure public key authentication is enabled (use your editor of choice, example with
vim
):sudo vim /etc/ssh/sshd_config
- using
/
, search for:RSAAuthentication
and make sure it is set toyes
PubkeyAuthentication
and make sure it is set toyes
UsePAM
and make sure it is set toyes
PreferredAuthentications
and make surepublickey
is the first in the list
- if the file is changed, restart the
sshd
service (service sshd restart
)
-
Ensure permissions are correctly set:
chmod 750 ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Secret variable name | Description | Example |
---|---|---|
DEPLOY_SSH_PRIVATE_KEY |
The private key generated on the server. Typically, the content of the id_rsa file. |
-----BEGIN RSA PRIVATE KEY----- |
DEPLOY_SSH_HOST |
The host name of the destination server. | sub.domain.tld , 888.888.888.888 |
DEPLOY_SSH_USER |
The user with which to identify when using rsync ; has the public key content in its ~/.ssh/authorized_keys file. |
www-data |
DEPLOY_PATH_FILTERS |
The filters given to rsync to determine what to deploy. |
+ included.html |
DEPLOY_PATH |
The destination folder on the destination server. | /var/www/public_html |
name: rsync-deploy
on:
push:
branches: master
jobs:
build:
runs-on: ubuntu-latest
env:
REMOTE_PATH: ${{ secrets.DEPLOY_PATH }}
SSH_KEY: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
SSH_FILE: id_rsa
SSH_USER: ${{ secrets.DEPLOY_SSH_USER }}
SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
SSH_FILE: id_rsa
# Sample directory tree:
# .
# ├── included
# │ ├── excluded
# │ │ └── excluded.html
# │ └── included.html
# ├── included_all
# │ ├── included
# │ │ └── included.html
# │ └── included.html
# ├── excluded.html
# └── included.html
#
# Sample RSYNC_FILTERS_CONTENT whitelist content:
# + included.html
# + /included/
# + /included/*
# + /included_all/***
# - /**
#
# Above filters combined with "--prune-empty-dirs" results in the following being transferred:
# included.html
# included/
# included/included.html
# included_all/
# included_all/included.html
# included_all/included/
# included_all/included/included.html
RSYNC_FILTERS_CONTENT: ${{ secrets.DEPLOY_PATH_FILTERS }}
RSYNC_FILTERS_FILE: rsync_filters.txt
steps:
# checkout the repo
- uses: actions/checkout@v3
- name: Configure SSH
# create ~/.ssh/ directory if not exists
# adjust permissions of ~/.ssh/ directory
# create the specified private key file from secrets
# adjust permissions of private key file
# get the remote host in a variable
# get the content to add to ~/.ssh/known_hosts
# setup ~/.ssh/known_hosts
run: |
mkdir -p ~/.ssh/
chmod 700 ~/.ssh
echo "$SSH_KEY" > ~/.ssh/$SSH_FILE
chmod 600 ~/.ssh/$SSH_FILE
host=SSH_HOST
hosts="$(dig +short "$host" | grep -v '\.$' | sed -z 's|\n|,|g')$host"
ssh-keyscan -H "$SSH_HOST" > ~/.ssh/known_hosts
- name: Setup rsync filters
run: |
touch $RSYNC_FILTERS_FILE
echo "$RSYNC_FILTERS_CONTENT" > $RSYNC_FILTERS_FILE
- name: Deploy with rsync
# Details:
# - checksum-based comparison, archive mode, compressed, quiet
# - connect with ssh using specified identification key file, suppressing logs that are not errors
# - specify that files not included in filtered source will be deleted at destination (other files are preserved)
# - apply filters as defined in rsync_filters.txt
# - specify empty directories will be removed from filtered source (other empty directories are preserved)
# - transfer the specified source path
# - transfer to specified remote destination path
# Remarks:
# - remove `q` and/or add `v` or `vv` in `-cazq` to show more info about file transfers
# - it is highly recommended to add a `--dry-run \` line before the specified source path `./ \` to test the filters ; remove afterwards
run: |
rsync -cazq \
-e "ssh -i ~/.ssh/$SSH_FILE -o LogLevel=ERROR" \
--delete \
--filter="merge $RSYNC_FILTERS_FILE" \
--prune-empty-dirs \
./ \
$SSH_USER@$SSH_HOST:$REMOTE_PATH