commit 0d95a6bd030ae728fe81ea024375035ffcba7a00 Author: Sam Date: Sat Oct 10 19:46:21 2026 +0000 One-shot Ubuntu agent2 installer (active checks) diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e984e2 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# zabbix-agent + +One-shot [Zabbix agent2](https://www.zabbix.com/) installer for Ubuntu, +using **active checks** — the agent connects *out* to the server, so it +works behind NAT/firewalls and needs no inbound ports. + +- Idempotent — safe to re-run +- Installs from the official Zabbix apt repo (agent2) +- Config: `ServerActive=`, `Server=127.0.0.1,`, + `Hostname=` (defaults to the short system hostname) + +## Use + +```bash +# clone once, then git pull for updates: +git clone https://gitea.noctools.nz/noctools/zabbix-agent.git +sudo ./install-zabbix-agent2-ubuntu.sh --server +``` + +Or with no local copy at all: + +```bash +curl -fsSL https://gitea.noctools.nz/noctools/zabbix-agent/raw/branch/main/install-zabbix-agent2-ubuntu.sh \ + | sudo bash -s -- --server +``` + +## Flags + +| flag | meaning | default | +|---|---|---| +| `--server HOST[:PORT]` | Zabbix server/proxy for active checks (**required**) | — | +| `--hostname NAME` | Zabbix host name — must equal the technical name on the server | short system hostname | +| `--major X.Y` | Zabbix repo series | `7.4` | + +After installing, create the host on the Zabbix server with the **exact same +technical name** and an active-agent template (e.g. "Linux by Zabbix agent +active"). The script reports whether the agent reached the server and warns +if the host doesn't exist yet — the warning clears by itself once it does. diff --git a/install-zabbix-agent2-ubuntu.sh b/install-zabbix-agent2-ubuntu.sh new file mode 100755 index 0000000..68a56a7 --- /dev/null +++ b/install-zabbix-agent2-ubuntu.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +# install-zabbix-agent2-ubuntu.sh — one-shot Zabbix agent2 (ACTIVE checks) +# installer for Ubuntu. Public-safe: nothing about any specific environment +# is baked in; the server address comes from the required --server flag. +# +# Idempotent — safe to re-run. What it does: +# 1. Installs Zabbix agent2 from the official Zabbix apt repo +# 2. Writes an ACTIVE-check config (agent connects OUT to the server; +# no inbound ports, no firewall changes needed — works behind NAT): +# ServerActive= +# Server=127.0.0.1, +# Hostname=<--hostname, else the short system hostname> +# 3. Enables + restarts the service and sanity-checks the connection. +# +# Usage: +# sudo ./install-zabbix-agent2-ubuntu.sh --server 203.0.113.10 +# sudo ./install-zabbix-agent2-ubuntu.sh --server 203.0.113.10:10051 --hostname web01 +# sudo ./install-zabbix-agent2-ubuntu.sh web01 --server 203.0.113.10 # positional hostname +# +# One-liner (no local copy needed): +# curl -fsSL /install-zabbix-agent2-ubuntu.sh | sudo bash -s -- --server 203.0.113.10 +# +# After it succeeds, the host must exist on the Zabbix server with the EXACT +# same technical name (an active-agent template, e.g. "Linux by Zabbix agent +# active"). The script warns if the agent cannot fetch its config yet — that +# warning clears by itself within ~a minute of the host being created. + +set -euo pipefail + +ZABBIX_MAJOR="7.4" +CONF="/etc/zabbix/zabbix_agent2.conf" +LOG="/var/log/zabbix/zabbix_agent2.log" + +say() { echo "[zabbix-agent2] $*"; } +die() { echo "[zabbix-agent2] ERROR: $*" >&2; exit 1; } +usage() { + cat < SA_HOST / SA_PORT (plain IPv4 or DNS name) +case "$SERVER_RAW" in + *:*) SA_HOST="${SERVER_RAW%%:*}"; SA_PORT="${SERVER_RAW##*:}" ;; + *) SA_HOST="$SERVER_RAW"; SA_PORT="10051" ;; +esac +[[ "$SA_PORT" =~ ^[0-9]+$ ]] || die "bad port in --server '${SERVER_RAW}' (IPv6 literals: give a DNS name instead)" +[ -n "$SA_HOST" ] && [ "$SA_HOST" != "$SA_PORT" ] || die "bad --server '${SERVER_RAW}'" +SA_ADDR="$SA_HOST"; [ "$SA_PORT" = "10051" ] || SA_ADDR="${SA_HOST}:${SA_PORT}" + +ZBX_HOSTNAME="${HOSTNAME_ARG:-$(hostname -s)}" +case "$ZBX_HOSTNAME" in + *[!A-Za-z0-9._-]*) die "hostname '${ZBX_HOSTNAME}' has odd chars — pass --hostname " ;; +esac + +[ "$(id -u)" -eq 0 ] || die "run as root (sudo)" + +# ---- platform ---------------------------------------------------------------- +. /etc/os-release +[ "${ID:-}" = "ubuntu" ] || die "Ubuntu only (found '${ID:-unknown}'); for Debian, swap 'ubuntu' for 'debian' in the repo URL below" +say "Ubuntu ${VERSION_ID} (${PRETTY_NAME:-ubuntu}) → server ${SA_ADDR}, hostname '${ZBX_HOSTNAME}'" + +# ---- 1. repo + package --------------------------------------------------------- +export DEBIAN_FRONTEND=noninteractive +APT_OPTS="-y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold" + +apt-get update -qq +apt-get install -qq $APT_OPTS ca-certificates curl >/dev/null + +DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_MAJOR}/release/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_MAJOR}+ubuntu${VERSION_ID}_all.deb" +curl -fsSI -o /dev/null "$DEB_URL" || die "no zabbix-release for Ubuntu ${VERSION_ID} (check --major); looked at ${DEB_URL}" +curl -fsS -o /tmp/zabbix-release.deb "$DEB_URL" +dpkg -i /tmp/zabbix-release.deb || apt-get -f install -qq $APT_OPTS +rm -f /tmp/zabbix-release.deb +apt-get update -qq +apt-get install -qq $APT_OPTS zabbix-agent2 +say "installed: $(zabbix_agent2 -V | head -1)" + +# ---- 2. config (replace-or-append: one authoritative line per key) ------------- +if [ -f "$CONF" ] && [ ! -f "${CONF}.pre-install.bak" ]; then + cp -a "$CONF" "${CONF}.pre-install.bak" + say "backed up stock config to ${CONF}.pre-install.bak" +fi + +set_conf() { # file key value — drop every existing (commented) line, append ours + sed -i -E "/^[[:space:]]*#?[[:space:]]*${2}=/d" "$1" + echo "${2}=${3}" >> "$1" +} +[ -f "$CONF" ] || die "expected ${CONF} to exist after install" + +set_conf "$CONF" ServerActive "$SA_ADDR" +set_conf "$CONF" Server "127.0.0.1,${SA_HOST}" +set_conf "$CONF" Hostname "$ZBX_HOSTNAME" + +# ---- 3. service + sanity ------------------------------------------------------- +systemctl enable zabbix-agent2 >/dev/null +systemctl restart zabbix-agent2 +sleep 3 +systemctl is-active --quiet zabbix-agent2 || { journalctl -u zabbix-agent2 -n 20 --no-pager; die "service not active"; } +zabbix_agent2 -t agent.version >/dev/null 2>&1 || die "agent fails to parse config / answer local test" + +# Active-check reachability: give the agent ~20s to fetch its first config. +loggrep() { # pattern — agent log file, else the journal + if [ -r "$LOG" ]; then grep -Eqi "$1" "$LOG"; else journalctl -u zabbix-agent2 --since '-2 min' | grep -Eqi "$1"; fi +} +WARN="" +for _ in $(seq 1 10); do + if loggrep 'started to fail|cannot obtain|no active checks|host not found'; then WARN=1; break; fi + sleep 2 +done + +echo +say "done. config:" +grep -E '^(Server|ServerActive|Hostname)=' "$CONF" | sed 's/^/ /' +if [ -n "$WARN" ]; then + say "WARNING: agent cannot fetch its config yet:" + if [ -r "$LOG" ]; then tail -n 4 "$LOG" | sed 's/^/ /' + else journalctl -u zabbix-agent2 -n 4 --no-pager | sed 's/^/ /'; fi + say "expected if the host doesn't exist on the Zabbix server yet — create it" + say "with the EXACT technical name '${ZBX_HOSTNAME}' and an active-agent" + say "template; the warning clears by itself within a minute." +else + say "active checks OK (no connection errors in the agent log)." +fi