diff --git a/CHANGELOG.md b/CHANGELOG.md index 930d2696..871c7b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,52 +4,105 @@ 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 aligned with the stable version of Debian. -The **minor** part changes with big changes (probably incompatible). -The **patch** part changes incrementally at each release. +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 -* Preliminary support for Debian 11 « Bullseye » -* apache: new variable for mpm mode (+ updated default config accordingly) +### Changed + +### Fixed + +### Removed + +### Security + +## [22.01] 2022-01-25 + +### Added + +* Support for Debian 11 « Bullseye » (with possible remaining blind spots) +* apache: new variable for MPM mode (+ updated default config accordingly) +* apache: prevent accessing Git or "env" related files * certbot: add script for manual deploy hooks execution +* docker-host: install additional dependencies +* dovecot: switch to TLS 1.2+ and external DH params +* etc-git: centralize cron jobs in dedicated crontab +* etc-git: manage commits with an optimized shell script instead of many slow Ansible tasks +* evolinux-base: add script backup-server-state +* evolinux-base: install molly-guard by default +* generate-ldif: detect RAID controller +* generate-ldif: detect mdadm * listupgrade: crontab is configurable +* logstash: logging to syslog is configurable (default: True) * mongodb: create munin plugins directory if missing +* munin: systemd override to unprotect home directory +* mysql: add evomariabackup 21.11 +* mysql: improve Bullseye compatibility * mysql: script "mysql_connections" to display a compact list of connections +* mysql: script "mysql-queries-killer.sh" to kill MySQL queries +* nagios-nrpe + evolinux-users: new check for ipmi +* nagios-nrpe + evolinux-users: new check for RAID (soft + hard) +* nagios-nrpe + evolinux-users: new checks for bkctld +* nagios-nrpe: new check influxdb +* openvpn: new role (beta) * redis: instance service for Debian 11 +* squid: add *.o.lencr.org to default whitelist ### Changed -* Use python3 modules for Debian 11 and later +* Change version pattern +* Install python 2 or 3 libraries according to running python version * Remove embedded GPG keys only if legacy keyring is present * apt: remove workaround for Evolix public repositories with Debian 11 * apt: use the new security repository for Bullseye * certbot: silence letsencrypt deprecation warnings -* elasticsearch: 7.x by default +* elasticsearch: elastic_stack_version = 7.x +* evoacme: exclude renewal-hooks directory from cron * evoadmin-web: simpler PHP packages lists -* evocheck: upstream release 21.07 +* evocheck: upstream release 21.10.4 * evolinux-base: alert5 comes after the network * evolinux-base: force Debian version to buster for Evolix repository (temporary) -* kibana: 7.x by default +* evolinux-base: install freeipmi by default on dedicated hw +* evolinux-base: logs are rotated with dateext by default +* evolinux-base: split dpkg logrotate configuration +* evolinux-users + nagios-nrpe: Add support for php-fpm80 in lxc +* evomaintenance: extract a config.yml tasks file +* evomaintenance: upstream release 22.01 +* filebeat/metricbeat: elastic_stack_version = 7.x +* kibana: elastic_stack_version = 7.x +* listupgrade: old-kernel-removal version 21.10 * listupgrade: upstream release 21.06.3 -* mysql: mariadb-client-10.5 on Debian 11 -* mysql: use python3 with Debian 11 and later +* logstash: elastic_stack_version = 7.x +* mongodb: Allow to specify a mongodb version for buster & bullseye +* mongodb: Deny the install on Debian 11 « Bullseye » when the version is unsupported +* mongodb: Support version 5.0 (for buster) +* mysql: use python3 and mariadb-client-10.5 with Debian 11 and later +* nodejs: default to version 16 LTS +* php: enforce Debian version with assert instead of fail * squid: improve default whitelist (more specific patterns) * squid: must be started in foreground mode for systemd * squid: remove obsolete variable on Squid 4 ### Fixed +* evolinux-base: fix alert5.service dependency syntax * certbot: sync_remote excludes itself +* lxc-php: fix config for opensmtpd on bullseye containers +* mysql : Create a default ~root/.my.cnf for compatibility reasons +* nginx : fix variable name and debug to actually use nginx-light +* packweb-apache : Support php 8.0 +* nagios-nrpe: Fix check_nfsserver for buster and bullseye ### Removed +* evocheck: package install is not supported anymore +* logstash: no more dependency on Java * php: remove php-gettext for 7.4 -### Security - ## [10.6.0] 2021-06-28 ### Added diff --git a/apache/files/evolinux-defaults.conf b/apache/files/evolinux-defaults.conf index 06e28d9e..65c8c921 100644 --- a/apache/files/evolinux-defaults.conf +++ b/apache/files/evolinux-defaults.conf @@ -48,12 +48,23 @@ MaxKeepAliveRequests 10 Deny from env=GoAway + + # We don't want to let the client know a file exist on the server, + # so we return 404 "Not found" instead of 403 "Forbidden". + Redirect 404 + - - Require all denied - +# File names starting with + + Redirect 404 + +# File names ending with + + Redirect 404 + Require all denied + diff --git a/certbot/files/hooks/deploy/sync_remote.sh b/certbot/files/hooks/deploy/sync_remote.sh index d1721fdb..7fc3ecf4 100644 --- a/certbot/files/hooks/deploy/sync_remote.sh +++ b/certbot/files/hooks/deploy/sync_remote.sh @@ -14,8 +14,15 @@ debug() { found_renewed_lineage() { test -f "${RENEWED_LINEAGE}/fullchain.pem" && test -f "${RENEWED_LINEAGE}/privkey.pem" } +cert_content() { + openssl x509 -text -in "${RENEWED_LINEAGE}/fullchain.pem" +} domain_from_cert() { - openssl x509 -noout -subject -in "${RENEWED_LINEAGE}/fullchain.pem" | sed 's/^.*CN\ *=\ *//' + if cert_content | grep -q "X509v3 Subject Alternative Name:" && cert_content | grep -q "DNS:"; then + cert_content | grep "DNS:" | sed -e 's/\s\+//g' -e 's/DNS://g' + else + cert_content | sed 's/^.*CN\ *=\ *//' + fi } main() { if [ -z "${RENEWED_LINEAGE}" ]; then @@ -44,7 +51,7 @@ main() { || error "Couldn't sync hooks on ${server}" # shellcheck disable=SC2029 - ssh "${remote_host}" "export RENEWED_LINEAGE=\"${remote_lineage}/\" RENEWED_DOMAINS=${RENEWED_DOMAINS}; find ${remote_dir}/hooks/ -mindepth 1 -maxdepth 1 -type f -executable -exec {} \;" \ + ssh "${remote_host}" "export RENEWED_LINEAGE=\"${remote_lineage}/\" RENEWED_DOMAINS=\"${RENEWED_DOMAINS}\"; find ${remote_dir}/hooks/ -mindepth 1 -maxdepth 1 -type f -executable -exec {} \;" \ || error "Something went wrong on ${server} for deploy hooks" done else diff --git a/certbot/tasks/install-legacy.yml b/certbot/tasks/install-legacy.yml index d9dfb382..446e557a 100644 --- a/certbot/tasks/install-legacy.yml +++ b/certbot/tasks/install-legacy.yml @@ -56,5 +56,5 @@ dest: "/etc/letsencrypt/cli.ini" section: null option: "no-self-upgrade" - value: 0 + value: "no" state: present diff --git a/certbot/tasks/main.yml b/certbot/tasks/main.yml index 9259e027..cede35a6 100644 --- a/certbot/tasks/main.yml +++ b/certbot/tasks/main.yml @@ -7,17 +7,17 @@ - ansible_distribution_major_version is version('8', '>=') msg: only compatible with Debian 9+ -- name: Install legacy script on Debian 8 and 9 +- name: Install legacy script on Debian 8 include: install-legacy.yml when: - ansible_distribution == "Debian" - - ansible_distribution_major_version is version('10', '<') + - ansible_distribution_major_version is version('9', '<') -- name: Install package on Debian 10+ +- name: Install package on Debian 9+ include: install-package.yml when: - ansible_distribution == "Debian" - - ansible_distribution_major_version is version('10', '>=') + - ansible_distribution_major_version is version('9', '>=') - include: acme-challenge.yml diff --git a/clamav/tasks/main.yml b/clamav/tasks/main.yml index be9e5b00..6d1da3eb 100644 --- a/clamav/tasks/main.yml +++ b/clamav/tasks/main.yml @@ -6,48 +6,48 @@ value: "{{ item.value }}" vtype: "{{ item.type }}" loop: - - { key: 'clamav-daemon/debconf', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/MaxHTMLNormalize', type: 'string', value: '10M' } - - { key: 'clamav-daemon/StatsPEDisabled', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/FollowDirectorySymlinks', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/StreamMaxLength', type: 'string', value: '25' } - - { key: 'clamav-daemon/ReadTimeout', type: 'string', value: '180' } - - { key: 'clamav-daemon/StatsEnabled', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/MaxConnectionQueueLength', type: 'string', value: '15' } - - { key: 'clamav-daemon/LogRotate', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/AllowAllMatchScan', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/ScanOnAccess', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/LogFile', type: 'string', value: '/var/log/clamav/clamav.log' } - - { key: 'clamav-daemon/ScanMail', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/BytecodeTimeout', type: 'string', value: '60000' } - - { key: 'clamav-daemon/LogTime', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/OnAccessMaxFileSize', type: 'string', value: '5M' } - - { key: 'clamav-daemon/TcpOrLocal', type: 'select', value: 'UNIX' } - - { key: 'clamav-daemon/MaxEmbeddedPE', type: 'string', value: '10M' } - - { key: 'clamav-daemon/FixStaleSocket', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/User', type: 'string', value: 'clamav' } - - { key: 'clamav-daemon/BytecodeSecurity', type: 'select', value: 'TrustSigned' } - - { key: 'clamav-daemon/ScanSWF', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/MaxDirectoryRecursion', type: 'string', value: '0' } - - { key: 'clamav-daemon/MaxThreads', type: 'string', value: '12' } - - { key: 'clamav-daemon/LocalSocketGroup', type: 'string', value: 'clamav' } - - { key: 'clamav-daemon/MaxScriptNormalize', type: 'string', value: '5M' } - - { key: 'clamav-daemon/ForceToDisk', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/StatsHostID', type: 'string', value: 'auto' } - - { key: 'clamav-daemon/FollowFileSymlinks', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/TCPSocket', type: 'string', value: '3310' } - - { key: 'clamav-daemon/TCPAddr', type: 'string', value: 'any' } - - { key: 'clamav-daemon/DisableCertCheck', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/SelfCheck', type: 'string', value: '3600' } - - { key: 'clamav-daemon/LocalSocket', type: 'string', value: '/var/run/clamav/clamd.ctl' } - - { key: 'clamav-daemon/LocalSocketMode', type: 'string', value: '666' } - - { key: 'clamav-daemon/StatsTimeout', type: 'string', value: '10' } - - { key: 'clamav-daemon/MaxZipTypeRcg', type: 'string', value: '1M' } - - { key: 'clamav-daemon/MaxHTMLNoTags', type: 'string', value: '2M' } - - { key: 'clamav-daemon/LogSyslog', type: 'boolean', value: 'false' } - - { key: 'clamav-daemon/AddGroups', type: 'string', value: '' } - - { key: 'clamav-daemon/Bytecode', type: 'boolean', value: 'true' } - - { key: 'clamav-daemon/ScanArchive', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/debconf', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/MaxHTMLNormalize', type: 'string', value: '10M' } + - { key: 'clamav-daemon/StatsPEDisabled', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/FollowDirectorySymlinks', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/StreamMaxLength', type: 'string', value: '25' } + - { key: 'clamav-daemon/ReadTimeout', type: 'string', value: '180' } + - { key: 'clamav-daemon/StatsEnabled', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/MaxConnectionQueueLength', type: 'string', value: '15' } + - { key: 'clamav-daemon/LogRotate', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/AllowAllMatchScan', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/ScanOnAccess', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/LogFile', type: 'string', value: '/var/log/clamav/clamav.log' } + - { key: 'clamav-daemon/ScanMail', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/BytecodeTimeout', type: 'string', value: '60000' } + - { key: 'clamav-daemon/LogTime', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/OnAccessMaxFileSize', type: 'string', value: '5M' } + - { key: 'clamav-daemon/TcpOrLocal', type: 'select', value: 'UNIX' } + - { key: 'clamav-daemon/MaxEmbeddedPE', type: 'string', value: '10M' } + - { key: 'clamav-daemon/FixStaleSocket', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/User', type: 'string', value: 'clamav' } + - { key: 'clamav-daemon/BytecodeSecurity', type: 'select', value: 'TrustSigned' } + - { key: 'clamav-daemon/ScanSWF', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/MaxDirectoryRecursion', type: 'string', value: '0' } + - { key: 'clamav-daemon/MaxThreads', type: 'string', value: '12' } + - { key: 'clamav-daemon/LocalSocketGroup', type: 'string', value: 'clamav' } + - { key: 'clamav-daemon/MaxScriptNormalize', type: 'string', value: '5M' } + - { key: 'clamav-daemon/ForceToDisk', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/StatsHostID', type: 'string', value: 'auto' } + - { key: 'clamav-daemon/FollowFileSymlinks', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/TCPSocket', type: 'string', value: '3310' } + - { key: 'clamav-daemon/TCPAddr', type: 'string', value: 'any' } + - { key: 'clamav-daemon/DisableCertCheck', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/SelfCheck', type: 'string', value: '3600' } + - { key: 'clamav-daemon/LocalSocket', type: 'string', value: '/var/run/clamav/clamd.ctl' } + - { key: 'clamav-daemon/LocalSocketMode', type: 'string', value: '666' } + - { key: 'clamav-daemon/StatsTimeout', type: 'string', value: '10' } + - { key: 'clamav-daemon/MaxZipTypeRcg', type: 'string', value: '1M' } + - { key: 'clamav-daemon/MaxHTMLNoTags', type: 'string', value: '2M' } + - { key: 'clamav-daemon/LogSyslog', type: 'boolean', value: 'false' } + - { key: 'clamav-daemon/AddGroups', type: 'string', value: '' } + - { key: 'clamav-daemon/Bytecode', type: 'boolean', value: 'true' } + - { key: 'clamav-daemon/ScanArchive', type: 'boolean', value: 'true' } tags: - clamav @@ -58,17 +58,17 @@ value: "{{ item.value }}" vtype: "{{ item.type }}" loop: - - { key: 'clamav-freshclam/autoupdate_freshclam', type: 'select', value: 'daemon' } - - { key: 'clamav-freshclam/proxy_user', type: 'string', value: '' } - - { key: 'clamav-freshclam/NotifyClamd', type: 'boolean', value: 'true' } - - { key: 'clamav-freshclam/local_mirror', type: 'select', value: 'db.fr.clamav.net' } - - { key: 'clamav-freshclam/http_proxy', type: 'string', value: '' } - - { key: 'clamav-freshclam/LogRotate', type: 'boolean', value: 'true' } - - { key: 'clamav-freshclam/Bytecode', type: 'boolean', value: 'true' } - - { key: 'clamav-freshclam/update_interval', type: 'string', value: '24' } - - { key: 'clamav-freshclam/SafeBrowsing', type: 'boolean', value: 'false' } - - { key: 'clamav-freshclam/PrivateMirror', type: 'string', value: '' } - - { key: 'clamav-freshclam/internet_interface', type: 'string', value: '' } + - { key: 'clamav-freshclam/autoupdate_freshclam', type: 'select', value: 'daemon' } + - { key: 'clamav-freshclam/proxy_user', type: 'string', value: '' } + - { key: 'clamav-freshclam/NotifyClamd', type: 'boolean', value: 'true' } + - { key: 'clamav-freshclam/local_mirror', type: 'select', value: 'db.fr.clamav.net' } + - { key: 'clamav-freshclam/http_proxy', type: 'string', value: '' } + - { key: 'clamav-freshclam/LogRotate', type: 'boolean', value: 'true' } + - { key: 'clamav-freshclam/Bytecode', type: 'boolean', value: 'true' } + - { key: 'clamav-freshclam/update_interval', type: 'string', value: '24' } + - { key: 'clamav-freshclam/SafeBrowsing', type: 'boolean', value: 'false' } + - { key: 'clamav-freshclam/PrivateMirror', type: 'string', value: '' } + - { key: 'clamav-freshclam/internet_interface', type: 'string', value: '' } tags: - clamav diff --git a/docker-host/tasks/main.yml b/docker-host/tasks/main.yml index 796c800d..026181f6 100644 --- a/docker-host/tasks/main.yml +++ b/docker-host/tasks/main.yml @@ -36,23 +36,25 @@ owner: root group: root -- name: Install docker and python-docker +- name: Install Docker apt: name: - docker-ce + - docker-ce-cli + - containerd.io update_cache: yes - name: python-docker is installed apt: name: python-docker state: present - when: ansible_distribution_major_version is version('10', '<=') + when: ansible_python_version is version('3', '<') - name: python3-docker is installed apt: name: python3-docker state: present - when: ansible_distribution_major_version is version('10', '>') + when: ansible_python_version is version('3', '>=') - name: Copy Docker daemon configuration file template: diff --git a/dovecot/tasks/main.yml b/dovecot/tasks/main.yml index aa817086..1bebbafc 100644 --- a/dovecot/tasks/main.yml +++ b/dovecot/tasks/main.yml @@ -10,6 +10,11 @@ tags: - dovecot +- name: Generate 4096 bits Diffie-Hellman parameters (may take several minutes) + openssl_dhparam: + path: /etc/ssl/dhparams.pem + size: 4096 + - name: disable pam auth replace: dest: /etc/dovecot/conf.d/10-auth.conf diff --git a/dovecot/templates/z-evolinux-defaults.conf.j2 b/dovecot/templates/z-evolinux-defaults.conf.j2 index 2c067b99..f913ff38 100644 --- a/dovecot/templates/z-evolinux-defaults.conf.j2 +++ b/dovecot/templates/z-evolinux-defaults.conf.j2 @@ -35,12 +35,27 @@ service login { } mail_max_userip_connections = 42 +# Configuration pour stats dovecot +service stats { + unix_listener stats-reader { + user = vmail + group = vmail + mode = 0660 + } + + unix_listener stats-writer { + user = vmail + group = vmail + mode = 0660 + } +} + # SSL/TLS ssl = yes ssl_prefer_server_ciphers = yes -ssl_dh_parameters_length = 2048 +ssl_dh=, + Jérémy Lecour + and others. + +evocommit comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. +END +} + +show_help() { + cat </dev/null; then + mountpoint=$(stat -c '%m' $1) + findmnt "${mountpoint}" --noheadings --output OPTIONS -O ro + else + grep /usr /proc/mounts | grep -E '\bro\b' + fi +} +remount_repository_readwrite() { + if [ "$(get_system)" = "OpenBSD" ]; then + partition=$(stat -f '%Sd' $1) + mount -u -w /dev/${partition} 2>/dev/null + else + mountpoint=$(stat -c '%m' $1) + mount -o remount,rw ${mountpoint} + syslog "Re-mount ${mountpoint} as read-write to commit in repository $1" + fi +} +remount_repository_readonly() { + if [ "$(get_system)" = "OpenBSD" ]; then + partition=$(stat -f '%Sd' $1) + mount -u -r /dev/${partition} 2>/dev/null + else + mountpoint=$(stat -c '%m' $1) + mount -o remount,ro ${mountpoint} 2>/dev/null + syslog "Re-mount ${mountpoint} as read-only after commit to repository $1" + fi +} +is_dry_run() { + test "${DRY_RUN}" = "1" +} +is_verbose() { + test "${VERBOSE}" = "1" +} +is_ansible() { + test "${ANSIBLE}" = "1" +} +main() { + rc=0 + lock="${GIT_DIR}/index.lock" + if [ -f "${lock}" ]; then + limit=$(date +"%s" -d "now - 1 hour") + updated_at=$(stat -c "%Y" "${lock}") + if [ "$updated_at" -lt "$limit" ]; then + rm -f "${lock}" + fi + fi + + git_status=$(${GIT_BIN} status --porcelain) + + if [ -n "${git_status}" ]; then + if is_dry_run; then + ${GIT_BIN} status + else + readonly_orig=0 + # remount mount point read-write if currently readonly + if is_repository_readonly "${REPOSITORY}"; then + readonly_orig=1; + remount_repository_readwrite "${REPOSITORY}"; + fi + author=$(logname) + email=$(git config --get user.email) + email=${email:-"${author}@evolix.net"} + + # commit changes + git_add_result=$(${GIT_BIN} add --all) + git_add_rc=$? + + if is_ansible; then + if [ ${git_add_rc} -ne 0 ]; then + printf "FAILED: %s\n%s" "can't add changes in ${REPOSITORY}" "${git_add_result}" + rc=1 + fi + fi + + git_commit_result=$(${GIT_BIN} commit --message "${MESSAGE}" --author "${author} <${email}>") + git_commit_rc=$? + + if is_ansible; then + if [ ${git_commit_rc} -eq 0 ]; then + printf "CHANGED: %s\n" "commit done in ${REPOSITORY} with \`${MESSAGE}'" + else + printf "FAILED: %s\n%s" "can't commit in ${REPOSITORY} \`${MESSAGE}'" "${git_commit_result}" + rc=1 + fi + fi + + # remount mount point read-only if it was before + if [ ${readonly_orig} -eq 1 ]; then + remount_repository_readonly "${REPOSITORY}" + fi + fi + else + if is_ansible; then + printf "INFO: %s\n" "no commit in ${REPOSITORY}'" + fi + fi + + unset GIT_DIR + unset GIT_WORK_TREE + + exit ${rc} +} +# Parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case ${1:-''} in + -h|-\?|--help) + show_help + exit 0 + ;; + -V|--version) + show_version + exit 0 + ;; + --message) + # message options, with value speparated by space + if [ -n "$2" ]; then + MESSAGE=$2 + shift + else + printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --message=?*) + # message options, with value speparated by = + MESSAGE=${1#*=} + ;; + --message=) + # message options, without value + printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + --repository) + # repository options, with value speparated by space + if [ -n "$2" ]; then + REPOSITORY=$2 + shift + else + printf 'ERROR: "--repository" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --repository=?*) + # repository options, with value speparated by = + REPOSITORY=${1#*=} + ;; + --repository=) + # repository options, without value + printf 'ERROR: "--repository" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + -n|--dry-run) + # disable actual commands + DRY_RUN=1 + ;; + -v|--verbose) + # print verbose information + VERBOSE=1 + ;; + --ansible) + # print information for Ansible + ANSIBLE=1 + ;; + --) + # End of all options. + shift + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + +if [ -z "${MESSAGE}" ]; then + echo "Error: missing message parameter" >&2 + show_usage + exit 1 +fi +if [ -z "${REPOSITORY}" ]; then + echo "Error: missing repository parameter" >&2 + show_usage + exit 1 +fi +DRY_RUN=${DRY_RUN:-0} +VERBOSE=${VERBOSE:-0} +ANSIBLE=${ANSIBLE:-0} + +GIT_BIN=$(command -v git) +readonly GIT_BIN + +LOGGER_BIN=$(command -v logger) +readonly LOGGER_BIN + +export GIT_DIR="${REPOSITORY}/.git" +export GIT_WORK_TREE="${REPOSITORY}" + +if [ -d "${GIT_DIR}" ]; then + main +else + echo "There is no Git repository in '${REPOSITORY}'" >&2 + exit 1 +fi \ No newline at end of file diff --git a/etc-git/files/optimize-etc-git b/etc-git/files/optimize-etc-git deleted file mode 100644 index a7b7510f..00000000 --- a/etc-git/files/optimize-etc-git +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -git --git-dir /etc/.git gc --quiet diff --git a/etc-git/tasks/commit.yml b/etc-git/tasks/commit.yml index 4bcf8e5c..3f993771 100644 --- a/etc-git/tasks/commit.yml +++ b/etc-git/tasks/commit.yml @@ -1,25 +1,52 @@ --- +# /etc - name: Is /etc a git repository stat: path: /etc/.git register: _etc_git -- include: do_commit.yml - vars: - git_folder: "/etc" +- name: "evocommit /etc" + command: "/usr/local/bin/evocommit --ansible --repository /etc --message \"{{ commit_message | mandatory }}\"" + changed_when: + - _etc_git_commit.stdout + - "'CHANGED:' in _etc_git_commit.stdout" + ignore_errors: yes + register: _etc_git_commit when: - _etc_git.stat.exists - _etc_git.stat.isdir +# /etc/bind +- name: Is /etc/bind a git repository + stat: + path: /etc/bind/.git + register: _etc_bind_git + +- name: "evocommit /etc/bind" + command: "/usr/local/bin/evocommit --ansible --repository /etc/bind --message \"{{ commit_message | mandatory }}\"" + changed_when: + - _etc_bind_git_commit.stdout + - "'CHANGED:' in _etc_bind_git_commit.stdout" + ignore_errors: yes + register: _etc_bind_git_commit + when: + - _etc_bind_git.stat.exists + - _etc_bind_git.stat.isdir + +# /usr/share/scripts - name: Is /usr/share/scripts a git repository stat: path: /usr/share/scripts/.git register: _usr_share_scripts_git -- include: do_commit.yml - vars: - git_folder: "/usr/share/scripts" +- name: "evocommit /usr/share/scripts" + command: "/usr/local/bin/evocommit --ansible --repository /usr/share/scripts --message \"{{ commit_message | mandatory }}\"" + changed_when: + - _usr_share_scripts_git_commit.stdout + - "'CHANGED:' in _usr_share_scripts_git_commit.stdout" + ignore_errors: yes + register: _usr_share_scripts_git_commit when: - _usr_share_scripts_git.stat.exists - _usr_share_scripts_git.stat.isdir diff --git a/etc-git/tasks/do_commit.yml b/etc-git/tasks/do_commit.yml deleted file mode 100644 index 6b09eaaf..00000000 --- a/etc-git/tasks/do_commit.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- - -- name: "Remount /usr if needed" - include_role: - name: remount-usr - when: git_folder is match('/usr/.*') - -- name: "is {{ git_folder }} clean?" - command: git status --porcelain - args: - chdir: "{{ git_folder }}" - changed_when: False - register: git_status - when: not ansible_check_mode - ignore_errors: yes - tags: - - etc-git - - commit - -- debug: - var: git_status - verbosity: 3 - tags: - - etc-git - - commit - -- name: fetch current Git user.email - git_config: - name: user.email - repo: "{{ git_folder }}" - register: git_config_user_email - ignore_errors: yes - tags: - - etc-git - - commit - -- name: "set commit author" - set_fact: - commit_author: '{% if ansible_env.SUDO_USER is not defined %}root{% else %}{{ ansible_env.SUDO_USER }}{% endif %}' - commit_email: '{% if git_config_user_email.config_value is not defined or not git_config_user_email.config_value %}root@localhost{% else %}{{ git_config_user_email.config_value }}{% endif %}' # noqa 204 - tags: - - etc-git - - commit - -- name: "{{ git_folder }} modifications are committed" - shell: "git add -A . && git commit -m \"{{ commit_message | mandatory }}\" --author \"{{ commit_author | mandatory }} <{{ commit_email | mandatory }}>\"" - args: - chdir: "{{ git_folder }}" - register: commit_end_run - when: - - not ansible_check_mode - - git_status.stdout | length > 0 - ignore_errors: yes - tags: - - etc-git - - commit - -- debug: - var: commit_end_run - verbosity: 4 - tags: - - etc-git - - commit diff --git a/etc-git/tasks/main.yml b/etc-git/tasks/main.yml index 37d1c692..11be1899 100644 --- a/etc-git/tasks/main.yml +++ b/etc-git/tasks/main.yml @@ -7,6 +7,18 @@ tags: - etc-git +- include_role: + name: evolix/remount-usr + +- name: "evocommit script is installed" + copy: + src: evocommit + dest: /usr/local/bin/evocommit + mode: "0755" + force: yes + tags: + - etc-git + - include: repository.yml vars: repository_path: "/etc" @@ -32,6 +44,24 @@ - _usr_share_scripts.stat.isdir - ansible_distribution_major_version is version('10', '>=') +- name: "etc-git-optimize script is installed" + copy: + src: etc-git-optimize + dest: /usr/share/scripts/etc-git-optimize + mode: "0755" + force: yes + tags: + - etc-git + +- name: "etc-git-status script is installed" + copy: + src: etc-git-status + dest: /usr/share/scripts/etc-git-status + mode: "0755" + force: yes + tags: + - etc-git + - name: Check if cron is installed shell: "set -o pipefail && dpkg -l cron 2>/dev/null | grep -q -E '^(i|h)i'" args: @@ -41,29 +71,44 @@ check_mode: no register: is_cron_installed -- name: Optimize script is installed in monthly crontab - copy: - src: optimize-etc-git - dest: /etc/cron.monthly/optimize-etc-git - mode: "0750" - force: no +- block: + - name: Legacy cron jobs for /etc/.git status are absent + file: + dest: "{{ item }}" + state: absent + loop: + - /etc/cron.monthly/optimize-etc-git + - /etc/cron.d/etc-git-status + + - name: Cron job for monthly git optimization + cron: + name: "Monthly optimization" + cron_file: etc-git + special_time: "monthly" + user: root + job: "/usr/share/scripts/etc-git-optimize" + + - name: Cron job for hourly git status + cron: + name: "Hourly warning for unclean Git repository if nobody is connected" + cron_file: etc-git + special_time: "hourly" + user: root + job: "who > /dev/null || /usr/share/scripts/etc-git-status" + state: "{{ etc_git_monitor_status | bool | ternary('present','absent') }}" + + - name: Cron job for daily git status + cron: + name: "Daily warning for unclean Git repository" + cron_file: etc-git + user: root + job: "/usr/share/scripts/etc-git-status" + minute: "21" + hour: "21" + weekday: "*" + day: "*" + month: "*" + state: "{{ etc_git_monitor_status | bool | ternary('present','absent') }}" when: is_cron_installed.rc == 0 tags: - - etc-git - -- name: Cron job for /etc/.git status is installed - template: - src: etc-git-status.j2 - dest: /etc/cron.d/etc-git-status - mode: "0644" - when: is_cron_installed.rc == 0 and etc_git_monitor_status - tags: - - etc-git - -- name: Cron job for /etc/.git status is removed - file: - dest: /etc/cron.d/etc-git-status - state: absent - when: is_cron_installed.rc == 0 and not etc_git_monitor_status - tags: - - etc-git + - etc-git \ No newline at end of file diff --git a/etc-git/tasks/repository.yml b/etc-git/tasks/repository.yml index e8599c1e..80987da2 100644 --- a/etc-git/tasks/repository.yml +++ b/etc-git/tasks/repository.yml @@ -70,4 +70,4 @@ register: git_commit when: git_log.rc != 0 or (git_init is defined and git_init is changed) tags: - - etc-git + - etc-git \ No newline at end of file diff --git a/etc-git/templates/etc-git-status.j2 b/etc-git/templates/etc-git-status.j2 deleted file mode 100644 index e1696c54..00000000 --- a/etc-git/templates/etc-git-status.j2 +++ /dev/null @@ -1,4 +0,0 @@ -# {{ ansible_managed }} - -@hourly root who > /dev/null || git --git-dir=/etc/.git --work-tree=/etc status --short -21 21 * * * root git --git-dir=/etc/.git --work-tree=/etc status --short diff --git a/evoacme/files/evoacme.cron b/evoacme/files/evoacme.cron index 4d849673..7d4b598c 100755 --- a/evoacme/files/evoacme.cron +++ b/evoacme/files/evoacme.cron @@ -15,12 +15,13 @@ find "${CRT_DIR}" \ -maxdepth 1 \ -mindepth 1 \ -type d \ - ! -path "*accounts" \ - ! -path "*archive" \ - ! -path "*csr" \ - ! -path "*hooks" \ - ! -path "*keys" \ - ! -path "*live" \ - ! -path "*renewal" \ + ! -path "${CRT_DIR}/accounts" \ + ! -path "${CRT_DIR}/archive" \ + ! -path "${CRT_DIR}/csr" \ + ! -path "${CRT_DIR}/hooks" \ + ! -path "${CRT_DIR}/keys" \ + ! -path "${CRT_DIR}/live" \ + ! -path "${CRT_DIR}/renewal" \ + ! -path "${CRT_DIR}/renewal-hooks" \ -printf "%f\n" \ | xargs --max-args=1 --no-run-if-empty evoacme diff --git a/evocheck/README.md b/evocheck/README.md index ce9999c0..6739bef8 100644 --- a/evocheck/README.md +++ b/evocheck/README.md @@ -16,6 +16,4 @@ A separate `exec.yml` file can be imported manually in playbooks or roles to exe ## Variables We can force install via : -* `evocheck_force_install: local` : will copy the script provided by the role -* `evocheck_force_install: package` : will install the package via repositories * `evocheck_update_crontab` : will update the crontab (default: `True`) diff --git a/evocheck/defaults/main.yml b/evocheck/defaults/main.yml index e2d80c2a..6dc43677 100644 --- a/evocheck/defaults/main.yml +++ b/evocheck/defaults/main.yml @@ -1,4 +1,4 @@ --- -evocheck_force_install: False + evocheck_update_crontab: True evocheck_bin_dir: /usr/share/scripts diff --git a/evocheck/files/evocheck.sh b/evocheck/files/evocheck.sh index 02fa4a6b..fb8a6eeb 100644 --- a/evocheck/files/evocheck.sh +++ b/evocheck/files/evocheck.sh @@ -4,7 +4,7 @@ # Script to verify compliance of a Debian/OpenBSD server # powered by Evolix -VERSION="21.07" +VERSION="21.10.4" readonly VERSION # base functions @@ -74,7 +74,7 @@ detect_os() { } is_debian() { - test -n "${DEBIAN_RELEASE}" + test -n "${DEBIAN_RELEASE}" } is_debian_lenny() { test "${DEBIAN_RELEASE}" = "lenny" @@ -220,7 +220,6 @@ check_vartmpfs() { else df /var/tmp | grep -q tmpfs || failed "IS_VARTMPFS" "/var/tmp is not a tmpfs" fi - } check_serveurbase() { is_installed serveur-base || failed "IS_SERVEURBASE" "serveur-base package is not installed" @@ -233,8 +232,19 @@ check_syslogconf() { || failed "IS_SYSLOGCONF" "syslog evolix config file missing" } check_debiansecurity() { - grep -q "^deb.*security" /etc/apt/sources.list \ - || failed "IS_DEBIANSECURITY" "missing debian security repository" + if is_debian_bullseye; then + # https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.html#security-archive + pattern="^deb https://deb\.debian\.org/debian-security/? bullseye-security main" + elif is_debian_buster; then + pattern="^deb http://security\.debian\.org/debian-security/? buster/updates main" + elif is_debian_stretch; then + pattern="^deb http://security\.debian\.org/debian-security/? stretch/updates main" + else + pattern="^deb.*security" + fi + + source_file="/etc/apt/sources.list" + grep -qE "${pattern}" "${source_file}" || failed "IS_DEBIANSECURITY" "missing debian security repository" } check_aptitudeonly() { if is_debian_squeeze || is_debian_wheezy; then @@ -305,7 +315,7 @@ check_customcrontab() { test "$found_lines" = 4 && failed "IS_CUSTOMCRONTAB" "missing custom field in crontab" } check_sshallowusers() { - grep -E -qi "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config \ + grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config /etc/ssh/sshd_config.d \ || failed "IS_SSHALLOWUSERS" "missing AllowUsers or AllowGroups directive in sshd_config" } check_diskperf() { @@ -345,6 +355,13 @@ check_minifw() { /sbin/iptables -L -n | grep -q -E "^ACCEPT\s*all\s*--\s*31\.170\.8\.4\s*0\.0\.0\.0/0\s*$" \ || failed "IS_MINIFW" "minifirewall seems not starded" } +check_minifw_includes() { + if is_debian_bullseye; then + if grep -q -e '/sbin/iptables' -e '/sbin/ip6tables' "${MINIFW_FILE}"; then + failed "IS_MINIFWINCLUDES" "minifirewall has direct iptables invocations in ${MINIFW_FILE} that should go in /etc/minifirewall.d/" + fi + fi +} check_nrpeperms() { if [ -d /etc/nagios ]; then nagiosDir="/etc/nagios" @@ -405,17 +422,20 @@ check_apachemunin() { check_mysqlutils() { MYSQL_ADMIN=${MYSQL_ADMIN:-mysqladmin} if is_installed mysql-server; then - # You can configure MYSQL_ADMIN in evocheck.cf - if ! grep -qs "$MYSQL_ADMIN" /root/.my.cnf; then - failed "IS_MYSQLUTILS" "mysqladmin missing in /root/.my.cnf" + # With Debian 11 and later, root can connect to MariaDB with the socket + if is_debian_wheezy || is_debian_jessie || is_debian_stretch || is_debian_buster; then + # You can configure MYSQL_ADMIN in evocheck.cf + if ! grep -qs "^user *= *${MYSQL_ADMIN}" /root/.my.cnf; then + failed "IS_MYSQLUTILS" "${MYSQL_ADMIN} missing in /root/.my.cnf" + fi fi if ! test -x /usr/bin/mytop; then if ! test -x /usr/local/bin/mytop; then failed "IS_MYSQLUTILS" "mytop binary missing" fi fi - if ! grep -qs debian-sys-maint /root/.mytop; then - failed "IS_MYSQLUTILS" "debian-sys-maint missing in /root/.mytop" + if ! grep -qs '^user *=' /root/.mytop; then + failed "IS_MYSQLUTILS" "credentials missing in /root/.mytop" fi fi } @@ -457,7 +477,8 @@ check_squid() { && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -d $host -j ACCEPT" "$MINIFW_FILE" \ && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -d 127.0.0.(1|0/8) -j ACCEPT" "$MINIFW_FILE" \ && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port.* $http_port" "$MINIFW_FILE"; - } || failed "IS_SQUID" "missing squid rules in minifirewall" + } || grep -qE "^PROXY='?on'?" "$MINIFW_FILE" \ + || failed "IS_SQUID" "missing squid rules in minifirewall" fi } check_evomaintenance_fw() { @@ -582,6 +603,7 @@ check_evobackup_exclude_mount() { failed "IS_EVOBACKUP_EXCLUDE_MOUNT" "${mount} is not excluded from ${evobackup_file} backup script" done done + rm -rf "${excludes_file}" } # Verification de la presence du userlogrotate check_userlogrotate() { @@ -1018,7 +1040,7 @@ check_phpevolinuxconf() { check_squidlogrotate() { if is_debian_stretch || is_debian_buster || is_debian_bullseye; then if is_installed squid; then - grep -q monthly /etc/logrotate.d/squid \ + grep -q -e monthly -e daily /etc/logrotate.d/squid \ || failed "IS_SQUIDLOGROTATE" "missing squid logrotate file" fi fi @@ -1331,6 +1353,133 @@ check_lxc_container_resolv_conf() { done fi } +download_versions() { + local file + file=${1:-} + + ## The file is supposed to list programs : each on a line, then its latest version number + ## Examples: + # evoacme 21.06 + # evomaintenance 0.6.4 + + if is_debian; then + versions_url="https://upgrades.evolix.org/versions-${DEBIAN_RELEASE}" + elif is_openbsd; then + versions_url="https://upgrades.evolix.org/versions-${OPENBSD_RELEASE}" + else + failed "IS_VERSIONS_CHECK" "error determining os release" + fi + + # fetch timeout, in seconds + timeout=10 + + if command -v curl > /dev/null; then + curl --max-time ${timeout} --fail --silent --output "${versions_file}" "${versions_url}" + elif command -v wget > /dev/null; then + wget --timeout=${timeout} --quiet "${versions_url}" -O "${versions_file}" + elif command -v GET; then + GET -t ${timeout}s "${versions_url}" > "${versions_file}" + else + failed "IS_VERSIONS_CHECK" "failed to find curl, wget or GET" + fi + test "$?" -eq 0 || failed "IS_VERSIONS_CHECK" "failed to download ${versions_url} to ${versions_file}" +} +get_command() { + local program + program=${1:-} + + case "${program}" in + ## Special cases where the program name is different than the command name + evocheck) echo "${0}" ;; + evomaintenance) command -v "evomaintenance.sh" ;; + listupgrade) command -v "evolistupgrade.sh" ;; + old-kernel-autoremoval) command -v "old-kernel-autoremoval.sh" ;; + mysql-queries-killer) command -v "mysql-queries-killer.sh" ;; + + ## General case, where the program name is the same as the command name + *) command -v "${program}" ;; + esac +} +get_version() { + local program + local command + program=${1:-} + command=${2:-} + + case "${program}" in + ## Special case if `command --version => 'command` is not the standard way to get the version + # my_command) + # /path/to/my_command --get-version + # ;; + + add-vm) + grep '^VERSION=' "${command}" | head -1 | cut -d '=' -f 2 + ;; + ## Let's try the --version flag before falling back to grep for the constant + kvmstats) + if ${command} --version > /dev/null 2> /dev/null; then + ${command} --version 2> /dev/null | head -1 | cut -d ' ' -f 3 + else + grep '^VERSION=' "${command}" | head -1 | cut -d '=' -f 2 + fi + ;; + + ## General case to get the version + *) ${command} --version 2> /dev/null | head -1 | cut -d ' ' -f 3 ;; + esac +} +check_version() { + local program + local expected_version + program=${1:-} + expected_version=${2:-} + + command=$(get_command "${program}") + if [ -n "${command}" ]; then + # shellcheck disable=SC2086 + actual_version=$(get_version "${program}" "${command}") + # printf "program:%s expected:%s actual:%s\n" "${program}" "${expected_version}" "${actual_version}" + if [ -z "${actual_version}" ]; then + failed "IS_VERSIONS_CHECK" "failed to lookup actual version of ${program}" + elif dpkg --compare-versions "${actual_version}" lt "${expected_version}"; then + failed "IS_VERSIONS_CHECK" "${program} version ${actual_version} is older than expected version ${expected_version}" + elif dpkg --compare-versions "${actual_version}" gt "${expected_version}"; then + failed "IS_VERSIONS_CHECK" "${program} version ${actual_version} is newer than expected version ${expected_version}, you should update tour index." + else + : # Version check OK + fi + fi +} +add_to_path() { + local new_path + new_path=${1:-} + + echo "$PATH" | grep -qF "${new_path}" || export PATH="${PATH}:${new_path}" +} +check_versions() { + versions_file=$(mktemp --tmpdir=/tmp "evocheck-versions.XXXXX") + # shellcheck disable=SC2064 + trap "rm -f ${versions_file}" 0 + download_versions "${versions_file}" + add_to_path "/usr/share/scripts" + + grep -v '^ *#' < "${versions_file}" | while IFS= read -r line; do + local program + local version + program=$(echo "${line}" | cut -d ' ' -f 1) + version=$(echo "${line}" | cut -d ' ' -f 2) + + if [ -n "${program}" ]; then + if [ -n "${version}" ]; then + check_version "${program}" "${version}" + else + failed "IS_VERSIONS_CHECK" "failed to lookup expected version for ${program}" + fi + fi + done + + rm -f "${versions_file}" +} main() { # Default return code : 0 = no error @@ -1386,6 +1535,8 @@ main() { test "${IS_ALERT5MINIFW:=1}" = 1 && test "${IS_MINIFW:=1}" = 1 && check_minifw test "${IS_NRPEPERMS:=1}" = 1 && check_nrpeperms test "${IS_MINIFWPERMS:=1}" = 1 && check_minifwperms + # Enable when minifirewall is released + test "${IS_MINIFWINCLUDES:=0}" = 1 && check_minifw_includes test "${IS_NRPEDISKS:=0}" = 1 && check_nrpedisks test "${IS_NRPEPID:=1}" = 1 && check_nrpepid test "${IS_GRSECPROCS:=1}" = 1 && check_grsecprocs @@ -1459,6 +1610,7 @@ main() { test "${IS_CHROOTED_BINARY_UPTODATE:=1}" = 1 && check_chrooted_binary_uptodate test "${IS_NGINX_LETSENCRYPT_UPTODATE:=1}" = 1 && check_nginx_letsencrypt_uptodate test "${IS_LXC_CONTAINER_RESOLV_CONF:=1}" = 1 && check_lxc_container_resolv_conf + test "${IS_CHECK_VERSIONS:=1}" = 1 && check_versions fi #----------------------------------------------------------- @@ -1598,6 +1750,7 @@ while :; do IS_KERNELUPTODATE=0 IS_UPTIME=0 IS_MELTDOWN_SPECTRE=0 + IS_CHECK_VERSIONS=0 ;; -v|--verbose) VERBOSE=1 diff --git a/evocheck/tasks/install_local.yml b/evocheck/tasks/install.yml similarity index 100% rename from evocheck/tasks/install_local.yml rename to evocheck/tasks/install.yml diff --git a/evocheck/tasks/install_package.yml b/evocheck/tasks/install_package.yml deleted file mode 100644 index 34e672e5..00000000 --- a/evocheck/tasks/install_package.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: install evocheck from package - apt: - name: evocheck - state: present diff --git a/evocheck/tasks/main.yml b/evocheck/tasks/main.yml index 87e2d636..2032740b 100644 --- a/evocheck/tasks/main.yml +++ b/evocheck/tasks/main.yml @@ -1,10 +1,13 @@ --- -- include: install_local.yml - when: evocheck_force_install == "local" +- name: Package install is not supported anymore + fail: + msg: Package install is not supported anymore + when: + - evocheck_force_install is defined + - evocheck_force_install == "package" -- include: install_package.yml - when: evocheck_force_install == "package" +- include: install.yml - include: cron.yml when: evocheck_update_crontab | bool diff --git a/evolinux-base/defaults/main.yml b/evolinux-base/defaults/main.yml index 26f6e4c8..591bc62b 100644 --- a/evolinux-base/defaults/main.yml +++ b/evolinux-base/defaults/main.yml @@ -89,6 +89,7 @@ evolinux_packages_invalid_mta: True evolinux_packages_delete_nfs: True evolinux_packages_listchanges: True evolinux_packages_logcheck_recipient: False +evolinux_packages_delete_aptlistchanges: True # system @@ -164,8 +165,10 @@ evolinux_logs_include: True evolinux_logs_logrotate_confs: True evolinux_logs_default_rotate: True +evolinux_logs_default_dateext : True evolinux_logs_disable_logrotate_rsyslog: True evolinux_logs_rsyslog_conf: True +evolinux_logrotate_dateformat: "-%Y%m%d%H" # default www @@ -206,7 +209,6 @@ evolinux_fail2ban_include: False # Evocheck evolinux_evocheck_include: True -evolinux_evocheck_force_install: "local" # Listupgrade @@ -218,3 +220,6 @@ evolinux_generateldif_include: True # Cron check_hpraid evolinux_cron_checkhpraid_frequency: daily + +# Motd +evolinux_motd_include: True \ No newline at end of file diff --git a/evolinux-base/files/alert5.service b/evolinux-base/files/alert5.service index eb5c72a9..ea966b37 100644 --- a/evolinux-base/files/alert5.service +++ b/evolinux-base/files/alert5.service @@ -1,10 +1,10 @@ [Unit] Description=Evolix alert5 script +After=network.target [Service] Type=oneshot ExecStart=/usr/share/scripts/alert5.sh [Install] -WantedBy=multi-user.target -After=network.target \ No newline at end of file +WantedBy=multi-user.target \ No newline at end of file diff --git a/evolinux-base/files/backup-server-state.sh b/evolinux-base/files/backup-server-state.sh new file mode 100644 index 00000000..0c503c37 --- /dev/null +++ b/evolinux-base/files/backup-server-state.sh @@ -0,0 +1,635 @@ +#!/bin/sh + +PROGNAME="backup-server-state" + +VERSION="22.01" +readonly VERSION + +backup_dir= +rc=0 + +# base functions + +show_version() { + cat <, + Jérémy Lecour + and others. + +${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 < "${backup_dir}/current_packages.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* dpkg OK" + else + debug "* dpkg ERROR :" + debug "${last_result}" + rc=10 + fi +} + +backup_uptime() { + debug "Backup uptime" + + last_result=$(uptime > "${backup_dir}/uptime.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* uptime OK" + else + debug "* uptime ERROR" + debug "${last_result}" + rc=10 + fi +} + +backup_processes() { + debug "Backup process list" + + last_result=$(ps fauxw > "${backup_dir}/ps.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* ps OK" + else + debug "* ps ERROR" + debug "${last_result}" + rc=10 + fi + + pstree_bin=$(command -v pstree) + + if [ -z "${pstree_bin}" ]; then + last_result=$(pstree -pan > "${backup_dir}/pstree.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* pstree OK" + else + debug "* pstree ERROR" + debug "${last_result}" + rc=10 + fi + fi +} + +backup_netstat() { + debug "Backup network status" + + ss_bin=$(command -v ss) + if [ -z "${ss_bin}" ]; then + last_result=$(${ss_bin} -tanpul > "${backup_dir}/netstat-ss.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* ss OK" + else + debug "* ss ERROR" + debug "${last_result}" + rc=10 + fi + fi + + netstat_bin=$(command -v netstat) + if [ -z "${netstat_bin}" ]; then + last_result=$(netstat -laputen > "${backup_dir}/netstat-legacy.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* netstat OK" + else + debug "* netstat ERROR" + debug "${last_result}" + rc=10 + fi + fi +} + +backup_netcfg() { + debug "Backup network configuration" + + last_result=$(ip address show > "${backup_dir}/ip-address.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* ip address OK" + else + debug "* ip address ERROR" + debug "${last_result}" + rc=10 + fi + + last_result=$(ip route show > "${backup_dir}/ip-route.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* ip route OK" + else + debug "* ip route ERROR" + debug "${last_result}" + rc=10 + fi +} + +backup_iptables() { + debug "Backup iptables" + + last_result=$({ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > "${backup_dir}/iptables.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* iptables OK" + else + debug "* iptables ERROR" + debug "${last_result}" + rc=10 + fi +} + +backup_sysctl() { + debug "Backup sysctl values" + + last_result=$(sysctl -a | sort -h > "${backup_dir}/sysctl.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* sysctl OK" + else + debug "* sysctl ERROR" + debug "${last_result}" + rc=10 + fi +} + +backup_virsh() { + debug "Backup virsh list" + + virsh_bin=$(command -v virsh) + + if [ -n "${virsh_bin}" ]; then + last_result=$(${virsh_bin} list --all > "${backup_dir}/virsh-list.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* virsh list OK" + else + debug "* virsh list ERROR" + debug "${last_result}" + rc=10 + fi + else + debug "* virsh not installed" + fi +} + +backup_lxc() { + debug "Backup lxc list" + + lxc_ls_bin=$(command -v lxc-ls) + + if [ -n "${lxc_ls_bin}" ]; then + last_result=$(${lxc_ls_bin} --fancy > "${backup_dir}/lxc-list.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* lxc list OK" + else + debug "* lxc list ERROR" + debug "${last_result}" + rc=10 + fi + else + debug "* lxc-ls not installed" + fi +} + +backup_mount() { + debug "Backup mount points" + + findmnt_bin=$(command -v findmnt) + mount_bin=$(command -v mount) + + if [ -n "${findmnt_bin}" ]; then + last_result=$(${findmnt_bin} > "${backup_dir}/mount.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* mount points OK" + else + debug "* mount points ERROR" + debug "${last_result}" + rc=10 + fi + elif [ -n "${mount_bin}" ]; then + last_result=$(${mount_bin} > "${backup_dir}/mount.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* mount points OK" + else + debug "* mount points ERROR" + debug "${last_result}" + rc=10 + fi + else + debug "* findmnt and mount not installed" + fi +} + +backup_df() { + debug "Backup df" + + df_bin=$(command -v df) + + if [ -n "${df_bin}" ]; then + last_result=$(${df_bin} --portability > "${backup_dir}/df.txt") + last_rc=$? + + if [ ${last_rc} -eq 0 ]; then + debug "* df OK" + else + debug "* df ERROR" + debug "${last_result}" + rc=10 + fi + else + debug "* df not installed" + fi +} + +main() { + if [ -z "${backup_dir}" ]; then + echo "ERROR: You must provide the --backup-dir argument" >&2 + exit 1 + fi + + if [ -d "${backup_dir}" ]; then + echo "ERROR: The backup directory ${backup_dir} already exists. Delete it first." >&2 + exit 2 + else + create_backup_dir + fi + + if [ "${DO_ETC}" -eq 1 ]; then + backup_etc + fi + if [ "${DO_DPKG}" -eq 1 ]; then + backup_dpkg + fi + if [ "${DO_APT}" -eq 1 ]; then + backup_apt + fi + if [ "${DO_PACKAGES}" -eq 1 ]; then + backup_packages + fi + if [ "${DO_PROCESSES}" -eq 1 ]; then + backup_processes + fi + if [ "${DO_UPTIME}" -eq 1 ]; then + backup_uptime + fi + if [ "${DO_NETSTAT}" -eq 1 ]; then + backup_netstat + fi + if [ "${DO_NETCFG}" -eq 1 ]; then + backup_netcfg + fi + if [ "${DO_IPTABLES}" -eq 1 ]; then + backup_iptables + fi + if [ "${DO_SYSCTL}" -eq 1 ]; then + backup_sysctl + fi + if [ "${DO_VIRSH}" -eq 1 ]; then + backup_virsh + fi + if [ "${DO_LXC}" -eq 1 ]; then + backup_lxc + fi + if [ "${DO_MOUNT}" -eq 1 ]; then + backup_mount + fi + if [ "${DO_DF}" -eq 1 ]; then + backup_df + fi + + debug "=> Your backup is available at ${backup_dir}" + exit ${rc} +} + +# parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case $1 in + -h|-\?|--help) + show_help + exit 0 + ;; + -V|--version) + show_version + exit 0 + ;; + -v|--verbose) + VERBOSE=1 + ;; + + -d|--backup-dir) + # with value separated by space + if [ -n "$2" ]; then + backup_dir=$2 + shift + else + printf 'ERROR: "-d|--backup-dir" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --backup-dir=?*) + # with value speparated by = + backup_dir=${1#*=} + ;; + --backup-dir=) + # without value + printf 'ERROR: "--backup-dir" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + + --etc) + DO_ETC=1 + ;; + --no-etc) + DO_ETC=0 + ;; + + --dpkg) + DO_DPKG=1 + ;; + --no-dpkg) + DO_DPKG=0 + ;; + + --apt) + DO_APT=1 + ;; + --no-apt) + DO_APT=0 + ;; + + --packages) + DO_PACKAGES=1 + ;; + --no-packages) + DO_PACKAGES=0 + ;; + + --processes) + DO_PROCESSES=1 + ;; + --no-processes) + DO_PROCESSES=0 + ;; + + --uptime) + DO_UPTIME=1 + ;; + --no-uptime) + DO_UPTIME=0 + ;; + + --netstat) + DO_NETSTAT=1 + ;; + --no-netstat) + DO_NETSTAT=0 + ;; + + --netcfg) + DO_NETCFG=1 + ;; + --no-netcfg) + DO_NETCFG=0 + ;; + + --iptables) + DO_IPTABLES=1 + ;; + --no-iptables) + DO_IPTABLES=0 + ;; + + --sysctl) + DO_SYSCTL=1 + ;; + --no-sysctl) + DO_SYSCTL=0 + ;; + + --virsh) + DO_VIRSH=1 + ;; + --no-virsh) + DO_VIRSH=0 + ;; + + --lxc) + DO_LXC=1 + ;; + --no-lxc) + DO_LXC=0 + ;; + + --mount) + DO_MOUNT=1 + ;; + --no-mount) + DO_MOUNT=0 + ;; + + --df) + DO_DF=1 + ;; + --no-df) + DO_DF=0 + ;; + + --) + # End of all options. + shift + break + ;; + -?*) + # ignore unknown options + printf 'WARN: Unknown option : %s\n' "$1" >&2 + exit 1 + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + +# Default values +: "${VERBOSE:=0}" +: "${DO_ETC:=0}" +: "${DO_DPKG:=0}" +: "${DO_APT:=1}" +: "${DO_PACKAGES:=1}" +: "${DO_PROCESSES:=1}" +: "${DO_UPTIME:=1}" +: "${DO_NETSTAT:=1}" +: "${DO_NETCFG:=1}" +: "${DO_IPTABLES:=1}" +: "${DO_SYSCTL:=1}" +: "${DO_VIRSH:=1}" +: "${DO_LXC:=1}" +: "${DO_MOUNT:=1}" +: "${DO_DF:=1}" + +export LC_ALL=C + +set -u + +main diff --git a/evolinux-base/files/logs/logrotate.d/alternatives b/evolinux-base/files/logs/logrotate.d/alternatives new file mode 100644 index 00000000..5fa5b7a1 --- /dev/null +++ b/evolinux-base/files/logs/logrotate.d/alternatives @@ -0,0 +1,9 @@ +/var/log/alternatives.log { + monthly + rotate 120 + compress + delaycompress + missingok + notifempty + create 644 root root +} diff --git a/evolinux-base/files/logs/logrotate.d/dpkg b/evolinux-base/files/logs/logrotate.d/dpkg index 16ac22fe..81f71969 100644 --- a/evolinux-base/files/logs/logrotate.d/dpkg +++ b/evolinux-base/files/logs/logrotate.d/dpkg @@ -6,14 +6,4 @@ missingok notifempty create 644 root root -} -/var/log/alternatives.log { - monthly - rotate 120 - compress - delaycompress - missingok - notifempty - create 644 root root -} - +} \ No newline at end of file diff --git a/evolinux-base/files/logs/logrotate.disabled/ldap b/evolinux-base/files/logs/logrotate.disabled/ldap index 59372a33..4be35fa8 100644 --- a/evolinux-base/files/logs/logrotate.disabled/ldap +++ b/evolinux-base/files/logs/logrotate.disabled/ldap @@ -2,8 +2,8 @@ weekly missingok rotate 3 - compress - notifempty + compress + notifempty create 640 root adm } diff --git a/evolinux-base/files/logs/logrotate.disabled/procmail b/evolinux-base/files/logs/logrotate.disabled/procmail index d42323f1..29dd2d7a 100644 --- a/evolinux-base/files/logs/logrotate.disabled/procmail +++ b/evolinux-base/files/logs/logrotate.disabled/procmail @@ -1,11 +1,7 @@ /var/log/procmail.log { daily rotate 365 - dateext - dateyesterday - dateformat .%Y%m%d missingok - rotate 365 create 640 root adm } diff --git a/evolinux-base/tasks/fstab.yml b/evolinux-base/tasks/fstab.yml index e10f483e..a3933844 100644 --- a/evolinux-base/tasks/fstab.yml +++ b/evolinux-base/tasks/fstab.yml @@ -1,5 +1,6 @@ --- # TODO: trouver comment faire une copie initiale de /etc/fstab +# - piste : paramètre "backup" du module mount https://docs.ansible.com/ansible/latest/collections/ansible/posix/mount_module.html # TODO: try to use the custom mount_uuid module for a different approach - name: Fetch fstab content diff --git a/evolinux-base/tasks/hardware.yml b/evolinux-base/tasks/hardware.yml index 9f0c6da3..e072f95c 100644 --- a/evolinux-base/tasks/hardware.yml +++ b/evolinux-base/tasks/hardware.yml @@ -30,13 +30,25 @@ - packages when: broadcom_netextreme_search.rc == 0 + +## Dedicated hardware +- name: Install freepmi when it's dedicated hardware + apt: + name: + - libipc-run-perl + - freeipmi + state: present + tags: + - packages + when: ansible_virtualization_role == "host" + ## RAID # Dell and others: MegaRAID SAS # HP gen <10: Hewlett-Packard Company Smart Array # HP gen >=10: Adaptec Smart Storage PQI - name: Detect if RAID is installed - shell: - cmd: "set -o pipefail && lspci -q | grep -e 'RAID bus controller' -e 'Serial Attached SCSI controller'" + shell: + cmd: "lspci -q | grep -e 'RAID bus controller' -e 'Serial Attached SCSI controller'" executable: /bin/bash check_mode: no register: raidmodel diff --git a/evolinux-base/tasks/kernel.yml b/evolinux-base/tasks/kernel.yml index b49968f1..6ddeb57f 100644 --- a/evolinux-base/tasks/kernel.yml +++ b/evolinux-base/tasks/kernel.yml @@ -8,8 +8,8 @@ state: present reload: yes loop: - - { name: kernel.panic_on_oops, value: 1 } - - { name: kernel.panic, value: 60 } + - { name: kernel.panic_on_oops, value: 1 } + - { name: kernel.panic, value: 60 } when: evolinux_kernel_reboot_after_panic | bool - name: Don't reboot after panic @@ -19,8 +19,8 @@ state: absent reload: yes loop: - - kernel.panic_on_oops - - kernel.panic + - kernel.panic_on_oops + - kernel.panic when: not evolinux_kernel_reboot_after_panic | bool - name: Disable net.ipv4.tcp_timestamps diff --git a/evolinux-base/tasks/logs.yml b/evolinux-base/tasks/logs.yml index 2bf28b98..8298486e 100644 --- a/evolinux-base/tasks/logs.yml +++ b/evolinux-base/tasks/logs.yml @@ -30,11 +30,34 @@ dest: /etc/logrotate.d/zsyslog when: evolinux_logs_logrotate_confs | bool -- name: Configure logrotate.conf +- name: Configure logrotate.conf default rotate value replace: dest: /etc/logrotate.conf regexp: "rotate [0-9]+" replace: "rotate 12" when: evolinux_logs_default_rotate | bool +- name: Enable logrotate.conf dateext option + lineinfile: + dest: /etc/logrotate.conf + line: "dateext" + regexp: "^#?\\s*dateext" + when: evolinux_logs_default_dateext | bool + +- name: Enable logrotate.conf dateformat option + lineinfile: + dest: /etc/logrotate.conf + line: "dateformat {{ evolinux_logrotate_dateformat | mandatory }}" + regexp: "^#?\\s*dateformat.*" + insertafter: 'dateext' + when: evolinux_logs_default_dateext | bool + +- name: Disable logrotate.conf dateyesterday option + lineinfile: + dest: /etc/logrotate.conf + line: "# dateyesterday" + regexp: "^\\s*dateyesterday" + insertafter: 'dateext' + when: evolinux_logs_default_dateext | bool + - meta: flush_handlers diff --git a/evolinux-base/tasks/main.yml b/evolinux-base/tasks/main.yml index b64badd6..bc11c030 100644 --- a/evolinux-base/tasks/main.yml +++ b/evolinux-base/tasks/main.yml @@ -97,6 +97,9 @@ when: evolinux_log2mail_include | bool - include: motd.yml + when: evolinux_motd_include | bool + +- include: utils.yml - name: Munin include_role: @@ -116,8 +119,6 @@ - name: Evocheck include_role: name: evolix/evocheck - vars: - evocheck_force_install: "{{ evolinux_evocheck_force_install }}" when: evolinux_evocheck_include | bool - name: Listupgrade diff --git a/evolinux-base/tasks/packages.yml b/evolinux-base/tasks/packages.yml index 8df64abd..f4eafc6c 100644 --- a/evolinux-base/tasks/packages.yml +++ b/evolinux-base/tasks/packages.yml @@ -16,6 +16,7 @@ - ssl-cert - ca-certificates - rename + - dmidecode when: evolinux_packages_system | bool - name: Install/Update diagnostic tools @@ -34,6 +35,7 @@ - telnet - traceroute - man + - molly-guard when: evolinux_packages_diagnostic | bool - name: Install/Update hardware tools @@ -143,5 +145,6 @@ when: - ansible_distribution == "Debian" - ansible_distribution_major_version is version('9', '>=') + - evolinux_packages_delete_aptlistchanges - meta: flush_handlers diff --git a/evolinux-base/tasks/system.yml b/evolinux-base/tasks/system.yml index 554bb02a..4b6c95b4 100644 --- a/evolinux-base/tasks/system.yml +++ b/evolinux-base/tasks/system.yml @@ -119,10 +119,10 @@ regexp: "{{ item.regexp }}" replace: "{{ item.replace }}" loop: - - { regexp: '^17((\s*\*){4})', replace: '{{ 59|random(start=1) }}\1' } - - { regexp: '^25\s*6((\s*\*){3})', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } - - { regexp: '^47\s*6((\s*\*){2}\s*7)', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } - - { regexp: '^52\s*6(\s*1(\s*\*){2})', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } + - { regexp: '^17((\s*\*){4})', replace: '{{ 59|random(start=1) }}\1' } + - { regexp: '^25\s*6((\s*\*){3})', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } + - { regexp: '^47\s*6((\s*\*){2}\s*7)', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } + - { regexp: '^52\s*6(\s*1(\s*\*){2})', replace: '{{ 59|random(start=1) }} {{ [0,1,3,4,5,6,7]|random }}\1' } when: - is_cron_installed.rc == 0 - evolinux_system_cron_random | bool @@ -182,6 +182,7 @@ - evolinux_system_alert5_init | bool - evolinux_system_alert5_enable | bool - ansible_distribution_major_version is version('10', '>=') + - not ansible_check_mode ## network interfaces diff --git a/evolinux-base/tasks/utils.yml b/evolinux-base/tasks/utils.yml new file mode 100644 index 00000000..ede5f4e8 --- /dev/null +++ b/evolinux-base/tasks/utils.yml @@ -0,0 +1,13 @@ +--- + +- include_role: + name: evolix/remount-usr + +- name: backup-server-state script is present + copy: + src: "backup-server-state.sh" + dest: /usr/local/sbin/backup-server-state + force: True + owner: root + group: root + mode: "0750" diff --git a/evolinux-base/templates/logs/zsyslog.j2 b/evolinux-base/templates/logs/zsyslog.j2 index 2fc2bd1a..cb6d931e 100644 --- a/evolinux-base/templates/logs/zsyslog.j2 +++ b/evolinux-base/templates/logs/zsyslog.j2 @@ -1,8 +1,13 @@ # Custom EvoLinux create 640 root adm +{% if not evolinux_logs_default_dateext %} +# BEGIN legacy setting +# … when global dateext and dateformat are not enabled dateext dateyesterday dateformat .%Y%m%d +# END legacy setting +{% endif %} missingok notifempty delaycompress diff --git a/evolinux-users/templates/sudoers_jessie.j2 b/evolinux-users/templates/sudoers_jessie.j2 index b82c67ac..c0703c49 100644 --- a/evolinux-users/templates/sudoers_jessie.j2 +++ b/evolinux-users/templates/sudoers_jessie.j2 @@ -7,6 +7,8 @@ nagios ALL = NOPASSWD: /usr/lib/nagios/plugins/check_procs nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_minifirewall nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_haproxy_stats nagios ALL = NOPASSWD: /usr/sbin/bkctld check +nagios ALL = NOPASSWD: /usr/sbin/bkctld check-jails +nagios ALL = NOPASSWD: /usr/sbin/bkctld check-setup nagios ALL = (clamav) NOPASSWD: /usr/bin/clamscan /tmp/safe.txt ADMINS ALL = (ALL:ALL) ALL diff --git a/evolinux-users/templates/sudoers_stretch.j2 b/evolinux-users/templates/sudoers_stretch.j2 index 539f871e..4a522e1b 100644 --- a/evolinux-users/templates/sudoers_stretch.j2 +++ b/evolinux-users/templates/sudoers_stretch.j2 @@ -6,10 +6,22 @@ nagios ALL = NOPASSWD: /usr/lib/nagios/plugins/check_procs nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_minifirewall nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_haproxy_stats nagios ALL = NOPASSWD: /usr/sbin/bkctld check +nagios ALL = NOPASSWD: /usr/sbin/bkctld check-jails +nagios ALL = NOPASSWD: /usr/sbin/bkctld check-setup nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php56/rootfs/etc/php5/fpm/pool.d/ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php70/rootfs/etc/php/7.0/fpm/pool.d/ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php73/rootfs/etc/php/7.3/fpm/pool.d/ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php74/rootfs/etc/php/7.4/fpm/pool.d/ +nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php74/rootfs/etc/php/8.0/fpm/pool.d/ +nagios ALL = NOPASSWD: /usr/sbin/megaclisas-status --nagios +nagios ALL = NOPASSWD: /usr/lib/nagios/plugins/check_ipmi_sensor +nagios ALL = NOPASSWD: /sbin/dmsetup status --noflush +nagios ALL = NOPASSWD: /sbin/megacli -PDList -aALL -NoLog +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 = (clamav) NOPASSWD: /usr/bin/clamscan /tmp/safe.txt %{{ evolinux_sudo_group }} ALL=(ALL:ALL) ALL diff --git a/evomaintenance/files/evomaintenance.sh b/evomaintenance/files/evomaintenance.sh index 1961ebf2..3903f2ef 100644 --- a/evomaintenance/files/evomaintenance.sh +++ b/evomaintenance/files/evomaintenance.sh @@ -4,16 +4,16 @@ # Dependencies (all OS): git postgresql-client # Dependencies (Debian): sudo -# Copyright 2007-2021 Evolix , Gregory Colpart , +# Copyright 2007-2022 Evolix , Gregory Colpart , # Jérémy Lecour and others. -VERSION="0.6.4" +VERSION="22.01" show_version() { cat <, +Copyright 2007-2022 Evolix , Gregory Colpart , Jérémy Lecour and others. @@ -303,6 +303,9 @@ From: ${FULLFROM} Content-Type: text/plain; charset=UTF-8 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit +X-Evomaintenance-Version: ${VERSION} +X-Evomaintenance-Host: ${HOSTNAME_TEXT} +X-Evomaintenance-User: ${USER} To: ${EVOMAINTMAIL} Subject: [evomaintenance] Intervention sur ${HOSTNAME_TEXT} (${USER}) diff --git a/evomaintenance/tasks/config.yml b/evomaintenance/tasks/config.yml new file mode 100644 index 00000000..097e9770 --- /dev/null +++ b/evomaintenance/tasks/config.yml @@ -0,0 +1,18 @@ +--- + +- assert: + that: + - evomaintenance_api_endpoint is not none + - evomaintenance_api_key is not none + msg: evomaintenance api variables must be set + +- name: Configuration is installed + template: + src: evomaintenance.j2 + dest: /etc/evomaintenance.cf + owner: root + group: root + mode: "0600" + force: "{{ evomaintenance_force_config | bool }}" + tags: + - evomaintenance diff --git a/evomaintenance/tasks/install_package_debian.yml b/evomaintenance/tasks/install_package_debian.yml index a5da77ea..ce9d90e7 100644 --- a/evomaintenance/tasks/install_package_debian.yml +++ b/evomaintenance/tasks/install_package_debian.yml @@ -12,15 +12,4 @@ name: evomaintenance allow_unauthenticated: yes tags: - - evomaintenance - -- name: Configuration is installed - template: - src: evomaintenance.j2 - dest: /etc/evomaintenance.cf - owner: root - group: root - mode: "0600" - force: "{{ evomaintenance_force_config | bool }}" - tags: - - evomaintenance + - evomaintenance \ No newline at end of file diff --git a/evomaintenance/tasks/install_vendor_debian.yml b/evomaintenance/tasks/install_vendor_debian.yml index 2faaac79..99448e3c 100644 --- a/evomaintenance/tasks/install_vendor_debian.yml +++ b/evomaintenance/tasks/install_vendor_debian.yml @@ -46,15 +46,4 @@ - { src: 'evomaintenance.sh', dest: '/usr/share/scripts/', mode: '0700' } - { src: 'evomaintenance.tpl', dest: '/usr/share/scripts/', mode: '0600' } tags: - - evomaintenance - -- name: Configuration is installed - template: - src: evomaintenance.j2 - dest: /etc/evomaintenance.cf - owner: root - group: root - mode: "0600" - force: "{{ evomaintenance_force_config | bool }}" - tags: - - evomaintenance + - evomaintenance \ No newline at end of file diff --git a/evomaintenance/tasks/main.yml b/evomaintenance/tasks/main.yml index 9826089b..0a4e5010 100644 --- a/evomaintenance/tasks/main.yml +++ b/evomaintenance/tasks/main.yml @@ -1,14 +1,5 @@ --- -- set_fact: - minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}" - -- assert: - that: - - evomaintenance_api_endpoint is not none - - evomaintenance_api_key is not none - msg: evomaintenance api variables must be set - - include: install_package_debian.yml when: - not (evomaintenance_install_vendor | bool) @@ -19,6 +10,8 @@ - evomaintenance_install_vendor | bool - ansible_distribution == "Debian" +- include: config.yml + - include: minifirewall.yml when: - evomaintenance_hook_db | bool diff --git a/evomaintenance/tasks/minifirewall.yml b/evomaintenance/tasks/minifirewall.yml index ad48e856..98dad15b 100644 --- a/evomaintenance/tasks/minifirewall.yml +++ b/evomaintenance/tasks/minifirewall.yml @@ -1,5 +1,8 @@ --- +- set_fact: + minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}" + - name: Is minifirewall installed? stat: path: /etc/default/minifirewall diff --git a/filebeat/defaults/main.yml b/filebeat/defaults/main.yml index 598a08ed..deed1508 100644 --- a/filebeat/defaults/main.yml +++ b/filebeat/defaults/main.yml @@ -1,5 +1,5 @@ --- -elastic_stack_version: "6.x" +elastic_stack_version: "7.x" filebeat_logstash_plugin: False diff --git a/filebeat/handlers/main.yml b/filebeat/handlers/main.yml index 0a6d83f9..3ad08a63 100644 --- a/filebeat/handlers/main.yml +++ b/filebeat/handlers/main.yml @@ -4,3 +4,4 @@ systemd: name: filebeat state: restarted + when: not ansible_check_mode diff --git a/filebeat/tasks/main.yml b/filebeat/tasks/main.yml index c84c4db8..dd326cc8 100644 --- a/filebeat/tasks/main.yml +++ b/filebeat/tasks/main.yml @@ -62,6 +62,7 @@ name: filebeat enabled: yes notify: restart filebeat + when: not ansible_check_mode - name: is logstash-plugin available? stat: @@ -140,7 +141,9 @@ when: - filebeat_elasticsearch_auth_username | length > 0 - filebeat_elasticsearch_auth_password | length > 0 - when: not (filebeat_use_config_template | bool) + when: + - not (filebeat_use_config_template | bool) + - not ansible_check_mode - name: Filebeat api_key for Elasticsearch are configured lineinfile: diff --git a/generate-ldif/templates/generateldif.sh.j2 b/generate-ldif/templates/generateldif.sh.j2 index d5c19411..aa3ec7dd 100755 --- a/generate-ldif/templates/generateldif.sh.j2 +++ b/generate-ldif/templates/generateldif.sh.j2 @@ -31,17 +31,30 @@ computerKernel=$(uname -r) HardwareSerial=$(dmidecode -s system-serial-number | grep -v '^#') type="baremetal" -lscpu | grep -q KVM && type="kvm" +lscpu | grep "Hypervisor vendor:" | grep -q KVM && type="kvm" +lscpu | grep "Hypervisor vendor:" | grep -q VMware && type="vmware" lscpu | grep -q Oracle && type="virtualbox" if [ "$type" = "kvm" ]; then + ComputerType="VM" HardwareMark="KVM" HardwareModel="Virtual Machine" cpuMark=$(lscpu | grep Vendor | tr -s '\t' ' ' | cut -d' ' -f3) cpuModel="Virtual $(lscpu | grep "Model name" | tr -s '\t' ' ' | cut -d' ' -f3-), $(nproc) vCPU" cpuFreq="$(lscpu | grep "CPU MHz" | tr -s '\t' ' ' | cut -d' ' -f3-)MHz" + +elif [ "$type" = "vmware" ]; then + ComputerType="VM" + HardwareMark="VMWare" + HardwareModel="Virtual Machine" + + cpuMark=$(lscpu | grep Vendor | tr -s '\t' ' ' | cut -d' ' -f3) + cpuModel="Virtual $(lscpu | grep "Model name" | tr -s '\t' ' ' | cut -d' ' -f3-), $(nproc) vCPU" + cpuFreq="$(lscpu | grep "CPU MHz" | tr -s '\t' ' ' | cut -d' ' -f3-)MHz" + elif [ "$type" = "virtualbox" ]; then + ComputerType="VM" HardwareMark="VirtualBox" HardwareModel="Virtual Machine" @@ -49,6 +62,7 @@ elif [ "$type" = "virtualbox" ]; then cpuModel="Virtual $(lscpu | grep "Model name" | tr -s '\t' ' ' | cut -d' ' -f3-), $(nproc) vCPU" cpuFreq="$(lscpu | grep "CPU MHz" | tr -s '\t' ' ' | cut -d' ' -f3-)MHz" else + ComputerType="Baremetal" HardwareModel=$(dmidecode -s system-product-name | grep -v '^#') cpuMark=$(dmidecode -s processor-manufacturer | grep -v '^#' | head -1) @@ -115,6 +129,7 @@ NagiosEnabled: ${NagiosEnabled} NagiosComments: ${monitoringType},${monitoringMode},${monitoringTimeout} HardwareSerial: ${HardwareSerial} clientNumber: ${clientNumber} +ComputerType: ${ComputerType} EOT # CPU @@ -174,6 +189,19 @@ NagiosEnabled: TRUE EOT fi +# raid hardware +if [ -n "${raidModel}" ]; then + cat <> "${ldif_file}" + +dn: HardwareName=raid_card,${computer_dn} +objectClass: EvoHardware +HardwareName: raid_card +HardwareType: disk +HardwareModel: ${raidModel} +NagiosEnabled: TRUE +EOT +fi + # Swap swap=$(free -h | grep Swap: | tr -s ' ' | cut -d ' ' -f2) if [ -n "${swap}" ]; then @@ -570,10 +598,27 @@ objectClass: EvoService ServiceName: postgresql ipServicePort: 5432 ServiceType: database -ServiceVersion: PostgreSQL ${elasticsearch_version} +ServiceVersion: PostgreSQL ${postgresql_version} EOT fi +# mdadm +if is_pkg_installed mdadm; then + mdadm_version=$(get_pkg_version mdadm) +fi +if [ -n "${mdadm_version}" ]; then + cat <> "${ldif_file}" + +dn: ServiceName=mdadm,${computer_dn} +NagiosEnabled: TRUE +objectClass: EvoService +ServiceName: mdadm +ServiceType: raid +ServiceVersion: mdadm ${mdadm_version} +EOT +fi + + # test if we have a stdout if [ -t 1 ]; then echo "Output is in ${ldif_file}" diff --git a/java/tasks/oracle.yml b/java/tasks/oracle.yml index c2ab5ebf..0b057695 100644 --- a/java/tasks/oracle.yml +++ b/java/tasks/oracle.yml @@ -14,9 +14,9 @@ state: directory mode: "0777" loop: - - /srv/java-package - - /srv/java-package/src - - /srv/java-package/tmp + - /srv/java-package + - /srv/java-package/src + - /srv/java-package/tmp tags: - java diff --git a/kvm-host/files/add-vm.sh b/kvm-host/files/add-vm.sh index ec50763d..51b5c737 100755 --- a/kvm-host/files/add-vm.sh +++ b/kvm-host/files/add-vm.sh @@ -10,6 +10,8 @@ # Bash strict mode set -euo pipefail +VERSION="21.10" + isDryRun() { test "${doDryRun}" = "true" } diff --git a/kvm-host/files/kvmstats.sh b/kvm-host/files/kvmstats.sh index 5fa20ccb..0dcfb4e8 100755 --- a/kvm-host/files/kvmstats.sh +++ b/kvm-host/files/kvmstats.sh @@ -1,96 +1,202 @@ #!/bin/sh +VERSION="21.10" + +PROGNAME=$(basename "$0") + +show_version() { + cat <, + Alexis Ben Miloud--Josselin , + Jérémy Lecour + and others. + +${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 Licence for details. +END +} + +show_help() { + cat < + or ${PROGNAME} --units +END +} + error () { echo "$0": "$@" >&2 exit 1 } -usage () { - echo 'usage:' "$0" '[-a] [-u k|m|g] [-o human|html|csv]' >&2 - exit 1 +main() { + for VM in $(virsh list --name --all | sed '/^$/d' | sort) + do + echo "$VM" + + # cpu + virsh vcpucount --current "$VM" + + # mem + # libvirt stores memory in KiB, POW must be lowered by 1 + virsh dommemstat "$VM" 2>/dev/null | awk 'BEGIN{ret=1}$1~/^actual$/{print $2 / '$((POW / 1024))';ret=0}END{exit ret}' || + virsh dumpxml "$VM" | awk -F'[<>]' '$2~/^memory unit/{print $3/'$((POW / 1024))'}' + + # disk + for BLK in $(virsh domblklist "$VM" | sed '1,2d;/-$/d;/^$/d' | awk '{print $1}') + do + virsh domblkinfo "$VM" "$BLK" 2>/dev/null + done | awk '/Physical:/ { size += $2 } END { print int(size / '${POW}') }' + + # state + virsh domstate "$VM" | grep -q '^running$' && echo yes || echo no + done | xargs -n5 | { + echo vm vcpu ram disk running + awk '{ print } /yes$/ { vcpu += $2; ram += $3; disk += $4; running++ } END { print "TOTAL(running)", vcpu, ram, disk, running }' + test "$SHOW_AVAIL" && { + nproc + awk '/^MemTotal:/ { print int($2 / '$((POW / 1024))' ) }' /proc/meminfo + } | xargs -r printf 'AVAILABLE %s %s %s %s\n' + } | case "$FMT" in + 'human') + column -t + ;; + 'html') + awk 'BEGIN{print "\n"}{printf "";for(i=1;i<=NF;i++)printf "", $i;print ""}END{print "
%s
\n"}' + ;; + 'csv') + tr ' ' ',' + ;; + esac } +parse_units() { + case "$1" in + 'k') + POW="$(echo '1024 ^ 1' | bc)" + ;; + 'm') + POW="$(echo '1024 ^ 2' | bc)" + ;; + 'g') + POW="$(echo '1024 ^ 3' | bc)" + ;; + *) + printf 'ERROR: Unknown unit value: %s. Possible values: %s\n' "$1" "k, m, g" >&2 + echo "" >&2 + show_usage >&2 + exit 1 + ;; + esac +} +parse_output() { + case "$1" in + 'csv'|'html'|'human') + FMT="$1" + ;; + *) + printf 'ERROR: Unknown output value : %s. Possible values: %s\n' "$1" "csv, html, human" >&2 + echo "" >&2 + show_usage >&2 + exit 1 + ;; + esac +} + +# Check dependencies for DEP in bc virsh do command -v "$DEP" > /dev/null || error "$DEP" 'command not found' done +# default values POW="$(echo '1024 ^ 3' | bc)" FMT='human' -while [ "$#" -ne 0 ] -do - case "$1" in - '-a') - SHOW_AVAIL='y' - ;; - '-o') - case "$2" in - 'csv'|'html'|'human') - FMT="$2" + +# Parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case $1 in + -h|-\?|--help) + show_help + exit 0 + ;; + -V|--version) + show_version + exit 0 + ;; + -a|--all) + SHOW_AVAIL='y' + ;; + -u|--units) + # with value separated by space + if [ -n "$2" ]; then + parse_units "$2" + shift + else + printf 'ERROR: "-u|--units" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --units=?*) + # with value speparated by = + parse_units ${1#*=} + ;; + --units=) + # without value + printf 'ERROR: "--units" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + + -o|--output) + # with value separated by space + if [ -n "$2" ]; then + parse_output "$2" + shift + else + printf 'ERROR: "-o|--output" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --output=?*) + # with value speparated by = + parse_output ${1#*=} + ;; + --output=) + # without value + printf 'ERROR: "--output" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + + --) + # End of all options. + shift + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + printf 'ERROR: Unknown option : %s\n' "$1" >&2 + echo "" >&2 + show_usage >&2 + exit 1 ;; *) - usage + # Default case: If no more options then break out of the loop. + break ;; - esac - shift - ;; - '-u') - case "$2" in - 'k') - POW="$(echo '1024 ^ 1' | bc)" - ;; - 'm') - POW="$(echo '1024 ^ 2' | bc)" - ;; - 'g') - POW="$(echo '1024 ^ 3' | bc)" - ;; - *) - usage - esac - shift - ;; - *) - usage esac + shift done -for VM in $(virsh list --name --all) -do - echo "$VM" - - # cpu - virsh vcpucount --current "$VM" - - # mem - # libvirt stores memory in KiB, POW must be lowered by 1 - virsh dommemstat "$VM" 2>/dev/null | awk 'BEGIN{ret=1}$1~/^actual$/{print $2 / '$((POW / 1024))';ret=0}END{exit ret}' || - virsh dumpxml "$VM" | awk -F'[<>]' '$2~/^memory unit/{print $3/'$((POW / 1024))'}' - - # disk - for BLK in $(virsh domblklist "$VM" | sed '1,2d;/-$/d;/^$/d' | awk '{print $1}') - do - virsh domblkinfo "$VM" "$BLK" 2>/dev/null - done | awk '/Physical:/ { size += $2 } END { print int(size / '${POW}') }' - - # state - virsh domstate "$VM" | grep -q '^running$' && echo yes || echo no -done | xargs -n5 | { - echo vm vcpu ram disk running - awk '{ print } /yes$/ { vcpu += $2; ram += $3; disk += $4; running++ } END { print "TOTAL(running)", vcpu, ram, disk, running }' - test "$SHOW_AVAIL" && { - nproc - awk '/^MemTotal:/ { print int($2 / '$((POW / 1024))' ) }' /proc/meminfo - } | xargs -r printf 'AVAILABLE %s %s %s %s\n' -} | case "$FMT" in -'human') - column -t - ;; -'html') - awk 'BEGIN{print "\n"}{printf "";for(i=1;i<=NF;i++)printf "", $i;print ""}END{print "
%s
\n"}' - ;; -'csv') - tr ' ' ',' - ;; -esac +main diff --git a/kvm-host/tasks/main.yml b/kvm-host/tasks/main.yml index 95cb7090..a2f6953c 100644 --- a/kvm-host/tasks/main.yml +++ b/kvm-host/tasks/main.yml @@ -5,7 +5,7 @@ when: kvm_install_drbd ## TODO: check why it's disabled -#- include: ssh.yml +- include: ssh.yml - include: packages.yml diff --git a/kvm-host/tasks/ssh.yml b/kvm-host/tasks/ssh.yml index fe71c287..a36f7549 100644 --- a/kvm-host/tasks/ssh.yml +++ b/kvm-host/tasks/ssh.yml @@ -15,36 +15,34 @@ debug: msg: "{{ ssh_keys.stdout }}" -- name: Autorize other kvm ssh key - authorized_key: - user: root - state: present - key: "{{ item[0] }}" - delegate_to: "{{ item[1] }}" - loop: "{{ _keys | product(_servers) | list }}" - vars: - _keys: ssh_keys.stdout - _servers: groups['hypervisors'] - when: item[1] != inventory_hostname +#- name: Autorize other kvm ssh key +# authorized_key: +# user: root +# state: present +# key: "{{ item[0] }}" +# delegate_to: "{{ item[1] }}" +# loop: "{{ _keys | product(_servers) | list }}" +# vars: +# _keys: ssh_keys.stdout +# _servers: groups['hypervisors'] +# when: item[1] != inventory_hostname - name: Crontab for sync libvirt xml file cron: - name: "sync libvirt xml on {{ item }}" + name: "sync libvirt xml on {{ kvm_pair }}" state: present special_time: "hourly" user: root - job: "rsync -a --delete /etc/libvirt/qemu/ {{ hostvars[item]['ansible_hostname'] }}:/root/libvirt-{{ inventory_hostname }}/" - loop: - - "{{ groups['hypervisors'] }}" - when: item != inventory_hostname + job: "rsync -a --delete /etc/libvirt/qemu/*xml {{ hostvars[kvm_pair]['lan.ip'] }}:/root/libvirt-{{ inventory_hostname }}/" + when: kvm_pair != inventory_hostname + tags: crontab - name: Crontab for sync list of running vm cron: - name: "sync list of libvirt running vm on {{ item }}" + name: "sync list of libvirt running vm on {{ kvm_pair }}" state: present special_time: "daily" user: root - job: "virsh list --all | ssh {{ hostvars[item]['ansible_hostname'] }} 'cat >/root/libvirt-{{ inventory_hostname }}/virsh-list.txt'" - loop: - - "{{ groups['hypervisors'] }}" - when: item != inventory_hostname + job: "virsh list --all | tee /root/virsh-list.txt | ssh {{ hostvars[kvm_pair]['lan.ip'] }} 'cat >/root/libvirt-{{ inventory_hostname }}/virsh-list.txt'" + when: kvm_pair != inventory_hostname + tags: crontab diff --git a/listupgrade/files/old-kernel-autoremoval.sh b/listupgrade/files/old-kernel-autoremoval.sh index ceed0b99..ce1c6002 100644 --- a/listupgrade/files/old-kernel-autoremoval.sh +++ b/listupgrade/files/old-kernel-autoremoval.sh @@ -4,13 +4,36 @@ # fork by reg from /etc/kernel/postinst.d/apt-auto-removal script -VERSION="21.06.3" +VERSION="21.10" +readonly VERSION -set -e +PROGNAME=$(basename "$0") -# shellcheck disable=SC2046 -eval $(apt-config shell DPKG Dir::bin::dpkg/f) -DPKG="${DPKG:-/usr/bin/dpkg}" +show_version() { + cat <, + Gregory Colpart , + Romain Dessort , + Ludovic Poujol , + Jérémy Lecour + and others. + +${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 Licence for details. +END +} +show_help() { + cat <&2 + fi + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + +set -e + + +main "${@}" diff --git a/logstash/defaults/main.yml b/logstash/defaults/main.yml index 456769cd..7cc40e49 100644 --- a/logstash/defaults/main.yml +++ b/logstash/defaults/main.yml @@ -1,8 +1,10 @@ --- -elastic_stack_version: "6.x" +elastic_stack_version: "7.x" logstash_jvm_xms: 256m logstash_jvm_xmx: 512g logstash_log_rotate_days: 365 logstash_custom_tmpdir: Null logstash_default_tmpdir: /var/lib/logstash/tmp +logstash_log_syslog_enabled: True +logstash_config_force: True \ No newline at end of file diff --git a/logstash/handlers/main.yml b/logstash/handlers/main.yml new file mode 100644 index 00000000..d21d4de3 --- /dev/null +++ b/logstash/handlers/main.yml @@ -0,0 +1,10 @@ +--- + +- name: restart logstash + systemd: + name: logstash + state: restarted + daemon_reload: yes + +- name: reload systemd + command: systemctl daemon-reload \ No newline at end of file diff --git a/logstash/meta/main.yml b/logstash/meta/main.yml index b2ef1210..bad2b6a5 100644 --- a/logstash/meta/main.yml +++ b/logstash/meta/main.yml @@ -24,5 +24,4 @@ galaxy_info: # NOTE: A tag is limited to a single word comprised of # alphanumeric characters. Maximum 20 tags per role. -dependencies: - - { role: evolix/java, java_alternative: 'openjdk', java_version: 8 } +dependencies: [] diff --git a/logstash/tasks/logs.yml b/logstash/tasks/logs.yml index 975cd8bc..b09ebaf2 100644 --- a/logstash/tasks/logs.yml +++ b/logstash/tasks/logs.yml @@ -16,3 +16,26 @@ group: root mode: "0750" when: is_cron_installed.rc == 0 + +- name: "Create a system config directory for systemd overrides" + file: + path: /etc/systemd/system/logstash.service.d + state: directory + +- name: "disable syslog" + ini_file: + path: /etc/systemd/system/logstash.service.d/override.conf + section: Service + option: "{{ item.option }}" + value: "{{ item.value }}" + owner: root + group: root + mode: "0644" + create: yes + no_extra_spaces: yes + state: "{{ logstash_log_syslog_enabled | bool | ternary('absent','present') }}" + loop: + - { option: "StandardOutput", value: "null" } + - { option: "StandardError", value: "null" } + notify: + - restart logstash \ No newline at end of file diff --git a/logstash/tasks/main.yml b/logstash/tasks/main.yml index 73bdab1d..856ceba1 100644 --- a/logstash/tasks/main.yml +++ b/logstash/tasks/main.yml @@ -88,7 +88,7 @@ owner: logstash group: logstash mode: "0640" - force: yes + force: "{{ logstash_config_force | bool }}" loop: "{{ query('first_found', templates) }}" vars: templates: diff --git a/lxc-php/defaults/main.yml b/lxc-php/defaults/main.yml index ce8a935d..9eb226f1 100644 --- a/lxc-php/defaults/main.yml +++ b/lxc-php/defaults/main.yml @@ -19,3 +19,4 @@ lxc_php_container_releases: php70: "stretch" php73: "buster" php74: "bullseye" + php80: "bullseye" diff --git a/lxc-php/handlers/main.yml b/lxc-php/handlers/main.yml index 95882838..27ba8157 100644 --- a/lxc-php/handlers/main.yml +++ b/lxc-php/handlers/main.yml @@ -1,4 +1,9 @@ --- +- name: Reload php80-fpm + lxc_container: + name: "{{ lxc_php_version }}" + container_command: "systemctl reload php8.0-fpm" + - name: Reload php74-fpm lxc_container: name: "{{ lxc_php_version }}" diff --git a/lxc-php/tasks/mail_opensmtpd.yml b/lxc-php/tasks/mail_opensmtpd.yml index 1b4dbea0..25dec9ea 100644 --- a/lxc-php/tasks/mail_opensmtpd.yml +++ b/lxc-php/tasks/mail_opensmtpd.yml @@ -11,3 +11,13 @@ dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/smtpd.conf" mode: "0644" notify: "Restart opensmtpd" + when: lxc_php_container_releases[lxc_php_version] in ["jessie", "stretch", "buster"] + + +- name: "{{ lxc_php_version }} - Configure opensmtpd (in the container)" + template: + src: smtpd.conf.bullseye.j2 + dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/smtpd.conf" + mode: "0644" + notify: "Restart opensmtpd" + when: not lxc_php_container_releases[lxc_php_version] in ["jessie", "stretch", "buster"] diff --git a/lxc-php/tasks/main.yml b/lxc-php/tasks/main.yml index 25c0a978..0de38336 100644 --- a/lxc-php/tasks/main.yml +++ b/lxc-php/tasks/main.yml @@ -21,4 +21,7 @@ - include: "php74.yml" when: lxc_php_version == "php74" +- include: "php80.yml" + when: lxc_php_version == "php80" + - include: "misc.yml" diff --git a/lxc-php/tasks/php74.yml b/lxc-php/tasks/php74.yml index 9438dcc7..a3a7eb44 100644 --- a/lxc-php/tasks/php74.yml +++ b/lxc-php/tasks/php74.yml @@ -1,42 +1,5 @@ --- -- name: "{{ lxc_php_version }} - Install dependency packages" - lxc_container: - name: "{{ lxc_php_version }}" - container_command: "DEBIAN_FRONTEND=noninteractive apt install -y wget apt-transport-https gnupg" - -- name: "{{ lxc_php_version }} - Add sury repo" - lineinfile: - dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/sources.list.d/sury.list" - line: "{{ item }}" - state: present - create: yes - mode: "0644" - loop: - - "deb https://packages.sury.org/php/ bullseye main" - - "deb http://pub.evolix.net/ bullseye-php74/" - -- name: copy pub.evolix.net GPG key - copy: - src: reg.asc - dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/reg.asc - mode: "0644" - owner: root - group: root - -- name: copy packages.sury.org GPG Key - copy: - src: sury.gpg - dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/sury.gpg - mode: "0644" - owner: root - group: root - -- name: "{{ lxc_php_version }} - Update APT cache" - lxc_container: - name: "{{ lxc_php_version }}" - container_command: "DEBIAN_FRONTEND=noninteractive apt update" - - name: "{{ lxc_php_version }} - Install PHP packages" lxc_container: name: "{{ lxc_php_version }}" diff --git a/lxc-php/tasks/php80.yml b/lxc-php/tasks/php80.yml new file mode 100644 index 00000000..390f7edf --- /dev/null +++ b/lxc-php/tasks/php80.yml @@ -0,0 +1,57 @@ +--- + +- name: "{{ lxc_php_version }} - Install dependency packages" + lxc_container: + name: "{{ lxc_php_version }}" + container_command: "DEBIAN_FRONTEND=noninteractive apt install -y wget apt-transport-https gnupg" + +- name: "{{ lxc_php_version }} - Add sury repo" + lineinfile: + dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/sources.list.d/sury.list" + line: "{{ item }}" + state: present + create: yes + mode: "0644" + loop: + - "deb https://packages.sury.org/php/ bullseye main" + - "deb http://pub.evolix.net/ bullseye-php74/" + +- name: copy pub.evolix.net GPG key + copy: + src: reg.asc + dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/reg.asc + mode: "0644" + owner: root + group: root + +- name: copy packages.sury.org GPG Key + copy: + src: sury.gpg + dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/sury.gpg + mode: "0644" + owner: root + group: root + +- name: "{{ lxc_php_version }} - Update APT cache" + lxc_container: + name: "{{ lxc_php_version }}" + container_command: "DEBIAN_FRONTEND=noninteractive apt update" + +- name: "{{ lxc_php_version }} - Install PHP packages" + lxc_container: + name: "{{ lxc_php_version }}" + container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-zip composer libphp-phpmailer" + +- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration" + template: + src: z-evolinux-defaults.ini.j2 + dest: "{{ line_item }}" + mode: "0644" + notify: "Reload {{ lxc_php_version }}-fpm" + loop: + - "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/8.0/fpm/conf.d/z-evolinux-defaults.ini" + - "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/8.0/cli/conf.d/z-evolinux-defaults.ini" + loop_control: + loop_var: line_item + +- include: "mail_opensmtpd.yml" diff --git a/lxc-php/templates/mailname.j2 b/lxc-php/templates/mailname.j2 new file mode 100644 index 00000000..e374dd45 --- /dev/null +++ b/lxc-php/templates/mailname.j2 @@ -0,0 +1 @@ +{{ansible_fqdn}} diff --git a/lxc-php/templates/smtpd.conf.bullseye.j2 b/lxc-php/templates/smtpd.conf.bullseye.j2 new file mode 100644 index 00000000..0a6d658f --- /dev/null +++ b/lxc-php/templates/smtpd.conf.bullseye.j2 @@ -0,0 +1,17 @@ +# This is the smtpd server system-wide configuration file. +# See smtpd.conf(5) for more information. + +# To accept external mail, replace with: listen on all +#listen on localhost + +# If you edit the file, you have to run "smtpctl update table aliases" +table aliases file:/etc/aliases + +action "mbox" mbox alias +action "relay" relay host "smtp://127.0.0.1" + +# Uncomment the following to accept external mail for domain "example.org" +#match from any for domain "example.org" action "mbox" + +match for local action "mbox" +match for any action "relay" diff --git a/lxc-solr/tasks/main.yml b/lxc-solr/tasks/main.yml index 3fad863f..d629bbf6 100644 --- a/lxc-solr/tasks/main.yml +++ b/lxc-solr/tasks/main.yml @@ -8,9 +8,7 @@ path: "/var/lib/lxc/{{ item.name }}/rootfs" state: directory mode: '0755' - loop: - - "{{ lxc_containers }}" + loop: "{{ lxc_containers }}" - include: "solr.yml name={{item.name}} solr_version={{item.solr_version}} solr_port={{item.solr_port}}" - loop: - - "{{ lxc_containers }}" + loop: "{{ lxc_containers }}" diff --git a/lxc/tasks/main.yml b/lxc/tasks/main.yml index 74ba69ae..daf2885a 100644 --- a/lxc/tasks/main.yml +++ b/lxc/tasks/main.yml @@ -6,19 +6,19 @@ - debootstrap - xz-utils -- name: python-lxc is installed +- name: python-lxc is installed (Debian <= 10) apt: name: python-lxc state: present - when: ansible_distribution_major_version is version('10', '<=') + when: ansible_python_version is version('3', '<') -- name: python3-lxc is installed +- name: python3-lxc is installed (Debian >= 10) apt: name: python3-lxc state: present - when: ansible_distribution_major_version is version('10', '>') + when: ansible_python_version is version('3', '>=') -- name: Install additional packages on Buster +- name: Install additional packages (Debian >= 10) apt: name: - apparmor @@ -43,6 +43,12 @@ - lxc_unprivilegied_containers | bool - root_subuids.rc != 0 +- name: Check if /var has not mount options nodev or noexec + shell: findmnt | grep -E "/var[^/]" | grep -e nodev -e noexec + register: check_var + changed_when: false + failed_when: "check_var.rc == 0" + - name: Create containers include: create-container.yml vars: diff --git a/metricbeat/defaults/main.yml b/metricbeat/defaults/main.yml index 9529bef3..780a4ffd 100644 --- a/metricbeat/defaults/main.yml +++ b/metricbeat/defaults/main.yml @@ -1,5 +1,5 @@ --- -elastic_stack_version: "6.x" +elastic_stack_version: "7.x" metricbeat_elasticsearch_hosts: - "localhost:9200" diff --git a/mongodb/defaults/main.yml b/mongodb/defaults/main.yml index ec255349..c118f588 100644 --- a/mongodb/defaults/main.yml +++ b/mongodb/defaults/main.yml @@ -6,3 +6,5 @@ mongodb_bind: 127.0.0.1 # Warning: config must not be overwritten by default # otherwise it can disable important settings, like authorization :/ mongodb_force_config: False + +mongodb_version: 4.4 \ No newline at end of file diff --git a/mongodb/files/server-5.0.asc b/mongodb/files/server-5.0.asc new file mode 100644 index 00000000..44ea4919 --- /dev/null +++ b/mongodb/files/server-5.0.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGAsKNUBEAClMqPCvvqm6gFmbiorEN9qp00GI8oaECkwbxtGGbqX9sqMSrKe +AB3sGI7kqG2Fl0K+xmmiq1QDjhNgFDA1jjXq+Bd66RNPtvu747IRxVs+9fX7bk67 +8Bruha7U3M5l4193x5oYLlbcZL9aC7RSJE2mggTyS6LarmF6vKQN9LMXDicnageV +KCPpF2i3jkZaGnLPzAisW/pOjPQpWCbatTVqKOKvtOyP3Fz1spYd4obu6ELu1PXa +gmhSfvWJYt1irpchOl29LWZfcmXuJszmb00bqm4gLcK12VrnK191iXv46A8h2hSO +f3eQqrkc+pF/kw4RyG54EV7QtHXyTe9TVCbJUfgtliWIQt/bCoJYfPLHJaWIMs83 +bzA6ZvOjCKIfMS0CY5ZJyVaBfiI3wURSjgZIYFZAXVwbreQIfOKKuik7UVVn3xUO +nWpmQ2zyI0W7cJMquxwLNjkI+RckPhIqxWFo5iNSV4v6pzrlHD1WmIfFGBKEn7m+ +edwVyHG53fNIFZjxyShO6Pf1vgb9Js/XmXB4lxYnNyx1tB+hQhXTjLlY6N5gPpw5 +Z/PWQc7vfYekUZGQMXhTyRxU0QTwmdEeKcb+fb9r23OH59bbAfzE10xTMzhqCd2L +lgSozMBvMmkHb1xs1x6FFuv/U/X7LjHTrHIf4M//DNwdP4l4I1jhPlTAxwARAQAB +tDdNb25nb0RCIDUuMCBSZWxlYXNlIFNpZ25pbmcgS2V5IDxwYWNrYWdpbmdAbW9u +Z29kYi5jb20+iQI+BBMBAgAoBQJgLCjVAhsDBQkJZgGABgsJCAcDAgYVCAIJCgsE +FgIDAQIeAQIXgAAKCRCwCgvR4sY8EawdD/0ewkyx3yE99K9n3y7gdvh5+2U8BsqU +7SWEfup7kPpf+4pF5xWqMaciEV/wRAGt7TiKlfVyAv3Q9iNsaLFN+s3kMaIcKhwD +8+q/iGfziIuOSTeo20dAxn9vF6YqrKGc7TbHdXf9AtYuJCfIU5j02uVZiupx+P9+ +rG39dEnjOXm3uY0Fv3pRGCpuGubDlWB1DYh0R5O481kDVGoMqBxmc3iTALu14L/u +g+AKxFYfT4DmgdzPVMDhppgywfyd/IOWxoOCl4laEhVjUt5CygBa7w07qdKwWx2w +gTd9U0KGHxnnSmvQYxrRrS5RX3ILPJShivTSZG+rMqnUe6RgCwBrKHCRU1L728Yv +1B3ZFJLxB1TlVT2Hjr+oigp0RY9W1FCIdO2uhb9GImpaJ1Y0ZZqUkt/d9D8U2wcw +SW6/6WYeO7wAi/zlJ25hrBwhxS2+88gM6wJ1yL9yrM9v8JUb7Kq0rCGsEO5kqscV +AmX90wsF2cZ6gHR53eGIDbAJK0MO5RHR73aQ4bpTivPnoTx4HTj5fyhW9z8yCSOe +BlQABoFFqFvOS7KBxoyIS3pxlDetWOSc6yQrvA1CwxnkB81OHNmJfWAbNbEtZkLm +xs2c8CIh2R81yi6HUzAaxyDH7mrThbwX3hUe/wsaD1koV91G6bDD4Xx3zpa9DG/O +HyB98+e983gslg== +=IQQF +-----END PGP PUBLIC KEY BLOCK----- diff --git a/mongodb/tasks/main.yml b/mongodb/tasks/main.yml index 1d238b00..3054ccfe 100644 --- a/mongodb/tasks/main.yml +++ b/mongodb/tasks/main.yml @@ -1,10 +1,5 @@ --- -# - fail: -# msg: only compatible with Debian 8 -# when: -# - ansible_distribution != "Debian" or ansible_distribution_release != "jessie" - - include: main_jessie.yml when: ansible_distribution_release == "jessie" diff --git a/mongodb/tasks/main_bullseye.yml b/mongodb/tasks/main_bullseye.yml index d9e6e0eb..e31ffed3 100644 --- a/mongodb/tasks/main_bullseye.yml +++ b/mongodb/tasks/main_bullseye.yml @@ -1,9 +1,11 @@ --- -- name: Look for legacy apt keyring - stat: - path: /etc/apt/trusted.gpg - register: _trusted_gpg_keyring +- fail: + msg: Not compatible with Debian 11 (Bullseye) + when: + - ansible_distribution_release == "bullseye" + - mongodb_version is version_compare('5.0', '<=') + - name: MongoDB embedded GPG key is absent apt_key: @@ -14,8 +16,8 @@ - name: Add MongoDB GPG key copy: - src: server-4.4.asc - dest: /etc/apt/trusted.gpg.d/mongodb-server-4.4.asc + src: "server-{{mongodb_version}}.asc" + dest: "/etc/apt/trusted.gpg.d/mongodb-server-{{mongodb_version}}.asc" force: yes mode: "0644" owner: root @@ -23,9 +25,9 @@ - name: enable APT sources list apt_repository: - repo: deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main + repo: "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/{{mongodb_version}} main" state: present - filename: mongodb-org-4.4 + filename: "mongodb-org-{{mongodb_version}}" update_cache: yes - name: Install packages @@ -40,30 +42,39 @@ name: mongod enabled: yes state: started - when: _mongodb_install_package.changed + when: _mongodb_install_package is changed - name: install dependency for monitoring apt: - name: python3-pymongo + name: python-pymongo state: present - name: Custom configuration template: - src: mongodb_bullseye.conf.j2 + src: mongodb_buster.conf.j2 dest: "/etc/mongod.conf" force: "{{ mongodb_force_config | bool | ternary('yes', 'no') }}" notify: restart mongod - name: Configure logrotate template: - src: logrotate_bullseye.j2 + src: logrotate_buster.j2 dest: /etc/logrotate.d/mongodb force: yes backup: no -- name: Munin plugins local directory exists +- include_role: + name: evolix/remount-usr + +- name: Create plugin directory file: - dest: /usr/local/share/munin/plugins/ + name: /usr/local/share/munin/ + state: directory + mode: "0755" + +- name: Create plugin directory + file: + name: /usr/local/share/munin/plugins/ state: directory mode: "0755" @@ -72,7 +83,7 @@ src: "munin/{{ item }}" dest: '/usr/local/share/munin/plugins/{{ item }}' force: yes - with_items: + loop: - mongo_btree - mongo_collections - mongo_conn @@ -88,7 +99,7 @@ src: '/usr/local/share/munin/plugins/{{ item }}' dest: /etc/munin/plugins/{{ item }} state: link - with_items: + loop: - mongo_btree - mongo_collections - mongo_conn diff --git a/mongodb/tasks/main_buster.yml b/mongodb/tasks/main_buster.yml index fc7ac7ed..cf5ce2ae 100644 --- a/mongodb/tasks/main_buster.yml +++ b/mongodb/tasks/main_buster.yml @@ -14,8 +14,8 @@ - name: Add MongoDB GPG key copy: - src: server-4.2.asc - dest: /etc/apt/trusted.gpg.d/mongodb-server-4.2.asc + src: "server-{{mongodb_version}}.asc" + dest: "/etc/apt/trusted.gpg.d/mongodb-server-{{mongodb_version}}.asc" force: yes mode: "0644" owner: root @@ -23,9 +23,9 @@ - name: enable APT sources list apt_repository: - repo: deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.2 main + repo: "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/{{mongodb_version}} main" state: present - filename: mongodb-org-4.2 + filename: "mongodb-org-{{mongodb_version}}" update_cache: yes - name: Install packages @@ -61,6 +61,9 @@ force: yes backup: no +- include_role: + name: evolix/remount-usr + - name: Create plugin directory file: name: /usr/local/share/munin/ diff --git a/munin/handlers/main.yml b/munin/handlers/main.yml index a0d465a2..8654181d 100644 --- a/munin/handlers/main.yml +++ b/munin/handlers/main.yml @@ -1,4 +1,5 @@ --- + - name: restart munin-node service: name: munin-node @@ -8,3 +9,7 @@ service: name: munin_node state: restarted + +- name: systemd daemon-reload + systemd: + daemon_reload: yes \ No newline at end of file diff --git a/munin/tasks/main.yml b/munin/tasks/main.yml index aab79f62..81769488 100644 --- a/munin/tasks/main.yml +++ b/munin/tasks/main.yml @@ -88,3 +88,20 @@ [swap] user root when: ansible_kernel is search("-grs-") + +- name: Create override directory for munin-node unit + file: + name: /etc/systemd/system/munin-node.service.d/ + state: directory + mode: "0755" + +- name: Override is present for protected home + ini_file: + dest: "/etc/systemd/system/munin-node.service.d/override.conf" + section: "Service" + option: "ProtectHome" + value: "false" + state: present + notify: + - systemd daemon-reload + - restart munin-node diff --git a/mysql-oracle/tasks/users.yml b/mysql-oracle/tasks/users.yml index e5a7e3da..d0c444e5 100644 --- a/mysql-oracle/tasks/users.yml +++ b/mysql-oracle/tasks/users.yml @@ -1,22 +1,24 @@ --- -# dependency for mysql_user and mysql_db -- name: python-mysqldb is installed (Ansible dependency) +- name: Python2 dependencies for Ansible are installed apt: - name: python-mysqldb + name: + - python-mysqldb + - python-pymysql state: present - when: ansible_distribution_major_version is version('10', '<=') tags: - mysql + when: ansible_python_version is version('3', '<') -# dependency for mysql_user and mysql_db -- name: python3-mysqldb is installed (Ansible dependency) +- name: Python3 dependencies for Ansible are installed apt: - name: python3-mysqldb + name: + - python3-mysqldb + - python3-pymysql state: present - when: ansible_distribution_major_version is version('10', '>') tags: - mysql + when: ansible_python_version is version('3', '>=') - name: create a password for mysqladmin command: "apg -n 1 -m 16 -M lcN" diff --git a/mysql/files/evomariabackup.sh b/mysql/files/evomariabackup.sh new file mode 100644 index 00000000..0e3de84b --- /dev/null +++ b/mysql/files/evomariabackup.sh @@ -0,0 +1,554 @@ +#!/bin/sh + +VERSION="21.11" + +show_version() { + cat <, + Éric Morino , + Jérémy Lecour + and others. + +evomariabackup comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU Affero General Public License v3.0 for details. +END +} +show_help() { + cat <> "${log_file}" + else + log_line "${level}" "${msg}" >&2 + fi + fi +} +log_info() { + level="INFO" + msg=$1 + if ! is_quiet; then + if is_log_file; then + log_line "${level}" "${msg}" >> "${log_file}" + else + log_line "${level}" "${msg}" >&2 + fi + fi +} +log_warning() { + level="WARNING" + msg=$1 + if ! is_quiet; then + if is_log_file; then + log_line "${level}" "${msg}" >> "${log_file}" + else + log_line "${level}" "${msg}" >&2 + fi + fi +} +log_error() { + level="ERROR" + msg=$1 + if ! is_quiet; then + if is_log_file; then + log_line "${level}" "${msg}" >> "${log_file}" + if tty -s; then + printf "%s\n" "${msg}" >&2 + fi + else + log_line "${level}" "${msg}" >&2 + fi + fi +} +log_fatal() { + level="FATAL" + msg=$1 + if is_log_file; then + log_line "${level}" "${msg}" >> "${log_file}" + if tty -s; then + printf "%s\n" "${msg}" >&2 + fi + else + log_line "${level}" "${msg}" >&2 + fi +} +duration_in_seconds() { + if echo "${1}" | grep -E -q '^([0-9]+[wdhms])+$'; then + duration=$(echo "${1}" | sed 's/w/ * 604800 + /g; s/d/ * 86400 + /g; s/h/ * 3600 + /g; s/m/ * 60 + /g; s/s/ + /g; s/+ $//' | xargs expr) + elif echo "${1}" | grep -E -q '^[0-9]+$'; then + duration=$(echo "${1} * 3600" | xargs expr) + else + return 1 + fi + log_debug "Duration \`${1}' translated to ${duration} seconds" + + echo "${duration}" +} +lock_file_created_at() { + created_at=$(stat -c %Z "${lock_file}") + log_debug "Lock file ${lock_file} created at ${created_at}" + + echo "${created_at}" +} +lock_file_age() { + last_change=$(lock_file_created_at) + now=$(date +"%s") + + age=$(( now - last_change )) + log_debug "Lock file ${lock_file} is ${age} seconds old" + + echo "${created_at}" +} +is_lock_file_too_old() { + test "$(lock_file_age)" -ge "${max_age}" +} +kill_or_clean_lockfile() { + lock_file=${1:-} + + if [ -f "${lock_file}" ]; then + # Get Process ID from the lock file + pid=$(cat "${lock_file}") + if [ -n "${pid}" ]; then + log_debug "Found pid ${pid} in ${lock_file}" + + if kill -0 "${pid}" 2> /dev/null; then + log_debug "Found process with pid ${pid}" + + lock_file_created_at_human=$(date --date "@$(lock_file_created_at)" +"%Y-%m-%d %H:%M:%S") + if is_lock_file_too_old ; then + # Kill the children + pkill -9 --parent "${pid}" + # Kill the parent + kill -9 "${pid}" + # Only one process can run in parallel + log_warning "Process \`${pid}' (started at ${lock_file_created_at_human}) has been killed by \`$$'" + else + log_info "Process \`${pid}' (started at ${lock_file_created_at_human}) has precedence. Let's leave it work." + # make sure that this exit doesn't remove the existing lockfile !! + exit 0 + fi + else + log_warning "Process not found at PID \`${pid}'. Ignoring lock file \`${lock_file}'." + fi + else + log_warning "Empty lockfile \`${lock_file}'. It should contain a PID." + fi + # Remove the lock file + rm -f "${lock_file}" + log_debug "Lock file ${lock_file} has been removed" + fi +} +new_lock_file() { + lock_file=${1:-} + lock_dir=$(dirname "${lock_file}") + + if mkdir --parents "${lock_dir}"; then + echo $$ > "${lock_file}" + log_debug "Lock file '${lock_file}' has been created" + else + log_fatal "Failed to acquire lock file '${lock_file}'. Abort." + exit 1 + fi +} +is_mariabackup_directory() { + directory=${1:-} + find "${directory}" -name 'ibdata*' -o -name 'ib_logfile*' -o -name 'xtrabackup_*' > /dev/null +} +check_backup_dir() { + if [ -d "${backup_dir:?}" ]; then + if [ "$(ls -A "${backup_dir:?}")" ]; then + if is_mariabackup_directory "${backup_dir:?}"; then + log_debug "The backup directory ${backup_dir:?} is not empty but looks like a mariabackup target. Let's clear it." + rm -rf "${backup_dir:?}" + else + log_fatal "The backup directory ${backup_dir:?} is not empty and doesn't look like a mariabackup target. Please verify and clear the directory if you are sure." + exit 1 + fi + else + log_debug "The backup directory ${backup_dir:?} exists but is empty. Let's proceed." + fi + else + log_debug "The backup directory ${backup_dir:?} doesn't exist. Let's proceed." + fi + mkdir -p "${backup_dir:?}" +} +check_compress_dir() { + if [ -d "${compress_dir:?}" ]; then + log_debug "The compress_dir directory ${compress_dir:?} exists. Let's proceed." + else + log_debug "The compress_dir directory ${compress_dir:?} doesn't exist. Let's proceed." + fi + mkdir -p "${compress_dir:?}" +} + +backup() { + if [ -z "${backup_dir}" ]; then + log_fatal "backup-dir option is empty" + else + check_backup_dir + fi + + mariabackup_bin=$(command -v mariabackup) + if [ -z "${mariabackup_bin}" ]; then + log_fatal "Couldn't find mariabackup.\nUse 'apt install mariadb-backup'." + exit 1 + fi + + backup_command="${mariabackup_bin} --backup --slave-info --target-dir=${backup_dir:?}" + + + if ! is_quiet; then + log_debug "${backup_command}" + log_info "BEGIN mariabackup backup phase" + fi + + if is_quiet || ! is_verbose ; then + ${backup_command} >/dev/null 2>&1 + backup_rc=$? + elif ! is_quiet; then + if is_log_file; then + ${backup_command} >>"${log_file}" 2>&1 + backup_rc=$? + else + ${backup_command} + backup_rc=$? + fi + fi + + if [ ${backup_rc} -ne 0 ]; then + log_fatal "Error executing mariabackup --backup" + exit 1 + elif ! is_quiet; then + log_info "END mariabackup backup phase" + fi + + prepare_command="${mariabackup_bin} --prepare --target-dir=${backup_dir:?}" + + if ! is_quiet; then + log_debug "${prepare_command}" + log_info "BEGIN mariabackup prepare phase" + fi + + if is_quiet || ! is_verbose ; then + ${prepare_command} >/dev/null 2>&1 + prepare_rc=$? + elif ! is_quiet; then + if is_log_file; then + ${prepare_command} >>"${log_file}" 2>&1 + prepare_rc=$? + else + ${prepare_command} + prepare_rc=$? + fi + fi + + if [ ${prepare_rc} -ne 0 ]; then + log_fatal "Error executing mariabackup --prepare" + exit 1 + elif ! is_quiet; then + log_info "END mariabackup prepare phase" + fi +} +compress() { + compress_dir=$(dirname "${compress_file}") + + if [ -z "${backup_dir}" ]; then + log_fatal "backup-dir option is empty" + exit 1 + elif [ -e "${backup_dir}" ] && [ ! -d "${backup_dir}" ]; then + log_fatal "backup directory '${backup_dir}' exists but is not a directory" + exit 1 + fi + if [ -z "${compress_file}" ]; then + log_fatal "compress-file option is empty" + exit 1 + fi + if [ -n "${compress_dir}" ]; then + check_compress_dir + fi + + pigz_bin=$(command -v pigz) + gzip_bin=$(command -v gzip) + + if [ -n "${pigz_bin}" ]; then + compress_program="${pigz_bin} --keep -6" + elif [ -n "${gzip_bin}" ]; then + compress_program="${gzip_bin} -6" + else + log_fatal "Couldn't find pigz nor gzip.\nUse 'apt install pigz' or 'apt install gzip'." + exit 1 + fi + + if ! is_quiet; then + log_debug "Compression of ${backup_dir} to ${compress_file} using \`${compress_program}'" + log_info "BEGIN compression phase" + fi + if is_quiet || ! is_verbose ; then + tar --use-compress-program="${compress_program}" -cf "${compress_file}" "${backup_dir}" >/dev/null 2>&1 + tar_rc=$? + elif ! is_quiet; then + if is_log_file; then + tar --use-compress-program="${compress_program}" -cf "${compress_file}" "${backup_dir}" >>"${log_file}" 2>&1 + tar_rc=$? + else + tar --use-compress-program="${compress_program}" -cf "${compress_file}" "${backup_dir}" + tar_rc=$? + fi + fi + + if [ ${tar_rc} -ne 0 ]; then + log_fatal "An error occured while compressing ${backup_dir} to ${compress_file}" + exit 1 + elif ! is_quiet; then + log_info "END compression phase" + fi +} +main() { + kill_or_clean_lockfile "${lock_file}" + # shellcheck disable=SC2064 + trap "rm -f ${lock_file};" 0 + new_lock_file "${lock_file}" + + if [ "${do_backup}" = "1" ] && [ -n "${backup_dir}" ]; then + backup "${backup_dir}" + fi + + if [ "${do_compress}" = "1" ] && [ -n "${compress_file}" ]; then + compress "${backup_dir}" "${compress_file}" + fi +} + +# Declare variables + +lock_file="" +log_file="" +verbose="" +quiet="" +max_age="" +max_age="" +do_backup="" +backup_dir="" +do_compress="" +compress_file="" + +# Parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case $1 in + -h|-\?|--help) + show_help + exit 0 + ;; + -V|--version) + show_version + exit 0 + ;; + + -m|--max-age) + # with value separated by space + if [ -n "$2" ]; then + max_age=$(duration_in_seconds "$2") + shift + else + log_fatal 'ERROR: "-m|--max-age" requires a non-empty option argument.' + fi + ;; + --max-age=?*) + # with value speparated by = + max_age=$(duration_in_seconds "${1#*=}") + ;; + --max-age=) + # without value + log_fatal 'ERROR: "--max-age" requires a non-empty option argument.' + ;; + + --backup) + do_backup=1 + ;; + + --no-backup) + do_backup=0 + ;; + + --backup-dir) + # with value separated by space + if [ -n "$2" ]; then + backup_dir="$2" + shift + else + log_fatal 'ERROR: "--backup-dir" requires a non-empty option argument.' + fi + ;; + --backup-dir=?*) + # with value speparated by = + backup_dir=${1#*=} + ;; + --backup-dir=) + # without value + log_fatal '"--backup-dir" requires a non-empty option argument.' + ;; + + --compress) + do_compress=1 + ;; + + --no-compress) + do_compress=0 + ;; + + --compress-file) + # with value separated by space + if [ -n "$2" ]; then + compress_file="$2" + if [ -z "${do_compress}" ]; then + do_compress=1 + fi + shift + else + log_fatal '"--compress-file" requires a non-empty option argument.' + fi + ;; + --compress-file=?*) + # with value speparated by = + compress_file=${1#*=} + if [ -z "${do_compress}" ]; then + do_compress=1 + fi + ;; + --compress-file=) + # without value + log_fatal '"--compress-file" requires a non-empty option argument.' + ;; + + --lock-file) + # with value separated by space + if [ -n "$2" ]; then + lock_file="$2" + shift + else + log_fatal '"--lock-file" requires a non-empty option argument.' + fi + ;; + --lock-file=?*) + # with value speparated by = + lock_file=${1#*=} + ;; + --lock-file=) + # without value + log_fatal '"--lock-file" requires a non-empty option argument.' + ;; + + --log-file) + # with value separated by space + if [ -n "$2" ]; then + log_file="$2" + shift + else + log_fatal '"--log-file" requires a non-empty option argument.' + fi + ;; + --log-file=?*) + # with value speparated by = + log_file=${1#*=} + ;; + --log-file=) + # without value + log_fatal '"--log-file" requires a non-empty option argument.' + ;; + + -v|--verbose) + verbose=1 + ;; + + --quiet) + quiet=1 + verbose=0 + ;; + + --) + # End of all options. + shift + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + if tty -s; then + printf 'Unknown option : %s\n' "$1" >&2 + echo "" >&2 + show_usage >&2 + exit 1 + else + log_fatal 'Unknown option : %s\n' "$1" >&2 + fi + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + +# Default values + +lock_file="${lock_file:-/run/lock/evomariabackup.lock}" +verbose=${verbose:-0} +quiet=${quiet:-0} +max_age="${max_age:-86400}" +do_backup="${do_backup:-1}" +do_compress="${do_compress:-0}" + +main \ No newline at end of file diff --git a/mysql/files/mysql-queries-killer.sh b/mysql/files/mysql-queries-killer.sh new file mode 100644 index 00000000..3ea6e00b --- /dev/null +++ b/mysql/files/mysql-queries-killer.sh @@ -0,0 +1,197 @@ +#!/bin/sh + +VERSION="21.08" + +show_version() { + cat <, + Jérémy Lecour + and others. + +mysql-queries-killer comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. +END +} + +show_help() { + cat <] --list [--time