Overview

Every post in this series so far has had agents talking to proxies, proxies talking to servers, and the API answering Powershell scripts all in clear text. That's fine on a trusted segment, but the moment a single hop crosses an untrusted network it needs to be encrypted.

This guide assumes you have working agents and proxies (from the earlier posts) and that you can edit their config files and restart the services.

Zabbix supports two encryption modes for every agent-proxy-server hop:

Mode Pros Cons
PSK Trivial to deploy, no PKI needed Per-host key management at scale
TLS Standard PKI, central revocation Requires a CA and cert distribution

A common pattern: PSK for agents (lots of hosts, simple to script) and TLS certificates for proxies and the API (few endpoints, benefits from PKI). That's what we'll build.

Part 1: PSK for Agents

A PSK is just an identity string + a hex-encoded random secret. Both ends must have the same value.

1. Generate a PSK

On any Linux host (or WSL):

openssl rand -hex 32 > /tmp/zbx-psk.key

This produces a 64-character hex string the minimum length Zabbix accepts is 32 characters, but 64 is a sensible default.

Treat this like a password. Store it in your secrets manager (Vault, Azure Key Vault, AWS Secrets Manager) and pull it from there at agent-install time.

2. Configure the Agent (Linux)

Copy the key to the agent and lock it down:

sudo mkdir -p /etc/zabbix/psk
sudo mv /tmp/zbx-psk.key /etc/zabbix/psk/agent.psk
sudo chown zabbix:zabbix /etc/zabbix/psk/agent.psk
sudo chmod 600 /etc/zabbix/psk/agent.psk

Edit /etc/zabbix/zabbix_agentd.conf:

TLSConnect=psk
TLSAccept=psk
TLSPSKIdentity=PSK-lnx-app01
TLSPSKFile=/etc/zabbix/psk/agent.psk
sudo systemctl restart zabbix-agent

3. Configure the Agent (Windows) with Powershell

The same idea, scripted. The function below fetches a PSK from a file (swap in your secrets-manager call) and patches the agent config in place.

<#
.SYNOPSIS
    Enables PSK encryption on a Zabbix Agent 2 install.
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)] [string]$Identity,        # e.g. PSK-WIN-APP01
    [Parameter(Mandatory)] [string]$PskHex,          # 64-char hex string
    [string]$ConfigPath = 'C:\Program Files\Zabbix Agent\zabbix_agentd.conf',
    [string]$PskPath = 'C:\Program Files\Zabbix Agent\agent.psk',
    [string]$ServiceName = 'Zabbix Agent'
)
begin
{
    $ErrorActionPreference = 'Stop'

    # 1. Write the PSK file with strict ACLs (SYSTEM + Zabbix service account only)
    Set-Content -Path $PskPath -Value $PskHex -Encoding ASCII -NoNewline
    function Set-ConfigValue
    {
        param
        (
            [Parameter()]
            [string[]]$Lines, 
            
            [Parameter()]
            [string]$Key, 
            
            [Parameter()]
            [string]$Value
        )
        process
        {
            $pattern = "^\s*#?\s*$Key\s*="
            $newLine = "$Key=$Value"
            if ($Lines -match $pattern) 
            { 
                return $Lines -replace $pattern + '.*', $newLine 
            }
            return $Lines + $newLine
        }
    }
}
process
{
    $acl = New-Object System.Security.AccessControl.FileSecurity
    $acl.SetAccessRuleProtection($true, $false)              # disable inheritance
    foreach ($principal in 'NT AUTHORITY\SYSTEM', 'BUILTIN\Administrators')
    {
        $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
            $principal, 'FullControl', 'Allow')
        $acl.AddAccessRule($rule)
    }
    Set-Acl -Path $PskPath -AclObject $acl

    # 2. Patch the config (idempotent - replaces existing keys)

    $lines = Get-Content -Path $ConfigPath
    $lines = Set-ConfigValue -Lines $lines -Key 'TLSConnect' -Value 'psk'
    $lines = Set-ConfigValue -Lines $lines -Key 'TLSAccept' -Value 'psk'
    $lines = Set-ConfigValue -Lines $lines -Key 'TLSPSKIdentity' -Value $Identity
    $lines = Set-ConfigValue -Lines $lines -Key 'TLSPSKFile' -Value $PskPath
    Set-Content -Path $ConfigPath -Value $lines -Encoding ASCII

    Restart-Service -Name $ServiceName
}

Run it as:

.\Enable-ZabbixPsk.ps1 -Identity 'PSK-WIN-APP01' -PskHex (Get-Content .\agent.psk -Raw).Trim()

4. Configure the Host in the Frontend

Go to Data Collection -> Hosts -> host -> Encryption tab:

  • Connections to host: PSK
  • Connections from host: tick PSK only
  • PSK identity: PSK-WIN-APP01 (must match the agent)
  • PSK: paste the same 64-character hex string

Save and wait one minute. The host's Availability indicator turns green again, and Reports -> Audit log will show the encryption change.

If the host stays red, check zabbix_agentd.log on the host. The two most common errors are invalid PSK identity (mismatched identity strings) and invalid PSK length (file has a trailing newline NoNewline matters).

5. Verify the Connection Is Encrypted

From the proxy or server:

zabbix_get -s 10.0.0.50 -k 'agent.ping' \
           --tls-connect=psk \
           --tls-psk-identity='PSK-WIN-APP01' \
           --tls-psk-file=/etc/zabbix/psk/win-app01.psk

A successful reply (1) confirms the agent only accepts PSK-encrypted requests. Try the same call without the TLS flags it should fail.

6. Automating PSK Rollout

Pair the script above with the autoregistration flow from the earlier post:

  1. Pull a fresh 64-char hex from your secrets manager during VM provisioning.
  2. Inject TLSPSKIdentity into HostMetadata (e.g. windows-prod|PSK-WIN-APP01).
  3. Run the PSK script as part of the Zabbix-agent install.
  4. After autoregistration, use the API (Powershell wrapper from the proxy load-balancing post) to set the tls_connect, tls_accept, tls_psk_identity, and tls_psk fields on the new host.

That gives you fully encrypted agents from first boot no UI clicks.

Part 2: TLS Certificates for Proxies and Server

PSK works fine for thousands of agents, but for the handful of proxies and especially for cross-site links TLS certificates are easier to rotate and easier to revoke.

1. Issue a Cert From Your CA

For a proxy named ZbxProxy01.example.com, generate a CSR and have your internal CA sign it. The cert needs:

  • Subject CN: ZbxProxy01.example.com (or whatever you'll use as TLSServerCertIssuer/Subject)
  • Extended Key Usage: serverAuth, clientAuth Zabbix uses the same cert for both directions

Drop the resulting files on the proxy:

sudo install -o zabbix -g zabbix -m 600 proxy01.crt /etc/zabbix/ssl/proxy01.crt
sudo install -o zabbix -g zabbix -m 600 proxy01.key /etc/zabbix/ssl/proxy01.key
sudo install -o zabbix -g zabbix -m 644 ca.crt      /etc/zabbix/ssl/ca.crt

2. Configure the Proxy

Edit /etc/zabbix/zabbix_proxy.conf:

TLSConnect=cert
TLSAccept=cert
TLSCAFile=/etc/zabbix/ssl/ca.crt
TLSCertFile=/etc/zabbix/ssl/proxy01.crt
TLSKeyFile=/etc/zabbix/ssl/proxy01.key
TLSServerCertIssuer=CN=Internal Issuing CA,O=Example,C=US
TLSServerCertSubject=CN=zabbix.example.com,O=Example,C=US
sudo systemctl restart zabbix-proxy

3. Configure the Server Side

Same idea, but on the Zabbix server itself. Then in the frontend:

Administration -> Proxies -> proxy -> Encryption tab:

  • Connections to proxy: Certificate
  • Connections from proxy: tick Certificate
  • Issuer and Subject: optional but strongly recommended they pin the exact cert that's allowed.

Pinning issuer and subject means a stolen cert from another internal service won't work even if it was signed by the same CA. Skip pinning only if you understand what you're giving up.

4. Verify

Watch the proxy log for the handshake:

sudo tail -f /var/log/zabbix/zabbix_proxy.log | grep -i tls

You want a line like TLS handshake with [10.0.0.21:10051] successful. If it loops on failed, mismatched issuer/subject is almost always the cause.

Operational Notes

  • Rotate PSKs annually. Generate a new key, push it to the host, update the frontend via the API, restart the agent. The script above is idempotent re-running it with a new PSK is the rotation procedure.
  • Monitor cert expiry. Add a system.run[openssl x509 -enddate -noout -in /etc/zabbix/ssl/proxy01.crt] item with a trigger 30 days before expiry. Or even better use the LLD pattern from the previous post to discover all certs in /etc/zabbix/ssl/ and graph their expiry dates.
  • Don't mix modes mid-flight. Set TLSAccept=psk,unencrypted temporarily during rollout, then drop unencrypted once every host is migrated. Leaving both on permanently defeats the purpose.

What to Do Next

PSK gives you a fast, scriptable way to encrypt the long tail of agents; TLS certificates give you proper PKI for the few high-value links between proxies and the server. Use both. PSK on hosts where rotating a 256-bit hex string is the simplest possible operation, and certs on the inter-tier links where revocation, expiry monitoring, and CA chains earn their keep.

Three concrete moves to harden your fleet this week:

  1. Stand up TLSAccept=psk,unencrypted for the rollout window. Don't try to flip the entire fleet at once. The dual-mode setting lets old and new agents coexist while you migrate; remove unencrypted only after you've verified every host reports as PSK-connected.
  2. Add a cert-expiry trigger today, not "soon". A system.run[openssl x509 -enddate -noout -in /etc/zabbix/ssl/proxy01.crt] item plus a 30-day-before-expiry trigger has saved more outages than the actual encryption has prevented. Schedule the trigger as part of the cert rollout itself so it's never forgotten.
  3. Script PSK rotation as the deployment. Generate a new key, push it to the host, update the frontend via the API, restart the agent. The same script is the rollout and the rotation. If the rollout script doesn't survive being re-run, neither will your annual rotation.

Pairs naturally with the templates as code post (so PSK assignments and TLS settings are version-controlled, not clicked into the UI) and the autoregistration post (so new hosts come up encrypted on the first connection, not after a cleanup pass).