7173fc06ea
All checks were successful
continuous-integration/drone/push Build is passing
This allows us to remove the shellcheck ignore warning comment and puts it in line with upstream. Apparently, the file can only contain one PID, so it's okay to quote the variable.
306 lines
11 KiB
Django/Jinja
306 lines
11 KiB
Django/Jinja
#!/bin/sh
|
|
# Careful, the zzz_evobackup template was last updated on 2020/04/15
|
|
#
|
|
# Script Evobackup client
|
|
# See https://gitea.evolix.org/evolix/evobackup
|
|
#
|
|
# Author: Gregory Colpart <reg@evolix.fr>
|
|
# Contributors:
|
|
# Romain Dessort <rdessort@evolix.fr>
|
|
# Benoît Série <bserie@evolix.fr>
|
|
# Tristan Pilat <tpilat@evolix.fr>
|
|
# Victor Laborie <vlaborie@evolix.fr>
|
|
# Jérémy Lecour <jlecour@evolix.fr>
|
|
#
|
|
# Licence: AGPLv3
|
|
#
|
|
# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES
|
|
|
|
# Fail on unassigned variables
|
|
set -u
|
|
|
|
##### Configuration ###################################################
|
|
|
|
# email adress for notifications
|
|
MAIL={{ evobackup_client__mail }}
|
|
|
|
# list of hosts (hostname or IP) and SSH port for Rsync
|
|
SERVERS="{% for host in evobackup_client__hosts %}{{ host.name }}:{{ host.port }}{% if loop.index != loop.length %} {% endif %}{% endfor %}"
|
|
|
|
# Should we fallback on servers when the first is unreachable ?
|
|
SERVERS_FALLBACK={{ evobackup_client__servers_fallback }}
|
|
|
|
# timeout (in seconds) for SSH connections
|
|
SSH_CONNECT_TIMEOUT=${SSH_CONNECT_TIMEOUT:-30}
|
|
|
|
## We use /home/backup : feel free to use your own dir
|
|
LOCAL_BACKUP_DIR="{{ evobackup_client__backup_path }}"
|
|
|
|
# You can set "linux" or "bsd" manually or let it choose automatically
|
|
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
|
|
|
|
# Change these 2 variables if you have more than one backup cron
|
|
PIDFILE="{{ evobackup_client__pid_path }}"
|
|
LOGFILE="{{ evobackup_client__log_path }}"
|
|
|
|
## Enable/Disable tasks
|
|
LOCAL_TASKS=${LOCAL_TASKS:-1}
|
|
SYNC_TASKS=${SYNC_TASKS:-1}
|
|
|
|
##### SETUP AND FUNCTIONS #############################################
|
|
|
|
BEGINNING=$(/bin/date +"%d-%m-%Y ; %H:%M")
|
|
|
|
# shellcheck disable=SC2174
|
|
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
|
|
|
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
|
|
## lang = C for english outputs
|
|
export LANGUAGE=C
|
|
export LANG=C
|
|
|
|
## Force umask
|
|
umask 077
|
|
|
|
## Initialize variable to store SSH connection errors
|
|
SERVERS_SSH_ERRORS=""
|
|
|
|
# Call test_server with "HOST:PORT" string
|
|
# It will return with 0 if the server is reachable.
|
|
# It will return with 1 and a message on stderr if not.
|
|
test_server() {
|
|
item=$1
|
|
# split HOST and PORT from the input string
|
|
host=$(echo "${item}" | cut -d':' -f1)
|
|
port=$(echo "${item}" | cut -d':' -f2)
|
|
|
|
# Test if the server is accepting connections
|
|
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" -i /root/.ssh/evobackup_id "${host}" -p "${port}" -t "exit"
|
|
# shellcheck disable=SC2181
|
|
if [ $? = 0 ]; then
|
|
# SSH connection is OK
|
|
return 0
|
|
else
|
|
# SSH connection failed
|
|
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
|
|
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
|
|
|
|
return 1
|
|
fi
|
|
}
|
|
# Call pick_server with an optional positive integer to get the nth server in the list.
|
|
pick_server() {
|
|
increment=${1:-0}
|
|
list_length=$(echo "${SERVERS}" | wc -w)
|
|
|
|
if [ "${increment}" -ge "${list_length}" ]; then
|
|
# We've reached the end of the list
|
|
new_error="No more server available"
|
|
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
|
|
|
|
# Log errors to stderr
|
|
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >&2
|
|
# Log errors to logfile
|
|
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >> $LOGFILE
|
|
return 1
|
|
fi
|
|
|
|
# Extract the day of month, without leading 0 (which would give an octal based number)
|
|
today=$(date +%e)
|
|
# A salt is useful to randomize the starting point in the list
|
|
# but stay identical each time it's called for a server (based on hostname).
|
|
salt=$(hostname | cksum | cut -d' ' -f1)
|
|
# Pick an integer between 0 and the length of the SERVERS list
|
|
# It changes each day
|
|
item=$(( (today + salt + increment) % list_length ))
|
|
# cut starts counting fields at 1, not 0.
|
|
field=$(( item + 1 ))
|
|
|
|
echo "${SERVERS}" | cut -d' ' -f${field}
|
|
}
|
|
|
|
## Verify other evobackup process and kill if needed
|
|
if [ -e "${PIDFILE}" ]; then
|
|
pid=$(cat "${PIDFILE}")
|
|
# Does process still exist ?
|
|
if kill -0 "${pid}" 2> /dev/null; then
|
|
# Killing the childs of evobackup.
|
|
for ppid in $(pgrep -P "${pid}"); do
|
|
kill -9 "${ppid}";
|
|
done
|
|
# Then kill the main PID.
|
|
kill -9 "${pid}"
|
|
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
|
|
else
|
|
rm -f ${PIDFILE}
|
|
fi
|
|
fi
|
|
echo "$$" > ${PIDFILE}
|
|
# shellcheck disable=SC2064
|
|
trap "rm -f ${PIDFILE}" EXIT
|
|
|
|
|
|
##### LOCAL BACKUP ####################################################
|
|
|
|
if [ "${LOCAL_TASKS}" = "1" ]; then
|
|
## Dump system and kernel versions
|
|
uname -a > ${LOCAL_BACKUP_DIR}/uname
|
|
|
|
## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
|
|
for addr in 8.8.8.8 www.evolix.fr travaux.evolix.net; do
|
|
mtr -r ${addr} > ${LOCAL_BACKUP_DIR}/mtr-${addr}
|
|
traceroute -n ${addr} > ${LOCAL_BACKUP_DIR}/traceroute-${addr} 2>&1
|
|
done
|
|
|
|
## Dump process with ps
|
|
ps auwwx >${LOCAL_BACKUP_DIR}/ps.out
|
|
|
|
if [ "${SYSTEM}" = "linux" ]; then
|
|
## Dump network connections with ss
|
|
ss -taupen > ${LOCAL_BACKUP_DIR}/netstat.out
|
|
|
|
## List Debian packages
|
|
dpkg -l > ${LOCAL_BACKUP_DIR}/packages
|
|
dpkg --get-selections > ${LOCAL_BACKUP_DIR}/packages.getselections
|
|
apt-cache dumpavail > ${LOCAL_BACKUP_DIR}/packages.available
|
|
|
|
## Dump MBR / table partitions
|
|
disks=$(lsblk -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | awk '{print $1}')
|
|
for disk in ${disks}; do
|
|
dd if="/dev/${disk}" of="${LOCAL_BACKUP_DIR}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)"
|
|
fdisk -l "/dev/${disk}" > "${LOCAL_BACKUP_DIR}/partitions-${disk}" 2>&1
|
|
done
|
|
cat ${LOCAL_BACKUP_DIR}/partitions-* > ${LOCAL_BACKUP_DIR}/partitions
|
|
|
|
## Dump iptables
|
|
if [ -x /sbin/iptables ]; then
|
|
{ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${LOCAL_BACKUP_DIR}/iptables.txt
|
|
fi
|
|
|
|
## Dump findmnt(8) output
|
|
FINDMNT_BIN=$(command -v findmnt)
|
|
if [ -x "${FINDMNT_BIN}" ]; then
|
|
${FINDMNT_BIN} > ${LOCAL_BACKUP_DIR}/findmnt.txt
|
|
fi
|
|
else
|
|
## Dump network connections with netstat
|
|
netstat -finet -atn > ${LOCAL_BACKUP_DIR}/netstat.out
|
|
|
|
## List OpenBSD packages
|
|
pkg_info -m > ${LOCAL_BACKUP_DIR}/packages
|
|
|
|
## Dump MBR / table partitions
|
|
disklabel sd0 > ${LOCAL_BACKUP_DIR}/partitions
|
|
|
|
## Dump pf infos
|
|
pfctl -sa > ${LOCAL_BACKUP_DIR}/pfctl-sa.txt
|
|
|
|
fi
|
|
|
|
## Dump rights
|
|
#getfacl -R /var > ${LOCAL_BACKUP_DIR}/rights-var.txt
|
|
#getfacl -R /etc > ${LOCAL_BACKUP_DIR}/rights-etc.txt
|
|
#getfacl -R /usr > ${LOCAL_BACKUP_DIR}/rights-usr.txt
|
|
#getfacl -R /home > ${LOCAL_BACKUP_DIR}/rights-home.txt
|
|
|
|
fi
|
|
|
|
##### REMOTE BACKUP ###################################################
|
|
|
|
n=0
|
|
server=""
|
|
if [ "${SERVERS_FALLBACK}" = "1" ]; then
|
|
# We try to find a suitable server
|
|
while :; do
|
|
server=$(pick_server "${n}")
|
|
test $? = 0 || exit 2
|
|
|
|
if test_server "${server}"; then
|
|
break
|
|
else
|
|
server=""
|
|
n=$(( n + 1 ))
|
|
fi
|
|
done
|
|
else
|
|
# we force the server
|
|
server=$(pick_server "${n}")
|
|
fi
|
|
|
|
SSH_SERVER=$(echo "${server}" | cut -d':' -f1)
|
|
SSH_PORT=$(echo "${server}" | cut -d':' -f2)
|
|
|
|
HOSTNAME=$(hostname)
|
|
|
|
if [ "${SYSTEM}" = "linux" ]; then
|
|
rep="/bin /boot /lib /opt /sbin /usr /srv"
|
|
else
|
|
rep="/bsd /bin /sbin /usr"
|
|
fi
|
|
|
|
|
|
if [ "${SYNC_TASKS}" = "1" ]; then
|
|
# /!\ DO NOT USE COMMENTS in the rsync command /!\
|
|
# It breaks the command and destroys data, simply remove (or add) lines.
|
|
|
|
# Remote shell command
|
|
RSH_COMMAND="ssh -i /root/.ssh/evobackup_id -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'"
|
|
|
|
# ignore check because we want it to split the different arguments to $rep
|
|
# shellcheck disable=SC2086
|
|
rsync -avzh --stats --delete --delete-excluded --force --ignore-errors --partial \
|
|
--exclude "lost+found" \
|
|
--exclude ".nfs.*" \
|
|
--exclude "/var/log" \
|
|
--exclude "/var/log/evobackup*" \
|
|
--exclude "/var/lib/mysql" \
|
|
--exclude "/var/lib/postgres" \
|
|
--exclude "/var/lib/postgresql" \
|
|
--exclude "/var/lib/sympa" \
|
|
--exclude "/var/lib/metche" \
|
|
--exclude "/var/run" \
|
|
--exclude "/var/lock" \
|
|
--exclude "/var/state" \
|
|
--exclude "/var/apt" \
|
|
--exclude "/var/cache" \
|
|
--exclude "/usr/src" \
|
|
--exclude "/usr/doc" \
|
|
--exclude "/usr/share/doc" \
|
|
--exclude "/usr/obj" \
|
|
--exclude "dev" \
|
|
--exclude "/var/spool/postfix" \
|
|
--exclude "/var/lib/amavis/amavisd.sock" \
|
|
--exclude "/var/lib/munin/*tmp*" \
|
|
--exclude "/var/lib/php5" \
|
|
--exclude "/var/spool/squid" \
|
|
--exclude "/var/lib/elasticsearch" \
|
|
--exclude "/var/lib/amavis/tmp" \
|
|
--exclude "/var/lib/clamav/*.tmp" \
|
|
--exclude "/home/mysqltmp" \
|
|
--exclude "/var/lib/php/sessions" \
|
|
${rep} \
|
|
/etc \
|
|
/root \
|
|
/var \
|
|
-e "${RSH_COMMAND}" \
|
|
"root@${SSH_SERVER}:/var/backup/" \
|
|
| tail -30 >> $LOGFILE
|
|
fi
|
|
|
|
##### REPORTING #######################################################
|
|
|
|
END=$(/bin/date +"%d-%m-%Y ; %H:%M")
|
|
|
|
printf "EvoBackup - %s - START %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
|
|
"${HOSTNAME}" "${BEGINNING}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
|
|
>> $LOGFILE
|
|
|
|
printf "EvoBackup - %s - STOP %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
|
|
"${HOSTNAME}" "${END}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
|
|
>> $LOGFILE
|
|
|
|
tail -10 $LOGFILE | \
|
|
mail -s "[info] EvoBackup - Client ${HOSTNAME}" \
|
|
${MAIL}
|