Skip to content

Encrypt the agent to Zabbix channel with TLS (PSK or certificate) - #235

Open
kroxiksut wants to merge 2 commits into
postgrespro:masterfrom
kroxiksut:zbx-tls-psk
Open

Encrypt the agent to Zabbix channel with TLS (PSK or certificate)#235
kroxiksut wants to merge 2 commits into
postgrespro:masterfrom
kroxiksut:zbx-tls-psk

Conversation

@kroxiksut

Copy link
Copy Markdown

What

Adds optional TLS encryption for the agent to Zabbix server channel, the same two modes the stock
Zabbix agent supports: a pre-shared key (psk) and certificates (cert).

It is configured by new tls_* parameters in the [zabbix] section and the matching
--zabbix-tls-* command line options; packaging/conf/example_linux.conf carries a full example.

[zabbix]
tls_connect = psk
tls_psk_identity = PSK db1
tls_psk_file = /etc/zabbix/zabbix_agentd.psk

Why

A Zabbix server with No encryption disabled for a host cannot accept anything from mamonsu today,
so on such installations the agent has to be replaced by scripts around zabbix_sender, even though
zabbix_sender itself works fine with the very same PSK. This closes that gap.

Compatibility

tls_connect = unencrypted is the default. With none of the new parameters set the behaviour is
exactly that of 3.5.17: the same plain socket, the same frame byte for byte, the same failed: N
parsing. No new runtime dependencies. Version bumped to 3.5.17.1.

Implementation notes

  • mamonsu/lib/senders/tls.py is a new module holding the two transports; zbx.py only picks one
    in _setup_tls() / _connect(). The wire format and the queue logic are untouched.
  • PSK: on Python 3.13+ the standard ssl module; on 3.7 to 3.12 libssl through ctypes, using only
    the public OpenSSL API, with no CPython internals involved. Pinned to TLS 1.2.
  • cert: pure stdlib, verification against the CA plus pinning by issuer and subject. The hostname is
    deliberately not checked, matching what the Zabbix agent does.
  • On a bad configuration the sender stops with an error in the log. There is no fallback to
    plaintext, neither in the daemon nor in mamonsu upload.
  • The PSK is read once at startup and never reaches the log.

Testing

tests/unit gives 35 passed, 2 skipped. The tests need neither docker nor Zabbix, and both files can
be run directly with python3 on a monitored host that has no pytest.

They cover a real handshake against openssl s_server in both modes with data going both ways; the
rejection of a wrong PSK, an unknown CA and a mismatching certificate subject; PSK file parsing and
the absence of the secret from error messages; and the unencrypted path staying unchanged.

Run on Linux with Python 3.12 and OpenSSL 3.0.13 (the ctypes path) and on Windows with Python 3.12
(cert mode on the standard library). The psk mode was then piloted on Astra Linux 1.7.6 with
Python 3.7 and OpenSSL 1.1.1, the oldest combination the ctypes path is meant to cover, with metrics
reaching a Zabbix 7.4 server.

Known limitations

  • TLS 1.3 with a PSK is not implemented (psk_use_session); the connection is pinned to TLS 1.2.
  • The PSK file is read once at startup, so the agent has to be restarted after the key is rotated.
  • On Windows psk needs Python 3.13 or newer, as there is no system libssl there.
  • cert mode has been verified against openssl s_server, not against a production Zabbix server.

TLS_ENCRYPTION.md documents the setup, both modes and the limitations in full.

…ificate, configured by the new [zabbix] tls_* parameters and --zabbix-tls-* options; without them the plain socket of 3.5.17 is kept, version bumped to 3.5.17.1
…1.1.1 in TLS_ENCRYPTION.md, replacing the note that the ctypes path had not been run there
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant