cp over all the bundles from kunsis bw repo
This commit is contained in:
parent
65b117b819
commit
1f73b04351
89 changed files with 3991 additions and 0 deletions
6
bundles/apt/files/apt-listchanges.conf
Normal file
6
bundles/apt/files/apt-listchanges.conf
Normal file
|
@ -0,0 +1,6 @@
|
|||
[apt]
|
||||
frontend=pager
|
||||
email_address=${data['mail']}
|
||||
confirm=0
|
||||
save_seen=/var/lib/apt/listchanges.db
|
||||
which=both
|
32
bundles/apt/files/check_unattended_upgrades
Normal file
32
bundles/apt/files/check_unattended_upgrades
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
statusfile="/var/tmp/unattended_upgrades.status"
|
||||
if ! [[ -f "$statusfile" ]]
|
||||
then
|
||||
echo "Status file not found"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
mtime=$(stat -c %Y $statusfile)
|
||||
now=$(date +%s)
|
||||
if (( $now - $mtime > 60*60*24*8 ))
|
||||
then
|
||||
echo "Status file is older than 8 days!"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
exitcode=$(cat $statusfile)
|
||||
case "$exitcode" in
|
||||
abort_ssh)
|
||||
echo "Upgrades skipped due to active SSH login"
|
||||
exit 1
|
||||
;;
|
||||
0)
|
||||
echo "OK"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Last exitcode was $exitcode"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
43
bundles/apt/files/do-unattended-upgrades
Normal file
43
bundles/apt/files/do-unattended-upgrades
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
apt-get update
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y -q -o Dpkg::Options::=--force-confold dist-upgrade
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y -q autoclean
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y -q autoremove
|
||||
|
||||
% if clean_old_kernels:
|
||||
existing=$(dpkg --get-selections | grep -E '^linux-(image|headers)-[0-9]' || true)
|
||||
|
||||
if [[ -z "$existing" ]]
|
||||
then
|
||||
echo "ERROR: No installed kernels found! Aborting!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current=$(uname -r | sed -r 's/-[a-zA-Z]+$//')
|
||||
latest=$(echo "$existing" | sort --version-sort -t- -k 3,4 | tail -n 1 | sed -r 's/[^0-9]+([0-9]\.[^-]+-[0-9]+).*/\1/')
|
||||
todelete=$(echo "$existing" | grep -v -E "($current|$latest)" | awk '{ print $1 }' || true)
|
||||
|
||||
if [[ -n "$todelete" ]]
|
||||
then
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -qy purge $todelete
|
||||
fi
|
||||
% endif
|
||||
|
||||
% for affected, restarts in sorted(restart_triggers.items()):
|
||||
up_since=$(systemctl show "${affected}" | sed -n 's/^ActiveEnterTimestamp=//p' || echo 0)
|
||||
up_since_ts=$(date -d "$up_since" +%s || echo 0)
|
||||
now=$(date +%s)
|
||||
|
||||
if [ $((now - up_since_ts)) -lt 3600 ]
|
||||
then
|
||||
% for restart in sorted(restarts):
|
||||
systemctl restart "${restart}" || true
|
||||
% endfor
|
||||
fi
|
||||
% endfor
|
15
bundles/apt/files/kernel-postinst.d
Normal file
15
bundles/apt/files/kernel-postinst.d
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
# /etc/kernel/postinst.d/unattended-upgrades
|
||||
|
||||
case "$DPKG_MAINTSCRIPT_PACKAGE::$DPKG_MAINTSCRIPT_NAME" in
|
||||
linux-image-extra*::postrm)
|
||||
exit 0;;
|
||||
esac
|
||||
|
||||
if [ -d /var/run ]; then
|
||||
touch /var/run/reboot-required
|
||||
if ! grep -q "^$DPKG_MAINTSCRIPT_PACKAGE$" /var/run/reboot-required.pkgs 2> /dev/null ; then
|
||||
echo "$DPKG_MAINTSCRIPT_PACKAGE" >> /var/run/reboot-required.pkgs
|
||||
fi
|
||||
fi
|
3
bundles/apt/files/sources.list-debian-bullseye
Normal file
3
bundles/apt/files/sources.list-debian-bullseye
Normal file
|
@ -0,0 +1,3 @@
|
|||
deb http://deb.debian.org/debian/ bullseye main non-free contrib
|
||||
deb http://security.debian.org/debian-security bullseye-security main contrib non-free
|
||||
deb http://deb.debian.org/debian/ bullseye-updates main contrib non-free
|
3
bundles/apt/files/sources.list-debian-buster
Normal file
3
bundles/apt/files/sources.list-debian-buster
Normal file
|
@ -0,0 +1,3 @@
|
|||
deb http://deb.debian.org/debian/ buster main non-free contrib
|
||||
deb http://security.debian.org/debian-security buster/updates main contrib non-free
|
||||
deb http://deb.debian.org/debian/ buster-updates main contrib non-free
|
1
bundles/apt/files/sources.list-debian-unstable
Normal file
1
bundles/apt/files/sources.list-debian-unstable
Normal file
|
@ -0,0 +1 @@
|
|||
deb http://deb.debian.org/debian/ unstable main non-free contrib
|
1
bundles/apt/files/sources.list-raspbian-buster
Normal file
1
bundles/apt/files/sources.list-raspbian-buster
Normal file
|
@ -0,0 +1 @@
|
|||
deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
|
56
bundles/apt/files/upgrade-and-reboot
Normal file
56
bundles/apt/files/upgrade-and-reboot
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash
|
||||
|
||||
# With systemd, we can force logging to the journal. This is better than
|
||||
# spamming the world with cron mails. You can then view these logs using
|
||||
# "journalctl -rat upgrade-and-reboot".
|
||||
if which logger >/dev/null 2>&1
|
||||
then
|
||||
# Dump stdout and stderr to logger, which will then put everything
|
||||
# into the journal.
|
||||
exec 1> >(logger -t upgrade-and-reboot -p user.info)
|
||||
exec 2> >(logger -t upgrade-and-reboot -p user.error)
|
||||
fi
|
||||
|
||||
. /etc/upgrade-and-reboot.conf
|
||||
|
||||
echo "Starting upgrade-and-reboot for node $nodename ..."
|
||||
|
||||
statusfile="/var/tmp/unattended_upgrades.status"
|
||||
# Workaround, because /var/tmp is usually 1777
|
||||
[[ "$UID" == 0 ]] && chown root:root "$statusfile"
|
||||
|
||||
logins=$(ps h -C sshd -o euser | awk '$1 != "root" && $1 != "sshd" && $1 != "sshmon"')
|
||||
if [[ -n "$logins" ]]
|
||||
then
|
||||
echo "Will abort now, there are active SSH logins: $logins"
|
||||
echo "abort_ssh" > "$statusfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
softlockdir=/var/lib/bundlewrap/soft-$nodename
|
||||
mkdir -p "$softlockdir"
|
||||
printf '{"comment": "UPDATE", "date": %s, "expiry": %s, "id": "UNATTENDED", "items": ["*"], "user": "root@localhost"}\n' \
|
||||
$(date +%s) \
|
||||
$(date -d 'now + 30 mins' +%s) \
|
||||
>"$softlockdir"/UNATTENDED
|
||||
trap 'rm -f "$softlockdir"/UNATTENDED' EXIT
|
||||
|
||||
do-unattended-upgrades
|
||||
ret=$?
|
||||
|
||||
echo "$ret" > "$statusfile"
|
||||
if (( $ret != 0 ))
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f /var/run/reboot-required ]]
|
||||
then
|
||||
if [[ -n "$reboot_mail_to" ]]
|
||||
then
|
||||
date | mail -s "SYSREBOOTNOW $nodename" "$reboot_mail_to"
|
||||
fi
|
||||
systemctl reboot
|
||||
else
|
||||
echo "upgrade-and-reboot for node $nodename is DONE"
|
||||
fi
|
2
bundles/apt/files/upgrade-and-reboot.conf
Normal file
2
bundles/apt/files/upgrade-and-reboot.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
nodename="${node.name}"
|
||||
reboot_mail_to="${node.metadata.get('apt/unattended-upgrades/reboot_mail_to', '')}"
|
191
bundles/apt/items.py
Normal file
191
bundles/apt/items.py
Normal file
|
@ -0,0 +1,191 @@
|
|||
from bundlewrap.exceptions import BundleError
|
||||
|
||||
supported_os = {
|
||||
'debian': {
|
||||
10: 'buster',
|
||||
11: 'bullseye',
|
||||
99: 'unstable',
|
||||
},
|
||||
'raspbian': {
|
||||
10: 'buster',
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
supported_os[node.os][node.os_version[0]]
|
||||
except (KeyError, IndexError):
|
||||
raise BundleError(f'{node.name}: OS {node.os} {node.os_version} is not supported by bundle:apt')
|
||||
|
||||
|
||||
actions = {
|
||||
'apt_update': {
|
||||
'command': 'apt-get update',
|
||||
'needed_by': {
|
||||
'pkg_apt:',
|
||||
},
|
||||
'triggered': True,
|
||||
'cascade_skip': False,
|
||||
},
|
||||
}
|
||||
|
||||
files = {
|
||||
'/etc/apt/sources.list': {
|
||||
'source': 'sources.list-{}-{}'.format(node.os, supported_os[node.os][node.os_version[0]]),
|
||||
'triggers': {
|
||||
'action:apt_update',
|
||||
},
|
||||
},
|
||||
'/etc/cloud': {
|
||||
'delete': True,
|
||||
},
|
||||
'/etc/kernel/postinst.d/unattended-upgrades': {
|
||||
'source': 'kernel-postinst.d',
|
||||
'mode': '0755',
|
||||
},
|
||||
'/etc/netplan': {
|
||||
'delete': True,
|
||||
},
|
||||
'/etc/upgrade-and-reboot.conf': {
|
||||
'content_type': 'mako',
|
||||
},
|
||||
'/usr/local/sbin/upgrade-and-reboot': {
|
||||
'mode': '0700',
|
||||
},
|
||||
'/usr/local/sbin/do-unattended-upgrades': {
|
||||
'content_type': 'mako',
|
||||
'mode': '0700',
|
||||
'context': {
|
||||
'clean_old_kernels': node.metadata.get('apt/clean_old_kernels', True),
|
||||
'restart_triggers': node.metadata.get('apt/restart_triggers', {}),
|
||||
}
|
||||
},
|
||||
'/usr/local/share/icinga/plugins/check_unattended_upgrades': {
|
||||
'mode': '0755',
|
||||
},
|
||||
'/var/lib/cloud': {
|
||||
'delete': True,
|
||||
},
|
||||
}
|
||||
|
||||
directories = {
|
||||
'/etc/apt/sources.list.d': {
|
||||
'purge': True,
|
||||
'triggers': {
|
||||
'action:apt_update',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
svc_systemd = {
|
||||
'apt-daily.timer': {
|
||||
'running': False,
|
||||
'enabled': False,
|
||||
},
|
||||
'apt-daily-upgrade.timer': {
|
||||
'running': False,
|
||||
'enabled': False,
|
||||
},
|
||||
}
|
||||
|
||||
pkg_apt = {
|
||||
'apt-transport-https': {},
|
||||
|
||||
'arping': {},
|
||||
'at': {},
|
||||
'build-essential': {},
|
||||
'bzip2': {},
|
||||
'curl': {},
|
||||
'diffutils': {},
|
||||
'dnsutils': {},
|
||||
'git': {},
|
||||
'grep': {},
|
||||
'gzip': {},
|
||||
'htop': {},
|
||||
'jq': {},
|
||||
'less': {},
|
||||
'logrotate': {},
|
||||
'lsof': {},
|
||||
'mailutils': {},
|
||||
'manpages': {},
|
||||
'moreutils': {},
|
||||
'mount': {},
|
||||
'mtr': {},
|
||||
'ncdu': {},
|
||||
'ncurses-term': {},
|
||||
'netcat': {},
|
||||
'nmap': {},
|
||||
'python3': {},
|
||||
'python3-dev': {},
|
||||
'python3-setuptools': {
|
||||
'needed_by': {
|
||||
'pkg_pip:',
|
||||
},
|
||||
},
|
||||
'python3-pip': {
|
||||
'needed_by': {
|
||||
'pkg_pip:',
|
||||
},
|
||||
},
|
||||
'python3-virtualenv': {},
|
||||
'rsync': {},
|
||||
'tar': {},
|
||||
'tcpdump': {},
|
||||
'telnet': {},
|
||||
'tmux': {},
|
||||
'tree': {},
|
||||
'unzip': {},
|
||||
'vim': {},
|
||||
'wget': {},
|
||||
'whois': {},
|
||||
'zip': {},
|
||||
|
||||
'cloud-init': {
|
||||
'installed': False,
|
||||
},
|
||||
'netplan.io': {
|
||||
'installed': False,
|
||||
},
|
||||
'popularity-contest': {
|
||||
'installed': False,
|
||||
},
|
||||
'unattended-upgrades': {
|
||||
'installed': False,
|
||||
},
|
||||
}
|
||||
|
||||
if node.os_version[0] >= 11:
|
||||
symlinks = {
|
||||
'/usr/bin/python': {
|
||||
'target': '/usr/bin/python3',
|
||||
'needs': {
|
||||
'pkg_apt:python3',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, data in node.metadata.get('apt/repos', {}).items():
|
||||
files['/etc/apt/sources.list.d/{}.list'.format(name)] = {
|
||||
'content_type': 'mako',
|
||||
'content': ("\n".join(sorted(data['items']))).format(
|
||||
os=node.os,
|
||||
os_release=supported_os[node.os][node.os_version[0]],
|
||||
),
|
||||
'triggers': {
|
||||
'action:apt_update',
|
||||
},
|
||||
}
|
||||
|
||||
if data.get('install_gpg_key', True):
|
||||
files['/etc/apt/sources.list.d/{}.list'.format(name)]['needs'] = {
|
||||
'file:/etc/apt/trusted.gpg.d/{}.list.asc'.format(name),
|
||||
}
|
||||
|
||||
files['/etc/apt/trusted.gpg.d/{}.list.asc'.format(name)] = {
|
||||
'source': 'gpg-keys/{}.asc'.format(name),
|
||||
'triggers': {
|
||||
'action:apt_update',
|
||||
},
|
||||
}
|
||||
|
||||
for package, options in node.metadata.get('apt/packages', {}).items():
|
||||
pkg_apt[package] = options
|
35
bundles/apt/metadata.py
Normal file
35
bundles/apt/metadata.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
defaults = {
|
||||
'apt': {
|
||||
'unattended_upgrades': {
|
||||
'day': 5,
|
||||
'hour': 21,
|
||||
},
|
||||
},
|
||||
'icinga2_api': {
|
||||
'apt': {
|
||||
'services': {
|
||||
'UNATTENDED UPGRADES': {
|
||||
'command_on_monitored_host': '/usr/local/share/icinga/plugins/check_unattended_upgrades',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor.provides(
|
||||
'cron/upgrade-and-reboot'
|
||||
)
|
||||
def patchday(metadata):
|
||||
day = metadata.get('apt/unattended_upgrades/day')
|
||||
hour = metadata.get('apt/unattended_upgrades/hour')
|
||||
|
||||
return {
|
||||
'cron': {
|
||||
'upgrade-and-reboot': '{minute} {hour} * * {day} root /usr/local/sbin/upgrade-and-reboot'.format(
|
||||
minute=node.magic_number % 30,
|
||||
hour=hour,
|
||||
day=day,
|
||||
),
|
||||
},
|
||||
}
|
12
bundles/basic/files/hosts
Normal file
12
bundles/basic/files/hosts
Normal file
|
@ -0,0 +1,12 @@
|
|||
127.0.0.1 localhost ${node.name} ${node.metadata['hostname']}
|
||||
|
||||
::1 ip6-localhost
|
||||
fe00::0 ip6-localnet
|
||||
ff00::0 ip6-mcastprefix
|
||||
ff02::1 ip6-allnodes
|
||||
ff02::2 ip6-allrouters
|
||||
ff02::3 ip6-allhosts
|
||||
|
||||
% for ip, entries in sorted(node.metadata.get('hosts/entries', {}).items()):
|
||||
${ip} ${' '.join(sorted(entries))}
|
||||
% endfor
|
39
bundles/basic/files/htoprc
Normal file
39
bundles/basic/files/htoprc
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Beware! This file is rewritten by htop when settings are changed in the interface.
|
||||
# The parser is also very primitive, and not human-friendly.
|
||||
fields=0 48 17 18 38 39 40 2 46 47 49 1
|
||||
sort_key=46
|
||||
sort_direction=-1
|
||||
tree_sort_key=0
|
||||
tree_sort_direction=1
|
||||
hide_kernel_threads=1
|
||||
hide_userland_threads=0
|
||||
shadow_other_users=0
|
||||
show_thread_names=0
|
||||
show_program_path=1
|
||||
highlight_base_name=1
|
||||
highlight_megabytes=0
|
||||
highlight_threads=1
|
||||
highlight_changes=0
|
||||
highlight_changes_delay_secs=5
|
||||
find_comm_in_cmdline=1
|
||||
strip_exe_from_cmdline=1
|
||||
show_merged_command=0
|
||||
tree_view=0
|
||||
tree_view_always_by_pid=0
|
||||
header_margin=1
|
||||
detailed_cpu_time=1
|
||||
cpu_count_from_one=1
|
||||
show_cpu_usage=1
|
||||
show_cpu_frequency=0
|
||||
show_cpu_temperature=0
|
||||
degree_fahrenheit=0
|
||||
update_process_names=0
|
||||
account_guest_in_cpu_meter=0
|
||||
color_scheme=0
|
||||
enable_mouse=0
|
||||
delay=10
|
||||
left_meters=Tasks LoadAverage Uptime Memory CPU LeftCPUs CPU
|
||||
left_meter_modes=2 2 2 1 1 1 2
|
||||
right_meters=Hostname CPU RightCPUs
|
||||
right_meter_modes=2 3 1
|
||||
hide_function_bar=0
|
1
bundles/basic/files/locale
Normal file
1
bundles/basic/files/locale
Normal file
|
@ -0,0 +1 @@
|
|||
LANG=${node.metadata['locale']['default']}
|
3
bundles/basic/files/locale.gen
Normal file
3
bundles/basic/files/locale.gen
Normal file
|
@ -0,0 +1,3 @@
|
|||
% for locale in sorted(node.metadata['locale']['installed']):
|
||||
${locale} ${locale.split('.')[-1]}
|
||||
% endfor
|
88
bundles/basic/items.py
Normal file
88
bundles/basic/items.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
from inspect import cleandoc
|
||||
from uuid import UUID
|
||||
|
||||
from bundlewrap.utils.text import italic
|
||||
|
||||
files = {
|
||||
'/etc/default/locale': {
|
||||
'content_type': 'mako',
|
||||
'needs': {
|
||||
'action:locale-gen',
|
||||
},
|
||||
},
|
||||
'/etc/hosts': {
|
||||
'content_type': 'mako',
|
||||
},
|
||||
'/etc/htoprc.global': {
|
||||
'source': 'htoprc',
|
||||
},
|
||||
'/etc/motd': {
|
||||
'content': '',
|
||||
},
|
||||
}
|
||||
|
||||
locale_needs = set()
|
||||
for locale in sorted(node.metadata['locale']['installed']):
|
||||
actions[f'ensure_locale_{locale}_is_enabled'] = {
|
||||
'command': f"sed -i '/{locale}/s/^# *//g' /etc/locale.gen",
|
||||
'unless': f"grep -e '^{locale}' /etc/locale.gen",
|
||||
'triggers': {
|
||||
'action:locale-gen',
|
||||
},
|
||||
'needs': locale_needs,
|
||||
}
|
||||
locale_needs = {f'action:ensure_locale_{locale}_is_enabled'}
|
||||
|
||||
actions = {
|
||||
'locale-gen': {
|
||||
'triggered': True,
|
||||
'command': 'locale-gen',
|
||||
},
|
||||
}
|
||||
|
||||
description = []
|
||||
|
||||
if not node.metadata.get('icinga_options/exclude_from_monitoring', False):
|
||||
description.append('icingaweb2: https://icinga.kunsmann.eu/monitoring/host/show?host={}'.format(node.name))
|
||||
|
||||
if node.has_bundle('telegraf'):
|
||||
description.append('Grafana: https://grafana.kunsmann.eu/d/{}'.format(UUID(int=node.magic_number).hex[:10]))
|
||||
|
||||
if (
|
||||
not node.metadata.get('icinga_options/exclude_from_monitoring', False) or
|
||||
node.has_bundle('telegraf')
|
||||
):
|
||||
description.append('') # divider line
|
||||
|
||||
if node.metadata.get('nginx/vhosts', {}):
|
||||
description.append('nginx vhosts:')
|
||||
|
||||
for vname, vconfig in sorted(node.metadata.get('nginx/vhosts', {}).items()):
|
||||
if vconfig.get('ssl', 'letsencrypt') is not None:
|
||||
proto = 'https'
|
||||
else:
|
||||
proto = 'http'
|
||||
|
||||
domain = vconfig.get('domain', vname)
|
||||
|
||||
description.append(' {}: {}://{}{}'.format(
|
||||
vname,
|
||||
proto,
|
||||
domain,
|
||||
vconfig.get('website_check_path', '/'),
|
||||
))
|
||||
|
||||
if node.metadata.get('description', []):
|
||||
description.append('') # divider line
|
||||
|
||||
for line in node.metadata.get('description', []):
|
||||
description.append('# {}'.format(italic(line)))
|
||||
|
||||
if description:
|
||||
files['/etc/node.description'] = {
|
||||
'content': '\n'.join(description) + '\n',
|
||||
}
|
||||
else:
|
||||
files['/etc/node.description'] = {
|
||||
'delete': True,
|
||||
}
|
25
bundles/basic/metadata.py
Normal file
25
bundles/basic/metadata.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
defaults = {
|
||||
'bash_functions': {
|
||||
'h': 'cp /etc/htoprc.global ~/.htoprc; mkdir -p ~/.config/htop; cp /etc/htoprc.global ~/.config/htop/htoprc; htop',
|
||||
},
|
||||
'locale': {
|
||||
'default': 'en_US.UTF-8',
|
||||
'installed': {
|
||||
'de_DE.UTF-8',
|
||||
'en_US.UTF-8',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor.provides(
|
||||
'locale/installed',
|
||||
)
|
||||
def ensure_default_is_installed(metadata):
|
||||
return {
|
||||
'locale': {
|
||||
'installed': {
|
||||
metadata.get('locale/default'),
|
||||
},
|
||||
},
|
||||
}
|
5
bundles/letsencrypt/files/config
Normal file
5
bundles/letsencrypt/files/config
Normal file
|
@ -0,0 +1,5 @@
|
|||
CONFIG_D=/etc/dehydrated/conf.d
|
||||
BASEDIR=/var/lib/dehydrated
|
||||
WELLKNOWN="${BASEDIR}/acme-challenges"
|
||||
DOMAINS_TXT="/etc/dehydrated/domains.txt"
|
||||
HOOK="/etc/dehydrated/hook.sh"
|
3
bundles/letsencrypt/files/domains.txt
Normal file
3
bundles/letsencrypt/files/domains.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
% for domain, aliases in sorted(node.metadata.get('letsencrypt/domains', {}).items()):
|
||||
${domain} ${' '.join(sorted(aliases))}
|
||||
% endfor
|
37
bundles/letsencrypt/files/hook.sh
Normal file
37
bundles/letsencrypt/files/hook.sh
Normal file
|
@ -0,0 +1,37 @@
|
|||
deploy_cert() {<%text>
|
||||
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"</%text>
|
||||
% for service, config in node.metadata.get('letsencrypt/concat_and_deploy', {}).items():
|
||||
|
||||
# concat_and_deploy ${service}
|
||||
if [ "$DOMAIN" = "${config['match_domain']}" ]; then
|
||||
cat $KEYFILE > ${config['target']}
|
||||
cat $FULLCHAINFILE >> ${config['target']}
|
||||
% if 'chown' in config:
|
||||
chown ${config['chown']} ${config['target']}
|
||||
% endif
|
||||
% if 'chmod' in config:
|
||||
chmod ${config['chmod']} ${config['target']}
|
||||
% endif
|
||||
% if 'commands' in config:
|
||||
% for command in config['commands']:
|
||||
${command}
|
||||
% endfor
|
||||
% endif
|
||||
fi
|
||||
% endfor
|
||||
}
|
||||
|
||||
|
||||
exit_hook() {<%text>
|
||||
local ERROR="${1:-}"</%text>
|
||||
|
||||
% for service in sorted(node.metadata.get('letsencrypt/reload_after', set())):
|
||||
systemctl reload-or-restart ${service}
|
||||
% endfor
|
||||
}
|
||||
|
||||
<%text>
|
||||
HANDLER="$1"; shift
|
||||
if [[ "${HANDLER}" =~ ^(deploy_cert|exit_hook)$ ]]; then
|
||||
"$HANDLER" "$@"
|
||||
fi</%text>
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
domain=$1
|
||||
just_check=$2
|
||||
|
||||
cert_path="/var/lib/dehydrated/certs/$domain"
|
||||
|
||||
already_exists=false
|
||||
if [ -f "$cert_path/privkey.pem" -a -f "$cert_path/fullchain.pem" -a -f "$cert_path/chain.pem" ]
|
||||
then
|
||||
already_exists=true
|
||||
fi
|
||||
|
||||
if [ "$just_check" = true ]
|
||||
then
|
||||
if [ "$already_exists" = true ]
|
||||
then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$already_exists" != true ]
|
||||
then
|
||||
rm -r "$cert_path"
|
||||
mkdir -p "$cert_path"
|
||||
openssl req -x509 -newkey rsa:4096 -nodes -days 1 -subj "/CN=$domain" -keyout "$cert_path/privkey.pem" -out "$cert_path/fullchain.pem"
|
||||
chmod 0600 "$cert_path/privkey.pem"
|
||||
cp "$cert_path/fullchain.pem" "$cert_path/chain.pem"
|
||||
fi
|
52
bundles/letsencrypt/items.py
Normal file
52
bundles/letsencrypt/items.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
repo.libs.tools.require_bundle(node, 'nginx', 'letsencrypt bundle needs nginx for http challenge')
|
||||
|
||||
directories = {
|
||||
'/etc/dehydrated/conf.d': {},
|
||||
'/var/lib/dehydrated/acme-challenges': {},
|
||||
}
|
||||
|
||||
actions = {
|
||||
'letsencrypt_update_certificates': {
|
||||
'command': 'dehydrated --cron --accept-terms --challenge http-01',
|
||||
'triggered': True,
|
||||
'needs': {
|
||||
'svc_systemd:nginx',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for domain, _ in node.metadata.get('letsencrypt/domains').items():
|
||||
actions['letsencrypt_ensure-some-certificate_{}'.format(domain)] = {
|
||||
'command': '/etc/dehydrated/letsencrypt-ensure-some-certificate {}'.format(domain),
|
||||
'unless': '/etc/dehydrated/letsencrypt-ensure-some-certificate {} true'.format(domain),
|
||||
'needs': {
|
||||
'file:/etc/dehydrated/letsencrypt-ensure-some-certificate',
|
||||
},
|
||||
'needed_by': {
|
||||
'svc_systemd:nginx',
|
||||
},
|
||||
'triggers': {
|
||||
'action:letsencrypt_update_certificates',
|
||||
},
|
||||
}
|
||||
|
||||
files = {
|
||||
'/etc/dehydrated/domains.txt': {
|
||||
'content_type': 'mako',
|
||||
'triggers': {
|
||||
'action:letsencrypt_update_certificates',
|
||||
},
|
||||
},
|
||||
'/etc/dehydrated/config': {
|
||||
'triggers': {
|
||||
'action:letsencrypt_update_certificates',
|
||||
},
|
||||
},
|
||||
'/etc/dehydrated/hook.sh': {
|
||||
'content_type': 'mako',
|
||||
'mode': '0755',
|
||||
},
|
||||
'/etc/dehydrated/letsencrypt-ensure-some-certificate': {
|
||||
'mode': '0755',
|
||||
},
|
||||
}
|
24
bundles/letsencrypt/metadata.py
Normal file
24
bundles/letsencrypt/metadata.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
defaults = {
|
||||
'apt': {
|
||||
'packages': {
|
||||
'dehydrated': {
|
||||
'needed_by': {
|
||||
'action:letsencrypt_update_certificates',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'cron': {
|
||||
'letsencrypt_renew': '{} 4 * * * root /usr/bin/dehydrated --cron --accept-terms --challenge http-01 > /dev/null'.format((node.magic_number % 60)),
|
||||
'letsencrypt_cleanup': '{} 4 * * 0 root /usr/bin/dehydrated --cleanup > /dev/null'.format((node.magic_number % 60)),
|
||||
},
|
||||
'pacman': {
|
||||
'packages': {
|
||||
'dehydrated': {
|
||||
'needed_by': {
|
||||
'action:letsencrypt_update_certificates',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
47
bundles/nftables/files/nftables.conf
Normal file
47
bundles/nftables/files/nftables.conf
Normal file
|
@ -0,0 +1,47 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0
|
||||
policy drop
|
||||
|
||||
tcp flags syn tcp option maxseg size 1-500 drop
|
||||
|
||||
ct state { established, related } accept
|
||||
ct state invalid drop
|
||||
|
||||
iif lo accept
|
||||
|
||||
icmp type timestamp-request drop
|
||||
icmp type timestamp-reply drop
|
||||
ip protocol icmp accept
|
||||
|
||||
ip6 nexthdr ipv6-icmp accept
|
||||
}
|
||||
|
||||
chain output {
|
||||
type filter hook output priority 0
|
||||
policy accept
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority 0
|
||||
policy drop
|
||||
|
||||
icmp type timestamp-request drop
|
||||
icmp type timestamp-reply drop
|
||||
}
|
||||
}
|
||||
|
||||
table nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority -100
|
||||
}
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 100
|
||||
}
|
||||
}
|
||||
|
||||
include "/etc/nftables-rules.d/*-*"
|
10
bundles/nftables/files/override.conf
Normal file
10
bundles/nftables/files/override.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Service]
|
||||
RemainAfterExit=yes
|
||||
|
||||
ExecStart=
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
|
||||
ExecStart=/usr/local/sbin/apply-sysctl
|
||||
|
||||
ExecReload=
|
||||
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
|
||||
ExecReload=/usr/local/sbin/apply-sysctl
|
3
bundles/nftables/files/rules-template
Normal file
3
bundles/nftables/files/rules-template
Normal file
|
@ -0,0 +1,3 @@
|
|||
% for rule in rules:
|
||||
add rule ${rule}
|
||||
% endfor
|
56
bundles/nftables/items.py
Normal file
56
bundles/nftables/items.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
if node.has_bundle('pacman'):
|
||||
package = 'pkg_pacman:nftables'
|
||||
else:
|
||||
package = 'pkg_apt:nftables'
|
||||
|
||||
directories = {
|
||||
# used by other bundles
|
||||
'/etc/nftables-rules.d': {
|
||||
'purge': True,
|
||||
'triggers': {
|
||||
'svc_systemd:nftables:reload',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
files = {
|
||||
'/etc/nftables.conf': {
|
||||
'needs': {
|
||||
'directory:/etc/nftables-rules.d',
|
||||
},
|
||||
'triggers': {
|
||||
'svc_systemd:nftables:reload',
|
||||
},
|
||||
},
|
||||
'/etc/systemd/system/nftables.service.d/bundlewrap.conf': {
|
||||
'source': 'override.conf',
|
||||
'triggers': {
|
||||
'action:systemd-reload',
|
||||
'svc_systemd:nftables:reload',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for ruleset, rules in node.metadata.get('nftables/rules', {}).items():
|
||||
files[f'/etc/nftables-rules.d/{ruleset}'] = {
|
||||
'source': 'rules-template',
|
||||
'content_type': 'mako',
|
||||
'context': {
|
||||
'rules': rules,
|
||||
},
|
||||
'needed_by': {
|
||||
'svc_systemd:nftables',
|
||||
},
|
||||
'triggers': {
|
||||
'svc_systemd:nftables:reload',
|
||||
},
|
||||
}
|
||||
|
||||
svc_systemd = {
|
||||
'nftables': {
|
||||
'needs': {
|
||||
'file:/etc/nftables.conf',
|
||||
package,
|
||||
},
|
||||
},
|
||||
}
|
96
bundles/nftables/metadata.py
Normal file
96
bundles/nftables/metadata.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
from bundlewrap.exceptions import BundleError
|
||||
|
||||
defaults = {
|
||||
'apt': {
|
||||
'packages': {
|
||||
'nftables': {},
|
||||
},
|
||||
},
|
||||
'pacman': {
|
||||
'packages': {
|
||||
'nftables': {},
|
||||
# https://github.com/bundlewrap/bundlewrap/issues/688
|
||||
# 'iptables': {
|
||||
# 'installed': False,
|
||||
# 'needed_by': {
|
||||
# 'pkg_pacman:iptables-nft',
|
||||
# },
|
||||
# },
|
||||
'iptables-nft': {
|
||||
'needed_by': {
|
||||
'pkg_pacman:nftables',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if not node.has_bundle('vmhost'):
|
||||
# see comment in bundles/vmhost/items.py
|
||||
defaults['apt']['packages']['iptables'] = {
|
||||
'installed': False,
|
||||
'needed_by': {
|
||||
'pkg_apt:nftables',
|
||||
},
|
||||
}
|
||||
|
||||
@metadata_reactor.provides(
|
||||
'nftables/rules/99-port_rules',
|
||||
)
|
||||
def port_rules_to_nftables(metadata):
|
||||
# Using this, bundles can simply set up port based rules. This
|
||||
# reactor will then take care of converting those rules to actual
|
||||
# nftables rules
|
||||
ruleset = set()
|
||||
|
||||
# Plese note we do not set any defaults for ports. Bundles are
|
||||
# expected to know themselves which default to use.
|
||||
for portdef, targets in metadata.get('firewall/port_rules', {}).items():
|
||||
if '/' in portdef:
|
||||
port, proto = portdef.split('/', 2)
|
||||
|
||||
if proto not in {'udp'}:
|
||||
raise BundleError(f'firewall/port_rules: illegal identifier {portdef} in metadata for {node.name}')
|
||||
else:
|
||||
port = portdef
|
||||
proto = 'tcp'
|
||||
|
||||
for target in targets:
|
||||
if port == '*' and target == '*':
|
||||
raise BundleError('firewall/port_rules: setting both port and target to * is unsupported')
|
||||
|
||||
comment = f'comment "port_rules {target}"'
|
||||
|
||||
if port != '*':
|
||||
if ':' in port:
|
||||
parts = port.split(':')
|
||||
port_str = f'{proto} dport {{ {parts[0]}-{parts[1]} }}'
|
||||
else:
|
||||
port_str = f'{proto} dport {port}'
|
||||
else:
|
||||
port_str = f'meta l4proto {proto}'
|
||||
|
||||
if target in ('ipv4', 'ipv6'):
|
||||
version_str = f'meta nfproto {target}'
|
||||
else:
|
||||
version_str = ''
|
||||
|
||||
if target in ('*', 'ipv4', 'ipv6'):
|
||||
ruleset.add(f'inet filter input {version_str} {port_str} accept {comment}')
|
||||
else:
|
||||
resolved = repo.libs.tools.resolve_identifier(repo, target)
|
||||
|
||||
for address in resolved['ipv4']:
|
||||
ruleset.add(f'inet filter input meta nfproto ipv4 {port_str} ip saddr {address} accept {comment}')
|
||||
|
||||
for address in resolved['ipv6']:
|
||||
ruleset.add(f'inet filter input meta nfproto ipv6 {port_str} ip6 saddr {address} accept {comment}')
|
||||
|
||||
return {
|
||||
'nftables': {
|
||||
'rules': {
|
||||
# order does not matter here.
|
||||
'99-port_rules': sorted(ruleset),
|
||||
},
|
||||
},
|
||||
}
|
9
bundles/nginx/files/arch-override.conf
Normal file
9
bundles/nginx/files/arch-override.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
|
||||
|
||||
ExecReload=
|
||||
ExecReload=/bin/sh -c "/bin/kill -s HUP $(/bin/cat /var/run/nginx.pid)"
|
||||
|
||||
ExecStop=
|
||||
ExecStop=/bin/sh -c "/bin/kill -s TERM $(/bin/cat /var/run/nginx.pid)"
|
475
bundles/nginx/files/check_nginx_status
Normal file
475
bundles/nginx/files/check_nginx_status
Normal file
|
@ -0,0 +1,475 @@
|
|||
#!/usr/bin/env perl
|
||||
# editorconfig-checker-disable-file
|
||||
# check_nginx_status.pl
|
||||
# Author : regis.leroy at makina-corpus.com
|
||||
# Licence : GPL - http://www.fsf.org/licenses/gpl.txt
|
||||
#
|
||||
# help : ./check_nginx_status.pl -h
|
||||
#
|
||||
# issues & updates: http://github.com/regilero/check_nginx_status
|
||||
use warnings;
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use LWP::UserAgent;
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
use Digest::MD5 qw(md5 md5_hex);
|
||||
use FindBin;
|
||||
|
||||
# ensure all outputs are in UTF-8
|
||||
binmode(STDOUT, ":utf8");
|
||||
|
||||
# Nagios specific
|
||||
use lib "/usr/lib/nagios/plugins";
|
||||
use utils qw($TIMEOUT);
|
||||
|
||||
# Globals
|
||||
my $Version='0.20';
|
||||
my $Name=$0;
|
||||
|
||||
my $o_host = undef; # hostname
|
||||
my $o_help= undef; # want some help ?
|
||||
my $o_port= undef; # port
|
||||
my $o_url = undef; # url to use, if not the default
|
||||
my $o_user= undef; # user for auth
|
||||
my $o_pass= ''; # password for auth
|
||||
my $o_realm= ''; # password for auth
|
||||
my $o_version= undef; # print version
|
||||
my $o_warn_a_level= -1; # Number of active connections that will cause a warning
|
||||
my $o_crit_a_level= -1; # Number of active connections that will cause an error
|
||||
my $o_warn_rps_level= -1; # Number of Request per second that will cause a warning
|
||||
my $o_crit_rps_level= -1; # Number of request Per second that will cause an error
|
||||
my $o_warn_cps_level= -1; # Number of Connections per second that will cause a warning
|
||||
my $o_crit_cps_level= -1; # Number of Connections per second that will cause an error
|
||||
my $o_timeout= 15; # Default 15s Timeout
|
||||
my $o_warn_thresold= undef; # warning thresolds entry
|
||||
my $o_crit_thresold= undef; # critical thresolds entry
|
||||
my $o_debug= undef; # debug mode
|
||||
my $o_servername= undef; # ServerName (host header in http request)
|
||||
my $o_https= undef; # SSL (HTTPS) mode
|
||||
my $o_disable_sslverifyhostname = 0;
|
||||
|
||||
my $TempPath = '/tmp/'; # temp path
|
||||
my $MaxTimeDif = 60*30; # Maximum uptime difference (seconds), default 30 minutes
|
||||
|
||||
my $nginx = 'NGINX'; # Could be used to store version also
|
||||
|
||||
# functions
|
||||
sub show_versioninfo { print "$Name version : $Version\n"; }
|
||||
|
||||
sub print_usage {
|
||||
print "Usage: $Name -H <host ip> [-p <port>] [-s servername] [-t <timeout>] [-w <WARN_THRESOLD> -c <CRIT_THRESOLD>] [-V] [-d] [-u <url>] [-U user -P pass -r realm]\n";
|
||||
}
|
||||
sub nagios_exit {
|
||||
my ( $nickname, $status, $message, $perfdata , $silent) = @_;
|
||||
my %STATUSCODE = (
|
||||
'OK' => 0
|
||||
, 'WARNING' => 1
|
||||
, 'CRITICAL' => 2
|
||||
, 'UNKNOWN' => 3
|
||||
, 'PENDING' => 4
|
||||
);
|
||||
if(!defined($silent)) {
|
||||
my $output = undef;
|
||||
$output .= sprintf('%1$s %2$s - %3$s', $nickname, $status, $message);
|
||||
if ($perfdata) {
|
||||
$output .= sprintf('|%1$s', $perfdata);
|
||||
}
|
||||
$output .= chr(10);
|
||||
print $output;
|
||||
}
|
||||
exit $STATUSCODE{$status};
|
||||
}
|
||||
|
||||
# Get the alarm signal
|
||||
$SIG{'ALRM'} = sub {
|
||||
nagios_exit($nginx,"CRITICAL","ERROR: Alarm signal (Nagios timeout)");
|
||||
};
|
||||
|
||||
sub help {
|
||||
print "Nginx Monitor for Nagios version ",$Version,"\n";
|
||||
print "GPL licence, (c)2012 Leroy Regis\n\n";
|
||||
print_usage();
|
||||
print <<EOT;
|
||||
-h, --help
|
||||
print this help message
|
||||
-H, --hostname=HOST
|
||||
name or IP address of host to check
|
||||
-p, --port=PORT
|
||||
Http port
|
||||
-u, --url=URL
|
||||
Specific URL to use, instead of the default "http://<hostname or IP>/nginx_status"
|
||||
-s, --servername=SERVERNAME
|
||||
ServerName, (host header of HTTP request) use it if you specified an IP in -H to match the good Virtualhost in your target
|
||||
-S, --ssl
|
||||
Wether we should use HTTPS instead of HTTP
|
||||
--disable-sslverifyhostname
|
||||
Disable SSL hostname verification
|
||||
-U, --user=user
|
||||
Username for basic auth
|
||||
-P, --pass=PASS
|
||||
Password for basic auth
|
||||
-r, --realm=REALM
|
||||
Realm for basic auth
|
||||
-d, --debug
|
||||
Debug mode (show http request response)
|
||||
-m, --maxreach=MAX
|
||||
Number of max processes reached (since last check) that should trigger an alert
|
||||
-t, --timeout=INTEGER
|
||||
timeout in seconds (Default: $o_timeout)
|
||||
-w, --warn=ACTIVE_CONN,REQ_PER_SEC,CONN_PER_SEC
|
||||
number of active connections, ReqPerSec or ConnPerSec that will cause a WARNING
|
||||
-1 for no warning
|
||||
-c, --critical=ACTIVE_CONN,REQ_PER_SEC,CONN_PER_SEC
|
||||
number of active connections, ReqPerSec or ConnPerSec that will cause a CRITICAL
|
||||
-1 for no CRITICAL
|
||||
-V, --version
|
||||
prints version number
|
||||
|
||||
Note :
|
||||
3 items can be managed on this check, this is why -w and -c parameters are using 3 values thresolds
|
||||
- ACTIVE_CONN: Number of all opened connections, including connections to backends
|
||||
- REQ_PER_SEC: Average number of request per second between this check and the previous one
|
||||
- CONN_PER_SEC: Average number of connections per second between this check and the previous one
|
||||
|
||||
Examples:
|
||||
|
||||
This one will generate WARNING and CRITICIAL alerts if you reach 10 000 or 20 000 active connection; or
|
||||
100 or 200 request per second; or 200 or 300 connections per second
|
||||
check_nginx_status.pl -H 10.0.0.10 -u /foo/nginx_status -s mydomain.example.com -t 8 -w 10000,100,200 -c 20000,200,300
|
||||
|
||||
this will generate WARNING and CRITICAL alerts only on the number of active connections (with low numbers for nginx)
|
||||
check_nginx_status.pl -H 10.0.0.10 -s mydomain.example.com -t 8 -w 10,-1,-1 -c 20,-1,-1
|
||||
|
||||
theses two equivalents will not generate any alert (if the nginx_status page is reachable) but could be used for graphics
|
||||
check_nginx_status.pl -H 10.0.0.10 -s mydomain.example.com -w -1,-1,-1 -c -1,-1,-1
|
||||
check_nginx_status.pl -H 10.0.0.10 -s mydomain.example.com
|
||||
|
||||
EOT
|
||||
}
|
||||
|
||||
sub check_options {
|
||||
Getopt::Long::Configure ("bundling");
|
||||
GetOptions(
|
||||
'h' => \$o_help, 'help' => \$o_help,
|
||||
'd' => \$o_debug, 'debug' => \$o_debug,
|
||||
'H:s' => \$o_host, 'hostname:s' => \$o_host,
|
||||
's:s' => \$o_servername, 'servername:s' => \$o_servername,
|
||||
'S:s' => \$o_https, 'ssl:s' => \$o_https,
|
||||
'u:s' => \$o_url, 'url:s' => \$o_url,
|
||||
'U:s' => \$o_user, 'user:s' => \$o_user,
|
||||
'P:s' => \$o_pass, 'pass:s' => \$o_pass,
|
||||
'r:s' => \$o_realm, 'realm:s' => \$o_realm,
|
||||
'p:i' => \$o_port, 'port:i' => \$o_port,
|
||||
'V' => \$o_version, 'version' => \$o_version,
|
||||
'w:s' => \$o_warn_thresold,'warn:s' => \$o_warn_thresold,
|
||||
'c:s' => \$o_crit_thresold,'critical:s' => \$o_crit_thresold,
|
||||
't:i' => \$o_timeout, 'timeout:i' => \$o_timeout,
|
||||
'disable-sslverifyhostname' => \$o_disable_sslverifyhostname,
|
||||
);
|
||||
|
||||
if (defined ($o_help)) {
|
||||
help();
|
||||
nagios_exit($nginx,"UNKNOWN","leaving","",1);
|
||||
}
|
||||
if (defined($o_version)) {
|
||||
show_versioninfo();
|
||||
nagios_exit($nginx,"UNKNOWN","leaving","",1);
|
||||
};
|
||||
|
||||
if (defined($o_warn_thresold)) {
|
||||
($o_warn_a_level,$o_warn_rps_level,$o_warn_cps_level) = split(',', $o_warn_thresold);
|
||||
}
|
||||
if (defined($o_crit_thresold)) {
|
||||
($o_crit_a_level,$o_crit_rps_level,$o_crit_cps_level) = split(',', $o_crit_thresold);
|
||||
}
|
||||
if (defined($o_debug)) {
|
||||
print("\nDebug thresolds: \nWarning: ($o_warn_thresold) => Active: $o_warn_a_level ReqPerSec :$o_warn_rps_level ConnPerSec: $o_warn_cps_level");
|
||||
print("\nCritical ($o_crit_thresold) => : Active: $o_crit_a_level ReqPerSec: $o_crit_rps_level ConnPerSec : $o_crit_cps_level\n");
|
||||
}
|
||||
if ((defined($o_warn_a_level) && defined($o_crit_a_level)) &&
|
||||
(($o_warn_a_level != -1) && ($o_crit_a_level != -1) && ($o_warn_a_level >= $o_crit_a_level)) ) {
|
||||
nagios_exit($nginx,"UNKNOWN","Check warning and critical values for Active Process (1st part of thresold), warning level must be < crit level!");
|
||||
}
|
||||
if ((defined($o_warn_rps_level) && defined($o_crit_rps_level)) &&
|
||||
(($o_warn_rps_level != -1) && ($o_crit_rps_level != -1) && ($o_warn_rps_level >= $o_crit_rps_level)) ) {
|
||||
nagios_exit($nginx,"UNKNOWN","Check warning and critical values for ReqPerSec (2nd part of thresold), warning level must be < crit level!");
|
||||
}
|
||||
if ((defined($o_warn_cps_level) && defined($o_crit_cps_level)) &&
|
||||
(($o_warn_cps_level != -1) && ($o_crit_cps_level != -1) && ($o_warn_cps_level >= $o_crit_cps_level)) ) {
|
||||
nagios_exit($nginx,"UNKNOWN","Check warning and critical values for ConnPerSec (3rd part of thresold), warning level must be < crit level!");
|
||||
}
|
||||
# Check compulsory attributes
|
||||
if (!defined($o_host)) {
|
||||
print_usage();
|
||||
nagios_exit($nginx,"UNKNOWN","-H host argument required");
|
||||
}
|
||||
}
|
||||
|
||||
########## MAIN ##########
|
||||
|
||||
check_options();
|
||||
|
||||
my $override_ip = $o_host;
|
||||
my $ua = LWP::UserAgent->new(
|
||||
protocols_allowed => ['http', 'https'],
|
||||
timeout => $o_timeout
|
||||
);
|
||||
|
||||
if ( $o_disable_sslverifyhostname ) {
|
||||
$ua->ssl_opts( 'verify_hostname' => 0 );
|
||||
}
|
||||
|
||||
# we need to enforce the HTTP request is made on the Nagios Host IP and
|
||||
# not on the DNS related IP for that domain
|
||||
@LWP::Protocol::http::EXTRA_SOCK_OPTS = ( PeerAddr => $override_ip );
|
||||
# this prevent used only once warning in -w mode
|
||||
my $ua_settings = @LWP::Protocol::http::EXTRA_SOCK_OPTS;
|
||||
|
||||
my $timing0 = [gettimeofday];
|
||||
my $response = undef;
|
||||
my $url = undef;
|
||||
|
||||
if (!defined($o_url)) {
|
||||
$o_url='/nginx_status';
|
||||
} else {
|
||||
# ensure we have a '/' as first char
|
||||
$o_url = '/'.$o_url unless $o_url =~ m(^/)
|
||||
}
|
||||
my $proto='http://';
|
||||
if(defined($o_https)) {
|
||||
$proto='https://';
|
||||
if (defined($o_port) && $o_port!=443) {
|
||||
if (defined ($o_debug)) {
|
||||
print "\nDEBUG: Notice: port is defined at $o_port and not 443, check you really want that in SSL mode! \n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defined($o_servername)) {
|
||||
if (!defined($o_port)) {
|
||||
$url = $proto . $o_servername . $o_url;
|
||||
} else {
|
||||
$url = $proto . $o_servername . ':' . $o_port . $o_url;
|
||||
}
|
||||
} else {
|
||||
if (!defined($o_port)) {
|
||||
$url = $proto . $o_host . $o_url;
|
||||
} else {
|
||||
$url = $proto . $o_host . ':' . $o_port . $o_url;
|
||||
}
|
||||
}
|
||||
if (defined ($o_debug)) {
|
||||
print "\nDEBUG: HTTP url: \n";
|
||||
print $url;
|
||||
}
|
||||
|
||||
my $req = HTTP::Request->new( GET => $url );
|
||||
|
||||
if (defined($o_servername)) {
|
||||
$req->header('Host' => $o_servername);
|
||||
}
|
||||
if (defined($o_user)) {
|
||||
$req->authorization_basic($o_user, $o_pass);
|
||||
}
|
||||
|
||||
if (defined ($o_debug)) {
|
||||
print "\nDEBUG: HTTP request: \n";
|
||||
print "IP used (better if it's an IP):" . $override_ip . "\n";
|
||||
print $req->as_string;
|
||||
}
|
||||
$response = $ua->request($req);
|
||||
my $timeelapsed = tv_interval ($timing0, [gettimeofday]);
|
||||
|
||||
my $InfoData = '';
|
||||
my $PerfData = '';
|
||||
#my @Time = (localtime); # list context and not scalar as we want the brutal timestamp
|
||||
my $Time = time;
|
||||
|
||||
my $webcontent = undef;
|
||||
if ($response->is_success) {
|
||||
$webcontent=$response->decoded_content;
|
||||
if (defined ($o_debug)) {
|
||||
print "\nDEBUG: HTTP response:";
|
||||
print $response->status_line;
|
||||
print "\n".$response->header('Content-Type');
|
||||
print "\n";
|
||||
print $webcontent;
|
||||
}
|
||||
if ($response->header('Content-Type') =~ m/text\/html/) {
|
||||
nagios_exit($nginx,"CRITICAL", "We have a response page for our request, but it's an HTML page, quite certainly not the status report of nginx");
|
||||
}
|
||||
# example of response content expected:
|
||||
#Active connections: 10
|
||||
#server accepts handled requests
|
||||
#38500 38500 50690
|
||||
#Reading: 5 Writing: 5 Waiting: 0
|
||||
|
||||
# number of all open connections including connections to backends
|
||||
my $ActiveConn = 0;
|
||||
if($webcontent =~ m/Active connections: (.*?)\n/) {
|
||||
$ActiveConn = $1;
|
||||
# triming
|
||||
$ActiveConn =~ s/^\s+|\s+$//g;
|
||||
}
|
||||
|
||||
|
||||
# 3 counters with a space: accepted conn, handled conn and number of requests
|
||||
my $counters = '';
|
||||
my $AcceptedConn = 0;
|
||||
my $HandledConn = 0;
|
||||
my $NbRequests = 0;
|
||||
if($webcontent =~ m/\nserver accepts handled requests\n(.*?)\n/) {
|
||||
$counters = $1;
|
||||
# triming
|
||||
$counters =~ s/^\s+|\s+$//g;
|
||||
#splitting
|
||||
($AcceptedConn,$HandledConn,$NbRequests) = split(' ', $counters);
|
||||
# triming
|
||||
$AcceptedConn =~ s/^\s+|\s+$//g;
|
||||
$HandledConn =~ s/^\s+|\s+$//g;
|
||||
$NbRequests =~ s/^\s+|\s+$//g;
|
||||
}
|
||||
|
||||
# nginx reads request header
|
||||
my $Reading = 0;
|
||||
# nginx reads request body, processes request, or writes response to a client
|
||||
my $Writing = 0;
|
||||
# keep-alive connections, actually it is active - (reading + writing)
|
||||
my $Waiting = 0;
|
||||
if($webcontent =~ m/Reading: (.*?)Writing: (.*?)Waiting: (.*?)$/) {
|
||||
$Reading = $1;
|
||||
$Writing = $2;
|
||||
$Waiting = $3;
|
||||
# triming
|
||||
$Reading =~ s/^\s+|\s+$//g;
|
||||
$Writing =~ s/^\s+|\s+$//g;
|
||||
$Waiting =~ s/^\s+|\s+$//g;
|
||||
}
|
||||
|
||||
# Debug
|
||||
if (defined ($o_debug)) {
|
||||
print ("\nDEBUG Parse results => Active :" . $ActiveConn . "\nAcceptedConn :" . $AcceptedConn . "\nHandledConn :" . $HandledConn . "\nNbRequests :".$NbRequests . "\nReading :" .$Reading . "\nWriting :" . $Writing . "\nWaiting :" . $Waiting . "\n");
|
||||
}
|
||||
|
||||
my $TempFile = $TempPath.$o_host.'_check_nginx_status'.md5_hex($url);
|
||||
my $FH;
|
||||
|
||||
my $LastTime = 0;
|
||||
my $LastAcceptedConn = 0;
|
||||
my $LastHandledConn = 0;
|
||||
my $LastNbRequests = 0;
|
||||
if ((-e $TempFile) && (-r $TempFile) && (-w $TempFile)) {
|
||||
open ($FH, '<',$TempFile) or nagios_exit($nginx,"UNKNOWN","unable to read temporary data from :".$TempFile);
|
||||
$LastTime = <$FH>;
|
||||
$LastAcceptedConn = <$FH>;
|
||||
$LastHandledConn = <$FH>;
|
||||
$LastNbRequests = <$FH>;
|
||||
close ($FH);
|
||||
if (defined ($o_debug)) {
|
||||
print ("\nDebug: data from temporary file: $TempFile\n");
|
||||
print (" LastTime: $LastTime LastAcceptedConn: $LastAcceptedConn LastHandledConn: $LastHandledConn LastNbRequests: $LastNbRequests \n");
|
||||
}
|
||||
}
|
||||
|
||||
open ($FH, '>'.$TempFile) or nagios_exit($nginx,"UNKNOWN","unable to write temporary data in :".$TempFile);
|
||||
#print $FH (@Time),"\n";
|
||||
print $FH "$Time\n";
|
||||
print $FH "$AcceptedConn\n";
|
||||
print $FH "$HandledConn\n";
|
||||
print $FH "$NbRequests\n";
|
||||
close ($FH);
|
||||
|
||||
my $ConnPerSec = 0;
|
||||
my $ReqPerSec = 0;
|
||||
my $RequestsNew = 0;
|
||||
# by default the average
|
||||
my $ReqPerConn = 0;
|
||||
if ($AcceptedConn > 0) {
|
||||
$ReqPerConn = $NbRequests/$AcceptedConn;
|
||||
}
|
||||
my $elapsed = $Time - $LastTime ;
|
||||
if (defined ($o_debug)) {
|
||||
print ("\nDebug: pre-computation\n");
|
||||
print ("Average ReqPerconn: $ReqPerConn, Seconds elapsed Since last check: $elapsed\n");
|
||||
}
|
||||
# check only if the counters may have been incremented
|
||||
# but not if it may have been too much incremented
|
||||
# if nginx was restarted ($NbRequests is now lower than previous value), just skip
|
||||
if ( ($elapsed < $MaxTimeDif) && ($elapsed != 0) && ($NbRequests >= $LastNbRequests) ) {
|
||||
$ConnPerSec = ($AcceptedConn-$LastAcceptedConn)/$elapsed;
|
||||
$RequestsNew = $NbRequests-$LastNbRequests;
|
||||
$ReqPerSec = $RequestsNew/$elapsed;
|
||||
# get finer value
|
||||
if ( $ConnPerSec!=0 ) {
|
||||
my $ReqPerConn = $ReqPerSec/$ConnPerSec;
|
||||
} else {
|
||||
my $ReqPerConn = 0;
|
||||
}
|
||||
}
|
||||
if (defined ($o_debug)) {
|
||||
print ("\nDebug: data computed\n");
|
||||
print ("ConnPerSec: $ConnPerSec ReqPerSec: $ReqPerSec ReqPerConn: $ReqPerConn\n");
|
||||
}
|
||||
$InfoData = sprintf (" %.3f sec. response time, Active: %d (Writing: %d Reading: %d Waiting: %d)"
|
||||
. " ReqPerSec: %.3f ConnPerSec: %.3f ReqPerConn: %.3f"
|
||||
,$timeelapsed,$ActiveConn,$Writing,$Reading,$Waiting,$ReqPerSec,$ConnPerSec,$ReqPerConn);
|
||||
|
||||
# Manage warn and crit values for the perfdata
|
||||
my $p_warn_a_level = "$o_warn_a_level";
|
||||
my $p_crit_a_level = "$o_crit_a_level";
|
||||
my $p_warn_rps_level = "$o_warn_rps_level";
|
||||
my $p_crit_rps_level = "$o_crit_rps_level";
|
||||
my $p_warn_cps_level = "$o_warn_cps_level";
|
||||
my $p_crit_cps_level = "$o_crit_cps_level";
|
||||
|
||||
if ($p_warn_a_level == "-1") {
|
||||
$p_warn_a_level = "";
|
||||
}
|
||||
if ($p_crit_a_level == "-1") {
|
||||
$p_crit_a_level = "";
|
||||
}
|
||||
if ($p_warn_rps_level == "-1") {
|
||||
$p_warn_rps_level = "";
|
||||
}
|
||||
if ($p_crit_rps_level == "-1") {
|
||||
$p_crit_rps_level = "";
|
||||
}
|
||||
if ($p_warn_cps_level == "-1") {
|
||||
$p_warn_cps_level = "";
|
||||
}
|
||||
if ($p_crit_cps_level == "-1") {
|
||||
$p_crit_cps_level = "";
|
||||
}
|
||||
|
||||
$PerfData = sprintf ("Writing=%d;;;; Reading=%d;;;; Waiting=%d;;;; Active=%d;%s;%s;; "
|
||||
. "ReqPerSec=%f;%s;%s;; ConnPerSec=%f;%s;%s;; ReqPerConn=%f;;;;"
|
||||
,($Writing),($Reading),($Waiting),($ActiveConn)
|
||||
,($p_warn_a_level),($p_crit_a_level)
|
||||
,($ReqPerSec),($p_warn_rps_level),($p_crit_rps_level)
|
||||
,($ConnPerSec),($p_warn_cps_level),($p_crit_cps_level)
|
||||
,($ReqPerConn));
|
||||
# first all critical exists by priority
|
||||
if (defined($o_crit_a_level) && (-1!=$o_crit_a_level) && ($ActiveConn >= $o_crit_a_level)) {
|
||||
nagios_exit($nginx,"CRITICAL", "Active Connections are critically high " . $InfoData,$PerfData);
|
||||
}
|
||||
if (defined($o_crit_rps_level) && (-1!=$o_crit_rps_level) && ($ReqPerSec >= $o_crit_rps_level)) {
|
||||
nagios_exit($nginx,"CRITICAL", "Request per second ratios is critically high " . $InfoData,$PerfData);
|
||||
}
|
||||
if (defined($o_crit_cps_level) && (-1!=$o_crit_cps_level) && ($ConnPerSec >= $o_crit_cps_level)) {
|
||||
nagios_exit($nginx,"CRITICAL", "Connection per second ratio is critically high " . $InfoData,$PerfData);
|
||||
}
|
||||
# Then WARNING exits by priority
|
||||
if (defined($o_warn_a_level) && (-1!=$o_warn_a_level) && ($ActiveConn >= $o_warn_a_level)) {
|
||||
nagios_exit($nginx,"WARNING", "Active Connections are high " . $InfoData,$PerfData);
|
||||
}
|
||||
if (defined($o_warn_rps_level) && (-1!=$o_warn_rps_level) && ($ReqPerSec >= $o_warn_rps_level)) {
|
||||
nagios_exit($nginx,"WARNING", "Requests per second ratio is high " . $InfoData,$PerfData);
|
||||
}
|
||||
if (defined($o_warn_cps_level) && (-1!=$o_warn_cps_level) && ($ConnPerSec >= $o_warn_cps_level)) {
|
||||
nagios_exit($nginx,"WARNING", "Connection per second ratio is high " . $InfoData,$PerfData);
|
||||
}
|
||||
|
||||
nagios_exit($nginx,"OK",$InfoData,$PerfData);
|
||||
|
||||
} else {
|
||||
nagios_exit($nginx,"CRITICAL", $response->status_line);
|
||||
}
|
26
bundles/nginx/files/fastcgi.conf
Normal file
26
bundles/nginx/files/fastcgi.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
fastcgi_param QUERY_STRING $query_string;
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param CONTENT_TYPE $content_type;
|
||||
fastcgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param DOCUMENT_URI $document_uri;
|
||||
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||
fastcgi_param REQUEST_SCHEME $scheme;
|
||||
fastcgi_param HTTPS $https if_not_empty;
|
||||
|
||||
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
|
||||
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||
fastcgi_param REMOTE_PORT $remote_port;
|
||||
fastcgi_param SERVER_ADDR $server_addr;
|
||||
fastcgi_param SERVER_PORT $server_port;
|
||||
fastcgi_param SERVER_NAME $server_name;
|
||||
|
||||
fastcgi_param REDIRECT_STATUS 200;
|
||||
|
||||
# This is the only thing that's different to the debian default.
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
28
bundles/nginx/files/logrotate.conf
Normal file
28
bundles/nginx/files/logrotate.conf
Normal file
|
@ -0,0 +1,28 @@
|
|||
/var/log/nginx/*.log {
|
||||
compress
|
||||
copytruncate
|
||||
create 0640 www-data adm
|
||||
daily
|
||||
dateext
|
||||
missingok
|
||||
notifempty
|
||||
rotate ${node.metadata.get('nginx/log_retention_days', 7)}
|
||||
sharedscripts
|
||||
prerotate
|
||||
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
|
||||
run-parts /etc/logrotate.d/httpd-prerotate; \
|
||||
fi
|
||||
endscript
|
||||
}
|
||||
|
||||
/var/log/nginx-timing/*.log {
|
||||
compress
|
||||
copytruncate
|
||||
create 0644 www-data adm
|
||||
dateext
|
||||
missingok
|
||||
notifempty
|
||||
rotate 3
|
||||
sharedscripts
|
||||
size 1M
|
||||
}
|
61
bundles/nginx/files/nginx.conf
Normal file
61
bundles/nginx/files/nginx.conf
Normal file
|
@ -0,0 +1,61 @@
|
|||
user ${username};
|
||||
worker_processes ${worker_processes};
|
||||