Merge branch 'master' into debian

This commit is contained in:
Victor LABORIE 2019-02-18 11:36:43 +01:00
commit 064aa55dea
42 changed files with 1555 additions and 1032 deletions

View file

@ -1,15 +1,18 @@
Bkctld (aka evobackup)
=========
Bkctld is a shell script to create and manage a backup server which will
handle the backup of many servers (clients). Licence is AGPLv3.
Bkctld is a shell script that creates and manages a backup server
which can handle the backups of many other servers (clients). It
is licensed under the AGPLv3.
The main principle uses SSH chroot (called "jails" in the FreeBSD
world) for each client to backup. Each client will upload his data every day
using rsync in his chroot (using root account).
Incrementals are stored outside of the chroot using hard links or btrfs snapshots.
(So incrementals are not available for clients). Using this method we can keep tens
of backup of each client securely and not using too much space.
It uses SSH chroots (called "jails" in the FreeBSD world) to sandbox
every clients backups. Each client will upload it's data every day
using rsync in it's chroot (using the root account). Prior backups
are stored incrementally outside of the chroot using hard links or
BTRFS snapshots. (So they can not be affected by the client).
Using this method, we can keep a large quantity of backups of each
client securely and efficiently.
~~~
Backup server
@ -20,56 +23,23 @@ Server 2 ------ SSH/rsync -------> * tcp/2223 *
************
~~~
This method uses standard tools (ssh, rsync, cp -al, btrfs subvolume). EvoBackup
is used for many years by Evolix for back up each day hundreds of servers which
uses many terabytes of data.
This method uses standard tools (ssh, rsync, cp -al, btrfs subvolume)
and has been used for many years by Evolix to backup hundreds of
servers, totaling many terabytes of data, each day. bkctld has
been tested on Debian Jessie and should be compatible with other
Debian versions or derived distributions like Ubuntu.
bkctld was test on Debian Jessie. It can be compatible with other Debian version
or derivated distribution like Ubuntu or Debian Wheezy.
A big size volume must be mount on /backup, we recommend usage of **btrfs** for
subvolume and snapshot fonctionnality.
This volume can be encrypted by **luks** for security reason.
A large enough volume must be mounted on `/backup`, we recommend
the usage of **BTRFS** so you can use sub-volumes and snapshots.
This volume can also be encrypted with **LUKS**.
## Install
A Debian package is available in Evolix repository
See the [installation guide](docs/install.md) for instructions.
~~~
echo "http://pub.evolix.net/ jessie/" >> /etc/apt/sources.list
apt update
apt install bkctld
~~~
## Testing
### Chroot dependency
Chroot jail use part of this package
~~~
apt install bash coreutils sed dash mount rsync openssh-server openssh-sftp-server libc6-i386 libc6
~~~
#### Install cron for incremental backup
Edit root crontab
~~~
crontab -e
~~~
Add this ligne
~~~
30 10 * * * /usr/sbin/bkctld inc && /usr/sbin/bkctld rm
~~~
> **Notes :**
> If you want mutiples backups in a day (1 by hour maximum) you can run `bkctld inc` multiples times
> If you want keep incremental backup **for ever**, you just need don't run `bkctld rm`
## Test
You can deploy tests environmments with Vagrant :
You can deploy test environments with Vagrant :
~~~
vagrant up
@ -77,7 +47,8 @@ vagrant up
### Deployment
Launch rsync-auto in a terminal for automatic synchronisation of your local code with Vagrant VM :
Launch rsync-auto in a terminal for automatic synchronization of
your local code with Vagrant VM :
~~~
vagrant rsync-auto
@ -85,7 +56,8 @@ vagrant rsync-auto
### Bats
You can run [bats](https://github.com/sstephenson/bats) test with *test* provisionner :
You can run [bats](https://github.com/sstephenson/bats) tests with
the *test* provision :
~~~
vagrant provision --provision-with test
@ -95,21 +67,31 @@ vagrant provision --provision-with test
See [docs/usage.md](docs/usage.md).
Man page, in roff language, can be generated with pandoc :
The man(1) page, in troff(7) language, can be generated with pandoc:
~~~
pandoc -f markdown -t man usage.md --template default.man -V title=bkctld -V section=8 -V date="$(date '+%d %b %Y')" -V footer="$(git describe --tags)" -V header="bkctld man page"
pandoc -f markdown \
-t man usage.md \
--template default.man \
-V title=bkctld \
-V section=8 \
-V date="$(date '+%d %b %Y')" \
-V footer="$(git describe --tags)" \
-V header="bkctld man page"
~~~
#### Client configuration
You can save various systems on evobackup jail : Linux, BSD, Windows, MacOSX. Only prequisites is rsync command.
You can save various systems in the evobackup jails : Linux, BSD,
Windows, MacOSX. The only prerequisite is the rsync command.
~~~
rsync -av -e "ssh -p SSH_PORT" /home/ root@SERVER_NAME:/var/backup/home/
~~~
An example script is present in zzz_evobackup, clone evobackup repo and read **CLIENT CONFIGURATION** section of the manual.
An example synchronization script is present in `zzz_evobackup`,
clone the evobackup repository and read the **CLIENT CONFIGURATION**
section of the manual.
~~~
git clone https://forge.evolix.org/evobackup.git

15
Vagrantfile vendored
View file

@ -9,6 +9,7 @@ load File.expand_path(vagrantfile) if File.exists?(vagrantfile)
Vagrant.configure('2') do |config|
config.vm.synced_folder "./", "/vagrant", type: "rsync", rsync__exclude: [ '.vagrant', '.git' ]
config.ssh.shell="/bin/sh"
config.vm.provider :libvirt do |libvirt|
libvirt.storage :file, :size => '10G', :device => 'vdb'
@ -16,14 +17,16 @@ Vagrant.configure('2') do |config|
$install = <<SCRIPT
ln -fs /vagrant/bkctld /usr/sbin/bkctld
ln -fs /vagrant/lib /usr/lib/bkctld
ln -fs /vagrant/tpl /usr/share/bkctld
ln -fs /vagrant/bash_completion /usr/share/bash-completion/completions/bkctld
ln -fs /vagrant/bkctld.conf /etc/default/bkctld
ln -fs /vagrant/bkctld.service /etc/systemd/system/bkctld.service && systemctl daemon-reload
mkdir -p /usr/lib/nagios/plugins/
SCRIPT
$deps = <<SCRIPT
DEBIAN_FRONTEND=noninteractive apt-get -yq install openssh-server btrfs-tools rsync lsb-base coreutils sed dash mount openssh-sftp-server libc6 bash-completion duc-nox cryptsetup
DEBIAN_FRONTEND=noninteractive apt-get -yq install openssh-server btrfs-tools rsync lsb-base coreutils sed dash mount openssh-sftp-server libc6 bash-completion duc-nox cryptsetup bats
SCRIPT
$pre_part = <<SCRIPT
@ -38,8 +41,6 @@ mount /dev/vdb /backup
SCRIPT
nodes = [
{ :version => "jessie", :fs => "btrfs" },
{ :version => "jessie", :fs => "ext4" },
{ :version => "stretch", :fs => "btrfs" },
{ :version => "stretch", :fs => "ext4" }
]
@ -49,14 +50,6 @@ SCRIPT
node.vm.hostname = "bkctld-#{i[:version]}-#{i[:fs]}"
node.vm.box = "debian/#{i[:version]}64"
config.vm.provision "deps", type: "shell", :inline => $deps
if ("#{i[:version]}" == "jessie") then
config.vm.provision "backports", type: "shell" do |s|
s.inline = "echo 'deb http://deb.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/backports.list && apt-get update"
end
config.vm.provision "bats", type: "shell", :inline => "DEBIAN_FRONTEND=noninteractive apt-get -yq install -t jessie-backports bats"
else
config.vm.provision "bats", type: "shell", :inline => "DEBIAN_FRONTEND=noninteractive apt-get -yq install bats"
end
config.vm.provision "install", type: "shell", :inline => $install
config.vm.provision "pre_part", type: "shell", :inline => $pre_part
config.vm.provision "part", type: "shell", :inline => "mkfs.btrfs -f /dev/vdb" if "#{i[:fs]}" == "btrfs"

View file

@ -9,7 +9,7 @@ function _bkctld()
cur=${COMP_WORDS[COMP_CWORD]};
prev=${COMP_WORDS[COMP_CWORD-1]};
commands="init update remove start stop reload restart sync status key port ip inc rm check stats"
commands=$(find /usr/lib/bkctld/ -name "bkctld-*" -exec basename {} \;|sed 's/^bkctld-//')
if [ $COMP_CWORD -eq 1 ]; then
COMPREPLY=($(compgen -W '${commands}' -- ${cur}))

672
bkctld
View file

@ -10,652 +10,42 @@
set -u
usage(){
cat <<EOF
Usage: $0 <subcommand> [options]
Subcommands:
init <jailname> Init jail <jailname>
update <jailname>|all Update jail <jailname> or all
remove <jailname>|all Remove jail <jailname> or all
start <jailname>|all Start jail <jailname> or all
stop <jailname>|all Stop jail <jailname> or all
reload <jailname>|all Reload jail <jailname> or all
restart <jailname>|all Restart jail <jailname> or all
sync <jailname>|all Sync jail <jailname> or all to another node
status [<jailname>] Print status of <jailname> (default all jail)
key <jailname> [<keyfile>] Set or get ssh pubic key of <jailname>
port <jailname> [<port>|auto] Set or get ssh port of <jailname>
ip <jailname> [<ip>|all] Set or get allowed(s) ip(s) of <jailname>
inc Make incremental inc of all jails
rm Remove old incremtal inc of all jails
check Run check on jails (NRPE output)
stats Make and display stats on jails (size, lastconn)
[ "$(id -u)" -ne 0 ] && error "You need to be root to run ${0} !"
EOF
}
[ -d './lib' ] && LIBDIR='lib'
[ -d '/usr/lib/bkctld' ] && LIBDIR='/usr/lib/bkctld'
. "${LIBDIR}/config"
## logging functions
subcommand="${1:-}"
jail="${2:-}"
option="${3:-}"
debug() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 7 ]; then
echo "${msg}"
logger -t bkctld -p daemon.debug "${msg}"
fi
}
if [ ! -x "${LIBDIR}/bkctld-${subcommand}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
info() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 6 ]; then
tty -s && echo "${msg}"
logger -t bkctld -p daemon.info "${msg}"
fi
}
notice() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "${msg}"
[ "${LOGLEVEL}" -ge 5 ] && logger -t bkctld -p daemon.notice "${msg}"
}
warning() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "WARNING : ${msg}" >&2
if [ "${LOGLEVEL}" -ge 4 ]; then
tty -s || echo "WARNING : ${msg}" >&2
logger -t bkctld -p daemon.warning "${msg}"
fi
}
error() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "ERROR : ${msg}" >&2
if [ "${LOGLEVEL}" -ge 5 ]; then
tty -s || echo "ERROR : ${msg}" >&2
logger -t bkctld -p daemon.error "${msg}"
fi
exit 1
}
## check functions
check_jail() {
jail="${1}"
[ -d "${JAILDIR}/${jail}" ] && return 0
return 1
}
check_jail_on() {
jail="${1}"
return=1
if [ -f "${JAILDIR}/${jail}/${SSHD_PID}" ]; then
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
ps -p "${pid}" > /dev/null && return=0
fi
if [ "${return}" -eq 1 ]; then
rm -f "${JAILDIR}/${jail}/${SSHD_PID}"
grep -q "${JAILDIR}/${jail}/proc" /proc/mounts && umount --lazy "${JAILDIR}/${jail}/proc/"
grep -q "${JAILDIR}/${jail}/dev" /proc/mounts && umount --lazy --recursive "${JAILDIR}/${jail}/dev"
fi
return "${return}"
}
## get functions : get info on jail
get_port() {
jail="${1}"
port=$(grep -E "Port [0-9]+" "${JAILDIR}/${jail}/${SSHD_CONFIG}"|grep -oE "[0-9]+")
echo "${port}"
}
get_key() {
jail="${1}"
if [ -f "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}" ]; then
cat "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
fi
}
get_ip() {
jail="${1}"
grep -E "^AllowUsers" "${JAILDIR}/$jail/${SSHD_CONFIG}"|grep -Eo "root@[^ ]+"| while read allow; do
echo "${allow}"|cut -d'@' -f2
done
}
get_inc() {
jail="${1}"
inc="0"
if [ -f "${CONFDIR}/${jail}" ]; then
day=$(grep -c "day" "${CONFDIR}/${jail}")
month=$(grep -c "month" "${CONFDIR}/${jail}")
inc="${day}/${month}"
fi
echo "${inc}"
}
## set functions : set info on jail
set_port() {
jail="${1}"
port="${2}"
if [ "${port}" = "auto" ]; then
port=$(grep -h Port "${JAILDIR}"/*/"${SSHD_CONFIG}" 2>/dev/null | grep -Eo "[0-9]+" | sort -n | tail -1)
port=$((port+1))
[ "${port}" -le 1 ] && port=2222
fi
sed -i "s/^Port .*/Port ${port}/" "${JAILDIR}/$jail/${SSHD_CONFIG}"
set_firewall "${jail}"
}
set_key() {
jail="${1}"
keyfile="${2}"
[ -e "${keyfile}" ] || error "Keyfile ${keyfile} dosen't exist !"
cat "${keyfile}" > "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
chmod 600 "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
}
set_ip() {
jail="${1}"
ip="${2}"
if [ "${ip}" = "all" ] || [ "${ip}" = "0.0.0.0/0" ]; then
ips="0.0.0.0/0"
case "${subcommand}" in
"inc" | "rm" | "check" | "stats" | "help" | "list")
"${LIBDIR}/bkctld-${subcommand}"
;;
"init" | "is-on")
"${LIBDIR}/bkctld-${subcommand}" "${jail}"
;;
"key" | "port" | "ip")
"${LIBDIR}/bkctld-${subcommand}" "${jail}" "${option}"
;;
"start" | "stop" | "reload" | "restart" | "sync" | "update" | "remove" | "firewall")
if [ "${jail}" = "all" ]; then
"${LIBDIR}/bkctld-list"|xargs --no-run-if-empty --max-args=1 --max-procs=0 "${LIBDIR}/bkctld-${subcommand}"
else
ips=$(get_ip "${jail}")
ips=$(echo "${ips}" "${ip}"|xargs -n1|grep -v "0.0.0.0/0"|sort|uniq)
"${LIBDIR}/bkctld-${subcommand}" "${jail}"
fi
allow="AllowUsers"
for ip in $ips; do
allow="${allow} root@${ip}"
done
sed -i "s~^AllowUsers .*~${allow}~" "${JAILDIR}/$jail/${SSHD_CONFIG}"
set_firewall "${jail}"
}
set_firewall() {
jail="${1}"
if [ -n "${FIREWALL_RULES}" ]; then
if [ -f "${FIREWALL_RULES}" ]; then
sed -i "/#${jail}$/d" "${FIREWALL_RULES}"
fi
if ( check_jail "${jail}" ); then
port=$(get_port "${jail}")
for ip in $(get_ip "${jail}"); do
echo "/sbin/iptables -A INPUT -p tcp --sport 1024: --dport ${port} -s ${ip} -j ACCEPT #${jail}" >> "${FIREWALL_RULES}"
done
if [ -f /etc/init.d/minifirewall ]; then
/etc/init.d/minifirewall restart >/dev/null
fi
fi
fi
}
## mk_jail function : create or update a jail
mk_jail() {
jail="${1}"
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"
umask 077
info "1 - Creating the chroot"
cd "${JAILDIR}/${jail}"
rm -rf bin lib lib64 run usr var/run etc/ssh/*key
mkdir -p dev proc
mkdir -p usr/bin usr/sbin usr/lib usr/lib/x86_64-linux-gnu usr/lib/openssh usr/lib64
mkdir -p etc/ssh var/log run/sshd
mkdir -p root/.ssh var/backup -m 0700
ln -s usr/bin bin
ln -s usr/lib lib
ln -s usr/lib64 lib64
ln -st 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
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}" "${JAILDIR}/${jail}/${dbin}";
for lib in $(ldd "${dbin}" | grep -Eo "/.*so.[0-9\.]+"); do
cp -p "${lib}" "${JAILDIR}/${jail}/${lib}"
done
done
}
## sub functions : functions call by subcommand
sub_init() {
jail="${1}"
sshd_config="${TPLDIR}/sshd_config"
inctpl="${TPLDIR}/inc.tpl"
[ -f "${LOCALTPLDIR}/sshd_config" ] && sshd_config="${LOCALTPLDIR}/sshd_config"
[ -f "${LOCALTPLDIR}/inc.tpl" ] && inctpl="${LOCALTPLDIR}/inc.tpl"
check_jail "${jail}" && error "${jail} : trying to create existant jail"
rootdir=$(dirname "${JAILDIR}")
rootdir_inode=$(stat --format=%i "${rootdir}")
jaildir_inode=$(stat --format=%i "${JAILDIR}")
if [ "${rootdir_inode}" -eq 256 ] || [ "${jaildir_inode}" -eq 256 ]; then
/bin/btrfs subvolume create "${JAILDIR}/${jail}"
;;
"status")
if [ -z "${jail}" ]; then
"${LIBDIR}/bkctld-list"|xargs --no-run-if-empty --max-args=1 "${LIBDIR}/bkctld-${subcommand}"
else
mkdir -p "${JAILDIR}/${jail}"
"${LIBDIR}/bkctld-${subcommand}" "${jail}"
fi
mk_jail "${jail}"
info "4 - Copie default sshd_config"
install -m 0640 "${sshd_config}" "${JAILDIR}/${jail}/${SSHD_CONFIG}"
info "5 - Set usable sshd port"
set_port "${jail}" auto
info "6 - Copie default inc configuration"
install -m 0640 "${inctpl}" "${CONFDIR}/${jail}"
notice "${jail} : created jail"
}
sub_update() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to update inexistant jail"
check_jail_on "${jail}" && sub_stop "${jail}"
mk_jail "${jail}"
notice "${jail} : updated jail"
}
sub_remove() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to remove inexistant jail"
check_jail_on "${jail}" && sub_stop "${jail}"
rm -f "${CONFDIR}/${jail}"
jail_inode=$(stat --format=%i "${JAILDIR}/${jail}")
if [ "${jail_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${JAILDIR}/${jail}" | debug
else
rm -rf "${JAILDIR}/${jail}" | debug
fi
if [ -d "${INCDIR}/${jail}" ]; then
incs=$(ls "${INCDIR}/${jail}")
for inc in ${incs}; do
inc_inode=$(stat --format=%i "${INCDIR}/${jail}/${inc}")
if [ "${inc_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${INCDIR}/${jail}/${inc}" | debug
else
warning "You need to purge ${INCDIR}/${jail}/${inc} manually !"
fi
done
rmdir --ignore-fail-on-non-empty "${INCDIR}/${jail}" | debug
fi
set_firewall "${jail}"
notice "${jail} : deleted jail"
}
sub_start() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to start inexistant jail"
check_jail_on "${jail}" && error "${jail} : trying to start already running jail"
cd "${JAILDIR}/${jail}"
grep -q "${JAILDIR}/${jail}/proc" /proc/mounts || mount -t proc "proc-${jail}" proc
grep -q "${JAILDIR}/${jail}/dev" /proc/mounts || mount -nt tmpfs "dev-${jail}" dev
[ -e "dev/console" ] || mknod -m 622 dev/console c 5 1
[ -e "dev/null" ] || mknod -m 666 dev/null c 1 3
[ -e "dev/zero" ] || mknod -m 666 dev/zero c 1 5
[ -e "dev/ptmx" ] || mknod -m 666 dev/ptmx c 5 2
[ -e "dev/tty" ] || mknod -m 666 dev/tty c 5 0
[ -e "dev/random" ] || mknod -m 444 dev/random c 1 8
[ -e "dev/urandom" ] || mknod -m 444 dev/urandom c 1 9
chown root:tty dev/console dev/ptmx dev/tty
ln -fs proc/self/fd dev/fd
ln -fs proc/self/fd/0 dev/stdin
ln -fs proc/self/fd/1 dev/stdout
ln -fs proc/self/fd/2 dev/stderr
ln -fs proc/kcore dev/core
mkdir -p dev/pts
mkdir -p dev/shm
grep -q "${JAILDIR}/${jail}/dev/pts" /proc/mounts || mount -t devpts -o gid=4,mode=620 none dev/pts
grep -q "${JAILDIR}/${jail}/dev/shm" /proc/mounts || mount -t tmpfs none dev/shm
chroot "${JAILDIR}/${jail}" /usr/sbin/sshd -E /var/log/authlog || error "${jail} : error on starting sshd"
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
notice "${jail} was started [${pid}]"
}
sub_stop() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to stop inexistant jail"
check_jail_on "${jail}" || error "${jail} : trying to stop not running jail"
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
for conn in $(ps --ppid "${pid}" -o pid=); do
kill "${conn}"
done
kill "${pid}" && notice "${jail} was stopped [${pid}]"
umount --lazy --recursive "${JAILDIR}/${jail}/dev"
umount --lazy "${JAILDIR}/${jail}/proc/"
}
sub_reload() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to reload inexistant jail"
check_jail_on "${jail}" || error "${jail} : trying to reload not running jail"
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
kill -HUP "${pid}" && notice "${jail} was reloaded [${pid}]"
}
sub_status() {
jail="${1}"
check_jail "${jail}" || error "${jail} : inexistant jail ! Use '$0 status' for list all"
inc=$(get_inc "${jail}")
if ( check_jail_on "${jail}" ); then
status="ON "
else
status="OFF"
fi
port=$(get_port "${jail}")
ip=$(get_ip "${jail}"|xargs|tr -s ' ' ',')
echo "${jail} ${status} ${port} ${inc} ${ip}" | awk '{ printf("%- 30s %- 10s %- 10s %- 10s %- 40s\n", $1, $2, $3, $4, $5); }'
}
sub_params() {
jail="${1}"
params="${2}"
option="${3}"
check_jail "${jail}" || error "${jail} : inexistant jail'"
if [ -z "${option}" ]; then
"get_${params}" "${jail}"
else
"set_${params}" "${jail}" "${option}"
check_jail_on "${jail}" && sub_reload "${jail}"
notice "${jail} : update ${params} => ${option}"
fi
}
sub_sync() {
jail="${1}"
check_jail "${jail}" || error "${jail} : trying to sync inexistant jail"
[ -n "${NODE}" ] || error "Sync need config of \$NODE in /etc/default/bkctld !"
jail="${1}"
ssh "${NODE}" bkctld init "${jail}" | debug
rsync -a "${JAILDIR}/${jail}/" "${NODE}:${JAILDIR}/${jail}/" --exclude proc/* --exclude sys/* --exclude dev/* --exclude run --exclude var/backup/*
rsync -a "${CONFDIR}/${jail}" "${NODE}:${CONFDIR}/${jail}"
if ( check_jail_on "${jail}" ); then
ssh "${NODE}" bkctld start "${jail}" | debug
fi
if [ -n "${FIREWALL_RULES}" ]; then
rsync -a "${FIREWALL_RULES}" "${NODE}:${FIREWALL_RULES}"
ssh "${NODE}" /etc/init.d/minifirewall restart | debug
fi
}
sub_inc() {
date=$(date +"%Y-%m-%d-%H")
jails=$(ls "${JAILDIR}")
for jail in ${jails}; do
inc="${INCDIR}/${jail}/${date}"
mkdir -p "${INCDIR}/${jail}"
if [ ! -d "${inc}" ]; then
start=$(date +"%H:%M:%S")
jail_inode=$(stat --format=%i "${JAILDIR}/${jail}")
if [ "$jail_inode" -eq 256 ]; then
/bin/btrfs subvolume snapshot -r "${JAILDIR}/${jail}" "${inc}" | debug
else
cp -alx "${JAILDIR}/${jail}/" "${inc}" | debug
fi
end=$(date +"%H:%M:%S")
notice "${jail} : made ${date} inc [${start}/${end}]"
else
warning "${jail} : trying to made already existant inc"
fi
done
}
sub_rm() {
empty="/tmp/bkctld-${$}-$(date +%N))"
mkdir "${empty}"
pidfile="/var/run/bkctld-rm.pid"
if [ -f "${pidfile}" ]; then
pid=$(cat "${pidfile}")
ps -u "${pid}" >/dev/null
if [ "${?}" -eq 0 ]; then
kill -9 "${pid}"
warning "${0} rm always run (PID ${pid}), killed by ${$} !"
fi
rm "${pidfile}"
fi
echo "${$}" > "${pidfile}"
jails=$(ls "${JAILDIR}")
for jail in ${jails}; do
incs=$(ls "${INCDIR}/${jail}")
if [ -f "${CONFDIR}/${jail}" ]; then
keepfile="${CONFDIR}/.keep-${jail}"
while read j; do
date=$( echo "${j}" | cut -d. -f1 )
before=$( echo "${j}" | cut -d. -f2 )
date -d "$(date "${date}") ${before}" "+%Y-%m-%d"
done < "${CONFDIR}/${jail}" > "${keepfile}"
for j in $(echo "${incs}" | grep -v -f "${keepfile}"); do
start=$(date +"%H:%M:%S")
inc_inode=$(stat --format=%i "${INCDIR}/${jail}/${j}")
if [ "${inc_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${INCDIR}/${jail}/${j}" | debug
else
cd "${INCDIR}/${jail}"
rsync -a --delete "${empty}/" "${j}/"
rmdir "${j}"
fi
end=$(date +"%H:%M:%S")
notice "${jail} : deleted ${j} inc [${start}/${end}]"
done
fi
done
rmdir "${empty}"
rm "${pidfile}"
}
sub_check() {
cur_time=$(date "+%s")
return=0
nb_crit=0
nb_warn=0
nb_ok=0
nb_unkn=0
output=""
if [ -b "${BACKUP_DISK}" ]; then
cryptsetup isLuks "${BACKUP_DISK}"
if [ "$?" -eq 0 ]; then
if [ ! -b '/dev/mapper/backup' ]; then
echo "Luks disk ${BACKUP_DISK} is not mounted !\n"
echo "cryptsetup luksOpen ${BACKUP_DISK} backup"
exit 2
fi
BACKUP_DISK='/dev/mapper/backup'
fi
grep -qE "^${BACKUP_DISK} " /etc/mtab
if [ "$?" -ne 0 ]; then
echo "Backup disk ${BACKUP_DISK} is not mounted !\n"
echo "mount ${BACKUP_DISK} /backup"
exit 2
fi
fi
jails=$(ls "${JAILDIR}")
for jail in ${jails}; do
if [ -f "${JAILDIR}/${jail}/var/log/lastlog" ]; then
last_conn=$(stat --format=%Y "${JAILDIR}/${jail}/var/log/lastlog")
date_diff=$(( (cur_time - last_conn) / (60*60) ))
if [ "${date_diff}" -gt "${CRITICAL}" ]; then
nb_crit=$((nb_crit + 1))
output="${output}CRITICAL - ${jail} - ${date_diff} hours\n"
[ "${return}" -le 2 ] && return=2
elif [ "${date_diff}" -gt "${WARNING}" ]; then
nb_warn=$((nb_warn + 1))
output="${output}WARNING - ${jail} - ${date_diff} hours\n"
[ "${return}" -le 1 ] && return=1
else
nb_ok=$((nb_ok + 1))
output="${output}OK - ${jail} - ${date_diff} hours\n"
fi
else
nb_unkn=$((nb_unkn + 1))
output="${output}UNKNOWN - ${jail} doesn't have lastlog !\n"
[ "${return}" -le 3 ] && return=3
fi
done
[ "${return}" -ge 0 ] && header="OK"
[ "${return}" -ge 1 ] && header="WARNING"
[ "${return}" -ge 2 ] && header="CRITICAL"
[ "${return}" -ge 3 ] && header="UNKNOW"
printf "%s - %s UNK / %s CRIT / %s WARN / %s OK\n\n" "${header}" "${nb_unkn}" "${nb_crit}" "${nb_warn}" "${nb_ok}"
printf "${output}" | grep -E "^UNKNOW"
printf "${output}" | grep -E "^CRITICAL"
printf "${output}" | grep -E "^WARNING"
printf "${output}" | grep -E "^OK"
exit "${return}"
}
sub_stats() {
lsof "${IDX_FILE}" >/dev/null 2>&1 || nohup sh -s -- <<EOF >/dev/null 2>&1 &
ionice -c3 "${DUC}" index -d "${IDX_FILE}" "${JAILDIR}"
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 !"
printf "Last update of index file : "
stat --format=%Y "${INDEX_DIR}/.lastrun.duc" | xargs -i -n1 date -R -d "@{}"
echo "<jail> <size> <incs> <lastconn>" | awk '{ printf("%- 30s %- 10s %- 10s %- 15s\n", $1, $2, $3, $4); }'
duc_output=$(mktemp)
stat_output=$(mktemp)
incs_output=$(mktemp)
trap "rm ${duc_output} ${incs_output} ${stat_output}" 0
"${DUC}" ls -d "${IDX_FILE}" "${JAILDIR}" > "${duc_output}"
awk '{ print $2 }' "${duc_output}" | while read jail; do
stat --format=%Y "/backup/jails/${jail}/var/log/lastlog" | xargs -i -n1 date -d "@{}" "+%d-%m-%Y" >> "${stat_output}"
get_inc "${jail}" >> "${incs_output}"
done
paste "${duc_output}" "${incs_output}" "${stat_output}" | awk '{ printf("%- 30s %- 10s %- 10s %- 15s\n", $2, $1, $3, $4); }'
}
## main function : check usage and valid params
main() {
[ "$(id -u)" -ne 0 ] && error "You need to be root to run ${0} !"
[ -f /etc/default/bkctld ] && . /etc/default/bkctld
CONFDIR="${CONFDIR:-/etc/evobackup}"
BACKUP_DISK="${BACKUP_DISK:-}"
JAILDIR="${JAILDIR:-/backup/jails}"
INCDIR="${INCDIR:-/backup/incs}"
TPLDIR="${TPLDIR:-/usr/share/bkctld}"
INDEX_DIR="${INDEX_DIR:-/backup/index}"
IDX_FILE="${IDX_FILE:-${INDEX_DIR}/bkctld-jails.idx}"
LOCALTPLDIR="${LOCALTPLDIR:-/usr/local/share/bkctld}"
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)
mkdir -p "${CONFDIR}" "${JAILDIR}" "${INCDIR}" "${INDEX_DIR}"
subcommand="${1:-}"
jail="${2:-}"
option="${3:-}"
case "${subcommand}" in
"" | "-h" | "--help")
usage
;;
"inc" | "rm" | "check" | "stats")
"sub_${subcommand}"
;;
"init")
if [ -n "${jail}" ]; then
"sub_${subcommand}" "${jail}"
else
usage
fi
;;
"key" | "port" | "ip")
if [ -n "${jail}" ]; then
sub_params "${jail}" "${subcommand}" "${option}"
else
usage
fi
;;
"start" | "stop" | "reload" | "restart" | "sync" | "update" | "remove")
if [ -n "${jail}" ]; then
if [ "${jail}" = "all" ]; then
jails=$(ls "${JAILDIR}")
for jail in ${jails}; do
case "${subcommand}" in
"start")
check_jail_on "${jail}" || "sub_${subcommand}" "${jail}"
;;
"stop" | "reload")
check_jail_on "${jail}" && "sub_${subcommand}" "${jail}"
;;
"restart")
check_jail_on "${jail}" && sub_stop "${jail}"
sub_start "${jail}"
;;
*)
"sub_${subcommand}" "${jail}"
;;
esac
done
else
if [ "${subcommand}" != "restart" ]; then
"sub_${subcommand}" "${jail}"
else
check_jail_on "${jail}" && sub_stop "${jail}"
sub_start "${jail}"
fi
fi
else
usage
fi
;;
"status")
if [ -z "${jail}" ]; then
jails=$(ls "${JAILDIR}")
for jail in ${jails}; do
"sub_${subcommand}" "${jail}"
done
else
"sub_${subcommand}" "${jail}"
fi
;;
*)
shift
warning "'${subcommand}' is not a known subcommand." && usage
exit 1
;;
esac
}
main "${@}"
;;
esac

179
bkctld.8 Normal file
View file

@ -0,0 +1,179 @@
.Dd December 27, 2018
.Dt BKCTLD 8
.Os
.Sh NAME
.Nm bkctld
.Nd tool to manage evobackup jails
.Sh SYNOPSIS
.Nm
.Op Ar operand...
.Sh DESCRIPTION
.Nm
is a shell script that creates and manages a backup server
which can handle the backups of many other servers (clients).
.Pp
It uses
.Xr ssh 1
and
.Xr chroot 8
to sandbox every client's backups.
Each client will upload it's data every day
using
.Xr rsync 1
in it's
.Xr chroot 8
(using the root account).
.Pp
Prior backups are stored incrementally outside of the
.Xr chroot 8
using
.Xr ln 1
hard links or BTRFS snapshots.
(So they can not be affected by the client),
which backups are kept over time can be configured in the jail's nominal
.Xr evobackup-incl 5
configuration file.
.Pp
A large enough volume must be mounted on
.Pa /backup ,
if the filesystem is formatted with BTRFS,
.Nm
will use sub-volumes and snapshots to save space.
.Pp
It's default settings can be overridden in
.Xr bkctld.conf 5
file.
.Pp
The following operands are available:
.Bl -tag -width Ds
.It Cm init Ar jailname
Create an evobackup jail
.It Cm update Cm all | Ar jailname
Update an evobackup jail
.It Cm remove Cm all | Ar jailname
Remove an evobackup jail
.It Cm start Cm all | Ar jailname
Start an evobackup jail
.It Cm stop Cm all | Ar jailname
Stop an evobackup jail
.It Cm reload Cm all | Ar jailname
Reload an evobackup jail
.It Cm restart Cm all | Ar jailname
Restart an evobackup jail
.It Cm sync Cm all | Ar jailname
Sync an evobackup jail, the mirror server is defined by the
.Ev $NODE
variable in
.Pa /etc/default/bkctld
.It Cm status Op Ar jailname
Print the status of all jails or only
.Op Ar jailname .
.It Cm key Ar jailname Op Ar keyfile
Print or set the
.Xr ssh 1
public key of an evobackup jail
.It Cm port Ar jailname Op Cm auto | Ar port
Print or set the
.Xr ssh 1
.Op Ar port
of an evobackup jail.
Using
.Op Cm auto
will set it to the next available port.
.It Cm ip Ar jailname Op Cm all | Ar address
Print or set the whitelisted IP
.Op Ar address
for an evobackup jail.
.Op Cm all
allows unrestricted access and is the default.
.It Cm inc
Generate incremental backups
.It Cm rm
Remove old incremental backups
.El
.Sh FILES
.Bl -tag -width Ds
.It Pa /etc/default/bkctld
Template for
.Xr bkctld.conf 5
.It Pa /usr/share/bkctld/incl.tpl
Default rules for the incremental backups are stored here.
.El
.Sh EXAMPLES
Before creating a jail and backing up a client,
the backup server administrator will need:
.Bl -bullet
.It
The host name of the client system.
.It
The public RSA
.Xr ssh 1
key for the
.Dq root
user of the client system,
it is recommended the private key be password-less if automation is desired.
.It
The IPv4 address of the client system is needed
if the administrator wishes to maintain a whitelist,
see
.Va FIREWALL_RULES
in
.Xr bkctld.conf 5
.El
.Pp
He can then create the jail:
.Bd -literal -offset indent
# bkctld init CLIENT_HOST_NAME
# bkctld key CLIENT_HOST_NAME /root/CLIENT_HOST_NAME.pub
# bkctld ip CLIENT_HOST_NAME CLIENT_IP_ADDRESS
# bkctld start CLIENT_HOST_NAME
# bkctld status CLIENT_HOST_NAME
.Ed
.Pp
And override the default
.Xr evobackup-incl 5
rules
.Bd -literal -offset indent
# $EDITOR /etc/evobackup/CLIENT_HOST_NAME
.Ed
.Pp
To sync itself,
the client server will need to install
.Xr rsync 1 .
It can then be run manually:
.Bd -literal -offset indent
# rsync -av -e "ssh -p JAIL_PORT" /home/ root@BACKUP_SERVER:/var/backup/home/
.Ed
.Pp
If a more automated setup is required,
a script can be written in any programming language.
In this case,
it may be useful to validate the backup server's identity before hand.
.Bd -literal -offset indent
# ssh -p JAIL_PORT BACKUP_SERVER
.Ed
.Pp
A
.Xr bash 1
example to be run under the
.Dq root
user's
.Xr crontab 5
can be found in the
.Lk https://gitea.evolix.org/evolix/evobackup/src/branch/master/zzz_evobackup "source repository"
.\" .Sh EXIT STATUS
.\" For sections 1, 6, and 8 only.
.\" .Sh DIAGNOSTICS
.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
.Sh SEE ALSO
.Xr rsync 1 ,
.Xr ssh-keygen 1 ,
.Xr bkctld 5 ,
.Xr evobackup-incl 5 ,
.Xr chroot 8 ,
.Xr cron 8 ,
.Xr sshd 8
.Sh AUTHORS
.An Victor Laborie
.\" .Sh CAVEATS
.\" .Sh BUGS

View file

@ -1,4 +1,5 @@
# Defaults for bkctld command (evobackup)
# bkctld.conf(5)
# Defaults for bkctld(8) command (evobackup)
# sourced by /usr/sbin/bkctld and /etc/init.d/bkctld
#CONFDIR='/etc/evobackup'
@ -10,5 +11,5 @@
#SSHD_PID='/var/run/sshd.pid'
#SSHD_CONFIG='/etc/ssh/sshd_config'
#AUTHORIZED_KEYS='/root/.ssh/authorized_keys'
#FIREWALL_RULES='/etc/firewall.rc.jails'
#FIREWALL_RULES=''
#LOGLEVEL=6

67
bkctld.conf.5 Normal file
View file

@ -0,0 +1,67 @@
.Dd December 28, 2018
.Dt BKCTLD.CONF 5
.Os
.Sh NAME
.Nm bkctld.conf
.Nd configuration file for
.Xr bkctld 8
.Sh SYNOPSIS
.D1 name=[value]
.Sh DESCRIPTION
The
.Nm
file contains variables that override the behavior of the
.Xr bkctld 8
script.
By default, it is located at
.Pa /etc/default/bkctld .
.Pp
Each line must be a valid
.Xr bash 1
variable definition.
Lines beginning with the
.Sq #
character are comments and are ignored.
The order of the definitions does not matter.
.Pp
The following variables may be defined:
.Bl -tag -width Ds
.It Va CONFDIR
Directory where
.Xr evobackup-incl 5
files are kept.
It's default value is
.Pa /etc/evobackup/ .
.It Va JAILDIR
Directory where the jails are stored,
it is recommended that this be inside a BTRFS file system.
It's default value is
.Pa /backup/jails/ .
.It Va INCDIR
Directory where incremental backups are stored,
it is recommended that this be inside a BTRFS file system.
It's default value is
.Pa /backup/incs/ .
.It Va TPLDIR
Directory where the default configuration files are stored.
It's default value is
.Pa /usr/share/bkctld/ .
.It Va LOCALTPLDIR
Directory where custom configuration files are stored.
It's default is
.Pa /usr/local/share/bkctld/ .
.It Va LOGLEVEL
Defines the amount of information to log, follows the same scale as in
.In syslog.h
and defaults to 6.
.It Va FIREWALL_RULES
Configuration file containing the firewall rules that govern jail access.
This file must be sourced by your firewall.
It does not have a default value and, if unset,
.Xr bkctld 8
will not automatically update the firewall.
.El
.Sh SEE ALSO
.Xr bash 1 ,
.Xr evobackup-incl 5 ,
.Xr bkctld 8

14
bkctld.service Normal file
View file

@ -0,0 +1,14 @@
[Unit]
Documentation=man:bkctld(8)
Description=Backup manager using rsync and OpenSSH chroot.
[Service]
Type=oneshot
ExecStart=/usr/sbin/bkctld start all
ExecStop=/usr/sbin/bkctld stop all
RemainAfterExit=true
KillMode=control-group
GuessMainPID=no
[Install]
WantedBy=multi-user.target

View file

@ -1,42 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: evobackup
# Required-Start: $syslog
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Backup manager using rsync and OpenSSH chroot.
### END INIT INFO
. /lib/lsb/init-functions
case "$1" in
start)
bkctld start all
;;
stop)
bkctld stop all
;;
reload|force-reload)
bkctld reload all
;;
restart)
bkctld restart all
;;
status)
bkctld status
;;
*)
echo "Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac
exit 0

86
docs/configuration.md Normal file
View file

@ -0,0 +1,86 @@
BKCTLD.CONF(5) - File Formats Manual
# NAME
**bkctld.conf** - configuration file for
bkctld(8)
# SYNOPSIS
> name=\[value]
# DESCRIPTION
The
**bkctld.conf**
file contains variables that override the behavior of the
bkctld(8)
script.
By default, it is located at
*/etc/default/bkctld*.
Each line must be a valid
bash(1)
variable definition.
Lines beginning with the
'#'
character are comments and are ignored.
The order of the definitions does not matter.
The following variables may be defined:
*CONFDIR*
> Directory where
> evobackup-incl(5)
> files are kept.
> It's default value is
> */etc/evobackup/*.
*JAILDIR*
> Directory where the jails are stored,
> it is recommended that this be inside a BTRFS file system.
> It's default value is
> */backup/jails/*.
*INCDIR*
> Directory where incremental backups are stored,
> it is recommended that this be inside a BTRFS file system.
> It's default value is
> */backup/incs/*.
*TPLDIR*
> Directory where the default configuration files are stored.
> It's default value is
> */usr/share/bkctld/*.
*LOCALTPLDIR*
> Directory where custom configuration files are stored.
> It's default is
> */usr/local/share/bkctld/*.
*LOGLEVEL*
> Defines the amount of information to log, follows the same scale as in
> &lt;*syslog.h*>
> and defaults to 6.
*FIREWALL\_RULES*
> Configuration file containing the firewall rules that govern jail access.
> This file must be sourced by your firewall.
> It does not have a default value and, if unset,
> bkctld(8)
> will not automatically update the firewall.
# SEE ALSO
bash(1),
evobackup-incl(5),
bkctld(8)
OpenBSD 6.4 - December 28, 2018

View file

@ -1,6 +1,7 @@
# Debian Package
**bkctld** package can be build from the **debian** branch of this Git repository with git-buildpackage and sbuild.
The **bkctld** package can be built from the **debian** branch of
this git repository with git-buildpackage and sbuild.
## Dependencies
@ -37,7 +38,7 @@ sbuild-createchroot --include=eatmydata,ccache,gnupg unstable /srv/chroot/sid ht
## Build
You must be in **debian** branch :
You must be in the **debian** branch :
~~~
git checkout debian

80
docs/incrementals.md Normal file
View file

@ -0,0 +1,80 @@
EVOBACKUP-INCL(5) - File Formats Manual
# NAME
**evobackup-incl** - incremental backup configuration
# SYNOPSIS
> \+%Y-%m-%d.-%day
# DESCRIPTION
Located by default in
*/etc/evobackup/*,
each
**evobackup-incl**
file is named after the
bkctld(8)
backup for which the rules it contains must apply.
The rules it defines decide which incremental backups are kept when running
# bkctld rm
Each line defines a single rule.
The first part of the rule describes when the backup was taken,
the second part decides how long to keep it.
Lines beginning with the
'#'
character are comments and are ignored.
The order of the rules does not matter.
Evobackups that do not have their nominal
**evobackup-incl**
file use the default rules defined in
*/usr/share/bkctld/inc.tpl*
# EXAMPLES
Keep today's backup:
+%Y-%m-%d.-0day
Keep yesterday's backup:
+%Y-%m-%d.-1day
Keep the first day of this month:
+%Y-%m-01.-0month
Keep the first day of last month:
+%Y-%m-01.-1month
Keep backups for every 15 days:
+%Y-%m-01.-1month
+%Y-%m-15.-1month
Keep a backup of the first day of january:
+%Y-01-01.-1month
Keep backups of the last 4 days and the first day of the last 2 months:
+%Y-%m-%d.-0day
+%Y-%m-%d.-1day
+%Y-%m-%d.-2day
+%Y-%m-%d.-3day
+%Y-%m-01.-0month
+%Y-%m-01.-1month
# SEE ALSO
bkctld(8),
cron(8),
*/etc/evobackup/tpl/inc.tpl*
OpenBSD 6.4 - December 28, 2018

47
docs/install.md Normal file
View file

@ -0,0 +1,47 @@
# Install
A Debian package is available in the Evolix repository
~~~
echo "http://pub.evolix.net/jessie/" >> /etc/apt/sources.list
apt update
apt install bkctld
~~~
Then edit `/etc//bkctld`
## Chroot dependencies
The chroot jails depend on these packages
~~~
apt install \
bash \
coreutils \
sed \
dash \
mount \
rsync \
openssh-server \
openssh-sftp-server \
libc6-i386 \
libc6
~~~
## Client dependencies
The clients only require OpenSSH and rsync.
### Cron job for incremental backups
Edit the root crontab
~~~
# crontab -e
+ 30 10 * * * /usr/sbin/bkctld inc && /usr/sbin/bkctld rm
~~~
## Notes
If you want mutiples backups in a day (1 by hour maximum) you can
run `bkctld inc` multiples times, if you want to keep incremental
backups **for ever**, just don't run `bkctld rm`.

View file

@ -1,232 +1,207 @@
BKCTLD(8) - System Manager's Manual
# NAME
bkctld - tool to manage evobackup jail
**bkctld** - tool to manage evobackup jails
# SYNOPSIS
~~~
bkctld <command> [<args>]
~~~
**bkctld**
\[*operand...*]
# DESCRIPTION
bkctld is a shell script used to set up and manage a backup server able to receive data from many servers (clients).
**bkctld**
is a shell script that creates and manages a backup server
which can handle the backups of many other servers (clients).
The aim is to run a SSH chroot environment (called "jails" in the FreeBSD world) for every single client. The client will then be able to send data over SSH using rsync in his own chroot environment (using root account).
It uses
ssh(1)
and
chroot(8)
to sandbox every client's backups.
Each client will upload it's data every day
using
rsync(1)
in it's
chroot(8)
(using the root account).
Incrementals are stored outside the chroot using hard links or btrfs snapshots (thus incrementals are not accessible by clients). This method has the advantage to keep incrementals securely isolated using low space on device.
Prior backups are stored incrementally outside of the
chroot(8)
using
ln(1)
hard links or BTRFS snapshots.
(So they can not be affected by the client),
which backups are kept over time can be configured in the jail's nominal
evobackup-incl(5)
configuration file.
A suitable volume size must be mounted on /backup (usage of **btrfs** is preferable, providing subvolume and snapshot fonctionnality). For security reason, you can use an encrypted volume (e.g. **luks**)
A large enough volume must be mounted on
*/backup*,
if the filesystem is formatted with BTRFS,
**bkctld**
will use sub-volumes and snapshots to save space.
# BKCTLD COMMANDS
It's default settings can be overridden in
bkctld.conf(5)
file.
Create an evobackup jail :
The following operands are available:
~~~
bkctld init <jailname>
~~~
**init** *jailname*
Update an evobackup jail or all :
> Create an evobackup jail
~~~
bkctld update <jailname>|all
~~~
**update** **all** | *jailname*
Remove an evobackup jail or all :
> Update an evobackup jail
~~~
bkctld remove <jailname>|all
~~~
**remove** **all** | *jailname*
Start an evobackup jail or all :
> Remove an evobackup jail
~~~
bkctld start <jailname>|all
~~~
**start** **all** | *jailname*
Stop an evobackup jail or all :
> Start an evobackup jail
~~~
bkctld stop <jailname>|all
~~~
**stop** **all** | *jailname*
Reload an evobackup jail or all :
> Stop an evobackup jail
~~~
bkctld reload <jailname>|all
~~~
Restart an evobackup jail or all :
**reload** **all** | *jailname*
~~~
bkctld restart <jailname>|all
~~~
> Reload an evobackup jail
Sync an evobackup jail or all.
Second server is defined by $NODE var in /etc/default/bkctld :
**restart** **all** | *jailname*
~~~
bkctld sync <jailname>|all
~~~
> Restart an evobackup jail
Print status of all evobackup jail or one jail :
**sync** **all** | *jailname*
~~~
bkctld status [<jailname>]
~~~
> Sync an evobackup jail, the mirror server is defined by the
> `$NODE`
> variable in
> */etc/default/bkctld*
Print or set the SSH public key of an evobackup jail :
**status** \[*jailname*]
~~~
bkctld key <jailname> [<keyfile>]
~~~
> Print the status of all jails or only
> \[*jailname*].
Print or set the SSH port of an evobackup jail.
Auto to set next available port (last + 1) :
**key** *jailname* \[*keyfile*]
~~~
bkctld port <jailname> [<ssh_port>|auto]
~~~
> Print or set the
> ssh(1)
> public key of an evobackup jail
Print or set allowed IP of an evobackup jail.
All for unrestricted access (default) :
**port** *jailname* \[**auto** | *port*]
~~~
bkctld ip <jailname> [<ip>|all]
~~~
> Print or set the
> ssh(1)
> \[*port*]
> of an evobackup jail.
> Using
> \[**auto**]
> will set it to the next available port.
Generate inc of an evobackup jail :
**ip** *jailname* \[**all** | *address*]
~~~
bkctld inc
~~~
> Print or set the whitelisted IP
> \[*address*]
> for an evobackup jail.
> \[**all**]
> allows unrestricted access and is the default.
Remove old inc of an evobackup jail :
**inc**
~~~
bkctld rm
~~~
> Generate incremental backups
# CONFIGURATION VARS
**rm**
bkctld configuration has to be set in /etc/default/bkctld file.
> Remove old incremental backups
## REQUIREDS VARS
# FILES
Default required vars are defined in bkctld script. Alter them to override default values.
*/etc/default/bkctld*
* CONFDIR (default: /etc/evobackup) : Dir where incremental backup is configured. See INCS CONFIGURATION section for details.
* JAILDIR (default : /backup/jails) : Dir for jail's root dir. BTRFS recommended.
* INCDIR (default : /backups/incs) : Dir where incremental backup is stored. BTRFS recommended.
* TPLDIR (default : /usr/share/bkctld) : Dir where jail template file is stored.
* LOCALTPLDIR (default : /usr/local/share/bkctld) : Dir for surcharge jail templates.
* LOGLEVEL (default : 6) : Define loglevel, based on syslog severity level.
> Template for
> bkctld.conf(5)
## OPTIONALS VARS
*/usr/share/bkctld/incl.tpl*
Optionnals vars are no default value. No set them desactivate correspondant fonctionnality.
> Default rules for the incremental backups are stored here.
* FIREWALL_RULES (default: no firewall auto configuration) : Configuration file were firewall was configured to allow jail access. This file must be sourced by your firewall configuration tool.
# EXAMPLES
# INCS CONFIGURATION
Before creating a jail and backing up a client,
the backup server administrator will need:
Incremental backups was configured in $CONFDIR/<jailname>. Some example of syntax.
* The host name of the client system.
Keep the incrememtal backup of today :
* The public RSA
ssh(1)
key for the
"root"
user of the client system,
it is recommended the private key be password-less if automation is desired.
~~~
+%Y-%m-%d.-0day
~~~
* The IPv4 address of the client system is needed
if the administrator wishes to maintain a whitelist,
see
*FIREWALL\_RULES*
in
bkctld.conf(5)
Keep the incremental backup of yesterday :
He can then create the jail:
~~~
+%Y-%m-%d.-1day
~~~
# bkctld init CLIENT_HOST_NAME
# bkctld key CLIENT_HOST_NAME /root/CLIENT_HOST_NAME.pub
# bkctld ip CLIENT_HOST_NAME CLIENT_IP_ADDRESS
# bkctld start CLIENT_HOST_NAME
# bkctld status CLIENT_HOST_NAME
Keep the incremental backup of the first day of this month :
And override the default
evobackup-incl(5)
rules
~~~
+%Y-%m-01.-0month
~~~
# $EDITOR /etc/evobackup/CLIENT_HOST_NAME
Keep the incremental backup of the first day of last month :
To sync itself,
the client server will need to install
rsync(1).
It can then be run manually:
~~~
+%Y-%m-01.-1month
~~~
# rsync -av -e "ssh -p JAIL_PORT" /home/ root@BACKUP_SERVER:/var/backup/home/
Keep the incremental backup of every 15 days :
If a more automated setup is required,
a script can be written in any programming language.
In this case,
it may be useful to validate the backup server's identity before hand.
~~~
+%Y-%m-01.-1month
+%Y-%m-15.-1month
~~~
# ssh -p JAIL_PORT BACKUP_SERVER
Keep the incremental backup of the first january :
~~~
+%Y-01-01.-1month
~~~
Default value : keep incremental of last 4 days and last 2 months. Change default in $LOCALTPLDIR/inc.tpl :
~~~
+%Y-%m-%d.-0day
+%Y-%m-%d.-1day
+%Y-%m-%d.-2day
+%Y-%m-%d.-3day
+%Y-%m-01.-0month
+%Y-%m-01.-1month
~~~
# CLIENT CONFIGURATION
You can save various systems on evobackup jail : Linux, BSD, Windows, MacOSX. Only prequisites is rsync command.
~~~
rsync -av -e "ssh -p SSH_PORT" /home/ root@SERVER_NAME:/var/backup/home/
~~~
You can simply create a shell script which use rsync for backup your's servers. An example script is available in zzz_evobackup for quickstart.
This documentation explain how to use this example script.
Install example script in crontab :
~~~
# For Linux
install -v -m700 zzz_evobackup /etc/cron.daily/
# For FreeBSD
install -v -m700 zzz_evobackup /etc/periodic/daily/
~~~
Generate an SSH key for root account with no passphrase :
~~~
ssh-keygen
~~~
Sent /root/.ssh/id_rsa.pub to backup server administrator or read BKCTLD COMMANDS section.
Edit zzz_evobackup script and update this variables :
* SSH_PORT : Port of corespondant evobackup jail.
* MAIL : Email address for notification.
* NODE : Use for alternate between mutiple backup servers. Default value permit to save on node0 on pair day and on node1 on impair day.
* SRV : Adress of your backup serveur.
Uncomment service dump, ex Mysql / LDAP / PostgreQL / ...
Itiniate SSH connection and validate fingerprint :
~~~
ssh -p SSH_PORT SERVER_NAME
~~~
Your daily evobackup is in place !
A
bash(1)
example to be run under the
"root"
user's
crontab(5)
can be found in the
[source repository](https://gitea.evolix.org/evolix/evobackup/src/branch/master/zzz_evobackup)
# SEE ALSO
rsync(1), sshd(8), chroot(8).
rsync(1),
ssh-keygen(1),
bkctld(5),
evobackup-incl(5),
chroot(8),
cron(8),
sshd(8)
# AUTHORS
Victor Laborie
OpenBSD 6.4 - December 27, 2018

79
evobackup-incl.5 Normal file
View file

@ -0,0 +1,79 @@
.Dd December 28, 2018
.Dt EVOBACKUP-INCL 5
.Os
.Sh NAME
.Nm evobackup-incl
.Nd incremental backup configuration
.Sh SYNOPSIS
.D1 +%Y-%m-%d.-%day
.Sh DESCRIPTION
Located by default in
.Pa /etc/evobackup/ ,
each
.Nm
file is named after the
.Xr bkctld 8
backup for which the rules it contains must apply.
.Pp
The rules it defines decide which incremental backups are kept when running
.Bd -literal -offset indent
# bkctld rm
.Ed
.Pp
Each line defines a single rule.
The first part of the rule describes when the backup was taken,
the second part decides how long to keep it.
Lines beginning with the
.Sq #
character are comments and are ignored.
The order of the rules does not matter.
.Pp
Evobackups that do not have their nominal
.Nm
file use the default rules defined in
.Pa /usr/share/bkctld/inc.tpl
.Sh EXAMPLES
Keep today's backup:
.Bd -literal -offset indent
+%Y-%m-%d.-0day
.Ed
.Pp
Keep yesterday's backup:
.Bd -literal -offset indent
+%Y-%m-%d.-1day
.Ed
.Pp
Keep the first day of this month:
.Bd -literal -offset indent
+%Y-%m-01.-0month
.Ed
.Pp
Keep the first day of last month:
.Bd -literal -offset indent
+%Y-%m-01.-1month
.Ed
.Pp
Keep backups for every 15 days:
.Bd -literal -offset indent
+%Y-%m-01.-1month
+%Y-%m-15.-1month
.Ed
.Pp
Keep a backup of the first day of january:
.Bd -literal -offset indent
+%Y-01-01.-1month
.Ed
.Pp
Keep backups of the last 4 days and the first day of the last 2 months:
.Bd -literal -offset indent
+%Y-%m-%d.-0day
+%Y-%m-%d.-1day
+%Y-%m-%d.-2day
+%Y-%m-%d.-3day
+%Y-%m-01.-0month
+%Y-%m-01.-1month
.Ed
.Sh SEE ALSO
.Xr bkctld 8 ,
.Xr cron 8 ,
.Pa /etc/evobackup/tpl/inc.tpl

70
lib/bkctld-check Executable file
View file

@ -0,0 +1,70 @@
#!/bin/sh
#
# Run check on jails (NRPE output)
# Usage: check
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
cur_time=$(date "+%s")
return=0
nb_crit=0
nb_warn=0
nb_ok=0
nb_unkn=0
output=""
if [ -b "${BACKUP_DISK}" ]; then
cryptsetup isLuks "${BACKUP_DISK}"
if [ "$?" -eq 0 ]; then
if [ ! -b '/dev/mapper/backup' ]; then
echo "Luks disk ${BACKUP_DISK} is not mounted !\n"
echo "cryptsetup luksOpen ${BACKUP_DISK} backup"
exit 2
fi
BACKUP_DISK='/dev/mapper/backup'
fi
grep -qE "^${BACKUP_DISK} " /etc/mtab
if [ "$?" -ne 0 ]; then
echo "Backup disk ${BACKUP_DISK} is not mounted !\n"
echo "mount ${BACKUP_DISK} /backup"
exit 2
fi
fi
for jail in $("${LIBDIR}/bkctld-list"); do
if [ -f "${JAILDIR}/${jail}/var/log/lastlog" ]; then
last_conn=$(stat --format=%Y "${JAILDIR}/${jail}/var/log/lastlog")
date_diff=$(( (cur_time - last_conn) / (60*60) ))
if [ "${date_diff}" -gt "${CRITICAL}" ]; then
nb_crit=$((nb_crit + 1))
output="${output}CRITICAL - ${jail} - ${date_diff} hours\n"
[ "${return}" -le 2 ] && return=2
elif [ "${date_diff}" -gt "${WARNING}" ]; then
nb_warn=$((nb_warn + 1))
output="${output}WARNING - ${jail} - ${date_diff} hours\n"
[ "${return}" -le 1 ] && return=1
else
nb_ok=$((nb_ok + 1))
output="${output}OK - ${jail} - ${date_diff} hours\n"
fi
else
nb_unkn=$((nb_unkn + 1))
output="${output}UNKNOWN - ${jail} doesn't have lastlog !\n"
[ "${return}" -le 3 ] && return=3
fi
done
[ "${return}" -ge 0 ] && header="OK"
[ "${return}" -ge 1 ] && header="WARNING"
[ "${return}" -ge 2 ] && header="CRITICAL"
[ "${return}" -ge 3 ] && header="UNKNOW"
printf "%s - %s UNK / %s CRIT / %s WARN / %s OK\n\n" "${header}" "${nb_unkn}" "${nb_crit}" "${nb_warn}" "${nb_ok}"
printf "${output}" | grep -E "^UNKNOW"
printf "${output}" | grep -E "^CRITICAL"
printf "${output}" | grep -E "^WARNING"
printf "${output}" | grep -E "^OK"
exit "${return}"

24
lib/bkctld-firewall Executable file
View file

@ -0,0 +1,24 @@
#!/bin/sh
#
# Update firewall rules of <jailname> or all
# Usage: firewall <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
if [ -n "${FIREWALL_RULES}" ]; then
[ -f "${FIREWALL_RULES}" ] && sed -i "/#${jail}$/d" "${FIREWALL_RULES}"
if [ -d "${JAILDIR}/${jail}" ]; then
port=$("${LIBDIR}/bkctld-port" "${jail}")
for ip in $("${LIBDIR}/bkctld-ip" "${jail}"); do
echo "/sbin/iptables -A INPUT -p tcp --sport 1024: --dport ${port} -s ${ip} -j ACCEPT #${jail}" >> "${FIREWALL_RULES}"
done
[ -f /etc/init.d/minifirewall ] && /etc/init.d/minifirewall restart >/dev/null
fi
notice "${jail} : firewall rules updated"
fi

21
lib/bkctld-help Executable file
View file

@ -0,0 +1,21 @@
#!/bin/sh
#
# Print this help
# Usage: help
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
cat <<EOF
Usage: bkctld <subcommand> [options]
Subcommands:
EOF
for subcommand in ${LIBDIR}/bkctld-*; do
name=$(basename "${subcommand}"|sed 's/^bkctld-//')
desc=$(grep -E "^#" "${subcommand}"|sed -n '3p'|sed "s/^# //")
usage=$(grep -E "^# Usage: ${name}" "${subcommand}"|sed "s/^# Usage: ${name}//")
printf " %- 10s %- 30s %- 40s\n" "${name}" "${usage}" "${desc}"
done
printf "\n"

26
lib/bkctld-inc Executable file
View file

@ -0,0 +1,26 @@
#!/bin/sh
#
# Make incremental inc of all jails
# Usage: inc
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
date=$(date +"%Y-%m-%d-%H")
for jail in $("${LIBDIR}/bkctld-list"); do
inc="${INCDIR}/${jail}/${date}"
mkdir -p "${INCDIR}/${jail}"
if [ ! -d "${inc}" ]; then
start=$(date +"%H:%M:%S")
jail_inode=$(stat --format=%i "${JAILDIR}/${jail}")
if [ "$jail_inode" -eq 256 ]; then
/bin/btrfs subvolume snapshot -r "${JAILDIR}/${jail}" "${inc}" | debug
else
cp -alx "${JAILDIR}/${jail}/" "${inc}" | debug
fi
end=$(date +"%H:%M:%S")
notice "${jail} : made ${date} inc [${start}/${end}]"
else
warning "${jail} : trying to made already existant inc"
fi
done

35
lib/bkctld-init Executable file
View file

@ -0,0 +1,35 @@
#!/bin/sh
#
# Init jail <jailname>
# Usage: init <jailname>
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] && error "${jail} : trying to create existant jail"
mkdir -p "${CONFDIR}" "${JAILDIR}"
sshd_config="${TPLDIR}/sshd_config"
inctpl="${TPLDIR}/inc.tpl"
[ -f "${LOCALTPLDIR}/sshd_config" ] && sshd_config="${LOCALTPLDIR}/sshd_config"
[ -f "${LOCALTPLDIR}/inc.tpl" ] && inctpl="${LOCALTPLDIR}/inc.tpl"
rootdir=$(dirname "${JAILDIR}")
rootdir_inode=$(stat --format=%i "${rootdir}")
jaildir_inode=$(stat --format=%i "${JAILDIR}")
if [ "${rootdir_inode}" -eq 256 ] || [ "${jaildir_inode}" -eq 256 ]; then
/bin/btrfs subvolume create "${JAILDIR}/${jail}"
else
mkdir -p "${JAILDIR}/${jail}"
fi
. "${LIBDIR}/mkjail"
info "4 - Copie default sshd_config"
install -m 0640 "${sshd_config}" "${JAILDIR}/${jail}/${SSHD_CONFIG}"
info "5 - Copie default inc configuration"
install -m 0640 "${inctpl}" "${CONFDIR}/${jail}"
"${LIBDIR}/bkctld-port" "${jail}" auto
notice "${jail} : created jail"

35
lib/bkctld-ip Executable file
View file

@ -0,0 +1,35 @@
#!/bin/sh
#
# Set or get allowed(s) ip(s) of <jailname>
# Usage: ip <jailname> [<ip>|all]
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
ip="${2:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : inexistant jail'"
if [ -z "${ip}" ]; then
grep -E "^AllowUsers" "${JAILDIR}/$jail/${SSHD_CONFIG}"|grep -Eo "root@[^ ]+"| while read allow; do
echo "${allow}"|cut -d'@' -f2
done
else
if [ "${ip}" = "all" ] || [ "${ip}" = "0.0.0.0/0" ]; then
ips="0.0.0.0/0"
else
ips=$("${LIBDIR}/bkctld-ip" "${jail}")
ips=$(echo "${ips}" "${ip}"|xargs -n1|grep -v "0.0.0.0/0"|sort|uniq)
fi
allow="AllowUsers"
for ip in $ips; do
allow="${allow} root@${ip}"
done
sed -i "s~^AllowUsers .*~${allow}~" "${JAILDIR}/$jail/${SSHD_CONFIG}"
notice "${jail} : update ip => ${ip}"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-reload" "${jail}"
"${LIBDIR}/bkctld-firewall" "${jail}"
fi

26
lib/bkctld-is-on Executable file
View file

@ -0,0 +1,26 @@
#!/bin/sh
#
# Check if a jail is on or not
# Usage: is-on <jailname>
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to check inexistant jail"
jail="${1}"
return=1
if [ -f "${JAILDIR}/${jail}/${SSHD_PID}" ]; then
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
ps -p "${pid}" > /dev/null && return=0
fi
if [ "${return}" -eq 1 ]; then
rm -f "${JAILDIR}/${jail}/${SSHD_PID}"
grep -q "${JAILDIR}/${jail}/proc" /proc/mounts && umount --lazy "${JAILDIR}/${jail}/proc/"
grep -q "${JAILDIR}/${jail}/dev" /proc/mounts && umount --lazy --recursive "${JAILDIR}/${jail}/dev"
fi
exit "${return}"

27
lib/bkctld-key Executable file
View file

@ -0,0 +1,27 @@
#!/bin/sh
#
# Set or get ssh pubic key of <jailname>
# Usage: key <jailname> [<keyfile>]
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
keyfile="${2:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : inexistant jail'"
if [ -z "${keyfile}" ]; then
if [ -f "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}" ]; then
cat "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
fi
else
[ -e "${keyfile}" ] || error "Keyfile ${keyfile} dosen't exist !"
cat "${keyfile}" > "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
chmod 600 "${JAILDIR}/${jail}/${AUTHORIZED_KEYS}"
notice "${jail} : update key => ${keyfile}"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-reload" "${jail}"
fi

12
lib/bkctld-list Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
#
# List jails
# Usage: list
#
set -eu
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
[ -d "${JAILDIR}" ] || exit 0
find "${JAILDIR}" -mindepth 1 -maxdepth 1 -type d|sed 's!.*/!!'

28
lib/bkctld-port Executable file
View file

@ -0,0 +1,28 @@
#!/bin/sh
#
# Set or get ssh port of <jailname>
# Usage: port <jailname> [<port>|auto]
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
port="${2:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : inexistant jail'"
if [ -z "${port}" ]; then
grep -E "Port [0-9]+" "${JAILDIR}/${jail}/${SSHD_CONFIG}"|grep -oE "[0-9]+"
else
if [ "${port}" = "auto" ]; then
port=$(grep -h Port "${JAILDIR}"/*/"${SSHD_CONFIG}" 2>/dev/null | grep -Eo "[0-9]+" | sort -n | tail -1)
port=$((port+1))
[ "${port}" -le 1 ] && port=2222
fi
sed -i "s/^Port .*/Port ${port}/" "${JAILDIR}/$jail/${SSHD_CONFIG}"
notice "${jail} : update port => ${port}"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-reload" "${jail}"
"${LIBDIR}/bkctld-firewall" "${jail}"
fi

18
lib/bkctld-reload Executable file
View file

@ -0,0 +1,18 @@
#!/bin/sh
#
# Reload jail <jailname> or all
# Usage: reload <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to reload inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" || exit 0
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
kill -HUP "${pid}" && notice "${jail} was reloaded [${pid}]"

36
lib/bkctld-remove Executable file
View file

@ -0,0 +1,36 @@
#!/bin/sh
#
# Remove jail <jailname> or all
# Usage: remove <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to remove inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-stop" "${jail}"
rm -f "${CONFDIR}/${jail}"
jail_inode=$(stat --format=%i "${JAILDIR}/${jail}")
if [ "${jail_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${JAILDIR}/${jail}" | debug
else
rm -rf "${JAILDIR}/${jail}" | debug
fi
if [ -d "${INCDIR}/${jail}" ]; then
incs=$(ls "${INCDIR}/${jail}")
for inc in ${incs}; do
inc_inode=$(stat --format=%i "${INCDIR}/${jail}/${inc}")
if [ "${inc_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${INCDIR}/${jail}/${inc}" | debug
else
warning "You need to purge ${INCDIR}/${jail}/${inc} manually !"
fi
done
rmdir --ignore-fail-on-non-empty "${INCDIR}/${jail}" | debug
fi
"${LIBDIR}/bkctld-firewall" "${jail}"
notice "${jail} : deleted jail"

17
lib/bkctld-restart Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
#
# Restart jail <jailname> or all
# Usage: restart <jailname>|all
#
set -eu
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to restart inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-stop" "${jail}"
"${LIBDIR}/bkctld-start" "${jail}"

48
lib/bkctld-rm Executable file
View file

@ -0,0 +1,48 @@
#!/bin/sh
#
# Remove old incremtal inc of all jails
# Usage: rm
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
empty="/tmp/bkctld-${$}-$(date +%N))"
mkdir "${empty}"
pidfile="/var/run/bkctld-rm.pid"
if [ -f "${pidfile}" ]; then
pid=$(cat "${pidfile}")
ps -u "${pid}" >/dev/null
if [ "${?}" -eq 0 ]; then
kill -9 "${pid}"
warning "${0} rm always run (PID ${pid}), killed by ${$} !"
fi
rm "${pidfile}"
fi
echo "${$}" > "${pidfile}"
for jail in $("${LIBDIR}/bkctld-list"); do
incs=$(ls "${INCDIR}/${jail}")
if [ -f "${CONFDIR}/${jail}" ]; then
keepfile="$(mktemp)"
while read j; do
date=$( echo "${j}" | cut -d. -f1 )
before=$( echo "${j}" | cut -d. -f2 )
date -d "$(date "${date}") ${before}" "+%Y-%m-%d"
done < "${CONFDIR}/${jail}" > "${keepfile}"
for j in $(echo "${incs}" | grep -v -f "${keepfile}"); do
start=$(date +"%H:%M:%S")
inc_inode=$(stat --format=%i "${INCDIR}/${jail}/${j}")
if [ "${inc_inode}" -eq 256 ]; then
/bin/btrfs subvolume delete "${INCDIR}/${jail}/${j}" | debug
else
cd "${INCDIR}/${jail}"
rsync -a --delete "${empty}/" "${j}/"
rmdir "${j}"
fi
end=$(date +"%H:%M:%S")
notice "${jail} : deleted ${j} inc [${start}/${end}]"
done
rm "${keepfile}"
fi
done
rmdir "${empty}"
rm "${pidfile}"

42
lib/bkctld-start Executable file
View file

@ -0,0 +1,42 @@
#!/bin/sh
#
# Start jail <jailname> or all
# Usage: start <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to start inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" && exit 0
cd "${JAILDIR}/${jail}"
grep -q "${JAILDIR}/${jail}/proc" /proc/mounts || mount -t proc "proc-${jail}" proc
grep -q "${JAILDIR}/${jail}/dev" /proc/mounts || mount -nt tmpfs "dev-${jail}" dev
[ -e "dev/console" ] || mknod -m 622 dev/console c 5 1
[ -e "dev/null" ] || mknod -m 666 dev/null c 1 3
[ -e "dev/zero" ] || mknod -m 666 dev/zero c 1 5
[ -e "dev/ptmx" ] || mknod -m 666 dev/ptmx c 5 2
[ -e "dev/tty" ] || mknod -m 666 dev/tty c 5 0
[ -e "dev/random" ] || mknod -m 444 dev/random c 1 8
[ -e "dev/urandom" ] || mknod -m 444 dev/urandom c 1 9
chown root:tty dev/console dev/ptmx dev/tty
ln -fs proc/self/fd dev/fd
ln -fs proc/self/fd/0 dev/stdin
ln -fs proc/self/fd/1 dev/stdout
ln -fs proc/self/fd/2 dev/stderr
ln -fs proc/kcore dev/core
mkdir -p dev/pts
mkdir -p dev/shm
grep -q "${JAILDIR}/${jail}/dev/pts" /proc/mounts || mount -t devpts -o gid=4,mode=620 none dev/pts
grep -q "${JAILDIR}/${jail}/dev/shm" /proc/mounts || mount -t tmpfs none dev/shm
chroot "${JAILDIR}/${jail}" /usr/sbin/sshd -E /var/log/authlog || error "${jail} : error on starting sshd"
pidfile="${JAILDIR}/${jail}/${SSHD_PID}"
for try in {1..10}; do
[ -f "${pidfile}" ] || sleep 0.3
done
pid=$(cat "${pidfile}")
notice "${jail} was started [${pid}]"

34
lib/bkctld-stats Executable file
View file

@ -0,0 +1,34 @@
#!/bin/sh
#
# Make and display stats on jails (size, lastconn)
# Usage: stats
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
mkdir -p "${INDEX_DIR}"
lsof "${IDX_FILE}" >/dev/null 2>&1 || nohup sh -s -- <<EOF >/dev/null 2>&1 &
ionice -c3 "${DUC}" index -d "${IDX_FILE}" "${JAILDIR}"
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 !"
printf "Last update of index file : "
stat --format=%Y "${INDEX_DIR}/.lastrun.duc" | xargs -i -n1 date -R -d "@{}"
echo "<jail> <size> <incs> <lastconn>" | awk '{ printf("%- 30s %- 10s %- 10s %- 15s\n", $1, $2, $3, $4); }'
duc_output=$(mktemp)
stat_output=$(mktemp)
incs_output=$(mktemp)
trap "rm ${duc_output} ${incs_output} ${stat_output}" 0
"${DUC}" ls -d "${IDX_FILE}" "${JAILDIR}" > "${duc_output}"
awk '{ print $2 }' "${duc_output}" | while read jail; do
stat --format=%Y "/backup/jails/${jail}/var/log/lastlog" | xargs -i -n1 date -d "@{}" "+%d-%m-%Y" >> "${stat_output}"
inc=0
if [ -f "${CONFDIR}/${jail}" ]; then
day=$(grep -c "day" "${CONFDIR}/${jail}")
month=$(grep -c "month" "${CONFDIR}/${jail}")
inc="${day}/${month}"
fi
echo "${inc}" >> "${incs_output}"
done
paste "${duc_output}" "${incs_output}" "${stat_output}" | awk '{ printf("%- 30s %- 10s %- 10s %- 15s\n", $2, $1, $3, $4); }'

25
lib/bkctld-status Executable file
View file

@ -0,0 +1,25 @@
#!/bin/sh
#
# Print status of <jailname> (default all jail)
# Usage: status [<jailname>]
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : inexistant jail ! Use '$0 status' for list all"
inc="0"
if [ -f "${CONFDIR}/${jail}" ]; then
day=$(grep -c "day" "${CONFDIR}/${jail}")
month=$(grep -c "month" "${CONFDIR}/${jail}")
inc="${day}/${month}"
fi
status="OFF"
"${LIBDIR}/bkctld-is-on" "${jail}" && status="ON "
port=$("${LIBDIR}/bkctld-port" "${jail}")
ip=$("${LIBDIR}/bkctld-ip" "${jail}"|xargs|tr -s ' ' ',')
echo "${jail} ${status} ${port} ${inc} ${ip}" | awk '{ printf("%- 30s %- 10s %- 10s %- 10s %- 40s\n", $1, $2, $3, $4, $5); }'

22
lib/bkctld-stop Executable file
View file

@ -0,0 +1,22 @@
#!/bin/sh
#
# Stop jail <jailname> or all
# Usage: stop <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to stop inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" || exit 0
pid=$(cat "${JAILDIR}/${jail}/${SSHD_PID}")
for conn in $(ps --ppid "${pid}" -o pid=); do
kill "${conn}"
done
kill "${pid}" && notice "${jail} was stopped [${pid}]"
umount --lazy --recursive "${JAILDIR}/${jail}/dev"
umount --lazy "${JAILDIR}/${jail}/proc/"

25
lib/bkctld-sync Executable file
View file

@ -0,0 +1,25 @@
#!/bin/sh
#
# Sync jail <jailname> or all to another node
# Usage: sync <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to sync inexistant jail"
[ -n "${NODE}" ] || error "Sync need config of \$NODE in /etc/default/bkctld !"
jail="${1}"
ssh "${NODE}" "${LIBDIR}/bkctld-init" "${jail}" | debug
rsync -a "${JAILDIR}/${jail}/" "${NODE}:${JAILDIR}/${jail}/" --exclude proc/* --exclude sys/* --exclude dev/* --exclude run --exclude var/backup/*
rsync -a "${CONFDIR}/${jail}" "${NODE}:${CONFDIR}/${jail}"
"${LIBDIR}/bkctld-is-on" "${jail}" && ssh "${NODE}" "${LIBDIR}/bkctld-start" "${jail}" | debug
if [ -n "${FIREWALL_RULES}" ]; then
rsync -a "${FIREWALL_RULES}" "${NODE}:${FIREWALL_RULES}"
ssh "${NODE}" /etc/init.d/minifirewall restart | debug
fi

17
lib/bkctld-update Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
#
# Update jail <jailname> or all
# Usage: update <jailname>|all
#
LIBDIR="$(dirname $0)" && . "${LIBDIR}/config"
jail="${1:-}"
if [ ! -n "${jail}" ]; then
"${LIBDIR}/bkctld-help" && exit 1
fi
[ -d "${JAILDIR}/${jail}" ] || error "${jail} : trying to update inexistant jail"
"${LIBDIR}/bkctld-is-on" "${jail}" && "${LIBDIR}/bkctld-stop" "${jail}"
. "${LIBDIR}/mkjail"
notice "${jail} : updated jail"

64
lib/config Executable file
View file

@ -0,0 +1,64 @@
#!/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:-}"
JAILDIR="${JAILDIR:-/backup/jails}"
INCDIR="${INCDIR:-/backup/incs}"
TPLDIR="${TPLDIR:-/usr/share/bkctld}"
INDEX_DIR="${INDEX_DIR:-/backup/index}"
IDX_FILE="${IDX_FILE:-${INDEX_DIR}/bkctld-jails.idx}"
LOCALTPLDIR="${LOCALTPLDIR:-/usr/local/share/bkctld}"
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)
debug() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 7 ]; then
echo "${msg}"
logger -t bkctld -p daemon.debug "${msg}"
fi
}
info() {
msg="${1:-$(cat /dev/stdin)}"
if [ "${LOGLEVEL}" -ge 6 ]; then
tty -s && echo "${msg}"
logger -t bkctld -p daemon.info "${msg}"
fi
}
notice() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "${msg}"
[ "${LOGLEVEL}" -ge 5 ] && logger -t bkctld -p daemon.notice "${msg}"
}
warning() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "WARNING : ${msg}" >&2
if [ "${LOGLEVEL}" -ge 4 ]; then
tty -s || echo "WARNING : ${msg}" >&2
logger -t bkctld -p daemon.warning "${msg}"
fi
}
error() {
msg="${1:-$(cat /dev/stdin)}"
tty -s && echo "ERROR : ${msg}" >&2
if [ "${LOGLEVEL}" -ge 5 ]; then
tty -s || echo "ERROR : ${msg}" >&2
logger -t bkctld -p daemon.error "${msg}"
fi
exit 1
}

44
lib/mkjail Executable file
View file

@ -0,0 +1,44 @@
#!/bin/sh
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"
umask 077
info "1 - Creating the chroot"
cd "${JAILDIR}/${jail}"
rm -rf bin lib lib64 run usr var/run etc/ssh/*key
mkdir -p dev proc
mkdir -p usr/bin usr/sbin usr/lib usr/lib/x86_64-linux-gnu usr/lib/openssh usr/lib64
mkdir -p etc/ssh var/log run/sshd
mkdir -p root/.ssh var/backup -m 0700
ln -s usr/bin bin
ln -s usr/lib lib
ln -s usr/lib64 lib64
ln -st 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
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}" "${JAILDIR}/${jail}/${dbin}";
for lib in $(ldd "${dbin}" | grep -Eo "/.*so.[0-9\.]+"); do
cp -p "${lib}" "${JAILDIR}/${jail}/${lib}"
done
done

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bats
@test "generate" {
jails="test0 test1 test2 test3 test4 test5"
for jail in ${jails}; do
bkctld init "${jail}"
random=$(od -An -N4 -i < /dev/urandom|grep -Eo "[0-9]{2}$")
date=$(date -d "${random} hour ago")
touch -m --date="${date}" "/backup/jails/${jail}/var/log/lastlog"
done
}

View file

@ -5,129 +5,129 @@ setup() {
date=$(date +"%Y-%m-%d-%H")
inode=$(stat --format=%i /backup)
rm -f /root/bkctld.key* && ssh-keygen -t rsa -N "" -f /root/bkctld.key -q
[ -f /etc/default/bkctld ] && . /etc/default/bkctld
CONFDIR="${CONFDIR:-/etc/evobackup}"
JAILDIR="${JAILDIR:-/backup/jails}"
INCDIR="${INCDIR:-/backup/incs}"
TPLDIR="${TPLDIR:-/usr/share/bkctld}"
LOCALTPLDIR="${LOCALTPLDIR:-/usr/local/share/bkctld}"
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}"
. /usr/lib/bkctld/config
JAILNAME=$(tr -cd '[:alnum:]' < /dev/urandom | fold -w15 | head -n1)
}
teardown() {
bkctld remove all && rm -rf "${INCDIR}/*"
/usr/lib/bkctld/bkctld-remove "${JAILNAME}" && rm -rf "${INCDIR}/*"
}
@test "init" {
bkctld init test
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
inode=$(stat --format=%i /backup)
if [ "${inode}" -eq 256 ]; then
run stat --format=%i "${JAILDIR}/test"
run stat --format=%i "${JAILDIR}/${JAILNAME}"
[ "${output}" -eq 256 ]
else
run test -d "${JAILDIR}/test"
run test -d "${JAILDIR}/${JAILNAME}"
[ "${status}" -eq 0 ]
fi
}
@test "update" {
skip
}
@test "start" {
bkctld init test
bkctld start test
pid=$(cat "${JAILDIR}/test/${SSHD_PID}")
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
pid=$(cat "${JAILDIR}/${JAILNAME}/${SSHD_PID}")
run ps --pid "${pid}"
[ "${status}" -eq 0 ]
}
@test "stop" {
bkctld init test
bkctld start test
pid=$(cat "${JAILDIR}/test/${SSHD_PID}")
bkctld stop test
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
pid=$(cat "${JAILDIR}/${JAILNAME}/${SSHD_PID}")
/usr/lib/bkctld/bkctld-stop "${JAILNAME}"
run ps --pid "${pid}"
[ "${status}" -ne 0 ]
}
@test "reload" {
bkctld init test
bkctld start test
bkctld reload test
run grep "Received SIGHUP; restarting." "${JAILDIR}/test/var/log/authlog"
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
/usr/lib/bkctld/bkctld-reload "${JAILNAME}"
run grep "Received SIGHUP; restarting." "${JAILDIR}/${JAILNAME}/var/log/authlog"
[ "${status}" -eq 0 ]
}
@test "restart" {
bkctld init test
bkctld start test
bpid=$(cat "${JAILDIR}/test/${SSHD_PID}")
bkctld restart test
apid=$(cat "${JAILDIR}/test/${SSHD_PID}")
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
bpid=$(cat "${JAILDIR}/${JAILNAME}/${SSHD_PID}")
/usr/lib/bkctld/bkctld-restart "${JAILNAME}"
apid=$(cat "${JAILDIR}/${JAILNAME}/${SSHD_PID}")
[ "${bpid}" -ne "${apid}" ]
}
@test "status" {
bkctld init test
run bkctld status test
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
run /usr/lib/bkctld/bkctld-status "${JAILNAME}"
[ "${status}" -eq 0 ]
}
@test "key" {
bkctld init test
bkctld start test
bkctld key test /root/bkctld.key.pub
run cat /backup/jails/test/root/.ssh/authorized_keys
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
/usr/lib/bkctld/bkctld-key "${JAILNAME}" /root/bkctld.key.pub
run cat "/backup/jails/${JAILNAME}/root/.ssh/authorized_keys"
[ "${status}" -eq 0 ]
[ "${output}" = $(cat /root/bkctld.key.pub) ]
}
@test "port" {
bkctld init test
bkctld start test
bkctld port test "${port}"
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
/usr/lib/bkctld/bkctld-port "${JAILNAME}" "${port}"
run nc -vz 127.0.0.1 "${port}"
[ "${status}" -eq 0 ]
}
@test "ip" {
skip
}
@test "inc" {
bkctld init test
bkctld inc
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-inc
if [ "${inode}" -eq 256 ]; then
run stat --format=%i "${INCDIR}/test/${date}"
run stat --format=%i "${INCDIR}/${JAILNAME}/${date}"
[ "${output}" -eq 256 ]
else
run test -d "${INCDIR}/test/${date}"
run test -d "${INCDIR}/${JAILNAME}/${date}"
[ "${status}" -eq 0 ]
fi
}
@test "rm" {
skip
}
@test "ssh" {
bkctld init test
bkctld start test
bkctld port test "${port}"
bkctld key test /root/bkctld.key.pub
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
/usr/lib/bkctld/bkctld-port "${JAILNAME}" "${port}"
/usr/lib/bkctld/bkctld-key "${JAILNAME}" /root/bkctld.key.pub
run ssh -p "${port}" -i /root/bkctld.key -oStrictHostKeyChecking=no root@127.0.0.1 ls
[ "$status" -eq 0 ]
}
@test "rsync" {
bkctld init test
bkctld start test
bkctld port test "${port}"
bkctld key test /root/bkctld.key.pub
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
/usr/lib/bkctld/bkctld-start "${JAILNAME}"
/usr/lib/bkctld/bkctld-port "${JAILNAME}" "${port}"
/usr/lib/bkctld/bkctld-key "${JAILNAME}" /root/bkctld.key.pub
run rsync -a -e "ssh -p ${port} -i /root/bkctld.key -oStrictHostKeyChecking=no" /tmp/ root@127.0.0.1:/var/backup/
[ "$status" -eq 0 ]
}
@test "check-ok" {
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
run /usr/lib/bkctld/bkctld-check
[ "$status" -eq 0 ]
}
@test "check-warning" {
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
touch --date="$(date -d -2days)" "/backup/jails/${JAILNAME}/var/log/lastlog"
run /usr/lib/bkctld/bkctld-check
[ "$status" -eq 1 ]
}
@test "check-critical" {
/usr/lib/bkctld/bkctld-init "${JAILNAME}"
touch --date="$(date -d -3days)" "/backup/jails/${JAILNAME}/var/log/lastlog"
run /usr/lib/bkctld/bkctld-check
[ "$status" -eq 2 ]
}

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bats
setup() {
bkctld init test
}
teardown() {
bkctld remove all
}
@test "ok" {
run bkctld check
[ "$status" -eq 0 ]
}
@test "warning" {
touch --date="$(date -d -2days)" /backup/jails/*/var/log/lastlog
run bkctld check
[ "$status" -eq 1 ]
}
@test "critical" {
touch --date="$(date -d -3days)" /backup/jails/*/var/log/lastlog
run bkctld check
[ "$status" -eq 2 ]
}

4
tpl/sshrc Normal file → Executable file
View file

@ -1,5 +1,3 @@
#!/bin/sh
# lastlog -S isn't available in login package on Debian Jessie (need Debian Stretch or superior)
#/usr/bin/lastlog -Su root
/usr/bin/touch /var/log/lastlog
/usr/bin/lastlog -Su root

View file

@ -1,12 +1,26 @@
#!/bin/sh
#
# Script Evobackup client
# See https://forge.evolix.org/projects/evobackup
# See https://gitea.evolix.org/evolix/evobackup
#
# Author: Gregory Colpart <reg@evolix.fr>
# Contributor: 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>
# 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
#
# The following variables must be changed:
# SSH_PORT: The Port used for the ssh(1) jail on the backup server
# MAIL: The email address to send notifications to.
# SRV: The hostname or IP address of the backup server.
#
# You must then uncomment the various
# examples that best suit your case
#
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin