This script automates backups of specified folders onto an external drive.
The idea is that you plug such a disk in only to perform a backup and plug it out,
perhaps storing it at a different location.
I used it with my eSATA (yes, yes, USB is unreliable, etc) disk as a measure against catastrophies -
to make sure my most important data stays safe in more than 2 locations.
One of the assumptions I've made is that these backups should be readable on Windows machines,
so the script does not use ZFS replication - instead it tar
s the data and encrypts the archives with openssl
.
Since I wanted it to be as automatic as possible, the program will do the following:
- detect whether your specific device has been plugged in and mount it,
- create compressed archives of the folders you specify and encrypt them (password is read from a file),
- create comprehensive lists of content of the folders you specify and encrypt them,
- mail you when it starts working and when it's done (or notify about any problems).
Limitations:
- there can be no whitespace characters in the paths you want to back up or list (but if the contents have whitespaces in their names it should be okay),
- the disk you connect is expected to only have a single partition (this can be hacked though),
- it has to run from the true root (i.e. not from a jail).
Requirements:
- no additional packages (tested on FreeNAS 11.1 but should work on any other as it only uses the basic tools).
This guide covers setting up and configuring it. I highly recommend reading the script first and doing a dry run. This has to be run from above the jails so I will assume that you know what you're doing. I'm running this weekly on my own NAS and everything is fine but of course no guarantees are given.
- First you need to know how to identify your disk.
The script works by scanning
dmesg
for the specific string containing the name or serial number of your disk. To obtain it, SSH into your jail, plug the disk in and check the output of the following:
dmesg | tail
You should see something similar to this:
ada6 at ahcich0 bus 0 scbus0 target 0 lun 0
ada6: <WDC WD10EARS-00MVWB0 51.0AB51> ATA8-ACS SATA 2.x device
ada6: Serial Number WD-xxxxxxxxxxxx
ada6: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
ada6: Command Queueing enabled
ada6: 953869MB (1953525168 512 byte sectors)
ada6: quirks=0x1<4K>
When you disconnect the drive, the output will contain something like that:
ada6 at ahcich0 bus 0 scbus0 target 0 lun 0
ada6: <WDC WD10EARS-00MVWB0 51.0AB51> s/n WD-xxxxxxxxxxxx detached
(ada6:ahcich0:0:0:0): Periph destroyed
I recommend using the disk serial number as the identifying string. Store it somewhere, it will be needed in step 3.
- Get the script.
I recommend creating some new folder for the script and its configs, its temporary files etc.
I created a folder
/opt/autobackup
andcd
's into it - so the rest of the guide assumes thatpwd
is that path.
Download the script:
wget https://raw.githubusercontent.com/Noiredd/freenas-scripts/master/autobackup/backup.sh
chmod +x backup.sh
# It turns out that somewhere between git and GitHub, '\n' endlines were changed to '\r\n'
# the following sed will get it back to unixy.
sed 's/\r\n/\n/g' <backup.sh >backup.sh
Important: if you decided on a different path, you have to edit the script accordingly:
variable BPATH
(line 10) tells the script where to look for configs, put any temps and so on.
- Configuration - main part.
The script expects a filemain.conf
in its directory. It shall contain 5 lines (and nothing else, no whitespace, no comments etc):- identifying string of the disk - mentioned in step 1,
- directory under which the disk will be mounted in
/mnt
- I leave it for the user to decide, to be sure there are no collisions (the path is relative to/mnt
, so a value ofbackup
will resolve to/mnt/backup
), - path to put the backups, relative to the above path - in case you had other stuff on your disk and wanted to put the backups in the specific folder on it; can be left empty if you want the archives directly on the disk root
(following the above example,
backups
will resolve to/mnt/backup/backups
), - mail address to notify about progress,
- path to a file containing the password to encrypt the backups (this line might have to be
\n
-terminated).
Example config file:
WD-xxxxxxxxxxxx
wd1tb
backup
[email protected]
/opt/autobackup/password.txt
Which will mount the drive WD-xxxxxxxxxxxx
under /mnt/wd1tb
and put backups in /mnt/wd1tb/backup
,
encrypting them with the password found in /opt/autobackup/password.txt
.
-
Configuration - paths.
In this step, you describe which folders you want backed up. As mentioned, there are two modes of operation here, and two configuration files that follow.
In the filepaths.conf
you are supposed to put paths of all the folders you want entirely archived, compressed and encrypted. Those will be fully backed up on your disk. The format of the file is as follows:
/path/to/folder/being/backed-up archive-name
This means all contents of/path/to/folder/being/backed-up
will be archived in the file (following the example mount point from before)/mnt/wd1tb/backup/yyyy-mm-dd-archive-name.tar.gz.aes
(substitute the current date foryyyy-mm-dd
). Note how there is exactly a single whitespace allowed in the path - it separates the path from archive name. As a consequence, locations with spaces in their paths cannot be backed up. This is however only a limitation of the config file parsing, so 1) it's not a problem if somewhere in/path/to/folder/being/backed-up
there exists afile with space.txt
, and 2) it might get eventually fixed (PRs welcome!).
Note: each path in the file has to be followed by\n
!
Second mode of operation is listing files. While a comprehensive backup is the only way to go with the most crucial files, for stuff like music collection I don't really need to store it all to the byte (the backup disk is only so big) - it's enough for me to know what was in there. This is exactly whatlists.conf
is for. Its format is like above, but the contents of each location will only be listed into a file. Each path will be scanned breadth-first, and printed files-first, alphabetically sorted, formatted likels -l
. The resulting txt file will be archived, compressed and encrypted as above, and stored as/mnt/wd1tb/backup/yyyy-mm-dd-archive-name.txt.tar.gz.aes
. Note the.txt
in the filename - this is to distinguish the file list archive from a complete backup, if you choose to do both for some location. -
With the three configuration files in place, all there's left to do is to set up a
cron
job to execute the script periodically. Log in to your FreeNAS GUI, navigate to Tasks, then Cron Jobs and add a job. I think you have to beroot
to mount any disks so enterroot
as the user, then path to the script under command (in the example case:/opt/autobackup/backup.sh
). I strongly recommend setting the schedule to every minute, every hour, every day - the whole point is to have this running all the time, so that when you decide to connect the disk it is noticed immediately. Execution of the script (if the disk hasn't been plugged in, so 99.9% of the time) ends right after callingdmesg
followed bydiff
andgrep
so its CPU impact should be unnoticeable.
With all this set up, the normal operation is very simple: the disk is not connected and nothing happens.
When you eventually decide to connect and power on your device, the following is done:
- new device is detected and an email is sent notifying that the script is working,
- the first partition of the disk is mounted (should this fail, an error message will be sent and the script will immediately exit),
- all locations from
paths.conf
aretar
ed (with-p
and-z
) and encrypted (usingopenssl
, AES-256-CBC with salt), - all locations from
lists.conf
are listed,tar
ed and encrypted (as above), - the disk is unmounted and its mount point removed (failure to unmount will be emailed),
- an email is sent notifying you that you can safely power off and disconnect your disk.
If your disk is powered on during the first run of the script, it will not detect it. So make sure the device is disconnected before you start the cron job.
PS: this script is not secured against power loss during backup. I recommend plugging your external HDD into the same UPS your box is in (if you have an HDD enclosure which needs power) in hope that a minor outage will not interrupt the operation. But in the case of a major outage (i.e. the server shuts down) the script will probably be interrupted halfway and you will have to clean up manually.
Since my main machine is running Windows, I made some choices in the design so that the (encrypted) backups can be read from Windows.
- Partitions
Your disk should be formatted as UFS - I know of no other file system that is both writable from FreeNAS (this eliminates NTFS) and readable from Windows (this eliminates ZFS). I've followed Randall Wood's guide to do that.
After plugging in your disk, figure out its device id (either by readingdmesg
or by manually checkingls /dev
). In my case it wasada6
.
# Start by removing the existing filesystem
dd if=/dev/zero of=/dev/ada6 bs=1m count=128
fdisk -BI /dev/ada6 # Format the external drive: one huge partition, bootable
# I recommend 'ls /dev' again to see the name assigned to the new partition - in my case it was ada6a
bsdlabel -wB /dev/ada6a # Write standard (bootable) disk label to the 1st partition
newfs -O2 -U /dev/ada6a # Format the partition with UFS2 and soft updates
I deliberately skipped the gpt
step as I don't think FreeNAS 11.1 ships it (at least it returned Command not found
for me),
replacing it with dd
as suggested by Dan in his guide.
By the way, do I have to remind that this is super dangerous if you're not careful?
- Mounting UFS on Windows
...is not possible. But you can use something likeufs2tools
to manually access the contents of a UFS partition. The steps are roughly as follows:
- navigate to Control Panel, then Computer Management (under Administrative Tools), then Disk Management,
- find the number of your disk and the index of the "slice" your data is in (starting at 1, not 0),
- use
bsdlabel
to figure out the index of the partition (a
translated to 0,b
to 1 etc. - yes this is 0-indexed for a change), - use
ufs2tool
to list (-l
) or extract (-g
) data from the disk.
This guide didn't exactly work for me, maybe because my disk only has a single partition.
Even though, according to their numbering scheme, my slice would be called 2/1
, when I did bsdlabel 2/1
I'd get a "could not open device" error.
Calling bsdlabel 2
did the trick.
Then you're supposed to do ufs2tool d/s/p -l
to list the data(d
for disk, s
for slice, p
for partition),
but when I did ufs2tool 2/1/0 -l
I'd again get the "could not open device".
Strangely enough, ufs2tool 2/0/ -l
worked - with this hanging slash.
To copy files from the UFS partition onto your internal HDD, you run ufs2tool d/s/p -g /path/to/the/archive.tar.gz.aes C:\save\this\here\archive.tar.gz.aes
, in my case ufs2tool 2/0/ -g ...
.
- Decrypt data on Windows
The script, unless you change something, encrypts the archive with OpenSSL's implementation of AES-256-CBC. In order to decrypt those files, I recommend getting an OpenSSL binary - for example from overbyte.eu. I've tried other AES-capable software but this probably has to do with OpenSSL's output file format. I also recommend checking which version does your FreeNAS box use (openssl version
) and getting the matching binary for Windows.
To decrypt an archive:
openssl aes-256-cbc -d -salt -in C:\path\to\archive.tar.gz.aes -out C:\path\to\unencrypted.tar.gz
it will prompt you for a password, or you could also pass something like-pass file:C:\my\password.txt
(untested).