Skip to content

Instantly share code, notes, and snippets.

Last active January 6, 2016 13:53
Show Gist options
  • Save jmiserez/f7771d0c82455128839d to your computer and use it in GitHub Desktop.
Save jmiserez/f7771d0c82455128839d to your computer and use it in GitHub Desktop.
Offline backup (cold, with shutdown/startup) of a XenServer VM with gzip, rate-limiting (pv), and public key + AES256 encryption (openssl). See ( for decryption. Note: You *must* trust both the remote and local server, as well as the SSH connection.
# Author: Jeremie Miserez <>
# Latest version:
# This script will:
# 1. Connect to a remote XenServer server over SSH
# 2. Shutdown the specified VM
# 3. Generate a symmetric session key from the public key specified.
# The session key is used later for the AES256 encryption of the large VM image. Using public key encryption directly
# does *not* work reliably for large files.
# 4. Encrypt the session key using the public key and write to disk.
# 5. vm-export the VM, gzip it, pipe it to the local machine, rate limit from the local machine using pv,
# encrypt the stream using the session key and write to disk.
# The script ( decrypts such images
# when given the encrypted session key and the private key.
# This script does *not* use snapshots yet, so backups of VMs can not be made while they are running. VMs will
# be shut down before the backup and started afterwards.
# Note the security implications of encrypting on the local machine. The advantage is that no software besides SSH and
# gzip needs to be installed on the XenServer, however a disadvantage is that the local machine may temporarily write
# unencrypted (but gzipped) parts of the stream to disk. You may need to move the encryption parts to the XenServer
# if this is an issue for you.
# Again: You *must* trust both the remote and local server, and trust that the SSH connection is secure! This only
# ensures encryption at rest.
set -e # exit on errors
# Some standard stuff. Works on Linux, not tested with Mac OS X.
SCRIPT=$(readlink -f $0)
SCRIPTNAME=$(basename "$SCRIPT")
if [ "$#" -lt 6 ]
echo "Usage: ./$SCRIPTNAME <user@host> <ssh-identity-file> <public-key-file> <backup-prefix> <vm uuid> <rate-limit>"
echo " e.g. ./$SCRIPTNAME id_rsa mykey.pem vm3-example 7ec86fdf-9e63-dc23-c4e4-46bdfa374e9b 512k"
exit 1
MYBKUPNAME="$4-`date +%Y-%m-%d-%H%M%S`.xva.gz"
echo "Testing connection: ssh -i \"$MYSSHIDENTITY\" \"$MYUSERHOST\" \"xe vm-list\""
ssh -i "$MYSSHIDENTITY" "$MYUSERHOST" "xe vm-list"
echo "Generating symmetric key."
MYSYMMETRICKEY=`openssl rand -base64 32`
echo "Writing encrypted symmetric session key."
openssl rsautl -encrypt -inkey "$MYPUBLICKEY" -pubin -in <( echo -n "$MYSYMMETRICKEY" ) -out "$MYBKUPNAME.key.enc"
echo "Shutting down $MYVMID."
ssh -i "$MYSSHIDENTITY" "$MYUSERHOST" "xe vm-list | grep -A2 $MYVMID; xe vm-shutdown vm=$MYVMID force=false; xe vm-list | grep -A2 $MYVMID"
echo "Starting encrypted backup of $MYVMID"
ssh -i "$MYSSHIDENTITY" "$MYUSERHOST" "ionice -c3 xe vm-export vm=$MYVMID filename= | gzip --rsyncable --fast -" | pv --rate-limit "$MYRATELIMIT" | openssl enc -aes-256-cbc -salt -out "$MYBKUPNAME.enc" -pass file:<( echo -n "$MYSYMMETRICKEY" )
echo "Transfer done, clearing symmetric session key from memory."
echo "Starting $MYVMID."
ssh -i "$MYSSHIDENTITY" "$MYUSERHOST" "xe vm-start force=true vm=$MYVMID; xe vm-list | grep -A2 $MYVMID"
echo "Creating md5sums"
md5sum "$MYBKUPNAME.key.enc" > "$MYBKUPNAME.key.enc.md5"
md5sum "$MYBKUPNAME.enc" > "$MYBKUPNAME.enc.md5"
echo "Backup $MYBKUPNAME of $MYVMID done."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment