diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..69f9b3f --- /dev/null +++ b/.drone.yml @@ -0,0 +1,30 @@ +--- +kind: pipeline +type: docker +name: default + +steps: + - name: lint markdown files + image: pipelinecomponents/remark-lint:latest + commands: + - "remark --no-stdout --color --use preset-lint-recommended ." + + - name: lint yaml files + image: pipelinecomponents/yamllint:latest + commands: + - "yamllint ." + + - name: lint ansible scripts + image: pipelinecomponents/ansible-lint:latest + commands: + - > + find . -maxdepth 1 -name '*.yml' + | sort + | grep -v '.drone.yml' + | xargs ansible-playbook --syntax-check --list-tasks + + - > + find . -maxdepth 1 -name '*.yml' + | sort + | grep -v '.drone.yml' + | xargs ansible-lint diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..825c09b --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,75 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [6.8.0] - 2020-10-23 + +### Added + +- Add a PF tag to be able to skip that part when rerunning EvoBSD +- Add a doas authorization for NRPE check_ipsecctl_critiques + +### Changed + +- The task mail.yml replace the former boot/reboot message only if it is untouched +- Replace the variable used to set the email address in etc-git role - now using inventory_hostname +- Not checking syspatch when OpenBSD <= 6.1 +- Amend fstab file adding noatime option to each entrie +- Import evocheck v.6.7.7 +- Comment NRPE checks that cannot be used as is + +### Fixed + +- Add the creation of the NRPE plugins directory in nagios-nrpe role +- Add collectd doas rights in the base role to avoid broking anything if EvoBSD is rerun without the collectd role included +- Do not add the motd cron if the same line is already there but uncommented +- Amend fstab entries only when the filesystem is ffs + +## [6.7.2] - 2020-10-13 + +### Added + +- Now handling deletion of evobackup crontab (replaced by daily.local cron) +- Customize fstab with noexec and softdep +- Collectd role + +### Changed + +- Improve rc.local file configuration +- Update evocheck to version 6.7.5 +- Hide default daily output mail content (VERBOSESTATUS=0) +- Add deletion of old log files in the OSPF role + +### Fixed + +- Fix duplicate evobackup cron if the entry is uncommented in daily.local + +## [6.7.1] - 2020-09-10 + +### Added + +- Add completions functions in root's profile dotfile +- Add check_connections_state.sh NRPE plugin +- Add an evocheck role +- Add stricter ssh and doas access +- Add an openvpn role +- Add an OpenBGPd NRPE plugin +- Add ospf and bgp roles +- Add an unbound NRPE check since it is part of the base system +- Add a motd-carp-state.sh script that checks the carp state and generates the /etc/motd file + +### Changed + +- Disable sndiod since it is not required on serveurs +- Replace sudo with doas for script executions +- Update evomaintenance version to 0.6.3 +- Disable mouse function in vim configuration +- Drop openup since syspatch can apply stable patches now +- Update evobackup script +- Rewrite newsyslog configuration +- Drop postgresql-client package since evomaintenance use an API now diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c50afd9..d8caf95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,17 +2,17 @@ Contributions are welcome, as long as they respect our current workflow: -1. The master branch is only for releases. Once properly tested, -the dev branch can be merged, the release tagged and a tar archive -created. +1. The master branch is only for releases. Once properly tested, + the dev branch can be merged, the release tagged and a tar archive + created. -2. The dev branch should never be commited to directly, unless -you're updating the CHANGELOG file. +2. The dev branch should never be commited to directly, unless + you're updating the CHANGELOG file. -3. Use feature branches for anything else, once they've passed all -CI tests and have been reviewed by other contributors through a -pull request, they may be merged into the dev branch. +3. Use feature branches for anything else, once they've passed all + CI test, lints and have been reviewed by other contributors through a + pull request, they may be merged into the dev branch. Open issues liberally, but please review closed and opened issues -for duplicates before hand. \ No newline at end of file +for duplicates before hand. diff --git a/LICENSE b/LICENSE index 8c08e49..f8ce407 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Evolix +Copyright (c) 2020 Evolix Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2d65ac2..c3f3b2c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# EvoBSD 1.0 +# EvoBSD 6.8.0 EvoBSD is an ansible project used for customising OpenBSD hosts used by Evolix. @@ -9,16 +9,24 @@ used by Evolix. Put your public key in the remote root's autorized_keys (/root/.ssh/authorized_keys) -1 - Install ansible's prerequisites +1. Install ansible's prerequisites ``` ansible-playbook prerequisite.yml -CDi hosts -l HOSTNAME ``` -2 - Run it +2. Run it + +First use (become_method: su) : ``` -ansible-playbook evolixisation.yml --ask-vault-pass -CDKi hosts -l HOSTNAME +ansible-playbook evolixisation.yml --ask-vault-pass -CDki hosts -u root -l HOSTNAME +``` + +Subsequent use (become_method: sudo) : + +``` +ansible-playbook evolixisation.yml --ask-vault-pass -CDKi hosts --skip-tags pf -l HOSTNAME ``` ### Testing @@ -26,100 +34,40 @@ ansible-playbook evolixisation.yml --ask-vault-pass -CDKi hosts -l HOSTNAME Changes can be tested by using [Packer](https://www.packer.io/) and [vmm(4)](https://man.openbsd.org/vmm.4) : -* This process depends on the [Go](https://golang.org/) programming language. +* This process depends on the [Go](https://golang.org/) programming language. + +**Packages** + +Needing a Golang eco system and some basics + +```` +pkg_add go-- packer-- git-- +```` + +* We use the [packer-builder-openbsd-vmm](https://github.com/double-p/packer-builder-openbsd-vmm) project to bridge Packer and vmm(4) + +```` +git clone https://github.com/double-p/packer-builder-openbsd-vmm.git +```` + +**builds** + +Set ````GOPATH```` (default: ~/go), if the 1.4GB dependencies wont fit. + +```` +make +make install +```` + +* You need your unprivileged user to be able to run vmctl(8) through doas(1) ``` -# pkg_add go packer +echo "permit nopass myunprivilegeduser as root cmd /usr/sbin/vmctl" >> /etc/doas.conf ``` -* We use the [packer-builder-vmm](https://github.com/prep/packer-builder-vmm) project to bridge Packer and vmm(4) +See packer-builder-openbsd-vmm/examples/README.examples for further instructions -``` -$ go get -u github.com/prep/packer-builder-vmm/cmd/packer-builder-vmm -``` - -* Here is an example build file - -``` -$ vim openbsd.json -``` - - { - "description": "OpenBSD installation on vmm(4)", - - "variables": { - "hostname": "evobsd", - "domain": "example.com", - - "password": "evolix" - }, - - "builders": [ - { - "type": "vmm", - "vm_name": "evobsd", - "disk_size": "2G", - "format": "qcow2", - "mem_size": "1024M", - - "iso_urls": ["downloads/install64.fs", "https://ftp.nluug.nl/pub/OpenBSD/6.4/amd64/install64.fs"], - "iso_checksum": "7aa4344cb39efbf67300f97ac7eec005b607e8c19d4e31a0a593a8ee2b7136e4", - "iso_checksum_type": "sha256", - - "boot_wait": "10s", - "boot_command": [ - "S", - - "cat <disklabel.template", - "/ 1G-* 100%", - "EOF", - - "cat <install.conf", - "System hostname = {{user `hostname`}}", - "DNS domain name = {{user `domain`}}", - "Password for root account = {{user `password`}}", - "Do you expect to run the X Window System = no", - "Setup a user = no", - "Which disk is the root disk = sd1", - "Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout = c", - "URL to autopartitioning template for disklabel = file://disklabel.template", - "Location of sets = disk", - "Is the disk partition already mounted = no", - "Set name(s) = -bsd.rd", - "Set name(s) = done", - "Directory does not contain SHA256.sig. Continue without verification = yes", - "What timezone are you in = Europe/Paris", - "EOF", - - "install -af install.conf", - "", - - "/sbin/halt -p" - ] - } - ] - } - - -* You need your unprivileged user to be able to run vmctl(8) through doas(1) - -``` -# echo "permit nopass myunprivilegeduser as root cmd /usr/sbin/vmctl" >> /etc/doas.conf -``` - -* Build the virtual machine - -``` -$ packer build openbsd.json -``` - -* Start it - -``` -doas vmctl start evobsd -cL -d output-vmm/evobsd.qcow2 -``` - -* Enable NAT on your host machine +* Enable NAT on your host machine ``` pass out on em0 inet from tap0:network to any nat-to (em0) diff --git a/evolixisation.yml b/evolixisation.yml index 3895536..8d17372 100644 --- a/evolixisation.yml +++ b/evolixisation.yml @@ -1,5 +1,9 @@ +# yamllint disable rule:line-length # Playbook command -# ansible-playbook evolixisation.yml --ask-vault-pass -CDKi hosts -l HOSTNAME +# First use (become_method: su) : +# ansible-playbook evolixisation.yml --ask-vault-pass -CDki hosts -u root -l HOSTNAME +# Subsequent use (become_method: sudo) : +# ansible-playbook evolixisation.yml --ask-vault-pass -CDKi hosts --skip-tags pf -l HOSTNAME --- - name: Evolixisation of an OpenBSD system @@ -7,11 +11,13 @@ become: true become_user: root become_method: sudo + # become_method: su vars_files: - vars/main.yml -# - vars/secrets.yml + - vars/secrets.yml + - vars/openbsd-secret.yml roles: - etc-git @@ -20,14 +26,20 @@ - pf - accounts - nagios-nrpe + - evocheck - post-install + # - openvpn + # - ospf + # - bgp + # - { role: collectd, collectd_server: "127.0.0.1" } post_tasks: - include: "tasks/commit_etc_git.yml" vars: commit_message: "Ansible - Evolixisation" + - include_role: + name: evocheck + tasks_from: exec.yml - environment: - PKG_PATH: "http://ftp.openbsd.org/pub/OpenBSD/{{ ansible_distribution_version }}/packages/{{ ansible_architecture }}/" - -# vim:ft=ansible +# environment: +# PKG_PATH: "http://ftp.openbsd.org/pub/OpenBSD/{{ ansible_distribution_version }}/packages/{{ ansible_architecture }}/" diff --git a/hosts b/hosts index 680aa87..8abb464 100644 --- a/hosts +++ b/hosts @@ -2,4 +2,4 @@ foo.example.com [openbsd:vars] -ansible_python_interpreter=/usr/local/bin/python2.7 +ansible_python_interpreter=/usr/local/bin/python3 diff --git a/prerequisite.yml b/prerequisite.yml index 4695566..f5a76f2 100644 --- a/prerequisite.yml +++ b/prerequisite.yml @@ -2,15 +2,16 @@ # ansible-playbook prerequisite.yml -CDi hosts -l HOSTNAME --- - - hosts: all - become: yes - become_method: su - user: root - gather_facts: no +- hosts: all + become: true + become_method: su + user: root + gather_facts: false - tasks: + tasks: - name: Install ansible's prerequisite - raw: export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/$(uname -r)/packages/$(uname -p)/; pkg_add -z python-2 + # yamllint disable-line rule:line-length + raw: export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/$(uname -r)/packages/$(uname -p)/; pkg_add -z python-3 # vim:ft=ansible diff --git a/roles/accounts/handlers/main.yml b/roles/accounts/handlers/main.yml new file mode 100644 index 0000000..5d24c46 --- /dev/null +++ b/roles/accounts/handlers/main.yml @@ -0,0 +1,6 @@ +--- + +- name: reload sshd + service: + name: sshd + state: reloaded diff --git a/roles/accounts/tasks/main.yml b/roles/accounts/tasks/main.yml index 0524374..1b097c3 100644 --- a/roles/accounts/tasks/main.yml +++ b/roles/accounts/tasks/main.yml @@ -1,7 +1,85 @@ --- +- name: "Create {{ evobsd_ssh_group }} group" + group: + name: "{{ evobsd_ssh_group }}" + system: true + +- name: "Create {{ evobsd_sudo_group }} group" + group: + name: "{{ evobsd_sudo_group }}" + system: true + - name: Create user accounts include: user.yml vars: user: "{{ item.value }}" - with_dict: "{{ evolinux_users }}" - when: evolinux_users != {} \ No newline at end of file + with_dict: "{{ evolix_users }}" + when: evolix_users != {} + +- name: verify AllowGroups directive + command: "grep -E '^AllowGroups' /etc/ssh/sshd_config" + changed_when: false + failed_when: false + check_mode: false + register: grep_allowgroups_ssh + +- name: verify AllowUsers directive + command: "grep -E '^AllowUsers' /etc/ssh/sshd_config" + changed_when: false + failed_when: false + check_mode: false + register: grep_allowusers_ssh + +- name: "Check that AllowUsers and AllowGroup do not override each other" + assert: + that: "not (grep_allowusers_ssh.rc == 0 and grep_allowgroups_ssh.rc == 0)" + msg: "We can't deal with AllowUsers and AllowGroups at the same time" + +- name: "If AllowGroups is present then use it" + set_fact: + ssh_allowgroups: + "{{ (grep_allowgroups_ssh.rc == 0) or (grep_allowusers_ssh.rc != 0) }}" + +- name: "Add AllowGroups sshd directive with '{{ evobsd_ssh_group }}'" + lineinfile: + dest: /etc/ssh/sshd_config + line: "\nAllowGroups {{ evobsd_ssh_group }}" + insertafter: 'Subsystem' + validate: '/usr/sbin/sshd -t -f %s' + notify: reload sshd + when: + - ssh_allowgroups + - grep_allowgroups_ssh.rc == 1 + +- name: "Append '{{ evobsd_ssh_group }}' to AllowGroups sshd directive" + replace: + dest: /etc/ssh/sshd_config + regexp: '^(AllowGroups ((?!\b{{ evobsd_ssh_group }}\b).)*)$' + replace: '\1 {{ evobsd_ssh_group }}' + validate: '/usr/sbin/sshd -t -f %s' + notify: reload sshd + when: + - ssh_allowgroups + - grep_allowgroups_ssh.rc == 0 + +- name: "Security directives for EvoBSD" + blockinfile: + dest: /etc/ssh/sshd_config + marker: "# {mark} EVOBSD PASSWORD RESTRICTIONS" + block: | + Match Address {{ evolix_trusted_ips | join(',') }} + PasswordAuthentication yes + Match Group {{ evobsd_ssh_group }} + PasswordAuthentication no + insertafter: EOF + validate: '/usr/sbin/sshd -t -f %s' + notify: reload sshd + when: + - evolix_trusted_ips != [] + +- name: "Disable root login" + replace: + dest: /etc/ssh/sshd_config + regexp: '^PermitRootLogin (yes|without-password|prohibit-password)' + replace: "PermitRootLogin no" + notify: reload sshd diff --git a/roles/accounts/tasks/user.yml b/roles/accounts/tasks/user.yml index 109c8a5..b0965f9 100644 --- a/roles/accounts/tasks/user.yml +++ b/roles/accounts/tasks/user.yml @@ -1,16 +1,31 @@ --- +- name: "Group '{{ user.name }}' is present" + group: + state: present + name: "{{ user.name }}" + gid: "{{ user.uid }}" + - name: "User '{{ user.name }}' is present" user: state: present name: '{{ user.name }}' uid: '{{ user.uid }}' password: '{{ user.password_hash_openbsd }}' + group: "{{ user.name }}" groups: wheel shell: /bin/ksh - append: yes + append: true tags: - admin +- name: "Home directory for '{{ user.name }}' is only accesible by owner" + file: + name: '/home/{{ user.name }}' + mode: "0700" + owner: "{{ user.name }}" + group: "{{ user.name }}" + state: directory + - name: "SSH public keys for '{{ user.name }}' are present" authorized_key: user: "{{ user.name }}" @@ -21,4 +36,20 @@ loop_var: ssk_key when: user.ssh_keys is defined tags: - - admin \ No newline at end of file + - admin + +- name: "Add {{ user.name }} to {{ evobsd_ssh_group }} group" + user: + name: "{{ user.name }}" + groups: "{{ evobsd_ssh_group }}" + append: true + tags: + - admin + +- name: "Add {{ user.name }} to {{ evobsd_sudo_group }} group" + user: + name: "{{ user.name }}" + groups: "{{ evobsd_sudo_group }}" + append: true + tags: + - admin diff --git a/roles/base/defaults/main.yml b/roles/base/defaults/main.yml index fe0e8fd..fa6f093 100644 --- a/roles/base/defaults/main.yml +++ b/roles/base/defaults/main.yml @@ -1,19 +1,39 @@ --- ntpd_servers: -- "ntp.evolix.net" + - "ntp.evolix.net" general_alert_email: "root@localhost" general_technical_realm: "example.com" evomaintenance_realm: "example.com" -evomaintenance_alert_email: "evomaintenance-{{ inventory_hostname }}@{{ evomaintenance_realm }}" -evomaintenance_hostname: "{{ inventory_hostname }}.{{ general_technical_realm }}" -evomaintenance_pg_host: Null -evomaintenance_pg_passwd: Null -evomaintenance_pg_db: Null -evomaintenance_pg_table: Null +evomaintenance_alert_email: + "evomaintenance-{{ inventory_hostname }}@{{ evomaintenance_realm }}" +evomaintenance_hostname: + "{{ inventory_hostname }}.{{ general_technical_realm }}" +evomaintenance_pg_host: null +evomaintenance_pg_passwd: null +evomaintenance_pg_db: null +evomaintenance_pg_table: null evomaintenance_from_domain: "{{ evomaintenance_realm }}" evomaintenance_from: "evomaintenance@{{ evomaintenance_from_domain }}" evomaintenance_full_from: "Evomaintenance <{{ evomaintenance_from }}>" evomaintenance_urgency_from: mama.doe@example.com evomaintenance_urgency_tel: "06.00.00.00.00" +evomaintenance_install_vendor: false +evomaintenance_force_config: true +evomaintenance_api_endpoint: null +evomaintenance_api_key: null +evomaintenance_hook_api: true +evomaintenance_hook_db: false +evomaintenance_hook_commit: true +evomaintenance_hook_mail: true +evomaintenance_default_hosts: [] +evomaintenance_additional_hosts: [] +evomaintenance_hosts: > + {{ evomaintenance_default_hosts + | union(evomaintenance_additional_hosts) + | unique }} + +evobsd_path: >- + "$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" +cron_root_path: "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" diff --git a/roles/base/files/evomaintenance.sh b/roles/base/files/evomaintenance.sh index d2a7f52..1cd4ce7 100644 --- a/roles/base/files/evomaintenance.sh +++ b/roles/base/files/evomaintenance.sh @@ -4,82 +4,489 @@ # Dependencies (all OS): git postgresql-client # Dependencies (Debian): sudo -# version 0.4.1 -# Copyright 2007-2018 Evolix +# Copyright 2007-2019 Evolix , Gregory Colpart , +# Jérémy Lecour and others. + +VERSION="0.6.3" + +show_version() { + cat <, + Gregory Colpart , + Jérémy Lecour + and others. + +evomaintenance comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. +END +} + +show_help() { + cat </dev/null + else + mountpoint=$(stat -c '%m' $1) + mount -o remount,rw ${mountpoint} + syslog "Re-mount ${mountpoint} as read-write to commit in repository $1" + fi +} +remount_repository_readonly() { + if [ "$(get_system)" = "OpenBSD" ]; then + partition=$(stat -f '%Sd' $1) + mount -u -r /dev/${partition} 2>/dev/null + else + mountpoint=$(stat -c '%m' $1) + mount -o remount,ro ${mountpoint} 2>/dev/null + syslog "Re-mount ${mountpoint} as read-only after commit to repository $1" + fi +} + +hook_commit() { + if [ -x "${GIT_BIN}" ]; then + # loop on possible directories managed by GIT + for dir in ${GIT_REPOSITORIES}; do + # tell Git where to find the repository and the work tree (no need to `cd …` there) + export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}" + # reset variable used to track if a mount point is readonly + READONLY_ORIG=0 + # If the repository and the work tree exist, try to commit changes + if [ -d "${GIT_DIR}" ] && [ -d "${GIT_WORK_TREE}" ]; then + CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ') + if [ "${CHANGED_LINES}" != "0" ]; then + if [ "${DRY_RUN}" = "1" ]; then + # STATS_SHORT=$(${GIT_BIN} diff --stat | tail -1) + STATS=$(${GIT_BIN} diff --stat | tail -n ${GIT_STATUS_MAX_LINES}) + # GIT_COMMITS_SHORT=$(printf "%s\n%s : %s" "${GIT_COMMITS_SHORT}" "${GIT_DIR}" "${STATS_SHORT}" | sed -e '/^$/d') + GIT_COMMITS=$(printf "%s\n%s\n%s" "${GIT_COMMITS}" "${GIT_DIR}" "${STATS}" | sed -e '/^$/d') + else + # remount mount point read-write if currently readonly + is_repository_readonly ${dir} && { READONLY_ORIG=1; remount_repository_readwrite ${dir}; } + # commit changes + ${GIT_BIN} add --all + ${GIT_BIN} commit --message "${MESSAGE}" --author="${USER} <${USER}@evolix.net>" --quiet + # remount mount point read-only if it was before + test "$READONLY_ORIG" = "1" && remount_repository_readonly ${dir} + # Add the SHA to the log file if something has been committed + SHA=$(${GIT_BIN} rev-parse --short HEAD) + # STATS_SHORT=$(${GIT_BIN} show --stat | tail -1) + STATS=$(${GIT_BIN} show --stat --pretty=format:"" | tail -n ${GIT_STATUS_MAX_LINES}) + # append commit data, without empty lines + # GIT_COMMITS_SHORT=$(printf "%s\n%s : %s –%s" "${GIT_COMMITS_SHORT}" "${GIT_DIR}" "${SHA}" "${STATS_SHORT}" | sed -e '/^$/d') + GIT_COMMITS=$(printf "%s\n%s : %s\n%s" "${GIT_COMMITS}" "${GIT_DIR}" "${SHA}" "${STATS}" | sed -e '/^$/d') + fi + fi + fi + # unset environment variables to prevent accidental influence on other git commands + unset GIT_DIR GIT_WORK_TREE + done + + if [ -n "${GIT_COMMITS}" ]; then + # if [ "${VERBOSE}" = "1" ]; then + printf "\n********** Commits ****************\n%s\n***********************************\n" "${GIT_COMMITS}" + # fi + if [ "${DRY_RUN}" != "1" ]; then + echo "${GIT_COMMITS}" >> "${LOGFILE}" + fi + fi + fi +} + +hook_db() { + SQL_DETAILS=$(echo "${MESSAGE}" | sed "s/'/''/g") + PG_QUERY="INSERT INTO evomaint(hostname,userid,ipaddress,begin_date,end_date,details) VALUES ('${HOSTNAME}','${USER}','${IP}','${BEGIN_DATE}',now(),'${SQL_DETAILS}')" + + if [ "${VERBOSE}" = "1" ]; then + printf "\n********** DB query **************\n%s\n***********************************\n" "${PG_QUERY}" + fi + if [ "${DRY_RUN}" != "1" ] && [ -x "${PSQL_BIN}" ]; then + echo "${PG_QUERY}" | ${PSQL_BIN} "${PGDB}" "${PGTABLE}" -h "${PGHOST}" + fi +} + +hook_api() { + if [ "${VERBOSE}" = "1" ]; then + printf "\n********** API call **************\n" + printf "curl -f -s -S -X POST [REDACTED] -k -F api_key=[REDACTED] -F action=insertEvoMaintenance -F hostname=%s -F userid=%s -F ipaddress=%s -F begin_date=%s -F end_date='now()' -F details=%s" \ + "${HOSTNAME}" "${USER}" "${IP}" "${BEGIN_DATE}" "${MESSAGE}" + printf "\n***********************************\n" + fi + + if [ "${DRY_RUN}" != "1" ] && [ -x "${CURL_BIN}" ]; then + API_RETURN_STATUS=$(curl -f -s -S -X POST \ + "${API_ENDPOINT}" -k \ + -F api_key="${API_KEY}" \ + -F action=insertEvoMaintenance \ + -F hostname="${HOSTNAME}" \ + -F userid="${USER}" \ + -F ipaddress="${IP}" \ + -F begin_date="${BEGIN_DATE}" \ + -F end_date='now()' \ + -F details="${MESSAGE}") + + # either cURL or the API backend can throw an error, otherwise it returns this JSON response + if [ "$API_RETURN_STATUS" = '{"status":"Ok"}' ]; then + echo "API call OK." + else + echo "API call FAILED." + fi + fi +} + +format_mail() { + cat <> "${LOGFILE}" + fi +} + +# load configuration if present. test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf -[ -n "${HOSTNAME}" ] || HOSTNAME=$(get_fqdn) -[ -n "${EVOMAINTMAIL}" ] || EVOMAINTMAIL=evomaintenance-$(echo "${HOSTNAME}" | cut -d- -f1)@${REALM} -[ -n "${LOGFILE}" ] || LOGFILE=/var/log/evomaintenance.log +HOSTNAME=${HOSTNAME:-$(get_fqdn)} +EVOMAINTMAIL=${EVOMAINTMAIL:-"evomaintenance-$(echo "${HOSTNAME}" | cut -d- -f1)@${REALM}"} +LOGFILE=${LOGFILE:-"/var/log/evomaintenance.log"} +HOOK_COMMIT=${HOOK_COMMIT:-"1"} +HOOK_DB=${HOOK_DB:-"0"} +HOOK_API=${HOOK_API:-"1"} +HOOK_MAIL=${HOOK_MAIL:-"1"} +DRY_RUN=${DRY_RUN:-"0"} +VERBOSE=${VERBOSE:-"0"} +AUTO=${AUTO:-"0"} +EVOCHECK=${EVOCHECK:-"0"} +GIT_STATUS_MAX_LINES=${GIT_STATUS_MAX_LINES:-20} +API_ENDPOINT=${API_ENDPOINT:-""} + +# initialize variables +MESSAGE="" +# GIT_COMMITS_SHORT="" +GIT_COMMITS="" + +# Parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case $1 in + -h|-\?|--help) + show_help + exit 0 + ;; + -V|--version) + show_version + exit 0 + ;; + -m|--message) + # message options, with value speparated by space + if [ -n "$2" ]; then + MESSAGE=$2 + shift + else + printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --message=?*) + # message options, with value speparated by = + MESSAGE=${1#*=} + ;; + --message=) + # message options, without value + printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + --no-commit) + # disable commit hook + HOOK_COMMIT=0 + ;; + --commit) + # enable commit hook + HOOK_COMMIT=1 + ;; + --no-db) + # disable DB hook + HOOK_DB=0 + ;; + --db) + # enable DB hook + HOOK_DB=1 + ;; + --no-api) + # disable API hook + HOOK_API=0 + ;; + --api) + # enable API hook + HOOK_API=1 + ;; + --no-mail) + # disable mail hook + HOOK_MAIL=0 + ;; + --mail) + # enable mail hook + HOOK_MAIL=1 + ;; + --no-auto) + # use "manual" mode + AUTO=0 + ;; + --auto) + # use "auto" mode + AUTO=1 + ;; + -n|--dry-run) + # disable actual commands + DRY_RUN=1 + ;; + -v|--verbose) + # print verbose information + VERBOSE=1 + ;; + --) + # End of all options. + shift + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + # Treat unset variables as an error when substituting. # Only after this line, because some config variables might be missing. set -u -REAL_HOSTNAME=$(get_fqdn) -if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then - HOSTNAME_TEXT="${HOSTNAME}" -else - HOSTNAME_TEXT="${HOSTNAME} (${REAL_HOSTNAME})" -fi - +# Gather information +HOSTNAME_TEXT=$(get_complete_hostname) # TTY=$(get_tty) # WHO=$(get_who) IP=$(get_ip) @@ -90,109 +497,281 @@ USER=$(logname) PATH=${PATH}:/usr/sbin SENDMAIL_BIN=$(command -v sendmail) -GIT_BIN=$(command -v git) - -GIT_REPOSITORIES="/etc /etc/bind" - -# git statuses -GIT_STATUSES="" - -if test -x "${GIT_BIN}"; then - # loop on possible directories managed by GIT - for dir in ${GIT_REPOSITORIES}; do - # tell Git where to find the repository and the work tree (no need to `cd …` there) - export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}" - # If the repository and the work tree exist, try to commit changes - if test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then - CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ') - if [ "${CHANGED_LINES}" != "0" ]; then - STATUS=$(${GIT_BIN} status --short | tail -n 10) - # append diff data, without empty lines - GIT_STATUSES=$(printf "%s\n%s\n%s\n" "${GIT_STATUSES}" "${GIT_DIR} (last 10 lines)" "${STATUS}" | sed -e '/^$/d') - fi - fi - # unset environment variables to prevent accidental influence on other git commands - unset GIT_DIR GIT_WORK_TREE - done - if [ -n "${GIT_STATUSES}" ]; then - echo "/!\ There are some uncommited changes. If you proceed, everything will be commited." - echo "${GIT_STATUSES}" - echo "" - fi +readonly SENDMAIL_BIN +if [ "${HOOK_MAIL}" = "1" ] && [ -z "${SENDMAIL_BIN}" ]; then + echo "No \`sendmail' command has been found, can't send mail." 2>&1 fi -# get input from stdin -echo "> Please, enter details about your maintenance" -read TEXTE +GIT_BIN=$(command -v git) +readonly GIT_BIN +if [ "${HOOK_COMMIT}" = "1" ] && [ -z "${GIT_BIN}" ]; then + echo "No \`git' command has been found, can't commit changes" 2>&1 +fi -if [ "${TEXTE}" = "" ]; then +PSQL_BIN=$(command -v psql) +readonly PSQL_BIN +if [ "${HOOK_DB}" = "1" ] && [ -z "${PSQL_BIN}" ]; then + echo "No \`psql' command has been found, can't save to the database." 2>&1 +fi + +CURL_BIN=$(command -v curl) +readonly CURL_BIN +if [ "${HOOK_API}" = "1" ] && [ -z "${CURL_BIN}" ]; then + echo "No \`curl' command has been found, can't call the API." 2>&1 +fi + +LOGGER_BIN=$(command -v logger) +readonly LOGGER_BIN + +if [ "${HOOK_API}" = "1" ] && [ -z "${API_ENDPOINT}" ]; then + echo "No API endpoint specified, can't call the API." 2>&1 +fi + +EVOCHECK_BIN="/usr/share/scripts/evocheck.sh" + +GIT_REPOSITORIES="/etc /etc/bind /usr/share/scripts" + +# initialize variable +GIT_STATUSES="" +# git statuses +if [ -x "${GIT_BIN}" ]; then + # loop on possible directories managed by GIT + for dir in ${GIT_REPOSITORIES}; do + RESULT=$(get_repository_status "${dir}") + if [ -n "${RESULT}" ]; then + # append diff data, without empty lines + GIT_STATUSES=$(printf "%s\n%s\n" "${GIT_STATUSES}" "${RESULT}" | sed -e '/^$/d') + fi + unset RESULT + done +fi + +# find out if running in interactive mode, or not +if [ -t 0 ]; then + INTERACTIVE=1 +else + INTERACTIVE=0 +fi +readonly INTERACTIVE + +if [ "${INTERACTIVE}" = "1" ] && [ "${EVOCHECK}" = "1" ]; then + get_evocheck +fi +if [ -n "${GIT_STATUSES}" ] && [ "${INTERACTIVE}" = "1" ]; then + printf "/!\\\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}" +fi + +if [ -z "${MESSAGE}" ]; then + if [ "${INTERACTIVE}" = "1" ]; then + printf "> Please, enter details about your maintenance:\n" + fi + read -r MESSAGE +fi + +if [ -z "${MESSAGE}" ]; then echo "no value..." exit 1 fi -# recapitulatif -BLOB=$(cat < Press to submit, or to cancel." -read enter - -# write log -echo "----------- $(get_now) ---------------" >> "${LOGFILE}" -echo "${BLOB}" >> "${LOGFILE}" - -# git commit -GIT_COMMITS="" - -if test -x "${GIT_BIN}"; then - # loop on possible directories managed by GIT - for dir in ${GIT_REPOSITORIES}; do - # tell Git where to find the repository and the work tree (no need to `cd …` there) - export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}" - # If the repository and the work tree exist, try to commit changes - if test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then - CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ') - if [ "${CHANGED_LINES}" != "0" ]; then - ${GIT_BIN} add --all - ${GIT_BIN} commit --message "${TEXTE}" --author="${USER} <${USER}@evolix.net>" --quiet - # Add the SHA to the log file if something has been committed - SHA=$(${GIT_BIN} rev-parse --short HEAD) - STATS=$(${GIT_BIN} show --stat | tail -1) - # append commit data, without empty lines - GIT_COMMITS=$(printf "%s\n%s : %s –%s" "${GIT_COMMITS}" "${GIT_DIR}" "${SHA}" "${STATS}" | sed -e '/^$/d') - fi +if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then + if [ "${HOOK_COMMIT}" = "1" ] || [ "${HOOK_MAIL}" = "1" ] || [ "${HOOK_DB}" = "1" ]; then + printf "\nActions to execute:\n" + if [ "${HOOK_COMMIT}" = "1" ]; then + printf "* commit changes in repositories\n" fi - # unset environment variables to prevent accidental influence on other git commands - unset GIT_DIR GIT_WORK_TREE - done - if [ -n "${GIT_COMMITS}" ]; then - echo "${GIT_COMMITS}" >> "${LOGFILE}" + if [ "${HOOK_MAIL}" = "1" ]; then + printf "* send mail to %s\n" "${EVOMAINTMAIL}" + fi + if [ "${HOOK_DB}" = "1" ]; then + printf "* save metadata to the database\n" + fi + if [ "${HOOK_API}" = "1" ]; then + printf "* send metadata to the API\n" + fi + echo "" + + answer="" + while :; do + printf "> Let's continue? [Y,n,i,?] " + read -r answer + case $answer in + [Yy]|"" ) + # force "auto" mode, but keep hooks settings + AUTO=1 + break + ;; + [Nn] ) + # force "auto" mode, and disable all hooks + HOOK_COMMIT=0 + HOOK_MAIL=0 + HOOK_DB=0 + HOOK_API=0 + AUTO=1 + break + ;; + [Ii] ) + # force "manual" mode + AUTO=0 + break + ;; + * ) + printf "y - yes, execute actions and exit\n" + printf "n - no, don't execute actions and exit\n" + printf "i - switch to interactive mode\n" + printf "? - print this help\n" + ;; + esac + done fi fi -# insert into PG -# SQL_TEXTE=`echo "${TEXTE}" | sed "s/'/\\\\\\'/g ; s@/@\\\\\/@g ; s@\\&@et@g"` -SQL_TEXTE=`echo "${TEXTE}" | sed "s/'/''/g"` +if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then + # Commit hook + if [ -n "${GIT_STATUSES}" ] && [ "${HOOK_COMMIT}" = "1" ]; then + printf "/!\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}" -PG_QUERY="INSERT INTO evomaint(hostname,userid,ipaddress,begin_date,end_date,details) VALUES ('${HOSTNAME}','${USER}','${IP}','${BEGIN_DATE}',now(),'${SQL_TEXTE}')" -echo "${PG_QUERY}" | psql ${PGDB} ${PGTABLE} -h ${PGHOST} --quiet + y="Y"; n="n" + answer="" + while :; do + printf "> Do you want to commit the changes? [%s] " "${y},${n}" + read -r answer + case $answer in + [Yy] ) + hook_commit; + break + ;; + [Nn] ) + break + ;; + "" ) + if [ "${HOOK_COMMIT}" = "1" ]; then + hook_commit + fi + break + ;; + * ) + echo "answer with a valid choice" + ;; + esac + done + fi -# send mail -MAIL_TEXTE=$(echo "${TEXTE}" | sed -e "s@/@\\\\\/@g ; s@&@\\\\&@") -MAIL_GIT_COMMITS=$(echo "${GIT_COMMITS}" | sed -e "s@/@\\\\\/@g ; s@&@\\\\&@") + # Mail hook + if [ "${HOOK_MAIL}" = "1" ]; then + y="Y"; n="n" + else + y="y"; n="N" + fi + answer="" + while :; do + printf "> Do you want to send an email to <%s>? [%s] " "${EVOMAINTMAIL}" "${y},${n},e" + read -r answer + case $answer in + [Yy] ) + hook_mail; + break + ;; + [Nn] ) + break + ;; + [Ee] ) + printf "> To: [%s] " "${EVOMAINTMAIL}" + read -r mail_recipient + if [ -n "${mail_recipient}" ]; then + EVOMAINTMAIL="${mail_recipient}" + fi + ;; + "" ) + if [ "${HOOK_MAIL}" = "1" ]; then + hook_mail + fi + break + ;; + * ) + echo "answer with a valid choice" + ;; + esac + done -cat /usr/share/scripts/evomaintenance.tpl | \ - sed -e "s/__TO__/${EVOMAINTMAIL}/ ; s/__HOSTNAME__/${HOSTNAME_TEXT}/ ; s/__USER__/${USER}/ ; s/__BEGIN_DATE__/${BEGIN_DATE}/ ; s/__END_DATE__/${END_DATE}/ ; s/__GIT_COMMITS__/${MAIL_GIT_COMMITS}/ ; s/__TEXTE__/${MAIL_TEXTE}/ ; s/__IP__/${IP}/ ; s/__FULLFROM__/${FULLFROM}/ ; s/__FROM__/${FROM}/ ; s/__URGENCYFROM__/${URGENCYFROM}/ ; s/__URGENCYTEL__/${URGENCYTEL}/" | \ - ${SENDMAIL_BIN} -oi -t -f ${FROM} + # Database hook + if [ "${HOOK_DB}" = "1" ]; then + y="Y"; n="n" + else + y="y"; n="N" + fi + answer="" + while :; do + printf "> Do you want to insert your message into the database? [%s] " "${y},${n}" + read -r answer + case $answer in + [Yy] ) + hook_db; + break + ;; + [Nn] ) + break + ;; + "" ) + if [ "${HOOK_DB}" = "1" ]; then + hook_db + fi + break + ;; + * ) + echo "answer with a valid choice" + ;; + esac + done + + # API hook + if [ "${HOOK_API}" = "1" ]; then + y="Y"; n="n" + else + y="y"; n="N" + fi + answer="" + while :; do + printf "> Do you want to send the metadata to the API? [%s] " "${y},${n}" + read -r answer + case $answer in + [Yy] ) + hook_api; + break + ;; + [Nn] ) + break + ;; + "" ) + if [ "${HOOK_API}" = "1" ]; then + hook_api + fi + break + ;; + * ) + echo "answer with a valid choice" + ;; + esac + done +fi + +# Log hook +hook_log + +if [ "${INTERACTIVE}" = "0" ] || [ "${AUTO}" = "1" ]; then + if [ "${HOOK_COMMIT}" = "1" ]; then + hook_commit + fi + if [ "${HOOK_MAIL}" = "1" ]; then + hook_mail + fi + if [ "${HOOK_DB}" = "1" ]; then + hook_db + fi + if [ "${HOOK_API}" = "1" ]; then + hook_api + fi +fi exit 0 diff --git a/roles/base/files/newsyslog.conf b/roles/base/files/newsyslog.conf new file mode 100644 index 0000000..4e84cb3 --- /dev/null +++ b/roles/base/files/newsyslog.conf @@ -0,0 +1,15 @@ +# EvoBSD configuration file for newsyslog +# +# logfile_name owner:group mode count size when flags +/var/cron/log root:wheel 600 52 * $W1 Z +/var/log/authlog root:wheel 640 52 * $W1 Z +/var/log/daemon 640 365 * $D0 Z +/var/log/lpd-errs 640 7 * $D0 Z +/var/log/maillog 640 52 * $W1 Z +/var/log/messages 644 365 * $D0 Z +/var/log/secure 600 52 * $W1 Z +/var/log/wtmp 644 52 * $W1 B +/var/log/xferlog 640 7 * $D0 Z +/var/log/pflog 600 30 * $D0 ZB "pkill -HUP -u root -U root -t - -x pflogd" +/var/www/logs/access.log 644 52 * $W1 Z "pkill -USR1 -u root -U root -x httpd" +/var/www/logs/error.log 644 52 * $W1 Z "pkill -USR1 -u root -U root -x httpd" diff --git a/roles/base/files/profile b/roles/base/files/profile deleted file mode 100644 index b153f2f..0000000 --- a/roles/base/files/profile +++ /dev/null @@ -1,27 +0,0 @@ -# $OpenBSD: dot.profile,v 1.9 2010/12/13 12:54:31 millert Exp $ -# -# sh/ksh initialization - -PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/sbin:/usr/local/bin -export PATH HOME TERM -export PS1="\u@\h:\w\\$ " -HISTFILE=$HOME/.histfile -export HISTSIZE=10000 -export HISTCONTROL='ignoredups:ignorespace' -export TMOUT=36000 -export PAGER=less -umask 022 - -export ENV='~/.kshrc' - -case "$-" in -*i*) # interactive shell - if [ -x /usr/bin/tset ]; then - if [ X"$XTERM_VERSION" = X"" ]; then - eval `/usr/bin/tset -sQ '-munknown:?vt220' $TERM` - else - eval `/usr/bin/tset -IsQ '-munknown:?vt220' $TERM` - fi - fi - ;; -esac diff --git a/roles/base/files/vimrc b/roles/base/files/vimrc index 581459c..df8a1cb 100644 --- a/roles/base/files/vimrc +++ b/roles/base/files/vimrc @@ -9,3 +9,4 @@ set smarttab set backspace=indent,eol,start set showcmd set encoding=utf-8 +set mouse="" diff --git a/roles/base/files/zzz_evobackup b/roles/base/files/zzz_evobackup index 5e2a4d9..ec64280 100755 --- a/roles/base/files/zzz_evobackup +++ b/roles/base/files/zzz_evobackup @@ -1,222 +1,419 @@ #!/bin/sh - # -# Script Evobackup plus ou moins forké -# See https://forge.evolix.org/projects/evobackup -# +# Script Evobackup client +# See https://gitea.evolix.org/evolix/evobackup +# +# Author: Gregory Colpart +# Contributors: +# Romain Dessort +# Benoît Série +# Tristan Pilat +# Victor Laborie +# Jérémy Lecour +# +# Licence: AGPLv3 +# +# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES + +# Fail on unassigned variables +set -u + +##### Configuration ################################################### + +# email adress for notifications +MAIL=jdoe@example.com + +# list of hosts (hostname or IP) and SSH port for Rsync +SERVERS="node0.backup.example.com:2XXX node1.backup.example.com:2XXX" + +# Should we fallback on servers when the first is unreachable ? +SERVERS_FALLBACK=${SERVERS_FALLBACK:-1} + +# timeout (in seconds) for SSH connections +SSH_CONNECT_TIMEOUT=${SSH_CONNECT_TIMEOUT:-30} + +## We use /home/backup : feel free to use your own dir +LOCAL_BACKUP_DIR="/home/backup" + +# You can set "linux" or "bsd" manually or let it choose automatically +SYSTEM=$(uname | tr '[:upper:]' '[:lower:]') + +# Change these 2 variables if you have more than one backup cron +PIDFILE="/var/run/evobackup.pid" +LOGFILE="/var/log/evobackup.log" + +## Enable/Disable tasks +LOCAL_TASKS=${LOCAL_TASKS:-1} +SYNC_TASKS=${SYNC_TASKS:-1} + +##### SETUP AND FUNCTIONS ############################################# + +BEGINNING=$(/bin/date +"%d-%m-%Y ; %H:%M") + +# shellcheck disable=SC2174 +mkdir -p -m 700 ${LOCAL_BACKUP_DIR} PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin ## lang = C for english outputs -LANGUAGE=C -LANG=C +export LANGUAGE=C +export LANG=C ## Force umask umask 077 +## Initialize variable to store SSH connection errors +SERVERS_SSH_ERRORS="" + +# Call test_server with "HOST:PORT" string +# It will return with 0 if the server is reachable. +# It will return with 1 and a message on stderr if not. +test_server() { + item=$1 + # split HOST and PORT from the input string + host=$(echo "${item}" | cut -d':' -f1) + port=$(echo "${item}" | cut -d':' -f2) + + # Test if the server is accepting connections + ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" "${host}" -p "${port}" -t "exit" + # shellcheck disable=SC2181 + if [ $? = 0 ]; then + # SSH connection is OK + return 0 + else + # SSH connection failed + new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}") + SERVERS_SSH_ERRORS=$(printf "%s\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d') + + return 1 + fi +} +# Call pick_server with an optional positive integer to get the nth server in the list. +pick_server() { + increment=${1:-0} + list_length=$(echo "${SERVERS}" | wc -w) + + if [ "${increment}" -ge "${list_length}" ]; then + # We've reached the end of the list + new_error="No more server available" + SERVERS_SSH_ERRORS=$(printf "%s\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d') + + # Log errors to stderr + printf "%s\n" "${SERVERS_SSH_ERRORS}" >&2 + # Log errors to logfile + printf "%s\n" "${SERVERS_SSH_ERRORS}" >> $LOGFILE + return 1 + fi + + # Extract the day of month, without leading 0 (which would give an octal based number) + today=$(date +%e) + # A salt is useful to randomize the starting point in the list + # but stay identical each time it's called for a server (based on hostname). + salt=$(hostname | cksum | cut -d' ' -f1) + # Pick an integer between 0 and the length of the SERVERS list + # It changes each day + item=$(( (today + salt + increment) % list_length )) + # cut starts counting fields at 1, not 0. + field=$(( item + 1 )) + + echo "${SERVERS}" | cut -d' ' -f${field} +} + ## Verify other evobackup process and kill if needed -PIDFILE=/var/run/evobackup.pid -if [ -e $PIDFILE ]; then - # Killing the childs of evobackup. - for pid in $(ps h --ppid $(cat $PIDFILE) -o pid | tr -s '\n' ' '); do - kill -9 $pid; +if [ -e "${PIDFILE}" ]; then + pid=$(cat "${PIDFILE}") + # Does process still exist ? + if kill -0 ${pid} 2> /dev/null; then + # Killing the childs of evobackup. + for ppid in $(pgrep -P "${pid}"); do + kill -9 "${ppid}"; + done + # Then kill the main PID. + kill -9 "${pid}" + printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\n" >&2 + else + rm -f ${PIDFILE} + fi +fi +echo "$$" > ${PIDFILE} +# shellcheck disable=SC2064 +trap "rm -f ${PIDFILE}" EXIT + +##### LOCAL BACKUP #################################################### + +if [ "${LOCAL_TASKS}" = "1" ]; then + # You can comment or uncomment sections below to customize the backup + + ## OpenLDAP : example with slapcat + # slapcat -l ${LOCAL_BACKUP_DIR}/ldap.bak + + ## MySQL + + ## example with global and compressed mysqldump + # mysqldump --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \ + # --opt --all-databases --force --events --hex-blob | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.bak.gz + + ## example with two dumps for each table (.sql/.txt) for all databases + # for i in $(echo SHOW DATABASES | mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \ + # | egrep -v "^(Database|information_schema|performance_schema|sys)" ); \ + # do mkdir -p -m 700 /home/mysqldump/$i ; chown -RL mysql /home/mysqldump ; \ + # mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 -Q --opt --events --hex-blob --skip-comments \ + # --fields-enclosed-by='\"' --fields-terminated-by=',' -T /home/mysqldump/$i $i; done + + ## example with SQL dump (schema only, no data) for each databases + # mkdir -p -m 700 /home/mysqldump/ + # for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \ + # | egrep -v "^(Database|information_schema|performance_schema|sys)"); do + # mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --no-data --databases $i > /home/mysqldump/${i}.schema.sql + # done + + ## example with compressed SQL dump (with data) for each databases + # mkdir -p -m 700 /home/mysqldump/ + # for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \ + # | egrep -v "^(Database|information_schema|performance_schema|sys)"); do + # mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --events --hex-blob $i | gzip --best > /home/mysqldump/${i}.sql.gz + # done + + ## example with *one* uncompressed SQL dump for *one* database (MYBASE) + # mkdir -p -m 700 /home/mysqldump/MYBASE + # chown -RL mysql /home/mysqldump/ + # mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -Q \ + # --opt --events --hex-blob --skip-comments -T /home/mysqldump/MYBASE MYBASE + + ## example with mysqlhotcopy + # mkdir -p -m 700 /home/mysqlhotcopy/ + # mysqlhotcopy BASE /home/mysqlhotcopy/ + + ## example for multiples MySQL instances + # mysqladminpasswd=$(grep -m1 'password = .*' /root/.my.cnf|cut -d" " -f3) + # grep -E "^port\s*=\s*\d*" /etc/mysql/my.cnf |while read instance; do + # instance=$(echo "$instance"|awk '{ print $3 }') + # if [ "$instance" != "3306" ] + # then + # mysqldump -P $instance --opt --all-databases --hex-blob -u mysqladmin -p$mysqladminpasswd > ${LOCAL_BACKUP_DIR}/mysql.$instance.bak + # fi + # done + + ## PostgreSQL + + ## example with pg_dumpall (warning: you need space in ~postgres) + # su - postgres -c "pg_dumpall > ~/pg.dump.bak" + # mv ~postgres/pg.dump.bak ${LOCAL_BACKUP_DIR}/ + ## another method with gzip directly piped + # cd /var/lib/postgresql + # sudo -u postgres pg_dumpall | gzip > ${LOCAL_BACKUP_DIR}/pg.dump.bak.gz + # cd - > /dev/null + + ## example with all tables from MYBASE excepts TABLE1 and TABLE2 + # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -t 'TABLE1' -t 'TABLE2' MYBASE + + ## example with only TABLE1 and TABLE2 from MYBASE + # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -T 'TABLE1' -T 'TABLE2' MYBASE + + ## MongoDB + + ## don't forget to create use with read-only access + ## > use admin + ## > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } ) + # test -d ${LOCAL_BACKUP_DIR}/mongodump/ && rm -rf ${LOCAL_BACKUP_DIR}/mongodump/ + # mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mongodump/ + # mongodump --quiet -u mongobackup -pPASS -o ${LOCAL_BACKUP_DIR}/mongodump/ + # if [ $? -ne 0 ]; then + # echo "Error with mongodump!" + # fi + + ## Redis + + ## example with copy .rdb file + # cp /var/lib/redis/dump.rdb ${LOCAL_BACKUP_DIR}/ + + ## ElasticSearch + + ## Take a snapshot as a backup. + ## Warning: You need to have a path.repo configured. + ## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes + # curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" -o /tmp/es_delete_snapshot.daily.log + # curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" -o /tmp/es_snapshot.daily.log + ## Clustered version here + ## It basically the same thing except that you need to check that NFS is mounted + # if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2' + # then + # curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" -o /tmp/es_delete_snapshot.daily.log + # curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" -o /tmp/es_snapshot.daily.log + # else + # echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.' + # fi + ## If you need to keep older snapshot, for example the last 10 daily snapshots, replace the XDELETE and XPUT lines by : + # for snapshot in $(curl -s -XGET "localhost:9200/_snapshot/snaprepo/_all?pretty=true" | grep -Eo 'snapshot_[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n -10); do + # curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/${snapshot}" | grep -v -Fx '{"acknowledged":true}' + # done + # date=$(date +%F) + # curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log + + ## RabbitMQ + + ## export config + #rabbitmqadmin export ${LOCAL_BACKUP_DIR}/rabbitmq.config >> $LOGFILE + + ## MegaCli config + + #megacli -CfgSave -f ${LOCAL_BACKUP_DIR}/megacli_conf.dump -a0 >/dev/null + + ## Dump system and kernel versions + uname -a > ${LOCAL_BACKUP_DIR}/uname + + ## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls) + for addr in 8.8.8.8 www.evolix.fr travaux.evolix.net; do + mtr -r ${addr} > ${LOCAL_BACKUP_DIR}/mtr-${addr} + traceroute -n ${addr} > ${LOCAL_BACKUP_DIR}/traceroute-${addr} 2>&1 done - # Then kill the main PID. - kill -9 $(cat $PIDFILE) - echo "$0 tourne encore (PID `cat $PIDFILE`). Processus killé" >&2 + + ## Dump process with ps + ps auwwx >${LOCAL_BACKUP_DIR}/ps.out + + if [ "${SYSTEM}" = "linux" ]; then + ## Dump network connections with ss + ss -taupen > ${LOCAL_BACKUP_DIR}/netstat.out + + ## List Debian packages + dpkg -l > ${LOCAL_BACKUP_DIR}/packages + dpkg --get-selections > ${LOCAL_BACKUP_DIR}/packages.getselections + apt-cache dumpavail > ${LOCAL_BACKUP_DIR}/packages.available + + ## Dump MBR / table partitions + disks=$(lsblk -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | awk '{print $1}') + for disk in ${disks}; do + dd if="/dev/${disk}" of="${LOCAL_BACKUP_DIR}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)" + fdisk -l "/dev/${disk}" > "${LOCAL_BACKUP_DIR}/partitions-${disk}" 2>&1 + done + cat ${LOCAL_BACKUP_DIR}/partitions-* > ${LOCAL_BACKUP_DIR}/partitions + + ## Dump iptables + if [ -x /sbin/iptables ]; then + { /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${LOCAL_BACKUP_DIR}/iptables.txt + fi + + ## Dump findmnt(8) output + FINDMNT_BIN=$(command -v findmnt) + if [ -x ${FINDMNT_BIN} ]; then + ${FINDMNT_BIN} > ${LOCAL_BACKUP_DIR}/findmnt.txt + fi + else + ## Dump network connections with netstat + netstat -finet -atn > ${LOCAL_BACKUP_DIR}/netstat.out + + ## List OpenBSD packages + pkg_info -m > ${LOCAL_BACKUP_DIR}/packages + + ## Dump MBR / table partitions + disklabel sd0 > ${LOCAL_BACKUP_DIR}/partitions + + ## Dump pf infos + pfctl -sa > ${LOCAL_BACKUP_DIR}/pfctl-sa.txt + + fi + + ## Dump rights + #getfacl -R /var > ${LOCAL_BACKUP_DIR}/rights-var.txt + #getfacl -R /etc > ${LOCAL_BACKUP_DIR}/rights-etc.txt + #getfacl -R /usr > ${LOCAL_BACKUP_DIR}/rights-usr.txt + #getfacl -R /home > ${LOCAL_BACKUP_DIR}/rights-home.txt + fi -echo "$$" > $PIDFILE -trap "rm -f $PIDFILE" EXIT -# Variable to choose different backup server with date -NODE=$(expr `date +%d` % 2 + 2) +##### REMOTE BACKUP ################################################### -# port SSH -SSH_PORT=2XXX +n=0 +server="" +if [ "${SERVERS_FALLBACK}" = "1" ]; then + # We try to find a suitable server + while :; do + server=$(pick_server "${n}") + test $? = 0 || exit 2 -# email adress for notifications -MAIL={{ general_alert_email }} - -# backup server used -SRV=node$NODE.backup2.evolix.net - -# choose "linux" or "bsd" -SYSTEME=$(uname | tr '[:upper:]' '[:lower:]') - -## We use /home/backup : feel free to use your own dir -mkdir -p -m 700 /home/backup - -## OpenLDAP : example with slapcat -# slapcat -l /home/backup/ldap.bak - -### MySQL - -## example with global and compressed mysqldump -# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \ -# --opt --all-databases --force --events --hex-blob | gzip --best > /home/backup/mysql.bak.gz - -## example with two dumps for each table (.sql/.txt) for all databases -# for i in $(echo SHOW DATABASES | mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \ -# | egrep -v "^(Database|information_schema|performance_schema)" ); \ -# do mkdir -p /home/mysqldump/$i ; chown -RL mysql /home/mysqldump ; \ -# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 -Q --opt --events --hex-blob --skip-comments -T \ -# /home/mysqldump/$i $i; done - -## example with compressed SQL dump for each databases -# mkdir -p /home/mysqldump/ -# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \ -# | egrep -v "^(Database|information_schema|performance_schema)"); do -# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --events --hex-blob $i | gzip --best > /home/mysqldump/${i}.sql.gz -# done - -## example with *one* uncompressed SQL dump for *one* database (MYBASE) -# mkdir -p -m 700 /home/mysqldump/MYBASE -# chown -RL mysql /home/mysqldump/ -# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -Q \ -# --opt --events --hex-blob --skip-comments -T /home/mysqldump/MYBASE MYBASE - -## example with mysqlhotcopy -# mkdir -p /home/mysqlhotcopy/ -# mysqlhotcopy BASE /home/mysqlhotcopy/ - -## example for multiples MySQL instances -# mysqladminpasswd=`cat /root/.my.cnf |grep -m1 'password = .*' |cut -d" " -f3` -# grep -E "^port\s*=\s*\d*" /etc/mysql/my.cnf |while read instance; do -# instance=$(echo $instance |tr -d '\t') -# instance=${instance// /} -# instance=${instance//port=/} -# if [ "$instance" != "3306" ] -# then -# mysqldump -P $instance --opt --all-databases --hex-blob -u mysqladmin -p$mysqladminpasswd > /home/backup/mysql.$instance.bak -# fi -# done - -### PostgreSQL - -## example with pg_dumpall (warning: you need space in ~postgres) -# su - postgres -c "pg_dumpall > ~/pg.dump.bak" -# mv ~postgres/pg.dump.bak /home/backup/ - -## example with all tables from MYBASE excepts TABLE1 and TABLE2 -# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f /home/backup/pg-backup.tar -t 'TABLE1' -t 'TABLE2' MYBASE - -## example with only TABLE1 and TABLE2 from MYBASE -# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f /home/backup/pg-backup.tar -T 'TABLE1' -T 'TABLE2' MYBASE - -## MongoDB : example with mongodump -## don't forget to create use with read-only access -## > use admin -## > db.addUser("mongobackup", "PASS", true); -# mongodump -u mongobackup -pPASS -o /home/backup/mongodump/ >/dev/null 2>&1 |grep -v "^connected to:" - -## Redis : example with copy .rdb file -# cp /var/lib/redis/dump.rdb /home/backup/ - -## ElasticSearch : example with rsync (warning: don't forget to use NFS if you have a cluster) -## Disable ES translog flush -# curl -s -XPUT 'localhost:9200/_settings' -d '{"index.translog.disable_flush": true}' >/dev/null -## Flushes translog -# curl -s 'localhost:9200/_flush' | grep -qe '"ok":true' -## If it succeed, do an rsync of the datadir -# if [ $? -eq 0 ]; then -# rsync -a /var/lib/elasticsearch /home/backup/ -# else -# echo "Error when flushing ES translog indexes." -# fi -## In any case re-enable translog flush -# curl -s -XPUT 'localhost:9200/_settings' -d '{"index.translog.disable_flush": false}' > /dev/null - -## Dump MBR / table partitions with dd and sfdisk -## Linux -# dd if=/dev/sda of=/home/backup/MBR bs=512 count=1 2>&1 | egrep -v "(records in|records out|512 bytes)" -# sfdisk -d /dev/sda > /home/backup/partitions 2>&1 | egrep -v "(Warning: extended partition does not start at a cylinder boundary|DOS and Linux will interpret the contents differently)" -## OpenBSD -# disklabel sd0 > /home/backup/partitions - -# backup MegaCli config -#megacli -CfgSave -f /home/backup/megacli_conf.dump -a0 >/dev/null - -## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls) -for addr in 8.8.8.8 backup.evolix.net www.evolix.fr www.evolix.net; do - mtr -r $addr > /home/backup/mtr-${addr} 2>/dev/null - traceroute -n $addr > /home/backup/traceroute-${addr} 2>/dev/null -done - -## Dump process with ps -ps aux >/home/backup/ps.out - -if [ $SYSTEME = "linux" ]; then - ## Dump network connections with netstat - netstat -taupen >/home/backup/netstat.out - - ## List Debian packages - dpkg -l >/home/backup/packages + if test_server "${server}"; then + break + else + server="" + n=$(( n + 1 )) + fi + done else - ## Dump network connections with netstat - netstat -finet -atn >/home/backup/netstat.out - - ## List OpenBSD packages - pkg_info -m >/home/backup/packages + # we force the server + server=$(pick_server "${n}") fi +SSH_SERVER=$(echo "${server}" | cut -d':' -f1) +SSH_PORT=$(echo "${server}" | cut -d':' -f2) + HOSTNAME=$(hostname) -DATE=$(/bin/date +"%d-%m-%Y") - -DEBUT=$(/bin/date +"%d-%m-%Y ; %H:%M") - -if [ $SYSTEME = "linux" ]; then +if [ "${SYSTEM}" = "linux" ]; then rep="/bin /boot /lib /opt /sbin /usr" else rep="/bsd /bin /sbin /usr" fi -/usr/local/bin/rsync -avzh --stats --delete --delete-excluded --force --ignore-errors --partial \ - --exclude "lost+found" \ - --exclude ".nfs.*" \ - --exclude "/var/log" \ - --exclude "/var/log/evobackup*" \ - --exclude "/var/lib/mysql" \ - --exclude "/var/lib/postgres" \ - --exclude "/var/lib/postgresql" \ - --exclude "/var/lib/sympa" \ - --exclude "/var/lib/metche" \ - --exclude "/var/run" \ - --exclude "/var/lock" \ - --exclude "/var/state" \ - --exclude "/var/apt" \ - --exclude "/var/cache" \ - --exclude "/usr/src" \ - --exclude "/usr/doc" \ - --exclude "/usr/share/doc" \ - --exclude "/usr/obj" \ - --exclude "dev" \ - --exclude "/var/spool/postfix" \ - --exclude "/var/lib/amavis/amavisd.sock" \ - --exclude "/var/lib/munin/*tmp*" \ - --exclude "/var/lib/php5" \ - --exclude "/var/spool/squid" \ - --exclude "/var/lib/elasticsearch" \ - --exclude "/var/lib/amavis/tmp" \ - --exclude "/var/lib/clamav/*.tmp" \ - --exclude "/home/mysqltmp" \ - $rep \ - /etc \ - /root \ - /var \ - /home \ - -e "ssh -p $SSH_PORT" \ - root@${SRV}:/var/backup/ \ - | tail -30 >> /var/log/evobackup.log -FIN=$(/bin/date +"%d-%m-%Y ; %H:%M") +if [ "${SYNC_TASKS}" = "1" ]; then + # /!\ DO NOT USE COMMENTS in the rsync command /!\ + # It breaks the command and destroys data, simply remove (or add) lines. -echo "EvoBackup - $HOSTNAME - START $DEBUT" \ - >> /var/log/evobackup.log + # Remote shell command + RSH_COMMAND="ssh -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'" -echo "EvoBackup - $HOSTNAME - STOP $FIN" \ - >> /var/log/evobackup.log + rsync -avzh --stats --delete --delete-excluded --force --ignore-errors --partial \ + --exclude "lost+found" \ + --exclude ".nfs.*" \ + --exclude "/var/log" \ + --exclude "/var/log/evobackup*" \ + --exclude "/var/lib/mysql" \ + --exclude "/var/lib/postgres" \ + --exclude "/var/lib/postgresql" \ + --exclude "/var/lib/sympa" \ + --exclude "/var/lib/metche" \ + --exclude "/var/run" \ + --exclude "/var/lock" \ + --exclude "/var/state" \ + --exclude "/var/apt" \ + --exclude "/var/cache" \ + --exclude "/usr/src" \ + --exclude "/usr/doc" \ + --exclude "/usr/share/doc" \ + --exclude "/usr/obj" \ + --exclude "dev" \ + --exclude "/var/spool/postfix" \ + --exclude "/var/lib/amavis/amavisd.sock" \ + --exclude "/var/lib/munin/*tmp*" \ + --exclude "/var/lib/php5" \ + --exclude "/var/spool/squid" \ + --exclude "/var/lib/elasticsearch" \ + --exclude "/var/lib/amavis/tmp" \ + --exclude "/var/lib/clamav/*.tmp" \ + --exclude "/home/mysqltmp" \ + --exclude "/var/lib/php/sessions" \ + ${rep} \ + /etc \ + /root \ + /var \ + /home \ + -e "${RSH_COMMAND}" \ + "root@${SSH_SERVER}:/var/backup/" \ + | tail -30 >> $LOGFILE +fi -tail -10 /var/log/evobackup.log | \ - mail -s "[info] EvoBackup - Client $HOSTNAME" \ - $MAIL +##### REPORTING ####################################################### + +END=$(/bin/date +"%d-%m-%Y ; %H:%M") + +printf "EvoBackup - %s - START %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\n" \ + "${HOSTNAME}" "${BEGINNING}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \ + >> $LOGFILE + +printf "EvoBackup - %s - STOP %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\n" \ + "${HOSTNAME}" "${END}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \ + >> $LOGFILE + +tail -10 $LOGFILE | \ + mail -s "[info] EvoBackup - Client ${HOSTNAME}" \ + ${MAIL} diff --git a/roles/base/handlers/main.yml b/roles/base/handlers/main.yml index ba888e0..13c34bc 100644 --- a/roles/base/handlers/main.yml +++ b/roles/base/handlers/main.yml @@ -1,3 +1,33 @@ --- - name: newaliases shell: smtpctl update table aliases + +- name: remount / noatime + command: mount -u -o noatime / + args: + warn: false + +- name: remount /var noatime + command: mount -u -o noatime /var + args: + warn: false + +- name: remount /usr noatime + command: mount -u -o noatime /usr + args: + warn: false + +- name: remount /tmp noexec + command: mount -u -o noexec /tmp + args: + warn: false + +- name: remount /tmp noatime + command: mount -u -o noatime /tmp + args: + warn: false + +- name: remount /home noatime + command: mount -u -o noatime /home + args: + warn: false diff --git a/roles/base/tasks/cron.yml b/roles/base/tasks/cron.yml new file mode 100644 index 0000000..87cce5f --- /dev/null +++ b/roles/base/tasks/cron.yml @@ -0,0 +1,19 @@ +--- +- name: Customize PATH variable of root crontab + cron: + name: PATH + env: true + value: "{{ cron_root_path }}" + tags: + - cron + +- name: Customize daily.local environment + lineinfile: + path: /etc/daily.local + line: 'VERBOSESTATUS=0' + insertbefore: BOF + owner: root + mode: "0644" + create: true + tags: + - cron diff --git a/roles/base/tasks/doas.yml b/roles/base/tasks/doas.yml index 9c866a5..538b1af 100644 --- a/roles/base/tasks/doas.yml +++ b/roles/base/tasks/doas.yml @@ -6,8 +6,6 @@ owner: root group: wheel mode: "0640" - backup: no + backup: false tags: - - doas - - + - doas diff --git a/roles/base/tasks/dotfiles.yml b/roles/base/tasks/dotfiles.yml index a132e03..eb8c144 100644 --- a/roles/base/tasks/dotfiles.yml +++ b/roles/base/tasks/dotfiles.yml @@ -1,7 +1,7 @@ --- - name: Customize root's .profile - copy: - src: profile + template: + src: profile.j2 dest: /root/.profile tags: - admin @@ -26,8 +26,8 @@ - dotfiles - name: Change default .profile skeleton - copy: - src: profile + template: + src: profile.j2 dest: /etc/skel/.profile tags: - admin @@ -39,10 +39,10 @@ dest: /etc/skel/.profile insertafter: EOF line: 'trap "doas /usr/share/scripts/evomaintenance.sh" 0' - create: yes + create: true tags: - - admin - - dotfiles + - admin + - dotfiles - name: Add vim configuration to dotfiles for new users copy: diff --git a/roles/base/tasks/evobackup.yml b/roles/base/tasks/evobackup.yml index 669c86f..c72a56b 100644 --- a/roles/base/tasks/evobackup.yml +++ b/roles/base/tasks/evobackup.yml @@ -6,7 +6,16 @@ owner: root group: wheel mode: "0755" - force: no + force: false + tags: + - evobackup + +- name: Fetch daily.local content + command: 'grep "sh /usr/share/scripts/zzz_evobackup" /etc/daily.local' + check_mode: false + register: daily_local_content + failed_when: false + changed_when: false tags: - evobackup @@ -16,5 +25,18 @@ line: '#sh /usr/share/scripts/zzz_evobackup' owner: root mode: "0644" + create: true + when: + - not (daily_local_content.stdout + | regex_search('sh /usr/share/scripts/zzz_evobackup')) + tags: + - evobackup + +- name: Delete evobackup root crontab replaced by daily.local cron + lineinfile: + path: /var/cron/tabs/root + regexp: '/usr/share/scripts/zzz_evobackup' + validate: /usr/bin/crontab %s + state: absent tags: - evobackup diff --git a/roles/base/tasks/evomaintenance.yml b/roles/base/tasks/evomaintenance.yml index 6471d28..9d51c7a 100644 --- a/roles/base/tasks/evomaintenance.yml +++ b/roles/base/tasks/evomaintenance.yml @@ -10,10 +10,15 @@ - evomaintenance - name: Copy evomaintenance script and template - copy: src={{ item.src }} dest={{ item.dest }} owner=root group=wheel mode="0755" + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: 'root' + group: 'wheel' + mode: '0755' with_items: - - { src: 'evomaintenance.sh', dest: '/usr/share/scripts/' } - - { src: 'evomaintenance.tpl', dest: '/usr/share/scripts/' } + - {src: 'evomaintenance.sh', dest: '/usr/share/scripts/'} + - {src: 'evomaintenance.tpl', dest: '/usr/share/scripts/'} tags: - evomaintenance - script-evomaintenance @@ -25,27 +30,6 @@ owner: root group: wheel mode: "0600" - backup: no + backup: false tags: - evomaintenance - -- name: Copy mailevomaintenance - template: - src: mailevomaintenance.sh.j2 - dest: /usr/share/scripts/mailevomaintenance.sh - owner: root - group: wheel - mode: "0700" - tags: - - evomaintenance - - mailevomaintenance - -- name: Add mailevomaintenance cron - cron: - name: "mailevomaintenance" - job: "/usr/share/scripts/mailevomaintenance.sh" - minute: "50" - hour: "22" - disabled: yes - tags: - - mailevomaintenance diff --git a/roles/base/tasks/fstab.yml b/roles/base/tasks/fstab.yml new file mode 100644 index 0000000..23a9af9 --- /dev/null +++ b/roles/base/tasks/fstab.yml @@ -0,0 +1,138 @@ +--- +- name: Fetch fstab content + command: "grep -v '^#' /etc/fstab" + check_mode: false + register: fstab_content + failed_when: false + changed_when: false + tags: + - fstab + +- name: / partition is customized - softdep + replace: + dest: /etc/fstab + regexp: '([^#]\s+/\s+ffs\s+rw)(.*)' + replace: '\1,softdep\2' + when: + - fstab_content.stdout | regex_search('\s/\s') + - not (fstab_content.stdout | regex_search('\s+/\s+ffs\s+rw,softdep')) + tags: + - fstab + +- name: / partition is customized - noatime + replace: + dest: /etc/fstab + regexp: '([^#]\s+/\s+ffs\s+rw)(\S*)(\s+.*)' + replace: '\1\2,noatime\3' + notify: remount / noatime + when: + - fstab_content.stdout | regex_search('\s/\s') + - not (fstab_content.stdout | regex_search('\s+/\s+ffs\s+rw\S*noatime')) + tags: + - fstab + +- name: /var partition is customized - softdep + replace: + dest: /etc/fstab + regexp: '([^#]\s+/var\s+ffs\s+rw)(.*)' + replace: '\1,softdep\2' + when: + - fstab_content.stdout | regex_search('\s/var\s') + - not (fstab_content.stdout | regex_search('\s+/var\s+ffs\s+rw,softdep')) + tags: + - fstab + +- name: /var partition is customized - noatime + replace: + dest: /etc/fstab + regexp: '([^#]\s+/var\s+ffs\s+rw)(\S*)(\s+.*)' + replace: '\1\2,noatime\3' + notify: remount /var noatime + when: + - fstab_content.stdout | regex_search('\s/var\s') + - not (fstab_content.stdout | regex_search('\s+/var\s+ffs\s+rw\S*noatime')) + tags: + - fstab + +- name: /usr partition is customized - softdep + replace: + dest: /etc/fstab + regexp: '([^#]\s+/usr\s+ffs\s+rw)(.*)' + replace: '\1,softdep\2' + when: + - fstab_content.stdout | regex_search('\s/usr\s') + - not (fstab_content.stdout | regex_search('\s+/usr\s+ffs\s+rw,softdep')) + tags: + - fstab + +- name: /usr partition is customized - noatime + replace: + dest: /etc/fstab + regexp: '([^#]\s+/usr\s+ffs\s+rw)(\S*)(\s+.*)' + replace: '\1\2,noatime\3' + notify: remount /usr noatime + when: + - fstab_content.stdout | regex_search('\s/usr\s') + - not (fstab_content.stdout | regex_search('\s+/usr\s+ffs\s+rw\S*noatime')) + tags: + - fstab + +- name: /tmp partition is customized - noexec + replace: + dest: /etc/fstab + regexp: '([^#]\s+/tmp\s+ffs\s+rw(,softdep)*)(.*)' + replace: '\1,noexec\3' + notify: remount /tmp noexec + when: + - fstab_content.stdout | regex_search('\s/tmp\s') + - not (fstab_content.stdout + | regex_search('\s+/tmp\s+ffs\s+rw,(softdep,)*noexec')) + tags: + - fstab + +- name: /tmp partition is customized - softdep + replace: + dest: /etc/fstab + regexp: '([^#]\s+/tmp\s+ffs\s+rw)(.*)' + replace: '\1,softdep\2' + when: + - fstab_content.stdout | regex_search('\s/tmp\s') + - not (fstab_content.stdout + | regex_search('\s+/tmp\s+ffs\s+rw,(noexec,)*softdep')) + tags: + - fstab + +- name: /tmp partition is customized - noatime + replace: + dest: /etc/fstab + regexp: '([^#]\s+/tmp\s+ffs\s+rw)(\S*)(\s+.*)' + replace: '\1\2,noatime\3' + notify: remount /tmp noatime + when: + - fstab_content.stdout | regex_search('\s/tmp\s') + - not (fstab_content.stdout | regex_search('\s+/tmp\s+ffs\s+rw\S*noatime')) + tags: + - fstab + +- name: /home partition is customized - softdep + replace: + dest: /etc/fstab + regexp: '([^#]\s+/home\s+ffs\s+rw)(.*)' + replace: '\1,softdep\2' + when: + - fstab_content.stdout | regex_search('\s/home\s') + - not (fstab_content.stdout | regex_search('\s+/home\s+ffs\s+rw,softdep')) + tags: + - fstab + +- name: /home partition is customized - noatime + replace: + dest: /etc/fstab + regexp: '([^#]\s+/home\s+ffs\s+rw)(\S*)(\s+.*)' + replace: '\1\2,noatime\3' + notify: remount /home noatime + when: + - fstab_content.stdout | regex_search('\s/home\s') + - not (fstab_content.stdout | regex_search('\s+/home\s+ffs\s+rw\S*noatime')) + tags: + - fstab diff --git a/roles/base/tasks/mail.yml b/roles/base/tasks/mail.yml index 321d837..c15d4d9 100644 --- a/roles/base/tasks/mail.yml +++ b/roles/base/tasks/mail.yml @@ -1,9 +1,32 @@ --- +- name: Fetch rc.local content + command: "grep -v '^#' /etc/rc.local" + check_mode: false + register: rclocal_content + failed_when: false + changed_when: false + tags: + - misc + - name: Configure rc.local lineinfile: path: /etc/rc.local - line: 'date | mail -s "boot/reboot of $(hostname -s)" {{ general_alert_email }}' - create: yes + line: + 'date | mail -s "boot/reboot of $(hostname -s)" {{ general_alert_email }}' + insertbefore: 'echo' + create: true + when: + - not (rclocal_content.stdout + | regex_search('date \| mail -s (\"|\')boot/reboot of \$\(hostname -s\)')) + tags: + - misc + +- name: Delete rc.local entry of boot/reboot not precising hostname + lineinfile: + path: /etc/rc.local + regexp: + "^.* mail -s (?!.*of.*).+$" + state: absent tags: - misc @@ -12,7 +35,7 @@ dest: /etc/mail/aliases regexp: "# root:" replace: "root: {{ general_alert_email }}" - backup: no + backup: false notify: - newaliases tags: diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 7df8981..bd467b3 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -7,3 +7,6 @@ - include: mail.yml - include: sudo.yml - include: evobackup.yml +- include: newsyslog.yml +- include: cron.yml +- include: fstab.yml diff --git a/roles/base/tasks/newsyslog.yml b/roles/base/tasks/newsyslog.yml new file mode 100644 index 0000000..936a6d5 --- /dev/null +++ b/roles/base/tasks/newsyslog.yml @@ -0,0 +1,7 @@ +--- +- name: Customize newsyslog + copy: + src: newsyslog.conf + dest: /etc/newsyslog.conf + tags: + - newsyslog diff --git a/roles/base/tasks/packages.yml b/roles/base/tasks/packages.yml index 6c78d9c..5bbe520 100644 --- a/roles/base/tasks/packages.yml +++ b/roles/base/tasks/packages.yml @@ -9,23 +9,20 @@ - name: Install packages (vim rsync mtr etc) openbsd_pkg: - name: "{{ item }}" - state: present - with_items: - - wget - - vim--no_x11 - - rsync-- - - mtr-- - - iftop - - postgresql-client + name: + - wget + - vim--no_x11 + - rsync-- + - mtr-- + - iftop + - sudo-- tags: - pkg -- name: Install sudo - openbsd_pkg: - name: "{{ item }}" - state: present - with_items: - - sudo-- +- name: Disable sndiod + service: + name: sndiod + enabled: false + state: stopped tags: - pkg diff --git a/roles/base/tasks/sudo.yml b/roles/base/tasks/sudo.yml index d00e460..980d064 100644 --- a/roles/base/tasks/sudo.yml +++ b/roles/base/tasks/sudo.yml @@ -1,14 +1,32 @@ --- -# dont't break the tab! +- name: Configure sudoers umask + lineinfile: + dest: /etc/sudoers + insertafter: '# Defaults specification' + line: 'Defaults umask=0077' + validate: 'visudo -cf %s' + tags: + - sudo + - name: Allow wheel group to run command as root in sudo lineinfile: dest: /etc/sudoers insertafter: '# and set environment variables.' - line: '%wheel ALL=(ALL) SETENV: ALL' + line: "%wheel\tALL=(ALL) SETENV: ALL" validate: 'visudo -cf %s' - backup: no + backup: false tags: - - sudo + - sudo + +- name: Delete line with space instead of tab + lineinfile: + dest: /etc/sudoers + line: "%wheel ALL=(ALL) SETENV: ALL" + validate: 'visudo -cf %s' + backup: false + state: absent + tags: + - sudo - name: Configure sudoers for evomaintenance and monitoring blockinfile: @@ -18,12 +36,8 @@ block: | Cmnd_Alias MAINT = /usr/share/scripts/evomaintenance.sh %wheel ALL=NOPASSWD: MAINT - _nrpe ALL=(root) NOPASSWD: /usr/local/libexec/nagios/plugins/check_ipsecctl.sh - _nrpe ALL=(root) NOPASSWD: /usr/local/libexec/nagios/check_mailq - _nrpe ALL=(root) NOPASSWD: /usr/local/libexec/nagios/plugins/check_ospfd_simple + %evolinux-sudo ALL=(ALL) SETENV: ALL validate: 'visudo -cf %s' - backup: no + backup: false tags: - - sudo - - + - sudo diff --git a/roles/base/templates/doas.conf.j2 b/roles/base/templates/doas.conf.j2 index 0d313a5..7653918 100644 --- a/roles/base/templates/doas.conf.j2 +++ b/roles/base/templates/doas.conf.j2 @@ -1,11 +1,18 @@ # {{ ansible_managed }} -permit setenv {ENV PS1 SSH_AUTH_SOCK SSH_TTY} :wheel +permit setenv {SSH_AUTH_SOCK SSH_TTY PKG_PATH HOME=/root ENV=/root/.profile} :{{ evobsd_sudo_group }} permit nopass root -permit setenv {ENV PS1 SSH_AUTH_SOCK SSH_TTY} nopass :wheel as root cmd /usr/share/scripts/evomaintenance.sh -permit nopass _nrpe cmd /usr/local/libexec/nagios/check_ipsecctl.sh -permit nopass _nrpe as root cmd /sbin/bioctl args sd2 -permit nopass _nrpe as root cmd /usr/local/libexec/nagios/check_openbgpd +permit setenv {ENV PS1 SSH_AUTH_SOCK SSH_TTY} nopass :{{ evobsd_ssh_group }} as root cmd /usr/share/scripts/evomaintenance.sh +permit nopass _collectd as root cmd /bin/cat permit nopass _collectd as root cmd /usr/sbin/bgpctl +permit nopass _nrpe as root cmd /sbin/bioctl args sd2 +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/check_mailq +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/check_dhcp +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_ipsecctl.sh +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_ospfd_simple permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_ospfd permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_ospf6d +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_openbgpd permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_pf_states +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_connections_state.sh +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_packetfilter.sh +permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_ipsecctl_critiques.sh diff --git a/roles/base/templates/evomaintenance.j2 b/roles/base/templates/evomaintenance.j2 index 79bc0cb..006d1c0 100644 --- a/roles/base/templates/evomaintenance.j2 +++ b/roles/base/templates/evomaintenance.j2 @@ -1,13 +1,20 @@ HOSTNAME={{ evomaintenance_hostname }} EVOMAINTMAIL={{ evomaintenance_alert_email or general_alert_email | mandatory }} -export PGPASSWORD={{ evomaintenance_pg_passwd | mandatory }} +export PGPASSWORD={{ evomaintenance_pg_passwd }} -PGDB={{ evomaintenance_pg_db | mandatory }} -PGTABLE={{ evomaintenance_pg_table | mandatory }} -PGHOST={{ evomaintenance_pg_host | mandatory }} +PGDB={{ evomaintenance_pg_db }} +PGTABLE={{ evomaintenance_pg_table }} +PGHOST={{ evomaintenance_pg_host }} FROM={{ evomaintenance_from }} FULLFROM="{{ evomaintenance_full_from }}" URGENCYFROM={{ evomaintenance_urgency_from }} URGENCYTEL="{{ evomaintenance_urgency_tel }}" REALM="{{ evomaintenance_realm }}" +API_ENDPOINT={{ evomaintenance_api_endpoint }} +API_KEY={{ evomaintenance_api_key }} + +HOOK_API={{ evomaintenance_hook_api | bool | ternary('1','0') }} +HOOK_DB={{ evomaintenance_hook_db | bool | ternary('1','0') }} +HOOK_COMMIT={{ evomaintenance_hook_commit | bool | ternary('1','0') }} +HOOK_MAIL={{ evomaintenance_hook_mail | bool | ternary('1','0') }} diff --git a/roles/base/templates/mailevomaintenance.sh.j2 b/roles/base/templates/mailevomaintenance.sh.j2 deleted file mode 100644 index d679ea5..0000000 --- a/roles/base/templates/mailevomaintenance.sh.j2 +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -set -eu - -cd /etc && _STATUS=$(/usr/local/bin/git status --porcelain) -[ -n "${_STATUS}" ] || exit 0 - -if [ -e /etc/realname ]; then - _HOSTNAME=$(/bin/cat /etc/realname) -else - _HOSTNAME=$(/bin/hostname) -fi - - -TMPFILE=$(/usr/bin/mktemp) || exit 1 -echo "Dear NOC,\n\nSome changes in /etc/ were not committed." >> $TMPFILE - -echo "" >> $TMPFILE -echo "${_STATUS}" >> $TMPFILE - -echo "" >> $TMPFILE -/usr/bin/last | head -n 10 >> $TMPFILE -echo "" >> $TMPFILE -echo "Please answer this mail to notify people when you've corrected the problem." >> $TMPFILE - -/bin/cat $TMPFILE | mail -s "Verif etc-git ${_HOSTNAME}" noc@{{ evomaintenance_realm }} - -/bin/rm $TMPFILE diff --git a/roles/base/templates/profile.j2 b/roles/base/templates/profile.j2 new file mode 100644 index 0000000..1b031ed --- /dev/null +++ b/roles/base/templates/profile.j2 @@ -0,0 +1,56 @@ +# $OpenBSD: dot.profile,v 1.5 2018/02/02 02:29:54 yasuoka Exp $ +# +# sh/ksh initialization + +PATH="{{ evobsd_path }}" +export PATH HOME TERM +export PS1="\u@\h:\w\\$ " +HISTFILE=$HOME/.histfile +export HISTSIZE=10000 +export HISTCONTROL='ignoredups:ignorespace' +export TMOUT=36000 +export PAGER=less +umask 022 + +export ENV='~/.kshrc' + +case "$-" in +*i*) # interactive shell + if [ -x /usr/bin/tset ]; then + if [ X"$XTERM_VERSION" = X"" ]; then + eval `/usr/bin/tset -sQ '-munknown:?vt220' $TERM` + else + eval `/usr/bin/tset -IsQ '-munknown:?vt220' $TERM` + fi + fi + ;; +esac + +PKG_LIST=$(ls -1 /var/db/pkg) + +set -A complete_kill_1 -- -9 -HUP -INFO -KILL -TERM +pgrep -q vmd +if [ $? = 0 ]; then + set -A complete_vmctl -- console load reload start stop reset status + set -A complete_vmctl_2 -- $(vmctl status | awk '!/NAME/{print $NF}') +fi +if [ -d ~/.password-store ]; then + PASS_LIST=$( + cd ~/.password-store + find . -type f -name \*.gpg | sed 's/^\.\///' | sed 's/\.gpg$//g' + ) + + set -A complete_pass -- $PASS_LIST -c generate edit insert git + set -A complete_pass_2 -- $PASS_LIST push +fi +set -A complete_pkg_delete -- $PKG_LIST +set -A complete_pkg_info -- $PKG_LIST +set -A complete_rcctl_1 -- disable enable get ls order set reload check restart stop start +set -A complete_rcctl_2 -- $(ls /etc/rc.d) +set -A complete_signify_1 -- -C -G -S -V +set -A complete_signify_2 -- -q -p -x -c -m -t -z +set -A complete_signify_3 -- -p -x -c -m -t -z +set -A complete_make_1 -- install clean repackage reinstall +set -A complete_gpg2 -- --refresh --receive-keys --armor --clearsign --sign --list-key --decrypt --verify --detach-sig +set -A complete_git -- pull push mpull mpush status clone branch add rm checkout fetch show tag commit +set -A complete_ifconfig_1 -- $(ifconfig | grep ^[a-z] | cut -d: -f1) diff --git a/roles/bgp/README.md b/roles/bgp/README.md new file mode 100644 index 0000000..cd6545d --- /dev/null +++ b/roles/bgp/README.md @@ -0,0 +1,14 @@ +# BGP + +Deployment of BGP check script with its cron, and a best route log cron. + +## Tasks + +Everything is in the `tasks/main.yml` file. + +## Available variables + +The full list of variables (with default values) can be found in `defaults/main.yml`. + +* `bgp_mailto` : email address the output of the script will be sent to when a change is detected +* `bgp_exclude_grep_command` : facultative grep -v command for some peers not to be checked diff --git a/roles/bgp/defaults/main.yml b/roles/bgp/defaults/main.yml new file mode 100644 index 0000000..8279e96 --- /dev/null +++ b/roles/bgp/defaults/main.yml @@ -0,0 +1,3 @@ +--- +bgp_mailto: "foobar@example.com" +bgp_exclude_grep_command: "" diff --git a/roles/bgp/tasks/main.yml b/roles/bgp/tasks/main.yml new file mode 100644 index 0000000..9b4c404 --- /dev/null +++ b/roles/bgp/tasks/main.yml @@ -0,0 +1,52 @@ +--- +- name: Deploy bgp check script + template: + src: bgpd-check-peers.sh.j2 + dest: /usr/share/scripts/bgpd-check-peers.sh + when: group_names | select('search','bgp') | list | count > 0 + tags: + - bgp + +- name: Cron job for bgp check script is installed + cron: + name: bgp check + job: "/bin/sh /usr/share/scripts/bgpd-check-peers.sh" + when: group_names | select('search','bgp') | list | count > 0 + tags: + - bgp + +- name: Create bgp log directory + file: + path: /var/log/bgp + state: directory + when: group_names | select('search','bgp') | list | count > 0 + tags: + - bgp + +- name: daily best routes cron job is installed + cron: + name: bgp best routes + minute: 0 + hour: 4 + job: > + /usr/sbin/bgpctl show rib selected + > /var/log/bgp/rib-selected-$(date +\%F) + when: group_names | select('search','bgp') | list | count > 0 + tags: + - bgp + +- name: weekly best routes clean up cron job is installed + cron: + name: bgp best routes clean up + minute: 0 + hour: 4 + weekday: 0 + job: > + /usr/bin/find /var/log/bgp/ + -type f + -name "rib-selected-*" + -mtime +30 + -exec rm {} \+ + when: group_names | select('search','bgp') | list | count > 0 + tags: + - bgp diff --git a/roles/bgp/templates/bgpd-check-peers.sh.j2 b/roles/bgp/templates/bgpd-check-peers.sh.j2 new file mode 100755 index 0000000..250ed54 --- /dev/null +++ b/roles/bgp/templates/bgpd-check-peers.sh.j2 @@ -0,0 +1,117 @@ +#!/bin/ksh + +# Script writen by Daniel Jakots + +# First we go through the list of neighbor and we write all the peer and +# their status in "${_TMPDIR}"/bgp-status. + +# Then we monitor if this file has changed between now and the previous run. + +# If it did, we send a mail with the states of the different sessions. + +set -u + +PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:. + +_MAILTO="{{ bgp_mailto }}" +_TMPDIR=/tmp/check-bgp +_PIDFILE="${_TMPDIR}"/bgpd-check-peers.pid + + +if [ -e /etc/realname ]; then + _REALNAME=$(cat /etc/realname) + _HOSTNAME=$(hostname -s) +else + _HOSTNAME=$(hostname) +fi + +mkdir -p "${_TMPDIR}" + +# Don't try to run if it's already running +if [ -e "${_PIDFILE}" ]; then + echo "$(date)" >> "${_TMPDIR}"/log + exit 1 +else + echo $$ >> "${_PIDFILE}" +fi + +# Create an history +if [[ -f "${_TMPDIR}"/bgp-status ]] ; then + mv "${_TMPDIR}"/bgp-status "${_TMPDIR}"/bgp-status.old +else + touch "${_TMPDIR}"/bgp-status + touch "${_TMPDIR}"/bgp-status.old +fi + +# List peers and loops on them to list them and their BGP state +bgpctl show neighbor | grep Description {{ bgp_exclude_grep_command }} | sed s,\ Description:\ ,,g > "${_TMPDIR}"/peers-list + +while read _PEER +do + _STATUS=$(/usr/sbin/bgpctl show neighbor "${_PEER}" | grep state | awk '{print $4}' |tr -d ',') + echo -n "${_PEER}" >> "${_TMPDIR}"/bgp-status + echo -n " " >> "${_TMPDIR}"/bgp-status + # we note only if it's established or not + if ! [[ "${_STATUS}" = "Established" ]] ; then + _STATUS="NotEstablished" + fi + echo "${_STATUS}" >> "${_TMPDIR}"/bgp-status + +done <"${_TMPDIR}"/peers-list + +# Check for difference with previous run +different=$(diff -q "${_TMPDIR}"/bgp-status.old "${_TMPDIR}"/bgp-status) + +if ! [[ -n "${different}" ]] ; then + rm -f "${_PIDFILE}" + exit 0 +fi + +# It changed so we're going to send a mail + +_TMPMAILDIR="${_TMPDIR}"/mail +mkdir -p "${_TMPMAILDIR}" + +# go through sessions and list them depending on their BGP state +echo "*** Session(s) OK ***\n" >> "${_TMPMAILDIR}"/bodyok +while read _LINE +do + # _LINE is session + status + _STATUS=$(echo "${_LINE##* }") + _SESSION=$(echo "${_LINE}" | awk '{$NF=""}1') + if [[ "${_STATUS}" = "Established" ]] ; then + bgpctl show | grep "${_SESSION}" >> "${_TMPMAILDIR}"/bodyok + else + bgpctl show | grep "${_SESSION}" >> "${_TMPMAILDIR}"/bodynok + fi +done <"${_TMPDIR}"/bgp-status + +# create the mail body + +echo "Dear NOC,\n\nThe state of one or more BGP session(s) has changed:\n" > "${_TMPMAILDIR}"/header +cat "${_TMPMAILDIR}"/header "${_TMPMAILDIR}"/bodyok > "${_TMPMAILDIR}"/body + +_STATE="OK" +if [[ -f "${_TMPMAILDIR}"/bodynok ]] ; then + _STATE="NOT OK" + echo "\n*** Session(s) on error ***\n" >> "${_TMPMAILDIR}"/body + cat "${_TMPMAILDIR}"/bodynok >> "${_TMPMAILDIR}"/body +fi + +# show a diff +echo "" >> "${_TMPMAILDIR}"/body +echo "Diff is " >> "${_TMPMAILDIR}"/body +diff -U0 "${_TMPDIR}"/bgp-status.old "${_TMPDIR}"/bgp-status >> "${_TMPMAILDIR}"/body + +# Send the mail whether we have a realname or not +if [ -n "${_REALNAME}" ]; then + cat "${_TMPMAILDIR}"/body | mail -s "[BGP] ${_REALNAME} (${_HOSTNAME}) - State change - ${_STATE}" "${_MAILTO}" +else + cat "${_TMPMAILDIR}"/body | mail -s "[BGP] ${_HOSTNAME} - State change" "${_MAILTO}" +fi + +# cleaning +if [[ -d "${_TMPMAILDIR}" ]] ; then + rm -rf "${_TMPMAILDIR}" +fi +rm -f "${_PIDFILE}" diff --git a/roles/collectd/README.md b/roles/collectd/README.md new file mode 100644 index 0000000..e63b47b --- /dev/null +++ b/roles/collectd/README.md @@ -0,0 +1,13 @@ +# Collectd + +Installation and custom configuration of Collectd daemon. + +## Tasks + +Everything is in the `tasks/main.yml` file. + +## Available variables + +The full list of variables (with default values) can be found in `defaults/main.yml`. + +* `collectd_server` : server to which the data will be sent (default: 127.0.0.1). diff --git a/roles/collectd/defaults/main.yml b/roles/collectd/defaults/main.yml new file mode 100644 index 0000000..7974087 --- /dev/null +++ b/roles/collectd/defaults/main.yml @@ -0,0 +1,31 @@ +--- + +# destination server + +collectd_server: "127.0.0.1" + +# execution interval + +collectd_interval: "300" + +# exec plugin + +collectd_plugin_exec_interval: "{{ collectd_interval }}" +collectd_plugin_exec_ifq_drops: false +collectd_plugin_exec_dns_stats: false # Based on unbound +collectd_plugin_exec_dns_stats_interval: "{{ collectd_interval }}" + +# others plugins + +collectd_plugin_cpu: true +collectd_plugin_df: true +collectd_plugin_disk: true +collectd_plugin_interface: true +collectd_plugin_load: true +collectd_plugin_memory: true +collectd_plugin_pf: true +collectd_plugin_processes: true +collectd_plugin_swap: true +collectd_plugin_tcpconns: true +collectd_plugin_uptime: true +collectd_plugin_users: true diff --git a/roles/collectd/files/dns_stats.sh b/roles/collectd/files/dns_stats.sh new file mode 100755 index 0000000..8a11d3d --- /dev/null +++ b/roles/collectd/files/dns_stats.sh @@ -0,0 +1,3 @@ +#!/bin/ksh + +echo "PUTVAL $(hostname)/dns_stats/count N:$(doas /bin/cat /var/log/daemon | grep "server stats" | grep -v "requestlist max" | awk '{print $13}' | tail -1)" diff --git a/roles/collectd/files/ifq_drops.sh b/roles/collectd/files/ifq_drops.sh new file mode 100755 index 0000000..25748f4 --- /dev/null +++ b/roles/collectd/files/ifq_drops.sh @@ -0,0 +1,3 @@ +#!/bin/ksh + +echo "PUTVAL $(hostname)/ifq_drops/count N:$(sysctl net.inet.ip.arpq.drops | awk -F= '{print $NF}')" diff --git a/roles/collectd/handlers/main.yml b/roles/collectd/handlers/main.yml new file mode 100644 index 0000000..00523b7 --- /dev/null +++ b/roles/collectd/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: restart collectd + service: + name: collectd + state: restarted + +- name: reload unbound + service: + name: unbound + state: reloaded diff --git a/roles/collectd/tasks/main.yml b/roles/collectd/tasks/main.yml new file mode 100644 index 0000000..28a2c46 --- /dev/null +++ b/roles/collectd/tasks/main.yml @@ -0,0 +1,97 @@ +--- +- name: Install Collectd package + openbsd_pkg: + name: "collectd" + tags: + - collectd + +- name: Deploy Collectd configuration + template: + src: "collectd.conf.j2" + dest: "/etc/collectd.conf" + notify: restart collectd + tags: + - collectd + +- name: Enabling Collectd + service: + name: collectd + enabled: true + tags: + - collectd + +- name: Create scripts directory for exec plugins + file: + path: /usr/local/share/collectd/scripts + state: directory + when: collectd_plugin_exec_ifq_drops or collectd_plugin_exec_dns_stats + tags: + - collectd + +- name: Copy ifq_drops.sh + copy: + src: ifq_drops.sh + dest: /usr/local/share/collectd/scripts/ifq_drops.sh + mode: 0755 + force: true + when: collectd_plugin_exec_ifq_drops + tags: + - collectd + +- name: Remove ifq_drops.sh + file: + path: /usr/local/share/collectd/scripts/ifq_drops.sh + state: absent + when: not collectd_plugin_exec_ifq_drops + tags: + - collectd + +- name: Copy dns_stats.sh + copy: + src: dns_stats.sh + dest: /usr/local/share/collectd/scripts/dns_stats.sh + mode: 0755 + force: true + when: collectd_plugin_exec_dns_stats + tags: + - collectd + +- name: Add stats DNS on unbound + lineinfile: + path: /var/unbound/etc/unbound.conf + regexp: 'statistics-interval' + line: + ' statistics-interval: {{ collectd_plugin_exec_dns_stats_interval }}' + insertafter: 'hide-version:' + backup: true + notify: reload unbound + when: collectd_plugin_exec_dns_stats + tags: + - collectd + +- name: Remove dns_stats.sh + file: + path: /usr/local/share/collectd/scripts/dns_stats.sh + state: absent + when: not collectd_plugin_exec_dns_stats + tags: + - collectd + +- name: Remove stats DNS on unbound + lineinfile: + path: /var/unbound/etc/unbound.conf + regexp: 'statistics-interval' + backup: true + state: absent + notify: reload unbound + when: not collectd_plugin_exec_dns_stats + tags: + - collectd + +- name: Add doas configuration for dns_stats.sh execution + lineinfile: + path: /etc/doas.conf + line: 'permit nopass _collectd as root cmd /bin/cat' + when: collectd_plugin_exec_dns_stats + tags: + - collectd diff --git a/roles/collectd/templates/collectd.conf.j2 b/roles/collectd/templates/collectd.conf.j2 new file mode 100644 index 0000000..2cae0ac --- /dev/null +++ b/roles/collectd/templates/collectd.conf.j2 @@ -0,0 +1,122 @@ +Interval {{ collectd_interval }} +Timeout 2 + +LoadPlugin syslog + + LogLevel warning + + +{% if (collectd_plugin_exec_ifq_drops is sameas true) or (collectd_plugin_exec_dns_stats is sameas true) %} + + Interval {{ collectd_plugin_exec_interval }} + + + +{% if collectd_plugin_exec_ifq_drops is sameas true %} + Exec "_collectd" "/usr/local/share/collectd/scripts/ifq_drops.sh" +{% endif %} +{% if collectd_plugin_exec_dns_stats is sameas true %} + Exec "_collectd" "/usr/local/share/collectd/scripts/dns_stats.sh" +{% endif %} + + +{% endif %} +{% if collectd_plugin_load is sameas true %} +LoadPlugin load +{% endif %} +{% if collectd_plugin_processes is sameas true %} +LoadPlugin processes +{% endif %} +{% if collectd_plugin_uptime is sameas true %} +LoadPlugin uptime +{% endif %} +{% if collectd_plugin_users is sameas true %} +LoadPlugin users +{% endif %} +{% if collectd_plugin_pf is sameas true %} +LoadPlugin pf +{% endif %} + +{% if collectd_plugin_df is sameas true %} +LoadPlugin df + + # expose host's mounts into container using -v /:/host:ro (location inside container does not matter much) + # ignore rootfs; else, the root file-system would appear twice, causing + # one of the updates to fail and spam the log + ## Seems to be fixed with collectd 5.5+ + ## FSType rootfs + # ignore the usual virtual / temporary file-systems + FSType sysfs + FSType proc + FSType devtmpfs + FSType devpts + FSType tmpfs + FSType fusectl + FSType cgroup + FSType overlay + FSType debugfs + FSType pstore + FSType securityfs + FSType hugetlbfs + FSType squashfs + FSType mqueue + IgnoreSelected true + + ReportByDevice false + ReportInodes true # Default false + ValuesAbsolute true + ValuesPercentage true + + +{% endif %} +{% if collectd_plugin_disk is sameas true %} +LoadPlugin disk + + #Disk "/^[hsv]d[a-z]/" + IgnoreSelected false + + +{% endif %} +{% if collectd_plugin_cpu is sameas true %} +LoadPlugin cpu + + ValuesPercentage true + + +{% endif %} +{% if collectd_plugin_memory is sameas true %} +LoadPlugin memory + + ValuesPercentage true + + +{% endif %} +{% if collectd_plugin_swap is sameas true %} +LoadPlugin swap + + ValuesPercentage true + + +{% endif %} +{% if collectd_plugin_interface is sameas true %} +LoadPlugin interface + + Interface "/^lo[0-9]*/" + Interface "/^veth.*/" + Interface "/^docker.*/" + IgnoreSelected true + ReportInactive false + + +{% endif %} +{% if collectd_plugin_tcpconns is sameas true %} +LoadPlugin tcpconns + + AllPortsSummary true + + +{% endif %} +LoadPlugin network + + Server "{{ collectd_server }}" "25826" + diff --git a/roles/etc-git/defaults/main.yml b/roles/etc-git/defaults/main.yml index 8a822ab..f160858 100644 --- a/roles/etc-git/defaults/main.yml +++ b/roles/etc-git/defaults/main.yml @@ -1,4 +1,4 @@ --- commit_message: Ansible run -etc_git_monitor_status: True +etc_git_monitor_status: true diff --git a/roles/etc-git/tasks/commit.yml b/roles/etc-git/tasks/commit.yml index e4166e7..95ab89a 100644 --- a/roles/etc-git/tasks/commit.yml +++ b/roles/etc-git/tasks/commit.yml @@ -3,20 +3,20 @@ command: git status --porcelain args: chdir: /etc - changed_when: False + changed_when: false register: git_status when: not ansible_check_mode - ignore_errors: yes + ignore_errors: true tags: - - etc-git - - commit-etc + - etc-git + - commit-etc - debug: var: git_status verbosity: 3 tags: - - etc-git - - commit-etc + - etc-git + - commit-etc - name: fetch current Git user.email git_config: @@ -24,33 +24,49 @@ repo: /etc scope: local register: git_config_user_email - ignore_errors: yes + ignore_errors: true tags: - - etc-git - - commit-etc + - etc-git + - commit-etc - name: set commit author set_fact: - commit_author: '{% if ansible_env.SUDO_USER is not defined %}root{% else %}{{ ansible_env.SUDO_USER }}{% endif %}' - commit_email: '{% if git_config_user_email.config_value is not defined or git_config_user_email.config_value == "" %}root@localhost{% else %}{{ git_config_user_email.config_value }}{% endif %}' + commit_author: > + {% if ansible_env.SUDO_USER is not defined %} + root + {% else %} + {{ ansible_env.SUDO_USER }} + {% endif %} + commit_email: > + {% if git_config_user_email.config_value is not defined + or git_config_user_email.config_value == "" %} + root@localhost + {% else %} + {{ git_config_user_email.config_value }} + {% endif %} tags: - - etc-git - - commit-etc + - etc-git + - commit-etc - name: /etc modifications are committed - shell: "git add -A . && git commit -m \"{{ commit_message | mandatory }}\" --author \"{{ commit_author | mandatory }} <{{ commit_email | mandatory }}>\"" + shell: > + git add -A . + && git commit + -m "{{ commit_message | mandatory }}" + --author + "{{ commit_author | mandatory }} <{{ commit_email | mandatory }}>" args: chdir: /etc register: etc_commit_end_run when: not ansible_check_mode and git_status.stdout != "" - ignore_errors: yes + ignore_errors: true tags: - - etc-git - - commit-etc + - etc-git + - commit-etc - debug: var: etc_commit_end_run verbosity: 4 tags: - - etc-git - - commit-etc + - etc-git + - commit-etc diff --git a/roles/etc-git/tasks/main.yml b/roles/etc-git/tasks/main.yml index 1068842..f36f9d6 100644 --- a/roles/etc-git/tasks/main.yml +++ b/roles/etc-git/tasks/main.yml @@ -12,7 +12,7 @@ args: chdir: /etc creates: /etc/.git/ - warn: no + warn: false register: git_init tags: - etc-git @@ -22,7 +22,7 @@ name: user.email repo: /etc scope: local - value: "root@{{ ansible_fqdn | default('localhost') }}" + value: "root@{{ inventory_hostname }}.{{ general_technical_realm }}" tags: - etc-git @@ -48,11 +48,11 @@ command: "git log" args: chdir: /etc - warn: no - changed_when: False - failed_when: False + warn: false + changed_when: false + failed_when: false register: git_log - check_mode: no + check_mode: false tags: - etc-git @@ -60,7 +60,7 @@ shell: "git add -A . && git commit -m \"Initial commit via Ansible\"" args: chdir: /etc - warn: no + warn: false register: git_commit when: git_log.rc != 0 or (git_init is defined and git_init.changed) tags: @@ -72,17 +72,31 @@ line: '/usr/local/bin/git --git-dir /etc/.git gc --quiet' owner: root mode: "0644" - create: yes + create: true tags: - etc-git - name: cron job for /etc/.git status is installed lineinfile: path: /etc/daily.local - line: '/usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short' + line: + '/usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short' owner: root mode: "0644" - create: yes + create: true + when: etc_git_monitor_status + tags: + - etc-git + +- name: cron job for /etc/.git status is installed - next_part + lineinfile: + path: /etc/daily.local + line: 'next_part "Checking /etc git status:"' + insertbefore: + '/usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short' + owner: root + mode: "0644" + create: true when: etc_git_monitor_status tags: - etc-git @@ -90,10 +104,13 @@ - name: cron job for /etc/.git status is removed lineinfile: path: /etc/daily.local - line: '/usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short' + line: "{{ item }}" owner: root mode: "0644" state: absent + with_items: + - 'next_part "Checking /etc git status:"' + - '/usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short' when: not etc_git_monitor_status tags: - etc-git @@ -102,7 +119,13 @@ cron: name: git status minute: 42 - job: "who > /dev/null || /usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short" + job: > + who + > /dev/null + || /usr/local/bin/git + --git-dir=/etc/.git + --work-tree=/etc + status --short when: etc_git_monitor_status tags: - etc-git @@ -111,7 +134,13 @@ cron: name: git status minute: 42 - job: "who > /dev/null || /usr/local/bin/git --git-dir=/etc/.git --work-tree=/etc status --short" + job: > + who + > /dev/null + || /usr/local/bin/git + --git-dir=/etc/.git + --work-tree=/etc + status --short state: absent when: not etc_git_monitor_status tags: diff --git a/roles/evocheck/README.md b/roles/evocheck/README.md new file mode 100644 index 0000000..a486e2c --- /dev/null +++ b/roles/evocheck/README.md @@ -0,0 +1,13 @@ +# evocheck + +Install and run evocheck ; a script for checking various settings automatically. + +## Tasks + +A separate `exec.yml` file can be imported manually in playbooks or roles to execute the script. Example : + +``` +- include_role: + name: evolix/evocheck + tasks_from: exec.yml +``` diff --git a/roles/evocheck/defaults/main.yml b/roles/evocheck/defaults/main.yml new file mode 100644 index 0000000..8160768 --- /dev/null +++ b/roles/evocheck/defaults/main.yml @@ -0,0 +1,2 @@ +--- +evocheck_bin_dir: /usr/share/scripts diff --git a/roles/evocheck/files/evocheck.cf b/roles/evocheck/files/evocheck.cf new file mode 100644 index 0000000..9eca204 --- /dev/null +++ b/roles/evocheck/files/evocheck.cf @@ -0,0 +1,5 @@ +# Managed by Ansible +# +# Configuration for evocheck +# Use this file to change configuration values defined in evocheck.sh +# Ex : IS_TMP_1777=0 diff --git a/roles/evocheck/files/evocheck.sh b/roles/evocheck/files/evocheck.sh new file mode 100644 index 0000000..73a7ef7 --- /dev/null +++ b/roles/evocheck/files/evocheck.sh @@ -0,0 +1,437 @@ +#!/bin/sh + +# EvoCheck +# Script to verify compliance of an OpenBSD server powered by Evolix + +readonly VERSION="6.7.7" + +# Disable LANG* + +export LANG=C +export LANGUAGE=C + + +# Default return code : 0 = no error +RC=0 + +# Verbose function +verbose() { + msg="${1:-$(cat /dev/stdin)}" + [ "${VERBOSE}" -eq 1 ] && [ -n "${msg}" ] && echo "${msg}" +} + +# Source configuration file +test -f /etc/evocheck.cf && . /etc/evocheck.cf + +# Functions + +show_help() { + cat < + Gregory Colpart + Jeremy Dubois + Jeremy Lecour + Ludovic Poujol + Romain Dessort + Tristan Pilat + Victor Laborie + +USAGE: evocheck + or evocheck --cron + or evocheck --quiet + or evocheck --verbose + +OPTIONS: + --cron disable a few checks + -v, --verbose increase verbosity of checks + -q, --quiet nothing is printed on stdout nor stderr + -h, --help, --version print this message and exit + +COPYRIGHT: + evocheck comes with ABSOLUTELY NO WARRANTY. This is free software, + and you are welcome to redistribute it under certain conditions. + See the GNU General Public License v3.0 for details. 2009-2020 +END +} + +is_installed(){ + for pkg in "$@"; do + pkg_info | grep -q $pkg || return 1 + done +} + +# logging +failed() { + check_name=$1 + shift + check_comments=$* + + RC=1 + if [ "${QUIET}" != 1 ]; then + if [ -n "${check_comments}" ] && [ "${VERBOSE}" = 1 ]; then + printf "%s FAILED! %s\n" "${check_name}" "${check_comments}" 2>&1 + else + printf "%s FAILED!\n" "${check_name}" 2>&1 + fi + fi +} + + +# If --cron is passed, ignore some checks. +if [ "$1" = "--cron" ]; then + IS_KERNELUPTODATE=0 + IS_UPTIME=0 +fi + +check_umasksudoers(){ + grep -E -qr "umask=0077" /etc/sudoers* || failed "IS_UMASKSUDOERS" "sudoers must set umask to 0077" +} + +check_tmpnoexec(){ + mount | grep "on /tmp" | grep -q noexec || failed "IS_TMPNOEXEC" "/tmp should be mounted with the noexec option" +} + +check_softdep(){ + if [ $(grep -c softdep /etc/fstab) -ne $(grep -c ffs /etc/fstab) ]; then + failed "IS_SOFTDEP" "All partitions should have the softdep option" + fi +} + +check_noatime(){ + if [ $(mount | grep -c noatime) -ne $(grep -c ffs /etc/fstab) ]; then + failed "IS_NOATIME" "All partitions should be mounted with the noatime option" + fi +} + +check_tmoutprofile(){ + grep -q TMOUT= /etc/skel/.profile /root/.profile || failed "IS_TMOUTPROFILE" "In order to fix, add 'export TMOUT=36000' to both /etc/skel/.profile and /root/.profile files" +} + +check_raidok(){ + egrep 'sd.*RAID' /var/run/dmesg.boot 1> /dev/null 2>&1 + RESULT=$? + if [ $RESULT -eq 0 ]; then + raid_device=$(egrep 'sd.*RAID' /var/run/dmesg.boot | awk '{ print $1 }' | tail -1) + raid_status=$(bioctl $raid_device | grep softraid | awk '{ print $3 }') + if [ $raid_status != "Online" ]; then + failed "IS_RAIDOK" "One of the RAID disk members is faulty. Use bioctl -h $raid_device for more informations" + fi + fi +} + +check_evobackup(){ + if [ -f /etc/daily.local ]; then + grep -qE "^sh /usr/share/scripts/zzz_evobackup" /etc/daily.local || failed "IS_EVOBACKUP" "Make sure 'sh /usr/share/scripts/zzz_evobackup' is present and activated in /etc/daily.local" + else + failed "IS_EVOBACKUP" "Make sure /etc/daily.local exists and 'sh /usr/share/scripts/zzz_evobackup' is present and activated in /etc/daily.local" + fi +} + +check_uptodate(){ + if [ $(command -v syspatch) ]; then + if syspatch -c | egrep "." 1> /dev/null 2>&1; then + failed "IS_UPTODATE" "Security update available! Update with syspatch(8)!" + fi + fi +} + +check_uptime(){ + if [ $(uptime | cut -d" " -f 4) -gt 365 ]; then + failed "IS_UPTIME" "The server is running for more than a year!" + fi +} + +check_backupuptodate(){ + backup_dir="/home/backup" + if [ -d "${backup_dir}" ]; then + if [ -n "$(ls -A ${backup_dir})" ]; then + for file in ${backup_dir}/*; do + let "limit = $(date +"%s") - 172800" + updated_at=$(stat -f "%m" "$file") + + if [ -f "$file" ] && [ "$limit" -gt "$updated_at" ]; then + failed "IS_BACKUPUPTODATE" "$file has not been backed up" + test "${VERBOSE}" = 1 || break; + fi + done + else + failed "IS_BACKUPUPTODATE" "${backup_dir}/ is empty" + fi + else + failed "IS_BACKUPUPTODATE" "${backup_dir}/ is missing" + fi +} + +check_gitperms(){ + test -d /etc/.git && [ "$(stat -f %p /etc/.git/)" = "40700" ] || failed "IS_GITPERMS" "The directiry /etc/.git sould be in 700" +} + +check_advbase(){ + if ls /etc/hostname.carp* 1> /dev/null 2>&1; then + bad_advbase=0 + for advbase in $(ifconfig carp | grep advbase | awk -F 'advbase' '{print $2}' | awk '{print $1}' | xargs); do + if [[ "$advbase" -gt 5 ]]; then + bad_advbase=1 + fi + done + if [[ "$bad_advbase" -eq 1 ]]; then + failed "IS_ADVBASE" "At least one CARP interface has advbase greater than 5 seconds!" + fi + fi +} + +check_preempt(){ + if ls /etc/hostname.carp* 1> /dev/null 2>&1; then + preempt=$(sysctl net.inet.carp.preempt | cut -d"=" -f2) + if [[ "$preempt" -ne 1 ]]; then + failed "IS_PREEMPT" "The preempt function is not activated! Please type 'sysctl net.inet.carp.preempt=1' in" + fi + if [ -f /etc/sysctl.conf ]; then + grep -qE "^net.inet.carp.preempt=1" /etc/sysctl.conf || failed "IS_PREEMPT" "The preempt parameter is not permanently activated! Please add 'net.inet.carp.preempt=1' in /etc/sysctl.conf" + else + failed "IS_PREEMPT" "Make sure /etc/sysctl.conf exists and contains the line 'net.inet.carp.preempt=1'" + fi + fi +} + +check_rebootmail(){ + if [ -f /etc/rc.local ]; then + grep -qE '^date \| mail -s "boot/reboot of' /etc/rc.local || failed "IS_REBOOTMAIL" "Make sure the line 'date | mail -s \"boot/reboot of \$hostname' is present in the /etc/rc.local file!" + else + failed "IS_REBOOTMAIL" "Make sure /etc/rc.local exist and 'date | mail -s \"boot/reboot of \$hostname' is present!" + fi +} + +check_pfenabled(){ + if pfctl -si | grep Disabled 1> /dev/null 2>&1; then + failed "IS_PFENABLED" "PF is disabled! Make sure pf=NO is absent from /etc/rc.conf.local and carefully run pfctl -e" + fi +} + +check_pfcustom(){ +} + +check_wheel(){ + if [ -f /etc/sudoers ]; then + grep -qE "^%wheel.*$" /etc/sudoers || failed "IS_WHEEL" "" + fi +} + +check_pkgmirror(){ + grep -qE "^https://cdn\.openbsd\.org/pub/OpenBSD" /etc/installurl || failed "IS_PKGMIRROR" "Check whether the right repo is present in the /etc/installurl file" +} + +check_history(){ + file=/root/.profile + grep -qE "^HISTFILE=\$HOME/.histfile" $file && grep -qE "^export HISTSIZE=10000" $file || failed "IS_HISTORY" "Make sure both 'HISTFILE=$HOME/.histfile' and 'export HISTSIZE=10000' are present in /root/.profile" +} + +check_vim(){ + if ! is_installed vim; then + failed "IS_VIM" "vim is not installed! Please add with pkg_add vim" + fi +} + +check_ttyc0secure(){ + grep -Eqv "^ttyC0.*secure$" /etc/ttys || failed "IS_TTYC0SECURE" "First tty should be secured" +} + +check_customsyslog(){ + grep -q EvoBSD /etc/newsyslog.conf || failed "IS_CUSTOMSYSLOG" "" +} + +check_sudomaint(){ + file=/etc/sudoers + grep -q "Cmnd_Alias MAINT = /usr/share/scripts/evomaintenance.sh" $file \ + && grep -q "%wheel ALL=NOPASSWD: MAINT" $file \ + || failed "IS_SUDOMAINT" "" +} + +check_nrpe(){ + if ! is_installed monitoring-plugins || ! is_installed nrpe; then + failed "IS_NRPE" "nrpe and/or monitoring-plugins are not installed! Please add with pkg_add nrpe monitoring-plugins" + fi +} + +check_rsync(){ + if ! is_installed rsync; then + failed "IS_RSYNC" "rsync is not installed! Please add with pkg_add rsync" + fi +} + +check_cronpath(){ + grep -q "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/share/scripts" /var/cron/tabs/root || failed "IS_CRONPATH" "" +} + +check_tmp1777(){ + ls -ld /tmp | grep -q drwxrwxrwt || failed "IS_TMP_1777" "" +} + +check_root0700(){ + ls -ld /root | grep -q drwx------ || failed "IS_ROOT_0700" "" +} + +check_usrsharescripts(){ + ls -ld /usr/share/scripts | grep -q drwx------ || failed "IS_USRSHARESCRIPTS" "" +} + +check_sshpermitrootno() { + grep -qE ^PermitRoot /etc/ssh/sshd_config && ( grep -E -qi "PermitRoot.*no" /etc/ssh/sshd_config || failed "IS_SSHPERMITROOTNO" "" ) +} + +check_evomaintenanceusers(){ + # Can be changed in evocheck.cf + homeDir=${homeDir:-/home} + sudoers="/etc/sudoers" + for i in $( (grep "^User_Alias *ADMIN" $sudoers | cut -d= -f2 | tr -d " "; grep ^sudo /etc/group |cut -d: -f 4) | tr "," "\n" |sort -u); do + grep -qs "^trap.*sudo.*evomaintenance.sh" ${homeDir}/${i}/.*profile + if [ $? != 0 ]; then + failed "IS_EVOMAINTENANCEUSERS" "$i doesn't have evomaintenance trap!" + fi + done +} + +check_evomaintenanceconf(){ + file=/etc/evomaintenance.cf + ( test -e $file \ + && test $(stat -f %p $file) = "100600" \ + && grep "^export PGPASSWORD" $file |grep -qv "your-passwd" \ + && grep "^PGDB" $file |grep -qv "your-db" \ + && grep "^PGTABLE" $file |grep -qv "your-table" \ + && grep "^PGHOST" $file |grep -qv "your-pg-host" \ + && grep "^FROM" $file |grep -qv "jdoe@example.com" \ + && grep "^FULLFROM" $file |grep -qv "John Doe " \ + && grep "^URGENCYFROM" $file |grep -qv "mama.doe@example.com" \ + && grep "^URGENCYTEL" $file |grep -qv "06.00.00.00.00" \ + && grep "^REALM" $file |grep -qv "example.com" ) || failed "IS_EVOMAINTENANCECONF" "" +} + +check_sync(){ + if ifconfig carp | grep carp 1> /dev/null 2>&1; then + sync_script=/usr/share/scripts/sync.sh + if [ ! -f $sync_script ]; then + failed "IS_SYNC" "The sync.sh script is absent! As a carp member, a sync.sh script should be present in /usr/share/scripts" + fi + fi +} + +check_defaultroute(){ + if [ -f /etc/mygate ]; then + file_route=$(cat /etc/mygate) + used_route=$(route -n show -priority 8 | grep default | awk '{print $2}') + if [ "$file_route" != "$used_route" ]; then + failed "IS_DEFAULTROUTE" "The default route in /etc/mygate is different from the one currently used" + fi + else + failed "IS_DEFAULTROUTE" "The file /etc/mygate does not exist. Make sure you have the same default route in this file as the one currently in use." + fi +} + +check_ntp(){ + if grep -q "server ntp.evolix.net" /etc/ntpd.conf; then + if [ $(wc -l /etc/ntpd.conf | awk '{print $1}') -ne 1 ]; then + failed "IS_NTP" "The /etc/ntpd.conf file should only contains \"server ntp.evolix.net\"." + fi + else + failed "IS_NTP" "The configuration in /etc/ntpd.conf is not compliant. It should contains \"server ntp.evolix.net\"." + fi +} + +check_openvpncronlog(){ + if /etc/rc.d/openvpn check > /dev/null 2>&1; then + grep -q 'cp /var/log/openvpn.log /var/log/openvpn.log.$(date +\\%F) && echo "$(date +\\%F. .\\%R) - logfile turned over via cron" > /var/log/openvpn.log && gzip /var/log/openvpn.log.$(date +\\%F) && find /var/log/ -type f -name "openvpn.log.\*" -mtime .365 -exec rm {} \\+' /var/cron/tabs/root || failed "IS_OPENVPNCRONLOG" "OpenVPN is enabled but there is no log rotation in the root crontab, or the cron is not up to date (OpenVPN log rotation in newsyslog is not used because a restart is needed)." + fi +} + + +main() { + # Default return code : 0 = no error + RC=0 + + test "${IS_UMASKSUDOERS:=1}" = 1 && check_umasksudoers + test "${IS_TMPNOEXEC:=1}" = 1 && check_tmpnoexec + test "${IS_SOFTDEP:=1}" = 1 && check_softdep + test "${IS_NOATIME:=1}" = 1 && check_noatime + test "${IS_TMOUTPROFILE:=1}" = 1 && check_tmoutprofile + test "${IS_RAIDOK:=1}" = 1 && check_raidok + test "${IS_EVOBACKUP:=1}" = 1 && check_evobackup + test "${IS_UPTODATE:=1}" = 1 && check_uptodate + test "${IS_UPTIME:=1}" = 1 && check_uptime + test "${IS_BACKUPUPTODATE:=1}" = 1 && check_backupuptodate + test "${IS_GITPERMS:=1}" = 1 && check_gitperms + test "${IS_ADVBASE:=1}" = 1 && check_advbase + test "${IS_PREEMPT:=1}" = 1 && check_preempt + test "${IS_REBOOTMAIL:=1}" = 1 && check_rebootmail + test "${IS_PFENABLED:=1}" = 1 && check_pfenabled + test "${IS_PFCUSTOM:=1}" = 1 && check_pfcustom + test "${IS_WHEEL:=1}" = 1 && check_wheel + test "${IS_PKGMIRROR:=1}" = 1 && check_pkgmirror + test "${IS_HISTORY:=1}" = 1 && check_history + test "${IS_VIM:=1}" = 1 && check_vim + test "${IS_TTYC0SECURE:=1}" = 1 && check_ttyc0secure + test "${IS_CUSTOMSYSLOG:=1}" = 1 && check_customsyslog + test "${IS_SUDOMAINT:=1}" = 1 && check_sudomaint + test "${IS_NRPE:=1}" = 1 && check_nrpe + test "${IS_RSYNC:=1}" = 1 && check_rsync + test "${IS_CRONPATH:=1}" = 1 && check_cronpath + test "${IS_TMP_1777:=1}" = 1 && check_tmp1777 + test "${IS_ROOT_0700:=1}" = 1 && check_root0700 + test "${IS_USRSHARESCRIPTS:=1}" = 1 && check_usrsharescripts + test "${IS_SSHPERMITROOTNO:=1}" = 1 && check_sshpermitrootno + test "${IS_EVOMAINTENANCEUSERS:=1}" = 1 && check_evomaintenanceusers + test "${IS_EVOMAINTENANCECONF:=1}" = 1 && check_evomaintenanceconf + test "${IS_SYNC:=1}" = 1 && check_sync + test "${IS_DEFAULTROUTE:=1}" = 1 && check_defaultroute + test "${IS_NTP:=1}" = 1 && check_ntp + test "${IS_OPENVPNCRONLOG:=1}" = 1 && check_openvpncronlog + + exit ${RC} +} +# Parse options +# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a +while :; do + case $1 in + -h|-\?|--help|--version) + show_help + exit 0 + ;; + --cron) + IS_KERNELUPTODATE=0 + IS_UPTIME=0 + ;; + -v|--verbose) + VERBOSE=1 + ;; + -q|--quiet) + QUIET=1 + VERBOSE=0 + ;; + --) + # End of all options. + shift + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift +done + +main ${ARGS} diff --git a/roles/evocheck/tasks/exec.yml b/roles/evocheck/tasks/exec.yml new file mode 100644 index 0000000..d7f9ac6 --- /dev/null +++ b/roles/evocheck/tasks/exec.yml @@ -0,0 +1,15 @@ +--- +- name: run evocheck + command: "{{ evocheck_bin_dir }}/evocheck.sh" + register: evocheck_run + changed_when: false + failed_when: false + check_mode: false + tags: + - evocheck-exec + +- debug: + var: evocheck_run.stdout_lines + when: evocheck_run.stdout != "" + tags: + - evocheck-exec diff --git a/roles/evocheck/tasks/install.yml b/roles/evocheck/tasks/install.yml new file mode 100644 index 0000000..d67e430 --- /dev/null +++ b/roles/evocheck/tasks/install.yml @@ -0,0 +1,49 @@ +--- +- name: Scripts dir is present + file: + path: "{{ evocheck_bin_dir }}" + state: directory + owner: root + group: wheel + mode: "0700" + tags: + - evocheck + +- name: Copy evocheck.sh + copy: + src: evocheck.sh + dest: "{{ evocheck_bin_dir }}/evocheck.sh" + mode: "0700" + owner: root + force: true + tags: + - evocheck + +- name: Copy evocheck.cf + copy: + src: evocheck.cf + dest: /etc/evocheck.cf + force: false + tags: + - evocheck + +- name: Add evocheck cron + lineinfile: + path: /etc/daily.local + line: 'sh /usr/share/scripts/evocheck.sh --verbose --cron' + owner: root + mode: "0644" + create: true + tags: + - evocheck + +- name: Add evocheck cron next_part + lineinfile: + path: /etc/daily.local + line: 'next_part "Evocheck output:"' + insertbefore: 'sh /usr/share/scripts/evocheck.sh --verbose --cron' + owner: root + mode: "0644" + create: true + tags: + - evocheck diff --git a/roles/evocheck/tasks/main.yml b/roles/evocheck/tasks/main.yml new file mode 100644 index 0000000..db69018 --- /dev/null +++ b/roles/evocheck/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include: install.yml diff --git a/roles/forwarding/tasks/main.yml b/roles/forwarding/tasks/main.yml index d424c35..f97c4af 100644 --- a/roles/forwarding/tasks/main.yml +++ b/roles/forwarding/tasks/main.yml @@ -4,7 +4,7 @@ name: net.inet.ip.forwarding value: 1 state: present - reload: yes + reload: true tags: - net @@ -13,6 +13,6 @@ name: net.inet6.ip6.forwarding value: 1 state: present - reload: yes + reload: true tags: - net diff --git a/roles/nagios-nrpe/README.md b/roles/nagios-nrpe/README.md index 3f3f9a9..42dde0e 100644 --- a/roles/nagios-nrpe/README.md +++ b/roles/nagios-nrpe/README.md @@ -8,6 +8,6 @@ Everything is in the `tasks/main.yml` file. ## Available variables -* `nagios_nrpe_allowed_hosts` : list of IP/hosts authorized (default: none). +* `nagios_nrpe_allowed_hosts` : list of IP/hosts authorized (default: none). The full list of variables (with default values) can be found in `defaults/main.yml`. diff --git a/roles/nagios-nrpe/defaults/main.yml b/roles/nagios-nrpe/defaults/main.yml index 96c3ddd..c27efa8 100644 --- a/roles/nagios-nrpe/defaults/main.yml +++ b/roles/nagios-nrpe/defaults/main.yml @@ -2,7 +2,8 @@ evolix_trusted_ips: [] additional_trusted_ips: [] # Let's merge evolix_trusted_ips with additional_trusted_ips -nagios_nrpe_allowed_hosts: "{{ evolix_trusted_ips | union(additional_trusted_ips) | unique }}" +nagios_nrpe_allowed_hosts: + "{{ evolix_trusted_ips | union(additional_trusted_ips) | unique }}" nagios_nrpe_ldap_dc: "dc=DOMAIN,dc=EXT" nagios_nrpe_ldap_passwd: LDAP_PASSWD nagios_nrpe_pgsql_passwd: PGSQL_PASSWD diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_connections_state.sh b/roles/nagios-nrpe/files/plugins_bsd/check_connections_state.sh new file mode 100755 index 0000000..ac73313 --- /dev/null +++ b/roles/nagios-nrpe/files/plugins_bsd/check_connections_state.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +STATE=0 +MAIN_CONNECTION_PINGABLE_IP="31.170.8.95" +MAIN_CONNECTION_GATEWAY="IP" +MAIN_CONNECTION_IP="IP" +SECOND_CONNECTION_PINGABLE_IP="31.170.8.243" +INFO_MAIN_CONNECTION="IP - Description" +INFO_SECOND_CONNECTION="IP - Description" +CURRENT_GATEWAY=$(/usr/bin/netstat -nr | /usr/bin/grep "default" | /usr/bin/awk '{print $2}') + +IS_GATEWAY_IN_FILE=1 # Check whether /etc/mygate has the IP of main connection +IS_VPN_USING_MAIN_CONNECTION=1 # Check whether ipsecctl use the main connection +IS_PF_USING_MAIN_CONNECTION=1 # Check whether PacketFilter has route-to using the main connection +IS_MISCELLANEOUS=1 # Check miscellaneous things +CHECK_CARP=0 # No check if host is backup + +# No check if host is backup +if [ "${CHECK_CARP}" = 1 ]; then + CARP_STATUS=$(/sbin/ifconfig carp0 | /usr/bin/grep "status" | /usr/bin/awk '{print $2}') + if [ "$CARP_STATUS" = "backup" ]; then + echo "No check, I'm a backup" + exit 0 + fi +fi + +# If main connection is UP but not used => critical and continue +# If main connection is DOWN (used or not) => warning and exit +/sbin/ping -c1 -w1 ${MAIN_CONNECTION_PINGABLE_IP} >/dev/null 2>&1 +if [ $? = 0 ]; then + if [ "${CURRENT_GATEWAY}" != "${MAIN_CONNECTION_GATEWAY}" ]; then + echo "Main connection is UP but not used as gateway !" + STATE=2 + fi +else + echo "Main connection (${INFO_MAIN_CONNECTION}) is down" + STATE=1 + IS_GATEWAY_IN_FILE=0 + IS_VPN_USING_MAIN_CONNECTION=0 + IS_PF_USING_MAIN_CONNECTION=0 + IS_MISCELLANEOUS=0 +fi + +# If second connection is DOWN => critical and continue +/sbin/ping -c1 -w1 ${SECOND_CONNECTION_PINGABLE_IP} >/dev/null 2>&1 +if [ $? != 0 ]; then + echo "Second connection (${INFO_SECOND_CONNECTION}) is down" + STATE=2 +fi + +# Check whether /etc/mygate has the IP of main connection +if [ "${IS_GATEWAY_IN_FILE}" = 1 ]; then + /usr/bin/grep -q "${MAIN_CONNECTION_GATEWAY}" /etc/mygate + if [ $? != 0 ]; then + echo "Main connection is not set in /etc/mygate" + STATE=2 + fi +fi + +# Check whether ipsecctl use the main connection +if [ "${IS_VPN_USING_MAIN_CONNECTION}" = 1 ]; then + /sbin/ipsecctl -sa | /usr/bin/grep -q "${MAIN_CONNECTION_IP}" + if [ $? != 0 ]; then + echo "VPN is not using the main connection !" + STATE=2 + fi +fi + +# Check whether PacketFilter has route-to using the main connection +if [ "${IS_PF_USING_MAIN_CONNECTION}" = 1 ]; then + /sbin/pfctl -sr | /usr/bin/grep "route-to" | /usr/bin/grep -q "${MAIN_CONNECTION_GATEWAY}" + if [ $? != 0 ]; then + echo "PF is not using the main connection !" + STATE=2 + fi +fi + +# Check miscellaneous things +if [ "${IS_MISCELLANEOUS}" = 1 ]; then + echo +fi + +if [ "${STATE}" = 0 ]; then + echo "OK - Main connection is UP and used, second connection is UP" +fi + +exit ${STATE} diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_openbgpd b/roles/nagios-nrpe/files/plugins_bsd/check_openbgpd new file mode 100755 index 0000000..4e63480 --- /dev/null +++ b/roles/nagios-nrpe/files/plugins_bsd/check_openbgpd @@ -0,0 +1,401 @@ +#!/usr/bin/perl -T +# $AFresh1: check_openbgpd,v 1.10 2015/03/26 03:44:15 andrew Exp $ +######################################################################## +# check_openbgpd *** A nagios check for OpenBSD bgpd +# +# 2009.11.12 #*#*# andrew fresh +######################################################################## +# +# MODIFIED VERSION FOR THE NEEDS OF EVOLIX +# By Jérémy Dubois +# +# Line 51 : +# added « open STDERR, '>&STDOUT'; » +# +# Lines 123 to 126 : +# added « or exit 2; » +# commented « or die $! » and the 2 lines below +# +######################################################################## +use strict; +use warnings; + +use 5.010; +use if $] >= 5.016, experimental => 'switch'; + +local %ENV = (); + +my $NAGIOS_OUTPUT = 1; + +my $LICENSE = <<'EOL'; +Copyright (c) 2009-2015 Andrew Fresh +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +EOL + +my $PROGNAME = 'check_openbgpd'; +my $BGPCTL = '/usr/sbin/bgpctl'; + +use POSIX; +use Config; +my $PREFIX; +open STDERR, '>&STDOUT'; + +BEGIN { + ## no critic 'warnings' + no warnings 'uninitialized'; + $PREFIX = "/usr/local" || '/usr/local'; # Magic for OpenBSD ports tree +} +use lib $PREFIX . '/libexec/nagios'; +use utils qw($TIMEOUT %ERRORS &support); + +$SIG{'ALRM'} = sub { + print("ERROR: $PROGNAME timeout\n"); + exit $ERRORS{'UNKNOWN'}; +}; +alarm($TIMEOUT); + +my %CHECKS = getopt(@ARGV); +if ( !%CHECKS ) { + print_help(); + exit $ERRORS{'OK'}; +} + +my @STATUS = read_status( $CHECKS{_SOCKET} ); +my %STATES = check_status( \@STATUS, \%CHECKS ); + +my $have_results = 0; +my $state = 'OK'; +foreach + my $error ( reverse sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS ) +{ + if ( exists $STATES{$error} ) { + $have_results++; + $state = $error if $ERRORS{$state} < $ERRORS{$error}; + + if ($NAGIOS_OUTPUT) { + print $error . ' (' . scalar( @{ $STATES{$error} } ) . ')'; + if ( $error ne 'OK' ) { + print '
'; + print map {" - $_
"} @{ $STATES{$error} }; + } + } + else { + print $error . ' (' . scalar( @{ $STATES{$error} } ) . "):\n"; + foreach ( @{ $STATES{$error} } ) { + print " $_\n"; + } + } + } +} +if ( $have_results == 0 ) { + print "No results found\n"; +} +exit $ERRORS{$state}; + +sub read_status { + my ($socket) = @_; + my @S; + + my @cmd = ($BGPCTL); + if ($socket) { + push @cmd, '-s', $socket; + } + push @cmd, 'show', 'summary'; + + #open my $fh, '<', 'output' # XXX + open my $fh, '-|', @cmd or die "Couldn't open bgpctl: $!\n"; + while (<$fh>) { + chomp; + push @S, parse_line($_); + } + ## no critic 'die' + close $fh + or exit 2; +# or die $! +# ? "Error closing sysctl pipe: $!\n" +# : "Exit status $? from sysctl\n"; + + return grep { exists $_->{neighbor} && $_->{as} ne 'AS' } @S; +} + +sub parse_line { + my ($c) = @_; + my ( $neighbor, $as, $rcvd, $sent, $outq, $updown, $state, ) + = $c + =~ /^(.*?)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/xms; + return { + neighbor => $neighbor, + as => $as, + rcvd => $rcvd, + sent => $sent, + outq => $outq, + updown => $updown, + state => $state, + line => $c, + }; +} + +sub parse_check { + my $check = shift; + + return { match => [] } unless $check; + my @values = split /,\s*/xms, $check; + + my %c = ( match => [] ); + foreach my $v (@values) { + if ( $v =~ /:/xms ) { + ( $c{low}, $c{high} ) = split /:/xms, $v; + } + else { + push @{ $c{match} }, $v; + } + } + + foreach my $d ( 'low', 'high' ) { + if ( defined $c{$d} ) { + $c{$d} =~ s/[^-\d\.\%]//gxms; + if ( !length $c{$d} ) { + delete $c{$d}; + } + } + } + + return \%c; +} + +sub check_status { + my ( $S, $C ) = @_; + + my %states; + my %neighbors = map { $_ => $C->{$_} } qw( _SOCKET _UNKNOWN ); +STATE: foreach my $s ( @{$S} ) { + my $n = $s->{neighbor}; + $neighbors{$n} = $s; + + my $result; + + if ( my $c = $C->{$n} || $C->{_UNKNOWN} ) { + CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) { + next CODE if ( ref $c->{$code} ne 'HASH' ); + my $data = $s->{state}; + + my $result = check_item( $data, $c->{$code} ); + + if ($result) { + push @{ $states{$code} }, "[$n] $result"; + next STATE; + } + } + } + else { + push @{ $states{CRITICAL} }, '[' . $n . '] Unknown Neighbor'; + next STATE; + } + + push @{ $states{OK} }, $n; + } + + foreach my $n ( keys %{$C} ) { + if ( !exists $neighbors{$n} ) { + push @{ $states{CRITICAL} }, '[' . $n . '] Missing Neighbor'; + } + } + + return %states; +} + +sub check_item { + my ( $d, $c ) = @_; + + my $result; + + if ( $c->{match} && @{ $c->{match} } ) { + foreach my $m ( @{ $c->{match} } ) { + return if $m eq $d; + } + $result = 'State (' . $d . ') is outside of acceptable values'; + } + + if ( $c->{low} || $c->{high} ) { + $result = undef; + my ( $num, $max ) = split m{/}xms, $d; + $num =~ s/[^-\d\.]//gxms; + + if ( !length $num ) { + return 'State (' . $d . ') is not numeric'; + } + + DIRECTION: foreach my $dir (qw( low high )) { + if ( !$c->{$dir} ) { next DIRECTION; } + + my $check = $c->{$dir}; + my $cnum = $num; + + if ( $check =~ s/\%$//xms ) { + if ( !defined $max ) { + return 'max-prefix not specified and % check requested'; + } + + # convert to percent + $cnum = 100 * $cnum / $max; + } + + my @nums = ( $cnum, $check ); + my $abovebelow = 'below'; + my $symbol = '<'; + if ( $dir eq 'high' ) { + @nums = ( $check, $cnum ); + $abovebelow = 'above'; + $symbol = '>'; + } + + if ( $nums[0] < $nums[1] ) { + return join q{ }, 'is', $abovebelow, + 'threshold (' . $d, + $symbol, $c->{$dir} . ')'; + } + } + } + + return $result; +} + +sub getopt { + my (@argv) = @_; + + my %checks; + while (@argv) { + state( $w, $c ); + + my $opt = shift @argv; + for ($opt) { + when ( '-V' || '--version' ) { + print_revision( $PROGNAME, '$Revision: 1.10 $ ' ); + exit $ERRORS{'OK'} + } + when (/^-?-h(?:elp)?/xms) { print_help(); exit $ERRORS{'OK'} } + when (/^-?-s(?:ocket)?/xms) { $checks{_SOCKET} = shift @argv } + when (/^-?-w(?:arning)?/xms) { $w = parse_check( shift @argv ) } + when (/^-?-c(?:ritical)?/xms) { $c = parse_check( shift @argv ) } + when (/^-?-u(?:nknown)?/xms) { + $checks{_UNKNOWN} = { + WARNING => $w, + CRITICAL => $c, + }; + } + when (/^-?-n(?:eighbor)?/xms) { + while ( @argv && $argv[0] !~ /^-/xms ) { + $checks{ shift @argv } = { + WARNING => $w, + CRITICAL => $c, + }; + } + } + default { print_help(); exit $ERRORS{'UNKNOWN'} } + } + } + return %checks; +} + +sub print_help { + print <<"EOL"; +$PROGNAME - checks status of OpenBGPd peers + $PROGNAME [ -s SOCKET ][ -w ENTRY ][ -c ENTRY ]( -u | -n NEIGHBOR ) + +Usage: + -s, --socket SOCKET + Path to bgpd socket to use. See -r in bgpd(8). + -w, --warning RANGE or single ENTRY + Exit with WARNING status if outside of RANGE or if != ENTRY + May be entered multiple times. + -c, --critical RANGE or single ENTRY + Exit with CRITICAL status if outside of RANGE or if != ENTRY + May be entered multiple times. + -n, --neighbor NEIGHBOR + The name of the Neighbor, can be a space separated list of neighbors. + May be entered multiple times. + -u, --unknown + As if you specified -n for all unknown neighbors + +ENTRY is a comma separated list of items to match against. Each item can be +a RANGE or it will just be matched against the status. + +RANGE is specified as two optional numbers separated with a colon (:). The +check is that the value is between the two numbers. If either number is left +off, that check is ignored. + +If either number in a RANGE is specified as a percent, check is that +max-prefix is specified and that the number is within the specified percent. + +NEIGHBOR is the name that shows when running "bgpctl show summary" + +Examples: +(where many of the numbers would probably have to be multiplied by 1000) + +Any time a NEIGHBOR is specified on the command line but does NOT show up in +the output causes a CRITICAL result. + +Any time a NEIGHBOR that is NOT specified on the command line shows up in the +output causes a CRITICAL result. If -u is specified, it treats NEIGHBOR as if +it were specified at that position. + + +$PROGNAME -c Idle -n P1 -c 1:1 -n P2 -w 200:300 -c Active,10: -n P3 + +CRITICAL + If P1 is any value but Idle. + If P2 is any value but 1. + If P3 is below 10 or any non-numeric value other than "Active". + +WARNING + If P3 is above 10 and below 200 or above 300. + + +$PROGNAME -u -w 50%:70% -c 10%:90% -n P2 P3 + +No checks of unknown neighbors. + +CRITICAL + If P2 or P3 do not have max-prefix set or if they do but learned prefixes + are below 10% or above 90% of max-prefix or any non-numeric value. + +WARNING + If P2 or P3 have learned prefixes below 50% or above 70% of max-prefix. + + +$PROGNAME -w 50%:70% -c 10%:90% -u + +CRITICAL + If any neighbor does not have max-prefix set or if they do but learned + prefixes are below 10% or above 90% of max-prefix or any non-numeric value. + +WARNING + If any neighbor have learned prefixes below 50% or above 70% of max-prefix. + +EOL + + print_revision( $PROGNAME, '$Revision: 1.10 $' ); + + print $LICENSE; + + return; +} + +sub print_revision { + my ( $prog, $rev ) = @_; + $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms; + + say $prog, q{ }, $rev; + + return; +} diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_openvpn b/roles/nagios-nrpe/files/plugins_bsd/check_openvpn index 4ae14ac..04490d4 100755 --- a/roles/nagios-nrpe/files/plugins_bsd/check_openvpn +++ b/roles/nagios-nrpe/files/plugins_bsd/check_openvpn @@ -1,9 +1,12 @@ #!/bin/sh +# +# Wrapper of check_openvpn.pl, to use when the server is CARP backup and OpenVPN should not run -if netstat -an|grep '.1194' >/dev/null; then - echo "VPN OK" - return 0 +carp=$(/sbin/ifconfig carp0 | /usr/bin/grep 'status' |cut -d' ' -f2) + +if [ $carp = 'backup' ]; then + echo "No check, I'm a backup" + return 0 else - echo "PROCESS NOT LISTENING" - return 2 + /usr/local/libexec/nagios/plugins/check_openvpn.pl -H 127.0.0.1 -p 1195 -P PASSWORD fi diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_openvpn.pl b/roles/nagios-nrpe/files/plugins_bsd/check_openvpn.pl new file mode 100755 index 0000000..78e0cdb --- /dev/null +++ b/roles/nagios-nrpe/files/plugins_bsd/check_openvpn.pl @@ -0,0 +1,215 @@ +#!/usr/bin/perl -w + +####################################################################### +# +# Copyright (c) 2007 Jaime Gascon Romero +# +# License Information: +# 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 . +# +# $Id: check_openvpn.pl,v 1.1 2014/09/29 08:39:24 rdessort Exp $ +# $Revision: 1.1 $ +# Home Site: http://emergeworld.blogspot.com/ +# ##################################################################### + +use diagnostics; +use strict; +use Net::Telnet (); +use Getopt::Long qw(:config no_ignore_case); +use vars qw($PROGNAME $VERSION); +use lib "/usr/local/libexec/nagios/"; +use utils qw(%ERRORS); + +$PROGNAME = "check_openvpn"; +$VERSION = '$Revision: 1.1 $'; + +$ENV{'PATH'}=''; +$ENV{'BASH_ENV'}=''; +$ENV{'ENV'}=''; + +my ($opt_h, $opt_H, $opt_p, $opt_P, $opt_t, $opt_i, $opt_n, $opt_c, $opt_w, $opt_C, $opt_r); + +sub print_help (); +sub print_usage (); + +GetOptions + ("h" => \$opt_h, "help" => \$opt_h, + "H=s" => \$opt_H, "host=s" => \$opt_H, + "p=i" => \$opt_p, "port=i" => \$opt_p, + "P=s" => \$opt_P, "password=s" => \$opt_P, + "t=i" => \$opt_t, "timeout=i" => \$opt_t, + "i" => \$opt_i, "ip" => \$opt_i, + "n" => \$opt_n, "numeric" => \$opt_n, + "c" => \$opt_c, "critical" => \$opt_c, + "w" => \$opt_w, "warning" => \$opt_w, + "C=s" => \$opt_C, "common_name=s" => \$opt_C, + "r=s" => \$opt_r, "remote_ip=s" => \$opt_r, + ) or exit $ERRORS{'UNKNOWN'}; + +# default values +unless ( defined $opt_t ) { + $opt_t = 10; +} + +if ($opt_h) {print_help(); exit $ERRORS{'OK'};} + +if ( ! defined($opt_H) || ! defined($opt_p) ) { + print_usage(); + exit $ERRORS{'UNKNOWN'} +} + +my @lines; +my @clients; +my @clients_ip; +my $t; + +eval { +$t = new Net::Telnet (Timeout => $opt_t, + Port => $opt_p, + Prompt => '/END$/' + ); +$t->open($opt_H); +if ( defined $opt_P ) { + $t->waitfor('/ENTER PASSWORD:$/'); + $t->print($opt_P); +} +$t->waitfor('/^$/'); +@lines = $t->cmd("status 2"); +$t->close; +}; + +if ($@) { + print "OpenVPN Critical: Can't connect to server\n"; + exit $ERRORS{'CRITICAL'}; +} + + +if (defined $opt_i || defined $opt_r) { + foreach (@lines) { + if ($_ =~ /CLIENT_LIST,.*,(\d+\.\d+\.\d+\.\d+):\d+,/) { + push @clients_ip, $1; + } +} + if (defined $opt_i) { + print "OpenVPN OK: "."@clients_ip "; + exit $ERRORS{'OK'}; + } elsif (defined $opt_r) { + if ( ! grep /\b$opt_r\b/, @clients_ip) { + if (defined $opt_c) { + print "OpenVPN CRITICAL: $opt_r don't found"; + exit $ERRORS{'CRITICAL'}; + } else { + print "OpenVPN WARNING: $opt_r don't found"; + exit $ERRORS{'WARNING'}; + } + } + print "OpenVPN OK: "."@clients_ip "; + exit $ERRORS{'OK'}; + } +} + +foreach (@lines) { + if ($_ =~ /CLIENT_LIST,(.*),\d+\.\d+\.\d+\.\d+:\d+,/) { + push @clients, $1; + } +} + +if (defined $opt_C) { + if ( ! grep /\b$opt_C\b/, @clients) { + if (defined $opt_c) { + print "OpenVPN CRITICAL: $opt_C don't found"; + exit $ERRORS{'CRITICAL'}; + } else { + print "OpenVPN WARNING: $opt_C don't found"; + exit $ERRORS{'WARNING'}; + } + } +} + + +if (defined $opt_n) { +print "OpenVPN OK: ".@clients." connected clients."; +exit $ERRORS{'OK'}; +} + +print "OpenVPN OK: "."@clients "; +exit $ERRORS{'OK'}; + +####################################################################### +###### Subroutines #################################################### + +sub print_usage() { + print "Usage: $PROGNAME -H | --host -p | --port [-P | --password] [-t | --timeout] + [-i | --ip] [-n | --numeric] [-C | --common_name] [-r | --remote_ip] [-c | --critical] [-w | --warning]\n\n"; + print " $PROGNAME [-h | --help]\n"; +} + +sub print_help() { + print "$PROGNAME $VERSION\n\n"; + print "Copyright (c) 2007 Jaime Gascon Romero + +Nagios plugin to check the clients connected to a openvpn server. + +"; + print_usage(); + print " +-H | --host + IP address or hostname of the openvpn server. + +-p | --port + Management port interface of the openvpn server. + +-P | --password + Password for the management interface of the openvpn server. + +-t | --timeout + Timeout for the connection attempt. Optional, default 10 seconds. + + + Optional parameters + =================== + +-i | --ip + Prints the IP address of the remote client instead of the common name. + +-n | --numeric + Prints the number of clients connected to the openvpn server. + + + Matching Parameters + =================== + +-C | --common_name + The common name, as it is specified in the client certificate, who is wanted to check. + +-r | --remote_ip + The client remote ip address who is wanted to check. + +-c | --critical + Exits with CRITICAL status if the client specified by the common name or the remote ip address is not connected. + +-w | --warning + Exits with WARNING status if the client specified by the common name or the remote ip address is not connected. + + + Other Parameters + ================ + +-h | --help + Show this help. +"; + +} + +# vim:sts=2:sw=2:ts=2:et diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_ospfd_simple b/roles/nagios-nrpe/files/plugins_bsd/check_ospfd_simple index 932e69e..2ec3692 100755 --- a/roles/nagios-nrpe/files/plugins_bsd/check_ospfd_simple +++ b/roles/nagios-nrpe/files/plugins_bsd/check_ospfd_simple @@ -3,10 +3,16 @@ . /usr/local/libexec/nagios/utils.sh # check if ospfd is running -if [[ "$(ospfctl show 2>&1)" = *"/var/run/ospfd.sock:"* ]]; then - echo "CRITICAL - OSPFD not running" +if ! ls /var/run/ospfd* > /dev/null 2>&1; then + echo "CRITICAL - OSPFD not running, no socket found" exit "$STATE_CRITICAL" else - echo "OK - OSPFD is running" - exit "$STATE_OK" + if ospfctl show 2>&1 | grep -q "Uptime"; then + uptime=$(ospfctl show | grep Uptime | awk '{print $2}') + echo "OK - OSPFD has been running for $uptime" + exit "$STATE_OK" + else + echo "CRITICAL - OSPFD not running" + exit "$STATE_CRITICAL" + fi fi diff --git a/roles/nagios-nrpe/files/plugins_bsd/check_packetfilter.sh b/roles/nagios-nrpe/files/plugins_bsd/check_packetfilter.sh new file mode 100755 index 0000000..4f064c1 --- /dev/null +++ b/roles/nagios-nrpe/files/plugins_bsd/check_packetfilter.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +. /usr/local/libexec/nagios/utils.sh + +is_pf_disabled() { + if [ -f /etc/rc.conf.local ]; then + grep -q "pf=NO" /etc/rc.conf.local + else + # If /etc/rc.conf.local does not exist, pf cannot be disabled + # If 0 then pf is disabled, so if /etc/rc.conf.local does not exist we have to return 1 => pf is not disabled + return 1 + fi +} + +is_pf_started() { + pfctl -si | grep -q "Status: Enabled for" +} + +main() { + if ! is_pf_disabled; then + if is_pf_started; then + echo "OK: PacketFilter is enabled and started." + exit "${STATE_OK}" + else + echo "CRITICAL: PacketFilter is enabled but not started." + exit "${STATE_CRITICAL}" + fi + else + if is_pf_started; then + echo "WARNING: PacketFilter is started but not enabled." + exit "${STATE_WARNING}" + else + echo "CRITICAL: PacketFilter is disabled and not started." + exit "${STATE_CRITICAL}" + fi + fi + +} + +main diff --git a/roles/nagios-nrpe/templates/plugins_bsd/check_pf_states.j2 b/roles/nagios-nrpe/files/plugins_bsd/check_pf_states similarity index 89% rename from roles/nagios-nrpe/templates/plugins_bsd/check_pf_states.j2 rename to roles/nagios-nrpe/files/plugins_bsd/check_pf_states index c32e305..670ea1b 100755 --- a/roles/nagios-nrpe/templates/plugins_bsd/check_pf_states.j2 +++ b/roles/nagios-nrpe/files/plugins_bsd/check_pf_states @@ -3,8 +3,8 @@ # Script writen by Evolix _MAX_STATES_LIMIT=$(/sbin/pfctl -sm | /usr/bin/grep states | awk '{print $4}') -_WARNING_STATES_LIMIT=$((_MAX_STATES_LIMIT*10/100)) -_CRTICAL_STATES_LIMIT=$((_MAX_STATES_LIMIT*15/100)) +_WARNING_STATES_LIMIT=$((_MAX_STATES_LIMIT*50/100)) +_CRTICAL_STATES_LIMIT=$((_MAX_STATES_LIMIT*65/100)) . /usr/local/libexec/nagios/utils.sh diff --git a/roles/nagios-nrpe/tasks/main.yml b/roles/nagios-nrpe/tasks/main.yml index ec97d0a..2445ebc 100644 --- a/roles/nagios-nrpe/tasks/main.yml +++ b/roles/nagios-nrpe/tasks/main.yml @@ -1,17 +1,15 @@ --- - name: Install nrpe openbsd_pkg: - name: "{{ item }}" + name: + - nrpe-- state: present - with_items: - - nrpe-- - name: Install monitoring-plugins openbsd_pkg: - name: "{{ item }}" + name: + - monitoring-plugins state: present - with_items: - - monitoring-plugins - name: Create nrpe.d dir file: @@ -32,29 +30,48 @@ dest: /etc/nrpe.d/evolix.cfg notify: restart nrpe -- name: Nagios plugins are installed - copy: - src: plugins_bsd/ - dest: /usr/local/libexec/nagios/plugins/ +- name: Create nrpe plugins dir + file: + path: /usr/local/libexec/nagios/plugins/ + state: directory owner: root group: wheel mode: "0755" + +- name: Nagios plugins are installed + copy: + src: plugins_bsd/{{ item.name }} + dest: /usr/local/libexec/nagios/plugins/{{ item.name }} + owner: root + group: wheel + mode: "0755" + force: "{{ item.force }}" + with_items: + - {name: 'check_carp_if', force: true} + - {name: 'check_connections_state.sh', force: false} + - {name: 'check_ipsecctl.sh', force: false} + - {name: 'check_openbgpd', force: true} + - {name: 'check_openvpn', force: false} + - {name: 'check_openvpn.pl', force: true} + - {name: 'check_ospfd_simple', force: true} + - {name: 'check_packetfilter.sh', force: true} + - {name: 'check_pf_states', force: false} notify: restart nrpe - name: Nagios plugins are installed - template template: - src: plugins_bsd/{{ item }}.j2 - dest: /usr/local/libexec/nagios/plugins/{{ item }} + src: plugins_bsd/{{ item.name }}.j2 + dest: /usr/local/libexec/nagios/plugins/{{ item.name }} owner: root group: wheel mode: "0755" + force: "{{ item.force }}" with_items: - - 'check_pf_states' - - 'check_free_mem.sh' + - {name: 'check_free_mem.sh', force: true} notify: restart nrpe - name: Starting and enabling nrpe service: name: nrpe - enabled: yes + enabled: true state: started diff --git a/roles/nagios-nrpe/templates/evolix_bsd.cfg.j2 b/roles/nagios-nrpe/templates/evolix_bsd.cfg.j2 index 0420fcb..6273506 100644 --- a/roles/nagios-nrpe/templates/evolix_bsd.cfg.j2 +++ b/roles/nagios-nrpe/templates/evolix_bsd.cfg.j2 @@ -2,37 +2,51 @@ # Custom NRPE configuration file. # Part of the EvoBSD distribution. # +# This is an Ansible managed file ! +# For local modifications use the /etc/nrpe.d/zzz_evolix.cfg file instead # Allowed IPs allowed_hosts={{ nagios_nrpe_allowed_hosts | join(',') }} +# System checks command[check_users]=/usr/local/libexec/nagios/check_users -w 5 -c 10 command[check_load]=/usr/local/libexec/nagios/check_load -w 15,10,5 -c 30,25,20 command[check_disk1]=/usr/local/libexec/nagios/check_disk -x /lib/init/rw -x /dev -x /dev/shm -w 10% -c 3% -W 10% -K 3% -C -w 5% -c 2% -W 5% -K 2% -p /home command[check_zombie_procs]=/usr/local/libexec/nagios/check_procs -w 5 -c 10 -s Z command[check_total_procs]=/usr/local/libexec/nagios/check_procs -w 150 -c 200 -command[check_imap]=/usr/local/libexec/nagios/check_imap -H localhost +command[check_swap]=/usr/local/libexec/nagios/check_swap --no-swap=ok -a -w 30% -c 20% + +# Generic services checks command[check_smtp]=/usr/local/libexec/nagios/check_smtp -H localhost -f {{ general_alert_email }} command[check_dns]=/usr/local/libexec/nagios/check_dns -H evolix.net -command[check_swap]=/usr/local/libexec/nagios/check_swap --no-swap=ok -a -w 30% -c 20% -command[check_ntp]=/usr/local/libexec/nagios/check_ntp -H ntp.evolix.net -command[check_http]=/usr/local/libexec/nagios/check_http -H localhost -p 80 -command[check_onduleur]=/usr/local/libexec/nagios/check_ups -H localhost -u onduleur -# Pour check_mailq, ajouter dans sudo : -# _nrpe ALL=NOPASSWD: /usr/local/libexec/nagios/check_mailq -command[check_mailq]=sudo /usr/local/libexec/nagios/check_mailq -w 10 -c 20 -command[check_bind]=/usr/local/libexec/nagios/check_dig -l evolix.net -H localhost +command[check_ntp]=/usr/local/libexec/nagios/check_ntp -H ntp-check.evolix.net command[check_ssh]=/usr/local/libexec/nagios/check_ssh -p 22 localhost -command[check_proxy]=/usr/local/libexec/nagios/check_tcp -p PORT -#command[check_vpn]=/usr/local/libexec/nagios/check_ping -H IPDISTANTE -p 1 -w 5000,100% -c 5000,100% -command[check_vpn]=sudo /usr/local/libexec/nagios/plugins/check_ipsecctl.sh IPDISTANTE IPLOCALE "VPN MARSEILLE-ROME" -command[check_openvpn]=/usr/local/libexec/nagios/plugins/check_openvpn -command[check_pf_states]=doas /usr/local/libexec/nagios/plugins/check_pf_states -command[check_carp1]=/usr/local/libexec/nagios/plugins/check_carp_if carp0 master -command[check_mem]=/usr/local/libexec/nagios/plugins/check_free_mem.sh -w 20 -c 10 -command[check_dhcpclient]=/usr/local/libexec/nagios/check_dhcp -i INTERFACE -command[check_smb]=/usr/local/libexec/nagios/check_tcp -H IPLOCALE -p 445 -#command[check_ospfd]=doas /usr/local/libexec/nagios/plugins/check_ospfd -#command[check_ospf6d]=doas /usr/local/libexec/nagios/plugins/check_ospf6d -command[check_ospfd_simple]=sudo /usr/local/libexec/nagios/plugins/check_ospfd_simple +command[check_mailq]=doas /usr/local/libexec/nagios/check_mailq -w 10 -c 20 + +# Specific services checks +command[check_imap]=/usr/local/libexec/nagios/check_imap -H localhost +command[check_http]=/usr/local/libexec/nagios/check_http -H localhost -p 80 +command[check_bind]=/usr/local/libexec/nagios/check_dig -l evolix.net -H localhost +command[check_unbound]=/usr/local/libexec/nagios/check_dig -l evolix.net -H localhost +#command[check_proxy]=/usr/local/libexec/nagios/check_tcp -p PORT +#command[check_smb]=/usr/local/libexec/nagios/check_tcp -H IPLOCALE -p 445 command[check_mysql]=/usr/local/libexec/nagios/check_mysql -H 127.0.0.1 -f /etc/nrpe.d/.my.cnf +#command[check_vpn]=/usr/local/libexec/nagios/check_ping -H IPDISTANTE -p 1 -w 5000,100% -c 5000,100% +#command[check_dhcpd]=doas /usr/local/libexec/nagios/check_dhcp -i INTERFACE -s IP -u + +# Local checks (not packaged) +#command[check_openvpn]=/usr/local/libexec/nagios/plugins/check_openvpn.pl -H 127.0.0.1 -p 1195 -P PASSWORD +#command[check_openvpn]=/usr/local/libexec/nagios/plugins/check_openvpn # Wrapper of check_openvpn.pl, to use when the server is CARP backup and OpenVPN should not run +#command[check_carp1]=/usr/local/libexec/nagios/plugins/check_carp_if carp0 master +command[check_mem]=/usr/local/libexec/nagios/plugins/check_free_mem.sh -w 20 -c 10 +#command[check_vpn]=doas /usr/local/libexec/nagios/plugins/check_ipsecctl.sh IPDISTANTE IPLOCALE "VPN MARSEILLE-ROME" +command[check_pf_states]=doas /usr/local/libexec/nagios/plugins/check_pf_states +command[check_ospfd]=doas /usr/local/libexec/nagios/plugins/check_ospfd +command[check_ospf6d]=doas /usr/local/libexec/nagios/plugins/check_ospf6d +command[check_ospfd_simple]=doas /usr/local/libexec/nagios/plugins/check_ospfd_simple +command[check_bgpd]=doas /usr/local/libexec/nagios/plugins/check_openbgpd -u +command[check_connections_state]=doas /usr/local/libexec/nagios/plugins/check_connections_state.sh +command[check_packetfilter]=doas /usr/local/libexec/nagios/plugins/check_packetfilter.sh + +# This is an Ansible managed file ! +# For local modifications use the /etc/nrpe.d/zzz_evolix.cfg file instead diff --git a/roles/nagios-nrpe/templates/plugins_bsd/check_free_mem.sh.j2 b/roles/nagios-nrpe/templates/plugins_bsd/check_free_mem.sh.j2 index ab5f7e2..ee47ad5 100755 --- a/roles/nagios-nrpe/templates/plugins_bsd/check_free_mem.sh.j2 +++ b/roles/nagios-nrpe/templates/plugins_bsd/check_free_mem.sh.j2 @@ -64,7 +64,7 @@ __EOT # Total memory size (in MB) tot_mem=$(( `/sbin/sysctl -n hw.physmem` / BYTES_IN_MB)) # Free memory size (in MB) -{% if ansible_distribution_version | version_compare("6.2",'<') %} +{% if ansible_distribution_version is version_compare("6.2",'<') %} free_mem=$(( `/usr/bin/vmstat | /usr/bin/tail -1 | /usr/bin/awk '{ print $5 }'` / KB_IN_MB )) {% else %} free_mem=$(/usr/bin/vmstat | /usr/bin/tail -1 | /usr/bin/awk '{ print $4 }' | tr -d 'M') diff --git a/roles/openvpn/README.md b/roles/openvpn/README.md new file mode 100644 index 0000000..18b459f --- /dev/null +++ b/roles/openvpn/README.md @@ -0,0 +1,13 @@ +# OpenVPN + +Installation and custom configuration of OpenVPN server. + +## Tasks + +Everything is in the `tasks/main.yml` file. + +## Available variables + +The full list of variables (with default values) can be found in `defaults/main.yml`. + +NOTE: Make sure you have already cloned shellpki in ~/GIT/ diff --git a/roles/openvpn/defaults/main.yml b/roles/openvpn/defaults/main.yml new file mode 100644 index 0000000..dbf2f80 --- /dev/null +++ b/roles/openvpn/defaults/main.yml @@ -0,0 +1,3 @@ +--- +openvpn_lan: "192.168.42.0" +openvpn_netmask: "255.255.255.0" diff --git a/roles/openvpn/files/check_openvpn.pl b/roles/openvpn/files/check_openvpn.pl new file mode 100755 index 0000000..78e0cdb --- /dev/null +++ b/roles/openvpn/files/check_openvpn.pl @@ -0,0 +1,215 @@ +#!/usr/bin/perl -w + +####################################################################### +# +# Copyright (c) 2007 Jaime Gascon Romero +# +# License Information: +# 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 . +# +# $Id: check_openvpn.pl,v 1.1 2014/09/29 08:39:24 rdessort Exp $ +# $Revision: 1.1 $ +# Home Site: http://emergeworld.blogspot.com/ +# ##################################################################### + +use diagnostics; +use strict; +use Net::Telnet (); +use Getopt::Long qw(:config no_ignore_case); +use vars qw($PROGNAME $VERSION); +use lib "/usr/local/libexec/nagios/"; +use utils qw(%ERRORS); + +$PROGNAME = "check_openvpn"; +$VERSION = '$Revision: 1.1 $'; + +$ENV{'PATH'}=''; +$ENV{'BASH_ENV'}=''; +$ENV{'ENV'}=''; + +my ($opt_h, $opt_H, $opt_p, $opt_P, $opt_t, $opt_i, $opt_n, $opt_c, $opt_w, $opt_C, $opt_r); + +sub print_help (); +sub print_usage (); + +GetOptions + ("h" => \$opt_h, "help" => \$opt_h, + "H=s" => \$opt_H, "host=s" => \$opt_H, + "p=i" => \$opt_p, "port=i" => \$opt_p, + "P=s" => \$opt_P, "password=s" => \$opt_P, + "t=i" => \$opt_t, "timeout=i" => \$opt_t, + "i" => \$opt_i, "ip" => \$opt_i, + "n" => \$opt_n, "numeric" => \$opt_n, + "c" => \$opt_c, "critical" => \$opt_c, + "w" => \$opt_w, "warning" => \$opt_w, + "C=s" => \$opt_C, "common_name=s" => \$opt_C, + "r=s" => \$opt_r, "remote_ip=s" => \$opt_r, + ) or exit $ERRORS{'UNKNOWN'}; + +# default values +unless ( defined $opt_t ) { + $opt_t = 10; +} + +if ($opt_h) {print_help(); exit $ERRORS{'OK'};} + +if ( ! defined($opt_H) || ! defined($opt_p) ) { + print_usage(); + exit $ERRORS{'UNKNOWN'} +} + +my @lines; +my @clients; +my @clients_ip; +my $t; + +eval { +$t = new Net::Telnet (Timeout => $opt_t, + Port => $opt_p, + Prompt => '/END$/' + ); +$t->open($opt_H); +if ( defined $opt_P ) { + $t->waitfor('/ENTER PASSWORD:$/'); + $t->print($opt_P); +} +$t->waitfor('/^$/'); +@lines = $t->cmd("status 2"); +$t->close; +}; + +if ($@) { + print "OpenVPN Critical: Can't connect to server\n"; + exit $ERRORS{'CRITICAL'}; +} + + +if (defined $opt_i || defined $opt_r) { + foreach (@lines) { + if ($_ =~ /CLIENT_LIST,.*,(\d+\.\d+\.\d+\.\d+):\d+,/) { + push @clients_ip, $1; + } +} + if (defined $opt_i) { + print "OpenVPN OK: "."@clients_ip "; + exit $ERRORS{'OK'}; + } elsif (defined $opt_r) { + if ( ! grep /\b$opt_r\b/, @clients_ip) { + if (defined $opt_c) { + print "OpenVPN CRITICAL: $opt_r don't found"; + exit $ERRORS{'CRITICAL'}; + } else { + print "OpenVPN WARNING: $opt_r don't found"; + exit $ERRORS{'WARNING'}; + } + } + print "OpenVPN OK: "."@clients_ip "; + exit $ERRORS{'OK'}; + } +} + +foreach (@lines) { + if ($_ =~ /CLIENT_LIST,(.*),\d+\.\d+\.\d+\.\d+:\d+,/) { + push @clients, $1; + } +} + +if (defined $opt_C) { + if ( ! grep /\b$opt_C\b/, @clients) { + if (defined $opt_c) { + print "OpenVPN CRITICAL: $opt_C don't found"; + exit $ERRORS{'CRITICAL'}; + } else { + print "OpenVPN WARNING: $opt_C don't found"; + exit $ERRORS{'WARNING'}; + } + } +} + + +if (defined $opt_n) { +print "OpenVPN OK: ".@clients." connected clients."; +exit $ERRORS{'OK'}; +} + +print "OpenVPN OK: "."@clients "; +exit $ERRORS{'OK'}; + +####################################################################### +###### Subroutines #################################################### + +sub print_usage() { + print "Usage: $PROGNAME -H | --host -p | --port [-P | --password] [-t | --timeout] + [-i | --ip] [-n | --numeric] [-C | --common_name] [-r | --remote_ip] [-c | --critical] [-w | --warning]\n\n"; + print " $PROGNAME [-h | --help]\n"; +} + +sub print_help() { + print "$PROGNAME $VERSION\n\n"; + print "Copyright (c) 2007 Jaime Gascon Romero + +Nagios plugin to check the clients connected to a openvpn server. + +"; + print_usage(); + print " +-H | --host + IP address or hostname of the openvpn server. + +-p | --port + Management port interface of the openvpn server. + +-P | --password + Password for the management interface of the openvpn server. + +-t | --timeout + Timeout for the connection attempt. Optional, default 10 seconds. + + + Optional parameters + =================== + +-i | --ip + Prints the IP address of the remote client instead of the common name. + +-n | --numeric + Prints the number of clients connected to the openvpn server. + + + Matching Parameters + =================== + +-C | --common_name + The common name, as it is specified in the client certificate, who is wanted to check. + +-r | --remote_ip + The client remote ip address who is wanted to check. + +-c | --critical + Exits with CRITICAL status if the client specified by the common name or the remote ip address is not connected. + +-w | --warning + Exits with WARNING status if the client specified by the common name or the remote ip address is not connected. + + + Other Parameters + ================ + +-h | --help + Show this help. +"; + +} + +# vim:sts=2:sw=2:ts=2:et diff --git a/roles/openvpn/files/shellpki b/roles/openvpn/files/shellpki new file mode 120000 index 0000000..3036d45 --- /dev/null +++ b/roles/openvpn/files/shellpki @@ -0,0 +1 @@ +/home/tpilat/GIT/shellpki/ \ No newline at end of file diff --git a/roles/openvpn/files/sudo_shellpki b/roles/openvpn/files/sudo_shellpki new file mode 100644 index 0000000..08ca1ab --- /dev/null +++ b/roles/openvpn/files/sudo_shellpki @@ -0,0 +1 @@ +%shellpki ALL = (root) /usr/local/sbin/shellpki diff --git a/roles/openvpn/handlers/main.yml b/roles/openvpn/handlers/main.yml new file mode 100644 index 0000000..b22f340 --- /dev/null +++ b/roles/openvpn/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart openvpn + service: + name: openvpn + state: restarted diff --git a/roles/openvpn/tasks/main.yml b/roles/openvpn/tasks/main.yml new file mode 100644 index 0000000..0fcd91d --- /dev/null +++ b/roles/openvpn/tasks/main.yml @@ -0,0 +1,121 @@ +--- +- name: Install OpenVPN package + openbsd_pkg: + name: "openvpn--" + tags: + - openvpn + +- name: Create /etc/openvpn directory + file: + path: /etc/openvpn + state: directory + owner: "root" + group: "wheel" + mode: "0755" + tags: + - openvpn + +- name: Deploy OpenVPN configuration + template: + src: "server.conf.j2" + dest: "/etc/openvpn/server.conf" + mode: "0600" + notify: restart openvpn + tags: + - openvpn + +- name: Enabling OpenVPN + service: + name: openvpn + enabled: true + tags: + - openvpn + +- name: Set OpenVPN flag + shell: 'rcctl set openvpn flags "--config /etc/openvpn/server.conf"' + tags: + - openvpn + +- name: Create shellpki user + user: + name: "_shellpki" + system: true + state: present + home: "/etc/shellpki/" + shell: "/sbin/nologin" + tags: + - openvpn + +- name: Copy some shellpki files + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: wheel + mode: "{{ item.mode }}" + force: true + with_items: + - src: 'files/shellpki/openssl.cnf' + dest: '/etc/shellpki/openssl.cnf' + mode: '0640' + - src: 'files/shellpki/shellpki' + dest: '/usr/local/sbin/shellpki' + mode: '0755' + tags: + - openvpn + +- name: Deploy DH PARAMETERS + template: + src: "dh2048.pem.j2" + dest: "/etc/shellpki/dh2048.pem" + mode: "0600" + tags: + - openvpn + +- name: Create /etc/sudoers.d directory + file: + path: /etc/sudoers.d + state: directory + owner: "root" + group: "wheel" + mode: "0755" + tags: + - openvpn + +- name: Include /etc/sudoers.d in sudoers configuration file + lineinfile: + path: /etc/sudoers + line: '#includedir /etc/sudoers.d' + tags: + - openvpn + +- name: Verify shellpki sudoers file presence + copy: + src: "sudo_shellpki" + dest: "/etc/sudoers.d/shellpki" + force: true + mode: "0440" + validate: '/usr/local/sbin/visudo -cf %s' + tags: + - openvpn + +- name: Copy check_openvpn + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: wheel + mode: "{{ item.mode }}" + force: true + with_items: + - src: 'files/check_openvpn.pl' + dest: '/usr/local/libexec/nagios/plugins/check_openvpn.pl' + mode: '0755' + tags: + - openvpn + +- name: Install needed package for check_openvpn + openbsd_pkg: + name: "p5-Net-Telnet" + tags: + - openvpn diff --git a/roles/openvpn/templates/dh2048.pem.j2 b/roles/openvpn/templates/dh2048.pem.j2 new file mode 100644 index 0000000..9db20bb --- /dev/null +++ b/roles/openvpn/templates/dh2048.pem.j2 @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAuimweC/f5W/AIIFhLX256Bi5IU+AkN9sKZ9sxGx0xc3J8NwIBnEP +R/2RgclJqJ8OodY70zeDHNLDyc01crGvihuupiWVlvQxS4osdhfdM+GoV9pcmCVr +TRTybsUPkkm4rQ/SC7I2MxiYnXwDrrYnpMvBDaRZjoHlgTKjOGoYSd+DIDZSFKkv +ASkXQkIC9FpvjnxfW5gtzzm6NheqgYUI2Y2QiqM6BmGVZiPcqyUpbWvRCcZLoPa2 +Z+FV9LxE4J7CX0ilTJXXhs3RaMlG8qZha3l0hEL4SAZp5xn74Ej/9hA5cWqnKEOQ +aLfwADI4rPe9uTu9Qnw87DgM2tQeETBlmwIBAg== +-----END DH PARAMETERS----- diff --git a/roles/openvpn/templates/server.conf.j2 b/roles/openvpn/templates/server.conf.j2 new file mode 100644 index 0000000..377b5b1 --- /dev/null +++ b/roles/openvpn/templates/server.conf.j2 @@ -0,0 +1,26 @@ +user nobody +group nogroup + +local {{ ansible_default_ipv4.address }} +port 1194 +proto udp +dev tun +mode server +keepalive 10 120 + +cipher AES-128-CBC # AES +#comp-lzo +# compress (à partir d'OpenVPN 2.4) + +persist-key +persist-tun + +status /var/log/openvpn-status.log +log-append /var/log/openvpn.log + +ca /etc/shellpki/cacert.pem +cert /etc/shellpki/certs/{{ ansible_fqdn }}.crt +key /etc/shellpki/private/{{ ansible_fqdn }}.key +dh /etc/shellpki/dh2048.pem + +server {{ openvpn_lan }} {{ openvpn_netmask }} diff --git a/roles/ospf/README.md b/roles/ospf/README.md new file mode 100644 index 0000000..7be1224 --- /dev/null +++ b/roles/ospf/README.md @@ -0,0 +1,14 @@ +# OSPF + +Deployment of OSPF check scripts with their cron. + +## Tasks + +Everything is in the `tasks/main.yml` file. + +## Available variables + +The full list of variables (with default values) can be found in `defaults/main.yml`. + +* `ospf_mailto` : email address the output of the scripts will be sent to when a change is detected +* `ospf_sed_command` : facultative sed command to modify the ospfctl output and add a name to IPs diff --git a/roles/ospf/defaults/main.yml b/roles/ospf/defaults/main.yml new file mode 100644 index 0000000..098bef5 --- /dev/null +++ b/roles/ospf/defaults/main.yml @@ -0,0 +1,3 @@ +--- +ospf_mailto: "foobar@example.com" +ospf_sed_command: "" diff --git a/roles/ospf/tasks/main.yml b/roles/ospf/tasks/main.yml new file mode 100644 index 0000000..d1b28be --- /dev/null +++ b/roles/ospf/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Deploy ospf check scripts + template: + src: "{{ item }}.j2" + dest: /usr/share/scripts/{{ item }} + with_items: + - "ospfd-check-peers.sh" + - "ospf6d-check-peers.sh" + when: group_names | select('search','ospf') | list | count > 0 + tags: + - ospf + +- name: Cron job for ospf check scripts is installed + cron: + name: "{{ item }} check" + job: "/bin/sh /usr/share/scripts/{{ item }}-check-peers.sh" + with_items: + - ospfd + - ospf6d + when: group_names | select('search','ospf') | list | count > 0 + tags: + - ospf diff --git a/roles/ospf/templates/ospf6d-check-peers.sh.j2 b/roles/ospf/templates/ospf6d-check-peers.sh.j2 new file mode 100755 index 0000000..2c1d294 --- /dev/null +++ b/roles/ospf/templates/ospf6d-check-peers.sh.j2 @@ -0,0 +1,126 @@ +#!/bin/ksh + +# Script writen by Daniel Jakots for BGP, adapted by Jeremy Dubois for OSPF + +# First we go through the list of neighbor and we write all the peer and +# their status in "${_TMPDIR}"/ospf6-status. + +# Then we monitor if this file has changed between now and the previous run. + +# If it did, we send a mail with the states of the different sessions. + +set -u + +PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:. + +_MAILTO="{{ ospf_mailto }}" +_TMPDIR=/tmp/check-ospf6 +_PIDFILE="${_TMPDIR}"/ospf6d-check-peers.pid + + +if [ -e /etc/realname ]; then + _REALNAME=$(cat /etc/realname) + _HOSTNAME=$(hostname -s) +else + _HOSTNAME=$(hostname) +fi + +mkdir -p "${_TMPDIR}" + +# Don't try to run if it's already running +if [ -e "${_PIDFILE}" ]; then + echo "$(date)" >> "${_TMPDIR}"/log + exit 1 +else + echo $$ >> "${_PIDFILE}" +fi + +# Create an history +if [[ -f "${_TMPDIR}"/ospf6-status ]] ; then + mv "${_TMPDIR}"/ospf6-status "${_TMPDIR}"/ospf6-status.old +else + touch "${_TMPDIR}"/ospf6-status + touch "${_TMPDIR}"/ospf6-status.old +fi + +# List peers and loops on them to list them and their OSPF6 state +ospf6ctl show neighbor | grep -v "^$" | grep -v "Uptime" | awk {'print $1'} > "${_TMPDIR}"/peers-list + +while read _PEER +do + _STATUS=$(/usr/sbin/ospf6ctl show neighbor | grep "${_PEER} " | awk {'print $3'}) + echo -n "${_PEER}" >> "${_TMPDIR}"/ospf6-status + echo -n " " >> "${_TMPDIR}"/ospf6-status + if ([[ "${_STATUS}" = "FULL/BCKUP" ]] || [[ "${_STATUS}" = "FULL/DR" ]] || [[ "${_STATUS}" = "2-WAY/OTHER" ]] || [[ "${_STATUS}" = "FULL/OTHER" ]]) ; then + _STATUS="UP" + else + _STATUS="DOWN" + fi + echo "${_STATUS}" >> "${_TMPDIR}"/ospf6-status + +done <"${_TMPDIR}"/peers-list + +# Check for difference with previous run +different=$(diff -q "${_TMPDIR}"/ospf6-status.old "${_TMPDIR}"/ospf6-status) + +if ! [[ -n "${different}" ]] ; then + rm -f "${_PIDFILE}" + exit 0 +fi + +# It changed so we're going to send a mail + +_TMPMAILDIR="${_TMPDIR}"/mail +mkdir -p "${_TMPMAILDIR}" + +# go through sessions and list them depending on their OSPF6 state +echo "*** Session(s) OK ***\n" >> "${_TMPMAILDIR}"/bodyok +while read _LINE +do + # _LINE is session + status + _STATUS=$(echo "${_LINE}" | awk {'print $2'}) + _SESSION=$(echo "${_LINE}" | awk {'print $1'}) + if [[ "${_STATUS}" = "UP" ]] ; then + ospf6ctl show neighbor | grep "${_SESSION} " {{ ospf_sed_command }} >> "${_TMPMAILDIR}"/bodyok + else + ospf6ctl show neighbor | grep "${_SESSION} " {{ ospf_sed_command }} >> "${_TMPMAILDIR}"/bodynok + fi +done <"${_TMPDIR}"/ospf6-status + +# create the mail body + +echo "Dear NOC,\n\nThe state of one or more OSPF6 session(s) has changed:\n" > "${_TMPMAILDIR}"/header +cat "${_TMPMAILDIR}"/header "${_TMPMAILDIR}"/bodyok > "${_TMPMAILDIR}"/body + +_STATE="OK" +if [[ -f "${_TMPMAILDIR}"/bodynok ]] ; then + _STATE="NOT OK" + echo "\n*** Session(s) on error ***\n" >> "${_TMPMAILDIR}"/body + cat "${_TMPMAILDIR}"/bodynok >> "${_TMPMAILDIR}"/body +fi + +# Add some infos +echo "\n\n*** Known OSPF routes ***\n" >> "${_TMPMAILDIR}"/body +ospf6ctl show fib ospf >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Network used memory ***\n" >> "${_TMPMAILDIR}"/body +netstat -m >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Server load ***\n" >> "${_TMPMAILDIR}"/body +w >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Processes ***\n" >> "${_TMPMAILDIR}"/body +top >> "${_TMPMAILDIR}"/body + +# Send the mail whether we have a realname or not +if [ -n "${_REALNAME}" ]; then + cat "${_TMPMAILDIR}"/body | mail -s "[OSPF6] ${_REALNAME} (${_HOSTNAME}) - State change - ${_STATE}" "${_MAILTO}" +else + cat "${_TMPMAILDIR}"/body | mail -s "[OSPF6] ${_HOSTNAME} - State change - ${_STATE}" "${_MAILTO}" +fi + +# cleaning +if [[ -d "${_TMPMAILDIR}" ]] ; then + rm -rf "${_TMPMAILDIR}" +fi +rm -f "${_PIDFILE}" diff --git a/roles/ospf/templates/ospfd-check-peers.sh.j2 b/roles/ospf/templates/ospfd-check-peers.sh.j2 new file mode 100755 index 0000000..ede2eec --- /dev/null +++ b/roles/ospf/templates/ospfd-check-peers.sh.j2 @@ -0,0 +1,127 @@ +#!/bin/ksh + +# Script writen by Daniel Jakots for BGP, adapted by Jeremy Dubois for OSPF + +# First we go through the list of neighbor and we write all the peer and +# their status in "${_TMPDIR}"/ospf-status. + +# Then we monitor if this file has changed between now and the previous run. + +# If it did, we send a mail with the states of the different sessions. + +set -u + +PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:. + +_MAILTO="{{ ospf_mailto }}" +_TMPDIR=/tmp/check-ospf +_PIDFILE="${_TMPDIR}"/ospfd-check-peers.pid + + +if [ -e /etc/realname ]; then + _REALNAME=$(cat /etc/realname) + _HOSTNAME=$(hostname -s) +else + _HOSTNAME=$(hostname) +fi + +mkdir -p "${_TMPDIR}" + +# Don't try to run if it's already running +if [ -e "${_PIDFILE}" ]; then + echo "$(date)" >> "${_TMPDIR}"/log + exit 1 +else + echo $$ >> "${_PIDFILE}" +fi + +# Create an history +if [[ -f "${_TMPDIR}"/ospf-status ]] ; then + mv "${_TMPDIR}"/ospf-status "${_TMPDIR}"/ospf-status.old +else + touch "${_TMPDIR}"/ospf-status + touch "${_TMPDIR}"/ospf-status.old +fi + +# List peers and loops on them to list them and their OSPF state +ospfctl show neighbor | grep -v "^$" | grep -v "Uptime" | awk {'print $1'} > "${_TMPDIR}"/peers-list + +while read _PEER +do + _STATUS=$(/usr/sbin/ospfctl show neighbor | grep "${_PEER} " | awk {'print $3'}) + echo -n "${_PEER}" >> "${_TMPDIR}"/ospf-status + echo -n " " >> "${_TMPDIR}"/ospf-status + if ([[ "${_STATUS}" = "FULL/BCKUP" ]] || [[ "${_STATUS}" = "FULL/DR" ]] || [[ "${_STATUS}" = "2-WAY/OTHER" ]] || [[ "${_STATUS}" = "FULL/OTHER" ]]) ; then + _STATUS="UP" + else + _STATUS="DOWN" + fi + echo "${_STATUS}" >> "${_TMPDIR}"/ospf-status + +done <"${_TMPDIR}"/peers-list + +# Check for difference with previous run +different=$(diff -q "${_TMPDIR}"/ospf-status.old "${_TMPDIR}"/ospf-status) + +if ! [[ -n "${different}" ]] ; then + rm -f "${_PIDFILE}" + exit 0 +fi + +# It changed so we're going to send a mail + +_TMPMAILDIR="${_TMPDIR}"/mail +mkdir -p "${_TMPMAILDIR}" + +# go through sessions and list them depending on their OSPF state +echo "*** Session(s) OK ***\n" >> "${_TMPMAILDIR}"/bodyok +while read _LINE +do + # _LINE is session + status + _STATUS=$(echo "${_LINE}" | awk {'print $2'}) + _SESSION=$(echo "${_LINE}" | awk {'print $1'}) + if [[ "${_STATUS}" = "UP" ]] ; then + ospfctl show neighbor | grep "${_SESSION} " {{ ospf_sed_command }} >> "${_TMPMAILDIR}"/bodyok + else + ospfctl show neighbor | grep "${_SESSION} " {{ ospf_sed_command }} >> "${_TMPMAILDIR}"/bodynok + fi +done <"${_TMPDIR}"/ospf-status + +# create the mail body + +echo "Dear NOC,\n\nThe state of one or more OSPF session(s) has changed:\n" > "${_TMPMAILDIR}"/header +cat "${_TMPMAILDIR}"/header "${_TMPMAILDIR}"/bodyok > "${_TMPMAILDIR}"/body + +_STATE="OK" +if [[ -f "${_TMPMAILDIR}"/bodynok ]] ; then + _STATE="NOT OK" + echo "\n*** Session(s) on error ***\n" >> "${_TMPMAILDIR}"/body + cat "${_TMPMAILDIR}"/bodynok >> "${_TMPMAILDIR}"/body +fi + +# Add some infos +echo "\n\n*** Known OSPF routes ***\n" >> "${_TMPMAILDIR}"/body +ospfctl show fib ospf >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Network used memory ***\n" >> "${_TMPMAILDIR}"/body +netstat -m >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Server load ***\n" >> "${_TMPMAILDIR}"/body +w >> "${_TMPMAILDIR}"/body + +echo "\n\n*** Processes ***\n" >> "${_TMPMAILDIR}"/body +top >> "${_TMPMAILDIR}"/body + + +# Send the mail whether we have a realname or not +if [ -n "${_REALNAME}" ]; then + cat "${_TMPMAILDIR}"/body | mail -s "[OSPF] ${_REALNAME} (${_HOSTNAME}) - State change - ${_STATE}" "${_MAILTO}" +else + cat "${_TMPMAILDIR}"/body | mail -s "[OSPF] ${_HOSTNAME} - State change - ${_STATE}" "${_MAILTO}" +fi + +# cleaning +if [[ -d "${_TMPMAILDIR}" ]] ; then + rm -rf "${_TMPMAILDIR}" +fi +rm -f "${_PIDFILE}" diff --git a/roles/pf/tasks/main.yml b/roles/pf/tasks/main.yml index 30aef63..4e52827 100644 --- a/roles/pf/tasks/main.yml +++ b/roles/pf/tasks/main.yml @@ -4,4 +4,6 @@ src: pf.conf.j2 dest: /etc/pf.conf mode: "0600" - backup: yes + backup: true + tags: + - pf diff --git a/roles/pf/templates/pf.conf.j2 b/roles/pf/templates/pf.conf.j2 index 1952e18..65ee69b 100644 --- a/roles/pf/templates/pf.conf.j2 +++ b/roles/pf/templates/pf.conf.j2 @@ -54,8 +54,8 @@ block log all #pass quick on $pfsync_if proto pfsync pass out -# 9999 = pfstat, 5666 = nrpe -pass in on $ext_if proto tcp from to (self) port { 9999, ssh, 5666 } +# 5666 = nrpe +pass in on $ext_if proto tcp from to (self) port { ssh, 5666 } # Block Attack # China 144.0.0.0/16 --> SSH diff --git a/roles/post-install/files/ldap.sh b/roles/post-install/files/ldap.sh index 8933e90..0fa304d 100755 --- a/roles/post-install/files/ldap.sh +++ b/roles/post-install/files/ldap.sh @@ -5,9 +5,10 @@ dnsPTRrecord=$(hostname) HardwareMark=$(sysctl hw.vendor| sed 's#hw.vendor=##') HardwareModel=$(sysctl hw.product| sed 's#hw.product=##') computerIP=$(ifconfig egress | grep inet | awk -v OFS="\n" '{ print $2, $NF }'| head -1) -computerOS=OpenBSD computerKernel=$(sysctl kern.osrelease | sed 's#kern.osrelease=##') -HardwareSerial=$(sysctl hw.serialno| sed 's#hw.serialno=##') +computerOS="OpenBSD $computerKernel" +HardwareSerial=$(sysctl hw.serialno 2>/dev/null | sed 's#hw.serialno=##') +if [ -z $HardwareSerial ]; then sysctl hw | grep -qi qemu && HardwareSerial="Not Specified"; fi clientNumber="XXX" cpuMark=$(sysctl hw.model| sed 's#hw.model=##') cpuModel=$(sysctl hw.model| sed 's#hw.model=##') @@ -62,8 +63,8 @@ HardwareSize: $cpuFreq HardwareType: CPU HardwareModel: $cpuModel -dn: HardwareName=ram0,EvoComputerName=${EvoComputerName},ou=computer,dc=evolix,dc=net -HardwareName: ram0 +dn: HardwareName=mem,EvoComputerName=${EvoComputerName},ou=computer,dc=evolix,dc=net +HardwareName: mem objectClass: EvoHardware HardwareSize: $mem HardwareType: mem @@ -119,7 +120,7 @@ objectClass: EvoService ipServicePort: 22 ServiceName: openssh ServiceType: ssh -ServiceVersion: OpenSSH 6.7 +ServiceVersion: OpenSSH 8.3 dn: ServiceName=opensmtpd,EvoComputerName=${EvoComputerName},ou=computer,dc=evolix,dc=net ipServiceProtocol: tcp @@ -128,13 +129,20 @@ objectClass: EvoService ServiceName: opensmtpd ipServicePort: 25 ServiceType: smtp -ServiceVersion: OpenSMTPD 5.4.3 +ServiceVersion: OpenSMTPD 6.7.1p1 dn: ServiceName=ntp,EvoComputerName=${EvoComputerName},ou=computer,dc=evolix,dc=net NagiosEnabled: TRUE objectClass: EvoService ServiceName: ntp ServiceType: ntp -ServiceVersion: OpenNTPd 4.6 +ServiceVersion: OpenNTPd 6.2p3 + +dn: ServiceName=packetfilter,EvoComputerName=${EvoComputerName},ou=computer,dc=evolix,dc=net +NagiosEnabled: TRUE +objectClass: EvoService +ServiceName: packetfilter +ServiceType: firewall +ServiceVersion: packetfilter EOT diff --git a/roles/post-install/files/motd-carp-state.sh b/roles/post-install/files/motd-carp-state.sh new file mode 100755 index 0000000..cc29db3 --- /dev/null +++ b/roles/post-install/files/motd-carp-state.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +if [ ! -f /etc/motd-original ]; then + cp /etc/motd /etc/motd-original +fi + +if [ ! -f /tmp/carp.state ]; then + echo "unknown" > /tmp/carp.state +fi + +ifconfig carp0 | grep -q master +master=$? +ifconfig carp0 | grep -q backup +backup=$? + +if [ "$master" -eq 0 ]; then + if [ $(cat /tmp/carp.state) = "master" ]; then + # We already were master, no change + exit 0 + fi +cat /etc/motd-original - << EOF > /etc/motd + __ ______ _____________________ + / |/ / | / ___/_ __/ ____/ __ \ + / /|_/ / /| | \__ \ / / / __/ / /_/ / + / / / / ___ |___/ // / / /___/ _, _/ +/_/ /_/_/ |_/____//_/ /_____/_/ |_| + +EOF +echo "master" > /tmp/carp.state +elif [ "$backup" -eq 0 ]; then + if [ $(cat /tmp/carp.state) = "backup" ]; then + # We already were backup, no change + exit 0 + fi +cat /etc/motd-original - << EOF > /etc/motd + ____ ___ ________ ____ ______ + / __ )/ | / ____/ //_/ / / / __ \ + / __ / /| |/ / / ,< / / / / /_/ / + / /_/ / ___ / /___/ /| / /_/ / ____/ +/_____/_/ |_\____/_/ |_\____/_/ + +EOF +echo "backup" > /tmp/carp.state +else + # No CARP + exit 0 +fi diff --git a/roles/post-install/tasks/main.yml b/roles/post-install/tasks/main.yml index 1876037..40f494c 100644 --- a/roles/post-install/tasks/main.yml +++ b/roles/post-install/tasks/main.yml @@ -2,3 +2,4 @@ # tasks files - include: ldif.yml - include: update.yml +- include: motd.yml diff --git a/roles/post-install/tasks/motd.yml b/roles/post-install/tasks/motd.yml new file mode 100644 index 0000000..58a0bde --- /dev/null +++ b/roles/post-install/tasks/motd.yml @@ -0,0 +1,25 @@ +--- +- name: Deploy dynamic motd script for CARP master or backup + copy: + src: motd-carp-state.sh + dest: /usr/share/scripts/motd-carp-state.sh + owner: root + group: wheel + mode: '0755' + +- name: Fetch root crontab content + command: > + 'grep "/bin/sh /usr/share/scripts/motd-carp-state.sh" /var/cron/tabs/root' + check_mode: false + register: root_crontab_content + failed_when: false + changed_when: false + +- name: Cron job for dynamic motd script is installed + cron: + name: dynamic motd for CARP + job: "/bin/sh /usr/share/scripts/motd-carp-state.sh" + disabled: true + when: + - not (root_crontab_content.stdout + | regex_search('/bin/sh /usr/share/scripts/motd-carp-state.sh')) diff --git a/roles/post-install/tasks/update.yml b/roles/post-install/tasks/update.yml index 1a6de6a..8ec4587 100644 --- a/roles/post-install/tasks/update.yml +++ b/roles/post-install/tasks/update.yml @@ -1,9 +1,4 @@ --- -- name: Fetch and install openup - get_url: - url: https://stable.mtier.org/openup - dest: /usr/local/bin/openup - mode: "0750" - -- name: Install updates (erratas) with openup - shell: /usr/local/bin/openup +- name: Check and install updates (erratas) if available + shell: /usr/sbin/syspatch + when: ansible_distribution_version is version_compare("6.1",'>=') diff --git a/tasks/commit_etc_git.yml b/tasks/commit_etc_git.yml index 032e898..e73dc85 100644 --- a/tasks/commit_etc_git.yml +++ b/tasks/commit_etc_git.yml @@ -3,19 +3,25 @@ command: git status --porcelain args: chdir: /etc - changed_when: False + changed_when: false register: git_status when: not ansible_check_mode - ignore_errors: yes + ignore_errors: true tags: - - commit-etc - + - commit-etc +# yamllint disable rule:line-length - name: /etc modifications are committed - shell: "git add -A . && git commit -m \"{{ commit_message | default('Ansible run') }}\" --author=\"{{ ansible_env.SUDO_USER | default('Root') }} <{{ ansible_env.SUDO_USER | default('Root') }}@{{ general_technical_realm }}>\"" + shell: > + git add -A . + && git commit + -m "{{ commit_message | default('Ansible run') }}" + --author="{{ ansible_env.SUDO_USER | default('Root') }} + <{{ ansible_env.SUDO_USER | default('Root') }}@{{ general_technical_realm }}>" args: chdir: /etc register: etc_commit_end_evolinux when: not ansible_check_mode and git_status.stdout != "" - ignore_errors: yes + ignore_errors: true tags: - - commit-etc + - commit-etc +# yamllint enable rule:line-length diff --git a/vars/main.yml b/vars/main.yml index ed3fb9f..38f109c 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -3,26 +3,31 @@ ## Edit and uncomment to overwrite the default values ## ######################################################## -#ntpd_servers: -#- "ntp.evolix.net" +# ntpd_servers: +# - "ntp.evolix.net" # -#general_alert_email: "root@localhost" -#general_technical_realm: "example.com" +# general_alert_email: "root@localhost" +# general_technical_realm: "example.com" # -#evomaintenance_realm: "example.com" -#evomaintenance_alert_email: "evomaintenance-{{ inventory_hostname }}@{{ evomaintenance_realm }}" -#evomaintenance_hostname: "{{ inventory_hostname }}.{{ general_technical_realm }}" -#evomaintenance_pg_host: Null -#evomaintenance_pg_passwd: Null -#evomaintenance_pg_db: Null -#evomaintenance_pg_table: Null -#evomaintenance_from_domain: "{{ evomaintenance_realm }}" -#evomaintenance_from: "evomaintenance@{{ evomaintenance_from_domain }}" -#evomaintenance_full_from: "Evomaintenance <{{ evomaintenance_from }}>" -#evomaintenance_urgency_from: mama.doe@example.com -#evomaintenance_urgency_tel: "06.00.00.00.00" +# evomaintenance_realm: "example.com" +# evomaintenance_alert_email: +# "evomaintenance-{{ inventory_hostname }}@{{ evomaintenance_realm }}" +# evomaintenance_hostname: +# "{{ inventory_hostname }}.{{ general_technical_realm }}" +# evomaintenance_pg_host: Null +# evomaintenance_pg_passwd: Null +# evomaintenance_pg_db: Null +# evomaintenance_pg_table: Null +# evomaintenance_from_domain: "{{ evomaintenance_realm }}" +# evomaintenance_from: "evomaintenance@{{ evomaintenance_from_domain }}" +# evomaintenance_full_from: "Evomaintenance <{{ evomaintenance_from }}>" +# evomaintenance_urgency_from: mama.doe@example.com +# evomaintenance_urgency_tel: "06.00.00.00.00" # -#evolix_users: +# evobsd_ssh_group: "foo-ssh" +# evobsd_sudo_group: "foo-sudo" +# +# evolix_users: # foo: # name: foo # uid: 1042 diff --git a/vars/openbsd-secret.yml b/vars/openbsd-secret.yml new file mode 100644 index 0000000..e69de29 diff --git a/vars/secrets.yml b/vars/secrets.yml new file mode 100644 index 0000000..e69de29