#!/bin/sh # # Config for bkctld # [ -f /etc/default/bkctld ] && . /etc/default/bkctld 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}" TPLDIR="${TPLDIR:-/usr/share/bkctld}" LOCALTPLDIR="${LOCALTPLDIR:-/usr/local/share/bkctld}" LOCKDIR="${LOCKDIR:-/run/lock/bkctld}" INDEX_DIR="${INDEX_DIR:-${BACKUP_PARTITION}/index}" 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}" DUC=$(command -v duc-nox || command -v duc) log_date() { echo "[$(date +"%Y-%m-%d %H:%M:%S")]" } process_name() { basename $0 } debug() { msg="${1:-$(cat /dev/stdin)}" if [ "${LOGLEVEL}" -ge 7 ]; then echo "$(log_date) DEBUG $(process_name) ${msg}" logger -t bkctld -p daemon.debug "$(process_name) ${msg}" fi } info() { msg="${1:-$(cat /dev/stdin)}" if [ "${LOGLEVEL}" -ge 6 ]; then tty -s && echo "$(log_date) INFO $(process_name) ${msg}" logger -t bkctld -p daemon.info "$(process_name) ${msg}" fi } notice() { msg="${1:-$(cat /dev/stdin)}" tty -s && echo "$(log_date) NOTICE $(process_name) ${msg}" [ "${LOGLEVEL}" -ge 5 ] && logger -t bkctld -p daemon.notice "$(process_name) ${msg}" } warning() { msg="${1:-$(cat /dev/stdin)}" tty -s && echo "$(log_date) WARNING $(process_name) ${msg}" >&2 if [ "${LOGLEVEL}" -ge 4 ]; then tty -s || echo "$(log_date) WARNING $(process_name) ${msg}" >&2 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 ${rc} } 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 all jails jails_list() { # shellcheck disable=SC2091 "${LIBDIR}/bkctld-list" } # Returns the list of all incs for a jail incs_list() { jail_name=${1:?} # shellcheck disable=SC2091 ls "$(incs_path "${jail_name}")/" } # Returns the complete path of a jail jail_path() { jail_name=${1:?} echo "${JAILDIR}/${jail_name}" } # 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 } 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" } 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" } 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}'" } setup_jail_chroot() { jail_name=${1:?} 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" cd "${jail_path}" || error "${jail_name}: failed to change directory to ${jail_path}." umask 077 info "1 - Creating the chroot" rm -rf ./bin ./lib ./lib64 ./run ./usr ./var/run ./etc/ssh/*key mkdir -p ./dev mkdir -p ./proc mkdir -p ./usr/bin mkdir -p ./usr/sbin mkdir -p ./usr/lib mkdir -p ./usr/lib/x86_64-linux-gnu mkdir -p ./usr/lib/openssh mkdir -p ./usr/lib64 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 ln -s ./usr/bin ./bin ln -s ./usr/lib ./lib ln -s ./usr/lib64 ./lib64 ln -s --target-directory=./var ../run touch ./var/log/lastlog ./var/log/wtmp ./run/utmp info "2 - Copying essential files" [ -f /etc/ssh/ssh_host_rsa_key ] && cp /etc/ssh/ssh_host_rsa_key ./etc/ssh [ -f /etc/ssh/ssh_host_ecdsa_key ] && cp /etc/ssh/ssh_host_ecdsa_key ./etc/ssh [ -f /etc/ssh/ssh_host_ed25519_key ] && cp /etc/ssh/ssh_host_ed25519_key ./etc/ssh touch "./${AUTHORIZED_KEYS}" chmod 600 "./${AUTHORIZED_KEYS}" 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 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 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:?} jail_path=$(jail_path "${jail_name}") jail_sshd_config="${jail_path}/${SSHD_CONFIG}" sshd_config_tpl="${TPLDIR}/sshd_config" test -f "${LOCALTPLDIR}/sshd_config" && sshd_config_tpl="${LOCALTPLDIR}/sshd_config" info "4 - Copie default sshd_config" install -m 0640 "${sshd_config_tpl}" "${jail_sshd_config}" inctpl="${TPLDIR}/inc.tpl" test -f "${LOCALTPLDIR}/inc.tpl" && inctpl="${LOCALTPLDIR}/inc.tpl" info "5 - Copie default inc configuration" jail_incs_policy_file=$(jail_incs_policy_file "${jail_name}") mkdir --parents "$(dirname "${jail_incs_policy_file}")" install -m 0640 "${inctpl}" "${jail_incs_policy_file}" "${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 }