Files

308 lines
12 KiB
Bash

#!/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 <nom>")
#
# 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}" <<EOF
# --- Identite / raccordement serveur ---
ProxyMode=0
Server=${ZBX_SERVER}
Hostname=${ZBX_PROXY_HOSTNAME}
# --- Base SQLite (creee automatiquement au 1er demarrage) ---
DBName=${PROXY_DB}
# --- Fichier PID ---
# L unite systemd du paquet est de Type=forking : elle attend ce fichier pour
# considerer le service demarre. Sans cette directive, zabbix_proxy tourne
# normalement mais n ecrit aucun PID, et le service reste bloque en
# "activating" jusqu au timeout — un demarrage qui parait fige alors que le
# proxy fonctionne. La conf du paquet la contient ; la reecrire sans elle la
# perd.
PidFile=/run/zabbix/zabbix_proxy.pid
# --- Resilience liaison internet ---
# Garde les donnees jusqu a 24h si ${ZBX_SERVER} est injoignable (coupure WAN)
ProxyOfflineBuffer=24
ProxyLocalBuffer=0
# Frequence d envoi des donnees collectees vers le serveur
DataSenderFrequency=1
# --- Chiffrement PSK (flux proxy -> 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 "$@"