Automatically backup Pfsense configuration files

Introduction

If you're using Pfsense as your firewall solution, then you've surely wondered about keeping your configuration files backed up. Normally, you have to go to Diagnostics - Backup/Restore to backup the configuration files manually as presented on the picture below.

pfsense2

But we're all aware that manual backups are not really viable solution for SOHO networks, since administrators often forget or don't have time to manually backup the configuration files. Rather than that, we can use an automatic solution described below. First we have to create a new user backup in System - User Manager, which has access to the 'Diagnostics - Backup/Restore page' as presented below.

pfsense1

If we login to the web interface as that user we'll see that the user only has permissions to access Backup/Restore functionality as presented on the next picture. This makes the user an ideal candidate to use with the automatic backup job, since the rest of the functionality is not accessible by the user. Let's now follow the steps to automatically update the configuration files on the Pfsense itself.

pfsense3

Update: I've found out that pfSense makes its own automatic configuration backups: Configuration History, which are stored in the /cf/conf/backup/ directory. The following text is a direct quote from the presented link:

pfSense automatically keeps a backup when a change is made to the configuration. This makes it easy to revert after an undesirable settings change, but should not be relied upon as a sole means of backup. There are *numerous reasons* why having an off-system backup is a good practice, especially the case of storage media failure.

Therefore, we can also keep /cf/conf/backup/ directory backed up by copying it to the off-site backup storage: we can use scp, rsync, bacula or any other backup solution already in place in our own network.

Pfsense Backup Steps

First we have to login to the Pfsense in order to get the required session cookies. We're going to use curl, since it's available on Pfsense by default opposite to the wget alternative. Normally, we have to login by sending the credentials to the index.php script as presented below.

# curl -k -b cookies.txt -c cookies.txt --data
'login=Login&usernamefld=backup&passwordfld=password'
https://127.0.0.1/index.php
<html><head><title>CSRF check failed</title><script
type="text/javascript">if (top != self) {top.location.href =
self.location.href;}</script><script type="text/javascript">var
csrfMagicToken =
"sid:4b68aaf265e3ff2ea4851327a71a3bc0ff77e9de,1408621652;ip:1076fd81c12a94586c554fceb0145506bc600a36,1408621652";var
csrfMagicName = "__csrf_magic";</script><script
src="/csrf/csrf-magic.js"
type="text/javascript"></script></head><body>CSRF check failed. Either
your session has expired, this page has been inactive too long, or you
need to enable cookies.<br />Debug: <script
type="text/javascript">CsrfMagic.end();</script></body></html>

In the output above, we can see that the index.php is protected by CSRF token, so in order to bypass it we have to parse the response to get the current value of CSRF token and resend it in another request. Rather than doing that we can send the request directly to the diag_backup.php script, which is not protected by the CSRF token. By using the diag_backup.php rather than index.php script, we'll be able to login to the Pfsense web interface without any problems as presented below. The -c option passed to curl will write all cookies to a file, which acts as a cookie jar where cookies are taken from in subsequent requests. The subsequent requests have to use the -b option to read the cookie jar and append appropriate cookies when sending the request to the server.

# curl -k -b cookies.txt -c cookies.txt --data
'login=Login&usernamefld=backup&passwordfld=password'
https://127.0.0.1/diag_backup.php

Message from syslogd@mypfsense at Aug 21 11:14:56 ...
mypfsense php: /diag_backup.php: Successful login for user 'backup'
from: 127.0.0.1

Note that at this point the cookies.txt file was created, which contains the PHPSESSID cookie that can be used to login to the web interface and actually make the backup request.

# cat cookies.txt
# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

#HttpOnly_127.0.0.1 FALSE / TRUE 0 PHPSESSID
277f89946c0edf972373738715b49405

Next, we can use the saved cookies in order to make an authenticated request to download the backup by using the curl command below.

# curl -k -b cookies.txt -o config-router-`date +%Y%m%d%H%M%S`.xml
--data 'Submit=download&donotbackuprrd=no'
https://127.0.0.1/diag_backup.php
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 241k 100 241k 100 33 5918k 808 --:--:-- --:--:-- --:--:-- 5893k

Note that the downloaded file is config-router-20140821111558.xml, which contains XML data as presented below.

# file config-router-20140821111558.xml
config-router-20140821111558.xml: XML document text

At this point we've seen that automatic backup of configuration files of Pfsense is possible, we just need to write a script that will do it for us. A backup script can be easily written by using the previously presented commands and is seen below. First we're deleting the cookies.txt file if already exists to start fresh. Then we're sending a request to login to the web interface, where the cookies are saved to cookies.txt file. After that we're sending a request to actually download the configuration file. Note that you have to use full paths in scripts, which you want to use with cronjobs; alternatively you can edit the PATH variable used by crontab, which must include the /usr/local/bin/ path (because curl is located at that path on the system).

#!/bin/sh

# Delete cookies.txt file if exists and start fresh.
if [ -f cookies.txt ]; then
rm cookies.txt
fi

# Get cookie values.
/usr/local/bin/curl -k -b cookies.txt -c cookies.txt --data
'login=Login&usernamefld=backup&passwordfld=password'
https://127.0.0.1/diag_backup.php

# Download the configuration.
/usr/local/bin/curl -k -b cookies.txt -o /root/config-router-`date
+%Y%m%d%H%M%S`.xml --data 'Submit=download&donotbackuprrd=no'
https://127.0.0.1/diag_backup.php

Afterwards we have to add a crontab entry by executing the crontab -e as root and adding the following entry to run the /root/backup script on Sunday at 02:00 AM. Note that an empty line has to be added to the end of crontab entries, which is not vidible below.

SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
# Order of crontab fields
# minute hour mday month wday command

0 2 * * 6 /root/scripts/backup

To test out the crontab entry with the same environment that crontab will use, you can run the following command, which can give be used for debugging purposes when something doesn't work as expected.

# env -i SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/root LOGNAME=root /root/backup

Then we're left only by copying the contents of /backup/ folder to the backup server either by using the scp command or any other backup software agent like Bacula.

Conclusion

In this article we've seen how we can automatically update the Pfsense configuration files to keep it updated at all times. Automatization is an intergral part of every backup procedure in order to keep backups regular and consistent.

Comments