async def _generate_certs( self, # type: HummingbotApplication from_client_password: bool = False ): cert_path: str = get_gateway_paths().local_certs_path.as_posix() if not from_client_password: if certs_files_exist(): self.notify(f"Gateway SSL certification files exist in {cert_path}.") self.notify("To create new certification files, please first manually delete those files.") return with begin_placeholder_mode(self): while True: pass_phase = await self.app.prompt( prompt='Enter pass phase to generate Gateway SSL certifications >>> ', is_password=True ) if pass_phase is not None and len(pass_phase) > 0: break self.notify("Error: Invalid pass phase") else: pass_phase = Security.password create_self_sign_certs(pass_phase) self.notify(f"Gateway SSL certification files are created in {cert_path}.") GatewayHttpClient.get_instance().reload_certs()
def create_self_sign_certs(pass_phase: str, client_config_map: "ClientConfigAdapter"): """ Create self-sign CA Cert """ cert_directory: str = get_gateway_paths( client_config_map).local_certs_path.as_posix() filepath_list = { 'ca_key': join(cert_directory, ca_key_filename), 'ca_cert': join(cert_directory, ca_cert_filename), 'server_key': join(cert_directory, server_key_filename), 'server_cert': join(cert_directory, server_cert_filename), 'server_csr': join(cert_directory, server_csr_filename), 'client_key': join(cert_directory, client_key_filename), 'client_cert': join(cert_directory, client_cert_filename), 'client_csr': join(cert_directory, client_csr_filename) } # Create CA Private & Public Keys for signing ca_private_key = generate_private_key(pass_phase, filepath_list['ca_key']) generate_public_key(ca_private_key, filepath_list['ca_cert']) # Create Server Private & Public Keys for signing server_private_key = generate_private_key(pass_phase, filepath_list['server_key']) # Create CSR generate_csr(server_private_key, filepath_list['server_csr']) # Load CSR with open(filepath_list['server_csr'], 'rb') as server_csr_file: server_csr = x509.load_pem_x509_csr(server_csr_file.read(), default_backend()) # Create Client CSR # local certificate must be unencrypted. Currently, Requests does not support using encrypted keys. client_private_key = generate_private_key(None, filepath_list['client_key']) # Create CSR generate_csr(client_private_key, filepath_list['client_csr']) # Load CSR with open(filepath_list['client_csr'], 'rb') as client_csr_file: client_csr = x509.load_pem_x509_csr(client_csr_file.read(), default_backend()) # Load CA public key with open(filepath_list['ca_cert'], 'rb') as ca_cert_file: ca_cert = x509.load_pem_x509_certificate(ca_cert_file.read(), default_backend()) # Load CA private key with open(filepath_list['ca_key'], 'rb') as ca_key_file: ca_key = serialization.load_pem_private_key( ca_key_file.read(), pass_phase.encode('utf-8'), default_backend(), ) # Sign Server Cert with CSR sign_csr(server_csr, ca_cert, ca_key, filepath_list['server_cert']) # Sign Client Cert with CSR sign_csr(client_csr, ca_cert, ca_key, filepath_list['client_cert'])
def certs_files_exist() -> bool: """ Check if the necessary key and certificate files exist """ required_certs = [ ca_key_filename, ca_cert_filename, server_key_filename, server_cert_filename, client_key_filename, client_cert_filename ] file_list = listdir(get_gateway_paths().local_certs_path.as_posix()) return all(elem in file_list for elem in required_certs)
def _http_client(cls, client_config_map: "ClientConfigAdapter", re_init: bool = False) -> aiohttp.ClientSession: """ :returns Shared client session instance """ if cls._shared_client is None or re_init: cert_path = get_gateway_paths(client_config_map).local_certs_path.as_posix() ssl_ctx = ssl.create_default_context(cafile=f"{cert_path}/ca_cert.pem") ssl_ctx.load_cert_chain(certfile=f"{cert_path}/client_cert.pem", keyfile=f"{cert_path}/client_key.pem", password=Security.secrets_manager.password.get_secret_value()) conn = aiohttp.TCPConnector(ssl_context=ssl_ctx) cls._shared_client = aiohttp.ClientSession(connector=conn) return cls._shared_client
async def _create_gateway(self): gateway_paths: GatewayPaths = get_gateway_paths() gateway_container_name: str = get_gateway_container_name() gateway_conf_mount_path: str = gateway_paths.mount_conf_path.as_posix() certificate_mount_path: str = gateway_paths.mount_certs_path.as_posix() logs_mount_path: str = gateway_paths.mount_logs_path.as_posix() gateway_port: int = get_default_gateway_port() # remove existing container(s) try: old_container = await docker_ipc( "containers", all=True, filters={"name": gateway_container_name} ) for container in old_container: self.notify(f"Removing existing gateway container with id {container['Id']}...") await docker_ipc( "remove_container", container["Id"], force=True ) except Exception: pass # silently ignore exception await self._generate_certs(from_client_password=True) # create cert if await self.check_gateway_image(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG): self.notify("Found Gateway docker image. No image pull needed.") else: self.notify("Pulling Gateway docker image...") try: await self.pull_gateway_docker(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG) self.logger().info("Done pulling Gateway docker image.") except Exception as e: self.notify("Error pulling Gateway docker image. Try again.") self.logger().network("Error pulling Gateway docker image. Try again.", exc_info=True, app_warning_msg=str(e)) return self.notify("Creating new Gateway docker container...") host_config: Dict[str, Any] = await docker_ipc( "create_host_config", port_bindings={5000: gateway_port}, binds={ gateway_conf_mount_path: { "bind": "/usr/src/app/conf/", "mode": "rw" }, certificate_mount_path: { "bind": "/usr/src/app/certs/", "mode": "rw" }, logs_mount_path: { "bind": "/usr/src/app/logs/", "mode": "rw" }, } ) container_info: Dict[str, str] = await docker_ipc( "create_container", image=f"{GATEWAY_DOCKER_REPO}:{GATEWAY_DOCKER_TAG}", name=gateway_container_name, ports=[5000], volumes=[ gateway_conf_mount_path, certificate_mount_path, logs_mount_path ], host_config=host_config, environment=[f"GATEWAY_PASSPHRASE={Security.password}"] ) self.notify(f"New Gateway docker container id is {container_info['Id']}.") # Save the gateway port number, if it's not already there. if global_config_map.get("gateway_api_port").value != gateway_port: global_config_map["gateway_api_port"].value = gateway_port global_config_map["gateway_api_host"].value = "localhost" save_to_yml(GLOBAL_CONFIG_PATH, global_config_map) GatewayHttpClient.get_instance().base_url = f"https://{global_config_map['gateway_api_host'].value}:" \ f"{global_config_map['gateway_api_port'].value}" await start_gateway() # create Gateway configs await self._generate_gateway_confs(container_id=container_info["Id"]) # Restarts the Gateway container to ensure that Gateway server reloads new configs try: await docker_ipc(method_name="restart", container=container_info["Id"]) except docker.errors.APIError as e: self.notify(f"Error restarting Gateway container. Error: {e}") self.notify(f"Loaded new configs into Gateway container {container_info['Id']}")
async def _create_gateway( self # type: HummingbotApplication ): gateway_paths: GatewayPaths = get_gateway_paths(self.client_config_map) gateway_container_name: str = get_gateway_container_name(self.client_config_map) gateway_conf_mount_path: str = gateway_paths.mount_conf_path.as_posix() certificate_mount_path: str = gateway_paths.mount_certs_path.as_posix() logs_mount_path: str = gateway_paths.mount_logs_path.as_posix() gateway_port: int = get_default_gateway_port(self.client_config_map) # remove existing container(s) try: old_container = await docker_ipc( "containers", all=True, filters={"name": gateway_container_name} ) for container in old_container: self.notify(f"Removing existing gateway container with id {container['Id']}...") await docker_ipc( "remove_container", container["Id"], force=True ) except Exception: pass # silently ignore exception await self._generate_certs(from_client_password=True) # create cert if await self.check_gateway_image(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG): self.notify("Found Gateway docker image. No image pull needed.") else: self.notify("Pulling Gateway docker image...") try: await self.pull_gateway_docker(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG) self.logger().info("Done pulling Gateway docker image.") except Exception as e: self.notify("Error pulling Gateway docker image. Try again.") self.logger().network("Error pulling Gateway docker image. Try again.", exc_info=True, app_warning_msg=str(e)) return self.notify("Creating new Gateway docker container...") host_config: Dict[str, Any] = await docker_ipc( "create_host_config", port_bindings={5000: gateway_port}, binds={ gateway_conf_mount_path: { "bind": "/usr/src/app/conf/", "mode": "rw" }, certificate_mount_path: { "bind": "/usr/src/app/certs/", "mode": "rw" }, logs_mount_path: { "bind": "/usr/src/app/logs/", "mode": "rw" }, } ) container_info: Dict[str, str] = await docker_ipc( "create_container", image=f"{GATEWAY_DOCKER_REPO}:{GATEWAY_DOCKER_TAG}", name=gateway_container_name, ports=[5000], volumes=[ gateway_conf_mount_path, certificate_mount_path, logs_mount_path ], host_config=host_config, environment=[f"GATEWAY_PASSPHRASE={Security.secrets_manager.password.get_secret_value()}"] ) self.notify(f"New Gateway docker container id is {container_info['Id']}.") # Save the gateway port number, if it's not already there. gateway_config_map = self.client_config_map.gateway if gateway_config_map.gateway_api_port != gateway_port: gateway_config_map.gateway_api_port = gateway_port gateway_config_map.gateway_api_host = "localhost" save_to_yml(CLIENT_CONFIG_PATH, self.client_config_map) self._get_gateway_instance().base_url = ( f"https://{gateway_config_map.gateway_api_host}:{gateway_config_map.gateway_api_port}" ) await start_gateway(self.client_config_map) # create Gateway configs await self._generate_gateway_confs(container_id=container_info["Id"]) self.notify("Gateway is starting, please wait a moment.") # wait about 30 seconds for the gateway to start docker_and_gateway_live = await self.ping_gateway_docker_and_api(30) if docker_and_gateway_live: self.notify("Gateway has started succesfully.") else: self.notify("Error starting Gateway container.")