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)
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()
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}')
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'
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)