Merge branch 'unstable' into stable
gitea/ansible-roles/pipeline/head This commit looks good Details
gitea/ansible-roles/pipeline/tag This commit looks good Details

This commit is contained in:
Jérémy Lecour 2022-09-19 17:06:57 +02:00 committed by Jérémy Lecour
commit c3670ce897
67 changed files with 1632 additions and 905 deletions

View File

@ -21,11 +21,11 @@ pipeline {
def major = versions[0]
def minor = versions[0] + '.' + versions[1]
def patch = version.trim()
/* No crendentials yet
docker.withRegistry('', 'hub.docker') {
im.push(major)
im.push(minor)
im.push(patch)
*/
}
}
}
}
@ -40,9 +40,9 @@ pipeline {
im.inside {
sh 'echo Test needed'
}
/* No crendentials yet
docker.withRegistry('', 'hub.docker') {
im.push('latest')
*/
}
}
}
}

View File

@ -1,36 +0,0 @@
kind: pipeline
name: default
steps:
- name: build tagged docker image
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: evolix/ansible-roles
auto_tag: true
environment:
ROLES_VERSION: $DRONE_COMMIT_SHA
when:
event:
- tag
- name: build latest docker image
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: evolix/ansible-roles
tags: latest
environment:
ROLES_VERSION: $DRONE_COMMIT_SHA
when:
branch:
- unstable

View File

@ -20,6 +20,45 @@ The **patch** part changes is incremented if multiple releases happen the same m
### Security
## [22.09] 2022-09-19
### Added
* evolinux_users: create only users who have a certain value for the `create` key (default: `always`).
* php: install php-xml with recent PHP versions
* vrrp: add an `ip.yml` task file to help create VRRP addresses
* webapps/nextcloud: Add compatibility with apache2, and apache2 mod_php.
* memcached: NRPE check for multi-instance setup
* munin: Add ipmi_ plugins on dedicated hardware
* proftpd: Add options to override configs (and add a warning if file was overriden)
* proftpd: Allow user auth with ssh keys
### Changed
* evocheck: upstream release 22.09
* evolinux-base: update-evobackup-canary upstream release 22.06
* generate-ldif: Support any MariaDB version
* minifirewall: use handlers to restart minifirewall
* openvpn: automate the initialization of the CA and the creation of the server certificate ; use openssl_dhparam module instead of a command
* generate-ldif: support any version of MariaDB (instead of only 10.0, 10.1 and 10.3)
* openvpn: Run OpenVPN with the \_openvpn user and group instead of nobody which is originally for NFS
* nagios-nrpe: Upgrade check_mongo
### Fixed
* fail2ban: fix dovecot-evolix regex syntax
* haproxy: make it so that munin doesn't break if there is a non default `haproxy_stats_path`
* mysql: Add missing Munin conf for Debian 11
* redis: config directory must be owned by the user that runs the service (to be able to write tmp config files in it)
* varnish: make `-j <jail_config>` the first argument on jessie/stretch as it has to be the first argument there.
* webapps/nextcloud: Add missing dependencies for imagick
### Removed
* evocheck: remove failure if deprecated variable is used
* webapps/nextcloud: Drop support for Nginx
## [22.07.1] 2022-07-28
### Changed

View File

@ -1,10 +1,10 @@
#!/bin/bash
# EvoCheck
# Script to verify compliance of a Debian/OpenBSD server
# Script to verify compliance of a Linux (Debian) server
# powered by Evolix
VERSION="22.07"
VERSION="22.09"
readonly VERSION
# base functions
@ -30,7 +30,7 @@ END
}
show_help() {
cat <<END
evocheck is a script that verifies Evolix conventions on Debian/OpenBSD servers.
evocheck is a script that verifies Evolix conventions on Linux (Debian) servers.
Usage: evocheck
or evocheck --cron
@ -50,7 +50,6 @@ detect_os() {
# OS detection
DEBIAN_RELEASE=""
LSB_RELEASE_BIN=$(command -v lsb_release)
OPENBSD_RELEASE=""
if [ -e /etc/debian_version ]; then
DEBIAN_VERSION=$(cut -d "." -f 1 < /etc/debian_version)
@ -68,9 +67,6 @@ detect_os() {
12) DEBIAN_RELEASE="bookworm";;
esac
fi
elif [ "$(uname -s)" = "OpenBSD" ]; then
# use a better release name
OPENBSD_RELEASE=$(uname -r)
fi
}
@ -107,9 +103,6 @@ debian_release() {
debian_version() {
printf "%s" "${DEBIAN_VERSION}"
}
is_openbsd() {
test -n "${OPENBSD_RELEASE}"
}
is_pack_web(){
test -e /usr/share/scripts/web-add.sh || test -e /usr/share/scripts/evoadmin/web-add.sh
@ -582,8 +575,8 @@ check_autoif() {
interfaces=$(/sbin/ifconfig -s | tail -n +2 | grep -E -v "^(lo|vnet|docker|veth|tun|tap|macvtap|vrrp)" | cut -d " " -f 1 |tr "\n" " ")
fi
for interface in $interfaces; do
if ! grep -q "^auto $interface" /etc/network/interfaces; then
failed "IS_AUTOIF" "Network interface \`${interface}' is not set to auto"
if grep -Rq "^iface $interface" /etc/network/interfaces* && ! grep -Rq "^auto $interface" /etc/network/interfaces*; then
failed "IS_AUTOIF" "Network interface \`${interface}' is statically defined but not set to auto"
test "${VERBOSE}" = 1 || break
fi
done
@ -1236,7 +1229,7 @@ check_sshpermitrootno() {
sshd_args=
fi
# shellcheck disable=SC2086
if ! (sshd -T ${sshd_args} | grep -q 'permitrootlogin no'); then
if ! (sshd -T ${sshd_args} 2> /dev/null | grep -qi 'permitrootlogin no'); then
failed "IS_SSHPERMITROOTNO" "PermitRoot should be set to no"
fi
}
@ -1408,8 +1401,6 @@ download_versions() {
if is_debian; then
versions_url="https://upgrades.evolix.org/versions-${DEBIAN_RELEASE}"
elif is_openbsd; then
versions_url="https://upgrades.evolix.org/versions-${OPENBSD_RELEASE}"
else
failed "IS_CHECK_VERSIONS" "error determining os release"
fi
@ -1536,9 +1527,7 @@ main() {
main_output_file=$(mktemp --tmpdir="${TMPDIR:-/tmp}" "evocheck.main.XXXXX")
files_to_cleanup="${files_to_cleanup} ${main_output_file}"
#-----------------------------------------------------------
# Tests communs à tous les systèmes
#-----------------------------------------------------------
MINIFW_FILE=$(minifirewall_file)
test "${IS_TMP_1777:=1}" = 1 && check_tmp_1777
test "${IS_ROOT_0700:=1}" = 1 && check_root_0700
@ -1549,13 +1538,6 @@ main() {
test "${IS_EVOMAINTENANCECONF:=1}" = 1 && check_evomaintenanceconf
test "${IS_PRIVKEYWOLRDREADABLE:=1}" = 1 && check_privatekeyworldreadable
#-----------------------------------------------------------
# Vérifie si c'est une debian et fait les tests appropriés.
#-----------------------------------------------------------
if is_debian; then
MINIFW_FILE=$(minifirewall_file)
test "${IS_LSBRELEASE:=1}" = 1 && check_lsbrelease
test "${IS_DPKGWARNING:=1}" = 1 && check_dpkgwarning
test "${IS_UMASKSUDOERS:=1}" = 1 && check_umasksudoers
@ -1661,109 +1643,6 @@ main() {
test "${IS_NGINX_LETSENCRYPT_UPTODATE:=1}" = 1 && check_nginx_letsencrypt_uptodate
test "${IS_LXC_CONTAINER_RESOLV_CONF:=1}" = 1 && check_lxc_container_resolv_conf
test "${IS_CHECK_VERSIONS:=1}" = 1 && check_versions
fi
#-----------------------------------------------------------
# Tests spécifiques à OpenBSD
#-----------------------------------------------------------
if is_openbsd; then
if [ "${IS_SOFTDEP:=1}" = 1 ]; then
grep -q "softdep" /etc/fstab || failed "IS_SOFTDEP"
fi
if [ "${IS_WHEEL:=1}" = 1 ]; then
grep -qE "^%wheel.*$" /etc/sudoers || failed "IS_WHEEL"
fi
if [ "${IS_SUDOADMIN:=1}" = 1 ]; then
grep -qE "^User_Alias ADMIN=.*$" /etc/sudoers || failed "IS_SUDOADMIN"
fi
if [ "${IS_PKGMIRROR:=1}" = 1 ]; then
grep -qE "^export PKG_PATH=http://ftp\.fr\.openbsd\.org/pub/OpenBSD/[0-9.]+/packages/[a-z0-9]+/$" /root/.profile \
|| failed "IS_PKGMIRROR"
fi
if [ "${IS_HISTORY:=1}" = 1 ]; then
f=/root/.profile
{ grep -q "^HISTFILE=\$HOME/.histfile" $f \
&& grep -q "^export HISTFILE" $f \
&& grep -q "^HISTSIZE=1000" $f \
&& grep -q "^export HISTSIZE" $f;
} || failed "IS_HISTORY"
fi
if [ "${IS_VIM:=1}" = 1 ]; then
command -v vim > /dev/null 2>&1 || failed "IS_VIM"
fi
if [ "${IS_TTYC0SECURE:=1}" = 1 ]; then
grep -Eqv "^ttyC0.*secure$" /etc/ttys || failed "IS_TTYC0SECURE"
fi
if [ "${IS_CUSTOMSYSLOG:=1}" = 1 ]; then
grep -q "Evolix" /etc/newsyslog.conf || failed "IS_CUSTOMSYSLOG"
fi
if [ "${IS_NOINETD:=1}" = 1 ]; then
grep -q "inetd=NO" /etc/rc.conf.local 2>/dev/null || failed "IS_NOINETD"
fi
if [ "${IS_SUDOMAINT:=1}" = 1 ]; then
f=/etc/sudoers
{ grep -q "Cmnd_Alias MAINT = /usr/share/scripts/evomaintenance.sh" $f \
&& grep -q "ADMIN ALL=NOPASSWD: MAINT" $f;
} || failed "IS_SUDOMAINT"
fi
if [ "${IS_POSTGRESQL:=1}" = 1 ]; then
pkg info | grep -q postgresql-client || failed "IS_POSTGRESQL" "postgresql-client is not installed"
fi
if [ "${IS_NRPE:=1}" = 1 ]; then
{ pkg info | grep -qE "nagios-plugins-[0-9.]" \
&& pkg info | grep -q nagios-plugins-ntp \
&& pkg info | grep -q nrpe;
} || failed "IS_NRPE" "NRPE is not installed"
fi
# if [ "${IS_NRPEDISKS:=1}" = 1 ]; then
# NRPEDISKS=$(grep command.check_disk /etc/nrpe.cfg 2>/dev/null | grep "^command.check_disk[0-9]" | sed -e "s/^command.check_disk\([0-9]\+\).*/\1/" | sort -n | tail -1)
# DFDISKS=$(df -Pl | grep -E -v "(^Filesystem|/lib/init/rw|/dev/shm|udev|rpc_pipefs)" | wc -l)
# [ "$NRPEDISKS" = "$DFDISKS" ] || failed "IS_NRPEDISKS"
# fi
# Verification du check_mailq dans nrpe.cfg (celui-ci doit avoir l'option "-M postfix" si le MTA est Postfix)
#
# if [ "${IS_NRPEPOSTFIX:=1}" = 1 ]; then
# pkg info | grep -q postfix && ( grep -q "^command.*check_mailq -M postfix" /etc/nrpe.cfg 2>/dev/null || failed "IS_NRPEPOSTFIX" )
# fi
if [ "${IS_NRPEDAEMON:=1}" = 1 ]; then
grep -q "echo -n ' nrpe'; /usr/local/sbin/nrpe -d" /etc/rc.local \
|| failed "IS_NREPEDAEMON"
fi
if [ "${IS_ALERTBOOT:=1}" = 1 ]; then
grep -qE "^date \| mail -sboot/reboot .*evolix.fr$" /etc/rc.local \
|| failed "IS_ALERTBOOT"
fi
if [ "${IS_RSYNC:=1}" = 1 ]; then
pkg info | grep -q rsync || failed "IS_RSYNC"
fi
if [ "${IS_CRONPATH:=1}" = 1 ]; then
grep -q "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" /var/cron/tabs/root \
|| failed "IS_CRONPATH"
fi
#TODO
# - Check en profondeur de postfix
# - NRPEDISK et NRPEPOSTFIX
fi
if [ -f "${main_output_file}" ]; then
lines_found=$(wc -l < "${main_output_file}")

View File

@ -1,12 +1,5 @@
---
- name: Package install is not supported anymore
fail:
msg: Package install is not supported anymore
when:
- evocheck_force_install is defined
- evocheck_force_install == "package"
- include: install.yml
- include: cron.yml

View File

@ -3,7 +3,7 @@
PROGNAME="update-evobackup-canary"
REPOSITORY="https://gitea.evolix.org/evolix/evobackup"
VERSION="22.05"
VERSION="22.06"
readonly VERSION
# base functions
@ -44,8 +44,8 @@ main() {
if [ -z "${canary_file:-}" ]; then
canary_file="/zzz_evobackup_canary"
fi
# This option is supported since (at least) Debian 8
date=$(date --iso-8601=seconds)
# This option is supported both on OpenBSD which does not use GNU date and on Debian
date=$(date "+%FT%T%z")
printf "%s %s\n" "${date}" "${who}" >> "${canary_file}"
}

View File

@ -17,7 +17,7 @@
- name: update-evobackup-canary script is present
copy:
src: "update-evobackup-canary.sh"
src: update-evobackup-canary
dest: /usr/local/bin/update-evobackup-canary
force: True
owner: root
@ -30,11 +30,11 @@
path: /usr/local/sbin/update-evobackup-canary
state: absent
- name: dir-check script is present
copy:
src: "dir-check.sh"
dest: /usr/local/bin/dir-check
force: True
owner: root
group: root
mode: "0755"
# - name: dir-check script is present
# copy:
# src: "dir-check.sh"
# dest: /usr/local/bin/dir-check
# force: True
# owner: root
# group: root
# mode: "0755"

View File

@ -19,6 +19,7 @@ evolinux_users:
groups: "baz"
password_hash: 'sdfgsdfgsdfgsdfg'
ssh_key: 'ssh-rsa AZERTYXYZ'
create: always
bar:
name: bar
uid: 1002
@ -30,6 +31,7 @@ evolinux_users:
ssh_keys:
- 'ssh-rsa QWERTYUIOP'
- 'ssh-ed25519 QWERTYUIOP'
create: on_demand
```
* `evolinux_sudo_group`: which group to use for sudo (default: `evolinux-sudo`)

View File

@ -6,3 +6,6 @@ evolinux_ssh_group: "evolinux-ssh"
evolinux_internal_group: ""
evolinux_root_disable_ssh: True
# Defines which groups of users are created
evolinux_users_create: always

View File

@ -16,7 +16,9 @@
vars:
user: "{{ item.value }}"
loop: "{{ evolinux_users | dict2items }}"
when: evolinux_users | length > 0
when:
- user.create == evolinux_users_create
- evolinux_users | length > 0
- name: Configure sudo
include: sudo.yml

View File

@ -50,6 +50,7 @@
user: "{{ item.value }}"
loop: "{{ evolinux_users | dict2items }}"
when:
- user.create == evolinux_users_create
- ssh_allowusers
- not ssh_allowgroups

View File

@ -6,6 +6,7 @@
loop: "{{ evolinux_users | dict2items }}"
when:
- evolinux_users | length > 0
- user.create == evolinux_users_create
- ansible_distribution_release == "jessie"
@ -16,6 +17,9 @@
vars:
user: "{{ item.value }}"
loop: "{{ evolinux_users | dict2items }}"
when:
- evolinux_users | length > 0
- user.create == evolinux_users_create
when:
- ansible_distribution_major_version is defined
- ansible_distribution_major_version is version('9', '>=')

View File

@ -1,3 +1,3 @@
[Definition]
failregex = (?: pop3-login|imap-login): .*(?:Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed|Aborted login \(\d+ authentication attempts).*rip=(?P<host>\S*),.*
failregex = (?: pop3-login|imap-login): .*(?:Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed|Aborted login \(\d+ authentication attempts).*rip=<HOST>,.*
ignoreregex =

View File

@ -408,12 +408,8 @@ EOT
fi
# MariaDB
if is_pkg_installed mariadb-server-10.3; then
mariadb_version=$(get_pkg_version mariadb-server-10.3)
elif is_pkg_installed mariadb-server-10.1; then
mariadb_version=$(get_pkg_version mariadb-server-10.1)
elif is_pkg_installed mariadb-server-10.0; then
mariadb_version=$(get_pkg_version mariadb-server-10.0)
if is_pkg_installed mariadb-server; then
mariadb_version=$(get_pkg_version mariadb-server)
fi
if [ -n "${mariadb_version}" ]; then
cat <<EOT >> "${ldif_file}"

View File

@ -1,4 +1,4 @@
[haproxy_*]
{% if haproxy_stats_internal_enable %}
env.url http://{{ haproxy_stats_internal_host }}:{{ haproxy_stats_internal_port }}/;csv;norefresh
env.url http://{{ haproxy_stats_internal_host }}:{{ haproxy_stats_internal_port }}{{ haproxy_stats_path }};csv;norefresh
{% endif %}

View File

@ -3,7 +3,7 @@
- name: "{{ lxc_php_version }} - Install PHP packages"
lxc_container:
name: "{{ lxc_php_version }}"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-zip composer libphp-phpmailer"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-xml php-zip composer libphp-phpmailer"
- name: "{{ lxc_php_version }} - fix bullseye repository"
replace:

View File

@ -46,7 +46,7 @@
- name: "{{ lxc_php_version }} - Install PHP packages"
lxc_container:
name: "{{ lxc_php_version }}"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-zip composer libphp-phpmailer"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-xml php-zip composer libphp-phpmailer"
- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration"
template:

View File

@ -46,7 +46,7 @@
- name: "{{ lxc_php_version }} - Install PHP packages"
lxc_container:
name: "{{ lxc_php_version }}"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-zip composer libphp-phpmailer"
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-xml php-zip composer libphp-phpmailer"
- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration"
template:

View File

@ -0,0 +1,82 @@
#!/bin/bash
# {{ ansible_managed }}
set -u
return=0
nb_crit=0
nb_warn=0
nb_ok=0
nb_unchk=0
output=""
vendored_check=/usr/local/lib/nagios/plugins/check_memcached.pl
if [ -x $vendored_check ]; then
check_bin=$vendored_check
else
echo "UNCHK - can't find check_memcached"
exit 3
fi
check_server() {
name=$1
conf_file=$2
host=$(config_var "-l" "${conf_file}")
port=$(config_var "-p" "${conf_file}")
cmd="${check_bin} -H ${host} -p ${port}"
result=$($cmd)
ret="${?}"
if [ "${ret}" -ge 2 ]; then
nb_crit=$((nb_crit + 1))
printf -v output "%s%s\n" "${output}" "${result}"
[ "${return}" -le 2 ] && return=2
elif [ "${ret}" -ge 1 ]; then
nb_warn=$((nb_warn + 1))
printf -v output "%s%s\n" "${output}" "${result}"
[ "${return}" -le 1 ] && return=1
else
nb_ok=$((nb_ok + 1))
printf -v output "%s%s\n" "${output}" "${result}"
[ "${return}" -le 0 ] && return=0
fi
}
config_var() {
variable=$1
file=$2
test -f "${file}" && grep -E "^${variable}\s+.+$" "${file}" | awk '{ print $2 }' | sed -e "s/^[\"']//" -e "s/[\"']$//"
}
# default instance
if systemctl is-enabled -q memcached; then
check_server "default" "/etc/memcached.conf"
fi
# additional instances
conf_files=$(ls -1 /etc/memcached_*.conf 2> /dev/null)
for conf_file in ${conf_files}; do
name=$(basename "${conf_file}" | sed '{s|memcached_||;s|\.conf||}')
if systemctl is-enabled -q "memcached@${name}.service"; then
check_server "${name}" "${conf_file}"
else
nb_unchk=$((nb_unchk + 1))
output="${output}UNCHK - ${name} (unit is disabled or missing)\n"
fi
done
[ "${return}" -ge 0 ] && header="OK"
[ "${return}" -ge 1 ] && header="WARNING"
[ "${return}" -ge 2 ] && header="CRITICAL"
printf "%s - %s UNCHK / %s CRIT / %s WARN / %s OK\n\n" "${header}" "${nb_unchk}" "${nb_crit}" "${nb_warn}" "${nb_ok}"
printf "%s" "${output}" | grep -E "CRITICAL"
printf "%s" "${output}" | grep -E "WARNING"
printf "%s" "${output}" | grep -E "OK"
printf "%s" "${output}" | grep -E "UNCHK"
exit "${return}"

View File

@ -0,0 +1,17 @@
- name: Memcached is configured.
template:
src: memcached.conf.j2
dest: /etc/memcached.conf
mode: "0644"
notify: restart memcached
tags:
- memcached
- name: Memcached is running and enabled on boot.
service:
name: memcached
enabled: yes
state: started
tags:
- memcached

View File

@ -0,0 +1,41 @@
---
- name: Add systemd unit template
copy:
src: memcached@.service
dest: /etc/systemd/system/memcached@.service
tags:
- memcached
- name: Disable default memcached systemd unit
systemd:
name: memcached
enabled: false
state: stopped
tags:
- memcached
- name: Make sure memcached.conf is absent
file:
path: /etc/memcached.conf
state: absent
tags:
- memcached
- name: "Create a configuration file for instance ({{ memcached_instance_name }})"
template:
src: memcached.conf.j2
dest: /etc/memcached_{{ memcached_instance_name }}.conf
mode: "0644"
tags:
- memcached
- name: "Enable and start the memcached instance ({{ memcached_instance_name }})"
systemd:
name: memcached@{{ memcached_instance_name }}
enabled: yes
state: started
daemon_reload: yes
masked: no
tags:
- memcached

View File

@ -1,73 +1,15 @@
- name: ensure packages are installed
- name: Ensure memcached is installed
apt:
name: memcached
state: present
tags:
- memcached
- name: Memcached is configured.
template:
src: memcached.conf.j2
dest: /etc/memcached.conf
mode: "0644"
notify: restart memcached
tags:
- memcached
when: memcached_instance_name | length == 0
- include: instance-default.yml
when: memcached_instance_name is undefined
- name: Memcached is running and enabled on boot.
service:
name: memcached
enabled: yes
state: started
tags:
- memcached
when: memcached_instance_name | length == 0
- name: Add systemd template
copy:
src: memcached@.service
dest: /etc/systemd/system/memcached@.service
tags:
- memcached
when: memcached_instance_name | length > 0
- name: Delete default memcached systemd configuration file
systemd:
name: memcached
enabled: false
state: stopped
tags:
- memcached
when: memcached_instance_name | length > 0
- name: Make sure memcached.conf is absent
file:
path: /etc/memcached.conf
state: absent
tags:
- memcached
when: memcached_instance_name | length > 0
- name: Create a configuration file
template:
src: memcached.conf.j2
dest: /etc/memcached_{{ memcached_instance_name }}.conf
mode: "0644"
tags:
- memcached
when: memcached_instance_name | length > 0
- name: Enable and start the memcached instance
systemd:
name: memcached@{{ memcached_instance_name }}
enabled: yes
state: started
daemon_reload: yes
masked: no
tags:
- memcached
when: memcached_instance_name | length > 0
- include: instance-multi.yml
when: memcached_instance_name is defined
- include: munin.yml

View File

@ -2,7 +2,7 @@
- name: Choose packages (Oracle)
set_fact:
multi: "multi_"
when: memcached_instance_name | length > 0
when: memcached_instance_name is defined
- name: is Munin present ?
stat:

View File

@ -1,6 +1,4 @@
---
- include_role:
name: evolix/remount-usr
- name: Is nrpe present ?
stat:
@ -10,7 +8,12 @@
- block:
- name: Install dependencies
apt:
name: libcache-memcached-perl
name:
- libcache-memcached-perl
- libmemcached11
- include_role:
name: evolix/remount-usr
- name: Copy Nagios check for memcached
copy:
@ -18,13 +21,29 @@
dest: /usr/local/lib/nagios/plugins/
mode: "0755"
# TODO: install a "multi-instances" check if the memcached_instance_name variable is not null
- name: install check_memcached_instances
copy:
src: check_memcached_instances.sh
dest: /usr/local/lib/nagios/plugins/check_memcached_instances
force: yes
mode: "0755"
owner: root
group: root
- name: Add NRPE check
- name: Add NRPE check (single instance)
lineinfile:
name: /etc/nagios/nrpe.d/evolix.cfg
regexp: '^command\[check_memcached\]='
line: 'command[check_memcached]=/usr/local/lib/nagios/plugins/check_memcached.pl -H 127.0.0.1 -p {{ memcached_port }}'
notify: restart nagios-nrpe-server
when: memcached_instance_name is undefined
- name: Add NRPE check (multi instance)
lineinfile:
name: /etc/nagios/nrpe.d/evolix.cfg
regexp: '^command\[check_memcached\]='
line: 'command[check_memcached]=/usr/local/lib/nagios/plugins/check_memcached_instances'
notify: restart nagios-nrpe-server
when: memcached_instance_name is defined
when: nrpe_evolix_config.stat.exists

View File

@ -4,3 +4,19 @@
service:
name: nagios-nrpe-server
state: restarted
- name: restart minifirewall (modern)
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'minifirewall failed' in minifirewall_init_restart.stdout"
- name: restart minifirewall (legacy)
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout"
- name: restart minifirewall (noop)
meta: noop
register: minifirewall_init_restart
failed_when: False
changed_when: False

View File

@ -197,21 +197,15 @@
path: "{{ minifirewall_main_file }}"
register: minifirewall_after
- name: restart minifirewall
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout"
- name: Schedule minifirewall restart (legacy)
command: /bin/true
notify: "restart minifirewall (legacy)"
when:
- minifirewall_install_mode == 'legacy'
- minifirewall_restart_if_needed | bool
- minifirewall_is_running.rc == 0
- minifirewall_before.stat.checksum != minifirewall_after.stat.checksum
- minifirewall_before.stat.checksum != minifirewall_after.stat.checksum or minifirewall_upgrade_script is changed or minifirewall_upgrade_config is changed
- name: restart minifirewall (noop)
meta: noop
register: minifirewall_init_restart
failed_when: False
changed_when: False
when: not (minifirewall_restart_if_needed | bool)
- debug:
var: minifirewall_init_restart

View File

@ -282,11 +282,11 @@
path: "/etc/default/minifirewall"
register: minifirewall_after
- name: restart minifirewall
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'minifirewall failed' in minifirewall_init_restart.stdout"
- name: Schedule minifirewall restart (modern)
command: /bin/true
notify: "restart minifirewall (modern)"
when:
- minifirewall_install_mode != 'legacy'
- minifirewall_restart_if_needed | bool
- minifirewall_is_running.rc == 0
- minifirewall_before.stat.checksum != minifirewall_after.stat.checksum or minifirewall_upgrade_script is changed or minifirewall_upgrade_config is changed

View File

@ -1,9 +1,5 @@
---
- name: Compose minifirewall_restart_handler_name variable
set_fact:
minifirewall_restart_handler_name: "{{ minifirewall_restart_if_needed | bool | ternary('restart minifirewall', 'restart minifirewall (noop)') }}"
# Legacy or modern mode? ##############################################
- name: Check minifirewall
@ -39,6 +35,25 @@
var: minifirewall_install_mode
verbosity: 1
- name: 'Set minifirewall_restart_handler_name to "noop"'
set_fact:
minifirewall_restart_handler_name: "restart minifirewall (noop)"
when: not (minifirewall_restart_if_needed | bool)
- name: 'Set minifirewall_restart_handler_name to "legacy"'
set_fact:
minifirewall_restart_handler_name: "restart minifirewall (legacy)"
when:
- minifirewall_restart_if_needed | bool
- minifirewall_install_mode == 'legacy'
- name: 'Set minifirewall_restart_handler_name to "modern"'
set_fact:
minifirewall_restart_handler_name: "restart minifirewall (modern)"
when:
- minifirewall_restart_if_needed | bool
- minifirewall_install_mode != 'legacy'
#######################################################################
- name: Fail if minifirewall_main_file is defined (legacy mode)
@ -106,18 +121,16 @@
var: minifirewall_restart_force | bool
verbosity: 1
- name: Force restart minifirewall (modern mode)
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'minifirewall failed' in minifirewall_init_restart.stdout"
when:
- minifirewall_install_mode != 'legacy'
- minifirewall_restart_force | bool
- name: Force restart minifirewall (legacy mode)
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout"
- name: Force restart minifirewall (legacy)
command: /bin/true
notify: "restart minifirewall (legacy)"
when:
- minifirewall_install_mode == 'legacy'
- minifirewall_restart_force | bool
- name: Force restart minifirewall (modern)
command: /bin/true
notify: "restart minifirewall (modern)"
when:
- minifirewall_install_mode != 'legacy'
- minifirewall_restart_force | bool

View File

@ -1,4 +1,22 @@
---
- name: Stat minifirewall config file (before)
stat:
path: "/etc/default/minifirewall"
register: minifirewall_before
- name: Check if minifirewall is running
shell:
cmd: /sbin/iptables -L -n | grep -E "^(DROP\s+udp|ACCEPT\s+icmp)\s+--\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0\s*$"
changed_when: False
failed_when: False
check_mode: no
register: minifirewall_is_running
- debug:
var: minifirewall_is_running
verbosity: 1
- name: Add some rules at the end of minifirewall file
template:
src: "{{ item }}"
@ -30,20 +48,14 @@
var: minifirewall_tail_source
verbosity: 1
- name: restart minifirewall
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout"
- name: Schedule minifirewall restart (legacy)
command: /bin/true
notify: "restart minifirewall (legacy)"
when:
- minifirewall_tail_template is changed
- minifirewall_install_mode == 'legacy'
- minifirewall_restart_if_needed | bool
- name: restart minifirewall (noop)
meta: noop
register: minifirewall_init_restart
failed_when: False
changed_when: False
when: not (minifirewall_restart_if_needed | bool)
- minifirewall_is_running.rc == 0
- minifirewall_tail_template is changed
- debug:
var: minifirewall_init_restart

View File

@ -1,4 +1,22 @@
---
- name: Stat minifirewall config file (before)
stat:
path: "/etc/default/minifirewall"
register: minifirewall_before
- name: Check if minifirewall is running
shell:
cmd: /sbin/iptables -L -n | grep -E "^(DROP\s+udp|ACCEPT\s+icmp)\s+--\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0\s*$"
changed_when: False
failed_when: False
check_mode: no
register: minifirewall_is_running
- debug:
var: minifirewall_is_running
verbosity: 1
- name: Add some rules at the end of minifirewall file
template:
src: "{{ item }}"
@ -18,20 +36,14 @@
var: minifirewall_tail_template
verbosity: 1
- name: restart minifirewall
command: /etc/init.d/minifirewall restart
register: minifirewall_init_restart
failed_when: "'minifirewall failed' in minifirewall_init_restart.stdout"
- name: Schedule minifirewall restart (modern)
command: /bin/true
notify: "restart minifirewall (modern)"
when:
- minifirewall_tail_template is changed
- minifirewall_install_mode != 'legacy'
- minifirewall_restart_if_needed | bool
- name: restart minifirewall (noop)
meta: noop
register: minifirewall_init_restart
failed_when: False
changed_when: False
when: not (minifirewall_restart_if_needed | bool)
- minifirewall_is_running.rc == 0
- minifirewall_tail_template is changed
- debug:
var: minifirewall_init_restart

View File

@ -1,12 +1,13 @@
---
- name: Ensure that Munin is installed
- name: Ensure that Munin (and useful dependencies) is installed
apt:
name:
- munin
- munin-node
- munin-plugins-core
- munin-plugins-extra
- gawk
state: present
tags:
- munin
@ -79,16 +80,32 @@
tags:
- munin
- name: Enable sensors plugin unless VM detected
- name: Enable sensors_ plugin on dedicated hardware
file:
src: /usr/share/munin/plugins/sensors_
dest: /etc/munin/plugins/sensors_temp
dest: "/etc/munin/plugins/sensors_{{ item }}"
state: link
when: ansible_virtualization_role != "guest"
with_items:
- fan
- temp
when: ansible_virtualization_role == "host"
notify: restart munin-node
tags:
- munin
- name: Enable ipmi_ plugin on dedicated hardware
file:
src: /usr/share/munin/plugins/ipmi_
dest: "/etc/munin/plugins/ipmi_{{ item }}"
state: link
when: ansible_virtualization_role == "host"
notify: restart munin-node
with_items:
- fans
- temp
- power
- volts
- name: adjustments for grsec kernel
blockinfile:
dest: /etc/munin/plugin-conf.d/munin-node

View File

@ -66,13 +66,42 @@
- replication
notify: restart munin-node
- name: verify Munin configuration for mysql
- name: verify Munin configuration for mysql < Debian 11
replace:
dest: /etc/munin/plugin-conf.d/munin-node
after: '\[mysql\*\]'
regexp: '^env.mysqluser (.+)$'
replace: 'env.mysqluser debian-sys-maint'
notify: restart munin-node
when: ansible_distribution_major_version is version_compare('11', '<')
- name: set Munin env.mysqluser option for mysql >= Debian 11
replace:
dest: /etc/munin/plugin-conf.d/munin-node
after: '\[mysql\*\]'
regexp: '^env.mysqluser (.+)$'
replace: 'env.mysqluser root'
notify: restart munin-node
when: ansible_distribution_major_version is version_compare('11', '>=')
- name: set Munin env.mysqlopts option for mysql >= Debian 11
replace:
dest: /etc/munin/plugin-conf.d/munin-node
after: '\[mysql\*\]'
regexp: '^env.mysqlopts (.+)$'
replace: 'env.mysqlopts --defaults-file=/root/.my.cnf'
notify: restart munin-node
when: ansible_distribution_major_version is version_compare('11', '>=')
- name: set Munin env.mysqlconnection option for mysql >= Debian 11
replace:
dest: /etc/munin/plugin-conf.d/munin-node
after: '\[mysql\*\]'
regexp: '^env.mysqlconnection (.+)$'
replace: 'env.mysqlconnection DBI:mysql:mysql;mysql_read_default_file=/root/.my.cnf'
notify: restart munin-node
when: ansible_distribution_major_version is version_compare('11', '>=')
when: munin_node_plugins_config.stat.exists
tags:

591
nagios-nrpe/files/plugins/check_mongodb Executable file → Normal file
View File

@ -17,24 +17,29 @@
# - Dag Stockstad <dag.stockstad@gmail.com>
# - @Andor on github
# - Steven Richards - Captainkrtek on github
# - Max Vernimmen
# - Max Vernimmen - @mvernimmen-CG / @mvernimmen on github
# - Kris Nova - @kris@nivenly.com github.com/kris-nova
# - Jan Kantert - firstname@lastname.net
#
# USAGE
#
# See the README.md
#
from __future__ import print_function
from __future__ import division
import sys
import time
import optparse
import textwrap
import re
import os
import numbers
import socket
try:
import pymongo
except ImportError, e:
print e
except ImportError as e:
print(e)
sys.exit(2)
# As of pymongo v 1.9 the SON API is part of the BSON package, therefore attempt
@ -78,37 +83,35 @@ def performance_data(perf_data, params):
def numeric_type(param):
if ((type(param) == float or type(param) == int or param == None)):
return True
return False
return param is None or isinstance(param, numbers.Real)
def check_levels(param, warning, critical, message, ok=[]):
if (numeric_type(critical) and numeric_type(warning)):
if param >= critical:
print "CRITICAL - " + message
print("CRITICAL - " + message)
sys.exit(2)
elif param >= warning:
print "WARNING - " + message
print("WARNING - " + message)
sys.exit(1)
else:
print "OK - " + message
print("OK - " + message)
sys.exit(0)
else:
if param in critical:
print "CRITICAL - " + message
print("CRITICAL - " + message)
sys.exit(2)
if param in warning:
print "WARNING - " + message
print("WARNING - " + message)
sys.exit(1)
if param in ok:
print "OK - " + message
print("OK - " + message)
sys.exit(0)
# unexpected param value
print "CRITICAL - Unexpected value : %d" % param + "; " + message
print("CRITICAL - Unexpected value : %d" % param + "; " + message)
return 2
@ -120,21 +123,32 @@ def get_server_status(con):
data = con.admin.command(son.SON([('serverStatus', 1)]))
return data
def split_host_port(string):
if not string.rsplit(':', 1)[-1].isdigit():
return (string, None)
string = string.rsplit(':', 1)
host = string[0] # 1st index is always host
port = int(string[1])
return (host, port)
def main(argv):
p = optparse.OptionParser(conflict_handler="resolve", description="This Nagios plugin checks the health of mongodb.")
p.add_option('-H', '--host', action='store', type='string', dest='host', default='127.0.0.1', help='The hostname you want to connect to')
p.add_option('-P', '--port', action='store', type='int', dest='port', default=27017, help='The port mongodb is runnung on')
p.add_option('-h', '--host-to-check', action='store', type='string', dest='host_to_check', default=None, help='The hostname you want to check (if this is different from the host you are connecting)')
p.add_option('--rdns-lookup', action='store_true', dest='rdns_lookup', default=False, help='RDNS(PTR) lookup on given host/host-to-check, to convert ip-address to fqdn')
p.add_option('-P', '--port', action='store', type='int', dest='port', default=27017, help='The port mongodb is running on')
p.add_option('--port-to-check', action='store', type='int', dest='port_to_check', default=None, help='The port you want to check (if this is different from the port you are connecting)')
p.add_option('-u', '--user', action='store', type='string', dest='user', default=None, help='The username you want to login as')
p.add_option('-p', '--pass', action='store', type='string', dest='passwd', default=None, help='The password you want to use for that user')
p.add_option('-W', '--warning', action='store', dest='warning', default=None, help='The warning threshold we want to set')
p.add_option('-C', '--critical', action='store', dest='critical', default=None, help='The critical threshold we want to set')
p.add_option('-W', '--warning', action='store', dest='warning', default=None, help='The warning threshold you want to set')
p.add_option('-C', '--critical', action='store', dest='critical', default=None, help='The critical threshold you want to set')
p.add_option('-A', '--action', action='store', type='choice', dest='action', default='connect', help='The action you want to take',
choices=['connect', 'connections', 'replication_lag', 'replication_lag_percent', 'replset_state', 'memory', 'memory_mapped', 'lock',
'flushing', 'last_flush_time', 'index_miss_ratio', 'databases', 'collections', 'database_size', 'database_indexes', 'collection_indexes', 'collection_size',
'queues', 'oplog', 'journal_commits_in_wl', 'write_data_files', 'journaled', 'opcounters', 'current_lock', 'replica_primary', 'page_faults',
'asserts', 'queries_per_second', 'page_faults', 'chunks_balance', 'connect_primary', 'collection_state', 'row_count', 'replset_quorum'])
'flushing', 'last_flush_time', 'index_miss_ratio', 'databases', 'collections', 'database_size', 'database_indexes', 'collection_documents', 'collection_indexes', 'collection_size',
'collection_storageSize', 'queues', 'oplog', 'journal_commits_in_wl', 'write_data_files', 'journaled', 'opcounters', 'current_lock', 'replica_primary',
'page_faults', 'asserts', 'queries_per_second', 'page_faults', 'chunks_balance', 'connect_primary', 'collection_state', 'row_count', 'replset_quorum'])
p.add_option('--max-lag', action='store_true', dest='max_lag', default=False, help='Get max replication lag (for replication_lag action only)')
p.add_option('--mapped-memory', action='store_true', dest='mapped_memory', default=False, help='Get mapped memory instead of resident (if resident memory can not be read)')
p.add_option('-D', '--perf-data', action='store_true', dest='perf_data', default=False, help='Enable output of Nagios performance data')
@ -145,12 +159,28 @@ def main(argv):
p.add_option('-q', '--querytype', action='store', dest='query_type', default='query', help='The query type to check [query|insert|update|delete|getmore|command] from queries_per_second')
p.add_option('-c', '--collection', action='store', dest='collection', default='admin', help='Specify the collection to check')
p.add_option('-T', '--time', action='store', type='int', dest='sample_time', default=1, help='Time used to sample number of pages faults')
p.add_option('-M', '--mongoversion', action='store', type='choice', dest='mongo_version', default='2', help='The MongoDB version you are talking with, either 2 or 3',
choices=['2','3'])
p.add_option('-a', '--authdb', action='store', type='string', dest='authdb', default='admin', help='The database you want to authenticate against')
p.add_option('--insecure', action='store_true', dest='insecure', default=False, help="Don't verify SSL/TLS certificates")
p.add_option('--ssl-ca-cert-file', action='store', type='string', dest='ssl_ca_cert_file', default=None, help='Path to Certificate Authority file for SSL')
p.add_option('-f', '--ssl-cert-file', action='store', type='string', dest='cert_file', default=None, help='Path to PEM encoded key and cert for client authentication')
p.add_option('-m','--auth-mechanism', action='store', type='choice', dest='auth_mechanism', default=None, help='Auth mechanism used for auth with mongodb',
choices=['MONGODB-X509','SCRAM-SHA-256','SCRAM-SHA-1'])
p.add_option('--disable_retry_writes', dest='retry_writes_disabled', default=False, action='callback', callback=optional_arg(True), help='Disable retryWrites feature')
options, arguments = p.parse_args()
host = options.host
host_to_check = options.host_to_check if options.host_to_check else options.host
rdns_lookup = options.rdns_lookup
if (rdns_lookup):
host_to_check = socket.getnameinfo((host_to_check, 0), 0)[0]
port = options.port
port_to_check = options.port_to_check if options.port_to_check else options.port
user = options.user
passwd = options.passwd
authdb = options.authdb
query_type = options.query_type
collection = options.collection
sample_time = options.sample_time
@ -164,9 +194,15 @@ def main(argv):
action = options.action
perf_data = options.perf_data
max_lag = options.max_lag
mongo_version = options.mongo_version
database = options.database
ssl = options.ssl
replicaset = options.replicaset
insecure = options.insecure
ssl_ca_cert_file = options.ssl_ca_cert_file
cert_file = options.cert_file
auth_mechanism = options.auth_mechanism
retry_writes_disabled = options.retry_writes_disabled
if action == 'replica_primary' and replicaset is None:
return "replicaset must be passed in when using replica_primary check"
@ -177,31 +213,35 @@ def main(argv):
# moving the login up here and passing in the connection
#
start = time.time()
err, con = mongo_connect(host, port, ssl, user, passwd, replicaset)
err, con = mongo_connect(host, port, ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
if err != 0:
return err
# Autodetect mongo-version and force pymongo to let us know if it can connect or not.
err, mongo_version = check_version(con)
if err != 0:
return err
conn_time = time.time() - start
conn_time = round(conn_time, 0)
if action == "connections":
return check_connections(con, warning, critical, perf_data)
elif action == "replication_lag":
return check_rep_lag(con, host, port, warning, critical, False, perf_data, max_lag, user, passwd)
return check_rep_lag(con, host_to_check, port_to_check, rdns_lookup, warning, critical, False, perf_data, max_lag, ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
elif action == "replication_lag_percent":
return check_rep_lag(con, host, port, warning, critical, True, perf_data, max_lag, user, passwd)
return check_rep_lag(con, host_to_check, port_to_check, rdns_lookup, warning, critical, True, perf_data, max_lag, ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
elif action == "replset_state":
return check_replset_state(con, perf_data, warning, critical)
elif action == "memory":
return check_memory(con, warning, critical, perf_data, options.mapped_memory)
return check_memory(con, warning, critical, perf_data, options.mapped_memory, host)
elif action == "memory_mapped":
return check_memory_mapped(con, warning, critical, perf_data)
elif action == "queues":
return check_queues(con, warning, critical, perf_data)
elif action == "lock":
return check_lock(con, warning, critical, perf_data)
return check_lock(con, warning, critical, perf_data, mongo_version)
elif action == "current_lock":
return check_current_lock(con, host, warning, critical, perf_data)
return check_current_lock(con, host, port, warning, critical, perf_data)
elif action == "flushing":
return check_flushing(con, warning, critical, True, perf_data)
elif action == "last_flush_time":
@ -223,22 +263,26 @@ def main(argv):
return check_database_size(con, database, warning, critical, perf_data)
elif action == "database_indexes":
return check_database_indexes(con, database, warning, critical, perf_data)
elif action == "collection_documents":
return check_collection_documents(con, database, collection, warning, critical, perf_data)
elif action == "collection_indexes":
return check_collection_indexes(con, database, collection, warning, critical, perf_data)
elif action == "collection_size":
return check_collection_size(con, database, collection, warning, critical, perf_data)
elif action == "collection_storageSize":
return check_collection_storageSize(con, database, collection, warning, critical, perf_data)
elif action == "journaled":
return check_journaled(con, warning, critical, perf_data)
elif action == "write_data_files":
return check_write_to_datafiles(con, warning, critical, perf_data)
elif action == "opcounters":
return check_opcounters(con, host, warning, critical, perf_data)
return check_opcounters(con, host, port, warning, critical, perf_data)
elif action == "asserts":
return check_asserts(con, host, warning, critical, perf_data)
return check_asserts(con, host, port, warning, critical, perf_data)
elif action == "replica_primary":
return check_replica_primary(con, host, warning, critical, perf_data, replicaset)
return check_replica_primary(con, host, warning, critical, perf_data, replicaset, mongo_version)
elif action == "queries_per_second":
return check_queries_per_second(con, query_type, warning, critical, perf_data)
return check_queries_per_second(con, query_type, warning, critical, perf_data, mongo_version)
elif action == "page_faults":
check_page_faults(con, sample_time, warning, critical, perf_data)
elif action == "chunks_balance":
@ -255,30 +299,73 @@ def main(argv):
return check_connect(host, port, warning, critical, perf_data, user, passwd, conn_time)
def mongo_connect(host=None, port=None, ssl=False, user=None, passwd=None, replica=None):
def mongo_connect(host=None, port=None, ssl=False, user=None, passwd=None, replica=None, authdb="admin", insecure=False, ssl_ca_cert_file=None, ssl_cert=None, auth_mechanism=None, retry_writes_disabled=False):
from pymongo.errors import ConnectionFailure
from pymongo.errors import PyMongoError
import ssl as SSL
con_args = dict()
if ssl:
if insecure:
con_args['ssl_cert_reqs'] = SSL.CERT_NONE
else:
con_args['ssl_cert_reqs'] = SSL.CERT_REQUIRED
con_args['ssl'] = ssl
if ssl_ca_cert_file:
con_args['ssl_ca_certs'] = ssl_ca_cert_file
if ssl_cert:
con_args['ssl_certfile'] = ssl_cert
if retry_writes_disabled:
con_args['retryWrites'] = False
try:
# ssl connection for pymongo > 2.3
if pymongo.version >= "2.3":
if replica is None:
con = pymongo.MongoClient(host, port)
con = pymongo.MongoClient(host, port, **con_args)
else:
con = pymongo.Connection(host, port, read_preference=pymongo.ReadPreference.SECONDARY, ssl=ssl, replicaSet=replica, network_timeout=10)
con = pymongo.MongoClient(host, port, read_preference=pymongo.ReadPreference.SECONDARY, replicaSet=replica, **con_args)
else:
if replica is None:
con = pymongo.Connection(host, port, slave_okay=True, network_timeout=10)
else:
con = pymongo.Connection(host, port, slave_okay=True, network_timeout=10)
#con = pymongo.Connection(host, port, slave_okay=True, replicaSet=replica, network_timeout=10)
# we must authenticate the connection, otherwise we won't be able to perform certain operations
if ssl_cert and ssl_ca_cert_file and user and auth_mechanism == 'SCRAM-SHA-256':
con.the_database.authenticate(user, mechanism='SCRAM-SHA-256')
elif ssl_cert and ssl_ca_cert_file and user and auth_mechanism == 'SCRAM-SHA-1':
con.the_database.authenticate(user, mechanism='SCRAM-SHA-1')
elif ssl_cert and ssl_ca_cert_file and user and auth_mechanism == 'MONGODB-X509':
con.the_database.authenticate(user, mechanism='MONGODB-X509')
try:
result = con.admin.command("ismaster")
except ConnectionFailure:
print("CRITICAL - Connection to Mongo server on %s:%s has failed" % (host, port) )
sys.exit(2)
if 'arbiterOnly' in result and result['arbiterOnly'] == True:
print("OK - State: 7 (Arbiter on port %s)" % (port))
sys.exit(0)
if user and passwd:
db = con["admin"]
if not db.authenticate(user, passwd):
db = con[authdb]
try:
db.authenticate(user, password=passwd)
except PyMongoError:
sys.exit("Username/Password incorrect")
except Exception, e:
# Ping to check that the server is responding.
con.admin.command("ping")
except Exception as e:
if isinstance(e, pymongo.errors.AutoReconnect) and str(e).find(" is an arbiter") != -1:
# We got a pymongo AutoReconnect exception that tells us we connected to an Arbiter Server
# This means: Arbiter is reachable and can answer requests/votes - this is all we need to know from an arbiter
print "OK - State: 7 (Arbiter)"
print("OK - State: 7 (Arbiter)")
sys.exit(0)
return exit_with_general_critical(e), None
return 0, con
@ -288,7 +375,7 @@ def exit_with_general_warning(e):
if isinstance(e, SystemExit):
return e
else:
print "WARNING - General MongoDB warning:", e
print("WARNING - General MongoDB warning:", e)
return 1
@ -296,19 +383,27 @@ def exit_with_general_critical(e):
if isinstance(e, SystemExit):
return e
else:
print "CRITICAL - General MongoDB Error:", e
print("CRITICAL - General MongoDB Error:", e)
return 2
def set_read_preference(db):
if pymongo.version >= "2.1":
if pymongo.version >= "2.2":
pymongo.read_preferences.Secondary
else:
db.read_preference = pymongo.ReadPreference.SECONDARY
def check_version(con):
try:
server_info = con.server_info()
except Exception as e:
return exit_with_general_critical(e), None
return 0, int(server_info['version'].split('.')[0].strip())
def check_connect(host, port, warning, critical, perf_data, user, passwd, conn_time):
warning = warning or 3
critical = critical or 6
message = "Connection took %i seconds" % conn_time
message = "Connection took %.3f seconds" % conn_time
message += performance_data(perf_data, [(conn_time, "connection_time", warning, critical)])
return check_levels(conn_time, warning, critical, message)
@ -330,13 +425,17 @@ def check_connections(con, warning, critical, perf_data):
(available, "available_connections")])
return check_levels(used_percent, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_lag, user, passwd):
def check_rep_lag(con, host, port, rdns_lookup, warning, critical, percent, perf_data, max_lag, ssl=False, user=None, passwd=None, replicaset=None, authdb="admin", insecure=None, ssl_ca_cert_file=None, cert_file=None, auth_mechanism=None, retry_writes_disabled=False):
# Get mongo to tell us replica set member name when connecting locally
if "127.0.0.1" == host:
if not "me" in list(con.admin.command("ismaster","1").keys()):
print("UNKNOWN - This is not replicated MongoDB")
return 3
host = con.admin.command("ismaster","1")["me"].split(':')[0]
if percent:
@ -348,16 +447,15 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
rs_status = {}
slaveDelays = {}
try:
set_read_preference(con.admin)
#set_read_preference(con.admin)
# Get replica set status
try:
rs_status = con.admin.command("replSetGetStatus")
except pymongo.errors.OperationFailure, e:
if e.code == None and str(e).find('failed: not running with --replSet"'):
print "OK - Not running with replSet"
return 0
except pymongo.errors.OperationFailure as e:
if ((e.code == None and str(e).find('failed: not running with --replSet"')) or (e.code == 76 and str(e).find('not running with --replSet"'))):
print("UNKNOWN - Not running with replSet")
return 3
serverVersion = tuple(con.server_info()['version'].split('.'))
if serverVersion >= tuple("2.0.0".split(".")):
#
@ -377,24 +475,32 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
for member in rs_status["members"]:
if member["stateStr"] == "PRIMARY":
primary_node = member
if member["name"].split(':')[0] == host and int(member["name"].split(':')[1]) == port:
# if rdns_lookup is true then lookup both values back to their rdns value so we can compare hostname vs fqdn
if rdns_lookup:
member_host, member_port = split_host_port(member.get('name'))
member_host = "{0}:{1}".format(socket.getnameinfo((member_host, 0), 0)[0], member_port)
if member_host == "{0}:{1}".format(socket.getnameinfo((host, 0), 0)[0], port):
host_node = member
# Exact match
elif member.get('name') == "{0}:{1}".format(host, port):
host_node = member
# Check if we're in the middle of an election and don't have a primary
if primary_node is None:
print "WARNING - No primary defined. In an election?"
print("WARNING - No primary defined. In an election?")
return 1
# Check if we failed to find the current host
# below should never happen
if host_node is None:
print "CRITICAL - Unable to find host '" + host + "' in replica set."
print("CRITICAL - Unable to find host '" + host + "' in replica set.")
return 2
# Is the specified host the primary?
if host_node["stateStr"] == "PRIMARY":
if max_lag == False:
print "OK - This is the primary."
print("OK - This is the primary.")
return 0
else:
#get the maximal replication lag
@ -407,7 +513,7 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
data = data + member['name'] + " lag=%d;" % replicationLag
maximal_lag = max(maximal_lag, replicationLag)
if percent:
err, con = mongo_connect(primary_node['name'].split(':')[0], int(primary_node['name'].split(':')[1]), False, user, passwd)
err, con = mongo_connect(split_host_port(primary_node['name'])[0], int(split_host_port(primary_node['name'])[1]), ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
if err != 0:
return err
primary_timediff = replication_get_time_diff(con)
@ -419,8 +525,8 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
message += performance_data(perf_data, [(maximal_lag, "replication_lag", warning, critical)])
return check_levels(maximal_lag, warning, critical, message)
elif host_node["stateStr"] == "ARBITER":
print "OK - This is an arbiter"
return 0
print("UNKNOWN - This is an arbiter")
return 3
# Find the difference in optime between current node and PRIMARY
@ -439,7 +545,7 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
lag = float(optime_lag.seconds + optime_lag.days * 24 * 3600)
if percent:
err, con = mongo_connect(primary_node['name'].split(':')[0], int(primary_node['name'].split(':')[1]), False, user, passwd)
err, con = mongo_connect(split_host_port(primary_node['name'])[0], int(split_host_port(primary_node['name'])[1]), ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
if err != 0:
return err
primary_timediff = replication_get_time_diff(con)
@ -471,19 +577,19 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
# Check if we're in the middle of an election and don't have a primary
if primary_node is None:
print "WARNING - No primary defined. In an election?"
print("WARNING - No primary defined. In an election?")
sys.exit(1)
# Is the specified host the primary?
if host_node["stateStr"] == "PRIMARY":
print "OK - This is the primary."
print("OK - This is the primary.")
sys.exit(0)
# Find the difference in optime between current node and PRIMARY
optime_lag = abs(primary_node[1] - host_node["optimeDate"])
lag = optime_lag.seconds
if percent:
err, con = mongo_connect(primary_node['name'].split(':')[0], int(primary_node['name'].split(':')[1]))
err, con = mongo_connect(split_host_port(primary_node['name'])[0], int(split_host_port(primary_node['name'])[1]), ssl, user, passwd, replicaset, authdb, insecure, ssl_ca_cert_file, cert_file, auth_mechanism, retry_writes_disabled=retry_writes_disabled)
if err != 0:
return err
primary_timediff = replication_get_time_diff(con)
@ -495,25 +601,33 @@ def check_rep_lag(con, host, port, warning, critical, percent, perf_data, max_la
message += performance_data(perf_data, [(lag, "replication_lag", warning, critical)])
return check_levels(lag, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_memory(con, warning, critical, perf_data, mapped_memory):
#
# These thresholds are basically meaningless, and must be customized to your system's ram
#
# Get the total system merory and calculate based on that how much memory used by Mongodb is ok or not.
#
# Check the memory usage of mongo. Alerting on this may be hard to get right
# because it'll try to get as much memory as it can. And that's probably
# a good thing.
#
def check_memory(con, warning, critical, perf_data, mapped_memory, host):
# Get the total system memory of this system (This is totally bogus if you
# are running this command remotely) and calculate based on that how much
# memory used by Mongodb is ok or not.
meminfo = open('/proc/meminfo').read()
matched = re.search(r'^MemTotal:\s+(\d+)', meminfo)
if matched:
mem_total_kB = int(matched.groups()[0])
# Old way
#critical = critical or 16
# The new way. if using >80% then warn, if >90% then critical level
if host != "127.0.0.1" and not warning:
# Running remotely and value was not set by user, use hardcoded value
warning = 12
else:
# running locally or user provided value
warning = warning or (mem_total_kB * 0.8) / 1024.0 / 1024.0
if host != "127.0.0.1" and not critical:
critical = 16
else:
critical = critical or (mem_total_kB * 0.9) / 1024.0 / 1024.0
# debugging
@ -522,7 +636,7 @@ def check_memory(con, warning, critical, perf_data, mapped_memory):
try:
data = get_server_status(con)
if not data['mem']['supported'] and not mapped_memory:
print "OK - Platform not supported for memory info"
print("OK - Platform not supported for memory info")
return 0
#
# convert to gigs
@ -559,7 +673,7 @@ def check_memory(con, warning, critical, perf_data, mapped_memory):
else:
return check_levels(mem_resident, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -572,7 +686,7 @@ def check_memory_mapped(con, warning, critical, perf_data):
try:
data = get_server_status(con)
if not data['mem']['supported']:
print "OK - Platform not supported for memory info"
print("OK - Platform not supported for memory info")
return 0
#
# convert to gigs
@ -589,28 +703,32 @@ def check_memory_mapped(con, warning, critical, perf_data):
message += " %.2fGB mappedWithJournal" % mem_mapped_journal
except:
mem_mapped_journal = 0
message += performance_data(perf_data, [("%.2f" % mem_mapped, "memory_mapped"), ("%.2f" % mem_mapped_journal, "mappedWithJournal")])
message += performance_data(perf_data, [("%.2f" % mem_mapped, "memory_mapped", warning, critical), ("%.2f" % mem_mapped_journal, "mappedWithJournal")])
if not mem_mapped == -1:
return check_levels(mem_mapped, warning, critical, message)
else:
print "OK - Server does not provide mem.mapped info"
print("OK - Server does not provide mem.mapped info")
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_lock(con, warning, critical, perf_data):
#
# Return the percentage of the time there was a global Lock
#
def check_lock(con, warning, critical, perf_data, mongo_version):
warning = warning or 10
critical = critical or 30
if mongo_version == 2:
try:
data = get_server_status(con)
lockTime = data['globalLock']['lockTime']
totalTime = data['globalLock']['totalTime']
#
# calculate percentage
#
lockTime = data['globalLock']['lockTime']
totalTime = data['globalLock']['totalTime']
if lockTime > totalTime:
lock_percentage = 0.00
else:
@ -618,9 +736,12 @@ def check_lock(con, warning, critical, perf_data):
message = "Lock Percentage: %.2f%%" % lock_percentage
message += performance_data(perf_data, [("%.2f" % lock_percentage, "lock_percentage", warning, critical)])
return check_levels(lock_percentage, warning, critical, message)
except Exception, e:
except Exception as e:
print("Couldn't get globalLock lockTime info from mongo, are you sure you're not using version 3? See the -M option.")
return exit_with_general_critical(e)
else:
print("OK - MongoDB version 3 doesn't report on global locks")
return 0
def check_flushing(con, warning, critical, avg, perf_data):
@ -632,6 +753,8 @@ def check_flushing(con, warning, critical, avg, perf_data):
critical = critical or 15000
try:
data = get_server_status(con)
try:
data['backgroundFlushing']
if avg:
flush_time = float(data['backgroundFlushing']['average_ms'])
stat_type = "Average"
@ -643,8 +766,11 @@ def check_flushing(con, warning, critical, avg, perf_data):
message += performance_data(perf_data, [("%.2fms" % flush_time, "%s_flush_time" % stat_type.lower(), warning, critical)])
return check_levels(flush_time, warning, critical, message)
except Exception:
print("OK - flushing stats not available for this storage engine")
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -655,6 +781,7 @@ def index_miss_ratio(con, warning, critical, perf_data):
data = get_server_status(con)
try:
data['indexCounters']
serverVersion = tuple(con.server_info()['version'].split('.'))
if serverVersion >= tuple("2.4.0".split(".")):
miss_ratio = float(data['indexCounters']['missRatio'])
@ -662,19 +789,24 @@ def index_miss_ratio(con, warning, critical, perf_data):
miss_ratio = float(data['indexCounters']['btree']['missRatio'])
except KeyError:
not_supported_msg = "not supported on this platform"
if data['indexCounters'].has_key('note'):
print "OK - MongoDB says: " + not_supported_msg
try:
data['indexCounters']
if 'note' in data['indexCounters']:
print("OK - MongoDB says: " + not_supported_msg)
return 0
else:
print "WARNING - Can't get counter from MongoDB"
print("WARNING - Can't get counter from MongoDB")
return 1
except Exception:
print("OK - MongoDB says: " + not_supported_msg)
return 0
message = "Miss Ratio: %.2f" % miss_ratio
message += performance_data(perf_data, [("%.2f" % miss_ratio, "index_miss_ratio", warning, critical)])
return check_levels(miss_ratio, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_replset_quorum(con, perf_data):
@ -698,7 +830,7 @@ def check_replset_quorum(con, perf_data):
message = "Cluster is not quorate and cannot operate"
return check_levels(state, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -713,44 +845,63 @@ def check_replset_state(con, perf_data, warning="", critical=""):
except:
critical = [8, 4, -1]
ok = range(-1, 8) # should include the range of all posiible values
ok = list(range(-1, 8)) # should include the range of all posiible values
try:
worst_state = -2
message = ""
try:
try:
set_read_preference(con.admin)
data = con.admin.command(pymongo.son_manipulator.SON([('replSetGetStatus', 1)]))
except:
data = con.admin.command(son.SON([('replSetGetStatus', 1)]))
state = int(data['myState'])
except pymongo.errors.OperationFailure, e:
if e.code == None and str(e).find('failed: not running with --replSet"'):
state = -1
members = data['members']
my_state = int(data['myState'])
worst_state = my_state
for member in members:
their_state = int(member['state'])
message += " %s: %i (%s)" % (member['name'], their_state, state_text(their_state))
if state_is_worse(their_state, worst_state, warning, critical):
worst_state = their_state
message += performance_data(perf_data, [(my_state, "state")])
if state == 8:
message = "State: %i (Down)" % state
elif state == 4:
message = "State: %i (Fatal error)" % state
elif state == 0:
message = "State: %i (Starting up, phase1)" % state
elif state == 3:
message = "State: %i (Recovering)" % state
elif state == 5:
message = "State: %i (Starting up, phase2)" % state
elif state == 1:
message = "State: %i (Primary)" % state
elif state == 2:
message = "State: %i (Secondary)" % state
elif state == 7:
message = "State: %i (Arbiter)" % state
elif state == -1:
message = "Not running with replSet"
else:
message = "State: %i (Unknown state)" % state
message += performance_data(perf_data, [(state, "state")])
return check_levels(state, warning, critical, message, ok)
except Exception, e:
except pymongo.errors.OperationFailure as e:
if ((e.code == None and str(e).find('failed: not running with --replSet"')) or (e.code == 76 and str(e).find('not running with --replSet"'))):
worst_state = -1
return check_levels(worst_state, warning, critical, message, ok)
except Exception as e:
return exit_with_general_critical(e)
def state_is_worse(state, worst_state, warning, critical):
if worst_state in critical:
return False
if worst_state in warning:
return state in critical
return (state in warning) or (state in critical)
def state_text(state):
if state == 8:
return "Down"
elif state == 4:
return "Fatal error"
elif state == 0:
return "Starting up, phase1"
elif state == 3:
return "Recovering"
elif state == 5:
return "Starting up, phase2"
elif state == 1:
return "Primary"
elif state == 2:
return "Secondary"
elif state == 7:
return "Arbiter"
elif state == -1:
return "Not running with replSet"
else:
return "Unknown state"
def check_databases(con, warning, critical, perf_data=None):
try:
@ -764,7 +915,7 @@ def check_databases(con, warning, critical, perf_data=None):
message = "Number of DBs: %.0f" % count
message += performance_data(perf_data, [(count, "databases", warning, critical, message)])
return check_levels(count, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -786,7 +937,7 @@ def check_collections(con, warning, critical, perf_data=None):
message += performance_data(perf_data, [(count, "collections", warning, critical, message)])
return check_levels(count, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -823,21 +974,21 @@ def check_database_size(con, database, warning, critical, perf_data):
try:
set_read_preference(con.admin)
data = con[database].command('dbstats')
storage_size = data['storageSize'] / 1024 / 1024
storage_size = data['storageSize'] // 1024 // 1024
if perf_data:
perfdata += " | database_size=%i;%i;%i" % (storage_size, warning, critical)
#perfdata += " database=%s" %(database)
if storage_size >= critical:
print "CRITICAL - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata)
print("CRITICAL - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata))
return 2
elif storage_size >= warning:
print "WARNING - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata)
print("WARNING - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata))
return 1
else:
print "OK - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata)
print("OK - Database size: %.0f MB, Database: %s%s" % (storage_size, database, perfdata))
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -851,20 +1002,42 @@ def check_database_indexes(con, database, warning, critical, perf_data):
try:
set_read_preference(con.admin)
data = con[database].command('dbstats')
index_size = data['indexSize'] / 1024 / 1024
index_size = data['indexSize'] / 1024 // 1024
if perf_data:
perfdata += " | database_indexes=%i;%i;%i" % (index_size, warning, critical)
if index_size >= critical:
print "CRITICAL - %s indexSize: %.0f MB %s" % (database, index_size, perfdata)
print("CRITICAL - %s indexSize: %.0f MB %s" % (database, index_size, perfdata))
return 2
elif index_size >= warning:
print "WARNING - %s indexSize: %.0f MB %s" % (database, index_size, perfdata)
print("WARNING - %s indexSize: %.0f MB %s" % (database, index_size, perfdata))
return 1
else:
print "OK - %s indexSize: %.0f MB %s" % (database, index_size, perfdata)
print("OK - %s indexSize: %.0f MB %s" % (database, index_size, perfdata))
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_collection_documents(con, database, collection, warning, critical, perf_data):
perfdata = ""
try:
set_read_preference(con.admin)
data = con[database].command('collstats', collection)
documents = data['count']
if perf_data:
perfdata += " | collection_documents=%i;%i;%i" % (documents, warning, critical)
if documents >= critical:
print("CRITICAL - %s.%s documents: %s %s" % (database, collection, documents, perfdata))
return 2
elif documents >= warning:
print("WARNING - %s.%s documents: %s %s" % (database, collection, documents, perfdata))
return 1
else:
print("OK - %s.%s documents: %s %s" % (database, collection, documents, perfdata))
return 0
except Exception as e:
return exit_with_general_critical(e)
@ -883,15 +1056,15 @@ def check_collection_indexes(con, database, collection, warning, critical, perf_
perfdata += " | collection_indexes=%i;%i;%i" % (total_index_size, warning, critical)
if total_index_size >= critical:
print "CRITICAL - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata)
print("CRITICAL - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata))
return 2
elif total_index_size >= warning:
print "WARNING - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata)
print("WARNING - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata))
return 1
else:
print "OK - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata)
print("OK - %s.%s totalIndexSize: %.0f MB %s" % (database, collection, total_index_size, perfdata))
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -908,7 +1081,7 @@ def check_queues(con, warning, critical, perf_data):
message += performance_data(perf_data, [(total_queues, "total_queues", warning, critical), (readers_queues, "readers_queues"), (writers_queues, "writers_queues")])
return check_levels(total_queues, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_collection_size(con, database, collection, warning, critical, perf_data):
@ -923,18 +1096,43 @@ def check_collection_size(con, database, collection, warning, critical, perf_dat
perfdata += " | collection_size=%i;%i;%i" % (size, warning, critical)
if size >= critical:
print "CRITICAL - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata)
print("CRITICAL - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata))
return 2
elif size >= warning:
print "WARNING - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata)
print("WARNING - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata))
return 1
else:
print "OK - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata)
print("OK - %s.%s size: %.0f MB %s" % (database, collection, size, perfdata))
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_queries_per_second(con, query_type, warning, critical, perf_data):
def check_collection_storageSize(con, database, collection, warning, critical, perf_data):
warning = warning or 100
critical = critical or 1000
perfdata = ""
try:
set_read_preference(con.admin)
data = con[database].command('collstats', collection)
storageSize = data['storageSize'] / 1024 / 1024
if perf_data:
perfdata += " | collection_storageSize=%i;%i;%i" % (storageSize, warning, critical)
if storageSize >= critical:
print("CRITICAL - %s.%s storageSize: %.0f MB %s" % (database, collection, storageSize, perfdata))
return 2
elif storageSize >= warning:
print("WARNING - %s.%s storageSize: %.0f MB %s" % (database, collection, storageSize, perfdata))
return 1
else:
print("OK - %s.%s storageSize: %.0f MB %s" % (database, collection, storageSize, perfdata))
return 0
except Exception as e:
return exit_with_general_critical(e)
def check_queries_per_second(con, query_type, warning, critical, perf_data, mongo_version):
warning = warning or 250
critical = critical or 500
@ -955,10 +1153,17 @@ def check_queries_per_second(con, query_type, warning, critical, perf_data):
diff_query = num - last_count['data'][query_type]['count']
diff_ts = ts - last_count['data'][query_type]['ts']
if diff_ts == 0:
message = "diff_query = " + str(diff_query) + " diff_ts = " + str(diff_ts)
return check_levels(0, warning, critical, message)
query_per_sec = float(diff_query) / float(diff_ts)
# update the count now
if mongo_version == 2:
db.nagios_check.update({u'_id': last_count['_id']}, {'$set': {"data.%s" % query_type: {'count': num, 'ts': int(time.time())}}})
else:
db.nagios_check.update_one({u'_id': last_count['_id']}, {'$set': {"data.%s" % query_type: {'count': num, 'ts': int(time.time())}}})
message = "Queries / Sec: %f" % query_per_sec
message += performance_data(perf_data, [(query_per_sec, "%s_per_sec" % query_type, warning, critical, message)])
@ -967,17 +1172,24 @@ def check_queries_per_second(con, query_type, warning, critical, perf_data):
# since it is the first run insert it
query_per_sec = 0
message = "First run of check.. no data"
if mongo_version == 2:
db.nagios_check.update({u'_id': last_count['_id']}, {'$set': {"data.%s" % query_type: {'count': num, 'ts': int(time.time())}}})
else:
db.nagios_check.update_one({u'_id': last_count['_id']}, {'$set': {"data.%s" % query_type: {'count': num, 'ts': int(time.time())}}})
except TypeError:
#
# since it is the first run insert it
query_per_sec = 0
message = "First run of check.. no data"
if mongo_version == 2:
db.nagios_check.insert({'check': 'query_counts', 'data': {query_type: {'count': num, 'ts': int(time.time())}}})
else:
db.nagios_check.insert_one({'check': 'query_counts', 'data': {query_type: {'count': num, 'ts': int(time.time())}}})
return check_levels(query_per_sec, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -1024,7 +1236,7 @@ def check_oplog(con, warning, critical, perf_data):
message += performance_data(perf_data, [("%.2f" % hours_in_oplog, 'oplog_time', warning, critical), ("%.2f " % approx_level, 'oplog_time_100_percent_used')])
return check_levels(-approx_level, -warning, -critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -1042,7 +1254,7 @@ Under very high write situations it is normal for this value to be nonzero. """
message += performance_data(perf_data, [(j_commits_in_wl, "j_commits_in_wl", warning, critical)])
return check_levels(j_commits_in_wl, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -1058,7 +1270,7 @@ def check_journaled(con, warning, critical, perf_data):
message += performance_data(perf_data, [("%.2f" % journaled, "journaled", warning, critical)])
return check_levels(journaled, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -1075,11 +1287,11 @@ than the amount physically written to disk."""
message += performance_data(perf_data, [("%.2f" % writes, "write_to_data_files", warning, critical)])
return check_levels(writes, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def get_opcounters(data, opcounters_name, host):
def get_opcounters(data, opcounters_name, host, port):
try:
insert = data[opcounters_name]['insert']
query = data[opcounters_name]['query']
@ -1087,21 +1299,21 @@ def get_opcounters(data, opcounters_name, host):
delete = data[opcounters_name]['delete']
getmore = data[opcounters_name]['getmore']
command = data[opcounters_name]['command']
except KeyError, e:
except KeyError as e:
return 0, [0] * 100
total_commands = insert + query + update + delete + getmore + command
new_vals = [total_commands, insert, query, update, delete, getmore, command]
return maintain_delta(new_vals, host, opcounters_name)
return maintain_delta(new_vals, host, port, opcounters_name)
def check_opcounters(con, host, warning, critical, perf_data):
def check_opcounters(con, host, port, warning, critical, perf_data):
""" A function to get all opcounters delta per minute. In case of a replication - gets the opcounters+opcountersRepl"""
warning = warning or 10000
critical = critical or 15000
data = get_server_status(con)
err1, delta_opcounters = get_opcounters(data, 'opcounters', host)
err2, delta_opcounters_repl = get_opcounters(data, 'opcountersRepl', host)
err1, delta_opcounters = get_opcounters(data, 'opcounters', host, port)
err2, delta_opcounters_repl = get_opcounters(data, 'opcountersRepl', host, port)
if err1 == 0 and err2 == 0:
delta = [(x + y) for x, y in zip(delta_opcounters, delta_opcounters_repl)]
delta[0] = delta_opcounters[0] # only the time delta shouldn't be summarized
@ -1109,14 +1321,14 @@ def check_opcounters(con, host, warning, critical, perf_data):
message = "Test succeeded , old values missing"
message = "Opcounters: total=%d,insert=%d,query=%d,update=%d,delete=%d,getmore=%d,command=%d" % tuple(per_minute_delta)
message += performance_data(perf_data, ([(per_minute_delta[0], "total", warning, critical), (per_minute_delta[1], "insert"),
(per_minute_delta[2], "query"), (per_minute_delta[3], "update"), (per_minute_delta[5], "delete"),
(per_minute_delta[2], "query"), (per_minute_delta[3], "update"), (per_minute_delta[4], "delete"),
(per_minute_delta[5], "getmore"), (per_minute_delta[6], "command")]))
return check_levels(per_minute_delta[0], warning, critical, message)
else:
return exit_with_general_critical("problem reading data from temp file")
def check_current_lock(con, host, warning, critical, perf_data):
def check_current_lock(con, host, port, warning, critical, perf_data):
""" A function to get current lock percentage and not a global one, as check_lock function does"""
warning = warning or 10
critical = critical or 30
@ -1125,7 +1337,7 @@ def check_current_lock(con, host, warning, critical, perf_data):
lockTime = float(data['globalLock']['lockTime'])
totalTime = float(data['globalLock']['totalTime'])
err, delta = maintain_delta([totalTime, lockTime], host, "locktime")
err, delta = maintain_delta([totalTime, lockTime], host, port, "locktime")
if err == 0:
lock_percentage = delta[2] / delta[1] * 100 # lockTime/totalTime*100
message = "Current Lock Percentage: %.2f%%" % lock_percentage
@ -1135,7 +1347,7 @@ def check_current_lock(con, host, warning, critical, perf_data):
return exit_with_general_warning("problem reading data from temp file")
def check_page_faults(con, host, warning, critical, perf_data):
def check_page_faults(con, host, port, warning, critical, perf_data):
""" A function to get page_faults per second from the system"""
warning = warning or 10
critical = critical or 30
@ -1147,7 +1359,7 @@ def check_page_faults(con, host, warning, critical, perf_data):
# page_faults unsupported on the underlaying system
return exit_with_general_critical("page_faults unsupported on the underlaying system")
err, delta = maintain_delta([page_faults], host, "page_faults")
err, delta = maintain_delta([page_faults], host, port, "page_faults")
if err == 0:
page_faults_ps = delta[1] / delta[0]
message = "Page faults : %.2f ps" % page_faults_ps
@ -1157,7 +1369,7 @@ def check_page_faults(con, host, warning, critical, perf_data):
return exit_with_general_warning("problem reading data from temp file")
def check_asserts(con, host, warning, critical, perf_data):
def check_asserts(con, host, port, warning, critical, perf_data):
""" A function to get asserts from the system"""
warning = warning or 1
critical = critical or 10
@ -1172,7 +1384,7 @@ def check_asserts(con, host, warning, critical, perf_data):
user = asserts['user']
rollovers = asserts['rollovers']
err, delta = maintain_delta([regular, warning_asserts, msg, user, rollovers], host, "asserts")
err, delta = maintain_delta([regular, warning_asserts, msg, user, rollovers], host, port, "asserts")
if err == 0:
if delta[5] != 0:
@ -1206,7 +1418,7 @@ def get_stored_primary_server_name(db):
return stored_primary_server
def check_replica_primary(con, host, warning, critical, perf_data, replicaset):
def check_replica_primary(con, host, warning, critical, perf_data, replicaset, mongo_version):
""" A function to check if the primary server of a replica set has changed """
if warning is None and critical is None:
warning = 1
@ -1229,7 +1441,10 @@ def check_replica_primary(con, host, warning, critical, perf_data, replicaset):
saved_primary = "None"
if current_primary != saved_primary:
last_primary_server_record = {"server": current_primary}
db.last_primary_server.update({"_id": "last_primary"}, {"$set": last_primary_server_record}, upsert=True, safe=True)
if mongo_version == 2:
db.last_primary_server.update({"_id": "last_primary"}, {"$set": last_primary_server_record}, upsert=True)
else:
db.last_primary_server.update_one({"_id": "last_primary"}, {"$set": last_primary_server_record}, upsert=True)
message = "Primary server has changed from %s to %s" % (saved_primary, current_primary)
primary_status = 1
return check_levels(primary_status, warning, critical, message)
@ -1251,9 +1466,9 @@ def check_page_faults(con, sample_time, warning, critical, perf_data):
try:
#on linux servers only
page_faults = (int(data2['extra_info']['page_faults']) - int(data1['extra_info']['page_faults'])) / sample_time
page_faults = (int(data2['extra_info']['page_faults']) - int(data1['extra_info']['page_faults'])) // sample_time
except KeyError:
print "WARNING - Can't get extra_info.page_faults counter from MongoDB"
print("WARNING - Can't get extra_info.page_faults counter from MongoDB")
sys.exit(1)
message = "Page Faults: %i" % (page_faults)
@ -1261,7 +1476,7 @@ def check_page_faults(con, sample_time, warning, critical, perf_data):
message += performance_data(perf_data, [(page_faults, "page_faults", warning, critical)])
check_levels(page_faults, warning, critical, message)
except Exception, e:
except Exception as e:
exit_with_general_critical(e)
@ -1277,35 +1492,35 @@ def chunks_balance(con, database, collection, warning, critical):
shards = col.distinct("shard")
except:
print "WARNING - Can't get chunks infos from MongoDB"
print("WARNING - Can't get chunks infos from MongoDB")
sys.exit(1)
if nscount == 0:
print "WARNING - Namespace %s is not sharded" % (nsfilter)
print("WARNING - Namespace %s is not sharded" % (nsfilter))
sys.exit(1)
avgchunksnb = nscount / len(shards)
warningnb = avgchunksnb * warning / 100
criticalnb = avgchunksnb * critical / 100
avgchunksnb = nscount // len(shards)
warningnb = avgchunksnb * warning // 100
criticalnb = avgchunksnb * critical // 100
for shard in shards:
delta = abs(avgchunksnb - col.find({"ns": nsfilter, "shard": shard}).count())
message = "Namespace: %s, Shard name: %s, Chunk delta: %i" % (nsfilter, shard, delta)
if delta >= criticalnb and delta > 0:
print "CRITICAL - Chunks not well balanced " + message
print("CRITICAL - Chunks not well balanced " + message)
sys.exit(2)
elif delta >= warningnb and delta > 0:
print "WARNING - Chunks not well balanced " + message
print("WARNING - Chunks not well balanced " + message)
sys.exit(1)
print "OK - Chunks well balanced across shards"
print("OK - Chunks well balanced across shards")
sys.exit(0)
except Exception, e:
except Exception as e:
exit_with_general_critical(e)
print "OK - Chunks well balanced across shards"
print("OK - Chunks well balanced across shards")
sys.exit(0)
@ -1321,7 +1536,7 @@ def check_connect_primary(con, warning, critical, perf_data):
data = con.admin.command(son.SON([('isMaster', 1)]))
if data['ismaster'] == True:
print "OK - This server is primary"
print("OK - This server is primary")
return 0
phost = data['primary'].split(':')[0]
@ -1339,17 +1554,17 @@ def check_connect_primary(con, warning, critical, perf_data):
return check_levels(pconn_time, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def check_collection_state(con, database, collection):
try:
con[database][collection].find_one()
print "OK - Collection %s.%s is reachable " % (database, collection)
print("OK - Collection %s.%s is reachable " % (database, collection))
return 0
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
@ -1361,14 +1576,18 @@ def check_row_count(con, database, collection, warning, critical, perf_data):
return check_levels(count, warning, critical, message)
except Exception, e:
except Exception as e:
return exit_with_general_critical(e)
def build_file_name(host, action):
def build_file_name(host, port, action):
#done this way so it will work when run independently and from shell
module_name = re.match('(.*//*)*(.*)\..*', __file__).group(2)
if (port == 27017):
return "/tmp/" + module_name + "_data/" + host + "-" + action + ".data"
else:
return "/tmp/" + module_name + "_data/" + host + "-" + str(port) + "-" + action + ".data"
def ensure_dir(f):
@ -1381,7 +1600,7 @@ def write_values(file_name, string):
f = None
try:
f = open(file_name, 'w')
except IOError, e:
except IOError as e:
#try creating
if (e.errno == 2):
ensure_dir(file_name)
@ -1400,11 +1619,11 @@ def read_values(file_name):
data = f.read()
f.close()
return 0, data
except IOError, e:
except IOError as e:
if (e.errno == 2):
#no previous data
return 1, ''
except Exception, e:
except Exception as e:
return 2, None
@ -1420,8 +1639,8 @@ def calc_delta(old, new):
return 0, delta
def maintain_delta(new_vals, host, action):
file_name = build_file_name(host, action)
def maintain_delta(new_vals, host, port, action):
file_name = build_file_name(host, port, action)
err, data = read_values(file_name)
old_vals = data.split(';')
new_vals = [str(int(time.time()))] + new_vals
@ -1442,8 +1661,8 @@ def replication_get_time_diff(con):
col = 'oplog.$main'
firstc = local[col].find().sort("$natural", 1).limit(1)
lastc = local[col].find().sort("$natural", -1).limit(1)
first = firstc.next()
last = lastc.next()
first = next(firstc)
last = next(lastc)
tfirst = first["ts"]
tlast = last["ts"]
delta = tlast.time - tfirst.time

View File

@ -0,0 +1,69 @@
#!/bin/bash
# Check permettant de monitorer une liste de certificats se trouvant dans
# /etc/nagios/ssl_local.cfg
#
# Développé par Will (2022)
#
certs_list_path="/etc/nagios/check_ssl_local_list.cfg"
# Dates in seconds
_10_days="864000"
_15_days="1296000"
critical=0
warning=0
if [[ ! -f "$certs_list_path" ]]; then
touch "$certs_list_path"
fi
certs_list=$(cat "$certs_list_path" | sed -E 's/(.*)#.*/\1/g' | grep -v -E '^$')
for cert_path in $certs_list; do
if [ ! -f "$cert_path" ] && [ ! -d "$cert_path" ]; then
>&2 echo "Warning: path '$cert_path' is not a file or a directory."
warning=1
continue
fi
enddate=$(openssl x509 -noout -enddate -in "$cert_path" | cut -d'=' -f2)
# Check cert expiré (critique)
if ! openssl x509 -checkend 0 -in "$cert_path" &> /dev/null; then
critical=1
>&2 echo "Critical: Cert '$cert_path' has expired on $enddate."
continue
fi
# Check cert expire < 10 jours (critique)
if ! openssl x509 -checkend "$_10_days" -in "$cert_path" &> /dev/null; then
critical=1
>&2 echo "Critical: Cert '$cert_path' will expire on $enddate."
continue
fi
# Check cert expire < 15 jours (warning)
if ! openssl x509 -checkend "$_15_days" -in "$cert_path" &> /dev/null; then
warning=1
>&2 echo "Warning: Cert '$cert_path' will expire on $enddate."
continue
fi
# Cert expire > 15 jours (OK)
echo "Cert '$cert_path' OK."
done
if [ $critical -eq 1 ]; then
exit 2
elif [ $warning -eq 1 ]; then
exit 1
else
exit 0
fi

View File

@ -34,7 +34,7 @@ command[check_pop]=/usr/lib/nagios/plugins/check_pop -H localhost
command[check_pops]=/usr/lib/nagios/plugins/check_pop -S -H localhost -p 995
command[check_ftp]=/usr/lib/nagios/plugins/check_ftp -H localhost
command[check_http]=/usr/lib/nagios/plugins/check_http -e 301 -I 127.0.0.1 -H localhost
command[check_https]=/usr/lib/nagios/plugins/check_http -e 403 -I 127.0.0.1 -S -p 443 --sni -H ssl.evolix.net
command[check_https]=/usr/lib/nagios/plugins/check_http -e 401,403 -I 127.0.0.1 -S -p 443 --sni -H ssl.evolix.net
command[check_bind]=/usr/lib/nagios/plugins/check_dig -l evolix.net -H localhost
command[check_unbound]=/usr/lib/nagios/plugins/check_dig -l evolix.net -H localhost
command[check_smb]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 445
@ -48,6 +48,7 @@ command[check_redis]=/usr/lib/nagios/plugins/check_tcp -p 6379
command[check_clamd]=/usr/lib/nagios/plugins/check_clamd -H /var/run/clamav/clamd.ctl -v
command[check_clamav_db]=/usr/lib/nagios/plugins/check_file_age -w 86400 -c 172800 -f /var/lib/clamav/evolix.ndb
command[check_ssl]=/usr/lib/nagios/plugins/check_http -f follow -I 127.0.0.1 -S -p 443 -H ssl.evolix.net -C 15,5
command[check_ssl_local]={{ nagios_plugins_directory }}/check_ssl_local
command[check_elasticsearch]=/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -u /_cat/health?h=st -p 9200 -r 'red' --invert-regex
command[check_memcached]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 11211
command[check_opendkim]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 54321

View File

@ -12,6 +12,14 @@
- client
- server
- name: Create the _openvpn user
user:
name: _openvpn
system: yes
create_home: no
home: "/nonexistent"
shell: "/usr/sbin/nologin"
- name: Create the shellpki user
user:
name: shellpki
@ -62,7 +70,9 @@
group: shellpki
- name: Generate dhparam
command: "openssl dhparam -out /etc/shellpki/dh2048.pem 2048"
openssl_dhparam:
path: /etc/shellpki/dh2048.pem
size: 2048
- include_role:
name: evolix/remount-usr
@ -239,7 +249,7 @@
- include_role:
name: evolix/remount-usr
- name: Copy shellpki script
- name: Copy script to check expirations
copy:
src: "shellpki/cert-expirations.sh"
dest: "/usr/share/scripts/cert-expirations.sh"
@ -253,15 +263,43 @@
special_time: monthly
job: '/usr/share/scripts/cert-expirations.sh | mail -E -s "PKI VPN {{ ansible_hostname }} : recapitulatif expirations" {{ client_email }}'
- name: Warn the user about command to execute manually
- name: Generate the CA password
set_fact:
ca_pwd: "{{ lookup('password', '/dev/null length=25 chars=ascii_letters,digits') }}"
check_mode: no
changed_when: no
- name: Initialization of the CA
shell: 'CA_PASSWORD="{{ ca_pwd }}" shellpki init --non-interactive {{ ansible_fqdn }}'
- name: Creation of the server's certificate
shell: 'CA_PASSWORD="{{ ca_pwd }}" shellpki create --days 3650 --non-interactive {{ ansible_fqdn }}'
- name: Get the server key
shell: 'ls -tr /etc/shellpki/private/ | tail -1'
register: ca_key
check_mode: no
changed_when: no
- name: Configure the server key
replace:
path: /etc/openvpn/server.conf
regexp: 'key /etc/shellpki/private/TO_COMPLETE'
replace: 'key /etc/shellpki/private/{{ ca_key.stdout }}'
- name: Restart OpenVPN
systemd:
name: "openvpn@server.service"
state: restarted
- name: Warn the user about manual checks
pause:
prompt: |
/!\ WARNING /!\
You have to manually create the CA on the server with "shellpki init {{ ansible_fqdn }}". The command will ask you to create a password, and will ask you again to give the same one several times.
You have to manually generate the CRL on the server with "openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf". The previously created password will be asked.
You have to manually create the server's certificate with "shellpki create {{ ansible_fqdn }}".
You have to adjust the config file "/etc/openvpn/server.conf" for the following parameters : local (to check), cert (to check), key (to add), server (to check), push (to complete if needed).
Finally, you can (re)start the OpenVPN service with "systemctl restart openvpn@server.service".
You must check and adjust if necessary the configuration file "/etc/openvpn/server.conf", and then restart the OpenVPN service with "systemctl restart openvpn@server.service".
The "push" parameter may be needed to push a route to the client, so that the client can access that route through OpenVPN.
Take note of the generated CA password and store it in your password manager : {{ ca_pwd }}
Press enter to exit when it's done.

View File

@ -56,7 +56,9 @@
group: _shellpki
- name: Generate dhparam
command: "openssl dhparam -out /etc/shellpki/dh2048.pem 2048"
openssl_dhparam:
path: /etc/shellpki/dh2048.pem
size: 2048
- name: Fix CRL rights in shellpki command
lineinfile:
@ -175,7 +177,7 @@
notify: restart nrpe
when: nrpe_evolix_config.stat.exists
- name: Copy shellpki script
- name: Copy script to check expirations
copy:
src: "shellpki/cert-expirations.sh"
dest: "/usr/share/scripts/cert-expirations.sh"
@ -189,15 +191,43 @@
special_time: monthly
job: '/usr/share/scripts/cert-expirations.sh | mail -E -s "PKI VPN {{ ansible_hostname }} : recapitulatif expirations" {{ client_email }}'
- name: Warn the user about command to execute manually
- name: Generate the CA password
set_fact:
ca_pwd: "{{ lookup('password', '/dev/null length=25 chars=ascii_letters,digits') }}"
check_mode: no
changed_when: no
- name: Initialization of the CA
shell: 'CA_PASSWORD="{{ ca_pwd }}" shellpki init --non-interactive {{ ansible_fqdn }}'
- name: Creation of the server's certificate
shell: 'CA_PASSWORD="{{ ca_pwd }}" shellpki create --days 3650 --non-interactive {{ ansible_fqdn }}'
- name: Get the server key
shell: 'ls -tr /etc/shellpki/private/ | tail -1'
register: ca_key
check_mode: no
changed_when: no
- name: Configure the server key
replace:
path: /etc/openvpn/server.conf
regexp: 'key /etc/shellpki/private/TO_COMPLETE'
replace: 'key /etc/shellpki/private/{{ ca_key.stdout }}'
- name: Restart OpenVPN
service:
name: openvpn
state: restarted
- name: Warn the user about manual checks
pause:
prompt: |
/!\ WARNING /!\
You have to manually create the CA on the server with "shellpki init {{ ansible_fqdn }}". The command will ask you to create a password, and will ask you again to give the same one several times.
You have to manually generate the CRL on the server with "openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf". The previously created password will be asked.
You have to manually create the server's certificate with "shellpki create {{ ansible_fqdn }}".
You have to adjust the config file "/etc/openvpn/server.conf" for the following parameters : local (to check), cert (to check), key (to add), server (to check), push (to complete if needed).
Finally, you can (re)start the OpenVPN service with "rcctl restart openvpn".
You must check and adjust if necessary the configuration file "/etc/openvpn/server.conf", and then restart the OpenVPN service with "rcctl restart openvpn".
The "push" parameter may be needed to push a route to the client, so that the client can access that route through OpenVPN.
Take note of the generated CA password and store it in your password manager : {{ ca_pwd }}
Press enter to exit when it's done.

View File

@ -1,5 +1,5 @@
user nobody
group nogroup
user _openvpn
group _openvpn
local {{ ansible_default_ipv4.address }}
port 1194

View File

@ -30,6 +30,7 @@
- php-sqlite3
- php-curl
- php-ssh2
- php-xml
- php-zip
- composer
- libphp-phpmailer

View File

@ -41,5 +41,5 @@ proftpd_accounts:
For generate the sha512 version of yours password :
~~~
echo "test" | mkpasswd --method=sha-512 -
printf "test" | mkpasswd --stdin --method=sha-512
~~~

View File

@ -3,12 +3,16 @@ proftpd_hostname: "{{ ansible_hostname }}"
proftpd_fqdn: "{{ ansible_fqdn }}"
proftpd_default_address: []
proftpd_ftp_enable: True
proftpd_ftp_override: False
proftpd_port: 21
proftpd_ftps_enable: False
proftpd_ftps_override: False
proftpd_ftps_port: 990
proftpd_ftps_cert: "/etc/ssl/certs/ssl-cert-snakeoil.pem"
proftpd_ftps_key: "/etc/ssl/private/ssl-cert-snakeoil.key"
proftpd_sftp_enable: False
proftpd_sftp_override: False
proftpd_sftp_use_publickeys: False
proftpd_sftp_port: 22222
proftpd_accounts: []
proftpd_accounts_final: []

View File

@ -60,3 +60,18 @@
when: proftpd_sftp_enable | bool
tags:
- proftpd
- name: Allow keys for SFTP account
blockinfile:
dest: "/etc/proftpd/sftp.authorized_keys/{{ item.name }}"
state: present
block: "{{ item.sshkeys }}"
create: yes
mode: 0600
loop: "{{ proftpd_accounts_final }}"
notify: restart proftpd
when:
- proftpd_sftp_enable | bool
- proftpd_sftp_use_publickeys | bool
tags:
- proftpd

View File

@ -20,7 +20,7 @@
src: evolinux.conf.j2
dest: /etc/proftpd/conf.d/z-evolinux.conf
mode: "0644"
force: no
force: "{{ proftpd_ftp_override }}"
notify: restart proftpd
when: proftpd_ftp_enable | bool
tags:
@ -31,7 +31,7 @@
src: ftps.conf.j2
dest: /etc/proftpd/conf.d/ftps.conf
mode: "0644"
force: no
force: "{{ proftpd_ftps_override }}"
notify: restart proftpd
when: proftpd_ftps_enable | bool
tags:
@ -42,12 +42,26 @@
src: sftp.conf.j2
dest: /etc/proftpd/conf.d/sftp.conf
mode: "0644"
force: no
force: "{{ proftpd_sftp_override }}"
notify: restart proftpd
when: proftpd_sftp_enable | bool
tags:
- proftpd
- name: SFTP key folder exists if needed
file:
path: /etc/proftpd/sftp.authorized_keys/
state: directory
mode: "0700"
owner: root
group: root
notify: restart proftpd
when:
- proftpd_sftp_enable | bool
- proftpd_sftp_use_publickeys | bool
tags:
- proftpd
- name: mod_tls_memcache is disabled
replace:
dest: /etc/proftpd/modules.conf

View File

@ -1,5 +1,13 @@
# Evolix's specific configuration
{% if proftpd_ftp_override %}
# WARNING : **Probably** ansible managed
{% endif %}
<IfModule !mod_ident.c>
LoadModule mod_ident.c
</IfModule>
ServerName "{{ proftpd_hostname }} FTP Server"
ServerIdent on "FTP Server Ready"
AccessGrantMsg "Hey, bienvenue %u sur le serveur FTP {{ proftpd_fqdn }} !"

View File

@ -1,8 +1,12 @@
{% if proftpd_ftps_override %}
# WARNING : **Probably** ansible managed
{% endif %}
<IfModule !mod_tls.c>
LoadModule mod_tls.c
</IfModule>
<VirtualHost 0.0.0.0>
<VirtualHost {{ proftpd_default_address | join(' ') }}>
TLSEngine on
TLSLog /var/log/proftpd/ftps.log
TLSProtocol TLSv1

View File

@ -1,3 +1,7 @@
{% if proftpd_sftp_override %}
# WARNING : **Probably** ansible managed
{% endif %}
<IfModule !mod_tls.c>
LoadModule mod_tls.c
</IfModule>
@ -6,7 +10,7 @@
LoadModule mod_sftp.c
</IfModule>
<VirtualHost 0.0.0.0>
<VirtualHost {{ proftpd_default_address | join(' ') }}>
SFTPEngine on
Port {{ proftpd_sftp_port }}
DefaultRoot ~
@ -14,7 +18,13 @@
SFTPLog /var/log/proftpd/sftp.log
TransferLog /var/log/proftpd/xferlog
{% if proftpd_sftp_use_publickeys %}
SFTPAuthMethods publickey password
SFTPAuthorizedUserKeys file:/etc/proftpd/sftp.authorized_keys/%u
{% else %}
SFTPAuthMethods password
{% endif %}
SFTPHostKey /etc/ssh/ssh_host_ecdsa_key
SFTPHostKey /etc/ssh/ssh_host_rsa_key

View File

@ -0,0 +1,226 @@
#!/usr/bin/env python3
from optparse import OptionParser
import shlex
import subprocess
import sys
import requests
import json
if "check_output" not in dir( subprocess ): # duck punch it in!
def f(*popenargs, **kwargs):
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
raise subprocess.CalledProcessError(retcode, cmd)
return output
subprocess.check_output = f
class RabbitCmdWrapper(object):
"""So basically this just runs rabbitmqctl commands and returns parsed output.
Typically this means you need root privs for this to work.
Made this it's own class so it could be used in other monitoring tools
if desired."""
@classmethod
def list_connections(cls):
args = shlex.split("sudo rabbitmqctl list_connections")
cmd_result = subprocess.check_output(args, text=True).strip()
results = cls._parse_list_results(cmd_result)
return results
@classmethod
def list_queues(cls):
args = shlex.split('sudo rabbitmqctl list_queues')
cmd_result = subprocess.check_output(args, text=True).strip()
results = cls._parse_list_results(cmd_result)
return results
@classmethod
def status(cls):
args = shlex.split('sudo rabbitmqctl status')
cmd_result = subprocess.check_output(args, text=True).strip()
results = cls._parse_list_results(cmd_result)
return results
@classmethod
def _parse_list_results(cls, result_string):
results = result_string.strip().split('\n')
#remove text fluff
if "Listing connections ..." in results: results.remove("Listing connections ...")
if "Listing queues ..." in results: results.remove("Listing queues ...")
return_data = []
for row in results:
return_data.append(row.split('\t'))
return return_data
def check_connection_count(critical=0, warning=0):
"""Checks to make sure the numbers of connections are within parameters."""
try:
count = len(RabbitCmdWrapper.list_connections())
if count >= critical:
print("CRITICAL - Connection Count %d" % count)
sys.exit(2)
elif count >= warning:
print("WARNING - Connection Count %d" % count)
sys.exit(1)
else:
print("OK - Connection Count %d" % count)
except Exception as err:
print("CRITICAL - %s" % err)
def check_queues_count(critical=1000, warning=1000):
"""
A blanket check to make sure all queues are within count parameters.
TODO: Possibly break this out so test can be done on individual queues.
"""
try:
critical_q = []
warning_q = []
results = RabbitCmdWrapper.list_queues()
for queue in results:
if queue.count == 2:
count = int(queue[1])
if count >= critical:
critical_q.append("%s: %s" % (queue[0], count))
elif count >= warning:
warning_q.append("%s: %s" % (queue[0], count))
if critical_q:
print("CRITICAL - %s" % ", ".join(critical_q))
sys.exit(2)
elif warning_q:
print("WARNING - %s" % ", ".join(warning_q))
sys.exit(1)
else:
print("OK - NO QUEUES EXCEED THRESHOLDS")
sys.exit(0)
except Exception as err:
print("CRITICAL - %s" % err)
sys.exit(2)
def check_mem_usage(critical=75, warning=50):
"""Check to make sure the RAM usage of rabbitmq process does not exceed 50%% of its max"""
try:
results = RabbitCmdWrapper.status()
for idx,val in enumerate(results):
if "memory," in str(val):
mem_used_raw = str(results[idx + 1])
if "vm_memory_limit" in str(val):
mem_limit_raw = str(val)
memory_used = float(filter(str.isdigit, mem_used_raw))
memory_limit = float(filter(str.isdigit, mem_limit_raw))
percent_usage = int(memory_used/memory_limit * 100)
if percent_usage > critical:
print("CRITICAL - RABBITMQ RAM USAGE at %s%% of max" % percent_usage)
sys.exit(2)
elif percent_usage > warning:
print("WARNING - RABBITMQ RAM USAGE at %s%% of max" % percent_usage)
sys.exit(1)
else:
print("OK - RABBITMQ RAM USAGE OK at %s%% of max" % percent_usage)
sys.exit(0)
except Exception as err:
print("Critical - %s" % err)
sys.exit(2)
def check_aliveness(username, password, timeout, cluster):
"""Declares a test queue, then publishes and consumes a message. Intended for use by monitoring tools. If everything is working correctly, will return HTTP status 200 with body"""
try:
r = requests.get("http://%s:15672/api/aliveness-test/%%2F" % cluster, auth=(username, password), timeout=timeout)
except requests.exceptions.RequestException as e: # Throw error if rabbitmq is down
print("Critical - %s" % e)
sys.exit(2)
if r.status_code == 200:
print("OK - RABBITMQ Aliveness Test Returns: %s" % r)
sys.exit(0)
elif r.status_code != 200:
print("CRITICAL - RabbitMQ Error: %s" % r.content)
sys.exit(2)
else:
print("UNKNOWN - RABBITMQ Aliveness Test")
sys.ext(1)
def check_cluster(username, password, timeout, cluster):
"""Checks the health of a cluster, if a node is not running mark as offline """
try:
url = "http://%s:15672/api/nodes" % cluster
r = requests.get(url, auth=(username, password), timeout=timeout)
except requests.exceptions.RequestException as e: # Throw error if no response
print("Critical - %s" % e)
sys.exit(2)
text = r.text
nodes = json.loads(text)
running_nodes = []
failed_nodes = []
for node in nodes:
if not node['running']:
failed_nodes.append(node['name'])
if node['running']:
running_nodes.append(node['name'])
if len(failed_nodes) == 1:
print("WARNING: RabbitMQ cluster is degraged: Not running %s" % failed_nodes[0])
sys.exit(1)
elif len(failed_nodes) >= 2:
print("CRITICAL: RabbitMQ cluster is critical: Not running %s" % failed_nodes)
sys.exit(2)
else:
print("OK: RabbitMQ cluster members: %s" % (" ".join(running_nodes)))
sys.exit(0)
USAGE = """Usage: ./check_rabbitmq -a [action] -C [critical] -W [warning]
Actions:
- connection_count
checks the number of connection in rabbitmq's list_connections
- queues_count
checks the count in each of the queues in rabbitmq's list_queues
- mem_usage
checks to ensure mem usage of rabbitmq process does not exceed 50%
- aliveness
Use the /api/aliveness-test API to send/receive a message. (requires -u username -p password args)
- cluster_status
Parse /api/nodes to check the cluster status. (requires -u username -p password"""
if __name__ == "__main__":
parser = OptionParser(USAGE)
parser.add_option("-a", "--action", dest="action",
help="Action to Check")
parser.add_option("-C", "--critical", dest="critical",
type="int", help="Critical Threshold")
parser.add_option("-W", "--warning", dest="warning",
type="int", help="Warning Threshold")
parser.add_option("-u", "--username", dest="username", default="guest",
type="string", help="RabbitMQ username, Default guest")
parser.add_option("-p", "--password", dest="password", default="guest",
type="string", help="RabbitMQ password, Default guest")
parser.add_option("-t", "--timeout", dest="timeout", default=1,
type="int", help="Request Timeout, defaults to 1 second")
parser.add_option("-c", "--cluster", dest="cluster", default="localhost",
type="string", help="Cluster IP/DNS name, defaults to localhost")
(options, args) = parser.parse_args()
if options.action == "connection_count":
check_connection_count(options.critical, options.warning)
elif options.action == "queues_count":
check_queues_count(options.critical, options.warning)
elif options.action == "mem_usage":
check_mem_usage(options.critical, options.warning)
elif options.action == "aliveness":
check_aliveness(options.username, options.password, options.timeout, options.cluster)
elif options.action == "cluster_status":
check_cluster(options.username, options.password, options.timeout, options.cluster)
else:
print("Invalid action: %s" % options.action)
print(USAGE)

View File

@ -24,6 +24,17 @@
group: root
mode: "0755"
force: yes
when: ansible_distribution_major_version is version('11', '<=')
- name: check_rabbitmq (Python 3 version) is installed
copy:
src: check_rabbitmq.python3
dest: /usr/local/lib/nagios/plugins/check_rabbitmq
owner: root
group: root
mode: "0755"
force: yes
when: ansible_distribution_major_version is version('11', '==')
- name: check_rabbitmq is available for NRPE
lineinfile:

View File

@ -11,6 +11,15 @@
tags:
- redis
- name: Config directory permissions are set
file:
dest: "{{ redis_conf_dir }}"
mode: "0750"
owner: redis
group: redis
tags:
- redis
- name: Redis is running and enabled on boot.
systemd:
name: "{{ redis_systemd_name }}"

View File

@ -28,9 +28,9 @@
- name: "Instance '{{ redis_instance_name }}' config directory is present"
file:
dest: "{{ redis_conf_dir }}"
mode: "0755"
owner: "root"
group: "root"
mode: "0750"
owner: "redis-{{ redis_instance_name }}"
group: "redis-{{ redis_instance_name }}"
follow: yes
state: directory
tags:
@ -39,9 +39,9 @@
- name: "Instance '{{ redis_instance_name }}' config hooks directories are present"
file:
dest: "{{ _dir }}"
mode: "0755"
owner: "root"
group: "root"
mode: "0750"
owner: "redis-{{ redis_instance_name }}"
group: "redis-{{ redis_instance_name }}"
follow: yes
state: directory
loop:

View File

@ -2,6 +2,6 @@
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd -F -j {{ varnish_jail }} {{ varnish_addresses | map('regex_replace', '^(.*)$', '-a \\1') | list | join(' ') }} -T {{ varnish_management_address }} -f {{ varnish_config_file }} -S {{ varnish_secret_file }} -s {{ varnish_storage }} -p thread_pools={{ varnish_thread_pools }} -p thread_pool_add_delay={{ varnish_thread_pool_add_delay }} -p thread_pool_min={{ varnish_thread_pool_min }} -p thread_pool_max={{ varnish_thread_pool_max }}
ExecStart=/usr/sbin/varnishd -j {{ varnish_jail }} -F {{ varnish_addresses | map('regex_replace', '^(.*)$', '-a \\1') | list | join(' ') }} -T {{ varnish_management_address }} -f {{ varnish_config_file }} -S {{ varnish_secret_file }} -s {{ varnish_storage }} -p thread_pools={{ varnish_thread_pools }} -p thread_pool_add_delay={{ varnish_thread_pool_add_delay }} -p thread_pool_min={{ varnish_thread_pool_min }} -p thread_pool_max={{ varnish_thread_pool_max }}
ExecReload=
ExecReload=/etc/varnish/reload-vcl.sh

13
vrrpd/defaults/main.yml Normal file
View File

@ -0,0 +1,13 @@
---
vrrp_addresses: []
# - {
# interface: Null # the interface name to run on
# delay: 10 # the advertisement interval (in sec) (default: 1)
# id: Null # the id of the virtual server [1-255]
# priority: Null # the priority of this host in the virtual server (default: 100)
# authentication: Null # authentification type: auth=(none|pw/hexkey|ah/hexkey) hexkey=0x[0-9a-fA-F]+
# label: Null # use this name is syslog messages (helps when several vrid are running)
# ip: Null # the ip address(es) (and optionnaly subnet mask) of the virtual server
# state: Null # 'started' or 'stopped'
# }

20
vrrpd/tasks/ip.yml Normal file
View File

@ -0,0 +1,20 @@
---
- name: set unit name
set_fact:
vrrp_systemd_unit_name: "vrrp-{{ vrrp_address.id }}.service"
- name: add systemd unit
template:
src: vrrp.service.j2
dest: "/etc/systemd/system/{{ vrrp_systemd_unit_name }}"
force: yes
register: vrrp_systemd_unit
- name: enable and start systemd unit
systemd:
name: "{{ vrrp_systemd_unit_name }}"
daemon_reload: yes
enabled: yes
state: "{{ vrrp_address.state }}"
when: vrrp_systemd_unit is changed

View File

@ -14,7 +14,36 @@
tags:
- vrrpd
- name: Adjust sysctl config
- name: Adjust sysctl config (except rp_filter)
sysctl:
name: "{{ item.name }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/vrrpd.conf
sysctl_set: yes
state: present
loop:
- { name: 'net.ipv4.conf.all.arp_ignore', value: 1 }
- { name: 'net.ipv4.conf.all.arp_announce', value: 2 }
- { name: 'net.ipv4.ip_nonlocal_bind', value: 1 }
tags:
- vrrpd
- name: look if rp_filter is managed by minifirewall
command: grep "SYSCTL_RP_FILTER=" /etc/default/minifirewall
failed_when: False
changed_when: False
check_mode: no
register: grep_sysctl_rp_filter_minifirewall
- name: Configure SYSCTL_RP_FILTER in minifirewall
lineinfile:
dest: "/etc/default/minifirewall"
line: "SYSCTL_RP_FILTER='0'"
regexp: "SYSCTL_RP_FILTER=('|\").*('|\")"
create: no
when: grep_sysctl_rp_filter_minifirewall.rc == 0
- name: Adjust sysctl config (only rp_filter)
sysctl:
name: "{{ item.name }}"
value: "{{ item.value }}"
@ -23,10 +52,13 @@
state: present
loop:
- { name: 'net.ipv4.conf.default.rp_filter', value: 0 }
- { name: 'net.ipv4.conf.eth0.rp_filter', value: 0 }
- { name: 'net.ipv4.conf.all.rp_filter', value: 0 }
- { name: 'net.ipv4.conf.all.arp_ignore', value: 1 }
- { name: 'net.ipv4.conf.all.arp_announce', value: 2 }
- { name: 'net.ipv4.ip_nonlocal_bind', value: 1 }
when: grep_sysctl_rp_filter_minifirewall.rc != 0
tags:
- vrrpd
- name: Create VRRP address
include: ip.yml
loop: "{{ vrrp_addresses }}"
loop_control:
loop_var: "vrrp_address"

View File

@ -0,0 +1,15 @@
[Unit]
Description=VRRP Daemon for IP {{ vrrp_address.ip }} on {{ vrrp_address.interface }}
After=network.target
[Service]
ExecStart=/usr/sbin/vrrpd -i {{ vrrp_address.interface | mandatory }} -x -D -d {{ vrrp_address.delay | mandatory }} -v {{ vrrp_address.id | mandatory }} -p {{ vrrp_address.priority | mandatory }} -a {{ vrrp_address.authentication | mandatory }} -l {{ vrrp_address.label | mandatory }} {{ vrrp_address.ip | mandatory }}
# PIDFile=/var/run/vrrpd_{{ vrrp_address.label }}_{{ vrrp_address.id }}.pid
Restart=on-failure
Type=forking
IgnoreSIGPIPE=no
KillMode=process
RemainAfterExit=yes
[Install]
WantedBy=default.target

View File

@ -1,7 +1,6 @@
---
nextcloud_webserver: 'nginx'
nextcloud_version: "21.0.0"
nextcloud_archive_name: "nextcloud-{{ nextcloud_version }}.tar.bz2"
nextcloud_version: "latest-24"
nextcloud_archive_name: "{{ nextcloud_version }}.tar.bz2"
nextcloud_releases_baseurl: "https://download.nextcloud.com/server/releases/"
nextcloud_instance_name: "nextcloud"

View File

@ -8,3 +8,8 @@
service:
name: nginx
state: reloaded
- name: reload apache
service:
name: apache2
state: reloaded

View File

@ -1,4 +1 @@
---
# dependencies:
# - { role: nginx, when: nextcloud_webserver == 'nginx' }
# - { role: php, php_fpm_enable: True }

View File

@ -0,0 +1,33 @@
---
- name: "Get PHP Version"
shell: 'php -v | grep "PHP [0-9]." | sed -E "s/PHP ([0-9]\.[0-9]).*/\1/g;"'
register: shell_php
check_mode: no
- name: "Set variables"
set_fact:
php_version: "{{ shell_php.stdout }}"
- name: Apply specific PHP settings (apache)
ini_file:
path: "/etc/php/{{ php_version }}/apache2/conf.d/zzz-evolinux-custom.ini"
section: ''
option: "{{ item.option }}"
value: "{{ item.value }}"
notify: reload apache
with_items:
- {option: 'allow_url_fopen', value: 'On'}
- {option: 'disable_functions', value: ''}
- {option: 'max_execution_time', value: '300'}
- {option: 'memory_limit', value: '512M'}
- name: Apply specific PHP settings (cli)
ini_file:
path: "/etc/php/{{ php_version }}/cli/conf.d/zzz-evolinux-custom.ini"
section: ''
option: "{{ item.option }}"
value: "{{ item.value }}"
with_items:
- {option: 'allow_url_fopen', value: 'On'}
- {option: 'apc.enable_cli', value: 'On'}

View File

@ -0,0 +1,23 @@
---
- name: Copy Apache vhost
template:
src: apache-vhost.conf.j2
dest: "/etc/apache2/sites-available/{{ nextcloud_instance_name }}.conf"
mode: "0640"
notify: reload apache
tags:
- nextcloud
- name: Enable Apache vhost
file:
src: "/etc/apache2/sites-available/{{ nextcloud_instance_name }}.conf"
dest: "/etc/apache2/sites-enabled/{{ nextcloud_instance_name }}.conf"
state: link
notify: reload apache
tags:
- nextcloud
# - name: Generate ssl config
# shell:
# cmd: "/usr/local/sbin/vhost-domains {{ nextcloud_instance_name }} | /usr/local/sbin/make-csr {{ nextcloud_instance_name }}"
# creates: "/etc/nginx/ssl/{{ nextcloud_instance_name }}.conf"

View File

@ -16,10 +16,12 @@
- php-apcu
- php-redis
- php-bcmath
- php-imagick
- libmagickcore-6.q16-6-extra
tags:
- nextcloud
# dependency for mysql_user and mysql_db
# dependency for mysql_user and mysql_db - python2
- name: python modules is installed (Ansible dependency)
apt:
name:
@ -30,7 +32,7 @@
- nextcloud
when: ansible_python_version is version('3', '<')
# dependency for mysql_user and mysql_db
# dependency for mysql_user and mysql_db - python3
- name: python3 modules is installed (Ansible dependency)
apt:
name:
@ -41,12 +43,14 @@
- nextcloud
when: ansible_python_version is version('3', '>=')
- include: apache-system.yml
- include: user.yml
- include: archive.yml
- include: vhost.yml
- include: apache-vhost.yml
- include: mysql.yml
- include: mysql-user.yml
- include: config.yml

View File

@ -1,15 +1,15 @@
---
- name: Create Nextcloud group
- name: Create {{ nextcloud_user }} unix group
group:
name: "{{ nextcloud_instance_name | mandatory }}"
name: "{{ nextcloud_user | mandatory }}"
state: present
tags:
- nextcloud
- name: Create Nextcloud user
- name: Create {{ nextcloud_user | mandatory }} unix user
user:
name: "{{ nextcloud_user | mandatory }}"
group: "{{ nextcloud_user }}"
group: "{{ nextcloud_user | mandatory }}"
home: "{{ nextcloud_home | mandatory }}"
shell: '/bin/bash'
create_home: True
@ -18,17 +18,11 @@
tags:
- nextcloud
- name: Add the user 'www-data' to Nextcloud group
user:
name: www-data
groups: "{{ nextcloud_user | mandatory }}"
append: yes
- name: Create top-level directories
file:
dest: "{{ item }}"
state: directory
mode: "0770"
mode: "0700"
owner: "{{ nextcloud_user }}"
group: "{{ nextcloud_user }}"
loop:

View File

@ -1,34 +0,0 @@
---
- block:
- name: Copy Nginx vhost
template:
src: nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ nextcloud_instance_name }}.conf"
mode: "0640"
notify: reload nginx
tags:
- nextcloud
- name: Enable Nginx vhost
file:
src: "/etc/nginx/sites-available/{{ nextcloud_instance_name }}.conf"
dest: "/etc/nginx/sites-enabled/{{ nextcloud_instance_name }}.conf"
state: link
notify: reload nginx
tags:
- nextcloud
- name: Generate ssl config
shell:
cmd: "/usr/local/sbin/vhost-domains {{ nextcloud_instance_name }} | /usr/local/sbin/make-csr {{ nextcloud_instance_name }}"
creates: "/etc/nginx/ssl/{{ nextcloud_instance_name }}.conf"
- name: Copy PHP-FPM pool
template:
src: php-fpm.conf.j2
dest: "/etc/php/7.3/fpm/pool.d/{{ nextcloud_instance_name }}.conf"
mode: "0640"
notify: reload php-fpm
tags:
- nextcloud
when: nextcloud_webserver == 'nginx'

View File

@ -0,0 +1,41 @@
<VirtualHost *:80 *:443>
ServerName {{ nextcloud_domains[0] }}
{% for domain_alias in nextcloud_domains[1:] %}
ServerAlias {{ domain_alias }}
{% endfor %}
# SSLEngine on
# SSLCertificateFile /etc/letsencrypt/live/{{ nextcloud_instance_name }}/fullchain.pem
# SSLCertificateKeyFile /etc/letsencrypt/live/{{ nextcloud_instance_name }}/privkey.pem
DocumentRoot {{ nextcloud_webroot }}/
<Directory {{ nextcloud_webroot }}/>
Require all granted
AllowOverride All
Options FollowSymLinks MultiViews
<IfModule mod_dav.c>
Dav off
</IfModule>
</Directory>
# SSL Redirect
# RewriteEngine On
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP:X-Forwarded-Proto} !=https
# RewriteRule ^ https://%{HTTP:Host}%{REQUEST_URI} [L,R=permanent]
# ITK
AssignUserID {{ nextcloud_user }} {{ nextcloud_user }}
# LOG
CustomLog /var/log/apache2/access.log vhost_combined
ErrorLog /var/log/apache2/error.log
# PHP
php_admin_value sendmail_path "/usr/sbin/sendmail -t -i -f {{ nextcloud_user }}"
php_admin_value open_basedir "/usr/share/php:{{ nextcloud_home }}:/tmp"
</VirtualHost>

View File

@ -1,134 +0,0 @@
upstream php-handler-{{ nextcloud_instance_name }} {
server unix:/var/run/php/php-fpm-{{ nextcloud_instance_name }}.sock;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ nextcloud_domains | join(' ') }};
access_log {{ nextcloud_home }}/log/access.log;
error_log {{ nextcloud_home }}/log/error.log;
include /etc/nginx/snippets/letsencrypt.conf;
include /etc/nginx/ssl/{{ nextcloud_instance_name }}.conf;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
root {{ nextcloud_webroot }};
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The following 6 rules are borrowed from `.htaccess`
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
# Anything else is dynamically handled by Nextcloud
location ^~ /.well-known { return 301 /index.php$uri; }
location ~ ^/.well-known/acme-challenge/* { allow all; }
try_files $uri $uri/ =404;
}
# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
location / {
rewrite ^ /index.php;
}
location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass php-handler-{{ nextcloud_instance_name }};
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
# Adding the cache control header for js, css and map files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Optional: Don't log access to assets
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
try_files $uri /index.php$request_uri;
# Optional: Don't log access to other assets
access_log off;
}
}

View File

@ -1,17 +0,0 @@
[{{ nextcloud_instance_name }}]
user = {{ nextcloud_user }}
group = {{ nextcloud_user }}
listen = /run/php/php-fpm-{{ nextcloud_instance_name }}.sock
listen.owner = {{ nextcloud_user }}
listen.group = {{ nextcloud_user }}
pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 120s
pm.status_path = /fpm_status
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = {{ nextcloud_home }}/tmp
env[TMPDIR] = {{ nextcloud_home }}/tmp
env[TEMP] = {{ nextcloud_home }}/tmp