Compare commits

..

1 commit
stable ... bind

Author SHA1 Message Date
Jérémy Lecour e330601b82 Merge branch 'unstable' into bind 2024-03-15 21:30:32 +01:00
66 changed files with 658 additions and 5150 deletions

View file

@ -15,68 +15,18 @@ The **patch** part is incremented if multiple releases happen the same month
### Changed
* autosysadmin-agent: upstream release 24.03.1
### Fixed
* certbot: Fix HAProxy renewal hook
* keepalived: Fix tasks that use file instead of copy
* memcached: Fix conditions not properly writen (installation was always in multi-instance mode)
### Removed
### Security
## [24.05] 2024-05-15
### Added
* apt: add list-upgradable-held-packages.sh
### Changed
* evobackup-client: upstream release 24.05.1
* evolinux-base: improve adding the current user to SSH AllowGroups of AllowUsers
* evolinux-users: improve SSH configuration
* evomaintenance: upstream release 24.05
* evomaintenance: move upstream files into upstream folder
### Fixed
* apt: use archive.debian.org with Buster
## [24.04] 2024-04-30
### Added
* proftpd: optional configuration of IP whitelists per groups of users
### Changed
* autosysadmin-agent: upstream release 24.03.2
* evobackup-client: replace non-functional role with install tasks
* evobackup-client: upstream release 24.04.1
* evolinux-base: Add new variable to disable global customisation of bash config
* evolinux-base: Disable logcheck monitoring of journald only if journald.logfiles exists
* evolinux-users: Add sudo mvcli for nagios user
* haproxy: support bookworm for backport packages
* nrpe: !disk1 exclude filesystem type overlay
* postfix/amavis: max servers is now 3 (previously 2)
* roundcube: Use /var/log/roundcube directly
* vrrpd: configure and restart minifirewall before starting VRRP
* vrrpd: configure minifirewall with blocks instead of lines
### Fixed
* certbot: Fix HAPEE renewal hook
* certbot: Fix HAProxy renewal hook
* evolinux-base/logcheck: fix conf patch, journal check was not disabled when asked
* fail2ban: SQLite purge script didn't vacuum as expected + error when vacuum cannot be done
* keepalived: Fix tasks that use file instead of copy
* memcached: Fix conditions not properly writen (installation was always in multi-instance mode)
* nagios-nrpe: create /etc/bash_completion.d if missing
* openvpn: install packages manually, because openbsd_pkg module is broken since OpenBSD 7.4 with the version of Ansible we currently use
* packweb: fix old bug (2017!) .orig file created by module patch and taken in account by ProFTPd
* redis: replace inline argument with environment variable for the password
### Removed
* docker-host: Removed `docker_conf_use_iptables` variable (iptable usage forced to true)
## [24.03] 2024-03-01
### Added

View file

@ -39,7 +39,7 @@ $sa_spam_subject_tag = '[SPAM]';
$log_level = 2;
# En fonction besoin/ressources, on a juste le nbre de process
$max_servers = 3;
$max_servers = 2;
$enable_ldap = 1;
$default_ldap = {

View file

@ -29,7 +29,4 @@ apt_check_hold_cron_weekday: "*"
apt_check_hold_cron_day: "*"
apt_check_hold_cron_month: "*"
apt_list_upgradable_held_enabled: False
apt_list_upgradable_held_special_time: "weekly"
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"

View file

@ -1,127 +0,0 @@
#!/bin/bash
#####
# This script will send an email if some packages are on hold
# but have available updates.
#####
readonly VERSION="24.05"
# set all programs to C language (english)
export LC_ALL=C
# If expansion is attempted on an unset variable or parameter, the shell prints an
# error message, and, if not interactive, exits with a non-zero status.
set -o nounset
# The pipeline's return status is the value of the last (rightmost) command
# to exit with a non-zero status, or zero if all commands exit successfully.
set -o pipefail
# Enable trace mode if called with environment variable TRACE=1
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi
# shellcheck disable=SC2155
readonly PROGPATH=$(readlink -m "${0}")
# readonly PROGNAME=$(basename "${PROGPATH}")
# # shellcheck disable=SC2124
# readonly ARGS=$@
# Fetch values from evomaintenance configuration
get_evomaintenance_mail() {
grep "EVOMAINTMAIL=" /etc/evomaintenance.cf | cut -d '=' -f2
}
get_fqdn() {
hostname --fqdn
}
get_complete_hostname() {
REAL_HOSTNAME="$(get_fqdn)"
if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then
echo "${HOSTNAME}"
else
echo "${HOSTNAME} (${REAL_HOSTNAME})"
fi
}
format_mail() {
cat <<EOTEMPLATE
From: Evolix <${EMAIL_FROM}>
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Script: ${PROGPATH}
X-Script-Version: ${VERSION}
To: ${EMAIL_CLIENT:-alert5@evolix.fr}
Subject: Mise a jour manuelle disponible
Bonjour,
Un ou plusieurs paquets dont la mise à jour automatique a été
explicitement bloquée ont une nouvelle version disponible.
Nom du serveur :
${HOSTNAME_TEXT}
Liste des paquets :
${upgradable_held_packages}
Pour que nous appliquions ces mises à jour vous devez
nous contacter explicitement, de préférence par ticket,
en mentionnant le serveur et les paquets concernés,
ainsi que les modalités de mise à jour (créneau horaire,
procédure technique…).
Cordialement
--
Evolix
EOTEMPLATE
}
main() {
held_packages=$(apt-mark showhold)
upgradable_packages=$(apt list --upgradable 2> /dev/null)
if [ -z "${held_packages}" ]; then
# No packages are on hold
exit 0
elif [ -z "${upgradable_packages}" ]; then
# No packages are upgradable
exit 0
fi
kept_back_output=$(LC_ALL=C apt-get upgrade --dry-run | grep -A 1 'The following packages have been kept back:')
if [ -z "${kept_back_output}" ]; then
# No packages are kept back
exit 0
fi
upgradable_held_packages=$(apt list --upgradable 2> /dev/null | grep -f <(echo "${kept_back_output}" | tail -1 | tr ' ' '\n' | sed -e '/^$/d'))
if [ -z "${upgradable_held_packages}" ]; then
# No held packages are upgradable
exit 0
fi
HOSTNAME="$(get_fqdn)"
HOSTNAME_TEXT="$(get_complete_hostname)"
EMAIL_CLIENT="$(get_evomaintenance_mail)"
EMAIL_FROM="equipe@evolix.fr"
MAIL_CONTENT="$(format_mail)"
SENDMAIL_BIN="$(command -v sendmail)"
if [ -z "${SENDMAIL_BIN}" ]; then
>&2 echo "ERROR: No \`sendmail' command has been found, can't send mail."
exit 1
fi
if [ ! -x "${SENDMAIL_BIN}" ]; then
>&2 echo "ERROR: \`${SENDMAIL_BIN}' is not executable, can't send mail."
exit 1
fi
echo "${MAIL_CONTENT}" | "${SENDMAIL_BIN}" -oi -t -f "equipe@evolix.fr"
exit 0
}
main

View file

@ -76,16 +76,7 @@
tags:
- apt
- name: List scripts is installed
ansible.builtin.copy:
src: list-upgradable-held-packages.sh
dest: /usr/share/scripts/list-upgradable-held-packages.sh
force: true
mode: "0755"
tags:
- apt
- name: Check if cron is installed
- name: Check if Cron is installed
ansible.builtin.shell:
cmd: "dpkg --list 'cron' 2>/dev/null | grep -q -E '^(i|h)i'"
register: is_cron
@ -110,15 +101,3 @@
tags:
- apt
when: is_cron.rc == 0
- name: List upgradable held packages (script)
ansible.builtin.cron:
cron_file: apt-hold-packages
name: list-upgradable-held-packages
job: "/usr/share/scripts/list-upgradable-held-packages.sh"
user: root
special_time: "{{ apt_list_upgradable_held_special_time | mandatory }}"
state: "{{ apt_list_upgradable_held_enabled | bool | ternary('present', 'absent') }}"
tags:
- apt
when: is_cron.rc == 0

View file

@ -1,3 +1,3 @@
# {{ ansible_managed }}
deb http://archive.debian.org/debian buster-backports {{ apt_backports_components | mandatory }}
deb http://mirror.evolix.org/debian buster-backports {{ apt_backports_components | mandatory }}

View file

@ -1,4 +1,5 @@
# {{ ansible_managed }}
deb http://archive.debian.org/debian buster {{ apt_basics_components | mandatory }}
deb http://mirror.evolix.org/debian buster {{ apt_basics_components | mandatory }}
deb http://mirror.evolix.org/debian buster-updates {{ apt_basics_components | mandatory }}
deb http://security.debian.org/debian-security buster/updates {{ apt_basics_components | mandatory }}

View file

@ -1,6 +1,6 @@
#!/bin/bash
VERSION="24.03.2"
VERSION="24.03.1"
# Common functions for "repair" and "restart" scripts

View file

@ -92,9 +92,9 @@ repair_lxc_php() {
lxc-start --daemon --name "${container_name}"
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart LXC container '${container_name}: OK"
log_all "Restart LXC container '${container_name}: OK"
else
log_action "Restart LXC container '${container_name}: failed"
log_all "Restart LXC container '${container_name}: failed"
fi
# Save LXC info (after restart)

View file

@ -40,7 +40,7 @@ concat_files() {
}
cert_and_key_mismatch() {
hapee_cert_md5=$(openssl x509 -noout -pubkey -in "${hapee_cert_file}" | openssl md5)
hapee_key_md5=$(openssl pkey -pubout -in "${hapee_cert_file}" | openssl md5)
hapee_key_md5=$(openssl pkey -noout -pubout -in "${hapee_cert_file}" | openssl md5)
test "${hapee_cert_md5}" != "${hapee_key_md5}"
}

View file

@ -3,25 +3,28 @@
docker_home: /var/lib/docker
docker_tmpdir: "{{ docker_home }}/tmp"
# Chose to use iptables instead of docker-proxy userland process
docker_conf_use_iptables: False
# Disable the possibility for containers processes to gain new privileges
docker_conf_no_newprivileges: false
docker_conf_no_newprivileges: False
# Toggle live restore (need to be disabled in swarm mode)
docker_conf_live_restore: true
docker_conf_live_restore: True
# Toggle user namespace
docker_conf_user_namespace: true
docker_conf_user_namespace: True
# Disable all default network connectivity
docker_conf_disable_default_networking: false
docker_conf_disable_default_networking: False
# Remote access
docker_remote_access_enabled: false
docker_remote_access_enabled: False
docker_daemon_port: 2376
docker_daemon_listening_ip: 0.0.0.0
# TLS
docker_tls_enabled: false
docker_tls_enabled: False
docker_tls_path: "{{ docker_home }}/tls"
docker_tls_ca: ca/ca.pem
docker_tls_ca_key: ca/ca-key.pem
@ -29,4 +32,4 @@ docker_tls_cert: server/cert.pem
docker_tls_key: server/key.pem
docker_tls_csr: server/server.csr
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"

View file

@ -1,14 +1,5 @@
# This role installs the docker daemon
---
- name: Fail if docker_conf_use_iptables is defined
ansible.builtin.fail:
msg: "Variable docker_conf_use_iptables is deprecated and not configurable anymore. Please remove it from your variables. Also double-check the daemon.json config for docker"
when:
- docker_conf_use_iptables is defined
tags:
- always
- name: Remove older docker packages
ansible.builtin.apt:
name:
@ -32,7 +23,7 @@
when: ansible_distribution_major_version is version('10', '<')
- name: "Ensure {{ apt_keyring_dir }} directory exists"
ansible.builtin.file:
file:
path: "{{ apt_keyring_dir }}"
state: directory
mode: "755"
@ -53,34 +44,35 @@
repo: 'deb [signed-by={{ apt_keyring_dir }}/docker-debian.asc] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable'
filename: docker
state: present
update_cache: true
update_cache: yes
when: ansible_distribution_major_version is version('12', '<')
- name: Add Docker repository (Debian >=12)
ansible.builtin.template:
src: docker.sources.j2
dest: /etc/apt/sources.list.d/docker.sources
owner: root
group: root
mode: "0644"
register: docker_sources
when: ansible_distribution_major_version is version('12', '>=')
- name: Update APT cache
ansible.builtin.apt:
update_cache: yes
when: docker_sources is changed
- name: Install Docker
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
update_cache: true
cache_valid_time: 3600
- name: Package python-docker is installed
- name: python-docker is installed
ansible.builtin.apt:
name: python-docker
state: present
when: ansible_python_version is version('3', '<')
- name: Package python3-docker is installed
- name: python3-docker is installed
ansible.builtin.apt:
name: python3-docker
state: present
@ -90,9 +82,6 @@
ansible.builtin.template:
src: daemon.json.j2
dest: /etc/docker/daemon.json
owner: root
group: root
mode: "0644"
notify: restart docker
- name: Creating Docker tmp directory

View file

@ -1,6 +1,5 @@
{
"debug": false
,"iptables": true
{# Docker data-dir (default to /var/lib/docker) #}
,"data-root": "{{ docker_home }}"
{# Keep containers running while docker daemon downtime #}
@ -8,6 +7,11 @@
{% if docker_conf_user_namespace %}
{# Turn on user namespace remaping #}
,"userns-remap": "default"
{% endif %}
{% if docker_conf_use_iptables %}
{# Use iptables instead of docker-proxy #}
,"userland-proxy": false
,"iptables": true
{% endif %}
{# Disable the possibility for containers processes to gain new privileges #}
,"no-new-privileges": {{ docker_conf_no_newprivileges | to_json }}

View file

@ -1,16 +1,23 @@
# evobackup-client
Install the necessary libraries and script to configure backup scripts.
Allows the configuration of backups to one or more remote filesystems.
Additional information:
The backup hosts and the ports in use need to be defined in
evobackup-client__hosts before running it.
* [evobackup-client documentation](https://gitea.evolix.org/evolix/evobackup/src/branch/master/client/README.md)
* canary
The default zzz_evobackup.sh configures a system only backup, but the
template can be overriden to configure a full backup instead. If
you change the variables in defaults/main.yml you can easily run
this again and configure backups to a second set of hosts.
## Available variables
Do not forget to set the evobackup-client__mail variable to an
email adress you control.
* `evobackup_client__lib_dir` : directory for libraries (default: `/usr/local/lib/evobackup`)
* `evobackup_client__bin_dir` : directory for scripts/binaries (default: `/usr/local/bin`)
* `evobackup_client__update_canary_enable` : should the canary be updated (default: `True`)
* `evobackup_client__update_canary_path` : path for the canary update script (default: `/etc/cron.daily/000-update-evobackup-canary`)
* `evobackup_client__update_canary_who` : who the canary update must be attributed to (default: `@daily`)
You can add this example to an installation playbook to create the
ssh key without running the rest of the role.
~~~
post_tasks:
- include_role:
name: evobackup-client tasks_from: ssh_key.yml
~~~

View file

@ -1,22 +1,15 @@
---
# evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
# evobackup_client__root_key_type: "ed25519"
# evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
# evobackup_client__cron_template_name: "zzz_evobackup"
# evobackup_client__mail: null
# evobackup_client__servers_fallback: -1
# evobackup_client__pid_path: "/var/run/evobackup.pid"
# evobackup_client__log_path: "/var/log/evobackup.log"
# evobackup_client__backup_path: "/home/backup"
# evobackup_client__hosts: null
evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
evobackup_client__root_key_type: "ed25519"
evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
evobackup_client__cron_template_name: "zzz_evobackup"
evobackup_client__mail: null
evobackup_client__servers_fallback: -1
evobackup_client__pid_path: "/var/run/evobackup.pid"
evobackup_client__log_path: "/var/log/evobackup.log"
evobackup_client__backup_path: "/home/backup"
evobackup_client__hosts: null
# - name: "backups.example.org"
# ip: "xxx.xxx.xxx.xxx"
# fingerprint: "ecdsa-sha2-nistp256 ..."
# port: xxxx
evobackup_client__lib_dir: "/usr/local/lib/evobackup"
evobackup_client__bin_dir: "/usr/local/bin"
evobackup_client__update_canary_enable: True
evobackup_client__update_canary_path: /etc/cron.daily/000-update-evobackup-canary
evobackup_client__update_canary_who: "@daily"

View file

@ -1,98 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
This project does not follow semantic versioning.
The **major** part of the version is the year
The **minor** part changes is the month
The **patch** part changes is incremented if multiple releases happen the same month
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [24.05.1] - 2022-05-14
### Fixed
* client: fix shell syntax error
## [24.05] - 2022-05-02
### Added
* evobackupctl: update LIBDIR when copying the template
### Changed
* evobackupctl: simplify the program path retrieval
## [24.04.1] - 2022-04-30
### Fixed
* evobackupctl: quote ARGS variable for options parsing.
## [24.04] - 2022-04-29
### Added
* Vagrant definition for manual tests
### Changed
* split functions into libraries
* add evobackupctl script
* change the "zzz_evobackup" script to a template, easy to copy with evobackupctl
* use env-based shebang for shell scripts
* use $TMPDIR if available
### Removed
* update-evobackup-canary is managed by ansible-roles.git
* deployment by Ansible is managed elsewhere (now in evolix-private.git, later in ansible-roles.git)
### Fixed
* don't exit the whole program if a sync task can't be done
## [22.12] - 2022-12-27
### 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] - 2022-04-03
Split client and server parts of the project

View file

@ -1,156 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2155
readonly PROGPATH=$(readlink -m "${0}")
readonly PROGNAME=$(basename "${PROGPATH}")
# shellcheck disable=SC2124
readonly ARGS=$@
# Change this to wherever you install the libraries
readonly LIBDIR="/usr/local/lib/evobackup"
source "${LIBDIR}/main.sh"
show_version() {
cat <<END
${PROGNAME} version ${VERSION}
Copyright 2024 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>.
${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} helps managing evobackup scripts
Options
-h, --help print this message and exit
-V, --version print version and exit
--jail-init-commands print jail init commands
--copy-template=PATH copy the backup template to PATH
END
}
jail_init_commands() {
if [ ! -f /root/.ssh/id_ed25519.pub ]; then
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ''
echo ""
fi
SSH_KEY=$(cat /root/.ssh/id_ed25519.pub)
SERVER_NAME=$(hostname -s)
if [ "$(uname -s)" = "OpenBSD" ]; then
SERVER_IP=$(ifconfig egress | grep "inet " | head -1 | awk '{ print $2}')
else
SERVER_IP=$(curl -4 https://ifconfig.me 2> /dev/null || hostname -I | awk '{ print $1}')
fi
echo "Copy-paste those lines on backup server(s) :"
echo "----------"
echo "SERVER_NAME=${SERVER_NAME}"
echo "SERVER_IP=${SERVER_IP}"
echo "echo '${SSH_KEY}' > /root/\${SERVER_NAME}.pub"
echo "bkctld init \${SERVER_NAME}"
echo "bkctld key \${SERVER_NAME} /root/\${SERVER_NAME}.pub"
echo "bkctld ip \${SERVER_NAME} \${SERVER_IP}"
echo "bkctld start \${SERVER_NAME}"
echo "bkctld status \${SERVER_NAME}"
echo "grep --quiet --extended-regexp \"^\\s?NODE=\" /etc/default/bkctld && bkctld sync \${SERVER_NAME}"
echo "----------"
}
copy_template() {
dest_path=${1}
dest_dir="$(dirname "${dest_path}")"
if [ -e "${dest_path}" ]; then
printf "Path for new evobackup script '%s' already exists.\n" "${dest_path}" >&2
exit 1
elif [ ! -e "${dest_dir}" ]; then
printf "Parent directory '%s' doesn't exist. Create it first.\n" "${dest_dir}" >&2
exit 1
else
if cp "${LIBDIR}/zzz_evobackup.sh" "${dest_path}"; then
chmod 750 "${dest_path}"
# Insert metadata about the template
sed -i "s|@COMMAND@|${PROGPATH} ${ARGS}|" "${dest_path}"
sed -i "s|@DATE@|$(date --iso-8601=seconds)|" "${dest_path}"
sed -i "s|@VERSION@|${VERSION}|" "${dest_path}"
# Make sure that the library directory is correct
sed -i "s|^LIBDIR=.\+|LIBDIR=\"${LIBDIR}\"|" "${dest_path}"
printf "New evobackup script has been saved to '%s'.\n" "${dest_path}"
printf "Remember to customize it (mail notifications, backup servers…).\n"
exit 0
fi
fi
}
main() {
# If no argument is provided, print help and exit
# shellcheck disable=SC2086
if [ -z "${ARGS}" ]; then
show_help
exit 0
fi
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
-V|--version)
show_version
exit 0
;;
-h|--help)
show_help
exit 0
;;
--jail-init-commands)
jail_init_commands
exit 0
;;
--copy-template)
# copy-template option, with value separated by space
if [ -n "$2" ]; then
copy_template "${2}"
shift
else
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
exit 1
fi
;;
--copy-template=?*)
# copy-template option, with value separated by =
copy_template "${1#*=}"
;;
--copy-template=)
# copy-template option, without value
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
printf "unknown option '%s'.\n" "${1}" >&2
exit 1
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
}
main ${ARGS}

View file

@ -1,301 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Snapshot Elasticsearch data
#
# Arguments:
# --protocol=<http|https> (default: http)
# --cacert=[String] (default: <none>)
# path to the CA certificate to use when using https
# --host=[String] (default: localhost)
# --port=[Integer] (default: 9200)
# --user=[String] (default: <none>)
# --password=[String] (default: <none>)
# --repository=[String] (default: snaprepo)
# --snapshot=[String] (default: snapshot.daily)
#######################################################################
dump_elasticsearch() {
local option_protocol="http"
local option_cacert=""
local option_host="localhost"
local option_port="9200"
local option_user=""
local option_password=""
local option_repository="snaprepo"
local option_snapshot="snapshot.daily"
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--protocol)
# protocol options, with value separated by space
if [ -n "$2" ]; then
option_protocol="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
exit 1
fi
;;
--protocol=?*)
# protocol options, with value separated by =
option_protocol="${1#*=}"
;;
--protocol=)
# protocol options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
exit 1
;;
--cacert)
# cacert options, with value separated by space
if [ -n "$2" ]; then
option_cacert="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
exit 1
fi
;;
--cacert=?*)
# cacert options, with value separated by =
option_cacert="${1#*=}"
;;
--cacert=)
# cacert options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
exit 1
;;
--host)
# host options, with value separated by space
if [ -n "$2" ]; then
option_host="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
exit 1
fi
;;
--host=?*)
# host options, with value separated by =
option_host="${1#*=}"
;;
--host=)
# host options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--repository)
# repository options, with value separated by space
if [ -n "$2" ]; then
option_repository="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
exit 1
fi
;;
--repository=?*)
# repository options, with value separated by =
option_repository="${1#*=}"
;;
--repository=)
# repository options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
exit 1
;;
--snapshot)
# snapshot options, with value separated by space
if [ -n "$2" ]; then
option_snapshot="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
exit 1
fi
;;
--snapshot=?*)
# snapshot options, with value separated by =
option_snapshot="${1#*=}"
;;
--snapshot=)
# snapshot options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
# Use the default Elasticsearch CA certificate when using HTTPS, if not specified directly
local default_cacert="/etc/elasticsearch/certs/http_ca.crt"
if [ "${option_protocol}" = "https" ] && [ -z "${option_cacert}" ] && [ -f "${default_cacert}" ]; then
option_cacert="${default_cacert}"
fi
local errors_dir="${ERRORS_DIR}/elasticsearch-${option_repository}-${option_snapshot}"
rm -rf "${errors_dir}"
mkdir -p "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${errors_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}"
## Take a snapshot as a backup.
## Warning: You need to have a path.repo configured.
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
local base_url="${option_protocol}://${option_host}:${option_port}"
local repository_url="${base_url}/_snapshot/${option_repository}"
local snapshot_url="${repository_url}/${option_snapshot}"
# Verify snapshot repository
local error_file="${errors_dir}/verify.err"
declare -a connect_options
connect_options=()
if [ -n "${option_cacert}" ]; then
connect_options+=(--cacert "${option_cacert}")
fi
if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then
local connect_options+=("--user ${option_user}:${option_password}")
fi
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
connect_options+=(${option_others})
fi
# Add the http return code at the end of the output
connect_options+=(--write-out '%{http_code}\n')
connect_options+=(--silent)
declare -a dump_options
dump_options=()
dump_options+=(--request POST)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${repository_url}/_verify?pretty"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > "${error_file}"
# test if the last line of the log file is "200"
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: repository verification returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
# Delete snapshot
declare -a dump_options
dump_options=()
dump_options+=(--request DELETE)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > /dev/null
# Create snapshot
local error_file="${errors_dir}/create.err"
declare -a dump_options
dump_options=()
dump_options+=(--request PUT)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}?wait_for_completion=true"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > "${error_file}"
# test if the last line of the log file is "200"
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: curl returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}"
}

View file

@ -1,559 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump LDAP files (config, data, all)
#
# Arguments: <none>
#######################################################################
dump_ldap() {
## OpenLDAP : example with slapcat
local dump_dir="${LOCAL_BACKUP_DIR}/ldap"
rm -rf "${dump_dir}"
mkdir -p "${dump_dir}"
chmod 700 "${dump_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${FUNCNAME[0]} to ${dump_dir}"
dump_cmd="slapcat -n 0 -l ${dump_dir}/config.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="slapcat -n 1 -l ${dump_dir}/data.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="slapcat -l ${dump_dir}/all.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${FUNCNAME[0]}"
}
#######################################################################
# Copy dump file of Redis instances
#
# Arguments:
# --instances=[Integer] (default: all)
#######################################################################
dump_redis() {
all_instances=$(find /var/lib/ -mindepth 1 -maxdepth 1 '(' -type d -o -type l ')' -name 'redis*')
local option_instances=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--instances)
# instances options, with key and value separated by space
if [ -n "$2" ]; then
if [ "${2}" == "all" ]; then
read -a option_instances <<< "${all_instances}"
else
IFS="," read -a option_instances <<< "${2}"
fi
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
exit 1
fi
;;
--instances=?*)
# instances options, with key and value separated by =
if [ "${1#*=}" == "all" ]; then
read -a option_instances <<< "${all_instances}"
else
IFS="," read -a option_instances <<< "${1#*=}"
fi
;;
--instances=)
# instances options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
for instance in "${option_instances[@]}"; do
name=$(basename "${instance}")
local dump_dir="${LOCAL_BACKUP_DIR}/${name}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
if [ -f "${instance}/dump.rdb" ]; then
local error_file="${errors_dir}/${name}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
# Copy the Redis database
dump_cmd="cp -a ${instance}/dump.rdb ${dump_dir}/dump.rdb"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: cp ${instance}/dump.rdb to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
# Compress the Redis database
dump_cmd="gzip ${dump_dir}/dump.rdb"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: gzip ${dump_dir}/dump.rdb returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '${instance}/dump.rdb' not found."
fi
done
}
#######################################################################
# Dump all collections of a MongoDB database
# using a custom authentication, instead of /etc/mysql/debian.cnf
#
# Arguments:
# --port=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# Other options after -- are passed as-is to mongodump
#
# don't forget to create use with read-only access
# > use admin
# > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
#######################################################################
dump_mongodb() {
local option_port=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mongodb-${option_dump_label}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
declare -a dump_options
dump_options=()
if [ -n "${option_port}" ]; then
dump_options+=(--port="${option_port}")
fi
if [ -n "${option_user}" ]; then
dump_options+=(--username="${option_user}")
fi
if [ -n "${option_password}" ]; then
dump_options+=(--password="${option_password}")
fi
dump_options+=(--out="${dump_dir}/")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mongodump ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd} > /dev/null"
${dump_cmd} 2> "${error_file}" > /dev/null
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mongodump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${FUNCNAME[0]}: ${dump_dir}"
}
#######################################################################
# Dump RAID configuration
#
# Arguments: <none>
#######################################################################
dump_raid_config() {
local dump_dir="${LOCAL_BACKUP_DIR}/raid"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
if command -v megacli > /dev/null; then
local error_file="${errors_dir}/megacli.cfg"
local dump_file="${dump_dir}/megacli.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
dump_cmd="megacli -CfgSave -f ${dump_file} -a0"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: megacli to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
elif command -v perccli > /dev/null; then
local error_file="${errors_dir}/perccli.cfg"
local dump_file="${dump_dir}/perccli.err"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
# TODO: find out what the correct command is
# dump_cmd="perccli XXXX"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
# ${dump_cmd} 2> ${error_file}
# local last_rc=$?
# # shellcheck disable=SC2086
# if [ ${last_rc} -ne 0 ]; then
# log_error "LOCAL_TASKS - ${FUNCNAME[0]}: perccli to ${dump_file} returned an error ${last_rc}" "${error_file}"
# GLOBAL_RC=${E_DUMPFAILED}
# else
# rm -f "${error_file}"
# fi
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' and 'perccli' not found, unable to dump RAID configuration"
fi
}
#######################################################################
# Save some traceroute/mtr results
#
# Arguments:
# --targets=[IP,HOST] (default: <none>)
#######################################################################
dump_traceroute() {
local option_targets=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--targets)
# targets options, with key and value separated by space
if [ -n "$2" ]; then
IFS="," read -a option_targets <<< "${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
exit 1
fi
;;
--targets=?*)
# targets options, with key and value separated by =
IFS="," read -a option_targets <<< "${1#*=}"
;;
--targets=)
# targets options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
local dump_dir="${LOCAL_BACKUP_DIR}/traceroute"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
mtr_bin=$(command -v mtr)
if [ -n "${mtr_bin}" ]; then
for target in "${option_targets[@]}"; do
local dump_file="${dump_dir}/mtr-${target}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
${mtr_bin} -r "${target}" > "${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
fi
traceroute_bin=$(command -v traceroute)
if [ -n "${traceroute_bin}" ]; then
for target in "${option_targets[@]}"; do
local dump_file="${dump_dir}/traceroute-${target}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
${traceroute_bin} -n "${target}" > "${dump_file}" 2>&1
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
fi
}
#######################################################################
# Save many system information, using dump_server_state
#
# Arguments:
# any option for dump-server-state (except --dump-dir) is usable
# (default: --all)
#######################################################################
dump_server_state() {
local dump_dir="${LOCAL_BACKUP_DIR}/server-state"
rm -rf "${dump_dir}"
# Do not create the directory
# mkdir -p -m 700 "${dump_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
# pass all options
read -a options <<< "${@}"
# if no option is given, use "--all" as fallback
if [ ${#options[@]} -le 0 ]; then
options=(--all)
fi
# add "--dump-dir" in case it is missing (as it should)
options+=(--dump-dir "${dump_dir}")
dump_server_state_bin=$(command -v dump-server-state)
if [ -z "${dump_server_state_bin}" ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state is missing"
rc=1
else
dump_cmd="${dump_server_state_bin} ${options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state returned an error ${last_rc}, check ${dump_dir}"
GLOBAL_RC=${E_DUMPFAILED}
fi
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
}
#######################################################################
# Save RabbitMQ data
#
# Arguments: <none>
#
# Warning: This has been poorly tested
#######################################################################
dump_rabbitmq() {
local dump_dir="${LOCAL_BACKUP_DIR}/rabbitmq"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
local dump_file="${dump_dir}/config"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
dump_cmd="rabbitmqadmin export ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Save Files ACL on various partitions.
#
# Arguments: <none>
#######################################################################
dump_facl() {
local dump_dir="${LOCAL_BACKUP_DIR}/facl"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
dump_cmd="getfacl -R /etc > ${dump_dir}/etc.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /home > ${dump_dir}/home.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /usr > ${dump_dir}/usr.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /var > ${dump_dir}/var.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
}

View file

@ -1,1551 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump complete summary of an instance (using pt-mysql-summary)
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#######################################################################
dump_mysql_summary() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-summary"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## Dump all grants (requires 'percona-toolkit' package)
if command -v pt-mysql-summary > /dev/null; then
local error_file="${errors_dir}/mysql-summary.err"
local dump_file="${dump_dir}/mysql-summary.out"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
declare -a options
options=()
options+=(--sleep=0)
dump_cmd="pt-mysql-summary ${options[*]} -- ${connect_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-mysql-summary to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-mysql-summary' not found, unable to dump summary"
fi
}
#######################################################################
# Dump grants of an instance
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#######################################################################
dump_mysql_grants() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_user=""
local option_password=""
local option_dump_label=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-grants"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## Dump all grants (requires 'percona-toolkit' package)
if command -v pt-show-grants > /dev/null; then
local error_file="${errors_dir}/all_grants.err"
local dump_file="${dump_dir}/all_grants.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a options
options=()
if [ -n "${option_defaults_file}" ]; then
options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_port}" ]; then
options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
options+=(--password="${option_password}")
fi
options+=(--flush)
options+=(--no-header)
dump_cmd="pt-show-grants ${options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-show-grants to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-show-grants' not found, unable to dump grants"
fi
}
#######################################################################
# Dump a single compressed file of all databases of an instance
# and a file containing only the schema.
#
# Arguments:
# --masterdata (default: <absent>)
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_global() {
local option_masterdata=""
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--masterdata)
option_masterdata="--masterdata"
;;
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
## Global all databases in one file
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/mysqldump.err"
local dump_file="${dump_dir}/mysqldump.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--opt)
dump_options+=(--force)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--all-databases)
if [ -n "${option_masterdata}" ]; then
dump_options+=("${option_masterdata}")
fi
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
## WARNING : logging and executing the command must be separate
## because otherwise Bash would interpret | and > as strings and not syntax.
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## Schema only (no data) for each databases
local error_file="${errors_dir}/mysqldump.schema.err"
local dump_file="${dump_dir}/mysqldump.schema.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--no-data)
dump_options+=(--all-databases)
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Dump a file of each databases of an instance
# and a file containing only the schema.
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_per_base() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
for database in ${databases}; do
local error_file="${errors_dir}/${database}.err"
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--opt)
dump_options+=(--force)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--databases "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
## WARNING : logging and executing the command must be separate
## because otherwise Bash would interpret | and > as strings and not syntax.
log "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump ${connect_options[*]} ${dump_options[*]} | ${compress_cmd} > ${dump_file}"
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## Schema only (no data) for each databases
local error_file="${errors_dir}/${database}.schema.err"
local dump_file="${dump_dir}/${database}.schema.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--no-data)
dump_options+=(--databases "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
}
#######################################################################
# Dump "tabs style" separate schema/data for each database of an instance
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_tabs() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
for database in ${databases}; do
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-tabs/${database}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
chown -RL mysql "${dump_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--quote-names)
dump_options+=(--opt)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--skip-comments)
dump_options+=(--fields-enclosed-by='\"')
dump_options+=(--fields-terminated-by=',')
dump_options+=(--tab="${dump_dir}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_options+=("${database}")
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
done
}

View file

@ -1,343 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump a single file of all PostgreSQL databases
#
# Arguments:
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to pg_dump
#######################################################################
dump_postgresql_global() {
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-global"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## example with pg_dumpall and with compression
local error_file="${errors_dir}/pg_dumpall.err"
local dump_file="${dump_dir}/pg_dumpall.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="(sudo -u postgres pg_dumpall ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dumpall to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## example with pg_dumpall and without compression
## WARNING: you need space in ~postgres
# local error_file="${errors_dir}/pg_dumpall.err"
# local dump_file="${dump_dir}/pg_dumpall.sql"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
#
# (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}"
# mv ~postgres/pg.dump.bak "${dump_file}"
#
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Dump a compressed file per database
#
# Arguments: <none>
#######################################################################
dump_postgresql_per_base() {
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
(
# shellcheck disable=SC2164
cd /var/lib/postgresql
databases=$(sudo -u postgres psql -U postgres -lt | awk -F \| '{print $1}' | grep -v "template.*")
for database in ${databases} ; do
local error_file="${errors_dir}/${database}.err"
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--create)
dump_options+=(-U postgres)
dump_options+=(-d "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="(sudo -u postgres /usr/bin/pg_dump ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
)
}
#######################################################################
# Dump a compressed file per database
#
# Arguments: <none>
#
# TODO: add arguments to include/exclude tables
#######################################################################
dump_postgresql_filtered() {
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-filtered"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/pg-backup.err"
local dump_file="${dump_dir}/pg-backup.tar"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
## 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 "${dump_file}" -t 'TABLE1' -t 'TABLE2' MYBASE 2> "${error_file}"
## example with only TABLE1 and TABLE2 from MYBASE
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -T 'TABLE1' -T 'TABLE2' MYBASE 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}

View file

@ -1,466 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317
readonly VERSION="24.05.1"
# set all programs to C language (english)
export LC_ALL=C
# If expansion is attempted on an unset variable or parameter, the shell prints an
# error message, and, if not interactive, exits with a non-zero status.
set -o nounset
# The pipeline's return status is the value of the last (rightmost) command
# to exit with a non-zero status, or zero if all commands exit successfully.
set -o pipefail
# Enable trace mode if called with environment variable TRACE=1
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi
source "${LIBDIR}/utilities.sh"
source "${LIBDIR}/dump/elasticsearch.sh"
source "${LIBDIR}/dump/mysql.sh"
source "${LIBDIR}/dump/postgresql.sh"
source "${LIBDIR}/dump/misc.sh"
# Called from main, it is wrapping the local_tasks function defined in the real script
local_tasks_wrapper() {
log "START LOCAL_TASKS"
# Remove old log directories (recursively)
find "${LOCAL_BACKUP_DIR}/" -type d -name "${PROGNAME}.errors-*" -ctime +30 -exec rm -rf \;
local_tasks_type="$(type -t local_tasks)"
if [ "${local_tasks_type}" = "function" ]; then
local_tasks
else
log_error "There is no 'local_tasks' function to execute"
fi
# TODO: check if this is still needed
# print_error_files_content
log "STOP LOCAL_TASKS"
}
# Called from main, it is wrapping the sync_tasks function defined in the real script
sync_tasks_wrapper() {
declare -a SERVERS # Indexed array for server/port values
declare -a RSYNC_INCLUDES # Indexed array for includes
declare -a RSYNC_EXCLUDES # Indexed array for excludes
case "${SYSTEM}" in
linux)
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_includes=(
/bin
/boot
/lib
/opt
/sbin
/usr
)
;;
*bsd)
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_includes=(
/bin
/bsd
/sbin
/usr
)
;;
*)
echo "Unknown system '${SYSTEM}'" >&2
exit 1
;;
esac
if [ -f "${CANARY_FILE}" ]; then
rsync_default_includes+=("${CANARY_FILE}")
fi
readonly rsync_default_includes
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_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
)
readonly rsync_default_excludes
sync_tasks_type="$(type -t sync_tasks)"
if [ "${sync_tasks_type}" = "function" ]; then
sync_tasks
else
log_error "There is no 'sync_tasks' function to execute"
fi
}
sync() {
local sync_name=${1}
local -a rsync_servers=("${!2}")
local -a rsync_includes=("${!3}")
local -a rsync_excludes=("${!4}")
## Initialize variable to store SSH connection errors
declare -a SSH_ERRORS=()
log "START SYNC_TASKS - sync=${sync_name}"
# echo "### sync ###"
# for server in "${rsync_servers[@]}"; do
# echo "server: ${server}"
# done
# for include in "${rsync_includes[@]}"; do
# echo "include: ${include}"
# done
# for exclude in "${rsync_excludes[@]}"; do
# echo "exclude: ${exclude}"
# done
local -i n=0
local server=""
if [ "${SERVERS_FALLBACK}" = "1" ]; then
# We try to find a suitable server
while :; do
server=$(pick_server ${n} "${sync_name}")
rc=$?
if [ ${rc} != 0 ]; then
GLOBAL_RC=${E_NOSRVAVAIL}
log "STOP SYNC_TASKS - sync=${sync_name}'"
return
fi
if test_server "${server}"; then
break
else
server=""
n=$(( n + 1 ))
fi
done
else
# we force the server
server=$(pick_server "${n}" "${sync_name}")
fi
rsync_server=$(echo "${server}" | cut -d':' -f1)
rsync_port=$(echo "${server}" | cut -d':' -f2)
log "SYNC_TASKS - sync=${sync_name}: use ${server}"
# Rsync complete log file for the current run
RSYNC_LOGFILE="/var/log/${PROGNAME}.${sync_name}.rsync.log"
# Rsync stats for the current run
RSYNC_STATSFILE="/var/log/${PROGNAME}.${sync_name}.rsync-stats.log"
# reset Rsync log file
if [ -n "$(command -v truncate)" ]; then
truncate -s 0 "${RSYNC_LOGFILE}"
truncate -s 0 "${RSYNC_STATSFILE}"
else
printf "" > "${RSYNC_LOGFILE}"
printf "" > "${RSYNC_STATSFILE}"
fi
# Initialize variable here, we need it later
local -a mtree_files=()
if [ "${MTREE_ENABLED}" = "1" ]; then
mtree_bin=$(command -v mtree)
if [ -n "${mtree_bin}" ]; then
# Dump filesystem stats with mtree
log "SYNC_TASKS - sync=${sync_name}: start mtree"
# Loop over Rsync includes
for i in "${!rsync_includes[@]}"; do
include="${rsync_includes[i]}"
if [ -d "${include}" ]; then
# … but exclude for mtree what will be excluded by Rsync
mtree_excludes_file="$(mktemp --tmpdir "${PROGNAME}.${sync_name}.mtree-excludes.XXXXXX")"
add_to_temp_files "${mtree_excludes_file}"
for j in "${!rsync_excludes[@]}"; do
echo "${rsync_excludes[j]}" | grep -E "^([^/]|${include})" | sed -e "s|^${include}|.|" >> "${mtree_excludes_file}"
done
mtree_file="/var/log/evobackup.$(basename "${include}").mtree"
add_to_temp_files "${mtree_file}"
${mtree_bin} -x -c -p "${include}" -X "${mtree_excludes_file}" > "${mtree_file}"
mtree_files+=("${mtree_file}")
fi
done
if [ "${#mtree_files[@]}" -le 0 ]; then
log_error "SYNC_TASKS - ${sync_name}: ERROR: mtree didn't produce any file"
fi
log "SYNC_TASKS - sync=${sync_name}: stop mtree (files: ${mtree_files[*]})"
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (missing)"
fi
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (disabled)"
fi
rsync_bin=$(command -v rsync)
# Build the final Rsync command
# Rsync main options
rsync_main_args=()
rsync_main_args+=(--archive)
rsync_main_args+=(--itemize-changes)
rsync_main_args+=(--quiet)
rsync_main_args+=(--stats)
rsync_main_args+=(--human-readable)
rsync_main_args+=(--relative)
rsync_main_args+=(--partial)
rsync_main_args+=(--delete)
rsync_main_args+=(--delete-excluded)
rsync_main_args+=(--force)
rsync_main_args+=(--ignore-errors)
rsync_main_args+=(--log-file "${RSYNC_LOGFILE}")
rsync_main_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
# Rsync excludes
for i in "${!rsync_excludes[@]}"; do
rsync_main_args+=(--exclude "${rsync_excludes[i]}")
done
# Rsync local sources
rsync_main_args+=("${rsync_includes[@]}")
# Rsync remote destination
rsync_main_args+=("root@${rsync_server}:${REMOTE_BACKUP_DIR}/")
# … log it
log "SYNC_TASKS - sync=${sync_name}: Rsync main command : ${rsync_bin} ${rsync_main_args[*]}"
# … execute it
${rsync_bin} "${rsync_main_args[@]}"
rsync_main_rc=$?
# Copy last lines of rsync log to the main log
tail -n 30 "${RSYNC_LOGFILE}" >> "${LOGFILE}"
# Copy Rsync stats to special file
tail -n 30 "${RSYNC_LOGFILE}" | grep --invert-match --extended-regexp " [\<\>ch\.\*]\S{10} " > "${RSYNC_STATSFILE}"
# We ignore rc=24 (vanished files)
if [ ${rsync_main_rc} -ne 0 ] && [ ${rsync_main_rc} -ne 24 ]; then
log_error "SYNC_TASKS - sync=${sync_name}: Rsync main command returned an error ${rsync_main_rc}" "${LOGFILE}"
GLOBAL_RC=${E_SYNCFAILED}
else
# Build the report Rsync command
local -a rsync_report_args
rsync_report_args=()
# Rsync options
rsync_report_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
# Rsync local sources
if [ "${#mtree_files[@]}" -gt 0 ]; then
# send mtree files if there is any
rsync_report_args+=("${mtree_files[@]}")
fi
if [ -f "${RSYNC_LOGFILE}" ]; then
# send rsync full log file if it exists
rsync_report_args+=("${RSYNC_LOGFILE}")
fi
if [ -f "${RSYNC_STATSFILE}" ]; then
# send rsync stats log file if it exists
rsync_report_args+=("${RSYNC_STATSFILE}")
fi
# Rsync remote destination
rsync_report_args+=("root@${rsync_server}:${REMOTE_LOG_DIR}/")
# … log it
log "SYNC_TASKS - sync=${sync_name}: Rsync report command : ${rsync_bin} ${rsync_report_args[*]}"
# … execute it
${rsync_bin} "${rsync_report_args[@]}"
fi
log "STOP SYNC_TASKS - sync=${sync_name}"
}
setup() {
# Default return-code (0 == succes)
GLOBAL_RC=0
# Possible error codes
readonly E_NOSRVAVAIL=21 # No server is available
readonly E_SYNCFAILED=20 # Failed sync task
readonly E_DUMPFAILED=10 # Failed dump task
# explicit PATH
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
# System name (linux, openbsd…)
: "${SYSTEM:=$(uname | tr '[:upper:]' '[:lower:]')}"
# Hostname (for logs and notifications)
: "${HOSTNAME:=$(hostname)}"
# Store pid in a file named after this program's name
: "${PROGNAME:=$(basename "$0")}"
: "${PIDFILE:="/var/run/${PROGNAME}.pid"}"
# Customize the log path if you want multiple scripts to have separate log files
: "${LOGFILE:="/var/log/evobackup.log"}"
# Canary file to update before executing tasks
: "${CANARY_FILE:="/zzz_evobackup_canary"}"
# Date format for log messages
: "${DATE_FORMAT:="%Y-%m-%d %H:%M:%S"}"
# Should we fallback on other servers when the first one is unreachable?
: "${SERVERS_FALLBACK:=1}"
# timeout (in seconds) for SSH connections
: "${SSH_CONNECT_TIMEOUT:=90}"
: "${LOCAL_BACKUP_DIR:="/home/backup"}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${LOCAL_BACKUP_DIR}"
: "${ERRORS_DIR:="${LOCAL_BACKUP_DIR}/${PROGNAME}.errors-${START_TIME}"}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${ERRORS_DIR}"
# Backup directory on remote server
: "${REMOTE_BACKUP_DIR:="/var/backup"}"
# Log directory in remote server
: "${REMOTE_LOG_DIR:="/var/log"}"
# Email address for notifications
: "${MAIL:="root"}"
# Email subject for notifications
: "${MAIL_SUBJECT:="[info] EvoBackup - Client ${HOSTNAME}"}"
# Enable/disable local tasks (default: enabled)
: "${LOCAL_TASKS:=1}"
# Enable/disable sync tasks (default: enabled)
: "${SYNC_TASKS:=1}"
# Enable/disable mtree (default: enabled)
: "${MTREE_ENABLED:=1}"
# If "setup_custom" exists and is a function, let's call it
setup_custom_type="$(type -t setup_custom)"
if [ "${setup_custom_type}" = "function" ]; then
setup_custom
fi
## Force umask
umask 077
# Initialize a list of temporary files
declare -a TEMP_FILES=()
# Any file in this list will be deleted when the program exits
trap "cleanup" EXIT
}
run_evobackup() {
# Start timer
START_EPOCH=$(/bin/date +%s)
START_TIME=$(/bin/date +"%Y%m%d%H%M%S")
# Configure variables and environment
setup
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
# /!\ Only one backup processus can run at the sametime /!\
# Based on PID file, kill any running process before continuing
enforce_single_process "${PIDFILE}"
# Update canary to keep track of each run
update-evobackup-canary --who "${PROGNAME}" --file "${CANARY_FILE}"
if [ "${LOCAL_TASKS}" = "1" ]; then
local_tasks_wrapper
fi
if [ "${SYNC_TASKS}" = "1" ]; then
sync_tasks_wrapper
fi
STOP_EPOCH=$(/bin/date +%s)
case "${SYSTEM}" in
*bsd)
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
;;
*)
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
;;
esac
duration=$(( STOP_EPOCH - START_EPOCH ))
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
send_mail
exit ${GLOBAL_RC}
}

View file

@ -1,143 +0,0 @@
#!/usr/bin/env bash
# Output a message to the log file
log() {
local msg="${1:-$(cat /dev/stdin)}"
local pid=$$
printf "[%s] %s[%s]: %s\\n" \
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
>> "${LOGFILE}"
}
log_error() {
local error_msg=${1}
local error_file=${2:-""}
if [ -n "${error_file}" ] && [ -f "${error_file}" ]; then
printf "\n### %s\n" "${error_msg}" >&2
# shellcheck disable=SC2046
if [ $(wc -l "${error_file}" | cut -d " " -f 1) -gt 30 ]; then
printf "~~~{%s (tail -30)}\n" "${error_file}" >&2
tail -n 30 "${error_file}" >&2
else
printf "~~~{%s}\n" "${error_file}" >&2
cat "${error_file}" >&2
fi
printf "~~~\n" >&2
log "${error_msg}, check ${error_file}"
else
printf "\n### %s\n" "${error_msg}" >&2
log "${error_msg}"
fi
}
add_to_temp_files() {
TEMP_FILES+=("${1}")
}
# Remove all temporary file created during the execution
cleanup() {
# shellcheck disable=SC2086
rm -f "${TEMP_FILES[@]}"
find "${ERRORS_DIR}" -type d -empty -delete
}
enforce_single_process() {
local pidfile=$1
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
add_to_temp_files "${pidfile}"
echo "$$" > "${pidfile}"
}
# Build the error directory (inside ERRORS_DIR) based on the dump directory path
errors_dir_from_dump_dir() {
local dump_dir=$1
local relative_path=$(realpath --relative-to="${LOCAL_BACKUP_DIR}" "${dump_dir}")
# return absolute path
realpath --canonicalize-missing "${ERRORS_DIR}/${relative_path}"
}
# Call test_server with "HOST:PORT" string
# It will return with 0 if the server is reachable.
# It will return with 1 and a message on stderr if not.
test_server() {
local item=$1
# split HOST and PORT from the input string
local host=$(echo "${item}" | cut -d':' -f1)
local port=$(echo "${item}" | cut -d':' -f2)
local new_error
# Test if the server is accepting connections
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" "${host}" -p "${port}" -t "exit"
# shellcheck disable=SC2181
if [ $? = 0 ]; then
# SSH connection is OK
return 0
else
# SSH connection failed
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
log "${new_error}"
SSH_ERRORS+=("${new_error}")
return 1
fi
}
# Call pick_server with an optional positive integer to get the nth server in the list.
pick_server() {
local -i increment=${1:-0}
local -i list_length=${#SERVERS[@]}
local sync_name=${2:-""}
if (( increment >= list_length )); then
# We've reached the end of the list
new_error="No more server available"
new_error="${new_error} for sync '${sync_name}'"
log "${new_error}"
SSH_ERRORS+=("${new_error}")
# Log errors to stderr
for i in "${!SSH_ERRORS[@]}"; do
printf "%s\n" "${SSH_ERRORS[i]}" >&2
done
return 1
fi
# Extract the day of month, without leading 0 (which would give an octal based number)
today=$(/bin/date +%e)
# A salt is useful to randomize the starting point in the list
# but stay identical each time it's called for a server (based on hostname).
salt=$(hostname | cksum | cut -d' ' -f1)
# Pick an integer between 0 and the length of the SERVERS list
# It changes each day
n=$(( (today + salt + increment) % list_length ))
echo "${SERVERS[n]}"
}
send_mail() {
tail -20 "${LOGFILE}" | mail -s "${MAIL_SUBJECT}" "${MAIL}"
}
path_to_str() {
echo "${1}" | sed -e 's|^/||; s|/$||; s|/|:|g'
}

View file

@ -1,326 +0,0 @@
#!/usr/bin/env bash
#
# Evobackup client
# See https://gitea.evolix.org/evolix/evobackup
#
# This is a generated backup script made by:
# command: @COMMAND@
# version: @VERSION@
# date: @DATE@
#######################################################################
#
# You must configure the MAIL variable to receive notifications.
#
# There is some optional configuration that you can do
# at the end of this script.
#
#######################################################################
# Email adress for notifications
MAIL=__NOTIFICATION_MAIL__
#######################################################################
#
# The "sync_tasks" function will be called by the "run_evobackup" function.
#
# You can customize the variables:
# * "SYNC_NAME" (String)
# * "SERVERS" (Array of HOST:PORT)
# * "RSYNC_INCLUDES" (Array of paths to include)
# * "RSYNC_EXCLUDES" (Array of paths to exclude)
#
# WARNING: remember to single-quote paths if they contain globs (*)
# and you want to pass them as-is to Rsync.
#
# The "sync" function can be called multiple times
# with a different set of variables.
# That way you can to sync to various destinations.
#
# Default includes/excludes are defined in the "main" library,
# referenced at this end of this file.
#
#######################################################################
# shellcheck disable=SC2034
sync_tasks() {
########## System-only backup (to Evolix servers) #################
SYNC_NAME="evolix-system"
SERVERS=(
__SRV0_HOST__:__SRV0_PORT__
__SRV1_HOST__:__SRV1_PORT__
)
RSYNC_INCLUDES=(
"${rsync_default_includes[@]}"
/etc
/root
/var
)
RSYNC_EXCLUDES=(
"${rsync_default_excludes[@]}"
)
sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
########## Full backup (to client servers) ########################
### SYNC_NAME="client-full"
### SERVERS=(
### client-backup00.evolix.net:2221
### client-backup01.evolix.net:2221
### )
### RSYNC_INCLUDES=(
### "${rsync_default_includes[@]}"
### /etc
### /root
### /var
### /home
### /srv
### )
### RSYNC_EXCLUDES=(
### "${rsync_default_excludes[@]}"
### )
### sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
}
#######################################################################
#
# The "local_tasks" function will be called by the "run_evobackup" function.
#
# You can call any available "dump_xxx" function
# (usually installed at /usr/local/lib/evobackup/dump-*.sh)
#
# You can also write some custom functions and call them.
# A "dump_custom" example is available further down.
#
#######################################################################
local_tasks() {
########## Server state ###########
# Run dump-server-state to extract system information
#
# Options : any dump-server-state supported option
# (except --dump-dir that will be overwritten)
# See 'dump-server-state -h' for details.
#
dump_server_state
########## MySQL ##################
# Very common strategy for a single instance server with default configuration :
#
### dump_mysql_global; dump_mysql_grants; dump_mysql_summary
#
# See below for details regarding dump functions for MySQL/MariaDB
# Dump all databases in a single compressed file
#
# Options :
# --masterdata (default: <absent>)
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_global
# Dump each database separately, in a compressed file
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_per_base
# Dump permissions of an instance (using pt-show-grants)
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
# WARNING - unsupported options :
# --defaults-extra-file
# --defaults-group-suffix
# You have to provide credentials manually
#
### dump_mysql_grants
# Dump complete summary of an instance (using pt-mysql-summary)
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_summary
# Dump each table in separate schema/data files
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_tabs
########## PostgreSQL #############
# Dump all databases in a single file (compressed or not)
#
### dump_postgresql_global
# Dump a specific databse with only some tables, or all but some tables (must be configured)
#
### dump_postgresql_filtered
# Dump each database separately, in a compressed file
#
### dump_postgresql_per_base
########## MongoDB ################
### dump_mongodb [--user=foo] [--password=123456789]
########## Redis ##################
# Copy data file for all instances
#
### dump_redis [--instances=<all|instance1|instance2>]
########## Elasticsearch ##########
# Snapshot data for a single-node cluster
#
### dump_elasticsearch_snapshot_singlenode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily]
# Snapshot data for a multi-node cluster
#
### dump_elasticsearch_snapshot_multinode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily] [--nfs-server=192.168.2.1]
########## RabbitMQ ###############
### dump_rabbitmq
########## MegaCli ################
# Copy RAID config
#
### dump_megacli_config
# Dump file access control lists
#
### dump_facl
########## OpenLDAP ###############
### dump_ldap
########## Network ################
# Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
#
### dump_traceroute --targets=host_or_ip[,host_or_ip]
dump_traceroute --targets=8.8.8.8,www.evolix.fr,travaux.evolix.net
# No-op, in case nothing is enabled
:
}
# This is an example for a custom dump function
# Uncomment, customize and call it from the "local_tasks" function
### dump_custom() {
### # Set dump and errors directories and files
### local dump_dir="${LOCAL_BACKUP_DIR}/custom"
### local dump_file="${dump_dir}/dump.gz"
### local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
### local error_file="${errors_dir}/dump.err"
###
### # Reset dump and errors directories
### rm -rf "${dump_dir}" "${errors_dir}"
### # shellcheck disable=SC2174
### mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
###
### # Log the start of the function
### log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
###
### # Prepare the dump command (errors go to the error file and the data to the dump file)
### dump_cmd="my-dump-command 2> ${error_file} > ${dump_file}"
### log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
###
### # Execute the dump command
### ${dump_cmd}
###
### # Check result and deal with potential errors
### local last_rc=$?
### # shellcheck disable=SC2086
### if [ ${last_rc} -ne 0 ]; then
### log_error "LOCAL_TASKS - ${FUNCNAME[0]}: my-dump-command to ${dump_file} returned an error ${last_rc}" "${error_file}"
### GLOBAL_RC=${E_DUMPFAILED}
### else
### rm -f "${error_file}"
### fi
###
### # Log the end of the function
### log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
### }
########## Optional configuration #####################################
setup_custom() {
# System name ("linux" and "openbsd" currently supported)
### SYSTEM="$(uname)"
# Host name for logs and notifications
### HOSTNAME="$(hostname)"
# Email subject for notifications
### MAIL_SUBJECT="[info] EvoBackup - Client ${HOSTNAME}"
# No-op in case nothing is executed
:
}
########## Libraries ##################################################
# Change this to wherever you install the libraries
LIBDIR="/usr/local/lib/evobackup"
source "${LIBDIR}/main.sh"
########## Let's go! ##################################################
run_evobackup

View file

@ -1,50 +0,0 @@
---
- name: Dependencies are present
ansible.builtin.apt:
name:
- rsync
- mtree-netbsd
state: present
- name: "Remount /usr if needed"
include_role:
name: remount-usr
when: evobackup_client__lib_dir is search("/usr") or evobackup_client__bin_dir is search("/usr")
- name: copy evobackup libs
ansible.builtin.copy:
src: upstream/lib/
dest: "{{ evobackup_client__lib_dir }}/"
force: True
mode: "0644"
owner: root
group: root
- name: copy evobackupctl script
ansible.builtin.copy:
src: upstream/bin/evobackupctl
dest: "{{ evobackup_client__bin_dir }}/evobackupctl"
force: True
mode: "0755"
owner: root
group: root
- name: LIBDIR is customized in evobackupctl
ansible.builtin.replace:
path: "{{ evobackup_client__bin_dir }}/evobackupctl"
regexp: "^LIBDIR=.+"
replace: "LIBDIR=\"{{ evobackup_client__lib_dir }}\""
- name: Evobackup canary cron is present
ansible.builtin.template:
src: update-evobackup-canary.sh.j2
dest: "{{ evobackup_client__update_canary_path }}"
mode: "0700"
when: evobackup_client__update_canary_enable | bool
- name: Evobackup canary cron is absent
ansible.builtin.file:
path: "{{ evobackup_client__update_canary_path }}"
state: absent
when: not ( evobackup_client__update_canary_enable | bool)

View file

@ -1,31 +1,26 @@
---
- name: Install evobackup client components
ansible.builtin.include: "install.yml"
- ansible.builtin.include: "ssh_key.yml"
tags:
- evobackup_client
- evobackup_client_backup_ssh_key
### This is commented because supposedly non-functionnal
- ansible.builtin.include: "jail.yml"
tags:
- evobackup_client
- evobackup_client_jail
# - ansible.builtin.include: "ssh_key.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_ssh_key
- ansible.builtin.include: "upload_scripts.yml"
tags:
- evobackup_client
- evobackup_client_backup_scripts
# - ansible.builtin.include: "jail.yml"
# tags:
# - evobackup_client
# - evobackup_client_jail
- ansible.builtin.include: "open_ssh_ports.yml"
tags:
- evobackup_client
- evobackup_client_backup_firewall
# - ansible.builtin.include: "upload_scripts.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_scripts
# - ansible.builtin.include: "open_ssh_ports.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_firewall
# - ansible.builtin.include: "verify_ssh.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_hosts
- ansible.builtin.include: "verify_ssh.yml"
tags:
- evobackup_client
- evobackup_client_backup_hosts

View file

@ -1,3 +0,0 @@
#!/bin/sh
update-evobackup-canary --who {{ evobackup_client__update_canary_who | mandatory }}

View file

@ -0,0 +1,305 @@
#!/bin/sh
# Careful, the zzz_evobackup template was last updated on 2020/06/08
#
# 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>
#
# Licence: AGPLv3
#
# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES
# Fail on unassigned variables
set -u
##### Configuration ###################################################
# email adress for notifications
MAIL={{ evobackup_client__mail }}
# list of hosts (hostname or IP) and SSH port for Rsync
SERVERS="{% for host in evobackup_client__hosts %}{{ host.name }}:{{ host.port }}{% if loop.index != loop.length %} {% endif %}{% endfor %}"
# Should we fallback on servers when the first is unreachable ?
SERVERS_FALLBACK={{ evobackup_client__servers_fallback }}
# timeout (in seconds) for SSH connections
SSH_CONNECT_TIMEOUT=${SSH_CONNECT_TIMEOUT:-30}
## We use /home/backup : feel free to use your own dir
LOCAL_BACKUP_DIR="{{ evobackup_client__backup_path }}"
# You can set "linux" or "bsd" manually or let it choose automatically
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
# Change these 2 variables if you have more than one backup cron
PIDFILE="{{ evobackup_client__pid_path }}"
LOGFILE="{{ evobackup_client__log_path }}"
## Enable/Disable tasks
LOCAL_TASKS=${LOCAL_TASKS:-1}
SYNC_TASKS=${SYNC_TASKS:-1}
##### SETUP AND FUNCTIONS #############################################
BEGINNING=$(/bin/date +"%d-%m-%Y ; %H:%M")
# shellcheck disable=SC2174
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
## lang = C for english outputs
export LANGUAGE=C
export LANG=C
## Force umask
umask 077
## Initialize variable to store SSH connection errors
SERVERS_SSH_ERRORS=""
# Call test_server with "HOST:PORT" string
# It will return with 0 if the server is reachable.
# It will return with 1 and a message on stderr if not.
test_server() {
item=$1
# split HOST and PORT from the input string
host=$(echo "${item}" | cut -d':' -f1)
port=$(echo "${item}" | cut -d':' -f2)
# Test if the server is accepting connections
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" -i {{ evobackup_client__root_key_path }} "${host}" -p "${port}" -t "exit"
# shellcheck disable=SC2181
if [ $? = 0 ]; then
# SSH connection is OK
return 0
else
# SSH connection failed
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
return 1
fi
}
# Call pick_server with an optional positive integer to get the nth server in the list.
pick_server() {
increment=${1:-0}
list_length=$(echo "${SERVERS}" | wc -w)
if [ "${increment}" -ge "${list_length}" ]; then
# We've reached the end of the list
new_error="No more server available"
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
# Log errors to stderr
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >&2
# Log errors to logfile
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >> $LOGFILE
return 1
fi
# Extract the day of month, without leading 0 (which would give an octal based number)
today=$(date +%e)
# A salt is useful to randomize the starting point in the list
# but stay identical each time it's called for a server (based on hostname).
salt=$(hostname | cksum | cut -d' ' -f1)
# Pick an integer between 0 and the length of the SERVERS list
# It changes each day
item=$(( (today + salt + increment) % list_length ))
# cut starts counting fields at 1, not 0.
field=$(( item + 1 ))
echo "${SERVERS}" | cut -d' ' -f${field}
}
## 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
##### LOCAL BACKUP ####################################################
if [ "${LOCAL_TASKS}" = "1" ]; then
## Dump system and kernel versions
uname -a > ${LOCAL_BACKUP_DIR}/uname
## 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
## Dump process with ps
ps auwwx >${LOCAL_BACKUP_DIR}/ps.out
if [ "${SYSTEM}" = "linux" ]; then
## Dump network connections with ss
ss -taupen > ${LOCAL_BACKUP_DIR}/netstat.out
## List Debian packages
dpkg -l > ${LOCAL_BACKUP_DIR}/packages
dpkg --get-selections > ${LOCAL_BACKUP_DIR}/packages.getselections
apt-cache dumpavail > ${LOCAL_BACKUP_DIR}/packages.available
## 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="${LOCAL_BACKUP_DIR}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)"
fdisk -l "/dev/${disk}" > "${LOCAL_BACKUP_DIR}/partitions-${disk}" 2>&1
done
cat ${LOCAL_BACKUP_DIR}/partitions-* > ${LOCAL_BACKUP_DIR}/partitions
## Dump iptables
if [ -x /sbin/iptables ]; then
{ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${LOCAL_BACKUP_DIR}/iptables.txt
fi
## Dump findmnt(8) output
FINDMNT_BIN=$(command -v findmnt)
if [ -x "${FINDMNT_BIN}" ]; then
${FINDMNT_BIN} > ${LOCAL_BACKUP_DIR}/findmnt.txt
fi
else
## Dump network connections with netstat
netstat -finet -atn > ${LOCAL_BACKUP_DIR}/netstat.out
## List OpenBSD packages
pkg_info -m > ${LOCAL_BACKUP_DIR}/packages
## Dump MBR / table partitions
disklabel sd0 > ${LOCAL_BACKUP_DIR}/partitions
## Dump pf infos
pfctl -sa > ${LOCAL_BACKUP_DIR}/pfctl-sa.txt
fi
## Dump rights
#getfacl -R /var > ${LOCAL_BACKUP_DIR}/rights-var.txt
#getfacl -R /etc > ${LOCAL_BACKUP_DIR}/rights-etc.txt
#getfacl -R /usr > ${LOCAL_BACKUP_DIR}/rights-usr.txt
#getfacl -R /home > ${LOCAL_BACKUP_DIR}/rights-home.txt
fi
##### REMOTE BACKUP ###################################################
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)
HOSTNAME=$(hostname)
if [ "${SYSTEM}" = "linux" ]; then
rep="/bin /boot /lib /opt /sbin /usr /srv"
else
rep="/bsd /bin /sbin /usr"
fi
if [ "${SYNC_TASKS}" = "1" ]; then
# /!\ DO NOT USE COMMENTS in the rsync command /!\
# It breaks the command and destroys data, simply remove (or add) lines.
# Remote shell command
RSH_COMMAND="ssh -i {{ evobackup_client__root_key_path }} -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'"
# ignore check because we want it to split the different arguments to $rep
# shellcheck disable=SC2086
rsync -avzh --stats --delete --delete-excluded --force --ignore-errors --partial \
--exclude "lost+found" \
--exclude ".nfs.*" \
--exclude "/var/log" \
--exclude "/var/log/evobackup*" \
--exclude "/var/lib/mysql" \
--exclude "/var/lib/postgres" \
--exclude "/var/lib/postgresql" \
--exclude "/var/lib/sympa" \
--exclude "/var/lib/metche" \
--exclude "/var/run" \
--exclude "/var/lock" \
--exclude "/var/state" \
--exclude "/var/apt" \
--exclude "/var/cache" \
--exclude "/usr/src" \
--exclude "/usr/doc" \
--exclude "/usr/share/doc" \
--exclude "/usr/obj" \
--exclude "dev" \
--exclude "/var/spool/postfix" \
--exclude "/var/lib/amavis/amavisd.sock" \
--exclude "/var/lib/munin/*tmp*" \
--exclude "/var/lib/php5" \
--exclude "/var/spool/squid" \
--exclude "/var/lib/elasticsearch" \
--exclude "/var/lib/amavis/tmp" \
--exclude "/var/lib/clamav/*.tmp" \
--exclude "/home/mysqltmp" \
--exclude "/var/lib/php/sessions" \
${rep} \
/etc \
/root \
/var \
-e "${RSH_COMMAND}" \
"root@${SSH_SERVER}:/var/backup/" \
| tail -30 >> $LOGFILE
fi
##### REPORTING #######################################################
END=$(/bin/date +"%d-%m-%Y ; %H:%M")
printf "EvoBackup - %s - START %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
"${HOSTNAME}" "${BEGINNING}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
>> $LOGFILE
printf "EvoBackup - %s - STOP %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
"${HOSTNAME}" "${END}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
>> $LOGFILE
tail -10 $LOGFILE | \
mail -s "[info] EvoBackup - Client ${HOSTNAME}" \
${MAIL}

View file

@ -37,6 +37,6 @@ Main variables are:
* `evolinux_postfix_purge_exim`: purge Exim packages (default: `True`) ;
* `evolinux_ssh_password_auth_addresses`: list of addresses that can authenticate with a password (default: `[]`)
* `evolinux_ssh_disable_root`: disable SSH access for root (default: `False`)
* `evolinux_ssh_allow_current_user`: don't lock yourself out is there is an AllowUsers or AllowGroups directive (default: `False`)
* `evolinux_ssh_allow_current_user`: don't lock yourself out (default: `False`)
The full list of variables (with default values) can be found in `defaults/main.yml`.

View file

@ -142,9 +142,6 @@ evolinux_ssh_group: "evolinux-ssh"
#
# evolinux_users_include: True
# bash
evolinux_bash_config_include: true
# root
evolinux_root_include: True

View file

@ -64,19 +64,12 @@
when: evolinux_logs_default_dateext | bool
# Logcheck
- name: Check if journald.logfiles exists
stat:
path: /etc/logcheck/logcheck.logfiles.d/journal.logfiles
register: _logcheck_journald_logfiles
- name: Disable logcheck monitoring of journald
ansible.builtin.lineinfile:
dest: /etc/logcheck/logcheck.logfiles.d/journal.logfiles
dest: /etc/logrotate.conf
line: "#journal"
regexp: "^journal"
when:
- _logcheck_journald_logfiles.stat.exists
- evolinux_logs_disable_logcheck_journald | bool
when: evolinux_logs_disable_logcheck_journald | bool
# Journald
- name: /etc/systemd/journald.conf.d/ is present

View file

@ -76,7 +76,6 @@
- name: Bash configuration
ansible.builtin.import_tasks: bash.yml
when: evolinux_bash_config_include | bool
- name: Root user configuration
ansible.builtin.import_tasks: root.yml

View file

@ -16,78 +16,34 @@
dest: /etc/ssh/sshd_config.d/z-evolinux-defaults.conf
mode: "0644"
# Should we allow the current user?
- name: Allow the current user
block:
- name: "Get current user's login"
ansible.builtin.command:
cmd: logname
changed_when: False
register: _logname
check_mode: no
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowUsers' /etc/ssh/sshd_config /etc/ssh/sshd_config.d"
failed_when: False
changed_when: False
register: grep_allowusers_ssh
check_mode: no
- name: verify AllowGroups directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowGroups' /etc/ssh/sshd_config /etc/ssh/sshd_config.d"
failed_when: False
changed_when: False
register: grep_allowgroups_ssh
check_mode: no
# If we have AllowUsers but not AllowGroups, append the user to the list
# (in the first file where we found the directive)
- name: "Append user to existing AllowUsers sshd directive"
ansible.builtin.replace:
dest: "{{ grep_allowusers_ssh.stdout_lines[0] }}"
regexp: '^(AllowUsers ((?!{{ _logname.stdout }}).)*)$'
replace: '\1 {{ _logname.stdout }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc == 0
- grep_allowgroups_ssh.rc != 0
# If we have AllowGroups but not AllowUsers, add the user to the group and append the group to the list
# (in the first file where we found the directive)
- name: "Append evolinux ssh group to AllowGroups sshd directive"
ansible.builtin.replace:
dest: "{{ grep_allowgroups_ssh.stdout_lines[0] }}"
regexp: '^(AllowGroups ((?!{{ evolinux_ssh_group }}).)*)$'
replace: '\1 {{ evolinux_ssh_group }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc != 0
- grep_allowgroups_ssh.rc == 0
- name: "evolinux ssh group is present"
ansible.builtin.group:
name: "{{ evolinux_ssh_group }}"
when:
- grep_allowusers_ssh.rc != 0
- grep_allowgroups_ssh.rc == 0
- name: "Add current user to evolinux ssh group"
ansible.builtin.user:
name: "{{ _logname.stdout }}"
group: "{{ evolinux_ssh_group }}"
append: yes
when:
- grep_allowusers_ssh.rc != 0
- grep_allowgroups_ssh.rc == 0
# If we don't have AllowGroups nor AllowUsers, do nothing
- name: "Get current user's group"
ansible.builtin.command:
cmd: logname
changed_when: False
register: logname
check_mode: no
when: evolinux_ssh_allow_current_user | bool
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "grep -ER '^AllowUsers' /etc/ssh"
failed_when: False
changed_when: False
register: grep_allowusers_ssh
check_mode: no
when: evolinux_ssh_allow_current_user | bool
- name: "Add AllowUsers sshd directive for current user"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config.d/allow_evolinux_user.conf
create: yes
line: "AllowUsers {{ logname.stdout }}"
insertafter: 'Subsystem'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when: evolinux_ssh_allow_current_user and grep_allowusers_ssh.rc != 0
- ansible.builtin.meta: flush_handlers
# TODO si allowusers et allowgroups, ajouter utilisateur aux deux
# TODO si allowgroups, ajouter groupe de lutilisateur

View file

@ -61,66 +61,40 @@
notify: reload sshd
when: ansible_distribution_major_version is version('9', '>=')
# Should we allow the current user?
- name: Allow the current user
block:
- name: "Get current user"
ansible.builtin.command:
cmd: logname
changed_when: False
register: _logname
check_mode: no
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "grep -E '^AllowUsers' /etc/ssh/sshd_config"
failed_when: False
changed_when: False
register: grep_allowusers_ssh
check_mode: no
- name: verify AllowGroups directive
ansible.builtin.command:
cmd: "grep -E '^AllowGroups' /etc/ssh/sshd_config"
failed_when: False
changed_when: False
register: grep_allowgroups_ssh
check_mode: no
# If we have AllowUsers but not AllowGroups, append the user to the list
- name: "Modify AllowUsers sshd directive for current user"
ansible.builtin.replace:
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers ((?!{{ _logname.stdout }}).)*)$'
replace: '\1 {{ _logname.stdout }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc == 0
- grep_allowgroups_ssh.rc != 0
# If we have AllowGroups but not AllowUsers, add the user to the group and append the group to the list
- name: "Add current user to {{ evolinux_ssh_group }} group"
ansible.builtin.user:
name: "{{ _logname.stdout }}"
group: "{{ evolinux_ssh_group }}"
append: yes
when:
- grep_allowusers_ssh.rc != 0
- grep_allowgroups_ssh.rc == 0
- name: "Append '{{ evolinux_ssh_group }}' to AllowGroups sshd directive"
ansible.builtin.replace:
dest: "/etc/ssh/sshd_config"
regexp: '^(AllowGroups ((?!{{ evolinux_ssh_group }}).)*)$'
replace: '\1 {{ evolinux_ssh_group }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc != 0
- grep_allowgroups_ssh.rc == 0
- name: "Get current user"
ansible.builtin.command:
cmd: logname
changed_when: False
register: logname
check_mode: no
when: evolinux_ssh_allow_current_user | bool
# we must double-escape caracters, because python
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "grep -E '^AllowUsers' /etc/ssh/sshd_config"
failed_when: False
changed_when: False
register: grep_allowusers_ssh
check_mode: no
when: evolinux_ssh_allow_current_user | bool
- name: "Add AllowUsers sshd directive for current user"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config
line: "\nAllowUsers {{ logname.stdout }}"
insertafter: 'Subsystem'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when: evolinux_ssh_allow_current_user and grep_allowusers_ssh.rc != 0
- name: "Modify AllowUsers sshd directive for current user"
ansible.builtin.replace:
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers ((?!{{ logname.stdout }}).)*)$'
replace: '\1 {{ logname.stdout }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when: evolinux_ssh_allow_current_user and grep_allowusers_ssh.rc == 0
- ansible.builtin.meta: flush_handlers

View file

@ -1,23 +1,8 @@
---
- name: Fetch SSHd config files
ansible.builtin.command:
cmd: "find /etc/ssh -type f \\( -name 'sshd_config' -o -path '/etc/ssh/sshd_config.d/*.conf' \\)"
changed_when: False
check_mode: no
register: _ssh_config_paths
- ansible.builtin.debug:
var: _ssh_config_paths
verbosity: 1
############################
# AllowUsers or AllowGroups
############################
- name: verify AllowGroups directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowGroups' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -Er '^AllowGroups' /etc/ssh"
changed_when: False
failed_when: False
check_mode: no
@ -29,7 +14,7 @@
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowUsers' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -Er '^AllowUsers' /etc/ssh"
changed_when: False
failed_when: False
check_mode: no
@ -57,14 +42,12 @@
var: ssh_allowusers
verbosity: 1
- name: Configure SSH in AllowGroups mode
ansible.builtin.include: ssh_allowgroups.yml
- ansible.builtin.include: ssh_allowgroups.yml
when:
- ssh_allowgroups
- not ssh_allowusers
- name: Configure SSH in AllowUsers mode
ansible.builtin.include: ssh_allowusers.yml
- ansible.builtin.include: ssh_allowusers.yml
vars:
user: "{{ item.value }}"
loop: "{{ evolinux_users | dict2items }}"
@ -73,24 +56,7 @@
- ssh_allowusers
- not ssh_allowgroups
# Do this again, to update the value
- name: Fetch SSHd config files
ansible.builtin.command:
cmd: "find /etc/ssh -type f \\( -name 'sshd_config' -o -path '/etc/ssh/sshd_config.d/*.conf' \\)"
changed_when: False
check_mode: no
register: _ssh_config_paths
##################
# PermitRootLogin
##################
### For Debian < 12
# if there is a commented value for PermitRootLogin
# we replace it with a "no"
- name: Root login is disabled (Debian < 12)
- name: disable root login
ansible.builtin.replace:
dest: /etc/ssh/sshd_config
regexp: '^#PermitRootLogin (yes|without-password|prohibit-password)'
@ -98,15 +64,11 @@
notify: reload sshd
when:
- evolinux_root_disable_ssh | bool
- ansible_distribution_major_version is version('12', '<')
### For Debian >= 12
# if there is no value for PermitRootLogin (anywhere)
# we add a "no" in z-evolinux-users.conf
- ansible_distribution_major_version is version('11', '<=')
- name: verify PermitRootLogin directive (Debian >= 12)
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^PermitRootLogin' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -Er '^PermitRootLogin' /etc/ssh"
changed_when: False
failed_when: False
check_mode: no
@ -114,7 +76,12 @@
when:
- ansible_distribution_major_version is version('12', '>=')
- name: Root login is disabled (Debian >= 12)
# TODO avertir lorsque PermitRootLogin est déjà configuré?
- ansible.builtin.debug:
var: grep_permitrootlogin_ssh
verbosity: 1
- name: disable root login (Debian >= 12)
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config.d/z-evolinux-users.conf
line: "PermitRootLogin no"
@ -126,48 +93,6 @@
when:
- evolinux_root_disable_ssh | bool
- ansible_distribution_major_version is version('12', '>=')
- grep_permitrootlogin_ssh.rc != 0
#####################
# Allow current user
#####################
- name: Allow current user
block:
- name: Check if evolinux ssh group is used
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowGroups.+{{ evolinux_ssh_group }}' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
changed_when: False
failed_when: False
check_mode: no
register: grep_evolinux_group_ssh
- debug:
var: grep_evolinux_group_ssh
- name: "Get current user's login"
ansible.builtin.command:
cmd: logname
changed_when: False
register: _logname
check_mode: no
- debug:
var: evolinux_ssh_group
- debug:
var: evolinux_ssh_allow_current_user
- name: "Add current user ({{ _logname.stdout }}) to {{ evolinux_ssh_group }} group"
ansible.builtin.user:
name: "{{ _logname.stdout }}"
groups: "{{ evolinux_ssh_group }}"
append: yes
when:
- grep_evolinux_group_ssh.rc == 0
when:
- evolinux_ssh_group is defined
- evolinux_ssh_group | length > 0
- evolinux_ssh_allow_current_user | bool
- grep_permitrootlogin_ssh.rc == 1
- ansible.builtin.meta: flush_handlers

View file

@ -1,27 +1,18 @@
---
###
# this check must be repeated for each user
# even if it's been done before
- name: Fetch SSHd config files
ansible.builtin.command:
cmd: "find /etc/ssh -type f \\( -name 'sshd_config' -o -path '/etc/ssh/sshd_config.d/*.conf' \\)"
changed_when: False
check_mode: no
register: _ssh_config_paths
- name: verify AllowGroups directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowGroups' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -Er '^AllowGroups' /etc/ssh"
changed_when: False
failed_when: False
check_mode: no
register: grep_allowgroups_ssh
when:
- ansible_distribution_major_version is version('11', '<=')
###
- name: "Add AllowGroups sshd directive with '{{ evolinux_ssh_group }}' (Debian < 12)"
- name: "Add AllowGroups sshd directive with '{{ evolinux_ssh_group }}'"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config
line: "\nAllowGroups {{ evolinux_ssh_group }}"
@ -30,25 +21,25 @@
notify: reload sshd
when:
- ansible_distribution_major_version is version('11', '<=')
- grep_allowgroups_ssh.rc == 1 # Not found
- name: "Add AllowGroups sshd directive with '{{ evolinux_ssh_group }}' (Debian >= 12)"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config.d/z-evolinux-users.conf
line: "\nAllowGroups {{ evolinux_ssh_group }}"
validate: '/usr/sbin/sshd -t -f %s'
create: yes
notify: reload sshd
when:
- ansible_distribution_major_version is version('12', '>=')
- grep_allowgroups_ssh.rc == 1 # Not found
- grep_allowgroups_ssh.rc != 0
- name: "Append '{{ evolinux_ssh_group }}' to AllowGroups sshd directive"
ansible.builtin.replace:
dest: "{{ grep_allowgroups_ssh.stdout_lines[0] }}"
dest: /etc/ssh/sshd_config
regexp: '^(AllowGroups ((?!\b{{ evolinux_ssh_group }}\b).)*)$'
replace: '\1 {{ evolinux_ssh_group }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowgroups_ssh.rc == 0 or grep_allowgroups_ssh.rc == 2 # Found, return code can be 0 or 2
- ansible_distribution_major_version is version('11', '<=')
- grep_allowgroups_ssh.rc == 0
- name: "Add AllowGroups sshd directive with '{{ evolinux_ssh_group }}'"
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config.d/z-evolinux-users.conf
line: "AllowGroups {{ evolinux_ssh_group }}"
create: yes
mode: "0644"
validate: '/usr/sbin/sshd -t -f %s'
when:
- ansible_distribution_major_version is version('12', '>=')

View file

@ -1,84 +1,55 @@
---
###
# these checks must be repeated for each user
# this check must be repeated for each user
# even if it's been done before
- name: Fetch SSHd config files
- name: verify AllowUsers directive
ansible.builtin.command:
cmd: "find /etc/ssh -type f \\( -name 'sshd_config' -o -path '/etc/ssh/sshd_config.d/*.conf' \\)"
changed_when: False
check_mode: no
register: _ssh_config_paths
- name: Verify AllowUsers directive
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^AllowUsers' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -E '^AllowUsers' /etc/ssh/sshd_config"
changed_when: False
failed_when: False
check_mode: no
register: grep_allowusers_ssh
###
- name: "Add AllowUsers sshd directive with '{{ user.name }}' (Debian < 12)"
- name: "Add AllowUsers sshd directive with '{{ user.name }}'"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config
line: "\nAllowUsers {{ user.name }}"
insertafter: 'Subsystem'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc != 0
- ansible_distribution_major_version is version('12', '<')
- name: "Add AllowUsers sshd directive with '{{ user.name }}' (Debian >= 12)"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config.d/z-evolinux-users.conf
line: "\nAllowUsers {{ user.name }}"
insertafter: 'Subsystem'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc != 0
- ansible_distribution_major_version is version('12', '>=')
when: grep_allowusers_ssh.rc != 0
- name: "Append '{{ user.name }}' to AllowUsers sshd directive"
ansible.builtin.replace:
dest: "{{ grep_allowusers_ssh.stdout_lines[0] }}"
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers ((?!\b{{ user.name }}\b).)*)$'
replace: '\1 {{ user.name }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_allowusers_ssh.rc == 0
when: grep_allowusers_ssh.rc == 0
- name: "verify Match User directive"
ansible.builtin.command:
cmd: "grep --extended-regexp --recursive --files-with-matches '^Match User' {{ _ssh_config_paths.stdout_lines | join(' ') }}"
cmd: "grep -E '^Match User' /etc/ssh/sshd_config"
changed_when: False
failed_when: False
check_mode: no
register: grep_matchuser_ssh
- name: "Add Match User sshd directive with '{{ user.name }}' (Debian <= 10)"
- name: "Add Match User sshd directive with '{{ user.name }}'"
ansible.builtin.lineinfile:
dest: /etc/ssh/sshd_config
line: "\nMatch User {{ user.name }}\n PasswordAuthentication no"
insertafter: "# END EVOLINUX PASSWORD RESTRICTIONS BY ADDRESS"
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_matchuser_ssh.rc != 0
- ansible_distribution_major_version is version('10', '<=')
when: grep_matchuser_ssh.rc != 0
- name: "Append '{{ user.name }}' to Match User's sshd directive (Debian <= 10)"
- name: "Append '{{ user.name }}' to Match User's sshd directive"
ansible.builtin.replace:
dest: "{{ grep_matchuser_ssh.stdout_lines[0] }}"
dest: /etc/ssh/sshd_config
regexp: '^(Match User ((?!{{ user.name }}).)*)$'
replace: '\1,{{ user.name }}'
validate: '/usr/sbin/sshd -t -f %s'
notify: reload sshd
when:
- grep_matchuser_ssh.rc == 0
- ansible_distribution_major_version is version('10', '<=')
when: grep_matchuser_ssh.rc == 0

View file

@ -24,8 +24,6 @@ nagios ALL = NOPASSWD: /sbin/megacli -LdInfo -Lall -aALL -NoLog
nagios ALL = NOPASSWD: /sbin/megacli -AdpBbuCmd -GetBbuStatus -aALL -NoLog
nagios ALL = NOPASSWD: /sbin/ssacli controller all show status
nagios ALL = NOPASSWD: /sbin/ssacli controller slot=0 logicaldrive all show
nagios ALL = NOPASSWD: /usr/local/bin/mvcli info -o blk
nagios ALL = NOPASSWD: /usr/local/bin/mvcli info -o vd
nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_gluster.rb
nagios ALL = (clamav) NOPASSWD: /usr/bin/clamscan /tmp/safe.txt

View file

@ -1,12 +1,12 @@
#!/bin/sh
VERSION="24.05"
VERSION="23.10.1"
show_version() {
cat <<END
evomaintenance version ${VERSION}
Copyright 2007-2024 Evolix <info@evolix.fr>,
Copyright 2007-2023 Evolix <info@evolix.fr>,
Gregory Colpart <reg@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>,
Brice Waegeneire <bwaegeneire@evolix.fr>,
@ -437,14 +437,6 @@ while :; do
printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2
exit 1
;;
--no-evocheck)
# disable evocheck hook
EVOCHECK=0
;;
--evocheck)
# enable evocheck hook
EVOCHECK=1
;;
--no-commit)
# disable commit hook
HOOK_COMMIT=0
@ -589,7 +581,7 @@ GIT_REPOSITORIES="/etc /etc/bind /usr/share/scripts"
# Add /etc directories from lxc containers if they are git directories
if [ -d /var/lib/lxc ]; then
GIT_REPOSITORIES="${GIT_REPOSITORIES} $(find -L /var/lib/lxc/ -maxdepth 3 -name 'etc' | tr '\n' ' ' | sed 's/[[:space:]]\+$//')"
GIT_REPOSITORIES="${GIT_REPOSITORIES} $(find /var/lib/lxc/ -maxdepth 3 -name 'etc' | tr '\n' ' ' | sed 's/[[:space:]]\+$//')"
fi
# initialize variable

View file

@ -0,0 +1,33 @@
From: __FULLFROM__
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
To: __TO__
Subject: [evomaintenance] Intervention sur __HOSTNAME__ (__USER__)
Bonjour,
Une intervention vient de se terminer sur votre serveur.
Voici les renseignements sur l'intervention :
Nom du serveur : __HOSTNAME__
Personne ayant réalisée l'intervention : __USER__
Intervention réalisée depuis : __IP__
Début de l'intervention : __BEGIN_DATE__
Fin de l'intervention : __END_DATE__
###
Renseignements sur l'intervention :
__TEXTE__
###
__GIT_COMMITS__
Pour réagir à cette intervention, vous pouvez répondre à ce message
(sur l'adresse mail __FROM__). En cas d'urgence, utilisez
l'adresse __URGENCYFROM__ ou notre téléphone portable d'astreinte
(__URGENCYTEL__)
Cordialement,
--
__FULLFROM__

View file

@ -1,100 +0,0 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project **does not adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)**.
## [Unreleased]
### Added
Add missing (but documented) `--(no-)evocheck` options
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [23.10.1] - 2023-10-09
### Fixed
* Use a special variable name since USER is always defined from the environment
## [23.10] - 2023-10-09
### Added
* Force a user name with `-u,--user` option (default is still `logname(1)`).
* More people credited
### Deprecated
* `--autosysadmin` is replaced by `--user autosysadmin`
## [22.07] - 2022-07-05
### Added
* Add `--autosysadmin` flag
* Commit change in /etc of lxc containers
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [22.01] - 2022-01-25
### Added
* version/host/user headers in sent email
### Changed
New version pattern
## [0.6.4] - 2021-06-17
### Added
* fallback if findmnt is absent
## [0.6.3] - 2020-02-02
### Added
* Notify syslog when partitions are re-mounted (Linux)
## [0.6.2] - 2020-02-02
### Fixed
* better detection of read-only partitions (Linux)
## [0.6.0] - 2019-11-05
### Added
* commit changes in /usr/share/scripts/ if needed
## Previous changelog
* 0.5.0 : options et mode interactif pour l'exécution des actions, meilleure compatibilité POSIX
* 0.4.1 : Utilisation de "printf" à la place de "echo" pour mieux gérer les sauts de ligne
* 0.4.0 : Amélioration de la récupération d'information (plus de cas gérés). Infos Git avant la saisie.
* 0.3.0 : Écriture dans un fichier de log, amélioration de la récupération d'informations, amélioration de la syntaxe shell
* 0.2.7 : Correction d'un bug lors de l'utilisation de '&' dans le texte
* 0.2.6 : Precision du charset dans les entetes du mail
* 0.2.5 : Correction d'un bug avec le path de sendmail sous OpenBSD
* 0.2.4 : Correction d'un bug lors de l'utilisation de '/' dans le texte
* 0.2.3 : Correction d'un bug avec $REALM

View file

@ -1,30 +0,0 @@
# Evomaintenance
```.plain
$ evomaintenance --help
evomaintenance is a program that helps reporting what you've done on a server
Usage: evomaintenance
or evomaintenance --message="add new host"
or evomaintenance --no-api --no-mail --no-commit
or echo "add new vhost" | evomaintenance
Options
-m, --message=MESSAGE set the message from the command line
--mail enable the mail hook (default)
--no-mail disable the mail hook
--db enable the database hook
--no-db disable the database hook (default)
--api enable the API hook (default)
--no-api disable the API hook
--commit enable the commit hook (default)
--no-commit disable the commit hook
--evocheck enable evocheck execution (default)
--no-evocheck disable evocheck execution
--auto use "auto" mode
--no-auto use "manual" mode (default)
-v, --verbose increase verbosity
-n, --dry-run actions are not executed
--help print this message and exit
--version print version and exit
```

View file

@ -35,12 +35,15 @@
- name: Evomaintenance script and template are installed
ansible.builtin.copy:
src: "upstream/evomaintenance.sh"
dest: "/usr/share/scripts/"
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: root
group: root
mode: "0700"
mode: "{{ item.mode }}"
force: true
backup: yes
loop:
- { src: 'evomaintenance.sh', dest: '/usr/share/scripts/', mode: '0700' }
- { src: 'evomaintenance.tpl', dest: '/usr/share/scripts/', mode: '0600' }
tags:
- evomaintenance
- evomaintenance

View file

@ -2,15 +2,12 @@
# Juin - Decembre 2022 : #64088
# Purge pour Stretch et Buster
/usr/bin/ionice -c3 /usr/bin/sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 ".timeout 5000; DELETE FROM bans WHERE datetime('now', '-{{ fail2ban_dbpurgeage_default }}') > datetime(timeofban, 'unixepoch');"
/usr/bin/ionice -c3 /usr/bin/sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "DELETE FROM bans WHERE datetime('now', '-{{ fail2ban_dbpurgeage_default }}') > datetime(timeofban, 'unixepoch');"
place_dispo="$(df /var/lib/fail2ban/fail2ban.sqlite3 --output="avail" --block-size=1 | tail -n1)"
place_pris="$(stat --format %s /var/lib/fail2ban/fail2ban.sqlite3)"
place_dispo=$( df -h /var/lib/fail2ban/fail2ban.sqlite3 --output="avail" -h --block-size=1 |tail -n1 )
place_pris=$( echo $(("$(stat --format %s /var/lib/fail2ban/fail2ban.sqlite3 ) * 2" )) )
if [ "$place_pris" -lt "$place_dispo" ]
if [ $place_pris -lt $place_dispo ]
then
/usr/bin/ionice -c3 /usr/bin/sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "VACUUM;"
else
>&2 echo "Fail2ban SQLite VACUUM not done because /var lacks of space (VACUUM may use twice the database size)."
exit 1
fi

View file

@ -59,7 +59,7 @@ bantime = {{ fail2ban_wordpress_soft_bantime }}
enabled = {{ fail2ban_roundcube }}
port = http, https
filter = roundcube
logpath = /var/log/roundcube/errors
logpath = /var/lib/roundcube/logs/errors
maxretry = {{ fail2ban_roundcube_maxretry }}
findtime = {{ fail2ban_roundcube_findtime }}
bantime = {{ fail2ban_roundcube_bantime }}

View file

@ -35,6 +35,5 @@ haproxy_deny_ips: []
haproxy_backports_packages_stretch: haproxy libssl1.0.0
haproxy_backports_packages_buster: haproxy
haproxy_backports_packages_bullseye: haproxy
haproxy_backports_packages_bookworm: haproxy
haproxy_allow_ip_nonlocal_bind: Null
haproxy_allow_ip_nonlocal_bind: Null

View file

@ -21,6 +21,7 @@
- name: Self-signed certificate is present in HAProxy ssl directory
ansible.builtin.shell:
cmd: "cat /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/ssl/private/ssl-cert-snakeoil.key > /etc/haproxy/ssl/ssl-cert-snakeoil.pem"
args:
creates: /etc/haproxy/ssl/ssl-cert-snakeoil.pem
notify: reload haproxy
tags:

View file

@ -19,10 +19,6 @@
haproxy_backports_packages: "{{ haproxy_backports_packages_bullseye }}"
when: ansible_distribution_release == 'bullseye'
- ansible.builtin.set_fact:
haproxy_backports_packages: "{{ haproxy_backports_packages_bookworm }}"
when: ansible_distribution_release == 'bookworm'
- name: Prefer HAProxy package from backports
ansible.builtin.template:
src: haproxy_apt_preferences.j2

View file

@ -74,7 +74,7 @@
#######################################################################
- name: Fail if minifirewall_main_file is defined (modern mode)
- name: Fail if minifirewall_main_file is defined (legacy mode)
ansible.builtin.fail:
msg: "Variable minifirewall_main_file is deprecated and not configurable anymore."
when:
@ -179,4 +179,4 @@
- always
when:
- minifirewall_install_mode != 'legacy'
- minifirewall_restart_force | bool
- minifirewall_restart_force | bool

View file

@ -1,224 +0,0 @@
#!/bin/bash
# -*- sh -*-
: << =cut
=head1 NAME
ipmi_ - Plugin to monitor temperature, fan speed, watts or volts using IPMI
=head1 CONFIGURATION
=head2 ENVIRONMENT VARIABLES
This plugin does not use environment variables
=head2 WILDCARD PLUGIN
This plugin should be linked as ipmi_temp, ipmi_fans, ipmi_power or ipmi_volts,
and will show either temperatures, fan speeds, watts or volts based on its link
name.
=head1 NOTE
WARNING: Munin has a 10 second default timeout on plugins. On some
hosts ipmitool takes longer than that to probe all your hardware. In
this case this plugin us unusable.
=head1 AUTHOR
Nicolai Langfeldt <janl@linpro.no>
Modified by Mathieu Gauthier-Pilote <mgauthier@evolix.ca> from Evolix to return amp values as well (2024/04).
=head1 LICENSE
Donated to the public domain by Nicolai Langfeldt (janl@linpro.no)
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf suggest
=cut
#### Parse commandline to determine what the job is
CONFIG=no
case $1 in
autoconf)
type -p ipmitool &>/dev/null ||
{ echo 'no (missing ipmitool command)' && exit 0; }
ipmitool sensor &>/dev/null ||
{ echo 'no (unable to access IPMI device)' && exit 0; }
echo yes
exit 0
;;
suggest) echo fans
echo temp
echo power
echo volts
echo amp
exit 0;;
config) CONFIG=config;;
esac
case $0 in
*_temp) MEASURE=temp;;
*_fans) MEASURE=fans;;
*_power) MEASURE=power;;
*_volts) MEASURE=volts;;
*_amp) MEASURE=amp;;
*) echo "Please invoke as ipmi_temp, ipmi_fans, ipmi_power ipmi_volts or ipmi_amp" >&2
exit 1;;
esac
export CONFIG MEASURE
#### Work is done in this awk script
ipmitool sensor | gawk -F'|' '
BEGIN {
FANS = "";
TEMPS = "";
POWER = "";
VOLTS = "";
AMP = "";
CFANS = "graph_title Fan speeds based on IPMI\ngraph_vlabel RPM or %\ngraph_category Sensors\n";
CTEMPS = "graph_title Machine temperature based on IPMI\ngraph_vlabel Degrees celcius\ngraph_category Sensors\n";
CPOWER = "graph_title Power usage based on IPMI\ngraph_vlabel W\ngraph_category Sensors\n";
CVOLTS = "graph_title Volts based on IPMI\ngraph_vlabel V\ngraph_category Sensors\n";
CAMP = "graph_title Amps based on IPMI\ngraph_vlabel A\ngraph_category Sensors\n";
}
# Remove extraneous spaces to make output prettyer
{ gsub(/\t/," "); gsub(/ +/," "); gsub(/ +\|/,"|"); gsub(/\| +/,"|") }
# Skip lines with 0x0 in first column
/^[^|]+\|0x0\|/ { next; };
# Skip lines with na in first column
/^[^|]+\|na\|/ { next; };
# Parse temperatures
/degrees C/ {
NAME=THING=$1;
gsub(/[^A-Za-z0-9]/,"",NAME);
TEMP=$2;
# Find unique name
while (NAMES[NAME] >= 1) {
NAME=sprintf("%si",NAME);
}
NAMES[NAME]=1;
WARN=$8;
CRIT=$9;
TEMPS = sprintf("%s%s.value %s\n",TEMPS,NAME,TEMP);
CTEMPS = sprintf("%s%s.label %s\n",CTEMPS,NAME,THING);
if (CRIT !~ /na/) {
CTEMPS = sprintf("%s%s.critical 0:%s\n",CTEMPS,NAME,CRIT);
}
if (WARN !~ /na/) {
CTEMPS = sprintf("%s%s.warning 0:%s\n",CTEMPS,NAME,WARN);
}
}
/(RPM|^Fan.*percent)/ {
NAME=THING=$1;
gsub(/[^A-Za-z0-9]/,"",NAME);
SPEED=$2;
# Find unique name
while (NAMES[NAME] >= 1) {
NAME=sprintf("%si",NAME);
}
NAMES[NAME]=1;
FANS = sprintf("%s%s.value %s\n",FANS,NAME,SPEED);
CFANS = sprintf("%s%s.label %s\n",CFANS,NAME,THING);
OK=$4;
MIN=$6;
if (MIN !~ /na/) {
CFANS = sprintf("%s%s.warning %s:\n",CFANS,NAME,MIN);
}
}
/Watts/ {
NAME=THING=$1;
gsub(/[^A-Za-z0-9]/,"",NAME);
WATTS=$2;
# Find unique name
while (NAMES[NAME] >= 1) {
NAME=sprintf("%si",NAME);
}
NAMES[NAME]=1;
POWER = sprintf("%s%s.value %s\n",POWER,NAME,WATTS);
CPOWER = sprintf("%s%s.label %s\n",CPOWER,NAME,THING);
}
/Volts/ {
NAME=THING=$1
gsub(/[^A-Za-z0-9]/,"",NAME);
VOLTS_SENSOR=$2;
# Find unique name
while (NAMES[NAME] >= 1) {
NAME=sprintf("%si",NAME);
}
NAMES[NAME]=1;
VOLTS = sprintf("%s%s.value %s\n",VOLTS,NAME,VOLTS_SENSOR);
CVOLTS = sprintf("%s%s.label %s\n",CVOLTS,NAME,THING);
}
/Amps/ {
NAME=THING=$1
gsub(/[^A-Za-z0-9]/,"",NAME);
AMPS=$2;
# Find unique name
while (NAMES[NAME] >= 1) {
NAME=sprintf("%si",NAME);
}
NAMES[NAME]=1;
AMP = sprintf("%s%s.value %s\n",AMP,NAME,AMPS);
CAMP = sprintf("%s%s.label %s\n",CAMP,NAME,THING);
}
END {
if (ENVIRON["MEASURE"] == "temp") {
VALUE=TEMPS;
CONFIG=CTEMPS;
} else if (ENVIRON["MEASURE"] == "power") {
VALUE=POWER;
CONFIG=CPOWER;
} else if (ENVIRON["MEASURE"] == "volts") {
VALUE=VOLTS;
CONFIG=CVOLTS;
} else if (ENVIRON["MEASURE"] == "amp") {
VALUE=AMP;
CONFIG=CAMP;
} else {
VALUE=FANS;
CONFIG=CFANS;
}
if (ENVIRON["CONFIG"] == "config")
printf "%s",CONFIG;
else
printf "%s",VALUE;
}
'
# vim: syntax=sh ts=4 et

View file

@ -104,7 +104,7 @@ redis_cli_args=''
sentinel_port=$(awk '/^port/{print $2}' "${sentinel_config_file}")
! test -z "$sentinel_port" && redis_cli_args="${redis_cli_args} -p ${sentinel_port}"
sentinel_pass=$(awk '/^requirepass/{print $2}' "${sentinel_config_file}")
! test -z "$sentinel_pass" && export REDISCLI_AUTH="${sentinel_pass}"
! test -z "$sentinel_pass" && redis_cli_args="${redis_cli_args} --pass ${sentinel_pass}"
alias _redis-cli="redis-cli ${redis_cli_args}"
# List all masters names known by sentinel

View file

@ -15,16 +15,10 @@
dest: /usr/local/bin/check-local
mode: "0755"
- name: Package bash-completion is installed
- name: Package bash-completion is intalled
ansible.builtin.apt:
name: bash-completion
- name: Directory /etc/bash_completion.d exists
ansible.builtin.file:
path: '/etc/bash_completion.d'
state: directory
mode: '0644'
- name: Completion for utilitary check-local is installed
ansible.builtin.copy:
src: check-local_completion

View file

@ -9,7 +9,7 @@ allowed_hosts={{ nagios_nrpe_allowed_hosts | join(',') }}
# System checks
command[check_load]=/usr/lib/nagios/plugins/check_load --percpu --warning=0.7,0.6,0.5 --critical=0.9,0.8,0.7
command[check_swap]=/usr/lib/nagios/plugins/check_swap -a -w 30% -c 20%
command[check_disk1]=/usr/lib/nagios/plugins/check_disk -e -w 10% -c 3% -W 10% -K 3% -C -w 5% -c 2% -W 5% -K 2% -p /home -x /lib/init/rw -x /dev -x /dev/shm -x /run -I '^/run/' -I '^/sys/' -X overlay
command[check_disk1]=/usr/lib/nagios/plugins/check_disk -e -w 10% -c 3% -W 10% -K 3% -C -w 5% -c 2% -W 5% -K 2% -p /home -x /lib/init/rw -x /dev -x /dev/shm -x /run -I '^/run/' -I '^/sys/'
command[check_zombie_procs]=sudo /usr/lib/nagios/plugins/check_procs -w 5 -c 10 -s Z
command[check_total_procs]=sudo /usr/lib/nagios/plugins/check_procs -w 400 -c 600
command[check_users]=/usr/lib/nagios/plugins/check_users -w 5 -c 10

View file

@ -1,29 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
Comment: GPGTools - https://gpgtools.org
mQENBFdDN1ABCADaNd/I3j3tn40deQNgz7hB2NvT+syXe6k4ZmdiEcOfBvFrkS8B
hNS67t93etHsxEy7E0qwsZH32bKazMqe9zDwoa3aVImryjh6SHC9lMtW27JPHFeM
Srkt9YmH1WMwWcRO6eSY9B3PpazquhnvbammLuUojXRIxkDroy6Fw4UKmUNSRr32
9Ej87jRoR1B2/57Kfp2Y4+vFGGzSvh3AFQpBHq51qsNHALU6+8PjLfIt+5TPvaWR
TB+kAZnQZkaIQM2nr1n3oj6ak2RATY/+kjLizgFWzgEfbCrbsyq68UoY5FPBnu4Z
E3iDZpaIqwKr0seUC7iA1xM5eHi5kty1oB7HABEBAAG0Ik5Tb2xpZCA8bnNvbGlk
LWdwZ0Bub2Rlc291cmNlLmNvbT6JATgEEwECACIFAldDN1ACGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEC9ZtfmbG+C0y7wH/i4xnab36dtrYW7RZwL8i6Sc
NjMx4j9+U1kr/F6YtqWd+JwCbBdar5zRghxPcYEq/qf7MbgAYcs1eSOuTOb7n7+o
xUwdH2iCtHhKh3Jr2mRw1ks7BbFZPB5KmkxHaEBfLT4d+I91ZuUdPXJ+0SXs9gzk
Dbz65Uhoz3W03aiF8HeL5JNARZFMbHHNVL05U1sTGTCOtu+1c/33f3TulQ/XZ3Y4
hwGCpLe0Tv7g7Lp3iLMZMWYPEa0a7S4u8he5IEJQLd8bE8jltcQvrdr3Fm8kI2Jg
BJmUmX4PSfhuTCFaR/yeCt3UoW883bs9LfbTzIx9DJGpRIu8Y0IL3b4sj/GoZVq5
AQ0EV0M3UAEIAKrTaC62ayzqOIPa7nS90BHHck4Z33a2tZF/uof38xNOiyWGhT8u
JeFoTTHn5SQq5Ftyu4K3K2fbbpuu/APQF05AaljzVkDGNMW4pSkgOasdysj831cu
ssrHX2RYS22wg80k6C/Hwmh5F45faEuNxsV+bPx7oPUrt5n6GMx84vEP3i1+FDBi
0pt/B/QnDFBXki1BGvJ35f5NwDefK8VaInxXP3ZN/WIbtn5dqxppkV/YkO7GiJlp
Jlju9rf3kKUIQzKQWxFsbCAPIHoWv7rH9RSxgDithXtG6Yg5R1aeBbJaPNXL9wpJ
YBJbiMjkAFaz4B95FOqZm3r7oHugiCGsHX0AEQEAAYkBHwQYAQIACQUCV0M3UAIb
DAAKCRAvWbX5mxvgtE/OB/0VN88DR3Y3fuqy7lq/dthkn7Dqm9YXdorZl3L152eE
IF882aG8FE3qZdaLGjQO4oShAyNWmRfSGuoH0XERXAI9n0r8m4mDMxE6rtP7tHet
y/5M8x3CTyuMgx5GLDaEUvBusnTD+/v/fBMwRK/cZ9du5PSG4R50rtst+oYyC2ao
x4I2SgjtF/cY7bECsZDplzatN3gv34PkcdIg8SLHAVlL4N5tzumDeizRspcSyoy2
K2+hwKU4C4+dekLLTg8rjnRROvplV2KtaEk6rxKtIRFDCoQng8wfJuIMrDNKvqZw
FRGt7cbvW5MCnuH8MhItOl9Uxp1wHp6gtav/h8Gp6MBa
=MARt
mQINBFObJLYBEADkFW8HMjsoYRJQ4nCYC/6Eh0yLWHWfCh+/9ZSIj4w/pOe2V6V+
W6DHY3kK3a+2bxrax9EqKe7uxkSKf95gfns+I9+R+RJfRpb1qvljURr54y35IZgs
fMG22Np+TmM2RLgdFCZa18h0+RbH9i0b+ZrB9XPZmLb/h9ou7SowGqQ3wwOtT3Vy
qmif0A2GCcjFTqWW6TXaY8eZJ9BCEqW3k/0Cjw7K/mSy/utxYiUIvZNKgaG/P8U7
89QyvxeRxAf93YFAVzMXhoKxu12IuH4VnSwAfb8gQyxKRyiGOUwk0YoBPpqRnMmD
Dl7SdmY3oQHEJzBelTMjTM8AjbB9mWoPBX5G8t4u47/FZ6PgdfmRg9hsKXhkLJc7
C1btblOHNgDx19fzASWX+xOjZiKpP6MkEEzq1bilUFul6RDtxkTWsTa5TGixgCB/
G2fK8I9JL/yQhDc6OGY9mjPOxMb5PgUlT8ox3v8wt25erWj9z30QoEBwfSg4tzLc
Jq6N/iepQemNfo6Is+TG+JzI6vhXjlsBm/Xmz0ZiFPPObAH/vGCY5I6886vXQ7ft
qWHYHT8jz/R4tigMGC+tvZ/kcmYBsLCCI5uSEP6JJRQQhHrCvOX0UaytItfsQfLm
EYRd2F72o1yGh3yvWWfDIBXRmaBuIGXGpajC0JyBGSOWb9UxMNZY/2LJEwARAQAB
tB9Ob2RlU291cmNlIDxncGdAbm9kZXNvdXJjZS5jb20+iQI4BBMBAgAiBQJTmyS2
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAWVaCraFdigHTmD/9OKhUy
jJ+h8gMRg6ri5EQxOExccSRU0i7UHktecSs0DVC4lZG9AOzBe+Q36cym5Z1di6JQ
kHl69q3zBdV3KTW+H1pdmnZlebYGz8paG9iQ/wS9gpnSeEyx0Enyi167Bzm0O4A1
GK0prkLnz/yROHHEfHjsTgMvFwAnf9uaxwWgE1d1RitIWgJpAnp1DZ5O0uVlsPPm
XAhuBJ32mU8S5BezPTuJJICwBlLYECGb1Y65Cil4OALU7T7sbUqfLCuaRKxuPtcU
VnJ6/qiyPygvKZWhV6Od0Yxlyed1kftMJyYoL8kPHfeHJ+vIyt0s7cropfiwXoka
1iJB5nKyt/eqMnPQ9aRpqkm9ABS/r7AauMA/9RALudQRHBdWIzfIg0Mlqb52yyTI
IgQJHNGNX1T3z1XgZhI+Vi8SLFFSh8x9FeUZC6YJu0VXXj5iz+eZmk/nYjUt4Mtc
pVsVYIB7oIDIbImODm8ggsgrIzqxOzQVP1zsCGek5U6QFc9GYrQ+Wv3/fG8hfkDn
xXLww0OGaEQxfodm8cLFZ5b8JaG3+Yxfe7JkNclwvRimvlAjqIiW5OK0vvfHco+Y
gANhQrlMnTx//IdZssaxvYytSHpPZTYw+qPEjbBJOLpoLrz8ZafN1uekpAqQjffI
AOqW9SdIzq/kSHgl0bzWbPJPw86XzzftewjKNbkCDQRTmyS2ARAAxSSdQi+WpPQZ
fOflkx9sYJa0cWzLl2w++FQnZ1Pn5F09D/kPMNh4qOsyvXWlekaV/SseDZtVziHJ
Km6V8TBG3flmFlC3DWQfNNFwn5+pWSB8WHG4bTA5RyYEEYfpbekMtdoWW/Ro8Kmh
41nuxZDSuBJhDeFIp0ccnN2Lp1o6XfIeDYPegyEPSSZqrudfqLrSZhStDlJgXjea
JjW6UP6txPtYaaila9/Hn6vF87AQ5bR2dEWB/xRJzgNwRiax7KSU0xca6xAuf+TD
xCjZ5pp2JwdCjquXLTmUnbIZ9LGV54UZ/MeiG8yVu6pxbiGnXo4Ekbk6xgi1ewLi
vGmz4QRfVklV0dba3Zj0fRozfZ22qUHxCfDM7ad0eBXMFmHiN8hg3IUHTO+UdlX/
aH3gADFAvSVDv0v8t6dGc6XE9Dr7mGEFnQMHO4zhM1HaS2Nh0TiL2tFLttLbfG5o
QlxCfXX9/nasj3K9qnlEg9G3+4T7lpdPmZRRe1O8cHCI5imVg6cLIiBLPO16e0fK
yHIgYswLdrJFfaHNYM/SWJxHpX795zn+iCwyvZSlLfH9mlegOeVmj9cyhN/VOmS3
QRhlYXoA2z7WZTNoC6iAIlyIpMTcZr+ntaGVtFOLS6fwdBqDXjmSQu66mDKwU5Ek
fNlbyrpzZMyFCDWEYo4AIR/18aGZBYUAEQEAAYkCHwQYAQIACQUCU5sktgIbDAAK
CRAWVaCraFdigIPQEACcYh8rR19wMZZ/hgYv5so6Y1HcJNARuzmffQKozS/rxqec
0xM3wceL1AIMuGhlXFeGd0wRv/RVzeZjnTGwhN1DnCDy1I66hUTgehONsfVanuP1
PZKoL38EAxsMzdYgkYH6T9a4wJH/IPt+uuFTFFy3o8TKMvKaJk98+Jsp2X/QuNxh
qpcIGaVbtQ1bn7m+k5Qe/fz+bFuUeXPivafLLlGc6KbdgMvSW9EVMO7yBy/2JE15
ZJgl7lXKLQ31VQPAHT3an5IV2C/ie12eEqZWlnCiHV/wT+zhOkSpWdrheWfBT+ac
hR4jDH80AS3F8jo3byQATJb3RoCYUCVc3u1ouhNZa5yLgYZ/iZkpk5gKjxHPudFb
DdWjbGflN9k17VCf4Z9yAb9QMqHzHwIGXrb7ryFcuROMCLLVUp07PrTrRxnO9A/4
xxECi0l/BzNxeU1gK88hEaNjIfviPR/h6Gq6KOcNKZ8rVFdwFpjbvwHMQBWhrqfu
G3KaePvbnObKHXpfIKoAM7X2qfO+IFnLGTPyhFTcrl6vZBTMZTfZiC1XDQLuGUnd
sckuXINIU3DFWzZGr0QrqkuE/jyr7FXeUJj9B7cLo+s/TXo+RaVfi3kOc9BoxIvy
/qiNGs/TKy2/Ujqp/affmIMoMXSozKmga81JSwkADO1JMgUy6dApXz9kP4EE3g==
=CLGF
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -24,7 +24,7 @@
- name: Add NodeJS repository (Debian <12)
ansible.builtin.apt_repository:
repo: "deb [signed-by={{ apt_keyring_dir }}/nodesource.asc] https://deb.nodesource.com/{{ nodejs_apt_version }} nodistro main"
repo: "deb [signed-by={{ apt_keyring_dir }}/nodesource.asc] https://deb.nodesource.com/{{ nodejs_apt_version }} {{ ansible_distribution_release }} main"
filename: nodesource
update_cache: yes
state: present

View file

@ -2,7 +2,7 @@
Types: deb
URIs: https://deb.nodesource.com/{{ nodejs_apt_version }}
Suites: nodistro
Suites: {{ ansible_distribution_release }}
Components: main
Signed-by: {{ apt_keyring_dir }}/nodesource.asc
Enabled: yes
Enabled: yes

View file

@ -1,21 +1,9 @@
---
# openbsd_pkg is broken since OpenBSD 7.4 with the version of Ansible we currently use
#- name: Install OpenVPN
# community.general.openbsd_pkg:
# name: openvpn--
- name: Check if OpenVPN is already installed
ansible.builtin.command:
cmd: pkg_info -Iq inst:openvpn
register: is_installed
ignore_errors: true
changed_when: false
- name: Install OpenVPN
ansible.builtin.command:
cmd: pkg_add openvpn--
when: "'Can\\'t find inst:' in is_installed.stderr"
community.general.openbsd_pkg:
name: openvpn--
when: ansible_distribution == 'OpenBSD'
- name: Create /etc/openvpn
ansible.builtin.file:
@ -128,25 +116,10 @@
check_mode: no
register: nrpe_evolix_config
# openbsd_pkg is broken since OpenBSD 7.4 with the version of Ansible we currently use
#- name: Install NRPE check dependency
# community.general.openbsd_pkg:
# name: p5-Net-Telnet
# when: nrpe_evolix_config.stat.exists
- name: Check if NRPE check dependency is already installed
ansible.builtin.command:
cmd: pkg_info -Iq inst:p5-Net-Telnet
register: is_installed
ignore_errors: true
changed_when: false
- name: Install NRPE check dependency
ansible.builtin.command:
cmd: pkg_add p5-Net-Telnet
when:
- "'Can\\'t find inst:' in is_installed.stderr"
- nrpe_evolix_config.stat.exists
- name: Install NRPE check dependencies
community.general.openbsd_pkg:
name: p5-Net-Telnet
when: nrpe_evolix_config.stat.exists
- name: Install OpenVPN NRPE check
ansible.builtin.copy:

View file

@ -134,7 +134,7 @@ localhost:10026 inet n - y - 10 smtpd
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
smtp-amavis unix - - y - 3 lmtp
smtp-amavis unix - - y - 2 lmtp
-o lmtp_data_done_timeout=1200
-o lmtp_send_xforward_command=yes

View file

@ -36,6 +36,7 @@
ansible.builtin.template:
src: postgresql.sources.j2
dest: /etc/apt/sources.list.d/postgresql.sources
state: present
register: postgresql_sources
when: ansible_distribution_major_version is version('12', '>=')

View file

@ -43,19 +43,3 @@ For generate the sha512 version of yours password :
~~~
printf "test" | mkpasswd --stdin --method=sha-512
~~~
## Add whitelist ip for accounts
If you want add an filtering by ip for accounts, you have to enabled variable `proftpd_sftp_enable_user_whitelist` and add variable `proftpd_sftp_ips_whitelist` and a group by accounts.
Example :
~~~
proftpd_sftp_enable_user_whitelist : True
proftpd_sftp_ips_whitelist:
foo: ['127.0.0.1', '192.168.0.1']
proftpd_accounts:
- { name: 'ftp3', home: '/home/ftp3/', uid: 116, gid: 65534, group: 'foo', password: '$6$/Yy0b0No3GWh$3ZY1GZFI25eyQDBrANyHw.NFPqPqdg6sCi89nM/aNitmESZ2jGfROveS5xowy.WjX9tMC7.KPoabKPyxOpBJY0' }
~~~

View file

@ -16,4 +16,3 @@ proftpd_sftp_use_publickeys: True
proftpd_sftp_port: 22222
proftpd_accounts: []
proftpd_accounts_final: []
proftpd_sftp_enable_user_whitelist : False

View file

@ -61,7 +61,7 @@
tags:
- proftpd
- name: IP Whitelists for SFTP users are present
- name: Whitelist ip for users (SFTP)
ansible.builtin.blockinfile:
dest: /etc/proftpd/conf.d/sftp.conf
marker: "# {mark} ANSIBLE MANAGED BLOCK - Whitelist ip for users"
@ -82,14 +82,6 @@
notify: restart proftpd
when: proftpd_sftp_enable_user_whitelist | bool
- name: IP Whitelists for SFTP users are absent
ansible.builtin.blockinfile:
dest: /etc/proftpd/conf.d/sftp.conf
marker: "# {mark} ANSIBLE MANAGED BLOCK - Whitelist ip for users"
state: absent
notify: restart proftpd
when: not (proftpd_sftp_enable_user_whitelist | bool)
- name: Allow keys for SFTP account
ansible.builtin.template:
dest: "/etc/proftpd/sftp.authorized_keys/{{ _proftpd_account.name }}"

View file

@ -9,12 +9,9 @@ vrrp_addresses: []
# priority: Null # the priority of this host in the virtual server (default: 100)
# authentication: Null # authentification type: auth=(none|pw/hexkey|ah/hexkey) hexkey=0x[0-9a-fA-F]+
# label: Null # use this name is syslog messages (helps when several vrid are running)
# ip: Null # the IP address(es) (and optionnaly subnet mask) of the virtual server
# peers: [IP1, IP2] # list of peers (IP), for minifirewall rules
# ip: Null # the ip address(es) (and optionnaly subnet mask) of the virtual server
# state: Null # 'started' or 'stopped'
# }
vrrp_manage_minifirewall: true
minifirewall_restart_if_needed: True
minifirewall_restart_force: False

View file

@ -1,60 +1,5 @@
---
# Configure and restart minifirewall before starting the VRRP service
- name: Check if a recent minifirewall is present
ansible.builtin.stat:
path: /etc/minifirewall.d/
register: _minifirewall_dir
- ansible.builtin.set_fact:
minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}"
- name: VRRP output is authorized in minifirewall
ansible.builtin.blockinfile:
path: /etc/minifirewall.d/vrrpd
marker: "## {mark} ANSIBLE MANAGED OUTPUT RULES FOR VRID {{ vrrp_address.id }}"
block: |
/sbin/iptables -A OUTPUT -o {{ vrrp_address.interface }} -p 112 -j ACCEPT # Allow VRRP output on {{ vrrp_address.interface }}
create: yes
mode: "0600"
owner: "root"
group: "root"
notify: "{{ minifirewall_restart_handler_name }}"
when:
- vrrp_manage_minifirewall | bool
- _minifirewall_dir.stat.exists
- name: VRRP input is authorized in minifirewall
ansible.builtin.blockinfile:
path: /etc/minifirewall.d/vrrpd
marker: "## {mark} ANSIBLE MANAGED INPUT RULES FOR VRID {{ vrrp_address.id }}"
block: |
{% if vrrp_address.peers | default([]) | length <= 0 %}
/sbin/iptables -A INPUT -i {{ vrrp_address.interface }} -d 224.0.0.0/8 -j ACCEPT # Allow VRRP input on {{ vrrp_address.interface }} for VRID {{ vrrp_address.id }}
{% else %}
{% for peer in vrrp_address.peers %}
/sbin/iptables -A INPUT -i {{ vrrp_address.interface }} -s {{ peer }} -d 224.0.0.0/8 -j ACCEPT # Allow VRRP input on {{ vrrp_address.interface }} from {{ peer }} for VRID {{ vrrp_address.id }}
{% endfor %}
{% endif %}
create: yes
mode: "0600"
owner: "root"
group: "root"
notify: "{{ minifirewall_restart_handler_name }}"
when:
- vrrp_manage_minifirewall | bool
- _minifirewall_dir.stat.exists
- name: Flush handlers to restart minifirewall
ansible.builtin.meta: flush_handlers
when:
- vrrp_manage_minifirewall | bool
- _minifirewall_dir.stat.exists
# Configure VRRP service
- name: set unit name
ansible.builtin.set_fact:
vrrp_systemd_unit_name: "vrrp-{{ vrrp_address.id }}.service"
@ -75,3 +20,38 @@
when:
- vrrp_systemd_unit is changed
- not ansible_check_mode
- name: Check if a recent minifirewall is present
ansible.builtin.stat:
path: /etc/minifirewall.d/
register: _minifirewall_dir
- ansible.builtin.set_fact:
minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}"
- name: VRRP output is authorized in minifirewall
lineinfile:
path: /etc/minifirewall.d/vrrpd
line: "/sbin/iptables -A OUTPUT -o {{ vrrp_address.interface }} -p 112 -j ACCEPT # Allow VRRP output on {{ vrrp_address.interface }}"
regexp: "# Allow VRRP output on {{ vrrp_address.interface }}$"
create: yes
mode: "0600"
owner: "root"
group: "root"
notify: "{{ minifirewall_restart_handler_name }}"
when: _minifirewall_dir.stat.exists
- name: VRRP input is authorized in minifirewall
lineinfile:
path: /etc/minifirewall.d/vrrpd
line: "/sbin/iptables -A INPUT -i {{ vrrp_address.interface }} -s {{ peer }} -d 224.0.0.0/8 -j ACCEPT # Allow VRRP input on {{ vrrp_address.interface }} from {{ peer }} for VRID {{ vrrp_address.id }}"
regexp: "# Allow VRRP input on {{ vrrp_address.interface }} from {{ peer }} for VRID {{ vrrp_address.id }}"
create: yes
mode: "0600"
owner: "root"
group: "root"
loop: "{{ vrrp_address.peers | default([]) }}"
loop_control:
loop_var: peer
notify: "{{ minifirewall_restart_handler_name }}"
when: _minifirewall_dir.stat.exists

View file

@ -10,9 +10,3 @@
remote_src: False
src: ftp/evolinux.conf.diff
dest: /etc/proftpd/conf.d/z-evolinux.conf
- name: Remove .orig file created by previous patch task
ansible.builtin.file:
path: /etc/proftpd/conf.d/z-evolinux.conf.orig
state: absent

View file

@ -20,8 +20,8 @@
# LOG
CustomLog /var/log/apache2/access.log vhost_combined
CustomLog /var/log/roundcube/access.log combined
ErrorLog /var/log/roundcube/error.log
CustomLog /var/lib/roundcube/logs/access.log combined
ErrorLog /var/lib/roundcube/logs/error.log
# REWRITE
UseCanonicalName On
@ -40,7 +40,7 @@
#php_admin_value upload_max_filesize 8M
#php_admin_flag allow_url_fopen Off
php_admin_value sendmail_path "/usr/sbin/sendmail -t -i -f www-roundcube"
php_admin_value error_log "/var/log/roundcube/php.log"
php_admin_value error_log "/home/roundcube/log/php.log"
#php_admin_value open_basedir "/usr/share/php:/home/roundcube:/tmp"
</VirtualHost>