Created
December 21, 2018 17:04
-
-
Save Gargravarr2112/4a2d677a0bf055ec9afca65919c178fc to your computer and use it in GitHub Desktop.
Automation-suitable backup and restore script for OpenLDAP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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