#!/bin/bash # # Zabbix Agent 2 Deployment Script - Linux ARM (aarch64 / armhf / armv6) # Installs and configures Zabbix Agent 2 with PSK auto-registration # For armhf/armv6: falls back to Zabbix Agent (v1) since Agent 2 is not # available for 32-bit ARM (see ZBX-24905). # Target server: zabbix.snarfnet.net # # Usage: sudo bash deploy_zabbix_agent_linux_arm.sh [psk_key] # psk_key - (optional) 128-char hex PSK. If omitted, one is generated. # set -euo pipefail ZABBIX_SERVER="zabbix.snarfnet.net" ZABBIX_VERSION="7.0.27" PSK_IDENTITY="PSK_autoregister" PSK_FILE="/etc/zabbix/zabbix_agent2.psk" AGENT_CONF="/etc/zabbix/zabbix_agent2.conf" HOST_METADATA="Linux" INSTALL_DIR="/opt/zabbix-agent2" USING_AGENT_V1=false # --- Functions --- log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } detect_arch() { ARCH=$(uname -m) case "${ARCH}" in aarch64|arm64) ARCH_LABEL="aarch64" TARBALL_ARCH="linux_arm64" ;; armv7l|armhf) ARCH_LABEL="armhf" TARBALL_ARCH="linux_arm" ;; armv6l) ARCH_LABEL="armv6" TARBALL_ARCH="linux_arm" ;; *) log "ERROR: Unsupported architecture '${ARCH}'." log " Use deploy_zabbix_agent_linux.sh for x86_64 systems." exit 1 ;; esac log "Detected architecture: ${ARCH} (${ARCH_LABEL})" } detect_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS_ID="${ID}" OS_VERSION="${VERSION_ID%%.*}" else OS_ID="unknown" OS_VERSION="0" fi } install_agent_package_manager() { detect_os case "${OS_ID}" in debian|ubuntu|raspbian) log "Installing via apt (Debian/Ubuntu/Raspbian)..." local dpkg_arch dpkg_arch=$(dpkg --print-architecture) # Zabbix provides separate repos for ARM architectures: # - debian-arm64 / ubuntu-arm64 for aarch64 (Bookworm/Trixie+ only) # - raspbian for armhf (Raspberry Pi OS) — but NOTE: Zabbix 7.0 does NOT # ship zabbix-agent2 for armhf (see ZBX-24905). Only zabbix-agent (v1). # The standard debian/ubuntu repos only carry amd64. local repo_distro="" local has_agent2=true case "${dpkg_arch}" in arm64) if [ "${OS_ID}" = "ubuntu" ]; then repo_distro="ubuntu-arm64" else repo_distro="debian-arm64" fi ;; armhf) repo_distro="raspbian" # Zabbix 7.0 raspbian repo does NOT include zabbix-agent2 for armhf. # Only zabbix-agent (v1) is available. has_agent2=false ;; *) log "Unexpected dpkg arch '${dpkg_arch}' on ARM script. Falling back to binary..." install_agent_binary return 0 ;; esac # Determine release codename (e.g. bullseye, bookworm, jammy) local codename="" if [ -f /etc/os-release ]; then codename=$(. /etc/os-release && echo "${VERSION_CODENAME:-}") fi if [ -z "${codename}" ]; then log "Could not determine release codename. Falling back to binary..." install_agent_binary return 0 fi # The arm64 Zabbix repos only support Debian 12+ (bookworm, trixie). # For older/EOL releases like bullseye, try Debian's own repos first, # then fall back to extracting from the bookworm .deb. case "${codename}" in buster|bullseye|stretch|jessie) if [ "${has_agent2}" = true ]; then log "Zabbix arm64 apt repo not available for EOL release '${codename}'." log "Attempting install from Debian's own repositories..." else log "Zabbix raspbian repo not available for EOL release '${codename}'." log "Attempting install from Debian's own repositories..." fi # Disable defunct backports repos first local backports_list backports_list=$(grep -rl 'backports' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null || true) if [ -n "${backports_list}" ]; then log "Disabling defunct backports repositories..." for f in ${backports_list}; do sed -i '/backports/s/^[^#]/#&/' "${f}" done fi # Try installing from existing repos (Debian's own packages) apt-get update 2>&1 | grep -v "^W:" || true if apt-get install -y zabbix-agent2 2>/dev/null; then log "Installed zabbix-agent2 from Debian repositories." return 0 fi # On armhf, try zabbix-agent (v1) as well if [ "${has_agent2}" = false ]; then log "zabbix-agent2 not available. Trying zabbix-agent (v1)..." if apt-get install -y zabbix-agent 2>/dev/null; then log "Installed zabbix-agent (v1) from Debian repositories." AGENT_CONF="/etc/zabbix/zabbix_agentd.conf" USING_AGENT_V1=true return 0 fi fi log "Not available in Debian repos. Falling back to binary extraction..." install_agent_binary return 0 ;; esac # Disable defunct backports repos to prevent apt-get update 404 errors (Debian EOL releases) local backports_list backports_list=$(grep -rl 'backports' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null || true) if [ -n "${backports_list}" ]; then log "Disabling defunct backports repositories to avoid 404 errors..." for f in ${backports_list}; do sed -i '/backports/s/^[^#]/#&/' "${f}" done fi # Download and install the Zabbix release package for the ARM-specific repo. # URL format: zabbix-release_latest+debian12_all.deb (for both raspbian & debian-arm64) local release_url="https://repo.zabbix.com/zabbix/7.0/${repo_distro}/pool/main/z/zabbix-release/zabbix-release_latest+debian${OS_VERSION}_all.deb" log "Adding Zabbix repository: ${repo_distro} (${codename})" log "Release package URL: ${release_url}" if wget -q "${release_url}" -O /tmp/zabbix-release.deb 2>/dev/null || \ curl -sfL "${release_url}" -o /tmp/zabbix-release.deb 2>/dev/null; then # Verify we actually got a .deb and not an error page if ! dpkg-deb --info /tmp/zabbix-release.deb >/dev/null 2>&1; then log "Downloaded file is not a valid .deb package. Falling back to binary..." rm -f /tmp/zabbix-release.deb install_agent_binary return 0 fi dpkg -i /tmp/zabbix-release.deb apt-get update # Try zabbix-agent2 first if apt-get install -y zabbix-agent2 2>/dev/null; then rm -f /tmp/zabbix-release.deb log "zabbix-agent2 package installation successful." return 0 fi # On armhf, agent2 won't exist — fall back to zabbix-agent (v1) if [ "${has_agent2}" = false ]; then log "zabbix-agent2 not available for armhf. Installing zabbix-agent (v1)..." if apt-get install -y zabbix-agent; then rm -f /tmp/zabbix-release.deb AGENT_CONF="/etc/zabbix/zabbix_agentd.conf" USING_AGENT_V1=true log "zabbix-agent (v1) package installation successful." return 0 fi fi fi log "Package manager install failed, falling back to binary tarball..." rm -f /tmp/zabbix-release.deb install_agent_binary ;; *) log "No ARM package available for '${OS_ID}', using binary tarball..." install_agent_binary ;; esac } install_agent_v1_armhf() { # Zabbix 7.0 raspbian repo provides zabbix-agent (v1) for armhf but NOT agent2. # This function installs agent v1 as a fallback for 32-bit ARM systems. detect_os local codename="" if [ -f /etc/os-release ]; then codename=$(. /etc/os-release && echo "${VERSION_CODENAME:-}") fi # Try adding the raspbian repo and installing zabbix-agent (v1) local release_url="https://repo.zabbix.com/zabbix/7.0/raspbian/pool/main/z/zabbix-release/zabbix-release_latest+debian${OS_VERSION}_all.deb" log "Trying Zabbix raspbian repo for zabbix-agent (v1)..." log "Release URL: ${release_url}" if wget -q "${release_url}" -O /tmp/zabbix-release.deb 2>/dev/null || \ curl -sfL "${release_url}" -o /tmp/zabbix-release.deb 2>/dev/null; then if dpkg-deb --info /tmp/zabbix-release.deb >/dev/null 2>&1; then dpkg -i /tmp/zabbix-release.deb 2>/dev/null || true apt-get update 2>/dev/null || true if apt-get install -y zabbix-agent 2>/dev/null; then rm -f /tmp/zabbix-release.deb AGENT_CONF="/etc/zabbix/zabbix_agentd.conf" USING_AGENT_V1=true log "Installed zabbix-agent (v1) successfully from raspbian repository." return 0 fi fi fi rm -f /tmp/zabbix-release.deb # Also try installing directly if it's already in repos if apt-get install -y zabbix-agent 2>/dev/null; then AGENT_CONF="/etc/zabbix/zabbix_agentd.conf" USING_AGENT_V1=true log "Installed zabbix-agent (v1) from existing repositories." return 0 fi log "Could not install zabbix-agent (v1) either." return 1 } install_agent_binary() { log "Installing Zabbix Agent 2 from direct .deb package..." # Zabbix does not publish static binary tarballs for ARM architectures. # Strategy: download the .deb and extract the binary without running post-install # scripts (which try to start the service before we've configured it). # # IMPORTANT: Zabbix 7.0 only provides zabbix-agent2 for arm64, NOT armhf. # For armhf/armv6 systems, we try arm64 .deb extraction (unlikely to work due to # arch mismatch), then fall back to zabbix-agent (v1) from the raspbian repo, # or suggest upgrading to a 64-bit OS. local deb_path="/tmp/zabbix_agent2.deb" local dpkg_arch dpkg_arch=$(dpkg --print-architecture 2>/dev/null || echo "unknown") # Select the correct pool and architecture for the .deb download local pool_base="" local deb_arch="" case "${dpkg_arch}" in arm64) pool_base="https://repo.zabbix.com/zabbix/7.0/debian-arm64/pool/main/z/zabbix" deb_arch="arm64" ;; armhf) # Zabbix 7.0 does NOT publish agent2 for armhf. Try the raspbian pool # for zabbix-agent (v1) first, then fall back to arm64 extraction attempt. log "NOTE: Zabbix 7.0 does not provide zabbix-agent2 for armhf (32-bit ARM)." log "Attempting to install zabbix-agent (v1) from raspbian repository..." if install_agent_v1_armhf; then return 0 fi # Last resort: try arm64 binary (won't work on 32-bit kernel) pool_base="https://repo.zabbix.com/zabbix/7.0/debian-arm64/pool/main/z/zabbix" deb_arch="arm64" ;; *) pool_base="https://repo.zabbix.com/zabbix/7.0/debian-arm64/pool/main/z/zabbix" deb_arch="arm64" ;; esac local deb_urls=( "${pool_base}/zabbix-agent2_${ZABBIX_VERSION}-1+debian12_${deb_arch}.deb" "${pool_base}/zabbix-agent2_${ZABBIX_VERSION}-1%2Bdebian12_${deb_arch}.deb" ) local downloaded=false for url in "${deb_urls[@]}"; do log "Trying: ${url}" if wget -q "${url}" -O "${deb_path}" 2>/dev/null; then if dpkg-deb --info "${deb_path}" >/dev/null 2>&1; then downloaded=true break fi fi if curl -sfL "${url}" -o "${deb_path}" 2>/dev/null; then if dpkg-deb --info "${deb_path}" >/dev/null 2>&1; then downloaded=true break fi fi rm -f "${deb_path}" done if [ "${downloaded}" = false ]; then # Fallback: try CDN static tarball (may work for some versions/arches) local tarball_url="https://cdn.zabbix.com/zabbix/binaries/stable/7.0/${ZABBIX_VERSION}/zabbix_agent2-${ZABBIX_VERSION}-${TARBALL_ARCH}-static.tar.gz" local tarball_path="/tmp/zabbix_agent2.tar.gz" log "Trying CDN tarball: ${tarball_url}" if wget -q "${tarball_url}" -O "${tarball_path}" 2>/dev/null || \ curl -sfL "${tarball_url}" -o "${tarball_path}" 2>/dev/null; then if file "${tarball_path}" 2>/dev/null | grep -q gzip; then install_from_tarball "${tarball_path}" return 0 fi fi rm -f "${tarball_path}" log "ERROR: Failed to download Zabbix Agent 2 for ${ARCH_LABEL}." log " Options:" log " 1. Upgrade to Debian 12 (Bookworm) and re-run this script" log " 2. Manually download from https://www.zabbix.com/download" exit 1 fi # Extract the .deb contents manually instead of dpkg -i. # This avoids post-install scripts that try to start the service before config is ready, # and avoids glibc version issues by testing the binary directly. log "Extracting .deb package contents..." local extract_dir="/tmp/zabbix_agent2_extract" rm -rf "${extract_dir}" mkdir -p "${extract_dir}" dpkg-deb -x "${deb_path}" "${extract_dir}" # Check if the binary actually runs on this system (glibc compatibility) local agent_bin="${extract_dir}/usr/sbin/zabbix_agent2" if [ ! -f "${agent_bin}" ]; then agent_bin=$(find "${extract_dir}" -name "zabbix_agent2" -type f 2>/dev/null | head -1) fi if [ -z "${agent_bin}" ] || [ ! -f "${agent_bin}" ]; then log "ERROR: Could not find zabbix_agent2 binary in package." rm -rf "${extract_dir}" "${deb_path}" exit 1 fi # Test if the binary can actually execute on this system chmod +x "${agent_bin}" if ! "${agent_bin}" --version >/dev/null 2>&1; then log "WARNING: Bookworm binary incompatible with this system (likely glibc mismatch)." log "Attempting full dpkg install with dependency resolution..." rm -rf "${extract_dir}" # Stop any existing broken service systemctl stop zabbix-agent2 2>/dev/null || true systemctl disable zabbix-agent2 2>/dev/null || true # Try full install — may work if deps can be satisfied dpkg -i --force-depends "${deb_path}" 2>/dev/null || true # Check if binary landed and works if [ -f /usr/sbin/zabbix_agent2 ] && /usr/sbin/zabbix_agent2 --version >/dev/null 2>&1; then log "Package installation succeeded despite warnings." rm -f "${deb_path}" return 0 fi # Clean up the broken install dpkg --remove --force-remove-reinstreq zabbix-agent2 2>/dev/null || true # On armhf, try zabbix-agent (v1) before giving up if [ "${dpkg_arch}" = "armhf" ]; then log "Attempting zabbix-agent (v1) as final fallback..." if install_agent_v1_armhf; then return 0 fi fi log "ERROR: Zabbix Agent 2 binary from Debian 12 is not compatible with this system." log " Your system runs glibc $(ldd --version 2>&1 | head -1 | grep -oP '\\d+\\.\\d+$' || echo 'unknown') but the package requires glibc 2.36+." log " Options:" log " 1. Upgrade to a 64-bit OS (Raspberry Pi OS 64-bit): https://www.raspberrypi.com/software/" log " 2. Use Zabbix Agent (v1) instead: apt install zabbix-agent" log " 3. Build from source: https://www.zabbix.com/documentation/current/en/manual/installation/install" rm -f "${deb_path}" exit 1 fi # Binary works! Install files to their proper locations. log "Binary compatibility verified. Installing files..." # Create zabbix user if it doesn't exist if ! id -u zabbix &>/dev/null; then useradd -r -s /sbin/nologin -d /var/lib/zabbix -M zabbix fi # Copy binary cp "${agent_bin}" /usr/sbin/zabbix_agent2 chmod +x /usr/sbin/zabbix_agent2 # Copy config files and directories mkdir -p /etc/zabbix if [ -d "${extract_dir}/etc/zabbix" ]; then cp -rn "${extract_dir}/etc/zabbix/"* /etc/zabbix/ 2>/dev/null || true fi # Set up directories mkdir -p /var/log/zabbix /var/run/zabbix /var/lib/zabbix chown -R zabbix:zabbix /var/log/zabbix /var/run/zabbix /var/lib/zabbix # Install systemd service from package or create our own if [ -f "${extract_dir}/lib/systemd/system/zabbix-agent2.service" ]; then cp "${extract_dir}/lib/systemd/system/zabbix-agent2.service" /lib/systemd/system/ systemctl daemon-reload else create_systemd_service fi rm -rf "${extract_dir}" "${deb_path}" log "Installation complete." } install_from_tarball() { local tarball_path="$1" log "Extracting tarball..." # Create directories mkdir -p "${INSTALL_DIR}/bin" mkdir -p /etc/zabbix mkdir -p /var/log/zabbix mkdir -p /var/run/zabbix # Extract tar -xzf "${tarball_path}" -C "${INSTALL_DIR}" --strip-components=1 2>/dev/null || \ tar -xzf "${tarball_path}" -C "${INSTALL_DIR}" 2>/dev/null # Find the binary local agent_bin agent_bin=$(find "${INSTALL_DIR}" -name "zabbix_agent2" -type f 2>/dev/null | head -1) if [ -z "${agent_bin}" ]; then log "ERROR: Could not find zabbix_agent2 binary in extracted archive." exit 1 fi # Move binary to expected location cp "${agent_bin}" "${INSTALL_DIR}/bin/zabbix_agent2" chmod +x "${INSTALL_DIR}/bin/zabbix_agent2" ln -sf "${INSTALL_DIR}/bin/zabbix_agent2" /usr/sbin/zabbix_agent2 # Create zabbix user if it doesn't exist if ! id -u zabbix &>/dev/null; then useradd -r -s /sbin/nologin -d /var/lib/zabbix -M zabbix fi chown -R zabbix:zabbix /var/log/zabbix /var/run/zabbix # Create systemd service create_systemd_service rm -f "${tarball_path}" log "Binary installation complete." } create_systemd_service() { log "Creating systemd service..." cat > /etc/systemd/system/zabbix-agent2.service << 'EOF' [Unit] Description=Zabbix Agent 2 After=syslog.target After=network.target [Service] Environment="CONFFILE=/etc/zabbix/zabbix_agent2.conf" Type=simple Restart=on-failure PIDFile=/var/run/zabbix/zabbix_agent2.pid KillMode=control-group ExecStart=/usr/sbin/zabbix_agent2 -c $CONFFILE ExecStop=/bin/kill -SIGTERM $MAINPID RestartSec=10s User=zabbix Group=zabbix [Install] WantedBy=multi-user.target EOF systemctl daemon-reload } generate_psk() { openssl rand -hex 32 } configure_agent() { local psk_key="$1" log "Writing PSK to ${PSK_FILE}..." echo "${psk_key}" > "${PSK_FILE}" chmod 640 "${PSK_FILE}" chown root:zabbix "${PSK_FILE}" log "Writing agent configuration to ${AGENT_CONF}..." # Back up existing config if present [ -f "${AGENT_CONF}" ] && cp "${AGENT_CONF}" "${AGENT_CONF}.bak.$(date +%s)" if [ "${USING_AGENT_V1}" = true ]; then # Zabbix Agent (v1) configuration — slightly different directives cat > "${AGENT_CONF}" << EOF # Zabbix Agent (v1) Configuration # Auto-generated by ARM deployment script on $(date '+%Y-%m-%d %H:%M:%S') # Architecture: ${ARCH} (${ARCH_LABEL}) # Note: Agent v1 installed because Agent 2 is not available for armhf Server=${ZABBIX_SERVER} ServerActive=${ZABBIX_SERVER} HostnameItem=system.hostname HostMetadata=${HOST_METADATA} # PSK Encryption TLSConnect=psk TLSAccept=psk TLSPSKIdentity=${PSK_IDENTITY} TLSPSKFile=${PSK_FILE} # Logging LogFile=/var/log/zabbix/zabbix_agentd.log LogFileSize=10 # Performance BufferSend=5 BufferSize=100 EOF else cat > "${AGENT_CONF}" << EOF # Zabbix Agent 2 Configuration # Auto-generated by ARM deployment script on $(date '+%Y-%m-%d %H:%M:%S') # Architecture: ${ARCH} (${ARCH_LABEL}) Server=${ZABBIX_SERVER} ServerActive=${ZABBIX_SERVER} HostnameItem=system.hostname HostMetadata=${HOST_METADATA} # PSK Encryption TLSConnect=psk TLSAccept=psk TLSPSKIdentity=${PSK_IDENTITY} TLSPSKFile=${PSK_FILE} # Logging LogFile=/var/log/zabbix/zabbix_agent2.log LogFileSize=10 # Performance BufferSend=5 BufferSize=100 EOF fi chown root:zabbix "${AGENT_CONF}" chmod 644 "${AGENT_CONF}" } start_agent() { local service_name="zabbix-agent2" if [ "${USING_AGENT_V1}" = true ]; then service_name="zabbix-agent" fi log "Enabling and starting ${service_name}..." systemctl enable "${service_name}" systemctl restart "${service_name}" sleep 2 if systemctl is-active --quiet "${service_name}"; then log "${service_name} is running." systemctl status "${service_name}" --no-pager else log "WARNING: Agent may not have started. Check logs:" log " journalctl -u ${service_name} -n 20" if [ "${USING_AGENT_V1}" = true ]; then log " cat /var/log/zabbix/zabbix_agentd.log" else log " cat /var/log/zabbix/zabbix_agent2.log" fi fi } # --- Main --- if [ "$(id -u)" -ne 0 ]; then echo "This script must be run as root." >&2 exit 1 fi PSK_KEY="${1:-}" if [ -z "${PSK_KEY}" ]; then PSK_KEY=$(generate_psk) log "Generated new PSK key." fi # Validate PSK is valid hex and at least 32 chars if ! echo "${PSK_KEY}" | grep -qE '^[0-9a-fA-F]{32,128}$'; then log "ERROR: PSK must be a 32-128 character hex string." exit 1 fi detect_arch log "=== Zabbix Agent 2 Deployment (ARM) ===" log "Server: ${ZABBIX_SERVER}" log "PSK Identity: ${PSK_IDENTITY}" log "Architecture: ${ARCH_LABEL}" install_agent_package_manager configure_agent "${PSK_KEY}" start_agent log "=== Deployment Complete ===" if [ "${USING_AGENT_V1}" = true ]; then log "Agent Version: Zabbix Agent (v1) — agent2 not available for ${ARCH_LABEL}" else log "Agent Version: Zabbix Agent 2" fi log "PSK Identity: ${PSK_IDENTITY}" log "PSK Key: ${PSK_KEY}" log "" log "IMPORTANT: Use this same PSK identity and key in your Zabbix server" log "auto-registration encryption settings."