#!/usr/bin/env bash set -euo pipefail export LC_ALL=C ################################################################################ # FAIT Zabbix - Installation d un proxy Zabbix (SQLite, PSK, mode actif) # # curl -fsSL https://.../setup_proxy.sh | sudo bash # # Ce script ne fait QUE le proxy : depot Zabbix officiel, zabbix-proxy-sqlite3, # cle PSK, /etc/zabbix/zabbix_proxy.conf, demarrage. Il n enrole pas la machine # dans la supervision FAIT (pas d agent, pas de paquet fait-zabbix, pas # d updater) : pour ca, lancer bootstrap.sh separement sur la meme machine. # # L identite du proxy n a pas de defaut : elle est demandee au terminal, ou # passee en variable. Les deux valeurs doivent correspondre au caractere pres # a ce qui est declare cote serveur. # # ZBX_PROXY_HOSTNAME nom du proxy (demande ; defaut propose : le FQDN) # ZBX_PSK_IDENTITY identite PSK (demandee ; defaut propose : "PSK ") # # Sans terminal (cron, provisionnement), ces deux variables sont obligatoires : # # curl -fsSL https://.../setup_proxy.sh \ # | sudo ZBX_PROXY_HOSTNAME=zbx-proxy.client.fr \ # ZBX_PSK_IDENTITY="PSK zbx-proxy client" bash # # Variables a defaut : # ZBX_SERVER serveur Zabbix a joindre (defaut sup.fabout.it) # ZABBIX_MAJOR branche Zabbix (defaut 7.0) # ZBX_PSK_FILE chemin de la cle PSK (defaut # /etc/zabbix/zabbix_proxy.psk) # # La cle PSK est generee si le fichier est absent, et affichee en fin # d execution : c est cette valeur qu il faut coller dans l onglet Encryption # du proxy cote serveur. Si le fichier existe deja, il est conserve tel quel — # relancer le script ne casse donc pas un proxy deja apparie. ################################################################################ ZABBIX_MAJOR="${ZABBIX_MAJOR:-7.0}" ZBX_SERVER="${ZBX_SERVER:-sup.fabout.it}" ZBX_PSK_FILE="${ZBX_PSK_FILE:-/etc/zabbix/zabbix_proxy.psk}" # Pas de defaut : l identite du proxy est propre a chaque site. Un defaut ici # ferait installer silencieusement un proxy portant le nom d un autre client, # qui ne se raccorderait jamais — et la panne serait attribuee au reseau bien # avant qu on pense a relire le Hostname. ZBX_PROXY_HOSTNAME="${ZBX_PROXY_HOSTNAME:-}" ZBX_PSK_IDENTITY="${ZBX_PSK_IDENTITY:-}" PROXY_CONF=/etc/zabbix/zabbix_proxy.conf PROXY_DB=/var/lib/zabbix/zabbix_proxy.sqlite3 log() { echo "INFO: $*" >&2; } warn() { echo "WARN: $*" >&2; } err() { echo "ERROR: $*" >&2; exit 1; } [[ "${EUID}" -eq 0 ]] || err "A lancer en root (sudo)." # --- identite du proxy -------------------------------------------------------- # Lit sur /dev/tty et non sur stdin : sous "curl ... | sudo bash", stdin est le # code source du script, et un read classique en avalerait la suite. /dev/tty # vise le terminal reel, qui reste disponible derriere le pipe. # # Sans terminal (cron, provisionnement, CI), il n y a personne pour repondre : # le script exige alors les variables plutot que de deviner. Un defaut invente # ici produirait un proxy qui s installe proprement et ne se raccorde jamais. demander() { local invite="$1" defaut="${2:-}" reponse="" if [[ ! -r /dev/tty ]]; then return 1 fi if [[ -n "${defaut}" ]]; then printf '%s [%s] : ' "${invite}" "${defaut}" > /dev/tty else printf '%s : ' "${invite}" > /dev/tty fi IFS= read -r reponse < /dev/tty || return 1 printf '%s' "${reponse:-${defaut}}" } if [[ -z "${ZBX_PROXY_HOSTNAME}" ]]; then ZBX_PROXY_HOSTNAME="$(demander "Nom du proxy (Hostname, identique au serveur)" \ "$(hostname -f 2>/dev/null || hostname)")" \ || err "Aucun terminal pour la saisie : passer ZBX_PROXY_HOSTNAME en variable." fi [[ -n "${ZBX_PROXY_HOSTNAME}" ]] || err "Le nom du proxy est obligatoire." if [[ -z "${ZBX_PSK_IDENTITY}" ]]; then # Defaut derive du nom : l identite n a pas a etre inventee, elle doit juste # etre stable et identique des deux cotes. ZBX_PSK_IDENTITY="$(demander "Identite PSK (TLSPSKIdentity, identique au serveur)" \ "PSK ${ZBX_PROXY_HOSTNAME}")" \ || err "Aucun terminal pour la saisie : passer ZBX_PSK_IDENTITY en variable." fi [[ -n "${ZBX_PSK_IDENTITY}" ]] || err "L identite PSK est obligatoire." [[ -r /etc/os-release ]] || err "/etc/os-release introuvable, distribution non supportee." # shellcheck disable=SC1091 . /etc/os-release # --- prerequis ---------------------------------------------------------------- ensure_prereqs() { local manquants=() for c in curl openssl; 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 ---------------------------------------------------- # Meme derivation que bootstrap.sh : Zabbix nomme ses artefacts Ubuntu avec la # version complete (ubuntu22.04) et Debian avec le seul majeur (debian13). Un # relver tronque a la majeure donnerait ubuntu22, qui n existe pas au depot. ajouter_depot_zabbix() { local dist relver url tmp_deb dist="${ID}"; [[ "${dist}" == "ubuntu" ]] || dist="debian" if [[ "${dist}" == "ubuntu" ]]; then relver="${VERSION_ID}" else relver="${VERSION_ID%%.*}" fi # Deja pose (par bootstrap.sh, ou par un passage precedent de ce script) : # inutile de retelecharger le .deb, apt a deja la source. if [[ -f /etc/apt/sources.list.d/zabbix.list ]] \ || [[ -f /etc/apt/sources.list.d/zabbix.sources ]]; then log "Depot Zabbix deja present." return 0 fi url="https://repo.zabbix.com/zabbix/${ZABBIX_MAJOR}/${dist}/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_MAJOR}+${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 temporaire et on n installe que si le # transfert a reussi ; un fichier 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 : ${url}" fi DEBIAN_FRONTEND=noninteractive dpkg -i "${tmp_deb}" >/dev/null rm -f "${tmp_deb}" } installer_proxy() { log "Installation de zabbix-proxy-sqlite3..." apt-get update -y >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y \ zabbix-proxy-sqlite3 zabbix-sql-scripts >/dev/null } # --- cle PSK ------------------------------------------------------------------ # Generee une seule fois. Un proxy deja apparie garde sa cle : reexecuter le # script pour ajuster la conf ne doit pas invalider l appairage cote serveur. poser_psk() { install -d -m 0755 "$(dirname "${ZBX_PSK_FILE}")" if [[ -s "${ZBX_PSK_FILE}" ]]; then log "Cle PSK existante conservee : ${ZBX_PSK_FILE}" else # Cree en 0600 AVANT toute ecriture : l ecrire puis la restreindre # laisserait le secret lisible par tous dans l intervalle. install -m 0600 /dev/null "${ZBX_PSK_FILE}" openssl rand -hex 32 > "${ZBX_PSK_FILE}" log "Cle PSK generee : ${ZBX_PSK_FILE}" fi chmod 0600 "${ZBX_PSK_FILE}" # zabbix_proxy tourne sous l utilisateur zabbix et lit la cle au demarrage. if id zabbix >/dev/null 2>&1; then chown zabbix:zabbix "${ZBX_PSK_FILE}" else warn "Utilisateur zabbix absent : proprietaire de la cle PSK non ajuste." fi } # --- conf proxy --------------------------------------------------------------- render_conf() { if [[ -f "${PROXY_CONF}" ]]; then local bak; bak="${PROXY_CONF}.bak.$(date +%Y%m%d-%H%M%S 2>/dev/null || echo "$$")" if cp -a "${PROXY_CONF}" "${bak}"; then log "Conf existante sauvegardee : ${bak}" else warn "Echec de la sauvegarde de ${PROXY_CONF} ; elle va etre reecrite sans copie." fi fi cat > "${PROXY_CONF}" < serveur) --- TLSConnect=psk TLSAccept=psk TLSPSKIdentity=${ZBX_PSK_IDENTITY} TLSPSKFile=${ZBX_PSK_FILE} # --- Perf / pollers (a ajuster selon le nb de hosts) --- StartPollers=5 StartPollersUnreachable=1 StartPingers=1 StartDiscoverers=1 CacheSize=32M # --- Logs --- LogFile=/var/log/zabbix/zabbix_proxy.log LogFileSize=10 EOF chmod 0644 "${PROXY_CONF}" log "Conf proxy ecrite (Hostname=${ZBX_PROXY_HOSTNAME}, Server=${ZBX_SERVER})." } # Le paquet cree /var/lib/zabbix, mais on ne le tient pas pour acquis : sans # repertoire accessible en ecriture par l utilisateur zabbix, le proxy ne peut # pas creer sa base au premier demarrage et sort en erreur. preparer_db() { local dir; dir="$(dirname "${PROXY_DB}")" install -d -m 0755 "${dir}" if id zabbix >/dev/null 2>&1; then chown zabbix:zabbix "${dir}" fi } demarrer() { systemctl enable zabbix-proxy >/dev/null 2>&1 || warn "Echec de l activation au demarrage." # Le premier demarrage cree le schema SQLite, ce qui prend quelques secondes. # # --no-block rend la main tout de suite : sans lui, un proxy qui n arrive pas # a joindre le serveur laisse systemctl attendre son timeout, et l installation # parait figee juste apres la derniere ligne affichee. On verifie l etat nous # memes ensuite, avec un message qui dit ou regarder. systemctl restart --no-block zabbix-proxy \ || warn "La demande de demarrage a echoue." local i=0 while [[ $i -lt 15 ]]; do if systemctl is-active --quiet zabbix-proxy; then log "Service zabbix-proxy actif." return 0 fi if systemctl is-failed --quiet zabbix-proxy; then warn "Le service a echoue. Voir : journalctl -u zabbix-proxy -n 50" return 0 fi sleep 1 i=$((i + 1)) done warn "Le service n est pas encore actif apres 15s (creation de la base ?)." warn "Suivre : journalctl -u zabbix-proxy -f" } resume() { echo echo "==============================================================" echo " Proxy installe. A declarer cote serveur (${ZBX_SERVER}) :" echo echo " Administration > Proxies > Create proxy" echo " Proxy name : ${ZBX_PROXY_HOSTNAME}" echo " Proxy mode : Active" echo " Encryption : Connections from proxy = PSK" echo " PSK identity : ${ZBX_PSK_IDENTITY}" echo " PSK value : $(cat "${ZBX_PSK_FILE}")" echo echo " Le nom du proxy et l identite PSK doivent correspondre au" echo " caractere pres, espaces compris." echo echo " Verification : journalctl -u zabbix-proxy -f" echo "==============================================================" } main() { ensure_prereqs ajouter_depot_zabbix installer_proxy poser_psk preparer_db render_conf demarrer resume } main "$@"