diff --git a/nagios/check_forgejo_version.sh b/nagios/check_forgejo_version.sh new file mode 100755 index 0000000..d9d1515 --- /dev/null +++ b/nagios/check_forgejo_version.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +# Little monitoring script to check that the version of a +# Forgejo instance is up to date. +# +# Licence: WTFPL +# Copyright: chl-dev@bugness.org 2023 + +PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' ) +REVISION="0.1" + +# Default values +DNS_RECORD_RELEASES="release.forgejo.org" +RELEASE_MEDIUM="dns" +URL_RSS_RELEASES="https://codeberg.org/forgejo/forgejo/releases.rss" +URL_LOCAL_HOSTNAME="localhost:3000" +URL_LOCAL_VERSION_TEMPLATE="%s://%s/api/v1/version" +URL_LOCAL_PROTOCOL="http" + +# Include check_range() +# Not needed at the moment +#. $PROGPATH/utils.sh +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# Stop at first uncaught error +set -e + +# Wrapper to use whatever is available to make HTTP queries +fetch_with_curl_wget_or_whatever () { + if which "wget" >/dev/null 2>&1; then + wget -q -O - "$1" + elif which "curl" >/dev/null 2>&1 ; then + curl -s "$1" + else + echo "UNKNOWN: no wget/curl/whatever available to make HTTP queries." + exit $STATE_UNKNOWN + fi +} + +# Wrapper to use whatever is available to make DNS queries +fetch_with_dig_or_whatever () { + if which "dig" >/dev/null 2>&1; then + dig +short "$1" "$2" + elif which "nslookup" >/dev/null 2>&1; then + nslookup -query="$2" "$1" | sed -n "/$1/p" + else + echo "UNKNOWN: no dig/nslookup/whatever available to make DNS queries." + exit $STATE_UNKNOWN + fi +} + +# Sort the multiple contents and print the last one. +# ex: 1.21.1-5 +# Note: Gitea has some spurious v1.22-dev/-rc releases, we ignore them. +get_latest_version_number_from_rss () { + fetch_with_curl_wget_or_whatever "$1" | while rdom; do + # In RSS streams, we only want the <title> in <item> + # In Atom streams, it's <entry> -> <title> + case "$E" in + "item"|"entry") INSIDE_ITEM=1;; + "/item"|"/entry") INSIDE_ITEM="";; + esac + + if [ -n "$INSIDE_ITEM" ] && [ "$E" == "title" ]; then + printf "%s\n" "$C" + fi + done | sed -e 's/^v//' -e '/-dev/d' -e '/-rc/d' | sort --version-sort | tail -n 1 +} + +# Sort the multiple "forgejo_versions=x,y,z" and print the last one. +# ex: 1.21.1-5 +get_latest_version_number_from_dns () { + fetch_with_dig_or_whatever "$1" "TXT" | \ + sed -e 's/.*=\(.*\)".*/\1/' -e 's/,/\n/g' | \ + sort --version-sort | \ + tail -n 1 +} + +print_url_local_version () { + printf "$URL_LOCAL_VERSION_TEMPLATE" "$URL_LOCAL_PROTOCOL" "$URL_LOCAL_HOSTNAME" +} + +# Quick 'n dirty function to parse XML without having to install +# a real parser (not necessary at the moment here, but may be +# needed in the future) +# The tag will be stored in $E and the content in $C +# note: this function is not POSIX-compliant (needs bash) +rdom () { + local IFS=\> + read -d \< E C +} + +# Help function +usage() { + cat <<EOF +Usage : + $0 [-H hostname] [-u local_URL] [-sS] [-D] [-d release_dns_record] [-R] [-r release_rss_URL] + + -D Use the DNS to determine the last release available (default) + -R Use the RSS to determine the last release available + -s Use HTTP to query the hostname (default) + -S Use HTTPS to query the hostname + +Example : + ./check_forgejo_version.sh -H forge.example.net + (seems to somewhat work for Gitea too :) + ./check_forgejo_version.sh -R -r https://github.com/go-gitea/gitea/releases.atom + +Default values: + local_URL: $( print_url_local_version ) + release_dns_record: $DNS_RECORD_RELEASES + release_rss_URL: $URL_RSS_RELEASES +EOF +} + +# Loop on parameters +while getopts hDH:r:RsSu: f; do + case "$f" in + 'h') + usage + exit + ;; + + 'D') + RELEASE_MEDIUM="dns" + ;; + + 'H') + URL_LOCAL_HOSTNAME="$OPTARG" + ;; + + 'r') + URL_RSS_RELEASES="$OPTARG" + ;; + + 'R') + RELEASE_MEDIUM="rss" + ;; + + 's') + URL_LOCAL_PROTOCOL="http" + ;; + + 'S') + URL_LOCAL_PROTOCOL="https" + ;; + + 'u') + URL_LOCAL_VERSION="$OPTARG" + ;; + + ?) + usage + exit 1 + ;; + esac +done + +# If the local URL hasn't been overridden, let's compute it. +if [ -z "$URL_LOCAL_VERSION" ]; then + URL_LOCAL_VERSION="$( print_url_local_version )" +fi + +# Query the upstream and local versions... +case "$RELEASE_MEDIUM" in + 'rss') UPSTREAM_VERSION="$( get_latest_version_number_from_rss "$URL_RSS_RELEASES" )" ;; + 'dns') UPSTREAM_VERSION="$( get_latest_version_number_from_dns "$DNS_RECORD_RELEASES" )" ;; +esac +# note : the API seems to switch 1.21.1-0 to 1.21.1+0. We dumbly switch back ? +LOCAL_VERSION="$( fetch_with_curl_wget_or_whatever "$URL_LOCAL_VERSION" | sed -n 's/{"version":"\([^"]\+\)"}/\1/p' | sed 's/+/-/' )" + +# ...and check if they match. +if [ "$UPSTREAM_VERSION" = "$LOCAL_VERSION" ]; then + echo "OK ($LOCAL_VERSION / $UPSTREAM_VERSION)" + exit $STATE_OK +else + # we are not yet able to distinguish security releases from simple upgrades + # so, to avoid alert fatigue, we chose to stay at "warning". + echo "WARNING ($LOCAL_VERSION / $UPSTREAM_VERSION)" + exit $STATE_WARNING +fi