Windows Remote Backup using OpenVPN and Robocopy

2016-01-04T20:30:00Z

It's not unusual for friends and relatives to ask for my help and/or advice when it comes to their own laptops, computers and Internet connections. This festive/new year period has been no exception. I took on the responsibility of setting up a laptop for a relative to use in their own self-employed business. My intention was to provide them with a good mix of portability and usability for a reasonable outlay. Hence a new laptop, docking station, laptop riser, external monitor, keyboard and mouse were purchased. The laptop received a clean install of Windows 10, followed by a good anti-virus product and productivity suite (Microsoft Office). Once the laptop user's files and folders were transferred across to the new laptop, the final step was creating a workable backup solution.

With no servers locally to use as a target for backups, I decided the best course of action was a remote backup to my Linux/ZFS file server at home. A massive boon in doing this is the support ZFS offers for snapshotting and transferring data. Already my ZFS file systems are snapshotted daily and each snapshot is sent to an identical backup server. That means obtaining a thirty day backup history without any extra work.

My home server of course, lives on a separate discrete network. Fortunately, I'm using pfSense as a firewall with an OpenVPN endpoint. I created an OpenVPN user account and certificate for the laptop user, and then using the pfSense Client Export utility downloaded an executable package to run on the Windows laptop. This installed the OpenVPN utilities and configuration required for the laptop to establish a VPN connection to my own home network.

I'd like to cover this in more detail, but I suspect it's better included as an article in it's own right, authored after my next pfSense install. Using your preferred search engine, you are sure to elicit plenty of other help and advice on using pfSense and creating an OpenVPN (or IPSec) endpoint.

The only other additional prerquisites were a samba share on my home server for storing the backups and a new samba user. The share was configured by adding the following configuration to the /etc/samba/smb.conf file on my Linux/ZFS home server:

[ruby]
    path = /zfs/biz/it/backups/ruby
    read only = no
    create mode = 0774
    directory mask = 0775
    valid users = kaye
    force user = kaye
    force group = kaye
    browseable = no
    guest ok = no
    hosts deny = 10.10.0.0/16

"ruby" happens to the hostname of the laptop that's getting backed up, hence the name of the share. My next step was creating a linux/samba account for the backup user. First create the linux account:

sudo useradd -r -u 1002 -g 1002 -s /bin/false <username>

What I've done here is use the "-r" flag to create a system/daemon account. This does a number of things:

Adding the "-s" flag, I've specified a shell of /bin/false. That means this user will never be able to log into an interactive shell.

The next step is adding the new user to the samba "SAM" database with pdbedit:

 pdbedit -a <username>

Before restarting smbd (server message block daemon) to instantiate these changes, I needed to create the underlying folder for the new share:

mkdir /zfs/biz/it/backups/ruby

Then give our new user ownership and read/write/execute permissions on that folder:

chown /zfs/biz/it/backups/ruby <newuser>:<newuser>
chmod 770 /zfs/biz/it/backups/ruby

I also had to give all users read only access to the parent folder in order for the new samba share to work. I was a little reluctant to do this, as previously only the root user has access to the parent folder. But given how tight my samba and NFS/Kerberos configurations are, it's more than secure enough for the kind of environment in which the server is running.

chmod o+r /zfs/biz/it/backups

I could of course have moved the backups folder somewhere a little less sensitive, but then the bigger danger would be my forgetting what the folder is and inadvertently removing it about six months time. Restarting smbd concludes the setup of the remote backup share.

At this stage I was able to connect the laptop to my home network's pfSense OpenVPN endpoint and then browse the new share by typing the server's ip address, followed by the share name into Windows Explorer, e.g. \10.10.1.10\ruby. Of course, I had to provide the credentials for the new samba account created earlier and optionally let Windows store them.

With all the groundwork in place, it was time to script something up. I need the backup process to start the OpenVPN connection and then, having made contact with the remote backup server, mirror the laptop end user's Desktop and Documents folders. Fortunately the installation included with the pfSense client export also installs an OpenVPNService, which automatically picks up the configuration for my pfSense endpoint. I chose to write a simple Windows Command Shell script to do the heavy lifting:

@ECHO OFF
ECHO Starting patent-pending ninja backup process of <username>'s Documents folder

ECHO Attempting to start the VPN Connection
REM Start VPN Connection

sc start OpenVpnService >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR10

REM Wait 30 Seconds for VPN connection to start
ECHO Allowing 30 seconds for the VPN connection to start...
timeout 30 >nul

ECHO Contacting backup server...
ping -n 1 10.10.1.10 >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR20

:BACKUP
ECHO Connecting to the remote backup share...
net use \\10.10.1.10\ruby <password> /user:<backupServerHostname>\<username> >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR50

ECHO Starting the backup...
robocopy c:\users\<username>\Documents \\10.10.1.10\ruby\Documents * /e /mir /r:200 /w:30 /ipg:500 /log+:c:\users\<username>\AppData\Local\Temp\DocumentsVPNRobocopyBackupDJC.log >nul
IF %ERRORLEVEL% GTR 3 GOTO ERROR60
robocopy c:\users\<username>\Desktop \\10.10.1.10\ruby\Desktop * /e /mir /r:200 /w:30 /ipg:500 /log+:c:\users\<username>\AppData\Local\Temp\DesktopVPNRobocopyBackupDJC.log >nul
IF %ERRORLEVEL% GTR 3 GOTO ERROR70
ECHO Your documents folder has been archived

:DISCONNECT
ECHO Stopping the VPN Connection...
sc stop OpenVpnService >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR100
ECHO The VPN Connection has been stopped
:END
ECHO !! Backup process complete. Please review the above output for any problems !!  
ECHO This message will self-destruct in 5 seconds (o:} ..
timeout 5 /NOBREAK >nul
EXIT 0

:ERROR10
ECHO Unfortunately I have not been able to establish a VPN connection. Please contact Technical Support! )o:=
pause
exit /b %errorlevel%

:ERROR20
ECHO Failed to contact backup server. Waiting 30 seconds and trying again...
ping -n 1 10.10.1.10 >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR30
GOTO BACKUP

:ERROR30
ECHO Failed to contact backup server. Waiting 30 seconds and trying one last time...
ping -n 1 10.10.1.10 >nul
IF %ERRORLEVEL% NEQ 0 GOTO ERROR40
GOTO BACKUP

:ERROR40
ECHO Unfortunately I have not been able to contact the backup server. Please contact Technical Support! )o:=
pause
GOTO DISCONNECT

:ERROR50
ECHO Unfortunately I have not been able to connect to the remote backup share (\\10.10.1.10\ruby). Please contact Technical Support! )o:=
pause
GOTO DISCONNECT

:ERROR60
ECHO Unfortunately a failure occured whilst backing up your documents folder. Please contact Technical Support! )o:=
pause
GOTO DISCONNECT

:ERROR70
ECHO Unfortunately a failure occured whilst backing up your desktop folder. Please contact Technical Support! )o:=
pause
GOTO DISCONNECT

:ERROR100
ECHO Unfortunately I've been unable to stop the VPN Connection. Please contact Technical Support! )o:=
exit /b %errorlevel%

This script simply starts the OpenVPNService ("sc start OpenVPNService"), waits 30 seconds, checks it can ping the backup server (if not, retries twice with some very crude GOTO statements), connects to the remote share ("net use") and then commences a synchronisation of the given users documents and desktop folders using robocopy with the "/mir" option.

I've not got the best internet connection in the world, and the last thing I want is this backup saturating the available incoming bandwidth. Hence I've given robocopy a very conservative inter-packet gap of 500ms ("/ipg:500"). I toyed with the idea of scheduling the script, but for now I've placed a prominent shortcut for the laptop user to run it manually. I'm not sure what the end users usage pattern of the new laptop would be like and I don't want the script kicking in whilst there in the middle of some work. Neither do I want the script scheduled to run regularly at a time that the laptop is usually completely powered off.

There are some compromises in implementing a backup this way that I'm not quite happy with. If the laptop itself is compromised in anyway, them I am to some degree rendering my own home network open to compromise by the would-be attacker. Particularly with only one pfSense remote access OpenVPN endpoint. I will look into a separate endpoint for these backups as a matter of urgency. Then I can severely filter the traffic allowed over this "backup" VPN without seriously limiting what I can use the usual remote access VPN for when I'm away from home myself. At the end of the day, I have to remember that there are other family members at home who already have access to the network and the risk of them attaching a compromised device is about the same.

In the longer-term I hope to set-up this relative up with their own file server and pfSense filewall and run a properly filtered VPN sitelink, which will in-turn offer me an alternative off-site location for some of my most critical backups too. In the meantime, I think the benefits and mitigations I have in place makes the risk of doing backups in this way more than acceptable.