diff --git a/CHANGELOG.md b/CHANGELOG.md index fd2d7b4..d69aab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security +## [2.3.2] - 2020-05-03 + +### Changed + +* Display help message if mandatory arguments are missing. +* Don't recreate jail on sync if it already exists +* Don't sync the whole firewall file, just remake rules for the current jail +* On sync, if local jail is running, reload remote jail if already running, start if not + ## [2.3.1] - 2020-04-22 ### Added diff --git a/bkctld b/bkctld index 080bd29..21ee88d 100755 --- a/bkctld +++ b/bkctld @@ -52,17 +52,30 @@ case "${subcommand}" in ;; "init" | "is-on") jail_name="${2:-}" - "${LIBDIR}/bkctld-${subcommand}" "${jail_name}" + if [ -z "${jail_name}" ]; then + "${LIBDIR}/bkctld-help" + exit 1 + else + "${LIBDIR}/bkctld-${subcommand}" "${jail_name}" + fi ;; "key" | "port" | "ip") jail_name="${2:-}" option="${3:-}" - "${LIBDIR}/bkctld-${subcommand}" "${jail_name}" "${option}" + if [ "${jail_name}" = "all" ] || [ -z "${jail_name}" ]; then + "${LIBDIR}/bkctld-help" + exit 1 + else + "${LIBDIR}/bkctld-${subcommand}" "${jail_name}" "${option}" + fi ;; "start" | "stop" | "reload" | "restart" | "sync" | "update" | "remove" | "firewall") jail_name="${2:-}" if [ "${jail_name}" = "all" ]; then "${LIBDIR}/bkctld-list" | xargs --no-run-if-empty --max-args=1 --max-procs=0 "${LIBDIR}/bkctld-${subcommand}" + elif [ -z "${jail_name}" ]; then + "${LIBDIR}/bkctld-help" + exit 1 else "${LIBDIR}/bkctld-${subcommand}" "${jail_name}" fi diff --git a/lib/bkctld-ip b/lib/bkctld-ip index cefcedb..cb0e60c 100755 --- a/lib/bkctld-ip +++ b/lib/bkctld-ip @@ -15,7 +15,7 @@ if [ ! -n "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 jail_sshd_config="${jail_path}/${SSHD_CONFIG}" diff --git a/lib/bkctld-is-on b/lib/bkctld-is-on index 7722f13..9a96b05 100755 --- a/lib/bkctld-is-on +++ b/lib/bkctld-is-on @@ -14,16 +14,17 @@ if [ -z "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 jail_pid_file="${jail_path}/${SSHD_PID}" -return=1 +# Error codes are references in "includes" file +return=100 if [ -f "${jail_pid_file}" ]; then pid=$(cat "${jail_pid_file}") ps -p "${pid}" > /dev/null && return=0 fi -if [ "${return}" -eq 1 ]; then +if [ "${return}" -gt 0 ]; then rm -f "${jail_pid_file}" grep -q "${jail_path}/proc" /proc/mounts && umount --lazy "${jail_path}/proc/" grep -q "${jail_path}/dev" /proc/mounts && umount --lazy --recursive "${jail_path}/dev" diff --git a/lib/bkctld-key b/lib/bkctld-key index e0b8c94..8384bf7 100755 --- a/lib/bkctld-key +++ b/lib/bkctld-key @@ -15,7 +15,7 @@ if [ ! -n "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 if [ -z "${keyfile}" ]; then if [ -f "${jail_path}/${AUTHORIZED_KEYS}" ]; then diff --git a/lib/bkctld-port b/lib/bkctld-port index 8ed125c..e2bcf66 100755 --- a/lib/bkctld-port +++ b/lib/bkctld-port @@ -15,7 +15,7 @@ if [ ! -n "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 jail_sshd_config="${jail_path}/${SSHD_CONFIG}" diff --git a/lib/bkctld-reload b/lib/bkctld-reload index 711405a..310b23c 100755 --- a/lib/bkctld-reload +++ b/lib/bkctld-reload @@ -13,7 +13,7 @@ if [ -z "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" || exit 0 diff --git a/lib/bkctld-remove b/lib/bkctld-remove index 0580e16..6303ac8 100755 --- a/lib/bkctld-remove +++ b/lib/bkctld-remove @@ -14,7 +14,7 @@ fi jail_path=$(jail_path "${jail_name}") incs_path=$(incs_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" && "${LIBDIR}/bkctld-stop" "${jail_name}" diff --git a/lib/bkctld-restart b/lib/bkctld-restart index e9e3dae..f3f2e87 100755 --- a/lib/bkctld-restart +++ b/lib/bkctld-restart @@ -15,7 +15,7 @@ if [ -z "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" && "${LIBDIR}/bkctld-stop" "${jail_name}" "${LIBDIR}/bkctld-start" "${jail_name}" diff --git a/lib/bkctld-rm b/lib/bkctld-rm index 67616f4..59035da 100755 --- a/lib/bkctld-rm +++ b/lib/bkctld-rm @@ -26,7 +26,7 @@ kill_or_clean_lockfile() { warning "Process not found at PID \`${pid}'. Ignoring lock file \`${lock_file}'." fi else - error "Empty lockfile \`${lock_file}'. It should contain a PID." + warning "Empty lockfile \`${lock_file}'. It should contain a PID." fi # Remove the lock file rm -f ${lock_file} diff --git a/lib/bkctld-start b/lib/bkctld-start index 81c1f92..e25ff0b 100755 --- a/lib/bkctld-start +++ b/lib/bkctld-start @@ -13,7 +13,7 @@ if [ -z "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" && exit 0 diff --git a/lib/bkctld-stats b/lib/bkctld-stats index f6734eb..c619b2d 100755 --- a/lib/bkctld-stats +++ b/lib/bkctld-stats @@ -16,7 +16,7 @@ touch "${INDEX_DIR}/.lastrun.duc" EOF [ ! -f "${INDEX_DIR}/.lastrun.duc" ] && notice "First run of DUC always in progress ..." && exit 0 -[ ! -f ${IDX_FILE} ] && error "Index file do not exits !" +[ ! -f ${IDX_FILE} ] && error "Index file doesn't exits !" printf "Last update of index file : " stat --format=%Y "${INDEX_DIR}/.lastrun.duc" | xargs -i -n1 date -R -d "@{}" diff --git a/lib/bkctld-stop b/lib/bkctld-stop index 7f02394..50438c5 100755 --- a/lib/bkctld-stop +++ b/lib/bkctld-stop @@ -13,7 +13,7 @@ if [ -z "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" || exit 0 diff --git a/lib/bkctld-sync b/lib/bkctld-sync index 90250f9..c228697 100755 --- a/lib/bkctld-sync +++ b/lib/bkctld-sync @@ -14,28 +14,54 @@ fi jail_path=$(jail_path "${jail_name}") jail_config_dir=$(jail_config_dir "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 [ -n "${NODE}" ] || error "Sync need config of \$NODE in /etc/default/bkctld !" -# Init jail on remote server -ssh "${NODE}" "${LIBDIR}/bkctld-init" "${jail_name}" | debug +ssh "${NODE}" "${LIBDIR}/bkctld-is-on ${jail_name} 2>/dev/null" +# return code 2 is for "missing jail" error +if [ "$?" -eq 2 ]; then + # Init jail on remote server + ssh "${NODE}" "${LIBDIR}/bkctld-init ${jail_name}" | debug +fi -# Sync Jail structure and configuration on remote server +# Sync jail structure and configuration on remote server rsync -a "${jail_path}/" "${NODE}:${jail_path}/" --exclude proc/* --exclude sys/* --exclude dev/* --exclude run --exclude var/backup/* -# New config directory -rsync -a "${jail_config_dir}" "${NODE}:${jail_config_dir}" -# Old incs policy config file -rsync -a "${CONFDIR}/${jail_name}" "${NODE}:${CONFDIR}/${jail_name}" - -# Sync state on remote server -if "${LIBDIR}/bkctld-is-on" "${jail_name}"; then - ssh "${NODE}" "${LIBDIR}/bkctld-start" "${jail_name}" | debug +# Sync config (new structure) +if [ -d "${jail_config_dir}" ]; then + rsync -a --delete "${jail_config_dir}" "${NODE}:${jail_config_dir}" else - ssh "${NODE}" "${LIBDIR}/bkctld-stop" "${jail_name}" | debug + ssh "${NODE}" "rm -rf ${jail_config_dir}" | debug +fi +# Sync config (legacy structure) +if [ -e "${CONFDIR}/${jail_name}" ]; then + rsync -a "${CONFDIR}/${jail_name}" "${NODE}:${CONFDIR}/${jail_name}" +else + ssh "${NODE}" "rm -f ${CONFDIR}/${jail_name}" | debug fi if [ -n "${FIREWALL_RULES}" ]; then - rsync -a "${FIREWALL_RULES}" "${NODE}:${FIREWALL_RULES}" - ssh "${NODE}" /etc/init.d/minifirewall restart | debug + ssh "${NODE}" "${LIBDIR}/bkctld-firewall ${jail_name}" | debug + ssh "${NODE}" "test -x /etc/init.d/minifirewall && /etc/init.d/minifirewall restart" | debug +fi + +# Sync state on remote server +if "${LIBDIR}/bkctld-is-on" "${jail_name}"; then + # fetch state of remote jail + ssh "${NODE}" "${LIBDIR}/bkctld-is-on ${jail_name} 2>/dev/null" + case "$?" in + 0) + # jail is already running : reload it + ssh "${NODE}" "${LIBDIR}/bkctld-reload ${jail_name}" | debug + ;; + 100) + # jail is stopped : start it + ssh "${NODE}" "${LIBDIR}/bkctld-start ${jail_name}" | debug + ;; + *) + error "Error evaluating jail \`${jail_name}' state. bkctld-is-on exited with \`$?'" + ;; + esac +else + ssh "${NODE}" "${LIBDIR}/bkctld-stop ${jail_name}" | debug fi diff --git a/lib/bkctld-update b/lib/bkctld-update index ba15687..c5b4ec2 100755 --- a/lib/bkctld-update +++ b/lib/bkctld-update @@ -13,7 +13,7 @@ if [ ! -n "${jail_name}" ]; then fi jail_path=$(jail_path "${jail_name}") -test -d "${jail_path}" || error "${jail_name}: jail not found" +test -d "${jail_path}" || error "${jail_name}: jail not found" 2 "${LIBDIR}/bkctld-is-on" "${jail_name}" && "${LIBDIR}/bkctld-stop" "${jail_name}" diff --git a/lib/includes b/lib/includes index 805df0b..6ba6fec 100755 --- a/lib/includes +++ b/lib/includes @@ -59,15 +59,19 @@ warning() { logger -t bkctld -p daemon.warning "$(process_name) ${msg}" fi } - +# Return codes +# 1 : generic error +# 2 : jail not found +# > 100 : subcommands specific errors error() { msg="${1:-$(cat /dev/stdin)}" + rc="${2:-1}" tty -s && echo "$(log_date) ERROR $(process_name) ${msg}" >&2 if [ "${LOGLEVEL}" -ge 5 ]; then tty -s || echo "$(log_date) ERROR $(process_name) ${msg}" >&2 logger -t bkctld -p daemon.error "$(process_name) ${msg}" fi - exit 1 + exit ${rc} } dry_run() { diff --git a/test/test_helper.bash b/test/test_helper.bash index df8f644..92de516 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -9,11 +9,11 @@ setup() { set_variable "/etc/default/bkctld" "BACKUP_DISK" "/dev/vdb" - JAILNAME=$(tr -cd '[:alnum:]' < /dev/urandom | fold -w15 | head -n1) + JAILNAME=$(random_jail_name) JAILPATH="/backup/jails/${JAILNAME}" INCSPATH="/backup/incs/${JAILNAME}" - PORT=$(awk -v min=2222 -v max=2999 'BEGIN{srand(); print int(min+rand()*(max-min+1))}') - INC_NAME=$(date +"%Y-%m-%d-%H") + PORT=$(random_port) + INC_NAME=$(inc_name_today) /usr/lib/bkctld/bkctld-init "${JAILNAME}" } @@ -23,6 +23,16 @@ teardown() { /usr/lib/bkctld/bkctld-remove "${JAILNAME}" && rm -rf "${INCSPATH}" } +random_jail_name() { + tr -cd '[:alnum:]' < /dev/urandom | fold -w15 | head -n1 +} +random_port() { + awk -v min=2222 -v max=2999 'BEGIN{srand(); print int(min+rand()*(max-min+1))}' +} +inc_name_today() { + date +"%Y-%m-%d-%H" +} + set_variable() { file=${1:?} var_name=${2:?} diff --git a/zzz_evobackup b/zzz_evobackup index f267171..e722d44 100755 --- a/zzz_evobackup +++ b/zzz_evobackup @@ -83,7 +83,7 @@ test_server() { 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') + SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d') return 1 fi @@ -96,16 +96,16 @@ pick_server() { 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') + 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 + printf "%s\\n" "${SERVERS_SSH_ERRORS}" >&2 # Log errors to logfile - printf "%s\n" "${SERVERS_SSH_ERRORS}" >> $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) + # 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). @@ -123,14 +123,14 @@ pick_server() { if [ -e "${PIDFILE}" ]; then pid=$(cat "${PIDFILE}") # Does process still exist ? - if kill -0 ${pid} 2> /dev/null; then + 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 + printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2 else rm -f ${PIDFILE} fi @@ -299,7 +299,7 @@ if [ "${LOCAL_TASKS}" = "1" ]; then ## Dump findmnt(8) output FINDMNT_BIN=$(command -v findmnt) - if [ -x ${FINDMNT_BIN} ]; then + if [ -x "${FINDMNT_BIN}" ]; then ${FINDMNT_BIN} > ${LOCAL_BACKUP_DIR}/findmnt.txt fi else @@ -366,6 +366,8 @@ if [ "${SYNC_TASKS}" = "1" ]; then # Remote shell command RSH_COMMAND="ssh -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.*" \ @@ -410,11 +412,11 @@ fi END=$(/bin/date +"%d-%m-%Y ; %H:%M") -printf "EvoBackup - %s - START %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\n" \ +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" \ +printf "EvoBackup - %s - STOP %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \ "${HOSTNAME}" "${END}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \ >> $LOGFILE