def _update_broker_cert_chain(self, svc, ca_bundle_file): """ Retrieve the latest ca bundle from the management service and update it on disk at the location supplied as the `ca_bundle_file` argument. :param dxlclient._cli._management_service.ManagementService svc: the management service to query for the new broker cert chain :param str ca_bundle_file: file at which to store the latest ca bundle """ cert_chain = svc.invoke_command(self._BROKER_CERT_CHAIN_COMMAND) validate_cert_pem(cert_chain, "Failed to process PEM for CA bundle") logger.info("Updating certs in %s", ca_bundle_file) DxlUtils.save_to_file(ca_bundle_file, cert_chain)
def _save_pem(pem, description, target_file): """ Save the content of the string in the `pem` argument to the file name stored in the `target_file` argument. :param pem: content of the pem :param description: description of the content of the `pem`, used in the content of a message for an validation `Exception`, if raised. :param target_file: file at which to save the pem :raise Exception: if the contents of `pem` does not appear to be a PEM wrapping a valid ASN.1-encoded certificate """ validate_cert_pem(pem, "Failed to process PEM for {}".format(description)) logger.info("Saving %s file to %s", description, target_file) DxlUtils.save_to_file(target_file, pem)
def save_csr_and_private_key(self, csr_filename, private_key_filename, passphrase=None): """ Save the certificate request and private key to disk in PEM format :param csr_filename: filename of the certificate request :param private_key_filename: filename of the private key :param passphrase: If a `str` object is supplied, encrypt the private key with the passphrase before converting it to PEM format. If `None` is supplied, convert it to PEM format without performing any encryption. """ logger.info("Saving csr file to %s", csr_filename) DxlUtils.save_to_file(csr_filename, self._csr.dump_to_pem()) logger.info("Saving private key file to %s", private_key_filename) DxlUtils.save_to_file(private_key_filename, self._key_pair.private_key_as_pem(passphrase), 0o600)
def test_updateconfig_with_prompt_for_server_user_and_password(self): responses = { "Enter server username:"******"myuser", "Enter server password:"******"mypass" } def prompt_response(arg): return responses[arg] with _TempDir("updateconfig_no_server_creds") as temp_dir, \ patch("sys.argv", command_args(["updateconfig", temp_dir, "myhost"])), \ patch.object(getpass, "getpass", side_effect=prompt_response) as mock_getpass, \ requests_mock.mock(case_sensitive=True) as req_mock: ca_bundle_file = os.path.join(temp_dir, "ca-bundle.crt") DxlUtils.save_to_file(ca_bundle_file, "old ca") config_file = os.path.join(temp_dir, "dxlclient.config") DxlUtils.save_to_file(config_file, make_config()) client_ca_url = get_server_client_ca_url("myhost") broker_list_url = get_server_broker_list_url("myhost") req_mock.get(client_ca_url, text=get_mock_ca_bundle_response_func()) req_mock.get(broker_list_url, text=get_mock_broker_list_response_func()) cli_run() self.assertEqual(2, len(req_mock.request_history)) # Validate auth credentials sent in requests expected_creds = "Basic {}".format( base64.b64encode(b"myuser:mypass").decode("utf8")) for request in req_mock.request_history: self.assertEqual(expected_creds, request.headers["Authorization"]) self.assertEqual(2, mock_getpass.call_count)
def test_provisionconfig_with_csr(self): csr_file = "myclient.csr" with _TempDir("provconfig_csr") as temp_dir, \ patch("sys.argv", command_args(["provisionconfig", temp_dir, "myhost", os.path.join(temp_dir, csr_file), "-u", "myuser", "-p", "mypass", "-r"])), \ requests_mock.mock(case_sensitive=True) as req_mock: client_cert_for_response = FAKE_CERTIFICATE csr_to_test = FAKE_CSR full_csr_file_path = os.path.join(temp_dir, csr_file) DxlUtils.save_to_file(full_csr_file_path, csr_to_test) req_mock.get(get_server_provision_url("myhost"), text=get_mock_provision_response_func( client_cert=client_cert_for_response)) cli_run() self.assertEqual(1, len(req_mock.request_history)) request = req_mock.request_history[0] # Validate csr saved to disk was not regenerated and matches csr # submitted for signing csr_bytes_from_file = slurp_file_into_bytes(full_csr_file_path) csr_bytes_in_request = flattened_query_params(request).get( "csrString") self.assertEqual(csr_bytes_in_request.encode("utf8"), csr_bytes_from_file) self.assertEqual(csr_to_test.encode("utf8"), csr_bytes_from_file) # Validate client cert returned for request matches stored file client_cert_file = os.path.join(temp_dir, "client.crt") self.assertTrue(os.path.exists(client_cert_file)) client_cert_from_file = slurp_file_into_bytes(client_cert_file) self.assertEqual(client_cert_for_response.encode("utf8"), client_cert_from_file)
def test_updateconfig_with_trusted_ca_cert_and_port(self): with _TempDir("updateconfig_ca_port") as temp_dir, \ patch("sys.argv", command_args(["updateconfig", temp_dir, "myhost", "-t", "58443", "-u", "myuser", "-p", "mypass", "-e", "mytruststore.pem"])), \ requests_mock.mock(case_sensitive=True) as req_mock: ca_bundle_file = os.path.join(temp_dir, "ca-bundle.crt") DxlUtils.save_to_file(ca_bundle_file, "old ca") config_file = os.path.join(temp_dir, "dxlclient.config") DxlUtils.save_to_file(config_file, make_config()) client_ca_url = get_server_client_ca_url("myhost", 58443) broker_list_url = get_server_broker_list_url("myhost", 58443) req_mock.get(client_ca_url, text=get_mock_ca_bundle_response_func()) req_mock.get(broker_list_url, text=get_mock_broker_list_response_func()) cli_run() self.assertEqual(2, len(req_mock.request_history)) request_urls = [] for request in req_mock.request_history: self.assertEqual("mytruststore.pem", request.verify) request_urls.append("{}://{}:{}{}".format( request.scheme, request.hostname, request.port, request.path)) # If each mock endpoint was hit once, the request should have been # made to the right port self.assertIn(client_ca_url, request_urls) self.assertIn(broker_list_url, request_urls)
def test_updateconfig_basic(self): with _TempDir("updateconfig_basic") as temp_dir, \ patch("sys.argv", command_args(["updateconfig", temp_dir, "myhost", "-u", "myuser", "-p", "mypass"])), \ requests_mock.mock(case_sensitive=True) as req_mock: base_broker_lines = broker_lines_for_config_file( make_broker_lines(2), add_comments=True) base_config_lines = make_basic_config( ca_bundle_file="mycabundle.pem", add_comments=True) base_config_content = make_config(base_config_lines, base_broker_lines) # Before the broker config update is done, there should be entries # for broker1 and broker2. The updated config contains entries for # broker2 (pre-existing), broker3 (new), and broker4 (new). broker1 # is expected to be deleted from the config on disk during the # update. The comment line above the entry for broker2 in the # config file should be preserved after the update. updated_brokers = make_broker_dict(4) del updated_brokers["brokers"][0] expected_brokers = make_broker_lines(4) del expected_brokers[0] expected_broker_lines = "# This is broker 2\n{}".format( broker_lines_for_config_file(expected_brokers)) expected_config_content = make_config(base_config_lines, expected_broker_lines) ca_bundle_file = os.path.join(temp_dir, "mycabundle.pem") DxlUtils.save_to_file(ca_bundle_file, "old ca") updated_ca_bundle = make_fake_ca_bundle(2) config_file = os.path.join(temp_dir, "dxlclient.config") DxlUtils.save_to_file(config_file, base_config_content) req_mock.get( get_server_client_ca_url("myhost"), text=get_mock_ca_bundle_response_func(updated_ca_bundle)) req_mock.get( get_server_broker_list_url("myhost"), text=get_mock_broker_list_response_func(updated_brokers)) cli_run() self.assertEqual(2, len(req_mock.request_history)) # Validate auth credentials sent in requests expected_creds = "Basic {}".format( base64.b64encode(b"myuser:mypass").decode("utf8")) for request in req_mock.request_history: self.assertEqual(expected_creds, request.headers["Authorization"]) # Validate updates to the ca bundle file self.assertTrue(os.path.exists(ca_bundle_file)) ca_bundle_from_file = slurp_file_into_bytes(ca_bundle_file) self.assertEqual(updated_ca_bundle.encode("utf8"), ca_bundle_from_file) # Validate updates to the config file self.assertTrue(os.path.exists(config_file)) config_from_file = slurp_file_into_bytes(config_file) self.assertEqual(expected_config_content, config_from_file)