#!/bin/bash # # Copyright (C) 2017 Tetras Libre <admin@tetras-libre.fr> # Author: Beniamine, David <David.Beniamine@tetras-libre.fr> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. do_clean(){ do_log "Demontage du disque veuillez patienter avant de le retirer" if $encfs then fusermount -zu $encfsmount fi if $luks then cryptsetup luksClose $luksname fi umount -f $dev } # If $1 is not 0, exit with message $2 test_and_fail(){ if [ $1 -ne 0 ] then echo "Echec de la sauvegarde : '$2'" do_clean exit $1 fi } # Retourne le premier element premier(){ echo $1 } # Supprime la sauvegarde la plus ancienne supprimer_anciennes_sauvegardes(){ dir=`premier \`/bin/ls $parent_dir | grep $postfix \`` [ -z "$dir" ] && return 1 do_log "Plus de place suppression de la sauvegarde la plus ancienne: $dir" rm -rf $parent_dir/$dir return $? } do_log(){ echo "Sauvegarde NoCloud - $@" } dump_databases(){ if $mysql then do_log "Sauvegarde mysql" mysqldump --events --single-transaction --flush-logs --all-databases \ | gzip > /root/db.sql.gz test_and_fail $? "Impossible de sauvegarde la base de donnée mysql" fi if $postgres then do_log "Sauvegarde postgresql" sudo -u postgres pg_dumpall > /root/pg_dump.sql test_and_fail $? "Impossible de sauvegarde la base de donnée postgresql" gzip /root/pg_dump.sql fi if $gitlab then # the backup should be in /var/opt/gitlab/backups thus in srv_directories do_log "Creation de la sauvegarde gitlab" /usr/bin/gitlab-rake $voptminus gitlab:backup:create backup_path=`grep "'backup_path'" /etc/gitlab/gitlab.rb | sed 's/^.*= "\(.*\)"$/\1/'` if [ -d "$backup_path" ] then /bin/ls -dt $backup_path/* | tail -n +11 | xargs rm -rf else do_log "Dossier de sauvegarde gitlab non trouvé, pensez à supprimer manuellement les anciennes sauvegardes" fi fi } sauvegarde_serveur(){ dump_databases do_log "Creation de l'archive configuration serveur" [ ! -z "$SERVICES" ] && systemctl stop $SERVICES tar czf$vopt $dest/serveur.tgz $srv_directories ret=$? [ ! -z "$SERVICES" ] && systemctl start $SERVICES if [ $ret -eq 1 ] then # only warn on tar return code 1 do_log "Attention: des fichiers on été modifié durant la sauvegarde de l'archive serveur" return 0 else return $ret fi } sauvegarde_donnees(){ if $archive then tar cvzf $dest/Donnees.tar.gz /home else cp $voptminus -a /home $dest/Donnees fi } sauvegarde_clone(){ dump_databases cmd="rsync -aAX /" EXCLUDE='/dev,/proc,/var/run,/var/cache,/var/backups,/var/lock,/sys,/tmp,/run,/mnt,/media,/lost+found"' for filtre in $(echo "$EXCLUDE,$EXCLUDEFROMCLONE" | sed 's/,/ /g') do cmd="$cmd --exclude=$filtre" done cmd="$cmd $dest" do_log "Running : '$cmd'" $cmd } sauvegarde_seafile(){ # On sauvegarde le contenu des bibliothèques echo "Sauvegarde - Contenu Seafile" if [ ! -d /mnt/seafile-fuse ] ; then mkdir /mnt/seafile-fuse ; fi # Demonte le dossier si file au cas ou le dernier backup l'ait laisse dans un drole d'etat fusermount -zu /mnt/seafile-fuse /srv/$seafile/seafile-server-latest/seaf-fuse.sh start /mnt/seafile-fuse if $archive then tar cvzf $dest/contenus_seafile.tar.gz /mnt/seafile-fuse else /usr/bin/rsync -rtv --exclude 'seafile-data/storage' --modify-window=2 /mnt/seafile-fuse/ /$dest/contenus_seafile/ fi /bin/sync /srv/$seafile/seafile-server-latest/seaf-fuse.sh stop } # tente de liberer de l'espace si c'est pertinent puis rejoue les commandes # passées en arguments liberer_espace(){ if [ "`df --output=avail $parent_dir | sed 1d`" -lt $MIN_SIZE ] then supprimer_anciennes_sauvegardes test_and_fail $? "Plus d'espace sur le disque et pas d'ancienne sauvegarde a supprimer" $@ ret=$? else if $warn_only then echo "l'action $@ a indiqué des erreurs, mais la sauvegarde continue" else test_and_fail 1 "l'action '$@' a planté innopinement" fi fi } usage(){ echo "Utilisation $0 [options] device" echo "Device doit etre un disque non monté" echo "Options" echo " -h | --help Affiche cette aide et quitte" echo " -v | --verbose Active le mode verbeux" echo " -a | --archive Archive les donnes au lieu de les copier" echo " -d | --data Sauvegarde les donnees (/home)" echo " -p | --postgresql Sauvegarde postgresql (implique --config)" echo " -m | --mysql Sauvegarde mysql (implique --config)" echo " -c | --config Sauvegarde le serveur ($srv_directories)" echo " -C | --Clone exclude Effectue un clone rsync, exclude est une liste de dossier a exclure separe par des ','" echo " -g | --gitlab Sauvegarde gitlab (implique --config)" echo " -u | --unifi Sauvegarde unifi (/var/lib/unifi, implique --config)" echo " -s | --seafile host Sauvegarde seafile host (seafile fuse)" echo " -e | --encfs pass Utilise des dossiers chiffrés encfs, protégés par le mot de passe donné" echo " -i | --interrupt list Interrompt les services donnés (liste séparé par des virgules) durant la création de l'archive tar." echo " -l | --luks pass Utilise des disques chiffres luks, protégés par le mot de passe donné" echo " -w | --warnonly En cas d'erreur d'une action de sauvegarde, le programme affiche un message mais ne s'nterrompt pas" echo " -P | --Postcmd Execute la commande donnee apres la sauvegarde" } dest=/mnt/backup date=`date +%Y-%m-%d_%H-%M-%S` postfix=_sauvegarde_`hostname` srv_directories="/root /etc /srv /var/www /usr /lib /opt /var/opt" data_directories="/home" ACTIONS="" gitlab=false mysql=false postgres=false encfs=false luks=false luksname=crypt_backup archive=false clone=false MIN_SIZE=$((1024*1024)) warn_only=false post_cmd="" # Transform long options to short ones for arg in "$@"; do shift set -- "$@" `echo $arg | sed 's/^-\(-.\).*$/\1/'` done optspec=":hvC:dcguws:mpe:i:l:aP:" while getopts "$optspec" optchar; do case "${optchar}" in h) usage exit 0 ;; v) vopt="v" voptminus="-v" ;; a) archive=true ;; d) ACTIONS+="\nsauvegarde_donnees" ;; c) ACTIONS+="\nsauvegarde_serveur" ;; g) ACTIONS+="\nsauvegarde_serveur" gitlab=true ;; u) ACTIONS+="\nsauvegarde_serveur" srv_directories+=" /var/lib/unifi" ;; s) seafile="$OPTARG" ACTIONS+="\nsauvegarde_seafile" ;; m) mysql=true ACTIONS+="\nsauvegarde_serveur" ;; p) postgres=true ACTIONS+="\nsauvegarde_serveur" ;; e) encfs=true ENCPASS="$OPTARG" ;; i) SERVICES="${OPTARG//,/ }" ;; l) luks=true ENCPASS="$OPTARG" ;; w) warn_only=true; ;; P) post_cmd="$OPTARG" ;; C) clone=true EXCLUDEFROMCLONE="$OPTARG" ACTIONS="\nsauvegarde_clone" ;; *) echo "Option inconnue -$optchar" usage exit 1 ;; esac done shift $(($OPTIND - 1)) ACTIONS=`echo -e $ACTIONS | sort -u | sed /^$/d` if [ -z "$1" ] then usage test_and_fail 1 "Pas de disque donnée, abandon" fi # $1 should be an unmounted device like /dev/sdb1 dev=$1 do_log "démarrage le `date`" [ ! -d "$dest" ] && mkdir "$dest" if $luks then echo $ENCPASS | cryptsetup luksOpen $dev $luksname test_and_fail $? "Impossible de déchiffrer le disque, abandon" /bin/mount -t auto /dev/mapper/$luksname $dest echo "OK" > $dest/test else /bin/mount -t auto $dev $dest fi test_and_fail $? "Impossible de monter le disque destination, abandon" if $encfs then crypted="$dest/crypted" encfsmount="$dest/backups" encfscmd="/usr/bin/encfs --stdinpass $crypted $encfsmount" if [ ! -d $crypted ] then # First call with --encfs rm -rf $encfsmount echo -e "y\ny\np\n$ENCPASS\n" | $encfscmd else echo -e "$ENCPASS\n" | $encfscmd fi test_and_fail $? "Impossible de monter le coffre chiffré" unset ENCPASS dest=$encfsmount fi parent_dir=$dest if ! $clone then dest="$parent_dir/$date$postfix" fi mkdir "$dest" [ ! -d "$dest" ] && liberer_espace mkdir "$dest" for action in $ACTIONS do $action ret=$? while [ $ret -ne 0 ] do liberer_espace $action ret=$? done nom=${action/_/ } do_log "$nom reussie" done # force sync of files to disk before unmounting /bin/sync do_log "Resultats de la sauvegarde:" du -h -d 1 $dest/ df -h $dev do_clean if [ ! -z "$post_cmd" ] then do_log "Execution de la commande post sauvegarde" $post_cmd fi do_log "Sauvegarde terminee le `date`"