Automatic Encrypted, Remote Backup Script

Status
Not open for further replies.

wvmark73

Cadet
Joined
Aug 3, 2017
Messages
4
After getting settled in to my FreeNAS, I set out to do backups via cron, and I wanted to encrypt them before copying them off to a remote location. I also put this users home dir on a dataset that has snapshots, so I can also use these to rollback to some degree using the snapshots.

This whole post is somewhat of a work in progress. Please feel free to suggest any edits or clarifications I need to make.

First, we need to generate a public and private keypair with OpenSSL. It will save it as a file, in this case "OpenNAS_Backup_Private_key.pem"

THIS IS THE MOST IMPORTANT FILE. DO NOT LOSE IT, DO NOT SHARE IT.

Create a private key:
Code:
Backup_User@freenas:~/test % openssl genrsa -out OpenNAS_Backup_Private_key.pem 2048
Generating RSA private key, 2048 bit long modulus
....................................+++
...........+++
e is 65537 (0x10001)
Backup_User@freenas:~/test %


Now that we have a private key, we can use it to create a public key. It will be saved as a file called "OpenNAS_Backup_Public_key.pem.

This file is important, but not secret. We could always generate a new one with our private key.

Code:

Backup_User@freenas:~/test % openssl rsa -in OpenNAS_Backup_Private_key.pem -out OpenNAS_Backup_Public_key.pem -outform PEM -pubout
writing RSA key
Backup_User@freenas:~/test %


Now that we have our keys, we can use them in our backup script. You will need to adjust the variable "KEYFILE" to the path and name of your PUBLIC key.

Code:
#!/bin/bash
############################################
# FreeNAS Config Backup and Encrypt		#
############################################
#Set variables							 #
############################################
#The path to where your config is. Should be /data
CONFIGPATH="/data"
#The name of the config. Should be freenas-v1.db
CONFIGFILE="freenas-v1.db"
#The path where you want the backups to be kept.
BUPATH="/mnt/NASVOL1/FreeNAS_Backups/Backup_User/backups"
#The full path to your openssl PUBLIC key.
KEYFILE="/mnt/NASVOL1/FreeNAS_Backups/Backup_User/FreeNAS_Backup_public_key.pem"
#Remote SSH user. Needs to be setup for SSH with key authentication.
REMOTEUSER="nasbackup"
#Remote SSH host where backups will be copied to.
REMOTEHOST="remote-backup.sneakernet.local"
#How many days of backups to keep.
DAYSTOKEEP="30"
#############################################
#End Variables							  #
#############################################
#Get the time and date we are running.
WHEN=`date +%a-%Y-%m-%d-%H:%M`

#Check for a file at the destination so we don't clobber it.
#If found, rename it based on the date.
if [ -f $BUPATH/$CONFIG ];
		then
				echo "Orphan config found!"
				mv $BUPATH/$CONFIGFILE $BUPATH/$CONFIGFILE.$WHEN.noclobber
fi

#Copy the current config into our home to work with it instead of the real file.
sudo /bin/cp $CONFIGPATH/$CONFIGFILE $BUPATH/$CONFIGFILE

#Generate a big honkin random password to encrypt the file with.
openssl rand -base64 15 > $BUPATH/$WHEN.pass

#Encrypt the config using the password we just generated.
#If successful, delete the unencypted config.
openssl enc -aes-256-cbc -a -salt -in $BUPATH/$CONFIGFILE -out $BUPATH/$CONFIGFILE.$WHEN.enc -pass file:$BUPATH/$WHEN.pass
if [ $? = 0 ];
then
rm -f $BUPATH/$CONFIGFILE
gzip $BUPATH/$CONFIGFILE.$WHEN.enc
else
echo "Failed to encrypt config"
fi

#Encrypt the password using our public key. You do have your private key stored safely, don't you?
#If successful, delete the unencypted password.
openssl rsautl -encrypt -inkey $KEYFILE -pubin -in $BUPATH/$WHEN.pass -out $BUPATH/$WHEN.pass.enc
if [ $? = 0 ];
then
rm -f $BUPATH/$WHEN.pass
gzip $BUPATH/$WHEN.pass.enc
else
echo "Failure to encrypt password"
fi

#Sync to our remote server, using the delete option to clean up.
rsync -avh $BUPATH/ $REMOTEUSER@$REMOTEHOST:~/backups/ --delete

#Finally, do some cleanup. Delete backups over 30 days old. Next time rsync runs, it will clean up the remote backups.
find $BUPATH/*.gz -mtime +$DAYSTOKEEP -exec rm {} \;



Recovery:

If you need to restore from a backup, it takes 3 steps.
1) Retrieve the backup files from the remote server. Both the config and password file.
2) Decrypt the password file using your PRIVATE key.
3) Use the password in the password file to decrypt the config.

I wont cover #1. It will vary with your environment and situation. We will assume you have both files and your private key.

Decrypt the password file:
You will need to substitute your PRIVATE key file name and backup name as well as the file to output.
Code:
Backup_User@freenas:~/test % openssl rsautl -decrypt -inkey ../FreeNAS_Backup_private_key.pem -in Thu-2017-08-03-15:00.pass.enc -out Thu-2017-08-03-15:00.pass
Backup_User@freenas:~/test % 


Now that it is decrypted, we can read the password:
Code:
Backup_User@freenas:~/test % cat Thu-2017-08-03-15:00.pass
EDTClPLC6zgqzcTr81Lw
Backup_User@freenas:~/test %


We can now use our password "EDTClPLC6zgqzcTr81Lw" to decrypt the config. Again, you will have to substitute your own filenames/paths.

Code:
Backup_User@freenas:~/test % openssl enc -d -aes-256-cbc -a -in freenas-v1.db.Thu-2017-08-03-15:00.enc -out freenas-v1.db
enter aes-256-cbc decryption password:
Backup_User@freenas:~/test %


We now have a decrypted config to restore!
 
Last edited:

m0nkey_

MVP
Joined
Oct 27, 2015
Messages
2,739
Cool. :cool:

Couple of questions:
  • How would somebody decrypt the backup? We know how to encrypt, but not how to reverse it using this script :)
  • Have you considered using environment variables and not hard coding paths? Hard coding is sloppy and it makes it difficult to transport the script to another machine. For example, you could do this:
Code:
export BACKUP_PATH="/mnt/NASVOL1/FreeNAS_Backups/Backup_User"
mv ${BACKUP_PATH}/freenas-v1.db ${BACKUP_PATH}/freenas-v1.db.$WHEN.noclobber
 

wvmark73

Cadet
Joined
Aug 3, 2017
Messages
4
Cool. :cool:

Couple of questions:
  • How would somebody decrypt the backup? We know how to encrypt, but not how to reverse it using this script :)
  • Have you considered using environment variables and not hard coding paths? Hard coding is sloppy and it makes it difficult to transport the script to another machine. For example, you could do this:
Code:
export BACKUP_PATH="/mnt/NASVOL1/FreeNAS_Backups/Backup_User"
mv ${BACKUP_PATH}/freenas-v1.db ${BACKUP_PATH}/freenas-v1.db.$WHEN.noclobber
You can't reverse it with the script, the script is one way. You would need to fetch the encrypted backup and password file. Decrypt the password file and then use that password to decrypt the config, then restore the config. It's not elegant or easy, but works. I could add some documentation on that process.

I started off using variables and may work towards them in another version. Most of this wouldn't be environment variables, but I could set variables at the beginning and use them so it was more easily ported. It's certainly a better way to go.
 

wvmark73

Cadet
Joined
Aug 3, 2017
Messages
4
Now with more variables!
Code:
#!/bin/bash
############################################
# FreeNAS Config Backup and Encrypt		#
############################################
#Set variables							 #
############################################
#The path to where your config is. Should be /data
CONFIGPATH="/data"
#The name of the config. Should be freenas-v1.db
CONFIGFILE="freenas-v1.db"
#The path where you want the backups to be kept.
BUPATH="/mnt/NASVOL1/FreeNAS_Backups/Backup_User/backups"
#The full path to your openssl PUBLIC key.
KEYFILE="/mnt/NASVOL1/FreeNAS_Backups/Backup_User/FreeNAS_Backup_public_key.pem"
#Remote SSH user. Needs to be setup for SSH with key authentication.
REMOTEUSER="nasbackup"
#Remote SSH host where backups will be copied to.
REMOTEHOST="remote-backup.sneakernet.local"
#How many days of backups to keep.
DAYSTOKEEP="30"
#############################################
#End Variables							  #
#############################################
#Get the time and date we are running.
WHEN=`date +%a-%Y-%m-%d-%H:%M`

#Check for a file at the destination so we don't clobber it.
#If found, rename it based on the date.
if [ -f $BUPATH/$CONFIG ];
		then
				echo "Orphan config found!"
				mv $BUPATH/$CONFIGFILE $BUPATH/$CONFIGFILE.$WHEN.noclobber
fi

#Copy the current config into our home to work with it instead of the real file.
sudo /bin/cp $CONFIGPATH/$CONFIGFILE $BUPATH/$CONFIGFILE

#Generate a big honkin random password to encrypt the file with.
openssl rand -base64 15 > $BUPATH/$WHEN.pass

#Encrypt the config using the password we just generated.
#If successful, delete the unencypted config.
openssl enc -aes-256-cbc -a -salt -in $BUPATH/$CONFIGFILE -out $BUPATH/$CONFIGFILE.$WHEN.enc -pass file:$BUPATH/$WHEN.pass
if [ $? = 0 ];
then
rm -f $BUPATH/$CONFIGFILE
gzip $BUPATH/$CONFIGFILE.$WHEN.enc
else
echo "Failed to encrypt config"
fi

#Encrypt the password using our public key. You do have your private key stored safely, don't you?
#If successful, delete the unencypted password.
openssl rsautl -encrypt -inkey $KEYFILE -pubin -in $BUPATH/$WHEN.pass -out $BUPATH/$WHEN.pass.enc
if [ $? = 0 ];
then
rm -f $BUPATH/$WHEN.pass
gzip $BUPATH/$WHEN.pass.enc
else
echo "Failure to encrypt password"
fi

#Sync to our remote server, using the delete option to clean up.
rsync -avh $BUPATH/ $REMOTEUSER@$REMOTEHOST:~/backups/ --delete

#Finally, do some cleanup. Delete backups over 30 days old. Next time rsync runs, it will clean up the remote backups.
find $BUPATH/*.gz -mtime +$DAYSTOKEEP -exec rm {} \;
 
Status
Not open for further replies.
Top