LDAP + BASH: скрипт резервного копирования и восстановления базы данных LDAP

Автор: | 03/05/2014
 

ldap-logoВсё работает на операционной системе:

# cat /etc/redhat-release CentOS release 6.5 (Final)

LDAP-сервер:

# yum list installed | grep openldap
openldap.x86_64         2.4.23-34.el6_5.1
openldap-clients.x86_64 2.4.23-34.el6_5.1
openldap-servers.x86_64 2.4.23-34.el6_5.1

Сервер сконфигурирован на использование файла конфигурации slapd.conf, а не базы cn=config. Это имеет значение только для функции dirdel (), в которой задаётся переменная $dirtodel.

В скрипте используется функция getopts(), о которой подробнее можно почитать в статье BASH: функция getopts — используем опции в скриптах.
Так же — формирование списков резервных копий и имеющихся на сервере DIT выполняется с использованием индексированного массива, подробнее —BASH: использование массивов.

В целом про резервное копирование OpenLDAP можно почитать в статье OpenLDAP: резервное копирование и восстановление — утилиты slapcat / slapadd и ldapsearch / ldapadd.

#!/bin/bash
bklog="/var/log/ldap_backup.log"
bkdir="/backup"
PATH="$PATH:/usr/sbin/"
curdate=`date +%Y-%M-%d-%H:%M:%S`
ardate=`echo $curdate  | sed -e 's/:/_/g' -e 's/-/_/g'`
slapconf="/etc/openldap/slapd.conf"

function answer ()
{
while read response; do
echo
case $response in
    [yY][eE][sS]|[yY])
        return 0
        break
        ;;
    [nN][oO]|[nN])
        return 1
        break
        ;;
        *)
        printf "Please, enter Y(yes) or N(no)! "
esac
done
}

slapstatus () {
service slapd status && { echo -e "nSlapd running!nCan't proceed.n"; exit 1; }

# used after backup completed to delete all files older then 30 days
clean () {
  find $bkdir -type f -mtime +30 -exec rm -f $v {} ;
}

# get list of used Root DN's on server
getnames () {
  ldapsearch -x -b '' -s base '(&)' namingContexts -LLL | cut -d" " -f 2 | grep -v "dn:"
}

# used in backup function, $setnames variable given inside loop when starting backup
arname_f () {
  echo $setnames | sed -e 's/=/_/g' -e 's/,/_/g'
}

backup () {
  local arname=$(arname_f)
  local filename="$2"/"$3"_"$arname".ldif
  slapcat $v -b "$1" -l $filename && echo -e "Saved to file $filename." || exit 1
}

# used for restore option to determine DN for restore and dirdel() function to delete files from
dnselect () {

declare -a names
a=1
for i in $(getnames); do
  echo -e "n$a"")" $i
  names[$a]=$i
  (( a++ ))
done
echo
read -p "Please select DN to restore: " selection

if [ ! -z $selection ]; then
  echo -e "nWill restore: ${names[$selection]}.n"
  dntorestore=${names[$selection]}
else
  echo -e "nThere is no number $selection!"
  exit
fi
}

# this will delete unccessary files from DN's directory exclude file DB_CONFIG
dirdel () {

service slapd stop
service slapd status && { echo -e "nSlapd running!nCan't proceed.n"; exit 1; }

dirtodel=`cat $slapconf | grep -A3 "$1" | grep directory | awk '{print $2}'`
echo -e "nAs your choice is $1, I'll remove directory: $dirtodel.n"

cd $dirtodel

echo -e "Ready to delete DB files.nnChecking current directory $PWD...n"

if [ $PWD != $dirtodel ]; then
  echo -e "nERROR: wrong directory!n"
  exit 1
else
  echo -e "OK, directory correct - will remove all in "$dirtodel".n"
  echo -en "Are you sure to proceed? If "No" selected - I'll just exit: "
  answer && ls -1 | grep -v "^DB_CONFIG" | xargs rm -fv || exit 1
fi
}

# select file from which will run slapadd
bkpSelect () {
declare -a bkpnames
a=1

  echo -e "nCurrent backup files: "

for i in `ls -1 $bkdir`; do
  echo -e "n$a"")" $i
  bkpnames[$a]=$i
  (( a++ ))
done
echo
read -p "Please select archive to restore from: " arSelection

if [ ! -z $arSelection ]; then
  echo -e "nWill restore $dntorestore from ${bkpnames[$arSelection]}.n"
  echo -en "Proceed? If "No" selected - I'll just exit: "
  answer || exit 1
  bakName=${bkpnames[$arSelection]}
else
  echo -e "nThere is no number $arSelection!"
  exit
fi
}
restore () {

slapadd $v -b "$1" -l "$2" && resResult=0 || resResult=1
chown -R ldap:ldap $dirtodel

if [ $resResult = 0 ]; then
  service slapd start
  echo -e "nResore complited sucsessfully at $curdate.n"
else
  echo -e "nError while restoring!n"
  exit 1
fi
}

usage () {
cat << EOF

Usage: $0 [ options ]

This script can create and restore LDAP Databases by specified DN.

Available options are:

 -h | (help)         Show this help and exit;
 -b | (backup)       Create ldif-files with backup of all DN's;
 -r | (restore)      Restore database from ldif-file;
 -v | (verbose)      Enable verbose mode.

ATTENTION: when restoring DN - script will delete all data in DN's directory!

All output writing to log-file /var/log/ldap_backup.log.

EOF
}

# reset control variables
backuprun=
restorerun=
v=

while getopts "hbrv" opt; do
  case $opt in
        h)
          usage && exit 1
          ;;
        b)
          backuprun=1
          ;;
        r)
          restorerun=1
         ;;
        v)
          v="-v"
         ;;
        *)
         usage && exit 1
         ;;
  esac
done

if [ -z $1 ]; then
  usage && exit
fi

{
if [[ $backuprun ]]; then
  echo -e "nStarting backup process at $curdaten"
  slapstatus;
    for setnames in $(getnames); do
      echo -e "Creating backup for $setnames...n"
      backup $setnames $bkdir $ardate && echo -e "Donen" || echo -e "Something going wrong!n"
    done
  echo -e "Deleteing old archives..."
  clean && echo -e "Deleted.n"
  echo -e "Backup process finished at $curdate.n"
fi
} 2>&1 | tee -a "$bklog"

{
if [[ $restorerun ]]; then
  echo -e "nStarting restore process at $curdate.n"
  slapstatus;
  dnselect;
  dirdel $dntorestore;
  bkpSelect;
  echo -e "Runnig slapadd process:"
  restore $dntorestore "$bkdir/$bakName";
fi
} 2>&1 | tee -a "$bklog"

Примеры использования

Создание копий всех баз и удаление копий старше 30-ти дней:

# ./ldap_backup.sh -b

Starting backup process at 2014-39-04-15:39:54

slapd is stopped

Slapd stopped. I can't proceed without it. If "No" selected - I'll exit. Start it? (y/n) y

Starting slapd:                                            [  OK  ]

Creating backup for ou=db_1,dc=com...

Saved to file /backup/2014_39_04_15_39_54_ou_db_1_dc_com.ldif.
Done

Creating backup for ou=db_1,dc=com...

Saved to file /backup/2014_39_04_15_39_54_ou_db_1_dc_com.ldif.
Done

Deleting old archives...
Deleted.

Backup process finished at 2014-39-04-15:39:54.

Восстановление db_2 — удаление старых файлов базы, загрузка данных из файла резервной копии 2014_39_04_15_39_54_ou_db_2_dc_com.ldif:

# ./ldap_backup.sh -r

Starting restore process at 2014-40-04-15:40:48.

slapd (pid  24008) is running...

1) ou=db_1,dc=com

2) ou=db_2,dc=com

Please select DN to restore: 2

Will restore: ou=db_2,dc=com.

Stopping slapd:                                            [  OK  ]
slapd is stopped

As your choice is ou=db_2,dc=com, I'll remove directory: /var/lib/ldap/db_2.

Ready to delete DB files.

Checking current directory /var/lib/ldap/db_2...

OK, directory correct - will remove all in /var/lib/ldap/db_2.

Are you sure to proceed? If "No" selected - I'll just exit: y

removed `alock'
removed `__db.001'
removed `__db.002'
removed `__db.003'
removed `__db.004'
removed `__db.005'
removed `__db.006'
removed `dn2id.bdb'
removed `id2entry.bdb'
removed `log.0000000001'

Current backup files:

1) 2014_39_04_15_39_54_ou_db_1_dc_com.ldif

2) 2014_39_04_15_39_54_ou_db_2_dc_com.ldif

Please select archive to restore from: 2

Will restore ou=db_2,dc=com from 2014_39_04_15_39_54_ou_db_2_dc_com.ldif.

Proceed? If "No" selected - I'll just exit: y

Runnig slapadd process:
Starting slapd:                                            [  OK  ]

Resore complited sucsessfully at 2014-40-04-15:40:48.

Добавляем скрипт в cron на выполнение каждый день в 06:30 утра:

# crontab -l
30 6 * * * /opt/ldap_backup.sh -b

Проверяем:

# ls -lh /backup/
total 20M
-rw-r--r-- 1 root root 314K Mar 5 06:30 2014_30_05_06_30_01_dc_auto1.ldif
-rw-r--r-- 1 root root 378 Mar 5 06:30 2014_30_05_06_30_01_dc_auto2.ldif
-rw-r--r-- 1 root root 378 Mar 5 06:30 2014_30_05_06_30_01_dc_auto3.ldif
-rw-r--r-- 1 root root 9.5M Mar 5 06:30 2014_30_05_06_30_01_dc_db_2.ldif

Что можно (и нужно) улучшить:

1) добавить поддержку cn=config;
2) сжимать ldif-файлы бекапов в bz2-архивы.

UPD

Для использования скрипта с cn=config — необходимо изменить функцию dirdel ():

dirdel () {

dirtodel=`ldapsearch -x -LLL -D 'cn=root,cn=config' -w superpuperpassword -b 'cn=config' '(&(olcDbDirectory=*)(olcSuffix='${1}'))' olcDbDirectory | grep "olcDbDirectory" | cut -d":" -f 2`

echo -e "nAs your choice is $1, I'll remove directory: $dirtodel.n"

service slapd stop
service slapd status && { echo -e "nSlapd running!nCan't proceed.n"; exit 1; }

cd $dirtodel

echo -e "Ready to delete DB files.nnChecking current directory $PWD...n"

if [ $PWD != $dirtodel ]; then
  echo -e "nERROR: wrong directory!n"
  exit 1
else
  echo -e "OK, directory correct - will remove all in "$dirtodel".n"
  echo -en "Are you sure to proceed? If "No" selected - I'll just exit: "
  answer && ls -1 | grep -v "^DB_CONFIG" | xargs rm -fv || exit 1
fi
}