Skip to content

Instantly share code, notes, and snippets.

@Gargravarr2112
Created December 21, 2018 17:04
Show Gist options
  • Save Gargravarr2112/4a2d677a0bf055ec9afca65919c178fc to your computer and use it in GitHub Desktop.
Save Gargravarr2112/4a2d677a0bf055ec9afca65919c178fc to your computer and use it in GitHub Desktop.
Automation-suitable backup and restore script for OpenLDAP
#!/bin/bash
#Backup or restore an OpenLDAP database
#Backup Mode produces time-stamped LDIF files of the config (n=0) and main (n=1) slapd databases. Does not interfere.
#Restore Mode assumes previous backups made by this script. Offers the user the backup file to restore, then in sequence:
#-stops slapd
#-deletes existing slapd config and database files
#-restores the LDIFs
#-sets permissions on restored files
#-starts slapd
#
#Usage:
#Backup: back-slap -B [-v] [-b backup-directory]
#Restore: back-slap -R [-v] [-b backup-directory] [-c slapd-config-directory] [-d slapd-data-directory]
#Assumes Debian and derivatives.
#Author: Rob Johnson
#Date: December 2017
set -uo pipefail;
if [ -f /etc/default/slapd ]; then
source /etc/default/slapd; #To get the user/group
else
SLAPD_USER='openldap';
SLAPD_GROUP='openldap';
fi
CONFIGDB=0;
DATADB=1;
VERBOSE='';
SLAPDCONFDIR='/etc/ldap/slapd.d/';
DATABASEDIR='/var/lib/ldap/';
BACKUPDIR='/var/backups/ldap/';
mode='';
if [ $(id -u) -ne 0 ]; then
echo "Must be root!";
exit 1;
fi
dontDoThat() {
echo "Please choose exactly one of -B for backup or -R for restore. Run $0 -h for help.";
exit 7;
}
displayHelp() {
echo "Usage:";
echo "$0 -B [-h] [-v] [-b backup-directory]";
echo "$0 -R [-h] [-v] [-b backup-directory] [-c slapd-config-directory] [-d slapd-data-directory] [-g group] [-u user]";
echo;
echo "Modes:";
echo " -B backup";
echo " -R restore";
echo;
echo "Options:";
echo " -b [path] backup directory to save to/restore from - default '$BACKUPDIR'";
echo " -v verbose mode";
echo " -h display this help";
echo " Restore only:";
echo " -c [path] slapd config directory to delete on restore - default '$SLAPDCONFDIR'";
echo " -d [path] slapd data directory to delete on restore - default '$DATABASEDIR'";
echo " -g [group] group that slapd runs under for permissions setting on restore - default '$SLAPD_GROUP'";
echo " -u [user] user that slapd runs under for permissions setting on restore - default '$SLAPD_USER'";
exit 0;
}
while getopts BRb:c:d:g:u:hv opt; do
case $opt in
B)
if [ ! -z $mode ]; then
dontDoThat;
fi
mode='backup';
;;
R)
if [ ! -z $mode ]; then
dontDoThat;
fi
mode='restore';
;;
c) #Config dir
if [ ! -d "$OPTARG" ]; then
echo "slapd config directory does not exist!";
exit 3;
else
SLAPDCONFDIR="$OPTARG";
fi
;;
b) #Backup dir
BACKUPDIR="$OPTARG";
;;
d) #Database dir
if [ ! -d "$OPTARG" ]; then
echo "slapd database directory does not exist!";
exit 3;
else
DATABASEDIR="$OPTARG";
fi
;;
v)
echo "Verbose selected";
VERBOSE='-v';
;;
h)
displayHelp;
;;
g)
SLAPD_GROUP="$OPTARG";
if [ ! $(getent group "$SLAPD_GROUP" > /dev/null) ]; then
echo "Specified group $SLAPD_GROUP does not exist!";
exit 9;
fi;
;;
u)
SLAPD_USER="$OPTARG";
if [ ! $(getent passwd "$SLAPD_USER" > /dev/null) ]; then
echo "Specified user $SLAPD_USER does not exist!";
exit 9;
fi;
;;
*)
echo "Invalid option: $opt";
exit 5;
;;
esac;
done
backupDatabase() {
TIMESTAMP=$(date +%F_%H%M%S);
set -e;
configBackupFile="config-$TIMESTAMP.ldif";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Backing up config database to $configBackupFile";
fi
slapcat $VERBOSE -n $CONFIGDB -o ldif-wrap=no -l "$BACKUPDIR/$configBackupFile";
databaseBackupFile="data-$TIMESTAMP.ldif";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Backing up main database to $databaseBackupFile";
fi
slapcat $VERBOSE -n $DATADB -o ldif-wrap=no -l "$BACKUPDIR/$databaseBackupFile";
tarballFile="$BACKUPDIR/$HOSTNAME-$TIMESTAMP.tbz2";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Compressing to $tarballFile";
fi
tar $VERBOSE --create --bzip2 --file "$tarballFile" --directory="$BACKUPDIR" $configBackupFile $databaseBackupFile;
rm $VERBOSE "$BACKUPDIR/$configBackupFile" "$BACKUPDIR/$databaseBackupFile";
echo "Backup $TIMESTAMP completed successfully";
return 0;
}
restoreDatabase() {
#Switch these on to allow 1. the backup file listing to work properly 2. checking for any files in the directories
shopt -s extglob nullglob dotglob;
selectedFile='';
chooseBackupFile;
if [ "x$VERBOSE" == "x-v" ]; then
echo "Restoring $selectedFile";
fi
#Extract the tarball
tempDir=$(mktemp --directory);
tar $VERBOSE --extract --file $selectedFile --directory="$tempDir";
#Ensure slapd isn't running
if [ -f /var/run/slapd/slapd.pid ]; then
slapdPID=$(</var/run/slapd/slapd.pid);
kill -0 $slapdPID 2> /dev/null; #-0 is a 'ping' signal for processes
if [ $? -ne 0 ]; then
echo "PID file exists but slapd is not running, you should check it has not crashed. Bailing out."
exit 2;
else
if [ "x$VERBOSE" == "x-v" ]; then
echo "slapd is running, stopping service";
fi
set -e;
service slapd stop;
fi
else
if [ "x$VERBOSE" == "x-v" ]; then
echo "slapd is not running";
fi
fi
#Wipe out the existing slapd database (safely!)
if [ -z "$SLAPDCONFDIR" ] || [ -z "$DATABASEDIR" ]; then #rm -r / protection!
echo "Config or database directory variable is blank! Bailing out!";
exit 4;
fi
#Do not remove protection above or this could go very wrong!
set -e;
configFiles=("$SLAPDCONFDIR"*);
if [ ${#configFiles[@]} -gt 0 ]; then
rm -vr "$SLAPDCONFDIR"*;
fi
dataFiles=("$DATABASEDIR"*);
if [ ${#dataFiles[@]} -gt 0 ]; then
rm -vr "$DATABASEDIR"*;
fi
configFile=("$tempDir/"config-*.ldif);
databaseFile=("$tempDir/"data-*.ldif);
if [ "x$VERBOSE" == "x-v" ]; then
echo "Config backup: $configFile, database backup: $databaseFile";
echo "Restoring config database";
fi
#Load the files into the empty directories
slapadd $VERBOSE -n $CONFIGDB -F "$SLAPDCONFDIR" -l "$configFile";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Restoring main database";
fi
slapadd $VERBOSE -n $DATADB -F "$SLAPDCONFDIR" -l "$databaseFile";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Setting ownership of config and database directories";
fi
chown -R $SLAPD_USER:$SLAPD_GROUP "$SLAPDCONFDIR";
chown -R $SLAPD_USER:$SLAPD_GROUP "$DATABASEDIR";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Restore succeeded. Starting slapd";
fi
service slapd start;
echo "Restore completed successfully";
if [ "x$VERBOSE" == "x-v" ]; then
echo "Clearing temporary folder";
fi
if [ ! -z "$tempDir" ] && [ $(echo $tempDir | wc --chars) -gt 10 ]; then #mktemp produces 20 characters on my system; this should avoid any system directories.
rm $VERBOSE -rf "$tempDir";
else
echo "Given temporary directory '$tempDir' is invalid, aborting deletion for safety. Please check and delete by hand."
return 1;
fi
return 0;
}
#List the contents of the $BACKUPDIR and present a menu (using bash's select) to the user to choose the backup to restore.
chooseBackupFile() {
backupFiles=( "$BACKUPDIR/$HOSTNAME"-*.tbz2 );
if [ -z "$backupFiles" ]; then
echo "No backup archives found in $BACKUPDIR, cannot continue";
exit 1;
fi
#Using globbing, it's easy to make a case statement choose a valid entry - build a string of each filename separated by pipes, making an 'or' selection
filenameString="@(${backupFiles[0]}";
for((i=1;i<${#backupFiles[@]};i++));
do
filenameString+="|${backupFiles[$i]}";
done;
filenameString+=")";
#Present the options to the user
select file in "${backupFiles[@]}" "quit"
do
case $file in
$filenameString)
selectedFile="$file";
break;
;;
"quit")
exit
;;
*)
file=""; #For next pass through the loop
echo "Please choose a number from 1 to $((${#backupFiles[@]}+1))";
;;
esac
done
}
case "$mode" in
"backup")
if [ ! -d "$BACKUPDIR" ]; then
read -n 1 -p "Given backup directory $BACKUPDIR does not exist, create it? [Y/N] " confirm;
if [ "x$confirm" == 'xY' ]; then
echo;
mkdir -v "$BACKUPDIR";
else
exit 2;
fi
fi
backupDatabase;
;;
"restore")
if [ ! -d "$BACKUPDIR" ]; then
echo "Backup directory $BACKUPDIR does not exist, cannot restore from it! Create a backup or override with -b [path]";
exit 8;
fi
restoreDatabase;
;;
*) #Shouldn't hit this, but just in case
dontDoThat;
;;
esac;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment