Compare commits

...

79 commits

Author SHA1 Message Date
Jérémy Lecour 859bb800d4 Merge pull request 'fix-tempfiles' (#72) from fix-tempfiles into master
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
Reviewed-on: evolix/evobackup#72
2024-03-22 11:27:34 +01:00
Brice Waegeneire 21533c6fb9 client: Write temporary files in /tmp
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
By default "mktemp" creates a temporary file in the current working directory,
which in the context of Debian's cron is /. To avoid littering /, we add the
option "--tmpdir" that use $TMPDIR or /tmp instead.
2024-03-22 10:05:10 +01:00
Brice Waegeneire 9510546d48 client: Correctly clean up temporary files
The function "build_rsync_main_cmd" is called in a subshell, so it can't
effectively modify it's parent variable "temp_files". To correctly cleanup
those temporary files, we do it when this specific function exits.
2024-03-22 09:56:03 +01:00
Mathieu Trossevin f1d4e6ed9d
fix(includes): Avoid breaking is_btrfs if path doesn't exists
Some checks failed
gitea/evobackup/pipeline/head There was a failure building this commit
This should return with a err code of 1, not result in the entire
command breaking
2024-02-06 15:03:21 +01:00
Jérémy Lecour 491c839014
Add note for includes/excludes brace expansion
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-12-28 15:29:39 +01:00
William Hirigoyen 4298da250b Add WARNING and CRITICAL values to default conf
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-12-19 10:26:30 +01:00
William Hirigoyen d359883700 server: fix install.md paths
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-12-19 09:45:25 +01:00
Bruno TATU 4cd1554780 Correction url dans README.md
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-10-27 11:30:53 +02:00
William Hirigoyen 981f5118ce Ajout exclusion /var/lib/amavis/virusmails
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-06-15 11:18:49 +02:00
Jérémy Lecour 5dac827bb5 invert dry-run logic
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-05-03 22:08:17 +02:00
William Hirigoyen 4807dfbc99 Fix missing dump.rdb in Redis dump compression, which caused compression fail.
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-04-19 17:46:34 +02:00
William Hirigoyen fd9bb57f8b #71538 : fix Redis dump list in case directory is a symlink
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-04-17 17:13:23 +02:00
Alexis Ben Miloud--Josselin d62455cd1a Compress Redis dump
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-04-04 15:05:30 +02:00
Bruno Tatu 72f5900cf3 On backup les données dans les bases
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2023-03-30 16:37:36 +02:00
Jérémy Lecour 05a62e17b5 client: Release 22.12
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 13:37:48 +01:00
Jérémy Lecour 8babc64e0d client: log line with more details
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 13:36:24 +01:00
Jérémy Lecour aa7366ce2e client: separate Rsync for the canary file if the main Rsync has finished without errors
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 11:56:07 +01:00
Jérémy Lecour ef744f77cf client: No more fallback if dump-server-state is missing
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 11:11:02 +01:00
Jérémy Lecour 80426c9ba9 whitespaces 2022-12-27 11:09:37 +01:00
Jérémy Lecour b34ee2c3dc client: use long options for readability 2022-12-27 11:09:11 +01:00
Jérémy Lecour 94b470770f client: use sub shells instead of moving around
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 10:46:53 +01:00
Jérémy Lecour ed0645c9d2 remodel how we build the rsync command (#63)
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
* use a log file for rsync
* build the command argument by argument, without backslashes
* move excludes into a file

Co-authored-by: Jeremy Lecour <jlecour@evolix.fr>
Reviewed-on: evolix/evobackup#63
2022-12-27 10:43:39 +01:00
Jérémy Lecour 140a498e28 client: Only one loop for all redis instances
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-27 10:38:55 +01:00
David Prevot bdd3ef7350 CI: Push *.buildinfo too
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-22 11:39:42 +01:00
Alexis Ben Miloud--Josselin 6e0ab85c6b client: ignore errors when listing instances
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-13 09:53:10 +01:00
David Prevot ff681275e3 CI: Don’t use Drone CI anymore
All checks were successful
gitea/evobackup/pipeline/head This commit looks good
2022-12-12 17:27:36 +01:00
David Prevot 1a892ba002 CI: Actually limit upload to .deb and .changes
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-12-12 17:13:28 +01:00
Alexis Ben Miloud--Josselin e3a969f3e2 client: replace rm ** by find -delete
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
** has no particular meaning in bash unless shopt globstar is
set (it isn't by default).
2022-12-12 16:06:58 +01:00
David Prevot ca4fe4ffb5 CI: Limit upload to .deb and .changes
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-12-12 11:42:19 +01:00
David Prevot 2338c92fe3 CI: Upload to pub2
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-12-12 10:56:13 +01:00
David Prevot d2a76dce9c CI: use build-area instead of outside of scope subdirectory 2022-12-12 10:26:06 +01:00
David Prevot 4476802182 Use sbuild during CI
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-12-09 13:42:44 +01:00
Mathieu Trossevin 1e35aaa4db
Corrige messages d'erreurs intempestif
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-12-08 14:09:37 +01:00
Jérémy Lecour 7b10b56e35 add gobal .gitignore
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
continuous-integration/drone/tag Build is passing
gitea/evobackup/pipeline/tag This commit looks good
2022-11-28 15:27:26 +01:00
Jérémy Lecour c8cfbe18aa server: release 22.11
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
continuous-integration/drone/tag Build is passing
2022-11-28 15:06:53 +01:00
Jérémy Lecour 777b469485 bkctld check-canary: add tests 2022-11-28 15:06:53 +01:00
Jérémy Lecour d3c75ab94e bkctld-stats: filter active jails and columnize the output 2022-11-28 15:06:53 +01:00
Jérémy Lecour 860b982556 better error message 2022-11-28 15:06:53 +01:00
Jérémy Lecour 8ee2aa3b51 bkctld-check-canary: new subcommand to check canary files and content 2022-11-28 15:06:53 +01:00
Alexis Ben Miloud--Josselin 2b83cd71bc postgresql: change wd before dump
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-11-14 11:29:43 +01:00
Jérémy Lecour 88a7907fd3 typo
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
continuous-integration/drone/tag Build is passing
2022-10-28 15:48:59 +02:00
Jérémy Lecour 18e0563377 quotes
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-10-28 14:02:26 +02:00
Jérémy Lecour e921c92cae More explicit warning for rsync comments/spaces 2022-10-27 18:48:35 +02:00
William Hirigoyen aff5dbba95 Add --no-header option for status command.
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-10-27 17:10:14 +02:00
Jérémy Lecour f683691853 client: tolerate absence of mtr or traceroute
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-10-13 09:09:01 +02:00
Eric Morino c769a6e823 Update link to the installation documentation
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-09-05 14:42:06 +02:00
William Hirigoyen 5739b8afe2 Revert "Fix variable reading in case VAR is specified multiple times in file."
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
This reverts commit 89580f2929.
2022-08-26 18:05:14 +02:00
William Hirigoyen 89580f2929 Fix variable reading in case VAR is specified multiple times in file.
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-08-26 10:58:48 +02:00
Alexis Ben Miloud--Josselin c80881debf Exclure datadir MongoDB
All checks were successful
continuous-integration/drone/push Build is passing
gitea/evobackup/pipeline/head This commit looks good
2022-08-24 14:40:30 +02:00
Jérémy Lecour e7b7f50d9d server: release 22.07
Some checks are pending
continuous-integration/drone/push Build is passing
gitea.evolix.org/evobackup/pipeline/head Build started...
gitea/evobackup/pipeline/head This commit looks good
gitea/evobackup/pipeline/tag This commit looks good
continuous-integration/drone/tag Build is passing
2022-07-20 14:31:22 +02:00
Jérémy Lecour a3ca2f0f68 check-setup: check minifirewall version only if minifirewall is present
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-20 14:24:12 +02:00
Jérémy Lecour 1891c98f57 check-setup: use findmnt with mountpoint instead of target 2022-07-20 14:23:13 +02:00
Jérémy Lecour aef2637c1e check-setup: get minifirewall version from internal variable
All checks were successful
continuous-integration/drone/push Build is passing
there is no other backward compatible way :
* really old version don't have a know version
* some versions used to display the version on each command, but it is removed.
* the VERSION variable seems to be the most forward-compatible way
2022-07-20 13:43:33 +02:00
David Prevot f0581fee47 CI: Drop .git directory that was not present during first build
All checks were successful
continuous-integration/drone/push Build is passing
gitea.evolix.org on plain agent00/evobackup/pipeline/head This commit looks good
2022-07-11 14:40:38 +02:00
David Prevot ba4629bee7 server/README.md: tfix
All checks were successful
continuous-integration/drone/push Build is passing
gitea.evolix.org on plain agent00/evobackup/pipeline/head This commit looks good
2022-06-30 16:13:29 +02:00
David Prevot 7901fd1950 Jenkins CI: Improve clean up
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-29 14:43:42 +02:00
David Prevot bdb84e809f CI: Don’t rely on /tmp
/tmp is mounted noexec, so can’t be used to run scripts.
2022-06-29 14:03:24 +02:00
David Prevot 3150d48ba5 server: release 22.06
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
gitea.evolix.org on plain agent00/evobackup/pipeline/head This commit looks good
2022-06-28 16:30:15 +02:00
David Prevot 6c6b64ed34 Update client changelog 2022-06-28 16:26:32 +02:00
David Prevot 604317d4a7 client: tfix (comment) 2022-06-28 16:16:04 +02:00
David Prevot 92ae8a1c7a Jenkins CI: Unconditional clean up 2022-06-28 16:03:14 +02:00
David Prevot 14ca808658 Jenkins CI: Provide .Jenkinsfile as .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
TODO: upload package when it works.
2022-06-28 15:35:17 +02:00
David Prevot 0bea9f24ab CI: Build Bullseye package under Bullseye 2022-06-28 15:34:11 +02:00
David Prevot f13c6404fd Drone CI: fetch tags 2022-06-28 15:32:29 +02:00
David Prevot e14ab10562 Drone CI: don’t sign packages (yet?) 2022-06-28 15:31:28 +02:00
Brice Waegeneire 1bc62d81cd client: fix pt-show-grants error path
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-20 17:52:26 +02:00
Jérémy Lecour ec638ecb3b move from sh to bash
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-15 14:19:50 +02:00
Jérémy Dubois 0d48a8eec3 update-evobackup-canary : do not use GNU date, for it to be compatible with OpenBSD
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-09 16:16:47 +02:00
Alexis Ben Miloud--Josselin 328763380f [elasticsearch] Écrire la sortie de curl dans log normal
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-07 10:38:04 +02:00
Mathieu Trossevin 36f3cccc92
Ajoute support pour findmnt de Debian 8
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-06 15:21:16 +02:00
Jérémy Lecour 417876cbba zzz_evobackup: replace rsync option --verbose by --itemize-changes
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-02 15:53:00 +02:00
Jérémy Lecour 6c2ba1bc79 zzz_evobackup: do not use rsync compression
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-02 14:56:57 +02:00
Jérémy Lecour 400be16e8a Ajout de liens vers les docs de la partie "client"
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-01 10:14:48 +02:00
Jérémy Lecour 7faedbeab1 remove RSYNC_LOGFILE
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-01 09:48:17 +02:00
Jérémy Lecour 86c01a1075 WIP: extract code into functions 2022-06-01 09:48:17 +02:00
Jérémy Dubois c621324845 Use --dump-dir instead of --backup-dir for OpenBSD too
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-18 09:51:34 +02:00
Jérémy Lecour 89b0636cf6 bkctld-init: create "incs/\<jail\>" directory for jails
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-05 11:43:02 +02:00
Jérémy Lecour f9a295daae fix broken test 2022-05-05 11:39:32 +02:00
Jérémy Lecour ed183fb854 Add canary to zzz_evobackup 2022-05-05 11:24:45 +02:00
21 changed files with 924 additions and 479 deletions

33
.Jenkinsfile Normal file
View file

@ -0,0 +1,33 @@
pipeline {
agent { label 'sbuild' }
stages {
stage('Build Debian package') {
when {
branch 'debian'
}
steps {
script {
sh 'gbp buildpackage'
}
archiveArtifacts allowEmptyArchive: true, artifacts: 'build-area/*.gz,build-area/*.bz2,build-area/*.xz,build-area/*.deb,build-area/*.dsc,build-area/*.changes,build-area/*.buildinfo,build-area/*.build,build-area/lintian.txt'
}
}
stage('Upload Debian package') {
when {
branch 'debian'
}
steps {
script {
sh 'rsync -avP build-area/bkctld*.deb build-area/bkctld*.changes build-area/bkctld*.buildinfo pub.evolix.org:/srv/upload/'
}
}
}
}
post {
// Clean after build
always {
cleanWs()
}
}
}

View file

@ -1,41 +0,0 @@
kind: pipeline
name: default
steps:
- name: build debian package
image: evolix/gbp:latest
branches:
- debian
commands:
- mk-build-deps --install --remove debian/control
- git clean --force
- gbp buildpackage
volumes:
- name: tmp
path: /tmp
when:
branch:
- debian
- name: upload debian package
image: drillster/drone-rsync
settings:
hosts: ["pub.evolix.net"]
port: 22
user: droneci
key:
from_secret: drone_private_key
target: /home/droneci/bkctld/
source: /tmp/bkctld/
delete: true
volumes:
- name: tmp
path: /tmp
when:
branch:
- debian
volumes:
- name: tmp
host:
path: /tmp

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.swp
.vagrant

View file

@ -10,18 +10,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
* Use --dump-dir instead of --backup-dir to supress dump-server-state warning
### Deprecated
### Removed
### Fixed
* Make start_time and stop_time compatible with OpenBSD
### Security
## [22.12]
### Changed
* Use --dump-dir instead of --backup-dir to suppress dump-server-state warning
* Do not use rsync compression
* Replace rsync option --verbose by --itemize-changes
* Add canary to zzz_evobackup
* update-evobackup-canary: do not use GNU date, for it to be compatible with OpenBSD
* Add AGPL License and README
* Script now depends on Bash
* tolerate absence of mtr or traceroute
* Only one loop for all Redis instances
* remodel how we build the rsync command
* use sub shells instead of moving around
* Separate Rsync for the canary file if the main Rsync has finished without errors
### Removed
* No more fallback if dump-server-state is missing
### Fixed
* Make start_time and stop_time compatible with OpenBSD
## [22.03]
Split client and server parts of the project

3
client/README.md Normal file
View file

@ -0,0 +1,3 @@
Pour l'installation de `zzz_evobackup`, voir <https://intra.evolix.net/OutilsInternes/EvoBackupClient#installer-et-configurer-le-client-evobackup>
Pour `update-evobackup-canary`, voir <https://intra.evolix.net/OutilsInternes/update-evobackup-canary>

View file

@ -0,0 +1,129 @@
#!/bin/sh
PROGNAME="update-evobackup-canary"
REPOSITORY="https://gitea.evolix.org/evolix/evobackup"
VERSION="22.06"
readonly VERSION
# base functions
show_version() {
cat <<END
${PROGNAME} version ${VERSION}
Copyright 2022 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>,
and others.
${REPOSITORY}
${PROGNAME} comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU General Public License v3.0 for details.
END
}
show_help() {
cat <<END
${PROGNAME} is updating a canary file for evobackup.
Usage: ${PROGNAME} [OPTIONS]
Main options
-w, --who who has updated the file (default: logname())
-f, --file path of the canary file (default: /zzz_evobackup_canary)
-V, --version print version and exit
-h, --help print this message and exit
END
}
main() {
if [ -z "${who:-}" ]; then
who=$(logname)
fi
if [ -z "${canary_file:-}" ]; then
canary_file="/zzz_evobackup_canary"
fi
# This option is supported both on OpenBSD which does not use GNU date and on Debian
date=$(date "+%FT%T%z")
printf "%s %s\n" "${date}" "${who}" >> "${canary_file}"
}
# parse options
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case $1 in
-h|-\?|--help)
show_help
exit 0
;;
-V|--version)
show_version
exit 0
;;
-w|--who)
# with value separated by space
if [ -n "$2" ]; then
who=$2
shift
else
printf 'ERROR: "-w|--who" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--who=?*)
# with value speparated by =
who=${1#*=}
;;
--who=)
# without value
printf 'ERROR: "--who" requires a non-empty option argument.\n' >&2
exit 1
;;
-f|--file)
# with value separated by space
if [ -n "$2" ]; then
canary_file=$2
shift
else
printf 'ERROR: "-f|--file" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--file=?*)
# with value speparated by =
canary_file=${1#*=}
;;
--file=)
# without value
printf 'ERROR: "--file" requires a non-empty option argument.\n' >&2
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*)
# ignore unknown options
printf 'WARN: Unknown option : %s\n' "$1" >&2
exit 1
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
export LC_ALL=C
set -u
main

View file

@ -1,26 +1,24 @@
#!/bin/sh
#!/bin/bash
#
# Script Evobackup client
# See https://gitea.evolix.org/evolix/evobackup
#
# Author: Gregory Colpart <reg@evolix.fr>
# Contributors:
# Romain Dessort <rdessort@evolix.fr>
# Benoît Série <bserie@evolix.fr>
# Tristan Pilat <tpilat@evolix.fr>
# Victor Laborie <vlaborie@evolix.fr>
# Jérémy Lecour <jlecour@evolix.fr>
# Authors: Evolix <info@evolix.fr>,
# Gregory Colpart <reg@evolix.fr>,
# Romain Dessort <rdessort@evolix.fr>,
# Benoit Série <bserie@evolix.fr>,
# Tristan Pilat <tpilat@evolix.fr>,
# Victor Laborie <vlaborie@evolix.fr>,
# Jérémy Lecour <jlecour@evolix.fr>
# and others.
#
# Licence: AGPLv3
#
# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES
# Fail on unassigned variables
set -u
##### Configuration ###################################################
VERSION="22.03"
VERSION="22.12"
# email adress for notifications
MAIL=jdoe@example.com
@ -28,7 +26,10 @@ MAIL=jdoe@example.com
# list of hosts (hostname or IP) and SSH port for Rsync
SERVERS="node0.backup.example.com:2XXX node1.backup.example.com:2XXX"
# Should we fallback on servers when the first is unreachable ?
# explicit PATH
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
# Should we fallback on other servers when the first one is unreachable?
SERVERS_FALLBACK=${SERVERS_FALLBACK:-1}
# timeout (in seconds) for SSH connections
@ -47,31 +48,486 @@ PIDFILE="/var/run/${PROGNAME}.pid"
# Customize the log path if you have multiple scripts and with separate logs
LOGFILE="/var/log/evobackup.log"
# Enable/Disable tasks
LOCAL_TASKS=${LOCAL_TASKS:-1}
SYNC_TASKS=${SYNC_TASKS:-1}
# Full Rsync log file, reset each time
RSYNC_LOGFILE="/var/log/${PROGNAME}.rsync.log"
HOSTNAME=$(hostname)
##### SETUP AND FUNCTIONS #############################################
START_EPOCH=$(/bin/date +%s)
DATE_FORMAT="%Y-%m-%d %H:%M:%S"
# shellcheck disable=SC2174
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
# Enable/disable local tasks (default: enabled)
: "${LOCAL_TASKS:=1}"
# Enable/disable sync tasks (default: enabled)
: "${SYNC_TASKS:=1}"
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
CANARY_FILE="/zzz_evobackup_canary"
## lang = C for english outputs
export LANGUAGE=C
export LANG=C
# Source paths can be customized
# Empty lines, and lines containing # or ; are ignored
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
RSYNC_INCLUDES="
/etc
/root
/var
/home
"
## Force umask
umask 077
# Excluded paths can be customized
# Empty lines, and lines beginning with # or ; are ignored
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
RSYNC_EXCLUDES="
/dev
/proc
/run
/sys
/tmp
/usr/doc
/usr/obj
/usr/share/doc
/usr/src
/var/apt
/var/cache
'/var/db/munin/*.tmp'
/var/lib/amavis/amavisd.sock
/var/lib/amavis/tmp
/var/lib/amavis/virusmails
'/var/lib/clamav/*.tmp'
/var/lib/elasticsearch
/var/lib/metche
/var/lib/mongodb
'/var/lib/munin/*tmp*'
/var/lib/mysql
/var/lib/php/sessions
/var/lib/php5
/var/lib/postgres
/var/lib/postgresql
/var/lib/sympa
/var/lock
/var/run
/var/spool/postfix
/var/spool/smtpd
/var/spool/squid
/var/state
/var/tmp
lost+found
'.nfs.*'
'lxc/*/rootfs/tmp'
'lxc/*/rootfs/usr/doc'
'lxc/*/rootfs/usr/obj'
'lxc/*/rootfs/usr/share/doc'
'lxc/*/rootfs/usr/src'
'lxc/*/rootfs/var/apt'
'lxc/*/rootfs/var/cache'
'lxc/*/rootfs/var/lib/php5'
'lxc/*/rootfs/var/lib/php/sessions'
'lxc/*/rootfs/var/lock'
'lxc/*/rootfs/var/run'
'lxc/*/rootfs/var/state'
'lxc/*/rootfs/var/tmp'
/home/mysqltmp
"
## Initialize variable to store SSH connection errors
SERVERS_SSH_ERRORS=""
##### FUNCTIONS #######################################################
local_tasks() {
log "START LOCAL_TASKS"
# You can comment or uncomment sections below to customize the backup
## OpenLDAP : example with slapcat
# slapcat -n 0 -l ${LOCAL_BACKUP_DIR}/config.ldap.bak
# slapcat -n 1 -l ${LOCAL_BACKUP_DIR}/data.ldap.bak
# slapcat -l ${LOCAL_BACKUP_DIR}/ldap.bak
## MySQL
## Purge previous dumps
# rm -f ${LOCAL_BACKUP_DIR}/mysql.*.gz
# rm -rf ${LOCAL_BACKUP_DIR}/mysql
# rm -rf ${LOCAL_BACKUP_DIR}/mysqlhotcopy
# rm -rf /home/mysqldump
# find ${LOCAL_BACKUP_DIR}/ -type f -name '*.err' -delete
## example with global and compressed mysqldump
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
# --opt --all-databases --force --events --hex-blob 2> ${LOCAL_BACKUP_DIR}/mysql.bak.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.bak.gz
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (global compressed) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql.bak.err"
# rc=101
# fi
## example with compressed SQL dump (with data) for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
# | grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)"); do
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --events --hex-blob $i 2> ${LOCAL_BACKUP_DIR}/${i}.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql/${i}.sql.gz
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (${i} compressed) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/${i}.err"
# rc=102
# fi
# done
## Dump all grants (requires 'percona-toolkit' package)
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# pt-show-grants --flush --no-header 2> ${LOCAL_BACKUP_DIR}/mysql/all_grants.err > ${LOCAL_BACKUP_DIR}/mysql/all_grants.sql
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "pt-show-grants returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql/all_grants.err"
# rc=103
# fi
# Dump all variables
# mysql -A -e"SHOW GLOBAL VARIABLES;" 2> ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.err > ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.txt
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysql (variables) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.err"
# rc=104
# fi
## example with SQL dump (schema only, no data) for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
# | grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)"); do
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --no-data --databases $i 2> ${LOCAL_BACKUP_DIR}/${i}.schema.err > ${LOCAL_BACKUP_DIR}/mysql/${i}.schema.sql
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (${i} schema) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/${i}.schema.err"
# rc=105
# fi
# done
## example with *one* uncompressed SQL dump for *one* database (MYBASE)
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/MYBASE
# chown -RL mysql ${LOCAL_BACKUP_DIR}/mysql/
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -Q \
# --opt --events --hex-blob --skip-comments -T ${LOCAL_BACKUP_DIR}/mysql/MYBASE MYBASE 2> ${LOCAL_BACKUP_DIR}/mysql/MYBASE.err
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (MYBASE) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql/MYBASE.err"
# rc=106
# fi
## example with two dumps for each table (.sql/.txt) for all databases
# for i in $(echo SHOW DATABASES | mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
# | grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)" ); do
# mkdir -p -m 700 /home/mysqldump/$i ; chown -RL mysql /home/mysqldump
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 -Q --opt --events --hex-blob --skip-comments \
# --fields-enclosed-by='\"' --fields-terminated-by=',' -T /home/mysqldump/$i $i 2> /home/mysqldump/$i.err"
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (${i} files) returned an error ${last_rc}, check /home/mysqldump/$i.err"
# rc=107
# fi
# done
## example with mysqlhotcopy
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysqlhotcopy/
# mysqlhotcopy MYBASE ${LOCAL_BACKUP_DIR}/mysqlhotcopy/ 2> ${LOCAL_BACKUP_DIR}/mysqlhotcopy/MYBASE.err
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqlhotcopy returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysqlhotcopy/MYBASE.err"
# rc=108
# fi
## example for multiples MySQL instances
# mysqladminpasswd=$(grep -m1 'password = .*' /root/.my.cnf|cut -d" " -f3)
# grep --extended-regexp "^port\s*=\s*\d*" /etc/mysql/my.cnf | while read instance; do
# instance=$(echo "$instance"|awk '{ print $3 }')
# if [ "$instance" != "3306" ]
# then
# mysqldump -P $instance --opt --all-databases --hex-blob -u mysqladmin -p$mysqladminpasswd 2> ${LOCAL_BACKUP_DIR}/mysql.${instance}.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.${instance}.bak.gz
# last_rc=$?
# if [ ${last_rc} -ne 0 ]; then
# error "mysqldump (instance ${instance}) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql.${instance}.err"
# rc=107
# fi
# fi
# done
## PostgreSQL
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/pg.*.gz
# rm -rf ${LOCAL_BACKUP_DIR}/pg-backup.tar
# rm -rf ${LOCAL_BACKUP_DIR}/postgresql/*
## example with pg_dumpall (warning: you need space in ~postgres)
# su - postgres -c "pg_dumpall > ~/pg.dump.bak"
# mv ~postgres/pg.dump.bak ${LOCAL_BACKUP_DIR}/
## another method with gzip directly piped
# (
# cd /var/lib/postgresql;
# sudo -u postgres pg_dumpall | gzip > ${LOCAL_BACKUP_DIR}/pg.dump.bak.gz
# )
## example with all tables from MYBASE excepts TABLE1 and TABLE2
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -t 'TABLE1' -t 'TABLE2' MYBASE
## example with only TABLE1 and TABLE2 from MYBASE
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -T 'TABLE1' -T 'TABLE2' MYBASE
## example with compressed PostgreSQL dump for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/postgresql
# chown postgres:postgres ${LOCAL_BACKUP_DIR}/postgresql
# (
# cd /var/lib/postgresql
# dbs=$(sudo -u postgres psql -U postgres -lt | awk -F\| '{print $1}' |grep -v template*)
# for databases in $dbs ; do sudo -u postgres /usr/bin/pg_dump --create -U postgres -d $databases | gzip --best -c > ${LOCAL_BACKUP_DIR}/postgresql/$databases.sql.gz ; done
# )
## MongoDB
## don't forget to create use with read-only access
## > use admin
## > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/mongodump/
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mongodump/
# mongodump --quiet -u mongobackup -pPASS -o ${LOCAL_BACKUP_DIR}/mongodump/
# if [ $? -ne 0 ]; then
# echo "Error with mongodump!"
# fi
## Redis
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/redis/
# rm -rf ${LOCAL_BACKUP_DIR}/redis-*
## Copy dump.rdb file for each found instance
# for instance in $(find /var/lib/ -mindepth 1 -maxdepth 1 '(' -type d -o -type l ')' -name 'redis*'); do
# if [ -f "${instance}/dump.rdb" ]; then
# name=$(basename $instance)
# mkdir -p ${LOCAL_BACKUP_DIR}/${name}
# cp -a "${instance}/dump.rdb" "${LOCAL_BACKUP_DIR}/${name}"
# gzip "${LOCAL_BACKUP_DIR}/${name}/dump.rdb"
# fi
# done
## ElasticSearch
## Take a snapshot as a backup.
## Warning: You need to have a path.repo configured.
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" >> "${LOGFILE}"
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" >> "${LOGFILE}"
## Clustered version here
## It basically the same thing except that you need to check that NFS is mounted
# if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2'
# then
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" >> "${LOGFILE}"
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" >> "${LOGFILE}"
# else
# echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
# fi
## If you need to keep older snapshot, for example the last 10 daily snapshots, replace the XDELETE and XPUT lines by :
# for snapshot in $(curl -s -XGET "localhost:9200/_snapshot/snaprepo/_all?pretty=true" | grep -Eo 'snapshot_[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n -10); do
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/${snapshot}" | grep -v -Fx '{"acknowledged":true}'
# done
# date=$(/bin/date +%F)
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" >> "${LOGFILE}"
## RabbitMQ
## export config
# rabbitmqadmin export ${LOCAL_BACKUP_DIR}/rabbitmq.config >> "${LOGFILE}"
## MegaCli config
# megacli -CfgSave -f ${LOCAL_BACKUP_DIR}/megacli_conf.dump -a0 >/dev/null
## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
network_targets="8.8.8.8 www.evolix.fr travaux.evolix.net"
mtr_bin=$(command -v mtr)
if [ -n "${mtr_bin}" ]; then
for addr in ${network_targets}; do
${mtr_bin} -r "${addr}" > "${LOCAL_BACKUP_DIR}/mtr-${addr}"
done
fi
traceroute_bin=$(command -v traceroute)
if [ -n "${traceroute_bin}" ]; then
for addr in ${network_targets}; do
${traceroute_bin} -n "${addr}" > "${LOCAL_BACKUP_DIR}/traceroute-${addr}" 2>&1
done
fi
server_state_dir="${LOCAL_BACKUP_DIR}/server-state"
dump_server_state_bin=$(command -v dump-server-state)
if [ -z "${dump_server_state_bin}" ]; then
error "dump-server-state is missing"
rc=1
else
if [ "${SYSTEM}" = "linux" ]; then
${dump_server_state_bin} --all --force --dump-dir "${server_state_dir}"
last_rc=$?
if [ ${last_rc} -ne 0 ]; then
error "dump-server-state returned an error ${last_rc}, check ${server_state_dir}"
rc=1
fi
else
${dump_server_state_bin} --all --force --dump-dir "${server_state_dir}"
last_rc=$?
if [ ${last_rc} -ne 0 ]; then
error "dump-server-state returned an error ${last_rc}, check ${server_state_dir}"
rc=1
fi
fi
fi
## Dump rights
# getfacl -R /var > ${server_state_dir}/rights-var.txt
# getfacl -R /etc > ${server_state_dir}/rights-etc.txt
# getfacl -R /usr > ${server_state_dir}/rights-usr.txt
# getfacl -R /home > ${server_state_dir}/rights-home.txt
log "STOP LOCAL_TASKS"
}
build_rsync_main_cmd() {
###################################################################
# /!\ WARNING /!\ WARNING /!\ WARNING /!\ WARNING /!\ WARNING /!\ #
###################################################################
# DO NOT USE COMMENTS in rsync lines #
# DO NOT ADD WHITESPACES AFTER \ in rsync lines #
# It breaks the command and destroys data #
# You should not modify this, unless you are really REALLY sure #
###################################################################
# Create a temp file for excludes and includes
includes_file="$(mktemp --tmpdir "${PROGNAME}.includes.XXXXXX")"
excludes_file="$(mktemp --tmpdir "${PROGNAME}.excludes.XXXXXX")"
# … and add them to the list of files to delete at exit
temp_files="${includes_file} ${excludes_file}"
trap "rm -f ${temp_files}" EXIT
# Store includes/excludes in files
# without blank lines of comments (# or ;)
echo "${RSYNC_INCLUDES}" | sed -e 's/\s*\(#\|;\).*//; /^\s*$/d' > "${includes_file}"
echo "${RSYNC_EXCLUDES}" | sed -e 's/\s*\(#\|;\).*//; /^\s*$/d' > "${excludes_file}"
# Rsync command
cmd="$(command -v rsync)"
# Rsync main options
cmd="${cmd} --archive"
cmd="${cmd} --itemize-changes"
cmd="${cmd} --quiet"
cmd="${cmd} --stats"
cmd="${cmd} --human-readable"
cmd="${cmd} --relative"
cmd="${cmd} --partial"
cmd="${cmd} --delete"
cmd="${cmd} --delete-excluded"
cmd="${cmd} --force"
cmd="${cmd} --ignore-errors"
cmd="${cmd} --log-file=${RSYNC_LOGFILE}"
cmd="${cmd} --rsh='ssh -p ${SSH_PORT} -o \"ConnectTimeout ${SSH_CONNECT_TIMEOUT}\"'"
# Rsync excludes
while read line ; do
cmd="${cmd} --exclude ${line}"
done < "${excludes_file}"
# Rsync local sources
cmd="${cmd} ${default_includes}"
while read line ; do
cmd="${cmd} ${line}"
done < "${includes_file}"
# Rsync remote destination
cmd="${cmd} root@${SSH_SERVER}:/var/backup/"
# output final command
echo "${cmd}"
}
build_rsync_canary_cmd() {
# Rsync command
cmd="$(command -v rsync)"
# Rsync options
cmd="${cmd} --rsh='ssh -p ${SSH_PORT} -o \"ConnectTimeout ${SSH_CONNECT_TIMEOUT}\"'"
# Rsync local source
cmd="${cmd} ${CANARY_FILE}"
# Rsync remote destination
cmd="${cmd} root@${SSH_SERVER}:/var/backup/"
# output final command
echo "${cmd}"
}
sync_tasks() {
n=0
server=""
if [ "${SERVERS_FALLBACK}" = "1" ]; then
# We try to find a suitable server
while :; do
server=$(pick_server "${n}")
test $? = 0 || exit 2
if test_server "${server}"; then
break
else
server=""
n=$(( n + 1 ))
fi
done
else
# we force the server
server=$(pick_server "${n}")
fi
SSH_SERVER=$(echo "${server}" | cut -d':' -f1)
SSH_PORT=$(echo "${server}" | cut -d':' -f2)
log "START SYNC_TASKS - server=${server}"
# default paths, depending on system
if [ "${SYSTEM}" = "linux" ]; then
default_includes="/bin /boot /lib /opt /sbin /usr"
else
default_includes="/bsd /bin /sbin /usr"
fi
# reset Rsync log file
if [ -n "$(command -v truncate)" ]; then
truncate -s 0 "${RSYNC_LOGFILE}"
else
printf "" > "${RSYNC_LOGFILE}"
fi
# Build the final Rsync command
rsync_main_cmd=$(build_rsync_main_cmd)
# … log it
log "SYNC_TASKS - Rsync main command : ${rsync_main_cmd}"
# … execute it
eval "${rsync_main_cmd}"
rsync_main_rc=$?
# Copy last lines of rsync log to the main log
tail -n 30 "${RSYNC_LOGFILE}" >> "${LOGFILE}"
if [ ${rsync_main_rc} -ne 0 ]; then
error "rsync returned an error ${rsync_main_rc}, check ${LOGFILE}"
rc=201
else
# Build the canary Rsync command
rsync_canary_cmd=$(build_rsync_canary_cmd)
# … log it
log "SYNC_TASKS - Rsync canary command : ${rsync_canary_cmd}"
# … execute it
eval "${rsync_canary_cmd}"
fi
log "STOP SYNC_TASKS - server=${server}"
}
# Call test_server with "HOST:PORT" string
# It will return with 0 if the server is reachable.
@ -133,388 +589,89 @@ log() {
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
>> "${LOGFILE}"
}
error() {
msg="${1:-$(cat /dev/stdin)}"
pid=$$
printf "[%s] %s[%s]: %s\\n" \
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
>&2
}
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
main() {
START_EPOCH=$(/bin/date +%s)
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
## Verify other evobackup process and kill if needed
if [ -e "${PIDFILE}" ]; then
pid=$(cat "${PIDFILE}")
# Does process still exist ?
if kill -0 "${pid}" 2> /dev/null; then
# Killing the childs of evobackup.
for ppid in $(pgrep -P "${pid}"); do
kill -9 "${ppid}";
done
# Then kill the main PID.
kill -9 "${pid}"
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
else
rm -f "${PIDFILE}"
fi
fi
echo "$$" > "${PIDFILE}"
# shellcheck disable=SC2064
trap "rm -f ${PIDFILE}" EXIT
# shellcheck disable=SC2174
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
##### LOCAL BACKUP ####################################################
## Force umask
umask 077
if [ "${LOCAL_TASKS}" = "1" ]; then
log "START LOCAL_TASKS"
## Initialize variable to store SSH connection errors
SERVERS_SSH_ERRORS=""
# You can comment or uncomment sections below to customize the backup
## OpenLDAP : example with slapcat
# slapcat -n 0 -l ${LOCAL_BACKUP_DIR}/config.ldap.bak
# slapcat -n 1 -l ${LOCAL_BACKUP_DIR}/data.ldap.bak
# slapcat -l ${LOCAL_BACKUP_DIR}/ldap.bak
## MySQL
## Purge previous dumps
# rm -f ${LOCAL_BACKUP_DIR}/mysql.*.gz
# rm -rf ${LOCAL_BACKUP_DIR}/mysql
# rm -rf ${LOCAL_BACKUP_DIR}/mysqlhotcopy
# rm -rf /home/mysqldump
## example with global and compressed mysqldump
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
# --opt --all-databases --force --events --hex-blob | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.bak.gz
## example with compressed SQL dump (with data) for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
# | egrep -v "^(Database|information_schema|performance_schema|sys)"); do
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --events --hex-blob $i | gzip --best > ${LOCAL_BACKUP_DIR}/mysql/${i}.sql.gz
# done
## Dump all grants (requires 'percona-toolkit' package)
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# pt-show-grants --flush --no-header > ${LOCAL_BACKUP_DIR}/mysql/all_grants.sql
# Dump all variables
# mysql -A -e"SHOW GLOBAL VARIABLES;" > ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.txt
## example with SQL dump (schema only, no data) for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
# | egrep -v "^(Database|information_schema|performance_schema|sys)"); do
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --no-data --databases $i > ${LOCAL_BACKUP_DIR}/mysql/${i}.schema.sql
# done
## example with *one* uncompressed SQL dump for *one* database (MYBASE)
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/MYBASE
# chown -RL mysql ${LOCAL_BACKUP_DIR}/mysql/
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -Q \
# --opt --events --hex-blob --skip-comments -T ${LOCAL_BACKUP_DIR}/mysql/MYBASE MYBASE
## example with two dumps for each table (.sql/.txt) for all databases
# for i in $(echo SHOW DATABASES | mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
# | egrep -v "^(Database|information_schema|performance_schema|sys)" ); \
# do mkdir -p -m 700 /home/mysqldump/$i ; chown -RL mysql /home/mysqldump ; \
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 -Q --opt --events --hex-blob --skip-comments \
# --fields-enclosed-by='\"' --fields-terminated-by=',' -T /home/mysqldump/$i $i; done
## example with mysqlhotcopy
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysqlhotcopy/
# mysqlhotcopy MYBASE ${LOCAL_BACKUP_DIR}/mysqlhotcopy/
## example for multiples MySQL instances
# mysqladminpasswd=$(grep -m1 'password = .*' /root/.my.cnf|cut -d" " -f3)
# grep -E "^port\s*=\s*\d*" /etc/mysql/my.cnf |while read instance; do
# instance=$(echo "$instance"|awk '{ print $3 }')
# if [ "$instance" != "3306" ]
# then
# mysqldump -P $instance --opt --all-databases --hex-blob -u mysqladmin -p$mysqladminpasswd | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.$instance.bak.gz
# fi
# done
## PostgreSQL
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/pg.*.gz
# rm -rf ${LOCAL_BACKUP_DIR}/pg-backup.tar
# rm -rf ${LOCAL_BACKUP_DIR}/postgresql/*
## example with pg_dumpall (warning: you need space in ~postgres)
# su - postgres -c "pg_dumpall > ~/pg.dump.bak"
# mv ~postgres/pg.dump.bak ${LOCAL_BACKUP_DIR}/
## another method with gzip directly piped
# cd /var/lib/postgresql
# sudo -u postgres pg_dumpall | gzip > ${LOCAL_BACKUP_DIR}/pg.dump.bak.gz
# cd - > /dev/null
## example with all tables from MYBASE excepts TABLE1 and TABLE2
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -t 'TABLE1' -t 'TABLE2' MYBASE
## example with only TABLE1 and TABLE2 from MYBASE
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -T 'TABLE1' -T 'TABLE2' MYBASE
## example with compressed PostgreSQL dump for each databases
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/postgresql
# chown postgres:postgres ${LOCAL_BACKUP_DIR}/postgresql
# dbs=$(sudo -u postgres psql -U postgres -lt | awk -F\| '{print $1}' |grep -v template*)
#
# for databases in $dbs ; do sudo -u postgres /usr/bin/pg_dump --create -s -U postgres -d $databases | gzip --best -c > ${LOCAL_BACKUP_DIR}/postgresql/$databases.sql.gz ; done
## MongoDB
## don't forget to create use with read-only access
## > use admin
## > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/mongodump/
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mongodump/
# mongodump --quiet -u mongobackup -pPASS -o ${LOCAL_BACKUP_DIR}/mongodump/
# if [ $? -ne 0 ]; then
# echo "Error with mongodump!"
# fi
## Redis
## Purge previous dumps
# rm -rf ${LOCAL_BACKUP_DIR}/redis/
# rm -rf ${LOCAL_BACKUP_DIR}/redis-*
## example with copy .rdb file
## for the default instance :
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/redis/
# cp /var/lib/redis/dump.rdb ${LOCAL_BACKUP_DIR}/redis/
## for multiple instances :
# for instance in $(ls -d /var/lib/redis-*); do
# name=$(basename $instance)
# mkdir -p ${LOCAL_BACKUP_DIR}/${name}
# cp -a ${instance}/dump.rdb ${LOCAL_BACKUP_DIR}/${name}
# done
## ElasticSearch
## Take a snapshot as a backup.
## Warning: You need to have a path.repo configured.
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" -o /tmp/es_delete_snapshot.daily.log
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" -o /tmp/es_snapshot.daily.log
## Clustered version here
## It basically the same thing except that you need to check that NFS is mounted
# if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2'
# then
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" -o /tmp/es_delete_snapshot.daily.log
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" -o /tmp/es_snapshot.daily.log
# else
# echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
# fi
## If you need to keep older snapshot, for example the last 10 daily snapshots, replace the XDELETE and XPUT lines by :
# for snapshot in $(curl -s -XGET "localhost:9200/_snapshot/snaprepo/_all?pretty=true" | grep -Eo 'snapshot_[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n -10); do
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/${snapshot}" | grep -v -Fx '{"acknowledged":true}'
# done
# date=$(/bin/date +%F)
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log
## RabbitMQ
## export config
#rabbitmqadmin export ${LOCAL_BACKUP_DIR}/rabbitmq.config >> $LOGFILE
## MegaCli config
#megacli -CfgSave -f ${LOCAL_BACKUP_DIR}/megacli_conf.dump -a0 >/dev/null
## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
for addr in 8.8.8.8 www.evolix.fr travaux.evolix.net; do
mtr -r ${addr} > ${LOCAL_BACKUP_DIR}/mtr-${addr}
traceroute -n ${addr} > ${LOCAL_BACKUP_DIR}/traceroute-${addr} 2>&1
done
server_state_dir="${LOCAL_BACKUP_DIR}/server-state"
dump_server_state_bin=$(command -v dump-server-state)
if [ "${SYSTEM}" = "linux" ]; then
if [ -n "${dump_server_state_bin}" ]; then
${dump_server_state_bin} --all --force --dump-dir "${server_state_dir}"
else
mkdir -p "${server_state_dir}"
## Dump system and kernel versions
uname -a > ${server_state_dir}/uname.txt
## Dump process with ps
ps auwwx > ${server_state_dir}/ps.txt
## Dump network connections with ss
ss -taupen > ${server_state_dir}/netstat.txt
## List Debian packages
dpkg -l > ${server_state_dir}/packages
dpkg --get-selections > ${server_state_dir}/packages.getselections
apt-cache dumpavail > ${server_state_dir}/packages.available
## Dump iptables
if [ -x /sbin/iptables ]; then
{ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${server_state_dir}/iptables.txt
fi
## Dump findmnt(8) output
FINDMNT_BIN=$(command -v findmnt)
if [ -x "${FINDMNT_BIN}" ]; then
${FINDMNT_BIN} > ${server_state_dir}/findmnt.txt
fi
## Dump MBR / table partitions
disks=$(lsblk -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | awk '{print $1}')
for disk in ${disks}; do
dd if="/dev/${disk}" of="${server_state_dir}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)"
fdisk -l "/dev/${disk}" > "${server_state_dir}/partitions-${disk}" 2>&1
## Verify other evobackup process and kill if needed
if [ -e "${PIDFILE}" ]; then
pid=$(cat "${PIDFILE}")
# Does process still exist ?
if kill -0 "${pid}" 2> /dev/null; then
# Killing the childs of evobackup.
for ppid in $(pgrep -P "${pid}"); do
kill -9 "${ppid}";
done
cat ${server_state_dir}/partitions-* > ${server_state_dir}/partitions
fi
else
if [ -n "${dump_server_state_bin}" ]; then
${dump_server_state_bin} --all --force --backup-dir "${server_state_dir}"
# Then kill the main PID.
kill -9 "${pid}"
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
else
mkdir -p "${server_state_dir}"
## Dump system and kernel versions
uname -a > ${server_state_dir}/uname
## Dump process with ps
ps auwwx > ${server_state_dir}/ps.out
## Dump network connections with fstat
fstat | head -1 > ${server_state_dir}/netstat.out
fstat | grep internet >> ${server_state_dir}/netstat.out
## List OpenBSD packages
pkg_info -m > ${server_state_dir}/packages
## Dump MBR / table partitions
disklabel sd0 > ${server_state_dir}/partitions
## Dump pf infos
pfctl -sa > ${server_state_dir}/pfctl-sa.txt
rm -f "${PIDFILE}"
fi
fi
echo "$$" > "${PIDFILE}"
## Dump rights
#getfacl -R /var > ${server_state_dir}/rights-var.txt
#getfacl -R /etc > ${server_state_dir}/rights-etc.txt
#getfacl -R /usr > ${server_state_dir}/rights-usr.txt
#getfacl -R /home > ${server_state_dir}/rights-home.txt
# Initialize a list of files to delete at exit
# Any file added to the list will also be deleted at exit
temp_files="${PIDFILE}"
log "STOP LOCAL_TASKS"
fi
# shellcheck disable=SC2064
trap "rm -f ${temp_files}" EXIT
##### REMOTE BACKUP ###################################################
# Update canary to keep track of each run
update-evobackup-canary --who "${PROGNAME}"
if [ "${SYNC_TASKS}" = "1" ]; then
n=0
server=""
if [ "${SERVERS_FALLBACK}" = "1" ]; then
# We try to find a suitable server
while :; do
server=$(pick_server "${n}")
test $? = 0 || exit 2
if test_server "${server}"; then
break
else
server=""
n=$(( n + 1 ))
fi
done
else
# we force the server
server=$(pick_server "${n}")
if [ "${LOCAL_TASKS}" = "1" ]; then
local_tasks
fi
SSH_SERVER=$(echo "${server}" | cut -d':' -f1)
SSH_PORT=$(echo "${server}" | cut -d':' -f2)
if [ "${SYSTEM}" = "linux" ]; then
rep="/bin /boot /lib /opt /sbin /usr"
else
rep="/bsd /bin /sbin /usr"
if [ "${SYNC_TASKS}" = "1" ]; then
sync_tasks
fi
log "START SYNC_TASKS - server=${server}"
STOP_EPOCH=$(/bin/date +%s)
# /!\ DO NOT USE COMMENTS in the rsync command /!\
# It breaks the command and destroys data, simply remove (or add) lines.
if [ "${SYSTEM}" = "openbsd" ]; then
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
else
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
fi
duration=$(( STOP_EPOCH - START_EPOCH ))
# Remote shell command
RSH_COMMAND="ssh -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'"
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
# ignore check because we want it to split the different arguments to $rep
# shellcheck disable=SC2086
rsync -avzh --relative --stats --delete --delete-excluded --force --ignore-errors --partial \
--exclude "dev" \
--exclude "lost+found" \
--exclude ".nfs.*" \
--exclude "/usr/doc" \
--exclude "/usr/obj" \
--exclude "/usr/share/doc" \
--exclude "/usr/src" \
--exclude "/var/apt" \
--exclude "/var/cache" \
--exclude "/var/lib/amavis/amavisd.sock" \
--exclude "/var/lib/amavis/tmp" \
--exclude "/var/lib/clamav/*.tmp" \
--exclude "/var/lib/elasticsearch" \
--exclude "/var/lib/metche" \
--exclude "/var/lib/munin/*tmp*" \
--exclude "/var/db/munin/*.tmp" \
--exclude "/var/lib/mysql" \
--exclude "/var/lib/php5" \
--exclude "/var/lib/php/sessions" \
--exclude "/var/lib/postgres" \
--exclude "/var/lib/postgresql" \
--exclude "/var/lib/sympa" \
--exclude "/var/lock" \
--exclude "/var/run" \
--exclude "/var/spool/postfix" \
--exclude "/var/spool/smtpd" \
--exclude "/var/spool/squid" \
--exclude "/var/state" \
--exclude "/var/tmp" \
--exclude "lxc/*/rootfs/tmp" \
--exclude "lxc/*/rootfs/usr/doc" \
--exclude "lxc/*/rootfs/usr/obj" \
--exclude "lxc/*/rootfs/usr/share/doc" \
--exclude "lxc/*/rootfs/usr/src" \
--exclude "lxc/*/rootfs/var/apt" \
--exclude "lxc/*/rootfs/var/cache" \
--exclude "lxc/*/rootfs/var/lib/php5" \
--exclude "lxc/*/rootfs/var/lib/php/sessions" \
--exclude "lxc/*/rootfs/var/lock" \
--exclude "lxc/*/rootfs/var/log" \
--exclude "lxc/*/rootfs/var/run" \
--exclude "lxc/*/rootfs/var/state" \
--exclude "lxc/*/rootfs/var/tmp" \
--exclude "/home/mysqltmp" \
${rep} \
/etc \
/root \
/var \
/home \
-e "${RSH_COMMAND}" \
"root@${SSH_SERVER}:/var/backup/" \
| tail -30 >> $LOGFILE
tail -20 "${LOGFILE}" | mail -s "[info] EvoBackup - Client ${HOSTNAME}" ${MAIL}
}
log "STOP SYNC_TASKS - server=${server}"
fi
# set all programs to C language (english)
export LC_ALL=C
##### REPORTING #######################################################
# Error on unassigned variable
set -u
STOP_EPOCH=$(/bin/date +%s)
# Default return-code (0 == succes)
rc=0
if [ "${SYSTEM}" = "openbsd" ]; then
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
else
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
fi
duration=$(( STOP_EPOCH - START_EPOCH ))
# execute main funciton
main
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
tail -20 "${LOGFILE}" \
| mail -s "[info] EvoBackup - Client ${HOSTNAME}" ${MAIL}
exit ${rc}

View file

@ -6,14 +6,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [22.11] - 2022-11-28
### Added
* check-canary: new subcommand to check canary files and content
### Changed
* stats: filter active jails and columnize the output
## [22.07] - 2022-07-20
### Changed
* check-setup: check minifirewall version only if minifirewall is present
* check-setup: get minifirewall version from internal variable (there is no other backward compatible way)
* check-setup: use findmnt with mountpoint instead of target
## [22.06] - 2022-06-28
### Added
* bkctld-init: create "incs/\<jail\>" directory for jails
### Fixed
* shell syntax error when ${btrfs_bin} variable is empty
* read_variable + read_numerical_variable: keep the last found value only
* Debian 8 findmnt(8) support
### Security

View file

@ -4,7 +4,7 @@ Bkctld (aka server-side evobackup)
bkctld helps you manage the receiving side of a backup infrastructure.
It is licensed under the AGPLv3.
With bkctld you create and manage "jails". They contain a chrooted and dedicated SSH server, with it's own TCP port and optionnaly it's own set of iptables rules.
With bkctld you create and manage "jails". They contain a chrooted and dedicated SSH server, with its own TCP port and optionally its own set of iptables rules.
With bkctld you can have hundreds of jails, one for each client to push its data (using Rsync/SFTP). Each client can only see its own data.
@ -30,9 +30,7 @@ This volume can also be encrypted with **LUKS**.
## Security considerations
The client obviously has access to its uploaded data (in the chroot), but the timestamped copies are outside the chroot, to reduce the risk or complete backup erasure from a compromised client.
Since the client connects to the backup server with root, it can mess with the jail and destroy the data. But the timestamped copies are out of reach because outside of the chroot.
The client obviously has access to its uploaded data (in the chroot), but the timestamped copies are outside the chroot, to reduce the risk of complete backup erasure from a compromised client.
It means that **if the client server is compromised**, an attacker can destroy the latest copy of the backed up data, but not the timestamped copies.
And **if the backup server is compromised** an attacker has complete access to all the backup data (inside and outside the jails), but they don't have any access to the client.
@ -41,7 +39,7 @@ This architecture is as secure as SSH, Rsync, chroot and iptables are.
## Install
See the [installation guide](docs/install.md) for instructions.
See the [installation guide](https://intra.evolix.net/OutilsInternes/bkctld) for instructions.
## Testing
@ -51,6 +49,12 @@ You can deploy test environments with Vagrant :
vagrant up
~~~
To destroy Vagrant VMs :
~~~
vagrant destroy
~~~
### Deployment
Run `vagrant rsync-auto` in a terminal for automatic synchronization of
@ -77,6 +81,8 @@ vagrant@buster-btrfs $ sudo -i
root@buster-btrfs # bats /vagrant/test/*.bats
~~~
[comment]: <> (* pour vim)
You should shellcheck your bats files, but with shellcheck > 0.4.6, because the 0.4.0 version doesn't support bats syntax.
## Usage
@ -99,7 +105,7 @@ pandoc -f markdown \
#### Client configuration
You can backup various systems in the evobackup jails : Linux, BSD,
Windows, macOS. The only need Rsync or an SFTP client.
Windows, macOS. The only need is Rsync or an SFTP client.
~~~
rsync -av -e "ssh -p SSH_PORT" /home/ root@SERVER_NAME:/var/backup/home/

View file

@ -53,6 +53,9 @@ while :; do
-f|--force)
export FORCE=1
;;
--no-header)
export HEADER=0
;;
*)
# Default case: If no more options then break out of the loop.
break
@ -64,7 +67,7 @@ done
subcommand="${1:-}"
case "${subcommand}" in
"inc" | "rm" | "check-jails" | "check-setup" | "stats" | "list")
"inc" | "rm" | "check-jails" | "check-setup" | "check-canary" | "stats" | "list")
"${LIBDIR}/bkctld-${subcommand}"
;;
"check")
@ -116,7 +119,9 @@ case "${subcommand}" in
;;
"status")
jail_name="${2:-}"
printf '%-30s %-10s %-10s %-25s %-20s\n' 'JAIL NAME' 'STATUS' 'PORT' 'RETENTION (DAY/MONTH)' 'IP'
if [ "${HEADER}" = "1" ]; then
printf '%-30s %-10s %-10s %-25s %-20s\n' 'JAIL NAME' 'STATUS' 'PORT' 'RETENTION (DAY/MONTH)' 'IP'
fi
if [ "${jail_name}" = "all" ] || [ -z "${jail_name}" ]; then
for jail in $("${LIBDIR}/bkctld-list"); do
"${LIBDIR}/bkctld-${subcommand}" "${jail}"

View file

@ -15,3 +15,5 @@
#LOGLEVEL=6
#NODE=''
#ARCHIVESDIR='/backup/archives'
#WARNING=48
#CRITICAL=72

View file

@ -2,15 +2,7 @@
## Install from package
A Debian package is available in the Evolix repository
~~~
echo "deb http://pub.evolix.net/ stretch" >> /etc/apt/sources.list
apt update
apt install bkctld
~~~
Then edit `/etc/default/bkctld`
The install documentation is [here](https://intra.evolix.net/OutilsInternes/bkctld)
## Instal from sources
@ -19,17 +11,17 @@ Warning: `cp`-ing the files without `-n` or `-i` will replace existing files !
~~~
# git clone https://gitea.evolix.org/evolix/evobackup.git
# cd evobackup
# cp bkctld /usr/local/sbin/
# cp server/bkctld /usr/local/sbin/
# mkdir -p /usr/local/lib/bkctld
# cp lib/* /usr/local/lib/bkctld/
# cp server/lib/* /usr/local/lib/bkctld/
# mkdir -p /usr/local/share/bkctld
# cp tpl/* /usr/local/share/bkctld/
# cp bkctld.service /lib/systemd/system/
# cp server/tpl/* /usr/local/share/bkctld/
# cp server/bkctld.service /lib/systemd/system/
# mkdir -p /usr/local/share/doc/bkctld
# cp zzz_evobackup /usr/local/share/doc/bkctld/
# cp client/zzz_evobackup /usr/local/share/doc/bkctld/
# mkdir -p /usr/local/share/bash_completion/
# cp bash_completion /usr/local/share/bash_completion/bkctld
# cp bkctld.conf /etc/default/bkctld
# cp server/bash_completion /usr/local/share/bash_completion/bkctld
# cp server/bkctld.conf /etc/default/bkctld
~~~
## Chroot dependencies

59
server/lib/bkctld-check-canary Executable file
View file

@ -0,0 +1,59 @@
#!/bin/sh
#
# Description: check canary file
# Usage: check-canary [<jailname>|all]
#
# shellcheck source=./includes
LIBDIR="$(dirname $0)" && . "${LIBDIR}/includes"
return=0
nb_crit=0
nb_warn=0
nb_ok=0
nb_unkn=0
output=""
date=$(date +"%Y-%m-%d")
# Check each jail status
check_jail() {
jail_name=$1
jail_path=$(jail_path "${jail_name}")
canary_absolute_file="${jail_path}/var/backup/${CANARY_RELATIVE_FILE}"
if [ -f "${canary_absolute_file}" ]; then
if grep --quiet --fixed-string "${date}" "${canary_absolute_file}"; then
nb_ok=$((nb_ok + 1))
output="${output}OK - ${jail_name} - entries found for ${date} in ${CANARY_RELATIVE_FILE} file\n"
else
nb_crit=$((nb_crit + 1))
output="${output}CRITICAL - ${jail_name} - No entry for ${date} in ${CANARY_RELATIVE_FILE} file\n"
[ "${return}" -le 2 ] && return=2
fi
else
nb_crit=$((nb_crit + 1))
output="${output}CRITICAL - ${jail_name} - missing ${CANARY_RELATIVE_FILE} file\n"
[ "${return}" -le 2 ] && return=2
fi
}
for jail_name in $(jails_list); do
check_jail "${jail_name}"
done
[ "${return}" -ge 0 ] && header="OK"
[ "${return}" -ge 1 ] && header="WARNING"
[ "${return}" -ge 2 ] && header="CRITICAL"
[ "${return}" -ge 3 ] && header="UNKNOWN"
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 "^UNKNOWN"
printf "${output}" | grep -E "^CRITICAL"
printf "${output}" | grep -E "^WARNING"
printf "${output}" | grep -E "^OK"
exit "${return}"

View file

@ -16,7 +16,7 @@ output=""
# Verify backup partition is mounted and writable
findmnt --mountpoint "${BACKUP_PARTITION}" -O rw > /dev/null
findmnt -O rw --mountpoint "${BACKUP_PARTITION}" > /dev/null
if [ "$?" -ne 0 ]; then
nb_crit=$((nb_crit + 1))
output="${output}CRITICAL - Backup disk \`/backup' is not mounted (or read-only) !\n"
@ -29,11 +29,12 @@ fi
# Check if the firewall file is sourced
minifirewall_config=/etc/default/minifirewall
minifirewall_version=$(/etc/init.d/minifirewall status | head -1 | cut -d ' ' -f 3)
if [ -n "${FIREWALL_RULES}" ] \
&& [ -r "${FIREWALL_RULES}" ] \
&& [ -f "${minifirewall_config}" ]; then
minifirewall_version=$(grep -E -o "^VERSION=(\S+)" /etc/init.d/minifirewall | head -1 | cut -d '=' -f 2 | tr -d "'" | tr -d '"')
if [ -n "${minifirewall_version}" ] && dpkg --compare-versions "${minifirewall_version}" ge "22.03"; then
# Minifirewall 22.03+ includes files automatically
nb_ok=$((nb_ok + 1))

View file

@ -12,11 +12,12 @@ if [ -z "${jail_name}" ]; then
show_help && exit 1
fi
jail_path=$(jail_path "${jail_name}")
incs_path=$(incs_path "${jail_name}")
test -d "${jail_path}" && error "Skip jail \`${jail_name}' : it already exists"
# Create config and jails directory
mkdir --parents "${CONFDIR}" "${JAILDIR}"
# Create config, jails and incs directories
mkdir --parents "${CONFDIR}" "${JAILDIR}" "${INCDIR}"
if is_btrfs "$(dirname "${JAILDIR}")" || is_btrfs "${JAILDIR}"; then
btrfs_bin=$(command -v btrfs)
@ -28,6 +29,8 @@ else
mkdir --parents "${jail_path}"
fi
mkdir --parents "${incs_path}"
setup_jail_chroot "${jail_name}"
setup_jail_config "${jail_name}"

View file

@ -51,15 +51,15 @@ if dry_run; then
else
mv "${jail_path}" "${new_jail_path}"
fi
if dry_run; then
if [ -d "${incs_path}" ]; then
if [ -d "${incs_path}" ]; then
if dry_run; then
echo "[dry-run] rename ${incs_path} to ${new_incs_path}"
fi
else
if [ -d "${incs_path}" ]; then
else
mv "${incs_path}" "${new_incs_path}"
fi
fi
if [ -d "${jail_config_dir}" ]; then
if dry_run; then
echo "[dry-run] rename ${jail_config_dir} to ${new_jail_config_dir}"

View file

@ -15,27 +15,29 @@ 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 "${INDEX_DIR}/.lastrun.duc" ] && notice "First run of DUC still in progress ..." && exit 0
[ ! -f ${IDX_FILE} ] && error "Index file doesn't exits !"
printf "Last update of index file : "
stat --format=%Y "${INDEX_DIR}/.lastrun.duc" | xargs -i -n1 date -R -d "@{}"
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)
jail_patterns_list=$(mktemp)
# shellcheck disable=SC2064
trap "rm ${duc_output} ${incs_output} ${stat_output}" 0
trap "rm ${duc_output} ${incs_output} ${stat_output} ${jail_patterns_list}" 0
"${DUC}" ls -d "${IDX_FILE}" "${JAILDIR}" > "${duc_output}"
"${DUC}" ls --database "${IDX_FILE}" "${JAILDIR}" > "${duc_output}"
awk '{ print $2 }' "${duc_output}" | while read jail_name; do
jails_list | sed -e "s/^\(.*\)$/\\\\b\1\\\\b/" > "${jail_patterns_list}"
grep -f "${jail_patterns_list}" "${duc_output}" | awk '{ print $2 }' | while read jail_name; do
jail_path=$(jail_path "${jail_name}")
stat --format=%Y "${jail_path}/var/log/lastlog" | xargs -i -n1 date -d "@{}" "+%d-%m-%Y" >> "${stat_output}"
incs_policy_file=$(current_jail_incs_policy_file ${jail_name})
incs_policy_file=$(current_jail_incs_policy_file "${jail_name}")
incs_policy="0"
if [ -r "${incs_policy_file}" ]; then
days=$(grep "^\+" "${incs_policy_file}" | grep --count "day")
@ -45,4 +47,7 @@ awk '{ print $2 }' "${duc_output}" | while read jail_name; do
echo "${incs_policy}" >> "${incs_output}"
done
paste "${duc_output}" "${incs_output}" "${stat_output}" | awk '{ printf("%- 30s %- 10s %- 10s %- 15s\n", $2, $1, $3, $4); }'
(
echo "<jail> <size> <incs> <lastconn>"
paste "${duc_output}" "${incs_output}" "${stat_output}" | awk '{ printf("%s %s %s %s\n", $2, $1, $3, $4); }'
) | column -t

View file

@ -1,7 +1,7 @@
#!/bin/sh
#
# Description: Display status of SSH server
# Usage: status [<jailname>|all]
# Usage: [--no-header] status [<jailname>|all]
#
# shellcheck source=./includes

View file

@ -6,7 +6,7 @@
[ -f /etc/default/bkctld ] && . /etc/default/bkctld
VERSION="22.04"
VERSION="22.11"
LIBDIR=${LIBDIR:-/usr/lib/bkctld}
CONFDIR="${CONFDIR:-/etc/evobackup}"
@ -20,6 +20,7 @@ LOCKDIR="${LOCKDIR:-/run/lock/bkctld}"
ARCHIVESDIR="${ARCHIVESDIR:-${BACKUP_PARTITION}/archives}"
INDEX_DIR="${INDEX_DIR:-${BACKUP_PARTITION}/index}"
IDX_FILE="${IDX_FILE:-${INDEX_DIR}/bkctld-jails.idx}"
CANARY_RELATIVE_FILE="${CANARY_RELATIVE_FILE:-/zzz_evobackup_canary}"
SSHD_PID="${SSHD_PID:-/run/sshd.pid}"
SSHD_CONFIG="${SSHD_CONFIG:-/etc/ssh/sshd_config}"
AUTHORIZED_KEYS="${AUTHORIZED_KEYS:-/root/.ssh/authorized_keys}"
@ -29,6 +30,7 @@ CRITICAL="${CRITICAL:-48}"
WARNING="${WARNING:-24}"
DUC=$(command -v duc-nox || command -v duc)
FORCE="${FORCE:-0}"
HEADER="${HEADER:-1}"
show_version() {
cat <<END
@ -62,6 +64,13 @@ EOF
printf "\n"
}
is_quiet() {
test ${QUIET} -eq 1
}
is_verbose() {
test ${VERBOSE} -eq 1
}
log_date() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")]"
}
@ -127,7 +136,7 @@ is_btrfs() {
inode=$(stat --format=%i "${path}")
test $inode -eq 256
test "$inode" -eq 256
}
# Returns the list of jails found in the "jails" directory (default)

View file

@ -144,7 +144,7 @@ OUT
@test "Check setup WARNING if firewall rules are not sourced" {
/usr/lib/bkctld/bkctld-start ${JAILNAME}
mkdir /etc/minifirewall.d/
mkdir --parents /etc/minifirewall.d/
firewall_rules_file="/etc/minifirewall.d/bkctld"
set_variable "/etc/default/bkctld" "FIREWALL_RULES" "${firewall_rules_file}"
echo "" > "${firewall_rules_file}"
@ -159,7 +159,7 @@ OUT
@test "Check setup OK if firewall rules are sourced" {
/usr/lib/bkctld/bkctld-start ${JAILNAME}
mkdir /etc/minifirewall.d/
mkdir --parents /etc/minifirewall.d/
firewall_rules_file="/etc/minifirewall.d/bkctld"
set_variable "/etc/default/bkctld" "FIREWALL_RULES" "${firewall_rules_file}"
echo "" > "${firewall_rules_file}"
@ -252,3 +252,25 @@ OUT
assert_failure
}
# TODO: write many more tests for bkctld-check-incs
@test "Check-canary fails if a canary file doesn't exist" {
run /usr/lib/bkctld/bkctld-check-canary "${JAILNAME}"
assert_equal "$status" "2"
assert_line "CRITICAL - ${JAILNAME} - missing /zzz_evobackup_canary file"
}
@test "Check-canary fails if a canary is missing today's entries" {
today="$(date +%Y-%m-%d)"
touch "${JAILPATH}/var/backup/zzz_evobackup_canary"
run /usr/lib/bkctld/bkctld-check-canary "${JAILNAME}"
assert_equal "$status" "2"
assert_line "CRITICAL - ${JAILNAME} - No entry for ${today} in /zzz_evobackup_canary file"
}
@test "Check-canary succeeds if a canary has today's entries" {
echo "$(date "+%FT%T%z") bats-test" >> "${JAILPATH}/var/backup/zzz_evobackup_canary"
run /usr/lib/bkctld/bkctld-check-canary "${JAILNAME}"
assert_success
}

View file

@ -9,6 +9,11 @@ load test_helper
run test -e "${CONFDIR}/${JAILNAME}.d/incs_policy"
[ "${status}" -eq 0 ]
}
@test "Inc directory after jail init" {
# An incs_policy file should exist
run test -d "${INCDIR}/${JAILNAME}"
[ "${status}" -eq 0 ]
}
@test "Normal inc creation" {
/usr/lib/bkctld/bkctld-inc