diff --git a/CHANGELOG.md b/CHANGELOG.md index 19a472e0..f305b5a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,50 +6,165 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project does not follow semantic versioning. The **major** part of the version is the year -The **minor** part changes is the month -The **patch** part changes is incremented if multiple releases happen the same month +The **minor** part is the month +The **patch** part is incremented if multiple releases happen the same month ## [Unreleased] ### Added -* Preliminary work for php83 +### Changed + +* autosysadmin-agent: upstream release 24.03.1 + +### Fixed + +* certbot: Fix HAProxy renewal hook +* keepalived: Fix tasks that use file instead of copy +* memcached: Fix conditions not properly writen (installation was always in multi-instance mode) + +### Removed + +### Security + +## [24.03] 2024-03-01 + +### Added + +* autosysadmin-agent: upstream release 24.03 +* autosysadmin-restart_nrpe: add role +* certbot: Renewal hook for NRPE +* kvm-host: add minifirewall rules if DRBD interface is configured +* proftpd: add whitelist ip + +### Changed + +* apt: add ftp.evolix.org as recognized system source +* autosysadmin-agent: logs clearing is done weekly +* autosysadmin-agent: rename /usr/share/scripts/autosysadmin/{auto,restart} +* certbot: use pkey to test the key +* evolinux-base: execute autosysadmin-agent and autosysadmin-restart_nrpe roles +* lxc-php, php: Update sury PGP key +* openvpn: earlier alert for CA expiration +* redis: create sysfs config file if missing +* nextcloud: use latest version by default + +### Removed + +* autosysadmin: replaced by autosysadmin-agent + +## [24.02.1] 2024-02-08 + +### Fixed + +* fail2ban: fix Ansible syntax + +## [24.02] 2024-02-08 + +### Added + +* Support for PHP 8.3 with bookworm LXC containers +* apt: add task file to install ELTS repository (default: False) +* autosysadmin: Add a role to automatically deploy autosysadmin on evolixisation +* check_free_space: added role +* etc-git: add /var/chroot-bind/etc/bind repo +* fail2ban: add script unban_ip +* generateldif: new Services for check_pressure_{cpu,io,mem} +* kvm-host: Automatically add an LVM filter when LVM is present +* lxc-php: Allow one to install php83 on Bookworm container +* minifirewall: Fix nagios check for old versions of minifirewall +* mongodb: add gpg key for 7.0 * nagios-nrpe: add check_sentinel for monitoring Redis Sentinel +* nagios-nrpe: new check_pressure_{cpu,io,mem} +* remount-usr: do not try to remount /usr RW if /usr is not a mounted partition +* vrrpd: configure minifirewall +* vrrpd: test if interface exists before deleting it +* webapps/evoadmin-mail: package installed via public.evolix.org/evolix repo starting with Bookworm +* webapps/nextcloud: Add condition for archive tasks +* webapps/nextcloud: Add condition for config tasks * webapps/nextcloud: Added var nextcloud_user_uid to enforce uid for nextcloud user +* webapps/nextcloud: Set ownership and permissions of data directory ### Changed * add-vm.sh: allow VM name max length > 20 +* amavis: make ldap_suffix mandatory * apache : fix goaway pattern for bad bots * apache : rename MaxRequestsPerChild to MaxConnectionsPerChild (new name) -* evocheck: upstream release 23.10 +* apache: use backward compatible Redirect directive +* apt: Disable archive repository for Debian 8 +* apt: Use the GPG version of the key for Debian 8-9 +* bind: Update role for Buster, Bullseye and Bookworm support +* dovecot: add variables for LDAP +* dovecot: Munin plugin conf path is now `/etc/munin/plugin-conf.d/zzz-dovecot` (instead of `z-evolinux-dovecot`) +* evocheck: upstream release 24.01 * evolinux-base: dump-server-state upstream release 23.11 * evolinux-base: use separate default config file for rsyslog +* kvmstats: use .capacity instead of .physical for disk size +* ldap: make ldap_suffix mandatory +* listupgrade : old-kernel-removal.sh upstream release 24.01 * log2mail: move custom config in separate file +* lxc: init /etc git repository in lxc container +* mysql: disable performance schema for Debian 8 +* nagios: add dockerd check in nrpe check template +* nagios: cleaning nrpe check template * nagios: rename var `nagios_nrpe_process_processes` into `nagios_nrpe_processes` and check systemd-timesyncd instead of ntpd in Debian 12 +* nagios: add option --full to check pressure IO and mem to avoid flaps * proftpd: in SFTP vhost, enable SSH keys login, enable ed25549 host key for Debian >= 11 +* redis: manage config template inside a block, to allow custom modifications outside +* spamassassin: Use spamd starting with Bookworm +* squid: config directory seems to have changed from /etc/squid3 to /etc/squid in Debian 8 +* unbound: Add config file to allow configuration reload on Debian 11 and lower +* unbound: Add munin configuration & setup plugin +* unbound: Big cleanup +* unbound: Move generated config file to `/etc/unbound/unbound.conf.d/evolinux.conf` +* unbound: Use root hints provided by debian package dns-root-data instead of downloading them +* vrrpd: replace switch script with custom one (fix MAC issue, use `ip(8)`, shell cleanup…) +* vrrpd: variable to force update the switch script (default: false) +* webapps/nextcloud: Add Ceph volume to fstab +* webapps/nextcloud: Set home directory's mode ### Fixed -* nginx: keep indentation -* evoadmin-web: Fix PHP version for Bookworm * Add php-fpm82 to LDAP when relevant -* nagios: fix default file to monitor for check_clamav_db -* php: Bullseye/Sury > Honor the php_version asked in the pub.evolix.org repository -* webapps/nextcloud: fix missing gid -* webapps/nextcloud: fix misplaced gid attribute -* webapps/nextcloud: added check that nexctcloud uid is over 3000 -* ProFTPd: set missing default listen IP for SFTP +* Check stat.exists before stat.isdir * apache: fix MaxRequestsPerChild value to be sync with wiki.e.o +* apt: use archive.debian.org with Stretch +* certbot: fix hook for dovecot when more than one certificate is used (eg. different certificates for POP3 and IMAP) +* dovecot: add missing LDAP conf iterate_filter to exclude disabled accounts in users list (caused « User no longer exists » errors in commands listing users like « doveadm user -u '*' » or « doveadm expunge -u "*" mailbox INBOX savedbefore 7d »). +* dovecot: fix missing default mails +* dovecot: fix plugin dovecot1 +* evoadmin-web: Fix PHP version for Bookworm +* evolinux-base: fix hardware.yml (wrong repo, missing update cache) +* evolinux-base: start to install linux-image-cloud-amd64 with Buster +* fail2ban: fix template marker +* minifirewall: ports 25, 53, 443, 993, 995 not opened publicly by default anymore, ports 20, 21, 110, 143 not opened semi-publicly by default anymore. +* nagios: fix default file to monitor for check_clamav_db +* nginx: add "when: not ansible_check_mode" in various tasks to prevent fail in check mode +* nginx: fix mistake between "check_mode: no" and "when: not ansible_check_mode" (fail in check mode) +* nginx: fix mistake between "check_mode: no" and "when: not ansible_check_mode" (fail in check mode) +* nginx: keep indentation +* nginx: take care of « already defined » and « not yet defined » server status suffix in check mode +* php: Bullseye/Sury > Honor the php_version asked in the pub.evolix.org repository +* php: drop apt_preferences(5) file for sury +* postfix: remove dependency on evolinux_fqdn var +* proftpd: set missing default listen IP for SFTP +* roundcube: set default SMTP port to 25 instead of 587, which failed because of missing SSL conf (local connexion does not need SSL) * ssl: no not execute haproxy tasks and reload if haproxy is disabled +* unbound: Add a apt cache validity to enforce an apt update if needed +* webapps/nextcloud: added check that nextcloud uid is over 3000 +* webapps/nextcloud: fix Add Ceph volume to fstab : missing UUID= in src +* webapps/nextcloud: fix misplaced gid attribute +* webapps/nextcloud: fix missing gid +* webapps/roundcube & evoadminmail: make roles more idempotent (were failing when played twice) +* amavis: Add variables for generate "ldap_suffix" +* proftpd: fix error when no SSH key is provided ### Removed * evolinux-base: no need to remove update-evobackup-canary from sbin anymore * evolinux-base: no need to symlink backup-server-state to dump-server-state anymore -### Security - ## [23.10] 2023-10-14 ### Added @@ -416,7 +531,6 @@ The **patch** part changes is incremented if multiple releases happen the same m ### Changed * evocheck: Upstream release 22.05 -* bind: Update role for Buster, Bullseye and Bookworm support ### Removed diff --git a/amavis/defaults/main.yml b/amavis/defaults/main.yml new file mode 100644 index 00000000..c353a3ba --- /dev/null +++ b/amavis/defaults/main.yml @@ -0,0 +1,5 @@ +--- + +ldap_hostname: "{{ ansible_hostname }}" +ldap_domain: "{{ ansible_domain }}" +ldap_suffix: "dc={{ ldap_hostname }},dc={{ ldap_domain.split('.')[-2] }},dc={{ ldap_domain.split('.')[-1] }}" diff --git a/amavis/tasks/main.yml b/amavis/tasks/main.yml index da46721e..e9f67b4d 100644 --- a/amavis/tasks/main.yml +++ b/amavis/tasks/main.yml @@ -6,7 +6,7 @@ - amavisd-new state: present tags: - - amavis + - amavis - name: configure Amavis ansible.builtin.template: @@ -15,7 +15,7 @@ mode: "0644" notify: restart amavis tags: - - amavis + - amavis - name: Install purge custom cron ansible.builtin.copy: @@ -23,5 +23,5 @@ dest: /etc/cron.daily/amavis_purge_virusmails mode: "0755" tags: - - amavis - - amavis_purge_cron + - amavis + - amavis_purge_cron diff --git a/amavis/templates/amavis.conf.j2 b/amavis/templates/amavis.conf.j2 index cbe597a2..8bc9bae8 100644 --- a/amavis/templates/amavis.conf.j2 +++ b/amavis/templates/amavis.conf.j2 @@ -44,7 +44,7 @@ $max_servers = 2; $enable_ldap = 1; $default_ldap = { hostname => '127.0.0.1', tls => 0, - base => '{{ ldap_suffix }}', scope => 'sub', + base => '{{ ldap_suffix | mandatory }}', scope => 'sub', query_filter => '(&(mailacceptinggeneralid=%m)(isActive=TRUE))' }; diff --git a/apache/files/evolinux-defaults.conf b/apache/files/evolinux-defaults.conf index 73b7f136..c05f77f2 100644 --- a/apache/files/evolinux-defaults.conf +++ b/apache/files/evolinux-defaults.conf @@ -48,17 +48,17 @@ MaxKeepAliveRequests 10 # 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 + Redirect 404 "-" # File names starting with - Redirect 404 + Redirect 404 "-" # File names ending with - Redirect 404 + Redirect 404 "-" diff --git a/apache/tasks/ip_whitelist.yml b/apache/tasks/ip_whitelist.yml index 5060f56e..bb7e8f46 100644 --- a/apache/tasks/ip_whitelist.yml +++ b/apache/tasks/ip_whitelist.yml @@ -5,6 +5,7 @@ dest: /etc/apache2/ipaddr_whitelist.conf line: "Require ip {{ item }}" state: present + create: yes loop: "{{ apache_ipaddr_whitelist_present }}" notify: reload apache tags: diff --git a/apt/defaults/main.yml b/apt/defaults/main.yml index 3720d893..772a8fb9 100644 --- a/apt/defaults/main.yml +++ b/apt/defaults/main.yml @@ -14,6 +14,7 @@ apt_install_backports: False apt_backports_components: "main" apt_install_evolix_public: True +apt_install_extended_lts: False apt_clean_gandi_sourceslist: False @@ -28,4 +29,4 @@ apt_check_hold_cron_weekday: "*" apt_check_hold_cron_day: "*" apt_check_hold_cron_month: "*" -apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}" \ No newline at end of file +apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}" diff --git a/apt/files/bookworm_backports_preferences b/apt/files/bookworm_backports_preferences deleted file mode 100644 index eaf76d52..00000000 --- a/apt/files/bookworm_backports_preferences +++ /dev/null @@ -1,3 +0,0 @@ -Package: * -Pin: release a=bookworm-backports -Pin-Priority: 50 diff --git a/apt/files/bullseye_backports_preferences b/apt/files/bullseye_backports_preferences deleted file mode 100644 index 3a667c93..00000000 --- a/apt/files/bullseye_backports_preferences +++ /dev/null @@ -1,3 +0,0 @@ -Package: * -Pin: release a=bullseye-backports -Pin-Priority: 50 diff --git a/apt/files/buster_backports_preferences b/apt/files/buster_backports_preferences deleted file mode 100644 index 30fef48d..00000000 --- a/apt/files/buster_backports_preferences +++ /dev/null @@ -1,3 +0,0 @@ -Package: * -Pin: release a=buster-backports -Pin-Priority: 50 diff --git a/apt/files/deb822-migration.py b/apt/files/deb822-migration.py index f8693b28..cb135972 100755 --- a/apt/files/deb822-migration.py +++ b/apt/files/deb822-migration.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 +########## +# This script takes a multi-lines input of "oneliner-style" APT sources definitions. +# It converts them into "deb822-style" sources. +# Each generated file will have only one stanza, possibly with multiple Types/Suites/Components +########## + import re import sys import os @@ -10,11 +16,16 @@ import apt_pkg # Order matters ! destinations = { "debian-security": "security.sources", + ".*-backports": "backports.sources", + ".debian.org": "system.sources", "mirror.evolix.org": "system.sources", + "ftp.evolix.org": "system.sources", + "pub.evolix.net": "evolix_public_old.sources.bak", "pub.evolix.org": "evolix_public.sources", + "artifacts.elastic.co": "elastic.sources", "download.docker.com": "docker.sources", "downloads.linux.hpe.com": "hp.sources", @@ -76,6 +87,11 @@ def prepare_sources(lines): key, value = option.split("=") options[key] = value + ### WARNING ### + # if there are multiple lines with different URIS for a given destination (eg. "system") + # each one will overwrite the previous one + # and the last evaluated will be what remains. + if dest in sources: sources[dest]["Types"].add(matches["type"]) sources[dest]["URIs"] = matches["uri"] diff --git a/apt/files/deb822-migration.sh b/apt/files/deb822-migration.sh index 10fb7889..7a4fb787 100755 --- a/apt/files/deb822-migration.sh +++ b/apt/files/deb822-migration.sh @@ -1,5 +1,11 @@ #!/bin/sh +########## +# This script changes all "one-line" APT sources into "deb822" sources. +# It is responsible for searching and processing the files. +# The actual format migration is done by a python script. +########## + deb822_migrate_script=$(command -v deb822-migration.py) if [ -z "${deb822_migrate_script}" ]; then @@ -46,4 +52,4 @@ for file in $(find /etc/apt/sources.list.d -mindepth 1 -maxdepth 1 -type f -name done echo "${count} file(s) migrated" -exit ${rc} \ No newline at end of file +exit ${rc} diff --git a/apt/files/freexian-archive-extended-lts.gpg b/apt/files/freexian-archive-extended-lts.gpg new file mode 100644 index 00000000..819c10ff Binary files /dev/null and b/apt/files/freexian-archive-extended-lts.gpg differ diff --git a/apt/files/jessie_backports_preferences b/apt/files/jessie_backports_preferences deleted file mode 100644 index dd3cef12..00000000 --- a/apt/files/jessie_backports_preferences +++ /dev/null @@ -1,3 +0,0 @@ -Package: * -Pin: release a=jessie-backports -Pin-Priority: 50 diff --git a/apt/files/stretch_backports_preferences b/apt/files/stretch_backports_preferences deleted file mode 100644 index 6d5d03be..00000000 --- a/apt/files/stretch_backports_preferences +++ /dev/null @@ -1,3 +0,0 @@ -Package: * -Pin: release a=stretch-backports -Pin-Priority: 50 diff --git a/apt/tasks/backports.deb822.yml b/apt/tasks/backports.deb822.yml index 0382892d..db117d94 100644 --- a/apt/tasks/backports.deb822.yml +++ b/apt/tasks/backports.deb822.yml @@ -10,19 +10,9 @@ tags: - apt -- name: Backports configuration - ansible.builtin.copy: - src: '{{ ansible_distribution_release }}_backports_preferences' - dest: /etc/apt/preferences.d/0-backports-defaults - force: true - mode: "0640" - register: apt_backports_config - tags: - - apt - - name: Apt update ansible.builtin.apt: update_cache: yes - when: apt_backports_sources is changed or apt_backports_config is changed + when: apt_backports_sources is changed tags: - apt diff --git a/apt/tasks/backports.oneline.yml b/apt/tasks/backports.oneline.yml index 11de5c52..1630de19 100644 --- a/apt/tasks/backports.oneline.yml +++ b/apt/tasks/backports.oneline.yml @@ -17,16 +17,6 @@ tags: - apt -- name: Backports configuration - ansible.builtin.copy: - src: '{{ ansible_distribution_release }}_backports_preferences' - dest: /etc/apt/preferences.d/0-backports-defaults - force: true - mode: "0640" - register: apt_backports_config - tags: - - apt - - name: Archived backport are accepted (jessie) ansible.builtin.lineinfile: dest: '/etc/apt/apt.conf.d/99no-check-valid-until' @@ -42,4 +32,4 @@ update_cache: yes tags: - apt - when: apt_backports_list is changed or apt_backports_config is changed + when: apt_backports_list is changed diff --git a/apt/tasks/evolix_public.deb822.yml b/apt/tasks/evolix_public.deb822.yml index 0a91dddf..0e6639c3 100644 --- a/apt/tasks/evolix_public.deb822.yml +++ b/apt/tasks/evolix_public.deb822.yml @@ -24,10 +24,16 @@ owner: root group: root +- name: Set Evolix GPG key format to ASC + set_fact: + apt_evolix_public_key: "{{ apt_keyring_dir }}/pub_evolix.asc" + tags: + - apt + - name: Add Evolix GPG key ansible.builtin.copy: src: pub_evolix.asc - dest: "{{ apt_keyring_dir }}/pub_evolix.asc" + dest: "{{ apt_evolix_public_key }}" force: true mode: "0644" owner: root diff --git a/apt/tasks/evolix_public.oneline.yml b/apt/tasks/evolix_public.oneline.yml index 9501e595..165a7b93 100644 --- a/apt/tasks/evolix_public.oneline.yml +++ b/apt/tasks/evolix_public.oneline.yml @@ -24,10 +24,26 @@ owner: root group: root +- name: Set Evolix GPG key format to GPG (Debian < 9) + set_fact: + apt_evolix_public_key: "pub_evolix.gpg" + when: + - ansible_distribution_major_version is version('9', '<') + tags: + - apt + +- name: Set Evolix GPG key format to ASC (Debian >= 9) + set_fact: + apt_evolix_public_key: "pub_evolix.asc" + when: + - ansible_distribution_major_version is version('9', '>=') + tags: + - apt + - name: Add Evolix GPG key ansible.builtin.copy: - src: pub_evolix.asc - dest: "{{ apt_keyring_dir }}/pub_evolix.asc" + src: "{{ apt_evolix_public_key }}" + dest: "{{ apt_keyring_dir }}/{{ apt_evolix_public_key }}" force: true mode: "0644" owner: root diff --git a/apt/tasks/extended-lts.oneline.yml b/apt/tasks/extended-lts.oneline.yml new file mode 100644 index 00000000..09974684 --- /dev/null +++ b/apt/tasks/extended-lts.oneline.yml @@ -0,0 +1,37 @@ +--- + +- name: "Ensure {{ apt_keyring_dir }} directory exists" + file: + path: "{{ apt_keyring_dir }}" + state: directory + mode: "755" + owner: root + group: root + +- name: Add Evolix GPG key + ansible.builtin.copy: + src: "freexian-archive-extended-lts.gpg" + dest: "{{ apt_keyring_dir }}/freexian-archive-extended-lts.gpg" + force: true + mode: "0644" + owner: root + group: root + tags: + - apt + +- name: ELTS list is installed + ansible.builtin.template: + src: "{{ ansible_distribution_release }}_extended-lts.list.j2" + dest: /etc/apt/sources.list.d/extended-lts.list + force: true + mode: "0640" + register: apt_extended_lts + tags: + - apt + +- name: Apt update + ansible.builtin.apt: + update_cache: yes + tags: + - apt + when: apt_extended_lts is changed diff --git a/apt/tasks/main.yml b/apt/tasks/main.yml index 4d357f8b..bc65d7b9 100644 --- a/apt/tasks/main.yml +++ b/apt/tasks/main.yml @@ -80,6 +80,14 @@ - apt_install_evolix_public | bool - ansible_distribution_major_version is version('12', '>=') +- name: Install Extended-LTS repositories (Debian < 10) + ansible.builtin.import_tasks: extended-lts.oneline.yml + tags: + - apt + when: + - apt_install_extended_lts | bool + - ansible_distribution_major_version is version('10', '<') + - name: Clean GANDI sources ansible.builtin.file: path: '{{ item }}' @@ -126,4 +134,4 @@ upgrade: dist when: apt_upgrade | bool tags: - - apt \ No newline at end of file + - apt diff --git a/apt/tasks/migrate-to-deb822.yml b/apt/tasks/migrate-to-deb822.yml index e7339e8b..e79ddf82 100644 --- a/apt/tasks/migrate-to-deb822.yml +++ b/apt/tasks/migrate-to-deb822.yml @@ -31,6 +31,11 @@ tags: - apt +- name: Is system.sources present? + ansible.builtin.stat: + path: /etc/apt/sources.list.d/system.sources + register: _system_sources + - name: Add signed-by when relevant for bookworm ansible.builtin.lineinfile: dest: /etc/apt/sources.list.d/system.sources @@ -39,6 +44,12 @@ state: present tags: - apt + when: _system_sources.stat.exists or not ansible_check_mode + +- name: Is security.sources present? + ansible.builtin.stat: + path: /etc/apt/sources.list.d/security.sources + register: _security_sources - name: Add signed-by when relevant for bookworm-security ansible.builtin.lineinfile: @@ -48,3 +59,4 @@ state: present tags: - apt + when: _security_sources.stat.exists or not ansible_check_mode diff --git a/apt/templates/bookworm_basics.sources.j2 b/apt/templates/bookworm_basics.sources.j2 index 948c4adf..747e2e53 100644 --- a/apt/templates/bookworm_basics.sources.j2 +++ b/apt/templates/bookworm_basics.sources.j2 @@ -3,6 +3,6 @@ Types: deb URIs: http://mirror.evolix.org/debian Suites: bookworm bookworm-updates -Components: {{ apt_basics_components | mandatory }} +Components: {{ apt_basics_components | mandatory }} Enabled: yes Signed-By: /usr/share/keyrings/debian-archive-bookworm-automatic.gpg diff --git a/apt/templates/bookworm_security.sources.j2 b/apt/templates/bookworm_security.sources.j2 index 07f1345b..b70fcec1 100644 --- a/apt/templates/bookworm_security.sources.j2 +++ b/apt/templates/bookworm_security.sources.j2 @@ -3,6 +3,6 @@ Types: deb URIs: https://security.debian.org/debian-security Suites: bookworm-security -Components: {{ apt_basics_components | mandatory }} +Components: {{ apt_basics_components | mandatory }} Enabled: yes -Signed-By: /usr/share/keyrings/debian-archive-bookworm-security-automatic.gpg \ No newline at end of file +Signed-By: /usr/share/keyrings/debian-archive-bookworm-security-automatic.gpg diff --git a/apt/templates/bullseye_basics.list.j2 b/apt/templates/bullseye_basics.list.j2 index 55f32b8d..d1820d0f 100644 --- a/apt/templates/bullseye_basics.list.j2 +++ b/apt/templates/bullseye_basics.list.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} deb http://mirror.evolix.org/debian bullseye {{ apt_basics_components | mandatory }} -deb http://mirror.evolix.org/debian/ bullseye-updates {{ apt_basics_components | mandatory }} +deb http://mirror.evolix.org/debian bullseye-updates {{ apt_basics_components | mandatory }} deb http://security.debian.org/debian-security bullseye-security {{ apt_basics_components | mandatory }} diff --git a/apt/templates/buster_basics.list.j2 b/apt/templates/buster_basics.list.j2 index 58209ba0..b392ce58 100644 --- a/apt/templates/buster_basics.list.j2 +++ b/apt/templates/buster_basics.list.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} deb http://mirror.evolix.org/debian buster {{ apt_basics_components | mandatory }} -deb http://mirror.evolix.org/debian/ buster-updates {{ apt_basics_components | mandatory }} +deb http://mirror.evolix.org/debian buster-updates {{ apt_basics_components | mandatory }} deb http://security.debian.org/debian-security buster/updates {{ apt_basics_components | mandatory }} diff --git a/apt/templates/evolix_public.list.j2 b/apt/templates/evolix_public.list.j2 index e00899e7..7ed18708 100644 --- a/apt/templates/evolix_public.list.j2 +++ b/apt/templates/evolix_public.list.j2 @@ -1,3 +1,3 @@ # {{ ansible_managed }} -deb [signed-by={{ apt_keyring_dir }}/pub_evolix.asc] http://pub.evolix.org/evolix {{ ansible_distribution_release }} main +deb [signed-by={{ apt_keyring_dir }}/{{ apt_evolix_public_key }}] http://pub.evolix.org/evolix {{ ansible_distribution_release }} main diff --git a/apt/templates/evolix_public.sources.j2 b/apt/templates/evolix_public.sources.j2 index defd1282..76bea737 100644 --- a/apt/templates/evolix_public.sources.j2 +++ b/apt/templates/evolix_public.sources.j2 @@ -1,6 +1,6 @@ # {{ ansible_managed }} -Types:deb +Types: deb URIs: http://pub.evolix.org/evolix Suites: {{ ansible_distribution_release }} Components: main diff --git a/apt/templates/jessie_basics.list.j2 b/apt/templates/jessie_basics.list.j2 index 467e7f30..7d72bfbd 100644 --- a/apt/templates/jessie_basics.list.j2 +++ b/apt/templates/jessie_basics.list.j2 @@ -1,4 +1,5 @@ # {{ ansible_managed }} -deb http://mirror.evolix.org/debian/ jessie {{ apt_basics_components | mandatory }} -deb http://security.debian.org/ jessie/updates {{ apt_basics_components | mandatory }} +### Those repositories are unusable. Move to ELTS (manually). +# deb http://archive.debian.org/debian jessie {{ apt_basics_components | mandatory }} +# deb http://archive.debian.org/debian-security jessie/updates {{ apt_basics_components | mandatory }} diff --git a/apt/templates/jessie_extended-lts.list.j2 b/apt/templates/jessie_extended-lts.list.j2 new file mode 100644 index 00000000..c20be4e7 --- /dev/null +++ b/apt/templates/jessie_extended-lts.list.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} + +deb [signed-by="{{ apt_keyring_dir }}/freexian-archive-extended-lts.gpg"] http://elts.evolix.org/extended-lts jessie main +deb [signed-by="{{ apt_keyring_dir }}/freexian-archive-extended-lts.gpg"] http://elts.evolix.org/extended-lts jessie-lts main diff --git a/apt/templates/stretch_basics.list.j2 b/apt/templates/stretch_basics.list.j2 index 2f0bf99e..f679e354 100644 --- a/apt/templates/stretch_basics.list.j2 +++ b/apt/templates/stretch_basics.list.j2 @@ -1,5 +1,4 @@ # {{ ansible_managed }} -deb http://mirror.evolix.org/debian stretch {{ apt_basics_components | mandatory }} -deb http://mirror.evolix.org/debian/ stretch-updates {{ apt_basics_components | mandatory }} -deb http://security.debian.org/debian-security stretch/updates {{ apt_basics_components | mandatory }} +deb http://archive.debian.org/debian stretch {{ apt_basics_components | mandatory }} +deb http://archive.debian.org/debian-security stretch/updates {{ apt_basics_components | mandatory }} diff --git a/apt/templates/stretch_extended-lts.list.j2 b/apt/templates/stretch_extended-lts.list.j2 new file mode 100644 index 00000000..374e571e --- /dev/null +++ b/apt/templates/stretch_extended-lts.list.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} + +deb [signed-by="{{ apt_keyring_dir }}/freexian-archive-extended-lts.gpg"] http://elts.evolix.org/extended-lts stretch main +deb [signed-by="{{ apt_keyring_dir }}/freexian-archive-extended-lts.gpg"] http://elts.evolix.org/extended-lts stretch-lts main diff --git a/autosysadmin-agent/defaults/main.yml b/autosysadmin-agent/defaults/main.yml new file mode 100644 index 00000000..b223a683 --- /dev/null +++ b/autosysadmin-agent/defaults/main.yml @@ -0,0 +1,17 @@ +--- + +general_scripts_dir: "/usr/share/scripts" + +autosysadmin_agent_bin_dir: "/usr/local/bin/autosysadmin" +autosysadmin_agent_lib_dir: "/usr/local/lib/autosysadmin" +autosysadmin_agent_auto_dir: "{{ general_scripts_dir }}/autosysadmin/restart" + +autosysadmin_agent_crontab_enabled: true +autosysadmin_agent_log_retention_days: 365 + +autosysadmin_config: [] + ### All repair are disabled if set to 'off' + ### even if a specific repair value is 'on' + # repair_all: 'on' + ### Default values for checks + # repair_foo: 'off' diff --git a/autosysadmin-agent/files/autosysadmin.logrotate.conf b/autosysadmin-agent/files/autosysadmin.logrotate.conf new file mode 100644 index 00000000..41606de5 --- /dev/null +++ b/autosysadmin-agent/files/autosysadmin.logrotate.conf @@ -0,0 +1,13 @@ +/var/log/autosysadmin.log { + daily + missingok + rotate 365 + compress + nodelaycompress + notifempty + dateext + dateformat .%Y-%m-%d + dateyesterday + copytruncate + create 0640 root adm +} diff --git a/autosysadmin-agent/files/autosysadmin.rsyslog.conf b/autosysadmin-agent/files/autosysadmin.rsyslog.conf new file mode 100644 index 00000000..dd3c037f --- /dev/null +++ b/autosysadmin-agent/files/autosysadmin.rsyslog.conf @@ -0,0 +1,3 @@ +$template autosysadmin, "/var/log/autosysadmin.log" +if $programname contains 'autosysadmin' then ?autosysadmin +& stop diff --git a/autosysadmin-agent/files/upstream/bin/delete_old_logs.sh b/autosysadmin-agent/files/upstream/bin/delete_old_logs.sh new file mode 100644 index 00000000..a39d9efe --- /dev/null +++ b/autosysadmin-agent/files/upstream/bin/delete_old_logs.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +days=${1:-365} +log_dir="/var/log/autosysadmin/" + +if [ -d "${log_dir}" ]; then + find_run_dirs() { + find "${log_dir}" \ + -mindepth 1 \ + -maxdepth 1 \ + -type d \ + -ctime "+${days}" \ + -print0 + } + log() { + /usr/bin/logger -p local0.notice -t autosysadmin "${1}" + } + + while IFS= read -r -d '' run_dir; do + rm --recursive --force "${run_dir}" + log "Delete ${run_dir} (older than ${days} days)" + done < <(find_run_dirs) +fi + +exit 0 diff --git a/autosysadmin-agent/files/upstream/lib/common.sh b/autosysadmin-agent/files/upstream/lib/common.sh new file mode 100755 index 00000000..9a7c7e23 --- /dev/null +++ b/autosysadmin-agent/files/upstream/lib/common.sh @@ -0,0 +1,907 @@ +#!/bin/bash + +VERSION="24.03.1" + +# Common functions for "repair" and "restart" scripts + +set -u + +# Initializes the program, context, configuration… +initialize() { + PATH="${PATH}":/usr/sbin:/sbin + + # Used in many places to refer to the program name. + # Examples: repair_mysql, restart_nrpe… + PROGNAME=$(basename "${0}") + + # find out if running in interactive mode, or not + if [ -t 0 ]; then + INTERACTIVE=1 + else + INTERACTIVE=0 + fi + readonly INTERACTIVE + + # Default empty value for Debug mode + DEBUG="${DEBUG:-""}" + + # Repair scripts obey to the value of a variable named after the script + # You can set the value ("on" or "off") in /etc/evolinux/autosysadmin + # Here we set the default value to "on". + declare -g "${PROGNAME}"=on # dynamic variable assignment ($PROGNAME == repair_*) + + PID=$$ + readonly PID + + # Each execution (run) gets a unique ID + RUN_ID="$(date +"%Y-%m-%d_%H-%M")_${PROGNAME}_${PID}" + readonly RUN_ID + + # Main log directory + MAIN_LOG_DIR="/var/log/autosysadmin" + readonly MAIN_LOG_DIR + # shellcheck disable=SC2174 + mkdir --mode=750 --parents "${MAIN_LOG_DIR}" + chgrp adm "${MAIN_LOG_DIR}" + + # Each execution store some information + # in a unique directory based on the RUN_ID + RUN_LOG_DIR="${MAIN_LOG_DIR}/${RUN_ID}" + readonly RUN_LOG_DIR + # shellcheck disable=SC2174 + mkdir --mode=750 --parents "${RUN_LOG_DIR}" + chgrp adm "${RUN_LOG_DIR}" + + # This log file contains all events + RUN_LOG_FILE="${RUN_LOG_DIR}/autosysadmin.log" + readonly RUN_LOG_FILE + + # This log file contains notable actions + ACTIONS_FILE="${RUN_LOG_DIR}/actions.log" + readonly ACTIONS_FILE + touch "${ACTIONS_FILE}" + # This log file contains abort reasons (if any) + ABORT_FILE="${RUN_LOG_DIR}/abort.log" + readonly ABORT_FILE + # touch "${ABORT_FILE}" + + # Date format for log messages + DATE_FORMAT="%Y-%m-%d %H:%M:%S" + + # This will contain lock, last-run markers… + # It's ok to lose the content after a reboot + RUN_DIR="/run/autosysadmin" + readonly RUN_DIR + mkdir -p "${RUN_DIR}" + + # Only a singe instace of each script can run simultaneously + # We use a customizable lock name for this. + # By default it's the script's name + LOCK_NAME=${LOCK_NAME:-${PROGNAME}} + # If a lock is found, we can wait for it to disappear. + # The value must be understood by sleep(1) + LOCK_WAIT="0" + + # Default values for email headers + EMAIL_FROM="equipe+autosysadmin@evolix.net" + EMAIL_INTERNAL="autosysadmin@evolix.fr" + + LOCK_FILE="${RUN_DIR}/${LOCK_NAME}.lock" + readonly LOCK_FILE + # Remove lock file at exit + cleanup() { + # shellcheck disable=SC2317 + rm -f "${LOCK_FILE}" + } + trap 'cleanup' 0 + + # Load configuration + # shellcheck disable=SC1091 + test -f /etc/evolinux/autosysadmin && source /etc/evolinux/autosysadmin + + log_all "Begin ${PROGNAME} RUN_ID: ${RUN_ID}" + log_all "Log directory is ${RUN_LOG_DIR}" +} + +# Executes a list of tasks before exiting: +# * prepare a summary of actions and possible abort reasons +# * send emails +# * do some cleanup +quit() { + log_all "End ${PROGNAME} RUN_ID: ${RUN_ID}" + + summary="RUN_ID: ${RUN_ID}" + if [ -s "${ABORT_FILE}" ]; then + # Add abort reasons to summary + summary="${summary}\n$(print_abort_reasons)" + hook_mail "abort" + + return_code=1 + else + if [ -s "${ACTIONS_FILE}" ]; then + # Add notable actions to summary + summary="${summary}\n$(print_actions "Aucune action")" + hook_mail "success" + fi + + return_code=0 + fi + + hook_mail "internal" + + if is_interactive; then + # shellcheck disable=SC2001 + echo "${summary}" | sed -e 's/\\n/\n/g' + else + /usr/share/scripts/evomaintenance.sh --auto --user autosysadmin --message "${summary}" --no-commit --no-mail + fi + + teardown + + # shellcheck disable=SC2086 + exit ${return_code} +} + +teardown() { + : +} + +# Return true/false +is_interactive() { + test "${INTERACTIVE}" -eq "1" +} + +save_server_state() { + DUMP_SERVER_STATE_BIN="$(command -v dump-server-state || command -v backup-server-state)" + + if [ -z "${DUMP_SERVER_STATE_BIN}" ]; then + log_all "Warning: dump-server-state is not present. No server state recorded." + fi + + if [ -x "${DUMP_SERVER_STATE_BIN}" ]; then + DUMP_DIR=$(file_path_in_log_dir "server-state") + # We don't want the logging to take too much time, + # so we kill it if it takes more than 20 seconds. + timeout --signal 9 20 \ + "${DUMP_SERVER_STATE_BIN}" \ + --dump-dir="${DUMP_DIR}" \ + --df \ + --dmesg \ + --iptables \ + --lxc \ + --netcfg \ + --netstat \ + --uname \ + --processes \ + --systemctl \ + --uptime \ + --virsh \ + --disks \ + --mysql-processes \ + --no-apt-states \ + --no-apt-config \ + --no-dpkg-full \ + --no-dpkg-status \ + --no-mount \ + --no-packages \ + --no-sysctl \ + --no-etc + + log_run "Server state saved in \`server-state' directory." + fi +} + +is_debug() { + # first time: do the check… + # other times: pass + if [ -z "${DEBUG:-""}" ]; then + debug_file="/etc/evolinux/autosysadmin.debug" + + if [ -e "${debug_file}" ]; then + last_change=$(stat -c %Z "${debug_file}") + limit_date=$(date --date "14400 seconds ago" +"%s") + + if [ $(( last_change - limit_date )) -le "0" ]; then + log_run "Debug mode disabled; file is too old (%{last_change} seconds)." + rm "${debug_file}" + # Debug mode disabled + DEBUG="0" + else + log_run "Debug mode enabled." + # Debug mode enabled + DEBUG="1" + fi + else + # log_run "Debug mode disabled; file is absent." + # Debug mode disabled + DEBUG="0" + fi + fi + # return the value + test "${DEBUG}" -eq "1" +} + +# Uses the who(1) definition of "active" +currently_active_users() { + LC_ALL=C who --users | grep --extended-regexp "\s+\.\s+" | awk '{print $1}' | sort --human-numeric-sort | uniq +} +# Users active in the last 29 minutes +recently_active_users() { + LC_ALL=C who --users | grep --extended-regexp "\s+00:(0|1|2)[0-9]\s+" | awk --field-separator ' ' '{print $1,$6}' +} +# Save the list of users to a file in the log directory +save_active_users() { + LC_ALL=C who --users | save_in_log_dir "who-users" +} + +# An autosysadmin must not perform actions if a user is active or was active recently. +# +# This can by bypassed in interactive mode. +# It's OK to lose this data after a reboot. +ensure_no_active_users_or_exit() { + # Save all active users + save_active_users + + if is_debug; then + log_run "Debug mode enabled: continue without checking active users." + return 0; + fi + + # Is there any currently active user? + currently_active_users=$(currently_active_users) + if [ -n "${currently_active_users}" ]; then + # shellcheck disable=SC2001 + users_oneliner=$(echo "${currently_active_users}" | sed -e 's/\n/ /') + log_run "Currently active users: ${users_oneliner}" + if is_interactive; then + echo "Some users are currently active:" + # shellcheck disable=SC2001 + echo "${currently_active_users}" | sed -e 's/\(.\+\)/* \1/' + answer="" + while :; do + printf "> Continue? [Y,n,?] " + read -r answer + case ${answer} in + [Yy]|"" ) + log_run "Active users check bypassed manually in interactive mode." + return + ;; + [Nn] ) + log_run "Active users check confirmed manually in interactive mode." + log_abort_and_quit "Active users detected: ${users_oneliner}" + ;; + * ) + printf "y - yes, continue\n" + printf "n - no, exit\n" + printf "? - print this help\n" + ;; + esac + done + else + log_abort_and_quit "Currently active users detected: ${users_oneliner}." + fi + else + # or recently (the last 30 minutes) active user? + recently_active_users=$(recently_active_users) + if [ -n "${recently_active_users}" ]; then + # shellcheck disable=SC2001 + users_oneliner=$(echo "${recently_active_users}" | sed -e 's/\n/ /') + log_run "Recently active users: ${users_oneliner}" + if is_interactive; then + echo "Some users were recently active:" + # shellcheck disable=SC2001 + echo "${recently_active_users}" | sed -e 's/\(.\+\)/* \1/' + answer="" + while :; do + printf "> Continue? [Y,n,?] " + read -r answer + case ${answer} in + [Yy]|"" ) + log_run "Active users check bypassed manually in interactive mode." + return + ;; + [Nn] ) + log_run "Active users check confirmed manually in interactive mode." + log_abort_and_quit "Recently active users detected: ${users_oneliner}." + ;; + * ) + printf "y - yes, continue\n" + printf "n - no, exit\n" + printf "? - print this help\n" + ;; + esac + done + else + log_abort_and_quit "Recently active users detected: ${users_oneliner}." + fi + fi + fi +} + +# Takes an NRPE command name as 1st parameter, +# and executes the full command if found in the configuration. +# Return the result and the return code of the command. +check_nrpe() { + check="$1" + + nrpe_files="" + + # Check if NRPE config is found + if [ -f "/etc/nagios/nrpe.cfg" ]; then + nrpe_files="${nrpe_files} /etc/nagios/nrpe.cfg" + else + msg="NRPE configuration not found: /etc/nagios/nrpe.cfg" + log_run "${msg}" + echo "${msg}" + return 3 + fi + + # Search for included files + # shellcheck disable=SC2086 + while IFS= read -r include_file; do + nrpe_files="${nrpe_files} ${include_file}" + done < <(grep --extended-regexp '^\s*include=.+' ${nrpe_files} | cut -d = -f 2) + + # Search for files in included directories + # shellcheck disable=SC2086 + while IFS= read -r include_dir; do + nrpe_files="${nrpe_files} ${include_dir}/*.cfg" + done < <(grep --extended-regexp '^\s*include_dir=.+' ${nrpe_files} | cut -d = -f 2) + + # Fetch uncommented commands in (sorted) config files + # shellcheck disable=SC2086 + nrpe_commands=$(grep --no-filename --exclude=*~ --fixed-strings "[${check}]" ${nrpe_files} | grep --invert-match --extended-regexp '^\s*#\s*command' | cut -d = -f 2) + nrpe_commands_count=$(echo "${nrpe_commands}" | wc -l) + + if is_debian_version "9" "<=" && [ "${nrpe_commands_count}" -gt "1" ]; then + # On Debian <= 9, NRPE loading was not sorted + # we need to raise an error if we have multiple defined commands + msg="Unable to determine which NRPE command to run" + log_run "${msg}" + echo "${msg}" + return 3 + else + # On Debian > 9, use the last command + nrpe_command=$(echo "${nrpe_commands}" | tail -n 1) + + nrpe_result=$(${nrpe_command}) + nrpe_rc=$? + + log_run "NRPE command (exited with ${nrpe_rc}): ${nrpe_command}" + log_run "${nrpe_result}" + + echo "${nrpe_result}" + return "${nrpe_rc}" + fi +} + +# An autosysadmin script must not run twice (or more) simultaneously. +# We use a customizable (with LOCK_NAME) lock file to keep track of this. +# A wait time can be configured. +# +# This can by bypassed in interactive mode. +# It's OK to lose this data after a reboot. +acquire_lock_or_exit() { + lock_file="${1:-${LOCK_FILE}}" + lock_wait="${2:-${LOCK_WAIT}}" + + # lock_wait must be compatible with sleep(1), otherwise fallback to 0 + if ! echo "${lock_wait}" | grep -Eq '^[0-9]+[smhd]?$'; then + log_run "Lock wait: incorrect value '${lock_wait}', fallback to 0." + lock_wait=0 + fi + + if [ "${lock_wait}" != "0" ] && [ -f "${lock_file}" ]; then + log_run "Lock file present. Let's wait ${lock_wait} and check again." + sleep "${lock_wait}" + fi + + if [ -f "${lock_file}" ]; then + log_abort_and_quit "Lock file still present." + else + log_run "Lock file absent. Let's put one." + touch "${lock_file}" + fi +} + +# If a script has been run in the ast 30 minutes, running it again won't fix the issue. +# We use a /run/ausosysadmin/${PROGNAME}_lastrun file to keep track of this. +# +# This can by bypassed in interactive mode. +# This is bypassed in debug mode. +# It's OK to lose this data after a reboot. +ensure_not_too_soon_or_exit() { + if is_debug; then + log_run "Debug mode enabled: continue without checking when was the last run." + return 0; + fi + + lastrun_file="${RUN_DIR}/${PROGNAME}_lastrun" + if [ -f "${lastrun_file}" ]; then + lastrun_age="$(($(date +%s)-$(stat -c "%Y" "${lastrun_file}")))" + log_run "Last run was ${lastrun_age} seconds ago." + if [ "${lastrun_age}" -lt 1800 ]; then + if is_interactive; then + echo "${PROGNAME} was run ${lastrun_age} seconds ago." + answer="" + while :; do + printf "> Continue? [Y,n,?] " + read -r answer + case ${answer} in + [Yy]|"" ) + log_run "Last run check bypassed manually in interactive mode." + break + ;; + [Nn] ) + log_run "Last run check confirmed manually in interactive mode." + log_abort_and_quit 'Last run too recent.' + ;; + * ) + printf "y - yes, continue\n" + printf "n - no, exit\n" + printf "? - print this help\n" + ;; + esac + done + else + log_abort_and_quit "Last run too recent." + fi + fi + fi + touch "${lastrun_file}" +} + +# Populate DEBIAN_VERSION and DEBIAN_RELEASE variables +# based on gathered information about the operating system +detect_os() { + DEBIAN_RELEASE="unknown" + DEBIAN_VERSION="unknown" + LSB_RELEASE_BIN="$(command -v lsb_release)" + + if [ -e /etc/debian_version ]; then + DEBIAN_VERSION="$(cut -d "." -f 1 < /etc/debian_version)" + if [ -x "${LSB_RELEASE_BIN}" ]; then + DEBIAN_RELEASE="$("${LSB_RELEASE_BIN}" --codename --short)" + else + case "${DEBIAN_VERSION}" in + 7) DEBIAN_RELEASE="wheezy" ;; + 8) DEBIAN_RELEASE="jessie" ;; + 9) DEBIAN_RELEASE="stretch" ;; + 10) DEBIAN_RELEASE="buster" ;; + 11) DEBIAN_RELEASE="bullseye" ;; + 12) DEBIAN_RELEASE="bookworm" ;; + 13) DEBIAN_RELEASE="trixie" ;; + esac + fi + # log_run "Detected OS: Debian version=${DEBIAN_VERSION} release=${DEBIAN_RELEASE}" + # else + # log_run "Detected OS: unknown (missing /etc/debian_version)" + fi +} + +is_debian_wheezy() { + test "${DEBIAN_RELEASE}" = "wheezy" +} +is_debian_jessie() { + test "${DEBIAN_RELEASE}" = "jessie" +} +is_debian_stretch() { + test "${DEBIAN_RELEASE}" = "stretch" +} +is_debian_buster() { + test "${DEBIAN_RELEASE}" = "buster" +} +is_debian_bullseye() { + test "${DEBIAN_RELEASE}" = "bullseye" +} +is_debian_bookworm() { + test "${DEBIAN_RELEASE}" = "bookworm" +} +is_debian_trixie() { + test "${DEBIAN_RELEASE}" = "trixie" +} +is_debian_version() { + local version=$1 + local relation=${2:-"eq"} + + if [ -z "${DEBIAN_VERSION:-""}" ]; then + detect_os + fi + + dpkg --compare-versions "${DEBIAN_VERSION}" "${relation}" "${version}" +} + +# List systemd services (only names), even if stopped +systemd_list_services() { + pattern=$1 + + systemctl list-units --all --no-legend --type=service "${pattern}" | grep --only-matching --extended-regexp '\S+\.service' +} + +is_systemd_enabled() { + systemctl --quiet is-enabled "$1" 2> /dev/null +} + +is_systemd_active() { + systemctl --quiet is-active "$1" 2> /dev/null +} + +is_sysvinit_enabled() { + find /etc/rc2.d/ -name "$1" > /dev/null +} + +get_fqdn() { + # shellcheck disable=SC2155 + local system=$(uname -s) + + if [ "${system}" = "Linux" ]; then + hostname --fqdn + elif [ "${system}" = "OpenBSD" ]; then + hostname + else + log_abort_and_quit "System '${system}' not recognized." + fi +} + +get_complete_hostname() { + REAL_HOSTNAME="$(get_fqdn)" + if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then + echo "${HOSTNAME}" + else + echo "${HOSTNAME} (${REAL_HOSTNAME})" + fi +} +# Fetch values from evomaintenance configuration +get_evomaintenance_mail() { + grep "EVOMAINTMAIL=" /etc/evomaintenance.cf | cut -d '=' -f2 +} +get_evomaintenance_emergency_mail() { + grep "URGENCYFROM=" /etc/evomaintenance.cf | cut -d '=' -f2 +} +get_evomaintenance_emergency_tel() { + grep "URGENCYTEL=" /etc/evomaintenance.cf | cut -d '=' -f2 +} + +# Log a message to the log file in the log directory +log_run() { + local msg="${1:-$(cat /dev/stdin)}" + # shellcheck disable=SC2155 + local date=$(/bin/date +"${DATE_FORMAT}") + + printf "[%s] %s[%s]: %s\\n" \ + "${date}" "${PROGNAME}" "${PID}" "${msg}" \ + >> "${RUN_LOG_FILE}" +} +# Log a message in the system log file (syslog or journald) +log_global() { + local msg="${1:-$(cat /dev/stdin)}" + + echo "${msg}" \ + | /usr/bin/logger -p local0.notice -t autosysadmin +} +# Log a message in both places +log_all() { + local msg="${1:-$(cat /dev/stdin)}" + + log_global "${msg}" + log_run "${msg}" +} +# Log a notable action in regular places +# and append it to the dedicated list +log_action() { + log_all "$*" + append_action "$*" +} +# Append a line in the actions.log file in the log directory +append_action() { + echo "$*" >> "${ACTIONS_FILE}" +} +# Print the content of the actions.log file +# or a fallback content (1st parameter) if empty +# shellcheck disable=SC2120 +print_actions() { + local fallback=${1:-""} + if [ -s "${ACTIONS_FILE}" ]; then + cat "${ACTIONS_FILE}" + elif [ -n "${fallback}" ]; then + echo "${fallback}" + fi +} + +# Log a an abort reason in regular places +# and append it to the dedicated list +log_abort() { + log_all "$*" + append_abort_reason "$*" +} +# Append a line in the abort.log file in the log directory +append_abort_reason() { + echo "$*" >> "${ABORT_FILE}" +} +# Print the content of the abort.log file +# or a fallback content (1st parameter) if empty +# shellcheck disable=SC2120 +print_abort_reasons() { + local fallback=${1:-""} + if [ -s "${ABORT_FILE}" ]; then + cat "${ABORT_FILE}" + elif [ -n "${fallback}" ]; then + echo "${fallback}" + fi +} +# Print the content of the main log from the log directory +print_main_log() { + cat "${RUN_LOG_FILE}" +} +# Log an abort reason and quit the script +log_abort_and_quit() { + log_abort "$*" + quit +} + +# Store the content from standard inpu +# into a file in the log directory named after the 1st parameter +save_in_log_dir() { + local file_name=$1 + local file_path="${RUN_LOG_DIR}/${file_name}" + + cat /dev/stdin > "${file_path}" + + log_run "Saved \`${file_name}' file." +} +# Return the full path of the file in log directory +# based on the name in the 1st parameter +file_path_in_log_dir() { + echo "${RUN_LOG_DIR}/${1}" +} + +format_mail_success() { + cat < +Content-Type: text/plain; charset=UTF-8 +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Script: ${PROGNAME} +X-RunId: ${RUN_ID} +To: ${EMAIL_CLIENT:-alert5@evolix.fr} +Cc: ${EMAIL_INTERNAL} +Subject: [autosysadmin] Intervention automatisée sur ${HOSTNAME_TEXT} + +Bonjour, + +Une intervention automatisée vient de se terminer. + +Nom du serveur : ${HOSTNAME_TEXT} +Heure d'intervention : $(LC_ALL=fr_FR.utf8 date) +Script déclenché : ${PROGNAME} + +### Actions réalisées + +$(print_actions "Aucune") + +### Réagir à cette intervention + +Vous pouvez répondre à ce message (${EMAIL_FROM}). + +En cas d'urgence, utilisez l'adresse ${EMERGENCY_MAIL} +ou notre ligne d'astreinte (${EMERGENCY_TEL}) + +-- +Votre AutoSysadmin +EOTEMPLATE +} + +format_mail_abort() { + cat < +Content-Type: text/plain; charset=UTF-8 +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Script: ${PROGNAME} +X-RunId: ${RUN_ID} +To: ${EMAIL_CLIENT:-alert5@evolix.fr} +Cc: ${EMAIL_INTERNAL} +Subject: [autosysadmin] Intervention automatisée interrompue sur ${HOSTNAME_TEXT} + +Bonjour, + +Une intervention automatisée a été déclenchée mais s'est interrompue. + +Nom du serveur : ${HOSTNAME_TEXT} +Heure d'intervention : $(LC_ALL=fr_FR.utf8 date) +Script déclenché : ${PROGNAME} + +### Actions réalisées + +$(print_actions "Aucune") + +### Raison(s) de l'interruption + +$(print_abort_reasons "Inconnue") + +### Réagir à cette intervention + +Vous pouvez répondre à ce message (${EMAIL_FROM}). + +En cas d'urgence, utilisez l'adresse ${EMERGENCY_MAIL} +ou notre ligne d'astreinte (${EMERGENCY_TEL}) + +-- +Votre AutoSysadmin +EOTEMPLATE +} + +# shellcheck disable=SC2028 +print_report_information() { + echo "**Uptime**" + echo "" + uptime + + echo "" + echo "**Utilisateurs récents**" + echo "" + who_file=$(file_path_in_log_dir "who-users") + if [ -s "${who_file}" ]; then + cat "${who_file}" + else + who --users + fi + + echo "" + echo "**Espace disque**" + echo "" + df_file=$(file_path_in_log_dir "server-state/df.txt") + if [ -s "${df_file}" ]; then + cat "${df_file}" + else + df -h + fi + + echo "" + echo "**Dmesg**" + echo "" + dmesg_file=$(file_path_in_log_dir "server-state/dmesg.txt") + if [ -s "${dmesg_file}" ]; then + tail -n 5 "${dmesg_file}" + else + dmesg | tail -n 5 + fi + + echo "" + echo "**systemd failed services**" + echo "" + failed_services_file=$(file_path_in_log_dir "server-state/systemctl-failed-services.txt") + if [ -s "${failed_services_file}" ]; then + cat "${failed_services_file}" + else + systemctl --no-legend --state=failed --type=service + fi + + if command -v lxc-ls > /dev/null 2>&1; then + echo "" + echo "**LXC containers**" + echo "" + lxc_ls_file=$(file_path_in_log_dir "server-state/lxc-list.txt") + if [ -s "${lxc_ls_file}" ]; then + cat "${lxc_ls_file}" + else + lxc-ls --fancy + fi + fi + + apache_errors_file=$(file_path_in_log_dir "apache-errors.log") + if [ -f "${apache_errors_file}" ]; then + echo "" + echo "**Apache errors**" + echo "" + cat "${apache_errors_file}" + fi + + nginx_errors_file=$(file_path_in_log_dir "nginx-errors.log") + if [ -f "${nginx_errors_file}" ]; then + echo "" + echo "**Nginx errors**" + echo "" + cat "${nginx_errors_file}" + fi +} + +format_mail_internal() { + cat < +Content-Type: text/plain; charset=UTF-8 +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Script: ${PROGNAME} +X-RunId: ${RUN_ID} +To: ${EMAIL_INTERNAL} +Subject: [autosysadmin] Rapport interne d'intervention sur ${HOSTNAME_TEXT} + +Bonjour, + +Une intervention automatique vient de se terminer. + +Nom du serveur : ${HOSTNAME_TEXT} +Heure d'intervention : $(LC_ALL=fr_FR.utf8 date) +Script déclenché : ${PROGNAME} + +### Actions réalisées + +$(print_actions "Aucune") + +### Raison(s) de l'interruption + +$(print_abort_reasons "Aucune") + +### Log autosysadmin + +$(print_main_log) + +### Informations additionnelles + +$(print_report_information) + +-- +Votre AutoSysadmin +EOTEMPLATE +} + +# Generic function to send emails at the end of the script. +# Takes a template as 1st parameter +hook_mail() { + if is_debug; then + log_run "Debug mode enabled: continue without sending mail." + return 0; + fi + + HOSTNAME="${HOSTNAME:-"$(get_fqdn)"}" + HOSTNAME_TEXT="$(get_complete_hostname)" + EMAIL_CLIENT="$(get_evomaintenance_mail)" + EMERGENCY_MAIL="$(get_evomaintenance_emergency_mail)" + EMERGENCY_TEL="$(get_evomaintenance_emergency_tel)" + + MAIL_CONTENT="$(format_mail_"$1")" + + SENDMAIL_BIN="$(command -v sendmail)" + + if [ -z "${SENDMAIL_BIN}" ]; then + log_global "ERROR: No \`sendmail' command has been found, can't send mail." + fi + if [ -x "${SENDMAIL_BIN}" ]; then + echo "${MAIL_CONTENT}" | "${SENDMAIL_BIN}" -oi -t -f "equipe@evolix.fr" + log_global "Sent '$1' mail for RUN_ID: ${RUN_ID}" + fi +} + +is_holiday() { + # gcal mark today as a holiday by surrounding with < and > the day + # of the month of that holiday line. For example if today is 2022-05-01 we'll + # get among other lines: + # Fête du Travail (FR) + Di, < 1>Mai 2022 + # Jour de la Victoire (FR) + Di, : 8:Mai 2022 = +7 jours + LANGUAGE=fr_FR.UTF-8 TZ=Europe/Paris gcal --cc-holidays=fr --holiday-list=short | grep -E '<[0-9 ]{2}>' --quiet +} + +is_weekend() { + day_of_week=$(date +%u) + if [ "${day_of_week}" != 6 ] && [ "${day_of_week}" != 7 ]; then + return 1 + fi +} + +is_workday() { + if is_holiday || is_weekend; then + return 1 + fi +} + +is_worktime() { + if ! is_workday; then + return 1 + fi + + hour=$(date +%H) + if [ "${hour}" -lt 9 ] || { [ "${hour}" -ge 12 ] && [ "${hour}" -lt 14 ] ; } || [ "${hour}" -ge 18 ]; then + return 1 + fi +} diff --git a/autosysadmin-agent/files/upstream/lib/repair.sh b/autosysadmin-agent/files/upstream/lib/repair.sh new file mode 100644 index 00000000..ddd243b5 --- /dev/null +++ b/autosysadmin-agent/files/upstream/lib/repair.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# Specific functions for "repair" scripts + +is_all_repair_disabled() { + # Fetch values from the config + # and if it is not defined or has no value, then assign "on" + + local status=${repair_all:=on} + + + test "${status}" = "off" || test "${status}" = "0" +} + +is_current_repair_disabled() { + # Fetch values from the config + # and if it is not defined or has no value, then assign "on" + + local status=${!PROGNAME:=on} + + test "${status}" = "off" || test "${status}" = "0" +} + +ensure_not_disabled_or_exit() { + if is_all_repair_disabled; then + log_global 'All repair scripts are disabled.' + exit 0 + fi + if is_current_repair_disabled; then + log_global "Current repair script (${PROGNAME}) is disabled." + exit 0 + fi +} + +# Set of actions to do at the begining of a "repair" script +pre_repair() { + initialize + + # Are we supposed to run? + ensure_not_disabled_or_exit + + # Has it recently been run? + ensure_not_too_soon_or_exit + + # Can we acquire a lock? + acquire_lock_or_exit + + # Is there any active user? + ensure_no_active_users_or_exit + + # Save important information + save_server_state +} + +# Set of actions to do at the end of a "repair" script +post_repair() { + quit +} + +repair_lxc_php() { + container_name=$1 + + if is_systemd_enabled 'lxc.service'; then + lxc_path=$(lxc-config lxc.lxcpath) + if lxc-info --name "${container_name}" > /dev/null; then + rootfs="${lxc_path}/${container_name}/rootfs" + case "${container_name}" in + php56) fpm_log_file="${rootfs}/var/log/php5-fpm.log" ;; + php70) fpm_log_file="${rootfs}/var/log/php7.0-fpm.log" ;; + php73) fpm_log_file="${rootfs}/var/log/php7.3-fpm.log" ;; + php74) fpm_log_file="${rootfs}/var/log/php7.4-fpm.log" ;; + php80) fpm_log_file="${rootfs}/var/log/php8.0-fpm.log" ;; + php81) fpm_log_file="${rootfs}/var/log/php8.1-fpm.log" ;; + php82) fpm_log_file="${rootfs}/var/log/php8.2-fpm.log" ;; + php83) fpm_log_file="${rootfs}/var/log/php8.3-fpm.log" ;; + *) + log_abort_and_quit "Unknown container '${container_name}'" + ;; + esac + + # Determine FPM Pool path + php_path_pool=$(find "${lxc_path}/${container_name}/" -type d -name "pool.d") + + # Save LXC info (before restart) + lxc-info --name "${container_name}" | save_in_log_dir "lxc-${container_name}.before.status" + # Save last lines of FPM log (before restart) + tail "${fpm_log_file}" | save_in_log_dir "$(basename "${fpm_log_file}" | sed -e 's/.log/.before.log/')" + # Save NRPE check (before restart) + /usr/local/lib/nagios/plugins/check_phpfpm_multi "${php_path_pool}" | save_in_log_dir "check_fpm_${container_name}.before.out" + + lxc-stop --timeout 20 --name "${container_name}" + lxc-start --daemon --name "${container_name}" + rc=$? + if [ "${rc}" -eq "0" ]; then + log_all "Restart LXC container '${container_name}: OK" + else + log_all "Restart LXC container '${container_name}: failed" + fi + + # Save LXC info (after restart) + lxc-info --name "${container_name}" | save_in_log_dir "lxc-${container_name}.after.status" + # Save last lines of FPM log (after restart) + tail "${fpm_log_file}" | save_in_log_dir "$(basename "${fpm_log_file}" | sed -e 's/.log/.after.log/')" + # Save NRPE check (after restart) + /usr/local/lib/nagios/plugins/check_phpfpm_multi "${php_path_pool}" | save_in_log_dir "check_fpm_${container_name}.after.out" + else + log_abort_and_quit "LXC container '${container_name}' doesn't exist." + fi + else + log_abort_and_quit 'LXC not found.' + fi +} diff --git a/autosysadmin-agent/files/upstream/lib/restart.sh b/autosysadmin-agent/files/upstream/lib/restart.sh new file mode 100644 index 00000000..78be5bb0 --- /dev/null +++ b/autosysadmin-agent/files/upstream/lib/restart.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Specific functions for "restart" scripts + +running_custom() { + # Placeholder that returns 1, to prevent running if not redefined + log_global "running_custom() function has not been redefined! Let's quit." + return 1 +} + +# Examine RUNNING variable and decide if the script should run or not +is_supposed_to_run() { + if is_debug; then return 0; fi + + case ${RUNNING} in + never) + # log_global "is_supposed_to_run: no (never)" + return 1 + ;; + always) + # log_global "is_supposed_to_run: yes (always)" + return 0 + ;; + nwh-fr) + ! is_worktime + rc=$? + # if [ ${rc} -eq 0 ]; then + # log_global "is_supposed_to_run: yes (nwh-fr returned ${rc})" + # else + # log_global "is_supposed_to_run: no (nwh-fr returned ${rc})" + # fi + return ${rc} + ;; + nwh-ca) + # Not implemented yet + return 0 + ;; + custom) + running_custom + rc=$? + # if [ ${rc} -eq 0 ]; then + # log_global "is_supposed_to_run: yes (custom returned ${rc})" + # else + # log_global "is_supposed_to_run: no (custom returned ${rc})" + # fi + return ${rc} + ;; + esac +} + +ensure_supposed_to_run_or_exit() { + if ! is_supposed_to_run; then + # simply quit (no logging, no notifications…) + # log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})." + exit 0 + fi +} + +# Set of actions to do at the begining of a "restart" script +pre_restart() { + initialize + + # Has it recently been run? + ensure_not_too_soon_or_exit + + # Can we acquire a lock? + acquire_lock_or_exit + + # Save important information + save_server_state +} + +# Set of actions to do at the end of a "restart" script +post_restart() { + quit +} diff --git a/autosysadmin-agent/files/upstream/repair/repair_disk b/autosysadmin-agent/files/upstream/repair/repair_disk new file mode 100755 index 00000000..70ed28a6 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_disk @@ -0,0 +1,157 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + + +# We always keep some reserved blocks to avoid missing some logs +# https://gitea.evolix.org/evolix/autosysadmin/issues/22 +RESERVED_BLOCKS_MIN=1 + +get_mountpoints() { + # the $(...) get the check_disk1 command + # the cut command selects the critical part of the check_disk1 output + # the grep command extracts the mountpoints and available disk space + # the last cut command selects the mountpoints + check_disk1_command=$(grep check_disk1 /etc/nagios/nrpe.d/evolix.cfg | cut -d'=' -f2-) + + ${check_disk1_command} -e | cut -d'|' -f1 | grep --extended-regexp --only-matching '/[[:graph:]]* [0-9]+ [A-Z][A-Z]' | cut -d' ' -f1 +} + +is_reserved_blocks_nominal() { + partition=${1} + + fs_type="$(findmnt -n --output=fstype "${partition}")" + if [ "${fs_type}" = "ext4" ]; then + device="$(findmnt -n --output=source "${partition}")" + reserved_block_count="$(tune2fs -l "${device}" | grep 'Reserved block count' | awk -F':' '{ gsub (" ", "", $0); print $2}')" + block_count="$(tune2fs -l "${device}" | grep 'Block count' | awk -F':' '{ gsub (" ", "", $0); print $2}')" + percentage=$(awk "BEGIN { pc=100*${reserved_block_count}/${block_count}; i=int(pc); print (pc-i<0.5)?i:i+1 }") + + log_run "Reserved blocks for ${partition} is currently at ${percentage}%" + if [ "${percentage}" -gt "${RESERVED_BLOCKS_MIN}" ]; then + log_run "Allowing tune2fs action to reduce the number of reserved blocks" + return 0 + else + log_run "Reserved blocks already at or bellow ${RESERVED_BLOCKS_MIN}%, no automatic action possible" + return 1 + fi + else + log_run "Filesystem for ${partition} (${fs_type}) is incompatible with reserved block reduction." + return 1 + fi +} + +reduce_reserved_blocks() { + partition=${1} + + device=$(findmnt -n --output=source "${partition}") + tune2fs -m "${RESERVED_BLOCKS_MIN}" "${device}" + log_action "Reserved blocks for ${partition} changed to ${RESERVED_BLOCKS_MIN} percent" +} + +is_tmp_to_delete() { + size="$(find /var/log/ -type f -ctime +1 -exec du {} \+ | awk '{s+=$1}END{print s / 1024}')" + if [ -n "${size}" ]; then + return 0 + else + return 1 + fi +} + +is_log_to_delete() { + size="$(find /var/log/ -type f -mtime +365 -exec du {} \+ | awk '{s+=$1}END{print s / 1024}')" + if [ -n "${size}" ]; then + return 0 + else + return 1 + fi +} + +clean_apt_cache() { + for container in $(lxc-ls -1); do + if [ -e "$(lxc-config lxc.lxcpath)/${container}/rootfs/var/cache" ]; then + lxc-attach --name "${container}" -- apt-get clean + log_action "Clean apt cache in LXC container ${container}"; + fi + done + + # NOTE: "head -n 1" might be superfluous, but let's be sure to have only the first returned value + biggest_subdir=$(du --summarize --one-file-system "/var/*" | sort --numeric-sort --reverse | sed 's/^[0-9]\+[[:space:]]\+//;q' | head -n 1) + case "${biggest_subdir}" in + '/var/cache') + apt-get clean + log_action 'Clean apt cache' + ;; + esac +} + +clean_amavis_virusmails() { + if du --inodes /var/lib/* | sort --numeric-sort | tail -n 3 | grep --quiet 'virusmails$'; then + find /var/lib/amavis/virusmails/ -type f -atime +30 -delete + log_action 'Clean amavis infected mails' + fi +} + +critical_mountpoints=$(get_mountpoints) + +if [ -z "${critical_mountpoints}" ]; then + log_abort_and_quit "No partition is in critical state, nothing left to do." +else + for mountpoint in ${critical_mountpoints}; do + case "${mountpoint}" in + /var) + #if is_log_to_delete + #then + # find /var/log/ -type f -mtime +365 -delete + # log_action "$size Mo of disk space freed in /var" + #fi + if is_reserved_blocks_nominal /var; then + reduce_reserved_blocks /var + clean_apt_cache + clean_amavis_virusmails + fi + ;; + /tmp) + #if is_tmp_to_delete + #then + # find /tmp/ -type f -ctime +1 -delete + # log_action "$size Mo of disk space freed in /tmp" + #fi + if is_reserved_blocks_nominal /tmp; then + reduce_reserved_blocks /tmp + fi + ;; + /home) + if is_reserved_blocks_nominal /home; then + reduce_reserved_blocks /home + fi + ;; + /srv) + if is_reserved_blocks_nominal /srv; then + reduce_reserved_blocks /srv + fi + ;; + /filer) + if is_reserved_blocks_nominal /filer; then + reduce_reserved_blocks /filer + fi + ;; + /) + if is_reserved_blocks_nominal /; then + reduce_reserved_blocks / + # Suggest remove old kernel ? + fi + ;; + *) + # unknown + log_run 'Unknown partition (or weird case) or nothing to do' + ;; + esac + done +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_elasticsearch b/autosysadmin-agent/files/upstream/repair/repair_elasticsearch new file mode 100755 index 00000000..5baffaaa --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_elasticsearch @@ -0,0 +1,35 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +service="elasticsearch.service" +service_name="elasticsearch" + +if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_abort_and_quit "${service} is active, nothing left to do." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + fi +else + log_abort_and_quit "${service} is disabled (or missing), nothing left to do." +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_http b/autosysadmin-agent/files/upstream/repair/repair_http new file mode 100755 index 00000000..1c6fa5c7 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_http @@ -0,0 +1,131 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +## Apache + +service="apache2.service" +service_name="apache2" + +if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_all "${service} is active. Skip." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # check syntax + if apache2ctl -t > /dev/null 2>&1; then + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + + # Save error logs + date=$(LANG=en_US.UTF-8 date '+%b %d') + grep "${date}" /home/*/log/error.log /var/log/apache2/*error.log \ + | grep -v \ + -e "Got error 'PHP message:" \ + -e "No matching DirectoryIndex" \ + -e "client denied by server configuration" \ + -e "server certificate does NOT include an ID which matches the server name" \ + | save_in_log_dir "apache-errors.log" + else + log_action "Restart ${service_name}: skip (invalid configuration)" + fi + fi +else + log_all "${service} is disabled (or missing). Skip." +fi + +## Nginx + +service="nginx.service" +service_name="nginx" + +if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_all "${service} is active. Skip." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # check syntax + if nginx -t > /dev/null 2>&1; then + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + + # Save error logs + ### Consider doing for Nginx the same as Apache + else + log_action "Restart ${service_name}: skip (invalid configuration)" + fi + fi +else + log_all "${service} is disabled (or missing). Skip." +fi + +## LXC + +if is_systemd_enabled 'lxc.service'; then + for container in $(lxc-ls -1 | grep --fixed-strings 'php' | grep --extended-regexp --invert-match --regexp '\bold\b' --regexp '\bdisabled\b'); do + repair_lxc_php "${container}" + done +else + log_all "LXC is disabled (or missing). Skip." +fi + +## FPM + +fpm_services=$(systemd_list_services 'php*-fpm*') +if [ -n "${fpm_services}" ]; then + for service in ${fpm_services}; do + service_name="${service//.service/}" + if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_all "${service} is active. Skip." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + fi + else + log_all "${service} is disabled (or missing). Skip." + fi + done +else + log_all "PHP FPM not found. Skip." +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_mysql b/autosysadmin-agent/files/upstream/repair/repair_mysql new file mode 100755 index 00000000..eb176743 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_mysql @@ -0,0 +1,69 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +if is_debian_version "8" "<="; then + + if is_sysvinit_enabled '*mysql*'; then + if ! pgrep -u mysql mysqld > /dev/null; then + + # Save service status before restart + timeout 2 mysqladmin status 2>&1 | save_in_log_dir "mysql.before.status" + + timeout 20 /etc/init.d/mysql restart > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart mysql: OK" + else + log_action "Restart mysql: failed" + fi + + # Save service status after restart + timeout 2 mysqladmin status 2>&1 | save_in_log_dir "mysql.after.status" + else + log_abort_and_quit "mysqld process alive. Aborting" + fi + else + log_abort_and_quit "MySQL not enabled. Aborting" + fi + +else + + if is_debian_version "12" ">="; then + service="mariadb.service" + service_name="mariadb" + else + service="mysql.service" + service_name="mysql" + fi + + if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_abort_and_quit "${service} is active, nothing left to do." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + fi + else + log_abort_and_quit "${service} is disabled (or missing), nothing left to do." + fi + +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_opendkim b/autosysadmin-agent/files/upstream/repair/repair_opendkim new file mode 100755 index 00000000..ab06d01d --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_opendkim @@ -0,0 +1,35 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +service="opendkim.service" +service_name="opendkim" + +if is_systemd_enabled "${service}"; then + if is_systemd_active "${service}"; then + log_abort_and_quit "${service} is active, nothing left to do." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + fi +else + log_abort_and_quit "${service} is disabled (or missing). Abort." +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm56 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm56 new file mode 100755 index 00000000..db2ed9d4 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm56 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php56 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm70 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm70 new file mode 100755 index 00000000..324acadb --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm70 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php70 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm73 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm73 new file mode 100755 index 00000000..9089aa6e --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm73 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php73 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm74 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm74 new file mode 100755 index 00000000..6d7f49bb --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm74 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php74 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm80 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm80 new file mode 100755 index 00000000..f61f45e6 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm80 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php80 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm81 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm81 new file mode 100755 index 00000000..ec9b20c0 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm81 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php81 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm82 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm82 new file mode 100755 index 00000000..8af2217e --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm82 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php82 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_php_fpm83 b/autosysadmin-agent/files/upstream/repair/repair_php_fpm83 new file mode 100755 index 00000000..7584e69c --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_php_fpm83 @@ -0,0 +1,14 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +LOCK_WAIT="15s" +LOCK_NAME="repair_http" + +pre_repair + +repair_lxc_php php83 + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_redis b/autosysadmin-agent/files/upstream/repair/repair_redis new file mode 100755 index 00000000..3873d16f --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_redis @@ -0,0 +1,32 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +for service in $(systemd_list_services 'redis-server*'); do + service_name="${service//.service/}" + + if is_systemd_active "${service}"; then + log_all "${service} is active. Skip." + else + # Save service status before restart + systemctl status "${service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK." + else + log_action "Restart ${service_name}: failed." + fi + + # Save service status after restart + systemctl status "${service}" | save_in_log_dir "${service_name}.after.status" + fi +done + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/repair_tomcat_instance b/autosysadmin-agent/files/upstream/repair/repair_tomcat_instance new file mode 100755 index 00000000..8cc76ae4 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/repair_tomcat_instance @@ -0,0 +1,34 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +pre_repair + +repair_tomcat_instance_handle_tomcat() { + + if /bin/su - "${1}" -c "/bin/systemctl --quiet --user is-active tomcat.service" ; then + if ! /bin/su - "${1}" -c "/usr/bin/timeout 20 /bin/systemctl --quiet --user restart tomcat.service" + then + log_abort_and_quit "Echec de redémarrage instance tomcat utilisateur ${1}" + else + log_action "Redémarrage instance tomcat utilisateur ${1}" + fi + elif /bin/systemctl --quiet is-active "${1}".service ; then + if ! /usr/bin/timeout 20 systemctl --quiet restart "${1}".service + then + log_abort_and_quit "Echec de redémarrage instance tomcat ${1}" + else + log_action "Redémarrage instance tomcat ${1}" + fi + fi + +} + +for instance in $( /usr/local/lib/nagios/plugins/check_tomcat_instance.sh |grep CRITICAL |awk '{print $3}' |sed '1d') ; +do + repair_tomcat_instance_handle_tomcat "${instance}" +done + +post_repair diff --git a/autosysadmin-agent/files/upstream/repair/zzz-repair_example.template b/autosysadmin-agent/files/upstream/repair/zzz-repair_example.template new file mode 100755 index 00000000..668d4d02 --- /dev/null +++ b/autosysadmin-agent/files/upstream/repair/zzz-repair_example.template @@ -0,0 +1,41 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1 + +## Custom lock wait and/or lock name +# LOCK_WAIT="15s" +# LOCK_NAME="repair_http" + +pre_repair + +## The name of the service, mainly for logging +service_name="example" +## The systemd service name +systemd_service="${service_name}.service" + +if is_systemd_enabled "${systemd_service}"; then + if is_systemd_active "${systemd_service}"; then + log_abort_and_quit "${systemd_service} is active, nothing left to do." + else + # Save service status before restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 systemctl restart "${systemd_service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status" + fi +else + log_abort_and_quit "${service_name} is disabled (or missing), nothing left to do." +fi + +post_repair diff --git a/autosysadmin-agent/files/upstream/restart/README b/autosysadmin-agent/files/upstream/restart/README new file mode 100644 index 00000000..83a3a9a2 --- /dev/null +++ b/autosysadmin-agent/files/upstream/restart/README @@ -0,0 +1,19 @@ +Autosysadmin "restart auto" scripts +=================================== + +In this directory you can place scripts that will be executed automatically by a cron job (stored in `/etc/cron.d/autosysadmin`). + +They must satisfy the default `run-parts(8)` constraints : + +* be "executable" +* belong to the Debian cron script namespace (`^[a-zA-Z0-9_-]+$`), example: `restart_amavis` + +Warning: scripts that do not satisfy those criteria will NOT be run (silently)! + +You can print the names of the scripts which would be run, without actually running them, with this command : + +``` +$ run-parts --test /usr/share/scripts/autosysadmin/restart +``` + +You can use `zzz-restart_example.template` as boilerplate code to make your own "restart" script. diff --git a/autosysadmin-agent/files/upstream/restart/zzz-restart_example.template b/autosysadmin-agent/files/upstream/restart/zzz-restart_example.template new file mode 100644 index 00000000..1051d132 --- /dev/null +++ b/autosysadmin-agent/files/upstream/restart/zzz-restart_example.template @@ -0,0 +1,120 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/restart.sh" || exit 1 + +# shellcheck disable=SC2034 +RUNNING="nwh-fr" + +## Possible values for RUNNING : +## never => disabled +## always => enabled +## nwh-fr => enabled during non-working-hours in France +## nwh-ca => enabled during non-working-hours in Canada (not supported yet) +## custom => enabled if `running_custom()` function returns 0, otherwise disabled. + +## Uncomment and customize this method if you want to have a special logic : +## +## return 1 if we should not run +## return 0 if we should run +## +## Some available functions : +## is_weekend() : Saturday or Sunday +## is_holiday() : holiday in France (based on `gcal(1)`) +## is_workday() : not weekend and not holiday +## is_worktime() : work day between 9-12h and 14-18h +# +# running_custom() { +# # implement your own custom method to decide if we should run or not +# } + +## The name of the service, mainly for logging +service_name="example" +## The SysVinit script name +sysvinit_script="${service_name}" +## The systemd service name +systemd_service="${service_name}.service" + +is_service_alive() { + ## this must return 0 if the service is alive, otherwise return 1 + ## Example: + pgrep -u USER PROCESS_NAME > /dev/null +} + +## Action for SysVinit system +sysvinit_action() { + # Save service status before restart + timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 "/etc/init.d/${sysvinit_script}" restart > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.after.status" +} + +## Action for systemd system +systemd_action() { + # Save service status before restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + # systemctl (only for NRPE ?) sometimes returns 0 even if the service has failed to start + # so we check the status explicitly + timeout 20 systemctl restart "${systemd_service}" > /dev/null \ + && sleep 1 \ + && systemctl status "${systemd_service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status" +} + +# Should we run? +if ! is_supposed_to_run; then + # log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})." + exit 0 +fi +if is_service_alive; then + # log_global "${service_name} process alive. Aborting" + exit 0 +fi + +# Yes we do, so check for sysvinit or systemd +if is_debian_version "8" "<="; then + if ! is_sysvinit_enabled "*${sysvinit_script}*"; then + # log_global "${service_name} not enabled. Aborting" + exit 0 + fi + + # Let's finally do the action + pre_restart + sysvinit_action + post_restart +else + if ! is_systemd_enabled "${systemd_service}"; then + # log_global "${service_name} is disabled (or missing), nothing left to do." + exit 0 + fi + if is_systemd_active "${systemd_service}"; then + # log_global "${service_name} is active, nothing left to do." + exit 0 + fi + + # Let's finally do the action + pre_restart + systemd_action + post_restart +fi diff --git a/autosysadmin-agent/handlers/main.yml b/autosysadmin-agent/handlers/main.yml new file mode 100644 index 00000000..f192d587 --- /dev/null +++ b/autosysadmin-agent/handlers/main.yml @@ -0,0 +1,16 @@ +--- + +- name: restart nagios-nrpe-server + service: + name: nagios-nrpe-server + state: restarted + +- name: restart nrpe + service: + name: nrpe + state: restarted + +- name: restart rsyslog + service: + name: rsyslog + state: restarted diff --git a/autosysadmin-agent/tasks/crontab.yml b/autosysadmin-agent/tasks/crontab.yml new file mode 100644 index 00000000..1fa090ab --- /dev/null +++ b/autosysadmin-agent/tasks/crontab.yml @@ -0,0 +1,25 @@ +--- + +- name: "Add begin marker if missing" + ansible.builtin.lineinfile: + path: "/etc/cron.d/autosysadmin" + line: "# BEGIN ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN" + insertbefore: BOF + create: yes + +- name: "Add end marker if missing" + ansible.builtin.lineinfile: + path: "/etc/cron.d/autosysadmin" + line: "# END ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN" + insertbefore: "EOF" + create: yes + +- name: "Create config if missing" + ansible.builtin.blockinfile: + path: "/etc/cron.d/autosysadmin" + marker: "# {mark} ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN" + block: "{{ lookup('ansible.builtin.template', '../templates/autosysadmin.cron.j2') }}" + owner: root + group: root + mode: "0750" + create: yes diff --git a/autosysadmin-agent/tasks/dependencies.yml b/autosysadmin-agent/tasks/dependencies.yml new file mode 100644 index 00000000..4f120944 --- /dev/null +++ b/autosysadmin-agent/tasks/dependencies.yml @@ -0,0 +1,4 @@ +--- +- name: Install gcal + ansible.builtin.apt: + name: gcal diff --git a/autosysadmin-agent/tasks/install.yml b/autosysadmin-agent/tasks/install.yml new file mode 100644 index 00000000..b8ecd752 --- /dev/null +++ b/autosysadmin-agent/tasks/install.yml @@ -0,0 +1,114 @@ +--- +- name: "Remount /usr if needed" + ansible.builtin.include_role: + name: remount-usr + +- name: Previous autosysadmin restart directory is renamed + command: + cmd: mv "/usr/share/scripts/autosysadmin/auto" "{{ autosysadmin_agent_auto_dir }}" + removes: "/usr/share/scripts/autosysadmin/auto" + creates: "{{ autosysadmin_agent_auto_dir }}" + +- name: Create autosysadmin directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "root" + group: "root" + mode: "0750" + loop: + - "{{ autosysadmin_agent_bin_dir }}" + - "{{ autosysadmin_agent_lib_dir }}" + - "{{ autosysadmin_agent_auto_dir }}" + +- name: Copy libraries + ansible.builtin.copy: + src: "upstream/lib/" + dest: "{{ autosysadmin_agent_lib_dir }}/" + owner: root + group: root + mode: "0750" + +- name: Copy repair scripts + ansible.builtin.copy: + src: "upstream/repair/" + dest: "{{ autosysadmin_agent_bin_dir }}/" + owner: root + group: root + mode: "0750" + +- name: Copy other utilities + ansible.builtin.copy: + src: "upstream/bin/" + dest: "{{ autosysadmin_agent_bin_dir }}/" + owner: root + group: root + mode: "0750" + +### WARNING: thos files are explicitly marked as non-executable +### to prevent them from being run automatically by run-parts + +- name: Copy restart scripts + ansible.builtin.copy: + src: "upstream/restart/" + dest: "{{ autosysadmin_agent_auto_dir }}/" + owner: root + group: root + mode: "0640" + +- name: Ensure /etc/evolinux folder exists + ansible.builtin.file: + path: "/etc/evolinux" + state: directory + owner: "root" + group: "root" + mode: "0700" + +- name: Copy the configuration file if missing + ansible.builtin.template: + src: "autosysadmin.cf.j2" + dest: "/etc/evolinux/autosysadmin" + owner: root + group: root + mode: "0640" + force: no + +# Repair scripts are supposed to be 'on' by default +# A line "repair_XXX=off" is added to the file only if the script is to be disabled. +# That's why all the ternary logic for the state is reversed. +- name: Update value per variable + ansible.builtin.lineinfile: + dest: "/etc/evolinux/autosysadmin" + line: "{{ item }}={{ autosysadmin_config[item] | default(true) | bool | ternary('on', 'off') }}" + regexp: '^(#\s*)?{{ item }}=.*' + state: "{{ autosysadmin_config[item] | default(true) | bool | ternary('absent', 'present') }}" + register: _line + loop: "{{ autosysadmin_repair_scripts | union(['repair_all']) }}" + +- name: Ensure restart folder exists + ansible.builtin.file: + path: "auto" + state: directory + owner: "root" + group: "root" + mode: "0700" + +- name: Legacy scripts are removed + ansible.builtin.file: + path: "{{ general_scripts_dir }}/autosysadmin/{{ item }}" + state: absent + loop: + - repair_amavis.sh + - repair_disk.sh + - repair_elasticsearch.sh + - repair_http.sh + - repair_mysql.sh + - repair_opendkim.sh + - repair_php_fpm56.sh + - repair_php_fpm70.sh + - repair_php_fpm73.sh + - repair_php_fpm74.sh + - repair_php_fpm80.sh + - repair_php_fpm81.sh + - repair_redis.sh + - repair_tomcat_instance.sh diff --git a/autosysadmin-agent/tasks/logrotate.yml b/autosysadmin-agent/tasks/logrotate.yml new file mode 100644 index 00000000..bf1e55b4 --- /dev/null +++ b/autosysadmin-agent/tasks/logrotate.yml @@ -0,0 +1,8 @@ +--- +- name: Copy logrotate configuration for autosysadmin + ansible.builtin.copy: + src: "files/autosysadmin.logrotate.conf" + dest: "/etc/logrotate.d/autosysadmin" + owner: root + group: root + mode: "0644" diff --git a/autosysadmin-agent/tasks/main.yml b/autosysadmin-agent/tasks/main.yml new file mode 100644 index 00000000..9ac8a7b6 --- /dev/null +++ b/autosysadmin-agent/tasks/main.yml @@ -0,0 +1,31 @@ +--- + +- name: The list of all repair scripts is composed. + set_fact: + autosysadmin_repair_scripts: "{{ lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map('basename') | sort }}" + +- name: Install dependencies + ansible.builtin.include_tasks: dependencies.yml + +- name: Install autosysadmin + ansible.builtin.include_tasks: install.yml + +- name: Crontab configuration + ansible.builtin.include_tasks: crontab.yml + +- name: NRPE configuration + ansible.builtin.include_tasks: nrpe.yml + +- name: sudo configuration + ansible.builtin.include_tasks: sudo.yml + +- name: rsyslog configuration + ansible.builtin.include_tasks: rsyslog.yml + +- name: logrotate configuration + ansible.builtin.include_tasks: logrotate.yml + +- name: Install latest version of dump-server-state + ansible.builtin.include_role: + name: evolinux-base + tasks_from: dump-server-state.yml diff --git a/autosysadmin-agent/tasks/nrpe.yml b/autosysadmin-agent/tasks/nrpe.yml new file mode 100644 index 00000000..b5a31922 --- /dev/null +++ b/autosysadmin-agent/tasks/nrpe.yml @@ -0,0 +1,9 @@ +--- +- name: custom configuration is present + ansible.builtin.template: + src: autosysadmin.nrpe.cfg.j2 + dest: /etc/nagios/nrpe.d/autosysadmin.cfg + group: nagios + mode: "0640" + force: yes + notify: restart nagios-nrpe-server diff --git a/autosysadmin-agent/tasks/rsyslog.yml b/autosysadmin-agent/tasks/rsyslog.yml new file mode 100644 index 00000000..bb57f24a --- /dev/null +++ b/autosysadmin-agent/tasks/rsyslog.yml @@ -0,0 +1,9 @@ +--- +- name: Copy rsyslog configuration for autosysadmin + ansible.builtin.copy: + src: "files/autosysadmin.rsyslog.conf" + dest: "/etc/rsyslog.d/autosysadmin.conf" + owner: root + group: root + mode: "0644" + notify: restart rsyslog diff --git a/autosysadmin-agent/tasks/sudo.yml b/autosysadmin-agent/tasks/sudo.yml new file mode 100644 index 00000000..a4fd35be --- /dev/null +++ b/autosysadmin-agent/tasks/sudo.yml @@ -0,0 +1,7 @@ +--- +- name: Add autosysadmin sudoers file + ansible.builtin.template: + src: autosysadmin.sudoers.j2 + dest: /etc/sudoers.d/autosysadmin + mode: "0600" + validate: "visudo -cf %s" diff --git a/autosysadmin-agent/templates/autosysadmin.cf.j2 b/autosysadmin-agent/templates/autosysadmin.cf.j2 new file mode 100644 index 00000000..763958ba --- /dev/null +++ b/autosysadmin-agent/templates/autosysadmin.cf.j2 @@ -0,0 +1,12 @@ +# This configuration is partially managed by Ansible +# You can change specific values manually, but they may be overridden by Ansible +# +# To be safe, update the hosts_vars/group_vars in the autosysadmin project +# https://gitea.evolix.org/evolix/autosysadmin/src/branch/master +# then use the "agent" playbook to deploy. +# +# Configuration for autosysadmin +# Use this file to change configuration values defined in repair scripts +# To disable all repair scripts : repair_all=off +# To disable "repair_http" : repair_http=off +# \ No newline at end of file diff --git a/autosysadmin-agent/templates/autosysadmin.cron.j2 b/autosysadmin-agent/templates/autosysadmin.cron.j2 new file mode 100644 index 00000000..90823d5e --- /dev/null +++ b/autosysadmin-agent/templates/autosysadmin.cron.j2 @@ -0,0 +1,7 @@ +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# Run each enabled script +*/5 * * * * root run-parts /usr/share/scripts/autosysadmin/restart + +# Clean run log files +@weekly root {{ autosysadmin_agent_bin_dir | mandatory }}/delete_old_logs.sh {{ autosysadmin_agent_log_retention_days | default('365') }} diff --git a/autosysadmin-agent/templates/autosysadmin.nrpe.cfg.j2 b/autosysadmin-agent/templates/autosysadmin.nrpe.cfg.j2 new file mode 100644 index 00000000..c3e1a40c --- /dev/null +++ b/autosysadmin-agent/templates/autosysadmin.nrpe.cfg.j2 @@ -0,0 +1,8 @@ +# +# Ansible managed - DO NOT MODIFY, your changes will be overwritten ! +# + +# Autosysadmin repair commands +{% for script in lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map("basename") | sort %} +command[{{ script }}]=sudo {{ autosysadmin_agent_bin_dir }}/{{ script }} +{% endfor %} \ No newline at end of file diff --git a/autosysadmin-agent/templates/autosysadmin.sudoers.j2 b/autosysadmin-agent/templates/autosysadmin.sudoers.j2 new file mode 100644 index 00000000..f182bb84 --- /dev/null +++ b/autosysadmin-agent/templates/autosysadmin.sudoers.j2 @@ -0,0 +1,7 @@ +# +# Ansible managed - DO NOT MODIFY, your changes will be overwritten ! +# + +{% for script in lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map("basename") | sort %} +nagios ALL = NOPASSWD: {{ autosysadmin_agent_bin_dir }}/{{ script }} +{% endfor %} \ No newline at end of file diff --git a/autosysadmin-restart_nrpe/defaults/main.yml b/autosysadmin-restart_nrpe/defaults/main.yml new file mode 100644 index 00000000..3d743a1b --- /dev/null +++ b/autosysadmin-restart_nrpe/defaults/main.yml @@ -0,0 +1,8 @@ +--- + +general_scripts_dir: "/usr/share/scripts" + +restart_nrpe_path: "{{ general_scripts_dir }}/autosysadmin/restart/restart_nrpe" + +# Change this to customize the RUNNING value in the script +restart_nrpe_running: Null diff --git a/autosysadmin-restart_nrpe/files/upstream/restart_nrpe b/autosysadmin-restart_nrpe/files/upstream/restart_nrpe new file mode 100755 index 00000000..b2dd7f44 --- /dev/null +++ b/autosysadmin-restart_nrpe/files/upstream/restart_nrpe @@ -0,0 +1,105 @@ +#!/bin/bash + +: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}" +source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1 +source "${AUTOSYSADMIN_LIB}/restart.sh" || exit 1 + +## Possible values for RUNNING : +## never => disabled +## always => enabled +## nwh-fr => enabled during non-working-hours in France +## nwh-ca => enabled during non-working-hours in Canada (not supported yet) +## custom => enabled if `running_custom()` function return 0, otherwise disabled. + +# shellcheck disable=SC2034 +RUNNING="nwh-fr" + +## The name of the service, mainly for logging +service_name="nagios-nrpe-server" +## The SysVinit script name +sysvinit_script="${service_name}" +## The systemd service name +systemd_service="${service_name}.service" + +is_service_alive() { + ## this must return 0 if the service is alive, otherwise return 1 + ## Example: + pgrep -u nagios nrpe > /dev/null +} + +## Action for SysVinit system +sysvinit_action() { + # Save service status before restart + timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.before.status" + + # Try to restart + timeout 20 "/etc/init.d/${sysvinit_script}" restart > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.after.status" +} + +## Action for systemd system +systemd_action() { + # Save service status before restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status" + + # Try to restart + # systemctl (only for NRPE ?) sometimes returns 0 even if the service has failed to start + # so we check the status explicitly + timeout 20 systemctl restart "${systemd_service}" > /dev/null \ + && sleep 1 \ + && systemctl status "${systemd_service}" > /dev/null + rc=$? + if [ "${rc}" -eq "0" ]; then + log_action "Restart ${service_name}: OK" + else + log_action "Restart ${service_name}: failed" + fi + + # Save service status after restart + systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status" +} + +# Should we run? +if ! is_supposed_to_run; then + # log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})." + exit 0 +fi +if is_service_alive; then + # log_global "${service_name} process alive. Aborting" + exit 0 +fi + +# Yes we do, so check for sysvinit or systemd +if is_debian_version "8" "<="; then + if ! is_sysvinit_enabled "*${sysvinit_script}*"; then + # log_global "${service_name} not enabled. Aborting" + exit 0 + fi + + # Let's finally do the action + pre_restart + sysvinit_action + post_restart +else + if ! is_systemd_enabled "${systemd_service}"; then + # log_global "${service_name} is disabled (or missing), nothing left to do." + exit 0 + fi + if is_systemd_active "${systemd_service}"; then + # log_global "${service_name} is active, nothing left to do." + exit 0 + fi + + # Let's finally do the action + pre_restart + systemd_action + post_restart +fi diff --git a/autosysadmin-restart_nrpe/tasks/main.yml b/autosysadmin-restart_nrpe/tasks/main.yml new file mode 100644 index 00000000..7a8ad5b0 --- /dev/null +++ b/autosysadmin-restart_nrpe/tasks/main.yml @@ -0,0 +1,24 @@ +--- + + - name: "Remount /usr if needed" + ansible.builtin.include_role: + name: remount-usr + + - name: "Copy restart_nrpe" + ansible.builtin.copy: + src: upstream/restart_nrpe + dest: "{{ restart_nrpe_path }}" + owner: "root" + group: "root" + mode: "0750" + + - name: "Customize RUNNING value" + ansible.builtin.lineinfile: + path: "{{ restart_nrpe_path }}" + line: "RUNNING=\"{{ restart_nrpe_running }}\"" + regexp: "^ *RUNNING=" + create: False + when: + - restart_nrpe_running is defined + - restart_nrpe_running != None + - restart_nrpe_running | length > 0 diff --git a/bind/files/bind-reload-zone.sh b/bind/files/bind-reload-zone.sh new file mode 100755 index 00000000..550e5b64 --- /dev/null +++ b/bind/files/bind-reload-zone.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Script utilitaire pour tester et recharger facilement une zone dans Bind +# + +usage() { + echo "Usage: bind-reload-zone " + echo " bind-reload-zone -h|--help" +} + +if [ $# -ne 1 ] ; then + usage + exit 1 +fi + +while :; do + case $1 in + -h|--help) + usage + exit 0 + ;; + *) + zone=$1 + break + ;; + esac + shift +done + +if ! [ -f "/etc/bind/db.${zone}" ]; then + >&2 echo "Error: zone for ${zone} not found." + usage + exit 1 +fi + +named-checkzone "${zone}" /etc/bind/db."${zone}" && rndc reload "${zone}" + diff --git a/bind/files/bind-reload-zone_completion.sh b/bind/files/bind-reload-zone_completion.sh new file mode 100644 index 00000000..2e87b12c --- /dev/null +++ b/bind/files/bind-reload-zone_completion.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +_bind_reload_zone_dynamic_completion() { + local cur; + cur=${COMP_WORDS[COMP_CWORD]}; + COMPREPLY=(); + COMPREPLY=( $( compgen -W '$(grep -v -h '"'"'//'"'"' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep -v arpa | awk '"'"'{gsub(/"/, "", $2); print $2}'"'"' | sort | uniq)' -- $cur ) ); + + # reverse ipv4 : + #grep -v -h '//' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep arpa | grep -v ip6 | awk '{gsub(/"/, "", $2); gsub(/.in-addr.arpa/, "", $2); print $2}' | sort | uniq | awk -F'.' '{ for (i=NF; i>1; i--) printf("%s.",$i); print $1 }' + + # reveres ipv6 : je bloque sur l'inversion 4 par 4 + #grep -v -h '//' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep arpa | grep ip6 | awk '{gsub(/"/, "", $2); gsub(/.ip6.arpa/, "", $2); print $2}' | sort | uniq | awk -F'.' '{ for (i=NF; i>1; i--) { if ($i % 4 == 0) printf("%s.",$i); else printf("%s",$i); } print $1 }' + +} + +complete -F _bind_reload_zone_dynamic_completion bind-reload-zone + diff --git a/bind/files/reload-zone b/bind/files/reload-zone deleted file mode 100755 index b9acc449..00000000 --- a/bind/files/reload-zone +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# -# Script utilitaire pour tester et recharger facilement un domaine dans Bind -# Usage : reload-zone -# -# TODO: -# - renommer le script (par ex bind-safe-reload) -# - vérifier le serial -# - ajouter un -h --help -# - prendre en charge plusieurs zones (ou aucune) -# - ajouter le script dans le role bind - -named-checkzone "$1" /etc/bind/db."$1" && rndc reload "$1" - diff --git a/bind/handlers/main.yml b/bind/handlers/main.yml index 5461579d..85eaa680 100644 --- a/bind/handlers/main.yml +++ b/bind/handlers/main.yml @@ -3,7 +3,6 @@ ansible.builtin.systemd: daemon-reload: yes - - name: restart apparmor ansible.builtin.systemd: name: apparmor diff --git a/bind/meta/main.yml b/bind/meta/main.yml index 6cf180b1..533f4335 100644 --- a/bind/meta/main.yml +++ b/bind/meta/main.yml @@ -14,6 +14,8 @@ galaxy_info: - jessie - stretch - buster + - bullseye + - bookworm galaxy_tags: [] # Be sure to remove the '[]' above if you add dependencies diff --git a/certbot/files/hooks/deploy/dovecot.sh b/certbot/files/hooks/deploy/dovecot.sh index 56e5b5ae..1ed4ab5d 100644 --- a/certbot/files/hooks/deploy/dovecot.sh +++ b/certbot/files/hooks/deploy/dovecot.sh @@ -16,7 +16,7 @@ config_check() { ${doveconf_bin} > /dev/null 2>&1 } letsencrypt_used() { - ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt" + ${doveconf_bin} | grep -E "^[[:blank:]]*ssl_cert[^_]" | grep -q "letsencrypt" } main() { if daemon_found_and_running; then diff --git a/certbot/files/hooks/deploy/hapee.sh b/certbot/files/hooks/deploy/hapee.sh index 89b04452..d39da25b 100644 --- a/certbot/files/hooks/deploy/hapee.sh +++ b/certbot/files/hooks/deploy/hapee.sh @@ -39,8 +39,8 @@ concat_files() { chown root: "${hapee_cert_file}" } cert_and_key_mismatch() { - hapee_cert_md5=$(openssl x509 -noout -modulus -in "${hapee_cert_file}" | openssl md5) - hapee_key_md5=$(openssl rsa -noout -modulus -in "${hapee_cert_file}" | openssl md5) + hapee_cert_md5=$(openssl x509 -noout -pubkey -in "${hapee_cert_file}" | openssl md5) + hapee_key_md5=$(openssl pkey -noout -pubout -in "${hapee_cert_file}" | openssl md5) test "${hapee_cert_md5}" != "${hapee_key_md5}" } diff --git a/certbot/files/hooks/deploy/haproxy.sh b/certbot/files/hooks/deploy/haproxy.sh index 932a3e90..36a09262 100644 --- a/certbot/files/hooks/deploy/haproxy.sh +++ b/certbot/files/hooks/deploy/haproxy.sh @@ -29,8 +29,8 @@ concat_files() { chown root: "${haproxy_cert_file}" } cert_and_key_mismatch() { - haproxy_cert_md5=$(openssl x509 -noout -modulus -in "${haproxy_cert_file}" | openssl md5) - haproxy_key_md5=$(openssl rsa -noout -modulus -in "${haproxy_cert_file}" | openssl md5) + haproxy_cert_md5=$(openssl x509 -noout -pubkey -in "${haproxy_cert_file}" | openssl md5) + haproxy_key_md5=$(openssl pkey -pubout -in "${haproxy_cert_file}" | openssl md5) test "${haproxy_cert_md5}" != "${haproxy_key_md5}" } diff --git a/certbot/files/hooks/deploy/nrpe.sh b/certbot/files/hooks/deploy/nrpe.sh new file mode 100644 index 00000000..578d6764 --- /dev/null +++ b/certbot/files/hooks/deploy/nrpe.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} +daemon_found_and_running() { + test -n "$(pidof nrpe)" +} +letsencrypt_lineaged_used() { + grep -r "^ssl_cert_file" /etc/nagios/ | grep "letsencrypt" | grep -q "$(basename "${RENEWED_LINEAGE}")" +} +copy_letsencrypt_cert() { + DEST_CERTIFICATE=$(grep -r "^ssl_cert_file" /etc/nagios/ | awk -F'=' '{print $2}') + DEST_PRIVATE_KEY=$(grep -r "^ssl_privatekey_file" /etc/nagios/ | awk -F'=' '{print $2}') + + install --mode 440 --group nagios ${RENEWED_LINEAGE}/fullchain.pem ${DEST_CERTIFICATE} + install --mode 440 --group nagios ${RENEWED_LINEAGE}/privkey.pem ${DEST_PRIVATE_KEY} +} +main() { + if daemon_found_and_running; then + if letsencrypt_lineaged_used; then + debug "NRPE detected... Copying certificates to the right place & permissions" + copy_letsencrypt_cert + debug "Restarting NRPE" + systemctl restart nagios-nrpe-server + else + debug "NRPE doesn't use the given Let's Encrypt certificate. Skip." + fi + else + debug "NRPE is not running or missing. Skip." + fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +main \ No newline at end of file diff --git a/certbot/tasks/main.yml b/certbot/tasks/main.yml index 3dcb1334..a32aa630 100644 --- a/certbot/tasks/main.yml +++ b/certbot/tasks/main.yml @@ -21,6 +21,8 @@ - ansible.builtin.include: acme-challenge.yml +# This is always going to mark a "change". +# Couldn't figure out why ! - name: Deploy hooks are present ansible.builtin.copy: src: hooks/deploy/ diff --git a/check_free_space/defaults/main.yml b/check_free_space/defaults/main.yml new file mode 100644 index 00000000..c699ad91 --- /dev/null +++ b/check_free_space/defaults/main.yml @@ -0,0 +1,6 @@ +--- +check_free_space_partitions: + - "/home" + - "/srv" +check_free_space_max_percent: 70 +check_free_space_mailto: Null diff --git a/check_free_space/files/check_free_space.sh b/check_free_space/files/check_free_space.sh new file mode 100755 index 00000000..f1b89d8c --- /dev/null +++ b/check_free_space/files/check_free_space.sh @@ -0,0 +1,166 @@ +#!/bin/sh + +# This script verifies if the specified partitions on a machine are filled +# at more than x%. +# +# If so, it sends a mail to the admin of that machine, warning him/her +# that mesures should be taken. +# +# Two outputs are provided to the recipient of the mail: +# * some general infos with `df` +# * a more indepth inspection with `duc` +# +# This script takes 3 (mandatory) arguments: +# * a list of the partitions to check (space separated) +# * the maximum allowed percentage +# * the email template to use +# +# This script should be ran by cron @daily. +# +# +# Copyright (C) 2016 Louis-Philippe Véronneau +# +# This program is licensed under GPLv3 + + + +# Check argument sanity + +PID_FILE='/var/run/check_free_space.pid' + +if test -f "$PID_FILE" +then + pid=$(cat "$PID_FILE") + ps -p "$pid" > /dev/null + if test $? -eq 0 + then + echo "$0 already run !" >&2 + exit 1 + else + rm $PID_FILE + fi +fi + +echo $$ > $PID_FILE + +if test -z "$1" || test -z "$2" || test -z "$3" # is non null +then + echo "Some arguments are missing. Please issue a partition list, a" \ + "maximum percentage and an email template." + exit 1 +elif ! [ "$2" -le 100 -a "$2" -ge 0 ] # is a percentage +then + echo "Please enter a maximum percentage value between 0 and 100." + exit 1 +fi + +# Argument processing + +partition_list=$1 +max_percentage=$((100-$2)) +email_template=$3 + +HOSTNAME=$(hostname) +debian_version=$(lsb_release -c) + +check_disk='/usr/lib/nagios/plugins/check_disk' + +test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf + + +# Test what version of df we have + +old_df=false + +case "$debian_version" in + *squeeze* ) old_df=true ;; + *wheezy* ) old_df=true ;; +esac + + +# Check disk space + +df_options="size,avail,pcent,itotal,iavail,ipcent" + +for partition in $partition_list +do + if ! $check_disk -w $max_percentage% -W $max_percentage% $partition > /dev/null + then + # the 'newline' is a hack to make sed behave + PARTITION_DATA="$PARTITION_DATA newline $partition newline" + if [ $old_df ] + then + PARTITION_DATA="$PARTITION_DATA $(/bin/df -h $partition) newline" + PARTITION_DATA="$PARTITION_DATA newline $(df -ih $partition) newlinenewline" + else + PARTITION_DATA="$PARTITION_DATA $(/bin/df -h --output=$df_options $partition) newline" + fi + full_partitions="$full_partitions $partition" + partname=$(echo $partition|tr -s '/' '-') + graph_list="$graph_list -a /home/duc${partname}.png" + fi +done + + +# Exit if everything is OK + +if test -z "$PARTITION_DATA" +then + exit 0 +fi + + +# If there is indeed a problem, get more infos with duc + +/usr/bin/ionice -c3 /usr/bin/duc index -H -d /home/duc.idx -x $full_partitions -q + +for partition in $full_partitions +do + duc_temp=$(/usr/bin/duc ls -d /home/duc.idx -Fg $partition) + duc_temp=$(printf "$duc_temp" | sed -e "s@]@]newline@" | grep -v "lost+found") + DUC_OUTPUT="$DUC_OUTPUT newline$partition newline$duc_temp" + partname=$(echo $partition|tr -s '/' '-') + duc graph -d /home/duc.idx -o /home/duc${partname}.png -l8 -s 1024 $partition +done + + +# Replace placeholders & send the mail ! + +PARTITION_DATA="$(echo "$PARTITION_DATA"|tr -d $'\n')" # make sed accept the input +DUC_OUTPUT="$(echo "$DUC_OUTPUT"|tr -d $'\n')" + +if [ $old_df ] +then + sed -e "s/__TO__/$EVOMAINTMAIL/" \ + -e "s/__HOSTNAME__/$HOSTNAME/" \ + -e "s@__PARTITION_DATA__@$PARTITION_DATA@" \ + -e "s@__DUC_OUTPUT__@$DUC_OUTPUT@" \ + -e "s/newline/\n/g" \ + -e "s/IUse%/IUse%\n/g" \ + -e "s/ Use%/ Use%\n/g" \ + -e "s@Filesystem \{12\}@@g" \ + -e "s@Mounted on\/dev\/[a-z]\{3\}[0-9]\+ \{13\}@@g" \ + -e "s@% \/[a-z]\+@%@g" \ + -e "s/__MAX_PERCENTAGE__/$max_percentage/" \ + -e "s/__FULLFROM__/$FULLFROM/" \ + -e "s/__FROM__/$FROM/" \ + -e "s/__URGENCYFROM__/$URGENCYFROM/" \ + -e "s/__URGENCYTEL__/$URGENCYTEL/" \ + $email_template | \ + /usr/bin/mutt -H - $graph_list +else + sed -e "s/__TO__/$EVOMAINTMAIL/" \ + -e "s/__HOSTNAME__/$HOSTNAME/" \ + -e "s@__PARTITION_DATA__@$PARTITION_DATA@" \ + -e "s@__DUC_OUTPUT__@$DUC_OUTPUT@" \ + -e "s/newline/\n/g" \ + -e "s/IUse%/IUse%\n/g" \ + -e "s/__MAX_PERCENTAGE__/$max_percentage/" \ + -e "s/__FULLFROM__/$FULLFROM/" \ + -e "s/__FROM__/$FROM/" \ + -e "s/__URGENCYFROM__/$URGENCYFROM/" \ + -e "s/__URGENCYTEL__/$URGENCYTEL/" \ + $email_template | \ + /usr/bin/mutt -H - $graph_list +fi + +rm -f $PID_FILE diff --git a/check_free_space/files/check_free_space.tpl b/check_free_space/files/check_free_space.tpl new file mode 100644 index 00000000..15ce5635 --- /dev/null +++ b/check_free_space/files/check_free_space.tpl @@ -0,0 +1,24 @@ +From: __FULLFROM__ +Content-Type: text/plain; charset=UTF-8 +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +To: __TO__ +Subject: [WARNING] Espace disque faible sur __HOSTNAME__ + +Bonjour, + +Ceci est un message automatique pour vous informer qu'il y a un +souci d'espace disque sur votre serveur __HOSTNAME__ + +Voici les informations sur l'espace disque qui pose problème : +__PARTITION_DATA__ +Détails sur les partitions problématiques : +__DUC_OUTPUT__ +Un graphe par partition problématique est disponible en pièce jointe. + +Nous vous recommandons d'effectuer du ménage pour maintenir +chaque partition avec un minimum de __MAX_PERCENTAGE__% d'espace disque libre. + +Cordialement, +-- +__FULLFROM__ diff --git a/check_free_space/tasks/main.yml b/check_free_space/tasks/main.yml new file mode 100644 index 00000000..b2b4aa07 --- /dev/null +++ b/check_free_space/tasks/main.yml @@ -0,0 +1,37 @@ +--- +- ansible.builtin.include_role: + name: evolix/remount-usr + +- name: Copy check_free_space.sh script + ansible.builtin.copy: + src: files/check_free_space.sh + dest: /usr/share/scripts/check_free_space + owner: root + group: root + mode: "0750" + +- name: Copy email template + ansible.builtin.copy: + src: files/check_free_space.tpl + dest: /usr/share/scripts/check_free_space.tpl + owner: root + group: root + mode: "0644" + +# not using the cron_module for this since it is buggy +- name: check_free_space.sh is run by cron + ansible.builtin.template: + src: templates/cron_check_free_space.j2 + dest: /etc/cron.d/check_free_space + owner: root + group: root + mode: "0644" + force: false + +- name: Duc and Mutt are installed + ansible.builtin.apt: + pkg: + - mutt + - duc + state: present + diff --git a/check_free_space/tasks/shell_script.yml b/check_free_space/tasks/shell_script.yml new file mode 100644 index 00000000..716304a1 --- /dev/null +++ b/check_free_space/tasks/shell_script.yml @@ -0,0 +1,30 @@ +--- + +- include_role: + name: evolix/remount-usr + +- name: shell script + copy: + src: files/check_free_space.sh + dest: /usr/share/scripts/check_free_space + owner: root + group: root + mode: "0750" + +- name: email template + copy: + src: files/check_free_space.tpl + dest: /usr/share/scripts/check_free_space.tpl + owner: root + group: root + mode: "0644" + +# not using the cron_module for this since it is buggy +- name: cron + template: + src: templates/cron_check_free_space.j2 + dest: /etc/cron.d/check_free_space + owner: root + group: root + mode: "0644" + force: false diff --git a/check_free_space/templates/cron_check_free_space.j2 b/check_free_space/templates/cron_check_free_space.j2 new file mode 100644 index 00000000..5017a67b --- /dev/null +++ b/check_free_space/templates/cron_check_free_space.j2 @@ -0,0 +1,4 @@ +{% if check_free_space_mailto and check_free_space_mailto != "" %} +MAILTO={{ check_free_space_mailto }} +{% endif %} +30 4 * * 1 root /usr/share/scripts/check_free_space "{{ check_free_space_partitions | join(' ') }}" {{ check_free_space_max_percent }} /usr/share/scripts/check_free_space.tpl diff --git a/docker-host/tasks/main.yml b/docker-host/tasks/main.yml index 556570f5..ec3781e7 100644 --- a/docker-host/tasks/main.yml +++ b/docker-host/tasks/main.yml @@ -119,4 +119,4 @@ cmd: "{{ docker_tls_path }}/shellpki.sh init" when: - docker_tls_enabled | bool - - not tls_certs_stat.stat.isdir + - not (tls_certs_stat.stat.exists and tls_certs_stat.stat.isdir) diff --git a/dovecot/defaults/main.yml b/dovecot/defaults/main.yml index 52e06bda..e190adb6 100644 --- a/dovecot/defaults/main.yml +++ b/dovecot/defaults/main.yml @@ -1,4 +1,12 @@ --- +general_alert_email: "root@localhost" +log2mail_alert_email: Null + dovecot_vmail_uid: 5000 dovecot_vmail_gid: 5000 + +ldap_hostname: "{{ ansible_hostname }}" +ldap_domain: "{{ ansible_domain }}" +ldap_suffix: "dc={{ ldap_hostname }},dc={{ ldap_domain.split('.')[-2] }},dc={{ ldap_domain.split('.')[-1] }}" +ldap_enabled: False diff --git a/dovecot/files/munin_plugin_dovecot1 b/dovecot/files/munin_plugin_dovecot1 old mode 100644 new mode 100755 index 83f4d897..27139b73 --- a/dovecot/files/munin_plugin_dovecot1 +++ b/dovecot/files/munin_plugin_dovecot1 @@ -53,15 +53,17 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { exit 0; } -if (-f "$logfile.0") { - $rotlogfile = $logfile . ".0"; -} elsif (-f "$logfile.1") { - $rotlogfile = $logfile . ".1"; -} elsif (-f "$logfile.01") { - $rotlogfile = $logfile . ".01"; -} else { - $rotlogfile = $logfile . ".0"; -} +# Disable rotated log inpection because name is not deterministic across systems +# and data loss is may 5 min +#if (-f "$logfile.0") { +# $rotlogfile = $logfile . ".0"; +#} elsif (-f "$logfile.1") { +# $rotlogfile = $logfile . ".1"; +#} elsif (-f "$logfile.01") { +# $rotlogfile = $logfile . ".01"; +#} else { +# $rotlogfile = $logfile . ".0"; +#} if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "multigraph dovecot_connections\n"; @@ -179,7 +181,9 @@ if (!defined $pos) { if ($startsize < $pos) { # Log rotated - parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); + # Disable rotated log inpection because name is not deterministic across systems + # and data loss is may 5 min + #parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); $pos = 0; } diff --git a/dovecot/files/z-evolinux-dovecot.conf b/dovecot/files/munin_plugins.conf similarity index 100% rename from dovecot/files/z-evolinux-dovecot.conf rename to dovecot/files/munin_plugins.conf diff --git a/dovecot/tasks/main.yml b/dovecot/tasks/main.yml index adb81238..8589a701 100644 --- a/dovecot/tasks/main.yml +++ b/dovecot/tasks/main.yml @@ -1,3 +1,5 @@ +--- + - name: ensure packages are installed ansible.builtin.apt: name: @@ -8,7 +10,7 @@ - dovecot-managesieved state: present tags: - - dovecot + - dovecot - name: Generate 4096 bits Diffie-Hellman parameters (may take several minutes) community.crypto.openssl_dhparam: @@ -21,7 +23,7 @@ regexp: "[^#]!include auth-system.conf.ext" replace: "#!include auth-system.conf.ext" tags: - - dovecot + - dovecot - name: update ldap auth ansible.builtin.lineinfile: @@ -33,14 +35,15 @@ - { key: 'hosts', value: '127.0.0.1' } - { key: 'auth_bind', value: 'yes' } - { key: 'ldap_version', value: 3 } - - { key: 'base', value: "{{ ldap_suffix }}" } + - { key: 'base', value: "{{ ldap_suffix | mandatory }}" } - { key: 'user_attrs', value: 'homeDirectory=home' } - { key: 'user_filter', value: '(&(isActive=TRUE)(uid=%u))' } - { key: 'pass_attrs', value: 'uid=user,userPassword=password' } - when: ldap_suffix is defined + - { key: 'iterate_filter', value: '(&(isActive=TRUE))' } + when: ldap_enabled | bool | default(False) notify: reload dovecot tags: - - dovecot + - dovecot - name: create vmail group ansible.builtin.group: @@ -48,7 +51,7 @@ gid: "{{ dovecot_vmail_gid }}" system: True tags: - - dovecot + - dovecot - name: create vmail user ansible.builtin.user: @@ -58,16 +61,16 @@ shell: /bin/false system: True tags: - - dovecot + - dovecot -- name: deploy evolix config +- name: deploy evolix config for Dovecot ansible.builtin.template: src: z-evolinux-defaults.conf.j2 dest: /etc/dovecot/conf.d/z-evolinux-defaults.conf mode: "0644" notify: reload dovecot tags: - - dovecot + - dovecot - name: deploy file for custom configuration ansible.builtin.template: @@ -76,7 +79,7 @@ mode: "0644" notify: reload dovecot tags: - - dovecot + - dovecot - ansible.builtin.include: munin.yml tags: @@ -86,7 +89,8 @@ ansible.builtin.apt: name: log2mail state: present - tags: dovecot + tags: + - dovecot - name: dovecot is configured in log2mail ansible.builtin.blockinfile: @@ -101,5 +105,6 @@ mailto = {{ log2mail_alert_email or general_alert_email | mandatory }} template = /etc/log2mail/mail notify: restart log2mail - tags: dovecot + tags: + - dovecot diff --git a/dovecot/tasks/munin.yml b/dovecot/tasks/munin.yml index 7227e991..4c3daf4a 100644 --- a/dovecot/tasks/munin.yml +++ b/dovecot/tasks/munin.yml @@ -9,15 +9,18 @@ - name: Munin plugins are present and configured block: - - name: Disable dovecot plugin + - name: Disable Dovecot plugin ansible.builtin.file: path: /etc/munin/plugins/dovecot state: absent - - name: Remove dovecot plugin conf + - name: Remove old Dovecot plugin conf ansible.builtin.file: - path: /etc/munin/plugin-conf.d/dovecot + path: "/etc/munin/plugin-conf.d/{{ item }}" state: absent + loop: + - dovecot + - z-evolinux-dovecot - name: "Remount /usr if needed" ansible.builtin.include_role: @@ -46,8 +49,8 @@ - name: Copy Munin config ansible.builtin.copy: - src: z-evolinux-dovecot.conf - dest: /etc/munin/plugin-conf.d/z-evolinux-dovecot + src: munin_plugins.conf + dest: /etc/munin/plugin-conf.d/zzz-dovecot mode: '0644' notify: restart munin-node diff --git a/etc-git/tasks/repositories.yml b/etc-git/tasks/repositories.yml index 6c76d265..7745183d 100644 --- a/etc-git/tasks/repositories.yml +++ b/etc-git/tasks/repositories.yml @@ -24,14 +24,36 @@ - ansible.builtin.include_role: name: evolix/remount-usr when: - - _usr_share_scripts.stat.isdir + - _usr_share_scripts.stat.exists and _usr_share_scripts.stat.isdir - ansible.builtin.import_tasks: repository.yml vars: repository_path: "/usr/share/scripts" gitignore_items: [] when: - - _usr_share_scripts.stat.isdir + - _usr_share_scripts.stat.exists and _usr_share_scripts.stat.isdir - ansible_distribution_major_version is version('10', '>=') tags: - - etc-git \ No newline at end of file + - etc-git + + +- name: verify /var/chroot-bind/ presence + ansible.builtin.stat: + path: /var/chroot-bind + register: _var_chroot_bind + tags: + - etc-git + +- name: /var/chroot-bind/etc/bind is a safe directory + ansible.builtin.shell: git config --global --add safe.directory /var/chroot-bind/etc/bind + +- ansible.builtin.import_tasks: repository.yml + vars: + repository_path: "/var/chroot-bind/etc/bind" + gitignore_items: [] + when: + - _var_chroot_bind.stat.exists and _var_chroot_bind.stat.isdir + - ansible_distribution_major_version is version('8', '>=') + tags: + - etc-git + diff --git a/evocheck/files/evocheck.jessie.sh b/evocheck/files/evocheck.jessie.sh index fba55fd5..585dd735 100755 --- a/evocheck/files/evocheck.jessie.sh +++ b/evocheck/files/evocheck.jessie.sh @@ -4,7 +4,7 @@ # Script to verify compliance of a Linux (Debian) server # powered by Evolix -VERSION="23.10" +VERSION="24.01" readonly VERSION # base functions diff --git a/evocheck/files/evocheck.sh b/evocheck/files/evocheck.sh index 89f17aba..f125e713 100755 --- a/evocheck/files/evocheck.sh +++ b/evocheck/files/evocheck.sh @@ -4,7 +4,7 @@ # Script to verify compliance of a Linux (Debian) server # powered by Evolix -VERSION="23.10" +VERSION="24.01" readonly VERSION # base functions @@ -68,6 +68,8 @@ detect_os() { 10) DEBIAN_RELEASE="buster";; 11) DEBIAN_RELEASE="bullseye";; 12) DEBIAN_RELEASE="bookworm";; + 13) DEBIAN_RELEASE="trixie";; + 14) DEBIAN_RELEASE="forky";; esac fi fi @@ -85,6 +87,12 @@ is_debian_bullseye() { is_debian_bookworm() { test "${DEBIAN_RELEASE}" = "bookworm" } +is_debian_trixie() { + test "${DEBIAN_RELEASE}" = "trixie" +} +is_debian_forky() { + test "${DEBIAN_RELEASE}" = "forky" +} is_pack_web(){ test -e /usr/share/scripts/web-add.sh || test -e /usr/share/scripts/evoadmin/web-add.sh @@ -148,13 +156,13 @@ check_dpkgwarning() { # Check if localhost, localhost.localdomain and localhost.$mydomain are set in Postfix mydestination option. check_postfix_mydestination() { # shellcheck disable=SC2016 - if ! grep mydestination /etc/postfix/main.cf | grep --quiet -E 'localhost([[:blank:]]|$)'; then + if ! grep mydestination /etc/postfix/main.cf | grep --quiet --extended-regexp 'localhost([[:blank:]]|$)'; then failed "IS_POSTFIX_MYDESTINATION" "'localhost' is missing in Postfix mydestination option." fi - if ! grep mydestination /etc/postfix/main.cf | grep --quiet 'localhost\.localdomain'; then + if ! grep mydestination /etc/postfix/main.cf | grep --quiet --fixed-strings 'localhost.localdomain'; then failed "IS_POSTFIX_MYDESTINATION" "'localhost.localdomain' is missing in Postfix mydestination option." fi - if ! grep mydestination /etc/postfix/main.cf | grep --quiet 'localhost\.\$mydomain'; then + if ! grep mydestination /etc/postfix/main.cf | grep --quiet --fixed-strings 'localhost.$mydomain'; then failed "IS_POSTFIX_MYDESTINATION" "'localhost.\$mydomain' is missing in Postfix mydestination option." fi } @@ -193,11 +201,40 @@ check_debiansecurity() { apt-cache policy | grep "\bl=Debian-Security\b" | grep "\bo=Debian\b" | grep --quiet "\bc=main\b" test $? -eq 0 || failed "IS_DEBIANSECURITY" "missing Debian-Security repository" } +check_debiansecurity_lxc() { + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + DEBIAN_LXC_VERSION=$(cut -d "." -f 1 < /var/lib/lxc/${container}/rootfs/etc/debian_version) + if [ $DEBIAN_LXC_VERSION -ge 9 ]; then + lxc-attach --name $container apt-cache policy | grep "\bl=Debian-Security\b" | grep "\bo=Debian\b" | grep --quiet "\bc=main\b" + test $? -eq 0 || failed "IS_DEBIANSECURITY_LXC" "missing Debian-Security repository in container ${container}" + fi + done + fi +} +check_backports_version() { + # Look for enabled "Debian Backports" sources from the "Debian" origin + apt-cache policy | grep "\bl=Debian Backports\b" | grep "\bo=Debian\b" | grep --quiet "\bc=main\b" + test $? -eq 1 || ( \ + apt-cache policy | grep "\bl=Debian Backports\b" | grep --quiet "\bn=${DEBIAN_RELEASE}-backports\b" && \ + test $? -eq 0 || failed "IS_BACKPORTS_VERSION" "Debian Backports enabled for another release than ${DEBIAN_RELEASE}" ) +} check_oldpub() { # Look for enabled pub.evolix.net sources (supersed by pub.evolix.org since Stretch) apt-cache policy | grep --quiet pub.evolix.net test $? -eq 1 || failed "IS_OLDPUB" "Old pub.evolix.net repository is still enabled" } +check_oldpub_lxc() { + # Look for enabled pub.evolix.net sources (supersed by pub.evolix.org since Buster as Sury safeguard) + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + lxc-attach --name $container apt-cache policy | grep --quiet pub.evolix.net + test $? -eq 1 || failed "IS_OLDPUB_LXC" "Old pub.evolix.net repository is still enabled in container ${container}" + done + fi +} check_newpub() { # Look for enabled pub.evolix.org sources apt-cache policy | grep "\bl=Evolix\b" | grep --quiet -v php @@ -208,7 +245,19 @@ check_sury() { apt-cache policy | grep --quiet packages.sury.org if [ $? -eq 0 ]; then apt-cache policy | grep "\bl=Evolix\b" | grep php --quiet - test $? -eq 0 || failed "IS_SURY" "packages.sury.org is present but our safeguard pub.evolix.org repository is missing" + test $? -eq 0 || failed "IS_SURY" "packages.sury.org is present but our safeguard pub.evolix.org repository is missing" + fi +} +check_sury_lxc() { + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + lxc-attach --name $container apt-cache policy | grep --quiet packages.sury.org + if [ $? -eq 0 ]; then + lxc-attach --name $container apt-cache policy | grep "\bl=Evolix\b" | grep php --quiet + test $? -eq 0 || failed "IS_SURY_LXC" "packages.sury.org is present but our safeguard pub.evolix.org repository is missing in container ${container}" + fi + done fi } check_aptitude() { @@ -249,8 +298,15 @@ check_customcrontab() { test "$found_lines" = 4 && failed "IS_CUSTOMCRONTAB" "missing custom field in crontab" } check_sshallowusers() { - 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" + if is_debian_bookworm; then + grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config.d \ + || failed "IS_SSHALLOWUSERS" "missing AllowUsers or AllowGroups directive in sshd_config.d/*" + grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config \ + && failed "IS_SSHALLOWUSERS" "AllowUsers or AllowGroups directive present in sshd_config" + else + 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" + fi } check_diskperf() { perfFile="/root/disk-perf.txt" @@ -307,7 +363,7 @@ check_minifw() { } || failed "IS_MINIFW" "minifirewall seems not started" } check_minifw_includes() { - if is_debian_bullseye; then + if { ! is_debian_stretch && ! is_debian_buster ; }; then if grep -q -e '/sbin/iptables' -e '/sbin/ip6tables' "/etc/default/minifirewall"; then failed "IS_MINIFWINCLUDES" "minifirewall has direct iptables invocations in /etc/default/minifirewall that should go in /etc/minifirewall.d/" fi @@ -334,13 +390,13 @@ check_nrpedisks() { test "$NRPEDISKS" = "$DFDISKS" || failed "IS_NRPEDISKS" "there must be $DFDISKS check_disk in nrpe.cfg" } check_nrpepid() { - if { is_debian_bullseye || is_debian_bookworm ; }; then + if { is_debian_stretch || is_debian_buster ; }; then { test -e /etc/nagios/nrpe.cfg \ - && grep -q "^pid_file=/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg; + && grep -q "^pid_file=/var/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg; } || failed "IS_NRPEPID" "missing or wrong pid_file directive in nrpe.cfg" else { test -e /etc/nagios/nrpe.cfg \ - && grep -q "^pid_file=/var/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg; + && grep -q "^pid_file=/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg; } || failed "IS_NRPEPID" "missing or wrong pid_file directive in nrpe.cfg" fi } @@ -550,14 +606,10 @@ check_evobackup_exclude_mount() { # then we verify that every mount is excluded if ! grep -q -- "^\s*--one-file-system" "${evobackup_file}"; then # old releases of evobackups don't have version - if grep -q "^VERSION=" "${evobackup_file}"; then - evobackup_version=$(sed -E -n 's/VERSION="(.*)"/\1/p' "${evobackup_file}") - # versions over 22.12 use a new syntax to exclude rsync files - if dpkg --compare-versions "$evobackup_version" ge 22.12 ; then - sed -En '/RSYNC_EXCLUDES="/,/"/ {s/(RSYNC_EXCLUDES=|")//g;p}' > "${excludes_file}" - else - grep -- "--exclude " "${evobackup_file}" | grep -E -o "\"[^\"]+\"" | tr -d '"' > "${excludes_file}" - fi + if grep -q "^VERSION=" "${evobackup_file}" && dpkg --compare-versions "$(sed -E -n 's/VERSION="(.*)"/\1/p' "${evobackup_file}")" ge 22.12 ; then + sed -En '/RSYNC_EXCLUDES="/,/"/ {s/(RSYNC_EXCLUDES=|")//g;p}' "${evobackup_file}" > "${excludes_file}" + else + grep -- "--exclude " "${evobackup_file}" | grep -E -o "\"[^\"]+\"" | tr -d '"' > "${excludes_file}" fi not_excluded=$(findmnt --type nfs,nfs4,fuse.sshfs, -o target --noheadings | grep -v -f "${excludes_file}") for mount in ${not_excluded}; do @@ -611,7 +663,7 @@ check_apacheipinallow() { check_muninapacheconf() { muninconf="/etc/apache2/conf-available/munin.conf" if is_installed apache2; then - test -e $muninconf && grep -vEq "^( |\t)*#" "$muninconf" \ + test -e $muninconf && grep --invert-match --extended-regexp --quiet "^( |\t)*#" "$muninconf" \ && failed "IS_MUNINAPACHECONF" "default munin configuration may be commented or disabled" fi } @@ -620,17 +672,17 @@ check_phpmyadminapacheconf() { phpmyadminconf0="/etc/apache2/conf-available/phpmyadmin.conf" phpmyadminconf1="/etc/apache2/conf-enabled/phpmyadmin.conf" if is_installed apache2; then - test -e $phpmyadminconf0 && grep -vEq "^( |\t)*#" "$phpmyadminconf0" \ - && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf0) may be commented or disabled" - test -e $phpmyadminconf1 && grep -vEq "^( |\t)*#" "$phpmyadminconf1" \ - && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf1) may be commented or disabled" + test -e $phpmyadminconf0 && grep --invert-match --extended-regexp --quiet "^( |\t)*#" "$phpmyadminconf0" \ + && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf0) should be commented or disabled" + test -e $phpmyadminconf1 && grep --invert-match --extended-regexp --quiet "^( |\t)*#" "$phpmyadminconf1" \ + && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf1) should be commented or disabled" fi } # Verification si le système doit redémarrer suite màj kernel. check_kerneluptodate() { if is_installed linux-image*; then # shellcheck disable=SC2012 - kernel_installed_at=$(date -d "$(ls --full-time -lcrt /boot | tail -n1 | awk '{print $6}')" +%s) + kernel_installed_at=$(date -d "$(ls --full-time -lcrt /boot/*lin* | tail -n1 | awk '{print $6}')" +%s) last_reboot_at=$(($(date +%s) - $(cut -f1 -d '.' /proc/uptime))) if [ "$kernel_installed_at" -gt "$last_reboot_at" ]; then failed "IS_KERNELUPTODATE" "machine is running an outdated kernel, reboot advised" @@ -697,6 +749,16 @@ check_etcgit() { git rev-parse --is-inside-work-tree > /dev/null 2>&1 \ || failed "IS_ETCGIT" "/etc is not a git repository" } +check_etcgit_lxc() { + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + export GIT_DIR="/var/lib/lxc/${container}/rootfs/etc/.git" GIT_WORK_TREE="/var/lib/lxc/${container}/rootfs/etc" + git rev-parse --is-inside-work-tree > /dev/null 2>&1 \ + || failed "IS_ETCGIT_LXC" "/etc is not a git repository in container ${container}" + done + fi +} # Check if /etc/.git/ has read/write permissions for root only. check_gitperms() { GIT_DIR="/etc/.git" @@ -706,6 +768,19 @@ check_gitperms() { [ "$expected" = "$actual" ] || failed "IS_GITPERMS" "$GIT_DIR must be $expected" fi } +check_gitperms_lxc() { + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + GIT_DIR="/var/lib/lxc/${container}/rootfs/etc/.git" + if test -d $GIT_DIR; then + expected="700" + actual=$(stat -c "%a" $GIT_DIR) + [ "$expected" = "$actual" ] || failed "IS_GITPERMS_LXC" "$GIT_DIR must be $expected (in container ${container})" + fi + done + fi +} # Check if no package has been upgraded since $limit. check_notupgraded() { last_upgrade=0 @@ -813,7 +888,7 @@ check_drbd_two_primaries() { failed "IS_DRBDTWOPRIMARIES" "Some DRBD ressources have two primaries, you risk a split brain!" fi elif command -v drbdadm >/dev/null; then - if drbdadm status | grep Primary -A2 | grep peer | grep -q Primary; then + if drbdadm role all 2>&1 | grep -q 'Primary/Primary'; then failed "IS_DRBDTWOPRIMARIES" "Some DRBD ressources have two primaries, you risk a split brain!" fi fi @@ -824,7 +899,7 @@ check_broadcomfirmware() { if [ -x "${LSPCI_BIN}" ]; then if ${LSPCI_BIN} | grep -q 'NetXtreme II'; then { is_installed firmware-bnx2 \ - && grep -q "^deb http://mirror.evolix.org/debian.* non-free" /etc/apt/sources.list; + && apt-cache policy | grep "\bl=Debian\b" | grep --quiet -v "\b,c=non-free\b" } || failed "IS_BROADCOMFIRMWARE" "missing non-free repository" fi else @@ -1000,6 +1075,7 @@ check_phpevolinuxconf() { is_debian_stretch && phpVersion="7.0" is_debian_buster && phpVersion="7.3" is_debian_bullseye && phpVersion="7.4" + is_debian_bookworm && phpVersion="8.2" if is_installed php; then { test -f "/etc/php/${phpVersion}/cli/conf.d/z-evolinux-defaults.ini" \ && test -f "/etc/php/${phpVersion}/cli/conf.d/zzz-evolinux-custom.ini" @@ -1131,16 +1207,10 @@ check_usrsharescripts() { test "$expected" = "$actual" || failed "IS_USRSHARESCRIPTS" "/usr/share/scripts must be $expected" } check_sshpermitrootno() { - sshd_args="-C addr=,user=,host=,laddr=,lport=0" - if is_debian_stretch; then - # Noop, we'll use the default $sshd_args - : - elif is_debian_buster; then + # You could change the SSH port in /etc/evocheck.cf + sshd_args="-C addr=,user=,host=,laddr=,lport=${SSH_PORT:-22}" + if is_debian_buster; then sshd_args="${sshd_args},rdomain=" - else - # NOTE: From Debian Bullseye 11 onward, with OpenSSH 8.1, the argument - # -T doesn't require the additional -C. - sshd_args= fi # shellcheck disable=SC2086 if ! (sshd -T ${sshd_args} 2> /dev/null | grep -qi 'permitrootlogin no'); then @@ -1261,7 +1331,7 @@ check_lxc_container_resolv_conf() { container_list=$(lxc-ls) current_resolvers=$(grep nameserver /etc/resolv.conf | sed 's/nameserver//g' ) - for container in $container_list; do + for container in $container_list; do if [ -f "/var/lib/lxc/${container}/rootfs/etc/resolv.conf" ]; then while read -r resolver; do @@ -1307,6 +1377,34 @@ check_lxc_php_fpm_service_umask_set() { fi fi } +# Check that LXC containers have the proper Debian version. +check_lxc_php_bad_debian_version() { + if is_installed lxc; then + php_containers_list=$(lxc-ls --filter php) + missing_umask="" + for container in $php_containers_list; do + if [ "$container" = "php56" ]; then + grep --quiet 'VERSION_ID="8"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Jessie" + elif [ "$container" = "php70" ]; then + grep --quiet 'VERSION_ID="9"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Stretch" + elif [ "$container" = "php73" ]; then + grep --quiet 'VERSION_ID="10"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Buster" + elif [ "$container" = "php74" ]; then + grep --quiet 'VERSION_ID="11"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Bullseye" + elif [ "$container" = "php82" ]; then + grep --quiet 'VERSION_ID="12"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Bookworm" + fi + done + fi +} +check_lxc_openssh() { + if is_installed lxc; then + container_list=$(lxc-ls) + for container in $container_list; do + test -e /var/lib/lxc/${container}/rootfs/usr/sbin/sshd && failed "IS_LXC_OPENSSH" "openssh-server should not be installed in container ${container}" + done + fi +} download_versions() { local file @@ -1460,9 +1558,13 @@ main() { test "${IS_LOGROTATECONF:=1}" = 1 && check_logrotateconf test "${IS_SYSLOGCONF:=1}" = 1 && check_syslogconf test "${IS_DEBIANSECURITY:=1}" = 1 && check_debiansecurity + test "${IS_DEBIANSECURITY_LXC:=1}" = 1 && check_debiansecurity_lxc + test "${IS_BACKPORTS_VERSION:=1}" = 1 && check_backports_version test "${IS_OLDPUB:=1}" = 1 && check_oldpub + test "${IS_OLDPUB_LXC:=1}" = 1 && check_oldpub_lxc test "${IS_NEWPUB:=1}" = 1 && check_newpub test "${IS_SURY:=1}" = 1 && check_sury + test "${IS_SURY_LXC:=1}" = 1 && check_sury_lxc test "${IS_APTITUDE:=1}" = 1 && check_aptitude test "${IS_APTGETBAK:=1}" = 1 && check_aptgetbak test "${IS_USRRO:=1}" = 1 && check_usrro @@ -1515,7 +1617,9 @@ main() { test "${IS_MUNINRUNNING:=1}" = 1 && check_muninrunning test "${IS_BACKUPUPTODATE:=1}" = 1 && check_backupuptodate test "${IS_ETCGIT:=1}" = 1 && check_etcgit + test "${IS_ETCGIT_LXC:=1}" = 1 && check_etcgit_lxc test "${IS_GITPERMS:=1}" = 1 && check_gitperms + test "${IS_GITPERMS_LXC:=1}" = 1 && check_gitperms_lxc test "${IS_NOTUPGRADED:=1}" = 1 && check_notupgraded test "${IS_TUNE2FS_M5:=1}" = 1 && check_tune2fs_m5 test "${IS_EVOLINUXSUDOGROUP:=1}" = 1 && check_evolinuxsudogroup @@ -1557,6 +1661,8 @@ main() { test "${IS_LXC_CONTAINER_RESOLV_CONF:=1}" = 1 && check_lxc_container_resolv_conf test "${IS_NO_LXC_CONTAINER:=1}" = 1 && check_no_lxc_container test "${IS_LXC_PHP_FPM_SERVICE_UMASK_SET:=1}" = 1 && check_lxc_php_fpm_service_umask_set + test "${IS_LXC_PHP_BAD_DEBIAN_VERSION:=1}" = 1 && check_lxc_php_bad_debian_version + test "${IS_LXC_OPENSSH:=1}" = 1 && check_lxc_openssh test "${IS_CHECK_VERSIONS:=1}" = 1 && check_versions if [ -f "${main_output_file}" ]; then @@ -1572,7 +1678,7 @@ main() { } cleanup() { # Cleanup tmp files - # shellcheck disable=SC2086,SC2317 + # shellcheck disable=SC2068,SC2317 rm -f ${files_to_cleanup[@]} log "$PROGNAME exit." diff --git a/evocheck/files/evocheck.wheezy.sh b/evocheck/files/evocheck.wheezy.sh index 3bbcc1f1..726a364f 100755 --- a/evocheck/files/evocheck.wheezy.sh +++ b/evocheck/files/evocheck.wheezy.sh @@ -4,7 +4,7 @@ # Script to verify compliance of a Linux (Debian) server # powered by Evolix -VERSION="23.10" +VERSION="24.01" readonly VERSION # base functions diff --git a/evolinux-base/defaults/main.yml b/evolinux-base/defaults/main.yml index f92fa90b..97f5540e 100644 --- a/evolinux-base/defaults/main.yml +++ b/evolinux-base/defaults/main.yml @@ -159,12 +159,10 @@ evolinux_root_disable_ssh: False # postfix evolinux_postfix_include: True - -evolinux_postfix_packages: True +evolinux_mail_aliases_include: True evolinux_postfix_users_alias_root: True evolinux_postfix_mailer_alias_root: True evolinux_postfix_root_alias: True -evolinux_postfix_purge_exim: True # logs @@ -211,6 +209,10 @@ evolinux_munin_include: True evolinux_nagios_nrpe_include: True +# check_free_space + +evolinux_check_free_space_include: True + # fail2ban evolinux_fail2ban_include: False @@ -235,3 +237,6 @@ evolinux_motd_include: True # Utils evolinux_utils_include: True + +# Autosysadmin +evolinux_autosysadmin_include: false diff --git a/evolinux-base/files/cert.sh b/evolinux-base/files/cert.sh new file mode 100644 index 00000000..f43a9dad --- /dev/null +++ b/evolinux-base/files/cert.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Shortcut to show certificate content or enddate. +# + +usage() { + echo "Usage : cert [date] " +} + +if [ "$#" -eq 1 ]; then + cert_path=$1 + if [ -f "${cert_path}" ]; then + openssl x509 -noout -in "${cert_path}" -text + else + >&2 echo "Error, file ${cert_path} does not exist." + fi + +elif [ "$#" -eq 2 ]; then + if [ "$1" = "date" ]; then + cert_path=$2 + if [ -f "${cert_path}" ]; then + openssl x509 -noout -in "$cert_path" -enddate + else + >&2 echo "Error, file ${cert_path} does not exist." + fi + else + >&2 echo "Error, two arguments provided but 'date' is only allowed as first." + usage + exit 1 + fi + +else + >&2 echo "Error, more than two arguments provided." + usage + exit 1 +fi + diff --git a/evolinux-base/tasks/hardware.yml b/evolinux-base/tasks/hardware.yml index 30badf70..b4280e09 100644 --- a/evolinux-base/tasks/hardware.yml +++ b/evolinux-base/tasks/hardware.yml @@ -6,11 +6,11 @@ tags: - packages -- name: firmware-non-free components are installed (Debian 12+) +- name: non-free-firmware components are installed (Debian 12+) ansible.builtin.replace: dest: /etc/apt/sources.list.d/system.sources - regexp: '^(Components: ((?!\bfirmware-non-free\b).)*)$' - replace: '\1 firmware-non-free' + regexp: '^(Components: ((?!\bnon-free-firmware\b).)*)$' + replace: '\1 non-free-firmware' when: - ansible_distribution_major_version is version('12', '>=') @@ -48,6 +48,7 @@ - firmware-linux-nonfree - intel-microcode state: present + update_cache: True tags: - packages diff --git a/evolinux-base/tasks/kernel.yml b/evolinux-base/tasks/kernel.yml index 231ec02f..148c72df 100644 --- a/evolinux-base/tasks/kernel.yml +++ b/evolinux-base/tasks/kernel.yml @@ -9,6 +9,7 @@ - ansible_machine == "x86_64" - ansible_virtualization_role == "guest" - evolinux_kernel_cloud_auto | bool + - ansible_distribution_major_version is version('10', '>=') - name: "Remove non-Cloud kernel on virtual servers" ansible.builtin.apt: diff --git a/evolinux-base/tasks/postfix.yml b/evolinux-base/tasks/mail_aliases.yml similarity index 52% rename from evolinux-base/tasks/postfix.yml rename to evolinux-base/tasks/mail_aliases.yml index d9dba3e2..4007d415 100644 --- a/evolinux-base/tasks/postfix.yml +++ b/evolinux-base/tasks/mail_aliases.yml @@ -1,36 +1,5 @@ --- -- name: Postfix packages are installed - ansible.builtin.apt: - name: - - postfix - - mailgraph - state: present - tags: - - packages - - postfix - when: evolinux_postfix_packages | bool - -- name: configure postfix myhostname - ansible.builtin.lineinfile: - dest: /etc/postfix/main.cf - state: present - line: "myhostname = {{ evolinux_fqdn }}" - regexp: '^myhostname' - notify: reload postfix - tags: - - postfix - -- name: configure postfix mynetworks - ansible.builtin.lineinfile: - dest: /etc/postfix/main.cf - state: present - line: "mydestination = {{ [evolinux_fqdn, evolinux_internal_fqdn] | unique | join(' ') }} localhost.localdomain localhost localhost.$mydomain" - regexp: '^mydestination' - notify: reload postfix - tags: - - postfix - - name: fetch users list ansible.builtin.shell: cmd: "set -o pipefail && getent passwd | cut -d':' -f 1 | grep -v root" @@ -81,18 +50,3 @@ - ansible.builtin.meta: flush_handlers -- name: exim4 is absent - ansible.builtin.apt: - name: - - exim4 - - exim4-base - - exim4-config - - exim4-daemon-light - purge: yes - state: absent - tags: - - packages - - postfix - when: evolinux_postfix_purge_exim | bool - -- ansible.builtin.meta: flush_handlers diff --git a/evolinux-base/tasks/main.yml b/evolinux-base/tasks/main.yml index 582bcfe0..456207df 100644 --- a/evolinux-base/tasks/main.yml +++ b/evolinux-base/tasks/main.yml @@ -81,9 +81,16 @@ ansible.builtin.import_tasks: root.yml when: evolinux_root_include | bool -- name: Postfix - ansible.builtin.import_tasks: postfix.yml +- name: Postfix role + ansible.builtin.include_role: + name: evolix/postfix when: evolinux_postfix_include | bool + tags: + - postfix + +- name: Configure /etc/aliases + ansible.builtin.import_tasks: mail_aliases.yml + when: evolinux_mail_aliases_include | bool - name: Logs management ansible.builtin.import_tasks: logs.yml @@ -127,6 +134,29 @@ name: evolix/nagios-nrpe when: evolinux_nagios_nrpe_include | bool +- name: check_free_space + ansible.builtin.include_role: + name: evolix/check_free_space + when: evolinux_check_free_space_include | bool + +# postfix role must be after nagios-nrpe role +- name: Postfix role + ansible.builtin.include_role: + name: evolix/postfix + when: evolinux_postfix_include | bool + tags: + - postfix + +- name: Autosysadmin (agent) + ansible.builtin.include_role: + name: 'evolix/autosysadmin-agent' + when: evolinux_autosysadmin_include | bool + +- name: Autosysadmin (restart_nrpe) + ansible.builtin.include_role: + name: 'evolix/autosysadmin-restart_nrpe' + when: evolinux_autosysadmin_include | bool + - name: fail2ban ansible.builtin.include_role: name: evolix/fail2ban diff --git a/evolinux-base/tasks/system.yml b/evolinux-base/tasks/system.yml index 52766796..96a77a70 100644 --- a/evolinux-base/tasks/system.yml +++ b/evolinux-base/tasks/system.yml @@ -214,3 +214,16 @@ - grep_hotplug_eni.rc == 0 - ansible.builtin.meta: flush_handlers + +# Htop / top config + +- name: Deploy htop configuration + ansible.builtin.copy: + src: htoprc + dest: /etc/htoprc + mode: "0644" + +- name: Deploy top configuration file + ansible.builtin.file: + path: /etc/topdefaultrc + state: absent \ No newline at end of file diff --git a/evolinux-base/tasks/utils.yml b/evolinux-base/tasks/utils.yml index e3477d08..b9b76e3b 100644 --- a/evolinux-base/tasks/utils.yml +++ b/evolinux-base/tasks/utils.yml @@ -23,14 +23,3 @@ owner: root group: root mode: "0755" - -- name: Deploy htop configuration - ansible.builtin.copy: - src: htoprc - dest: /etc/htoprc - mode: "0644" - -- name: Deploy top configuration file - ansible.builtin.file: - path: /etc/topdefaultrc - state: absent diff --git a/evolinux-base/templates/hardware/hwraid.le-vert.net.sources.j2 b/evolinux-base/templates/hardware/hwraid.le-vert.net.sources.j2 index 9d424a5b..8fbc2d84 100644 --- a/evolinux-base/templates/hardware/hwraid.le-vert.net.sources.j2 +++ b/evolinux-base/templates/hardware/hwraid.le-vert.net.sources.j2 @@ -4,5 +4,5 @@ Types: deb URIs: http://hwraid.le-vert.net/debian Suites: {{ ansible_distribution_release }} Components: main -Signed-by: {{ apt_keyring_dir }}/hwraid.le-vert.net.asc] +Signed-by: {{ apt_keyring_dir }}/hwraid.le-vert.net.asc Enabled: yes diff --git a/evolinux-base/templates/log2mail/evolinux-defaults.j2 b/evolinux-base/templates/log2mail/evolinux-defaults.j2 index c030ed41..e5e15d10 100644 --- a/evolinux-base/templates/log2mail/evolinux-defaults.j2 +++ b/evolinux-base/templates/log2mail/evolinux-defaults.j2 @@ -8,4 +8,9 @@ template = /etc/log2mail/mail file = /var/log/syslog pattern = "memory read error" mailto = {{ log2mail_alert_email or general_alert_email | mandatory }} -template = /etc/log2mail/mail \ No newline at end of file +template = /etc/log2mail/mail + +file = /var/log/cron.log +pattern = "Syntax error" +mailto = {{ log2mail_alert_email or general_alert_email | mandatory }} +template = /etc/log2mail/mail diff --git a/evolinux-users/templates/sudoers.j2 b/evolinux-users/templates/sudoers.j2 index 60b5b782..0e8471bf 100644 --- a/evolinux-users/templates/sudoers.j2 +++ b/evolinux-users/templates/sudoers.j2 @@ -15,6 +15,7 @@ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php80/rootfs/etc/php/8.0/fpm/pool.d/ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php81/rootfs/etc/php/8.1/fpm/pool.d/ nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php82/rootfs/etc/php/8.2/fpm/pool.d/ +nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php83/rootfs/etc/php/8.3/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 diff --git a/fail2ban/files/unban_ip.sh b/fail2ban/files/unban_ip.sh new file mode 100644 index 00000000..c13f2ecd --- /dev/null +++ b/fail2ban/files/unban_ip.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } + +IP="$1" +if [ "$IP" == "" ]; then + echo + echo -e "\033${TERM_COLOR_LIGHT_RED}Usage: $FUNCNAME \033${TERM_COLOR_NORMAL}" + echo + cat <&1 | grep -v "$IP is not banned"; + else + iptables -D f2b-$JAIL -s $IP -j DROP 2>&1 | grep -v 'iptables: Bad rule' && sleep 5 || echo "$IP is not banned"; + fi +done + +exit 0 + diff --git a/fail2ban/tasks/main.yml b/fail2ban/tasks/main.yml index 1629a02a..eabd0f36 100644 --- a/fail2ban/tasks/main.yml +++ b/fail2ban/tasks/main.yml @@ -94,7 +94,7 @@ - name: "Extend dbpurgeage if recidive jail is enabled" ansible.builtin.blockinfile: dest: /etc/fail2ban/fail2ban.d/recidive_dbpurgeage - marker: "# ANSIBLE MANAGED" + marker: "# {mark} ANSIBLE MANAGED" block: | [DEFAULT] dbpurgeage = {{ fail2ban_recidive_bantime }} @@ -111,3 +111,12 @@ - ansible_distribution_release == "stretch" or ansible_distribution_release == "buster" tags: - fail2ban + +- name: Script unban_ip is installed + ansible.builtin.copy: + src: unban_ip.sh + dest: /usr/local/sbin/unban_ip + mode: '0700' + tags: + - fail2ban + diff --git a/filebeat/handlers/main.yml b/filebeat/handlers/main.yml index 8456ee33..c5f7001f 100644 --- a/filebeat/handlers/main.yml +++ b/filebeat/handlers/main.yml @@ -4,4 +4,3 @@ ansible.builtin.systemd: name: filebeat state: restarted - when: not ansible_check_mode diff --git a/generate-ldif/templates/generateldif.sh.j2 b/generate-ldif/templates/generateldif.sh.j2 index 7e35ff5a..39fc30cf 100755 --- a/generate-ldif/templates/generateldif.sh.j2 +++ b/generate-ldif/templates/generateldif.sh.j2 @@ -14,6 +14,7 @@ get_pkg_version() { sed 's/[~+-].\+//' | sed 's/.\+://' | sed 's/p.*//' | cut -d'.' -f1,2 } +debianVersion=$(cut -d "." -f 1 < /etc/debian_version) clientNumber="{{ client_number | mandatory }}" monitoringMode="{{ monitoring_mode | mandatory }}" monitoringType="{{ monitoring_type | mandatory }}" @@ -699,6 +700,20 @@ ServiceVersion: PHP-FPM 8.2 (multiphp) EOT fi +if lxc-ls | grep -q php83 ; then + cat <> "${ldif_file}" + +dn: ServiceName=php-fpm83,${computer_dn} +NagiosEnabled: TRUE +ipServiceProtocol: tcp +objectClass: EvoService +ServiceName: php-fpm83 +ipServicePort: 443 +ServiceType: web +ServiceVersion: PHP-FPM 8.3 (multiphp) +EOT +fi + fi # END - LXC (multiphp) @@ -749,6 +764,32 @@ ServiceVersion: Undefined EOT fi +# Check pressure +if [ "${debianVersion}" -ge 12 ]; then + cat <> "${ldif_file}" + +dn: ServiceName=pressure_cpu,${computer_dn} +objectClass: EvoService +NagiosEnabled: TRUE +ServiceName: pressure_cpu +ServiceType: pressure +ServiceVersion: Undefined + +dn: ServiceName=pressure_io,${computer_dn} +objectClass: EvoService +NagiosEnabled: TRUE +ServiceName: pressure_io +ServiceType: pressure +ServiceVersion: Undefined + +dn: ServiceName=pressure_mem,${computer_dn} +objectClass: EvoService +NagiosEnabled: TRUE +ServiceName: pressure_mem +ServiceType: pressure +ServiceVersion: Undefined +EOT +fi # test if we have a stdout if [ -t 1 ]; then diff --git a/keepalived/tasks/main.yml b/keepalived/tasks/main.yml index 30f9557a..3f436d0e 100644 --- a/keepalived/tasks/main.yml +++ b/keepalived/tasks/main.yml @@ -8,7 +8,7 @@ - keepalived - name: Add notify.sh script for NRPE check - ansible.builtin.file: + ansible.builtin.copy: src: notify.sh dest: /etc/keepalived/notify.sh mode: "0755" @@ -21,7 +21,7 @@ - nrpe - name: check_keepalived is installed - ansible.builtin.file: + ansible.builtin.copy: src: check_keepalived dest: /usr/local/lib/nagios/plugins/check_keepalived mode: "0755" diff --git a/kvm-host/defaults/main.yml b/kvm-host/defaults/main.yml index 574c249f..981f2429 100644 --- a/kvm-host/defaults/main.yml +++ b/kvm-host/defaults/main.yml @@ -3,4 +3,12 @@ kvm_custom_libvirt_images_path: '' kvm_install_drbd: True kvm_scripts_dir: /usr/local/sbin -kvm_pair: null \ No newline at end of file +kvm_pair: null + +# A "r|.*/|" is always added in order to make this an allowlist +# Default = all sata/scsi disks + all nvme + all md (+partitions) +lvm_filter: + - '"a|^/dev/sd[a-zA-Z]+[0-9]*$|"' + - '"a|^/dev/nvme[0-9]+(n[0-9]+)?(p[0-9]+)?$|"' + - '"a|^/dev/md[0-9]+$|"' +kvm_drbd_interface: null diff --git a/kvm-host/files/add-vm.sh b/kvm-host/files/add-vm.sh index 6ded1103..2dbe0ff2 100755 --- a/kvm-host/files/add-vm.sh +++ b/kvm-host/files/add-vm.sh @@ -231,7 +231,7 @@ EOT if ! isDryRun; then sleep 5 - drbdadm status | tail -4 + ( drbdadm status || drbd-overview ) 2>/dev/null | tail -4 drbdDiskPath="/dev/drbd/by-res/${vmName}/0" if ! [ -b "${drbdDiskPath}" ]; then diff --git a/kvm-host/files/kvmstats.sh b/kvm-host/files/kvmstats.sh index 0258b322..d8e27b81 100755 --- a/kvm-host/files/kvmstats.sh +++ b/kvm-host/files/kvmstats.sh @@ -57,7 +57,7 @@ BEGIN { if (!mem) mem = $2 } -/block\.[0-9]+\.physical/ { +/block\.[0-9]+\.capacity/ { disksize += $2 } /state\.state/ { diff --git a/kvm-host/handlers/main.yml b/kvm-host/handlers/main.yml index 5ca5295a..963105f9 100644 --- a/kvm-host/handlers/main.yml +++ b/kvm-host/handlers/main.yml @@ -3,3 +3,11 @@ ansible.builtin.service: name: munin-node state: restarted + +- name: Update initramfs + ansible.builtin.command: + argv: + - '/usr/sbin/update-initramfs' + - '-k' + - 'all' + - '-u' \ No newline at end of file diff --git a/kvm-host/tasks/firewall.yml b/kvm-host/tasks/firewall.yml new file mode 100644 index 00000000..328d045c --- /dev/null +++ b/kvm-host/tasks/firewall.yml @@ -0,0 +1,9 @@ +--- +- name: Allow all traffic through DRBD interface + ansible.builtin.lineinfile: + path: /etc/minifirewall.d/drbd + line: "/sbin/iptables -I INPUT -p tcp -i {{ kvm_drbd_interface }} -j ACCEPT" + create: yes + when: + - kvm_drbd_interface is defined + - kvm_drbd_interface | length > 0 diff --git a/kvm-host/tasks/lvm.yml b/kvm-host/tasks/lvm.yml new file mode 100644 index 00000000..41b12b2d --- /dev/null +++ b/kvm-host/tasks/lvm.yml @@ -0,0 +1,37 @@ +--- + +- name: 'Figure out if /etc/lvm/lvm.conf exists' + ansible.builtin.stat: + path: '/etc/lvm/lvm.conf' + follow: true + get_checksum: false + get_mime: false + get_attributes: false + ignore_errors: true + register: lvm_conf_stat + +- name: Add LVM filter + ansible.builtin.lineinfile: + path: '/etc/lvm/lvm.conf' + insertafter: '# Configuration option devices/filter.' + regexp: '^\s*(#\s*)?filter\s*=\s*\[.*\]' + line: " filter = [ {{ lvm_filter | list | join(', ') }}, \"r|.*/|\" ]" + state: present + firstmatch: true + notify: 'Update initramfs' + when: + - lvm_conf_stat is succeeded + - lvm_conf_stat.stat.exists | bool + +- name: Add LVM global_filter + ansible.builtin.lineinfile: + path: '/etc/lvm/lvm.conf' + insertafter: '# Configuration option devices/global_filter.' + regexp: '^\s*(#\s*)?global_filter\s*=\s*\[.*\]' + line: " global_filter = [ {{ lvm_filter | list | join(', ') }}, \"r|.*/|\" ]" + state: present + firstmatch: true + notify: 'Update initramfs' + when: + - lvm_conf_stat is succeeded + - lvm_conf_stat.stat.exists | bool diff --git a/kvm-host/tasks/main.yml b/kvm-host/tasks/main.yml index c6004b7b..ae0108cd 100644 --- a/kvm-host/tasks/main.yml +++ b/kvm-host/tasks/main.yml @@ -4,6 +4,8 @@ name: evolix/drbd when: kvm_install_drbd +- ansible.builtin.import_tasks: lvm.yml + ## TODO: check why it's disabled - ansible.builtin.include: ssh.yml @@ -14,3 +16,5 @@ - ansible.builtin.include: images.yml - ansible.builtin.include: tools.yml + +- ansible.builtin.include: firewall.yml diff --git a/ldap/tasks/nagios.yml b/ldap/tasks/nagios.yml index 58120baa..1fa17241 100644 --- a/ldap/tasks/nagios.yml +++ b/ldap/tasks/nagios.yml @@ -50,8 +50,8 @@ mode: "0640" loop: - { option: 'hostname', value: '127.0.0.1' } - - { option: 'base', value: "{{ ldap_suffix }}" } - - { option: 'bind', value: "cn=nagios,ou=ldapusers,{{ ldap_suffix }}" } + - { option: 'base', value: "{{ ldap_suffix | mandatory }}" } + - { option: 'bind', value: "cn=nagios,ou=ldapusers,{{ ldap_suffix | mandatory }}" } - { option: 'pass', value: "{{ ldap_nagios_password }}" } when: not nagios_monitoring_plugins_path.stat.exists @@ -73,4 +73,4 @@ ansible.builtin.command: cmd: "slappasswd -s {{ ldap_nagios_password }}" register: ldap_nagios_password_ssha - changed_when: False \ No newline at end of file + changed_when: False diff --git a/listupgrade/files/old-kernel-autoremoval.sh b/listupgrade/files/old-kernel-autoremoval.sh index ce1c6002..6ad762e1 100644 --- a/listupgrade/files/old-kernel-autoremoval.sh +++ b/listupgrade/files/old-kernel-autoremoval.sh @@ -4,7 +4,7 @@ # fork by reg from /etc/kernel/postinst.d/apt-auto-removal script -VERSION="21.10" +VERSION="24.01" readonly VERSION PROGNAME=$(basename "$0") @@ -13,7 +13,7 @@ show_version() { cat <, +Copyright 2018-2024 Evolix , Gregory Colpart , Romain Dessort , Ludovic Poujol , @@ -105,7 +105,7 @@ main() { echo "BEFORE" dpkg -l | grep linux-image - dpkg --get-selections | tr '\t' ' ' | cut -d" " -f1 | grep '^linux-image-[234]' | grep -v -E "(${kernels})" | xargs --no-run-if-empty ${APT} -o Dir::State::Lists="${listupgrade_state_dir}" -y purge + dpkg --get-selections | tr '\t' ' ' | cut -d" " -f1 | grep '^linux-image-[0-9]' | grep -v -E "(${kernels})" | xargs --no-run-if-empty ${APT} -o Dir::State::Lists="${listupgrade_state_dir}" -y purge echo " AFTER" diff --git a/listupgrade/tasks/main.yml b/listupgrade/tasks/main.yml index dec4881d..9261b3fa 100644 --- a/listupgrade/tasks/main.yml +++ b/listupgrade/tasks/main.yml @@ -45,6 +45,12 @@ owner: root group: root +- name: Remove old lisupgrade typo + ansible.builtin.cron: + name: "lisupgrade.sh" + cron_file: "listupgrade" + state: absent + - name: Enable listupgrade cron ansible.builtin.cron: name: "listupgrade.sh" diff --git a/lxc-php/defaults/main.yml b/lxc-php/defaults/main.yml index c0612f4e..561a11f3 100644 --- a/lxc-php/defaults/main.yml +++ b/lxc-php/defaults/main.yml @@ -13,6 +13,7 @@ php_conf_mysql_socket_dir: /mysqld php_conf_mysql_default_socket: "{{ php_conf_mysql_socket_dir }}/mysqld.sock" lxc_php_version: Null +lxc_php_container_name: "{{ lxc_php_version }}" lxc_php_container_releases: php56: "jessie" @@ -22,6 +23,7 @@ lxc_php_container_releases: php80: "bullseye" php81: "bullseye" php82: "bookworm" + php83: "bookworm" lxc_php_services: php56: 'php5-fpm.service' @@ -31,6 +33,7 @@ lxc_php_services: php80: 'php8.0-fpm.service' php81: 'php8.1-fpm.service' php82: 'php8.2-fpm.service' + php83: 'php8.3-fpm.service' apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}" apt_basics_components: "{{ (ansible_virtualization_role == 'host') | ternary('main contrib non-free', 'main') }}" diff --git a/lxc-php/files/munin_php-fpm b/lxc-php/files/munin_php-fpm new file mode 100755 index 00000000..3647767d --- /dev/null +++ b/lxc-php/files/munin_php-fpm @@ -0,0 +1,234 @@ +#!/usr/bin/php + + 0, + 'memory' => 0, + 'cpu' => 0, + 'time' => 0 + ); + } + + //add values + $groups[$groupName]['count']++; + $groups[$groupName]['cpu'] += $cpu; + $groups[$groupName]['time'] += timeToSeconds($time); + $groups[$groupName]['memory'] += $ram / 1024; +} +foreach ($php_inactive_pools_list as $line) { + //split fields + $line = trim($line); + $groupName = $line; + //add group + if (!isset($groups[$groupName])) { + $groups[$groupName] = array( + 'count' => 0, + 'memory' => 0, + 'cpu' => 0, + 'time' => 0 + ); + } +} + +//check args +if(!isset($argv) || !isset($argv[0])) { + die("Error: No Plugin name provided\n"); +} + +$isConfig = isset($argv[1]) && $argv[1] == 'config'; + +//which plugin? +switch ($plugin_output) { +// ------------------------------------------------------ + case 'memory': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $ramMb = 0; + if($array['count'] !== 0){ + $ramMb = $array['memory'] / $array['count']; + } + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $ramMb + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Average Process Memory", + 'graph_vlabel' => 'MB' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'cpu': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $cpu = $array['cpu']; + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $cpu + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM CPU", + 'graph_vlabel' => '%', + 'graph_scale' => 'no' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'count': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $array['count'] + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Processes", + 'graph_vlabel' => 'processes' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'time': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $time=0; + if( $array['count'] !== 0){ + $time = round($array['time'] / $array['count']); + } + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $time + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Average Process Age", + 'graph_vlabel' => 'seconds', + 'graph_scale' => 'no' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + default: + die("Error: Unrecognized Plugin output name $plugin_output\n"); +} + +//output +ksort($config['elements']); +if ($isConfig) { + //graph params + echo "graph_category $php_container PHP-FPM\n"; + foreach($config['params'] as $key=>$value) { + echo $key . ' ' . $value . "\n"; + } + + //element params + foreach($config['elements'] as $element=>$data) { + foreach ($data as $key=>$value) { + if ($key == 'value') continue; + echo $element . '.' . $key . ' ' . $value . "\n"; + } + } +} else { + //element values + foreach ($config['elements'] as $pool=>$element) { + echo $pool . '.value ' . $element['value'] . "\n"; + } +} + +//functions +function timeToSeconds ($time) { + $seconds = 0; + + //days + $parts = explode('-', $time); + if(count($parts) == 2) { + $seconds += $parts[0] * 86400; + $time = $parts[1]; + } + + //hours + $parts = explode(':', $time); + if(count($parts) == 3) { + $seconds += array_shift($parts) * 3600; + } + + //minutes/seconds + $seconds += $parts[0] * 60 + $parts[1]; + return $seconds; +} diff --git a/lxc-php/files/sury.gpg b/lxc-php/files/sury.gpg index 384771a0..28043b0a 100644 Binary files a/lxc-php/files/sury.gpg and b/lxc-php/files/sury.gpg differ diff --git a/lxc-php/handlers/main.yml b/lxc-php/handlers/main.yml index b703933b..c1f58f92 100644 --- a/lxc-php/handlers/main.yml +++ b/lxc-php/handlers/main.yml @@ -10,6 +10,11 @@ name: "{{ lxc_php_version }}" container_command: "systemctl restart {{ lxc_php_services[lxc_php_version] }}" +- name: Reload php83-fpm + community.general.lxc_container: + name: "{{ lxc_php_version }}" + container_command: "systemctl reload php8.3-fpm" + - name: Reload php82-fpm community.general.lxc_container: name: "{{ lxc_php_version }}" diff --git a/lxc-php/tasks/mail_opensmtpd.yml b/lxc-php/tasks/mail_opensmtpd.yml index 35d0e75b..f532e8e8 100644 --- a/lxc-php/tasks/mail_opensmtpd.yml +++ b/lxc-php/tasks/mail_opensmtpd.yml @@ -1,11 +1,11 @@ --- -- name: "{{ lxc_php_version }} - Install opensmtpd" +- name: "{{ lxc_php_container_name }} - Install opensmtpd" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" container_command: "DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y opensmtpd" -- name: "{{ lxc_php_version }} - Configure opensmtpd (in the container)" +- name: "{{ lxc_php_container_name }} - Configure opensmtpd (in the container)" ansible.builtin.template: src: smtpd.conf.j2 dest: "{{ lxc_rootfs }}/etc/smtpd.conf" @@ -14,7 +14,7 @@ when: lxc_php_container_releases[lxc_php_version] in ["jessie", "stretch", "buster"] -- name: "{{ lxc_php_version }} - Configure opensmtpd (in the container)" +- name: "{{ lxc_php_container_name }} - Configure opensmtpd (in the container)" ansible.builtin.template: src: smtpd.conf.bullseye.j2 dest: "{{ lxc_rootfs }}/etc/smtpd.conf" diff --git a/lxc-php/tasks/mail_ssmtp.yml b/lxc-php/tasks/mail_ssmtp.yml index 07b54118..62066b2e 100644 --- a/lxc-php/tasks/mail_ssmtp.yml +++ b/lxc-php/tasks/mail_ssmtp.yml @@ -1,11 +1,11 @@ --- -- name: "{{ lxc_php_version }} - Install ssmtp" +- name: "{{ lxc_php_container_name }} - Install ssmtp" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" container_command: "DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends --yes --force-yes ssmtp " -- name: "{{ lxc_php_version }} - Configure ssmtp" +- name: "{{ lxc_php_container_name }} - Configure ssmtp" ansible.builtin.template: src: ssmtp.conf.j2 dest: "{{ lxc_rootfs }}/etc/ssmtp/ssmtp.conf" diff --git a/lxc-php/tasks/main.yml b/lxc-php/tasks/main.yml index 035bfe15..770031fa 100644 --- a/lxc-php/tasks/main.yml +++ b/lxc-php/tasks/main.yml @@ -9,16 +9,16 @@ name: evolix/lxc vars: lxc_containers: - - { name: "{{ lxc_php_version }}", release: "{{ lxc_php_container_releases[lxc_php_version] }}" } + - { name: "{{ lxc_php_container_name }}", release: "{{ lxc_php_container_releases[lxc_php_version] }}" } when: lxc_php_version is defined - name: set LXC rootfs ansible.builtin.set_fact: - lxc_rootfs: "/var/lib/lxc/{{ lxc_php_version }}/rootfs" + lxc_rootfs: "/var/lib/lxc/{{ lxc_php_container_name }}/rootfs" -- name: "Update APT cache in container {{ lxc_php_version }}" +- name: "Update APT cache in container {{ lxc_php_container_name }}" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" container_command: "apt-get update" - ansible.builtin.import_tasks: "php56.yml" @@ -42,6 +42,9 @@ - ansible.builtin.import_tasks: "php82.yml" when: lxc_php_version == "php82" +- ansible.builtin.import_tasks: "php83.yml" + when: lxc_php_version == "php83" + - ansible.builtin.import_tasks: "umask.yml" - ansible.builtin.import_tasks: "misc.yml" diff --git a/lxc-php/tasks/misc.yml b/lxc-php/tasks/misc.yml index 248aa8e2..7fc4575e 100644 --- a/lxc-php/tasks/misc.yml +++ b/lxc-php/tasks/misc.yml @@ -1,31 +1,31 @@ --- -- name: "{{ lxc_php_version }} - Configure timezone for the container" +- name: "{{ lxc_php_container_name }} - Configure timezone for the container" ansible.builtin.copy: remote_src: yes src: "/etc/timezone" dest: "{{ lxc_rootfs }}/etc/timezone" -- name: "{{ lxc_php_version }} - Ensure container's root directory is 755" +- name: "{{ lxc_php_container_name }} - Ensure container's root directory is 755" ansible.builtin.file: path: "{{ lxc_rootfs }}" state: directory mode: '0755' -- name: "{{ lxc_php_version }} - Configure mailname for the container" +- name: "{{ lxc_php_container_name }} - Configure mailname for the container" ansible.builtin.copy: content: "{{ evolinux_hostname }}.{{ evolinux_domain }}\n" dest: "{{ lxc_rootfs }}/etc/mailname" notify: "Restart opensmtpd" -- name: "{{ lxc_php_version }} - Install misc packages" +- name: "{{ lxc_php_container_name }} - Install misc packages" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" container_command: "DEBIAN_FRONTEND=noninteractive apt install -y cron logrotate git zip unzip" -- name: "{{ lxc_php_version }} - Add MySQL socket to container default mounts" +- name: "{{ lxc_php_container_name }} - Add MySQL socket to container default mounts" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" container_config: - "lxc.mount.entry = /run/mysqld {{ php_conf_mysql_socket_dir | replace('/', '', 1) }} none bind,create=dir 0 0" when: diff --git a/lxc-php/tasks/php73.yml b/lxc-php/tasks/php73.yml index ade67b97..1335e8a1 100644 --- a/lxc-php/tasks/php73.yml +++ b/lxc-php/tasks/php73.yml @@ -1,11 +1,11 @@ --- -- name: "{{ lxc_php_version }} - Install PHP packages" +- name: "{{ lxc_php_container_name }} - Install PHP packages" community.general.lxc_container: - name: "{{ lxc_php_version }}" + name: "{{ lxc_php_container_name }}" 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-gettext php-curl php-ssh2 php-zip php-mbstring php-zip composer libphp-phpmailer" -- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration" +- name: "{{ lxc_php_container_name }} - Copy evolinux PHP configuration" ansible.builtin.template: src: z-evolinux-defaults.ini.j2 dest: "{{ line_item }}" diff --git a/lxc-php/tasks/php83.yml b/lxc-php/tasks/php83.yml new file mode 100644 index 00000000..80c33eb6 --- /dev/null +++ b/lxc-php/tasks/php83.yml @@ -0,0 +1,91 @@ +--- + +- name: set APT keyring + ansible.builtin.set_fact: + lxc_apt_keyring_dir: /etc/apt/trusted.gpg.d + +- name: "{{ lxc_php_version }} - Install dependency packages" + community.general.lxc_container: + name: "{{ lxc_php_version }}" + container_command: "DEBIAN_FRONTEND=noninteractive apt install -y wget gnupg" + +- name: "{{ lxc_php_version }} - delete sources.list bookworm repository" + ansible.builtin.file: + path: "{{ lxc_rootfs }}/etc/apt/sources.list" + state: absent + +- name: "{{ lxc_php_version }} - system bookworm repository" + ansible.builtin.template: + src: bookworm_basics.sources.j2 + dest: "{{ lxc_rootfs }}/etc/apt/sources.list.d/system.sources" + force: true + mode: "0644" + +- name: "{{ lxc_php_version }} - security bookworm repository" + ansible.builtin.template: + src: bookworm_security.sources.j2 + dest: "{{ lxc_rootfs }}/etc/apt/sources.list.d/security.sources" + force: true + mode: "0644" + +- name: "{{ lxc_php_version }} - Add sury repo" + ansible.builtin.template: + src: sury.sources.j2 + dest: "{{ lxc_rootfs }}/etc/apt/sources.list.d/sury.sources" + force: true + mode: "0644" + +- name: "{{ lxc_php_version }} - Add sury failsafe repo" + ansible.builtin.template: + src: evolix_sury.sources.j2 + dest: "{{ lxc_rootfs }}/etc/apt/sources.list.d/evolix_sury.sources" + force: true + mode: "0644" + +- name: "Ensure {{ lxc_rootfs }}{{ lxc_apt_keyring_dir }} directory exists" + file: + path: "{{ lxc_rootfs }}{{ lxc_apt_keyring_dir }}" + state: directory + mode: "755" + owner: root + group: root + +- name: copy pub.evolix.org GPG key + ansible.builtin.copy: + src: pub_evolix.asc + dest: "{{ lxc_rootfs }}{{ lxc_apt_keyring_dir }}/pub_evolix.asc" + mode: "0644" + owner: root + group: root + +- name: copy packages.sury.org GPG Key + ansible.builtin.copy: + src: sury.gpg + dest: "{{ lxc_rootfs }}{{ lxc_apt_keyring_dir }}/sury.gpg" + mode: "0644" + owner: root + group: root + +- name: "{{ lxc_php_version }} - Update APT cache" + community.general.lxc_container: + name: "{{ lxc_php_version }}" + container_command: "DEBIAN_FRONTEND=noninteractive apt update" + +- name: "{{ lxc_php_version }} - Install PHP packages" + community.general.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-xml php-zip composer libphp-phpmailer" + +- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration" + ansible.builtin.template: + src: z-evolinux-defaults.ini.j2 + dest: "{{ line_item }}" + mode: "0644" + notify: "Reload {{ lxc_php_version }}-fpm" + loop: + - "{{ lxc_rootfs }}/etc/php/8.3/fpm/conf.d/z-evolinux-defaults.ini" + - "{{ lxc_rootfs }}/etc/php/8.3/cli/conf.d/z-evolinux-defaults.ini" + loop_control: + loop_var: line_item + +- ansible.builtin.include: "mail_opensmtpd.yml" diff --git a/lxc-php/templates/bookworm_basics.sources.j2 b/lxc-php/templates/bookworm_basics.sources.j2 index 948c4adf..8599143d 100644 --- a/lxc-php/templates/bookworm_basics.sources.j2 +++ b/lxc-php/templates/bookworm_basics.sources.j2 @@ -2,7 +2,7 @@ Types: deb URIs: http://mirror.evolix.org/debian -Suites: bookworm bookworm-updates +Suites: {{ lxc_php_container_releases[lxc_php_version] }} {{ lxc_php_container_releases[lxc_php_version] }}-updates Components: {{ apt_basics_components | mandatory }} Enabled: yes Signed-By: /usr/share/keyrings/debian-archive-bookworm-automatic.gpg diff --git a/lxc-php/templates/bookworm_security.sources.j2 b/lxc-php/templates/bookworm_security.sources.j2 index c98d5671..a3ccc581 100644 --- a/lxc-php/templates/bookworm_security.sources.j2 +++ b/lxc-php/templates/bookworm_security.sources.j2 @@ -2,7 +2,7 @@ Types: deb URIs: https://security.debian.org/debian-security -Suites: bookworm-security +Suites: {{ lxc_php_container_releases[lxc_php_version] }}-security Components: {{ apt_basics_components | mandatory }} Enabled: yes Signed-By: /usr/share/keyrings/debian-archive-bookworm-security-automatic.gpg diff --git a/lxc-php/templates/evolix_sury.sources.j2 b/lxc-php/templates/evolix_sury.sources.j2 new file mode 100644 index 00000000..ee09a474 --- /dev/null +++ b/lxc-php/templates/evolix_sury.sources.j2 @@ -0,0 +1,8 @@ +# {{ ansible_managed }} + +Types:deb +URIs: http://pub.evolix.org/evolix +Suites: {{ lxc_php_container_releases[lxc_php_version] }}-{{ lxc_php_version }} +Components: main +Signed-by: {{ apt_keyring_dir }}/pub_evolix.asc +Enabled: yes diff --git a/lxc-php/templates/sury.sources.j2 b/lxc-php/templates/sury.sources.j2 index 22725a58..2c17ef9d 100644 --- a/lxc-php/templates/sury.sources.j2 +++ b/lxc-php/templates/sury.sources.j2 @@ -2,7 +2,7 @@ Types: deb URIs: https://packages.sury.org/php/ -Suites: {{ ansible_distribution_release }} +Suites: {{ lxc_php_container_releases[lxc_php_version] }} Components: main Signed-by: {{ lxc_apt_keyring_dir }}/sury.gpg -Enabled: yes \ No newline at end of file +Enabled: yes diff --git a/lxc/tasks/create-container.yml b/lxc/tasks/create-container.yml index 4d3851b6..45f7270b 100644 --- a/lxc/tasks/create-container.yml +++ b/lxc/tasks/create-container.yml @@ -58,13 +58,13 @@ name: "{{ name }}" state: started -- name: "Ensure /etc/profile.d exists in container" +- name: "Ensure /etc/profile.d exists in container {{ name }}" ansible.builtin.file: path: "/var/lib/lxc/{{ name }}/rootfs/etc/profile.d" mode: '0755' state: directory -- name: "Copy host /etc/profile.d/evolinux into container" +- name: "Copy host /etc/profile.d/evolinux into container {{ name }}" ansible.builtin.copy: src: "/etc/profile.d/evolinux.sh" remote_src: true @@ -75,3 +75,22 @@ community.general.lxc_container: name: "{{ name }}" container_command: "DEBIAN_FRONTEND=noninteractive apt-get purge -y openssh-server" + +- name: "Init /etc git repository in container {{ name }}" + ansible.builtin.include_role: + name: 'etc-git' + tasks_from: 'repository.yml' + apply: + vars: + gitignore_items: + - "aliases.db" + - "*.swp" + - "postfix/sa-blacklist.access" + - "postfix/*.db" + - "postfix/spamd.cidr" + - "evobackup/.keep-*" + - "letsencrypt/.certbot.lock" + become: yes + loop: ["/var/lib/lxc/{{ name }}/rootfs/etc/"] + loop_control: + loop_var: 'repository_path' diff --git a/memcached/tasks/main.yml b/memcached/tasks/main.yml index 96060d4a..05455c0b 100644 --- a/memcached/tasks/main.yml +++ b/memcached/tasks/main.yml @@ -6,10 +6,10 @@ - memcached - ansible.builtin.include: instance-default.yml - when: memcached_instance_name is undefined + when: memcached_instance_name | length == 0 - ansible.builtin.include: instance-multi.yml - when: memcached_instance_name is defined + when: memcached_instance_name | length > 0 - ansible.builtin.include: munin.yml diff --git a/memcached/tasks/munin.yml b/memcached/tasks/munin.yml index b25b9275..f4b262a9 100644 --- a/memcached/tasks/munin.yml +++ b/memcached/tasks/munin.yml @@ -2,7 +2,7 @@ - name: Choose packages (Oracle) ansible.builtin.set_fact: multi: "multi_" - when: memcached_instance_name is defined + when: memcached_instance_name | length > 0 - name: is Munin present ? ansible.builtin.stat: diff --git a/memcached/tasks/nrpe.yml b/memcached/tasks/nrpe.yml index aba43da6..70c69c00 100644 --- a/memcached/tasks/nrpe.yml +++ b/memcached/tasks/nrpe.yml @@ -36,7 +36,7 @@ regexp: '^command\[check_memcached\]=' line: 'command[check_memcached]=/usr/local/lib/nagios/plugins/check_memcached.pl -H 127.0.0.1 -p {{ memcached_port }}' notify: restart nagios-nrpe-server - when: memcached_instance_name is undefined + when: memcached_instance_name | length == 0 - name: Add NRPE check (multi instance) ansible.builtin.lineinfile: @@ -44,6 +44,6 @@ regexp: '^command\[check_memcached\]=' line: 'command[check_memcached]=/usr/local/lib/nagios/plugins/check_memcached_instances' notify: restart nagios-nrpe-server - when: memcached_instance_name is defined + when: memcached_instance_name | length > 0 when: nrpe_evolix_config.stat.exists diff --git a/minifirewall/defaults/main.yml b/minifirewall/defaults/main.yml index 18d7d5b3..edb849b9 100644 --- a/minifirewall/defaults/main.yml +++ b/minifirewall/defaults/main.yml @@ -34,9 +34,9 @@ minifirewall_privilegied_ips: [] minifirewall_protected_ports_tcp: [22] minifirewall_protected_ports_udp: [] -minifirewall_public_ports_tcp: [25, 53, 443, 993, 995, 22222] -minifirewall_public_ports_udp: [53] -minifirewall_semipublic_ports_tcp: [20, 21, 22, 80, 110, 143] +minifirewall_public_ports_tcp: [22222] +minifirewall_public_ports_udp: [] +minifirewall_semipublic_ports_tcp: [22, 80, 443] minifirewall_semipublic_ports_udp: [] minifirewall_private_ports_tcp: [5666] minifirewall_private_ports_udp: [] diff --git a/minifirewall/files/check_minifirewall b/minifirewall/files/check_minifirewall index fc034de4..565a912d 100644 --- a/minifirewall/files/check_minifirewall +++ b/minifirewall/files/check_minifirewall @@ -87,7 +87,7 @@ main() { append_details "configuration is up-to-date" summary_ok "minifirewall is started and configuration is up-to-date" else - if echo "${check_result}" | grep --quiet --regexp 'usage'; then + if echo "${check_result}" | grep --ignore-case --quiet --regexp 'usage'; then append_details "minifirewall is too old to check active configuration" else case "${check_rc}" in diff --git a/mongodb/files/server-7.0.asc b/mongodb/files/server-7.0.asc new file mode 100644 index 00000000..7f4911ae --- /dev/null +++ b/mongodb/files/server-7.0.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQINBGPILWABEACqeWP/ktugdlWEyk7YTXo3n19+5Om4AlSdIyKv49vAlKtzCfMA +QkZq3mfvjXiKMuLnL2VeElAJQIYcPoqnHf6tJbdrNv4AX2uI1cTsvGW7YS/2WNwJ +C/+vBa4o+yA2CG/MVWZRbtOjkFF/W07yRFtNHAcgdmpIjdWgSnPQr9eIqLuWXIhy +H7EerKsba227Vd/HfvKnAy30Unlsdywy7wi1FupzGJck0TPoOVGmsSpSyIQu9A4Z +uC6TE/NcJHvaN0JuHwM+bQo9oWirGsZ1NCoVqSY8/sasdUc7T9r90MbUcH674YAR +8OKYVBzU0wch4VTFhfHZecKHQnZf+V4dmP9oXnu4fY0/0w3l4jaew7Ind7kPg3yN +hvgAkBK8yRAbSu1NOtHDNiRoHGEQFgct6trVOvCqHbN/VToLNtGk0rhKGOp8kuSF +OJ02PJPxF3/zHGP8n8khCjUJcrilYPqRghZC8ZWnCj6GJVg6WjwLi+hPwNMi8xK6 +cjKhRW3eCy5Wcn73PzVBX9f7fSeFDJec+IfS47eNkxunHAOUMXa2+D+1xSWgEfK0 +PClfyWPgLIXY2pGQ6v8l3A6P5gJv4o38/E1h1RTcO3H1Z6cgZLIORZHPyAj50SPQ +cjzftEcz56Pl/Cyw3eMYC3qlbABBgsdeb6KB6G5dkNxI4or3MgmxcwfnkwARAQAB +tDdNb25nb0RCIDcuMCBSZWxlYXNlIFNpZ25pbmcgS2V5IDxwYWNrYWdpbmdAbW9u +Z29kYi5jb20+iQI+BBMBAgAoBQJjyC1gAhsDBQkJZgGABgsJCAcDAgYVCAIJCgsE +FgIDAQIeAQIXgAAKCRAWDSa7F4W6OM+eD/sE7KbJyRNWyPCRTqqJXrXvyPqZtbFX +8sio0lQ8ghn4f7lmb7LnFroUsmBeWaYirM8O3b2+iQ9oj4GeR3gbRZsEhFXQfL54 +SfrmG9hrWWpJllgPP7Six+jrzcjvkf1TENqw4jRP+cJhuihH1Gfizo9ktwwoN9Yr +m7vgh+focEEmx8dysS38ApLxKlUEfTsE9bYsClgqyY1yrt3v4IpGbf66yfyBHNgY +sObR3sngDRVbap7PwNyREGsuAFfKr/Dr37HfrjY7nsn3vH7hbDpSBh+H7a0b/chS +mM60aaG4biWpvmSC7uxA/t0gz+NQuC4HL+qyNPUxvyIO+TwlaXfCI6ixazyrH+1t +F7Bj5mVsne7oeWjRrSz85jK3Tpn9tj3Fa7PCDA6auAlPK8Upbhuoajev4lIydNd2 +70yO0idm/FtpX5a8Ck7KSHDvEnXpN70imayoB4Fs2Kigi2BdZOOdib16o5F/9cx9 +piNa7HotHCLTfR6xRmelGEPWKspU1Sm7u2A5vWgjfSab99hiNQ89n+I7BcK1M3R1 +w/ckl6qBtcxz4Py+7jYIJL8BYz2tdreWbdzWzjv+XQ8ZgOaMxhL9gtlfyYqeGfnp +hYW8LV7a9pavxV2tLuVjMM+05ut/d38IkTV7OSJgisbSGcmycXIzxsipyXJVGMZt +MFw3quqJhQMRsA== +=gbRM +-----END PGP PUBLIC KEY BLOCK----- diff --git a/mysql/defaults/main.yml b/mysql/defaults/main.yml index af43f495..871dd599 100644 --- a/mysql/defaults/main.yml +++ b/mysql/defaults/main.yml @@ -59,5 +59,5 @@ mysql_binlog_format: mixed mysql_server_id: null mysql_bind_address: null mysql_repl_password: '' -mysql_read_only: 0 +mysql_read_only: False diff --git a/mysql/tasks/config_jessie.yml b/mysql/tasks/config_jessie.yml index 3d8c494d..364ee175 100644 --- a/mysql/tasks/config_jessie.yml +++ b/mysql/tasks/config_jessie.yml @@ -2,6 +2,7 @@ - ansible.builtin.set_fact: mysql_config_directory: /etc/mysql/conf.d + mysql_performance_schema: False - name: "Copy MySQL defaults config file (jessie)" ansible.builtin.copy: diff --git a/mysql/templates/evolinux-custom.cnf.j2 b/mysql/templates/evolinux-custom.cnf.j2 index 119943a1..cc66df94 100644 --- a/mysql/templates/evolinux-custom.cnf.j2 +++ b/mysql/templates/evolinux-custom.cnf.j2 @@ -38,7 +38,7 @@ lower_case_table_names = {{ mysql_lower_case_table_names }} {% if mysql_innodb_log_file_size %} innodb_log_file_size = {{ mysql_innodb_log_file_size }} {% endif %} -read_only = {{ mysql_read_only }} +read_only = {{ mysql_read_only | bool | ternary('1','0') }} {% if mysql_performance_schema %} performance_schema = ON performance-schema-instrument='stage/%=ON' diff --git a/nagios-nrpe/defaults/main.yml b/nagios-nrpe/defaults/main.yml index 8ac9c3f4..c8695bc8 100644 --- a/nagios-nrpe/defaults/main.yml +++ b/nagios-nrpe/defaults/main.yml @@ -8,8 +8,17 @@ nagios_nrpe_default_ntp_server: "pool.ntp.org" nagios_nrpe_ntp_server: Null # Use nagios_nrpe_processes to override -nagios_nrpe_processes_default_before_debian_12: [ cron rsyslogd ntpd munin-node ] -nagios_nrpe_processes_default: [ cron rsyslogd systemd-timesyn munin-node ] +nagios_nrpe_processes_default_before_debian_12: + - cron + - rsyslogd + - ntpd + - munin-node +nagios_nrpe_processes_default: + - cron + - rsyslogd + - systemd-timesyn + - munin-node + # Built dynamically : nagios_nrpe_processes: Null diff --git a/nagios-nrpe/files/plugins/check_phpfpm_multi b/nagios-nrpe/files/plugins/check_phpfpm_multi index b02fc7e2..e21ecf87 100644 --- a/nagios-nrpe/files/plugins/check_phpfpm_multi +++ b/nagios-nrpe/files/plugins/check_phpfpm_multi @@ -19,14 +19,21 @@ nb_ok=0 nb_unchk=0 output="" -readonly POOL_FOLDER=${1:-$(detect_pool_dir)} +# We want globbing to be expanded here +# shellcheck disable=SC2206 +readonly POOL_FOLDER=( ${1:-$(detect_pool_dir)} ) -if [[ ! -d "$POOL_FOLDER" ]]; then - echo "CRITICAL - $POOL_FOLDER does not exists" +if [ "${#POOL_FOLDER[@]}" -gt 1 ]; then + echo "CRITICAL - '${POOL_FOLDER[*]}' contains more than one directories" + exit 2 +fi; + +if [[ ! -d "${POOL_FOLDER[0]}" ]]; then + echo "CRITICAL - ${POOL_FOLDER[0]} does not exists" exit 2 fi; -readonly POOL_FILES=$(find "$POOL_FOLDER" -name "*.conf") +readonly POOL_FILES=$(find "${POOL_FOLDER[0]}" -name "*.conf") for pool_file in $POOL_FILES; do diff --git a/nagios-nrpe/templates/evolix.cfg.j2 b/nagios-nrpe/templates/evolix.cfg.j2 index de495868..d725bb3b 100644 --- a/nagios-nrpe/templates/evolix.cfg.j2 +++ b/nagios-nrpe/templates/evolix.cfg.j2 @@ -48,7 +48,6 @@ command[check_redis]=/usr/lib/nagios/plugins/check_tcp -p 6379 command[check_clamd]=/usr/lib/nagios/plugins/check_clamd -H /var/run/clamav/clamd.ctl -v command[check_clamav_db]=/usr/lib/nagios/plugins/check_file_age -w 86400 -c 172800 -f /var/lib/clamav/daily.cld command[check_ssl]=/usr/lib/nagios/plugins/check_http -f follow -I 127.0.0.1 -S -p 443 -H ssl.evolix.net -C 15,5 -command[check_ssl_local]={{ nagios_plugins_directory }}/check_ssl_local command[check_elasticsearch]=/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -u /_cat/health?h=st -p 9200 -r 'red' --invert-regex command[check_memcached]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 11211 command[check_opendkim]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 8891 @@ -59,6 +58,9 @@ command[check_bkctld]=sudo /usr/sbin/bkctld check command[check_postgrey]=/usr/lib/nagios/plugins/check_tcp -p10023 command[check_influxdb]=/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -u /health -p 8086 -r '"status":"pass"' command[check_dhcpd]=/usr/lib/nagios/plugins/check_procs -c1:1 -C dhcpd -t 60 +command[check_ipmi_sensors]=sudo /usr/lib/nagios/plugins/check_ipmi_sensor +command[check_raid_status]=/usr/lib/nagios/plugins/check_raid +command[check_dockerd]=/usr/lib/nagios/plugins/check_tcp -H /var/run/docker.sock --escape -s "GET /_ping HTTP/1.1\nHost: http\n\n" -e OK # Local checks (not packaged) command[check_mem]={{ nagios_plugins_directory }}/check_mem -f -C -w 20 -c 10 @@ -85,9 +87,12 @@ command[check_php-fpm74]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi command[check_php-fpm80]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php80/rootfs/etc/php/8.0/fpm/pool.d/ command[check_php-fpm81]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php81/rootfs/etc/php/8.1/fpm/pool.d/ command[check_php-fpm82]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php82/rootfs/etc/php/8.2/fpm/pool.d/ -command[check_ipmi_sensors]=sudo /usr/lib/nagios/plugins/check_ipmi_sensor -command[check_raid_status]=/usr/lib/nagios/plugins/check_raid +command[check_php-fpm83]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php83/rootfs/etc/php/8.3/fpm/pool.d/ command[check_dhcp_pool]={{ nagios_plugins_directory }}/check_dhcp_pool +command[check_ssl_local]={{ nagios_plugins_directory }}/check_ssl_local +command[check_pressure_cpu]=/usr/lib/nagios/plugins/check_pressure --cpu -w 100000 -c 500000 +command[check_pressure_mem]=/usr/lib/nagios/plugins/check_pressure --mem --full -w 100000 -c 500000 +command[check_pressure_io]=/usr/lib/nagios/plugins/check_pressure --io --full -w 100000 -c 500000 # Check HTTP "many". Use this to check many websites (http, https, ports, sockets and SSL certificates). # Beware! All checks must not take more than 10s! diff --git a/nginx/tasks/ip_whitelist.yml b/nginx/tasks/ip_whitelist.yml index fc4fd2d2..537e4a66 100644 --- a/nginx/tasks/ip_whitelist.yml +++ b/nginx/tasks/ip_whitelist.yml @@ -5,6 +5,7 @@ dest: /etc/nginx/snippets/ipaddr_whitelist line: "allow {{ item }};" state: present + create: yes loop: "{{ nginx_ipaddr_whitelist_present }}" notify: reload nginx tags: diff --git a/nginx/tasks/main.yml b/nginx/tasks/main.yml index 57a036d4..e8009804 100644 --- a/nginx/tasks/main.yml +++ b/nginx/tasks/main.yml @@ -20,20 +20,22 @@ - name: customize worker_connections ansible.builtin.lineinfile: dest: /etc/nginx/nginx.conf - regexp: '^(\s*)(worker_connections)\s+.+;' + regexp: '^(\s*)worker_connections\s+.+;' line: '\1worker_connections 1024;' insertafter: 'events \{' backrefs: yes + when: not ansible_check_mode tags: - nginx - name: use epoll ansible.builtin.lineinfile: dest: /etc/nginx/nginx.conf - regexp: '^(\s*)(use)\s+.+;' + regexp: '^(\s*)use\s+.+;' line: '\1use epoll;' insertafter: 'events \{' backrefs: yes + when: not ansible_check_mode tags: - nginx @@ -145,6 +147,7 @@ name: nginx enabled: yes state: started + when: not ansible_check_mode tags: - nginx diff --git a/nginx/tasks/munin_vhost.yml b/nginx/tasks/munin_vhost.yml index 98cc8672..2c9261f4 100644 --- a/nginx/tasks/munin_vhost.yml +++ b/nginx/tasks/munin_vhost.yml @@ -30,10 +30,14 @@ ansible.builtin.copy: src: systemd/spawn-fcgi-munin-graph.service dest: /etc/systemd/system/spawn-fcgi-munin-graph.service + force: yes +# WARN: there is no (apparent) way to check if the service exists +# so we disable this task in check mode. - name: Systemd unit for Munin-fcgi is started ansible.builtin.systemd: name: spawn-fcgi-munin-graph daemon_reload: yes enabled: yes state: started + when: not ansible_check_mode diff --git a/nginx/tasks/server_status_read.yml b/nginx/tasks/server_status_read.yml index d6cecbe3..eddc3fe8 100644 --- a/nginx/tasks/server_status_read.yml +++ b/nginx/tasks/server_status_read.yml @@ -18,20 +18,27 @@ - name: generate random string for server-status suffix ansible.builtin.shell: - cmd: "apg -a 1 -M N -n 1 > {{ nginx_serverstatus_suffix_file }}" - args: + cmd: "apg -a 1 -M N -n 1 | tee {{ nginx_serverstatus_suffix_file }}" creates: "{{ nginx_serverstatus_suffix_file }}" + register: generated_nginx_serverstatus_suffix + +- name: check if nginx suffix file exists + ansible.builtin.stat: + path: "{{ nginx_serverstatus_suffix_file }}" + register: nginx_serverstatus_suffix_file_check - name: read nginx server status suffix ansible.builtin.command: cmd: "tail -n 1 {{ nginx_serverstatus_suffix_file }}" changed_when: False check_mode: no - register: new_nginx_serverstatus_suffix + when: nginx_serverstatus_suffix_file_check.stat.exists + register: read_nginx_serverstatus_suffix +# If the file exists and the read value is not empty, then use it, otherwhise use the generated value - name: overwrite nginx_serverstatus_suffix ansible.builtin.set_fact: - nginx_serverstatus_suffix: "{{ new_nginx_serverstatus_suffix.stdout }}" + nginx_serverstatus_suffix: "{{ (nginx_serverstatus_suffix_file_check.stat.exists and (read_nginx_serverstatus_suffix.stdout | length > 0)) | ternary(read_nginx_serverstatus_suffix.stdout, generated_nginx_serverstatus_suffix.stdout) }}" - ansible.builtin.debug: var: nginx_serverstatus_suffix diff --git a/nginx/tasks/server_status_write.yml b/nginx/tasks/server_status_write.yml index dbed56cb..08031635 100644 --- a/nginx/tasks/server_status_write.yml +++ b/nginx/tasks/server_status_write.yml @@ -17,4 +17,5 @@ dest: /etc/nginx/sites-available/evolinux-default.conf regexp: 'location /server-status-? {' replace: 'location /server-status-{{ nginx_serverstatus_suffix }} {' + when: not ansible_check_mode notify: reload nginx diff --git a/openvpn/files/check_openvpn_certificates.sh b/openvpn/files/check_openvpn_certificates.sh index 26808868..1ec3aaed 100644 --- a/openvpn/files/check_openvpn_certificates.sh +++ b/openvpn/files/check_openvpn_certificates.sh @@ -35,6 +35,7 @@ fi # Dates in seconds _15_days="1296000" _30_days="2592000" +_60_days="5184000" current_date=$($date_cmd +"%s") # Trying to define the OpenVPN conf file location - default to /etc/openvpn/server.conf @@ -90,15 +91,15 @@ test_ca_expiration() { if [ $current_date -ge $1 ]; then CA_ECHO="CRITICAL - The server CA has expired on $formated_ca_expiration_date" CA_STATE=$STATE_CRITICAL - # Expiration in 15 days or less - CA file - elif [ $((current_date+_15_days)) -ge $1 ]; then - CA_ECHO="CRITICAL - The server CA expires in 15 days or less : $formated_ca_expiration_date" - CA_STATE=$STATE_CRITICAL # Expiration in 30 days or less - CA file elif [ $((current_date+_30_days)) -ge $1 ]; then - CA_ECHO="WARNING - The server CA expires in 30 days or less : $formated_ca_expiration_date" + CA_ECHO="CRITICAL - The server CA expires in 30 days or less : $formated_ca_expiration_date" + CA_STATE=$STATE_CRITICAL + # Expiration in 60 days or less - CA file + elif [ $((current_date+_60_days)) -ge $1 ]; then + CA_ECHO="WARNING - The server CA expires in 60 days or less : $formated_ca_expiration_date" CA_STATE=$STATE_WARNING - # Expiration in more than 30 days - CA file + # Expiration in more than 60 days - CA file else CA_ECHO="OK - The server CA expires on $formated_ca_expiration_date" CA_STATE=$STATE_OK @@ -193,8 +194,8 @@ main() { echo $RESTART_ECHO exit $CERT_STATE else - echo $CERT_ECHO echo $CA_ECHO + echo $CERT_ECHO echo $RESTART_ECHO exit $CERT_STATE fi diff --git a/php/files/munin_php-fpm b/php/files/munin_php-fpm new file mode 100755 index 00000000..3647767d --- /dev/null +++ b/php/files/munin_php-fpm @@ -0,0 +1,234 @@ +#!/usr/bin/php + + 0, + 'memory' => 0, + 'cpu' => 0, + 'time' => 0 + ); + } + + //add values + $groups[$groupName]['count']++; + $groups[$groupName]['cpu'] += $cpu; + $groups[$groupName]['time'] += timeToSeconds($time); + $groups[$groupName]['memory'] += $ram / 1024; +} +foreach ($php_inactive_pools_list as $line) { + //split fields + $line = trim($line); + $groupName = $line; + //add group + if (!isset($groups[$groupName])) { + $groups[$groupName] = array( + 'count' => 0, + 'memory' => 0, + 'cpu' => 0, + 'time' => 0 + ); + } +} + +//check args +if(!isset($argv) || !isset($argv[0])) { + die("Error: No Plugin name provided\n"); +} + +$isConfig = isset($argv[1]) && $argv[1] == 'config'; + +//which plugin? +switch ($plugin_output) { +// ------------------------------------------------------ + case 'memory': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $ramMb = 0; + if($array['count'] !== 0){ + $ramMb = $array['memory'] / $array['count']; + } + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $ramMb + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Average Process Memory", + 'graph_vlabel' => 'MB' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'cpu': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $cpu = $array['cpu']; + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $cpu + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM CPU", + 'graph_vlabel' => '%', + 'graph_scale' => 'no' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'count': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $array['count'] + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Processes", + 'graph_vlabel' => 'processes' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + case 'time': +// ------------------------------------------------------ + $elements = array(); + foreach ($groups as $name=>$array) { + $time=0; + if( $array['count'] !== 0){ + $time = round($array['time'] / $array['count']); + } + $label = 'Pool ' . $name; + $elements[$name] = array( + 'label' => $label, + 'type' => 'GAUGE', + 'value' => $time + ); + } + $config = array( + 'params' => array( + 'graph_title' => "$php_container PHP-FPM Average Process Age", + 'graph_vlabel' => 'seconds', + 'graph_scale' => 'no' + ), + 'elements' => $elements + ); + break; +// ------------------------------------------------------ + default: + die("Error: Unrecognized Plugin output name $plugin_output\n"); +} + +//output +ksort($config['elements']); +if ($isConfig) { + //graph params + echo "graph_category $php_container PHP-FPM\n"; + foreach($config['params'] as $key=>$value) { + echo $key . ' ' . $value . "\n"; + } + + //element params + foreach($config['elements'] as $element=>$data) { + foreach ($data as $key=>$value) { + if ($key == 'value') continue; + echo $element . '.' . $key . ' ' . $value . "\n"; + } + } +} else { + //element values + foreach ($config['elements'] as $pool=>$element) { + echo $pool . '.value ' . $element['value'] . "\n"; + } +} + +//functions +function timeToSeconds ($time) { + $seconds = 0; + + //days + $parts = explode('-', $time); + if(count($parts) == 2) { + $seconds += $parts[0] * 86400; + $time = $parts[1]; + } + + //hours + $parts = explode(':', $time); + if(count($parts) == 3) { + $seconds += array_shift($parts) * 3600; + } + + //minutes/seconds + $seconds += $parts[0] * 60 + $parts[1]; + return $seconds; +} diff --git a/php/files/sury.gpg b/php/files/sury.gpg index 384771a0..28043b0a 100644 Binary files a/php/files/sury.gpg and b/php/files/sury.gpg differ diff --git a/php/files/sury.preferences b/php/files/sury.preferences deleted file mode 100644 index adcc5918..00000000 --- a/php/files/sury.preferences +++ /dev/null @@ -1,7 +0,0 @@ -Package: php* libapache2-mod-php* libpcre2* libzip4* libgd* libpcre3* -Pin: origin packages.sury.org -Pin-Priority: 999 - -Package: * -Pin: origin packages.sury.org -Pin-Priority: 50 diff --git a/php/handlers/main.yml b/php/handlers/main.yml index b333fe9b..d2b96b99 100644 --- a/php/handlers/main.yml +++ b/php/handlers/main.yml @@ -34,3 +34,8 @@ ansible.builtin.service: name: php8.2-fpm state: restarted + +- name: restart php8.3-fpm + ansible.builtin.service: + name: php8.3-fpm + state: restarted diff --git a/php/tasks/sury_pre.yml b/php/tasks/sury_pre.yml index a0640e4c..599f9425 100644 --- a/php/tasks/sury_pre.yml +++ b/php/tasks/sury_pre.yml @@ -27,15 +27,6 @@ repo: "deb [signed-by={{ apt_keyring_dir }}/pub_evolix.asc] http://pub.evolix.org/evolix {{ ansible_distribution_release }}-php{{ php_version | replace('.', '')}} main" filename: evolix-php state: present - when: - - ansible_distribution_release == "bullseye" - -- name: Setup deb.sury.org repository - Add preferences file - ansible.builtin.copy: - src: sury.preferences - dest: /etc/apt/preferences.d/z-sury - when: - - ansible_distribution_release != "bullseye" - name: Setup deb.sury.org repository - Add GPG key ansible.builtin.copy: diff --git a/postfix/defaults/main.yml b/postfix/defaults/main.yml index 55ab72cd..53ae5360 100644 --- a/postfix/defaults/main.yml +++ b/postfix/defaults/main.yml @@ -1,5 +1,7 @@ --- -postfix_hostname: "{{ ansible_fqdn }}" +postfix_hostname: "{{ evolinux_fqdn }}" postfix_force_main_cf: False postfix_packmail: False -postfix_slow_transport_include: "{{ postfix_packmail }}" +postfix_slow_transport_include: False +postfix_purge_exim: True + diff --git a/postfix/files/spam.sh b/postfix/files/spam.sh index 29dc0b40..b8685f85 100644 --- a/postfix/files/spam.sh +++ b/postfix/files/spam.sh @@ -40,7 +40,7 @@ function cleanup { } # Postfix -postfix_dbs="client.access sender.access recipient.access header_kill sa-blacklist.access spamd.cidr" +postfix_dbs="client.access sender.access recipient.access header_kill" for db in ${postfix_dbs}; do if is_new "${db}"; then download "${db}" diff --git a/postfix/tasks/common.yml b/postfix/tasks/common.yml index 29e6dd07..b2a09398 100644 --- a/postfix/tasks/common.yml +++ b/postfix/tasks/common.yml @@ -1,11 +1,32 @@ ---- -- name: check if main.cf is default +- name: Postfix packages are installed + ansible.builtin.apt: + name: + - postfix + - mailgraph + state: present + tags: + - postfix + +- name: exim4 is absent + ansible.builtin.apt: + name: + - exim4 + - exim4-base + - exim4-config + - exim4-daemon-light + purge: yes + state: absent + tags: + - postfix + when: postfix_purge_exim | bool + +- name: compute main.cf SHA1 checksum ansible.builtin.shell: - cmd: 'grep -v -E "^(myhostname|mydestination|mailbox_command)" /etc/postfix/main.cf | md5sum -' + cmd: 'grep -v -E "^(myhostname|mydestination|mailbox_command)" /etc/postfix/main.cf | sha1sum | cut -d " " -f1' changed_when: False check_mode: no - register: default_main_cf + register: main_cf_checksum tags: - postfix diff --git a/postfix/tasks/minimal.yml b/postfix/tasks/minimal.yml index 36327b3e..b7bd29d0 100644 --- a/postfix/tasks/minimal.yml +++ b/postfix/tasks/minimal.yml @@ -1,10 +1,8 @@ --- -- name: ensure packages are installed - ansible.builtin.apt: - name: postfix - state: present - tags: - - postfix + +- name: display checksums of known main.cf files + debug: + var: main_cf_known_checksums - name: create minimal main.cf ansible.builtin.template: @@ -15,6 +13,6 @@ mode: "0644" force: true notify: restart postfix - when: (postfix_force_main_cf | bool) or (postfix_maincf_md5_jessie in default_main_cf.stdout) or (postfix_maincf_md5_stretch in default_main_cf.stdout) + when: (postfix_force_main_cf | bool) or (main_cf_checksum.stdout in main_cf_known_checksums) tags: - postfix diff --git a/postfix/tasks/packmail-spam.yml b/postfix/tasks/packmail-spam.yml index 114be769..f90f8cae 100644 --- a/postfix/tasks/packmail-spam.yml +++ b/postfix/tasks/packmail-spam.yml @@ -1,8 +1,6 @@ --- -- name: "mount /usr in rw" - ansible.builtin.command: - cmd: 'mount -o remount,rw /usr' - changed_when: False +- ansible.builtin.include_role: + name: evolix/remount-usr tags: postfix - name: copy spam.sh script diff --git a/postfix/tasks/packmail.yml b/postfix/tasks/packmail.yml index 6562e0d3..692024fa 100644 --- a/postfix/tasks/packmail.yml +++ b/postfix/tasks/packmail.yml @@ -1,11 +1,9 @@ --- -- name: ensure packages are installed +- name: Complementary packmail packages are installed ansible.builtin.apt: name: - - postfix - postfix-ldap - postfix-policyd-spf-python - - mailgraph state: present tags: - postfix @@ -24,6 +22,10 @@ enabled: yes state: started +- name: display checksums of known main.cf files + debug: + var: main_cf_known_checksums + - name: create packmail main.cf ansible.builtin.template: src: packmail_main.cf.j2 @@ -33,7 +35,7 @@ mode: "0644" force: true notify: restart postfix - when: (postfix_force_main_cf | bool) or (postfix_maincf_md5_jessie in default_main_cf.stdout) or (postfix_maincf_md5_stretch in default_main_cf.stdout) + when: (postfix_force_main_cf | bool) or (main_cf_checksum.stdout in main_cf_known_checksums) tags: - postfix diff --git a/postfix/templates/packmail_main.cf.j2 b/postfix/templates/packmail_main.cf.j2 index 65c95866..4bcbb2dd 100644 --- a/postfix/templates/packmail_main.cf.j2 +++ b/postfix/templates/packmail_main.cf.j2 @@ -66,7 +66,7 @@ smtpd_banner = $myhostname ESMTP mail server # Indique le nom d'hote pleinement qualifie ou se trouve postfix [OBLIGATOIRE] #par defaut, = [retour de la commande Unix hostname] -myhostname = {{ ansible_fqdn }} +myhostname = {{ postfix_hostname }} # Variable indiquant le domaine dans lequel se trouve la machine #par defaut, = [partie domain de la variable $myhostname] diff --git a/postfix/vars/main.yml b/postfix/vars/main.yml index e8a773c9..8d612f2e 100644 --- a/postfix/vars/main.yml +++ b/postfix/vars/main.yml @@ -1,5 +1,9 @@ --- -## MD5 hash of default main.cf filter, obtained with this command : -# grep -v -E "^(myhostname|mydestination|mailbox_command)" /etc/postfix/main.cf | md5sum - -postfix_maincf_md5_jessie: "5450c05d65878e99dad696c7c722e511" -postfix_maincf_md5_stretch: "30022953f1f61f002bfb72e163ecb27e" +# Output of default main.cf: +# grep -v -E "^(myhostname|mydestination|mailbox_command)" /etc/postfix/main.cf | sha1sum | cut -d " " -f1 +main_cf_known_checksums: + - 72bd6999f053ea89b359e233cf252616ee6e2fbb # bookworm + - efd078215285ad520addee5b11af869717627b95 # bullseye + - c7d1e008120565927b213be1bf646fddfa949dc6 # buster + - 8b4de47321a9c003bf414a683c0d056b4469b325 # stretch + - f72feb50754830a7d8ae46f28e86d758881bcfc3 # jessie diff --git a/proftpd/tasks/accounts.yml b/proftpd/tasks/accounts.yml index 99b036c9..11b2f60d 100644 --- a/proftpd/tasks/accounts.yml +++ b/proftpd/tasks/accounts.yml @@ -61,6 +61,27 @@ tags: - proftpd +- name: Whitelist ip for users (SFTP) + ansible.builtin.blockinfile: + dest: /etc/proftpd/conf.d/sftp.conf + marker: "# {mark} ANSIBLE MANAGED BLOCK - Whitelist ip for users" + block: | + {% for user in proftpd_accounts_final %} + {% if user.group is defined %} + + + {% for ip in proftpd_sftp_ips_whitelist[user.group] %} + Allow from {{ ip }} + {% endfor %} + DenyAll + + + {% endif %} + {% endfor %} + insertbefore: "" + notify: restart proftpd + when: proftpd_sftp_enable_user_whitelist | bool + - name: Allow keys for SFTP account ansible.builtin.template: dest: "/etc/proftpd/sftp.authorized_keys/{{ _proftpd_account.name }}" @@ -73,5 +94,6 @@ when: - proftpd_sftp_enable | bool - proftpd_sftp_use_publickeys | bool + - _proftpd_account.sshkeys is defined tags: - proftpd diff --git a/redis/defaults/main.yml b/redis/defaults/main.yml index b5547597..dc5a5d06 100644 --- a/redis/defaults/main.yml +++ b/redis/defaults/main.yml @@ -3,6 +3,8 @@ redis_systemd_name: redis-server redis_conf_dir_prefix: /etc/redis +redis_conf_marker_label: "ANSIBLE MANAGED CONFIGURATION" + redis_force_instance_port: False redis_port: 6379 diff --git a/redis/files/munin_redis b/redis/files/munin_redis index 55474435..ef3b61ad 100644 --- a/redis/files/munin_redis +++ b/redis/files/munin_redis @@ -1,243 +1,439 @@ #!/usr/bin/perl -w -# -## Copyright (C) 2009 Gleb Voronich -## -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; version 2 dated June, -## 1991. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -## -## -## $Log$ -## -## Based on Redis module code v0.08 2009/from http://svn.rot13.org/index.cgi/Redis -## -## Installation process: -## -## 1. Download the plugin to your plugins directory (e.g. /usr/share/munin/plugins) -## 2. Create 3 symlinks at the directory that us used by munin for plugins detection (e.g. /etc/munin/plugins): redis_connected_clients, redis_per_sec and and redis_used_memory -## 3. Edit plugin-conf.d/munin-node if it is needed (env.host and env.port variables are accepted; set env.password for password protected Redis server) -## 4. Restart munin-node service -## -## Magic Markers -#%# family=auto -#%# capabilities=autoconf suggest +=head CONFIGURATION + + Based on Redis module code v0.08 2009/from http://svn.rot13.org/index.cgi/Redis + + Installation process: + + 1. Download the plugin to your plugins directory (e.g. /usr/share/munin/plugins) + 2. Symlink it to your configuration directory (e.g. ln -s /usr/share/munin/plugins/redis /etc/munin/plugins/redis) + 3. Edit plugin-conf.d/munin-node with the options to connect to your redis instances (see below for an example) + 4. Restart munin-node service + + Example config + [redis] + env.host1 127.0.0.1 + env.port1 6379 + env.password1 password + env.title_prefix1 redis-1 + env.host2 /run/redis.sock + env.title_prefix2 redis-2 + + Each host should be specified with at least a host or unixsocket variable suffixed with + a number, the first being 1, the second being 2 etc. They must be in sequence. + Other options are: + * port - the redis port to connect to + * password - the password to use with the AUTH command + * title_prefix - a prefix to put before the title of the graph, this is strongly recommended for multiple instances + + Graphs: + This generates multigraphs for: + * Connected clients + * Key Hit vs Miss ratio + * Keys per second, hits/misses/expirations/evictions + * Replication backlog + * Replication lag + * Request per second + * Total number of keys and keys with expires + * Used memory + +=head COPYRIGHT + + Copyright (C) 2020 Rowan Wookey + Copyright (C) 2009 Gleb Voronich + +=head LICENSE + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 dated June, + 1991. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +=head MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut use strict; use IO::Socket::INET; use IO::Socket::UNIX; -use Switch; -my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "127.0.0.1"; -my $UNIX_SOCKET = exists $ENV{'unixsocket'} ? $ENV{'unixsocket'} : ''; # path to Redis Unix sock file -my $PORT = exists $ENV{'port'} ? $ENV{'port'} : 6379; -my $PASSWORD = exists $ENV{'password'} ? $ENV{'password'} : undef; -my $TITLE_PREFIX = exists $ENV{'title_prefix'} ? $ENV{'title_prefix'} . ": " : ""; +my %INSTANCES; +my $HOST; +my $PORT; +my $PASSWORD; + +for (my $i = 1; $ENV{"host$i"}; $i++) +{ + $HOST = exists $ENV{"host$i"} ? $ENV{"host$i"} : "127.0.0.1"; + $PORT = exists $ENV{"port$i"} ? $ENV{"port$i"} : 6379; + $PASSWORD = exists $ENV{"password$i"} ? $ENV{"password$i"} : undef; + my $TITLE_PREFIX = exists $ENV{"title_prefix$i"} ? $ENV{"title_prefix$i"} . ": " : ""; + my $SOCK = &get_conn(); + $INSTANCES{"instance$i"} = { + HOST => $HOST, + PORT => $PORT, + PASSWORD => $PASSWORD, + TITLE_PREFIX => $TITLE_PREFIX, + SOCK => $SOCK + }; +} + -my $sock = &get_conn(); my $config = ( defined $ARGV[0] and $ARGV[0] eq "config" ); my $autoconf = ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ); if ( $autoconf ) { - if ( defined( $sock ) ) { + if (!%INSTANCES) { + print "no (no redis instances configured)\n"; + exit 0; + } + my $err = ''; + for my $INSTANCE (keys %INSTANCES) { + if (! defined( $INSTANCES{$INSTANCE}{'SOCK'} ) ) { + $err = "no (unable to connect to ".$INSTANCES{$INSTANCE}{'HOST'}."\[:". $INSTANCES{$INSTANCE}{'PORT'}."\])\n"; + } + } + if ($err) { + print $err; + } else { print "yes\n"; - exit 0; - } else { - print "no (unable to connect to $HOST\[:$PORT\])\n"; - exit 0; - } -} -my $suggest = ( defined $ARGV[0] and $ARGV[0] eq "suggest" ); -if ( $suggest ) { - if ( defined( $sock ) ) { - my @plugins = ('connected_clients', 'key_ratio', 'keys_per_sec', 'per_sec', 'used_keys', 'used_memory'); - foreach my $plugin (@plugins) { - print "$plugin\n"; - } - exit 0; - } else { - print "no (unable to connect to $HOST\[:$PORT\])\n"; - exit 0; } + exit 0; } -my $hash=&get_info(); +my $total = 0; -$0 =~ s/(.+)redis_//g; +my $multi_graph_output = ''; +my $instance_graph_output = ''; -switch ($0) { - case "connected_clients" { - if ( $config ) { - my $maxclients= get_config("maxclients")->{"maxclients"}; - print "graph_title ${TITLE_PREFIX}Connected clients\n"; - print "graph_vlabel Connected clients\n"; - print "graph_category search\n"; - print "graph_args -l 0\n"; - print "connected_clients.line $maxclients:ff0000:Limit\n"; - print "connected_clients.label connected clients\n"; - exit 0; +my $connected_clients = 0; +my $keyspace_hits = 0; +my $keyspace_misses = 0; +my $expired_keys = 0; +my $evicted_keys = 0; +my $total_commands_processed = 0; +my $total_connections_received = 0; +my $repl_backlog_size = 0; +my $used_memory = 0; +my $used_memory_rss = 0; +my $used_memory_peak = 0; +my $total_keys = 0; +my $total_expires = 0; +foreach my $INSTANCE (keys %INSTANCES) { + + my $sock = $INSTANCES{$INSTANCE}{'SOCK'}; + my $TITLE_PREFIX = $INSTANCES{$INSTANCE}{'TITLE_PREFIX'}; + my $hash = get_info($sock); + + my $dbs; + foreach my $key (keys %{$hash}) { + if ( $key =~ /^db\d+$/ && $hash->{$key} =~ /keys=(\d+),expires=(\d+)/ ) { + $total_keys += $1; + $total_expires += $2; + $dbs->{$key} = [ $1, $2 ]; } - - print "connected_clients.value " . $hash->{'connected_clients'} . "\n"; } - - case "keys_per_sec" { - if ( $config ) { - print "graph_title ${TITLE_PREFIX}Keys Per Second\n"; - print "graph_vlabel per \${graph_period}\n"; - print "graph_category search\n"; - print "graph_args -l 0\n"; - print "hits.label hits\n"; - print "hits.type COUNTER\n"; - print "misses.label misses\n"; - print "misses.type COUNTER\n"; - print "expired.label expirations\n"; - print "expired.type COUNTER\n"; - print "evictions.label evictions\n"; - print "evictions.type COUNTER\n"; - exit 0; + if ( $config ) { + my $ret = get_config("maxclients", $sock); + # if the CONFIG command is disabled we don't show the max clients + my $maxclients = defined $ret ? $ret->{"maxclients"} : 0; + $instance_graph_output .= "multigraph redis_connected_clients.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Connected clients\n"; + $instance_graph_output .= "graph_vlabel Connected clients\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; + if ($maxclients) { + $instance_graph_output .= "connected_clients.line $maxclients:ff0000:Limit\n"; } - - print "hits.value " . $hash->{'keyspace_hits'} . "\n"; - print "misses.value " . $hash->{'keyspace_misses'} . "\n"; - print "expired.value " . $hash->{'expired_keys'} . "\n"; - print "evictions.value " . $hash->{'evicted_keys'} . "\n"; - } - - case "key_ratio" { - if ( $config ) { - print "graph_title ${TITLE_PREFIX}Key Hit vs Miss Ratio\n"; - print "graph_vlabel per \${graph_period}\n"; - print "graph_category search\n"; - print "graph_args -u 100 -l 0 -r --base 1000\n"; - print "hitratio.label hit ratio\n"; - print "hitratio.type GAUGE\n"; - print "hitratio.draw AREA\n"; - print "missratio.label miss ratio\n"; - print "missratio.type GAUGE\n"; - print "missratio.draw STACK\n"; - exit 0; - } - - my $total = $hash->{'keyspace_hits'} + $hash->{'keyspace_misses'}; - my $hitratio = 0; - my $missratio = 0; - if ($total > 0) { - $hitratio = $hash->{'keyspace_hits'} / $total * 100; - $missratio = $hash->{'keyspace_misses'} / $total * 100; - } - printf("hitratio.value %.2f\n", $hitratio); - printf("missratio.value %.2f\n", $missratio); - } - - - case "per_sec" { - if ( $config ) { - print "graph_title ${TITLE_PREFIX}Per second\n"; - print "graph_vlabel per \${graph_period}\n"; - print "graph_category search\n"; - print "graph_args -l 0\n"; - print "requests.label requests\n"; - print "requests.type COUNTER\n"; - print "connections.label connections\n"; - print "connections.type COUNTER\n"; - exit 0; - } - - print "requests.value ". $hash->{'total_commands_processed'} ."\n"; - print "connections.value ". $hash->{'total_connections_received'} ."\n"; - } - - - case "used_memory" { - if ( $config ) { - my $maxmemory = get_config("maxmemory")->{"maxmemory"}; - print "graph_title ${TITLE_PREFIX}Used memory\n"; - print "graph_vlabel Used memory\n"; - print "graph_category search\n"; - print "graph_args -l 0 --base 1024\n"; - print "used_memory.line $maxmemory:ff0000:Limit\n"; - print "used_memory.label used memory\n"; - print "used_memory_peak.label used memory in peak\n"; - print "used_memory_rss.label Resident set size memory usage\n"; - exit 0; - } - - print "used_memory.value ". $hash->{'used_memory'} ."\n"; - print "used_memory_rss.value ". $hash->{'used_memory_rss'} ."\n"; - print "used_memory_peak.value ". $hash->{'used_memory_peak'} ."\n"; - } - - case "used_keys" { - my $dbs; - foreach my $key (keys %{$hash}) { - if ( $key =~ /^db\d+$/ && $hash->{$key} =~ /keys=(\d+),expires=(\d+)/ ) { - $dbs->{$key} = [ $1, $2 ]; - } - } - - if ( $config ) { - print "graph_title ${TITLE_PREFIX}Used keys\n"; - print "graph_vlabel Used keys\n"; - print "graph_category search\n"; - print "graph_args -l 0\n"; - - foreach my $db (keys %{$dbs}) { - printf "%s_keys.label %s keys\n", $db, $db; - printf "%s_expires.label %s expires\n", $db, $db; - } - - exit 0; + $instance_graph_output .= "connected_clients.label connected clients\n"; + $instance_graph_output .= "multigraph keys_per_sec.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Keys Per Second\n"; + $instance_graph_output .= "graph_vlabel per \${graph_period}\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; + $instance_graph_output .= "hits.label hits\n"; + $instance_graph_output .= "hits.type COUNTER\n"; + $instance_graph_output .= "misses.label misses\n"; + $instance_graph_output .= "misses.type COUNTER\n"; + $instance_graph_output .= "expired.label expirations\n"; + $instance_graph_output .= "expired.type COUNTER\n"; + $instance_graph_output .= "evicted_keys.label evictions\n"; + $instance_graph_output .= "evicted_keys.type COUNTER\n"; + $instance_graph_output .= "multigraph redis_key_ratio.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Key Hit vs Miss Ratio\n"; + $instance_graph_output .= "graph_vlabel per \${graph_period}\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -u 100 -l 0 -r --base 1000\n"; + $instance_graph_output .= "hitratio.label hit ratio\n"; + $instance_graph_output .= "hitratio.type GAUGE\n"; + $instance_graph_output .= "hitratio.draw AREA\n"; + $instance_graph_output .= "missratio.label miss ratio\n"; + $instance_graph_output .= "missratio.type GAUGE\n"; + $instance_graph_output .= "missratio.draw STACK\n"; + $instance_graph_output .= "multigraph redis_per_sec.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Requests Per second\n"; + $instance_graph_output .= "graph_vlabel per \${graph_period}\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; + $instance_graph_output .= "requests.label requests\n"; + $instance_graph_output .= "requests.type COUNTER\n"; + $instance_graph_output .= "connections.label connections\n"; + $instance_graph_output .= "connections.type COUNTER\n"; + $instance_graph_output .= "multigraph redis_repl_backlog_size.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}replication backlog\n"; + $instance_graph_output .= "graph_vlabel replication backlog\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; + $instance_graph_output .= "repl_backlog_size.label bytes behind master\n"; + $instance_graph_output .= "multigraph redis_repl_lag.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}replication lag\n"; + $instance_graph_output .= "graph_vlabel replication lag\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; + $instance_graph_output .= "repl_backlog_size.label amount behind master\n"; + # if the CONFIG command is disabled we don't show maxmemory + $ret = get_config("maxmemory", $sock); + my $maxmemory = defined $ret ? $ret->{"maxmemory"} : 0; + $instance_graph_output .= "multigraph redis_used_memory.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Used memory\n"; + $instance_graph_output .= "graph_vlabel Used memory\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0 --base 1024\n"; + if ($maxmemory) { + $instance_graph_output .= "used_memory.line $maxmemory:ff0000:Limit\n"; } + $instance_graph_output .= "used_memory.label used memory\n"; + $instance_graph_output .= "used_memory_peak.label used memory in peak\n"; + $instance_graph_output .= "used_memory_rss.label Resident set size memory usage\n"; + $instance_graph_output .= "multigraph redis_used_keys.$INSTANCE\n"; + $instance_graph_output .= "graph_title ${TITLE_PREFIX}Used keys\n"; + $instance_graph_output .= "graph_vlabel Used keys\n"; + $instance_graph_output .= "graph_category db\n"; + $instance_graph_output .= "graph_args -l 0\n"; foreach my $db (keys %{$dbs}) { - printf "%s_keys.value %d\n", $db, $dbs->{$db}[0]; - printf "%s_expires.value %d\n", $db, $dbs->{$db}[1]; + $instance_graph_output .= sprintf "%s_keys.label %s keys\n", $db, $db; + $instance_graph_output .= sprintf "%s_expires.label %s expires\n", $db, $db; } + + next; } + + $instance_graph_output .= "multigraph redis_connected_clients.$INSTANCE\n"; + $instance_graph_output .= "connected_clients.value " . $hash->{'connected_clients'} . "\n"; + $connected_clients += $hash->{'connected_clients'}; + $instance_graph_output .= "multigraph keys_per_sec.$INSTANCE\n"; + $instance_graph_output .= "hits.value " . $hash->{'keyspace_hits'} . "\n"; + $keyspace_hits += $hash->{'keyspace_hits'}; + $instance_graph_output .= "misses.value " . $hash->{'keyspace_misses'} . "\n"; + $keyspace_misses += $hash->{'keyspace_misses'}; + $instance_graph_output .= "expired.value " . $hash->{'expired_keys'} . "\n"; + $expired_keys += $hash->{'expired_keys'}; + $instance_graph_output .= "evicted_keys.value " . $hash->{'evicted_keys'} . "\n"; + $evicted_keys += $hash->{'evicted_keys'}; + $instance_graph_output .= "multigraph redis_key_ratio.$INSTANCE\n"; + my $total = $hash->{'keyspace_hits'} + $hash->{'keyspace_misses'}; + my $hitratio = 0; + my $missratio = 0; + if ($total > 0) { + $hitratio = $hash->{'keyspace_hits'} / $total * 100; + $missratio = $hash->{'keyspace_misses'} / $total * 100; + } + $instance_graph_output .= sprintf("hitratio.value %.2f\n", $hitratio); + $instance_graph_output .= sprintf("missratio.value %.2f\n", $missratio); + $instance_graph_output .= "multigraph redis_per_sec.$INSTANCE\n"; + $instance_graph_output .= "requests.value ". $hash->{'total_commands_processed'} ."\n"; + $total_commands_processed += $hash->{'total_commands_processed'}; + $instance_graph_output .= "connections.value ". $hash->{'total_connections_received'} ."\n"; + $total_connections_received += $hash->{'total_connections_received'}; + $instance_graph_output .= "multigraph redis_repl_backlog_size.$INSTANCE\n"; + $instance_graph_output .= "repl_backlog_size.value " . $hash->{'repl_backlog_size'} . "\n"; + $repl_backlog_size += $hash->{'repl_backlog_size'}; + + $instance_graph_output .= "multigraph redis_repl_lag.$INSTANCE\n"; + if (exists $hash->{slave0} && $hash->{slave0} =~ /lag=(\d+)/) { + $repl_backlog_size += $1; + $instance_graph_output .= "repl_backlog_size.value " . $1 . "\n"; + } else { + $instance_graph_output .= "repl_backlog_size.value 0\n"; + } + + + $instance_graph_output .= "multigraph redis_used_memory.$INSTANCE\n"; + $instance_graph_output .= "used_memory.value ". $hash->{'used_memory'} ."\n"; + + $used_memory += $hash->{'used_memory'}; + $instance_graph_output .= "used_memory_rss.value ". $hash->{'used_memory_rss'} ."\n"; + $used_memory_rss += $hash->{'used_memory_rss'}; + $instance_graph_output .= "used_memory_peak.value ". $hash->{'used_memory_peak'} ."\n"; + $used_memory_peak += $hash->{'used_memory_peak'}; + + $instance_graph_output .= "multigraph redis_used_keys.$INSTANCE\n"; + foreach my $db (keys %{$dbs}) { + $instance_graph_output .= sprintf "%s_keys.value %d\n", $db, $dbs->{$db}[0]; + $instance_graph_output .= sprintf "%s_expires.value %d\n", $db, $dbs->{$db}[1]; + } + close ($sock); } -close ($sock); +# multigraph output +if ($config) { + $multi_graph_output .= "multigraph redis_connected_clients\n"; + $multi_graph_output .= "graph_title Connected clients\n"; + $multi_graph_output .= "graph_vlabel Connected clients\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "connected_clients.label connected clients\n"; + $multi_graph_output .= "multigraph keys_per_sec\n"; + $multi_graph_output .= "graph_title Keys Per Second\n"; + $multi_graph_output .= "graph_vlabel per \${graph_period}\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "hits.label hits\n"; + $multi_graph_output .= "hits.type COUNTER\n"; + $multi_graph_output .= "misses.label misses\n"; + $multi_graph_output .= "misses.type COUNTER\n"; + $multi_graph_output .= "expired.label expirations\n"; + $multi_graph_output .= "expired.type COUNTER\n"; + $multi_graph_output .= "evicted_keys.label evictions\n"; + $multi_graph_output .= "evicted_keys.type COUNTER\n"; + $multi_graph_output .= "multigraph redis_key_ratio\n"; + $multi_graph_output .= "graph_title Key Hit vs Miss Ratio\n"; + $multi_graph_output .= "graph_vlabel per \${graph_period}\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -u 100 -l 0 -r --base 1000\n"; + $multi_graph_output .= "hitratio.label hit ratio\n"; + $multi_graph_output .= "hitratio.type GAUGE\n"; + $multi_graph_output .= "hitratio.draw AREA\n"; + $multi_graph_output .= "missratio.label miss ratio\n"; + $multi_graph_output .= "missratio.type GAUGE\n"; + $multi_graph_output .= "missratio.draw STACK\n"; + $multi_graph_output .= "multigraph redis_per_sec\n"; + $multi_graph_output .= "graph_title Requests Per second\n"; + $multi_graph_output .= "graph_vlabel per \${graph_period}\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "requests.label requests\n"; + $multi_graph_output .= "requests.type COUNTER\n"; + $multi_graph_output .= "connections.label connections\n"; + $multi_graph_output .= "connections.type COUNTER\n"; + $multi_graph_output .= "multigraph redis_repl_backlog_size\n"; + $multi_graph_output .= "graph_title replication backlog\n"; + $multi_graph_output .= "graph_vlabel replication backlog\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "repl_backlog_size.label bytes behind master\n"; + $multi_graph_output .= "multigraph redis_repl_lag\n"; + $multi_graph_output .= "graph_title replication lag\n"; + $multi_graph_output .= "graph_vlabel replication lag\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "repl_backlog_size.label amount behind master\n"; + $multi_graph_output .= "multigraph redis_used_memory\n"; + $multi_graph_output .= "graph_title Used memory\n"; + $multi_graph_output .= "graph_vlabel Used memory\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0 --base 1024\n"; + $multi_graph_output .= "used_memory.label used memory\n"; + $multi_graph_output .= "used_memory_peak.label used memory in peak\n"; + $multi_graph_output .= "used_memory_rss.label Resident set size memory usage\n"; + $multi_graph_output .= "multigraph redis_used_keys\n"; + $multi_graph_output .= "graph_title Used keys\n"; + $multi_graph_output .= "graph_vlabel Used keys\n"; + $multi_graph_output .= "graph_category db\n"; + $multi_graph_output .= "graph_args -l 0\n"; + $multi_graph_output .= "total_keys.label Total keys\n"; + $multi_graph_output .= "total_expires.label Total expires\n"; +} else { + + $multi_graph_output .= "multigraph redis_connected_clients\n"; + $multi_graph_output .= "connected_clients.value " . $connected_clients . "\n"; + $multi_graph_output .= "multigraph keys_per_sec\n"; + $multi_graph_output .= "hits.value " . $keyspace_hits . "\n"; + $multi_graph_output .= "misses.value " . $keyspace_misses . "\n"; + $multi_graph_output .= "expired.value " . $expired_keys . "\n"; + $multi_graph_output .= "evicted_keys.value " . $evicted_keys . "\n"; + $multi_graph_output .= "multigraph redis_key_ratio\n"; + my $total = $keyspace_hits + $keyspace_misses; + my $hitratio = 0; + my $missratio = 0; + if ($total > 0) { + $hitratio = $keyspace_hits / $total * 100; + $missratio = $keyspace_misses / $total * 100; + } + $multi_graph_output .= sprintf("hitratio.value %.2f\n", $hitratio); + $multi_graph_output .= sprintf("missratio.value %.2f\n", $missratio); + $multi_graph_output .= "multigraph redis_per_sec\n"; + $multi_graph_output .= "requests.value ". $total_commands_processed ."\n"; + $multi_graph_output .= "connections.value ". $total_connections_received ."\n"; + $multi_graph_output .= "multigraph redis_repl_backlog_size\n"; + $multi_graph_output .= "repl_backlog_size.value " . $repl_backlog_size . "\n"; + + $multi_graph_output .= "multigraph redis_repl_lag\n"; + $multi_graph_output .= "repl_backlog_size.value " . $repl_backlog_size . "\n"; + + + $multi_graph_output .= "multigraph redis_used_memory\n"; + $multi_graph_output .= "used_memory.value ". $used_memory ."\n"; + + $multi_graph_output .= "used_memory_rss.value ". $used_memory_rss ."\n"; + $multi_graph_output .= "used_memory_peak.value ". $used_memory_peak ."\n"; + + $multi_graph_output .= "multigraph redis_used_keys\n"; + $multi_graph_output .= "total_keys.value $total_keys\n"; + $multi_graph_output .= "total_expires.value $total_expires\n"; + +} +print $multi_graph_output; +print $instance_graph_output; sub get_conn { - + my $sock; - - if( $UNIX_SOCKET && -S $UNIX_SOCKET ){ - - $sock = IO::Socket::UNIX->new( - Type => SOCK_STREAM(), - Peer => $UNIX_SOCKET, - ); - + + if(-S $HOST ){ + + $sock = IO::Socket::UNIX->new( + Type => SOCK_STREAM(), + Peer => $HOST, + ); }else{ - - $sock = IO::Socket::INET->new( - PeerAddr => $HOST, - PeerPort => $PORT, - Timeout => 10, - Proto => 'tcp' - ); + + $sock = IO::Socket::INET->new( + PeerAddr => $HOST, + PeerPort => $PORT, + Timeout => 10, + Proto => 'tcp' + ); } - + + if (! defined($sock)) { + die "can't read socket: $!"; + } + if ( defined( $PASSWORD ) ) { print $sock "AUTH ", $PASSWORD, "\r\n"; my $result = <$sock> || die "can't read socket: $!"; } + return $sock; } sub get_info{ + my $sock = $_[0]; print $sock "INFO\r\n"; my $result = <$sock> || die "can't read socket: $!"; @@ -257,13 +453,12 @@ sub get_info{ # This subroutine returns configuration matched to supplied as object sub get_config{ - + my $sock = $_[1]; print $sock "*3\r\n\$6\r\nCONFIG\r\n\$3\r\nGET\r\n\$".length($_[0])."\r\n".$_[0]."\r\n"; # Response will look like like # *2\r\n$9\r\nmaxmemory\r\n$10\r\n3221225472\r\n my $type = <$sock> || die "can't read socket: $!"; - my $conf; if( substr($type,0,1) ne "*" ) { return $conf; diff --git a/redis/tasks/default-server.yml b/redis/tasks/default-server.yml index 89a664e6..89ba641e 100644 --- a/redis/tasks/default-server.yml +++ b/redis/tasks/default-server.yml @@ -1,12 +1,32 @@ --- -- name: Redis is configured. - ansible.builtin.template: - src: redis.conf.j2 - dest: "{{ redis_conf_dir }}/redis.conf" +- name: "Add begin marker if missing" + ansible.builtin.lineinfile: + path: "{{ redis_conf_dir }}/redis.conf" + line: "# BEGIN {{ redis_conf_marker_label }}" + insertbefore: BOF + create: yes + tags: + - redis + +- name: "Add end marker if missing" + ansible.builtin.lineinfile: + path: "{{ redis_conf_dir }}/redis.conf" + line: "# END {{ redis_conf_marker_label }}" + insertbefore: "Generated by CONFIG REWRITE" + create: yes + tags: + - redis + +- name: "Create config if missing" + ansible.builtin.blockinfile: + path: "{{ redis_conf_dir }}/redis.conf" + marker: "# {mark} {{ redis_conf_marker_label }}" + block: "{{ lookup('ansible.builtin.template', '../templates/redis.conf.j2') }}" mode: "0640" owner: redis group: redis + create: yes notify: "{{ redis_restart_handler_name }}" tags: - redis diff --git a/redis/tasks/instance-server.yml b/redis/tasks/instance-server.yml index 42dc1876..b5f11053 100644 --- a/redis/tasks/instance-server.yml +++ b/redis/tasks/instance-server.yml @@ -118,14 +118,33 @@ tags: - redis +- name: "Add begin marker if missing" + ansible.builtin.lineinfile: + path: "{{ redis_conf_dir }}/redis.conf" + line: "# BEGIN {{ redis_conf_marker_label }}" + insertbefore: BOF + create: yes + tags: + - redis -- name: "Instance '{{ redis_instance_name }}' configuration file is present" - ansible.builtin.template: - src: redis.conf.j2 - dest: "{{ redis_conf_dir }}/redis.conf" +- name: "Add end marker if missing" + ansible.builtin.lineinfile: + path: "{{ redis_conf_dir }}/redis.conf" + line: "# END {{ redis_conf_marker_label }}" + insertbefore: "# Generated by CONFIG REWRITE" + create: yes + tags: + - redis + +- name: "Create config if missing" + ansible.builtin.blockinfile: + path: "{{ redis_conf_dir }}/redis.conf" + marker: "# {mark} {{ redis_conf_marker_label }}" + block: "{{ lookup('ansible.builtin.template', '../templates/redis.conf.j2') }}" mode: "0640" owner: redis-{{ redis_instance_name }} group: redis-{{ redis_instance_name }} + create: yes notify: "{{ redis_restart_handler_name }}" tags: - redis diff --git a/redis/tasks/thp.yml b/redis/tasks/thp.yml index 7a215788..133466b7 100644 --- a/redis/tasks/thp.yml +++ b/redis/tasks/thp.yml @@ -23,6 +23,7 @@ path: /etc/sysfs.conf line: kernel/mm/transparent_hugepage/enabled = {{ redis_sysctl_transparent_hugepage_enabled }} regexp: "kernel/mm/transparent_hugepage/enabled" + create: yes tags: - redis - kernel @@ -32,4 +33,4 @@ cmd: "echo '{{ redis_sysctl_transparent_hugepage_enabled }}' >> /sys/kernel/mm/transparent_hugepage/enabled" tags: - redis - - kernel \ No newline at end of file + - kernel diff --git a/remount-usr/tasks/main.yml b/remount-usr/tasks/main.yml index eb5c0109..034a66f5 100644 --- a/remount-usr/tasks/main.yml +++ b/remount-usr/tasks/main.yml @@ -1,17 +1,18 @@ --- # findmnt returns 0 on hit, 1 on miss # If the return code is higher than 1, it's a blocking failure + - name: "check if /usr is a read-only partition" ansible.builtin.command: cmd: 'findmnt /usr --noheadings --options ro' changed_when: False failed_when: usr_partition.rc > 1 - check_mode: no register: usr_partition + check_mode: False - name: "mount /usr in rw" ansible.builtin.command: - cmd: 'mount -o remount,rw /usr' - changed_when: False + cmd: 'mount --options remount,rw /usr' when: usr_partition.rc == 0 notify: remount usr + changed_when: False diff --git a/spamassasin/handlers/main.yml b/spamassasin/handlers/main.yml index 78597a37..7dbc9c7f 100644 --- a/spamassasin/handlers/main.yml +++ b/spamassasin/handlers/main.yml @@ -3,3 +3,8 @@ ansible.builtin.service: name: spamassassin state: restarted + +- name: restart spamd + ansible.builtin.service: + name: spamd + state: restarted diff --git a/spamassasin/tasks/main.yml b/spamassasin/tasks/main.yml index 9f2889ca..0ee6f76a 100644 --- a/spamassasin/tasks/main.yml +++ b/spamassasin/tasks/main.yml @@ -1,29 +1,47 @@ --- -- name: install SpamAssasin + +- name: For Debian < 12 + set_fact: + spamassassin_restart_handler: restart spamassassin + spamassassin_package_name: spamassassin + tags: + - spamassassin + when: ansible_distribution_major_version is version('12', '<') + +# the package is called "spamd" after Debian 12 +- name: For Debian >= 12 + set_fact: + spamassassin_restart_handler: restart spamd + spamassassin_package_name: spamd + tags: + - spamassassin + when: ansible_distribution_major_version is version('12', '>=') + +- name: Install SpamAssassin ansible.builtin.apt: name: - - spamassassin + - "{{ spamassassin_package_name }}" state: present tags: - spamassassin -- name: configure SpamAssasin +- name: Configure SpamAssassin ansible.builtin.copy: src: spamassassin.cf dest: /etc/spamassassin/local_evolix.cf mode: "0644" - notify: restart spamassassin + notify: "{{ spamassassin_restart_handler }}" tags: - spamassassin -- name: enable SpamAssasin +- name: Enable SpamAssassin ansible.builtin.replace: dest: /etc/default/spamassassin regexp: 'ENABLED=0' replace: 'ENABLED=1' - notify: restart spamassassin tags: - spamassassin + when: ansible_distribution_major_version is version('12', '<') - name: add amavis user to debian-spamd group ansible.builtin.user: @@ -97,5 +115,15 @@ name: spamassassin state: started enabled: True + when: ansible_distribution_major_version is version('12', '<') + tags: + - spamassassin + +- name: ensure spamd is started and enabled + ansible.builtin.systemd: + name: spamd + state: started + enabled: True + when: ansible_distribution_major_version is version('12', '>=') tags: - spamassassin diff --git a/squid/README.md b/squid/README.md index 8811a91f..aba25b4d 100644 --- a/squid/README.md +++ b/squid/README.md @@ -6,7 +6,7 @@ Installation and configuration of Squid Everything is in the `tasks/main.yml` file. -A blank file is created at `/etc/squid3/whitelist-custom.conf` to add addresses in the whitelist. +A blank file is created at `/etc/squid/whitelist-custom.conf` to add addresses in the whitelist. ## Available variables diff --git a/squid/tasks/main.yml b/squid/tasks/main.yml index 2f0e94aa..965be04b 100644 --- a/squid/tasks/main.yml +++ b/squid/tasks/main.yml @@ -38,14 +38,14 @@ - name: "squid.conf is present (jessie)" ansible.builtin.template: src: squid.conf.j2 - dest: /etc/squid3/squid.conf + dest: /etc/squid/squid.conf notify: "restart squid3" when: ansible_distribution_release == "jessie" - name: "evolix whitelist is present (jessie)" ansible.builtin.copy: src: whitelist-evolinux.conf - dest: /etc/squid3/whitelist.conf + dest: /etc/squid/whitelist.conf force: false notify: "reload squid3" when: ansible_distribution_release == "jessie" @@ -135,7 +135,7 @@ - name: add some URL in whitelist (Debian 8) ansible.builtin.lineinfile: insertafter: EOF - dest: /etc/squid3/whitelist.conf + dest: /etc/squid/whitelist.conf line: "{{ item }}" state: present loop: '{{ squid_whitelist_items }}' diff --git a/squid/templates/squid.conf.j2 b/squid/templates/squid.conf.j2 index 108a3bc1..4c89a777 100644 --- a/squid/templates/squid.conf.j2 +++ b/squid/templates/squid.conf.j2 @@ -8,7 +8,7 @@ acl localhost src 127.0.0.0/32 acl INTERNE src {{ squid_address }}/32 127.0.0.0/8 acl Safe_ports port 80 # http acl SSL_ports port 443 563 -acl WHITELIST url_regex "/etc/squid3/whitelist.conf" +acl WHITELIST url_regex "/etc/squid/whitelist.conf" http_access deny !WHITELIST http_access allow INTERNE http_access deny all diff --git a/timesyncd/templates/timesyncd.conf.j2 b/timesyncd/templates/timesyncd.conf.j2 index 8aebc1be..f58f81ee 100644 --- a/timesyncd/templates/timesyncd.conf.j2 +++ b/timesyncd/templates/timesyncd.conf.j2 @@ -1,3 +1,3 @@ # {{ ansible_managed }} [Time] -NTP="{{ timesyncd_ntp_servers | join(' ') }}" +NTP={{ timesyncd_ntp_servers | join(' ') }} diff --git a/unbound/defaults/main.yml b/unbound/defaults/main.yml index 86f51822..0d7807c1 100644 --- a/unbound/defaults/main.yml +++ b/unbound/defaults/main.yml @@ -1,9 +1,11 @@ --- + unbound_interfaces: -- '127.0.0.1' -- '::1' + - '127.0.0.1' + - '::1' + unbound_acls: -- '0.0.0.0/0 refuse' -- '127.0.0.0/8 allow_snoop' -- '::0/0 refuse' -- '::1 allow_snoop' + - '0.0.0.0/0 refuse' + - '127.0.0.0/8 allow_snoop' + - '::0/0 refuse' + - '::1 allow_snoop' diff --git a/unbound/files/munin-plugin.conf b/unbound/files/munin-plugin.conf new file mode 100644 index 00000000..cf008d48 --- /dev/null +++ b/unbound/files/munin-plugin.conf @@ -0,0 +1,5 @@ +[unbound*] +user root +env.statefile /tmp/munin-unbound-state +env.unbound_conf /etc/unbound/unbound.conf +env.unbound_control /usr/sbin/unbound-control diff --git a/unbound/handlers/main.yml b/unbound/handlers/main.yml index 7c801751..fdb9a629 100644 --- a/unbound/handlers/main.yml +++ b/unbound/handlers/main.yml @@ -1,5 +1,15 @@ --- -- name: reload unbound +- name: Restart unbound + ansible.builtin.service: + name: unbound + state: restarted + +- name: Reload unbound ansible.builtin.service: name: unbound state: reloaded + +- name: Restart munin-node + ansible.builtin.service: + name: munin-node + state: restarted diff --git a/unbound/tasks/main.yml b/unbound/tasks/main.yml index 976c6386..acc24812 100644 --- a/unbound/tasks/main.yml +++ b/unbound/tasks/main.yml @@ -1,38 +1,74 @@ --- - name: Install Unbound package ansible.builtin.apt: - name: unbound + name: + - unbound + - unbound-anchor + - dns-root-data state: present - when: ansible_distribution == "Debian" + cache_valid_time: 3600 tags: - - unbound - -- name: Retrieve list of root DNS servers - ansible.builtin.get_url: - url: https://www.internic.net/domain/named.cache - dest: /etc/unbound/root.hints - force: true - mode: "0644" - notify: reload unbound - tags: - - unbound + - unbound - name: Copy Unbound config ansible.builtin.template: - src: unbound.conf.j2 - dest: /etc/unbound/unbound.conf + src: evolinux.conf.j2 + dest: /etc/unbound/unbound.conf.d/evolinux.conf owner: root group: root mode: "0644" - when: ansible_distribution == "Debian" - notify: reload unbound + notify: Reload unbound tags: - - unbound + - unbound + +- name: Copy Unbound config for reloading (Debian 11 and older) + ansible.builtin.template: + src: evolinux-reload.conf.j2 + dest: /etc/unbound/unbound.conf.d/evolinux-reload.conf + owner: root + group: root + mode: "0644" + when: + - ansible_distribution_major_version is version('11', '<=') + notify: Restart unbound + tags: + - unbound + +- name: Copy munin plugin config + ansible.builtin.copy: + src: munin-plugin.conf + dest: /etc/munin/plugin-conf.d/unbound + owner: root + group: root + mode: "0644" + tags: + - unbound + +- name: Enable unbound munin plugin + ansible.builtin.file: + src: /usr/share/munin/plugins/unbound_munin_ + dest: "/etc/munin/plugins/unbound_munin_{{ plugin_name }}" + state: link + loop: + - hits + - queue + - memory + - by_type + - by_class + - by_opcode + - by_rcode + - by_flags + - histogram + loop_control: + loop_var: plugin_name + notify: Restart munin-node + tags: + - unbound - name: Starting and enabling Unbound ansible.builtin.service: name: unbound - enabled: yes + enabled: true state: started tags: - - unbound + - unbound diff --git a/unbound/templates/evolinux-reload.conf.j2 b/unbound/templates/evolinux-reload.conf.j2 new file mode 100644 index 00000000..f2f395ae --- /dev/null +++ b/unbound/templates/evolinux-reload.conf.j2 @@ -0,0 +1,7 @@ +# {{ ansible_managed }} + +remote-control: + control-enable: yes + # by default the control interface is is 127.0.0.1 and ::1 and port 8953 + # it is possible to use a unix socket too + control-interface: /run/unbound.ctl diff --git a/unbound/templates/evolinux.conf.j2 b/unbound/templates/evolinux.conf.j2 new file mode 100644 index 00000000..339dfe45 --- /dev/null +++ b/unbound/templates/evolinux.conf.j2 @@ -0,0 +1,53 @@ +server: + #interface: X.X.X.X + #interface: 127.0.0.1@5353 # listen on alternative port +{% for interface in unbound_interfaces %} + interface: {{ interface }} +{% endfor %} + +{% for acl in unbound_acls %} + access-control: {{ acl }} +{% endfor %} + + hide-identity: yes + hide-version: yes + + root-hints: "/usr/share/dns/root.hints" + + # Uncomment to enable DNSSEC validation. + #auto-trust-anchor-file: "/etc/unbound/root.key" + + # Enable extended stats for munin plugin + extended-statistics: yes + statistics-cumulative: no + statistics-interval: 0 + + + # Serve zones authoritatively from Unbound to resolver clients. + # Not for external service. + + #local-zone: "local." static + #local-data: "mycomputer.local. IN A 192.0.2.51" + #local-zone: "2.0.192.in-addr.arpa." static + #local-data-ptr: "192.0.2.51 mycomputer.local + # UDP EDNS reassembly buffer advertised to peers. Default 4096. + # May need lowering on broken networks with fragmentation/MTU issues, + # particularly if validating DNSSEC. + + #edns-buffer-size: 1480 + # Use TCP for "forward-zone" requests. Useful if you are making + # DNS requests over an SSH port forwarding. + #tcp-upstream: yes + + +# Use an upstream forwarder (recursive resolver) for specific zones. +# Example addresses given below are public resolvers valid as of 2014/03. +# +#forward-zone: +# name: "." # use for ALL queries +# forward-addr: 74.82.42.42 # he.net +# forward-addr: 2001:470:20::2 # he.net v6 +# forward-addr: 8.8.8.8 # google.com +# forward-addr: 2001:4860:4860::8888 # google.com v6 +# forward-addr: 208.67.222.222 # opendns.com +# forward-first: yes # try direct if forwarder fails diff --git a/unbound/templates/unbound.conf.j2 b/unbound/templates/unbound.conf.j2 deleted file mode 100644 index a97e725d..00000000 --- a/unbound/templates/unbound.conf.j2 +++ /dev/null @@ -1,45 +0,0 @@ -server: - #interface: X.X.X.X - #interface: 127.0.0.1@5353 # listen on alternative port -{% for interface in unbound_interfaces %} - interface: {{ interface }} -{% endfor %} - #do-ip6: no - -{% for acl in unbound_acls %} - access-control: {{ acl }} -{% endfor %} - - hide-identity: yes - hide-version: yes - - root-hints: "/etc/unbound/root.hints" - # Uncomment to enable DNSSEC validation. - #auto-trust-anchor-file: "/etc/unbound/root.key" - # Serve zones authoritatively from Unbound to resolver clients. - # Not for external service. - - #local-zone: "local." static - #local-data: "mycomputer.local. IN A 192.0.2.51" - #local-zone: "2.0.192.in-addr.arpa." static - #local-data-ptr: "192.0.2.51 mycomputer.local - # UDP EDNS reassembly buffer advertised to peers. Default 4096. - # May need lowering on broken networks with fragmentation/MTU issues, - # particularly if validating DNSSEC. - - #edns-buffer-size: 1480 - # Use TCP for "forward-zone" requests. Useful if you are making - # DNS requests over an SSH port forwarding. - #tcp-upstream: yes - -# Use an upstream forwarder (recursive resolver) for specific zones. -# Example addresses given below are public resolvers valid as of 2014/03. -# -#forward-zone: -# name: "." # use for ALL queries -# forward-addr: 74.82.42.42 # he.net -# forward-addr: 2001:470:20::2 # he.net v6 -# forward-addr: 8.8.8.8 # google.com -# forward-addr: 2001:4860:4860::8888 # google.com v6 -# forward-addr: 208.67.222.222 # opendns.com -# forward-first: yes # try direct if forwarder fails diff --git a/vrrpd/defaults/main.yml b/vrrpd/defaults/main.yml index f5950a14..1c7abb10 100644 --- a/vrrpd/defaults/main.yml +++ b/vrrpd/defaults/main.yml @@ -1,4 +1,5 @@ --- +vrrp_force_update_switch_script: false vrrp_addresses: [] # - { @@ -10,4 +11,7 @@ vrrp_addresses: [] # label: Null # use this name is syslog messages (helps when several vrid are running) # ip: Null # the ip address(es) (and optionnaly subnet mask) of the virtual server # state: Null # 'started' or 'stopped' -# } \ No newline at end of file +# } + +minifirewall_restart_if_needed: True +minifirewall_restart_force: False diff --git a/vrrpd/files/vrrp_switch.sh b/vrrpd/files/vrrp_switch.sh new file mode 100644 index 00000000..8806a7fe --- /dev/null +++ b/vrrpd/files/vrrp_switch.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +set -u +set -e + +# Input values +STATE=$1 +VRID=$2 +VIRTUAL_IP=$3 +INTERFACE_NAME=$4 +LABEL=$5 +PRIORITY=$6 +ADVERT_INT=$7 +PREEMPT=$8 +OTHER=${9:-} + +LOG_DIR=/var/log/vrrpd/ +[ ! -d "${LOG_DIR}" ] && mkdir -p "${LOG_DIR}" +LOG_FILE="${LOG_DIR}/state.${VRID}" + +STATE_DIR=/var/run/vrrpd/ +[ ! -d "${STATE_DIR}" ] && mkdir -p "${STATE_DIR}" +STATE_FILE="${STATE_DIR}/vrrp-${LABEL}" + +# Log state change to file +printf "%s %s %s %s %s %s %s %s : %s\n" \ + "${STATE}" \ + "${VIRTUAL_IP}" \ + "${INTERFACE_NAME}" \ + "${LABEL}" \ + "${PRIORITY}" \ + "${ADVERT_INT}" \ + "${PREEMPT}" \ + "${OTHER}" \ + "$(date)" \ + >> "${LOG_FILE}" + +# Replace information in state file +{ + echo "VRRP - ${LABEL}" + echo "Group ${VRID}" + echo "State is ${STATE}" + echo "Virtual IP address is ${VIRTUAL_IP}" +} > "${STATE_FILE}" + +# Choose virtual interface name (limited in size) +INTERFACE_PREFIX="vrrp_${VRID}_" +INTERFACE_PREFIX_LEN=${#INTERFACE_PREFIX} +INTERFACE_LEN=$(( ${#INTERFACE_PREFIX} + ${#INTERFACE_NAME} )) +INTERFACE_MAX_LEN=15 + +if [ ${INTERFACE_LEN} -gt ${INTERFACE_MAX_LEN} ]; then + INTERFACE_SUFFIX=$(echo "${INTERFACE_NAME}" | tail -c $(( INTERFACE_MAX_LEN + 1 - INTERFACE_PREFIX_LEN ))) +else + INTERFACE_SUFFIX="${INTERFACE_NAME}" +fi +VIRTUAL_INTERFACE_NAME="${INTERFACE_PREFIX}${INTERFACE_SUFFIX}" + +# Apply state +case "${STATE}" in + + "master" ) + # Choose a MAC address + MAC_SUFFIX=$(printf %02x "${VRID}") + MAC="00:00:5e:00:01:${MAC_SUFFIX}" + # Create macvlan interface + ip link add link "${INTERFACE_NAME}" address "${MAC}" "${VIRTUAL_INTERFACE_NAME}" type macvlan + # Add IP to interface + ip address add "${VIRTUAL_IP}" dev "${VIRTUAL_INTERFACE_NAME}" + # Enable interface + ip link set dev "${VIRTUAL_INTERFACE_NAME}" up + ;; + + "slave" ) + # Delete interface if it exists + if ip link show "${VIRTUAL_INTERFACE_NAME}" >/dev/null 2>&1; then + ip link delete "${VIRTUAL_INTERFACE_NAME}" + fi + ;; + + * ) + # Error on unknown value for state + echo "Unknown state '${STATE}'" >&2 + exit 1 + ;; + +esac + +exit 0 diff --git a/vrrpd/handlers/main.yml b/vrrpd/handlers/main.yml new file mode 100644 index 00000000..63cfcd86 --- /dev/null +++ b/vrrpd/handlers/main.yml @@ -0,0 +1,15 @@ +--- + +- name: restart minifirewall + ansible.builtin.command: + cmd: /etc/init.d/minifirewall restart + register: minifirewall_init_restart + failed_when: + - "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout" + - "'minifirewall started' not in minifirewall_init_restart.stdout" + +- name: restart minifirewall (noop) + ansible.builtin.meta: noop + register: minifirewall_init_restart + failed_when: False + changed_when: False diff --git a/vrrpd/tasks/ip.yml b/vrrpd/tasks/ip.yml index b46a8954..81c9f08f 100644 --- a/vrrpd/tasks/ip.yml +++ b/vrrpd/tasks/ip.yml @@ -18,5 +18,40 @@ enabled: yes state: "{{ vrrp_address.state }}" when: - - vrrp_systemd_unit is changed - - not ansible_check_mode \ No newline at end of file + - vrrp_systemd_unit is changed + - not ansible_check_mode + +- name: Check if a recent minifirewall is present + ansible.builtin.stat: + path: /etc/minifirewall.d/ + register: _minifirewall_dir + +- ansible.builtin.set_fact: + minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}" + +- name: VRRP output is authorized in minifirewall + lineinfile: + path: /etc/minifirewall.d/vrrpd + line: "/sbin/iptables -A OUTPUT -o {{ vrrp_address.interface }} -p 112 -j ACCEPT # Allow VRRP output on {{ vrrp_address.interface }}" + regexp: "# Allow VRRP output on {{ vrrp_address.interface }}$" + create: yes + mode: "0600" + owner: "root" + group: "root" + notify: "{{ minifirewall_restart_handler_name }}" + when: _minifirewall_dir.stat.exists + +- name: VRRP input is authorized in minifirewall + lineinfile: + path: /etc/minifirewall.d/vrrpd + line: "/sbin/iptables -A INPUT -i {{ vrrp_address.interface }} -s {{ peer }} -d 224.0.0.0/8 -j ACCEPT # Allow VRRP input on {{ vrrp_address.interface }} from {{ peer }} for VRID {{ vrrp_address.id }}" + regexp: "# Allow VRRP input on {{ vrrp_address.interface }} from {{ peer }} for VRID {{ vrrp_address.id }}" + create: yes + mode: "0600" + owner: "root" + group: "root" + loop: "{{ vrrp_address.peers | default([]) }}" + loop_control: + loop_var: peer + notify: "{{ minifirewall_restart_handler_name }}" + when: _minifirewall_dir.stat.exists diff --git a/vrrpd/tasks/main.yml b/vrrpd/tasks/main.yml index 605fb0fd..78b0ee3b 100644 --- a/vrrpd/tasks/main.yml +++ b/vrrpd/tasks/main.yml @@ -1,4 +1,5 @@ --- + - name: Install Evolix public repositry ansible.builtin.include_role: name: evolix/apt @@ -14,6 +15,15 @@ tags: - vrrpd +- name: install custom switch script + ansible.builtin.copy: + src: vrrp_switch.sh + dest: /etc/vrrpd/vrrp_switch + mode: "0700" + owner: "root" + group: "root" + force: "{{ vrrp_force_update_switch_script | bool | ternary('yes','no') }}" + - name: Adjust sysctl config (except rp_filter) ansible.posix.sysctl: name: "{{ item.name }}" @@ -62,4 +72,4 @@ ansible.builtin.include: ip.yml loop: "{{ vrrp_addresses }}" loop_control: - loop_var: "vrrp_address" \ No newline at end of file + loop_var: "vrrp_address" diff --git a/webapps/evoadmin-mail/tasks/apache.yml b/webapps/evoadmin-mail/tasks/apache.yml index 26c2b53b..87eb3d2a 100644 --- a/webapps/evoadmin-mail/tasks/apache.yml +++ b/webapps/evoadmin-mail/tasks/apache.yml @@ -12,6 +12,7 @@ src: "/etc/apache2/sites-available/evoadminmail.conf" dest: "/etc/apache2/sites-enabled/evoadminmail.conf" state: link + force: true notify: reload apache2 when: evoadminmail_enable_vhost | bool tags: diff --git a/webapps/evoadmin-mail/tasks/main.yml b/webapps/evoadmin-mail/tasks/main.yml index a1018eca..76b1b8f6 100644 --- a/webapps/evoadmin-mail/tasks/main.yml +++ b/webapps/evoadmin-mail/tasks/main.yml @@ -6,8 +6,17 @@ - name: Install evoadmin-mail package ansible.builtin.apt: - deb: /tmp/evoadmin-mail.deb + deb: https://pub.evolix.org/evolix/pool/main/e/evoadmin-mail/evoadmin-mail_1.0.9-2_all.deb state: present + when: ansible_distribution_major_version is version('12', '<') + tags: + - evoadmin-mail + +- name: Install evoadmin-mail package + ansible.builtin.apt: + name: evoadmin-mail + state: present + when: ansible_distribution_major_version is version('12', '>=') tags: - evoadmin-mail diff --git a/webapps/nextcloud/defaults/main.yml b/webapps/nextcloud/defaults/main.yml index 72ce812d..2c70832c 100644 --- a/webapps/nextcloud/defaults/main.yml +++ b/webapps/nextcloud/defaults/main.yml @@ -1,5 +1,5 @@ --- -nextcloud_version: "latest-24" +nextcloud_version: "latest" nextcloud_archive_name: "{{ nextcloud_version }}.tar.bz2" nextcloud_releases_baseurl: "https://download.nextcloud.com/server/releases/" @@ -17,3 +17,6 @@ nextcloud_db_name: "{{ nextcloud_instance_name }}" nextcloud_admin_login: "admin" nextcloud_admin_password: "" + +nextcloud_get_archive: True +nextcloud_do_config: True diff --git a/webapps/nextcloud/tasks/main.yml b/webapps/nextcloud/tasks/main.yml index 02304334..9bbab5b5 100644 --- a/webapps/nextcloud/tasks/main.yml +++ b/webapps/nextcloud/tasks/main.yml @@ -48,9 +48,11 @@ - ansible.builtin.include: user.yml - ansible.builtin.include: archive.yml + when: nextcloud_get_archive - ansible.builtin.include: apache-vhost.yml - ansible.builtin.include: mysql-user.yml - ansible.builtin.include: config.yml + when: nextcloud_do_config diff --git a/webapps/nextcloud/tasks/user.yml b/webapps/nextcloud/tasks/user.yml index c0ce5172..e9a398b9 100644 --- a/webapps/nextcloud/tasks/user.yml +++ b/webapps/nextcloud/tasks/user.yml @@ -26,6 +26,11 @@ tags: - nextcloud +- name: Set mode for home directory + ansible.builtin.file: + path: "{{ nextcloud_home }}" + mode: "0701" + - name: Create top-level directories ansible.builtin.file: dest: "{{ item }}" @@ -43,7 +48,14 @@ - name: Mount up Ceph volume by UUID ansible.posix.mount: path: "{{ nextcloud_data }}" - src: "{{ nextcloud_data_uuid }}" + src: "UUID={{ nextcloud_data_uuid }}" fstype: ext4 opts: defaults,noexec,nosuid,nodev,relatime,lazytime - state: present + state: mounted + +- name: Set volume's root permissions and ownership + ansible.builtin.file: + path: "{{ nextcloud_data }}" + owner: "{{ nextcloud_user }}" + group: "{{ nextcloud_user }}" + mode: "0700" diff --git a/webapps/roundcube/tasks/main.yml b/webapps/roundcube/tasks/main.yml index 17422246..a0fd25b9 100644 --- a/webapps/roundcube/tasks/main.yml +++ b/webapps/roundcube/tasks/main.yml @@ -58,7 +58,7 @@ tags: - roundcube -- name: configure roudcube imap port +- name: configure roundcube imap port ansible.builtin.lineinfile: dest: /etc/roundcube/config.inc.php regexp: "\\$config\\['default_port'\\]" @@ -67,6 +67,15 @@ tags: - roundcube +- name: configure roundcube smtp port + ansible.builtin.lineinfile: + dest: /etc/roundcube/config.inc.php + regexp: "\\$config\\['smtp_port'\\]" + insertafter: "\\$config\\['smtp_port'\\]" + line: "$config['smtp_port'] = 25;" + tags: + - roundcube + - name: configure managesieve plugin ansible.builtin.copy: src: /usr/share/roundcube/plugins/managesieve/config.inc.php.dist @@ -99,6 +108,7 @@ src: /etc/apache2/sites-available/roundcube.conf dest: /etc/apache2/sites-enabled/roundcube.conf state: link + force: true notify: reload apache2 when: roundcube_webserver == "apache" tags: