def key_housekeeping(reg, platform=None, create=True):

    """Clean any existing authentication keys and create new ones.
        If create is set to false, keys will only be cleaned from
        server."""
    workflow_srv_dir = get_workflow_srv_dir(reg)
    keys = {
        "client_public_key": KeyInfo(
            KeyType.PUBLIC,
            KeyOwner.CLIENT,
            workflow_srv_dir=workflow_srv_dir, install_target=platform),
        "client_private_key": KeyInfo(
            KeyType.PRIVATE,
            KeyOwner.CLIENT,
            workflow_srv_dir=workflow_srv_dir),
        "server_public_key": KeyInfo(
            KeyType.PUBLIC,
            KeyOwner.SERVER,
            workflow_srv_dir=workflow_srv_dir),
        "server_private_key": KeyInfo(
            KeyType.PRIVATE,
            KeyOwner.SERVER,
            workflow_srv_dir=workflow_srv_dir)
    }
    remove_keys_on_server(keys)
    if create:
        create_server_keys(keys, workflow_srv_dir)
Beispiel #2
0
    def _socket_bind(self, min_port, max_port, srv_prv_key_loc=None):
        """Bind socket.

        Will use a port range provided to select random ports.

        """
        if srv_prv_key_loc is None:
            # Create new KeyInfo object for the server private key
            workflow_srv_dir = get_workflow_srv_dir(self.workflow)
            srv_prv_key_info = KeyInfo(KeyType.PRIVATE,
                                       KeyOwner.SERVER,
                                       workflow_srv_dir=workflow_srv_dir)
        else:
            srv_prv_key_info = KeyInfo(KeyType.PRIVATE,
                                       KeyOwner.SERVER,
                                       full_key_path=srv_prv_key_loc)

        # create socket
        self.socket = self.context.socket(self.pattern)
        self._socket_options()

        try:
            server_public_key, server_private_key = zmq.auth.load_certificate(
                srv_prv_key_info.full_key_path)
        except (ValueError):
            raise ServiceFileError(f"Failed to find server's public "
                                   f"key in "
                                   f"{srv_prv_key_info.full_key_path}.")
        except (OSError):
            raise ServiceFileError(f"IO error opening server's private "
                                   f"key from "
                                   f"{srv_prv_key_info.full_key_path}.")
        if server_private_key is None:  # this can't be caught by exception
            raise ServiceFileError(f"Failed to find server's private "
                                   f"key in "
                                   f"{srv_prv_key_info.full_key_path}.")
        self.socket.curve_publickey = server_public_key
        self.socket.curve_secretkey = server_private_key
        self.socket.curve_server = True

        try:
            if min_port == max_port:
                self.port = min_port
                self.socket.bind(f'tcp://*:{min_port}')
            else:
                self.port = self.socket.bind_to_random_port(
                    'tcp://*', min_port, max_port)
        except (zmq.error.ZMQError, zmq.error.ZMQBindError) as exc:
            raise CylcError(f'could not start Cylc ZMQ server: {exc}')

        if self.barrier is not None:
            self.barrier.wait()
Beispiel #3
0
    def _socket_connect(self, host, port, srv_public_key_loc=None):
        """Connect socket to stub."""
        workflow_srv_dir = get_workflow_srv_dir(self.workflow)
        if srv_public_key_loc is None:
            # Create new KeyInfo object for the server public key
            srv_pub_key_info = KeyInfo(KeyType.PUBLIC,
                                       KeyOwner.SERVER,
                                       workflow_srv_dir=workflow_srv_dir)

        else:
            srv_pub_key_info = KeyInfo(KeyType.PUBLIC,
                                       KeyOwner.SERVER,
                                       full_key_path=srv_public_key_loc)

        self.host = host
        self.port = port
        self.socket = self.context.socket(self.pattern)
        self._socket_options()

        client_priv_key_info = KeyInfo(KeyType.PRIVATE,
                                       KeyOwner.CLIENT,
                                       workflow_srv_dir=workflow_srv_dir)
        error_msg = "Failed to find user's private key, so cannot connect."
        try:
            client_public_key, client_priv_key = zmq.auth.load_certificate(
                client_priv_key_info.full_key_path)
        except (OSError, ValueError):
            raise ClientError(error_msg)
        if client_priv_key is None:  # this can't be caught by exception
            raise ClientError(error_msg)
        self.socket.curve_publickey = client_public_key
        self.socket.curve_secretkey = client_priv_key

        # A client can only connect to the server if it knows its public key,
        # so we grab this from the location it was created on the filesystem:
        try:
            # 'load_certificate' will try to load both public & private keys
            # from a provided file but will return None, not throw an error,
            # for the latter item if not there (as for all public key files)
            # so it is OK to use; there is no method to load only the
            # public key.
            server_public_key = zmq.auth.load_certificate(
                srv_pub_key_info.full_key_path)[0]
            self.socket.curve_serverkey = server_public_key
        except (OSError, ValueError):  # ValueError raised w/ no public key
            raise ClientError(
                "Failed to load the workflow's public key, so cannot connect.")

        self.socket.connect(f'tcp://{host}:{port}')
Beispiel #4
0
    def _remote_init_callback(self, proc_ctx, platform, tmphandle, curve_auth,
                              client_pub_key_dir):
        """Callback when "cylc remote-init" exits.

        Write public key for install target into client public key
        directory.
        Set remote_init__map status to REMOTE_INIT_DONE on success which
        in turn will trigger file installation to start.
        Set remote_init_map status to REMOTE_INIT_FAILED on error.

        """
        with suppress(OSError):  # E.g. ignore bad unlink, etc
            tmphandle.close()
        install_target = platform['install target']
        if proc_ctx.ret_code == 0 and "KEYSTART" in proc_ctx.out:
            regex_result = re.search('KEYSTART((.|\n|\r)*)KEYEND',
                                     proc_ctx.out)
            key = regex_result.group(1)
            workflow_srv_dir = get_workflow_srv_dir(self.workflow)
            public_key = KeyInfo(KeyType.PUBLIC,
                                 KeyOwner.CLIENT,
                                 workflow_srv_dir=workflow_srv_dir,
                                 install_target=install_target)
            old_umask = os.umask(0o177)
            with open(public_key.full_key_path, 'w',
                      encoding='utf8') as text_file:
                text_file.write(key)
            os.umask(old_umask)
            # configure_curve must be called every time certificates are
            # added or removed, in order to update the Authenticator's
            # state.
            curve_auth.configure_curve(domain='*',
                                       location=(client_pub_key_dir))
            self.remote_init_map[install_target] = REMOTE_INIT_DONE
            self.ready = True
            return
        # Bad status
        LOG.error(
            PlatformError(
                PlatformError.MSG_INIT,
                platform['name'],
                cmd=proc_ctx.cmd,
                ret_code=proc_ctx.ret_code,
                out=proc_ctx.out,
                err=proc_ctx.err,
            ))

        self.remote_init_map[platform['install target']] = REMOTE_INIT_FAILED
        self.ready = True
    def _get_status_msg(self, w_id: str, is_active: bool) -> str:
        """Derive a status message for the workflow.

        Running schedulers provide their own status messages.

        We must derive a status message for stopped workflows.
        """
        if is_active:
            # this will get overridden when we sync with the workflow
            # set a sensible default here incase the sync takes a while
            return 'Running'
        w_id = Tokens(w_id)['workflow']
        db_file = Path(get_workflow_srv_dir(w_id), WorkflowFiles.Service.DB)
        if db_file.exists():
            # the workflow has previously run
            return 'Stopped'
        else:
            # the workflow has not yet run
            return 'Not yet run'
Beispiel #6
0
def setup_keys(workflow_name):
    workflow_srv_dir = get_workflow_srv_dir(workflow_name)
    server_keys = {
        "client_public_key":
        KeyInfo(KeyType.PUBLIC,
                KeyOwner.CLIENT,
                workflow_srv_dir=workflow_srv_dir),
        "client_private_key":
        KeyInfo(KeyType.PRIVATE,
                KeyOwner.CLIENT,
                workflow_srv_dir=workflow_srv_dir),
        "server_public_key":
        KeyInfo(KeyType.PUBLIC,
                KeyOwner.SERVER,
                workflow_srv_dir=workflow_srv_dir),
        "server_private_key":
        KeyInfo(KeyType.PRIVATE,
                KeyOwner.SERVER,
                workflow_srv_dir=workflow_srv_dir)
    }
    remove_keys_on_server(server_keys)
    remove_keys_on_client(workflow_srv_dir, None, full_clean=True)
    create_server_keys(server_keys, workflow_srv_dir)
    create_client_keys(workflow_srv_dir, None)