ZAUTH(3)
ZAUTH(3) β fresh from the CZMQ Manual, and bestie today we are doing authentication. Locking the door, checking the guest list, the whole vibe. πβ¨
π NAME π
zauth β the class for authentication for ZeroMQ security mechanisms. That's it, that's the whole job, and she does it flawlessly. π
β¨ SYNOPSIS β¨
Okay first, the magic string you'll need later:
#define CURVE_ALLOW_ANY "*"
This is the CZMQ v3 API, so it's for zsock. Not zsocket β zsocket is deprecated, we do not speak of her, no cap. π
π± Creating a zauth actor
Make a brand new zauth actor instance. Doing this installs authentication on all zsock sockets. And here's the tea: until you actually add policies, all incoming NULL connections are allowed (that's just classic ZeroMQ behaviour, not us being messy), while all PLAIN and CURVE connections get denied.
zactor_t *auth = zactor_new (zauth, NULL);
π Destroying it
Destroy the zauth instance. This removes authentication entirely and lets every connection pass through, no authentication whatsoever:
zactor_destroy (&auth);
Heads up bestie: every zauth command is synchronous, so your application always waits for a signal back from the actor after each command. It's giving very "text me when you get home" energy. π«Ά
π Verbose mode
Turn on verbose logging of commands and activity. This is genuinely so helpful when you're debugging non-trivial authentication policies, trust:
zstr_send (auth, "VERBOSE");
zsock_wait (auth);
β ALLOW β the guest list
Allow a list of IP addresses. For NULL, all clients coming from those addresses will be accepted. For PLAIN and CURVE, they'll be allowed to continue on with authentication. You can call this multiple times to allow more IP addresses. And if you allow one or more addresses, then any address that's not allowed gets treated as blocked. Periodt.
zstr_sendx (auth, "ALLOW", "127.0.0.1", "127.0.0.2", NULL);
zsock_wait (auth);
π« DENY β the block list
Deny (block) a list of IP addresses. For all security mechanisms, this rejects the connection with no further authentication at all. Use either an allowed list or a blocked list β not not both. π And if you define both an allowed list and a blocked list, only the allowed list takes effect.
zstr_sendx (auth, "DENY", "192.168.0.1", "192.168.0.2", NULL);
zsock_wait (auth);
π PLAIN authentication
Configure PLAIN authentication using a plain-text password file. You can modify that password file whenever you want β zauth will reload it automatically if it's been modified externally. She's self-sufficient, we love that for her. π
zstr_sendx (auth, "PLAIN", filename, NULL);
zsock_wait (auth);
π¦ CURVE authentication
Configure CURVE authentication using a directory that holds all the public client certificates, i.e. their public keys. Those certificates must be in zcert_save format. You can add and remove certificates in that directory at any time. And if you want to allow all client keys without checking anything, pass CURVE_ALLOW_ANY as the directory name. π«£
zstr_sendx (auth, "CURVE", directory, NULL);
zsock_wait (auth);
π§ GSSAPI authentication
Configure GSSAPI authentication, using an underlying mechanism (usually Kerberos) to establish a secure context and perform mutual authentication. Mutual! Both sides checking each other! Iconic. π
zstr_sendx (auth, "GSSAPI", NULL);
zsock_wait (auth);
ποΈ The constructor, as a zactor_fn
This is the zauth constructor as a zactor_fn:
CZMQ_EXPORT void
zauth (zsock_t *pipe, void *certstore);
π§ͺ Selftest
CZMQ_EXPORT void
zauth_test (bool verbose);
π Please add '@interface' section in './../src/zauth.c'.
π DESCRIPTION π
A zauth actor takes over authentication for all incoming connections in its context. You can allow or block peers based on IP address, and you can define policies for securing PLAIN, CURVE, and GSSAPI connections. Very main character, very "I run this building". π’β¨
This class replaces zauth_v2, and it's meant for applications that use the CZMQ v3 API β meaning, zsock. π¦
π EXAMPLE π
okay bestie so this is the example straight out of zauth_test β like, the actual method the test suite runs, and it's lowkey a whole journey. it's long, it's giving "final boss of selftests", and every single line is below because we do NOT gatekeep. read it like a story: auth goes on, auth goes off, and the vibes get checked at every step. π
const char *SELFTEST_DIR_RW = "src/selftest-rw";
const char *testbasedir = ".test_zauth";
const char *testpassfile = "password-file";
const char *testcertfile = "mycert.txt";
char *basedirpath = NULL; // subdir in a test, under SELFTEST_DIR_RW
char *passfilepath = NULL; // pathname to testfile in a test, in dirpath
char *certfilepath = NULL; // pathname to testfile in a test, in dirpath
basedirpath = zsys_sprintf ("%s/%s", SELFTEST_DIR_RW, testbasedir);
assert (basedirpath);
passfilepath = zsys_sprintf ("%s/%s", basedirpath, testpassfile);
assert (passfilepath);
certfilepath = zsys_sprintf ("%s/%s", basedirpath, testcertfile);
assert (certfilepath);
// Make sure old aborted tests do not hinder us
zdir_t *dir = zdir_new (basedirpath, NULL);
if (dir) {
zdir_remove (dir, true);
zdir_destroy (&dir);
}
zsys_file_delete (passfilepath);
zsys_file_delete (certfilepath);
zsys_dir_delete (basedirpath);
// Create temporary directory for test files
zsys_dir_create (basedirpath);
// Check there's no authentication
zsock_t *server = zsock_new (ZMQ_PULL);
assert (server);
zsock_t *client = zsock_new (ZMQ_PUSH);
assert (client);
bool success = s_can_connect (&server, &client, true);
assert (success);
// Install the authenticator
zactor_t *auth = zactor_new (zauth, NULL);
assert (auth);
if (verbose) {
zstr_sendx (auth, "VERBOSE", NULL);
zsock_wait (auth);
}
// Check there's no authentication on a default NULL server
success = s_can_connect (&server, &client, true);
assert (success);
// When we set a domain on the server, we switch on authentication
// for NULL sockets, but with no policies, the client connection
// will be allowed.
zsock_set_zap_domain (server, "global");
success = s_can_connect (&server, &client, true);
assert (success);
// Block 127.0.0.1, connection should fail
zsock_set_zap_domain (server, "global");
zstr_sendx (auth, "DENY", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (!success);
// Allow our address, which overrides the block list
zsock_set_zap_domain (server, "global");
zstr_sendx (auth, "ALLOW", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (success);
// Try PLAIN authentication
zsock_set_zap_domain (server, "global");
zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Password");
success = s_can_connect (&server, &client, true);
assert (!success);
FILE *password = fopen (passfilepath, "w");
assert (password);
fprintf (password, "admin=Password\n");
fclose (password);
zsock_set_zap_domain (server, "global");
zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Password");
zstr_sendx (auth, "PLAIN", passfilepath, NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, false);
assert (success);
#if (ZMQ_VERSION >= ZMQ_MAKE_VERSION (4, 1, 0))
// Test that the User-Id metadata is present
zframe_t *frame = zframe_recv (server);
assert (frame != NULL);
const char *user_id = zframe_meta (frame, "User-Id");
assert (user_id != NULL);
assert (streq (user_id, "admin"));
zframe_destroy (&frame);
#endif
s_renew_sockets(&server, &client);
zsock_set_zap_domain (server, "global");
zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Bogus");
success = s_can_connect (&server, &client, true);
assert (!success);
if (zsys_has_curve ()) {
// Try CURVE authentication
// We'll create two new certificates and save the client public
// certificate on disk; in a real case we'd transfer this securely
// from the client machine to the server machine.
zcert_t *server_cert = zcert_new ();
assert (server_cert);
zcert_t *client_cert = zcert_new ();
assert (client_cert);
const char *server_key = zcert_public_txt (server_cert);
// Test without setting-up any authentication
zcert_apply (server_cert, server);
zcert_apply (client_cert, client);
zsock_set_curve_server (server, 1);
zsock_set_curve_serverkey (client, server_key);
zsock_set_zap_domain (server, "global");
success = s_can_connect (&server, &client, true);
assert (!success);
// Test CURVE_ALLOW_ANY
zcert_apply (server_cert, server);
zcert_apply (client_cert, client);
zsock_set_curve_server (server, 1);
zsock_set_curve_serverkey (client, server_key);
zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (success);
// Test full client authentication using certificates
zcert_set_meta (client_cert, "Hello", "%s", "World!");
zcert_apply (server_cert, server);
zcert_apply (client_cert, client);
zsock_set_curve_server (server, 1);
zsock_set_curve_serverkey (client, server_key);
zcert_save_public (client_cert, certfilepath);
zstr_sendx (auth, "CURVE", basedirpath, NULL);
zsock_wait (auth);
zsock_set_zap_domain (server, "global");
success = s_can_connect (&server, &client, false);
assert (success);
#if (ZMQ_VERSION >= ZMQ_MAKE_VERSION (4, 1, 0))
// Test send/recv certificate metadata
zframe_t *frame = zframe_recv (server);
assert (frame != NULL);
const char *meta = zframe_meta (frame, "Hello");
assert (meta != NULL);
assert (streq (meta, "World!"));
const char *user_id = zframe_meta (frame, "User-Id");
assert (user_id != NULL);
assert (streq (user_id, zcert_public_txt(client_cert)));
zframe_destroy (&frame);
s_renew_sockets(&server, &client);
#endif
zcert_destroy (&server_cert);
zcert_destroy (&client_cert);
// Test custom zcertstore
zcertstore_t *certstore = zcertstore_new (NULL);
zcertstore_set_loader (certstore, s_test_loader, NULL, NULL);
zactor_destroy(&auth);
auth = zactor_new (zauth, certstore);
assert (auth);
if (verbose) {
zstr_sendx (auth, "VERBOSE", NULL);
zsock_wait (auth);
}
byte public_key [32] = { 105, 76, 150, 58, 214, 191, 218, 65, 50, 172,
131, 188, 247, 211, 136, 170, 227, 26, 57, 170,
185, 63, 246, 225, 177, 230, 12, 8, 134, 136,
105, 106 };
byte secret_key [32] = { 245, 217, 172, 73, 106, 28, 195, 17, 218, 132,
135, 209, 99, 240, 98, 232, 7, 137, 244, 100,
242, 23, 29, 114, 70, 223, 83, 1, 113, 207,
132, 149 };
zcert_t *shared_cert = zcert_new_from (public_key, secret_key);
assert (shared_cert);
zcert_apply (shared_cert, server);
zcert_apply (shared_cert, client);
zsock_set_curve_server (server, 1);
zsock_set_curve_serverkey (client, "x?T*N/1Y{8goubv{Ts}#&#f}TXJ//DVe#D2HkoLU");
success = s_can_connect (&server, &client, true);
assert (success);
zcert_destroy (&shared_cert);
}
// Remove the authenticator and check a normal connection works
zactor_destroy (&auth);
success = s_can_connect (&server, &client, true);
assert (success);
zsock_destroy (&client);
zsock_destroy (&server);
// Delete all test files
dir = zdir_new (basedirpath, NULL);
assert (dir);
zdir_remove (dir, true);
zdir_destroy (&dir);
zstr_free (&passfilepath);
zstr_free (&certfilepath);
zstr_free (&basedirpath);
#endif
#if defined (__WINDOWS__)
zsys_shutdown();
#endif
like. it's giving comprehensive. it's giving "she did her homework and then some". the DENY then ALLOW on 127.0.0.1, the PLAIN with a real password-file written by hand, the User-Id metadata grab, CURVE_ALLOW_ANY, the full cert dance, the custom zcertstore with s_test_loader and a hardcoded public_key/secret_key pair, and then the grand finale of just destroying the authenticator and proving a normal connection works again. periodt. π
π AUTHORS π
the czmq manual was written by the authors in the AUTHORS file. go stan them, they ate. π«Ά
π RESOURCES π
Main web site:
Report bugs to the email zeromq-dev@lists.zeromq.org[1] β yep, that's the spot for your bug reports, bestie. don't be shy, but also pls be specific. π
π COPYRIGHT π
Copyright (c) the Contributors as noted in the AUTHORS file. This file is part of CZMQ, the high-level C binding for 0MQ: http://czmq.zeromq.org. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. LICENSE included with the czmq distribution.
okay so legally this is the serious part and I need you to actually read it: MPL v2.0, source code form, and the license is in the czmq distribution. no skipping. π
πΈ NOTES πΈ
zeromq-dev@lists.zeromq.orgmailto:zeromq-dev@lists.zeromq.org
and that's the whole page, bestie!! π this was czmq 4.2.1, dated 01/16/2026 β yes that's the footer, and yes I'm telling you about it instead of copying it, because we're cute like that. go forth, authenticate responsibly, and remember: no cap, you understood all of this. π
β¨
π¦ where this page comes from
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- ubuntu resolute: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- ubuntu stonking: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms