From 2990e7e9694d29fb71671ef330bfc7ad4f730166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Thu, 6 Mar 2025 08:25:26 +0100 Subject: [PATCH 01/34] refs #8553 - add ntp_servers variable --- roles/dhcp/templates/dhcpd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/dhcp/templates/dhcpd.conf b/roles/dhcp/templates/dhcpd.conf index 47f5af2..7059f42 100644 --- a/roles/dhcp/templates/dhcpd.conf +++ b/roles/dhcp/templates/dhcpd.conf @@ -14,7 +14,7 @@ max-lease-time 86400; option domain-name "{{ domain_name.name }}"; option domain-name-servers {{ domain_name.servers }}; -option ntp-servers {{ ntp_servers }}; +option ntp-servers {{ domain_name.ntp_servers }}; # DHCP daemon uses default time zone UTC db-time-format local; From 7d7d855384c0690f8cbcc26b11aa6689c66d1b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Wed, 12 Mar 2025 14:22:50 +0100 Subject: [PATCH 02/34] db: refs #8414 - Add jenkins Url and nrpe threshold --- roles/db/files/nrpe/95-mariadb.cfg | 1 + roles/db/templates/mariabackup/apply.config.sh | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/db/files/nrpe/95-mariadb.cfg b/roles/db/files/nrpe/95-mariadb.cfg index 79e9e36..5e28bf2 100644 --- a/roles/db/files/nrpe/95-mariadb.cfg +++ b/roles/db/files/nrpe/95-mariadb.cfg @@ -2,3 +2,4 @@ command[check_disk_mysqldata]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p command[check_disk_mysqlbin]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p /mnt/mysqlbin command[check_disk_backup]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p /mnt/local-backup command[check_mysql_scheduler]=/etc/nagios/plugins/check_mysql_scheduler +command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 600 -c 700 diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh index a175dd8..c74176b 100755 --- a/roles/db/templates/mariabackup/apply.config.sh +++ b/roles/db/templates/mariabackup/apply.config.sh @@ -15,7 +15,7 @@ dbClusterSiblings=() # Jenkins authentication string jenkinsAuth=jenkins:{{ lookup(passbolt, 'jenkinsAuth', folder_parent_id=passbolt_folder).password }} -{% if db.jenkinsUrl is defined %} +{% if db.jenkinsJob is defined %} # Jenkins job URL -jenkinsUrl={{ db.jenkinsUrl }} +jenkinsUrl={{ jenkinsUrl }}{{ db.jenkinsJob }} {% endif %} From eaddc25c38bd7c1eb50c727ff92c16f4d25a48e0 Mon Sep 17 00:00:00 2001 From: Juan Ferrer <juan@verdnatura.es> Date: Wed, 12 Mar 2025 17:35:47 +0000 Subject: [PATCH 03/34] fix: refs #8762 Fix table cache hitrate --- roles/db/files/conf/z90-vn.cnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/db/files/conf/z90-vn.cnf b/roles/db/files/conf/z90-vn.cnf index 1911135..c7db964 100644 --- a/roles/db/files/conf/z90-vn.cnf +++ b/roles/db/files/conf/z90-vn.cnf @@ -30,7 +30,7 @@ interactive_timeout = 1800 wait_timeout = 1800 open_files_limit = 20000 low_priority_updates = 1 -table_open_cache = 40000 +table_open_cache = 50000 table_definition_cache = 10000 table_open_cache_instances = 1 key_buffer_size = 256K From 00239750a21688a8f0d68a05c9db5420235a5ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 11:29:31 +0100 Subject: [PATCH 04/34] vpn: refs #8748 - Initial approche --- playbooks/vpn-ipsec.yml | 6 ++++ roles/ipsec/defaults/main.yml | 10 ++++++ roles/ipsec/files/vn.conf | 19 +++++++++++ roles/ipsec/handlers/main.yml | 4 +++ roles/ipsec/tasks/ipsec.yml | 43 ++++++++++++++++++++++++ roles/ipsec/tasks/main.yml | 3 ++ roles/ipsec/templates/ipsec.conf | 32 ++++++++++++++++++ roles/ipsec/templates/ipsec.secrets | 2 ++ roles/ipsec/templates/vn-attr.conf | 8 +++++ roles/ipsec/templates/vn-eap-radius.conf | 21 ++++++++++++ 10 files changed, 148 insertions(+) create mode 100644 playbooks/vpn-ipsec.yml create mode 100644 roles/ipsec/defaults/main.yml create mode 100644 roles/ipsec/files/vn.conf create mode 100644 roles/ipsec/handlers/main.yml create mode 100644 roles/ipsec/tasks/ipsec.yml create mode 100644 roles/ipsec/tasks/main.yml create mode 100644 roles/ipsec/templates/ipsec.conf create mode 100644 roles/ipsec/templates/ipsec.secrets create mode 100644 roles/ipsec/templates/vn-attr.conf create mode 100644 roles/ipsec/templates/vn-eap-radius.conf diff --git a/playbooks/vpn-ipsec.yml b/playbooks/vpn-ipsec.yml new file mode 100644 index 0000000..c8f0979 --- /dev/null +++ b/playbooks/vpn-ipsec.yml @@ -0,0 +1,6 @@ +- name: Configure DHCP + hosts: all + tasks: + - name: Configure services to install in the server + import_role: + name: ipsec \ No newline at end of file diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml new file mode 100644 index 0000000..9113d34 --- /dev/null +++ b/roles/ipsec/defaults/main.yml @@ -0,0 +1,10 @@ +strongswan_requeriments: + - strongswan + - libstrongswan-standard-plugins + - strongswan-pki + - tcpdump + - iperf + - conntrack +certificates: + - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' } + - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' } \ No newline at end of file diff --git a/roles/ipsec/files/vn.conf b/roles/ipsec/files/vn.conf new file mode 100644 index 0000000..0b26373 --- /dev/null +++ b/roles/ipsec/files/vn.conf @@ -0,0 +1,19 @@ +charon { + cisco_unity = yes + + filelog { + log { + path = /var/log/strongswan/charon.log + append = yes + default = 1 + flush_line = yes + ike_name = yes + time_format = %Y-%m-%d %H:%M:%S + } + } + syslog { + identifier = charon + daemon { + } + } +} diff --git a/roles/ipsec/handlers/main.yml b/roles/ipsec/handlers/main.yml new file mode 100644 index 0000000..79978af --- /dev/null +++ b/roles/ipsec/handlers/main.yml @@ -0,0 +1,4 @@ +- name: restart-ipsec + systemd: + name: strongswan-starter.service + state: restarted diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml new file mode 100644 index 0000000..0b786ba --- /dev/null +++ b/roles/ipsec/tasks/ipsec.yml @@ -0,0 +1,43 @@ +- name: Update apt cache + apt: + update_cache: yes +- name: Install VPN package requirements + apt: + name: "{{ strongswan_requeriments }}" + state: present + install_recommends: no +- name: Insert certificates + no_log: true + copy: + content: "{{ item.content }}" + dest: "{{ item.dest }}" + owner: root + group: root + mode: "{{ item.mode }}" + loop: "{{ certificates }}" +- name: Add private key + copy: + content: "{{ lookup(passbolt, 'ipsec_private_key', folder_parent_id=passbolt_folder).description }}" + dest: /etc/ipsec.d/private/key.pem + owner: root + group: root + mode: u=r,g=r,o= +- name: Configure ipsec.conf and charon + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: root + mode: "{{ item.mode }}" + loop: + - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' } + - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' } + - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' } + - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' } +- name: Copy Configure file + copy: + src: vn.conf + dest: /etc/strongswan.d/vn.conf + owner: root + group: root + mode: u=rw,g=r,o=r \ No newline at end of file diff --git a/roles/ipsec/tasks/main.yml b/roles/ipsec/tasks/main.yml new file mode 100644 index 0000000..d3dd860 --- /dev/null +++ b/roles/ipsec/tasks/main.yml @@ -0,0 +1,3 @@ +- import_tasks: ipsec.yml + tags: ipsec + diff --git a/roles/ipsec/templates/ipsec.conf b/roles/ipsec/templates/ipsec.conf new file mode 100644 index 0000000..76d3627 --- /dev/null +++ b/roles/ipsec/templates/ipsec.conf @@ -0,0 +1,32 @@ + +config setup + charondebug="ike 1, knl 1, cfg 0" + uniqueids=no + +conn %default + auto=add + compress=no + type=tunnel + keyexchange=ikev2 + fragmentation=yes + forceencaps=yes + eap_identity=%identity + + dpdaction=clear + dpddelay=300s + rekey=no + + left=%any + leftid=@{{ leftid }} + leftcert=cert.pem + leftsendcert=always + leftsubnet={{ leftsubnet }} + + right=%any + rightid=%any + rightauth=eap-radius + rightdns={{ rightdns }} + rightsendcert=never + +{{ ipsec_groups }} + diff --git a/roles/ipsec/templates/ipsec.secrets b/roles/ipsec/templates/ipsec.secrets new file mode 100644 index 0000000..9956a00 --- /dev/null +++ b/roles/ipsec/templates/ipsec.secrets @@ -0,0 +1,2 @@ +{{ leftid }} : RSA "key.pem" +admin %any% : EAP "{{ lookup(passbolt, 'eap', folder_parent_id=passbolt_folder).password }}" diff --git a/roles/ipsec/templates/vn-attr.conf b/roles/ipsec/templates/vn-attr.conf new file mode 100644 index 0000000..94b2b2f --- /dev/null +++ b/roles/ipsec/templates/vn-attr.conf @@ -0,0 +1,8 @@ +attr { + load = yes + dns = {{ rightdns }} + split-include = {{ leftsubnet }} + split-exclude = 0.0.0.0/0 + 28674 = {{ leftid }} + 25 = {{ leftid }} +} diff --git a/roles/ipsec/templates/vn-eap-radius.conf b/roles/ipsec/templates/vn-eap-radius.conf new file mode 100644 index 0000000..de69c64 --- /dev/null +++ b/roles/ipsec/templates/vn-eap-radius.conf @@ -0,0 +1,21 @@ +eap-radius { + load = yes + accounting = yes + class_group = yes + servers { + primary { + #address = radius1.verdnatura.es + address = {{ address_radiusA }} + auth_port = {{ auth_port }} + acct_port = {{ acct_port }} + secret = {{ lookup(passbolt, 'eap-radius', folder_parent_id=passbolt_folder).password }} + } + secondary { + #address = radius2.verdnatura.es + address = {{ address_radiusB }} + auth_port = {{ auth_port }} + acct_port = {{ acct_port }} + secret = {{ lookup(passbolt, 'eap-radius', folder_parent_id=passbolt_folder).password }} + } + } +} \ No newline at end of file From 9ac8501f148d597b3e6a9d4810c0eadd1ec6969b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 11:33:01 +0100 Subject: [PATCH 05/34] vpn: refs #8748 - Names fix --- playbooks/dhcp.yml | 2 +- roles/ipsec/tasks/ipsec.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playbooks/dhcp.yml b/playbooks/dhcp.yml index 9a89db8..ebfc0c5 100644 --- a/playbooks/dhcp.yml +++ b/playbooks/dhcp.yml @@ -1,4 +1,4 @@ -- name: Configure DHCP +- name: Configure IPsec StrongSwan hosts: all tasks: - name: Configure services to install in the server diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml index 0b786ba..ce822dd 100644 --- a/roles/ipsec/tasks/ipsec.yml +++ b/roles/ipsec/tasks/ipsec.yml @@ -22,7 +22,7 @@ owner: root group: root mode: u=r,g=r,o= -- name: Configure ipsec.conf and charon +- name: Configure ipsec and charon template: src: "{{ item.src }}" dest: "{{ item.dest }}" From 8c168d4fa872861f0a9d7a872c2ebe459b38c4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 11:34:21 +0100 Subject: [PATCH 06/34] vpn: refs #8748 - Names fix --- playbooks/dhcp.yml | 2 +- playbooks/vpn-ipsec.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playbooks/dhcp.yml b/playbooks/dhcp.yml index ebfc0c5..9a89db8 100644 --- a/playbooks/dhcp.yml +++ b/playbooks/dhcp.yml @@ -1,4 +1,4 @@ -- name: Configure IPsec StrongSwan +- name: Configure DHCP hosts: all tasks: - name: Configure services to install in the server diff --git a/playbooks/vpn-ipsec.yml b/playbooks/vpn-ipsec.yml index c8f0979..aa9b29a 100644 --- a/playbooks/vpn-ipsec.yml +++ b/playbooks/vpn-ipsec.yml @@ -1,4 +1,4 @@ -- name: Configure DHCP +- name: Configure IPsec StrongSwan hosts: all tasks: - name: Configure services to install in the server From 0e393b49c8f76e25e2c055e247744bf8a73547ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 11:41:13 +0100 Subject: [PATCH 07/34] vpn: refs #8748 - Variables array --- roles/ipsec/defaults/main.yml | 7 ++++++- roles/ipsec/tasks/ipsec.yml | 6 +----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml index 9113d34..0553fc3 100644 --- a/roles/ipsec/defaults/main.yml +++ b/roles/ipsec/defaults/main.yml @@ -7,4 +7,9 @@ strongswan_requeriments: - conntrack certificates: - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' } - - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' } \ No newline at end of file + - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' } +config_ipsec_files: + - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' } + - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' } + - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' } + - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' } diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml index ce822dd..36ad57e 100644 --- a/roles/ipsec/tasks/ipsec.yml +++ b/roles/ipsec/tasks/ipsec.yml @@ -29,11 +29,7 @@ owner: root group: root mode: "{{ item.mode }}" - loop: - - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' } - - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' } - - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' } - - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' } + loop: "{{ config_ipsec_files }}" - name: Copy Configure file copy: src: vn.conf From c1074a90e51fbeb4804168c6238a8fb7ec37f95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 12:58:15 +0100 Subject: [PATCH 08/34] vpn: refs #8748 - Iptables approche - what to do --- roles/ipsec/defaults/main.yml | 1 + roles/ipsec/tasks/ipsec.yml | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml index 0553fc3..fedeaef 100644 --- a/roles/ipsec/defaults/main.yml +++ b/roles/ipsec/defaults/main.yml @@ -5,6 +5,7 @@ strongswan_requeriments: - tcpdump - iperf - conntrack + - iptables-persistent certificates: - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' } - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' } diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml index 36ad57e..a1ed9cd 100644 --- a/roles/ipsec/tasks/ipsec.yml +++ b/roles/ipsec/tasks/ipsec.yml @@ -36,4 +36,28 @@ dest: /etc/strongswan.d/vn.conf owner: root group: root - mode: u=rw,g=r,o=r \ No newline at end of file + mode: u=rw,g=r,o=r +- name: IP forward as a router + sysctl: + name: net.ipv4.ip_forward + value: "1" + state: present + sysctl_set: yes + reload: yes +- name: Add iptables rules in rules.v4 file + blockinfile: + path: /etc/iptables/rules.v4 + marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN MANGED" + block: | + *mangle + :PREROUTING ACCEPT [0:0] + :INPUT ACCEPT [0:0] + :FORWARD ACCEPT [0:0] + :OUTPUT ACCEPT [0:0] + :POSTROUTING ACCEPT [0:0] + -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 + -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 + register: iptables +- name: Reload iptables rules + command: netfilter-persistent reload + when: iptables.changed \ No newline at end of file From aba7121907ea0307f829d8d9ee10de75c4fc1f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 14 Mar 2025 14:33:53 +0100 Subject: [PATCH 09/34] vpn: refs #8748 - Final touch --- roles/ipsec/defaults/main.yml | 13 +++++++++ roles/ipsec/files/charon | 11 ++++++++ roles/ipsec/tasks/ipsec.yml | 50 +++++++++++++++++++++++++---------- 3 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 roles/ipsec/files/charon diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml index fedeaef..c8b1cd0 100644 --- a/roles/ipsec/defaults/main.yml +++ b/roles/ipsec/defaults/main.yml @@ -14,3 +14,16 @@ config_ipsec_files: - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' } - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' } - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' } +mangle_block: | + *mangle + :PREROUTING ACCEPT [0:0] + :INPUT ACCEPT [0:0] + :FORWARD ACCEPT [0:0] + :OUTPUT ACCEPT [0:0] + :POSTROUTING ACCEPT [0:0] + -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 + -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 + COMMIT +config_and_logrotate: + - { src: vn.conf, dest: '/etc/strongswan.d/vn.conf' } + - { src: charon, dest: '/etc/logrotate.d/charon' } diff --git a/roles/ipsec/files/charon b/roles/ipsec/files/charon new file mode 100644 index 0000000..9a05de0 --- /dev/null +++ b/roles/ipsec/files/charon @@ -0,0 +1,11 @@ +/var/log/strongswan/charon.log +{ + copytruncate + create 644 root root + rotate 10 + weekly + missingok + notifempty + compress + delaycompress +} diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml index a1ed9cd..37f63ce 100644 --- a/roles/ipsec/tasks/ipsec.yml +++ b/roles/ipsec/tasks/ipsec.yml @@ -6,6 +6,13 @@ name: "{{ strongswan_requeriments }}" state: present install_recommends: no +- name: Create directory /var/log/strongswan + file: + path: /var/log/strongswan + state: directory + owner: root + group: root + mode: '0755' - name: Insert certificates no_log: true copy: @@ -30,13 +37,16 @@ group: root mode: "{{ item.mode }}" loop: "{{ config_ipsec_files }}" -- name: Copy Configure file + notify: restart-ipsec +- name: Copy Configure file and logrotate Charon copy: - src: vn.conf - dest: /etc/strongswan.d/vn.conf + src: "{{ item.src }}" + dest: "{{ item.dest }}" owner: root group: root mode: u=rw,g=r,o=r + loop: "{{ config_and_logrotate }}" + notify: restart-ipsec - name: IP forward as a router sysctl: name: net.ipv4.ip_forward @@ -47,17 +57,29 @@ - name: Add iptables rules in rules.v4 file blockinfile: path: /etc/iptables/rules.v4 - marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN MANGED" - block: | - *mangle - :PREROUTING ACCEPT [0:0] - :INPUT ACCEPT [0:0] - :FORWARD ACCEPT [0:0] - :OUTPUT ACCEPT [0:0] - :POSTROUTING ACCEPT [0:0] - -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 - -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 + marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN" + block: "{{ mangle_block }}" register: iptables - name: Reload iptables rules command: netfilter-persistent reload - when: iptables.changed \ No newline at end of file + when: iptables.changed +- name: Get default IPv4 interface + command: ip -o -4 route show default + register: default_route +- name: Extract interface default name + set_fact: + active_interface: "{{ default_route.stdout.split()[-1] }}" +- name: Routing table for VPN + lineinfile: + path: /etc/iproute2/rt_tables + line: "10 vpn" + state: present + regexp: "vpn" +- name: Static routing rules to send VPN traffic directly to the firewall + lineinfile: + path: /etc/network/interfaces + insertafter: "dhcp" + line: "{{ item }}" + state: present + loop: "{{ static_routes }}" + \ No newline at end of file From 7f372d20f4a86c3590c7c2600a902996fb40fca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Mon, 17 Mar 2025 15:57:11 +0100 Subject: [PATCH 10/34] vpn: refs #8140 - Refactor main yml and add pmm template --- roles/db/defaults/main.yaml | 5 ++++ roles/db/handlers/main.yml | 4 +++ roles/db/tasks/mariadb.yml | 49 ++++++------------------------- roles/db/templates/pmm-agent.yaml | 34 +++++++++++++++++++++ 4 files changed, 52 insertions(+), 40 deletions(-) create mode 100644 roles/db/templates/pmm-agent.yaml diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml index b1818b7..7dd354f 100644 --- a/roles/db/defaults/main.yaml +++ b/roles/db/defaults/main.yaml @@ -33,6 +33,11 @@ required_mariabackup_files_and_scripts: - { src: scripts/events-demote.sh, dest: /root/scripts/events-demote.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/README.md, dest: /root/scripts/README.md, mode: u=rw,g=r,o=r } - { src: scripts/scheduler-log.sh, dest: /root/scripts/scheduler-log.sh, mode: u=rwx,g=rx,o=rx } +configuration_files: + - { src: "conf/z99-local.cnf", dest: "/etc/mysql/mariadb.conf.d/z99-local.cnf", owner: "root", group: "root", mode: "u=rw,g=r,o=r" } + - { src: "mariabackup/my.cnf", dest: "/root/mariabackup/my.cnf", owner: "root", group: "root", mode: "u=rw,g=,o=" } + - { src: "pmm-agent.yaml", dest: "/usr/local/percona/pmm2/config/pmm-agent.yaml", owner: "pmm-agent", group: "pmm-agent", mode: "u=rw,g=rw,o=" } + - { src: "check-memory.cron", dest: "/etc/cron.d/vn-check-memory", owner: "root", group: "root", mode: "u=rw,g=r,o=r" } downloads: - url: https://r.mariadb.com/downloads/mariadb_repo_setup dest: /tmp/mariadb_repo_setup diff --git a/roles/db/handlers/main.yml b/roles/db/handlers/main.yml index 320b475..11143ee 100644 --- a/roles/db/handlers/main.yml +++ b/roles/db/handlers/main.yml @@ -9,3 +9,7 @@ service: name: nagios-nrpe-server state: restarted +- name: restart-percona + service: + name: pmm-agent.service + state: restarted diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml index a3980ce..806125a 100644 --- a/roles/db/tasks/mariadb.yml +++ b/roles/db/tasks/mariadb.yml @@ -3,35 +3,29 @@ name: "{{ mariadb_requeriments }}" state: present install_recommends: no - - name: Download required setup files get_url: url: "{{ item.url }}" dest: "{{ item.dest }}" mode: "{{ item.mode }}" loop: "{{ downloads }}" - - name: Run MariaDB repository setup script command: cmd: "/bin/bash /tmp/mariadb_repo_setup --mariadb-server-version={{ db.version | default('10.11.10') }}" creates: "/etc/apt/sources.list.d/mariadb.list" - - name: Install Percona repository package apt: deb: "/tmp/percona-release_latest.generic_all.deb" state: present install_recommends: no - - name: Update apt cache apt: update_cache: yes - - name: Install MariaDB packages apt: name: "{{ mariadb_base_packages }}" state: present install_recommends: no - - name: Add tmpfs in /etc/fstab blockinfile: path: /etc/fstab @@ -39,15 +33,6 @@ block: | tmpfs /mnt/mysqltmp tmpfs rw,size={{ mysqltmpsize }} 0 0 register: fstab - -- name: Configure MariaDB memory check CRON - template: - src: check-memory.cron - dest: /etc/cron.d/vn-check-memory - owner: root - group: root - mode: u=rw,g=r,o=r - - name: Configure MariaDB scheduler log CRON copy: src: scheduler-log.cron @@ -55,7 +40,6 @@ owner: root group: root mode: u=rw,g=r,o=r - - name: Insert MySQL certificates no_log: true copy: @@ -66,7 +50,6 @@ mode: "{{ item.mode }}" loop: "{{ certificates }}" notify: restart-mariadb - - name: Ensure required directories exist file: path: "{{ item.path }}" @@ -75,7 +58,6 @@ group: "{{ item.group }}" mode: "{{ item.mode }}" loop: "{{ required_directories }}" - - name: Copy required MariaBackup files and scripts copy: src: "{{ item.src }}" @@ -84,7 +66,15 @@ group: root mode: "{{ item.mode }}" loop: "{{ required_mariabackup_files_and_scripts }}" - +- name: Deploy configuration files + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: "{{ item.owner }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + loop: "{{ configuration_files }}" + notify: restart-percona - name: Set MariaDB common configuration copy: src: conf/z90-vn.cnf @@ -93,23 +83,6 @@ group: root mode: u=rw,g=r,o=r notify: restart-mariadb - -- name: Set MariaDB local configuration template - template: - src: conf/z99-local.cnf - dest: /etc/mysql/mariadb.conf.d/ - owner: root - group: root - mode: u=rw,g=r,o=r - -- name: Set MariaBackup connection configuration - template: - src: mariabackup/my.cnf - dest: /root/mariabackup/ - owner: root - group: root - mode: u=rw,g=,o= - - name: Override MariaDB systemd service configuration copy: src: mariadb_override.conf @@ -118,7 +91,6 @@ group: root mode: u=rw,g=r,o=r notify: reload-systemd - - name: Set MariaDB NRPE configuration copy: src: nrpe/95-mariadb.cfg @@ -127,12 +99,10 @@ group: root mode: u=rw,g=r,o=r notify: restart-nrpe - - name: Check if /var/lib/mysql/ exists stat: path: /var/lib/mysql/ register: mysql_dir - - when: mysql_dir.stat.exists block: @@ -149,7 +119,6 @@ file: path: /var/lib/mysql/ state: absent - - name: Mount all filesystems from /etc/fstab command: mount -a when: fstab.changed diff --git a/roles/db/templates/pmm-agent.yaml b/roles/db/templates/pmm-agent.yaml new file mode 100644 index 0000000..04e2b6a --- /dev/null +++ b/roles/db/templates/pmm-agent.yaml @@ -0,0 +1,34 @@ +# Updated by `pmm-agent setup`. +--- +id: /agent_id/{{ pmm.id }} +listen-address: 127.0.0.1 +listen-port: 7777 +server: + address: {{ pmm.address }} + username: {{ pmm.username }} + password: {{ pmm.password }} + insecure-tls: {{ pmm.insecure }} +paths: + paths_base: /usr/local/percona/pmm2 + exporters_base: /usr/local/percona/pmm2/exporters + node_exporter: /usr/local/percona/pmm2/exporters/node_exporter + mysqld_exporter: /usr/local/percona/pmm2/exporters/mysqld_exporter + mongodb_exporter: /usr/local/percona/pmm2/exporters/mongodb_exporter + postgres_exporter: /usr/local/percona/pmm2/exporters/postgres_exporter + proxysql_exporter: /usr/local/percona/pmm2/exporters/proxysql_exporter + rds_exporter: /usr/local/percona/pmm2/exporters/rds_exporter + azure_exporter: /usr/local/percona/pmm2/exporters/azure_exporter + vmagent: /usr/local/percona/pmm2/exporters/vmagent + tempdir: {{ pmm.tempdir }} + pt_summary: /usr/local/percona/pmm2/tools/pt-summary + pt_pg_summary: /usr/local/percona/pmm2/tools/pt-pg-summary + pt_mysql_summary: /usr/local/percona/pmm2/tools/pt-mysql-summary + pt_mongodb_summary: /usr/local/percona/pmm2/tools/pt-mongodb-summary +ports: + min: 42000 + max: 51999 +log-level: warn +debug: false +trace: false +loglinescount: 1024 +window-connected-time: 1h0m0s From aef6cb5bc0b7c90a3e2faa44e26d18abd5431fd4 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 17 Mar 2025 17:49:06 +0100 Subject: [PATCH 11/34] refs #8776 PVE and Ceph monitoring --- roles/ceph/files/nrpe.cfg | 2 +- roles/ceph/files/nrpe/check_ceph_mon | 163 +++ roles/ceph/files/nrpe/check_ceph_osd | 154 ++ roles/ceph/files/nrpe/check_chrony | 128 ++ .../files/nrpe/check_smartdisk.sh | 0 roles/ceph/files/sudoers | 1 + roles/ceph/tasks/main.yml | 10 +- roles/ceph/tasks/nrpe.yml | 24 + .../pve/files/{nrpe.cfg => nrpe.d/95-pve.cfg} | 0 .../pve/files/{nrpe => plugins}/check_chrony | 0 roles/pve/files/plugins/check_ilo2_health.pl | 1234 +++++++++++++++++ roles/pve/files/plugins/check_smartdisk.sh | 22 + .../pve/files/{nrpe => plugins}/check_zfs.pl | 0 roles/pve/tasks/nrpe.yml | 12 +- roles/pve/tasks/vhost.yml | 10 +- 15 files changed, 1743 insertions(+), 17 deletions(-) create mode 100755 roles/ceph/files/nrpe/check_ceph_mon create mode 100755 roles/ceph/files/nrpe/check_ceph_osd create mode 100755 roles/ceph/files/nrpe/check_chrony rename roles/{pve => ceph}/files/nrpe/check_smartdisk.sh (100%) create mode 100644 roles/ceph/files/sudoers create mode 100644 roles/ceph/tasks/nrpe.yml rename roles/pve/files/{nrpe.cfg => nrpe.d/95-pve.cfg} (100%) rename roles/pve/files/{nrpe => plugins}/check_chrony (100%) create mode 100755 roles/pve/files/plugins/check_ilo2_health.pl create mode 100755 roles/pve/files/plugins/check_smartdisk.sh rename roles/pve/files/{nrpe => plugins}/check_zfs.pl (100%) diff --git a/roles/ceph/files/nrpe.cfg b/roles/ceph/files/nrpe.cfg index 76d252f..afc0035 100644 --- a/roles/ceph/files/nrpe.cfg +++ b/roles/ceph/files/nrpe.cfg @@ -1,4 +1,4 @@ -command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 700 -c 1000 +command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 1300 -c 1500 command[check_chrony]=/usr/lib/nagios/plugins/check_chrony 1 2 command[check_smartdisk]=/etc/nagios/plugins/check_smartdisk.sh /dev/sda /dev/sdb command[check_raid]=/usr/lib/nagios/plugins/check_raid diff --git a/roles/ceph/files/nrpe/check_ceph_mon b/roles/ceph/files/nrpe/check_ceph_mon new file mode 100755 index 0000000..f8decea --- /dev/null +++ b/roles/ceph/files/nrpe/check_ceph_mon @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Catalyst IT http://www.catalyst.net.nz +# Copyright (c) 2015 SWITCH http://www.switch.ch +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import print_function +import argparse +import socket +import os +import re +import subprocess +import sys +import json + +__version__ = '1.5.0' + +# default ceph values +CEPH_EXEC = '/usr/bin/ceph' +CEPH_COMMAND = 'quorum_status' + +# nagios exit code +STATUS_OK = 0 +STATUS_WARNING = 1 +STATUS_ERROR = 2 +STATUS_UNKNOWN = 3 + +## +# ceph quorum_status output example +## +ceph_quorum_status_output_example = '''{ + "quorum_leader_name" : "s0001", + "monmap" : { + "mons" : [ + { + "name" : "s0001", + "addr" : "[2001:620:5ca1:8000::1001]:6789/0", + "rank" : 0 + }, + { + "name" : "s0003", + "addr" : "[2001:620:5ca1:8000::1003]:6789/0", + "rank" : 1 + } + ], + "created" : "2014-12-15 08:28:35.153650", + "epoch" : 2, + "modified" : "2014-12-15 08:28:40.371878", + "fsid" : "22348d2b-b69d-46cc-9a79-ca93cd6bae84" + }, + "quorum_names" : [ + "s0001", + "s0003" + ], + "quorum" : [ + 0, + 1 + ], + "election_epoch" : 24 +}''' + +def main(): + + # parse args + parser = argparse.ArgumentParser(description="'ceph quorum_status' nagios plugin.") + parser.add_argument('-e','--exe', help='ceph executable [%s]' % CEPH_EXEC) + parser.add_argument('-c','--conf', help='alternative ceph conf file') + parser.add_argument('-m','--monaddress', help='ceph monitor to use for queries (address[:port])') + parser.add_argument('-i','--id', help='ceph client id') + parser.add_argument('-k','--keyring', help='ceph client keyring file') + parser.add_argument('-V','--version', help='show version and exit', action='store_true') + parser.add_argument('-I','--monid', help='mon ID to be checked for availability') + args = parser.parse_args() + + if args.version: + print('version %s' % __version__) + return STATUS_OK + + # validate args + ceph_exec = args.exe if args.exe else CEPH_EXEC + if not os.path.exists(ceph_exec): + print("MON ERROR: ceph executable '%s' doesn't exist" % ceph_exec) + return STATUS_UNKNOWN + + if args.conf and not os.path.exists(args.conf): + print("MON ERROR: ceph conf file '%s' doesn't exist" % args.conf) + return STATUS_UNKNOWN + + if args.keyring and not os.path.exists(args.keyring): + print("MON ERROR: keyring file '%s' doesn't exist" % args.keyring) + return STATUS_UNKNOWN + + if not args.monid: + print("MON ERROR: no MON ID given, use -I/--monid parameter") + return STATUS_UNKNOWN + + # build command + ceph_cmd = [ceph_exec] + if args.monaddress: + ceph_cmd.append('-m') + ceph_cmd.append(args.monaddress) + if args.conf: + ceph_cmd.append('-c') + ceph_cmd.append(args.conf) + if args.id: + ceph_cmd.append('--id') + ceph_cmd.append(args.id) + if args.keyring: + ceph_cmd.append('--keyring') + ceph_cmd.append(args.keyring) + ceph_cmd.append(CEPH_COMMAND) + + # exec command + p = subprocess.Popen(ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + output, err = p.communicate() + + if p.returncode != 0 or not output: + print("MON ERROR: %s" % err) + return STATUS_ERROR + + # load json output and parse + quorum_status = False + try: + quorum_status = json.loads(output) + except Exception as e: + print("MON ERROR: could not parse '%s' output: %s: %s" % (CEPH_COMMAND,output,e)) + return STATUS_UNKNOWN + + #print "XXX: quorum_status['quorum_names']:", quorum_status['quorum_names'] + + # do our checks + is_monitor = False + for mon in quorum_status['monmap']['mons']: + if mon['name'] == args.monid: + is_monitor = True + if not is_monitor: + print("MON WARN: mon '%s' is not in monmap: %s" % (args.monid,quorum_status['monmap']['mons'])) + return STATUS_WARNING + + in_quorum = args.monid in quorum_status['quorum_names'] + if in_quorum: + print("MON OK") + return STATUS_OK + else: + print("MON WARN: no MON '%s' found in quorum" % args.monid) + return STATUS_WARNING + +# main +if __name__ == "__main__": + sys.exit(main()) diff --git a/roles/ceph/files/nrpe/check_ceph_osd b/roles/ceph/files/nrpe/check_ceph_osd new file mode 100755 index 0000000..2ee9de6 --- /dev/null +++ b/roles/ceph/files/nrpe/check_ceph_osd @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Catalyst IT http://www.catalyst.net.nz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# 1.5.2 (2019-06-16) Martin Seener: fixed regex to work with Ceph Nautilus (14.2.x) + +from __future__ import print_function +import argparse +import os +import re +import subprocess +import sys +import socket + +__version__ = '1.5.2' + +# default ceph values +CEPH_COMMAND = '/usr/bin/ceph' + +# nagios exit code +STATUS_OK = 0 +STATUS_WARNING = 1 +STATUS_ERROR = 2 +STATUS_UNKNOWN = 3 + +def main(): + + # parse args + parser = argparse.ArgumentParser(description="'ceph osd' nagios plugin.") + parser.add_argument('-e','--exe', help='ceph executable [%s]' % CEPH_COMMAND) + parser.add_argument('-c','--conf', help='alternative ceph conf file') + parser.add_argument('-m','--monaddress', help='ceph monitor address[:port]') + parser.add_argument('-i','--id', help='ceph client id') + parser.add_argument('-k','--keyring', help='ceph client keyring file') + parser.add_argument('-V','--version', help='show version and exit', action='store_true') + parser.add_argument('-H','--host', help='osd host', required=True) + parser.add_argument('-I','--osdid', help='osd id', required=False) + parser.add_argument('-C','--crit', help='Number of failed OSDs to trigger critical (default=2)',type=int,default=2, required=False) + parser.add_argument('-o','--out', help='check osds that are set OUT', default=False, action='store_true', required=False) + args = parser.parse_args() + + # validate args + ceph_exec = args.exe if args.exe else CEPH_COMMAND + if not os.path.exists(ceph_exec): + print("OSD ERROR: ceph executable '%s' doesn't exist" % ceph_exec) + return STATUS_UNKNOWN + + if args.version: + print('version %s' % __version__) + return STATUS_OK + + if args.conf and not os.path.exists(args.conf): + print("OSD ERROR: ceph conf file '%s' doesn't exist" % args.conf) + return STATUS_UNKNOWN + + if args.keyring and not os.path.exists(args.keyring): + print("OSD ERROR: keyring file '%s' doesn't exist" % args.keyring) + return STATUS_UNKNOWN + + if not args.osdid: + args.osdid = '[^ ]*' + + if not args.host: + print("OSD ERROR: no OSD hostname given") + return STATUS_UNKNOWN + + try: + addrinfo = socket.getaddrinfo(args.host, None, 0, socket.SOCK_STREAM) + args.host = addrinfo[0][-1][0] + if addrinfo[0][0] == socket.AF_INET6: + args.host = "[%s]" % args.host + except: + print('OSD ERROR: could not resolve %s' % args.host) + return STATUS_UNKNOWN + + + # build command + ceph_cmd = [ceph_exec] + if args.monaddress: + ceph_cmd.append('-m') + ceph_cmd.append(args.monaddress) + if args.conf: + ceph_cmd.append('-c') + ceph_cmd.append(args.conf) + if args.id: + ceph_cmd.append('--id') + ceph_cmd.append(args.id) + if args.keyring: + ceph_cmd.append('--keyring') + ceph_cmd.append(args.keyring) + ceph_cmd.append('osd') + ceph_cmd.append('dump') + + # exec command + p = subprocess.Popen(ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + output, err = p.communicate() + output = output.decode('utf8') + + if err or not output: + print("OSD ERROR: %s" % err) + return STATUS_ERROR + + # escape IPv4 host address + osd_host = args.host.replace('.', '\.') + # escape IPv6 host address + osd_host = osd_host.replace('[', '\[') + osd_host = osd_host.replace(']', '\]') + up = re.findall(r"^(osd\.%s) up.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + if args.out: + down = re.findall(r"^(osd\.%s) down.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + down_in = re.findall(r"^(osd\.%s) down[ ]+in.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + down_out = re.findall(r"^(osd\.%s) down[ ]+out.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + else: + down = re.findall(r"^(osd\.%s) down[ ]+in.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + down_in = down + down_out = re.findall(r"^(osd\.%s) down[ ]+out.*%s:" % (args.osdid, osd_host), output, re.MULTILINE) + + if down: + print("OSD %s: Down OSD%s on %s: %s" % ('CRITICAL' if len(down)>=args.crit else 'WARNING' ,'s' if len(down)>1 else '', args.host, " ".join(down))) + print("Up OSDs: " + " ".join(up)) + print("Down+In OSDs: " + " ".join(down_in)) + print("Down+Out OSDs: " + " ".join(down_out)) + print("| 'osd_up'=%d 'osd_down_in'=%d;;%d 'osd_down_out'=%d;;%d" % (len(up), len(down_in), args.crit, len(down_out), args.crit)) + if len(down)>=args.crit: + return STATUS_ERROR + else: + return STATUS_WARNING + + if up: + print("OSD OK") + print("Up OSDs: " + " ".join(up)) + print("Down+In OSDs: " + " ".join(down_in)) + print("Down+Out OSDs: " + " ".join(down_out)) + print("| 'osd_up'=%d 'osd_down_in'=%d;;%d 'osd_down_out'=%d;;%d" % (len(up), len(down_in), args.crit, len(down_out), args.crit)) + return STATUS_OK + + print("OSD WARN: no OSD.%s found on host %s" % (args.osdid, args.host)) + return STATUS_WARNING + +if __name__ == "__main__": + sys.exit(main()) diff --git a/roles/ceph/files/nrpe/check_chrony b/roles/ceph/files/nrpe/check_chrony new file mode 100755 index 0000000..1d220f4 --- /dev/null +++ b/roles/ceph/files/nrpe/check_chrony @@ -0,0 +1,128 @@ +#!/usr/bin/env perl +#=============================================================================== +# DESCRIPTION: Icinga2 / Nagios Check for chrony time sync status and offset +# +# OPTIONS: -h : Help +# -w [warning threshold in seconds] +# -c [critical threshold in seconds] +# +# REQUIREMENTS: Chrony, perl version 5.10.1+ +# +# AUTHOR: Dennis Ullrich (request@decstasy.de) +# +# BUGS ETC: https://github.com/Decstasy/check_chrony +# +# LICENSE: GPL v3 (GNU General Public License, Version 3) +# see https://www.gnu.org/licenses/gpl-3.0.txt +#=============================================================================== + +use 5.10.1; +use strict; +use warnings; +use utf8; +use Getopt::Std; + +# +# Variables +# +my $chronyDaemonName = "chronyd"; +my $leapOk = "Normal"; + +my $rc = 3; +my $msg= ""; +my $perfdata = ""; + +# +# Subroutines +# + +sub help { + print "check_chrony [options] + -w [warning threshold in seconds] + -c [critical threshold in seconds] + e.g.: check_chrony -w 0.6 -c 2\n"; + exit(3); +} + +# Script exit with Nagios / Icinga typical output +sub _exit { + my ( $return, $line ) = @_; + my @state = ( "OK", "WARNING", "CRITICAL", "UNKNOWN" ); + print "$state[$return]: $line\n"; + exit( $return ); +} + +# Checks if a process with $_[0] as name exists +sub proc_exists { + my $PID = `ps -C $_[0] -o pid=`; + if ( ${^CHILD_ERROR_NATIVE} == 0 ){ + return 1; + } + return 0; +} + +# +# Options +# + +my %options=(); +getopts( "hw:c:", \%options ); + +# Check input +if ( keys %options == 0 || defined $options{h} ){ + &help; +} + +for my $key ( keys %options ){ + if ( $options{$key} !~ /^[\d\.]+$/ ){ + &_exit( 3, "Value of option -$key is not a valid number!" ); + } +} + +# +# Check chrony process +# + +&_exit( 2, "$chronyDaemonName is not running!" ) if not &proc_exists( $chronyDaemonName ); + +# +# Get tracking data +# + +my $chronyOutput = `chronyc tracking`; +&_exit( 3, "Chronyc tracking command failed!" ) if ${^CHILD_ERROR_NATIVE} != 0; + +my ( $offset, $dir ) = $chronyOutput =~ /(?:System\stime)[^\d]+([\d\.]+)(?:.*?)(fast|slow)/; +my ( $leap ) = $chronyOutput =~ /(?:Leap)[^\:]+(?::\s+)([\w\h]+)/; + +# +# Check stuff +# + +# Check offset +if ( $offset >= $options{"c"} ){ + $rc = 2; # Critical +} +elsif ( $offset >= $options{"w"} ){ + $rc = 1; # Warning +} +else { + $rc = 0; # Ok +} + +# Prepare offset performace data +$offset = $dir =~ "slow" ? "-$offset" : "+$offset"; +$msg = sprintf( "Time offset of %+.9f seconds to reference.", $offset); +$perfdata = sprintf( "|offset=%.9fs;%.9f;%.9f", ${offset}, $options{'w'}, $options{'c'}); + +# Check leap +if( $leap !~ $leapOk ){ + &_exit( 2, "Chrony leap status \"$leap\" is not equal to \"$leapOk\"! $msg $perfdata" ); +} + +# +# Return stuff +# + +&_exit($rc, "$msg $perfdata"); + diff --git a/roles/pve/files/nrpe/check_smartdisk.sh b/roles/ceph/files/nrpe/check_smartdisk.sh similarity index 100% rename from roles/pve/files/nrpe/check_smartdisk.sh rename to roles/ceph/files/nrpe/check_smartdisk.sh diff --git a/roles/ceph/files/sudoers b/roles/ceph/files/sudoers new file mode 100644 index 0000000..5e2f0cd --- /dev/null +++ b/roles/ceph/files/sudoers @@ -0,0 +1 @@ +nagios ALL=(root) NOPASSWD: /usr/sbin/smartctl,/sbin/dmsetup \ No newline at end of file diff --git a/roles/ceph/tasks/main.yml b/roles/ceph/tasks/main.yml index c1af77e..f334f87 100644 --- a/roles/ceph/tasks/main.yml +++ b/roles/ceph/tasks/main.yml @@ -1,8 +1,2 @@ -- name: Set NRPE Ceph configuration - copy: - src: nrpe.cfg - dest: /etc/nagios/nrpe.d/95-ceph.cfg - owner: root - group: root - mode: u=rw,g=r,o=r - notify: restart-nrpe +- import_tasks: nrpe.yml + tags: nrpe diff --git a/roles/ceph/tasks/nrpe.yml b/roles/ceph/tasks/nrpe.yml new file mode 100644 index 0000000..c94df0a --- /dev/null +++ b/roles/ceph/tasks/nrpe.yml @@ -0,0 +1,24 @@ +- name: Set NRPE Ceph configuration + copy: + src: nrpe.cfg + dest: /etc/nagios/nrpe.d/95-ceph.cfg + owner: root + group: root + mode: u=rw,g=r,o=r + notify: restart-nrpe +- name: Copy Ceph NRPE plugins + copy: + src: nrpe/ + dest: /etc/nagios/plugins/ + owner: root + group: root + mode: u=rwx,g=rx,o=rx + notify: restart-nrpe +- name: Add nagios to sudoers + copy: + src: sudoers + dest: /etc/sudoers.d/nagios + mode: u=rw,g=r,o= + owner: root + group: root + notify: restart-nrpe diff --git a/roles/pve/files/nrpe.cfg b/roles/pve/files/nrpe.d/95-pve.cfg similarity index 100% rename from roles/pve/files/nrpe.cfg rename to roles/pve/files/nrpe.d/95-pve.cfg diff --git a/roles/pve/files/nrpe/check_chrony b/roles/pve/files/plugins/check_chrony similarity index 100% rename from roles/pve/files/nrpe/check_chrony rename to roles/pve/files/plugins/check_chrony diff --git a/roles/pve/files/plugins/check_ilo2_health.pl b/roles/pve/files/plugins/check_ilo2_health.pl new file mode 100755 index 0000000..4f5dba4 --- /dev/null +++ b/roles/pve/files/plugins/check_ilo2_health.pl @@ -0,0 +1,1234 @@ +#!/usr/bin/perl +# icinga: -epn + +# check_ilo2_health.pl +# based on check_stuff.pl and locfg.pl +# +# Nagios plugin using the Nagios::Plugin or Monitoring::Plugin module and the +# HP Lights-Out XML PERL Scripting Sample from +# ftp://ftp.hp.com/pub/softlib2/software1/pubsw-linux/p391992567/v60711/linux-LOsamplescripts3.00.0-2.tgz +# checks if all sensors are ok, returns warning on high temperatures and +# fan failures and critical on overall health failure +# +# Alexander Greiner-Baer <alexander.greiner-baer@web.de> 2007 - 2021 +# Matthew Stier <Matthew.Stier@us.fujitsu.com> 2011 +# Claudio Kuenzler <ck@claudiokuenzler.com> 2021 +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +# +# +# Changelog: +# 1.66 Wed, 21 Apr 2021 13:00:00 +0200 +# support both Nagios::Plugin and Monitoring::Plugin modules +# add option "-W" for checking server's current power usage (in Watt) +# 1.65 Sat, 06 Feb 2021 13:00:03 +0100 +# new option "--ignorecacheother|-O" +# ignores cache status "Other" +# 1.64 Tue, 02 Jun 2020 18:56:30 +0200 +# fix memory status (was Unknown on an otherwise healthy G8) +# fix drive check for ilo2, was broken for 5 years... +# new option "-L" +# retrieve event log from RIB_INFO section +# fix help message for "-l" +# new option "-S" +# check self_test value from https://<ilohost>/json/login_session +# 1.63 Tue, 13 Nov 2018 18:41:48 +0100 +# support iLO5 firmware infos +# applied patch from Rene Koch +# ignore link unknown (option "-U") +# 1.62 Mon, 14 May 2018 19:05:22 +0200 +# retrieve firmware infos only when using --getinfos +# 1.61 Thu, 01 Jun 2017 20:05:04 +0200 +# fix for iLO4 2.50 link state when using --ignorelinkdown +# 1.60 Wed, 12 Aug 2015 18:20:13 +0200 +# provide --sslopts to override defaults settings +# fix }; for GET_EVENT_LOG +# applied patch from Rene Koch <rene.koch@siedl.net>: +# handle missing values when using "-g" +# CONTROLLER_STATUS not present on iLO4 anymore, use STATUS instead +# put SSL_VERIFY_NONE in '' +# 1.59 Wed, 28 Jan 2015 18:56:26 +0100 +# fix chunk size handling +# corrected HTTP/1.1 HOST Header +# applied patch from Max Winterstein <winterstein@siriusonline.de>: +# sslv3 support +# add retries option +# catch XMLin() errors +# applied patch from Rene Koch <rene.koch@siedl.net>: +# ignore battery not installed status (option "-x") +# display server name (option "-g") +# added warning for logical drive status "Degraded (Recovering)" +# display system details (hardware model, serial number, SystemROM, iLO version) +# display memory size and part number in case of memory failure +# display hard disk model number in case of hard disk failure +# display power supply part number in case of power supply failure +# 1.58 Thu, 08 Aug 2013 18:17:02 +0200 +# ignore network link down status (option "-i") +# added ENCLOSURE_ADDR to drive bay label (bay numbering was inconsistent) +# ignore spare drives +# 1.57 Fri, 17 May 2013 19:30:48 +0200 +# SSL_verify_mode SSL_VERIFY_NONE (IO::Socket::SSL changed default) +# event log support for ilo2 +# disable embedded perl in icinga +# 1.56 Fri, 15 Mar 2013 20:47:13 +0100 +# applied patch from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se>: +# check processor and memory details +# applied patches from Dragan Sekerovic <dragan.sekerovic@onestep2.at>: +# add location label to temperature (option "-b") +# support for checking event log (option "-l") +# add iLO version to output +# add 2 new values for power supply status +# -- +# 1.55 Sun, 05 Aug 2012 20:18:46 +0200 +# faulty drive (option "-c") exits now with CRITICAL instead of WARNING +# applied patches from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se>: +# iLO4 RAID Controller Status +# nodriveexit +# add g6 drive status +# overall health probes every element now +# fixed bug with drive bay index +# supports iLO3 with multiple backplanes +# supports iLO4 disk check +# Note: overall health may show drive/storage status, even without "-c" +# -- +# 1.54 Thu, 14 Jun 2012 21:36:40 +0200 +# applied fix for iLO4 from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se> +# -- +# 1.53 Tue, 14 Feb 2012 19:47:40 +0100 +# added new disk bay variant +# added power supply NOT APPLICABLE +# -- +# 1.52 Wed, 27 Jul 2011 20:46:14 +0200 +# fixed <LABEL VALUE = "Power Supplies"/> again +# -- +# 1.51 Mon, 25 Jul 2011 19:36:53 +0200 +# fixed bug with chunked replies by Matthew Stier +# -- +# 1.5 Sat, 16 Jul 2011 10:02:10 +0200 +# optimized by Matthew Stier +# -- +# 1.47 Thu, 14 Jul 2011 12:02:01 +0200 +# also print perfdata when temperature output is disabled +# -- +# 1.46 Wed, 06 Jul 2011 08:46:51 +0200 +# fixed bug with nagios embedded perl interpreter +# -- +# 1.45 Wed, 13 Oct 2010 22:17:01 +0200 +# new option "--ilo3" +# +# "--checkdrives" enhancements +# +# <LABEL VALUE = "Power Supplies"/> shows always "Failed" even when the power +# supplies are redundant +# +# improved "--fanredundancy" and "--powerredundancy" +# -- +# 1.44 Mon, 14 Dec 2009 20:11:37 +0100 +# new option "--checkdrives" +# -- +# 1.43 Mon, 17 Aug 2009 20:50:13 +0200 +# new option "--fanredundancy" +# +# new option "--powerredundancy" +# -- +# 1.42 Mon, 17 Aug 2009 12:52:23 +0100 +# check power supply and fans redundancy +# gcivitella@enter.it +# -- +# 1.41 Thu, 26 Jul 2007 17:42:36 +0200 +# perfdata label ist now quoted +# -- +# 1.4 Mon, 25 Jun 2007 09:45:52 +0200 +# check vrm and power supply +# +# new option "--notemperatures" +# +# new option "--perfdata" +# +# some minor changes +# -- +# 1.3beta Wed, 20 Jun 2007 09:57:46 +0200 +# do some error checking +# +# new option "--inputfile" +# read bmc output from file +# -- +# 1.2 Mon, 18 Jun 2007 09:33:17 +0200 +# new option "--skipsyntaxerrors" +# ignores syntax errors in the xml output, maybe required by older firmwares +# +# introduce a date to the changelog ;) +# -- +# 1.1 do not return warning if temperature status is n/a +# +# add "<LOCFG VERSION="2.21" />" to get rid of the +# "<INFORM>Scripting utility should be updated to the latest version.</INFORM>" +# message +# -- +# 1 initial release + +use strict; +use warnings; +use strict 'refs'; + +use Sys::Hostname; +use IO::Socket::SSL; +use XML::Simple; + +$Net::SSLeay::slowly = 5; + +use vars qw($VERSION $PROGNAME $verbose $warn $critical $timeout $result); +$VERSION = 1.66; + +$PROGNAME = "check_ilo2_health"; + +# instantiate Nagios::Plugin or Monitoring::Plugin +sub load_module { + my @names = @_; + my $module; + for my $name (@names) { + my $file = $name; + # requires need either a bare word or a file name + $file =~ s{::}{/}gsxm; + $file .= '.pm'; + eval { + require $file; + $name->import(); + $module = $name; + }; + last if $module; + } + return $module; +} + +my $plugin_module; +BEGIN { + $plugin_module = load_module( 'Monitoring::Plugin', 'Nagios::Plugin' ); +} + + +our $p = $plugin_module->new( + usage => "Usage: %s [-H <host>] [ -u|--user=<USERNAME> ] + [ -p|--password=<PASSWORD> ] [ -f|--inputfile=<filename> ] + [ -a|--fanredundancy ] [ -c|--checkdrives ] [ -d|--perfdata ] + [ -e|--skipsyntaxerrors ] [ -n|--notemperatures ] [ -3|--ilo3 ] + [ -o|--powerredundancy ] [ -b|--locationlabel ] [ -l|--eventlogcheck] + [ -i|--ignorelinkdown ] [ -x|--ignorebatterymissing ] [ -s|--sslv3 ] + [ -t <timeout> ] [ -r <retries> ] [ -g|--getinfos ] [ --sslopts ] + [ -U|--ignorelinkunknown ] [ -L|--eventlogiLO ] [ -S|--iLOselftest ] + [ -O|--ignorecacheother ] [ -W|--powerusage ] + [ -v|--verbose ] ", + version => $VERSION, + blurb => 'This plugin checks the health status on a remote iLO2|3|4|5 device +and will return OK, WARNING or CRITICAL. iLO (integrated Lights-Out) +can be found on HP Proliant servers.' +); + +$p->add_arg( + spec => 'host|H=s', + help => + qq{-H, --host=STRING + Specify the host on the command line.}, +); + +# add all arguments +$p->add_arg( + spec => 'user|u=s', + help => + qq{-u, --user=STRING + Specify the username on the command line.}, +); + +$p->add_arg( + spec => 'password|p=s', + help => + qq{-p, --password=STRING + Specify the password on the command line.}, +); + +$p->add_arg( + spec => 'inputfile|f=s', + help => + qq{-f, --inputfile=STRING + Read input from file.}, +); + +$p->add_arg( + spec => 'fanredundancy|a', + help => + qq{-a, --fanredundancy + Check fan redundancy}, +); + +$p->add_arg( + spec => 'checkdrives|c', + help => + qq{-c, --checkdrives + Check drive bays.}, +); + +$p->add_arg( + spec => 'perfdata|d', + help => + qq{-d, --perfdata + Enable perfdata on output.}, +); + +$p->add_arg( + spec => 'locationlabel|b', + help => + qq{-b, --locationlabel + Show temperature with location.}, +); + +$p->add_arg( + spec => 'eventlogcheck|l', + help => + qq{-l, --eventlogcheck + Parse iLO event log "Integrated Management Log" (SERVER_INFO) for interesting events (f.e. broken memory).}, +); + +$p->add_arg( + spec => 'eventlogiLO|L', + help => + qq{-L, --eventlogiLO + Parse iLO event log "iLO Event Log" (RIB_INFO) for interesting events (f.e. Embedded media manager failed media attach).}, +); + +$p->add_arg( + spec => 'skipsyntaxerrors|e', + help => + qq{-e, --skipsyntaxerrors + Skip syntax errors on older firmwares.}, +); + +$p->add_arg( + spec => 'ignorebatterymissing|x', + help => + qq{-x, --ignorebatterymissing + Ignore Battery missing status.}, +); + +$p->add_arg( + spec => 'ignorelinkdown|i', + help => + qq{-i, --ignorelinkdown + Ignore NIC Link Down status (iLO4).}, +); + +$p->add_arg( + spec => 'ignorelinkunknown|U', + help => + qq{-U, --ignorelinkunknown + Ignore NIC Link Unknown status (iLO5).}, +); + +$p->add_arg( + spec => 'notemperatures|n', + help => + qq{-n, --notemperatures + Disable temperature listing.}, +); + +$p->add_arg( + spec => 'powerredundancy|o', + help => + qq{-o, --powerredundancy + Check power redundancy.}, +); + +$p->add_arg( + spec => 'getinfos|g', + help => + qq{-g, --getinfos + Display additional infos like firmware version and servername. May need increased timeout.}, +); + +$p->add_arg( + spec => 'ilo3|3', + help => + qq{-3, --ilo3 + Check iLO version >= 3 device.}, +); + +$p->add_arg( + spec => 'retries|r=i', + help => + qq{-r, --retries=INTEGER + Number of retries.}, +); + +$p->add_arg( + spec => 'sslv3|s', + help => + qq{-s, --sslv3 + Use sslv3 for connection.}, +); + +$p->add_arg( + spec => 'sslopts=s', + help => + qq{--sslopts + Sets IO::Socket:SSL Options, defaults to 'SSL_verify_mode => SSL_VERIFY_NONE'. + Some firmware may need --sslopts 'SSL_verify_mode => SSL_VERIFY_NONE, SSL_version => "TLSv1"'.}, +); + +$p->add_arg( + spec => 'selftest|S', + help => + qq{-S, --selftest + Make additional check for iLO self test from https://<ilohost>/json/login_session. May only work on iLO5.}, +); + +$p->add_arg( + spec => 'ignorecacheother|O', + help => + qq{-O, --ignorecacheother + Ignore cache status "Other".}, +); + +$p->add_arg( + spec => 'powerusage|W', + help => + qq{-W, --powerusage + Check servers current power usage (in Watt)}, +); + +# parse arguments +$p->getopts; + +my $return = "OK"; +my $message = ""; +our $xmlinput = ""; +our $isinput = 0; +our $drive_input = ""; +our $is_drive_input = 0; +our $drive_xml_broken = 0; +our $client; +our $is_event_input = 0; +our $event_severity = ""; +our $event_class = ""; +our $event_description = ""; +our %event_status; +my $host = $p->opts->host; +my $hostname = $p->opts->host; +my $username = $p->opts->user; +my $password = $p->opts->password; +my $inputfile = $p->opts->inputfile; +our $skipsyntaxerrors = defined($p->opts->skipsyntaxerrors) ? 1 : 0; +my $optfanredundancy = defined($p->opts->fanredundancy) ? 1 : 0; +my $optpowerredundancy = defined($p->opts->powerredundancy) ? 1 : 0; +my $notemperatures = defined($p->opts->notemperatures) ? 1 : 0; +our $optcheckdrives = defined($p->opts->checkdrives) ? 1 : 0; +my $optilo3 = defined($p->opts->ilo3) ? 1 : 0; +my $iloboardversion = defined($p->opts->ilo3) ? "ILO>=3" : "ILO2"; +my $perfdata = defined($p->opts->perfdata) ? 1 : 0; +my $locationlabel = defined($p->opts->locationlabel) ? 1 : 0; +my $eventlogcheck = defined($p->opts->eventlogcheck) ? 1 : 0; +my $eventlogiLO = defined($p->opts->eventlogiLO) ? 1 : 0; +my $ignorelinkdown = defined($p->opts->ignorelinkdown) ? 1 : 0; +my $ignorelinkunknown = defined($p->opts->ignorelinkunknown) ? 1 : 0; +my $ignorebatterymissing = defined($p->opts->ignorebatterymissing) ? 1 : 0; +my $optpowerusage = defined($p->opts->powerusage) ? 1 : 0; +my $getinfos = defined($p->opts->getinfos) ? 1 : 0; +our %drives; +our $drive; +our $drivestatus; +our $product_name = ""; +our $serial_number = ""; +our $server_name = ""; +my $retries=0; +my $xml; +my $sslv3 = defined($p->opts->sslv3) ? 1 : 0; +my $sslopts = 'SSL_verify_mode => SSL_VERIFY_NONE'; +our @product; +our @serial; +our @sname; +my $optselftest = defined($p->opts->selftest) ? 1 : 0; +my $ignorecacheother = defined($p->opts->ignorecacheother) ? 1 : 0; + +$message = "(Board-Version: $iloboardversion) "; + +unless ( ( defined($inputfile) ) || + ( defined($host) && defined($username) && defined($password) ) ) { + $p->nagios_die("ERROR: Missing host, password and user."); +} + +if ( defined ( $p->opts->retries ) ) { + $retries = $p->opts->retries; +} + +if ( defined ( $p->opts->sslopts ) ) { + $sslopts = $p->opts->sslopts; +} + +alarm $p->opts->timeout; + +my $boundary; +our $sendsize; +my $localhost = hostname() || 'localhost'; +print "hostname is $localhost\n" if ( $p->opts->verbose ); + +for (my $i=0;$i<=$retries;$i++) { + print "retry: $i\n" if ( $p->opts->verbose ); + unless ( defined($inputfile) ) { + # query code from locfg.pl + # Set the default SSL port number if no port is specified + $host .= ":443" unless ($host =~ m/:/); + # + # Open the SSL connection and the input file + $client = new IO::Socket::SSL->new(PeerAddr => $host, eval $sslopts, $sslv3 ? + ( SSL_version => 'SSLv3' ) : () ); + unless ( $client ) { + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: Failed to establish SSL connection with $host $! $SSL_ERROR." + ); + } + + if ( $optilo3 ) { + print "sending ilo3\n" if ( $p->opts->verbose ); + my $cmd = '<?xml version="1.0"?>'; + $cmd .= '<LOCFG VERSION="2.21" />'; + $cmd .= '<RIBCL VERSION="2.21">'; + $cmd .= '<LOGIN USER_LOGIN="'.$username.'" PASSWORD="'.$password.'">'; + $cmd .= '<SERVER_INFO MODE="read">'; + $cmd .= '<GET_EMBEDDED_HEALTH />'; + if ( $eventlogcheck ) { + $cmd .= '<GET_EVENT_LOG />'; + } + if ( $getinfos ) { + $cmd .= '<GET_HOST_DATA />'; + $cmd .= '<GET_PRODUCT_NAME />'; + $cmd .= '<GET_SERVER_NAME />'; + } + $cmd .= '</SERVER_INFO>'; + if ( $eventlogiLO ) { + $cmd .= '<RIB_INFO MODE="read">'; + $cmd .= '<GET_EVENT_LOG />'; + $cmd .= '</RIB_INFO>'; + } + $cmd .= '</LOGIN>'; + $cmd .= '</RIBCL>'; + $cmd .= "\r\n"; + send_or_calculate(0,$cmd); + + send_to_client(0, "POST /ribcl HTTP/1.1\r\n"); + send_to_client(0, "HOST: $hostname\r\n"); # Mandatory for http 1.1 + send_to_client(0, "TE: chunked\r\n"); + send_to_client(0, "Connection: Close\r\n"); # Required + send_to_client(0, "Content-length: $sendsize\r\n"); # Mandatory for http 1.1 + send_to_client(0, "\r\n"); + send_or_calculate(1,$cmd); #Send it to iLO + } + else { + # send xml to BMC + print $client '<?xml version="1.0"?>' . "\r\n"; + print $client '<LOCFG VERSION="2.21" />' . "\r\n"; + print $client '<RIBCL VERSION="2.21">' . "\r\n"; + print $client '<LOGIN USER_LOGIN="'.$username.'" PASSWORD="'.$password.'">' . "\r\n"; + print $client '<SERVER_INFO MODE="read">' . "\r\n"; + print $client '<GET_EMBEDDED_HEALTH />' . "\r\n"; + if ( $eventlogcheck ) { + print $client '<GET_EVENT_LOG />' . "\r\n"; + } + if ( $getinfos ) { + print $client '<GET_HOST_DATA />' . "\r\n"; + print $client '<GET_PRODUCT_NAME />' . "\r\n"; + print $client '<GET_SERVER_NAME />' . "\r\n"; + } + print $client '</SERVER_INFO>' . "\r\n"; + if ( $eventlogiLO ) { + print $client '<RIB_INFO MODE="read">' . "\r\n"; + print $client '<GET_EVENT_LOG />' . "\r\n"; + print $client '</RIB_INFO>' . "\r\n"; + } + print $client '</LOGIN>' . "\r\n"; + print $client '</RIBCL>' . "\r\n"; + } + } + else { + open($client,$inputfile) or $p->nagios_die("ERROR: $inputfile not found"); + } + +# retrieve data + if ( $optilo3 && !$inputfile ) { + read_chunked_reply(); + } + else { + while (my $ln = <$client>) { + parse_reply($ln); + } + close $client; + } + +# parse with XML::Simple + if ( $xmlinput && $isinput == 0 ) { + $xml = eval { XMLin($xmlinput, ForceArray => 1) }; + if ( $@ ) { + if ( $i < $retries ) { + next; + } + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: $@" + ); + } + else { + last; + } + } + else { + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: No parseable output." + ); + } +} + +if ( $getinfos ) { + $serial_number = ""; + if ( defined $serial[3] ) { + $serial[3] =~ tr/ //ds ; + $serial_number = "Serial: $serial[3]"; + } + $server_name = ""; + $server_name = " - Servername: $sname[1]" if defined $sname[1]; + my $system_rom = undef; + my $firmware_name = undef; + my $firmware_version = undef; + + # loop through firmware hash + foreach my $index (keys %{ $xml->{'FIRMWARE_INFORMATION'}[0] }) { + if (defined $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'}) { + if ($xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} =~ m/^iLO/) { + $firmware_name = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'}; + $firmware_version = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_VERSION'}[0]->{'VALUE'}; + } elsif ($xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} eq "HP ProLiant System ROM" || $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} eq "System ROM") { + $system_rom = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_VERSION'}[0]->{'VALUE'}; + } + } + } + + my $product_name = undef; + if (! defined $product[1]) { + $product_name = "Unknown product"; + } else { + $product_name = $product[1] + } + $serial_number = "Unknown serial" if ! defined $serial_number; + $firmware_name = "iLO" if ! defined $firmware_name; + $firmware_version = "Unknown firmware version" if ! defined $firmware_version; + $server_name = "Unknown server name" if ! defined $server_name; + + if (defined $system_rom) { + $message = "($product_name - SystemROM: $system_rom - $serial_number - $firmware_name FW $firmware_version" . "$server_name) "; + } else { + $message = "($product_name - $serial_number - $firmware_name FW $firmware_version" . "$server_name) "; + } +} + +my $drive_xml; +if ( $optcheckdrives && !$drive_xml_broken ) { + if ( $drive_input && $is_drive_input == 0 ) { + $drive_xml = eval { XMLin($drive_input, ForceArray => 1) }; + if ( $@ ) { + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: $@" + ); + } + } + elsif ( ref $xml->{'STORAGE'}[0]->{'CONTROLLER'} ) { + # iLO4 specific, no need for $drive_input + } + else { + # No need to error out if host uncapable of checking drive status + warn "No drive_input found" if ( $p->opts->verbose ); + } +} + +my $temperatures = $xml->{'TEMPERATURE'}[0]->{'TEMP'}; +my $backplanes = $drive_xml->{'BACKPLANE'}; +my $raidcontroller = $xml->{'STORAGE'}[0]->{'CONTROLLER'}; +my @checks; +push(@checks,$xml->{'FANS'}[0]->{'FAN'}); +push(@checks,$xml->{'VRM'}[0]->{'MODULE'}); +push(@checks,$xml->{'POWER_SUPPLIES'}[0]->{'SUPPLY'}); +if($xml->{'PROCESSORS'}) { + push(@checks,$xml->{'PROCESSORS'}[0]->{'PROCESSOR'}); +} +my $memdetails; +if($xml->{'MEMORY'}) { + $memdetails = $xml->{'MEMORY'}[0]->{'MEMORY_DETAILS'}[0]; +} +my $health = $xml->{'HEALTH_AT_A_GLANCE'}[0]; +my $label; +my $status; +my $temperature; +my $cautiontemp; +my $criticaltemp; + +## check overall health status + +my $componentstate; +foreach (keys %{$health}) { + $componentstate = $health->{$_}[0]->{'STATUS'}; + if ( defined($componentstate) && ( $componentstate !~ m/^Ok$|^OTHER$|^NOT APPLICABLE$/i ) ) { + if ($_ eq 'STORAGE') { + if ( ref($raidcontroller) ) { + # For iLO4 we can look at the raid controller to get a more detailed + # status, so just log a WARNING unless we find something CRITICAL + # later on. + $return = "WARNING" unless ( $return eq "CRITICAL" ); + } + else { + $return = "CRITICAL"; + } + } + elsif ( ( $_ eq 'BATTERY' ) && $ignorebatterymissing && ( $componentstate =~ m/^Not Installed$/i ) ) { + next; + } + elsif ( ( $_ eq 'NETWORK' ) && $ignorelinkdown && ( $componentstate =~ m/^Link Down$/i || $componentstate =~ m/^Degraded$/i ) ) { + next; + } + elsif ( ( $_ eq 'NETWORK' ) && $ignorelinkunknown && ( $componentstate =~ m/^Unknown$/i ) ) { + next; + } + else { + $return = "CRITICAL"; + } + $message .= "$_ $componentstate, "; + } +} + +if ( $optpowerredundancy ) { + my $powerredundancy = $health->{'POWER_SUPPLIES'}[1]->{'REDUNDANCY'}; + if ( defined($powerredundancy) && + ( $powerredundancy !~ m/^Fully Redundant$|^REDUNDANT$|^NOT APPLICABLE$/i ) ) { + $return = "CRITICAL"; + $message .= "Power supply $powerredundancy, "; + } +} + +if ( $optpowerusage ) { + my $powerusage=$xml->{'POWER_SUPPLIES'}[0]->{'POWER_SUPPLY_SUMMARY'}[0]->{'PRESENT_POWER_READING'}[0]->{'VALUE'}; + my ($powerperf) = $powerusage =~ m/(\d+) [Ww]atts/i; + $message .= "Power Usage: $powerusage, "; + if ( $perfdata ) { + $p->add_perfdata( + label => "power", + value => $powerperf, + uom => "" + ); + } +} + +if ( $optfanredundancy ) { + my $fanredundancy = $health->{'FANS'}[1]->{'REDUNDANCY'}; + if ( defined($fanredundancy) && + ( $fanredundancy !~ m/^Fully Redundant$|^REDUNDANT$|^NOT APPLICABLE$/i ) ) { + $return = "CRITICAL"; + $message .= "Fans $fanredundancy, "; + } +} + +# check fans, vrm and power supplies +foreach my $check ( @checks ) { + if ( ref($check) ) { + foreach my $item ( @$check ) { + $label=$item->{'LABEL'}[0]->{'VALUE'}; + $status=$item->{'STATUS'}[0]->{'VALUE'}; + if ( defined($label) && defined($status) ) { + # misleading output on some iLO3 shows always failed, skip it + if ($label =~ m/^Power Supplies$/) { + next; + } + if ($label =~ m/^Power Supply/) { + # get details for power supplies + $label =~ s/ /_/g; + if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Installed$|^Unknown$"i ) ) { + $return = "WARNING" unless ( $return eq "CRITICAL" ); + if ( defined($item->{'MODEL'}[0]->{'VALUE'}) ) { + $message .= "$label is $status (ModelNumber: $item->{'MODEL'}[0]->{'VALUE'}) "; + } + else { + $message .= "$label is $status "; + } + } + next; + } + $label =~ s/ /_/g; + if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Installed$|^Unknown$"i ) ) { + $return = "WARNING" unless ( $return eq "CRITICAL" ); + $message .= "$label: $status, "; + } + } + } + } +} + +# check memory status (iLO4 only?) +if ( ref($memdetails) ) { + foreach my $loc ( sort keys %{$memdetails} ) { + foreach ( @{$memdetails->{$loc}} ) { + $status = $_->{'STATUS'}[0]->{'VALUE'}; + if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Present$|^Unknown$"i ) ) { + $return = "WARNING" unless ( $return eq "CRITICAL" ); + my $socket = $_->{'SOCKET'}[0]->{'VALUE'}; + my $size = $_->{'SIZE'}[0]->{'VALUE'}; + my $part = $_->{'PART'}[0]->{'NUMBER'}; + if ( defined $part ) { + # works only with new iLO4 firmware + $message .= "Mem $loc $socket: $status (Size: $size, PartNumber: $part), "; + } + else { + $message .= "Mem $loc $socket: $status (Size: $size), "; + } + } + } + } +} + +# check newer drive bays (iLO3) +if ( ref($backplanes) ) { + my $backplane = 0; + foreach ( @{$backplanes} ) { + if ( defined($_->{'ENCLOSURE_ADDR'}[0]->{'VALUE'} ) ) { + $backplane = $_->{'ENCLOSURE_ADDR'}[0]->{'VALUE'}; + } + else { + $backplane++; + } + if ( $_->{'DRIVE_BAY'} ) { + for ( my $i=0; $i<= $#{$_->{'DRIVE_BAY'}}; $i++ ) { + $label=$backplane." ".$_->{'DRIVE_BAY'}[$i]->{'VALUE'}; + $status=$_->{'STATUS'}[$i]->{'VALUE'}; + $drives{$label}{'status'} = $status; + } + } + if ( $_->{'DRIVE'} ) { + for ( my $i=0; $i<= $#{$_->{'DRIVE'}}; $i++ ) { + $label=$backplane." ".$_->{'DRIVE'}[$i]->{'BAY'}; + $status=$_->{'DRIVE_STATUS'}[$i]->{'VALUE'}; + $drives{$label}{'status'} = $status; + } + } + } +} + +# seems that iLO4 reads the state from the RAID controller, nice +if ( ref($raidcontroller) ) { + foreach ( @{$raidcontroller} ) { + my $ctrllabel = $_->{'LABEL'}[0]->{'VALUE'}; + my $ctrlstatus = $_->{'CONTROLLER_STATUS'}[0]->{'VALUE'} || $_->{'STATUS'}[0]->{'VALUE'}; + if($ctrlstatus ne 'OK') { + $return = "CRITICAL"; + $message .= "SmartArray $ctrllabel Status: $ctrlstatus, "; + } + my $cachestatus = $_->{'CACHE_MODULE_STATUS'}[0]->{'VALUE'}; + if($cachestatus && $cachestatus ne 'OK') { + # FIXME: There are probably other valid cache module states that + # needs to be excluded. + if($ignorecacheother && $cachestatus eq 'Other') { + next; + } + $return = "CRITICAL"; + $message .= "SmartArray $ctrllabel Cache Status: $cachestatus, "; + } + foreach ( @{$_->{'DRIVE_ENCLOSURE'}} ) { + my $enclabel = $_->{'LABEL'}[0]->{'VALUE'}; + my $encstatus = $_->{'STATUS'}[0]->{'VALUE'}; + my $encmodel = $_->{'MODEL_NUMBER'}[0]->{'VALUE'}; + if($encstatus ne 'OK') { + $message .= "SmartArray $ctrllabel Enclosure $enclabel: $encstatus (ModelNumber: $encmodel) - check hardware status in OS, "; + $return = "CRITICAL"; + } + } + foreach ( @{$_->{'LOGICAL_DRIVE'}} ) { + my $ldlabel = $_->{'LABEL'}[0]->{'VALUE'}; + my $ldstatus = $_->{'STATUS'}[0]->{'VALUE'}; + if($ldstatus ne 'OK') { + $message .= "SmartArray $ctrllabel LD $ldlabel: $ldstatus, "; + if($ldstatus eq 'Degraded (Rebuilding)' || $ldstatus eq 'Degraded (Recovering)') { + $return = "WARNING" unless ( $return eq "CRITICAL" ); + } + else { + $return = "CRITICAL"; + } + } + foreach ( @{$_->{'PHYSICAL_DRIVE'}} ) { + $label = "$ctrllabel $_->{'LABEL'}[0]->{'VALUE'}"; + $status = $_->{'STATUS'}[0]->{'VALUE'}; + my $model = $_->{'MODEL'}[0]->{'VALUE'}; + $drives{$label}{'status'} = $status; + $drives{$label}{'model'} = $model; + } + } + } +} + +# check drive bays +if ( $optcheckdrives ) { + foreach ( sort keys(%drives) ) { + if ( ( $drives{$_}{'status'} !~ m"^(Ok)$|^(n/a)$|^(Spare)$|^(Not Installed)|^(Not Present/Not Installed)$|^(spun down)$"i ) ) { + $return = "CRITICAL"; + $message .= "$_: ".$drives{$_}{'status'}; + if (defined $drives{$_}{'model'}){ + $message .= " (Drive ModelNumber: " . $drives{$_}{'model'} ."), "; + } + } + } +} + +# check event logs +if ( $eventlogcheck || $eventlogiLO ) { + foreach ( keys %event_status ) { + next if ( $event_status{$_} =~ m/Repaired/ ); + $message .= " $_:$event_status{$_} "; + $return = "WARNING" unless ( $return eq "CRITICAL" ); + } +} + +# check self test +if ( $optselftest ) { + my %ret = getselfteststatus(); + if ($ret{'status'}) { + $return = "CRITICAL"; + $message .= "iLO self test is $ret{'selftest'}, "; + if ( defined($ret{'faults'}) ) { + $message .= "self test fault is $ret{'faults'}, "; + } + } +} + + +unless ( $message ) { + $message .= "No faults detected, "; +} + +# check temperatures +if ( ref($temperatures) ) { + unless ( $notemperatures ) { + $message .= "Temperatures: "; + } + foreach my $temp ( @$temperatures ) { + $label=$temp->{'LABEL'}[0]->{'VALUE'}; + if ( $locationlabel && defined($temp->{'LOCATION'}[0]->{'VALUE'}) ) { + $label .= " (" . $temp->{'LOCATION'}[0]->{'VALUE'} . ")"; + } + $status=$temp->{'STATUS'}[0]->{'VALUE'}; + $temperature=$temp->{'CURRENTREADING'}[0]->{'VALUE'}; + if ( defined($label) && defined($status) && defined($temperature) ) { + $label =~ s/ /_/g; + unless ( ( $status =~ m"^Ok$|^n/a$|^Not Installed$"i ) ) { + $return = "WARNING" unless ( $return eq "CRITICAL" ); + $message .= "$label ($status): $temperature, " + if ( $notemperatures ); + } + unless ( ( $status =~ m"^n/a$|^Not Installed$"i ) ) { + $message .= "$label ($status): $temperature, " + unless ( $notemperatures ); + if ( $perfdata ) { + $cautiontemp=$temp->{'CAUTION'}[0]->{'VALUE'}; + $criticaltemp=$temp->{'CRITICAL'}[0]->{'VALUE'}; + # Returned value can be 'N/A', enforce this being a number + if($cautiontemp && $cautiontemp !~ /^[0-9]+/) { + $cautiontemp=undef; + } + if($criticaltemp && $criticaltemp !~ /^[0-9]+/) { + $criticaltemp=undef; + } + if ( defined($cautiontemp) && defined($criticaltemp) ) { + $p->set_thresholds( + warning => $cautiontemp, + critical => $criticaltemp, + ); + my $threshold = $p->threshold; + # add perfdata + $p->add_perfdata( + label => $label, + value => $temperature, + uom => "", + threshold => $threshold, + ); + } + } + } + } + else { + $message .= "no reading, "; + } + } +} + + +# strip trailing "," +$message =~ s/, $//; + +$p->nagios_exit( + return_code => $return, + message => $message +); + + +# send_to_client, send_or_calculate and read_chunked_reply +# are adapted from locfg.pl + +sub send_to_client +{ + my ($send, $cmd) = @_; + print $cmd if ( $p->opts->verbose && length($cmd) < 1024 ); + print $client $cmd; + $sendsize -= length($cmd) if ( $send ); +} + +sub send_or_calculate # used for iLO 3 only +{ + $sendsize = 0; + my ($send, $cmd) = @_; + if ($send) { + print $client $cmd; + } + $sendsize += length($cmd); + print "size $sendsize\n" if ( $p->opts->verbose ); +} + + +sub read_chunked_reply # used for iLO 3 only +{ + my $ln = ""; + my $lp = ""; + my $hide = 1; + my $chunk = 1; + my $chunkSize; + + while( 1 ) { + # Read a line + $ln = <$client>; + # Get length of line + my $length = length($ln); + # Exit loop if zero + if ( $length == 0 ) { + if ( $p->opts->verbose ) { + print "read_chunked_reply: read a zero-length line. Continue...\n"; + } + last; + } + # Skip HTTP headers and first line of chunked responses + if ( $hide ) { + $hide = 0 if ( $ln =~ m/^\r\n$/ ); + print "Head: " . $ln if ( $p->opts->verbose ); + next; + } + # Get size of chunk + if ( $chunk ) { + print "chunk: " . $ln if ( $p->opts->verbose ); + $ln =~ s/\r|\n//g; + $chunkSize = hex($ln); + $chunk = 0; + print "chunk size: $chunkSize\n" if ( $p->opts->verbose ); + next; + } + # Last Chunk + if ( $chunkSize == 0 ) { + print "read_chunked_reply: reach end of responses.\n" if ($p->opts->verbose); + last; + } + # End of chunk, process incomplete line + if ( $chunkSize < $length ) { + $chunk = 1; # Next line, new chunk + $hide = 0; # Skip hide + $lp .= substr($ln, 0, $chunkSize); # Truncate and append + } + # End of chunk, process complete line + elsif ( $chunkSize == $length ) { + $chunk = 1; # Next line, new chunk + $hide = 1; # Hide new chunk's first line + $lp .= $ln; # Append line as-is + } + # Process line + else { + $chunkSize -= $length; # Decrement chunk size + $lp .= $ln; # Append line as-is + } + # Skip incomplete line + next unless ( $lp =~ m/\n$/ ); + # Parse complete line + parse_reply($lp); + # Line parsed, clear line + $lp = ""; + } + if ($client->error()) { + print "Error: connection error " . $client->error() . "\n"; + } +} + +sub parse_reply +{ + my ($line) = @_; + $line =~ s/\r\n$/\n/; + print $line if ( $p->opts->verbose ); + + if ( $getinfos ) { + # Prune all unnecessary lines + $isinput = 1 if ( $line =~ m"<GET_EMBEDDED_HEALTH_DATA>|</DRIVES>" ); + $xmlinput .= $line if ( $isinput ); + $isinput = 0 if ( $line =~ m"</GET_EMBEDDED_HEALTH_DATA>|<DRIVES>" ); + $product_name = $line if ( $line =~ m"<PRODUCT_NAME VALUE" ); + $serial_number = $line if ( $line =~ m/FIELD NAME="Serial Number"/ ); + $server_name = $line if ( $line =~ m"<SERVER_NAME" ); + @product = split (/"/, $product_name) if defined $product_name; + @serial = split (/"/, $serial_number) if defined $serial_number; + @sname = split (/"/, $server_name ) if defined $server_name; + } + else { + # Prune all unnecessary lines + $isinput = 1 if ( $line =~ m"<GET_EMBEDDED_HEALTH_DATA>|</DRIVES>|</FIRMWARE_INFORMATION>" ); + $xmlinput .= $line if ( $isinput ); + $isinput = 0 if ( $line =~ m"</GET_EMBEDDED_HEALTH_DATA>|<DRIVES>|<FIRMWARE_INFORMATION>" ); + } + + # drive check needs special handling + # <DRIVES> + # <BACKPLANE> + # <FIRMWARE_VERSION VALUE="1.18"/> + # <ENCLOSURE_ADDR VALUE="224"/> + # <DRIVE_BAY VALUE = "1"/> + # <PRODUCT_ID VALUE = "EH0300FBQDD "/> + # <STATUS VALUE = "Ok"/> + # <UID_LED VALUE = "Off"/> + # <DRIVE_BAY VALUE = "2"/> + # <PRODUCT_ID VALUE = "EH0300FBQDD "/> + # <STATUS VALUE = "Fault"/> + # <UID_LED VALUE = "Off"/> + # </BACKPLANE> + # </DRIVES> + + $is_drive_input = 1 if ( $line =~ m"<DRIVES>" ); + $drive_input .= $line if ( $is_drive_input ); + $is_drive_input = 0 if ( $line =~ m"</DRIVES>" ); + + # because on many (older?) iLOs drive status is not XML + if ($optcheckdrives) { + if ( $line =~ m/<Drive Bay: / ) { + $drive_xml_broken = 1; + # <Drive Bay: "3"; status: "Smart Error"; uid led="Off"/> + ( $drive, $drivestatus ) = ( $line =~ + m/Drive Bay: "(.*)"; status: "(.*)"; uid led: ".*"/ ); + if ( defined($drive) && defined($drivestatus) ) { + $drives{$drive}{'status'} = $drivestatus; + } + } + if ( $line =~ m/<DRIVE BAY=".*" PRODUCT_ID="/ ) { + $drive_xml_broken = 1; + # <DRIVE BAY="3" PRODUCT_ID="N/A"STATUS="Smart Error" UID_LED="Off"/> + ( $drive, $drivestatus ) = ( $line =~ + m/DRIVE BAY="(.*)" PRODUCT_ID=".*"STATUS="(.*)" UID_LED=".*"/ ); + if ( defined($drive) && defined($drivestatus) ) { + $drives{$drive}{'status'} = $drivestatus; + } + } + } + + if ( $eventlogcheck || $eventlogiLO ) { + $is_event_input = 1 if ( $line =~ m"<EVENT" ); + if ( $is_event_input ) { + if ( $line =~ m/SEVERITY="(.*?)"/ ) { + $event_severity = $1; + #print "SEV: $event_severity\n"; + } + if ( $line =~ m/CLASS="(.*?)"/ ) { + $event_class = $1; + #print "CLASS: $event_class\n"; + } + if ( $line =~ m/DESCRIPTION="(.*?)"/ ) { + $event_description = $1; + #print "DESCRIPTION: $event_description\n"; + } + } + $is_event_input = 0 if ( $is_event_input && $line =~ m"/>" ); + if ( $is_event_input == 0 && $event_class ) { + if ( ($event_class !~ m/POST|Maintenance/) && ( $event_severity !~ m/Informational/) ) { + $event_status{$event_description} = $event_severity; + $event_class = ""; + } + } + } + + if ( $line =~ m/MESSAGE='(.*)'/ ) { + my $msg = $1; + + if ( $msg =~ m/No error/i ) { + # Skip + } + elsif ( $msg =~ m/Syntax error/i && $skipsyntaxerrors ) { + # Skip + } + else { + close $client; + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: $msg." + ); + } + } +} + +sub getselfteststatus { + my ($json, $selftest, $selftestfaults, $selfteststatus); + # Set the default SSL port number if no port is specified + $host .= ":443" unless ($host =~ m/:/); + # Open the SSL connection and the input file + $client = new IO::Socket::SSL->new(PeerAddr => $host, eval $sslopts, $sslv3 ? + ( SSL_version => 'SSLv3' ) : () ); + unless ( $client ) { + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: Failed to establish SSL connection with $host $! $SSL_ERROR." + ); + } + + print "sending self test status request\n" if ( $p->opts->verbose ); + + send_to_client(0, "GET /json/login_session HTTP/1.1\r\n"); + send_to_client(0, "HOST: $hostname\r\n"); # Mandatory for http 1.1 + send_to_client(0, "Connection: Close\r\n"); # Required + send_to_client(0, "\r\n"); + + # retrieve data + while (my $ln = <$client>) { + $json .= $ln; + } + close $client; + + if ( $json ) { + print "$json\n" if ( $p->opts->verbose ); + ( $selftest ) = ( $json =~ m/"self_test":"([^"]+)"/ ); + ( $selftestfaults ) = ( $json =~ m/"self_test_faults":"([^"]+)"/ ); + $selfteststatus = 0; + if ( defined($selftest) ) { + unless ( $selftest =~ m/^OP_STATUS_OK$/ ) { + $selfteststatus = 1; + } + } + return ( 'selftest' => $selftest, 'faults' => $selftestfaults, 'status' => $selfteststatus); + } + else { + $p->nagios_exit( + return_code => "UNKNOWN", + message => "ERROR: No parseable output from self test." + ); + } +} diff --git a/roles/pve/files/plugins/check_smartdisk.sh b/roles/pve/files/plugins/check_smartdisk.sh new file mode 100755 index 0000000..605ea12 --- /dev/null +++ b/roles/pve/files/plugins/check_smartdisk.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Checks status of disks SMART + +STATUS_LABEL="SMART Health Status:" +STATUS_OK="$STATUS_LABEL OK" + +if [[ "$#" == "0" ]]; then + echo "Usage: $0 <disk1> [<disk2> ... <diskX>]" + exit +fi + +for DISK in "$@" +do + STATUS=$(sudo /usr/sbin/smartctl -H -d scsi "$DISK" | grep "$STATUS_LABEL") + + if [ "$STATUS" != "$STATUS_OK" ]; then + echo "CRITICAL: $DISK: $STATUS" + exit 2 + fi +done + +echo "OK: $STATUS_OK" diff --git a/roles/pve/files/nrpe/check_zfs.pl b/roles/pve/files/plugins/check_zfs.pl similarity index 100% rename from roles/pve/files/nrpe/check_zfs.pl rename to roles/pve/files/plugins/check_zfs.pl diff --git a/roles/pve/tasks/nrpe.yml b/roles/pve/tasks/nrpe.yml index e280c13..f242d49 100644 --- a/roles/pve/tasks/nrpe.yml +++ b/roles/pve/tasks/nrpe.yml @@ -1,14 +1,20 @@ +- name: Install base packages + apt: + name: + - libxml-simple-perl + - libmonitoring-plugin-perl + state: present - name: Set NRPE PVE configuration copy: - src: nrpe.cfg - dest: /etc/nagios/nrpe.d/95-pve.cfg + src: nrpe.d/95-pve.cfg + dest: /etc/nagios/nrpe.d/ owner: root group: root mode: u=rw,g=r,o=r notify: restart-nrpe - name: Copy PVE NRPE plugins copy: - src: nrpe/ + src: plugins/ dest: /etc/nagios/plugins/ owner: root group: root diff --git a/roles/pve/tasks/vhost.yml b/roles/pve/tasks/vhost.yml index 77c25b5..3287de5 100644 --- a/roles/pve/tasks/vhost.yml +++ b/roles/pve/tasks/vhost.yml @@ -7,8 +7,8 @@ group: root register: copy_result -- name: Reboot the system if file was copied - reboot: - reboot_timeout: 600 - become: true - when: copy_result.changed \ No newline at end of file +#- name: Reboot the system if file was copied +# reboot: +# reboot_timeout: 600 +# become: true +# when: copy_result.changed \ No newline at end of file From 2cbf9c561930cb8cbf8d3eee9da9cd1f08833879 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 17 Mar 2025 17:50:56 +0100 Subject: [PATCH 12/34] refs #8776 PVE and Ceph monitoring --- roles/ceph/files/{nrpe.cfg => nrpe.d/95-ceph.cfg} | 0 roles/ceph/files/{nrpe => plugins}/check_ceph_mon | 0 roles/ceph/files/{nrpe => plugins}/check_ceph_osd | 0 roles/ceph/files/{nrpe => plugins}/check_chrony | 0 roles/ceph/files/{nrpe => plugins}/check_smartdisk.sh | 0 roles/ceph/tasks/nrpe.yml | 6 +++--- 6 files changed, 3 insertions(+), 3 deletions(-) rename roles/ceph/files/{nrpe.cfg => nrpe.d/95-ceph.cfg} (100%) rename roles/ceph/files/{nrpe => plugins}/check_ceph_mon (100%) rename roles/ceph/files/{nrpe => plugins}/check_ceph_osd (100%) rename roles/ceph/files/{nrpe => plugins}/check_chrony (100%) rename roles/ceph/files/{nrpe => plugins}/check_smartdisk.sh (100%) diff --git a/roles/ceph/files/nrpe.cfg b/roles/ceph/files/nrpe.d/95-ceph.cfg similarity index 100% rename from roles/ceph/files/nrpe.cfg rename to roles/ceph/files/nrpe.d/95-ceph.cfg diff --git a/roles/ceph/files/nrpe/check_ceph_mon b/roles/ceph/files/plugins/check_ceph_mon similarity index 100% rename from roles/ceph/files/nrpe/check_ceph_mon rename to roles/ceph/files/plugins/check_ceph_mon diff --git a/roles/ceph/files/nrpe/check_ceph_osd b/roles/ceph/files/plugins/check_ceph_osd similarity index 100% rename from roles/ceph/files/nrpe/check_ceph_osd rename to roles/ceph/files/plugins/check_ceph_osd diff --git a/roles/ceph/files/nrpe/check_chrony b/roles/ceph/files/plugins/check_chrony similarity index 100% rename from roles/ceph/files/nrpe/check_chrony rename to roles/ceph/files/plugins/check_chrony diff --git a/roles/ceph/files/nrpe/check_smartdisk.sh b/roles/ceph/files/plugins/check_smartdisk.sh similarity index 100% rename from roles/ceph/files/nrpe/check_smartdisk.sh rename to roles/ceph/files/plugins/check_smartdisk.sh diff --git a/roles/ceph/tasks/nrpe.yml b/roles/ceph/tasks/nrpe.yml index c94df0a..2b87bbe 100644 --- a/roles/ceph/tasks/nrpe.yml +++ b/roles/ceph/tasks/nrpe.yml @@ -1,14 +1,14 @@ - name: Set NRPE Ceph configuration copy: - src: nrpe.cfg - dest: /etc/nagios/nrpe.d/95-ceph.cfg + src: nrpe.d/95-ceph.cfg + dest: /etc/nagios/nrpe.d/ owner: root group: root mode: u=rw,g=r,o=r notify: restart-nrpe - name: Copy Ceph NRPE plugins copy: - src: nrpe/ + src: plugins/ dest: /etc/nagios/plugins/ owner: root group: root From d7a1de2f5cc0f52b5c11fcc81181d60c3bacac21 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 17 Mar 2025 17:51:25 +0100 Subject: [PATCH 13/34] refs #8776 PVE and Ceph monitoring --- roles/pve/tasks/nrpe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/pve/tasks/nrpe.yml b/roles/pve/tasks/nrpe.yml index f242d49..4d70576 100644 --- a/roles/pve/tasks/nrpe.yml +++ b/roles/pve/tasks/nrpe.yml @@ -1,4 +1,4 @@ -- name: Install base packages +- name: Install PVE NRPE base packages apt: name: - libxml-simple-perl From 93586290dbc4cbfcf99f2b509f84e0fc2c1fa813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Wed, 26 Mar 2025 13:15:41 +0100 Subject: [PATCH 14/34] dns: refs #8552 - Main approche - before final named.conf --- playbooks/ns.yml | 6 +++ roles/ns/defaults/main.yml | 28 ++++++++++ roles/ns/files/delete.ns | 14 +++++ roles/ns/files/gen-key.sh | 10 ++++ roles/ns/files/isp1.ns | 16 ++++++ roles/ns/files/isp2.ns | 16 ++++++ roles/ns/files/switch-isp.sh | 23 ++++++++ roles/ns/files/sync-conf | 27 ++++++++++ roles/ns/handlers/main.yml | 4 ++ roles/ns/tasks/main.yml | 2 + roles/ns/tasks/ns.yml | 34 ++++++++++++ roles/ns/templates/certbot.key | 4 ++ roles/ns/templates/dhcp.key | 4 ++ roles/ns/templates/lan.key | 4 ++ roles/ns/templates/named.conf.local.j2 | 20 +++++++ roles/ns/templates/named.conf.master.j2 | 72 +++++++++++++++++++++++++ roles/ns/templates/named.conf.slave.j2 | 68 +++++++++++++++++++++++ roles/ns/templates/rndc.key | 4 ++ roles/ns/templates/wan.key | 4 ++ 19 files changed, 360 insertions(+) create mode 100644 playbooks/ns.yml create mode 100644 roles/ns/defaults/main.yml create mode 100644 roles/ns/files/delete.ns create mode 100644 roles/ns/files/gen-key.sh create mode 100644 roles/ns/files/isp1.ns create mode 100644 roles/ns/files/isp2.ns create mode 100644 roles/ns/files/switch-isp.sh create mode 100644 roles/ns/files/sync-conf create mode 100644 roles/ns/handlers/main.yml create mode 100644 roles/ns/tasks/main.yml create mode 100644 roles/ns/tasks/ns.yml create mode 100644 roles/ns/templates/certbot.key create mode 100644 roles/ns/templates/dhcp.key create mode 100644 roles/ns/templates/lan.key create mode 100644 roles/ns/templates/named.conf.local.j2 create mode 100644 roles/ns/templates/named.conf.master.j2 create mode 100644 roles/ns/templates/named.conf.slave.j2 create mode 100644 roles/ns/templates/rndc.key create mode 100644 roles/ns/templates/wan.key diff --git a/playbooks/ns.yml b/playbooks/ns.yml new file mode 100644 index 0000000..e29c441 --- /dev/null +++ b/playbooks/ns.yml @@ -0,0 +1,6 @@ +- name: Configure bind9 name server + hosts: all + tasks: + - name: Configure services to install in the server + import_role: + name: ns \ No newline at end of file diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml new file mode 100644 index 0000000..f0be459 --- /dev/null +++ b/roles/ns/defaults/main.yml @@ -0,0 +1,28 @@ +bind_packages: + - bind9 + - bind9-dnsutils + - bind9-host + - bind9-libs + - bind9-utils + - dnsutils + - python3-pycurl +bind_config_templates: + - { src: 'named.conf.master.j2', dest: '/etc/bind/named.conf.master', mode: 'u=rw,g=r,o=r' } + - { src: 'named.conf.local.j2', dest: '/etc/bind/named.conf.local', mode: 'u=rw,g=r,o=r' } + - { src: 'named.conf.slave.j2', dest: '/etc/bind/named.conf.slave', mode: 'u=rw,g=r,o=r' } + - { src: 'certbot.key', dest: '/etc/bind/keys/certbot.key', mode: 'u=rw,g=r,o=' } + - { src: 'lan.key', dest: '/etc/bind/keys/lan.key', mode: 'u=rw,g=r,o=' } + - { src: 'wan.key', dest: '/etc/bind/keys/wan.key', mode: 'u=rw,g=r,o=' } + - { src: 'rndc.key', dest: '/etc/bind/rndc.key', mode: 'u=rw,g=r,o=' } + - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' } +directory: + - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } + - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' } + - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' } +required_files: + - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } + - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } + - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } + - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } + - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } + - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' } diff --git a/roles/ns/files/delete.ns b/roles/ns/files/delete.ns new file mode 100644 index 0000000..cbb97ad --- /dev/null +++ b/roles/ns/files/delete.ns @@ -0,0 +1,14 @@ +update delete verdnatura.es A +update delete kube-proxy.verdnatura.es A +update delete smtp.verdnatura.es A +update delete imap.verdnatura.es A +update delete autodiscover.verdnatura.es A +update delete time1.verdnatura.es A +update delete time2.verdnatura.es A +update delete dc-ip01.verdnatura.es A +update delete dc-ip02.verdnatura.es A +update delete dc-ip03.verdnatura.es A +update delete dc-ip04.verdnatura.es A +update delete mailgw1.verdnatura.es A +update delete mailgw2.verdnatura.es A +send diff --git a/roles/ns/files/gen-key.sh b/roles/ns/files/gen-key.sh new file mode 100644 index 0000000..4c062a2 --- /dev/null +++ b/roles/ns/files/gen-key.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +KEYNAME=$1 + +if [ -z "$KEYNAME" ]; then + echo "Usage: $0 <key_name>" + exit 1 +fi + +tsig-keygen -a hmac-sha512 "$KEYNAME" diff --git a/roles/ns/files/isp1.ns b/roles/ns/files/isp1.ns new file mode 100644 index 0000000..e9ed9b6 --- /dev/null +++ b/roles/ns/files/isp1.ns @@ -0,0 +1,16 @@ +update add verdnatura.es 3600 A 89.6.245.230 +update add verdnatura.es 3600 A 89.6.245.231 +update add kube-proxy.verdnatura.es 3600 A 89.6.245.230 +update add kube-proxy.verdnatura.es 3600 A 89.6.245.231 +update add smtp.verdnatura.es 3600 A 89.6.245.230 +update add imap.verdnatura.es 3600 A 89.6.245.230 +update add autodiscover.verdnatura.es 3600 A 89.6.245.230 +update add time1.verdnatura.es 3600 A 89.6.245.230 +update add time2.verdnatura.es 3600 A 89.6.245.230 +update add dc-ip01.verdnatura.es 3600 A 89.6.245.228 +update add dc-ip02.verdnatura.es 3600 A 89.6.245.229 +update add dc-ip03.verdnatura.es 3600 A 89.6.245.230 +update add dc-ip04.verdnatura.es 3600 A 89.6.245.231 +update add mailgw1.verdnatura.es 43200 A 89.6.245.232 +update add mailgw2.verdnatura.es 43200 A 89.6.245.233 +send diff --git a/roles/ns/files/isp2.ns b/roles/ns/files/isp2.ns new file mode 100644 index 0000000..f5bd027 --- /dev/null +++ b/roles/ns/files/isp2.ns @@ -0,0 +1,16 @@ +update add verdnatura.es 3600 A 195.77.191.180 +update add verdnatura.es 3600 A 195.77.191.181 +update add kube-proxy.verdnatura.es 3600 A 195.77.191.180 +update add kube-proxy.verdnatura.es 3600 A 195.77.191.181 +update add smtp.verdnatura.es 3600 A 195.77.191.180 +update add imap.verdnatura.es 3600 A 195.77.191.180 +update add autodiscover.verdnatura.es 3600 A 195.77.191.180 +update add time1.verdnatura.es 3600 A 195.77.191.180 +update add time2.verdnatura.es 3600 A 195.77.191.180 +update add dc-ip01.verdnatura.es 3600 A 195.77.191.178 +update add dc-ip02.verdnatura.es 3600 A 195.77.191.179 +update add dc-ip03.verdnatura.es 3600 A 195.77.191.180 +update add dc-ip04.verdnatura.es 3600 A 195.77.191.181 +update add mailgw1.verdnatura.es 43200 A 195.77.191.180 +update add mailgw2.verdnatura.es 43200 A 195.77.191.181 +send diff --git a/roles/ns/files/switch-isp.sh b/roles/ns/files/switch-isp.sh new file mode 100644 index 0000000..5444192 --- /dev/null +++ b/roles/ns/files/switch-isp.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +ISP=$1 + +if [ -z "$ISP" ]; then + echo "Usage: $0 <isp_name>" + exit 1 +fi + +KEY_FILE="/etc/bind/keys/wan.key" +NS_DIR="/root/scripts/switch-isp" +ISP_FILE="$NS_DIR/$ISP.ns" + +if [ ! -f "$ISP_FILE" ]; then + echo "ISP file for nsupdate not found: $ISP_FILE" + exit 2 +fi + +echo "Deleting ISP dependent DNS records." +nsupdate -k "$KEY_FILE" "$NS_DIR/delete.ns" + +echo "Registering $ISP DNS records." +nsupdate -k "$KEY_FILE" "$ISP_FILE" diff --git a/roles/ns/files/sync-conf b/roles/ns/files/sync-conf new file mode 100644 index 0000000..a3b947b --- /dev/null +++ b/roles/ns/files/sync-conf @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ "$(hostname)" = "ns1" ]; then + partner=root@ns2.servers.dc.verdnatura.es +else + partner=root@ns1.servers.dc.verdnatura.es +fi + +confDir=/etc/bind + +echo "Restarting service." +service bind9 restart + +if [ $? -eq "0" ]; then + echo "Synchronizing partner configuration." + + scp "$confDir/named.conf.local" $partner:$confDir + scp "$confDir/named.conf.master" $partner:$confDir + scp "$confDir/named.conf.slave" $partner:$confDir + + ssh "$partner" rm -rf "$confDir/keys" + ssh "$partner" mkdir "$confDir/keys" + scp -r "$confDir/keys/"* $partner:"$confDir/keys" + + echo "Restarting partner service." + ssh $partner service bind9 restart +fi diff --git a/roles/ns/handlers/main.yml b/roles/ns/handlers/main.yml new file mode 100644 index 0000000..840d041 --- /dev/null +++ b/roles/ns/handlers/main.yml @@ -0,0 +1,4 @@ +- name: restart-dns + systemd: + name: bind9 + state: restarted \ No newline at end of file diff --git a/roles/ns/tasks/main.yml b/roles/ns/tasks/main.yml new file mode 100644 index 0000000..0948ab4 --- /dev/null +++ b/roles/ns/tasks/main.yml @@ -0,0 +1,2 @@ +- import_tasks: ns.yml + tags: ns diff --git a/roles/ns/tasks/ns.yml b/roles/ns/tasks/ns.yml new file mode 100644 index 0000000..7943efa --- /dev/null +++ b/roles/ns/tasks/ns.yml @@ -0,0 +1,34 @@ +- name: Update apt cache + apt: + update_cache: yes +- name: Install bind package requirements + apt: + name: "{{ bind_packages }}" + state: present + install_recommends: no +- name: Create directory + file: + path: "{{ item.path }}" + state: directory + owner: "{{ item.group }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + loop: "{{ directory }}" +- name: Copy required files and scripts + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: "{{ item.group }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + loop: "{{ required_files }}" +- name: Deploy BIND config templates + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: bind + mode: "{{ item.mode }}" + loop: "{{ bind_config_templates }}" + notify: restart-dns + diff --git a/roles/ns/templates/certbot.key b/roles/ns/templates/certbot.key new file mode 100644 index 0000000..6e9f1b9 --- /dev/null +++ b/roles/ns/templates/certbot.key @@ -0,0 +1,4 @@ +key "certbot" { + algorithm hmac-sha512; + secret "{{ lookup(passbolt, 'certbot', folder_parent_id=passbolt_folder).password }}"; +}; diff --git a/roles/ns/templates/dhcp.key b/roles/ns/templates/dhcp.key new file mode 100644 index 0000000..a732d1e --- /dev/null +++ b/roles/ns/templates/dhcp.key @@ -0,0 +1,4 @@ +key "dhcp" { + algorithm hmac-sha512; + secret "{{ lookup(passbolt, 'dhcpkey', folder_parent_id=passbolt_folder).password }}"; +}; diff --git a/roles/ns/templates/lan.key b/roles/ns/templates/lan.key new file mode 100644 index 0000000..c99ad33 --- /dev/null +++ b/roles/ns/templates/lan.key @@ -0,0 +1,4 @@ +key "lan" { + algorithm hmac-sha512; + secret "{{ lookup(passbolt, 'lankey', folder_parent_id=passbolt_folder).password }}"; +}; diff --git a/roles/ns/templates/named.conf.local.j2 b/roles/ns/templates/named.conf.local.j2 new file mode 100644 index 0000000..45ac42f --- /dev/null +++ b/roles/ns/templates/named.conf.local.j2 @@ -0,0 +1,20 @@ +{% for path in bind_key_includes %} +include "{{ path }}"; +{% endfor %} + +{% for server in bind_bogus_servers %} +server {{ server }} { bogus yes; }; +{% endfor %} + +{% for acl_name, networks in bind_acls.items() %} +acl {{ acl_name }} { + {% for net in networks %} + {{ net }}; + {% endfor %} +}; +{% endfor %} + +controls { + inet {{ bind_controls["inet"] }} allow { {{ bind_controls["allow"] | join('; ') }}; } keys { {{ bind_controls["keys"] | map('regex_replace', '^(.*)$', '"\\1"') | join('; ') }}; }; +}; + diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2 new file mode 100644 index 0000000..814c5a8 --- /dev/null +++ b/roles/ns/templates/named.conf.master.j2 @@ -0,0 +1,72 @@ +options { + directory "{{ bind_cache_dir }}"; + max-cache-size {{ bind_max_cache_size }}; + auth-nxdomain no; + listen-on-v6 { none; }; + version "{{ bind_version }}"; + allow-update { none; }; + blackhole { rfc5735; }; + + allow-transfer { + {% for ip in bind_allow_transfer %} + {{ ip }}; + {% endfor %} + }; +}; + +view "lan" { + match-clients { + {% for item in bind_match_clients_lan %} + {{ item }}; + {% endfor %} + }; + + recursion yes; + allow-recursion { any; }; + empty-zones-enable yes; + notify {{ bind_notify_lan | default('yes') }}; + + include "/etc/bind/named.conf.default-zones"; + + {% for zone in bind_zones.lan %} + zone "{{ zone.name }}" { + type master; + forwarders {}; + allow-update { key {{ zone.key }}; }; + file "{{ zone.file }}"; + }; + {% endfor %} +}; + +view "wan" { + match-clients { any; }; + + recursion no; + allow-query-cache { none; }; + empty-zones-enable no; + + notify {{ bind_notify_wan }}; + + also-notify { + {% for entry in bind_also_notify %} + {{ entry.ip }} key {{ entry.key }}; + {% endfor %} + }; + + {% for zone in bind_zones.wan %} + {% if zone.in_view is defined %} + {% for z in zone.in_view %} + zone "{{ z }}" { + in-view "lan"; + }; + {% endfor %} + {% else %} + zone "{{ zone.name }}" { + type master; + forwarders {}; + allow-update { key {{ zone.key }}; }; + file "{{ zone.file }}"; + }; + {% endif %} + {% endfor %} +}; diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2 new file mode 100644 index 0000000..b4078b1 --- /dev/null +++ b/roles/ns/templates/named.conf.slave.j2 @@ -0,0 +1,68 @@ +options { + directory "{{ bind_cache_dir }}"; + + max-cache-size {{ bind_max_cache_size }}; + auth-nxdomain no; + listen-on-v6 { none; }; + version "{{ bind_version }}"; + allow-update { none; }; + blackhole { rfc5735; }; + + notify {{ bind_slave_notify }}; + allow-transfer { none; }; + masterfile-format {{ bind_masterfile_format }}; +}; + +masters master-ips { +{% for ip in bind_slave_masters %} + {{ ip }}; +{% endfor %} +}; + +view "lan" { + match-clients { + {% for item in bind_match_clients_lan %} + {{ item }}; + {% endfor %} + }; + + recursion yes; + allow-recursion { any; }; + empty-zones-enable yes; + + include "/etc/bind/named.conf.default-zones"; + + {% for zone in bind_zones.lan %} + zone "{{ zone.name }}" { + type slave; + masters { master-ips; }; + forwarders {}; + file "{{ zone.file }}"; + }; + {% endfor %} +}; + +view "wan" { + match-clients { any; }; + + recursion no; + allow-query-cache { none; }; + empty-zones-enable no; + + {% for zone in bind_zones.wan %} + {% if zone.in_view is defined %} + {% for z in zone.in_view %} + zone "{{ z }}" { + in-view "lan"; + }; + {% endfor %} + {% else %} + zone "{{ zone.name }}" { + type slave; + masters { {{ bind_slave_masters | join('; ') }} key {{ zone.key }}; }; + forwarders {}; + file "{{ zone.file }}"; + }; + {% endif %} + {% endfor %} +}; diff --git a/roles/ns/templates/rndc.key b/roles/ns/templates/rndc.key new file mode 100644 index 0000000..40d9600 --- /dev/null +++ b/roles/ns/templates/rndc.key @@ -0,0 +1,4 @@ +key "rndc-key" { + algorithm hmac-md5; + secret "{{ lookup(passbolt, 'rndc-key', folder_parent_id=passbolt_folder).password }}"; +}; diff --git a/roles/ns/templates/wan.key b/roles/ns/templates/wan.key new file mode 100644 index 0000000..daa655c --- /dev/null +++ b/roles/ns/templates/wan.key @@ -0,0 +1,4 @@ +key "wan-key" { + algorithm hmac-md5; + secret "{{ lookup(passbolt, 'wan-key', folder_parent_id=passbolt_folder).password }}"; +}; From 24d8864cf65d16f4fac3ef25883a256972fa687a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Wed, 26 Mar 2025 14:49:26 +0100 Subject: [PATCH 15/34] dns: refs #8552 - Main approche - final touch --- roles/ns/defaults/main.yml | 5 ++-- roles/ns/templates/named.conf.j2 | 16 ++++++++++ roles/ns/templates/named.conf.local.j2 | 40 +++++++++++++++---------- roles/ns/templates/named.conf.master.j2 | 24 +++++++++------ roles/ns/templates/named.conf.slave.j2 | 25 +++++++++------- 5 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 roles/ns/templates/named.conf.j2 diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml index f0be459..528f6c4 100644 --- a/roles/ns/defaults/main.yml +++ b/roles/ns/defaults/main.yml @@ -7,6 +7,7 @@ bind_packages: - dnsutils - python3-pycurl bind_config_templates: + - { src: 'named.conf.j2', dest: '/etc/bind/named.conf', mode: 'u=rw,g=r,o=r' } - { src: 'named.conf.master.j2', dest: '/etc/bind/named.conf.master', mode: 'u=rw,g=r,o=r' } - { src: 'named.conf.local.j2', dest: '/etc/bind/named.conf.local', mode: 'u=rw,g=r,o=r' } - { src: 'named.conf.slave.j2', dest: '/etc/bind/named.conf.slave', mode: 'u=rw,g=r,o=r' } @@ -17,8 +18,8 @@ bind_config_templates: - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' } directory: - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' } - - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' } + - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } + - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } required_files: - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } diff --git a/roles/ns/templates/named.conf.j2 b/roles/ns/templates/named.conf.j2 new file mode 100644 index 0000000..32e13f7 --- /dev/null +++ b/roles/ns/templates/named.conf.j2 @@ -0,0 +1,16 @@ +// This is the primary configuration file for the BIND DNS server named. +// +// Please read /usr/share/doc/bind9/README.Debian.gz for information on the +// structure of BIND configuration files in Debian, *BEFORE* you customize +// this configuration file. +// +// If you are just adding zones, please do that in /etc/bind/named.conf.local + +#include "/etc/bind/named.conf.options"; +include "/etc/bind/named.conf.local"; +{% if bind_is_master %} +include "/etc/bind/named.conf.master"; +{% else %} +include "/etc/bind/named.conf.slave"; +{% endif %} +#include "/etc/bind/named.conf.default-zones"; diff --git a/roles/ns/templates/named.conf.local.j2 b/roles/ns/templates/named.conf.local.j2 index 45ac42f..09a8935 100644 --- a/roles/ns/templates/named.conf.local.j2 +++ b/roles/ns/templates/named.conf.local.j2 @@ -1,20 +1,30 @@ -{% for path in bind_key_includes %} -include "{{ path }}"; -{% endfor %} +include "/etc/bind/rndc.key"; +include "/etc/bind/keys/wan.key"; +include "/etc/bind/keys/lan.key"; +include "/etc/bind/keys/certbot.key"; +include "/etc/bind/keys/dhcp.key"; -{% for server in bind_bogus_servers %} -server {{ server }} { bogus yes; }; -{% endfor %} +server fe80::/16 { bogus yes; }; -{% for acl_name, networks in bind_acls.items() %} -acl {{ acl_name }} { - {% for net in networks %} - {{ net }}; - {% endfor %} -}; -{% endfor %} +acl lan { + 10.0.0.0/8; + 172.16.0.0/12; + 192.168.0.0/16; + }; + +acl rfc5735 { + 0.0.0.0/8; + 169.254.0.0/16; + 192.0.0.0/24; + 192.0.2.0/24; + 192.88.99.0/24; + 198.18.0.0/15; + 198.51.100.0/24; + 203.0.113.0/24; + 224.0.0.0/4; + 240.0.0.0/4; + }; controls { - inet {{ bind_controls["inet"] }} allow { {{ bind_controls["allow"] | join('; ') }}; } keys { {{ bind_controls["keys"] | map('regex_replace', '^(.*)$', '"\\1"') | join('; ') }}; }; + inet * allow { localhost; lan; } keys { "rndc-key"; }; }; - diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2 index 814c5a8..dc63834 100644 --- a/roles/ns/templates/named.conf.master.j2 +++ b/roles/ns/templates/named.conf.master.j2 @@ -1,9 +1,9 @@ options { - directory "{{ bind_cache_dir }}"; - max-cache-size {{ bind_max_cache_size }}; + directory "/var/cache/bind"; + max-cache-size 500m; auth-nxdomain no; listen-on-v6 { none; }; - version "{{ bind_version }}"; + version "DNS"; allow-update { none; }; blackhole { rfc5735; }; @@ -15,16 +15,22 @@ options { }; view "lan" { - match-clients { - {% for item in bind_match_clients_lan %} + match-clients { + {% for item in key_match_clients_lan_master if item.startswith("!key") %} + {{ item }}; + {% endfor %} +{% for item in acl_match_clients %} {{ item }}; - {% endfor %} - }; + {% endfor %} +{% for item in key_match_clients_lan_master if not item.startswith("!key") %} + {{ item }}; + {% endfor %} +}; recursion yes; allow-recursion { any; }; empty-zones-enable yes; - notify {{ bind_notify_lan | default('yes') }}; + notify yes; include "/etc/bind/named.conf.default-zones"; @@ -45,7 +51,7 @@ view "wan" { allow-query-cache { none; }; empty-zones-enable no; - notify {{ bind_notify_wan }}; + notify explicit; also-notify { {% for entry in bind_also_notify %} diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2 index b4078b1..c1142a1 100644 --- a/roles/ns/templates/named.conf.slave.j2 +++ b/roles/ns/templates/named.conf.slave.j2 @@ -1,16 +1,15 @@ options { - directory "{{ bind_cache_dir }}"; - - max-cache-size {{ bind_max_cache_size }}; + directory "/var/cache/bind"; + max-cache-size 500m; auth-nxdomain no; listen-on-v6 { none; }; - version "{{ bind_version }}"; + version "DNS"; allow-update { none; }; blackhole { rfc5735; }; - notify {{ bind_slave_notify }}; + notify no; allow-transfer { none; }; - masterfile-format {{ bind_masterfile_format }}; + masterfile-format text; }; masters master-ips { @@ -20,11 +19,17 @@ masters master-ips { }; view "lan" { - match-clients { - {% for item in bind_match_clients_lan %} + match-clients { + {% for item in key_match_clients_lan_master if item.startswith("!key") %} + {{ item }}; + {% endfor %} +{% for item in acl_match_clients %} {{ item }}; - {% endfor %} - }; + {% endfor %} +{% for item in key_match_clients_lan_slave if not item.startswith("!key") %} + {{ item }}; + {% endfor %} +}; recursion yes; allow-recursion { any; }; From babf43cfba656526271c105aa08a60151b83a7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Thu, 27 Mar 2025 10:43:50 +0100 Subject: [PATCH 16/34] dns: refs #8552 - Juan Request https://gitea.verdnatura.es/verdnatura/vn-ansible/pulls/79 --- roles/ns/defaults/main.yml | 4 ++-- roles/ns/files/isp1.ns | 16 ---------------- roles/ns/files/isp2.ns | 16 ---------------- roles/ns/templates/isp1.ns | 4 ++++ roles/ns/templates/isp2.ns | 4 ++++ 5 files changed, 10 insertions(+), 34 deletions(-) delete mode 100644 roles/ns/files/isp1.ns delete mode 100644 roles/ns/files/isp2.ns create mode 100644 roles/ns/templates/isp1.ns create mode 100644 roles/ns/templates/isp2.ns diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml index 528f6c4..daeccf4 100644 --- a/roles/ns/defaults/main.yml +++ b/roles/ns/defaults/main.yml @@ -16,14 +16,14 @@ bind_config_templates: - { src: 'wan.key', dest: '/etc/bind/keys/wan.key', mode: 'u=rw,g=r,o=' } - { src: 'rndc.key', dest: '/etc/bind/rndc.key', mode: 'u=rw,g=r,o=' } - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' } + - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' } + - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' } directory: - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } required_files: - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } - - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } - - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' } diff --git a/roles/ns/files/isp1.ns b/roles/ns/files/isp1.ns deleted file mode 100644 index e9ed9b6..0000000 --- a/roles/ns/files/isp1.ns +++ /dev/null @@ -1,16 +0,0 @@ -update add verdnatura.es 3600 A 89.6.245.230 -update add verdnatura.es 3600 A 89.6.245.231 -update add kube-proxy.verdnatura.es 3600 A 89.6.245.230 -update add kube-proxy.verdnatura.es 3600 A 89.6.245.231 -update add smtp.verdnatura.es 3600 A 89.6.245.230 -update add imap.verdnatura.es 3600 A 89.6.245.230 -update add autodiscover.verdnatura.es 3600 A 89.6.245.230 -update add time1.verdnatura.es 3600 A 89.6.245.230 -update add time2.verdnatura.es 3600 A 89.6.245.230 -update add dc-ip01.verdnatura.es 3600 A 89.6.245.228 -update add dc-ip02.verdnatura.es 3600 A 89.6.245.229 -update add dc-ip03.verdnatura.es 3600 A 89.6.245.230 -update add dc-ip04.verdnatura.es 3600 A 89.6.245.231 -update add mailgw1.verdnatura.es 43200 A 89.6.245.232 -update add mailgw2.verdnatura.es 43200 A 89.6.245.233 -send diff --git a/roles/ns/files/isp2.ns b/roles/ns/files/isp2.ns deleted file mode 100644 index f5bd027..0000000 --- a/roles/ns/files/isp2.ns +++ /dev/null @@ -1,16 +0,0 @@ -update add verdnatura.es 3600 A 195.77.191.180 -update add verdnatura.es 3600 A 195.77.191.181 -update add kube-proxy.verdnatura.es 3600 A 195.77.191.180 -update add kube-proxy.verdnatura.es 3600 A 195.77.191.181 -update add smtp.verdnatura.es 3600 A 195.77.191.180 -update add imap.verdnatura.es 3600 A 195.77.191.180 -update add autodiscover.verdnatura.es 3600 A 195.77.191.180 -update add time1.verdnatura.es 3600 A 195.77.191.180 -update add time2.verdnatura.es 3600 A 195.77.191.180 -update add dc-ip01.verdnatura.es 3600 A 195.77.191.178 -update add dc-ip02.verdnatura.es 3600 A 195.77.191.179 -update add dc-ip03.verdnatura.es 3600 A 195.77.191.180 -update add dc-ip04.verdnatura.es 3600 A 195.77.191.181 -update add mailgw1.verdnatura.es 43200 A 195.77.191.180 -update add mailgw2.verdnatura.es 43200 A 195.77.191.181 -send diff --git a/roles/ns/templates/isp1.ns b/roles/ns/templates/isp1.ns new file mode 100644 index 0000000..57eba65 --- /dev/null +++ b/roles/ns/templates/isp1.ns @@ -0,0 +1,4 @@ +{% for record in dns_records_main %} +update add {{ record.name.ljust(30) }} {{ record.ttl }} A {{ record.ip }} +{% endfor %} +send diff --git a/roles/ns/templates/isp2.ns b/roles/ns/templates/isp2.ns new file mode 100644 index 0000000..e89b38e --- /dev/null +++ b/roles/ns/templates/isp2.ns @@ -0,0 +1,4 @@ +{% for record in dns_records_secondary %} +update add {{ record.name.ljust(30) }} {{ record.ttl }} A {{ record.ip }} +{% endfor %} +send From 651ee7edf620b7a7eba914da4884b1a05a2ce1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 28 Mar 2025 14:12:42 +0100 Subject: [PATCH 17/34] dns: refs #8552 - disable ipv6 and move delete ns file --- roles/ns/defaults/main.yml | 2 +- roles/ns/files/delete.ns | 14 -------------- roles/ns/tasks/ns.yml | 7 +++++++ roles/ns/templates/delete.ns | 4 ++++ roles/ns/templates/named.conf.master.j2 | 6 ++++++ roles/ns/templates/named.conf.slave.j2 | 11 ++++++++--- 6 files changed, 26 insertions(+), 18 deletions(-) delete mode 100644 roles/ns/files/delete.ns create mode 100644 roles/ns/templates/delete.ns diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml index daeccf4..4b1213f 100644 --- a/roles/ns/defaults/main.yml +++ b/roles/ns/defaults/main.yml @@ -18,12 +18,12 @@ bind_config_templates: - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' } - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' } - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' } + - { src: 'delete.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' } directory: - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' } required_files: - - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' } - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' } - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' } diff --git a/roles/ns/files/delete.ns b/roles/ns/files/delete.ns deleted file mode 100644 index cbb97ad..0000000 --- a/roles/ns/files/delete.ns +++ /dev/null @@ -1,14 +0,0 @@ -update delete verdnatura.es A -update delete kube-proxy.verdnatura.es A -update delete smtp.verdnatura.es A -update delete imap.verdnatura.es A -update delete autodiscover.verdnatura.es A -update delete time1.verdnatura.es A -update delete time2.verdnatura.es A -update delete dc-ip01.verdnatura.es A -update delete dc-ip02.verdnatura.es A -update delete dc-ip03.verdnatura.es A -update delete dc-ip04.verdnatura.es A -update delete mailgw1.verdnatura.es A -update delete mailgw2.verdnatura.es A -send diff --git a/roles/ns/tasks/ns.yml b/roles/ns/tasks/ns.yml index 7943efa..beb5d3a 100644 --- a/roles/ns/tasks/ns.yml +++ b/roles/ns/tasks/ns.yml @@ -6,6 +6,13 @@ name: "{{ bind_packages }}" state: present install_recommends: no +- name: Ensure BIND9 starts with IPv4 only (-4) + lineinfile: + path: /etc/default/named + regexp: '^OPTIONS=' + line: 'OPTIONS="-u bind -4"' + backrefs: yes + notify: restart-dns - name: Create directory file: path: "{{ item.path }}" diff --git a/roles/ns/templates/delete.ns b/roles/ns/templates/delete.ns new file mode 100644 index 0000000..899f5cc --- /dev/null +++ b/roles/ns/templates/delete.ns @@ -0,0 +1,4 @@ +{% for record in dns_records_delete %} +update delete {{ record.name.ljust(30) }} A +{% endfor %} +send diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2 index dc63834..e923cc5 100644 --- a/roles/ns/templates/named.conf.master.j2 +++ b/roles/ns/templates/named.conf.master.j2 @@ -27,6 +27,12 @@ view "lan" { {% endfor %} }; + plugin query "filter-aaaa.so" { + filter-aaaa-on-v4 yes; + filter-aaaa-on-v6 yes; + filter-aaaa { any; }; + }; + recursion yes; allow-recursion { any; }; empty-zones-enable yes; diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2 index c1142a1..cfe4ade 100644 --- a/roles/ns/templates/named.conf.slave.j2 +++ b/roles/ns/templates/named.conf.slave.j2 @@ -20,17 +20,22 @@ masters master-ips { view "lan" { match-clients { - {% for item in key_match_clients_lan_master if item.startswith("!key") %} + {%- for item in key_match_clients_lan_master if item.startswith("!key") -%} {{ item }}; {% endfor %} -{% for item in acl_match_clients %} +{%- for item in acl_match_clients -%} {{ item }}; {% endfor %} -{% for item in key_match_clients_lan_slave if not item.startswith("!key") %} +{%- for item in key_match_clients_lan_slave if not item.startswith("!key") -%} {{ item }}; {% endfor %} }; + plugin query "filter-aaaa.so" { + filter-aaaa-on-v4 yes; + filter-aaaa-on-v6 yes; + filter-aaaa { any; }; + }; recursion yes; allow-recursion { any; }; empty-zones-enable yes; From 7ee760f5068da7a1acbae9ea6a0537ddb917e53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 28 Mar 2025 15:49:06 +0100 Subject: [PATCH 18/34] dns: refs #8552 - indentation jinga master.conf --- roles/ns/templates/named.conf.master.j2 | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2 index e923cc5..3d0a2e0 100644 --- a/roles/ns/templates/named.conf.master.j2 +++ b/roles/ns/templates/named.conf.master.j2 @@ -15,17 +15,17 @@ options { }; view "lan" { - match-clients { - {% for item in key_match_clients_lan_master if item.startswith("!key") %} - {{ item }}; - {% endfor %} + match-clients { +{% for item in key_match_clients_lan_master if item.startswith("!key") %} + {{ item }}; +{% endfor %} {% for item in acl_match_clients %} {{ item }}; - {% endfor %} +{% endfor %} {% for item in key_match_clients_lan_master if not item.startswith("!key") %} {{ item }}; - {% endfor %} -}; +{% endfor %} + }; plugin query "filter-aaaa.so" { filter-aaaa-on-v4 yes; @@ -40,14 +40,14 @@ view "lan" { include "/etc/bind/named.conf.default-zones"; - {% for zone in bind_zones.lan %} +{% for zone in bind_zones.lan %} zone "{{ zone.name }}" { type master; forwarders {}; allow-update { key {{ zone.key }}; }; file "{{ zone.file }}"; }; - {% endfor %} +{% endfor %} }; view "wan" { @@ -60,25 +60,26 @@ view "wan" { notify explicit; also-notify { - {% for entry in bind_also_notify %} +{% for entry in bind_also_notify %} {{ entry.ip }} key {{ entry.key }}; - {% endfor %} +{% endfor %} }; - {% for zone in bind_zones.wan %} - {% if zone.in_view is defined %} - {% for z in zone.in_view %} +{% for zone in bind_zones.wan %} +{% if zone.in_view is defined %} +{% for z in zone.in_view %} zone "{{ z }}" { in-view "lan"; }; - {% endfor %} - {% else %} +{% endfor %} +{% else %} zone "{{ zone.name }}" { type master; forwarders {}; allow-update { key {{ zone.key }}; }; file "{{ zone.file }}"; }; - {% endif %} - {% endfor %} +{% endif %} +{% endfor %} }; + From 0e073c7ba12b7e650cd1ff14f7f66341bf892f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Wed, 2 Apr 2025 11:34:17 +0200 Subject: [PATCH 19/34] vpn: refs #8748 - add conntrack iptables default block --- roles/ipsec/defaults/main.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml index c8b1cd0..a7d3b9d 100644 --- a/roles/ipsec/defaults/main.yml +++ b/roles/ipsec/defaults/main.yml @@ -24,6 +24,13 @@ mangle_block: | -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 COMMIT + *filter + :INPUT ACCEPT [0:0] + :FORWARD ACCEPT [0:0] + :OUTPUT ACCEPT [0:0] + -A FORWARD -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT + -A FORWARD -m conntrack --ctstate INVALID -j LOG --log-prefix "CT INVALID: " + COMMIT config_and_logrotate: - { src: vn.conf, dest: '/etc/strongswan.d/vn.conf' } - { src: charon, dest: '/etc/logrotate.d/charon' } From 3fd2bcd6d0535a37b046701360c63d063b2ea5a3 Mon Sep 17 00:00:00 2001 From: Juan Ferrer <juan@verdnatura.es> Date: Wed, 2 Apr 2025 15:35:47 +0000 Subject: [PATCH 20/34] Update roles/db/templates/conf/z99-local.cnf --- roles/db/templates/conf/z99-local.cnf | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/db/templates/conf/z99-local.cnf b/roles/db/templates/conf/z99-local.cnf index a434496..ad99d65 100644 --- a/roles/db/templates/conf/z99-local.cnf +++ b/roles/db/templates/conf/z99-local.cnf @@ -1,6 +1,7 @@ [mysqld] server-id = {{ serverid }} +#read_only = ON #bind-address = 127.0.0.1 #event-scheduler = OFF #skip-log-bin From 54733ee4fe44b85e8f0522abd844336775ce0fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 4 Apr 2025 13:04:34 +0200 Subject: [PATCH 21/34] db: refs #8140 - add new script to change server rol --- roles/db/files/scripts/role-change.sh | 123 ++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 roles/db/files/scripts/role-change.sh diff --git a/roles/db/files/scripts/role-change.sh b/roles/db/files/scripts/role-change.sh new file mode 100644 index 0000000..1ba3e0b --- /dev/null +++ b/roles/db/files/scripts/role-change.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# Script to manage MariaDB events during replication role changes. +# +# Author: Xavi Lleó & GhatGPT ® +# Copyright (c) 2025 Verdnatura S.L. All rights reserved. +# Version: 1.4.0 +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <https://www.gnu.org/licenses/>. +# +# Description: +# This script toggles MariaDB events during replication role changes. +# It supports three modes: +# --promote : Re-enable events previously marked SLAVESIDE_DISABLED +# --demote : Disable currently ENABLED events using DISABLE ON SLAVE +# --status : Display current status of all events +# All actions are logged to /var/log/mariadb_admin.log. + +set -euo pipefail + +LOG_FILE="/var/log/mariadb_admin.log" +TIMESTAMP="$(date '+%F %T')" + +log() { + echo "$TIMESTAMP - $1" | tee -a "$LOG_FILE" +} + +disable_scheduler() { + log "Disabling event_scheduler..." + if mysql -e "SET GLOBAL event_scheduler = OFF"; then + log "Successfully disabled event_scheduler" + else + log "ERROR: Failed to disable event_scheduler" + exit 1 + fi +} + +enable_scheduler() { + log "Re-enabling event_scheduler..." + if mysql -e "SET GLOBAL event_scheduler = ON"; then + log "Successfully re-enabled event_scheduler" + else + log "ERROR: Failed to re-enable event_scheduler" + exit 1 + fi +} + +final_status() { + log "Final event_scheduler status:" + mysql --table -e "SHOW VARIABLES LIKE 'event_scheduler';" | tee -a "$LOG_FILE" + + log "Final state of all events:" + mysql --table -e "SELECT db, name, status FROM mysql.event;" | tee -a "$LOG_FILE" +} + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 --promote | --demote | --status" + exit 1 +fi + +MODE="$1" + +case "$MODE" in + --status) + log "Fetching current event_scheduler status:" + mysql --table -e "SHOW VARIABLES LIKE 'event_scheduler';" | tee -a "$LOG_FILE" + + log "Fetching current event status:" + mysql --table -e "SELECT db, name, status FROM mysql.event;" | tee -a "$LOG_FILE" + ;; + + --promote) + disable_scheduler + + log "Promoting: Enabling events with SLAVESIDE_DISABLED status..." + mysql --raw --silent -e "SELECT db, name FROM mysql.event WHERE status = 'SLAVESIDE_DISABLED'" | \ + awk '{ gsub("`", "``", $1); gsub("`", "``", $2); print "`"$1"`.`"$2"`" }' | \ + while read -r event; do + if mysql -e "ALTER EVENT $event ENABLE"; then + log "Enabled event: $event" + else + log "ERROR: Failed to enable event: $event" + fi + done + + enable_scheduler + final_status + ;; + + --demote) + disable_scheduler + + log "Demoting: Disabling events currently ENABLED..." + mysql --raw --silent -e "SELECT db, name FROM mysql.event WHERE status = 'ENABLED'" | \ + awk '{ gsub("`", "``", $1); gsub("`", "``", $2); print "`"$1"`.`"$2"`" }' | \ + while read -r event; do + if mysql -e "ALTER EVENT $event DISABLE ON SLAVE"; then + log "Disabled event: $event" + else + log "ERROR: Failed to disable event: $event" + fi + done + + enable_scheduler + final_status + ;; + + *) + echo "Invalid mode: $MODE" + echo "Usage: $0 --promote | --demote | --status" + exit 1 + ;; +esac From 1f745a1c8c14a27369f576e3c055de221c046726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 4 Apr 2025 13:38:56 +0200 Subject: [PATCH 22/34] db: refs #8140 - clean party --- roles/db/defaults/main.yaml | 3 +-- roles/db/files/scripts/events-demote.sh | 16 ---------------- roles/db/files/scripts/events-promote.sh | 15 --------------- 3 files changed, 1 insertion(+), 33 deletions(-) delete mode 100755 roles/db/files/scripts/events-demote.sh delete mode 100755 roles/db/files/scripts/events-promote.sh diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml index 7dd354f..81e7852 100644 --- a/roles/db/defaults/main.yaml +++ b/roles/db/defaults/main.yaml @@ -29,8 +29,7 @@ required_mariabackup_files_and_scripts: - { src: scripts/check-memory.sh, dest: /root/scripts/check-memory.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/export-privs.sh, dest: /root/scripts/export-privs.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/mysqltuner.pl, dest: /root/scripts/mysqltuner.pl, mode: u=rwx,g=rx,o=rx } - - { src: scripts/events-promote.sh, dest: /root/scripts/events-promote.sh, mode: u=rwx,g=rx,o=rx } - - { src: scripts/events-demote.sh, dest: /root/scripts/events-demote.sh, mode: u=rwx,g=rx,o=rx } + - { src: scripts/role-change.sh, dest: /root/scripts/role-change.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/README.md, dest: /root/scripts/README.md, mode: u=rw,g=r,o=r } - { src: scripts/scheduler-log.sh, dest: /root/scripts/scheduler-log.sh, mode: u=rwx,g=rx,o=rx } configuration_files: diff --git a/roles/db/files/scripts/events-demote.sh b/roles/db/files/scripts/events-demote.sh deleted file mode 100755 index 4856f4f..0000000 --- a/roles/db/files/scripts/events-demote.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -mysql -e "SET GLOBAL event_scheduler = OFF" - -echo "SELECT db, name FROM mysql.event WHERE status = 'ENABLED'" | mysql --raw --silent | \ -awk '{ - gsub("`", "``", $1); - gsub("`", "``", $2); - print "`"$1"`.`"$2"`"; -}' | \ -while read event; do - mysql -e "ALTER EVENT $event DISABLE ON SLAVE" -done - -mysql -e "SET GLOBAL event_scheduler = ON" - diff --git a/roles/db/files/scripts/events-promote.sh b/roles/db/files/scripts/events-promote.sh deleted file mode 100755 index d4b5b9f..0000000 --- a/roles/db/files/scripts/events-promote.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -mysql -e "SET GLOBAL event_scheduler = OFF" - -echo "SELECT db, name FROM mysql.event WHERE status = 'SLAVESIDE_DISABLED'" | mysql --raw --silent | \ -awk '{ - gsub("`", "``", $1); - gsub("`", "``", $2); - print "`"$1"`.`"$2"`"; -}' | \ -while read event; do - mysql -e "ALTER EVENT $event ENABLE" -done - -mysql -e "SET GLOBAL event_scheduler = ON" From 7a925b67c4454c824ed08b463afd6b319c74a50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 4 Apr 2025 14:27:11 +0200 Subject: [PATCH 23/34] db: refs #8140 - clean festival --- roles/db/defaults/main.yaml | 5 +++++ roles/db/tasks/mariadb.yml | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml index 81e7852..61f6c73 100644 --- a/roles/db/defaults/main.yaml +++ b/roles/db/defaults/main.yaml @@ -44,3 +44,8 @@ downloads: - url: https://repo.percona.com/apt/percona-release_latest.generic_all.deb dest: /tmp/percona-release_latest.generic_all.deb mode: u=rw,g=r,o=r +clean_config_and_scripts: + - { path: /root/scripts/events-promote.sh } + - { path: /root/scripts/events-demote.sh } + - { path: /root/scripts/promote-master.sh } + - { path: /root/scripts/promote-slave.sh } \ No newline at end of file diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml index 806125a..db09ac0 100644 --- a/roles/db/tasks/mariadb.yml +++ b/roles/db/tasks/mariadb.yml @@ -122,3 +122,9 @@ - name: Mount all filesystems from /etc/fstab command: mount -a when: fstab.changed + +- name: Clean old configs or scripts + file: + path: "{{ item.path }}" + state: absent + loop: "{{ clean_config_and_scripts }}" From 772a3f3fa2a23fdae93ddaf3e4fdacd850049f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es> Date: Fri, 4 Apr 2025 15:09:03 +0200 Subject: [PATCH 24/34] db: refs #8140 - variable local-backup --- roles/db/templates/mariabackup/apply.config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh index c74176b..190a00a 100755 --- a/roles/db/templates/mariabackup/apply.config.sh +++ b/roles/db/templates/mariabackup/apply.config.sh @@ -1,7 +1,7 @@ #!/bin/bash # Bacula directory for restore -baculaDir=/mnt/mysqldata/bacula-restore +baculaDir=/mnt/local-backup/bacula-restore # Database branch name dbBranch={{ db.branch }} From a0a0026f6ef7bc029662fdf36473e8f119d37c18 Mon Sep 17 00:00:00 2001 From: Juan Ferrer <juan@verdnatura.es> Date: Fri, 4 Apr 2025 16:25:15 +0000 Subject: [PATCH 25/34] Update roles/db/templates/conf/z99-local.cnf --- roles/db/templates/conf/z99-local.cnf | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/db/templates/conf/z99-local.cnf b/roles/db/templates/conf/z99-local.cnf index ad99d65..390df19 100644 --- a/roles/db/templates/conf/z99-local.cnf +++ b/roles/db/templates/conf/z99-local.cnf @@ -1,6 +1,7 @@ [mysqld] server-id = {{ serverid }} +#log_slave_updates = ON #read_only = ON #bind-address = 127.0.0.1 #event-scheduler = OFF From 5e5aed051f1c5e2cfc705da59b0f7f1cbf9a40db Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Sun, 6 Apr 2025 06:46:43 +0200 Subject: [PATCH 26/34] fix: refs #8140 fix call to role-change script --- roles/db/files/mariabackup/bacula-after.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh index 6857ed7..6cbaa68 100755 --- a/roles/db/files/mariabackup/bacula-after.sh +++ b/roles/db/files/mariabackup/bacula-after.sh @@ -46,7 +46,7 @@ fi echo "Promoting Events." -"/root/scripts/events-promote.sh" +"/root/scripts/role-change.sh --promote" for node in "${dbClusterSiblings[@]}"; do ssh root@$node service mysql start From 9c2ff170a0f2307a55e31e7f3304cd36bc4088a6 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Sun, 6 Apr 2025 18:20:30 +0200 Subject: [PATCH 27/34] feat: refs #8869 differential backups --- roles/db/defaults/main.yaml | 12 +-- roles/db/files/mariabackup/bacula-after.sh | 4 +- roles/db/files/mariabackup/bacula-before.sh | 31 ------- roles/db/files/mariabackup/config.sh | 2 + roles/db/files/mariabackup/inc-backup.sh | 38 -------- roles/db/files/mariabackup/make-backup.sh | 89 +++++++++++++++++++ roles/db/files/mariabackup/restore-backup.sh | 2 +- roles/db/tasks/mariadb.yml | 2 +- .../db/templates/mariabackup/apply.config.sh | 3 - roles/db/templates/mariabackup/my.cnf | 4 +- 10 files changed, 104 insertions(+), 83 deletions(-) delete mode 100644 roles/db/files/mariabackup/bacula-before.sh delete mode 100644 roles/db/files/mariabackup/inc-backup.sh create mode 100644 roles/db/files/mariabackup/make-backup.sh diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml index 61f6c73..4828592 100644 --- a/roles/db/defaults/main.yaml +++ b/roles/db/defaults/main.yaml @@ -22,7 +22,7 @@ required_directories: - { path: /etc/systemd/system/mariadb.service.d, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' } required_mariabackup_files_and_scripts: - { src: mysql-flush.sh, dest: /etc/qemu/fsfreeze-hook.d/mysql-flush.sh, mode: u=rwx,g=rx,o=rx } - - { src: mariabackup/bacula-before.sh, dest: /root/mariabackup/bacula-before.sh, mode: u=rwx,g=rx,o=rx } + - { src: mariabackup/make-backup.sh, dest: /root/mariabackup/make-backup.sh, mode: u=rwx,g=rx,o=rx } - { src: mariabackup/config.sh, dest: /root/mariabackup/config.sh, mode: u=rwx,g=rx,o=x } - { src: mariabackup/inc-backup.sh, dest: /root/mariabackup/inc-backup.sh, mode: u=rwx,g=rx,o=rx } - { src: mariabackup/restore-backup.sh, dest: /root/mariabackup/restore-backup.sh, mode: u=rwx,g=rx,o=rx } @@ -45,7 +45,9 @@ downloads: dest: /tmp/percona-release_latest.generic_all.deb mode: u=rw,g=r,o=r clean_config_and_scripts: - - { path: /root/scripts/events-promote.sh } - - { path: /root/scripts/events-demote.sh } - - { path: /root/scripts/promote-master.sh } - - { path: /root/scripts/promote-slave.sh } \ No newline at end of file + - /root/scripts/events-promote.sh + - /root/scripts/events-demote.sh + - /root/scripts/promote-master.sh + - /root/scripts/promote-slave.sh + - /root/scripts/inc-backup.sh + - /root/scripts/bacula-before.sh \ No newline at end of file diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh index 6cbaa68..d38e399 100755 --- a/roles/db/files/mariabackup/bacula-after.sh +++ b/roles/db/files/mariabackup/bacula-after.sh @@ -5,12 +5,12 @@ myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" . "$myDir/config.sh" . "$myDir/apply.config.sh" -pattern="$baculaDir/mnt/local-backup/*_full.gz" +pattern="$backupDir/*_full.gz" files=($pattern) backupFile="${files[0]}" "$myDir/restore-backup.sh" "$backupFile" -rm -r "$baculaDir" +rm -r "$backupDir/"* if [[ "${#dbClusterSiblings[@]}" -gt "0" ]]; then for node in "${dbClusterSiblings[@]}"; do diff --git a/roles/db/files/mariabackup/bacula-before.sh b/roles/db/files/mariabackup/bacula-before.sh deleted file mode 100644 index f4723a3..0000000 --- a/roles/db/files/mariabackup/bacula-before.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# https://mariadb.com/kb/en/mariabackup/ -set -e - -myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -. "$myDir/config.sh" - -todayDir="$(date +%Y-%m-%d)" -backupName="${todayDir}_$(date +"%H-%M")_full" -backupFile="$backupDir/$backupName.gz" - -if [ -d "$backupDir" ]; then - rm -rf "$backupDir/"* -fi - -ulimit -n 8192 -mariabackup \ - --defaults-extra-file="$myDir/my.cnf" \ - --backup \ - --extra-lsndir="$backupDir/$backupName" \ - --history="$todayDir" \ - --stream=xbstream \ - --parallel=4 \ - 2>> "$logFile" \ - | pigz -p 12 \ - > "$backupFile" - -if [ $? != "0" ]; then - echo "An error ocurred during backup, please take a look at log file: $logFile" - exit 1 -fi diff --git a/roles/db/files/mariabackup/config.sh b/roles/db/files/mariabackup/config.sh index bb61fd8..3aacc00 100644 --- a/roles/db/files/mariabackup/config.sh +++ b/roles/db/files/mariabackup/config.sh @@ -18,3 +18,5 @@ restoreDir=/mnt/mysqldata/mysql-restore # Directory of MySQL data dataDir=/mnt/mysqldata/mysql +# Number of procs created by pigz +pigzProcs=12 diff --git a/roles/db/files/mariabackup/inc-backup.sh b/roles/db/files/mariabackup/inc-backup.sh deleted file mode 100644 index c6d6e91..0000000 --- a/roles/db/files/mariabackup/inc-backup.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/ -set -e - -myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -. "$myDir/config.sh" - -todayDir="$(date +%Y-%m-%d)" -todayPath="$historyDir/$todayDir" - -pattern="$todayPath/${todayDir}_??-??_full.xb.gz.enc" -files=($pattern) -backupFile="${files[0]}" -backupBase=$(basename -- "$backupFile") -backupName="${backupBase%%.*}" - -incrementalName="${todayDir}_$(date +"%H-%M")_incremental" -incrementalFile="$backupDir/${incrementalName}.xb.gz.enc" - -ulimit -n 24098 -mariabackup \ - --defaults-extra-file="$myDir/my.cnf" \ - --backup \ - --incremental-basedir="$backupDir/$backupName" \ - --extra-lsndir="$backupDir/$incrementalName" \ - --incremental-history-name="$todayDir" \ - 2>> "$logFile" \ - | gzip \ - | openssl enc -aes-256-cbc -pbkdf2 -kfile "$myDir/xbcrypt.key" \ - > "$incrementalFile" - -if [ $? != "0" ]; then - echo "An error ocurred during backup, please take a look at log file: $logFile" - exit 1 -fi - -cp "$incrementalFile" "$todayPath" -cp -r "$backupDir/$incrementalName" "$todayPath" diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh new file mode 100644 index 0000000..db9d503 --- /dev/null +++ b/roles/db/files/mariabackup/make-backup.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# https://mariadb.com/kb/en/mariabackup/ +# https://mariadb.com/kb/en/mariabackup-options/ +# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/ +# https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/ +set -e + +myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$myDir/config.sh" + +level=$1 +historyName=$2 +todayDir="$(date +%Y-%m-%d)" + +usage() { + echo "Usage: $0 <Full|Differential|Incremental> [historyName]" + exit 1 +} +backup_error() { + echo "An error ocurred during backup, please take a look at log file: $logFile" + exit 2 +} + +if [ -z "$level" ]; then + usage +fi + +case "$level" in + Full) + backupName="${todayDir}_$(date +"%H-%M")_full" + backupFile="$backupDir/$backupName.gz" + + if [ -d "$backupDir" ]; then + rm -rf "$backupDir/"* + fi + if [ -z "$historyName" ]; then + historyName=$backupName + fi + + ulimit -n 8192 + mariabackup \ + --defaults-extra-file="$myDir/my.cnf" \ + --backup \ + --history="$historyName" \ + --extra-lsndir="$backupDir/$backupName" \ + --slave-info \ + --safe-slave-backup \ + 2>> "$logFile" \ + | pigz -p "$pigzProcs" \ + > "$backupFile" + + if [ $? != "0" ]; then + backup_error + fi + ;; + Differential|Incremental) + pattern="$backupDir/${todayDir}_??-??_full.gz" + files=($pattern) + backupFile="${files[0]}" + backupBase=$(basename -- "$backupFile") + backupName="${backupBase%%.*}" + + incrementalName="${backupName%%_full}_diff_$(date +"%H-%M")" + incrementalFile="$backupDir/${incrementalName}.gz" + + if [ -z "$historyName" ]; then + historyName=$incrementalName + fi + + ulimit -n 24098 + mariabackup \ + --defaults-extra-file="$myDir/my.cnf" \ + --backup \ + --history="$historyName" \ + --extra-lsndir="$backupDir/$incrementalName" \ + --incremental-basedir="$backupDir/$backupName" \ + --slave-info \ + --safe-slave-backup \ + 2>> "$logFile" \ + | pigz -p "$pigzProcs" \ + > "$incrementalFile" + + if [ $? != "0" ]; then + backup_error + fi + ;; + *) + usage +esac diff --git a/roles/db/files/mariabackup/restore-backup.sh b/roles/db/files/mariabackup/restore-backup.sh index 73d06e2..ecf2fd7 100644 --- a/roles/db/files/mariabackup/restore-backup.sh +++ b/roles/db/files/mariabackup/restore-backup.sh @@ -28,7 +28,7 @@ mkdir -p "$restoreDir" echo "$(formatted_date)" echo "Decompresing backup." pigz --decompress --processes 4 --stdout "$backupFile" \ - | mbstream --extract --parallel=4 --directory="$restoreDir" + | mbstream --extract --parallel=6 --directory="$restoreDir" echo "Preparing backup." mariabackup \ diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml index db09ac0..4b204cd 100644 --- a/roles/db/tasks/mariadb.yml +++ b/roles/db/tasks/mariadb.yml @@ -125,6 +125,6 @@ - name: Clean old configs or scripts file: - path: "{{ item.path }}" + path: "{{ item }}" state: absent loop: "{{ clean_config_and_scripts }}" diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh index 190a00a..ca604ae 100755 --- a/roles/db/templates/mariabackup/apply.config.sh +++ b/roles/db/templates/mariabackup/apply.config.sh @@ -1,8 +1,5 @@ #!/bin/bash -# Bacula directory for restore -baculaDir=/mnt/local-backup/bacula-restore - # Database branch name dbBranch={{ db.branch }} diff --git a/roles/db/templates/mariabackup/my.cnf b/roles/db/templates/mariabackup/my.cnf index 7037bef..47207c2 100644 --- a/roles/db/templates/mariabackup/my.cnf +++ b/roles/db/templates/mariabackup/my.cnf @@ -2,6 +2,6 @@ host = localhost user = mariabackup password = {{ lookup(passbolt, 'mariabackup', folder_parent_id=passbolt_folder).password }} -use-memory = 1G -parallel = 4 +use-memory = 4G +parallel = 8 stream = xbstream From 61da00046e1c1a12a9e2fc3bbf1a1715c897eeb2 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Sun, 6 Apr 2025 21:12:32 +0200 Subject: [PATCH 28/34] feat: refs #8869 remove previous differential backup --- roles/db/files/mariabackup/make-backup.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh index db9d503..da5ea55 100644 --- a/roles/db/files/mariabackup/make-backup.sh +++ b/roles/db/files/mariabackup/make-backup.sh @@ -10,7 +10,7 @@ myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" level=$1 historyName=$2 -todayDir="$(date +%Y-%m-%d)" +curDate="$(date +%Y-%m-%d)" usage() { echo "Usage: $0 <Full|Differential|Incremental> [historyName]" @@ -27,7 +27,7 @@ fi case "$level" in Full) - backupName="${todayDir}_$(date +"%H-%M")_full" + backupName="${curDate}_$(date +"%H-%M")_full" backupFile="$backupDir/$backupName.gz" if [ -d "$backupDir" ]; then @@ -54,7 +54,7 @@ case "$level" in fi ;; Differential|Incremental) - pattern="$backupDir/${todayDir}_??-??_full.gz" + pattern="$backupDir/${curDate}_??-??_full.gz" files=($pattern) backupFile="${files[0]}" backupBase=$(basename -- "$backupFile") @@ -63,6 +63,9 @@ case "$level" in incrementalName="${backupName%%_full}_diff_$(date +"%H-%M")" incrementalFile="$backupDir/${incrementalName}.gz" + if [ "$level" = "Differential" ]; then + rm -rf "$backupDir/${curDate}_"??-??_diff_??-?? + fi if [ -z "$historyName" ]; then historyName=$incrementalName fi From 5a700d188fa7617edd7f75347bfcb9d6580f9fc9 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Sun, 6 Apr 2025 21:13:27 +0200 Subject: [PATCH 29/34] feat: refs #8869 remove previous differential backup --- roles/db/files/mariabackup/make-backup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh index da5ea55..47bad8f 100644 --- a/roles/db/files/mariabackup/make-backup.sh +++ b/roles/db/files/mariabackup/make-backup.sh @@ -89,4 +89,5 @@ case "$level" in ;; *) usage + ;; esac From d2f8dc69c2fb3aa477f3313f8ab750158ba2ccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Lle=C3=B3?= <xavi@verdnatura.es> Date: Mon, 7 Apr 2025 07:00:53 +0000 Subject: [PATCH 30/34] Actualizar roles/db/files/mariabackup/make-backup.sh Nota absurda --- roles/db/files/mariabackup/make-backup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh index 47bad8f..3a5e7b1 100644 --- a/roles/db/files/mariabackup/make-backup.sh +++ b/roles/db/files/mariabackup/make-backup.sh @@ -3,6 +3,7 @@ # https://mariadb.com/kb/en/mariabackup-options/ # https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/ # https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/ +# Pudo escribir en el fichero, pero no puedo escribir en gitea para dejarte una nota, valgame dior set -e myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" From a73ec2b34018a7d9086eaeb38b53351bd51c4740 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 7 Apr 2025 09:41:43 +0200 Subject: [PATCH 31/34] doc: refs #8869 make-backup.sh doc added --- roles/db/files/mariabackup/make-backup.sh | 35 ++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh index 47bad8f..91e1963 100644 --- a/roles/db/files/mariabackup/make-backup.sh +++ b/roles/db/files/mariabackup/make-backup.sh @@ -1,8 +1,35 @@ #!/bin/bash -# https://mariadb.com/kb/en/mariabackup/ -# https://mariadb.com/kb/en/mariabackup-options/ -# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/ -# https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/ +# +# Author: Juan Ferrer +# Copyright (c) 2025 Verdnatura S.L. All rights reserved. +# Version: 0.0.2 +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <https://www.gnu.org/licenses/>. +# +# Backups the MariaDB database, it supports three modes: +# - Full: Full backup of all DB data, deletes all previous backups. +# - Differential: Backup changes since the last full backup, deletes all +# previous differential backups. +# - Incremental: Backup changes since the last full backup, same as +# differential, but does not erase any previous backups. +# +# References: +# - https://mariadb.com/kb/en/mariabackup/ +# - https://mariadb.com/kb/en/mariabackup-options/ +# - https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/ +# - https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/ +# set -e myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" From e31b19f78bb0eed11cba54ae09a8b8f9543a7898 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 7 Apr 2025 10:20:25 +0200 Subject: [PATCH 32/34] fix: refs #8140 remove role-change script quotes --- roles/db/files/mariabackup/bacula-after.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh index d38e399..32bb6b9 100755 --- a/roles/db/files/mariabackup/bacula-after.sh +++ b/roles/db/files/mariabackup/bacula-after.sh @@ -46,7 +46,7 @@ fi echo "Promoting Events." -"/root/scripts/role-change.sh --promote" +/root/scripts/role-change.sh --promote for node in "${dbClusterSiblings[@]}"; do ssh root@$node service mysql start From 3dae1cb8dccb912c6a2110f3f8bd7aaa4e071c86 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio <juan@verdnatura.es> Date: Mon, 7 Apr 2025 10:52:48 +0200 Subject: [PATCH 33/34] doc: refs #8869 remove deprecated inc-backup script ref --- roles/db/defaults/main.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml index 4828592..6820f53 100644 --- a/roles/db/defaults/main.yaml +++ b/roles/db/defaults/main.yaml @@ -24,7 +24,6 @@ required_mariabackup_files_and_scripts: - { src: mysql-flush.sh, dest: /etc/qemu/fsfreeze-hook.d/mysql-flush.sh, mode: u=rwx,g=rx,o=rx } - { src: mariabackup/make-backup.sh, dest: /root/mariabackup/make-backup.sh, mode: u=rwx,g=rx,o=rx } - { src: mariabackup/config.sh, dest: /root/mariabackup/config.sh, mode: u=rwx,g=rx,o=x } - - { src: mariabackup/inc-backup.sh, dest: /root/mariabackup/inc-backup.sh, mode: u=rwx,g=rx,o=rx } - { src: mariabackup/restore-backup.sh, dest: /root/mariabackup/restore-backup.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/check-memory.sh, dest: /root/scripts/check-memory.sh, mode: u=rwx,g=rx,o=rx } - { src: scripts/export-privs.sh, dest: /root/scripts/export-privs.sh, mode: u=rwx,g=rx,o=rx } From 15c0cf2cce251f46d3926ec5518af145932433a6 Mon Sep 17 00:00:00 2001 From: Juan Ferrer <juan@verdnatura.es> Date: Mon, 7 Apr 2025 13:24:14 +0000 Subject: [PATCH 34/34] refs #8869 fix delete differential glob pattern --- roles/db/files/mariabackup/make-backup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh index 91e1963..41d4c37 100644 --- a/roles/db/files/mariabackup/make-backup.sh +++ b/roles/db/files/mariabackup/make-backup.sh @@ -91,7 +91,7 @@ case "$level" in incrementalFile="$backupDir/${incrementalName}.gz" if [ "$level" = "Differential" ]; then - rm -rf "$backupDir/${curDate}_"??-??_diff_??-?? + rm -rf "$backupDir/${curDate}_"??-??_diff_??-??{,.gz} fi if [ -z "$historyName" ]; then historyName=$incrementalName