#!/bin/bash # Exit codes : # - 30 : $skip_releases or $skip_packages is set to "all" # - 40 : current release is in $skip_releases list # - 50 : all upgradable packages are in the $skip_packages list # - 60 : current release is not in the $r_releases list # - 70 : at least an upgradable package is not in the $r_packages list set -e configFile="/etc/evolinux/listupgrade.cnf" packages=$(mktemp --tmpdir=/tmp evoupdate.XXX) packagesHold=$(mktemp --tmpdir=/tmp evoupdate.XXX) servicesToRestart=$(mktemp --tmpdir=/tmp evoupdate.XXX) template=$(mktemp --tmpdir=/tmp evoupdate.XXX) clientmail=$(grep EVOMAINTMAIL /etc/evomaintenance.cf | cut -d'=' -f2) mailto="${clientmail}" date="Ce jeudi entre 18h00 et 23h00." hostname=$(grep HOSTNAME /etc/evomaintenance.cf | cut -d'=' -f2) hostname=${hostname%%.evolix.net} # If hostname is composed with -, remove the first part. if [[ "${hostname}" =~ "-" ]]; then hostname=$(echo "${hostname}" | cut -d'-' -f2-) fi # Edit $configFile to override some variables. # shellcheck disable=SC1090,SC1091 [ -r "${configFile}" ] && . "${configFile}" # Remove temporary files on exit. # shellcheck disable=SC2064 trap "rm ${packages} ${packagesHold} ${servicesToRestart} ${template}" EXIT # Parse line in retrieved upgrade file and ensure there is no malicious values. get_value() { file="$1" variable="$2" value="$(grep "^${variable}:" "${file}" | head -n 1 | cut -d ':' -f 2 | sed 's/^ //')" if echo "${value}" | grep -q -E '^[-.: [:alnum:]]*$'; then echo "${value}" else printf >&2 "Error parsing value \"%s\" for variable %s.\n" "${value}" "${variable}" fi } # Fetch which packages/releases will be upgraded. fetch_upgrade_info() { upgradeInfo=$(mktemp --tmpdir=/tmp evoupdate.XXX) wget --no-check-certificate --quiet --output-document="${upgradeInfo}" https://upgrades.evolix.org/upgrade # shellcheck disable=SC2181 if [ "$?" != "0" ]; then printf >&2 "Error fetching upgrade directives.\n" fi r_releases="$(get_value "${upgradeInfo}" "releases")" r_skip_releases="$(get_value "${upgradeInfo}" "skip_releases")" r_packages="$(get_value "${upgradeInfo}" "packages")" r_skip_packages="$(get_value "${upgradeInfo}" "skip_packages")" rm "${upgradeInfo}" } # Check if element $element is in (space separated) list $list. is_in() { list="$1" element="$2" for i in ${list}; do if [ "${element}" = "${i}" ]; then return 0 fi done return 1 } if [[ "$1" != "--cron" ]]; then echo "À quel date/heure allez vous planifier l'envoi ?" echo "Exemple : le jeudi 6 mars entre 18h00 et 23h00" echo -n "> " read -r date echo "À qui envoyer le mail ?" echo -n "> " read -r mailto fi # Update APT cache and get packages to upgrade and packages on hold. aptUpdateOutput=$(apt update 2>&1 | (grep -E -ve '^(Listing|WARNING|$)' -e upgraded -e 'up to date' || true )) if echo "${aptUpdateOutput}" | grep -E "^Err(:[0-9]+)? http"; then echo "FATAL - Not able to fetch all sources (probably a pesky (mini)firewall). Please, fix me" exit 100 fi apt-mark showhold > "${packagesHold}" apt list --upgradable 2>&1 | grep -v -f "${packagesHold}" | grep -Ev '^(Listing|WARNING|$)' > "${packages}" packagesParsable=$(cut -f 1 -d / < "${packages}" |tr '\n' ' ') # No updates? Exit! test ! -s "${packages}" && exit 0 test ! -s "${packagesHold}" && echo 'Aucun' > "${packagesHold}" fetch_upgrade_info local_release=$(cut -f 1 -d . > "${servicesToRestart}" elif echo "${pkg}" | grep -q "^nginx"; then echo "Nginx" >> "${servicesToRestart}" elif echo "${pkg}" | grep -q "^php5-fpm"; then echo "PHP FPM" >> "${servicesToRestart}" elif echo "${pkg}" | grep -q "^mysql-server"; then echo "MySQL" >> "${servicesToRestart}" elif echo "${pkg}" | grep -q "^mariadb-server"; then echo "MariaDB" >> "${servicesToRestart}" elif echo "${pkg}" | grep -qE "^postgresql-[[:digit:]]+\.[[:digit:]]+$"; then echo "PostgreSQL" >> "${servicesToRestart}" elif echo "${pkg}" | grep -qE "^tomcat[[:digit:]]+$"; then echo "Tomcat" >> "${servicesToRestart}" elif [ "${pkg}" = "redis-server" ]; then echo "Redis" >> "${servicesToRestart}" elif [ "${pkg}" = "mongodb-server" ]; then echo "MondoDB" >> "${servicesToRestart}" elif echo "${pkg}" | grep -qE "^courier-(pop|imap)"; then echo "Courier POP/IMAP" >> "${servicesToRestart}" elif echo "${pkg}" | grep -qE "^dovecot-(pop|imap)d"; then echo "Dovecot POP/IMAP" >> "${servicesToRestart}" elif [ "${pkg}" = "samba" ]; then echo "Samba" >> "${servicesToRestart}" elif [ "${pkg}" = "slapd" ]; then echo "OpenLDAP" >> "${servicesToRestart}" elif [ "${pkg}" = "bind9" ]; then echo "Bind9" >> "${servicesToRestart}" elif [ "${pkg}" = "postfix" ]; then echo "Postfix" >> "${servicesToRestart}" elif [ "${pkg}" = "haproxy" ]; then echo "HAProxy" >> "${servicesToRestart}" elif [ "${pkg}" = "varnish" ]; then echo "Varnish" >> "${servicesToRestart}" elif [ "${pkg}" = "squid" ]; then echo "Squid" >> "${servicesToRestart}" elif [ "${pkg}" = "elasticsearch" ]; then echo "Elasticsearch" >> "${servicesToRestart}" elif [ "${pkg}" = "logstash" ]; then echo "Logstash" >> "${servicesToRestart}" elif [ "${pkg}" = "kibana" ]; then echo "Kibana" >> "${servicesToRestart}" elif [ "${pkg}" = "libc6" ]; then echo "Tous les services sont susceptibles d'être redémarrés (mise à jour de libc6)." > "${servicesToRestart}" break elif [ "${pkg}" = "libstdc++6" ]; then echo "Tous les services sont susceptibles d'être redémarrés (mise à jour de libstdc++6)." > "${servicesToRestart}" break elif echo "${pkg}" | grep -q "^libssl"; then echo "Tous les services sont susceptibles d'être redémarrés (mise à jour de libssl)." > "${servicesToRestart}" break fi done test ! -s "${servicesToRestart}" && echo "Aucun" > "${servicesToRestart}" cat << EOT > "${template}" Content-Type: text/plain; charset="utf-8" Reply-To: equipe@evolix.fr From: equipe@evolix.net To: ${clientmail} Subject: Prochain creneau pour mise a jour de votre serveur ${hostname} X-Debian-Release: ${local_release} X-Packages: ${packagesParsable} X-Date: ${date} Bonjour, Des mises-à-jour de sécurité ou mineures sont à réaliser sur votre serveur ${hostname}. Sauf indication contraire de votre part, le prochain créneau prévu pour intervenir manuellement pour réaliser ces mises-à-jour est : ${date} Si nous intervenons, un redémarrage des éventuels services concernés sera réalisé, entraînant a priori quelques secondes de coupure. Si nous ne sommes pas intervenus sur ce créneau, vous recevrez une nouvelle notification la semaine prochaine. Voici la listes de packages qui seront mis à jour : $(cat "${packages}") Liste des packages dont la mise-à-jour a été manuellement suspendue : $(cat "${packagesHold}") Liste des services qui seront redémarrés : $(cat "${servicesToRestart}") N'hésitez pas à nous faire toute remarque sur ce créneau d'intervention le plus tôt possible. Cordialement, -- Équipe Evolix - Hébergement et Infogérance Open Source http://evolix.com | Twitter: @Evolix @EvolixNOC | http://blog.evolix.com EOT < "${template}" /usr/sbin/sendmail "${mailto}" # Now we try to fetch all the packages for the next update session downloadstatus=$(apt dist-upgrade --assume-yes --download-only -q2 2>&1) echo "${downloadstatus}" | grep -q 'Download complete and in download only mode' # shellcheck disable=SC2181 if [ $? -ne 0 ]; then echo "${downloadstatus}" fi; # Also, we try to update each container apt sources if which lxc-ls > /dev/null; then for container in $(lxc-ls); do aptUpdateOutput=$(lxc-attach -n "${container}" -- apt update 2>&1 | (grep -Eve '^(Listing|WARNING|$)' -e upgraded -e 'up to date' || true )) if (echo "${aptUpdateOutput}" | grep -E "^Err(:[0-9]+)? http"); then echo "FATAL CONTAINER - Not able to fetch all sources (probably a pesky (mini)firewall). Please, fix me" exit 150 fi # Now we try to fetch all the packages for the next update session downloadstatus=$(lxc-attach -n "${container}" -- apt dist-upgrade --assume-yes --download-only -q2 2>&1) echo "${downloadstatus}" | grep -q 'Download complete and in download only mode' if [ $? -ne 0 ]; then echo "${downloadstatus}" fi; done fi