From 96dea2fd7905f796a5a939e30f10662e35e9fd58 Mon Sep 17 00:00:00 2001 From: Martin Raspaud Date: Wed, 22 May 2024 09:16:52 +0200 Subject: [PATCH] Add script for generating keys and documentation --- doc/source/index.rst | 49 ++++++++++++++++++++++ posttroll/backends/zmq/__init__.py | 22 ++++++++++ posttroll/tests/test_secure_zmq_backend.py | 11 +++++ pyproject.toml | 1 + 4 files changed, 83 insertions(+) diff --git a/doc/source/index.rst b/doc/source/index.rst index b556936..7ff0e12 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -150,6 +150,55 @@ relevant socket options. .. _zmq_setsockopts: http://api.zeromq.org/master:zmq-setsockopt +Using secure ZeroMQ backend +--------------------------- + +To use securely authenticated sockets with posttroll (uses ZMQ's curve authentication), the backend needs to be defined +through posttroll config system, for example using an environment variable:: + + POSTTROLL_BACKEND=secure_zmq + +On the server side (for example a publisher), we need to define the server's secret key and the directory where the +accepted client keys are provided:: + + POSTTROLL_SERVER_SECRET_KEY_FILE=/path/to/server.key_secret + POSTTROLL_PUBLIC_SECRET_KEYS_DIRECTORY=/path/to/client_public_keys/ + +On the client side (for example a subscriber), we need to define the server's public key file and the client's secret +key file:: + + POSTTROLL_CLIENT_SECRET_KEY_FILE=/path/to/client.key_secret + POSTTROLL_SERVER_PUBLIC_KEY_FILE=/path/to/server.key + +These settings can also be set using the posttroll config object, for example:: + + >>> from posttroll import config + >>> with config.set(backend="secure_zmq", server_pubic_key_file="..."): + ... + +The posttroll configuration uses donfig, for more information, check https://donfig.readthedocs.io/en/latest/. + + +Generating the public and secret key pairs +****************************************** + +In order for the secure ZMQ backend to work, public/secret key pairs need to be generated, one for the client side and +one for the server side. A command-line script is provided for this purpose:: + + > posttroll-generate-keys -h + usage: posttroll-generate-keys [-h] [-d DIRECTORY] name + + Create a public/secret key pair for the secure zmq backend. This will create two files (in the current directory if not otherwise specified) with the suffixes '.key' and '.key_secret'. The name of the files will be the one provided. + + positional arguments: + name Name of the file. + + options: + -h, --help show this help message and exit + -d DIRECTORY, --directory DIRECTORY + Directory to place the keys in. + + Converting from older posttroll versions ---------------------------------------- diff --git a/posttroll/backends/zmq/__init__.py b/posttroll/backends/zmq/__init__.py index d59a127..c943737 100644 --- a/posttroll/backends/zmq/__init__.py +++ b/posttroll/backends/zmq/__init__.py @@ -1,8 +1,11 @@ """Main module for the zmq backend.""" +import argparse import logging import os +from pathlib import Path import zmq +from zmq.auth.certs import create_certificates from posttroll import config @@ -21,17 +24,20 @@ def get_context(): logger.debug("renewed context for PID %d", pid) return context[pid] + def destroy_context(linger=None): """Destroy the context.""" pid = os.getpid() context.pop(pid).destroy(linger) + def _set_tcp_keepalive(socket): """Set the tcp keepalive parameters on *socket*.""" keepalive_options = get_tcp_keepalive_options() for param, value in keepalive_options.items(): socket.setsockopt(param, value) + def get_tcp_keepalive_options(): """Get the tcp_keepalive options from config.""" keepalive_options = dict() @@ -46,3 +52,19 @@ def get_tcp_keepalive_options(): param = getattr(zmq, opt.upper()) keepalive_options[param] = value return keepalive_options + + +def generate_keys(args=None): + """Generate a public/secret key pair.""" + parser = argparse.ArgumentParser( + prog="posttroll-generate-keys", + description=("Create a public/secret key pair for the secure zmq backend. This will create two " + "files (in the current directory if not otherwise specified) with the suffixes '.key'" + " and '.key_secret'. The name of the files will be the one provided.")) + + parser.add_argument("name", type=str, help="Name of the file.") + parser.add_argument("-d", "--directory", help="Directory to place the keys in.", default=".", type=Path) + + parsed = parser.parse_args(args) + + create_certificates(parsed.directory, parsed.name) diff --git a/posttroll/tests/test_secure_zmq_backend.py b/posttroll/tests/test_secure_zmq_backend.py index 01d7f97..74619c9 100644 --- a/posttroll/tests/test_secure_zmq_backend.py +++ b/posttroll/tests/test_secure_zmq_backend.py @@ -161,3 +161,14 @@ def test_switch_to_secure_backend_for_nameserver(tmp_path): with create_nameserver_instance(): res = get_pub_address("some_name") assert res == "" + + + +def test_create_certificates_cli(tmp_path): + """Test the certificate creation cli.""" + from posttroll.backends.zmq import generate_keys + name = "server" + args = [name, "-d", str(tmp_path)] + generate_keys(args) + assert (tmp_path / (name + ".key")).exists() + assert (tmp_path / (name + ".key_secret")).exists() diff --git a/pyproject.toml b/pyproject.toml index 5f1c8e3..09c9663 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ [project.scripts] pytroll-logger = "posttroll.logger:run" +posttroll-generate-keys = "posttroll.backends.zmq:generate_keys" [project.urls] Homepage = "https://github.com/pytroll/posttroll"