#!/usr/bin/env bash set -euo pipefail export LC_ALL=C ################################################################################ # FAIT Zabbix - Enrolement d un noeud de supervision # # curl -fsSL https://.../bootstrap.sh | sudo FAIT_CLIENT=acme FAIT_TOKEN=xxx bash # # La meme ligne vaut pour toutes les machines d un meme client : rien n est a # personnaliser par machine. # # FAIT_TOKEN sur la ligne de commande ci-dessus finit dans l argv de sudo, # donc dans /proc//cmdline, lisible par tout utilisateur local le temps # que la commande tourne. Pour l eviter, ecrire le jeton dans un fichier et # passer FAIT_TOKEN_FILE a la place : # # curl -fsSL https://.../bootstrap.sh | sudo FAIT_CLIENT=acme FAIT_TOKEN_FILE=/root/jeton bash # # FAIT_TOKEN reste accepte pour compatibilite, mais FAIT_TOKEN_FILE est la # forme recommandee des que le jeton n est pas jetable. # # Le noeud ne clone plus le depot. Il recoit un paquet Debian signe, et se met # a jour tout seul ensuite via fait-zabbix-update.timer. ################################################################################ FAIT_CLIENT="${FAIT_CLIENT:-}" FAIT_TOKEN="${FAIT_TOKEN:-}" FAIT_TOKEN_FILE="${FAIT_TOKEN_FILE:-}" ZABBIX_MAJOR="${ZABBIX_MAJOR:-7.0}" ZBX_SERVER="${ZBX_SERVER:-sup.fabout.it}" ZBX_SERVER_ACTIVE="${ZBX_SERVER_ACTIVE:-${ZBX_SERVER}}" ZBX_HOSTNAME="${ZBX_HOSTNAME:-}" ZBX_BIND_IP="${ZBX_BIND_IP:-}" REPO_HOST="${FAIT_REPO_HOST:-repo.fabout.it}" AUTH_FILE=/etc/apt/auth.conf.d/fait-zabbix.conf KEYRING=/etc/apt/keyrings/fait-supervision.asc SOURCE_LIST=/etc/apt/sources.list.d/fait-zabbix.list CLIENT_FILE=/etc/fait-zabbix/client # Lu par update.sh : si FAIT_REPO_HOST a surcharge le defaut ici, l updater # doit interroger le meme hote, sinon le fichier d authentification (dont la # ligne "machine" vise REPO_HOST) ne correspond plus a l URL interrogee et # les requetes partent sans identifiants. REPO_HOST_FILE=/etc/fait-zabbix/repo-host log() { echo "INFO: $*" >&2; } warn() { echo "WARN: $*" >&2; } err() { echo "ERROR: $*" >&2; exit 1; } # Les parametres passent avant le controle de root : signaler une variable # oubliee est plus utile que reclamer un sudo quand les deux manquent, et # evite de relancer en root juste pour decouvrir la vraie erreur. [[ -n "${FAIT_CLIENT}" ]] || err "FAIT_CLIENT est obligatoire : le nom du client, tel qu il figure dans le manifeste." # FAIT_TOKEN_FILE evite que le jeton apparaisse dans /proc//cmdline : on # le lit ici, avant le controle d absence, pour que les deux formes soient # equivalentes au reste du script. if [[ -n "${FAIT_TOKEN_FILE}" ]]; then [[ -r "${FAIT_TOKEN_FILE}" ]] || err "FAIT_TOKEN_FILE illisible : ${FAIT_TOKEN_FILE}" FAIT_TOKEN="$(tr -d ' \t\r\n' < "${FAIT_TOKEN_FILE}")" fi [[ -n "${FAIT_TOKEN}" ]] || err "FAIT_TOKEN (ou FAIT_TOKEN_FILE) est obligatoire : le jeton de lecture du client." [[ "${EUID}" -eq 0 ]] || err "A lancer en root (sudo)." [[ -r /etc/os-release ]] || err "/etc/os-release introuvable, distribution non supportee." # shellcheck disable=SC1091 . /etc/os-release # --- prerequis ---------------------------------------------------------------- ensure_prereqs() { local manquants=() # shellcheck disable=SC2043 # La boucle ne porte qu un seul outil aujourd hui, mais reste une boucle : # c est la ou un futur prerequis obligatoire s ajouterait, sans reprendre # toute la fonction. for c in curl; do command -v "$c" >/dev/null 2>&1 || manquants+=("$c"); done if [[ ${#manquants[@]} -gt 0 ]] || ! dpkg -s ca-certificates >/dev/null 2>&1; then log "Installation des prerequis : ${manquants[*]:-} ca-certificates" apt-get update -y >/dev/null 2>&1 || true DEBIAN_FRONTEND=noninteractive apt-get install -y \ ${manquants[@]+"${manquants[@]}"} ca-certificates >/dev/null fi } # --- depot Zabbix officiel ---------------------------------------------------- # zabbix-agent2 est une dependance stricte du paquet : sans ce depot, apt # refuserait la transaction entiere. ajouter_depot_zabbix() { if command -v zabbix_agent2 >/dev/null 2>&1; then log "zabbix-agent2 deja installe." return 0 fi local dist relver url tmp_deb dist="${ID}"; [[ "${dist}" == "ubuntu" ]] || dist="debian" # Zabbix nomme ses artefacts Ubuntu avec la version complete (ubuntu22.04), # mais Debian avec le seul majeur (debian12) : un relver tronque a la # majeure donnerait ubuntu22, qui n existe pas au depot Zabbix. if [[ "${dist}" == "ubuntu" ]]; then relver="${VERSION_ID}" else relver="${VERSION_ID%%.*}" fi url="https://repo.zabbix.com/zabbix/${ZABBIX_MAJOR}/${dist}/pool/main/z/zabbix-release/zabbix-release_latest+${dist}${relver}_all.deb" log "Ajout du depot Zabbix ${ZABBIX_MAJOR} : ${url}" # Pas de --remove-on-error : absent avant curl 7.83, donc de Debian 11 et # Ubuntu 22.04. On telecharge dans un fichier temporaire et on ne l installe # que si le telechargement a reussi ; un transfert tronque ne passe jamais # a dpkg. tmp_deb="$(mktemp --suffix=.deb)" if ! curl -fsSL -o "${tmp_deb}" "${url}"; then rm -f "${tmp_deb}" err "Telechargement zabbix-release echoue." fi DEBIAN_FRONTEND=noninteractive dpkg -i "${tmp_deb}" >/dev/null rm -f "${tmp_deb}" } # --- identifiants du client --------------------------------------------------- poser_identifiants() { install -d -m 0755 /etc/apt/auth.conf.d # Cree en 0600 AVANT toute ecriture : l ecrire puis le restreindre laisserait # le jeton lisible par tous dans l intervalle. install -m 0600 /dev/null "${AUTH_FILE}" cat > "${AUTH_FILE}" < "${CLIENT_FILE}" chmod 0644 "${CLIENT_FILE}" printf '%s\n' "${REPO_HOST}" > "${REPO_HOST_FILE}" chmod 0644 "${REPO_HOST_FILE}" log "Identifiants poses pour le client ${FAIT_CLIENT}." } # --- cle de signature --------------------------------------------------------- # L organisation etant privee, la cle n est pas servie anonymement : elle est # recuperee avec les identifiants poses juste avant, jamais passes en argument. # Pas de --remove-on-error (absent avant curl 7.83, donc de Debian 11 et # Ubuntu 22.04) : on telecharge vers un fichier temporaire et on ne le # promeut en cle qu apres coup, ce qui protege pareillement contre un # transfert tronque qui passerait quand meme le controle head -1 ci-dessous. poser_cle() { install -d -m 0755 /etc/apt/keyrings local tmp_cle tmp_cle="$(mktemp)" if ! curl -fsSL --netrc-file "${AUTH_FILE}" \ "https://${REPO_HOST}/repository.key" -o "${tmp_cle}"; then rm -f "${tmp_cle}" err "Recuperation de la cle de signature echouee." fi head -1 "${tmp_cle}" | grep -q "BEGIN PGP PUBLIC KEY BLOCK" || { rm -f "${tmp_cle}" err "Le fichier recupere n est pas une cle PGP." } install -m 0644 "${tmp_cle}" "${KEYRING}" rm -f "${tmp_cle}" log "Cle de signature en place." } poser_source() { cat > "${SOURCE_LIST}" </dev/null 2>&1 || return 0 getent group docker >/dev/null 2>&1 || return 0 id zabbix >/dev/null 2>&1 || return 0 id -nG zabbix 2>/dev/null | tr ' ' '\n' | grep -qx docker && return 0 log "Ajout de l utilisateur zabbix au groupe docker." usermod -aG docker zabbix || warn "Echec de l ajout au groupe docker." } main() { ensure_prereqs ajouter_depot_zabbix poser_identifiants poser_cle poser_source log "Installation de fait-zabbix..." apt-get update -y >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y fait-zabbix render_conf ensure_docker_access systemctl restart zabbix-agent2 || true # Le paquet vient d etre installe a la version la plus recente du depot ; # le manifeste peut en vouloir une autre, ou geler ce client. On converge # donc une fois, tout de suite, plutot que d attendre le premier reveil du # timer et de laisser tourner une version non assignee pendant dix minutes. if [[ -x /usr/lib/fait-zabbix/update.sh ]]; then /usr/lib/fait-zabbix/update.sh || warn "La convergence initiale a echoue ; le timer reessaiera." fi log "Enrolement termine. La machine converge desormais seule vers le manifeste." } main "$@"