evobackup/lib/includes

452 lines
13 KiB
Plaintext
Raw Normal View History

2019-01-04 13:51:05 +01:00
#!/bin/sh
2019-01-07 16:57:12 +01:00
#
# Config for bkctld
#
# shellcheck disable=SC2034
2019-01-04 13:51:05 +01:00
[ -f /etc/default/bkctld ] && . /etc/default/bkctld
2020-11-13 15:49:43 +01:00
VERSION="2.7.0"
2019-01-04 13:51:05 +01:00
LIBDIR=${LIBDIR:-/usr/lib/bkctld}
CONFDIR="${CONFDIR:-/etc/evobackup}"
BACKUP_DISK="${BACKUP_DISK:-}"
BACKUP_PARTITION="${BACKUP_PARTITION:-/backup}"
JAILDIR="${JAILDIR:-${BACKUP_PARTITION}/jails}"
INCDIR="${INCDIR:-${BACKUP_PARTITION}/incs}"
2019-01-04 13:51:05 +01:00
TPLDIR="${TPLDIR:-/usr/share/bkctld}"
2020-04-02 00:30:48 +02:00
LOCALTPLDIR="${LOCALTPLDIR:-/usr/local/share/bkctld}"
LOCKDIR="${LOCKDIR:-/run/lock/bkctld}"
INDEX_DIR="${INDEX_DIR:-${BACKUP_PARTITION}/index}"
2019-01-04 13:51:05 +01:00
IDX_FILE="${IDX_FILE:-${INDEX_DIR}/bkctld-jails.idx}"
SSHD_PID="${SSHD_PID:-/run/sshd.pid}"
SSHD_CONFIG="${SSHD_CONFIG:-/etc/ssh/sshd_config}"
AUTHORIZED_KEYS="${AUTHORIZED_KEYS:-/root/.ssh/authorized_keys}"
FIREWALL_RULES="${FIREWALL_RULES:-}"
LOGLEVEL="${LOGLEVEL:-6}"
CRITICAL="${CRITICAL:-48}"
WARNING="${WARNING:-24}"
2020-04-05 11:43:52 +02:00
DUC=$(command -v duc-nox || command -v duc)
FORCE="${FORCE:-0}"
2019-01-04 13:51:05 +01:00
show_version() {
cat <<END
bkctld version ${VERSION}
Copyright 2004-2020 Evolix <info@evolix.fr>,
Victor Laborie <vlaborie@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>
and others.
bkctld comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU Affero General Public License v3.0 for details.
END
}
show_help() {
cat <<EOF
Usage: bkctld [options] <subcommand> [arguments]
Options
-h|--help|-? Display help
-V|--version Display version, authors and license
Subcommands:
EOF
for filename in ${LIBDIR}/bkctld-*; do
desc=$(grep -E "^# Description:" "${filename}"|sed "s/^# Description: //")
usage=$(grep -E "^# Usage:" "${filename}"|sed "s/^# Usage: //")
printf " %- 32s %s\n" "${usage}" "${desc}"
done
printf "\n"
}
2020-04-20 08:29:21 +02:00
log_date() {
2020-04-20 23:43:45 +02:00
echo "[$(date +"%Y-%m-%d %H:%M:%S")]"
2020-04-20 08:29:21 +02:00
}
process_name() {
basename $0
}
2019-01-07 16:57:12 +01:00
debug() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 7 ]; then
2020-04-20 08:29:21 +02:00
echo "$(log_date) DEBUG $(process_name) ${msg}"
logger -t bkctld -p daemon.debug "$(process_name) ${msg}"
2019-01-07 16:57:12 +01:00
fi
}
info() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 6 ]; then
2020-04-20 23:43:56 +02:00
tty -s && echo "$(log_date) INFO $(process_name) ${msg}"
2020-04-20 08:29:21 +02:00
logger -t bkctld -p daemon.info "$(process_name) ${msg}"
2019-01-07 16:57:12 +01:00
fi
}
notice() {
msg="${1:-$(cat /dev/stdin)}"
2020-04-20 08:29:21 +02:00
tty -s && echo "$(log_date) NOTICE $(process_name) ${msg}"
[ "${LOGLEVEL}" -ge 5 ] && logger -t bkctld -p daemon.notice "$(process_name) ${msg}"
2019-01-07 16:57:12 +01:00
}
warning() {
msg="${1:-$(cat /dev/stdin)}"
2020-04-20 08:29:21 +02:00
tty -s && echo "$(log_date) WARNING $(process_name) ${msg}" >&2
2019-01-07 16:57:12 +01:00
if [ "${LOGLEVEL}" -ge 4 ]; then
2020-04-20 08:29:21 +02:00
tty -s || echo "$(log_date) WARNING $(process_name) ${msg}" >&2
logger -t bkctld -p daemon.warning "$(process_name) ${msg}"
2019-01-07 16:57:12 +01:00
fi
}
# Return codes
# 1 : generic error
# 2 : jail not found
# > 100 : subcommands specific errors
2019-01-07 16:57:12 +01:00
error() {
msg="${1:-$(cat /dev/stdin)}"
rc="${2:-1}"
2020-04-20 08:29:21 +02:00
tty -s && echo "$(log_date) ERROR $(process_name) ${msg}" >&2
2019-01-07 16:57:12 +01:00
if [ "${LOGLEVEL}" -ge 5 ]; then
2020-04-20 08:29:21 +02:00
tty -s || echo "$(log_date) ERROR $(process_name) ${msg}" >&2
logger -t bkctld -p daemon.error "$(process_name) ${msg}"
2019-01-07 16:57:12 +01:00
fi
exit ${rc}
2019-01-07 16:57:12 +01:00
}
dry_run() {
test "$DRY_RUN" = "1"
}
current_time() {
date +"%H:%M:%S"
}
# Returns true if the given path is on a btrfs filesystem
is_btrfs() {
path=$1
inode=$(stat --format=%i "${path}")
test $inode -eq 256
}
# Returns the list of jails found in the "jails" directory (default)
jails_list() {
# TODO: try if this command works the same :
# find "${JAILDIR}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n'
find "${JAILDIR}" -mindepth 1 -maxdepth 1 -type d | sed 's!.*/!!' | sort -h
}
# Returns the list of jails found in the "incs" directory
jails_with_incs_list() {
find "${INCDIR}" -mindepth 1 -maxdepth 1 -type d | sed 's!.*/!!' | sort -h
}
# Returns the complete path of a jail
jail_path() {
jail_name=${1:?}
echo "${JAILDIR}/${jail_name}"
}
jail_config_dir() {
jail_name=${1:?}
echo "${CONFDIR}/${jail_name}.d"
}
jail_incs_policy_file() {
jail_name=${1:?}
jail_config_dir=$(jail_config_dir "${jail_name}")
echo "${jail_config_dir}/incs_policy"
}
# Returns the path of incs for a jail
incs_path() {
jail_name=${1:?}
echo "${INCDIR}/${jail_name}"
}
# Returns the path of a specific inc for a jail
inc_path() {
jail_name=${1:?}
inc_name=${2:?}
echo "${INCDIR}/${jail_name}/${inc_name}"
}
# Test the existence of an inc pattern for a jail
inc_exists() {
jail_name=${1-?}
inc_pattern=${2-?}
inc_path=$(inc_path "${jail_name}" "${inc_pattern}")
# inc_path must not be quoted because it can contain globs
ls -d ${inc_path} > /dev/null 2>&1
}
# Returns the list of all incs for a jail
incs_list() {
jail_name=${1:?}
find "$(incs_path "${jail_name}")" -mindepth 1 -maxdepth 1 -type d | sed 's!.*/!!' | sort -h
2020-04-02 18:26:53 +02:00
}
# Return the list of empty incs directories
empty_incs_list() {
find ${INCDIR} -mindepth 1 -maxdepth 1 -type d -empty
}
2020-04-02 18:26:53 +02:00
current_jail_incs_policy_file() {
jail_name=${1:?}
new_file="$(jail_incs_policy_file "${jail_name}")"
old_file="${CONFDIR}/${jail_name}"
if [ -f "${new_file}" ]; then
echo "${new_file}"
elif [ -f "${old_file}" ]; then
echo "${old_file}"
else
echo ""
fi
}
jail_check_policy_file() {
jail_name=${1:?}
jail_config_dir=$(jail_config_dir "${jail_name}")
echo "${jail_config_dir}/check_policy"
2020-04-02 18:26:53 +02:00
}
current_jail_check_policy_file() {
jail_name=${1:?}
new_file="$(jail_check_policy_file "${jail_name}")"
# old_file="${JAILDIR}/${jail_name}/etc/bkctld-check"
if [ -f "${new_file}" ]; then
echo "${new_file}"
# elif [ -f "${old_file}" ]; then
# echo "${old_file}"
else
echo ""
fi
}
# relative_date "+%Y-%m-%d.-2day"
relative_date() {
format=$(echo $1 | cut -d'.' -f1)
time_jump=$(echo $1 | cut -d'.' -f2)
reference_date=$(date "${format}")
past_date=$(date --date "${reference_date} ${time_jump}" +"%Y-%m-%d")
echo ${past_date}
}
new_tmp_file() {
name=${1:-}
mktemp --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
}
new_tmp_dir() {
name=${1:-}
mktemp --directory --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
}
cleanup_tmp() {
find /tmp -name "bkctld.${$}.*" -delete
}
new_lock_file() {
lock_file=${1:-}
lock_dir=$(dirname "${lock_file}")
mkdir --parents "${lock_dir}" && echo $$ > ${lock_file} || error "Failed to acquire lock file '${lock_file}'"
}
2020-04-02 00:30:48 +02:00
pkg_version() {
# $(command -v ssh) -V 2>&1 | grep -iEo 'OpenSSH_(\S+)' | cut -d '_' -f2
dpkg-query -W -f='${Version}\n' $1 \
| sed 's/[~+-].\+//' \
| sed 's/.\+://' \
| sed 's/p.*//' \
| cut -d. -f1,2
}
ssh_keygen_with_prefix() {
# openssh-client 7.9 provides ssh-keygen with "-f prefix_path" option
dpkg --compare-versions "$(pkg_version 'openssh-client')" ge "7.9"
}
2020-04-02 00:30:48 +02:00
setup_jail_chroot() {
jail_name=${1:?}
2020-04-02 00:30:48 +02:00
jail_path=$(jail_path "${jail_name}")
passwd="${TPLDIR}/passwd"
shadow="${TPLDIR}/shadow"
group="${TPLDIR}/group"
sshrc="${TPLDIR}/sshrc"
[ -f "${LOCALTPLDIR}/passwd" ] && passwd="${LOCALTPLDIR}/passwd"
[ -f "${LOCALTPLDIR}/shadow" ] && shadow="${LOCALTPLDIR}/shadow"
[ -f "${LOCALTPLDIR}/group" ] && group="${LOCALTPLDIR}/group"
[ -f "${LOCALTPLDIR}/sshrc" ] && group="${LOCALTPLDIR}/sshrc"
2020-04-02 01:07:12 +02:00
cd "${jail_path}" || error "${jail_name}: failed to change directory to ${jail_path}."
2020-04-02 00:30:48 +02:00
umask 077
info "1 - Creating the chroot"
rm -rf ./bin
rm -rf ./lib
rm -rf ./lib64
rm -rf ./run
rm -rf ./usr
rm -rf ./var/run
# Let's not delete the existing SSH host keys,
# otherwise the clients will have to accept the new keys
2020-04-02 00:30:48 +02:00
mkdir -p ./dev
mkdir -p ./proc
mkdir -p ./usr/bin
mkdir -p ./usr/sbin
2020-04-03 09:58:10 +02:00
mkdir -p ./usr/lib
mkdir -p ./usr/lib/x86_64-linux-gnu
mkdir -p ./usr/lib/openssh
mkdir -p ./usr/lib64
2020-04-02 00:30:48 +02:00
mkdir -p ./etc/ssh
mkdir -p ./var/log
mkdir -p ./run/sshd
# shellcheck disable=SC2174
mkdir -p ./root/.ssh --mode 0700
# shellcheck disable=SC2174
mkdir -p ./var/backup --mode 0700
2020-08-19 13:58:23 +02:00
2020-04-02 00:30:48 +02:00
ln -s ./usr/bin ./bin
ln -s ./usr/lib ./lib
ln -s ./usr/lib64 ./lib64
ln -s --target-directory=./var ../run
2020-08-19 13:58:23 +02:00
2020-04-02 00:30:48 +02:00
touch ./var/log/lastlog ./var/log/wtmp ./run/utmp
info "2 - Copying essential files"
2020-08-19 13:58:23 +02:00
#
if ssh_keygen_with_prefix; then
# Generate SSH host keys if missing in jail
ssh-keygen -A -f "${jail_path}"
else
# Copy SSH host keys from host if missing in jail
for key in /etc/ssh/*_key; do
cp --no-clobber ${key} ${jail_path}${key};
done
fi
2020-08-19 13:58:23 +02:00
2020-04-02 00:30:48 +02:00
touch "./${AUTHORIZED_KEYS}"
chmod 600 "./${AUTHORIZED_KEYS}"
2020-08-19 13:58:23 +02:00
2020-04-02 00:30:48 +02:00
cp "${passwd}" ./etc
cp "${shadow}" ./etc
cp "${group}" ./etc
cp "${sshrc}" ./etc/ssh
info "3 - Copying binaries"
cp -f /lib/ld-linux.so.2 ./lib 2>/dev/null || cp -f /lib64/ld-linux-x86-64.so.2 ./lib64
cp /lib/x86_64-linux-gnu/libnss* ./lib/x86_64-linux-gnu
2020-08-19 13:58:23 +02:00
for dbin in \
/bin/sh \
/bin/ls \
/bin/mkdir \
/bin/cat \
/bin/rm \
/bin/sed \
/usr/bin/rsync \
/usr/bin/lastlog \
/usr/bin/touch \
/usr/sbin/sshd \
/usr/lib/openssh/sftp-server\
; do
2020-04-02 00:30:48 +02:00
cp -f "${dbin}" "./${dbin}";
for lib in $(ldd "${dbin}" | grep -Eo "/.*so.[0-9\.]+"); do
cp -p "${lib}" "./${lib}"
done
done
}
setup_jail_config() {
jail_name=${1:?}
2020-04-02 00:30:48 +02:00
jail_path=$(jail_path "${jail_name}")
2020-04-08 00:32:15 +02:00
jail_sshd_config="${jail_path}/${SSHD_CONFIG}"
2020-04-02 00:30:48 +02:00
2020-04-08 00:32:15 +02:00
sshd_config_tpl="${TPLDIR}/sshd_config"
test -f "${LOCALTPLDIR}/sshd_config" && sshd_config_tpl="${LOCALTPLDIR}/sshd_config"
2020-04-02 00:30:48 +02:00
info "4 - Copie default sshd_config"
2020-04-08 00:32:15 +02:00
install -m 0640 "${sshd_config_tpl}" "${jail_sshd_config}"
2020-04-02 00:30:48 +02:00
info "5 - Copie default inc configuration"
2021-02-22 10:15:53 +01:00
incs_policy_tpl="${TPLDIR}/incs_policy.tpl"
test -f "${LOCALTPLDIR}/incs_policy.tpl" && incs_policy_tpl="${LOCALTPLDIR}/incs_policy.tpl"
2020-04-02 18:28:41 +02:00
jail_incs_policy_file=$(jail_incs_policy_file "${jail_name}")
mkdir --parents "$(dirname "${jail_incs_policy_file}")"
2021-02-22 10:15:53 +01:00
install -m 0640 "${incs_policy_tpl}" "${jail_incs_policy_file}"
check_policy_tpl="${TPLDIR}/check_policy.tpl"
test -f "${LOCALTPLDIR}/check_policy.tpl" && check_policy_tpl="${LOCALTPLDIR}/check_policy.tpl"
jail_check_policy_file=$(jail_check_policy_file "${jail_name}")
mkdir --parents "$(dirname "${jail_check_policy_file}")"
install -m 0640 "${check_policy_tpl}" "${jail_check_policy_file}"
2020-04-02 00:30:48 +02:00
"${LIBDIR}/bkctld-port" "${jail_name}" auto
}
is_mounted_inside_jail() {
target=${1:?}
# TODO: try to find why it doesn't work with this findmnt(8) command
# findmnt --target "${target}" --tab-file /proc/mounts
grep -q "${target}" /proc/mounts
}
mount_jail_fs() {
jail_name=${1:?}
jail_path=$(jail_path "${jail_name}")
is_mounted_inside_jail "${jail_path}/dev" || mount -nt tmpfs "dev-${jail_name}" "${jail_path}/dev"
[ -e "dev/console" ] || mknod -m 622 "${jail_path}/dev/console" c 5 1
chown root:tty "${jail_path}/dev/console"
[ -e "dev/null" ] || mknod -m 666 "${jail_path}/dev/null" c 1 3
[ -e "dev/zero" ] || mknod -m 666 "${jail_path}/dev/zero" c 1 5
[ -e "dev/ptmx" ] || mknod -m 666 "${jail_path}/dev/ptmx" c 5 2
chown root:tty "${jail_path}/dev/ptmx"
[ -e "dev/tty" ] || mknod -m 666 "${jail_path}/dev/tty" c 5 0
chown root:tty "${jail_path}/dev/tty"
[ -e "dev/random" ] || mknod -m 444 "${jail_path}/dev/random" c 1 8
[ -e "dev/urandom" ] || mknod -m 444 "${jail_path}/dev/urandom" c 1 9
mkdir -p "${jail_path}/dev/pts"
is_mounted_inside_jail "${jail_path}/dev/pts" || mount -t devpts -o gid=4,mode=620 none "${jail_path}/dev/pts"
mkdir -p "${jail_path}/dev/shm"
is_mounted_inside_jail "${jail_path}/dev/shm" || mount -t tmpfs none "${jail_path}/dev/shm"
is_mounted_inside_jail "${jail_path}/proc" || mount -t proc "proc-${jail_name}" "${jail_path}/proc"
ln -fs "${jail_path}/proc/self/fd" "${jail_path}/dev/fd"
ln -fs "${jail_path}/proc/self/fd/0" "${jail_path}/dev/stdin"
ln -fs "${jail_path}/proc/self/fd/1" "${jail_path}/dev/stdout"
ln -fs "${jail_path}/proc/self/fd/2" "${jail_path}/dev/stderr"
ln -fs "${jail_path}/proc/kcore" "${jail_path}/dev/core"
}
read_variable() {
file=${1:?}
var_name=${2:?}
pattern="^\s*${var_name}=.+"
grep --extended-regexp --only-matching "${pattern}" "${file}" | cut -d= -f2
}
read_numerical_variable() {
file=${1:?}
var_name=${2:?}
pattern="^\s*${var_name}=-?[0-9]+"
grep --extended-regexp --only-matching "${pattern}" "${file}" | cut -d= -f2
}