From 39c6d77a15df8b3ac650952077cb0c061a6af534 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 2 Sep 2024 13:53:00 +0200 Subject: [PATCH 01/54] [fix] ski extension marking in openssl handler --- .github/actions/deb_build/action.yml | 4 ++-- examples/ca_handler/openssl_ca_handler.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/deb_build/action.yml b/.github/actions/deb_build/action.yml index 244bf093..cb41df40 100644 --- a/.github/actions/deb_build/action.yml +++ b/.github/actions/deb_build/action.yml @@ -28,13 +28,13 @@ runs: Pin: origin packages.mozilla.org Pin-Priority: 1000 ' | sudo tee /etc/apt/preferences.d/mozilla - sudo apt update && sudo apt install firefox + sudo apt update && sudo apt install -y firefox --allow-downgrades shell: bash - name: "Prepare environment to build deb package" run: | sudo apt-get update && sudo apt-get -y upgrade - sudo apt-get -y install build-essential fakeroot dpkg-dev devscripts debhelper + sudo apt-get -y install build-essential fakeroot dpkg-dev devscripts debhelper --allow-downgrades rm setup.py rm -f examples/ngnix/acme2certifier.te rm -f examples/nginx/supervisord.conf diff --git a/examples/ca_handler/openssl_ca_handler.py b/examples/ca_handler/openssl_ca_handler.py index 822c84b8..ecc78a36 100644 --- a/examples/ca_handler/openssl_ca_handler.py +++ b/examples/ca_handler/openssl_ca_handler.py @@ -135,6 +135,7 @@ def _cert_extension_dic_parse(self, cert_extension_dic: Dict[str, str], cert: st elif ext_name.lower() == 'subjectkeyidentifier': self.logger.info('CAhandler.cert_extesion_dic_parse(): subjectKeyIdentifier') _tmp_dic['name'] = SubjectKeyIdentifier.from_public_key(cert.public_key()) + _tmp_dic['critical'] = False elif ext_name.lower() == 'authoritykeyidentifier': self.logger.info('CAhandler.cert_extesion_dic_parse(): authorityKeyIdentifier') _tmp_dic['name'] = AuthorityKeyIdentifier.from_issuer_public_key(ca_cert.public_key()) From 23f9748da1f2bb7ab8ab812241142f1a6ca95348 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 2 Sep 2024 15:43:11 +0200 Subject: [PATCH 02/54] [tst] unittests for nclm_ca_handler.py --- test/test_nclm_ca_handler.py | 484 ++++++++++++++++++++--------------- 1 file changed, 275 insertions(+), 209 deletions(-) diff --git a/test/test_nclm_ca_handler.py b/test/test_nclm_ca_handler.py index 86fa331f..a715513d 100644 --- a/test/test_nclm_ca_handler.py +++ b/test/test_nclm_ca_handler.py @@ -46,8 +46,17 @@ def test_003__api_post(self, mock_post): self.assertEqual('exc_api_post', self.cahandler._api_post('url', 'data')) self.assertIn('ERROR:test_a2c:CAhandler._api_post() returned error: exc_api_post', lcm.output) + @patch.object(requests, 'post') + def test_004__api_post(self, mock_req): + """ test _api_post successful run """ + mockresponse = Mock() + mock_req.return_value = mockresponse + mockresponse.status_code = 'status_code' + mockresponse.json = Exception('json_exc') + self.assertEqual({'status': 'status_code'}, self.cahandler._api_post('url', 'data')) + @patch('requests.get') - def test_004_ca_id_lookup(self, mock_req): + def test_005_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns nothing """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -60,7 +69,7 @@ def test_004_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:ca_id.lookup() no CAs found in response ...', lcm.output) @patch('requests.get') - def test_005_ca_id_lookup(self, mock_req): + def test_006_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns wrong data """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -73,7 +82,7 @@ def test_005_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:ca_id.lookup() no CAs found in response ...', lcm.output) @patch('requests.get') - def test_006_ca_id_lookup(self, mock_req): + def test_007_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns wrong data """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -86,7 +95,7 @@ def test_006_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:ca_id.lookup() no CAs found in response ...', lcm.output) @patch('requests.get') - def test_007_ca_id_lookup(self, mock_req): + def test_008_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns empty ca-list """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -99,7 +108,7 @@ def test_007_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_008_ca_id_lookup(self, mock_req): + def test_009_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns wrong ca-list """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -112,7 +121,7 @@ def test_008_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_009_ca_id_lookup(self, mock_req): + def test_010_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with name not matching """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -125,7 +134,7 @@ def test_009_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_010_ca_id_lookup(self, mock_req): + def test_011_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with name matching but no id """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -138,7 +147,7 @@ def test_010_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_011_ca_id_lookup(self, mock_req): + def test_012_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with name matching and id """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -149,7 +158,7 @@ def test_011_ca_id_lookup(self, mock_req): self.assertEqual('id', self.cahandler._ca_id_lookup()) @patch('requests.get') - def test_012_ca_id_lookup(self, mock_req): + def test_013_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with desc not matching """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -162,7 +171,7 @@ def test_012_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_013_ca_id_lookup(self, mock_req): + def test_014_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with desc matching but no id """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -175,7 +184,7 @@ def test_013_ca_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:_ca_id_lookup(): no ca id found for ca_name', lcm.output) @patch('requests.get') - def test_014_ca_id_lookup(self, mock_req): + def test_015_ca_id_lookup(self, mock_req): """ CAhandler._ca_id_lookup() returns ca-list with desc matching and id """ self.cahandler.api_host = 'api_host' self.cahandler.ca_name = 'ca_name' @@ -186,7 +195,7 @@ def test_014_ca_id_lookup(self, mock_req): self.assertEqual('id', self.cahandler._ca_id_lookup()) @patch('requests.get') - def test_015_ca_id_lookup(self, mock_get): + def test_016_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns everything with one ca cert """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -197,7 +206,7 @@ def test_015_ca_id_lookup(self, mock_get): self.assertEqual((None, 'pempemca', 'der'), self.cahandler._cert_bundle_build('foo')) @patch('requests.get') - def test_016_ca_id_lookup(self, mock_get): + def test_017_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns everything with two ca cert """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -210,7 +219,7 @@ def test_016_ca_id_lookup(self, mock_get): self.assertEqual((None, 'pempemca1pemca2', 'der'), self.cahandler._cert_bundle_build('foo')) @patch('requests.get') - def test_017_ca_id_lookup(self, mock_get): + def test_018_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns everything without der in cert_dic """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -223,7 +232,7 @@ def test_017_ca_id_lookup(self, mock_get): self.assertIn('ERROR:test_a2c:CAhandler._cert_bundle_build(): no der certificate returned for id: foo', lcm.output) @patch('requests.get') - def test_018_ca_id_lookup(self, mock_get): + def test_019_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns everything without pem in cert_dic """ self.cahandler.api_host = 'api_host' self.cahandler.ca_id_list = [1] @@ -237,7 +246,7 @@ def test_018_ca_id_lookup(self, mock_get): self.assertIn('ERROR:test_a2c:CAhandler._cert_bundle_build(): no pem certificate returned for id: foo', lcm.output) @patch('requests.get') - def test_019_ca_id_lookup(self, mock_get): + def test_020_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns everything without pem in ca_dic """ self.cahandler.api_host = 'api_host' self.cahandler.ca_id_list = [1] @@ -251,7 +260,7 @@ def test_019_ca_id_lookup(self, mock_get): self.assertIn('ERROR:test_a2c:CAhandler._cert_bundle_build(): no pem certificate returned for id: id', lcm.output) @patch('requests.get') - def test_020_ca_id_lookup(self, mock_get): + def test_021_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns wrong ca_dic """ self.cahandler.api_host = 'api_host' self.cahandler.ca_id_list = [1] @@ -265,7 +274,7 @@ def test_020_ca_id_lookup(self, mock_get): self.assertIn('ERROR:test_a2c:CAhandler._cert_bundle_build(): invalid reponse returned for id: id', lcm.output) @patch('requests.get') - def test_021_ca_id_lookup(self, mock_get): + def test_022_ca_id_lookup(self, mock_get): """ CAhandler._cert_bundle_build() returns wrong cert_dic """ self.cahandler.api_host = 'api_host' self.cahandler.ca_id_list = [1] @@ -280,7 +289,7 @@ def test_021_ca_id_lookup(self, mock_get): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_022_cert_id_lookup(self, mock_fetch, mock_comp): + def test_023_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - one cert - ok """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'subjectAltName': 'subjectAltName', 'certificateId': 1}] @@ -289,7 +298,7 @@ def test_022_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_023_cert_id_lookup(self, mock_fetch, mock_comp): + def test_024_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - two certs - match 2nd entry in list """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'subjectAltName': 'subjectAltName1', 'certificateId': 1}, {'subjectAltName': 'subjectAltName2', 'certificateId': 2}] @@ -298,7 +307,7 @@ def test_023_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_024_cert_id_lookup(self, mock_fetch, mock_comp): + def test_025_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - no cn two certs - match 2nd entry in list """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'subjectAltName': 'subjectAltName1', 'certificateId': 1}, {'subjectAltName': 'subjectAltName2', 'certificateId': 2}] @@ -307,7 +316,7 @@ def test_024_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_025_cert_id_lookup(self, mock_fetch, mock_comp): + def test_026_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - two certs - match 1st entry in list """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'subjectAltName': 'subjectAltName1', 'certificateId': 1}, {'subjectAltName': 'subjectAltName2', 'certificateId': 2}] @@ -316,7 +325,7 @@ def test_025_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_026_cert_id_lookup(self, mock_fetch, mock_comp): + def test_027_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - no certificateid return from nclm """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -328,7 +337,7 @@ def test_026_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_027_cert_id_lookup(self, mock_fetch, mock_comp): + def test_028_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - two certs match """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'subjectAltName': 'subjectAltName1', 'certificateId': 1}, {'subjectAltName': 'subjectAltName2', 'certificateId': 2}] @@ -337,7 +346,7 @@ def test_027_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_028_cert_id_lookup(self, mock_fetch, mock_comp): + def test_029_cert_id_lookup(self, mock_fetch, mock_comp): """ CAhandler._cert_id_lookup() - one cert - no san in """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = [{'foo': 'bar', 'certificateId': 'certificateId'}] @@ -346,7 +355,7 @@ def test_028_cert_id_lookup(self, mock_fetch, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_029_cert_id_lookup(self, mock_req, mock_comp): + def test_030_cert_id_lookup(self, mock_req, mock_comp): """ CAhandler._cert_id_lookup() - no san_list in function """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -357,7 +366,7 @@ def test_029_cert_id_lookup(self, mock_req, mock_comp): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._san_compare') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_030_cert_id_lookup(self, mock_fetch, mock_comp,): + def test_031_cert_id_lookup(self, mock_fetch, mock_comp,): """ CAhandler._cert_id_lookup() - _cert_list_fetch() does not return anything """ self.cahandler.api_host = 'api_host' mock_fetch.return_value = None @@ -367,7 +376,7 @@ def test_030_cert_id_lookup(self, mock_fetch, mock_comp,): self.assertIn('ERROR:test_a2c:_cert_id_lookup(): no certificates found for csr_cn', lcm.output) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_list_fetch') - def test_031_cert_id_lookup(self, mock_fetch): + def test_032_cert_id_lookup(self, mock_fetch): """ CAhandler._cert_id_lookup() - request raises exception """ self.cahandler.api_host = 'api_host' mock_fetch.side_effect = Exception('req_exc') @@ -375,14 +384,14 @@ def test_031_cert_id_lookup(self, mock_fetch): self.assertFalse(self.cahandler._cert_id_lookup('csr_cn', 'san_list')) self.assertIn('ERROR:test_a2c:CAhandler._cert_id_lookup() returned error: req_exc', lcm.output) - def test_032__config_check(self): + def test_033__config_check(self): """ CAhandler._config.check() no api_host """ with self.assertLogs('test_a2c', level='INFO') as lcm: self.cahandler._config_check() self.assertEqual('api_host to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"api_host" to be set in config file', lcm.output) - def test_033__config_check(self): + def test_034__config_check(self): """ CAhandler._config.check() no api_user """ self.cahandler.api_host = 'api_host' with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -390,7 +399,7 @@ def test_033__config_check(self): self.assertEqual('api_user to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"api_user" to be set in config file', lcm.output) - def test_034__config_check(self): + def test_035__config_check(self): """ CAhandler._config.check() no api_user """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': False} @@ -399,7 +408,7 @@ def test_034__config_check(self): self.assertEqual('api_user to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"api_user" to be set in config file', lcm.output) - def test_035__config_check(self): + def test_036__config_check(self): """ CAhandler._config.check() no api_password """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user'} @@ -408,7 +417,7 @@ def test_035__config_check(self): self.assertEqual('api_password to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"api_password" to be set in config file', lcm.output) - def test_036__config_check(self): + def test_037__config_check(self): """ CAhandler._config.check() no api_password """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user', 'api_password': False} @@ -417,7 +426,7 @@ def test_036__config_check(self): self.assertEqual('api_password to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"api_password" to be set in config file', lcm.output) - def test_037__config_check(self): + def test_038__config_check(self): """ CAhandler._config.check() no tsg_name """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user', 'api_password': 'api_password'} @@ -426,7 +435,7 @@ def test_037__config_check(self): self.assertEqual('tsg_name to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"tsg_name" to be set in config file', lcm.output) - def test_038__config_check(self): + def test_039__config_check(self): """ CAhandler._config.check() no tsg_name """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user', 'api_password': 'api_password'} @@ -436,7 +445,7 @@ def test_038__config_check(self): self.assertEqual('tsg_name to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"tsg_name" to be set in config file', lcm.output) - def test_039__config_check(self): + def test_040__config_check(self): """ CAhandler._config.check() no ca_name """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user', 'api_password': 'api_password'} @@ -446,7 +455,7 @@ def test_039__config_check(self): self.assertEqual('ca_name to be set in config file', self.cahandler.error) self.assertIn('ERROR:test_a2c:"ca_name" to be set in config file', lcm.output) - def test_040__config_check(self): + def test_041__config_check(self): """ CAhandler._config.check() ca_bundle False """ self.cahandler.api_host = 'api_host' self.cahandler.credential_dic = {'api_user': 'api_user', 'api_password': 'api_password'} @@ -460,7 +469,7 @@ def test_040__config_check(self): self.assertIn('WARNING:test_a2c:"ca_bundle" set to "False" - validation of server certificate disabled', lcm.output) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_041_config_load(self, mock_load_cfg): + def test_042_config_load(self, mock_load_cfg): """ CAhandler._config_load no cahandler section """ mock_load_cfg.return_value = {} self.cahandler._config_load() @@ -474,7 +483,7 @@ def test_041_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_042_config_load(self, mock_load_cfg): + def test_043_config_load(self, mock_load_cfg): """ CAhandler._config_load api_host """ mock_load_cfg.return_value = {'CAhandler': {'api_host': 'api_host'}} self.cahandler._config_load() @@ -488,7 +497,7 @@ def test_042_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_043_config_load(self, mock_load_cfg): + def test_044_config_load(self, mock_load_cfg): """ CAhandler._config_load api_user """ mock_load_cfg.return_value = {'CAhandler': {'api_user': 'api_user'}} self.cahandler._config_load() @@ -502,7 +511,7 @@ def test_043_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_044_config_load(self, mock_load_cfg): + def test_045_config_load(self, mock_load_cfg): """ CAhandler._config_load api_password """ mock_load_cfg.return_value = {'CAhandler': {'api_password': 'api_password'}} self.cahandler._config_load() @@ -516,7 +525,7 @@ def test_044_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_045_config_load(self, mock_load_cfg): + def test_046_config_load(self, mock_load_cfg): """ CAhandler._config_load ca_name """ mock_load_cfg.return_value = {'CAhandler': {'ca_name': 'ca_name'}} self.cahandler._config_load() @@ -530,7 +539,7 @@ def test_045_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_046_config_load(self, mock_load_cfg): + def test_047_config_load(self, mock_load_cfg): """ CAhandler._config_load tsg_name """ mock_load_cfg.return_value = {'CAhandler': {'tsg_name': 'tsg_name'}} self.cahandler._config_load() @@ -544,7 +553,7 @@ def test_046_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_047_config_load(self, mock_load_cfg): + def test_048_config_load(self, mock_load_cfg): """ CAhandler._config_load ca_bundle string """ mock_load_cfg.return_value = {'CAhandler': {'ca_bundle': 'ca_bundle'}} self.cahandler._config_load() @@ -558,7 +567,7 @@ def test_047_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_048_config_load(self, mock_load_cfg): + def test_049_config_load(self, mock_load_cfg): """ CAhandler._config_load ca_bundle False """ mock_load_cfg.return_value = {'CAhandler': {'ca_bundle': False}} self.cahandler._config_load() @@ -572,7 +581,7 @@ def test_048_config_load(self, mock_load_cfg): self.assertEqual(20, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_049_config_load(self, mock_load_cfg): + def test_050_config_load(self, mock_load_cfg): """ CAhandler._config_load template_name """ mock_load_cfg.return_value = {'CAhandler': {'template_name': 'template_name'}} self.cahandler._config_load() @@ -586,7 +595,7 @@ def test_049_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_user_var': 'user_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_050_config_load(self, mock_load_cfg): + def test_051_config_load(self, mock_load_cfg): """ CAhandler._config_load load username from variable """ mock_load_cfg.return_value = {'CAhandler': {'api_user_variable': 'api_user_var'}} self.cahandler._config_load() @@ -596,7 +605,7 @@ def test_050_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_user_var': 'user_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_051_config_load(self, mock_load_cfg): + def test_052_config_load(self, mock_load_cfg): """ CAhandler._config_load load username from non existing """ mock_load_cfg.return_value = {'CAhandler': {'api_user_variable': 'does_not_exist'}} with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -608,7 +617,7 @@ def test_051_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_user_var': 'user_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_052_config_load(self, mock_load_cfg): + def test_053_config_load(self, mock_load_cfg): """ CAhandler._config_load load username from wich gets overwritten from cfg-file """ mock_load_cfg.return_value = {'CAhandler': {'api_user_variable': 'api_user_var', 'api_user': 'api_user'}} with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -620,7 +629,7 @@ def test_052_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_password_var': 'password_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_053_config_load(self, mock_load_cfg): + def test_054_config_load(self, mock_load_cfg): """ CAhandler._config_load load password from variable """ mock_load_cfg.return_value = {'CAhandler': {'api_password_variable': 'api_password_var'}} self.cahandler._config_load() @@ -630,7 +639,7 @@ def test_053_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_password_var': 'password_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_054_config_load(self, mock_load_cfg): + def test_055_config_load(self, mock_load_cfg): """ CAhandler._config_load load password from non existing variable """ mock_load_cfg.return_value = {'CAhandler': {'api_password_variable': 'does_not_exist'}} with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -642,7 +651,7 @@ def test_054_config_load(self, mock_load_cfg): @patch.dict('os.environ', {'api_password_var': 'password_var'}) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_055_config_load(self, mock_load_cfg): + def test_056_config_load(self, mock_load_cfg): """ CAhandler._config_load load password from variable which gets overwritten """ mock_load_cfg.return_value = {'CAhandler': {'api_password_variable': 'api_password_var', 'api_password': 'api_password'}} with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -655,7 +664,7 @@ def test_055_config_load(self, mock_load_cfg): @patch('examples.ca_handler.nclm_ca_handler.parse_url') @patch('json.loads') @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_056_config_load(self, mock_load_cfg, mock_json, mock_url): + def test_057_config_load(self, mock_load_cfg, mock_json, mock_url): """ test _config_load ca_handler configured load proxies """ mock_load_cfg.return_value = {'DEFAULT': {'proxy_server_list': 'foo'}} mock_url.return_value = {'foo': 'bar'} @@ -669,7 +678,7 @@ def test_056_config_load(self, mock_load_cfg, mock_json, mock_url): @patch('examples.ca_handler.nclm_ca_handler.parse_url') @patch('json.loads') @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_057_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): + def test_058_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): """ test _config_load ca_handler configured load proxies """ mock_load_cfg.return_value = {'DEFAULT': {'proxy_server_list': 'foo'}} mock_url.return_value = {'host': 'bar:8888'} @@ -686,7 +695,7 @@ def test_057_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): @patch('examples.ca_handler.nclm_ca_handler.parse_url') @patch('json.loads') @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_058_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): + def test_059_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): """ test _config_load ca_handler configured load proxies """ mock_load_cfg.return_value = {'DEFAULT': {'proxy_server_list': 'foo'}} mock_url.return_value = {'host': 'bar'} @@ -702,7 +711,7 @@ def test_058_config_load(self, mock_load_cfg, mock_json, mock_url, mock_chk): self.assertEqual(300, self.cahandler.request_delta_treshold) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_059_config_load(self, mock_load_cfg): + def test_060_config_load(self, mock_load_cfg): """ CAhandler._config_load request_delta_treshold """ mock_load_cfg.return_value = {'CAhandler': {'request_delta_treshold': 60}} self.cahandler._config_load() @@ -713,7 +722,7 @@ def test_059_config_load(self, mock_load_cfg): self.assertEqual(60, self.cahandler.request_delta_treshold) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_060_config_load(self, mock_load_cfg): + def test_061_config_load(self, mock_load_cfg): """ CAhandler._config_load request_delta_treshold string """ mock_load_cfg.return_value = {'CAhandler': {'request_delta_treshold': 'aaa'}} with self.assertLogs('test_a2c', level='INFO') as lcm: @@ -726,14 +735,14 @@ def test_060_config_load(self, mock_load_cfg): self.assertIn('ERROR:test_a2c:CAhandler._config_load() could not load request_delta_treshold:aaa', lcm.output) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_061_config_load(self, mock_load_cfg): + def test_062_config_load(self, mock_load_cfg): """ CAhandler._config_load request_delta_treshold """ mock_load_cfg.return_value = {'CAhandler': {'request_timeout': 10}} self.cahandler._config_load() self.assertEqual(10, self.cahandler.request_timeout) @patch('examples.ca_handler.nclm_ca_handler.load_config') - def test_062_config_load(self, mock_load_cfg): + def test_063_config_load(self, mock_load_cfg): """ CAhandler._config_load request_delta_treshold """ mock_load_cfg.return_value = {'CAhandler': {'request_timeout': 'aa'}} self.cahandler._config_load() @@ -743,7 +752,7 @@ def test_062_config_load(self, mock_load_cfg): @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_063__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_064__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - all ok """ mock_utsnow.return_value = 1000 mock_uts.return_value = 900 @@ -755,7 +764,7 @@ def test_063__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_064__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_065__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - no requestID in list exception triggered """ mock_utsnow.return_value = 1000 mock_uts.return_value = 900 @@ -769,7 +778,7 @@ def test_064__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_065__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_066__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - cn in mock_unureq not correctly ordered """ mock_utsnow.return_value = 1000 mock_uts.return_value = 900 @@ -781,7 +790,7 @@ def test_065__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_066__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_067__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - empty subjectName """ mock_utsnow.return_value = 1000 mock_uts.return_value = 900 @@ -793,7 +802,7 @@ def test_066__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_067__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_068__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - no subjectName """ mock_utsnow.return_value = 1000 mock_uts.return_value = 900 @@ -805,7 +814,7 @@ def test_067__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_068__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): + def test_069__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastreq): """ CAhandler._csr_id_lookup - requests to old """ mock_utsnow.return_value = 1000 mock_uts.return_value = 100 @@ -818,7 +827,7 @@ def test_068__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_lastr @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_069__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_070__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn one san """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -834,7 +843,7 @@ def test_069__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_070__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_071__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn two sans """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -850,7 +859,7 @@ def test_070__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_071__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_072__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn two sans to be reordered """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -866,7 +875,7 @@ def test_071__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_072__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_073__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn no requestID """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -882,7 +891,7 @@ def test_072__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_073__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_074__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn sans are not matching """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -898,7 +907,7 @@ def test_073__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_074__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_075__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn no pkcs10 """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -914,7 +923,7 @@ def test_074__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, @patch('examples.ca_handler.nclm_ca_handler.date_to_uts_utc') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._unusedrequests_get') @patch('examples.ca_handler.nclm_ca_handler.uts_now') - def test_075__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): + def test_076__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, mock_lastreq): """ CAhandler._csr_id_lookup - no csr_cn trigger excption in loop """ self.cahandler.api_host = 'api_host' mock_utsnow.return_value = 1000 @@ -928,14 +937,14 @@ def test_075__csr_id_lookup(self, mock_utsnow, mock_unureq, mock_uts, mock_san, self.assertIn("ERROR:test_a2c:_csr_id_lookup(): response incomplete: 'requestId'", lcm.output) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - def test_076__request_import(self, mock_req): + def test_077__request_import(self, mock_req): """ CAhandler._request_import """ self.cahandler.api_host = 'api_host' mock_req.return_value = 'foo' self.assertEqual('foo', self.cahandler._request_import('csr')) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - def test_077__request_import(self, mock_req): + def test_078__request_import(self, mock_req): """ CAhandler._request_import - req raises an exception """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('exc_req_import') @@ -944,7 +953,7 @@ def test_077__request_import(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._request_import() returned error: exc_req_import', lcm.output) @patch('requests.get') - def test_078__unusedrequests_get(self, mock_req): + def test_079__unusedrequests_get(self, mock_req): """ CAhandler._unusedrequests_get """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -953,7 +962,7 @@ def test_078__unusedrequests_get(self, mock_req): self.assertEqual({'foo': 'bar'}, self.cahandler._unusedrequests_get()) @patch('requests.get') - def test_079__unusedrequests_get(self, mock_req): + def test_080__unusedrequests_get(self, mock_req): """ CAhandler._unusedrequests_get """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('exc_req_unused') @@ -962,7 +971,7 @@ def test_079__unusedrequests_get(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._unusedrequests_get() returned error: exc_req_unused', lcm.output) @patch('requests.get') - def test_080__login(self, mock_get): + def test_081__login(self, mock_get): """ CAhandler._unusedrequests_get """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -977,7 +986,7 @@ def test_080__login(self, mock_get): @patch('requests.post') @patch('requests.get') - def test_081__login(self, mock_get, mock_post): + def test_082__login(self, mock_get, mock_post): """ CAhandler._unusedrequests_get """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -991,7 +1000,7 @@ def test_081__login(self, mock_get, mock_post): @patch('requests.post') @patch('requests.get') - def test_082__login(self, mock_get, mock_post): + def test_083__login(self, mock_get, mock_post): """ CAhandler._unusedrequests_get mock_post without username""" self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1005,7 +1014,7 @@ def test_082__login(self, mock_get, mock_post): @patch('requests.post') @patch('requests.get') - def test_083__login(self, mock_get, mock_post): + def test_084__login(self, mock_get, mock_post): """ CAhandler._unusedrequests_get mock_post without username""" self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1022,7 +1031,7 @@ def test_083__login(self, mock_get, mock_post): @patch('requests.post') @patch('requests.get') - def test_084__login(self, mock_get, mock_post): + def test_085__login(self, mock_get, mock_post): """ CAhandler._unusedrequests_get mock_post without realms""" self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1036,7 +1045,7 @@ def test_084__login(self, mock_get, mock_post): @patch('requests.post') @patch('requests.get') - def test_085__login(self, mock_get, mock_post): + def test_086__login(self, mock_get, mock_post): """ CAhandler._unusedrequests_get mock_post without access tooken""" self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1051,40 +1060,40 @@ def test_085__login(self, mock_get, mock_post): self.assertFalse(self.cahandler.headers) self.assertIn('ERROR:test_a2c:CAhandler._login(): No token returned. Aborting...', lcm.output) - def test_086__san_compare(self): + def test_087__san_compare(self): """ CAhandler._san_compare all ok """ csr_san_list = ['foo:foo'] cert_san_list = {'foo': ['foo']} self.assertTrue(self.cahandler._san_compare(csr_san_list, cert_san_list)) - def test_087__san_compare(self): + def test_088__san_compare(self): """ CAhandler._san_compare multiple """ csr_san_list = ['foo:foo', 'foo:bar'] cert_san_list = {'foo': ['foo', 'bar']} self.assertTrue(self.cahandler._san_compare(csr_san_list, cert_san_list)) - def test_088__san_compare(self): + def test_089__san_compare(self): """ CAhandler._san_compare multiple """ csr_san_list = ['foo:foo,foo:bar'] cert_san_list = {'foo': ['foo', 'bar']} self.assertTrue(self.cahandler._san_compare(csr_san_list, cert_san_list)) - def test_089__san_compare(self): + def test_090__san_compare(self): """ CAhandler._san_compare multiple """ csr_san_list = ['foo:foo,foo:bar1'] cert_san_list = {'foo': ['foo', 'bar']} self.assertFalse(self.cahandler._san_compare(csr_san_list, cert_san_list)) - def test_090_poll(self): + def test_091_poll(self): """ CAhandler.poll() """ self.assertEqual(('Method not implemented.', None, None, 'poll_identifier', False), self.cahandler.poll('cert_name', 'poll_identifier', 'csr')) - def test_091_trigger(self): + def test_092_trigger(self): """ CAhandler.trigger() """ self.assertEqual(('Method not implemented.', None, None), self.cahandler.trigger('payload')) @patch('requests.get') - def test_092___tsg_id_lookup(self, mock_get): + def test_093___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - all ok """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1095,7 +1104,7 @@ def test_092___tsg_id_lookup(self, mock_get): self.assertEqual({'name': 'name', 'id': 'id'}, self.cahandler.tsg_info_dic) @patch('requests.get') - def test_093___tsg_id_lookup(self, mock_get): + def test_094___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - multipe returned 1st matches """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1106,7 +1115,7 @@ def test_093___tsg_id_lookup(self, mock_get): self.assertEqual({'name': 'name', 'id': 'id'}, self.cahandler.tsg_info_dic) @patch('requests.get') - def test_094___tsg_id_lookup(self, mock_get): + def test_095___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - multipe returned 2nd matches """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1117,7 +1126,7 @@ def test_094___tsg_id_lookup(self, mock_get): self.assertEqual({'name': 'name', 'id': 'id'}, self.cahandler.tsg_info_dic) @patch('requests.get') - def test_095___tsg_id_lookup(self, mock_get): + def test_096___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - id is missing """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1130,7 +1139,7 @@ def test_095___tsg_id_lookup(self, mock_get): self.assertIn("ERROR:test_a2c:CAhandler._tsg_id_lookup() incomplete response: {'name': 'name'}", lcm.output) @patch('requests.get') - def test_096___tsg_id_lookup(self, mock_get): + def test_097___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - name is missing """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1143,7 +1152,7 @@ def test_096___tsg_id_lookup(self, mock_get): self.assertIn("ERROR:test_a2c:CAhandler._tsg_id_lookup() incomplete response: {'foo': 'bar', 'id': 'id'}", lcm.output) @patch('requests.get') - def test_097___tsg_id_lookup(self, mock_get): + def test_098___tsg_id_lookup(self, mock_get): """ CAhandler._tsg_id_lookup() - targetSystemGroups is missing """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1156,7 +1165,7 @@ def test_097___tsg_id_lookup(self, mock_get): self.assertIn('ERROR:test_a2c:CAhandler._tsg_id_lookup() no target-system-groups found for filter: name...', lcm.output) @patch('requests.get') - def test_098__tsg_id_lookup(self, mock_req): + def test_099__tsg_id_lookup(self, mock_req): """ CAhandler._request_import - req raises an exception """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('exc_tsg_id_lookup') @@ -1165,7 +1174,7 @@ def test_098__tsg_id_lookup(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._tsg_id_lookup() returned error: exc_tsg_id_lookup', lcm.output) @patch('requests.get') - def test_099__template_id_lookup(self, mock_get): + def test_100__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - all ok """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1176,7 +1185,7 @@ def test_099__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': 10}, self.cahandler.template_info_dic) @patch('requests.get') - def test_100__template_id_lookup(self, mock_get): + def test_101__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - linkId None """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1187,7 +1196,7 @@ def test_100__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_101__template_id_lookup(self, mock_get): + def test_102__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - No linkId """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1198,7 +1207,7 @@ def test_101__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_102__template_id_lookup(self, mock_get): + def test_103__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - no match in template names """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1209,7 +1218,7 @@ def test_102__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_103__template_id_lookup(self, mock_get): + def test_104__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - allowed false """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1220,7 +1229,7 @@ def test_103__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_104__template_id_lookup(self, mock_get): + def test_105__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - template in lower cases """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1231,7 +1240,7 @@ def test_104__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': 10}, self.cahandler.template_info_dic) @patch('requests.get') - def test_105__template_id_lookup(self, mock_get): + def test_106__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - no template """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1242,7 +1251,7 @@ def test_105__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_106__template_id_lookup(self, mock_get): + def test_107__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - no linktype """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1253,7 +1262,7 @@ def test_106__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_107__template_id_lookup(self, mock_get): + def test_108__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - empty list """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1264,7 +1273,7 @@ def test_107__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_108__template_id_lookup(self, mock_get): + def test_109__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - no items """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1275,7 +1284,7 @@ def test_108__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_109__template_id_lookup(self, mock_get): + def test_110__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - wrong dict """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1286,7 +1295,7 @@ def test_109__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_110__template_id_lookup(self, mock_get): + def test_111__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - wrong dict """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1297,7 +1306,7 @@ def test_110__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_111__template_id_lookup(self, mock_get): + def test_112__template_id_lookup(self, mock_get): """ CAhandler._template_id_lookup() - empty response """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1308,7 +1317,7 @@ def test_111__template_id_lookup(self, mock_get): self.assertEqual({'name': 'template_name', 'id': None}, self.cahandler.template_info_dic) @patch('requests.get') - def test_112__template_id_lookup(self, mock_req): + def test_113__template_id_lookup(self, mock_req): """ CAhandler._cert_id_lookup() - request raises exception """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('req_exc') @@ -1322,7 +1331,7 @@ def test_112__template_id_lookup(self, mock_req): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_113__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_114__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter """ self.cahandler.__enter__() self.assertTrue(mock_load.called) @@ -1334,7 +1343,7 @@ def test_113__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_114__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_115__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter with host already defined """ self.cahandler.api_host = 'api_host' self.cahandler.__enter__() @@ -1347,7 +1356,7 @@ def test_114__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_115__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_116__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter with header defined """ self.cahandler.headers = 'header' self.cahandler.__enter__() @@ -1360,7 +1369,7 @@ def test_115__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_116__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_117__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter with error defined """ self.cahandler.error = 'error' self.cahandler.__enter__() @@ -1373,7 +1382,7 @@ def test_116__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_117__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_118__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter with tst_info_dic defined """ self.cahandler.tsg_info_dic = {'id': 'foo'} self.cahandler.__enter__() @@ -1386,7 +1395,7 @@ def test_117__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._login') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._tsg_id_lookup') - def test_118__enter__(self, mock_lookup, mock_login, mock_check, mock_load): + def test_119__enter__(self, mock_lookup, mock_login, mock_check, mock_load): """ test enter with error defined """ self.cahandler.tsg_info_dic = {'id': 'foo'} self.cahandler.error = 'error' @@ -1398,7 +1407,7 @@ def test_118__enter__(self, mock_lookup, mock_login, mock_check, mock_load): @patch('examples.ca_handler.nclm_ca_handler.cert_serial_get') @patch('requests.get') - def test_119_revoke(self, mock_get, mock_serial): + def test_120_revoke(self, mock_get, mock_serial): """ test revoke empty certificate list has been returned """ self.cahandler.api_host = 'api_host' mock_serial.return_value = 11 @@ -1409,7 +1418,7 @@ def test_119_revoke(self, mock_get, mock_serial): @patch('examples.ca_handler.nclm_ca_handler.cert_serial_get') @patch('requests.get') - def test_120_revoke(self, mock_get, mock_serial): + def test_121_revoke(self, mock_get, mock_serial): """ test revoke request get aborted with exception """ self.cahandler.api_host = 'api_host' mock_serial.return_value = 11 @@ -1420,7 +1429,7 @@ def test_120_revoke(self, mock_get, mock_serial): @patch('examples.ca_handler.nclm_ca_handler.cert_serial_get') @patch('requests.get') - def test_121_revoke(self, mock_get, mock_serial): + def test_122_revoke(self, mock_get, mock_serial): """ test revoke certificates in certificate_list but content is bogus """ self.cahandler.api_host = 'api_host' mock_serial.return_value = 11 @@ -1432,7 +1441,7 @@ def test_121_revoke(self, mock_get, mock_serial): @patch('requests.post') @patch('examples.ca_handler.nclm_ca_handler.cert_serial_get') @patch('requests.get') - def test_122_revoke(self, mock_get, mock_serial, mock_post): + def test_123_revoke(self, mock_get, mock_serial, mock_post): """ test revoke certificates in certificate_list all good """ self.cahandler.api_host = 'api_host' mock_serial.return_value = 11 @@ -1447,7 +1456,7 @@ def test_122_revoke(self, mock_get, mock_serial, mock_post): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') @patch('examples.ca_handler.nclm_ca_handler.cert_serial_get') @patch('requests.get') - def test_123_revoke(self, mock_get, mock_serial, mock_post): + def test_124_revoke(self, mock_get, mock_serial, mock_post): """ test revoke certificates in certificate_list but request.post returns execption """ self.cahandler.api_host = 'api_host' mock_serial.return_value = 11 @@ -1457,65 +1466,52 @@ def test_123_revoke(self, mock_get, mock_serial, mock_post): mock_post.side_effect = Exception('ex_req_post') self.assertEqual((500, 'urn:ietf:params:acme:error:serverInternal', 'Revocation operation failed'), self.cahandler.revoke('cert', 'rev_reason', 'rev_date')) - def test_124_enroll(self): + def test_125_enroll(self): """ enroll() if there is an error """ self.cahandler.error = 'foo' with self.assertLogs('test_a2c', level='INFO') as lcm: self.assertEqual((None, None, None, None), self.cahandler.enroll('csr')) self.assertIn('ERROR:test_a2c:foo', lcm.output) - def test_125_enroll(self): + def test_126_enroll(self): """ enroll() no target-system-id """ self.cahandler.tsg_info_dic = {'id': None, 'name': 'name'} self.assertEqual(('CAhandler.eroll(): ID lookup for targetSystemGroup "name" failed.', None, None, None), self.cahandler.enroll('csr')) - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._csr_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._request_import') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_enroll') @patch('examples.ca_handler.nclm_ca_handler.csr_san_get') @patch('examples.ca_handler.nclm_ca_handler.csr_cn_get') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_policylink_id_lookup') - def test_126_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup): - """ enroll() without certid """ + def test_127_enroll(self, mock_lookup, mock_tmpl_lookup, mock_cn_get, mock_san_get, mock_enroll): + """ enroll() with certid """ self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} self.cahandler.wait_interval = 0 mock_lookup.return_value = 10 mock_cn_get.return_value = 'cn' mock_san_get.return_value = ['foo.bar.local'] - mock_reqimp.return_value = True - mock_csr_lookup.return_value = 10 - mock_post.return_value = True - mock_cert_lookup.return_value = None - self.assertEqual(("certifcate id lookup failed for: cn, ['foo.bar.local']", None, None, None), self.cahandler.enroll('csr')) + mock_enroll.return_value = ['error', 'cert_bundle', 'cert_raw'] + self.assertEqual(('error', 'cert_bundle', 'cert_raw', None), self.cahandler.enroll('csr')) self.assertFalse(mock_tmpl_lookup.called) - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._csr_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._request_import') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_enroll') @patch('examples.ca_handler.nclm_ca_handler.csr_san_get') @patch('examples.ca_handler.nclm_ca_handler.csr_cn_get') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_policylink_id_lookup') - def test_127_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup, mock_bundle): + def test_128_enroll(self, mock_lookup, mock_tmpl_lookup, mock_cn_get, mock_san_get, mock_enroll): """ enroll() with certid """ self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': None} self.cahandler.wait_interval = 0 mock_lookup.return_value = 10 mock_cn_get.return_value = 'cn' mock_san_get.return_value = ['foo.bar.local'] - mock_reqimp.return_value = True - mock_csr_lookup.return_value = 10 - mock_post.return_value = True - mock_cert_lookup.return_value = 10 - mock_bundle.return_value = ('error', 'cert_bundle', 'cert_raw') + mock_enroll.return_value = ['error', 'cert_bundle', 'cert_raw'] self.assertEqual(('error', 'cert_bundle', 'cert_raw', None), self.cahandler.enroll('csr')) - self.assertFalse(mock_tmpl_lookup.called) + self.assertTrue(mock_tmpl_lookup.called) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') @@ -1526,13 +1522,13 @@ def test_127_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, m @patch('examples.ca_handler.nclm_ca_handler.csr_san_get') @patch('examples.ca_handler.nclm_ca_handler.csr_cn_get') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_policylink_id_lookup') - def test_128_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup, mock_bundle): - """ enroll() no tmpload """ + def test_129_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup, mock_bundle): + """ enroll() tmpload """ self.cahandler.api_host = 'api_host' - self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.tsg_info_dic = {'id': 'id', 'name': 'name'} self.cahandler.wait_interval = 0 - self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} - mock_lookup.return_value = 10 + self.cahandler.template_info_dic = {'name': 'name', 'id': None} + mock_lookup.return_value = 0 mock_cn_get.return_value = 'cn' mock_san_get.return_value = ['foo.bar.local'] mock_reqimp.return_value = True @@ -1540,63 +1536,133 @@ def test_128_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, m mock_post.return_value = True mock_cert_lookup.return_value = 10 mock_bundle.return_value = ('error', 'cert_bundle', 'cert_raw') - self.assertEqual(('error', 'cert_bundle', 'cert_raw', None), self.cahandler.enroll('csr')) + self.assertEqual(('enrollment aborted. policylink_id: 0, tsg_id: id', None, None, None), self.cahandler.enroll('csr')) self.assertFalse(mock_tmpl_lookup.called) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._csr_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._request_import') - @patch('examples.ca_handler.nclm_ca_handler.csr_san_get') - @patch('examples.ca_handler.nclm_ca_handler.csr_cn_get') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_policylink_id_lookup') - def test_129_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup, mock_bundle): - """ enroll() tmpload """ + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_130_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ + self.cahandler.wait_interval = 0 self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + mock_post.return_value = 'mock_post' + self.assertEqual(('enrollment failed: mock_post', None, None), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertFalse(mock_cert_lookup.called) + self.assertFalse(mock_bundle.called) + + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_131_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ self.cahandler.wait_interval = 0 - self.cahandler.template_info_dic = {'name': 'name', 'id': None} - mock_lookup.return_value = 10 - mock_cn_get.return_value = 'cn' - mock_san_get.return_value = ['foo.bar.local'] - mock_reqimp.return_value = True - mock_csr_lookup.return_value = 10 - mock_post.return_value = True - mock_cert_lookup.return_value = 10 - mock_bundle.return_value = ('error', 'cert_bundle', 'cert_raw') - self.assertEqual(('error', 'cert_bundle', 'cert_raw', None), self.cahandler.enroll('csr')) - self.assertTrue(mock_tmpl_lookup.called) + self.cahandler.api_host = 'api_host' + self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} + mock_post.return_value = 'mock_post' + self.assertEqual(('enrollment failed: mock_post', None, None), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertFalse(mock_cert_lookup.called) + self.assertFalse(mock_bundle.called) @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._template_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._csr_id_lookup') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._request_import') - @patch('examples.ca_handler.nclm_ca_handler.csr_san_get') - @patch('examples.ca_handler.nclm_ca_handler.csr_cn_get') - @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_policylink_id_lookup') - def test_130_enroll(self, mock_lookup, mock_cn_get, mock_san_get, mock_reqimp, mock_csr_lookup, mock_post, mock_cert_lookup, mock_tmpl_lookup, mock_bundle): - """ enroll() tmpload """ + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_132_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ + self.cahandler.wait_interval = 0 self.cahandler.api_host = 'api_host' - self.cahandler.tsg_info_dic = {'id': 'id', 'name': 'name'} + self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} + mock_post.return_value = {'status': 400} + self.assertEqual(("enrollment failed: {'status': 400}", None, None), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertFalse(mock_cert_lookup.called) + self.assertFalse(mock_bundle.called) + + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_133_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ self.cahandler.wait_interval = 0 - self.cahandler.template_info_dic = {'name': 'name', 'id': None} - mock_lookup.return_value = 0 - mock_cn_get.return_value = 'cn' - mock_san_get.return_value = ['foo.bar.local'] - mock_reqimp.return_value = True - mock_csr_lookup.return_value = 10 - mock_post.return_value = True + self.cahandler.api_host = 'api_host' + self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} + mock_post.return_value = {'status': 400, 'message': 'message'} + self.assertEqual(("enrollment failed: message", None, None), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertFalse(mock_cert_lookup.called) + self.assertFalse(mock_bundle.called) + + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_134_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ + self.cahandler.wait_interval = 0 + self.cahandler.api_host = 'api_host' + self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} + mock_post.return_value = {'status': 200, 'message': None} + mock_cert_lookup.return_value = None + self.assertEqual(("certifcate id lookup failed for: cn, ['san']", None, None), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertTrue(mock_cert_lookup.called) + self.assertFalse(mock_bundle.called) + + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_bundle_build') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._cert_id_lookup') + @patch('examples.ca_handler.nclm_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.nclm_ca_handler.b64_encode') + @patch('examples.ca_handler.nclm_ca_handler.convert_string_to_byte') + @patch('examples.ca_handler.nclm_ca_handler.build_pem_file') + def test_135_cert_enroll(self, mock_build, mock_byte, mock_b64, mock_post, mock_cert_lookup, mock_bundle): + """ test cert_enroll() """ + self.cahandler.wait_interval = 0 + self.cahandler.api_host = 'api_host' + self.cahandler.tsg_info_dic = {'id': 10, 'name': 'name'} + self.cahandler.template_info_dic = {'name': 'name', 'id': 'id'} + mock_post.return_value = {'status': 200, 'message': None} mock_cert_lookup.return_value = 10 mock_bundle.return_value = ('error', 'cert_bundle', 'cert_raw') - self.assertEqual(('enrollment aborted. policylink_id: 0, tsg_id: id', None, None, None), self.cahandler.enroll('csr')) - self.assertFalse(mock_tmpl_lookup.called) + self.assertEqual(('error', 'cert_bundle', 'cert_raw'), self.cahandler._cert_enroll('csr', 'cn', ['san'], 'policylink_id'))# + self.assertTrue(mock_build.called) + self.assertTrue(mock_byte.called) + self.assertTrue(mock_b64.called) + self.assertTrue(mock_cert_lookup.called) + self.assertTrue(mock_bundle.called) @patch('requests.get') - def test_131__cert_list_fetch(self, mock_req): + def test_136__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - response without next """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1605,7 +1671,7 @@ def test_131__cert_list_fetch(self, mock_req): self.assertEqual(['foo', 'bar'], self.cahandler._cert_list_fetch('url')) @patch('requests.get') - def test_132__cert_list_fetch(self, mock_req): + def test_137__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - response 1x pagination """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1616,7 +1682,7 @@ def test_132__cert_list_fetch(self, mock_req): self.assertEqual(['foo1', 'bar1', 'foo2', 'bar2'], self.cahandler._cert_list_fetch('url')) @patch('requests.get') - def test_133__cert_list_fetch(self, mock_req): + def test_138__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - response 2x pagination """ self.cahandler.api_host = 'api_host' mockresponse1 = Mock() @@ -1629,7 +1695,7 @@ def test_133__cert_list_fetch(self, mock_req): self.assertEqual(['foo1', 'bar1', 'foo2', 'bar2', 'foo3', 'bar3'], self.cahandler._cert_list_fetch('url')) @patch('requests.get') - def test_134__cert_list_fetch(self, mock_req): + def test_139__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - empty response """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1638,7 +1704,7 @@ def test_134__cert_list_fetch(self, mock_req): self.assertFalse(self.cahandler._cert_list_fetch('url')) @patch('requests.get') - def test_135__cert_list_fetch(self, mock_req): + def test_140__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - request.get triggers execption """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('foo') @@ -1647,7 +1713,7 @@ def test_135__cert_list_fetch(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._cert_list_fetch() returned error: foo', lcm.output) @patch('requests.get') - def test_136__lastrequests_get(self, mock_req): + def test_141__lastrequests_get(self, mock_req): """ test_132__lastrequests_get() - all ok """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1656,7 +1722,7 @@ def test_136__lastrequests_get(self, mock_req): self.assertEqual(['foo', 'bar', 'foo', 'bar'], self.cahandler._lastrequests_get()) @patch('requests.get') - def test_137__lastrequests_get(self, mock_req): + def test_142__lastrequests_get(self, mock_req): """ test_132__lastrequests_get() - all ok """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1665,7 +1731,7 @@ def test_137__lastrequests_get(self, mock_req): self.assertEqual(['foo', 'bar', 'foo', 'bar'], self.cahandler._lastrequests_get()) @patch('requests.get') - def test_138__lastrequests_get(self, mock_req): + def test_143__lastrequests_get(self, mock_req): """ test_132__lastrequests_get() - no request list in response """ self.cahandler.api_host = 'api_host' mockresponse = Mock() @@ -1676,7 +1742,7 @@ def test_138__lastrequests_get(self, mock_req): self.assertIn('ERROR:test_a2c:_lastrequests_get(): response incomplete:', lcm.output) @patch('requests.get') - def test_139__cert_list_fetch(self, mock_req): + def test_144__cert_list_fetch(self, mock_req): """ _cert_list_fetch() - request.get triggers execption """ self.cahandler.api_host = 'api_host' mock_req.side_effect = Exception('foo') @@ -1684,33 +1750,33 @@ def test_139__cert_list_fetch(self, mock_req): self.assertFalse(self.cahandler._lastrequests_get()) self.assertIn('ERROR:test_a2c:CAhandler._lastrequests_get() returned error: foo', lcm.output) - def test_140__ca_id_get(self): + def test_145__ca_id_get(self): """ test _ca_id_get() """ ca_list = {} self.assertFalse(self.cahandler._ca_id_get(ca_list)) - def test_141__ca_id_get(self): + def test_146__ca_id_get(self): """ test _ca_id_get() """ ca_list = {'ca': {'foo': 'bar'}} self.assertFalse(self.cahandler._ca_id_get(ca_list)) - def test_142__ca_id_get(self): + def test_147__ca_id_get(self): """ test _ca_id_get() """ ca_list = {'ca': {'items': 'bar'}} self.assertFalse(self.cahandler._ca_id_get(ca_list)) - def test_143__ca_id_get(self): + def test_148__ca_id_get(self): """ test _ca_id_get() """ ca_list = {'ca': {'items': [{'foo': 'bar'}]}} self.assertFalse(self.cahandler._ca_id_get(ca_list)) - def test_144__ca_id_get(self): + def test_149__ca_id_get(self): """ test _ca_id_get() """ self.cahandler.ca_name = 'ca_name' ca_list = {'ca': {'items': [{'displayName': 'ca_name', 'policyLinkId': 'id'}]}} self.assertEqual('id', self.cahandler._ca_id_get(ca_list)) - def test_145__ca_id_get(self): + def test_150__ca_id_get(self): """ test _ca_id_get() """ self.cahandler.ca_name = 'ca_name' ca_list = {'ca': {'items': [{'displayName': 'ca_name', 'foo': 'id'}]}} @@ -1718,7 +1784,7 @@ def test_145__ca_id_get(self): self.assertFalse(self.cahandler._ca_id_get(ca_list)) self.assertIn('ERROR:test_a2c:ca_id.lookup() policyLinkId field is missing ...', lcm.output) - def test_146__ca_id_get(self): + def test_151__ca_id_get(self): """ test _ca_id_get() """ self.cahandler.ca_name = 'ca_name1' ca_list = {'ca': {'items': [{'displayName': 'ca_name', 'policyLinkId': 'id'}]}} @@ -1726,7 +1792,7 @@ def test_146__ca_id_get(self): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_id_get') @patch('requests.get') - def test_147__ca_policylink_id_lookup(self, mock_req, mock_caid): + def test_152__ca_policylink_id_lookup(self, mock_req, mock_caid): """ test _ca_policylink_id_lookup() """ self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 'id'} @@ -1739,7 +1805,7 @@ def test_147__ca_policylink_id_lookup(self, mock_req, mock_caid): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_id_get') @patch('requests.get') - def test_148__ca_policylink_id_lookup(self, mock_req, mock_caid): + def test_153__ca_policylink_id_lookup(self, mock_req, mock_caid): """ test _ca_policylink_id_lookup() """ self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 'id'} @@ -1754,7 +1820,7 @@ def test_148__ca_policylink_id_lookup(self, mock_req, mock_caid): @patch('examples.ca_handler.nclm_ca_handler.CAhandler._ca_id_get') @patch('requests.get') - def test_149__ca_policylink_id_lookup(self, mock_req, mock_caid): + def test_154__ca_policylink_id_lookup(self, mock_req, mock_caid): """ test _ca_policylink_id_lookup() """ self.cahandler.api_host = 'api_host' self.cahandler.tsg_info_dic = {'id': 'id'} From 4dc24b86735355c3bfe7b5f1a3808faed81a45ba Mon Sep 17 00:00:00 2001 From: grindsa Date: Fri, 6 Sep 2024 20:58:44 +0200 Subject: [PATCH 03/54] [fix] removed dependency on python3-impacket --- examples/install_scripts/debian/control | 2 +- examples/install_scripts/rpm/acme2certifier.spec | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/install_scripts/debian/control b/examples/install_scripts/debian/control index 63fd840a..db357d03 100644 --- a/examples/install_scripts/debian/control +++ b/examples/install_scripts/debian/control @@ -11,7 +11,7 @@ Rules-Requires-Root: no Package: acme2certifier Architecture: all -Depends: ${misc:Depends}, tzdata, python3-setuptools, python3-jwcrypto, python3-cryptography, python3-openssl, python3-dnspython, python3-pytzdata, python3-configargparse, python3-dateutil, python3-requests, python3-requests-ntlm, python3-socks, python3-josepy, python3-acme, python3-impacket, python3-xmltodict, python3-pyasn1, python3-pyasn1-modules, python3-django, python3-mysqldb, python3-pymysql, python3-psycopg2, python3-yaml +Depends: ${misc:Depends}, tzdata, python3-setuptools, python3-jwcrypto, python3-cryptography, python3-openssl, python3-dnspython, python3-pytzdata, python3-configargparse, python3-dateutil, python3-requests, python3-requests-ntlm, python3-socks, python3-josepy, python3-acme, python3-xmltodict, python3-pyasn1, python3-pyasn1-modules, python3-django, python3-mysqldb, python3-pymysql, python3-psycopg2, python3-yaml Description: Library implementing ACME server functionality acme2certifier is development project to create an ACME protocol proxy. Main intention is to provide ACME services on CA servers which do not support this protocol yet. After installation remember to install either NGINX or apache2! diff --git a/examples/install_scripts/rpm/acme2certifier.spec b/examples/install_scripts/rpm/acme2certifier.spec index e75cf545..44370602 100644 --- a/examples/install_scripts/rpm/acme2certifier.spec +++ b/examples/install_scripts/rpm/acme2certifier.spec @@ -36,7 +36,6 @@ Requires: python3-requests-pkcs12 Requires: python3-pysocks Requires: python3-josepy Requires: python3-acme -Requires: python3-impacket Requires: python3-xmltodict Requires: python3-pyasn1 Requires: python3-pyasn1-modules From 0ebfd35bde9d759fca01c76d8d98b69550d09996 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 7 Sep 2024 06:49:06 +0200 Subject: [PATCH 04/54] [doc] updated documentation --- docs/install_rpm.md | 2 +- docs/mswcce.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/install_rpm.md b/docs/install_rpm.md index b8d33c6f..5443d77d 100644 --- a/docs/install_rpm.md +++ b/docs/install_rpm.md @@ -31,7 +31,7 @@ Backports of these packages being part of RHEL9 can be found in the [the a2c rpm Depending on your ca_handler you may need additional modules: -- [python3-impacket-0.11.0-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-1.el8.noarch.rpm) when using [MS wcce handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mswcce.md) +- [python3-impacket-0.11.0-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) when using [MS wcce handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mswcce.md) - [python3-ntlm-auth-1.5.0-2.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-ntlm-auth-1.5.0-2.el8.noarch.rpm) when using [MS wse handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mscertsrv.md) - [python3-requests_ntlm-1.1.0-14.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-requests_ntlm-1.1.0-14.el8.noarch.rpm) when using [MS wse handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mscertsrv.md) - [python3-requests-pkcs12-1.16-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-requests-pkcs12-1.16-1.el8.noarch.rpm) when using [EST](https://github.com/grindsa/acme2certifier/blob/master/docs/est.md) or [EJBCA](https://github.com/grindsa/acme2certifier/blob/master/docs/ejbca.md) handler diff --git a/docs/mswcce.md b/docs/mswcce.md index 3b63b084..e6a59dff 100644 --- a/docs/mswcce.md +++ b/docs/mswcce.md @@ -20,6 +20,8 @@ When using the handler please be aware of the following limitations: - install the [impacket](https://github.com/SecureAuthCorp/impacket) via pip (the module is already part of the docker images) +Some malware scanners like Microsoft Defender classify the impacket module as hacking-tool (see [forta/impacket#1762](https://github.com/fortra/impacket/issues/1762) or [forta/impacket#1271](https://github.com/fortra/impacket/issues/1271#issuecomment-1058729047)). Main reason for the alarms are not the library itself but rather the example script coming along with it. To avoid hazzle with your CSIRT team created to install slimmed down versions of for [RH8](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) and [RH9](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel9/python3-impacket-0.11.0-2grindsa.el9.noarch.rpm) which do not contain the scripts flagged by the scanners. + ```bash root@rlh:~# pip install impacket ``` From 6ec0e34ada30b2596dfaf92da61cbda163360e04 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 7 Sep 2024 17:39:08 +0200 Subject: [PATCH 05/54] [fix] strip down python-impacket in docker images --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bbb2f893..2f762c63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,6 @@ requests pysocks josepy acme -impacket xmltodict pyasn1 pyasn1_modules From 4cba8f08b1c54f50b0b28ac9daf873eea122f05e Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 7 Sep 2024 17:45:07 +0200 Subject: [PATCH 06/54] [fix] strip down impacket module when building container --- examples/Docker/apache2/django/Dockerfile | 3 ++- examples/Docker/apache2/wsgi/Dockerfile | 3 ++- examples/Docker/nginx/django/Dockerfile | 3 ++- examples/Docker/nginx/wsgi/Dockerfile | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/Docker/apache2/django/Dockerfile b/examples/Docker/apache2/django/Dockerfile index c6be88ec..11497884 100644 --- a/examples/Docker/apache2/django/Dockerfile +++ b/examples/Docker/apache2/django/Dockerfile @@ -29,7 +29,8 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ cp /var/www/acme2certifier/examples/apache2/apache_django.conf /etc/apache2/sites-enabled/acme2certifier.conf && \ cp -R /var/www/acme2certifier/examples/django/* /var/www/acme2certifier/ && \ cp /var/www/acme2certifier/examples/db_handler/django_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ diff --git a/examples/Docker/apache2/wsgi/Dockerfile b/examples/Docker/apache2/wsgi/Dockerfile index 035de9f9..1f80d433 100644 --- a/examples/Docker/apache2/wsgi/Dockerfile +++ b/examples/Docker/apache2/wsgi/Dockerfile @@ -24,7 +24,8 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ cp /var/www/acme2certifier/examples/apache2/apache_wsgi.conf /etc/apache2/sites-enabled/acme2certifier.conf && \ cp /var/www/acme2certifier/examples/acme2certifier_wsgi.py /var/www/acme2certifier/acme2certifier_wsgi.py && \ cp /var/www/acme2certifier/examples/db_handler/wsgi_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ diff --git a/examples/Docker/nginx/django/Dockerfile b/examples/Docker/nginx/django/Dockerfile index dff12580..d979808d 100644 --- a/examples/Docker/nginx/django/Dockerfile +++ b/examples/Docker/nginx/django/Dockerfile @@ -25,7 +25,8 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ -RUN pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ cp -R /var/www/acme2certifier/examples/django/* /var/www/acme2certifier/ && \ cp /var/www/acme2certifier/examples/db_handler/django_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ cp /var/www/acme2certifier/examples/nginx/acme2certifier.ini /var/www/acme2certifier && \ diff --git a/examples/Docker/nginx/wsgi/Dockerfile b/examples/Docker/nginx/wsgi/Dockerfile index fc9a9a4f..f3c346a9 100644 --- a/examples/Docker/nginx/wsgi/Dockerfile +++ b/examples/Docker/nginx/wsgi/Dockerfile @@ -20,7 +20,8 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ cp /var/www/acme2certifier/examples/acme2certifier_wsgi.py /var/www/acme2certifier/acme2certifier_wsgi.py && \ cp /var/www/acme2certifier/examples/db_handler/wsgi_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ cp /var/www/acme2certifier/examples/nginx/acme2certifier.ini /var/www/acme2certifier && \ From a1938f42032d8b2dc95cdf4e7be70cee70535e06 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 8 Sep 2024 07:46:39 +0200 Subject: [PATCH 07/54] [doc] impacket instructions in mswcce_hanndler --- docs/mswcce.md | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/mswcce.md b/docs/mswcce.md index e6a59dff..f2926f6b 100644 --- a/docs/mswcce.md +++ b/docs/mswcce.md @@ -16,16 +16,42 @@ When using the handler please be aware of the following limitations: 3. (optional): In case you are installing from RPM or DEB and plan to use kerberos authentication you need an updated [impacket modules of version 0.11 or higher](https://github.com/fortra/impacket) as older versions have issues with the handling of utf8-encoded passwords. If you have no clue from where to get these packaages feel free to use the one being part of [the a2c github repository](https://github.com/grindsa/sbom/tree/main/rpm-repo/RPMs) 4. You need to have a set of credentials with permissions to access the service and enrollment templates -## Installation +## Local Installation -- install the [impacket](https://github.com/SecureAuthCorp/impacket) via pip (the module is already part of the docker images) +- install the [impacket](https://github.com/fortra/impacket) module -Some malware scanners like Microsoft Defender classify the impacket module as hacking-tool (see [forta/impacket#1762](https://github.com/fortra/impacket/issues/1762) or [forta/impacket#1271](https://github.com/fortra/impacket/issues/1271#issuecomment-1058729047)). Main reason for the alarms are not the library itself but rather the example script coming along with it. To avoid hazzle with your CSIRT team created to install slimmed down versions of for [RH8](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) and [RH9](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel9/python3-impacket-0.11.0-2grindsa.el9.noarch.rpm) which do not contain the scripts flagged by the scanners. +*IMPORTANT*: + +Some malware scanners like Microsoft Defender classify the impacket module as hacking-tool (see [forta/impacket#1762](https://github.com/fortra/impacket/issues/1762) or [forta/impacket#1271](https://github.com/fortra/impacket/issues/1271#issuecomment-1058729047)). Main reason for the alarms are not the library itself but rather the example script coming along with it. To avoid hazzle with your CSIRT team I suggest to install a slimmed down versions impacket which which do not contain the scripts flagged by the scanners. Packages for [RH8](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) and [RH9](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel9/python3-impacket-0.11.0-2grindsa.el9.noarch.rpm) can be found in my [SBOM repo](https://github.com/grindsa/sbom/tree/main/rpm-repo) + +In case you install impacket from pip or form sources I suggest to: + +- download the impacket package: + +```bash +pip3 download impacket --no-deps +``` + +- unpack the archive + +```bash + tar xvfz impacket-0.11.0.tar.gz +``` + +- delete all files and subdirectories in `examples` sub-directory + +```bash +rm -rf impacket-0.11.0/examples/* +``` + +- install the package ```bash -root@rlh:~# pip install impacket +python3 setup.py install ``` +## Configuration + - modify the server configuration (acme_srv/acme_srv.cfg) and add the following parameters ```config From a65d24cdc4258d59220207aa5004ef8ec1959281 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 8 Sep 2024 07:50:19 +0200 Subject: [PATCH 08/54] [doc] fix typo --- docs/mswcce.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mswcce.md b/docs/mswcce.md index f2926f6b..5483001d 100644 --- a/docs/mswcce.md +++ b/docs/mswcce.md @@ -22,7 +22,7 @@ When using the handler please be aware of the following limitations: *IMPORTANT*: -Some malware scanners like Microsoft Defender classify the impacket module as hacking-tool (see [forta/impacket#1762](https://github.com/fortra/impacket/issues/1762) or [forta/impacket#1271](https://github.com/fortra/impacket/issues/1271#issuecomment-1058729047)). Main reason for the alarms are not the library itself but rather the example script coming along with it. To avoid hazzle with your CSIRT team I suggest to install a slimmed down versions impacket which which do not contain the scripts flagged by the scanners. Packages for [RH8](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) and [RH9](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel9/python3-impacket-0.11.0-2grindsa.el9.noarch.rpm) can be found in my [SBOM repo](https://github.com/grindsa/sbom/tree/main/rpm-repo) +Some malware scanners like Microsoft Defender classify the impacket module as hacking-tool (see [forta/impacket#1762](https://github.com/fortra/impacket/issues/1762) or [forta/impacket#1271](https://github.com/fortra/impacket/issues/1271#issuecomment-1058729047)). Main reason for the alarms are not the library itself but rather the example script coming along with it. To avoid hazzle with your CSIRT team I suggest to install a strip-down version of impacket which which do not contain the scripts flagged by the scanners. Packages for [RH8](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) and [RH9](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel9/python3-impacket-0.11.0-2grindsa.el9.noarch.rpm) can be found in my [SBOM repo](https://github.com/grindsa/sbom/tree/main/rpm-repo) In case you install impacket from pip or form sources I suggest to: From 2efc0e36f8e3146bf94082169afc5c3e2af6f7ae Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 8 Sep 2024 09:26:09 +0200 Subject: [PATCH 09/54] [tst] fix in cert_extension_dic_parse testing --- test/test_openssl_ca_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_openssl_ca_handler.py b/test/test_openssl_ca_handler.py index 5b3d809b..7c4632e4 100644 --- a/test/test_openssl_ca_handler.py +++ b/test/test_openssl_ca_handler.py @@ -1016,7 +1016,7 @@ def test_118__cert_extension_dic_parse(self, mock_bc, mock_ski, mock_aki, mock_ mock_ku.return_value = 'mock_ku' mock_eku.return_value = 'mock_eku' cert_extension_dic = {'subjectKeyIdentifier': {'critical': True, 'value': 'value'}} - result = [{'critical': True, 'name': 'mock_ski'}] + result = [{'critical': False, 'name': 'mock_ski'}] with self.assertLogs('test_a2c', level='INFO') as lcm: self.assertEqual(result, self.cahandler._cert_extension_dic_parse(cert_extension_dic, cert, cert)) self.assertIn('INFO:test_a2c:CAhandler.cert_extesion_dic_parse(): subjectKeyIdentifier', lcm.output) From 83ee1a152788e28b314fa53caad02fc2200be249 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 5 Oct 2024 08:22:40 +0100 Subject: [PATCH 10/54] [feat] subject profiling --- acme_srv/helper.py | 96 ++++++++++++++++++++++++++-- test/test_helper.py | 150 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 237 insertions(+), 9 deletions(-) diff --git a/acme_srv/helper.py b/acme_srv/helper.py index c3fbaf72..f6d139d5 100644 --- a/acme_srv/helper.py +++ b/acme_srv/helper.py @@ -437,6 +437,7 @@ def cert_ski_get(logger: logging.Logger, certificate: str) -> str: def cryptography_version_get(logger: logging.Logger) -> int: """ get version number of cryptography module """ logger.debug('Helper.cryptography_version_get()') + # pylint: disable=c0415 import cryptography try: @@ -484,7 +485,7 @@ def cert_extensions_py_openssl_get(logger, certificate, recode=True): ext = cert.get_extension(i) extension_list.append(convert_byte_to_string(base64.b64encode(ext.get_data()))) - logger.debug('cert_extensions_py_openssl_get() ended with: {0}'.format(extension_list)) + logger.debug('cert_extensions_py_openssl_get() ended with: %s', extension_list) return extension_list @@ -650,6 +651,22 @@ def csr_extensions_get(logger: logging.Logger, csr: str) -> List[str]: return extension_list +def csr_subject_get(logger: logging.Logger, csr: str) -> str: + """ get subject from csr as a list of tuples """ + logger.debug('Helper.csr_subject_get()') + # pylint: disable=w0212 + + csr_obj = csr_load(logger, csr) + subject_dic = {} + # get subject and look for common name + subject = csr_obj.subject + for attr in subject: + subject_dic[attr.oid._name] = attr.value + + logger.debug('Helper.csr_subject_get() ended') + return subject_dic + + def decode_deserialize(logger: logging.Logger, string: str) -> Dict: """ decode and deserialize string """ logger.debug('Helper.decode_deserialize()') @@ -1708,6 +1725,75 @@ def eab_profile_header_info_check(logger: logging.Logger, cahandler, csr: str, h return error +def cn_validate(logger: logging.Logger, cn: str) -> bool: + """ validate common name """ + logger.debug('Helper.cn_validate(%s)', cn) + + error = False + if cn: + # check if CN is a valid IP address + result = validate_ip(logger, cn) + if not result: + # check if CN is a valid fqdn + result = validate_fqdn(logger, cn) + if not result: + error = 'Profile subject check failed: CN validation failed' + else: + error = 'Profile subject check failed: commonName missing' + + logger.debug('Helper.cn_validate() ended with: %s', error) + return error + + +def eab_profile_subject_string_check(logger: logging.Logger, profile_subject_dic, key: str, value: str) -> str: + """ check if a for a string value taken from profile if its a variable inside a class and apply value """ + logger.debug('Helper.eab_profile_subject_string_check(): string: key: %s, value: %s', key, value) + + error = False + if key == 'commonName': + # check if CN is a valid IP address or fqdn + error = cn_validate(logger, value) + elif key in profile_subject_dic: + if isinstance(profile_subject_dic[key], str) and value == profile_subject_dic[key]: + logger.debug('Helper.eab_profile_subject_check() successul for string : %s', key) + del profile_subject_dic[key] + elif isinstance(profile_subject_dic[key], list) and value in profile_subject_dic[key]: + logger.debug('Helper.eab_profile_subject_check() successul for list : %s', key) + del profile_subject_dic[key] + else: + logger.error('Helper.eab_profile_subject_check() failed for: %s: value: %s expected: %s', key, value, profile_subject_dic[key]) + error = f'Profile subject check failed for {key}' + else: + logger.error('Helper.eab_profile_subject_check() failed for: %s', key) + error = f'Profile subject check failed for {key}' + + logger.debug('Helper.eab_profile_subject_string_check() ended') + return error + + +def eab_profile_subject_check(logger: logging.Logger, csr: str, profile_subject_dic: str) -> str: + """ check subject against profile information""" + logger.debug('Helper.eab_profile_subject_check()') + error = None + + # get subject from csr + subject_dic = csr_subject_get(logger, csr) + + # check if all profile subject entries are in csr + for key, value in subject_dic.items(): + error = eab_profile_subject_string_check(logger, profile_subject_dic, key, value) + if error: + break + + # check if we have any entries left in the profile_subject_dic + if not error and profile_subject_dic: + logger.error('Helper.eab_profile_subject_check() failed for: %s', list(profile_subject_dic.keys())) + error = 'Profile subject check failed' + + logger.debug('Helper.eab_profile_subject_check() ended with: %s', error) + return error + + def eab_profile_check(logger: logging.Logger, cahandler, csr: str, handler_hifield: str) -> str: """ check eab profile""" logger.debug('Helper.eab_profile_check()') @@ -1716,7 +1802,9 @@ def eab_profile_check(logger: logging.Logger, cahandler, csr: str, handler_hifie with cahandler.eab_handler(logger) as eab_handler: eab_profile_dic = eab_handler.eab_profile_get(csr) for key, value in eab_profile_dic.items(): - if isinstance(value, str): + if key == 'subject': + result = eab_profile_subject_check(logger, csr, value) + elif isinstance(value, str): eab_profile_string_check(logger, cahandler, key, value) elif isinstance(value, list): # check if we need to execute a function from the handler @@ -1724,8 +1812,8 @@ def eab_profile_check(logger: logging.Logger, cahandler, csr: str, handler_hifie result = cahandler.eab_profile_list_check(eab_handler, csr, key, value) else: result = eab_profile_list_check(logger, cahandler, eab_handler, csr, key, value) - if result: - break + if result: + break # we need to reject situations where profiling is enabled but the header_hifiled is not defined in json if cahandler.header_info_field and handler_hifield not in eab_profile_dic: diff --git a/test/test_helper.py b/test/test_helper.py index 8e6114eb..78cf4957 100644 --- a/test/test_helper.py +++ b/test/test_helper.py @@ -26,7 +26,7 @@ def setUp(self): """ setup unittest """ import logging logging.basicConfig(level=logging.CRITICAL) - from acme_srv.helper import b64decode_pad, b64_decode, b64_encode, b64_url_encode, b64_url_recode, convert_string_to_byte, convert_byte_to_string, decode_message, decode_deserialize, get_url, generate_random_string, signature_check, validate_email, uts_to_date_utc, date_to_uts_utc, load_config, cert_serial_get, cert_san_get, cert_san_pyopenssl_get, cert_dates_get, build_pem_file, date_to_datestr, datestr_to_date, dkeys_lower, csr_cn_get, cert_pubkey_get, csr_pubkey_get, url_get, url_get_with_own_dns, dns_server_list_load, csr_san_get, csr_san_byte_get, csr_extensions_get, fqdn_resolve, fqdn_in_san_check, sha256_hash, sha256_hash_hex, cert_der2pem, cert_pem2der, cert_extensions_get, csr_dn_get, logger_setup, logger_info, print_debug, jwk_thumbprint_get, allowed_gai_family, patched_create_connection, validate_csr, servercert_get, txt_get, proxystring_convert, proxy_check, handle_exception, ca_handler_load, eab_handler_load, hooks_load, error_dic_get, _logger_nonce_modify, _logger_certificate_modify, _logger_token_modify, _logger_challenges_modify, config_check, cert_issuer_get, cert_cn_get, string_sanitize, pembundle_to_list, certid_asn1_get, certid_check, certid_hex_get, v6_adjust, ipv6_chk, ip_validate, header_info_get, encode_url, uts_now, cert_ski_get, cert_ski_pyopenssl_get, cert_aki_get, cert_aki_pyopenssl_get, validate_fqdn, validate_ip, validate_identifier, header_info_field_validate, header_info_lookup, config_eab_profile_load, config_headerinfo_load, domainlist_check, allowed_domainlist_check, eab_profile_string_check, eab_profile_list_check, eab_profile_check, eab_profile_header_info_check, cert_extensions_py_openssl_get, cryptography_version_get + from acme_srv.helper import b64decode_pad, b64_decode, b64_encode, b64_url_encode, b64_url_recode, convert_string_to_byte, convert_byte_to_string, decode_message, decode_deserialize, get_url, generate_random_string, signature_check, validate_email, uts_to_date_utc, date_to_uts_utc, load_config, cert_serial_get, cert_san_get, cert_san_pyopenssl_get, cert_dates_get, build_pem_file, date_to_datestr, datestr_to_date, dkeys_lower, csr_cn_get, cert_pubkey_get, csr_pubkey_get, url_get, url_get_with_own_dns, dns_server_list_load, csr_san_get, csr_san_byte_get, csr_extensions_get, fqdn_resolve, fqdn_in_san_check, sha256_hash, sha256_hash_hex, cert_der2pem, cert_pem2der, cert_extensions_get, csr_dn_get, logger_setup, logger_info, print_debug, jwk_thumbprint_get, allowed_gai_family, patched_create_connection, validate_csr, servercert_get, txt_get, proxystring_convert, proxy_check, handle_exception, ca_handler_load, eab_handler_load, hooks_load, error_dic_get, _logger_nonce_modify, _logger_certificate_modify, _logger_token_modify, _logger_challenges_modify, config_check, cert_issuer_get, cert_cn_get, string_sanitize, pembundle_to_list, certid_asn1_get, certid_check, certid_hex_get, v6_adjust, ipv6_chk, ip_validate, header_info_get, encode_url, uts_now, cert_ski_get, cert_ski_pyopenssl_get, cert_aki_get, cert_aki_pyopenssl_get, validate_fqdn, validate_ip, validate_identifier, header_info_field_validate, header_info_lookup, config_eab_profile_load, config_headerinfo_load, domainlist_check, allowed_domainlist_check, eab_profile_string_check, eab_profile_list_check, eab_profile_check, eab_profile_header_info_check, cert_extensions_py_openssl_get, cryptography_version_get, cn_validate, csr_subject_get, eab_profile_subject_string_check, eab_profile_subject_check self.logger = logging.getLogger('test_a2c') self.allowed_gai_family = allowed_gai_family self.b64_decode = b64_decode @@ -122,6 +122,10 @@ def setUp(self): self.eab_profile_list_check = eab_profile_list_check self.eab_profile_check = eab_profile_check self.eab_profile_header_info_check = eab_profile_header_info_check + self.cn_validate = cn_validate + self.csr_subject_get = csr_subject_get + self.eab_profile_subject_string_check = eab_profile_subject_string_check + self.eab_profile_subject_check = eab_profile_subject_check def test_001_helper_b64decode_pad(self): """ test b64decode_pad() method with a regular base64 encoded string """ @@ -2905,6 +2909,22 @@ def test_362_eab_profile_check(self, mock_string, mock_list): self.assertFalse(mock_string.called) self.assertFalse(mock_list.called) + @patch('acme_srv.helper.eab_profile_subject_check') + @patch('acme_srv.helper.eab_profile_list_check') + @patch('acme_srv.helper.eab_profile_string_check') + def test_363_eab_profile_check(self, mock_string, mock_list, mock_subject): + self.cahandler = MagicMock() + self.csr = "testCSR" + self.handler_hifield = None + mock_subject.return_value = 'mock_subject' + self.cahandler.eab_handler.return_value.__enter__.return_value.eab_profile_get.return_value = {"subject": ["listValue"]} + self.cahandler.eab_profile_list_check.return_value = 'eab_list_check' + mock_list.return_value = None + self.assertEqual('mock_subject', self.eab_profile_check(self.logger, self.cahandler, self.csr, self.handler_hifield)) + self.assertFalse(mock_string.called) + self.assertFalse(mock_list.called) + self.assertTrue(mock_subject.called) + @patch('cryptography.__version__', '3.4.7') def test_363_cryptography_version_get_success(self): self.assertEqual(3, self.cryptography_version_get(self.logger)) @@ -2915,12 +2935,132 @@ def test_364_cryptography_version_get_success(self): self.assertEqual(36, self.cryptography_version_get(self.logger)) self.assertIn("ERROR:test_a2c:cryptography_version_get(): Error: 'NoneType' object has no attribute 'split'", lcm.output) + @patch('acme_srv.helper.validate_fqdn') + @patch('acme_srv.helper.validate_ip') + def test_365_cn_validate(self, mock_ip, mock_fqdn): + """ test cn_validate() """ + mock_ip.return_value = True + mock_fqdn.return_value = True + self.assertFalse(self.cn_validate(self.logger, 'foo.bar.com')) + self.assertFalse(mock_fqdn.called) - #@patch('cryptography.__version__', new_callable=lambda: None) - #def test_364_cryptography_version_get_missing_version(self): - # with self.assertRaises(AttributeError): - # cryptography_version_get() + @patch('acme_srv.helper.validate_fqdn') + @patch('acme_srv.helper.validate_ip') + def test_366_cn_validate(self, mock_ip, mock_fqdn): + """ test cn_validate() """ + mock_ip.return_value = False + mock_fqdn.return_value = True + self.assertFalse(self.cn_validate(self.logger, 'foo.bar.com')) + self.assertTrue(mock_fqdn.called) + + @patch('acme_srv.helper.validate_fqdn') + @patch('acme_srv.helper.validate_ip') + def test_367_cn_validate(self, mock_ip, mock_fqdn): + """ test cn_validate() """ + mock_ip.return_value = False + mock_fqdn.return_value = False + self.assertEqual('Profile subject check failed: CN validation failed', self.cn_validate(self.logger, 'foo.bar.com')) + self.assertTrue(mock_fqdn.called) + + @patch('acme_srv.helper.validate_fqdn') + @patch('acme_srv.helper.validate_ip') + def test_368_cn_validate(self, mock_ip, mock_fqdn): + """ test cn_validate() """ + mock_ip.return_value = False + mock_fqdn.return_value = False + self.assertEqual('Profile subject check failed: commonName missing', self.cn_validate(self.logger, None)) + self.assertFalse(mock_fqdn.called) + def test_369_csr_subject_get(self): + """ test csr_subject_get() """ + csr = 'MIICwDCCAagCAQAwVDESMBAGA1UEAwwJbGVnby5hY21lMQ0wCwYDVQQKDARhY21lMQwwCgYDVQQLDANmb28xCzAJBgNVBAYTAlVTMRQwEgYDVQQFEwswMC0xMS0yMi0zMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM5AKMmB3o8LLEEGuHo0Ipl4K8z9m3EyM9teSVocQz39DK8s2dKpx8MrsVkTg6M3fuL4yPlim8v0+unPtB18dFeThkijHetxL5x08pVvMVwa7Cjk/22e5IRgBGSQYCO6KCUsNh2vhH93r7x71wlTV3sYe2t0HaEdGqBxdct76J9kyeCY06Br+4PMR7afRvHv4vFH6Y2+hSD4oOd5cSTZXnNWcWRbjNFY7aytzl4JpJiEK0ealDMSf/ZP0n8Sdx1vCx8amaozrLg5z3eLULiAUUgCtqOWOgNLQFNSqjyhZmMTZGGJcTgb43KAKWsO3bfM6rvNTZRbrM7dAsg/bQsK6mMCAwEAAaAnMCUGCSqGSIb3DQEJDjEYMBYwFAYDVR0RBA0wC4IJbGVnby5hY21lMA0GCSqGSIb3DQEBCwUAA4IBAQA19j8Lge9Vqxc/hvWYcU1Kx3KBx5TN97PK0wQFPIIWX20/JRoodzfrMSqO0EgZWB+czoRi8G+2ezbK13sV02dKovo8ISoSvgSZtt53UKBz+JmQd7Q7G1vONZ7d2PT0nTUN4fTA5YQs5nys3O8/2oOxJiJO6IyhmpiVqUbrlU6Harb4MfjNTb+teSQRSCOAX/8U9TdPwuAi6rXdWjXAUxBDQySWkW/B3pd77Ztt5nDFP2DT+7f7mAoWG4+XY6iXcXs1GsDA4XRTx2rCvhQtQomVGAKFwd8aTpHL/ZwNt1GOw6oMZkKKf+axVA1pvAYGhey/4x3uwKf654VB3e2iOCea' + result_dic = {'commonName': 'lego.acme', 'organizationName': 'acme', 'organizationalUnitName': 'foo', 'countryName': 'US', 'serialNumber': '00-11-22-33'} + self.assertEqual(result_dic, self.csr_subject_get(self.logger, csr)) + + def test_370_csr_subject_get(self): + """ test csr_subject_get() """ + csr = 'MIICcDCCAVgCAQAwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOKk0E61QJ2K/NiGSO0aJyqrLfmHytPr35ptLwNdfKQ/8Vb2uoHYAvxVEO9weNTQVlZ9ApkJquBTRoSdqTy6p87inh8JwzFM/neJAsMg2ZiH3gRRRfmIb/4Kce0BUQ66DFSV8sWThyv13EcL+pZYdqRvONujVn7XVPbmB2ZI8qI4iXswRq45mFBW5Dyt3Rlw+KOBu1ejo0lqB2FGQiBONxQrFDyF4nVWN3R9BlhuybSF4Elhos7pkiEfrE+8EzYy+7yMEiDh1m+TmwZRNEdtSWNORF51CF3bYUz8pvpt66vKGi/F6k2iljelw1kNsswZAciNi2jG7S0M+MWMFi680sCAwEAAaArMCkGCSqGSIb3DQEJDjEcMBowGAYDVR0RBBEwD4INZm9vLmJhci5sb2NhbDANBgkqhkiG9w0BAQsFAAOCAQEAivCrcL+uVzDdykT87073atC4B2DHky5bzL+iI8C+BkPq0jRdcVkExMrUtTdtp8Ot1zQHtYc/c/Tj+aYDZ6SdMYtrtHUgxS5JyFh0p+MEvkgZHcWOVC+VlWA+lC9kdX3WetsGT6xqCG4l+BpgCUERghFJ5/+K0bbCI4jT/5ZCT7+pO0qZtw0eg6tQBLPSXzXN98x3nmuaw9PzO1rVG5IMItyU+TlX3pJRXKpqSOHEbeaGWHizMUlbDKzoIiUf+11I9RwTeLlp/HPG8uvRc/zZ1einZPLQgow5kU15jFQSgQtzFHV4ZxuYmWN7oMIruwBNP1hkoTNL1kJcPeOwtEdOMw==' + self.assertFalse(self.csr_subject_get(self.logger, csr)) + + @patch('acme_srv.helper.cn_validate') + def test_371_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': 'bar1'} + self.assertEqual('Profile subject check failed for foo', self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar')) + self.assertFalse(mock_validate.called) + + @patch('acme_srv.helper.cn_validate') + def test_372_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': 'bar'} + self.assertFalse(self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar')) + self.assertFalse(mock_validate.called) + + @patch('acme_srv.helper.cn_validate') + def test_373_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': ['bar1', 'bar2', 'bar3']} + self.assertEqual('Profile subject check failed for foo', self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar')) + self.assertFalse(mock_validate.called) + + @patch('acme_srv.helper.cn_validate') + def test_374_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': ['bar1', 'bar2', 'bar3']} + self.assertFalse(self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar2')) + self.assertFalse(mock_validate.called) + + @patch('acme_srv.helper.cn_validate') + def test_375_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': ['bar1', 'bar2', 'bar3']} + mock_validate.return_value = 'error' + self.assertEqual('error', self.eab_profile_subject_string_check(self.logger, profile_dic, 'commonName', 'bar')) + self.assertTrue(mock_validate.called) + + @patch('acme_srv.helper.cn_validate') + def test_376_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': ['bar1', 'bar2', 'bar3']} + mock_validate.return_value = 'error' + self.assertEqual('Profile subject check failed for bar', self.eab_profile_subject_string_check(self.logger, profile_dic, 'bar', 'bar')) + self.assertFalse(mock_validate.called) + + @patch('acme_srv.helper.eab_profile_subject_string_check') + @patch('acme_srv.helper.csr_subject_get') + def test_377_eab_profile_subject_check(self, mock_cn, mock_strchk): + """ test eab_profile_subject_check() """ + profile_dic = {'foo': 'bar'} + mock_cn.return_value = {'o': 'o', 'ou': 'ou', 'cn': 'cn'} + mock_strchk.side_effect = ['o', 'ou', 'cn'] + self.assertEqual('o', self.eab_profile_subject_check(self.logger, 'csr', profile_dic)) + + @patch('acme_srv.helper.eab_profile_subject_string_check') + @patch('acme_srv.helper.csr_subject_get') + def test_378_eab_profile_subject_check(self, mock_cn, mock_strchk): + """ test eab_profile_subject_check() """ + profile_dic = {'foo': 'bar'} + mock_cn.return_value = {'o': 'o', 'ou': 'ou', 'cn': 'cn'} + mock_strchk.side_effect = [False, 'ou', 'cn'] + self.assertEqual('ou', self.eab_profile_subject_check(self.logger, 'csr', profile_dic)) + + @patch('acme_srv.helper.eab_profile_subject_string_check') + @patch('acme_srv.helper.csr_subject_get') + def test_379_eab_profile_subject_check(self, mock_cn, mock_strchk): + """ test eab_profile_subject_check() """ + profile_dic = {'foo': 'bar'} + mock_cn.return_value = {'o': 'o', 'ou': 'ou', 'cn': 'cn'} + mock_strchk.side_effect = [False, False, 'cn'] + self.assertEqual('cn', self.eab_profile_subject_check(self.logger, 'csr', profile_dic)) + + @patch('acme_srv.helper.eab_profile_subject_string_check') + @patch('acme_srv.helper.csr_subject_get') + def test_378_eab_profile_subject_check(self, mock_cn, mock_strchk): + """ test eab_profile_subject_check() """ + profile_dic = {'foo': 'bar'} + mock_cn.return_value = {'o': 'o', 'ou': 'ou', 'cn': 'cn'} + mock_strchk.side_effect = [False, False, False] + self.assertEqual('Profile subject check failed', self.eab_profile_subject_check(self.logger, 'csr', profile_dic)) if __name__ == '__main__': unittest.main() From d2d47555ecd64f4eaae9f119d942a9b09c801e7f Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 6 Oct 2024 07:53:39 +0100 Subject: [PATCH 11/54] [fix] add wildcard handling --- acme_srv/helper.py | 2 +- test/test_helper.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/acme_srv/helper.py b/acme_srv/helper.py index f6d139d5..a1ad5a33 100644 --- a/acme_srv/helper.py +++ b/acme_srv/helper.py @@ -1754,7 +1754,7 @@ def eab_profile_subject_string_check(logger: logging.Logger, profile_subject_dic # check if CN is a valid IP address or fqdn error = cn_validate(logger, value) elif key in profile_subject_dic: - if isinstance(profile_subject_dic[key], str) and value == profile_subject_dic[key]: + if isinstance(profile_subject_dic[key], str) and (value == profile_subject_dic[key] or profile_subject_dic[key] == '*'): logger.debug('Helper.eab_profile_subject_check() successul for string : %s', key) del profile_subject_dic[key] elif isinstance(profile_subject_dic[key], list) and value in profile_subject_dic[key]: diff --git a/test/test_helper.py b/test/test_helper.py index 78cf4957..f278dd18 100644 --- a/test/test_helper.py +++ b/test/test_helper.py @@ -2983,12 +2983,19 @@ def test_370_csr_subject_get(self): self.assertFalse(self.csr_subject_get(self.logger, csr)) @patch('acme_srv.helper.cn_validate') - def test_371_eab_profile_subjet_string_check(self, mock_validate): + def test_370_eab_profile_subjet_string_check(self, mock_validate): """ test eab_profile_subject_string_check() """ profile_dic = {'foo': 'bar1'} self.assertEqual('Profile subject check failed for foo', self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar')) self.assertFalse(mock_validate.called) + @patch('acme_srv.helper.cn_validate') + def test_371_eab_profile_subjet_string_check(self, mock_validate): + """ test eab_profile_subject_string_check() """ + profile_dic = {'foo': '*'} + self.assertFalse(self.eab_profile_subject_string_check(self.logger, profile_dic, 'foo', 'bar')) + self.assertFalse(mock_validate.called) + @patch('acme_srv.helper.cn_validate') def test_372_eab_profile_subjet_string_check(self, mock_validate): """ test eab_profile_subject_string_check() """ From f20805a427808469bb824f564a463d9756dae19e Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 7 Oct 2024 19:17:18 +0100 Subject: [PATCH 12/54] [wf] subject profiling tests in xca handler workflow --- .github/actions/acme_clients/action.yml | 4 +- .../xca_ca_handler/enroll_eab_sp/action.yml | 362 ++++++++++++++++++ .github/workflows/ca_handler_tests_xca.yml | 99 ++++- 3 files changed, 460 insertions(+), 5 deletions(-) create mode 100644 .github/actions/wf_specific/xca_ca_handler/enroll_eab_sp/action.yml diff --git a/.github/actions/acme_clients/action.yml b/.github/actions/acme_clients/action.yml index 2b428c24..99054378 100644 --- a/.github/actions/acme_clients/action.yml +++ b/.github/actions/acme_clients/action.yml @@ -44,8 +44,8 @@ runs: - name: "Create directories" run: | mkdir -p acme-sh/ - mkdir -p certbot/ - mkdir -p lego/ca + sudo mkdir -p certbot/ + sudo mkdir -p lego/ca sudo cp .github/acme2certifier_cabundle.pem certbot/ sudo cp .github/acme2certifier_cabundle.pem lego/ if [ -f cert-2.pem ]; then diff --git a/.github/actions/wf_specific/xca_ca_handler/enroll_eab_sp/action.yml b/.github/actions/wf_specific/xca_ca_handler/enroll_eab_sp/action.yml new file mode 100644 index 00000000..9b97905d --- /dev/null +++ b/.github/actions/wf_specific/xca_ca_handler/enroll_eab_sp/action.yml @@ -0,0 +1,362 @@ +name: "enroll_eab" +description: "enroll_eab" +inputs: + DEPLOYMENT_TYPE: + description: "Deployment type" + required: true + default: "container" + + +runs: + using: "composite" + steps: + - name: "EAB - Sleep for 10s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 10s + + - name: "EAB - Test http://acme-srv/directory is accessible" + run: docker run -i --rm --network acme curlimages/curl -f http://acme-srv/directory + shell: bash + + - name: "EAB - Test if https://acme-srv/directory is accessible" + run: docker run -i --rm --network acme curlimages/curl --insecure -f https://acme-srv/directory + shell: bash + + - name: "EAB SP - 01a - SUCC - Enroll acme - 1st list entry" + run: | + mkdir -p acme-sh + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=acme corp/OU=acme1/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme/ca.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "EAB SP - 01a - SUCC - Enroll lego - 1st list entry" + run: | + sudo mkdir -p lego + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=acme corp/OU=acme1/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "EAB SP - 01B - SUCC - Enroll acme - 2nd list entry" + run: | + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=acme corp/OU=acme2/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "EAB SP - 01B - SUCC - Enroll lego - 2nd list entry" + run: | + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=acme corp/OU=acme2/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "EAB SP - 01C - FAIL - Enroll acme - entry not in list" + id: acmefail01 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=acme corp/OU=acme3/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "Check result" + if: steps.acmefail01.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail01.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "organizationalUnitName: value: acme3 expected: \['acme1', 'acme2'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "organizationalUnitName: value: acme3 expected: \['acme1', 'acme2'\]" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 01C - Fail - Enroll lego - entry not in list" + id: legofail01 + continue-on-error: true + run: | + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=acme corp/OU=acme3/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "Check result" + if: steps.legofail01.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail01.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "organizationalUnitName: value: acme3 expected: \['acme1', 'acme2'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "organizationalUnitName: value: acme3 expected: \['acme1', 'acme2'\]" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 02 - FAIL - Enroll acme - wildcard entry not present" + id: acmefail02 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=acme corp/OU=acme1/C=AC' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "Check result" + if: steps.acmefail02.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail02.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: \['serialNumber'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: \['serialNumber'\]" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 02 - FAIL - Enroll lego - wildcard entry not present" + id: legofail02 + continue-on-error: true + run: | + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=acme corp/OU=acme1/C=AC' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "Check result" + if: steps.legofail02.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail02.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: \['serialNumber'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: \['serialNumber'\]" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 03 - FAIL - Enroll acme - string check failed" + id: acmefail03 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=noacme corp/OU=acme2/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "Check result" + if: steps.acmefail03.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail03.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: organizationName: value: noacme corp expected: acme corp" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: organizationName: value: noacme corp expected: acme corp" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 03 - FAIL - Enroll lego - string check failed" + id: legofail03 + continue-on-error: true + run: | + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=noacme corp/OU=acme2/C=AC/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "Check result" + if: steps.legofail03.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail03.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: organizationName: value: noacme corp expected: acme corp" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: organizationName: value: noacme corp expected: acme corp" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 04 - FAIL - Enroll acme - string parameter not present" + id: acmefail04 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + openssl genrsa -out acme-sh/acme-sh.acme.key 2048 + openssl req -new -key acme-sh/acme-sh.acme.key -subj '/CN=acme-sh.acme/O=acme corp/OU=acme2/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:acme-sh.acme" -outform pem -out acme-sh/acme-sh.acme.csr + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh neilpang/acme.sh:latest --signcsr --csr acme.sh/acme-sh.acme.csr --server http://acme-srv --standalone --debug 1 --output-insecure --insecure + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme/acme-sh.acme.cer + openssl x509 -in acme-sh/acme-sh.acme/acme-sh.acme.cer -text -noout + shell: bash + + - name: "Check result" + if: steps.acmefail04.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail04.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: \['countryName'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: \['countryName'\]" /var/log/messages + fi + shell: bash + + - name: "EAB SP - 04 - FAIL - Enroll acme - string parameter not present" + id: legofail04 + continue-on-error: true + run: | + sudo rm -rf lego/* + sudo openssl genrsa -out lego/lego.acme.key 2048 + sudo openssl req -new -key lego/lego.acme.key -subj '/CN=lego.acme/O=acme corp/OU=acme2/serialNumber=00-11-22-33' --addext "subjectAltName = DNS:lego.acme" -outform pem -out lego/lego.acme.csr + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --csr .lego/lego.acme.csr --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.crt + sudo openssl x509 -in lego/certificates/lego.acme.crt -text -noout + shell: bash + + - name: "Check result" + if: steps.legofail04.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail04.outcome }}" + exit 1 + shell: bash + + - name: "Sleep for 2s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 2s + + - name: "Check logs for errors" + working-directory: examples/Docker/ + run: | + if [ ${{ inputs.DEPLOYMENT_TYPE }} == "container" ]; then + docker-compose logs > docker-compose.log + cat docker-compose.log | grep "failed for: \['countryName'\]" + sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' acme2certifier_acme-srv_1) + # elif [ ${{ inputs.DEPLOYMENT_TYPE }} == "rpm" ]; then + # docker exec acme-srv grep "failed for: \['countryName'\]" /var/log/messages + fi + shell: bash + diff --git a/.github/workflows/ca_handler_tests_xca.yml b/.github/workflows/ca_handler_tests_xca.yml index f57a6745..57e1db19 100644 --- a/.github/workflows/ca_handler_tests_xca.yml +++ b/.github/workflows/ca_handler_tests_xca.yml @@ -170,6 +170,53 @@ jobs: - name: "EAB - enrollment" uses: ./.github/actions/wf_specific/xca_ca_handler/enroll_eab + - name: "EAB subject profiling - Setup a2c with xca_ca_handler " + run: | + sudo cp .github/acme2certifier.pem examples/Docker/data/acme2certifier.pem + sudo cp .github/acme2certifier_cert.pem examples/Docker/data/acme2certifier_cert.pem + sudo cp .github/acme2certifier_key.pem examples/Docker/data/acme2certifier_key.pem + sudo cp .github/django_settings.py examples/Docker/data/settings.py + sudo mkdir -p examples/Docker/data/xca + sudo chmod -R 777 examples/Docker/data/xca + sudo cp test/ca/acme2certifier-clean.xdb examples/Docker/data/xca/$XCA_DB_NAME + sudo mkdir -p examples/Docker/data/acme_ca/certs + sudo cp test/ca/sub-ca-key.pem test/ca/sub-ca-crl.pem test/ca/sub-ca-cert.pem test/ca/root-ca-cert.pem examples/Docker/data/acme_ca/ + sudo touch examples/Docker/data/acme_srv.cfg + sudo chmod 777 examples/Docker/data/acme_srv.cfg + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_choosen_handler.cfg > examples/Docker/data/acme_srv.cfg + sudo echo "handler_file: /var/www/acme2certifier/examples/ca_handler/xca_ca_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "xdb_file: volume/xca/$XCA_DB_NAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "issuing_ca_name: $XCA_ISSUING_CA" >> examples/Docker/data/acme_srv.cfg + sudo echo "passphrase: $XCA_PASSPHRASE" >> examples/Docker/data/acme_srv.cfg + sudo echo "ca_cert_chain_list: [\"root-ca\"]" >> examples/Docker/data/acme_srv.cfg + sudo echo "template_name: $XCA_TEMPLATE" >> examples/Docker/data/acme_srv.cfg + sudo echo "eab_profiling: True" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" examples/Docker/data/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> examples/Docker/data/acme_srv.cfg + sudo echo "eab_handler_file: /var/www/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "key_file: volume/kid_profiles.json" >> examples/Docker/data/acme_srv.cfg + + sudo cp examples/eab_handler/kid_profiles.json examples/Docker/data/kid_profiles.json + sudo chmod 777 examples/eab_handler/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"template_name\"\: \"acme\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"template_name\"\: \"template\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"issuing_ca_name\": \"root-ca\",\n \"issuing_ca_key\": \"root-ca\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\",/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/example.net/acme/g" examples/Docker/data/kid_profiles.json + sudo sed -i '19,20d' examples/Docker/data/kid_profiles.json + sudo sed -i '9d' examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"api_user\"\: \"api_user\",/\"subject\"\: \{\n \"serialNumber\"\: \"*\",\n \"organizationName\"\: \"acme corp\",\n \"organizationalUnitName\"\: \[\"acme1\", \"acme2\"\],\n \"countryName\"\: \"AC\"\n \}/g" examples/Docker/data/kid_profiles.json + cd examples/Docker/ + docker-compose restart + env: + XCA_PASSPHRASE: ${{ secrets.XCA_PASSPHRASE }} + XCA_ISSUING_CA: ${{ secrets.XCA_ISSUING_CA }} + XCA_TEMPLATE: ${{ secrets.XCA_TEMPLATE }} + XCA_DB_NAME: ${{ secrets.XCA_DB_NAME }} + + - name: "EAB subject profiling - enrollment" + uses: ./.github/actions/wf_specific/xca_ca_handler/enroll_eab_sp + - name: "[ * ] collecting test logs" if: ${{ failure() }} run: | @@ -227,11 +274,11 @@ jobs: XCA_TEMPLATE: ${{ secrets.XCA_TEMPLATE }} XCA_DB_NAME: ${{ secrets.XCA_DB_NAME }} - - name: "Execute install scipt" + - name: "No template - Execute install scipt" run: | docker exec acme-srv sh /tmp/acme2certifier/rpm_tester.sh - - name: "Test enrollment" + - name: "No template - Test enrollment" uses: ./.github/actions/acme_clients - name: "No Template - enrollment" @@ -262,7 +309,7 @@ jobs: run: | docker exec acme-srv sh /tmp/acme2certifier/rpm_tester.sh restart - - name: "Test enrollment" + - name: "Template - Test enrollment" uses: ./.github/actions/acme_clients - name: "Template - enrollment" @@ -341,6 +388,52 @@ jobs: - name: "EAB - enrollment" uses: ./.github/actions/wf_specific/xca_ca_handler/enroll_eab + - name: "EAB subject profiling - Setup a2c with xca_ca_handler" + run: | + mkdir -p data/acme_ca + sudo cp test/ca/acme2certifier-clean.xdb data/acme_ca/$XCA_DB_NAME + sudo mkdir -p examples/Docker/data/acme_ca/certs + sudo cp test/ca/sub-ca-key.pem test/ca/sub-ca-crl.pem test/ca/sub-ca-cert.pem test/ca/root-ca-cert.pem data/acme_ca/ + sudo touch data/acme_srv.cfg + sudo chmod 777 data/acme_srv.cfg + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > data/acme_srv.cfg + sudo echo "handler_file: /opt/acme2certifier/examples/ca_handler/xca_ca_handler.py" >> data/acme_srv.cfg + sudo echo "xdb_file: volume/acme_ca/$XCA_DB_NAME" >> data/acme_srv.cfg + sudo echo "issuing_ca_name: $XCA_ISSUING_CA" >> data/acme_srv.cfg + sudo echo "passphrase: $XCA_PASSPHRASE" >> data/acme_srv.cfg + sudo echo "ca_cert_chain_list: [\"root-ca\"]" >> data/acme_srv.cfg + sudo echo "template_name: $XCA_TEMPLATE" >> data/acme_srv.cfg + sudo echo "eab_profiling: True" >> data/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" data/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> data/acme_srv.cfg + sudo echo "eab_handler_file: /opt/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> data/acme_srv.cfg + sudo echo "key_file: /opt/acme2certifier/volume/acme_ca/kid_profiles.json" >> data/acme_srv.cfg + sudo cp examples/eab_handler/kid_profiles.json data/acme_ca/kid_profiles.json + sudo chmod 777 data/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"template_name\"\: \[\"template\", \"acme\"\]/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"template_name\"\: \"template\"/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"issuing_ca_name\": \"root-ca\",\n \"issuing_ca_key\": \"root-ca\"/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\",/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/example.net/acme/g" data/acme_ca/kid_profiles.json + sudo sed -i '19,20d' data/acme_ca/kid_profiles.json + sudo sed -i '9d' data/acme_ca/kid_profiles.json + sudo sed -i "s/\"api_user\"\: \"api_user\",/\"subject\"\: \{\n \"serialNumber\"\: \"*\",\n \"organizationName\"\: \"acme corp\",\n \"organizationalUnitName\"\: \[\"acme1\", \"acme2\"\],\n \"countryName\"\: \"AC\"\n \}/g" data/acme_ca/kid_profiles.json + env: + XCA_PASSPHRASE: ${{ secrets.XCA_PASSPHRASE }} + XCA_ISSUING_CA: ${{ secrets.XCA_ISSUING_CA }} + XCA_TEMPLATE: ${{ secrets.XCA_TEMPLATE }} + XCA_DB_NAME: ${{ secrets.XCA_DB_NAME }} + + - name: "EAB subject profiling - Reconfigure a2c " + run: | + docker exec acme-srv sh /tmp/acme2certifier/rpm_tester.sh + docker exec acme-srv sh /tmp/acme2certifier/rpm_tester.sh restart + + - name: "EAB subject profiling - enrollment" + uses: ./.github/actions/wf_specific/xca_ca_handler/enroll_eab_sp + with: + DEPLOYMENT_TYPE: "rpm" + - name: "[ * ] collecting test logs" if: ${{ failure() }} run: | From adae18a22dec679ab38d94b1038ffe17b68af6c1 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 7 Oct 2024 21:13:22 +0100 Subject: [PATCH 13/54] [fix] pytest wf - install impacket dependency --- .github/workflows/python-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index dcedc089..985ac1f3 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -33,7 +33,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pytest + pip install pytest impacket if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: cp From 90b8594d3f2ae26eb9f4d59d1963e6343550d34c Mon Sep 17 00:00:00 2001 From: grindsa Date: Wed, 9 Oct 2024 18:16:47 +0100 Subject: [PATCH 14/54] [doc] subject profiling documentation --- docs/eab_profiling.md | 79 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/docs/eab_profiling.md b/docs/eab_profiling.md index a5a27c16..02fa0fbe 100644 --- a/docs/eab_profiling.md +++ b/docs/eab_profiling.md @@ -26,12 +26,12 @@ eab_handler_file: examples/eab_handler/kid_profile_handler.py key_file: volume/kid_profiles.json ``` -The `key_file` allows the specification enrollmenmt parameters per (external) acme-account. Main identifier is the key_id to be used during account registration. Any parameter used in the [CAhandler] configuration section of a handler can be customized. Below an example configuration to be used for [Insta Certifier](certifier.md) with some explaination: +The `key_file` allows the specification enrollmenmt parameters per (external) acme-account. Main identifier is the key_id to be used during account registration. Any parameter used in the [CAhandler] configuration section of a handler can be customized. Below an example configuration to be used for [Insta Certifier](certifier.md) with some explanation: ```json { "keyid_00": { - "hmac": "V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw", + "hmac": "hmac-key", "cahandler": { "profile_id": "profile_1", "allowed_domainlist": ["*.example.com", "*.example.org", "*.example.fi"], @@ -41,14 +41,14 @@ The `key_file` allows the specification enrollmenmt parameters per (external) ac } }, "keyid_01": { - "hmac": "YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg", + "hmac": "hmac-key", "cahandler": { "profile_id": ["profile_1", "profile_2", "profile_3"], - "allowed_domainlist": ["*.example.fi", "*.acme"], + "allowed_domainlist": ["*.example.fi", "*.acme"] } }, "keyid_02": { - "hmac": "YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr" + "hmac": "hmac-key" } } ``` @@ -57,6 +57,75 @@ The `key_file` allows the specification enrollmenmt parameters per (external) ac - Acme-accounts created with keyid "keyid_01" and can specify 3 different profile_ids by using the [header_info feature](header_info.md). Enrollment requests having other profile_ids will be rejected. In case no profile_id get specified the first profile_id in the list ("profile_1") will be used. SAN/CNs to be used are restricted to "example.fi" and ".local" All other enrollment paramters will be taken from acme_srv.cfg - Acme-accounts created with keyid "keyid_02" do not have any restriction. Enrolment parameters will be taken from the [CAhandler] section in ´acme_srv.cfg` +Starting from v0.36 acme2certifier does support profile configuration in yaml format. Below a configuration example providing the same level of functionality than the above json configuration + +```yaml +--- +{ +--- +keyid_00: + hmac: "hmac-key" + cahandler: + profile_id: "profile_1" + allowed_domainlist: + - "*.example.com" + - "*.example.org" + - "*.example.fi" + ca_name: "non_default_ca" + api_user: "non_default_api_user" + api_password: "api_password" +keyid_01: + hmac: "hmac-key" + cahandler: + profile_id: + - "profile_1" + - "profile_2" + - "profile_3" + allowed_domainlist: + - "*.example.fi" + - "*.acme" +keyid_02: + hmac: "hmac-key" +``` + +## subject profiling + +Starting from v0.36 the eab-profiling feature can be used to check and white-list the certificate subject DN. + +Attribute names must follow [RFC3039](https://www.rfc-editor.org/rfc/rfc3039.html#section-3.1.2); every RDN can be white-listed as: + +- string - attribute in CSR DN must match this value +- list - attribute in CSR DN must match one of the list entries +- "*" - any value matches as long as the attribute is present + +The below example configuration will only allow CSR matching the following ciriterias: + +- serial number can be of any value but must be included +- organizationalUnitName must be either "acme1" or "acme2" +- organizationName must be "acme corp" +- countryName must be "AC" +- additional CSR DN such as localityName or stateOrProvinceName are not allowed + +```json +... +{ + "keyid_00": { + "hmac": "hmac-key", + "cahandler": { + "template_name": ["template", "acme"], + "allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"], + "subject": { + "serialNumber": "*", + "organizationName": "acme corp", + "organizationalUnitName": ["acme1", "acme2"], + "countryName": "AC" + } + } + } +} +... +``` + ## Profile verification In the keyfile can be checked for consistency by using the `tools/eab_chk.py` utility. From 3c2eb3edacef7bbd87ac549a4446d38232be24c8 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 12 Oct 2024 08:27:11 +0100 Subject: [PATCH 15/54] [feat] digicert ca_handler --- README.md | 30 +- docs/digicert.md | 114 +++ examples/ca_handler/digicert_ca_handler.py | 352 ++++++++++ test/test_digicert.py | 767 +++++++++++++++++++++ 4 files changed, 1250 insertions(+), 13 deletions(-) create mode 100644 docs/digicert.md create mode 100644 examples/ca_handler/digicert_ca_handler.py create mode 100644 test/test_digicert.py diff --git a/README.md b/README.md index d8964c4b..917ef548 100644 --- a/README.md +++ b/README.md @@ -21,19 +21,23 @@ on [rfc8555](https://tools.ietf.org/html/rfc8555) - ca_handler.py - interface towards CA server. The intention of this library is to be modular that an [adaption to other CA servers](docs/ca_handler.md) should be straight forward. As of today the following handlers are available: - - [NetGuard Certificate Manager/Insta Certifier](docs/certifier.md) - - [NetGuard Certificate Lifecycle Manager](docs/nclm.md) - - [Insta ActiveCMS](docs/asa.md) - - [EJBCA](docs/ejbca.md) - - [OpenXPKI](docs/openxpki.md) - - [Microsoft Certificate Enrollment Web Services](docs/mscertsrv.md) - - [Microsoft Windows Client Certificate Enrollment Protocol (MS-WCCE) via RPC/DCOM](docs/mswcce.md) - - [Generic ACME protocol handler supporting Letsencrypt, BuyPass.com and ZeroSSL](docs/acme_ca.md) - - [Generic EST protocol handler](docs/est.md) - - [Generic CMPv2 protocol handler](docs/cmp.md) - - [Openssl](docs/openssl.md) - - [XCA](docs/xca.md) - - [acme2dfn](https://github.com/pfisterer/acme2dfn) (external; ACME proxy for the [German research network's PKI](https://www.pki.dfn.de/ueberblick-dfn-pki/) + +| E - Certificte Enrollment, R - Certificte Revocation, P - [EAB Profiling](docs/eab_profiling.md) |E|R|P| +| :-------- | - | - | - | +| [DigiCert® CertCentral](docs/digicert.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [EJBCA](docs/ejbca.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [Generic ACME protocol handler supporting Letsencrypt, BuyPass.com and ZeroSSL](docs/acme_ca.md) | :x: | :x: | :white_check_mark: | +| [Generic CMPv2 protocol handler](docs/cmp.md) | :white_check_mark: | :x: | :x: | +| [Generic EST protocol handler](docs/est.md) | :white_check_mark: | :x: | :x: | +| [Insta ActiveCMS](docs/asa.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [Microsoft Certificate Enrollment Web Services](docs/mscertsrv.md) | :white_check_mark: | :x: | :white_check_mark: | +| [Microsoft Windows Client Certificate Enrollment Protocol (MS-WCCE) via RPC/DCOM](docs/mswcce.md) | :white_check_mark: | :x: | :white_check_mark: | +| [NetGuard Certificate Lifecycle Manager](docs/nclm.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [NetGuard Certificate Manager/Insta Certifier](docs/certifier.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [Openssl](docs/openssl.md) | :white_check_mark: | :white_check_mark: | :x: | +| [OpenXPKI](docs/openxpki.md) | :white_check_mark: | :white_check_mark: | :x: | +| [XCA](docs/xca.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [acme2dfn](https://github.com/pfisterer/acme2dfn) (external; ACME proxy for the [German research network's PKI](https://www.pki.dfn.de/ueberblick-dfn-pki/)| :white_check_mark: | :x: | :x: | For more up-to-date information and further documentation, please visit the project's home page at: [https://github.com/grindsa/acme2certifier](https://github.com/grindsa/acme2certifier) diff --git a/docs/digicert.md b/docs/digicert.md new file mode 100644 index 00000000..fb7286ee --- /dev/null +++ b/docs/digicert.md @@ -0,0 +1,114 @@ + + +# Connecting to DigiCert CertCentral + +This handler can be used to enroll certificates from [DigiCert CertCentral](https://dev.digicert.com/en/certcentral-apis.html). + +## Prerequisites + +- you'll need: + - a DigiCert CertCentral subscription :-) + - an [API-Key](https://dev.digicert.com/en/certcentral-apis/authentication.html) for Authentication and Authorization + - an [Organization](https://dev.digicert.com/en/certcentral-apis/services-api/organizations.html) + - a [whitelisted domain](https://dev.digicert.com/en/certcentral-apis/services-api/domains.html) + +## Configuration + +- modify the server configuration (`acme_srv.cfg`) and add the first thre of the below mentioned parameters + +```confag +[CAhandler] +handler_file: examples/ca_handler/digicert_ca_handler.py +api_key: +organization_name: + +allowed_domainlist: +api_url: +organization_id: +cert_type: +signature_hash: +order_validity: +request_timeout: +eab_profiling: +``` + +- api_key - required - API key to access the API +- organization_name - required - Organization name as specified in DigiCert CertCentral +- allowed_domainlist: list of domain-names allowed for enrollment in json format (example: ["bar.local$, bar.foo.local]) +- api_url - optional - DigiCert CertCentral (default: https://www.digicert.com/services/v2/) +- organization_id - optional - organization id - configuration prevents additional rest-lookups +- cert_type - optional - [certificte type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be isused. (default: ssl_basic) +- signature_hash - optional - hash algorithm used for certificate signing - (default: sha256) +- order_validity - optional - oder validity (default: 1 year) +- request_timeout - optional - requests timeout in seconds for requests (default: 5s) +- eab_profiling - optional - [activate eab profiling](eab_profiling.md) (default: False) + +Use your favorite acme client for certificate enrollment. A list of clients used in our regression can be found in the [disclaimer section of our README file](../README.md) + +## Passing a cert_type from client to server + +The handler makes use of the [header_info_list feature](header_info.md) allowing an acme-client to specify a [certificate type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be used during certificate enrollment. This feature is disabled by default and must be activate in `acme_srv.cfg` as shown below + +```config +[Order] +... +header_info_list: ["HTTP_USER_AGENT"] +``` + +The acme-client can then specify the cert_type as part of its user-agent string. + +Example for acme.sh: + +```bash +docker exec -i acme-sh acme.sh --server http:// --issue -d --standalone --useragent cert_type=ssl_securesite_pro --debug 3 --output-insecure +``` + +Example for lego: + +```bash +docker run -i -v $PWD/lego:/.lego/ --rm --name lego goacme/lego -s http:// -a --email "lego@example.com" --user-agent cert_type=ssl_securesite_pro -d --http run +``` + +# eab profiling + +This handler can use the [eab profiling feture](eab_profiling.md) to allow individual enrollment configuration per acme-account as well as restriction of CN and SANs to be submitted within the CSR. The feature is disabled by default and must be activated in `acme_srv.cfg` + +```cfg +[EABhandler] +eab_handler_file: examples/eab_handler/kid_profile_handler.py +key_file: + +[CAhandler] +eab_profiling: True +``` + +below an example key-file used during regression testing: + +```json +{ + "keyid_00": { + "hmac": "V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw", + "cahandler": { + "cert_type": ["ssl_basic", "ssl_securesite_pro", "ssl_securesite_flex"], + "allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"], + "organization_name": "acme2certifier" + } + }, + "keyid_01": { + "hmac": "YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg", + "cahandler": { + "allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"], + "cert_type": "ssl_securesite_pro" + } + }, + "keyid_02": { + "hmac": "dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM", + "cahandler": { + "allowed_domainlist": ["www.example.com", "www.example.org"] + } + }, + "keyid_03": { + "hmac": "YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr" + } +} +``` diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py new file mode 100644 index 00000000..39ce08e7 --- /dev/null +++ b/examples/ca_handler/digicert_ca_handler.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +""" CA handler using Digicert CertCentralAPI""" +from __future__ import print_function +from typing import Tuple, Dict +import json +import requests +# pylint: disable=e0401 +from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load + + +CONTENT_TYPE = 'application/json' + + +class CAhandler(object): + """ Digicert CertCentralAP handler """ + + def __init__(self, _debug: bool = None, logger: object = None): + self.logger = logger + self.api_url = 'https://www.digicert.com/services/v2/' + self.api_key = None + self.cert_type = 'ssl_basic' + self.signature_hash = 'sha256' + self.order_validity = 1 + self.proxy = None + self.request_timeout = 10 + self.organization_id = None + self.organization_name = None + self.allowed_domainlist = [] + self.header_info_field = False + self.eab_handler = None + self.eab_profiling = False + + def __enter__(self): + """ Makes CAhandler a Context Manager """ + if not self.api_key: + self._config_load() + return self + + def __exit__(self, *args): + """ cose the connection at the end of the context """ + + def _allowed_domainlist_check(self, csr: str) -> str: + """ check allowed domainlist """ + self.logger.debug('CAhandler._allowed_domainlist_check()') + + error = None + # check CN and SAN against black/whitlist + if self.allowed_domainlist: + # check sans / cn against list of allowed comains from config + result = allowed_domainlist_check(self.logger, csr, self.allowed_domainlist) + if not result: + error = 'Either CN or SANs are not allowed by configuration' + + self.logger.debug('CAhandler._allowed_domainlist_check() ended with %s', error) + return error + + def _api_get(self, url: str) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_get()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.get(url=url, headers=headers, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_get() returned error during json parsing: %s', err_) + content = str(err_) + except Exception as err_: + self.logger.error('CAhandler._api_get() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _api_post(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_post()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.post(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + if api_response.text: + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_post() returned error during json parsing: %s', err_) + content = str(err_) + else: + content = None + except Exception as err_: + self.logger.error('CAhandler._api_post() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _api_put(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_put()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.put(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + if api_response.text: + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_put() returned error during json parsing: %s', err_) + content = str(err_) + else: + content = None + except Exception as err_: + self.logger.error('CAhandler._api_put() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _config_check(self) -> str: + """ check config """ + self.logger.debug('CAhandler._config_check()') + + error = None + for ele in ['api_url', 'api_key', 'organization_name']: + if not getattr(self, ele): + error = f'{ele} parameter in missing in config file' + self.logger.error('CAhandler._config_check() ended with error: %s', error) + break + + self.logger.debug('CAhandler._config_check() ended with: %s', error) + return error + + def _config_load(self): + """" load config from file """ + self.logger.debug('CAhandler._config_load()') + + config_dic = dict(load_config(self.logger, 'CAhandler')) + if 'CAhandler' in config_dic: + self.api_url = config_dic.get('CAhandler', {}).get('api_url', 'https://www.digicert.com/services/v2/') + self.api_key = config_dic.get('CAhandler', {}).get('api_key', None) + self.cert_type = config_dic.get('CAhandler', {}).get('cert_type', 'ssl_basic') + self.signature_hash = config_dic.get('CAhandler', {}).get('signature_hash', 'sha256') + self.order_validity = config_dic.get('CAhandler', {}).get('order_validity', 1) + self.request_timeout = config_dic.get('CAhandler', {}).get('request_timeout', 10) + self.organization_id = config_dic.get('CAhandler', {}).get('organization_id', None) + self.organization_name = config_dic.get('CAhandler', {}).get('organization_name', None) + + if 'allowed_domainlist' in config_dic['CAhandler']: + try: + self.allowed_domainlist = json.loads(config_dic['CAhandler']['allowed_domainlist']) + except Exception as err: + self.logger.error('CAhandler._config_load(): failed to parse allowed_domainlist: %s', err) + self.allowed_domainlist = 'failed to parse' + + # load profiling + self.eab_profiling, self.eab_handler = config_eab_profile_load(self.logger, config_dic) + # load header info + self.header_info_field = config_headerinfo_load(self.logger, config_dic) + + self.logger.debug('CAhandler._config_load() ended') + + def _order_send(self, csr: str, csr_cn) -> Tuple[str, str]: + """ place certificate order """ + self.logger.debug('CAhandler._order_send()') + order_url = f'{self.api_url}order/certificate/{self.cert_type}' + + if not csr.endswith('='): + # padding if needed + csr = csr + '=' * (-len(csr) % 4) + + if (not self.organization_id or self.eab_profiling) and self.organization_name and self.api_key: + self.organization_id = self._organiation_id_get() + + if self.organization_id: + data_dic = { + 'certificate': { + 'common_name': csr_cn, + 'csr': csr, + 'signature_hash': self.signature_hash, + 'server_platform': { + 'id': 34 + } + }, + 'organization': { + 'id': self.organization_id, + }, + 'order_validity': { + 'years': self.order_validity + } + } + + # enroll certificate + code, content = self._api_post(order_url, data_dic) + else: + self.logger.error('CAhandler._order_send() failed: configuration incomplete: organisation_id is missing') + code = 500 + content = 'organisation_id is missing' + + self.logger.debug('CAhandler._order_send() ended with code: %s', code) + return code, content + + def _order_response_parse(self, content: Dict[str, str]) -> Tuple[str, str, str]: + """ parse order response """ + self.logger.debug('CAhandler._order_response_parse()') + + cert_bundle = None + cert_raw = None + poll_identifier = None + + if content and 'certificate_chain' in content: + cert_bundle = '' + for cert in content['certificate_chain']: + if 'pem' in cert: + cert_bundle += cert['pem'] + '\n' + else: + self.logger.error('CAhandler._order_response_parse() failed: no pem in certificate_chain') + cert_raw = b64_encode(self.logger, cert_pem2der(content['certificate_chain'][0]['pem'])) + if 'id' in content: + poll_identifier = content['id'] + else: + self.logger.error('CAhandler._order_response_parse() polling_identifier generation failed: no id in response') + else: + self.logger.error('CAhandler._order_response_parse() failed: no certificate_chain in response') + + self.logger.debug('CAhandler._order_response_parse() ended') + return cert_bundle, cert_raw, poll_identifier + + def _organiation_id_get(self): + """ get organization ID """ + self.logger.debug('CAhandler._organiation_id_get()') + + org_url = f'{self.api_url}organization' + code, content = self._api_get(org_url) + + organization_id = None + if code in (200, 201): + for org in content['organizations']: + if org['name'] == self.organization_name: + self.logger.debug('CAhandler._organiation_id_get() found organization ID: %s', org['id']) + organization_id = org['id'] + break + + if not organization_id: + self.logger.error('CAhandler._organiation_id_get() failed') + + self.logger.debug('CAhandler._organiation_id_get() ended with: %s', organization_id) + return organization_id + + def _csr_check(self, csr: str) -> str: + """ check csr """ + self.logger.debug('CAhandler._csr_check()') + + # check for allowed domainlist + error = self._allowed_domainlist_check(csr) + + # check for eab profiling and header_info + if not error: + error = eab_profile_header_info_check(self.logger, self, csr, 'cert_type') + + self.logger.debug('CAhandler._csr_check() ended with: %s', error) + return error + + def enroll(self, csr: str) -> Tuple[str, str, str, str]: + """ enroll certificate """ + self.logger.debug('CAhandler.enroll()') + + cert_bundle = None + error = None + cert_raw = None + poll_indentifier = None + + # check configuration + error = self._config_check() + + if not error: + # check csr and profiling + error = self._csr_check(csr) + + if not error: + csr_cn = csr_cn_get(self.logger, csr) + code, content = self._order_send(csr, csr_cn) + + if code in (200, 201): + # successful + cert_bundle, cert_raw, poll_indentifier = self._order_response_parse(content) + else: + if 'errors' in content: + error = f'Error during order creation: {code} - {content['errors']}' + else: + error = f'Error during order creation: {code} - {content}' + + self.logger.debug('Certificate.enroll() ended') + return (error, cert_bundle, cert_raw, poll_indentifier) + + def poll(self, _cert_name: str, poll_identifier: str, _csr: str) -> Tuple[str, str, str, str, bool]: + """ poll status of pending CSR and download certificates """ + self.logger.debug('CAhandler.poll()') + + error = 'Method not implemented.' + cert_bundle = None + cert_raw = None + rejected = False + + self.logger.debug('CAhandler.poll() ended') + return (error, cert_bundle, cert_raw, poll_identifier, rejected) + + def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_date: str = uts_to_date_utc(uts_now())) -> Tuple[int, str, str]: + """ revoke certificate """ + self.logger.debug('CAhandler.revoke()') + + code = None + message = None + detail = None + + cert_serial = cert_serial_get(self.logger, certificate_raw, hexformat=True) + + if cert_serial: + revocation_url = f'{self.api_url}certificate/{cert_serial}/revoke' + data_dic = { + 'skip_approval': True + } + code, detail = self._api_put(revocation_url, data_dic) + else: + code = 500 + detail = 'Failed to parse certificate serial' + + self.logger.debug('Certificate.revoke() ended') + return (code, message, detail) + + def trigger(self, _payload: str) -> Tuple[str, str, str]: + """ process trigger message and return certificate """ + self.logger.debug('CAhandler.trigger()') + + error = 'Method not implemented.' + cert_bundle = None + cert_raw = None + + self.logger.debug('CAhandler.trigger() ended with error: %s', error) + return (error, cert_bundle, cert_raw) diff --git a/test/test_digicert.py b/test/test_digicert.py new file mode 100644 index 00000000..3b410df3 --- /dev/null +++ b/test/test_digicert.py @@ -0,0 +1,767 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" unittests for openxpki_ca_handler """ +# pylint: disable=C0415, R0904, W0212 +import sys +import os +import unittest +from unittest.mock import patch, Mock, MagicMock +import requests +import base64 +from OpenSSL import crypto + +sys.path.insert(0, '.') +sys.path.insert(1, '..') + +class FakeDBStore(object): + """ face DBStore class needed for mocking """ + # pylint: disable=W0107, R0903 + pass + +class TestACMEHandler(unittest.TestCase): + """ test class for cgi_handler """ + + def setUp(self): + """ setup unittest """ + models_mock = MagicMock() + models_mock.acme_srv.db_handler.DBstore.return_value = FakeDBStore + modules = {'acme_srv.db_handler': models_mock} + patch.dict('sys.modules', modules).start() + import logging + from examples.ca_handler.digicert_ca_handler import CAhandler + logging.basicConfig(level=logging.CRITICAL) + self.logger = logging.getLogger('test_a2c') + self.cahandler = CAhandler(False, self.logger) + self.dir_path = os.path.dirname(os.path.realpath(__file__)) + + def test_001_default(self): + """ default test which always passes """ + self.assertEqual('foo', 'foo') + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_load') + def test_002__enter__(self, mock_cfg): + """ test enter called """ + mock_cfg.return_value = True + self.cahandler.__enter__() + self.assertTrue(mock_cfg.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_load') + def test_003__enter__(self, mock_cfg): + """ test enter api hosts defined """ + mock_cfg.return_value = True + self.cahandler.api_key = 'api_key' + self.cahandler.__enter__() + self.assertFalse(mock_cfg.called) + + def test_003_poll(self): + """ test polling """ + self.assertEqual(('Method not implemented.', None, None, 'poll_identifier', False), self.cahandler.poll('cert_name', 'poll_identifier', 'csr')) + + def test_004_trigger(self): + """ test polling """ + self.assertEqual(('Method not implemented.', None, None), self.cahandler.trigger('payload')) + + @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') + def test_004_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = False + self.assertFalse(self.cahandler._allowed_domainlist_check('csr')) + self.assertFalse(mock_adc.called) + + @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') + def test_005_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = ["test.com"] + mock_adc.return_value = True + self.assertFalse(self.cahandler._allowed_domainlist_check('csr')) + self.assertTrue(mock_adc.called) + + @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') + def test_006_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = ["test.com"] + mock_adc.return_value = False + self.assertEqual('Either CN or SANs are not allowed by configuration', self.cahandler._allowed_domainlist_check('csr')) + self.assertTrue(mock_adc.called) + + @patch.object(requests, 'post') + def test_018__api_post(self, mock_req): + """ test _api_post() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = lambda: {'foo': 'bar'} + mock_req.return_value = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_post('url', 'data')) + + @patch('requests.post') + def test_019__api_post(self, mock_req): + """ test _api_post() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = "aaaa" + mock_req.return_value = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', "'str' object is not callable"), self.cahandler._api_post('url', 'data')) + self.assertIn("ERROR:test_a2c:CAhandler._api_post() returned error during json parsing: 'str' object is not callable", lcm.output) + + @patch('requests.post') + def test_020__api_post(self, mock_req): + """ test _api_post() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.text = None + mock_req.return_value = mockresponse + self.assertEqual(('status_code', None), self.cahandler._api_post('url', 'data')) + + @patch('requests.post') + def test_021__api_post(self, mock_req): + """ test _api_post(= """ + self.cahandler.api_host = 'api_host' + self.cahandler.auth = 'auth' + mock_req.side_effect = Exception('exc_api_post') + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_post'), self.cahandler._api_post('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_post() returned error: exc_api_post', lcm.output) + + @patch.object(requests, 'get') + def test_022__api_get(self, mock_req): + """ test _api_get() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = lambda: {'foo': 'bar'} + mock_req.return_value = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_get('url')) + + @patch('requests.get') + def test_023__api_get(self, mock_req): + """ test _api_get() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = "aaaa" + mock_req.return_value = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', "'str' object is not callable"), self.cahandler._api_get('url')) + self.assertIn("ERROR:test_a2c:CAhandler._api_get() returned error during json parsing: 'str' object is not callable", lcm.output) + + @patch('requests.get') + def test_024__api_get(self, mock_req): + """ test _api_get() """ + self.cahandler.api_host = 'api_host' + self.cahandler.auth = 'auth' + mock_req.side_effect = Exception('exc_api_get') + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_get'), self.cahandler._api_get('url')) + self.assertIn('ERROR:test_a2c:CAhandler._api_get() returned error: exc_api_get', lcm.output) + + @patch.object(requests, 'put') + def test_018__api_put(self, mock_req): + """ test _api_put() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = lambda: {'foo': 'bar'} + mock_req.return_value = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_put('url', 'data')) + + @patch('requests.put') + def test_019__api_put(self, mock_req): + """ test _api_put() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.json = "aaaa" + mock_req.return_value = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', "'str' object is not callable"), self.cahandler._api_put('url', 'data')) + self.assertIn("ERROR:test_a2c:CAhandler._api_put() returned error during json parsing: 'str' object is not callable", lcm.output) + + @patch('requests.put') + def test_020__api_put(self, mock_req): + """ test _api_put() """ + mockresponse = Mock() + mockresponse.status_code = 'status_code' + mockresponse.text = None + mock_req.return_value = mockresponse + self.assertEqual(('status_code', None), self.cahandler._api_put('url', 'data')) + + @patch('requests.put') + def test_021__api_put(self, mock_req): + """ test _api_put() """ + self.cahandler.api_host = 'api_host' + self.cahandler.auth = 'auth' + mock_req.side_effect = Exception('exc_api_put') + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_put'), self.cahandler._api_put('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_put() returned error: exc_api_put', lcm.output) + + def test_023__config_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.assertEqual('api_key parameter in missing in config file', self.cahandler._config_check()) + + def test_024__config_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.cahandler.api_key = 'api_key' + self.assertEqual('organization_name parameter in missing in config file', self.cahandler._config_check()) + + def test_025__config_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.cahandler.api_key = 'api_key' + self.cahandler.organization_name = 'organization_name' + self.assertFalse(self.cahandler._config_check()) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_026_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar'} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_027_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'api_url': 'api_url'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('api_url', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_028_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'api_key': 'api_key'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertEqual('api_key', self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_028_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'signature_hash': 'signature_hash'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('signature_hash', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_029_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'cert_type': 'cert_type'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('cert_type', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_030_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'order_validity': 2}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(2, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_031_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'request_timeout': 20}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(20, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_032_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'organization_name': 'organization_name'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('organization_name', self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_033_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'organization_id': 'organization_id'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('organization_id', self.cahandler.organization_id) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_033_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': '["foo", "bar"]'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertEqual(['foo', 'bar'], self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_034_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': '["foo"]'}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertEqual(['foo'], self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.digicert_ca_handler.load_config') + def test_035_config_load(self, mock_load, mock_eab, mock_hdl): + """ test _config_load() """ + mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': "foo"}} + mock_eab.return_value = True, 'eab' + mock_hdl.return_value = 'hdl' + self.cahandler._config_load() + self.assertTrue(mock_load.called) + self.assertEqual('https://www.digicert.com/services/v2/', self.cahandler.api_url) + self.assertFalse(self.cahandler.api_key) + self.assertEqual('ssl_basic', self.cahandler.cert_type) + self.assertEqual('sha256', self.cahandler.signature_hash) + self.assertEqual(1, self.cahandler.order_validity) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertFalse(self.cahandler.organization_name) + self.assertFalse(self.cahandler.organization_id) + self.assertEqual('failed to parse', self.cahandler.allowed_domainlist) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual('eab', self.cahandler.eab_handler) + self.assertEqual('hdl', self.cahandler.header_info_field) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_036_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.organization_id = 'organization_id' + self.assertEqual(('code', 'content'), self.cahandler._order_send('csr', 'cn')) + self.assertFalse(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_037_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.assertEqual((500, 'organisation_id is missing'), self.cahandler._order_send('csr', 'cn')) + self.assertFalse(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_036_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.eab_profiling = True + self.cahandler.api_key = 'api_key' + self.cahandler.organization_name = 'organization_name' + self.cahandler.organization_id = 'organization_id' + self.assertEqual(('code', 'content'), self.cahandler._order_send('csr', 'cn')) + self.assertTrue(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_037_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.eab_profiling = True + self.cahandler.api_key = 'api_key' + self.cahandler.organization_name = 'organization_name' + self.cahandler.organization_id = None + mock_orgid.return_value = 1 + self.assertEqual(('code', 'content'), self.cahandler._order_send('csr', 'cn')) + self.assertTrue(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_037_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.api_key = 'api_key' + self.cahandler.organization_name = 'organization_name' + self.cahandler.organization_id = None + mock_orgid.return_value = 1 + self.assertEqual(('code', 'content'), self.cahandler._order_send('csr', 'cn')) + self.assertTrue(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_038_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.api_key = 'api_key' + self.cahandler.organization_name = None + self.cahandler.organization_id = None + mock_orgid.return_value = 1 + self.assertEqual((500, 'organisation_id is missing'), self.cahandler._order_send('csr', 'cn')) + self.assertFalse(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') + def test_039_order_send(self, mock_post, mock_orgid): + """ test _order_send() """ + mock_post.return_value = ('code', 'content') + self.cahandler.api_key = None + self.cahandler.organization_name = 'organization_name' + self.cahandler.organization_id = None + mock_orgid.return_value = 1 + self.assertEqual((500, 'organisation_id is missing'), self.cahandler._order_send('csr', 'cn')) + self.assertFalse(mock_orgid.called) + + @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') + @patch('examples.ca_handler.digicert_ca_handler.b64_encode') + def test_038_order_response_parse(self, mock_b64, mock_pem2der): + """ test _order_parse() """ + content_dic = {'id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} + mock_b64.return_value = 'b64' + self.assertEqual(('pem1\npem2\npem3\n', 'b64', 'id'), self.cahandler._order_response_parse(content_dic)) + + @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') + @patch('examples.ca_handler.digicert_ca_handler.b64_encode') + def test_039_order_response_parse(self, mock_b64, mock_pem2der): + """ test _order_parse() """ + content_dic = {'id': 'id', 'cert_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} + mock_b64.return_value = 'b64' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((None, None, None), self.cahandler._order_response_parse(content_dic)) + self.assertIn('ERROR:test_a2c:CAhandler._order_response_parse() failed: no certificate_chain in response', lcm.output) + + @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') + @patch('examples.ca_handler.digicert_ca_handler.b64_encode') + def test_040_order_response_parse(self, mock_b64, mock_pem2der): + """ test _order_parse() """ + content_dic = {'id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'_pem': 'pem2'}, {'pem': 'pem3'}]} + mock_b64.return_value = 'b64' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('pem1\npem3\n', 'b64', 'id'), self.cahandler._order_response_parse(content_dic)) + self.assertIn('ERROR:test_a2c:CAhandler._order_response_parse() failed: no pem in certificate_chain', lcm.output) + + @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') + @patch('examples.ca_handler.digicert_ca_handler.b64_encode') + def test_041_order_response_parse(self, mock_b64, mock_pem2der): + """ test _order_parse() """ + content_dic = {'_id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} + mock_b64.return_value = 'b64' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('pem1\npem2\npem3\n', 'b64', None), self.cahandler._order_response_parse(content_dic)) + self.assertIn('ERROR:test_a2c:CAhandler._order_response_parse() polling_identifier generation failed: no id in response', lcm.output) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') + def test_042_organiation_id_get(self, mock_get): + """ test _organiation_id_get() """ + mock_get.return_value = (500, {'id': 'id'}) + self.cahandler.organization_name = 'organization_name' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._organiation_id_get() + self.assertIn('ERROR:test_a2c:CAhandler._organiation_id_get() failed', lcm.output) + self.assertFalse(self.cahandler.organization_id) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') + def test_043_organiation_id_get(self, mock_get): + """ test _organiation_id_get() """ + mock_get.return_value = (200, {'organizations': [{'name': 'name1', 'id': 'id1'}, {'name': 'name2', 'id': 'id2'}, {'name': 'name3', 'id': 'id3'}]}) + self.cahandler.organization_name = 'name1' + self.assertEqual('id1', self.cahandler._organiation_id_get()) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') + def test_044_organiation_id_get(self, mock_get): + """ test _organiation_id_get() """ + mock_get.return_value = (200, {'organizations': [{'name': 'name1', 'id': 'id1'}, {'name': 'name2', 'id': 'id2'}, {'name': 'name3', 'id': 'id3'}]}) + self.cahandler.organization_name = 'name2' + self.assertEqual('id2', self.cahandler._organiation_id_get()) + + @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') + def test_045_csr_check(self, mock_dlchk, mock_ehichk): + """ test _csr_check() """ + mock_dlchk.return_value = 'mock_dlchk' + mock_ehichk.return_value = 'mock_hichk' + + self.assertEqual('mock_dlchk', self.cahandler._csr_check('csr')) + + @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') + def test_046_csr_check(self, mock_dlchk, mock_ehichk): + """ test _csr_check() """ + mock_dlchk.return_value = False + mock_ehichk.return_value = 'mock_hichk' + self.assertEqual('mock_hichk', self.cahandler._csr_check('csr')) + + @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') + def test_047_csr_check(self, mock_dlchk, mock_ehichk): + """ test _csr_check() """ + mock_dlchk.return_value = False + mock_ehichk.return_value = False + self.assertFalse(self.cahandler._csr_check('csr')) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') + def test_048_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + """ test enroll() """ + mock_cfgchk.return_value = 'mock_cfgchk' + mock_csrchk.return_value = 'mock_csrchk' + mock_cnget.return_value = 'cn' + mock_ordersend.return_value = ('code', 'content') + mock_orderparse.return_value = ('pem', 'b64', 'id') + self.assertEqual(('mock_cfgchk', None, None, None), self.cahandler.enroll('csr')) + self.assertTrue(mock_cfgchk.called) + self.assertFalse(mock_csrchk.called) + self.assertFalse(mock_cnget.called) + self.assertFalse(mock_ordersend.called) + self.assertFalse(mock_orderparse.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') + def test_049_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + """ test enroll() """ + mock_cfgchk.return_value = False + mock_csrchk.return_value = 'mock_csrchk' + mock_cnget.return_value = 'cn' + mock_ordersend.return_value = ('code', 'content') + mock_orderparse.return_value = ('pem', 'b64', 'id') + self.assertEqual(('mock_csrchk', None, None, None), self.cahandler.enroll('csr')) + self.assertTrue(mock_cfgchk.called) + self.assertTrue(mock_csrchk.called) + self.assertFalse(mock_cnget.called) + self.assertFalse(mock_ordersend.called) + self.assertFalse(mock_orderparse.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') + def test_050_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + """ test enroll() """ + mock_cfgchk.return_value = False + mock_csrchk.return_value = False + mock_cnget.return_value = 'cn' + mock_ordersend.return_value = ('code', 'content') + mock_orderparse.return_value = ('pem', 'b64', 'id') + self.assertEqual(('Error during order creation: code - content', None, None, None), self.cahandler.enroll('csr')) + self.assertTrue(mock_cfgchk.called) + self.assertTrue(mock_csrchk.called) + self.assertTrue(mock_cnget.called) + self.assertTrue(mock_ordersend.called) + self.assertFalse(mock_orderparse.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') + def test_051_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + """ test enroll() """ + mock_cfgchk.return_value = False + mock_csrchk.return_value = False + mock_cnget.return_value = 'cn' + mock_ordersend.return_value = ('code', {'errors': [{'code': 'code', 'message': 'content'}]}) + mock_orderparse.return_value = ('pem', 'b64', 'id') + self.assertEqual(("Error during order creation: code - [{'code': 'code', 'message': 'content'}]", None, None, None), self.cahandler.enroll('csr')) + self.assertTrue(mock_cfgchk.called) + self.assertTrue(mock_csrchk.called) + self.assertTrue(mock_cnget.called) + self.assertTrue(mock_ordersend.called) + self.assertFalse(mock_orderparse.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') + def test_052enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + """ test enroll() """ + mock_cfgchk.return_value = False + mock_csrchk.return_value = False + mock_cnget.return_value = 'cn' + mock_ordersend.return_value = (200, 'content') + mock_orderparse.return_value = ('pem', 'b64', 'id') + self.assertEqual((False, 'pem', 'b64', 'id'), self.cahandler.enroll('csr')) + self.assertTrue(mock_cfgchk.called) + self.assertTrue(mock_csrchk.called) + self.assertTrue(mock_cnget.called) + self.assertTrue(mock_ordersend.called) + self.assertTrue(mock_orderparse.called) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_put') + @patch('examples.ca_handler.digicert_ca_handler.cert_serial_get') + def test_053_revoke(self, mock_serial, mock_put): + """ test revoke() """ + mock_serial.return_value = 'serial' + mock_put.return_value = ('code', 'content') + self.assertEqual(('code', None, 'content'), self.cahandler.revoke('cert')) + + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_put') + @patch('examples.ca_handler.digicert_ca_handler.cert_serial_get') + def test_054_revoke(self, mock_serial, mock_put): + """ test revoke() """ + mock_serial.return_value = None + mock_put.return_value = ('code', 'content') + self.assertEqual((500, None, 'Failed to parse certificate serial'), self.cahandler.revoke('cert')) + +if __name__ == '__main__': + + unittest.main() From e13afd83fc013b293a7d3bbb574e03f1800f63f7 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 12 Oct 2024 10:10:57 +0100 Subject: [PATCH 16/54] [fix] revocation --- .github/actions/acme_clients/action.yml | 107 +++++++++++++-------- docs/digicert.md | 2 + examples/ca_handler/digicert_ca_handler.py | 3 + 3 files changed, 70 insertions(+), 42 deletions(-) diff --git a/.github/actions/acme_clients/action.yml b/.github/actions/acme_clients/action.yml index 2b428c24..aad95611 100644 --- a/.github/actions/acme_clients/action.yml +++ b/.github/actions/acme_clients/action.yml @@ -36,6 +36,10 @@ inputs: HOSTNAME_SUFFIX: description: "Hostname suffix" required: true + NAME_SPACE: + description: "Namespace" + required: true + default: "acme" runs: using: "composite" @@ -65,30 +69,32 @@ runs: time: 5s - name: "Test if http://acme-srv/directory is accessible" - run: docker run -i --rm --network acme curlimages/curl -f http://$ACME_SERVER:$HTTP_PORT/directory + run: docker run -i --rm --network $NAME_SPACE curlimages/curl -f http://$ACME_SERVER:$HTTP_PORT/directory shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "Test if https://acme-srv/directory is accessible" - run: docker run -i --rm --network acme curlimages/curl --insecure -f https://$ACME_SERVER:$HTTPS_PORT/directory + run: docker run -i --rm --network $NAME_SPACE curlimages/curl --insecure -f https://$ACME_SERVER:$HTTPS_PORT/directory shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Enroll lego" run: | echo "##### HTTPS - Enroll lego #####" if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run else echo "use RSA" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.acme --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run fi shell: bash env: @@ -97,27 +103,28 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Enroll acme.sh" run: | echo "##### HTTPS - Enroll acme.sh #####" if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --alpn --standalone --debug 1 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --alpn --standalone --debug 1 --output-insecure --insecure ECC="_ecc" else echo "use RSA" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --alpn --standalone --keylength 2048 --debug 1 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --alpn --standalone --keylength 2048 --debug 1 --output-insecure --insecure fi - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/ca.cer + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/ca.cer if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then echo "Multiple CA certs" - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer else echo "Single Root ca" - openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer fi fi shell: bash @@ -128,6 +135,7 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Renew acme.sh" if: ${{ inputs.RENEWAL == 'true' }} @@ -139,15 +147,15 @@ runs: else echo "use RSA" fi - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --renew --server https://$ACME_SERVER:$HTTPS_PORT --force --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --alpn --standalone --debug 1 --output-insecure --insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/ca.cer + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --renew --server https://$ACME_SERVER:$HTTPS_PORT --force --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --alpn --standalone --debug 1 --output-insecure --insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/ca.cer if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then echo "Multiple CA certs" - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer else echo "Single Root ca" - openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer fi fi shell: bash @@ -158,29 +166,32 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Revoke HTTP-01 single domain acme.sh" if: ${{ inputs.REVOCATION == 'true' }} run: | echo "##### HTTPS - Revoke HTTP-01 single domain acme.sh #####" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network acme neilpang/acme.sh:latest --revoke --server https://$ACME_SERVER:$HTTPS_PORT --revoke -d acme-sh$HOSTNAME_SUFFIX.acme --standalone --debug 2 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --revoke --server https://$ACME_SERVER:$HTTPS_PORT --revoke -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --debug 2 --output-insecure --insecure shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Decativate acme.sh #####" run: | echo "##### HTTPS - Decativate acme.sh" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network acme neilpang/acme.sh:latest --deactivate-account --server https://$ACME_SERVER:$HTTPS_PORT --debug 2 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --deactivate-account --server https://$ACME_SERVER:$HTTPS_PORT --debug 2 --output-insecure --insecure shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Enroll acme.sh" run: | @@ -188,19 +199,19 @@ runs: sudo rm -rf acme-sh/* if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server http://$ACME_SERVER:$HTTP_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --standalone --debug 1 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server http://$ACME_SERVER:$HTTP_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --debug 1 --output-insecure --insecure ECC="_ecc" else echo "use RSA" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server http://$ACME_SERVER:$HTTP_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --standalone --keylength 2048 --debug 1 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server http://$ACME_SERVER:$HTTP_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --keylength 2048 --debug 1 --output-insecure --insecure fi - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/ca.cer + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/ca.cer if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer else echo "single root ca" - openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer fi fi shell: bash @@ -211,6 +222,7 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Renew acme.sh" if: ${{ inputs.RENEWAL == 'true' }} @@ -222,14 +234,14 @@ runs: else echo "use RSA" fi - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --renew --server http://$ACME_SERVER:$HTTP_PORT --force --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.acme --standalone --debug 1 --output-insecure --insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/ca.cer + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --renew --server http://$ACME_SERVER:$HTTP_PORT --force --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --debug 1 --output-insecure --insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/ca.cer if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer else echo "single root ca" - openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.acme${ECC}/acme-sh$HOSTNAME_SUFFIX.acme.cer + openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer fi fi shell: bash @@ -240,38 +252,41 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Revoke HTTP-01 single domain acme.sh" if: ${{ inputs.REVOCATION == 'true' }} run: | echo "##### HTTP - Revoke HTTP-01 single domain acme.sh #####" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network acme neilpang/acme.sh:latest --revoke --server http://$ACME_SERVER:$HTTP_PORT --revoke -d acme-sh$HOSTNAME_SUFFIX.acme --standalone --debug 2 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --revoke --server http://$ACME_SERVER:$HTTP_PORT --revoke -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --debug 2 --output-insecure --insecure shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Decativate acme.sh" run: | echo "##### HTTP - Decativate acme.sh #####" - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network acme neilpang/acme.sh:latest --deactivate-account --server http://$ACME_SERVER:$HTTP_PORT --debug 2 --output-insecure --insecure + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --deactivate-account --server http://$ACME_SERVER:$HTTP_PORT --debug 2 --output-insecure --insecure shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Enroll certbot" if: ${{ inputs.USE_CERTBOT == 'true' }} run: | echo "##### HTTPS - Enroll certbot #####" if [ "$USE_RSA" == "false" ]; then - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.acme --cert-name certbot --issuance-timeout 120 + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 else - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' --key-type rsa -d certbot$HOSTNAME_SUFFIX.acme --cert-name certbot --issuance-timeout 120 + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' --key-type rsa -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 fi if [ "$VERIFY_CERT" == "true" ]; then @@ -289,27 +304,29 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Revoke certbot" if: ${{ (inputs.USE_CERTBOT == 'true') && (inputs.REVOCATION == 'true') }} run: | echo "##### HTTPS - Revoke certbot #####" - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot revoke --server https://$ACME_SERVER:$HTTPS_PORT --no-verify-ssl --delete-after-revoke --cert-name certbot + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot revoke --server https://$ACME_SERVER:$HTTPS_PORT --no-verify-ssl --delete-after-revoke --cert-name certbot shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Enroll certbot #####" if: ${{ inputs.USE_CERTBOT == 'true' }} run: | echo "##### HTTPS - Enroll certbot #####" if [ "$USE_RSA" == "false" ]; then - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server http://$ACME_SERVER:$HTTP_PORT --standalone --preferred-challenges http --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.acme --cert-name certbot --issuance-timeout 120 + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server http://$ACME_SERVER:$HTTP_PORT --standalone --preferred-challenges http --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 else - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server http://$ACME_SERVER:$HTTP_PORT --standalone --preferred-challenges http --agree-tos -m 'certbot@example.com' --key-type rsa -d certbot$HOSTNAME_SUFFIX.acme --cert-name certbot --issuance-timeout 120 + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server http://$ACME_SERVER:$HTTP_PORT --standalone --preferred-challenges http --agree-tos -m 'certbot@example.com' --key-type rsa -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 fi if [ "$VERIFY_CERT" == "true" ]; then @@ -327,36 +344,38 @@ runs: HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Revoke certbot" if: ${{ (inputs.USE_CERTBOT == 'true') && (inputs.REVOCATION == 'true') }} run: | echo "##### HTTP - Revoke certbot #####" - docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network acme -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot revoke --server http://$ACME_SERVER:$HTTP_PORT --delete-after-revoke --cert-name certbot + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot revoke --server http://$ACME_SERVER:$HTTP_PORT --delete-after-revoke --cert-name certbot shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Enroll lego" run: | echo "##### HTTPS - Enroll lego #####" if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run else echo "use RSA" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.acme --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run fi if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then - sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.acme.crt + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.$NAME_SPACE.crt else echo "single root ca" - sudo openssl verify -CAfile cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.acme.crt + sudo openssl verify -CAfile cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.$NAME_SPACE.crt fi fi shell: bash @@ -367,18 +386,20 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTPS - Revoke lego" if: ${{ inputs.REVOCATION == 'true' }} run: | echo "##### HTTPS - Revoke lego #####" - # docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme revoke + # docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Enroll lego" run: | @@ -386,17 +407,17 @@ runs: sudo rm -rf lego/* if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme --http run + docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --http run else echo "use RSA" - docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.acme --http run + docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --http run fi if [ "$VERIFY_CERT" == "true" ]; then if [ -f cert-2.pem ]; then - sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.acme.crt + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.$NAME_SPACE.crt else echo "single root ca" - sudo openssl verify -CAfile cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.acme.crt + sudo openssl verify -CAfile cert-1.pem lego/certificates/lego$HOSTNAME_SUFFIX.$NAME_SPACE.crt fi fi shell: bash @@ -407,18 +428,20 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} USE_RSA: ${{ inputs.USE_RSA }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "HTTP - Revoke lego" if: ${{ inputs.REVOCATION == 'true' }} run: | echo "#### HTTP - Revoke lego" - docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network acme goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme revoke + docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} HTTP_PORT: ${{ inputs.HTTP_PORT }} HTTPS_PORT: ${{ inputs.HTTPS_PORT }} HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} - name: "Delete acme-sh, letsencypt and lego folders" run: | diff --git a/docs/digicert.md b/docs/digicert.md index fb7286ee..80e5ee05 100644 --- a/docs/digicert.md +++ b/docs/digicert.md @@ -45,6 +45,8 @@ eab_profiling: Use your favorite acme client for certificate enrollment. A list of clients used in our regression can be found in the [disclaimer section of our README file](../README.md) +*Important:* the DigiCert API expectes a CommonName to be set. Hence, certbot cannot be used for certificate enrollment. + ## Passing a cert_type from client to server The handler makes use of the [header_info_list feature](header_info.md) allowing an acme-client to specify a [certificate type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be used during certificate enrollment. This feature is disabled by default and must be activate in `acme_srv.cfg` as shown below diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py index 39ce08e7..56fc75e7 100644 --- a/examples/ca_handler/digicert_ca_handler.py +++ b/examples/ca_handler/digicert_ca_handler.py @@ -333,6 +333,9 @@ def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_da 'skip_approval': True } code, detail = self._api_put(revocation_url, data_dic) + if code == 204: + # rewrite reponse code to not confuse with success + code = 200 else: code = 500 detail = 'Failed to parse certificate serial' From 62019d06397227405baacb08b2d347fe040f8628 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 12 Oct 2024 16:28:51 +0100 Subject: [PATCH 17/54] [fix] empty cn-handling --- examples/ca_handler/digicert_ca_handler.py | 24 +++++++++++++-- test/test_digicert.py | 34 ++++++++++++++++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py index 56fc75e7..e4041f3e 100644 --- a/examples/ca_handler/digicert_ca_handler.py +++ b/examples/ca_handler/digicert_ca_handler.py @@ -5,7 +5,7 @@ import json import requests # pylint: disable=e0401 -from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load +from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get CONTENT_TYPE = 'application/json' @@ -201,7 +201,6 @@ def _order_send(self, csr: str, csr_cn) -> Tuple[str, str]: 'years': self.order_validity } } - # enroll certificate code, content = self._api_post(order_url, data_dic) else: @@ -273,6 +272,25 @@ def _csr_check(self, csr: str) -> str: self.logger.debug('CAhandler._csr_check() ended with: %s', error) return error + def _csr_cn_lookup(self, csr: str) -> str: + """ lookup CN/ 1st san from CSR """ + self.logger.debug('CAhandler._csr_cn_lookup()') + + csr_cn = csr_cn_get(self.logger, csr) + if not csr_cn: + # lookup first san + san_list = csr_san_get(self.logger, csr) + if len(san_list) > 0: + for san in san_list: + try: + csr_cn = san.split(':')[1] + break + except Exception as err: + self.logger.error('CAhandler._csr_cn_lookup() split failed: %s', err) + + self.logger.debug('CAhandler._csr_cn_lookup() ended with: %s', csr_cn) + return csr_cn + def enroll(self, csr: str) -> Tuple[str, str, str, str]: """ enroll certificate """ self.logger.debug('CAhandler.enroll()') @@ -290,7 +308,7 @@ def enroll(self, csr: str) -> Tuple[str, str, str, str]: error = self._csr_check(csr) if not error: - csr_cn = csr_cn_get(self.logger, csr) + csr_cn = self._csr_cn_lookup(csr) code, content = self._order_send(csr, csr_cn) if code in (200, 201): diff --git a/test/test_digicert.py b/test/test_digicert.py index 3b410df3..147a8dcb 100644 --- a/test/test_digicert.py +++ b/test/test_digicert.py @@ -653,7 +653,7 @@ def test_047_csr_check(self, mock_dlchk, mock_ehichk): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') - @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') def test_048_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): @@ -672,7 +672,7 @@ def test_048_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') - @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') def test_049_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): @@ -691,7 +691,7 @@ def test_049_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') - @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') def test_050_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): @@ -710,7 +710,7 @@ def test_050_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') - @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') def test_051_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): @@ -729,7 +729,7 @@ def test_051_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_response_parse') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._order_send') - @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') def test_052enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): @@ -762,6 +762,30 @@ def test_054_revoke(self, mock_serial, mock_put): mock_put.return_value = ('code', 'content') self.assertEqual((500, None, 'Failed to parse certificate serial'), self.cahandler.revoke('cert')) + @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + def test_055__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = 'cn' + mock_san_get.return_value = ['foo:san1', 'foo:san2'] + self.assertEqual('cn', self.cahandler._csr_cn_lookup('csr')) + + @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = ['foo:san1', 'foo:san2'] + self.assertEqual('san1', self.cahandler._csr_cn_lookup('csr')) + + @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = ['foosan1', 'foo:san2'] + self.assertEqual('san2', self.cahandler._csr_cn_lookup('csr')) + if __name__ == '__main__': unittest.main() From c8b108131727662853430b4e295284175bd3d98d Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 12 Oct 2024 16:34:32 +0100 Subject: [PATCH 18/54] [fix] invalid san-format handling --- examples/ca_handler/digicert_ca_handler.py | 4 +++- test/test_digicert.py | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py index e4041f3e..e9df8e2a 100644 --- a/examples/ca_handler/digicert_ca_handler.py +++ b/examples/ca_handler/digicert_ca_handler.py @@ -280,13 +280,15 @@ def _csr_cn_lookup(self, csr: str) -> str: if not csr_cn: # lookup first san san_list = csr_san_get(self.logger, csr) - if len(san_list) > 0: + if san_list and len(san_list) > 0: for san in san_list: try: csr_cn = san.split(':')[1] break except Exception as err: self.logger.error('CAhandler._csr_cn_lookup() split failed: %s', err) + else: + self.logger.error('CAhandler._csr_cn_lookup() no SANs found in CSR') self.logger.debug('CAhandler._csr_cn_lookup() ended with: %s', csr_cn) return csr_cn diff --git a/test/test_digicert.py b/test/test_digicert.py index 147a8dcb..0cfb672b 100644 --- a/test/test_digicert.py +++ b/test/test_digicert.py @@ -784,7 +784,19 @@ def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): """ test _csr_cn_lookup() """ mock_cnget.return_value = None mock_san_get.return_value = ['foosan1', 'foo:san2'] - self.assertEqual('san2', self.cahandler._csr_cn_lookup('csr')) + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual('san2', self.cahandler._csr_cn_lookup('csr')) + self.assertIn('ERROR:test_a2c:CAhandler._csr_cn_lookup() split failed: list index out of range', lcm.output) + + @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') + @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') + def test_057__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = None + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler._csr_cn_lookup('csr')) + self.assertIn('ERROR:test_a2c:CAhandler._csr_cn_lookup() no SANs found in CSR', lcm.output) if __name__ == '__main__': From fa88c2d45eaa6ec15984fb15cfadf84b12bd37a9 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 13 Oct 2024 07:30:47 +0100 Subject: [PATCH 19/54] [fix] eab-parsing error in load_cfg --- .github/actions/acme_clients/action.yml | 2 +- .../digicert_ca_handler/enroll_eab/action.yml | 179 ++++++++++++++++++ examples/ca_handler/digicert_ca_handler.py | 19 +- 3 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 .github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml diff --git a/.github/actions/acme_clients/action.yml b/.github/actions/acme_clients/action.yml index aad95611..5e7afe0b 100644 --- a/.github/actions/acme_clients/action.yml +++ b/.github/actions/acme_clients/action.yml @@ -322,7 +322,7 @@ runs: - name: "HTTP - Enroll certbot #####" if: ${{ inputs.USE_CERTBOT == 'true' }} run: | - echo "##### HTTPS - Enroll certbot #####" + echo "##### HTTP - Enroll certbot #####" if [ "$USE_RSA" == "false" ]; then docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server http://$ACME_SERVER:$HTTP_PORT --standalone --preferred-challenges http --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 else diff --git a/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml b/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml new file mode 100644 index 00000000..0bcb91ce --- /dev/null +++ b/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml @@ -0,0 +1,179 @@ +name: "enroll_eab" +description: "enroll_eab" + +runs: + using: "composite" + steps: + - name: "Sleep for 5s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 5s + + - name: "EAB - Test http://acme-srv/directory is accessible" + run: docker run -i --rm --network acme.dynamop.de curlimages/curl -f http://acme-srv/directory + shell: bash + + - name: "EAB - Test if https://acme-srv/directory is accessible" + run: docker run -i --rm --network acme.dynamop.de curlimages/curl --insecure -f https://acme-srv/directory + shell: bash + + - name: "EAB - 01 - Enroll acme with a template_name taken from list in kid.json" + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + openssl x509 -in acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer -text -noout + shell: bash + + - name: "EAB - 01 - Enroll lego with a template_name taken from list in kid.json" + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt + sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -text -noout + shell: bash + + - name: "EAB - 02a - Enroll acme with a template_name taken from header_info NOT included in kid.json (to fail)" + id: acmefail01 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=unknown -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + shell: bash + + - name: "EAB - 02a - check result " + if: steps.acmefail01.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail01.outcome }}" + exit 1 + shell: bash + + - name: "EAB - 02b - Enroll acme with a template_name taken from header_info included in kid.json" + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=ssl_securesite_pro -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + shell: bash + + - name: "EAB - 02a - Enroll lego with a template_name taken from header_info NOT included in kid.json (to fail)" + id: legofail01 + continue-on-error: true + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --user-agent cert_type=unknown -d lego.acme.dynamop.de --http run + shell: bash + + - name: "EAB - 02a - check result " + if: steps.legofail01.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail01.outcome }}" + exit 1 + shell: bash + + - name: "EAB - 02b - Enroll lego with a template_name taken from header_info included in kid.json" + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --user-agent cert_type=ssl_securesite_pro -d lego.acme.dynamop.de --http run + sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -ext extendedKeyUsage -noout + sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -issuer --noout + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt + shell: bash + + - name: "EAB - 03 - Enroll acme with a template_name/ca_name taken from kid.json" + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_01 --eab-hmac-key YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + shell: bash + + - name: "EAB - 03 - Enroll lego with a template_name/ca_name taken from kid.json" + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_01 --hmac YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg -d lego.acme.dynamop.de --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt + shell: bash + + - name: "EAB - 04 - Enroll acme with a not allowed fqdn in kid.json (to fail)" + id: acmefail02 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_02 --eab-hmac-key dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme1.dynamop.de --standalone --debug 3 --output-insecure + shell: bash + + - name: "EAB - 04 - check result " + if: steps.acmefail02.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail02.outcome }}" + exit 1 + shell: bash + + - name: "EAB - 04 - Enroll lego with a not allowed fqdn in kid.json (to fail)" + id: legofail02 + continue-on-error: true + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_02 --hmac dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM -d lego.acme1.dynamop.de --http run + shell: bash + + - name: "EAB - 04a - check result " + if: steps.legofail02.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail02.outcome }}" + exit 1 + shell: bash + + - name: "EAB - 05 - Enroll acme with default values from acme.cfg" + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_03 --eab-hmac-key YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + shell: bash + + - name: "EAB - 05 - Enroll lego with default values from acme.cfg" + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_03 --hmac YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr -d lego.acme.dynamop.de --http run + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt + shell: bash + + - name: "EAB - 06 - Enroll acme with not allowed headerinfo-field (should fail)" + id: acmefail03 + continue-on-error: true + run: | + sudo rm -rf acme-sh/* + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_02 --eab-hmac-key dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM --debug 3 + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=ssl_securesite_pro -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure + shell: bash + + - name: "EAB - 06 - check result " + if: steps.acmefail03.outcome != 'failure' + run: | + echo "acmefail outcome is ${{steps.acmefail03.outcome }}" + exit 1 + shell: bash + + - name: "EAB - 06 - Enroll lego with not allowed headerinfo-field (should fail)" + id: legofail03 + continue-on-error: true + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_02 --hmac dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM --user-agent cert_type=ssl_securesite_pro -d lego.acme.dynamop.de --http run + shell: bash + + - name: "EAB - 06 - check result " + if: steps.legofail03.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail03.outcome }}" + exit 1 + shell: bash \ No newline at end of file diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py index e9df8e2a..57e1350b 100644 --- a/examples/ca_handler/digicert_ca_handler.py +++ b/examples/ca_handler/digicert_ca_handler.py @@ -147,16 +147,17 @@ def _config_load(self): """" load config from file """ self.logger.debug('CAhandler._config_load()') - config_dic = dict(load_config(self.logger, 'CAhandler')) + config_dic = load_config(self.logger, 'CAhandler') if 'CAhandler' in config_dic: - self.api_url = config_dic.get('CAhandler', {}).get('api_url', 'https://www.digicert.com/services/v2/') - self.api_key = config_dic.get('CAhandler', {}).get('api_key', None) - self.cert_type = config_dic.get('CAhandler', {}).get('cert_type', 'ssl_basic') - self.signature_hash = config_dic.get('CAhandler', {}).get('signature_hash', 'sha256') - self.order_validity = config_dic.get('CAhandler', {}).get('order_validity', 1) - self.request_timeout = config_dic.get('CAhandler', {}).get('request_timeout', 10) - self.organization_id = config_dic.get('CAhandler', {}).get('organization_id', None) - self.organization_name = config_dic.get('CAhandler', {}).get('organization_name', None) + cfg_dic = dict(config_dic['CAhandler']) + self.api_url = cfg_dic.get('api_url', 'https://www.digicert.com/services/v2/') + self.api_key = cfg_dic.get('api_key', None) + self.cert_type = cfg_dic.get('cert_type', 'ssl_basic') + self.signature_hash = cfg_dic.get('signature_hash', 'sha256') + self.order_validity = cfg_dic.get('order_validity', 1) + self.request_timeout = cfg_dic.get('request_timeout', 10) + self.organization_id = cfg_dic.get('organization_id', None) + self.organization_name = cfg_dic.get('organization_name', None) if 'allowed_domainlist' in config_dic['CAhandler']: try: From 12dedac663e385ad1c5a3350cb16b653893e7d73 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 13 Oct 2024 09:01:55 +0100 Subject: [PATCH 20/54] [wf] digicert --- .../digicert_ca_handler/enroll_eab/action.yml | 89 +----- .../workflows/ca_handler_tests_digicert.yml | 289 ++++++++++++++++++ examples/ca_handler/digicert_ca_handler.py | 2 +- 3 files changed, 294 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/ca_handler_tests_digicert.yml diff --git a/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml b/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml index 0bcb91ce..cc9e027c 100644 --- a/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml +++ b/.github/actions/wf_specific/digicert_ca_handler/enroll_eab/action.yml @@ -17,47 +17,13 @@ runs: run: docker run -i --rm --network acme.dynamop.de curlimages/curl --insecure -f https://acme-srv/directory shell: bash - - name: "EAB - 01 - Enroll acme with a template_name taken from list in kid.json" - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer - openssl x509 -in acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer -text -noout - shell: bash - - name: "EAB - 01 - Enroll lego with a template_name taken from list in kid.json" run: | sudo rm -rf lego/* docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de --http run sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -text -noout - shell: bash - - - name: "EAB - 02a - Enroll acme with a template_name taken from header_info NOT included in kid.json (to fail)" - id: acmefail01 - continue-on-error: true - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=unknown -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - shell: bash - - - name: "EAB - 02a - check result " - if: steps.acmefail01.outcome != 'failure' - run: | - echo "acmefail outcome is ${{steps.acmefail01.outcome }}" - exit 1 - shell: bash - - - name: "EAB - 02b - Enroll acme with a template_name taken from header_info included in kid.json" - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_00 --eab-hmac-key V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=ssl_securesite_pro -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + sudo docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de revoke shell: bash - name: "EAB - 02a - Enroll lego with a template_name taken from header_info NOT included in kid.json (to fail)" @@ -82,15 +48,7 @@ runs: sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -ext extendedKeyUsage -noout sudo openssl x509 -in lego/certificates/lego.acme.dynamop.de.crt -issuer --noout sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt - shell: bash - - - name: "EAB - 03 - Enroll acme with a template_name/ca_name taken from kid.json" - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_01 --eab-hmac-key YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer + sudo docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de revoke shell: bash - name: "EAB - 03 - Enroll lego with a template_name/ca_name taken from kid.json" @@ -98,22 +56,7 @@ runs: sudo rm -rf lego/* docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_01 --hmac YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg -d lego.acme.dynamop.de --http run sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt - shell: bash - - - name: "EAB - 04 - Enroll acme with a not allowed fqdn in kid.json (to fail)" - id: acmefail02 - continue-on-error: true - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_02 --eab-hmac-key dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme1.dynamop.de --standalone --debug 3 --output-insecure - shell: bash - - - name: "EAB - 04 - check result " - if: steps.acmefail02.outcome != 'failure' - run: | - echo "acmefail outcome is ${{steps.acmefail02.outcome }}" - exit 1 + sudo docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de revoke shell: bash - name: "EAB - 04 - Enroll lego with a not allowed fqdn in kid.json (to fail)" @@ -131,36 +74,12 @@ runs: exit 1 shell: bash - - name: "EAB - 05 - Enroll acme with default values from acme.cfg" - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_03 --eab-hmac-key YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh.acme.dynamop.de_ecc/ca.cer - openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh.acme.dynamop.de_ecc/acme-sh.acme.dynamop.de.cer - shell: bash - - name: "EAB - 05 - Enroll lego with default values from acme.cfg" run: | sudo rm -rf lego/* docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_03 --hmac YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr -d lego.acme.dynamop.de --http run sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem lego/certificates/lego.acme.dynamop.de.crt - shell: bash - - - name: "EAB - 06 - Enroll acme with not allowed headerinfo-field (should fail)" - id: acmefail03 - continue-on-error: true - run: | - sudo rm -rf acme-sh/* - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --register-account --server http://acme-srv --accountemail 'acme-sh@example.com' --eab-kid keyid_02 --eab-hmac-key dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM --debug 3 - docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network acme.dynamop.de --name=acme-sh neilpang/acme.sh:latest --issue --server http://acme-srv --useragent cert_type=ssl_securesite_pro -d acme-sh.acme.dynamop.de --standalone --debug 3 --output-insecure - shell: bash - - - name: "EAB - 06 - check result " - if: steps.acmefail03.outcome != 'failure' - run: | - echo "acmefail outcome is ${{steps.acmefail03.outcome }}" - exit 1 + sudo docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network acme.dynamop.de goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.acme.dynamop.de revoke shell: bash - name: "EAB - 06 - Enroll lego with not allowed headerinfo-field (should fail)" diff --git a/.github/workflows/ca_handler_tests_digicert.yml b/.github/workflows/ca_handler_tests_digicert.yml new file mode 100644 index 00000000..8bfed44a --- /dev/null +++ b/.github/workflows/ca_handler_tests_digicert.yml @@ -0,0 +1,289 @@ +name: CA handler Tests - Digicert CertCentral + +on: + push: + branches: [ devel, master, digicert ] + pull_request: + branches: [ devel ] + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 2 * * 6' + +jobs: + digicert_handler_tests: + name: "digicert_handler_tests" + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + fail-fast: false + matrix: + websrv: ['apache2'] + dbhandler: ['wsgi', 'django'] + steps: + - name: "checkout GIT" + uses: actions/checkout@v4 + + - name: "create folders" + run: | + mkdir lego + mkdir acme-sh + mkdir certbot + + - name: "Build container" + uses: ./.github/actions/container_prep + with: + DB_HANDLER: ${{ matrix.dbhandler }} + WEB_SRV: ${{ matrix.websrv }} + NAME_SPACE: acme.dynamop.de + + - name: "Setup a2c with digicert_ca_handler" + run: | + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg examples/Docker/data/acme_srv.cfg + sudo chmod 777 examples/Docker/data/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem examples/Docker/data/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > examples/Docker/data/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/digicert_ca_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> examples/Docker/data/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" examples/Docker/data/acme_srv.cfg + cd examples/Docker/ + docker-compose restart + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "Test enrollment" + uses: ./.github/actions/acme_clients + with: + NAME_SPACE: acme.dynamop.de + USE_CERTBOT: false + + - name: "EAB - Setup a2c with digicert_ca_handler" + run: | + mkdir -p examples/Docker/data + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg examples/Docker/data/acme_srv.cfg + sudo chmod 777 examples/Docker/data/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem examples/Docker/data/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > examples/Docker/data/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/digicert_ca_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> examples/Docker/data/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" examples/Docker/data/acme_srv.cfg + sudo echo "eab_profiling: True" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" examples/Docker/data/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> examples/Docker/data/acme_srv.cfg + sudo echo "eab_handler_file: /var/www/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "key_file: volume/kid_profiles.json" >> examples/Docker/data/acme_srv.cfg + + sudo cp examples/eab_handler/kid_profiles.json examples/Docker/data/kid_profiles.json + sudo chmod 777 examples/eab_handler/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"cert_type\"\: \[\"ssl_basic\", \"ssl_securesite_pro\", \"ssl_securesite_flex\"\]/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"cert_type\"\: \"ssl_securesite_pro\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"unknown_key\": \"unknown_value\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/www.example.org/*.acme.dynamop.de/g" examples/Docker/data/kid_profiles.json + sudo sed -i '18,19d' examples/Docker/data/kid_profiles.json + sudo sed -i '8,9d' examples/Docker/data/kid_profiles.json + cd examples/Docker/ + docker-compose restart + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "EAB - Test enrollment" + uses: ./.github/actions/wf_specific/digicert_ca_handler/enroll_eab + + - name: "Check container configuration" + uses: ./.github/actions/container_check + with: + DB_HANDLER: ${{ matrix.dbhandler }} + WEB_SRV: ${{ matrix.websrv }} + + - name: "[ * ] collecting test logs" + if: ${{ failure() }} + run: | + mkdir -p ${{ github.workspace }}/artifact/upload + sudo cp -rp examples/Docker/data/ ${{ github.workspace }}/artifact/data/ + sudo cp -rp acme-sh/ ${{ github.workspace }}/artifact/acme-sh/ + sudo cp -rp certbot/ ${{ github.workspace }}/artifact/certbot/ + sudo cp -rp lego/ ${{ github.workspace }}/artifact/lego/ + cd examples/Docker + docker-compose logs > ${{ github.workspace }}/artifact/docker-compose.log + sudo tar -C ${{ github.workspace }}/artifact/ -cvzf ${{ github.workspace }}/artifact/upload/artifact.tar.gz docker-compose.log data acme-sh certbot lego + + - name: "[ * ] uploading artificates" + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: digicert-${{ matrix.websrv }}-${{ matrix.dbhandler }}.tar.gz + path: ${{ github.workspace }}/artifact/upload/ + + digicert_ca_handler_tests_rpm: + name: "digicert_ca_handler_tests_rpm" + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + fail-fast: false + matrix: + rhversion: [8] + execscript: ['rpm_tester.sh', 'django_tester.sh'] + + steps: + - name: "checkout GIT" + uses: actions/checkout@v4 + + - name: "Prepare Alma environment" + uses: ./.github/actions/rpm_prep + with: + GH_SBOM_USER: ${{ secrets.GH_SBOM_USER }} + GH_SBOM_TOKEN: ${{ secrets.GH_SBOM_TOKEN }} + RH_VERSION: ${{ matrix.rhversion }} + NAME_SPACE: acme.dynamop.de + + - name: "Setup a2c with digicert_ca_handler" + if: matrix.execscript == 'rpm_tester.sh' + run: | + sudo mkdir -p data/acme_ca/certs + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg data/acme_srv.cfg + sudo chmod 777 data/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem data/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > data/acme_srv.cfg + sudo echo "handler_file: /opt/acme2certifier/examples/ca_handler/digicert_ca_handler.py" >> data/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> data/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" data/acme_srv.cfg + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "Setup a2c with digicert_ca_handler for django" + if: matrix.execscript == 'django_tester.sh' + run: | + sudo mkdir -p data/volume/acme_ca/certs + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg data/volume/acme_srv.cfg + sudo chmod 777 data/volume/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem data/volume/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > data/volume/acme_srv.cfg + sudo echo "handler_file: /opt/acme2certifier/examples/ca_handler/digicert_ca_handler.py" >> data/volume/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> data/volume/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> data/volume/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> data/volume/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" data/volume/acme_srv.cfg + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "Execute install scipt" + run: | + docker exec acme-srv sh /tmp/acme2certifier/$EXEC_SCRIPT + env: + EXEC_SCRIPT: ${{ matrix.execscript }} + + - name: "Test enrollment" + uses: ./.github/actions/acme_clients + with: + NAME_SPACE: acme.dynamop.de + USE_CERTBOT: false + + - name: "EAB - Setup a2c with digicert_ca_handler" + if: matrix.execscript == 'rpm_tester.sh' + run: | + sudo mkdir -p data/acme_ca/certs + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg data/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem data/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > data/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/digicert_ca_handler.py" >> data/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> data/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" data/acme_srv.cfg + sudo echo "eab_profiling: True" >> data/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" data/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> data/acme_srv.cfg + sudo echo "eab_handler_file: /opt/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> data/acme_srv.cfg + sudo echo "key_file: /opt/acme2certifier/volume/acme_ca/kid_profiles.json" >> data/acme_srv.cfg + + sudo cp examples/eab_handler/kid_profiles.json data/acme_ca/kid_profiles.json + sudo chmod 777 data/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"cert_type\"\: \[\"ssl_basic\", \"ssl_securesite_pro\", \"ssl_securesite_flex\"\]/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"cert_type\"\: \"ssl_securesite_pro\"/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\"/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"unknown_key\": \"unknown_value\"/g" data/acme_ca/kid_profiles.json + sudo sed -i "s/www.example.org/*.acme.dynamop.de/g" data/acme_ca/kid_profiles.json + sudo sed -i '18,19d' data/acme_ca/kid_profiles.json + sudo sed -i '8,9d' data/acme_ca/kid_profiles.json + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "EAB - Setup a2c with digicert_ca_handler" + if: matrix.execscript == 'django_tester.sh' + run: | + sudo mkdir -p data/volume/acme_ca/certs + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg data/volume/acme_srv.cfg + sudo chmod 777 data/volume/acme_srv.cfg + sudo cp test/ca/certsrv_ca_certs.pem data/ca_certs.pem + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > data/volume/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/digicert_ca_handler.py" >> data/volume/acme_srv.cfg + sudo echo "api_key: $DIGICERT_API_KEY" >> data/volume/acme_srv.cfg + sudo echo "organization_name: $DIGICERT_ORGNAME" >> data/volume/acme_srv.cfg + sudo echo "allowed_domainlist: [\"$DIGICERT_DOMAIN\", \"bar.local$\"]" >> data/volume/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" data/volume/acme_srv.cfg + sudo echo "eab_profiling: True" >> data/volume/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" data/volume/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> data/volume/acme_srv.cfg + sudo echo "eab_handler_file: /opt/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> data/volume/acme_srv.cfg + sudo echo "key_file: /opt/acme2certifier/volume/acme_ca/kid_profiles.json" >> data/volume/acme_srv.cfg + + sudo cp examples/eab_handler/kid_profiles.json data/volume/acme_ca/kid_profiles.json + sudo chmod 777 777 data/volume/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"cert_type\"\: \[\"ssl_basic\", \"ssl_securesite_pro\", \"ssl_securesite_flex\"\]/g" data/volume/acme_ca/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"cert_type\"\: \"ssl_securesite_pro\"/g" data/volume/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\"/g" data/volume/acme_ca/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"unknown_key\": \"unknown_value\"/g" data/volume/acme_ca/kid_profiles.json + sudo sed -i "s/www.example.org/*.acme.dynamop.de/g" data/volume/acme_ca/kid_profiles.json + sudo sed -i '18,19d' data/volume/acme_ca/kid_profiles.json + sudo sed -i '8,9d' data/volume/acme_ca/kid_profiles.json + env: + DIGICERT_API_KEY: ${{ secrets.DIGICERT_API_KEY }} + DIGICERT_ORGNAME: ${{ secrets.DIGICERT_ORGNAME }} + DIGICERT_DOMAIN: ${{ secrets.DIGICERT_DOMAIN }} + + - name: "Reconfigure a2c" + run: | + docker exec acme-srv sh /tmp/acme2certifier/$EXEC_SCRIPT restart + env: + EXEC_SCRIPT: ${{ matrix.execscript }} + + - name: "EAB - Test enrollment" + uses: ./.github/actions/wf_specific/digicert_ca_handler/enroll_eab + + - name: "[ * ] collecting test logs" + if: ${{ failure() }} + run: | + mkdir -p ${{ github.workspace }}/artifact/upload + docker exec acme-srv tar cvfz /tmp/acme2certifier/a2c.tgz /opt/acme2certifier + sudo cp -rp data/ ${{ github.workspace }}/artifact/data/ + sudo rm ${{ github.workspace }}/artifact/data/*.rpm + sudo cp -rp acme-sh/ ${{ github.workspace }}/artifact/acme-sh/ + sudo cp -rp certbot/ ${{ github.workspace }}/artifact/certbot/ + sudo cp -rp lego/ ${{ github.workspace }}/artifact/lego/ + docker exec acme-srv cat /etc/nginx/nginx.conf.orig > ${{ github.workspace }}/artifact/data/nginx.conf.orig + docker exec acme-srv cat /etc/nginx/nginx.conf > ${{ github.workspace }}/artifact/data/nginx.conf + docker exec acme-srv cat /var/log/messages > ${{ github.workspace }}/artifact/acme-srv.log + sudo tar -C ${{ github.workspace }}/artifact/ -cvzf ${{ github.workspace }}/artifact/upload/artifact.tar.gz data acme-srv.log acme-sh certbot lego + + - name: "[ * ] uploading artificates" + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: digicert_ca_handler_tests_rpm-rh${{ matrix.rhversion }}-${{ matrix.execscript }}.tar.gz + path: ${{ github.workspace }}/artifact/upload/ diff --git a/examples/ca_handler/digicert_ca_handler.py b/examples/ca_handler/digicert_ca_handler.py index 57e1350b..3af0cc9b 100644 --- a/examples/ca_handler/digicert_ca_handler.py +++ b/examples/ca_handler/digicert_ca_handler.py @@ -319,7 +319,7 @@ def enroll(self, csr: str) -> Tuple[str, str, str, str]: cert_bundle, cert_raw, poll_indentifier = self._order_response_parse(content) else: if 'errors' in content: - error = f'Error during order creation: {code} - {content['errors']}' + error = f"Error during order creation: {code} - {content['errors']}" else: error = f'Error during order creation: {code} - {content}' From 9d116485fb58024794c9c946e3f6aab005243264 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 13 Oct 2024 09:02:37 +0100 Subject: [PATCH 21/54] [wf] digicert workflow --- .github/workflows/ca_handler_tests_digicert.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ca_handler_tests_digicert.yml b/.github/workflows/ca_handler_tests_digicert.yml index 8bfed44a..479d7784 100644 --- a/.github/workflows/ca_handler_tests_digicert.yml +++ b/.github/workflows/ca_handler_tests_digicert.yml @@ -2,7 +2,7 @@ name: CA handler Tests - Digicert CertCentral on: push: - branches: [ devel, master, digicert ] + branches: [ 'devel', 'master', 'digicert' ] pull_request: branches: [ devel ] schedule: From dedc2391bbcc34f30f5d9ea503a9dc1500a6efcd Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 13 Oct 2024 10:11:07 +0100 Subject: [PATCH 22/54] [wf] fix in digicert workflow --- .github/workflows/ca_handler_tests_digicert.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ca_handler_tests_digicert.yml b/.github/workflows/ca_handler_tests_digicert.yml index 479d7784..73d6f892 100644 --- a/.github/workflows/ca_handler_tests_digicert.yml +++ b/.github/workflows/ca_handler_tests_digicert.yml @@ -2,7 +2,7 @@ name: CA handler Tests - Digicert CertCentral on: push: - branches: [ 'devel', 'master', 'digicert' ] + branches: [ 'devel', 'master', 'digicert', 'digicert_wf'] pull_request: branches: [ devel ] schedule: @@ -244,7 +244,7 @@ jobs: sudo echo "key_file: /opt/acme2certifier/volume/acme_ca/kid_profiles.json" >> data/volume/acme_srv.cfg sudo cp examples/eab_handler/kid_profiles.json data/volume/acme_ca/kid_profiles.json - sudo chmod 777 777 data/volume/acme_ca/kid_profiles.json + sudo chmod 777 data/volume/acme_ca/kid_profiles.json sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"cert_type\"\: \[\"ssl_basic\", \"ssl_securesite_pro\", \"ssl_securesite_flex\"\]/g" data/volume/acme_ca/kid_profiles.json sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"cert_type\"\: \"ssl_securesite_pro\"/g" data/volume/acme_ca/kid_profiles.json sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\"/g" data/volume/acme_ca/kid_profiles.json From ed61e38775659b32f3fe2c40657b59e2fe705049 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 15:05:04 +0200 Subject: [PATCH 23/54] [fix] impacket in codescanner workflows --- .github/workflows/codescanner.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codescanner.yml b/.github/workflows/codescanner.yml index 5d49a3e0..91e9748a 100644 --- a/.github/workflows/codescanner.yml +++ b/.github/workflows/codescanner.yml @@ -61,7 +61,7 @@ jobs: python -m pip install --upgrade pip pip install lxml beautifulsoup4 html5lib pip install pytest - pip install pytest-cov + pip install pytest-cov impacket if [ -f requirements.txt ]; then pip install -r requirements.txt; fi pytest --cov=./ --cov-report=xml @@ -94,7 +94,7 @@ jobs: - name: Install pytest coverage and any other packages run: | python -m pip install --upgrade pip - pip install pytest coverage + pip install pytest coverage impacket if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: run coverage From ab17d3585235d0ba7c53528f856bff0bc02951fb Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 15:28:51 +0200 Subject: [PATCH 24/54] [doc] remove link for digicert.md --- docs/digicert.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/digicert.md b/docs/digicert.md index 80e5ee05..e3bf9687 100644 --- a/docs/digicert.md +++ b/docs/digicert.md @@ -35,7 +35,7 @@ eab_profiling: - api_key - required - API key to access the API - organization_name - required - Organization name as specified in DigiCert CertCentral - allowed_domainlist: list of domain-names allowed for enrollment in json format (example: ["bar.local$, bar.foo.local]) -- api_url - optional - DigiCert CertCentral (default: https://www.digicert.com/services/v2/) +- api_url - optional - URL of the CertCentral API - organization_id - optional - organization id - configuration prevents additional rest-lookups - cert_type - optional - [certificte type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be isused. (default: ssl_basic) - signature_hash - optional - hash algorithm used for certificate signing - (default: sha256) From c0decc26237f46c108052a34c0d83eda8b2cedf6 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 15:31:10 +0200 Subject: [PATCH 25/54] [doc] documentation update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 917ef548..4bb68508 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ should be straight forward. As of today the following handlers are available: | [Insta ActiveCMS](docs/asa.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [Microsoft Certificate Enrollment Web Services](docs/mscertsrv.md) | :white_check_mark: | :x: | :white_check_mark: | | [Microsoft Windows Client Certificate Enrollment Protocol (MS-WCCE) via RPC/DCOM](docs/mswcce.md) | :white_check_mark: | :x: | :white_check_mark: | -| [NetGuard Certificate Lifecycle Manager](docs/nclm.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [NetGuard Certificate Lifecycle Manager](docs/nclm.md) | :white_check_mark: | :white_check_mark: | :x: | | [NetGuard Certificate Manager/Insta Certifier](docs/certifier.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [Openssl](docs/openssl.md) | :white_check_mark: | :white_check_mark: | :x: | | [OpenXPKI](docs/openxpki.md) | :white_check_mark: | :white_check_mark: | :x: | From 14914da50b14c4faa6bbbb0f93812e5c53ea04e1 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 15:58:33 +0200 Subject: [PATCH 26/54] [fix] address codesmells --- acme_srv/helper.py | 2 +- examples/Docker/almalinux-systemd/Dockerfile | 2 +- examples/Docker/apache2/django/Dockerfile | 23 ++++++++++----- examples/Docker/apache2/wsgi/Dockerfile | 21 +++++++++---- examples/Docker/nginx/django/Dockerfile | 31 +++++++++++++------- examples/Docker/nginx/wsgi/Dockerfile | 28 +++++++++++++----- examples/Docker/soap-srv/Dockerfile | 11 +++---- 7 files changed, 79 insertions(+), 39 deletions(-) diff --git a/acme_srv/helper.py b/acme_srv/helper.py index a1ad5a33..b6ee66b5 100644 --- a/acme_srv/helper.py +++ b/acme_srv/helper.py @@ -651,7 +651,7 @@ def csr_extensions_get(logger: logging.Logger, csr: str) -> List[str]: return extension_list -def csr_subject_get(logger: logging.Logger, csr: str) -> str: +def csr_subject_get(logger: logging.Logger, csr: str) -> Dict[str, str]: """ get subject from csr as a list of tuples """ logger.debug('Helper.csr_subject_get()') # pylint: disable=w0212 diff --git a/examples/Docker/almalinux-systemd/Dockerfile b/examples/Docker/almalinux-systemd/Dockerfile index 9cdd452b..cfafc2ef 100644 --- a/examples/Docker/almalinux-systemd/Dockerfile +++ b/examples/Docker/almalinux-systemd/Dockerfile @@ -11,5 +11,5 @@ RUN rm -rf /lib/systemd/system/multi-user.target.wants/ \ && rm -rf /lib/systemd/system/basic.target.wants/ \ && rm -f /lib/systemd/system/anaconda.target.wants/* -VOLUME [ “/sys/fs/cgroup” ] +VOLUME [ "/sys/fs/cgroup" ] CMD ["/usr/sbin/init"] diff --git a/examples/Docker/apache2/django/Dockerfile b/examples/Docker/apache2/django/Dockerfile index 11497884..b5137cd1 100644 --- a/examples/Docker/apache2/django/Dockerfile +++ b/examples/Docker/apache2/django/Dockerfile @@ -8,19 +8,19 @@ ENV APACHE_LOG_DIR /var/log/apache2 RUN apt-get update && \ DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends -y tzdata && \ DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ - python3-pip \ apache2 \ apache2-data \ - libapache2-mod-wsgi-py3 \ curl \ krb5-user \ + libapache2-mod-wsgi-py3 \ libgssapi-krb5-2 \ libkrb5-3 \ - python3-gssapi \ python3-django \ + python3-gssapi \ python3-mysqldb \ - python3-pymysql \ + python3-pip \ python3-psycopg2 \ + python3-pymysql \ python3-yaml \ && rm -rf /var/lib/apt/lists/* &&\ mkdir -p /var/www/acme2certifier/volume && \ @@ -29,14 +29,23 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ +RUN pip3 install impacket --break-system-packages \\ + && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ cp /var/www/acme2certifier/examples/apache2/apache_django.conf /etc/apache2/sites-enabled/acme2certifier.conf && \ cp -R /var/www/acme2certifier/examples/django/* /var/www/acme2certifier/ && \ cp /var/www/acme2certifier/examples/db_handler/django_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ - rm /var/www/acme2certifier/CHANGES.md /var/www/acme2certifier/README.md /var/www/acme2certifier/SECURITY.md /var/www/acme2certifier/setup.py /var/www/acme2certifier/requirements.txt && \ + rm /var/www/acme2certifier/CHANGES.md && \ + rm /var/www/acme2certifier/README.md && \ + rm /var/www/acme2certifier/SECURITY.md && \ + rm /var/www/acme2certifier/setup.py && \ + rm /var/www/acme2certifier/requirements.txt && \ cp /var/www/acme2certifier/examples/Docker/apache2/django/docker-entrypoint.sh /docker-entrypoint.sh && \ - rm -rf /var/www/acme2certifier/examples/Docker /var/www/acme2certifier/examples/db_handler /var/www/acme2certifier/examples/nginx /var/www/acme2certifier/examples/acme_srv.db.example /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ + rm -rf /var/www/acme2certifier/examples/Docker && \ + rm -rf /var/www/acme2certifier/examples/db_handler &&\ + rm -rf /var/www/acme2certifier/examples/nginx && \ + rm -rf /var/www/acme2certifier/examples/acme_srv.db.example && \ + rm -rf /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ rm /var/www/acme2certifier/acme2certifier/settings.py && \ chown -R www-data:www-data /var/www/acme2certifier/ && \ sed -i "s/default = default_sect/\default = default_sect\nlegacy = legacy_sect/g" /etc/ssl/openssl.cnf && \ diff --git a/examples/Docker/apache2/wsgi/Dockerfile b/examples/Docker/apache2/wsgi/Dockerfile index 1f80d433..fa6eed5e 100644 --- a/examples/Docker/apache2/wsgi/Dockerfile +++ b/examples/Docker/apache2/wsgi/Dockerfile @@ -8,15 +8,15 @@ ENV APACHE_LOG_DIR /var/log/apache2 RUN apt-get update && \ DEBIAN_FRONTEND="noninteractive" apt-get -y install --no-install-recommends tzdata && \ DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ - python3-pip \ apache2 \ apache2-data \ - libapache2-mod-wsgi-py3 \ curl \ krb5-user \ + libapache2-mod-wsgi-py3 \ libgssapi-krb5-2 \ libkrb5-3 \ python3-gssapi \ + python3-pip \ && rm -rf /var/lib/apt/lists/* &&\ mkdir -p /var/www/acme2certifier/volume && \ mkdir -p /var/www/acme2certifier/examples /var/www/acme2certifier/examples/ @@ -24,14 +24,25 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ +RUN pip3 install impacket --break-system-packages && \ + rm /usr/local/bin/*.py && \ + rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ cp /var/www/acme2certifier/examples/apache2/apache_wsgi.conf /etc/apache2/sites-enabled/acme2certifier.conf && \ cp /var/www/acme2certifier/examples/acme2certifier_wsgi.py /var/www/acme2certifier/acme2certifier_wsgi.py && \ cp /var/www/acme2certifier/examples/db_handler/wsgi_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ - rm /var/www/acme2certifier/CHANGES.md /var/www/acme2certifier/README.md /var/www/acme2certifier/SECURITY.md /var/www/acme2certifier/setup.py /var/www/acme2certifier/requirements.txt && \ + rm /var/www/acme2certifier/CHANGES.md && \ + rm /var/www/acme2certifier/README.md && \ + rm /var/www/acme2certifier/SECURITY.md && \ + rm /var/www/acme2certifier/setup.py && \ + rm /var/www/acme2certifier/requirements.txt && \ cp /var/www/acme2certifier/examples/Docker/apache2/wsgi/docker-entrypoint.sh /docker-entrypoint.sh && \ - rm -rf /var/www/acme2certifier/examples/Docker /var/www/acme2certifier/examples/django /var/www/acme2certifier/examples/db_handler /var/www/acme2certifier/examples/nginx /var/www/acme2certifier/examples/acme_srv.db.example /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ + rm -rf /var/www/acme2certifier/examples/Docker && \ + rm -rf /var/www/acme2certifier/examples/django && \ + rm -rf /var/www/acme2certifier/examples/db_handler && \ + rm -rf /var/www/acme2certifier/examples/nginx && \ + rm -rf /var/www/acme2certifier/examples/acme_srv.db.example && \ + rm -rf /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ chown -R www-data:www-data /var/www/acme2certifier/ && \ sed -i "s/default = default_sect/\default = default_sect\nlegacy = legacy_sect/g" /etc/ssl/openssl.cnf && \ sed -i "s/\[default_sect\]/\[default_sect\]\nactivate = 1\n\[legacy_sect\]\nactivate = 1/g" /etc/ssl/openssl.cnf && \ diff --git a/examples/Docker/nginx/django/Dockerfile b/examples/Docker/nginx/django/Dockerfile index d979808d..27f74e58 100644 --- a/examples/Docker/nginx/django/Dockerfile +++ b/examples/Docker/nginx/django/Dockerfile @@ -4,20 +4,20 @@ LABEL maintainer="grindelsack@gmail.com" RUN apt-get update && \ DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends tzdata && \ DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ - python3-pip \ - nginx \ - uwsgi \ - uwsgi-plugin-python3 \ curl \ krb5-user \ libgssapi-krb5-2 \ libkrb5-3 \ - python3-gssapi \ + nginx \ python3-django \ + python3-gssapi \ python3-mysqldb \ - python3-pymysql \ + python3-pip \ python3-psycopg2 \ + python3-pymysql \ python3-yaml \ + uwsgi \ + uwsgi-plugin-python3 \ && rm -rf /var/lib/apt/lists/* &&\ mkdir -p /var/www/acme2certifier/volume && \ mkdir -p /var/www/acme2certifier/examples /var/www/acme2certifier/examples/ && \ @@ -25,8 +25,11 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ -RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ - pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && \ + rm /usr/local/bin/*.py && \ + rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ + pip3 install supervisor --break-system-packages && \ cp -R /var/www/acme2certifier/examples/django/* /var/www/acme2certifier/ && \ cp /var/www/acme2certifier/examples/db_handler/django_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ cp /var/www/acme2certifier/examples/nginx/acme2certifier.ini /var/www/acme2certifier && \ @@ -40,8 +43,16 @@ RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && r sed -i "s/default = default_sect/\default = default_sect\nlegacy = legacy_sect/g" /etc/ssl/openssl.cnf && \ sed -i "s/\[default_sect\]/\[default_sect\]\nactivate = 1\n\[legacy_sect\]\nactivate = 1/g" /etc/ssl/openssl.cnf && \ rm /etc/nginx/sites-enabled/default && \ - rm /var/www/acme2certifier/CHANGES.md /var/www/acme2certifier/README.md /var/www/acme2certifier/SECURITY.md /var/www/acme2certifier/setup.py /var/www/acme2certifier/requirements.txt && \ - rm -rf /var/www/acme2certifier/examples/Docker /var/www/acme2certifier/examples/db_handler /var/www/acme2certifier/examples/apache2 /var/www/acme2certifier/examples/acme_srv.db.example /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ + rm /var/www/acme2certifier/CHANGES.md && \ + rm /var/www/acme2certifier/README.md && \ + rm /var/www/acme2certifier/SECURITY.md && \ + rm /var/www/acme2certifier/setup.py && \ + rm /var/www/acme2certifier/requirements.txt && \ + rm -rf /var/www/acme2certifier/examples/Docker && \ + rm -rf /var/www/acme2certifier/examples/db_handler && \ + rm -rf /var/www/acme2certifier/examples/apache2 && \ + rm -rf /var/www/acme2certifier/examples/acme_srv.db.example && \ + rm -rf /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ rm /var/www/acme2certifier/acme2certifier/settings.py && \ chmod a+rx /docker-entrypoint.sh diff --git a/examples/Docker/nginx/wsgi/Dockerfile b/examples/Docker/nginx/wsgi/Dockerfile index f3c346a9..052c65ae 100644 --- a/examples/Docker/nginx/wsgi/Dockerfile +++ b/examples/Docker/nginx/wsgi/Dockerfile @@ -3,15 +3,15 @@ LABEL maintainer="grindelsack@gmail.com" RUN apt-get update && \ DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends tzdata && \ DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ - python3-pip \ - nginx \ - uwsgi \ - uwsgi-plugin-python3 \ curl \ krb5-user \ libgssapi-krb5-2 \ libkrb5-3 \ + nginx \ python3-gssapi \ + python3-pip \ + uwsgi \ + uwsgi-plugin-python3 \ && rm -rf /var/lib/apt/lists/* &&\ mkdir -p /var/www/acme2certifier/volume && \ mkdir -p /var/www/acme2certifier/examples /var/www/acme2certifier/examples/ && \ @@ -20,8 +20,11 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ - pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && pip3 install supervisor --break-system-packages && \ +RUN pip3 install impacket --break-system-packages && \ + rm /usr/local/bin/*.py && \ + rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ + pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ + pip3 install supervisor --break-system-packages && \ cp /var/www/acme2certifier/examples/acme2certifier_wsgi.py /var/www/acme2certifier/acme2certifier_wsgi.py && \ cp /var/www/acme2certifier/examples/db_handler/wsgi_handler.py /var/www/acme2certifier/acme_srv/db_handler.py && \ cp /var/www/acme2certifier/examples/nginx/acme2certifier.ini /var/www/acme2certifier && \ @@ -35,8 +38,17 @@ RUN pip3 install impacket --break-system-packages && rm /usr/local/bin/*.py && r sed -i "s/default = default_sect/\default = default_sect\nlegacy = legacy_sect/g" /etc/ssl/openssl.cnf && \ sed -i "s/\[default_sect\]/\[default_sect\]\nactivate = 1\n\[legacy_sect\]\nactivate = 1/g" /etc/ssl/openssl.cnf && \ rm /etc/nginx/sites-enabled/default && \ - rm /var/www/acme2certifier/CHANGES.md /var/www/acme2certifier/README.md /var/www/acme2certifier/SECURITY.md /var/www/acme2certifier/setup.py /var/www/acme2certifier/requirements.txt && \ - rm -rf /var/www/acme2certifier/examples/Docker /var/www/acme2certifier/examples/django /var/www/acme2certifier/examples/db_handler /var/www/acme2certifier/examples/apache2 /var/www/acme2certifier/examples/acme_srv.db.example /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ + rm /var/www/acme2certifier/CHANGES.md && \ + rm /var/www/acme2certifier/README.md && \ + rm /var/www/acme2certifier/SECURITY.md && \ + rm /var/www/acme2certifier/setup.py && \ + rm /var/www/acme2certifier/requirements.txt && \ + rm -rf /var/www/acme2certifier/examples/Docker && \ + rm -rf /var/www/acme2certifier/examples/django && \ + rm -rf /var/www/acme2certifier/examples/db_handler && \ + rm -rf /var/www/acme2certifier/examples/apache2 && \ + rm -rf /var/www/acme2certifier/examples/acme_srv.db.example && \ + rm -rf /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ chmod a+rx /docker-entrypoint.sh WORKDIR /var/www/acme2certifier diff --git a/examples/Docker/soap-srv/Dockerfile b/examples/Docker/soap-srv/Dockerfile index 3bb53542..0b4dce1e 100644 --- a/examples/Docker/soap-srv/Dockerfile +++ b/examples/Docker/soap-srv/Dockerfile @@ -4,25 +4,22 @@ LABEL maintainer="grindelsack@gmail.com" RUN apt-get update && \ DEBIAN_FRONTEND="noninteractive" apt-get -y install --no-install-recommends tzdata && \ DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ - python3-pip \ apache2 \ apache2-data \ - libapache2-mod-wsgi-py3 \ curl \ krb5-user \ + libapache2-mod-wsgi-py3 \ libgssapi-krb5-2 \ libkrb5-3 \ python3-gssapi \ + python3-pip \ && rm -rf /var/lib/apt/lists/* # install python requirements COPY requirements.txt /tmp/requirements.txt -RUN pip3 install -r /tmp/requirements.txt - - -# create folders for acme2certifier -RUN mkdir -p /usr/local/soap-srv/acme_srv && \ +RUN pip3 install -r /tmp/requirements.txt &&\ + mkdir -p /usr/local/soap-srv/acme_srv && \ mkdir -p /usr/local/soap-srv/examples/ca_handler COPY examples/soap/mock_soap_srv.py /usr/local/soap-srv/ From 8f2373aef54db803570a71de8235a27f06bcd226 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 16:07:09 +0200 Subject: [PATCH 27/54] [fix] ap django Dockerfile --- examples/Docker/apache2/django/Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/Docker/apache2/django/Dockerfile b/examples/Docker/apache2/django/Dockerfile index b5137cd1..4159c0d2 100644 --- a/examples/Docker/apache2/django/Dockerfile +++ b/examples/Docker/apache2/django/Dockerfile @@ -29,8 +29,9 @@ RUN apt-get update && \ COPY ./ /var/www/acme2certifier/ # configure acme2certifier -RUN pip3 install impacket --break-system-packages \\ - && rm /usr/local/bin/*.py && rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ +RUN pip3 install impacket --break-system-packages && \ + rm /usr/local/bin/*.py && \ + rm -rf /usr/local/lib/python3.12/dist-packages/impacket/examples/* && \ pip3 install -r /var/www/acme2certifier/requirements.txt --break-system-packages && \ cp /var/www/acme2certifier/examples/apache2/apache_django.conf /etc/apache2/sites-enabled/acme2certifier.conf && \ cp -R /var/www/acme2certifier/examples/django/* /var/www/acme2certifier/ && \ @@ -42,7 +43,7 @@ RUN pip3 install impacket --break-system-packages \\ rm /var/www/acme2certifier/requirements.txt && \ cp /var/www/acme2certifier/examples/Docker/apache2/django/docker-entrypoint.sh /docker-entrypoint.sh && \ rm -rf /var/www/acme2certifier/examples/Docker && \ - rm -rf /var/www/acme2certifier/examples/db_handler &&\ + rm -rf /var/www/acme2certifier/examples/db_handler && \ rm -rf /var/www/acme2certifier/examples/nginx && \ rm -rf /var/www/acme2certifier/examples/acme_srv.db.example && \ rm -rf /var/www/acme2certifier/examples/acme2certifier_wsgi.py && \ From 97e247a9f83ccb4b577b32444e252a64f89b7140 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 16:13:51 +0200 Subject: [PATCH 28/54] [fix] revert changes in alma Dockerfile --- examples/Docker/almalinux-systemd/Dockerfile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/Docker/almalinux-systemd/Dockerfile b/examples/Docker/almalinux-systemd/Dockerfile index cfafc2ef..2e35cbb3 100644 --- a/examples/Docker/almalinux-systemd/Dockerfile +++ b/examples/Docker/almalinux-systemd/Dockerfile @@ -3,13 +3,13 @@ ENV container docker RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in ; do [ "$i" == systemd-tmpfiles-setup.service ] || rm -f "$i"; done); -RUN rm -rf /lib/systemd/system/multi-user.target.wants/ \ -&& rm -rf /etc/systemd/system/.wants/ \ -&& rm -rf /lib/systemd/system/local-fs.target.wants/ \ -&& rm -f /lib/systemd/system/sockets.target.wants/udev \ -&& rm -f /lib/systemd/system/sockets.target.wants/initctl \ -&& rm -rf /lib/systemd/system/basic.target.wants/ \ -&& rm -f /lib/systemd/system/anaconda.target.wants/* +RUN rm -rf /lib/systemd/system/multi-user.target.wants/ && \ + rm -rf /etc/systemd/system/.wants/ && \ + rm -rf /lib/systemd/system/local-fs.target.wants/ && \ + rm -f /lib/systemd/system/sockets.target.wants/udev && \ + rm -f /lib/systemd/system/sockets.target.wants/initctl && \ + rm -rf /lib/systemd/system/basic.target.wants/ && \ + rm -f /lib/systemd/system/anaconda.target.wants/* -VOLUME [ "/sys/fs/cgroup" ] +VOLUME ["/sys/fs/cgroup"] CMD ["/usr/sbin/init"] From 4e9376566dea5253cf51c90529d6eeb536a4a711 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 16:20:16 +0200 Subject: [PATCH 29/54] [fix] alma-linux docker file --- examples/Docker/almalinux-systemd/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Docker/almalinux-systemd/Dockerfile b/examples/Docker/almalinux-systemd/Dockerfile index 2e35cbb3..e47cd17d 100644 --- a/examples/Docker/almalinux-systemd/Dockerfile +++ b/examples/Docker/almalinux-systemd/Dockerfile @@ -11,5 +11,5 @@ RUN rm -rf /lib/systemd/system/multi-user.target.wants/ && \ rm -rf /lib/systemd/system/basic.target.wants/ && \ rm -f /lib/systemd/system/anaconda.target.wants/* -VOLUME ["/sys/fs/cgroup"] +VOLUME [ “/sys/fs/cgroup” ] CMD ["/usr/sbin/init"] From e716bd83d9c87a0f880de9474289298a94c21402 Mon Sep 17 00:00:00 2001 From: grindsa Date: Mon, 14 Oct 2024 16:58:11 +0200 Subject: [PATCH 30/54] [fix] alma Dockerfile --- examples/Docker/almalinux-systemd/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Docker/almalinux-systemd/Dockerfile b/examples/Docker/almalinux-systemd/Dockerfile index e47cd17d..d263a374 100644 --- a/examples/Docker/almalinux-systemd/Dockerfile +++ b/examples/Docker/almalinux-systemd/Dockerfile @@ -1,9 +1,9 @@ FROM almalinux:9 ENV container docker -RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in ; do [ "$i" == systemd-tmpfiles-setup.service ] || rm -f "$i"; done); - -RUN rm -rf /lib/systemd/system/multi-user.target.wants/ && \ +WORKDIR /lib/systemd/system/sysinit.target.wants/ +RUN for i in ; do [ "$i" == systemd-tmpfiles-setup.service ] || rm -f "$i"; done && \ + rm -rf /lib/systemd/system/multi-user.target.wants/ && \ rm -rf /etc/systemd/system/.wants/ && \ rm -rf /lib/systemd/system/local-fs.target.wants/ && \ rm -f /lib/systemd/system/sockets.target.wants/udev && \ @@ -11,5 +11,5 @@ RUN rm -rf /lib/systemd/system/multi-user.target.wants/ && \ rm -rf /lib/systemd/system/basic.target.wants/ && \ rm -f /lib/systemd/system/anaconda.target.wants/* -VOLUME [ “/sys/fs/cgroup” ] +# VOLUME [ “/sys/fs/cgroup” ] CMD ["/usr/sbin/init"] From ef5eeaef6440f595497e3fdd679788eef792191e Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 19 Oct 2024 07:08:10 +0200 Subject: [PATCH 31/54] [fix] downloadlink for impacket rpm --- docs/install_rpm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install_rpm.md b/docs/install_rpm.md index b8d33c6f..5443d77d 100644 --- a/docs/install_rpm.md +++ b/docs/install_rpm.md @@ -31,7 +31,7 @@ Backports of these packages being part of RHEL9 can be found in the [the a2c rpm Depending on your ca_handler you may need additional modules: -- [python3-impacket-0.11.0-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-1.el8.noarch.rpm) when using [MS wcce handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mswcce.md) +- [python3-impacket-0.11.0-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-impacket-0.11.0-2grindsa.el8.noarch.rpm) when using [MS wcce handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mswcce.md) - [python3-ntlm-auth-1.5.0-2.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-ntlm-auth-1.5.0-2.el8.noarch.rpm) when using [MS wse handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mscertsrv.md) - [python3-requests_ntlm-1.1.0-14.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-requests_ntlm-1.1.0-14.el8.noarch.rpm) when using [MS wse handler](https://github.com/grindsa/acme2certifier/blob/master/docs/mscertsrv.md) - [python3-requests-pkcs12-1.16-1.el8.noarch.rpm](https://github.com/grindsa/sbom/raw/main/rpm-repo/RPMs/rhel8/python3-requests-pkcs12-1.16-1.el8.noarch.rpm) when using [EST](https://github.com/grindsa/acme2certifier/blob/master/docs/est.md) or [EJBCA](https://github.com/grindsa/acme2certifier/blob/master/docs/ejbca.md) handler From 57fdbc9f90804a5805ff5ae9aa7dc1e7f9927382 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 19 Oct 2024 07:31:16 +0200 Subject: [PATCH 32/54] [feat] emtpy handler --- examples/ca_handler/entrust_ca_handler.py | 223 ++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 examples/ca_handler/entrust_ca_handler.py diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py new file mode 100644 index 00000000..98bac745 --- /dev/null +++ b/examples/ca_handler/entrust_ca_handler.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +""" CA handler using Digicert CertCentralAPI""" +from __future__ import print_function +from typing import Tuple, Dict +import json +import requests +# pylint: disable=e0401 +from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get + + +CONTENT_TYPE = 'application/json' + + +class CAhandler(object): + """ Digicert CertCentralAP handler """ + + def __init__(self, _debug: bool = None, logger: object = None): + self.logger = logger + self.api_url = '' + self.api_key = 'foo' + + self.allowed_domainlist = [] + self.header_info_field = False + self.eab_handler = None + self.eab_profiling = False + + def __enter__(self): + """ Makes CAhandler a Context Manager """ + if not self.api_key: + self._config_load() + return self + + def __exit__(self, *args): + """ cose the connection at the end of the context """ + + def _allowed_domainlist_check(self, csr: str) -> str: + """ check allowed domainlist """ + self.logger.debug('CAhandler._allowed_domainlist_check()') + + error = None + # check CN and SAN against black/whitlist + if self.allowed_domainlist: + # check sans / cn against list of allowed comains from config + result = allowed_domainlist_check(self.logger, csr, self.allowed_domainlist) + if not result: + error = 'Either CN or SANs are not allowed by configuration' + + self.logger.debug('CAhandler._allowed_domainlist_check() ended with %s', error) + return error + + def _api_get(self, url: str) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_get()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.get(url=url, headers=headers, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_get() returned error during json parsing: %s', err_) + content = str(err_) + except Exception as err_: + self.logger.error('CAhandler._api_get() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _api_post(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_post()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.post(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + if api_response.text: + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_post() returned error during json parsing: %s', err_) + content = str(err_) + else: + content = None + except Exception as err_: + self.logger.error('CAhandler._api_post() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _api_put(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]]: + """ post data to API """ + self.logger.debug('CAhandler._api_put()') + headers = { + 'X-DC-DEVKEY': self.api_key, + 'Content-Type': CONTENT_TYPE + } + + try: + api_response = requests.put(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + code = api_response.status_code + if api_response.text: + try: + content = api_response.json() + except Exception as err_: + self.logger.error('CAhandler._api_put() returned error during json parsing: %s', err_) + content = str(err_) + else: + content = None + except Exception as err_: + self.logger.error('CAhandler._api_put() returned error: %s', err_) + code = 500 + content = str(err_) + + return code, content + + def _config_check(self) -> str: + """ check config """ + self.logger.debug('CAhandler._config_check()') + + error = None + for ele in ['api_url', 'api_key', 'organization_name']: + if not getattr(self, ele): + error = f'{ele} parameter in missing in config file' + self.logger.error('CAhandler._config_check() ended with error: %s', error) + break + + self.logger.debug('CAhandler._config_check() ended with: %s', error) + return error + + def _config_load(self): + """" load config from file """ + self.logger.debug('CAhandler._config_load()') + + config_dic = load_config(self.logger, 'CAhandler') + if 'CAhandler' in config_dic: + cfg_dic = dict(config_dic['CAhandler']) + self.api_url = cfg_dic.get('api_url', 'https://www.digicert.com/services/v2/') + self.api_key = cfg_dic.get('api_key', None) + self.cert_type = cfg_dic.get('cert_type', 'ssl_basic') + self.signature_hash = cfg_dic.get('signature_hash', 'sha256') + self.order_validity = cfg_dic.get('order_validity', 1) + self.request_timeout = cfg_dic.get('request_timeout', 10) + self.organization_id = cfg_dic.get('organization_id', None) + self.organization_name = cfg_dic.get('organization_name', None) + + if 'allowed_domainlist' in config_dic['CAhandler']: + try: + self.allowed_domainlist = json.loads(config_dic['CAhandler']['allowed_domainlist']) + except Exception as err: + self.logger.error('CAhandler._config_load(): failed to parse allowed_domainlist: %s', err) + self.allowed_domainlist = 'failed to parse' + + # load profiling + self.eab_profiling, self.eab_handler = config_eab_profile_load(self.logger, config_dic) + # load header info + self.header_info_field = config_headerinfo_load(self.logger, config_dic) + + self.logger.debug('CAhandler._config_load() ended') + + + def enroll(self, csr: str) -> Tuple[str, str, str, str]: + """ enroll certificate """ + self.logger.debug('CAhandler.enroll()') + + cert_bundle = None + error = None + cert_raw = None + poll_indentifier = None + + # check configuration + error = self._config_check() + + if not error: + pass + + self.logger.debug('Certificate.enroll() ended') + return (error, cert_bundle, cert_raw, poll_indentifier) + + def poll(self, _cert_name: str, poll_identifier: str, _csr: str) -> Tuple[str, str, str, str, bool]: + """ poll status of pending CSR and download certificates """ + self.logger.debug('CAhandler.poll()') + + error = 'Method not implemented.' + cert_bundle = None + cert_raw = None + rejected = False + + self.logger.debug('CAhandler.poll() ended') + return (error, cert_bundle, cert_raw, poll_identifier, rejected) + + def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_date: str = uts_to_date_utc(uts_now())) -> Tuple[int, str, str]: + """ revoke certificate """ + self.logger.debug('CAhandler.revoke()') + + code = None + message = None + detail = 'Method not implemented.' + + self.logger.debug('CAhandler.poll() ended') + + self.logger.debug('Certificate.revoke() ended') + return (code, message, detail) + + def trigger(self, _payload: str) -> Tuple[str, str, str]: + """ process trigger message and return certificate """ + self.logger.debug('CAhandler.trigger()') + + error = 'Method not implemented.' + cert_bundle = None + cert_raw = None + + self.logger.debug('CAhandler.trigger() ended with error: %s', error) + return (error, cert_bundle, cert_raw) From f4ccb9172abaf9864bc911109124371bace6b5d6 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 19 Oct 2024 07:45:39 +0200 Subject: [PATCH 33/54] [fix] openssl handler --- .github/workflows/ca_handler_tests_openssl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ca_handler_tests_openssl.yml b/.github/workflows/ca_handler_tests_openssl.yml index 49ee5842..fac0b65c 100644 --- a/.github/workflows/ca_handler_tests_openssl.yml +++ b/.github/workflows/ca_handler_tests_openssl.yml @@ -45,7 +45,7 @@ jobs: sudo echo -e "\nopenssl_conf: volume/acme_ca/openssl.cnf" >> examples/Docker/data/acme_srv.cfg sudo touch examples/Docker/data/acme_ca/openssl.cnf sudo chmod 777 examples/Docker/data/acme_ca/openssl.cnf - sudo echo -e "[extensions]\nbasicConstraints = critical, CA:FALSE\nsubjectKeyIdentifier = critical, hash, issuer:always\nauthorityKeyIdentifier = keyid:always, issuer:always" >> examples/Docker/data/acme_ca/openssl.cnf + sudo echo -e "[extensions]\nbasicConstraints = critical, CA:FALSE\nsubjectKeyIdentifier = hash, issuer:always\nauthorityKeyIdentifier = keyid:always, issuer:always" >> examples/Docker/data/acme_ca/openssl.cnf sudo echo -e "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement\nextendedKeyUsage = critical, serverAuth, OCSPSigning\n" >> examples/Docker/data/acme_ca/openssl.cnf cd examples/Docker/ docker-compose restart From 2ceb3a4ade05f876610a5095fc9f6bc994b9bade Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 19 Oct 2024 07:45:39 +0200 Subject: [PATCH 34/54] [fix] openssl handler --- .github/workflows/ca_handler_tests_openssl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ca_handler_tests_openssl.yml b/.github/workflows/ca_handler_tests_openssl.yml index 49ee5842..fac0b65c 100644 --- a/.github/workflows/ca_handler_tests_openssl.yml +++ b/.github/workflows/ca_handler_tests_openssl.yml @@ -45,7 +45,7 @@ jobs: sudo echo -e "\nopenssl_conf: volume/acme_ca/openssl.cnf" >> examples/Docker/data/acme_srv.cfg sudo touch examples/Docker/data/acme_ca/openssl.cnf sudo chmod 777 examples/Docker/data/acme_ca/openssl.cnf - sudo echo -e "[extensions]\nbasicConstraints = critical, CA:FALSE\nsubjectKeyIdentifier = critical, hash, issuer:always\nauthorityKeyIdentifier = keyid:always, issuer:always" >> examples/Docker/data/acme_ca/openssl.cnf + sudo echo -e "[extensions]\nbasicConstraints = critical, CA:FALSE\nsubjectKeyIdentifier = hash, issuer:always\nauthorityKeyIdentifier = keyid:always, issuer:always" >> examples/Docker/data/acme_ca/openssl.cnf sudo echo -e "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement\nextendedKeyUsage = critical, serverAuth, OCSPSigning\n" >> examples/Docker/data/acme_ca/openssl.cnf cd examples/Docker/ docker-compose restart From 348654b2a2f4219dea4b20d3ab4b7da01ebd6e6f Mon Sep 17 00:00:00 2001 From: ctrought <65360454+ctrought@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:59:03 -0400 Subject: [PATCH 35/54] [feat] url option for mscertsrv --- examples/ca_handler/certsrv.py | 47 +++++++++++++++++---- examples/ca_handler/mscertsrv_ca_handler.py | 20 +++++++-- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/examples/ca_handler/certsrv.py b/examples/ca_handler/certsrv.py index 8c335d21..53199534 100644 --- a/examples/ca_handler/certsrv.py +++ b/examples/ca_handler/certsrv.py @@ -73,10 +73,11 @@ class Certsrv(object): the password parameter the path to a (unencrypted) private key. """ # pylint: disable=r0913 - def __init__(self, server, username, password, auth_method="basic", + def __init__(self, server, url, username, password, auth_method="basic", cafile=None, verify=True, timeout=TIMEOUT, proxies=None): self.server = server + self.url = url self.timeout = timeout self.auth_method = auth_method self.session = requests.Session() @@ -197,7 +198,11 @@ def get_cert(self, csr, template, encoding="b64", attributes=None): "SaveCert": "yes", } - url = "https://{0}/certsrv/certfnsh.asp".format(self.server) + url = "" + if self.url: + url = "{0}/certfnsh.asp".format(self.url) + else: + url = "https://{0}/certsrv/certfnsh.asp".format(self.server) response = self._post(url, data=data) @@ -241,7 +246,12 @@ def get_existing_cert(self, req_id, encoding="b64"): while fetching the cert. """ - cert_url = "https://{0}/certsrv/certnew.cer".format(self.server) + cert_url = "" + if self.url: + cert_url = "{0}/certnew.cer".format(self.url) + else: + cert_url = "https://{0}/certsrv/certnew.cer".format(self.server) + params = {"ReqID": req_id, "Enc": encoding} response = self._get(cert_url, params=params) @@ -270,7 +280,11 @@ def get_ca_cert(self, encoding="b64"): Returns: The newest CA certificate from the server. """ - url = "https://{0}/certsrv/certcarc.asp".format(self.server) + url = "" + if self.url: + url = "{0}/certcarc.asp".format(self.url) + else: + url = "https://{0}/certsrv/certcarc.asp".format(self.server) response = self._get(url) @@ -278,7 +292,11 @@ def get_ca_cert(self, encoding="b64"): # so that we get the newest CA cert. renewals = re.search(r"var nRenewals=(\d+);", response.text).group(1) - cert_url = "https://{0}/certsrv/certnew.cer".format(self.server) + cert_url = "" + if self.url: + cert_url = "{0}/certnew.cer".format(self.url) + else: + cert_url = "https://{0}/certsrv/certnew.cer".format(self.server) params = {"ReqID": "CACert", "Enc": encoding, "Renewal": renewals} response = self._get(cert_url, params=params) @@ -301,14 +319,23 @@ def get_chain(self, encoding="bin"): Returns: The CA chain from the server, in PKCS#7 format. """ - url = "https://{0}/certsrv/certcarc.asp".format(self.server) + url = "" + if self.url: + url = "{0}/certcarc.asp".format(self.url) + else: + url = "https://{0}/certsrv/certcarc.asp".format(self.server) response = self._get(url) # We have to check how many renewals this server has had, so that we get the newest chain renewals = re.search(r"var nRenewals=(\d+);", response.text).group(1) - chain_url = "https://{0}/certsrv/certnew.p7b".format(self.server) + chain_url = "" + if self.url: + chain_url = "{0}/certnew.p7b".format(self.url) + else: + chain_url = "https://{0}/certsrv/certnew.p7b".format(self.server) + params = {"ReqID": "CACert", "Renewal": renewals, "Enc": encoding} chain_response = self._get(chain_url, params=params) @@ -327,7 +354,11 @@ def check_credentials(self): Returns: True if authentication succeeded, False if it failed. """ - url = "https://{0}/certsrv/".format(self.server) + url = "" + if self.url: + url = "{0}".format(self.url) + else: + url = "https://{0}/certsrv/".format(self.server) try: self._get(url) diff --git a/examples/ca_handler/mscertsrv_ca_handler.py b/examples/ca_handler/mscertsrv_ca_handler.py index 438b6d7c..e8f8e401 100644 --- a/examples/ca_handler/mscertsrv_ca_handler.py +++ b/examples/ca_handler/mscertsrv_ca_handler.py @@ -18,6 +18,7 @@ class CAhandler(object): def __init__(self, _debug: bool = False, logger: object = None): self.logger = logger self.host = None + self.url = None self.user = None self.password = None self.auth_method = 'basic' @@ -120,9 +121,21 @@ def _config_hostname_load(self, config_dic: Dict[str, str]): if self.host: self.logger.info('CAhandler._config_load() overwrite host') self.host = config_dic['CAhandler']['host'] - self.logger.debug('CAhandler._config_hostname_load() ended') + def _config_url_load(self, config_dic: Dict[str, str]): + if 'url_variable' in config_dic['CAhandler']: + try: + self.url = os.environ[config_dic['CAhandler']['url_variable']] + except Exception as err: + self.logger.error('CAhandler._config_load() could not load url_variable:%s', err) + if 'url' in config_dic['CAhandler']: + if self.url: + self.logger.info('CAhandler._config_load() overwrite url') + self.url = config_dic['CAhandler']['url'] + + self.logger.debug('CAhandler._config_url_load() ended') + def _config_parameters_load(self, config_dic: Dict[str, str]): """ load hostname """ self.logger.debug('CAhandler._config_parameters_load()') @@ -170,6 +183,7 @@ def _config_load(self): if 'CAhandler' in config_dic: # load parameters from config dic self._config_hostname_load(config_dic) + self._config_url_load(config_dic) self._config_user_load(config_dic) self._config_password_load(config_dic) self._config_parameters_load(config_dic) @@ -296,13 +310,13 @@ def enroll(self, csr: str) -> Tuple[str, str, str, bool]: self._parameter_overwrite(csr) - if self.host and self.user and self.password and self.template: + if (self.host or self.url) and self.user and self.password and self.template: result = self._domainlist_check(csr) if result: # setup certserv - ca_server = Certsrv(self.host, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) + ca_server = Certsrv(self.host, self.url, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) # check connection and credentials auth_check = self._check_credentials(ca_server) From 428b0ce0701cfa148d2b29dd3477a8d0c5dd5c4c Mon Sep 17 00:00:00 2001 From: grindsa Date: Fri, 25 Oct 2024 07:11:47 +0200 Subject: [PATCH 36/54] [feat] entrust_ca_handler --- .gitignore | 1 + acme_srv/helper.py | 4 +- examples/ca_handler/entrust_ca_handler.py | 384 ++++++++++++++++++++-- 3 files changed, 353 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index da25b0d5..dfad8fb0 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ acme_srv/ejbca/* acme_srv/ssl/* acme_srv/cn_dump/* acme_srv/fixture +acme_srv/entrust/* *.dll acme_srv/cmp/WindowsCMPOpenSSL/openssl.exe acme_srv/migrations.old diff --git a/acme_srv/helper.py b/acme_srv/helper.py index b6ee66b5..32876138 100644 --- a/acme_srv/helper.py +++ b/acme_srv/helper.py @@ -831,14 +831,14 @@ def header_info_lookup(logger, csr: str, header_info_field, key: str) -> str: return result -def header_info_get(logger: logging.Logger, csr: str, vlist: List[str] = ('id', 'name', 'header_info')) -> List[str]: +def header_info_get(logger: logging.Logger, csr: str, vlist: List[str] = ('id', 'name', 'header_info'), field_name: str = 'csr') -> List[str]: """ lookup header information """ logger.debug('Helper.header_info_get()') try: from acme_srv.db_handler import DBstore # pylint: disable=c0415 dbstore = DBstore(logger=logger) - result = dbstore.certificates_search('csr', csr, vlist) + result = dbstore.certificates_search(field_name, csr, vlist) except Exception as err: result = [] logger.error('Helper.header_info_get(): error: %s', err) diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index 98bac745..978538a2 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -1,23 +1,65 @@ # -*- coding: utf-8 -*- """ CA handler using Digicert CertCentralAPI""" from __future__ import print_function -from typing import Tuple, Dict +from typing import Tuple, Dict, List +import datetime +import os import json import requests +from requests_pkcs12 import Pkcs12Adapter # pylint: disable=e0401 -from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get +from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get, header_info_get CONTENT_TYPE = 'application/json' +# hardcoded Entrust Root Certification Authority - G2 +ENTRUST_ROOT_CA = '''-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- +''' + + class CAhandler(object): """ Digicert CertCentralAP handler """ def __init__(self, _debug: bool = None, logger: object = None): self.logger = logger - self.api_url = '' - self.api_key = 'foo' + self.api_url = 'https://api.entrust.net/enterprise/v2' + self.client_cert = None + self.cert_passphrase = None + self.username = None + self.password = None + self.organization_name = None + self.certtype = 'STANDARD_SSL' + self.cert_validity_days = 365 + self.entrust_root_cert = ENTRUST_ROOT_CA + self.proxy = None + self.session = None + self.request_timeout = 10 self.allowed_domainlist = [] self.header_info_field = False @@ -26,7 +68,7 @@ def __init__(self, _debug: bool = None, logger: object = None): def __enter__(self): """ Makes CAhandler a Context Manager """ - if not self.api_key: + if not self.session: self._config_load() return self @@ -52,12 +94,11 @@ def _api_get(self, url: str) -> Tuple[int, Dict[str, str]]: """ post data to API """ self.logger.debug('CAhandler._api_get()') headers = { - 'X-DC-DEVKEY': self.api_key, 'Content-Type': CONTENT_TYPE } try: - api_response = requests.get(url=url, headers=headers, proxies=self.proxy, timeout=self.request_timeout) + api_response = self.session.get(url=url, headers=headers, proxies=self.proxy, timeout=self.request_timeout) code = api_response.status_code try: content = api_response.json() @@ -75,12 +116,11 @@ def _api_post(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str] """ post data to API """ self.logger.debug('CAhandler._api_post()') headers = { - 'X-DC-DEVKEY': self.api_key, 'Content-Type': CONTENT_TYPE } try: - api_response = requests.post(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + api_response = self.session.post(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) code = api_response.status_code if api_response.text: try: @@ -101,12 +141,11 @@ def _api_put(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]] """ post data to API """ self.logger.debug('CAhandler._api_put()') headers = { - 'X-DC-DEVKEY': self.api_key, 'Content-Type': CONTENT_TYPE } try: - api_response = requests.put(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) + api_response = self.session.put(url=url, headers=headers, json=data, proxies=self.proxy, timeout=self.request_timeout) code = api_response.status_code if api_response.text: try: @@ -123,19 +162,19 @@ def _api_put(self, url: str, data: Dict[str, str]) -> Tuple[int, Dict[str, str]] return code, content - def _config_check(self) -> str: - """ check config """ - self.logger.debug('CAhandler._config_check()') + def _certificates_get_from_serial(self, cert_serial: str) -> List[str]: + """ get certificates """ + self.logger.debug('CAhandler._certificates_get_from_serial()') - error = None - for ele in ['api_url', 'api_key', 'organization_name']: - if not getattr(self, ele): - error = f'{ele} parameter in missing in config file' - self.logger.error('CAhandler._config_check() ended with error: %s', error) - break + code, content = self._api_get(self.api_url + f'/certificates?serialNumber={cert_serial}') - self.logger.debug('CAhandler._config_check() ended with: %s', error) - return error + if code == 200 and 'certificates' in content: + cert_list = content['certificates'] + else: + cert_list = [] + + self.logger.debug('CAhandler._certificates_get_from_serial() ended with code: %s and %s certificate', code, len(cert_list)) + return cert_list def _config_load(self): """" load config from file """ @@ -144,14 +183,14 @@ def _config_load(self): config_dic = load_config(self.logger, 'CAhandler') if 'CAhandler' in config_dic: cfg_dic = dict(config_dic['CAhandler']) - self.api_url = cfg_dic.get('api_url', 'https://www.digicert.com/services/v2/') - self.api_key = cfg_dic.get('api_key', None) - self.cert_type = cfg_dic.get('cert_type', 'ssl_basic') - self.signature_hash = cfg_dic.get('signature_hash', 'sha256') - self.order_validity = cfg_dic.get('order_validity', 1) - self.request_timeout = cfg_dic.get('request_timeout', 10) - self.organization_id = cfg_dic.get('organization_id', None) + self.api_url = cfg_dic.get('api_url', 'https://api.entrust.net/enterprise/v2') + self.request_timeout = int(cfg_dic.get('request_timeout', 10)) + self.cert_validity_days = int(cfg_dic.get('cert_validity_days', 365)) + self.username = cfg_dic.get('username', None) + self.password = cfg_dic.get('password', None) self.organization_name = cfg_dic.get('organization_name', None) + self.certtype = cfg_dic.get('certtype', 'STANDARD_SSL') + self._config_session_load(config_dic) if 'allowed_domainlist' in config_dic['CAhandler']: try: @@ -159,6 +198,8 @@ def _config_load(self): except Exception as err: self.logger.error('CAhandler._config_load(): failed to parse allowed_domainlist: %s', err) self.allowed_domainlist = 'failed to parse' + # load root CA + self._config_root_load(config_dic) # load profiling self.eab_profiling, self.eab_handler = config_eab_profile_load(self.logger, config_dic) @@ -167,6 +208,265 @@ def _config_load(self): self.logger.debug('CAhandler._config_load() ended') + def _config_passphrase_load(self, config_dic: Dict[str, str]): + """ load passphrase """ + self.logger.debug('CAhandler._config_passphrase_load()') + if 'cert_passphrase_variable' in config_dic['CAhandler'] or 'cert_passphrase' in config_dic['CAhandler']: + if 'cert_passphrase_variable' in config_dic['CAhandler']: + self.logger.debug('CAhandler._config_passphrase_load(): load passphrase from environment variable') + try: + self.cert_passphrase = os.environ[config_dic['CAhandler']['cert_passphrase_variable']] + except Exception as err: + self.logger.error('CAhandler._config_passphrase_load() could not load cert_passphrase_variable:%s', err) + + if 'cert_passphrase' in config_dic['CAhandler']: + self.logger.debug('CAhandler._config_passphrase_load(): load passphrase from config file') + if self.cert_passphrase: + self.logger.info('CAhandler._config_load() overwrite cert_passphrase') + self.cert_passphrase = config_dic['CAhandler']['cert_passphrase'] + self.logger.debug('CAhandler._config_passphrase_load() ended') + + def _config_root_load(self, config_dic: Dict[str, str]): + """ load root CA """ + self.logger.debug('CAhandler._config_root_load()') + + if 'entrust_root_cert' in config_dic['CAhandler']: + if os.path.isfile(config_dic['CAhandler']['entrust_root_cert']): + self.logger.debug('CAhandler._config_root_load(): load root CA from config file') + with open(config_dic['CAhandler']['entrust_root_cert'], 'r', encoding='utf8') as ca_file: + self.entrust_root_cert = ca_file.read() + else: + self.logger.error('CAhandler._config_root_load(): root CA file configured but not not found. Using default one.') + + self.logger.debug('CAhandler._config_root_load() ended') + + def _config_session_load(self, config_dic: Dict[str, str]): + """ load session """ + self.logger.debug('CAhandler._config_session_load()') + + with requests.Session() as self.session: + # client auth via pem files + if 'client_cert' in config_dic['CAhandler'] and 'client_key' in config_dic['CAhandler']: + self.logger.debug('CAhandler._config_session_load() cert and key in pem format') + self.session.cert = (config_dic['CAhandler']['client_cert'], config_dic['CAhandler']['client_key']) + + else: + self._config_passphrase_load(config_dic) + if 'client_cert' in config_dic['CAhandler'] and self.cert_passphrase: + self.session.mount(self.api_url, Pkcs12Adapter(pkcs12_filename=config_dic['CAhandler']['client_cert'], pkcs12_password=self.cert_passphrase)) + else: + self.logger.error('CAhandler._config_load() configuration incomplete: either "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file') + self.session.auth = (self.username, self.password) + + self.logger.debug('CAhandler._config_session_load() ended') + + def _csr_cn_lookup(self, csr: str) -> str: + """ lookup CN/ 1st san from CSR """ + self.logger.debug('CAhandler._csr_cn_lookup()') + + csr_cn = csr_cn_get(self.logger, csr) + if not csr_cn: + # lookup first san + san_list = csr_san_get(self.logger, csr) + if san_list and len(san_list) > 0: + for san in san_list: + try: + csr_cn = san.split(':')[1] + break + except Exception as err: + self.logger.error('CAhandler._csr_cn_lookup() split failed: %s', err) + else: + self.logger.error('CAhandler._csr_cn_lookup() no SANs found in CSR') + + self.logger.debug('CAhandler._csr_cn_lookup() ended with: %s', csr_cn) + return csr_cn + + def _org_domain_cfg_check(self) -> str: + """ check organizations """ + self.logger.debug('CAhandler._organizations_check()') + + error = None + org_dic = self._organizations_get() + if self.organization_name not in org_dic: + error = f'Organization {self.organization_name} not found in Entrust API' + self.logger.error('CAhandler._organizations_check() ended with error: %s', error) + else: + domain_list = self._domains_get(org_dic[self.organization_name]) + if not self.allowed_domainlist: + self.logger.info('CAhandler._organizations_check(): allowed_domainlist is empty, using domains from Entrust API') + self.allowed_domainlist = domain_list + + self.logger.debug('CAhandler._organizations_check() ended with %s', error) + return error + + def _organizations_get(self) -> Dict[str, str]: + """ get organizations """ + self.logger.debug('CAhandler._organizations_get()') + + code, content = self._api_get(self.api_url + '/organizations') + + org_dic = {} + if code == 200 and 'organizations' in content: + self.logger.debug('CAhandler._organizations_get() ended with code: 200') + for org in content['organizations']: + if org['verificationStatus'] == 'APPROVED': + org_dic[org['name']] = org['clientId'] + + self.logger.debug('CAhandler._organizations_get() ended with code: %s', code) + return org_dic + + def _domains_get(self, org_id: str) -> List[str]: + """ get domains """ + self.logger.debug('CAhandler._domains_get()') + + code, content = self._api_get(self.api_url + f'/clients/{org_id}/domains') + + api_domain_list = [] + if code == 200 and 'domains' in content: + self.logger.debug('CAhandler._domains_get() ended with code: 200') + + for domain in content['domains']: + if domain['verificationStatus'] == 'APPROVED': + api_domain_list.append(domain['domainName']) + + self.logger.debug('CAhandler._domains_get() ended with code: %s', code) + return api_domain_list + + def _credential_check(self): + """ test connection to Entrust api """ + self.logger.debug('CAhandler._credential_check()') + + error = None + code, content = self._api_get(self.api_url + '/application/version') + if code != 200: + error = f'Connection to Entrust API failed: {content}' + + self.logger.debug('CAhandler._credential_check() ended with code: %s', code) + return error + + def _config_check(self) -> str: + """ check config """ + self.logger.debug('CAhandler._config_check()') + + error = None + for ele in ['api_url', 'username', 'password', 'organization_name']: + if not getattr(self, ele): + error = f'{ele} parameter in missing in config file' + self.logger.error('CAhandler._config_check() ended with error: %s', error) + break + + self.logger.debug('CAhandler._config_check() ended with: %s', error) + return error + + def _enroll_check(self, csr: str) -> str: + """ check csr """ + self.logger.debug('CAhandler._enroll_check()') + + # check for eab profiling and header_info + error = eab_profile_header_info_check(self.logger, self, csr, 'cert_type') + + if not error: + error = self._config_check() + + if not error: + error = self._credential_check() + + if not error: + error = self._org_domain_cfg_check() + + if not error: + # check for allowed domainlist + error = self._allowed_domainlist_check(csr) + + self.logger.debug('CAhandler._enroll_check() ended with %s', error) + return error + + def _trackingid_get(self, cert_raw: str) -> int: + """ get tracking id """ + self.logger.debug('CAhandler._trackingid_get()') + + tracking_id = None + # we misuse header_info_get() to get the tracking id from database + pid_list = header_info_get(self.logger, csr=cert_raw, vlist=['poll_identifier'], field_name='cert_raw') + if pid_list and len(pid_list) > 0: + tracking_id = pid_list[0]['poll_identifier'] + + if not tracking_id: + # lookup through Entrust API + self.logger.info('CAhandler._trackingid_get(): tracking_id not found in database. Lookup trough Entrust API') + cert_serial = cert_serial_get(self.logger, cert_raw, hexformat=True) + certificate_list = self._certificates_get_from_serial(cert_serial) + if certificate_list and len(certificate_list) > 0: + tracking_id = certificate_list[0]['trackingId'] + + self.logger.debug('CAhandler._trackingid_get() ended with %s', tracking_id) + return tracking_id + + def _response_parse(self, content: Dict[str, str]) -> Tuple[str, str]: + """ parse response """ + self.logger.debug('CAhandler._response_parse()') + + cert_bundle = None + cert_raw = None + poll_indentifier = None + + if 'trackingId' in content: + poll_indentifier = content['trackingId'] + if 'endEntityCert' in content and 'chainCerts' in content: + cert_raw = b64_encode(self.logger, cert_pem2der(content['endEntityCert'])) + for cnt, ca_cert in enumerate(content['chainCerts']): + if cnt == 0: + cert_bundle = ca_cert + '\n' + else: + cert_bundle += ca_cert + '\n' + + # add Entrust Root CA + cert_bundle += self.entrust_root_cert + '\n' + + self.logger.debug('CAhandler._response_parse() ended') + return cert_bundle, cert_raw, poll_indentifier + + def _enroll(self, csr: str) -> Tuple[str, str]: + """ enroll certificate """ + self.logger.debug('CAhandler._enroll()') + + error = None + cert_raw = None + cert_bundle = None + poll_indentifier = None + + # get CN and SANs + cn = self._csr_cn_lookup(csr) + + # calculate cert expiry date + certexpirydate = datetime.datetime.now() + datetime.timedelta(days=self.cert_validity_days) + + data_dic = { + 'csr': csr, + 'signingAlg': 'SHA-2', + 'eku': "SERVER_AND_CLIENT_AUTH", + 'cn': cn, + 'org': self.organization_name, + 'endUserKeyStorageAgreement': True, + 'certType': self.certtype, + "certExpiryDate": certexpirydate.strftime('%Y-%m-%d') + } + + code, content = self._api_post(self.api_url + '/certificates', data_dic) + + if code == 201: + + cert_bundle, cert_raw, poll_indentifier = self._response_parse(content) + + else: + if 'errors' in content: + error = f"Error during order creation: {code} - {content['errors']}" + else: + error = f'Error during order creation: {code} - {content}' + self.logger.error('CAhandler._enroll() failed with error: %s', error) + + self.logger.debug('CAhandler._enroll() ended with code: %s', code) + return error, cert_bundle, cert_raw, poll_indentifier def enroll(self, csr: str) -> Tuple[str, str, str, str]: """ enroll certificate """ @@ -177,14 +477,13 @@ def enroll(self, csr: str) -> Tuple[str, str, str, str]: cert_raw = None poll_indentifier = None - # check configuration - error = self._config_check() + error = self._enroll_check(csr) if not error: - pass + error, cert_bundle, cert_raw, poll_indentifier = self._enroll(csr) self.logger.debug('Certificate.enroll() ended') - return (error, cert_bundle, cert_raw, poll_indentifier) + return error, cert_bundle, cert_raw, poll_indentifier def poll(self, _cert_name: str, poll_identifier: str, _csr: str) -> Tuple[str, str, str, str, bool]: """ poll status of pending CSR and download certificates """ @@ -204,7 +503,24 @@ def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_da code = None message = None - detail = 'Method not implemented.' + detail = None + + # get tracking id as input for revocation call + tracking_id = self._trackingid_get(certificate_raw) + # tracking_id = 7347070 + + if tracking_id: + code, content = self._api_post(self.api_url + f'/certificates/{tracking_id}/revocations', {'crlReason': _rev_reason, 'revocationComment': 'revoked by acme2certifier'}) + if code == 200: + message = 'Certificate revoked' + else: + code = 500 + message = 'urn:ietf:params:acme:error:serverInternal' + detail = content + else: + code = 500 + message = 'urn:ietf:params:acme:error:serverInternal' + detail = 'Failed to get tracking id' self.logger.debug('CAhandler.poll() ended') From e1536fab8f46e056106d138040531a610f9d4f9e Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 26 Oct 2024 06:50:03 +0200 Subject: [PATCH 37/54] [tst] additional unittests --- examples/ca_handler/mscertsrv_ca_handler.py | 2 +- test/test_msca_handler.py | 28 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/ca_handler/mscertsrv_ca_handler.py b/examples/ca_handler/mscertsrv_ca_handler.py index cdd31b97..ace57865 100644 --- a/examples/ca_handler/mscertsrv_ca_handler.py +++ b/examples/ca_handler/mscertsrv_ca_handler.py @@ -314,7 +314,7 @@ def enroll(self, csr: str) -> Tuple[str, str, str, bool]: result = self._domainlist_check(csr) if result: - + # check for eab profiling and header_info error = eab_profile_header_info_check(self.logger, self, csr, 'template') diff --git a/test/test_msca_handler.py b/test/test_msca_handler.py index 0c6090a3..e83a54b0 100644 --- a/test/test_msca_handler.py +++ b/test/test_msca_handler.py @@ -796,6 +796,34 @@ def test_058_config_headerinfo_load(self): self.assertFalse(self.cahandler.header_info_field) self.assertIn('WARNING:test_a2c:Order._config_orderconfig_load() header_info_list failed with error: Expecting value: line 1 column 1 (char 0)', lcm.output) + def test_059__config_url_load(self): + """ test _config_url_load()""" + config_dic = {'CAhandler': {'url': 'foo'}} + self.cahandler._config_url_load(config_dic) + self.assertEqual( 'foo', self.cahandler.url) + + @patch.dict('os.environ', {'url_variable': 'foo1'}) + def test_060__config_url_load(self): + """ test _config_url_load()""" + config_dic = {'CAhandler': {'url_variable': 'url_variable'}} + self.cahandler._config_url_load(config_dic) + self.assertEqual( 'foo1', self.cahandler.url) + + @patch.dict('os.environ', {'url_variable': 'foo1'}) + def test_061__config_url_load(self): + """ test _config_url_load()""" + config_dic = {'CAhandler': {'url_variable': 'url_variable', 'url': 'foo'}} + self.cahandler._config_url_load(config_dic) + self.assertEqual( 'foo', self.cahandler.url) + + @patch.dict('os.environ', {'url_variable': 'foo1'}) + def test_062__config_url_load(self): + """ test _config_url_load()""" + config_dic = {'CAhandler': {'url_variable': 'doesnotexist'}} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_url_load(config_dic) + self.assertFalse(self.cahandler.url) + self.assertIn("ERROR:test_a2c:CAhandler._config_load() could not load url_variable:'doesnotexist'", lcm.output) if __name__ == '__main__': From dc88e12419646a238e45b22514751c6511f2d394 Mon Sep 17 00:00:00 2001 From: grindsa Date: Wed, 30 Oct 2024 07:58:22 +0300 Subject: [PATCH 38/54] [tst] unittests --- examples/ca_handler/entrust_ca_handler.py | 53 +- test/test_digicert.py | 118 +- test/test_entrust.py | 1191 +++++++++++++++++++++ 3 files changed, 1284 insertions(+), 78 deletions(-) create mode 100644 test/test_entrust.py diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index 978538a2..c58c431e 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -171,6 +171,7 @@ def _certificates_get_from_serial(self, cert_serial: str) -> List[str]: if code == 200 and 'certificates' in content: cert_list = content['certificates'] else: + self.logger.error('CAhandler._certificates_get_from_serial() for %s failed with code: %s', cert_serial, code) cert_list = [] self.logger.debug('CAhandler._certificates_get_from_serial() ended with code: %s and %s certificate', code, len(cert_list)) @@ -184,8 +185,15 @@ def _config_load(self): if 'CAhandler' in config_dic: cfg_dic = dict(config_dic['CAhandler']) self.api_url = cfg_dic.get('api_url', 'https://api.entrust.net/enterprise/v2') - self.request_timeout = int(cfg_dic.get('request_timeout', 10)) - self.cert_validity_days = int(cfg_dic.get('cert_validity_days', 365)) + try: + self.request_timeout = int(cfg_dic.get('request_timeout', 10)) + except Exception as err: + self.logger.error('CAhandler._config_load(): failed to parse request_timeout %s', err) + try: + self.cert_validity_days = int(cfg_dic.get('cert_validity_days', 365)) + except Exception as err: + self.logger.error('CAhandler._config_load(): failed to parse cert_validity_days %s', err) + self.username = cfg_dic.get('username', None) self.password = cfg_dic.get('password', None) self.organization_name = cfg_dic.get('organization_name', None) @@ -198,8 +206,8 @@ def _config_load(self): except Exception as err: self.logger.error('CAhandler._config_load(): failed to parse allowed_domainlist: %s', err) self.allowed_domainlist = 'failed to parse' - # load root CA - self._config_root_load(config_dic) + # load root CA + self._config_root_load(config_dic) # load profiling self.eab_profiling, self.eab_handler = config_eab_profile_load(self.logger, config_dic) @@ -229,7 +237,6 @@ def _config_passphrase_load(self, config_dic: Dict[str, str]): def _config_root_load(self, config_dic: Dict[str, str]): """ load root CA """ self.logger.debug('CAhandler._config_root_load()') - if 'entrust_root_cert' in config_dic['CAhandler']: if os.path.isfile(config_dic['CAhandler']['entrust_root_cert']): self.logger.debug('CAhandler._config_root_load(): load root CA from config file') @@ -304,13 +311,15 @@ def _organizations_get(self) -> Dict[str, str]: self.logger.debug('CAhandler._organizations_get()') code, content = self._api_get(self.api_url + '/organizations') - org_dic = {} if code == 200 and 'organizations' in content: self.logger.debug('CAhandler._organizations_get() ended with code: 200') for org in content['organizations']: - if org['verificationStatus'] == 'APPROVED': - org_dic[org['name']] = org['clientId'] + if 'verificationStatus' in org and org['verificationStatus'] == 'APPROVED': + if 'name' in org and 'clientId' in org: + org_dic[org['name']] = org['clientId'] + else: + self.logger.error('CAhandler._organizations_get(): malformed response') self.logger.debug('CAhandler._organizations_get() ended with code: %s', code) return org_dic @@ -326,8 +335,11 @@ def _domains_get(self, org_id: str) -> List[str]: self.logger.debug('CAhandler._domains_get() ended with code: 200') for domain in content['domains']: - if domain['verificationStatus'] == 'APPROVED': - api_domain_list.append(domain['domainName']) + if 'verificationStatus' in domain and domain['verificationStatus'] == 'APPROVED': + if 'domainName' in domain: + api_domain_list.append(domain['domainName']) + else: + self.logger.error('CAhandler._domains_get(): malformed response') self.logger.debug('CAhandler._domains_get() ended with code: %s', code) return api_domain_list @@ -388,16 +400,20 @@ def _trackingid_get(self, cert_raw: str) -> int: tracking_id = None # we misuse header_info_get() to get the tracking id from database pid_list = header_info_get(self.logger, csr=cert_raw, vlist=['poll_identifier'], field_name='cert_raw') - if pid_list and len(pid_list) > 0: - tracking_id = pid_list[0]['poll_identifier'] + for ele in pid_list: + if 'poll_identifier' in ele: + tracking_id = ele['poll_identifier'] + break if not tracking_id: # lookup through Entrust API self.logger.info('CAhandler._trackingid_get(): tracking_id not found in database. Lookup trough Entrust API') cert_serial = cert_serial_get(self.logger, cert_raw, hexformat=True) certificate_list = self._certificates_get_from_serial(cert_serial) - if certificate_list and len(certificate_list) > 0: - tracking_id = certificate_list[0]['trackingId'] + for ele in certificate_list: + if 'trackingId' in ele: + tracking_id = ele['trackingId'] + break self.logger.debug('CAhandler._trackingid_get() ended with %s', tracking_id) return tracking_id @@ -421,8 +437,10 @@ def _response_parse(self, content: Dict[str, str]) -> Tuple[str, str]: cert_bundle += ca_cert + '\n' # add Entrust Root CA - cert_bundle += self.entrust_root_cert + '\n' - + if cert_bundle: + cert_bundle += self.entrust_root_cert + '\n' + else: + cert_bundle = self.entrust_root_cert + '\n' self.logger.debug('CAhandler._response_parse() ended') return cert_bundle, cert_raw, poll_indentifier @@ -455,9 +473,7 @@ def _enroll(self, csr: str) -> Tuple[str, str]: code, content = self._api_post(self.api_url + '/certificates', data_dic) if code == 201: - cert_bundle, cert_raw, poll_indentifier = self._response_parse(content) - else: if 'errors' in content: error = f"Error during order creation: {code} - {content['errors']}" @@ -507,7 +523,6 @@ def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_da # get tracking id as input for revocation call tracking_id = self._trackingid_get(certificate_raw) - # tracking_id = 7347070 if tracking_id: code, content = self._api_post(self.api_url + f'/certificates/{tracking_id}/revocations', {'crlReason': _rev_reason, 'revocationComment': 'revoked by acme2certifier'}) diff --git a/test/test_digicert.py b/test/test_digicert.py index 0cfb672b..96d068a9 100644 --- a/test/test_digicert.py +++ b/test/test_digicert.py @@ -53,23 +53,23 @@ def test_003__enter__(self, mock_cfg): self.cahandler.__enter__() self.assertFalse(mock_cfg.called) - def test_003_poll(self): + def test_004_poll(self): """ test polling """ self.assertEqual(('Method not implemented.', None, None, 'poll_identifier', False), self.cahandler.poll('cert_name', 'poll_identifier', 'csr')) - def test_004_trigger(self): + def test_005_trigger(self): """ test polling """ self.assertEqual(('Method not implemented.', None, None), self.cahandler.trigger('payload')) @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') - def test_004_allowed_domainlist_check(self, mock_adc): + def test_006_allowed_domainlist_check(self, mock_adc): """ test allowed_domainlist_check """ self.cahandler.allowed_domainlist = False self.assertFalse(self.cahandler._allowed_domainlist_check('csr')) self.assertFalse(mock_adc.called) @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') - def test_005_allowed_domainlist_check(self, mock_adc): + def test_007_allowed_domainlist_check(self, mock_adc): """ test allowed_domainlist_check """ self.cahandler.allowed_domainlist = ["test.com"] mock_adc.return_value = True @@ -77,7 +77,7 @@ def test_005_allowed_domainlist_check(self, mock_adc): self.assertTrue(mock_adc.called) @patch('examples.ca_handler.digicert_ca_handler.allowed_domainlist_check') - def test_006_allowed_domainlist_check(self, mock_adc): + def test_008_allowed_domainlist_check(self, mock_adc): """ test allowed_domainlist_check """ self.cahandler.allowed_domainlist = ["test.com"] mock_adc.return_value = False @@ -85,7 +85,7 @@ def test_006_allowed_domainlist_check(self, mock_adc): self.assertTrue(mock_adc.called) @patch.object(requests, 'post') - def test_018__api_post(self, mock_req): + def test_009__api_post(self, mock_req): """ test _api_post() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -94,7 +94,7 @@ def test_018__api_post(self, mock_req): self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_post('url', 'data')) @patch('requests.post') - def test_019__api_post(self, mock_req): + def test_010__api_post(self, mock_req): """ test _api_post() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -105,7 +105,7 @@ def test_019__api_post(self, mock_req): self.assertIn("ERROR:test_a2c:CAhandler._api_post() returned error during json parsing: 'str' object is not callable", lcm.output) @patch('requests.post') - def test_020__api_post(self, mock_req): + def test_011__api_post(self, mock_req): """ test _api_post() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -114,7 +114,7 @@ def test_020__api_post(self, mock_req): self.assertEqual(('status_code', None), self.cahandler._api_post('url', 'data')) @patch('requests.post') - def test_021__api_post(self, mock_req): + def test_012__api_post(self, mock_req): """ test _api_post(= """ self.cahandler.api_host = 'api_host' self.cahandler.auth = 'auth' @@ -124,7 +124,7 @@ def test_021__api_post(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._api_post() returned error: exc_api_post', lcm.output) @patch.object(requests, 'get') - def test_022__api_get(self, mock_req): + def test_013__api_get(self, mock_req): """ test _api_get() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -133,7 +133,7 @@ def test_022__api_get(self, mock_req): self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_get('url')) @patch('requests.get') - def test_023__api_get(self, mock_req): + def test_014__api_get(self, mock_req): """ test _api_get() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -144,7 +144,7 @@ def test_023__api_get(self, mock_req): self.assertIn("ERROR:test_a2c:CAhandler._api_get() returned error during json parsing: 'str' object is not callable", lcm.output) @patch('requests.get') - def test_024__api_get(self, mock_req): + def test_015__api_get(self, mock_req): """ test _api_get() """ self.cahandler.api_host = 'api_host' self.cahandler.auth = 'auth' @@ -154,7 +154,7 @@ def test_024__api_get(self, mock_req): self.assertIn('ERROR:test_a2c:CAhandler._api_get() returned error: exc_api_get', lcm.output) @patch.object(requests, 'put') - def test_018__api_put(self, mock_req): + def test_016__api_put(self, mock_req): """ test _api_put() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -163,7 +163,7 @@ def test_018__api_put(self, mock_req): self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_put('url', 'data')) @patch('requests.put') - def test_019__api_put(self, mock_req): + def test_017__api_put(self, mock_req): """ test _api_put() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -174,7 +174,7 @@ def test_019__api_put(self, mock_req): self.assertIn("ERROR:test_a2c:CAhandler._api_put() returned error during json parsing: 'str' object is not callable", lcm.output) @patch('requests.put') - def test_020__api_put(self, mock_req): + def test_018__api_put(self, mock_req): """ test _api_put() """ mockresponse = Mock() mockresponse.status_code = 'status_code' @@ -183,7 +183,7 @@ def test_020__api_put(self, mock_req): self.assertEqual(('status_code', None), self.cahandler._api_put('url', 'data')) @patch('requests.put') - def test_021__api_put(self, mock_req): + def test_019__api_put(self, mock_req): """ test _api_put() """ self.cahandler.api_host = 'api_host' self.cahandler.auth = 'auth' @@ -192,18 +192,18 @@ def test_021__api_put(self, mock_req): self.assertEqual((500, 'exc_api_put'), self.cahandler._api_put('url', 'data')) self.assertIn('ERROR:test_a2c:CAhandler._api_put() returned error: exc_api_put', lcm.output) - def test_023__config_check(self): + def test_020__config_check(self): """ test _config_check() """ self.cahandler.api_url = 'api_url' self.assertEqual('api_key parameter in missing in config file', self.cahandler._config_check()) - def test_024__config_check(self): + def test_021__config_check(self): """ test _config_check() """ self.cahandler.api_url = 'api_url' self.cahandler.api_key = 'api_key' self.assertEqual('organization_name parameter in missing in config file', self.cahandler._config_check()) - def test_025__config_check(self): + def test_022__config_check(self): """ test _config_check() """ self.cahandler.api_url = 'api_url' self.cahandler.api_key = 'api_key' @@ -213,7 +213,7 @@ def test_025__config_check(self): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_026_config_load(self, mock_load, mock_eab, mock_hdl): + def test_023_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar'} mock_eab.return_value = True, 'eab' @@ -236,7 +236,7 @@ def test_026_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_027_config_load(self, mock_load, mock_eab, mock_hdl): + def test_024_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'api_url': 'api_url'}} mock_eab.return_value = True, 'eab' @@ -259,7 +259,7 @@ def test_027_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_028_config_load(self, mock_load, mock_eab, mock_hdl): + def test_025_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'api_key': 'api_key'}} mock_eab.return_value = True, 'eab' @@ -282,7 +282,7 @@ def test_028_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_028_config_load(self, mock_load, mock_eab, mock_hdl): + def test_026_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'signature_hash': 'signature_hash'}} mock_eab.return_value = True, 'eab' @@ -305,7 +305,7 @@ def test_028_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_029_config_load(self, mock_load, mock_eab, mock_hdl): + def test_027_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'cert_type': 'cert_type'}} mock_eab.return_value = True, 'eab' @@ -328,7 +328,7 @@ def test_029_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_030_config_load(self, mock_load, mock_eab, mock_hdl): + def test_028_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'order_validity': 2}} mock_eab.return_value = True, 'eab' @@ -351,7 +351,7 @@ def test_030_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_031_config_load(self, mock_load, mock_eab, mock_hdl): + def test_029_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'request_timeout': 20}} mock_eab.return_value = True, 'eab' @@ -374,7 +374,7 @@ def test_031_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_032_config_load(self, mock_load, mock_eab, mock_hdl): + def test_030_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'organization_name': 'organization_name'}} mock_eab.return_value = True, 'eab' @@ -397,7 +397,7 @@ def test_032_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_033_config_load(self, mock_load, mock_eab, mock_hdl): + def test_031_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'organization_id': 'organization_id'}} mock_eab.return_value = True, 'eab' @@ -420,7 +420,7 @@ def test_033_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_033_config_load(self, mock_load, mock_eab, mock_hdl): + def test_032_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': '["foo", "bar"]'}} mock_eab.return_value = True, 'eab' @@ -443,7 +443,7 @@ def test_033_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_034_config_load(self, mock_load, mock_eab, mock_hdl): + def test_033_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': '["foo"]'}} mock_eab.return_value = True, 'eab' @@ -466,7 +466,7 @@ def test_034_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.digicert_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.digicert_ca_handler.load_config') - def test_035_config_load(self, mock_load, mock_eab, mock_hdl): + def test_034_config_load(self, mock_load, mock_eab, mock_hdl): """ test _config_load() """ mock_load.return_value = {'foo': 'bar', 'CAhandler': {'allowed_domainlist': "foo"}} mock_eab.return_value = True, 'eab' @@ -488,7 +488,7 @@ def test_035_config_load(self, mock_load, mock_eab, mock_hdl): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_036_order_send(self, mock_post, mock_orgid): + def test_035_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.organization_id = 'organization_id' @@ -497,7 +497,7 @@ def test_036_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_037_order_send(self, mock_post, mock_orgid): + def test_036_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.assertEqual((500, 'organisation_id is missing'), self.cahandler._order_send('csr', 'cn')) @@ -505,7 +505,7 @@ def test_037_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_036_order_send(self, mock_post, mock_orgid): + def test_037_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.eab_profiling = True @@ -517,7 +517,7 @@ def test_036_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_037_order_send(self, mock_post, mock_orgid): + def test_038_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.eab_profiling = True @@ -530,7 +530,7 @@ def test_037_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_037_order_send(self, mock_post, mock_orgid): + def test_039_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.api_key = 'api_key' @@ -542,7 +542,7 @@ def test_037_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_038_order_send(self, mock_post, mock_orgid): + def test_040_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.api_key = 'api_key' @@ -554,7 +554,7 @@ def test_038_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._organiation_id_get') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_post') - def test_039_order_send(self, mock_post, mock_orgid): + def test_041_order_send(self, mock_post, mock_orgid): """ test _order_send() """ mock_post.return_value = ('code', 'content') self.cahandler.api_key = None @@ -566,7 +566,7 @@ def test_039_order_send(self, mock_post, mock_orgid): @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') @patch('examples.ca_handler.digicert_ca_handler.b64_encode') - def test_038_order_response_parse(self, mock_b64, mock_pem2der): + def test_042_order_response_parse(self, mock_b64, mock_pem2der): """ test _order_parse() """ content_dic = {'id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} mock_b64.return_value = 'b64' @@ -574,7 +574,7 @@ def test_038_order_response_parse(self, mock_b64, mock_pem2der): @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') @patch('examples.ca_handler.digicert_ca_handler.b64_encode') - def test_039_order_response_parse(self, mock_b64, mock_pem2der): + def test_043_order_response_parse(self, mock_b64, mock_pem2der): """ test _order_parse() """ content_dic = {'id': 'id', 'cert_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} mock_b64.return_value = 'b64' @@ -584,7 +584,7 @@ def test_039_order_response_parse(self, mock_b64, mock_pem2der): @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') @patch('examples.ca_handler.digicert_ca_handler.b64_encode') - def test_040_order_response_parse(self, mock_b64, mock_pem2der): + def test_044_order_response_parse(self, mock_b64, mock_pem2der): """ test _order_parse() """ content_dic = {'id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'_pem': 'pem2'}, {'pem': 'pem3'}]} mock_b64.return_value = 'b64' @@ -594,7 +594,7 @@ def test_040_order_response_parse(self, mock_b64, mock_pem2der): @patch('examples.ca_handler.digicert_ca_handler.cert_pem2der') @patch('examples.ca_handler.digicert_ca_handler.b64_encode') - def test_041_order_response_parse(self, mock_b64, mock_pem2der): + def test_045_order_response_parse(self, mock_b64, mock_pem2der): """ test _order_parse() """ content_dic = {'_id': 'id', 'certificate_chain': [{'pem': 'pem1'}, {'pem': 'pem2'}, {'pem': 'pem3'}]} mock_b64.return_value = 'b64' @@ -603,7 +603,7 @@ def test_041_order_response_parse(self, mock_b64, mock_pem2der): self.assertIn('ERROR:test_a2c:CAhandler._order_response_parse() polling_identifier generation failed: no id in response', lcm.output) @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') - def test_042_organiation_id_get(self, mock_get): + def test_046_organiation_id_get(self, mock_get): """ test _organiation_id_get() """ mock_get.return_value = (500, {'id': 'id'}) self.cahandler.organization_name = 'organization_name' @@ -613,14 +613,14 @@ def test_042_organiation_id_get(self, mock_get): self.assertFalse(self.cahandler.organization_id) @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') - def test_043_organiation_id_get(self, mock_get): + def test_047_organiation_id_get(self, mock_get): """ test _organiation_id_get() """ mock_get.return_value = (200, {'organizations': [{'name': 'name1', 'id': 'id1'}, {'name': 'name2', 'id': 'id2'}, {'name': 'name3', 'id': 'id3'}]}) self.cahandler.organization_name = 'name1' self.assertEqual('id1', self.cahandler._organiation_id_get()) @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_get') - def test_044_organiation_id_get(self, mock_get): + def test_048_organiation_id_get(self, mock_get): """ test _organiation_id_get() """ mock_get.return_value = (200, {'organizations': [{'name': 'name1', 'id': 'id1'}, {'name': 'name2', 'id': 'id2'}, {'name': 'name3', 'id': 'id3'}]}) self.cahandler.organization_name = 'name2' @@ -628,7 +628,7 @@ def test_044_organiation_id_get(self, mock_get): @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') - def test_045_csr_check(self, mock_dlchk, mock_ehichk): + def test_049_csr_check(self, mock_dlchk, mock_ehichk): """ test _csr_check() """ mock_dlchk.return_value = 'mock_dlchk' mock_ehichk.return_value = 'mock_hichk' @@ -637,7 +637,7 @@ def test_045_csr_check(self, mock_dlchk, mock_ehichk): @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') - def test_046_csr_check(self, mock_dlchk, mock_ehichk): + def test_050_csr_check(self, mock_dlchk, mock_ehichk): """ test _csr_check() """ mock_dlchk.return_value = False mock_ehichk.return_value = 'mock_hichk' @@ -645,7 +645,7 @@ def test_046_csr_check(self, mock_dlchk, mock_ehichk): @patch('examples.ca_handler.digicert_ca_handler.eab_profile_header_info_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._allowed_domainlist_check') - def test_047_csr_check(self, mock_dlchk, mock_ehichk): + def test_051_csr_check(self, mock_dlchk, mock_ehichk): """ test _csr_check() """ mock_dlchk.return_value = False mock_ehichk.return_value = False @@ -656,7 +656,7 @@ def test_047_csr_check(self, mock_dlchk, mock_ehichk): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') - def test_048_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + def test_052_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): """ test enroll() """ mock_cfgchk.return_value = 'mock_cfgchk' mock_csrchk.return_value = 'mock_csrchk' @@ -675,7 +675,7 @@ def test_048_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') - def test_049_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + def test_053_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): """ test enroll() """ mock_cfgchk.return_value = False mock_csrchk.return_value = 'mock_csrchk' @@ -694,7 +694,7 @@ def test_049_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') - def test_050_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + def test_054_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): """ test enroll() """ mock_cfgchk.return_value = False mock_csrchk.return_value = False @@ -713,7 +713,7 @@ def test_050_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') - def test_051_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + def test_055_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): """ test enroll() """ mock_cfgchk.return_value = False mock_csrchk.return_value = False @@ -732,7 +732,7 @@ def test_051_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_cn_lookup') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._csr_check') @patch('examples.ca_handler.digicert_ca_handler.CAhandler._config_check') - def test_052enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): + def test_056_enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, mock_orderparse): """ test enroll() """ mock_cfgchk.return_value = False mock_csrchk.return_value = False @@ -748,7 +748,7 @@ def test_052enroll(self, mock_cfgchk, mock_csrchk, mock_cnget, mock_ordersend, m @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_put') @patch('examples.ca_handler.digicert_ca_handler.cert_serial_get') - def test_053_revoke(self, mock_serial, mock_put): + def test_057_revoke(self, mock_serial, mock_put): """ test revoke() """ mock_serial.return_value = 'serial' mock_put.return_value = ('code', 'content') @@ -756,7 +756,7 @@ def test_053_revoke(self, mock_serial, mock_put): @patch('examples.ca_handler.digicert_ca_handler.CAhandler._api_put') @patch('examples.ca_handler.digicert_ca_handler.cert_serial_get') - def test_054_revoke(self, mock_serial, mock_put): + def test_058_revoke(self, mock_serial, mock_put): """ test revoke() """ mock_serial.return_value = None mock_put.return_value = ('code', 'content') @@ -764,7 +764,7 @@ def test_054_revoke(self, mock_serial, mock_put): @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') - def test_055__csr_cn_lookup(self, mock_cnget, mock_san_get): + def test_059__csr_cn_lookup(self, mock_cnget, mock_san_get): """ test _csr_cn_lookup() """ mock_cnget.return_value = 'cn' mock_san_get.return_value = ['foo:san1', 'foo:san2'] @@ -772,7 +772,7 @@ def test_055__csr_cn_lookup(self, mock_cnget, mock_san_get): @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') - def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): + def test_060__csr_cn_lookup(self, mock_cnget, mock_san_get): """ test _csr_cn_lookup() """ mock_cnget.return_value = None mock_san_get.return_value = ['foo:san1', 'foo:san2'] @@ -780,7 +780,7 @@ def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') - def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): + def test_061__csr_cn_lookup(self, mock_cnget, mock_san_get): """ test _csr_cn_lookup() """ mock_cnget.return_value = None mock_san_get.return_value = ['foosan1', 'foo:san2'] @@ -790,7 +790,7 @@ def test_056__csr_cn_lookup(self, mock_cnget, mock_san_get): @patch('examples.ca_handler.digicert_ca_handler.csr_san_get') @patch('examples.ca_handler.digicert_ca_handler.csr_cn_get') - def test_057__csr_cn_lookup(self, mock_cnget, mock_san_get): + def test_062__csr_cn_lookup(self, mock_cnget, mock_san_get): """ test _csr_cn_lookup() """ mock_cnget.return_value = None mock_san_get.return_value = None diff --git a/test/test_entrust.py b/test/test_entrust.py new file mode 100644 index 00000000..8a6301ce --- /dev/null +++ b/test/test_entrust.py @@ -0,0 +1,1191 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" unittests for openxpki_ca_handler """ +# pylint: disable=C0415, R0904, W0212 +import sys +import os +import unittest +from unittest.mock import patch, mock_open, Mock, MagicMock +import requests +import base64 +from OpenSSL import crypto + +sys.path.insert(0, '.') +sys.path.insert(1, '..') + +class FakeDBStore(object): + """ face DBStore class needed for mocking """ + # pylint: disable=W0107, R0903 + pass + +class TestACMEHandler(unittest.TestCase): + """ test class for cgi_handler """ + + def setUp(self): + """ setup unittest """ + models_mock = MagicMock() + models_mock.acme_srv.db_handler.DBstore.return_value = FakeDBStore + modules = {'acme_srv.db_handler': models_mock} + patch.dict('sys.modules', modules).start() + import logging + from examples.ca_handler.entrust_ca_handler import CAhandler + logging.basicConfig(level=logging.CRITICAL) + self.logger = logging.getLogger('test_a2c') + self.cahandler = CAhandler(False, self.logger) + self.dir_path = os.path.dirname(os.path.realpath(__file__)) + + def test_001_default(self): + """ default test which always passes """ + self.assertEqual('foo', 'foo') + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_load') + def test_002__enter__(self, mock_cfg): + """ test enter called """ + mock_cfg.return_value = True + self.cahandler.__enter__() + self.assertTrue(mock_cfg.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_load') + def test_003__enter__(self, mock_cfg): + """ test enter api hosts defined """ + mock_cfg.return_value = True + self.cahandler.session = 'session' + self.cahandler.__enter__() + self.assertFalse(mock_cfg.called) + + def test_004_poll(self): + """ test polling """ + self.assertEqual(('Method not implemented.', None, None, 'poll_identifier', False), self.cahandler.poll('cert_name', 'poll_identifier', 'csr')) + + def test_005_trigger(self): + """ test polling """ + self.assertEqual(('Method not implemented.', None, None), self.cahandler.trigger('payload')) + + @patch('examples.ca_handler.entrust_ca_handler.allowed_domainlist_check') + def test_006_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = False + self.assertFalse(self.cahandler._allowed_domainlist_check('csr')) + self.assertFalse(mock_adc.called) + + @patch('examples.ca_handler.entrust_ca_handler.allowed_domainlist_check') + def test_007_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = ["test.com"] + mock_adc.return_value = True + self.assertFalse(self.cahandler._allowed_domainlist_check('csr')) + self.assertTrue(mock_adc.called) + + @patch('examples.ca_handler.entrust_ca_handler.allowed_domainlist_check') + def test_008_allowed_domainlist_check(self, mock_adc): + """ test allowed_domainlist_check """ + self.cahandler.allowed_domainlist = ["test.com"] + mock_adc.return_value = False + self.assertEqual('Either CN or SANs are not allowed by configuration', self.cahandler._allowed_domainlist_check('csr')) + self.assertTrue(mock_adc.called) + + def test_009__api_post(self): + """ test _api_post() """ + mockresponse = Mock() + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json = lambda: {'foo': 'bar'} + mockresponse = Mock() + mockresponse.post.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_post('url', 'data')) + + def test_010__api_post(self): + """ test _api_post() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json.side_effect = Exception('ex_json') + mockresponse = Mock() + mockresponse.post.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', 'ex_json'), self.cahandler._api_post('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_post() returned error during json parsing: ex_json', lcm.output) + + def test_011__api_post(self): + """ test _api_post() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = None + mockresponse2.json = lambda: {'foo': 'bar'} + mockresponse = Mock() + mockresponse.post.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + self.assertEqual(('status_code', None), self.cahandler._api_post('url', 'data')) + + def test_012__api_post(self): + """ test _api_post(= """ + mockresponse = Mock() + mockresponse.post.side_effect = [Exception('exc_api_post')] + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_post'), self.cahandler._api_post('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_post() returned error: exc_api_post', lcm.output) + + def test_013__api_get(self): + """ test _api_get() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json = lambda: {'foo': 'bar'} + mockresponse = Mock() + mockresponse.get.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_get('url')) + + def test_014__api_get(self): + """ test _api_get() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json.side_effect = Exception('ex_json') + mockresponse = Mock() + mockresponse.get.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', 'ex_json'), self.cahandler._api_get('url')) + self.assertIn('ERROR:test_a2c:CAhandler._api_get() returned error during json parsing: ex_json', lcm.output) + + def test_015__api_get(self): + """ test _api_get() """ + mockresponse = Mock() + mockresponse.get.side_effect = [Exception('exc_api_get')] + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_get'), self.cahandler._api_get('url')) + self.assertIn('ERROR:test_a2c:CAhandler._api_get() returned error: exc_api_get', lcm.output) + + def test_016__api_put(self): + """ test _api_put() """ + mockresponse = Mock() + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json = lambda: {'foo': 'bar'} + mockresponse = Mock() + mockresponse.put.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + self.assertEqual(('status_code', {'foo': 'bar'}), self.cahandler._api_put('url', 'data')) + + def test_017__api_put(self): + """ test _api_put() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'status_code' + mockresponse2.text = 'foo' + mockresponse2.json.side_effect = Exception('ex_json') + mockresponse = Mock() + mockresponse.put.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(('status_code', 'ex_json'), self.cahandler._api_put('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_put() returned error during json parsing: ex_json', lcm.output) + + def test_018__api_put(self): + """ test _api_put() """ + mockresponse2 = Mock() + mockresponse2.status_code = 'foo' + mockresponse2.text = None + mockresponse2.json = lambda: {'foo': 'bar'} + mockresponse = Mock() + mockresponse.put.side_effect = [mockresponse2] + self.cahandler.session = mockresponse + self.assertEqual(('foo', None), self.cahandler._api_put('url', 'data')) + + def test_019__api_put(self): + """ test _api_put() """ + mockresponse = Mock() + mockresponse.put.side_effect = Exception('exc_api_put') + self.cahandler.session = mockresponse + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual((500, 'exc_api_put'), self.cahandler._api_put('url', 'data')) + self.assertIn('ERROR:test_a2c:CAhandler._api_put() returned error: exc_api_put', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_020_certificates_get_from_serial(self, mock_api): + """ test certificates_get_from_serial """ + mock_api.return_value = (200, {'certificates': ['foo', 'bar']}) + self.assertEqual(['foo', 'bar'], self.cahandler._certificates_get_from_serial('serial')) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_021_certificates_get_from_serial(self, mock_api): + """ test certificates_get_from_serial """ + mock_api.return_value = (300, {'certificates': ['foo', 'bar']}) + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler._certificates_get_from_serial('serial')) + self.assertIn('ERROR:test_a2c:CAhandler._certificates_get_from_serial() for serial failed with code: 300', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_022_certificates_get_from_serial(self, mock_api): + """ test certificates_get_from_serial """ + mock_api.return_value = (200, {'certificates1': ['foo', 'bar']}) + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler._certificates_get_from_serial('serial')) + self.assertIn('ERROR:test_a2c:CAhandler._certificates_get_from_serial() for serial failed with code: 200', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_023_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'foo': 'bar'} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertFalse(mock_session.called) + self.assertFalse(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_024_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_025_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'api_url': 'api_url', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('api_url', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_026_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'request_timeout': '15', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(15, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_027_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'request_timeout': 'aa', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_load() + self.assertIn("ERROR:test_a2c:CAhandler._config_load(): failed to parse request_timeout invalid literal for int() with base 10: 'aa'", lcm.output) + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_028_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'cert_validity_days': '10', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(10, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_029_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'cert_validity_days': 'aa', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_load() + self.assertIn("ERROR:test_a2c:CAhandler._config_load(): failed to parse cert_validity_days invalid literal for int() with base 10: 'aa'", lcm.output) + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_030_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'username': 'username', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertEqual('username', self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_031_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'password': 'password', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertEqual('password', self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_032_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'organization_name': 'organization_name', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertEqual('organization_name', self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_033_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'certtype': 'certtype', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('certtype', self.cahandler.certtype) + self.assertFalse(self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_034_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'allowed_domainlist': '["foo", "bar"]', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertEqual(['foo', 'bar'], self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') + @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_session_load') + @patch('examples.ca_handler.entrust_ca_handler.load_config') + def test_035_config_load(self, mock_load, mock_session, mock_root, mock_eab, mock_header): + """ test load_config() """ + mock_load.return_value = {'CAhandler': {'allowed_domainlist': 'bar', 'foo': 'bar'}} + mock_eab.return_value = (True, 'handler') + mock_header.return_value = 'hil' + self.cahandler._config_load() + self.assertTrue(mock_session.called) + self.assertTrue(mock_root.called) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_header.called) + self.assertEqual('https://api.entrust.net/enterprise/v2', self.cahandler.api_url) + self.assertEqual(10, self.cahandler.request_timeout) + self.assertEqual('handler', self.cahandler.eab_handler) + self.assertTrue(self.cahandler.eab_profiling) + self.assertEqual(365, self.cahandler.cert_validity_days) + self.assertFalse(self.cahandler.username) + self.assertFalse(self.cahandler.password) + self.assertFalse(self.cahandler.organization_name) + self.assertEqual('STANDARD_SSL', self.cahandler.certtype) + self.assertEqual('failed to parse', self.cahandler.allowed_domainlist) + self.assertEqual('hil', self.cahandler.header_info_field) + + @patch.dict('os.environ', {'cert_passphrase_var': 'user_var'}) + def test_036_config_passphrase_load(self): + """ test _config_load - load template with user variable """ + config_dic = {'CAhandler': {'cert_passphrase_variable': 'cert_passphrase_var'}} + self.cahandler._config_passphrase_load(config_dic) + self.assertEqual('user_var', self.cahandler.cert_passphrase) + + @patch.dict('os.environ', {'cert_passphrase_var': 'user_var'}) + def test_037_config_passphrase_load(self): + """ test _config_load - load template with user variable """ + config_dic = {'CAhandler': {'cert_passphrase_variable': 'does_not_exist'}} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_passphrase_load(config_dic) + self.assertFalse(self.cahandler.cert_passphrase) + self.assertIn("ERROR:test_a2c:CAhandler._config_passphrase_load() could not load cert_passphrase_variable:'does_not_exist'", lcm.output) + + @patch.dict('os.environ', {'cert_passphrase_var': 'user_var'}) + def test_038_config_passphrase_load(self): + """ test _config_load - load template with user variable """ + config_dic = {'CAhandler': {'cert_passphrase_variable': 'cert_passphrase_var', 'cert_passphrase': 'cert_passphrase'}} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_passphrase_load(config_dic) + self.assertIn('INFO:test_a2c:CAhandler._config_load() overwrite cert_passphrase', lcm.output) + self.assertEqual('cert_passphrase', self.cahandler.cert_passphrase) + + @patch("builtins.open", mock_open(read_data='cert'), create=True) + @patch('os.path.isfile') + def test_039_config_root_load(self, mock_file): + """ _config_root_load() """ + mock_file.return_value = True + config_dic = {'CAhandler': {'entrust_root_cert': 'root_cert'}} + self.cahandler._config_root_load(config_dic) + self.assertEqual('cert', self.cahandler.entrust_root_cert) + + @patch("builtins.open", mock_open(read_data='cert'), create=True) + @patch('os.path.isfile') + def test_040_config_root_load(self, mock_file): + """ _config_root_load() """ + mock_file.return_value = False + config_dic = {'CAhandler': {'entrust_root_cert': 'root_cert'}} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_root_load(config_dic) + self.assertIn('ERROR:test_a2c:CAhandler._config_root_load(): root CA file configured but not not found. Using default one.', lcm.output) + self.assertIn('290IENlcnRpZmljYXRpb24g', self.cahandler.entrust_root_cert) + + @patch("builtins.open", mock_open(read_data='cert'), create=True) + @patch('os.path.isfile') + def test_041_config_root_load(self, mock_file): + """ _config_root_load() """ + mock_file.return_value = False + config_dic = {'CAhandler': {'unk': 'root_cert'}} + self.cahandler._config_root_load(config_dic) + self.assertIn('290IENlcnRpZmljYXRpb24g', self.cahandler.entrust_root_cert) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_passphrase_load') + def test_042_config_session_load(self, mock_sl): + """ _config_session_load() """ + config_dic = {'CAhandler': {'client_cert': 'client_cert', 'client_key': 'client_key'}} + self.cahandler._config_session_load(config_dic) + self.assertFalse(mock_sl.called) + self.assertTrue(self.cahandler.session) + + @patch('examples.ca_handler.entrust_ca_handler.requests.Session') + @patch('examples.ca_handler.entrust_ca_handler.Pkcs12Adapter') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_passphrase_load') + def test_043_config_session_load(self, mock_sl, mock_pkcs12, mock_session): + """ _config_session_load() """ + config_dic = {'CAhandler': {'client_cert': 'client_cert'}} + mock_session.return_value.__enter__.return_value = Mock() + self.cahandler.cert_passphrase = 'cert_passphrase' + self.cahandler._config_session_load(config_dic) + self.assertTrue(mock_sl.called) + self.assertTrue(self.cahandler.session) + self.assertTrue(mock_pkcs12.called) + + @patch('examples.ca_handler.entrust_ca_handler.requests.Session') + @patch('examples.ca_handler.entrust_ca_handler.Pkcs12Adapter') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_passphrase_load') + def test_044_config_session_load(self, mock_sl, mock_pkcs12, mock_session): + """ _config_session_load() """ + config_dic = {'CAhandler': {'foo': 'client_cert'}} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_session_load(config_dic) + self.assertTrue(mock_sl.called) + self.assertTrue(self.cahandler.session) + self.assertIn('ERROR:test_a2c:CAhandler._config_load() configuration incomplete: either "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file', lcm.output) + self.assertFalse(mock_pkcs12.called) + + @patch('examples.ca_handler.entrust_ca_handler.csr_san_get') + @patch('examples.ca_handler.entrust_ca_handler.csr_cn_get') + def test_045__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = 'cn' + mock_san_get.return_value = ['foo:san1', 'foo:san2'] + self.assertEqual('cn', self.cahandler._csr_cn_lookup('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.csr_san_get') + @patch('examples.ca_handler.entrust_ca_handler.csr_cn_get') + def test_046__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = ['foo:san1', 'foo:san2'] + self.assertEqual('san1', self.cahandler._csr_cn_lookup('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.csr_san_get') + @patch('examples.ca_handler.entrust_ca_handler.csr_cn_get') + def test_047__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = ['foosan1', 'foo:san2'] + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual('san2', self.cahandler._csr_cn_lookup('csr')) + self.assertIn('ERROR:test_a2c:CAhandler._csr_cn_lookup() split failed: list index out of range', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.csr_san_get') + @patch('examples.ca_handler.entrust_ca_handler.csr_cn_get') + def test_048__csr_cn_lookup(self, mock_cnget, mock_san_get): + """ test _csr_cn_lookup() """ + mock_cnget.return_value = None + mock_san_get.return_value = None + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler._csr_cn_lookup('csr')) + self.assertIn('ERROR:test_a2c:CAhandler._csr_cn_lookup() no SANs found in CSR', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._domains_get') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._organizations_get') + def test_049_org_domain_cfg_check(self, mock_org, mock_domain): + """ test _org_domain_cfg_check()""" + mock_org.return_value = [] + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual('Organization None not found in Entrust API', self.cahandler._org_domain_cfg_check()) + self.assertTrue(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._domains_get') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._organizations_get') + def test_050_org_domain_cfg_check(self, mock_org, mock_domain): + """ test _org_domain_cfg_check()""" + mock_org.return_value = {'foo': 1, 'bar': 2} + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual('Organization None not found in Entrust API', self.cahandler._org_domain_cfg_check()) + self.assertTrue(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._domains_get') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._organizations_get') + def test_051_org_domain_cfg_check(self, mock_org, mock_domain): + """ test _org_domain_cfg_check()""" + mock_org.return_value = {'foo': 1, 'bar': 2} + mock_domain.return_value = ['foo1', 'foo2'] + self.cahandler.organization_name = 'foo1' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual('Organization foo1 not found in Entrust API', self.cahandler._org_domain_cfg_check()) + self.assertTrue(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._domains_get') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._organizations_get') + def test_052_org_domain_cfg_check(self, mock_org, mock_domain): + """ test _org_domain_cfg_check()""" + mock_org.return_value = {'foo': 1, 'bar': 2} + mock_domain.return_value = ['foo1', 'foo2'] + self.cahandler.organization_name = 'foo' + self.assertFalse(self.cahandler._org_domain_cfg_check()) + self.assertTrue(mock_org.called) + self.assertTrue(mock_domain.called) + self.assertEqual(['foo1', 'foo2'], self.cahandler.allowed_domainlist) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._domains_get') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._organizations_get') + def test_053_org_domain_cfg_check(self, mock_org, mock_domain): + """ test _org_domain_cfg_check()""" + mock_org.return_value = {'foo': 1, 'bar': 2} + mock_domain.return_value = ['foo1', 'foo2'] + self.cahandler.organization_name = 'foo' + self.cahandler.allowed_domainlist = ['foo3', 'foo4'] + self.assertFalse(self.cahandler._org_domain_cfg_check()) + self.assertTrue(mock_org.called) + self.assertTrue(mock_domain.called) + self.assertEqual(['foo3', 'foo4'], self.cahandler.allowed_domainlist) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_054__organizations_get(self, mock_api): + """ test _organizations_get() """ + mock_api.return_value = (500, 'foo') + self.cahandler.organization_name = 'organization_name' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._organizations_get() + self.assertIn('ERROR:test_a2c:CAhandler._organizations_get(): malformed response', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_055__organizations_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'organizations': [{'verificationStatus': 'APPROVED', 'name': 'foo', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'name': 'bar', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.cahandler.organization_name = 'organization_name' + self.assertEqual({'foo': 1, 'bar': 2}, self.cahandler._organizations_get()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_056__organizations_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'organizations': [{'verificationStatus': 'NOTAPPROVED', 'name': 'foo', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'name': 'bar', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.cahandler.organization_name = 'organization_name' + self.assertEqual({'bar': 2}, self.cahandler._organizations_get()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_057__organizations_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'organizations': [{'name': 'foo', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'name': 'bar', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.cahandler.organization_name = 'organization_name' + self.assertEqual({'bar': 2}, self.cahandler._organizations_get()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_058__organizations_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'organizations': [{'verificationStatus': 'APPROVED', '_name': 'foo', '_clientId': 1}, {'verificationStatus': 'APPROVED', 'name': 'bar', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.cahandler.organization_name = 'organization_name' + self.assertEqual({'bar': 2}, self.cahandler._organizations_get()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_059__domains_get(self, mock_api): + """ test _organizations_get() """ + mock_api.return_value = (500, 'foo') + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._domains_get(1) + self.assertIn('ERROR:test_a2c:CAhandler._domains_get(): malformed response', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_060__domains_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'domains': [{'verificationStatus': 'APPROVED', 'domainName': 'foo.bar', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'domainName': 'bar.foo', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.assertEqual(['foo.bar', 'bar.foo'], self.cahandler._domains_get(1)) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_061__domains_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'domains': [{'verificationStatus': 'NOTAPPROVED', 'domainName': 'foo.bar', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'domainName': 'bar.foo', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.assertEqual(['bar.foo'], self.cahandler._domains_get(1)) + + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_062__domains_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'domains': [{'Status': 'APPROVED', 'domainName': 'foo.bar', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'domainName': 'bar.foo', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.assertEqual(['bar.foo'], self.cahandler._domains_get(1)) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_063__domains_get(self, mock_api): + """ test _organizations_get() """ + input_dic = {'domains': [{'verificationStatus': 'APPROVED', 'Name': 'foo.bar', 'clientId': 1}, {'verificationStatus': 'APPROVED', 'domainName': 'bar.foo', 'clientId': 2}]} + mock_api.return_value = (200, input_dic) + self.assertEqual(['bar.foo'], self.cahandler._domains_get(1)) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_064__credential_check(self, mock_api): + """ test _organizations_get() """ + mock_api.return_value = (500, 'foo') + self.assertEqual('Connection to Entrust API failed: foo', self.cahandler._credential_check()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_065__credential_check(self, mock_api): + """ test _organizations_get() """ + mock_api.return_value = (200, 'foo') + self.assertFalse(self.cahandler._credential_check()) + + def test_066_oonfig_check(self): + """ test _config_check() """ + self.cahandler.api_url = None + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_check() + self.assertIn('ERROR:test_a2c:CAhandler._config_check() ended with error: api_url parameter in missing in config file', lcm.output) + + def test_067_oonfig_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_check() + self.assertIn('ERROR:test_a2c:CAhandler._config_check() ended with error: username parameter in missing in config file', lcm.output) + + def test_068_oonfig_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.cahandler.username = 'username' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_check() + self.assertIn('ERROR:test_a2c:CAhandler._config_check() ended with error: password parameter in missing in config file', lcm.output) + + def test_069_oonfig_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.cahandler.username = 'username' + self.cahandler.password = 'password' + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.cahandler._config_check() + self.assertIn('ERROR:test_a2c:CAhandler._config_check() ended with error: organization_name parameter in missing in config file', lcm.output) + + def test_070_oonfig_check(self): + """ test _config_check() """ + self.cahandler.api_url = 'api_url' + self.cahandler.username = 'username' + self.cahandler.password = 'password' + self.cahandler.organization_name = 'organization_name' + self.assertFalse(self.cahandler._config_check()) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') + @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') + def test_071_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): + """ test _enroll_check() """ + mock_eab.return_value = 'mock_eab_error' + mock_config.return_value = 'mock_config_error' + mock_credential.return_value = 'mock_credential_error' + mock_org.return_value = 'mock_org_error' + mock_domain.return_value = 'mock_domain_error' + self.assertEqual('mock_eab_error', self.cahandler._enroll_check('csr')) + self.assertTrue(mock_eab.called) + self.assertFalse(mock_config.called) + self.assertFalse(mock_credential.called) + self.assertFalse(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') + @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') + def test_072_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): + """ test _enroll_check() """ + mock_eab.return_value = False + mock_config.return_value = 'mock_config_error' + mock_credential.return_value = 'mock_credential_error' + mock_org.return_value = 'mock_org_error' + mock_domain.return_value = 'mock_domain_error' + self.assertEqual('mock_config_error', self.cahandler._enroll_check('csr')) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_config.called) + self.assertFalse(mock_credential.called) + self.assertFalse(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') + @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') + def test_073_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): + """ test _enroll_check() """ + mock_eab.return_value = False + mock_config.return_value = False + mock_credential.return_value = 'mock_credential_error' + mock_org.return_value = 'mock_org_error' + mock_domain.return_value = 'mock_domain_error' + self.assertEqual('mock_credential_error', self.cahandler._enroll_check('csr')) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_config.called) + self.assertTrue(mock_credential.called) + self.assertFalse(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') + @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') + def test_074_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): + """ test _enroll_check() """ + mock_eab.return_value = False + mock_config.return_value = False + mock_credential.return_value = False + mock_org.return_value = 'mock_org_error' + mock_domain.return_value = 'mock_domain_error' + self.assertEqual('mock_org_error', self.cahandler._enroll_check('csr')) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_config.called) + self.assertTrue(mock_credential.called) + self.assertTrue(mock_org.called) + self.assertFalse(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') + @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') + def test_075_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): + """ test _enroll_check() """ + mock_eab.return_value = False + mock_config.return_value = False + mock_credential.return_value = False + mock_org.return_value = False + mock_domain.return_value = 'mock_domain_error' + self.assertEqual('mock_domain_error', self.cahandler._enroll_check('csr')) + self.assertTrue(mock_eab.called) + self.assertTrue(mock_config.called) + self.assertTrue(mock_credential.called) + self.assertTrue(mock_org.called) + self.assertTrue(mock_domain.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._certificates_get_from_serial') + @patch('examples.ca_handler.entrust_ca_handler.cert_serial_get') + @patch('examples.ca_handler.entrust_ca_handler.header_info_get') + def test_076__trackingid_get(self, mock_header, mock_serial, mock_cert): + """ test _trackingid_get() """ + mock_header.return_value = [{'poll_identifier': 'tracking_id'}, {'poll_identifier': 'tracking_id2'}] + self.assertEqual('tracking_id', self.cahandler._trackingid_get('csr')) + self.assertFalse(mock_serial.called) + self.assertFalse(mock_cert.called) + + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._certificates_get_from_serial') + @patch('examples.ca_handler.entrust_ca_handler.cert_serial_get') + @patch('examples.ca_handler.entrust_ca_handler.header_info_get') + def test_077__trackingid_get(self, mock_header, mock_serial, mock_cert): + """ test _trackingid_get() """ + mock_header.return_value = [{'identifier': 'tracking_id1'}, {'poll_identifier': 'tracking_id2'}] + self.assertEqual('tracking_id2', self.cahandler._trackingid_get('csr')) + self.assertFalse(mock_serial.called) + self.assertFalse(mock_cert.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._certificates_get_from_serial') + @patch('examples.ca_handler.entrust_ca_handler.cert_serial_get') + @patch('examples.ca_handler.entrust_ca_handler.header_info_get') + def test_078__trackingid_get(self, mock_header, mock_serial, mock_cert): + """ test _trackingid_get() """ + mock_header.return_value = [] + mock_serial.return_value = 'serial' + mock_cert.return_value = [{'trackingId': 'tracking_id'}] + self.assertEqual('tracking_id', self.cahandler._trackingid_get('csr')) + self.assertTrue(mock_serial.called) + self.assertTrue(mock_cert.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._certificates_get_from_serial') + @patch('examples.ca_handler.entrust_ca_handler.cert_serial_get') + @patch('examples.ca_handler.entrust_ca_handler.header_info_get') + def test_079__trackingid_get(self, mock_header, mock_serial, mock_cert): + """ test _trackingid_get() """ + mock_header.return_value = [] + mock_serial.return_value = 'serial' + mock_cert.return_value = [{'id': 'tracking_id'}] + self.assertFalse(self.cahandler._trackingid_get('csr')) + self.assertTrue(mock_serial.called) + self.assertTrue(mock_cert.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._certificates_get_from_serial') + @patch('examples.ca_handler.entrust_ca_handler.cert_serial_get') + @patch('examples.ca_handler.entrust_ca_handler.header_info_get') + def test_080__trackingid_get(self, mock_header, mock_serial, mock_cert): + """ test _trackingid_get() """ + mock_header.return_value = [] + mock_serial.return_value = 'serial' + mock_cert.return_value = [{'id': 'tracking_id1'}, {'trackingId': 'tracking_id2'}] + self.assertEqual('tracking_id2', self.cahandler._trackingid_get('csr')) + self.assertTrue(mock_serial.called) + self.assertTrue(mock_cert.called) + + @patch('examples.ca_handler.entrust_ca_handler.b64_encode') + @patch('examples.ca_handler.entrust_ca_handler.cert_pem2der') + def test_081_response_parse(self, mock_der, mock_enc): + """ test _rsponse_parse() """ + mock_der.return_value = 'cert_data' + mock_enc.return_value = 'mock_enc' + self.cahandler.entrust_root_cert = 'root_cert' + response = {"trackingId": "trackingId", 'endEntityCert': 'endEntityCert', 'chainCerts': ['foo1', 'foo2']} + self.assertEqual(('foo1\nfoo2\nroot_cert\n', 'mock_enc', 'trackingId'), self.cahandler._response_parse(response)) + self.assertTrue(mock_der.called) + self.assertTrue(mock_enc.called) + + @patch('examples.ca_handler.entrust_ca_handler.b64_encode') + @patch('examples.ca_handler.entrust_ca_handler.cert_pem2der') + def test_082_response_parse(self, mock_der, mock_enc): + """ test _rsponse_parse() """ + mock_der.return_value = 'cert_data' + mock_enc.return_value = 'mock_enc' + self.cahandler.entrust_root_cert = 'root_cert' + response = {"falsetrackingId": "trackingId", 'endEntityCert': 'endEntityCert', 'chainCerts': ['foo1', 'foo2']} + self.assertEqual(('foo1\nfoo2\nroot_cert\n', 'mock_enc', None), self.cahandler._response_parse(response)) + self.assertTrue(mock_der.called) + self.assertTrue(mock_enc.called) + + @patch('examples.ca_handler.entrust_ca_handler.b64_encode') + @patch('examples.ca_handler.entrust_ca_handler.cert_pem2der') + def test_083_response_parse(self, mock_der, mock_enc): + """ test _rsponse_parse() """ + mock_der.return_value = 'cert_data' + mock_enc.return_value = 'mock_enc' + self.cahandler.entrust_root_cert = 'root_cert' + response = {"trackingId": "trackingId", 'endEntityCert': 'endEntityCert', 'Certs': ['foo1', 'foo2']} + self.assertEqual((None, None, 'trackingId'), self.cahandler._response_parse(response)) + self.assertFalse(mock_der.called) + self.assertFalse(mock_enc.called) + + @patch('examples.ca_handler.entrust_ca_handler.b64_encode') + @patch('examples.ca_handler.entrust_ca_handler.cert_pem2der') + def test_084_response_parse(self, mock_der, mock_enc): + """ test _rsponse_parse() """ + mock_der.return_value = 'cert_data' + mock_enc.return_value = 'mock_enc' + self.cahandler.entrust_root_cert = 'root_cert' + response = {"trackingId": "trackingId", 'EntityCert': 'endEntityCert', 'chainCerts': ['foo1', 'foo2']} + self.assertEqual((None, None, 'trackingId'), self.cahandler._response_parse(response)) + self.assertFalse(mock_der.called) + self.assertFalse(mock_enc.called) + + @patch('examples.ca_handler.entrust_ca_handler.b64_encode') + @patch('examples.ca_handler.entrust_ca_handler.cert_pem2der') + def test_085_response_parse(self, mock_der, mock_enc): + """ test _rsponse_parse() """ + mock_der.return_value = 'cert_data' + mock_enc.return_value = 'mock_enc' + self.cahandler.entrust_root_cert = 'root_cert' + response = {"trackingId": "trackingId", 'endEntityCert': 'endEntityCert', 'chainCerts': []} + self.assertEqual(('root_cert\n', 'mock_enc', 'trackingId'), self.cahandler._response_parse(response)) + self.assertTrue(mock_der.called) + self.assertTrue(mock_enc.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._response_parse') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._csr_cn_lookup') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + def test_086_enroll(self, mock_req, mock_cn, mock_parse): + """ test _enroll() """ + mock_cn.return_value = 'cn' + mock_req.return_value = (201, 'response') + mock_parse.return_value = ('cert_bundle', 'cert_raw', 'poll_indentifier') + self.assertEqual((None, 'cert_bundle', 'cert_raw', 'poll_indentifier'), self.cahandler._enroll('csr')) + self.assertTrue(mock_cn.called) + self.assertTrue(mock_req.called) + self.assertTrue(mock_parse.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._response_parse') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._csr_cn_lookup') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + def test_087_enroll(self, mock_req, mock_cn, mock_parse): + """ test _enroll() """ + mock_cn.return_value = 'cn' + mock_req.return_value = (404, 'response') + mock_parse.return_value = ('cert_bundle', 'cert_raw', 'poll_indentifier') + self.assertEqual(('Error during order creation: 404 - response', None, None, None), self.cahandler._enroll('csr')) + self.assertTrue(mock_cn.called) + self.assertTrue(mock_req.called) + self.assertFalse(mock_parse.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._response_parse') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._csr_cn_lookup') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + def test_088_enroll(self, mock_req, mock_cn, mock_parse): + """ test _enroll() """ + mock_cn.return_value = 'cn' + mock_req.return_value = (404, {'errors': 'response, response2'}) + mock_parse.return_value = ('cert_bundle', 'cert_raw', 'poll_indentifier') + self.assertEqual(('Error during order creation: 404 - response, response2', None, None, None), self.cahandler._enroll('csr')) + self.assertTrue(mock_cn.called) + self.assertTrue(mock_req.called) + self.assertFalse(mock_parse.called) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._enroll') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._enroll_check') + def test_089_enroll(self, mock_chk, mock_enroll): + """ test enroll() """ + mock_chk.return_value = None + mock_enroll.return_value = ('mock_err', 'mock_bundle', 'mock_raw', 'mock_poll') + self.assertEqual(('mock_err', 'mock_bundle', 'mock_raw', 'mock_poll'), self.cahandler.enroll('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._enroll') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._enroll_check') + def test_090_enroll(self, mock_chk, mock_enroll): + """ test enroll() """ + mock_chk.return_value = 'mock_chk' + mock_enroll.return_value = ('mock_err', 'mock_bundle', 'mock_raw', 'mock_poll') + self.assertEqual(('mock_chk', None, None, None), self.cahandler.enroll('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._trackingid_get') + def test_091_revoke(self, mock_track, mock_req): + """ test revoke() """ + mock_track.return_value = 'tracking_id' + mock_req.return_value = (200, 'response') + self.assertEqual((200, 'Certificate revoked', None), self.cahandler.revoke('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._trackingid_get') + def test_092_revoke(self, mock_track, mock_req): + """ test revoke() """ + mock_track.return_value = 'tracking_id' + mock_req.return_value = (500, 'response') + self.assertEqual((500, 'urn:ietf:params:acme:error:serverInternal', 'response'), self.cahandler.revoke('csr')) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_post') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._trackingid_get') + def test_093_revoke(self, mock_track, mock_req): + """ test revoke() """ + mock_track.return_value = None + mock_req.return_value = (200, 'response') + self.assertEqual((500, 'urn:ietf:params:acme:error:serverInternal', 'Failed to get tracking id'), self.cahandler.revoke('csr')) + + +if __name__ == '__main__': + + unittest.main() From 0bad0fe7dca7df6885dbfc30899f0fc9dbbed917 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 2 Nov 2024 07:17:03 +0100 Subject: [PATCH 39/54] [feat] entrust_mgr.py --- examples/ca_handler/entrust_ca_handler.py | 48 +++++++++++++++--- tools/entrust_mgr.py | 62 +++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 tools/entrust_mgr.py diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index c58c431e..92c44fbe 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -""" CA handler using Digicert CertCentralAPI""" +""" CA handler using Entrust ECS Enterprise""" from __future__ import print_function from typing import Tuple, Dict, List import datetime @@ -344,16 +344,16 @@ def _domains_get(self, org_id: str) -> List[str]: self.logger.debug('CAhandler._domains_get() ended with code: %s', code) return api_domain_list - def _credential_check(self): + def credential_check(self): """ test connection to Entrust api """ - self.logger.debug('CAhandler._credential_check()') + self.logger.debug('CAhandler.credential_check()') error = None code, content = self._api_get(self.api_url + '/application/version') if code != 200: error = f'Connection to Entrust API failed: {content}' - self.logger.debug('CAhandler._credential_check() ended with code: %s', code) + self.logger.debug('CAhandler.credential_check() ended with code: %s', code) return error def _config_check(self) -> str: @@ -381,7 +381,7 @@ def _enroll_check(self, csr: str) -> str: error = self._config_check() if not error: - error = self._credential_check() + error = self.credential_check() if not error: error = self._org_domain_cfg_check() @@ -484,6 +484,42 @@ def _enroll(self, csr: str) -> Tuple[str, str]: self.logger.debug('CAhandler._enroll() ended with code: %s', code) return error, cert_bundle, cert_raw, poll_indentifier + def revoke_by_trackingid(self, tracking_id: int, _rev_reason: str = 'unspecified') -> Tuple[int, str]: + """ revoke certificate """ + self.logger.debug('CAhandler.revoke_by_trackingid()') + code, content = self._api_post(self.api_url + f'/certificates/{tracking_id}/revocations', {'crlReason': _rev_reason, 'revocationComment': 'revoked by acme2certifier'}) + self.logger.debug('CAhandler.revoke_by_trackingid() ended with code: %s', code) + return code, content + + def certificates_get(self, limit=200) -> List[str]: + """ get certificates """ + self.logger.debug('CAhandler.certificates_get()') + + offset = 0 + code, content = self._api_get(self.api_url + f'/certificates?limit={limit}&offset={offset}') + + cert_list = [] + total = 999 + while(len(cert_list) < total): + self.logger.info('fetching certs offset: %s, limit: %s, total: %s, buffered: %s', offset, limit, total, len(cert_list)) + code, content = self._api_get(self.api_url + f'/certificates?limit={limit}&offset={offset}') + + if code == 200 and offset == 0 and 'summary' in content and 'total' in content['summary']: + self.logger.debug('CAhandler.certificates_get() total number of certificates: %s', content['summary']['total']) + total = content['summary']['total'] # get total number of certificates + + if code == 200 and 'certificates' in content: + cert_list.extend(content['certificates']) + offset = offset + limit + + else: + self.logger.error('CAhandler.certificates_get() failed with code: %s', code) + break + + self.logger.debug('CAhandler.certificates_get() ended with code: %s and %s certificate', code, len(cert_list)) + return cert_list + + def enroll(self, csr: str) -> Tuple[str, str, str, str]: """ enroll certificate """ self.logger.debug('CAhandler.enroll()') @@ -525,7 +561,7 @@ def revoke(self, certificate_raw: str, _rev_reason: str = 'unspecified', _rev_da tracking_id = self._trackingid_get(certificate_raw) if tracking_id: - code, content = self._api_post(self.api_url + f'/certificates/{tracking_id}/revocations', {'crlReason': _rev_reason, 'revocationComment': 'revoked by acme2certifier'}) + code, content = self.revoke_by_trackingid(tracking_id, _rev_reason) if code == 200: message = 'Certificate revoked' else: diff --git a/tools/entrust_mgr.py b/tools/entrust_mgr.py new file mode 100644 index 00000000..dea15850 --- /dev/null +++ b/tools/entrust_mgr.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" entrust manager """ +from __future__ import print_function +import sys +import os +import argparse +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))) +# pylint: disable=E0401, E0611, C0209, C0413 +from acme_srv.helper import logger_setup # nopep8 +from examples.ca_handler.entrust_ca_handler import CAhandler # nopep8 + + +def arg_parse(): + """ simple argparser """ + + parser = argparse.ArgumentParser(description='enturst_mgr.py - a simple enturst certificate mananger') + parser.add_argument('-d', '--debug', help='debug mode', action="store_true", default=False) + parser.add_argument('-p', '--pagination', help='amout of certificates to be fetch with a single rest-call', default=200) + clist = parser.add_mutually_exclusive_group() + clist.add_argument('-a', '--filteractive', help='filter output to active accounts', action="store_true", default=False) + clist.add_argument('-r', '--revoke', help='revoke ', default=None) + + args = parser.parse_args() + + debug = args.debug + config_dic = { + 'debug': args.debug, + 'filteractive': args.filteractive, + 'revoke': args.revoke, + 'pagination': int(args.pagination) + } + return (debug, config_dic) + + +if __name__ == '__main__': + + DEBUG, CONFIG_DIC = arg_parse() + + # initialize logger + LOGGER = logger_setup(DEBUG) + + with CAhandler(logger=LOGGER) as ca_handler: + if not ca_handler.credential_check(): + + if CONFIG_DIC['revoke']: + print("Revoking certificate with transaction_id: ", CONFIG_DIC['revoke']) + CODE, CONTENT = ca_handler.revoke_by_trackingid(CONFIG_DIC['revoke']) + if CODE == 200: + print("Revocation successful") + else: + print(f"Revocation failed with error: {CONTENT}") + else: + # get list of certificates + cert_list = ca_handler.certificates_get(limit=CONFIG_DIC['pagination']) + for cert in cert_list: + if (CONFIG_DIC['filteractive'] and cert['status'] == 'ACTIVE') or not CONFIG_DIC['filteractive']: + print(cert) + else: + print("Credential check failed") + sys.exit(1) From b178d920f1a7eddb82bc22fb00a6f15aa9746064 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 2 Nov 2024 07:33:29 +0100 Subject: [PATCH 40/54] [fix] sort certificates in entrust_mgr.py --- tools/entrust_mgr.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/entrust_mgr.py b/tools/entrust_mgr.py index dea15850..8b105c82 100644 --- a/tools/entrust_mgr.py +++ b/tools/entrust_mgr.py @@ -18,6 +18,7 @@ def arg_parse(): parser = argparse.ArgumentParser(description='enturst_mgr.py - a simple enturst certificate mananger') parser.add_argument('-d', '--debug', help='debug mode', action="store_true", default=False) parser.add_argument('-p', '--pagination', help='amout of certificates to be fetch with a single rest-call', default=200) + parser.add_argument('-s', '--sortby', help='sortby fieldname [trackigId, status, serialNumber, expiresAfter]', default='trackingId') clist = parser.add_mutually_exclusive_group() clist.add_argument('-a', '--filteractive', help='filter output to active accounts', action="store_true", default=False) clist.add_argument('-r', '--revoke', help='revoke ', default=None) @@ -29,7 +30,8 @@ def arg_parse(): 'debug': args.debug, 'filteractive': args.filteractive, 'revoke': args.revoke, - 'pagination': int(args.pagination) + 'pagination': int(args.pagination), + 'sortby': args.sortby } return (debug, config_dic) @@ -54,7 +56,7 @@ def arg_parse(): else: # get list of certificates cert_list = ca_handler.certificates_get(limit=CONFIG_DIC['pagination']) - for cert in cert_list: + for cert in sorted(cert_list, key=lambda k: k[CONFIG_DIC['sortby']]): if (CONFIG_DIC['filteractive'] and cert['status'] == 'ACTIVE') or not CONFIG_DIC['filteractive']: print(cert) else: From cbaca63d416f776c6f3122992b1ef0ee04fe3d9e Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 2 Nov 2024 17:07:54 +0100 Subject: [PATCH 41/54] [wf] entrust workflow --- .github/actions/acme_clients/action.yml | 21 +- .../entrust_ca_handler/enroll/action.yml | 237 ++++++++++++++++++ .../entrust_ca_handler/enroll_eab/action.yml | 41 +++ .github/workflows/ca_handler_tests_enrust.yml | 154 ++++++++++++ 4 files changed, 449 insertions(+), 4 deletions(-) create mode 100644 .github/actions/wf_specific/entrust_ca_handler/enroll/action.yml create mode 100644 .github/actions/wf_specific/entrust_ca_handler/enroll_eab/action.yml create mode 100644 .github/workflows/ca_handler_tests_enrust.yml diff --git a/.github/actions/acme_clients/action.yml b/.github/actions/acme_clients/action.yml index 1784faec..742fb325 100644 --- a/.github/actions/acme_clients/action.yml +++ b/.github/actions/acme_clients/action.yml @@ -1,4 +1,4 @@ -name: "acme_clients" +name: "acme_clients - enroll, renew and revoke certificates" description: "Test if acme.sh, certbot and lego can enroll, renew and certificates" inputs: ACME_SERVER: @@ -86,9 +86,9 @@ runs: HTTPS_PORT: ${{ inputs.HTTPS_PORT }} NAME_SPACE: ${{ inputs.NAME_SPACE }} - - name: "HTTPS - Enroll lego" + - name: "HTTP - Enroll lego" run: | - echo "##### HTTPS - Enroll lego #####" + echo "##### HTTP - Enroll lego #####" if [ "$USE_RSA" == "false" ]; then echo "use ECC" docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run @@ -105,6 +105,19 @@ runs: HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} NAME_SPACE: ${{ inputs.NAME_SPACE }} + - name: "HTTP - Revoke lego" + if: ${{ inputs.REVOCATION == 'true' }} + run: | + echo "#### HTTP - Revoke lego" + docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + - name: "HTTPS - Enroll acme.sh" run: | echo "##### HTTPS - Enroll acme.sh #####" @@ -392,7 +405,7 @@ runs: if: ${{ inputs.REVOCATION == 'true' }} run: | echo "##### HTTPS - Revoke lego #####" - # docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke shell: bash env: ACME_SERVER: ${{ inputs.ACME_SERVER }} diff --git a/.github/actions/wf_specific/entrust_ca_handler/enroll/action.yml b/.github/actions/wf_specific/entrust_ca_handler/enroll/action.yml new file mode 100644 index 00000000..697565b0 --- /dev/null +++ b/.github/actions/wf_specific/entrust_ca_handler/enroll/action.yml @@ -0,0 +1,237 @@ +name: "acme_clients - enroll, renew and revoke certificates" +description: "Test if acme.sh, certbot and lego can enroll, renew and certificates" +inputs: + ACME_SERVER: + description: "ACME server URL" + required: true + default: "acme-srv" + REVOCATION: + description: "Revocation method" + required: true + default: "true" + USE_RSA: + description: "Use RSA" + required: true + default: "false" + HTTP_PORT: + description: "HTTP port" + required: true + default: "80" + HTTPS_PORT: + description: "HTTPS port" + required: true + default: "443" + HOSTNAME_SUFFIX: + description: "Hostname suffix" + required: true + NAME_SPACE: + description: "Namespace" + required: true + default: "acme" + +runs: + using: "composite" + steps: + + - name: "Create directories" + run: | + sudo mkdir -p certbot/ + sudo mkdir -p lego/ca + sudo cp .github/acme2certifier_cabundle.pem certbot/ + sudo cp .github/acme2certifier_cabundle.pem lego/ + if [ -f cert-2.pem ]; then + echo "delete cert-2.pem" + rm -f cert-2.pem + fi + if [ -f cert-1.pem ]; then + echo "delete cert-1.pem" + rm -f cert-1.pem + fi + ls -la + shell: bash + + - name: "Sleep for 5s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 5s + + - name: "Test if http://acme-srv/directory is accessible" + run: docker run -i --rm --network $NAME_SPACE curlimages/curl -f http://$ACME_SERVER:$HTTP_PORT/directory + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "Test if https://acme-srv/directory is accessible" + run: docker run -i --rm --network $NAME_SPACE curlimages/curl --insecure -f https://$ACME_SERVER:$HTTPS_PORT/directory + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTP - Enroll lego" + run: | + echo "##### HTTP - Enroll lego #####" + if [ "$USE_RSA" == "false" ]; then + echo "use ECC" + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run + else + echo "use RSA" + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run + fi + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + USE_RSA: ${{ inputs.USE_RSA }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTP - Revoke lego" + if: ${{ inputs.REVOCATION == 'true' }} + run: | + echo "#### HTTP - Revoke lego" + docker run -i -v $PWD/lego:/.lego/ --rm --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE revoke + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTPS - Enroll acme.sh" + run: | + echo "##### HTTPS - Enroll acme.sh #####" + if [ "$USE_RSA" == "false" ]; then + echo "use ECC" + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --alpn --standalone --debug 1 --output-insecure --insecure + ECC="_ecc" + else + echo "use RSA" + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --network $NAME_SPACE --name acme-sh$HOSTNAME_SUFFIX neilpang/acme.sh:latest --issue --server https://$ACME_SERVER:$HTTPS_PORT --accountemail 'acme-sh@example.com' -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --alpn --standalone --keylength 2048 --debug 1 --output-insecure --insecure + fi + + awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert-" c ".pem"}' < acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/ca.cer + if [ "$VERIFY_CERT" == "true" ]; then + if [ -f cert-2.pem ]; then + echo "Multiple CA certs" + openssl verify -CAfile cert-2.pem -untrusted cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer + else + echo "Single Root ca" + openssl verify -CAfile cert-1.pem acme-sh/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE${ECC}/acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE.cer + fi + fi + shell: bash + env: + VERIFY_CERT: ${{ inputs.VERIFY_CERT }} + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + USE_RSA: ${{ inputs.USE_RSA }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTPS - Revoke HTTP-01 single domain acme.sh" + if: ${{ inputs.REVOCATION == 'true' }} + run: | + echo "##### HTTPS - Revoke HTTP-01 single domain acme.sh #####" + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --revoke --server https://$ACME_SERVER:$HTTPS_PORT --revoke -d acme-sh$HOSTNAME_SUFFIX.$NAME_SPACE --standalone --debug 2 --output-insecure --insecure + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTPS - Decativate acme.sh #####" + run: | + echo "##### HTTPS - Decativate acme.sh" + docker run --rm -i -v "$(pwd)/acme-sh":/acme.sh --name acme-sh$HOSTNAME_SUFFIX --network $NAME_SPACE neilpang/acme.sh:latest --deactivate-account --server https://$ACME_SERVER:$HTTPS_PORT --debug 2 --output-insecure --insecure + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTPS - Enroll certbot" + run: | + echo "##### HTTPS - Enroll certbot #####" + if [ "$USE_RSA" == "false" ]; then + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 + else + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot certonly --server https://$ACME_SERVER:$HTTPS_PORT --standalone --preferred-challenges http --no-verify-ssl --agree-tos -m 'certbot@example.com' --key-type rsa -d certbot$HOSTNAME_SUFFIX.$NAME_SPACE --cert-name certbot --issuance-timeout 120 + fi + + if [ "$VERIFY_CERT" == "true" ]; then + if [ -f cert-2.pem ]; then + sudo openssl verify -CAfile cert-2.pem -untrusted cert-1.pem certbot/live/certbot/cert.pem + else + echo "single root ca" + sudo openssl verify -CAfile cert-1.pem certbot/live/certbot/cert.pem + fi + fi + shell: bash + env: + VERIFY_CERT: ${{ inputs.VERIFY_CERT }} + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + USE_RSA: ${{ inputs.USE_RSA }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTPS - Revoke certbot" + if: ${{ inputs.REVOCATION == 'true' }} + run: | + echo "##### HTTPS - Revoke certbot #####" + docker run -i --rm --name certbot$HOSTNAME_SUFFIX --network $NAME_SPACE -v $PWD/certbot:/etc/letsencrypt/ certbot/certbot revoke --server https://$ACME_SERVER:$HTTPS_PORT --no-verify-ssl --delete-after-revoke --cert-name certbot + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "HTTP - Enroll lego with wrong domain - should fail" + id: legofail01 + continue-on-error: true + run: | + echo "##### HTTP - Enroll lego #####" + if [ "$USE_RSA" == "false" ]; then + echo "use ECC" + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.acme --tls run + else + echo "use RSA" + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.acme --tls run + fi + shell: bash + env: + ACME_SERVER: ${{ inputs.ACME_SERVER }} + HTTP_PORT: ${{ inputs.HTTP_PORT }} + HTTPS_PORT: ${{ inputs.HTTPS_PORT }} + USE_RSA: ${{ inputs.USE_RSA }} + HOSTNAME_SUFFIX: ${{ inputs.HOSTNAME_SUFFIX }} + NAME_SPACE: ${{ inputs.NAME_SPACE }} + + - name: "Check result " + if: steps.legofail01.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail01.outcome }}" + exit 1 + shell: bash + + - name: "Delete acme-sh, letsencypt and lego folders" + run: | + sudo rm -rf lego/* + sudo rm -rf acme-sh/* + sudo rm -rf certbot/* + shell: bash \ No newline at end of file diff --git a/.github/actions/wf_specific/entrust_ca_handler/enroll_eab/action.yml b/.github/actions/wf_specific/entrust_ca_handler/enroll_eab/action.yml new file mode 100644 index 00000000..1ea5936b --- /dev/null +++ b/.github/actions/wf_specific/entrust_ca_handler/enroll_eab/action.yml @@ -0,0 +1,41 @@ +name: "enroll_eab" +description: "enroll_eab" + +runs: + using: "composite" + steps: + - name: "Sleep for 5s" + uses: juliangruber/sleep-action@v2.0.3 + with: + time: 5s + + - name: "EAB - Test http://acme-srv/directory is accessible" + run: docker run -i --rm --network rm-rf.ninja curlimages/curl -f http://acme-srv/directory + shell: bash + + - name: "EAB - Test if https://acme-srv/directory is accessible" + run: docker run -i --rm --network rm-rf.ninja curlimages/curl --insecure -f https://acme-srv/directory + shell: bash + + - name: "EAB - 01 - Enroll lego with a template_name taken from list in kid.json" + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network rm-rf.ninja goacme/lego -s http://acme-srv -a --email "lego@example.com" --key-type=rsa2048 --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.rm-rf.ninja --http run + sudo docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network rm-rf.ninja goacme/lego -s http://acme-srv -a --email "lego@example.com" --eab --kid keyid_00 --hmac V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw -d lego.rm-rf.ninja revoke + shell: bash + + - name: "EAB - 02 - Enroll lego with a not allowed fqdn in kid.json (to fail)" + id: legofail01 + continue-on-error: true + run: | + sudo rm -rf lego/* + docker run -i -v $PWD/lego:/.lego/ --rm --name lego --network rm-rf.ninja goacme/lego -s http://acme-srv -a --email "lego@example.com" --key-type=rsa2048 --eab --kid keyid_02 --hmac dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM -d lego.rm-rf.ninja --http run + shell: bash + + - name: "EAB - 04a - check result " + if: steps.legofail01.outcome != 'failure' + run: | + echo "legofail outcome is ${{steps.legofail01.outcome }}" + exit 1 + shell: bash + diff --git a/.github/workflows/ca_handler_tests_enrust.yml b/.github/workflows/ca_handler_tests_enrust.yml new file mode 100644 index 00000000..1f4d040d --- /dev/null +++ b/.github/workflows/ca_handler_tests_enrust.yml @@ -0,0 +1,154 @@ +name: CA handler Tests - Entrust Enterprise API + +on: + push: + branches: [ 'devel', 'master', 'entrust', 'entrust_wf'] + pull_request: + branches: [ devel ] + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 2 * * 6' + +jobs: + entrust_handler_tests: + name: "entrust_handler_tests" + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + fail-fast: false + matrix: + websrv: ['apache2'] + dbhandler: ['wsgi'] + # dbhandler: ['wsgi', 'django'] + steps: + - name: "checkout GIT" + uses: actions/checkout@v4 + + - name: "create folder" + run: | + mkdir lego + mkdir acme-sh + mkdir certbot + + - name: "Build container" + uses: ./.github/actions/container_prep + with: + DB_HANDLER: ${{ matrix.dbhandler }} + WEB_SRV: ${{ matrix.websrv }} + NAME_SPACE: rm-rf.ninja + + - name: "Setup a2c with entrust_ca_handler" + run: | + sudo mkdir -p examples/Docker/data/ + sudo touch examples/Docker/data/entrust_cert.p12 + sudo chmod a+rw examples/Docker/data/entrust_cert.p12 + sudo echo $ENTRUST_CERT | base64 --decode > examples/Docker/data/entrust_cert.p12 + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg examples/Docker/data/acme_srv.cfg + sudo chmod 777 examples/Docker/data/acme_srv.cfg + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > examples/Docker/data/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/entrust_ca_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "client_cert: volume/entrust_cert.p12" >> examples/Docker/data/acme_srv.cfg + sudo echo "cert_passphrase: $ENTRUST_CERT_PASSPHRASE" >> examples/Docker/data/acme_srv.cfg + sudo echo "organization_name: $ENTRUST_ORGNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "username: $ENTRUST_USERNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "password: $ENTRUST_PASSWORD" >> examples/Docker/data/acme_srv.cfg + sudo echo "certtype: $ENTRUST_CERTTYPE" >> examples/Docker/data/acme_srv.cfg + sudo echo "cert_validity_days: 10" >> examples/Docker/data/acme_srv.cfg + sudo echo "request_timeout: 20" >> examples/Docker/data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"*.rm-rf.ninja\", \"bar.local$\"]" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" examples/Docker/data/acme_srv.cfg + sudo sed -i "s/challenge_validation_disable: False/challenge_validation_disable: True/g" examples/Docker/data/acme_srv.cfg + # cd examples/Docker/ + # docker-compose restart + env: + ENTRUST_CERT: ${{ secrets.ENTRUST_CERT }} + ENTRUST_CERT_PASSPHRASE: ${{ secrets.ENTRUST_CERT_PASSPHRASE }} + ENTRUST_ORGNAME: ${{ secrets.ENTRUST_ORGNAME }} + ENTRUST_USERNAME: ${{ secrets.ENTRUST_USER }} + ENTRUST_PASSWORD: ${{ secrets.ENTRUST_PASSWORD }} + ENTRUST_CERTTYPE: ${{ secrets.ENTRUST_CERTTYPE }} + + - name: "Test enrollment" + uses: ./.github/actions/wf_specific/entrust_ca_handler/enroll + with: + NAME_SPACE: rm-rf.ninja + USE_CERTBOT: false + USE_RSA: true + + - name: "EAB - Setup a2c with entrust_ca_handler" + run: | + sudo mkdir -p examples/Docker/data/ + sudo touch examples/Docker/data/entrust_cert.p12 + sudo chmod a+rw examples/Docker/data/entrust_cert.p12 + sudo echo $ENTRUST_CERT | base64 --decode > examples/Docker/data/entrust_cert.p12 + sudo cp .github/openssl_ca_handler.py_acme_srv_default_handler.cfg examples/Docker/data/acme_srv.cfg + sudo chmod 777 examples/Docker/data/acme_srv.cfg + sudo head -n -8 .github/openssl_ca_handler.py_acme_srv_default_handler.cfg > examples/Docker/data/acme_srv.cfg + sudo echo "handler_file: examples/ca_handler/entrust_ca_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "client_cert: volume/entrust_cert.p12" >> examples/Docker/data/acme_srv.cfg + sudo echo "cert_passphrase: $ENTRUST_CERT_PASSPHRASE" >> examples/Docker/data/acme_srv.cfg + sudo echo "organization_name: $ENTRUST_ORGNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "username: $ENTRUST_USERNAME" >> examples/Docker/data/acme_srv.cfg + sudo echo "password: $ENTRUST_PASSWORD" >> examples/Docker/data/acme_srv.cfg + sudo echo "certtype: $ENTRUST_CERTTYPE" >> examples/Docker/data/acme_srv.cfg + sudo echo "cert_validity_days: 10" >> examples/Docker/data/acme_srv.cfg + sudo echo "request_timeout: 20" >> examples/Docker/data/acme_srv.cfg + sudo echo "allowed_domainlist: [\"*.rm-rf.ninja\", \"bar.local$\"]" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/revocation_reason_check_disable: False/revocation_reason_check_disable: False\nenrollment_timeout:15/g" examples/Docker/data/acme_srv.cfg + sudo sed -i "s/challenge_validation_disable: False/challenge_validation_disable: True/g" examples/Docker/data/acme_srv.cfg + sudo echo "eab_profiling: True" >> examples/Docker/data/acme_srv.cfg + sudo sed -i "s/tnauthlist_support: False/tnauthlist_support: False\nheader_info_list: [\"HTTP_USER_AGENT\"]/g" examples/Docker/data/acme_srv.cfg + sudo echo -e "\n\n[EABhandler]" >> examples/Docker/data/acme_srv.cfg + sudo echo "eab_handler_file: /var/www/acme2certifier/examples/eab_handler/kid_profile_handler.py" >> examples/Docker/data/acme_srv.cfg + sudo echo "key_file: volume/kid_profiles.json" >> examples/Docker/data/acme_srv.cfg + + sudo cp examples/eab_handler/kid_profiles.json examples/Docker/data/kid_profiles.json + sudo chmod 777 examples/eab_handler/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \[\"profile_1\", \"profile_2\", \"profile_3\"\]/\"cert_type\"\: \[\"STANDARD_SSL\", \"ADVANTAGE_SSL\"\]/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"profile_id\"\: \"profile_2\"/\"cert_type\"\: \"ADVANTAGE_SSL\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca\",/\"unknown_key\": \"unknown_value\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/\"ca_name\": \"example_ca_2\",/\"unknown_key\": \"unknown_value\"/g" examples/Docker/data/kid_profiles.json + sudo sed -i "s/*.example.net/*.rm-rf.ninja/" examples/Docker/data/kid_profiles.json + sudo sed -i '18,19d' examples/Docker/data/kid_profiles.json + sudo sed -i '8,9d' examples/Docker/data/kid_profiles.json + cd examples/Docker/ + docker-compose restart + env: + ENTRUST_CERT: ${{ secrets.ENTRUST_CERT }} + ENTRUST_CERT_PASSPHRASE: ${{ secrets.ENTRUST_CERT_PASSPHRASE }} + ENTRUST_ORGNAME: ${{ secrets.ENTRUST_ORGNAME }} + ENTRUST_USERNAME: ${{ secrets.ENTRUST_USER }} + ENTRUST_PASSWORD: ${{ secrets.ENTRUST_PASSWORD }} + ENTRUST_CERTTYPE: ${{ secrets.ENTRUST_CERTTYPE }} + + - name: "Test enrollment" + uses: ./.github/actions/wf_specific/entrust_ca_handler/enroll_eab + with: + USE_RSA: true + + - name: "Check container configuration" + uses: ./.github/actions/container_check + with: + DB_HANDLER: ${{ matrix.dbhandler }} + WEB_SRV: ${{ matrix.websrv }} + + - name: "[ * ] collecting test logs" + if: ${{ failure() }} + run: | + mkdir -p ${{ github.workspace }}/artifact/upload + sudo cp -rp examples/Docker/data/ ${{ github.workspace }}/artifact/data/ + sudo cp -rp acme-sh/ ${{ github.workspace }}/artifact/acme-sh/ + sudo cp -rp certbot/ ${{ github.workspace }}/artifact/certbot/ + sudo cp -rp lego/ ${{ github.workspace }}/artifact/lego/ + cd examples/Docker + docker-compose logs > ${{ github.workspace }}/artifact/docker-compose.log + # touch ${{ github.workspace }}/artifact/docker-compose.log + sudo tar -C ${{ github.workspace }}/artifact/ -cvzf ${{ github.workspace }}/artifact/upload/artifact.tar.gz docker-compose.log data acme-sh certbot lego + + - name: "[ * ] uploading artificates" + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: entrust_handler_tests-${{ matrix.websrv }}-${{ matrix.dbhandler }}.tar.gz + path: ${{ github.workspace }}/artifact/upload/ + From f2d92e5aea01f47931f9475158ebeb0e83d259c7 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sat, 2 Nov 2024 18:25:10 +0100 Subject: [PATCH 42/54] [fix] enroll_clients action --- .github/actions/acme_clients/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/acme_clients/action.yml b/.github/actions/acme_clients/action.yml index 742fb325..230527de 100644 --- a/.github/actions/acme_clients/action.yml +++ b/.github/actions/acme_clients/action.yml @@ -377,10 +377,10 @@ runs: echo "##### HTTPS - Enroll lego #####" if [ "$USE_RSA" == "false" ]; then echo "use ECC" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run else echo "use RSA" - docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s http://$ACME_SERVER:$HTTP_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run + docker run -i --rm -e LEGO_CA_CERTIFICATES=.lego/acme2certifier_cabundle.pem -v $PWD/lego:/.lego/ --name lego$HOSTNAME_SUFFIX --network $NAME_SPACE goacme/lego -s https://$ACME_SERVER:$HTTPS_PORT -a --email "lego@example.com" --key-type=rsa2048 -d lego$HOSTNAME_SUFFIX.$NAME_SPACE --tls run fi if [ "$VERIFY_CERT" == "true" ]; then From aa8b36447b8d7e916ccab67c0036e17fd6fa245e Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 07:01:50 +0100 Subject: [PATCH 43/54] [tst] unittests --- examples/ca_handler/entrust_ca_handler.py | 35 +++++++----- test/test_entrust.py | 70 ++++++++++++++++++++--- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index 92c44fbe..e0f602b8 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -495,23 +495,33 @@ def certificates_get(self, limit=200) -> List[str]: """ get certificates """ self.logger.debug('CAhandler.certificates_get()') + # set initial values offset = 0 - code, content = self._api_get(self.api_url + f'/certificates?limit={limit}&offset={offset}') - cert_list = [] - total = 999 + prev_data = [] + total = 1 while(len(cert_list) < total): self.logger.info('fetching certs offset: %s, limit: %s, total: %s, buffered: %s', offset, limit, total, len(cert_list)) code, content = self._api_get(self.api_url + f'/certificates?limit={limit}&offset={offset}') - - if code == 200 and offset == 0 and 'summary' in content and 'total' in content['summary']: - self.logger.debug('CAhandler.certificates_get() total number of certificates: %s', content['summary']['total']) - total = content['summary']['total'] # get total number of certificates - - if code == 200 and 'certificates' in content: - cert_list.extend(content['certificates']) - offset = offset + limit - + if code == 200: + # updte loop limit or throw error + if offset == 0: + if 'summary' in content and 'total' in content['summary']: + self.logger.debug('CAhandler.certificates_get() total number of certificates: %s', content['summary']['total']) + total = content['summary']['total'] # get total number of certificates + else: + self.logger.error('CAhandler.certificates_get() failed did not get any total value: %s', content) + break + # extend certificate list or throw error + if 'certificates' in content: + # cover cases where we wont get new data as we have to avoid loops + if prev_data == content['certificates']: + self.logger.error('CAhandler.certificates_get() failed to get new data') + break + else: + cert_list.extend(content['certificates']) + prev_data = content['certificates'] + offset = offset + limit else: self.logger.error('CAhandler.certificates_get() failed with code: %s', code) break @@ -519,7 +529,6 @@ def certificates_get(self, limit=200) -> List[str]: self.logger.debug('CAhandler.certificates_get() ended with code: %s and %s certificate', code, len(cert_list)) return cert_list - def enroll(self, csr: str) -> Tuple[str, str, str, str]: """ enroll certificate """ self.logger.debug('CAhandler.enroll()') diff --git a/test/test_entrust.py b/test/test_entrust.py index 8a6301ce..dad2eee9 100644 --- a/test/test_entrust.py +++ b/test/test_entrust.py @@ -844,16 +844,16 @@ def test_063__domains_get(self, mock_api): self.assertEqual(['bar.foo'], self.cahandler._domains_get(1)) @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') - def test_064__credential_check(self, mock_api): + def test_064_credential_check(self, mock_api): """ test _organizations_get() """ mock_api.return_value = (500, 'foo') - self.assertEqual('Connection to Entrust API failed: foo', self.cahandler._credential_check()) + self.assertEqual('Connection to Entrust API failed: foo', self.cahandler.credential_check()) @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') - def test_065__credential_check(self, mock_api): + def test_065_credential_check(self, mock_api): """ test _organizations_get() """ mock_api.return_value = (200, 'foo') - self.assertFalse(self.cahandler._credential_check()) + self.assertFalse(self.cahandler.credential_check()) def test_066_oonfig_check(self): """ test _config_check() """ @@ -896,7 +896,7 @@ def test_070_oonfig_check(self): @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') - @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler.credential_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') def test_071_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): @@ -915,7 +915,7 @@ def test_071_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') - @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler.credential_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') def test_072_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): @@ -934,7 +934,7 @@ def test_072_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') - @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler.credential_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') def test_073_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): @@ -953,7 +953,7 @@ def test_073_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') - @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler.credential_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') def test_074_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): @@ -972,7 +972,7 @@ def test_074_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org @patch('examples.ca_handler.entrust_ca_handler.CAhandler._allowed_domainlist_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._org_domain_cfg_check') - @patch('examples.ca_handler.entrust_ca_handler.CAhandler._credential_check') + @patch('examples.ca_handler.entrust_ca_handler.CAhandler.credential_check') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_check') @patch('examples.ca_handler.entrust_ca_handler.eab_profile_header_info_check') def test_075_enroll_check(self, mock_eab, mock_config, mock_credential, mock_org, mock_domain): @@ -1185,6 +1185,58 @@ def test_093_revoke(self, mock_track, mock_req): mock_req.return_value = (200, 'response') self.assertEqual((500, 'urn:ietf:params:acme:error:serverInternal', 'Failed to get tracking id'), self.cahandler.revoke('csr')) + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_094_certificates_get(self, mock_req): + """ test certificates_get() """ + mock_req.return_value = (500, 'response') + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler.certificates_get()) + self.assertIn('ERROR:test_a2c:CAhandler.certificates_get() failed with code: 500', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_095_certificates_get(self, mock_req): + """ test certificates_get() """ + content = {'certificates': [1, 2, 3, 4], 'summary': {'total': 4}} + mock_req.return_value = (200, content) + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual([1, 2, 3, 4], self.cahandler.certificates_get()) + self.assertIn('INFO:test_a2c:fetching certs offset: 0, limit: 200, total: 1, buffered: 0', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_096_certificates_get(self, mock_req): + """ test certificates_get() """ + response1 = (200, {'certificates': [1, 2, 3, 4], 'summary': {'total': 8}}) + response2 = (200, {'certificates': [5, 6, 7, 8]}) + mock_req.side_effect = [response1, response2] + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual([1, 2, 3, 4, 5, 6, 7, 8], self.cahandler.certificates_get()) + self.assertIn('INFO:test_a2c:fetching certs offset: 0, limit: 200, total: 1, buffered: 0', lcm.output) + self.assertIn('INFO:test_a2c:fetching certs offset: 200, limit: 200, total: 8, buffered: 4', lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_097_certificates_get(self, mock_req): + """ test certificates_get() """ + response1 = (200, {'certificates': [1, 2, 3, 4]}) + response2 = (200, {'certificates': [5, 6, 7, 8]}) + mock_req.side_effect = [response1, response2] + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertFalse(self.cahandler.certificates_get()) + self.assertIn("ERROR:test_a2c:CAhandler.certificates_get() failed did not get any total value: {'certificates': [1, 2, 3, 4]}", lcm.output) + + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_098_certificates_get(self, mock_req): + """ test certificates_get() """ + response1 = (200, {'certificates': [1, 2, 3, 4], 'summary': {'total': 9}}) + response2 = (200, {'certificates': [5, 6, 7, 8]}) + response3 = (200, {'certificates': [5, 6, 7, 8]}) + mock_req.side_effect = [response1, response2, response3] + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual([1, 2, 3, 4, 5, 6, 7, 8], self.cahandler.certificates_get()) + self.assertIn('INFO:test_a2c:fetching certs offset: 0, limit: 200, total: 1, buffered: 0', lcm.output) + self.assertIn('INFO:test_a2c:fetching certs offset: 200, limit: 200, total: 9, buffered: 4', lcm.output) + self.assertIn('INFO:test_a2c:fetching certs offset: 400, limit: 200, total: 9, buffered: 8', lcm.output) + self.assertIn('ERROR:test_a2c:CAhandler.certificates_get() failed to get new data', lcm.output) + if __name__ == '__main__': From d5bea9107015be377c60b7be2029e80cb9f27d80 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 07:10:38 +0100 Subject: [PATCH 44/54] [fix] linting --- examples/ca_handler/entrust_ca_handler.py | 12 ++++++------ test/test_entrust.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index e0f602b8..6c7afb53 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -262,7 +262,7 @@ def _config_session_load(self, config_dic: Dict[str, str]): if 'client_cert' in config_dic['CAhandler'] and self.cert_passphrase: self.session.mount(self.api_url, Pkcs12Adapter(pkcs12_filename=config_dic['CAhandler']['client_cert'], pkcs12_password=self.cert_passphrase)) else: - self.logger.error('CAhandler._config_load() configuration incomplete: either "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file') + self.logger.warning('CAhandler._config_load() configuration might be incomplete: "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file') self.session.auth = (self.username, self.password) self.logger.debug('CAhandler._config_session_load() ended') @@ -500,7 +500,7 @@ def certificates_get(self, limit=200) -> List[str]: cert_list = [] prev_data = [] total = 1 - while(len(cert_list) < total): + while len(cert_list) < total: self.logger.info('fetching certs offset: %s, limit: %s, total: %s, buffered: %s', offset, limit, total, len(cert_list)) code, content = self._api_get(self.api_url + f'/certificates?limit={limit}&offset={offset}') if code == 200: @@ -515,13 +515,13 @@ def certificates_get(self, limit=200) -> List[str]: # extend certificate list or throw error if 'certificates' in content: # cover cases where we wont get new data as we have to avoid loops - if prev_data == content['certificates']: - self.logger.error('CAhandler.certificates_get() failed to get new data') - break - else: + if prev_data != content['certificates']: cert_list.extend(content['certificates']) prev_data = content['certificates'] offset = offset + limit + else: + self.logger.error('CAhandler.certificates_get() failed to get new data') + break else: self.logger.error('CAhandler.certificates_get() failed with code: %s', code) break diff --git a/test/test_entrust.py b/test/test_entrust.py index dad2eee9..8f9287b0 100644 --- a/test/test_entrust.py +++ b/test/test_entrust.py @@ -669,7 +669,7 @@ def test_044_config_session_load(self, mock_sl, mock_pkcs12, mock_session): self.cahandler._config_session_load(config_dic) self.assertTrue(mock_sl.called) self.assertTrue(self.cahandler.session) - self.assertIn('ERROR:test_a2c:CAhandler._config_load() configuration incomplete: either "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file', lcm.output) + self.assertIn('WARNING:test_a2c:CAhandler._config_load() configuration might be incomplete: "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file', lcm.output) self.assertFalse(mock_pkcs12.called) @patch('examples.ca_handler.entrust_ca_handler.csr_san_get') From 532458ec2ee891f5c5981dee0514e06755da3889 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 07:20:30 +0100 Subject: [PATCH 45/54] [doc] entrust documentation --- README.md | 1 + docs/entrust.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 docs/entrust.md diff --git a/README.md b/README.md index 4bb68508..1762a642 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ should be straight forward. As of today the following handlers are available: | E - Certificte Enrollment, R - Certificte Revocation, P - [EAB Profiling](docs/eab_profiling.md) |E|R|P| | :-------- | - | - | - | | [DigiCert® CertCentral](docs/digicert.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [Entrust ECS Enterprise](docs/entrust.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [EJBCA](docs/ejbca.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [Generic ACME protocol handler supporting Letsencrypt, BuyPass.com and ZeroSSL](docs/acme_ca.md) | :x: | :x: | :white_check_mark: | | [Generic CMPv2 protocol handler](docs/cmp.md) | :white_check_mark: | :x: | :x: | diff --git a/docs/entrust.md b/docs/entrust.md new file mode 100644 index 00000000..fb2f6721 --- /dev/null +++ b/docs/entrust.md @@ -0,0 +1,114 @@ + + +# Connecting to Entrust ECS Enterprise + +This handler can be used to enroll certificates from [Entrust ECS Enterprisel](https://www.entrust.com/products/digital-certificates/tls-ssl/entrust-certificate-services). + +## Prerequisites + +- you'll need: + - Username and Password for HTTP-BASIC authentication + - if configured - a client certificate for mutual TLS authentication towards the Entrust RESt API + - an pre-validated Organization name + +## Configuration + +- modify the server configuration (`acme_srv.cfg`) and add the first thre of the below mentioned parameters + +```confag +[CAhandler] +handler_file: examples/ca_handler/entrust_ca_handler.py +username: +password: +certtype: +organization_name: + +client_cert: +cert_passphrase: +cert_validity_days: +allowed_domainlist: +request_timeout: +eab_profiling: +``` + +- username - required - username access the API +- password - required - password to access the PI +- organization_name - required - Organization name as specified in DigiCert CertCentral +- client_cert - optional - client certificate to access the API (to be stored in either pem or pkcs#12 format) +- client_key - optional - client private key to access the API (must be stored in pem format) +- client_passphrase - passphrase to access the client_cert (if stored in pkcs#2 format) +- cert_type - optional - certificate type to be isused. (default: STANDARD_SSL) +- cert_validity_days - certificate validity in days (default: 365) +- allowed_domainlist: list of domain-names allowed for enrollment in json format (example: ["bar.local$, bar.foo.local]) +- request_timeout - optional - requests timeout in seconds for requests (default: 5s) +- eab_profiling - optional - [activate eab profiling](eab_profiling.md) (default: False) + +Use your favorite acme client for certificate enrollment. A list of clients used in our regression can be found in the [disclaimer section of our README file](../README.md) + +## Passing a cert_type from client to server + +The handler makes use of the [header_info_list feature](header_info.md) allowing an acme-client to specify a [certificate type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be used during certificate enrollment. This feature is disabled by default and must be activate in `acme_srv.cfg` as shown below + +```config +[Order] +... +header_info_list: ["HTTP_USER_AGENT"] +``` + +The acme-client can then specify the cert_type as part of its user-agent string. + +Example for acme.sh: + +```bash +docker exec -i acme-sh acme.sh --server http:// --issue -d --standalone --useragent cert_type=ADVANTAGE_SSL --debug 3 --output-insecure +``` + +Example for lego: + +```bash +docker run -i -v $PWD/lego:/.lego/ --rm --name lego goacme/lego -s http:// -a --email "lego@example.com" --user-agent cert_type=ADVANTAGE_SSL -d --http run +``` + +# eab profiling + +This handler can use the [eab profiling feture](eab_profiling.md) to allow individual enrollment configuration per acme-account as well as restriction of CN and SANs to be submitted within the CSR. The feature is disabled by default and must be activated in `acme_srv.cfg` + +```cfg +[EABhandler] +eab_handler_file: examples/eab_handler/kid_profile_handler.py +key_file: + +[CAhandler] +eab_profiling: True +``` + +below an example key-file used during regression testing: + +```json +{ + "keyid_00": { + "hmac": "V2VfbmVlZF9hbm90aGVyX3ZlcnkfX2xvbmdfaG1hY190b19jaGVja19lYWJfZm9yX2tleWlkXzAwX2FzX2xlZ29fZW5mb3JjZXNfYW5faG1hY19sb25nZXJfdGhhbl8yNTZfYml0cw", + "cahandler": { + "cert_type": ["ADVANTAGE_SSL", "STANDARD_PLUS_SSL", "WILDCARD_SSL"], + "allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"], + "organization_name": "acme2certifier" + } + }, + "keyid_01": { + "hmac": "YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg", + "cahandler": { + "allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"], + "cert_type": "ADVANTAGE_SSL" + } + }, + "keyid_02": { + "hmac": "dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM", + "cahandler": { + "allowed_domainlist": ["www.example.com", "www.example.org"] + } + }, + "keyid_03": { + "hmac": "YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr" + } +} +``` From 4d482bec75e86a40d4d8592f4577d32e13d3af22 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 07:23:45 +0100 Subject: [PATCH 46/54] [fix] link fix --- docs/entrust.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/entrust.md b/docs/entrust.md index fb2f6721..baf7166c 100644 --- a/docs/entrust.md +++ b/docs/entrust.md @@ -47,7 +47,7 @@ Use your favorite acme client for certificate enrollment. A list of clients used ## Passing a cert_type from client to server -The handler makes use of the [header_info_list feature](header_info.md) allowing an acme-client to specify a [certificate type](https://dev.digicert.com/en/certcentral-apis/services-api/orders.html) to be used during certificate enrollment. This feature is disabled by default and must be activate in `acme_srv.cfg` as shown below +The handler makes use of the [header_info_list feature](header_info.md) allowing an acme-client to specify a certificate type to be used during certificate enrollment. This feature is disabled by default and must be activate in `acme_srv.cfg` as shown below ```config [Order] From a2cede8e4fecdac91370d040528b75108969167c Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 07:42:42 +0100 Subject: [PATCH 47/54] [fix] address codesmell in mscertsrv_ca_handler --- examples/ca_handler/mscertsrv_ca_handler.py | 41 +++++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/examples/ca_handler/mscertsrv_ca_handler.py b/examples/ca_handler/mscertsrv_ca_handler.py index d76ee928..e55dadef 100644 --- a/examples/ca_handler/mscertsrv_ca_handler.py +++ b/examples/ca_handler/mscertsrv_ca_handler.py @@ -285,6 +285,29 @@ def _domainlist_check(self, csr: str) -> bool: self.logger.debug('CAhandler._domainlist_check() ended with: %s', result) return result + def _enroll(self, csr: str) -> Tuple[str, str, str]: + """ enroll certificate """ + self.logger.debug('CAhandler._enroll()') + # setup certserv + ca_server = Certsrv(self.host, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) + + error = None + cert_bundle = None + cert_raw = None + + # check connection and credentials + auth_check = self._check_credentials(ca_server) + + if auth_check: + # enroll certificate + (error, cert_bundle, cert_raw) = self._csr_process(ca_server, csr) + else: + self.logger.error('Connection or Credentialcheck failed') + error = 'Connection or Credentialcheck failed.' + + self.logger.debug('CAhandler._enroll() ended with error: %s', error) + return (error, cert_bundle, cert_raw) + def enroll(self, csr: str) -> Tuple[str, str, str, bool]: """ enroll certificate from via MS certsrv """ self.logger.debug('CAhandler.enroll(%s)', self.template) @@ -300,25 +323,11 @@ def enroll(self, csr: str) -> Tuple[str, str, str, bool]: result = self._domainlist_check(csr) if result: - # check for eab profiling and header_info error = eab_profile_header_info_check(self.logger, self, csr, 'template') - if not error: - # setup certserv - ca_server = Certsrv(self.host, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) - - # check connection and credentials - auth_check = self._check_credentials(ca_server) - - if auth_check: - - # enroll certificate - (error, cert_bundle, cert_raw) = self._csr_process(ca_server, csr) - - else: - self.logger.error('Connection or Credentialcheck failed') - error = 'Connection or Credentialcheck failed.' + # enroll certificate + (error, cert_bundle, cert_raw) = self._enroll(csr) else: self.logger.error('EAB profile check failed') else: From b8c331ed9f7410867a3e48216609b275774296f6 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 08:11:18 +0100 Subject: [PATCH 48/54] [doc] documentation updates for v0.36 --- CHANGES.md | 22 ++++++++++++++++++++++ SECURITY.md | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3d7e3da2..245f99ff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,28 @@ This is a high-level summary of the most important changes. For a full list of changes, see the [git commit log](https://github.com/grindsa/acme2certifier/commits) and pick the appropriate release branch. +# Changes in 0.36 + +**Features and Improvements**: + +- [ca handler](docs/digicert.md) using the [DigiCert CertCentral API](https://dev.digicert.com/en/certcentral-apis.html) +- [ca handler](docs/entrust.md) using the [Entrust ECS Enterprise APIl](https://www.entrust.com/products/digital-certificates/tls-ssl/entrust-certificate-services) +- [EAB Profiling support](docs/eab_profiling.md) in Microsoft CA handlers +- [#187](https://github.com/grindsa/acme2certifier/pull/187) url option for mscertsrv ca handler +- subject profiling feature +- [strip down python-impacket module](https://github.com/grindsa/acme2certifier/blob/master/docs/mswcce.md#local-installation) in docker images +- [strip down impacket RPM package](https://github.com/grindsa/sbom/tree/main/rpm-repo/RPMs/rhel9) +- YAML config file format supported in [EAB-Profiling feature](docs/eab_profiling.md) +- Upgrade Container images to Ubuntu 24.04 + +**Bugfixes**: + +- openssl-ca-handler: basicConstraints extension will not be marked as critical anymore +- openssl-ca-handler: subjectkeyidentifier extension will not be marked as critical anymore +- fall-back option to python-openssl for Redhat deployments +- detect and handle django installations on Debian/Ubunto systems +- automated schema updates in case of RPM updates + # Changes in 0.35 **Features and Improvements**: diff --git a/SECURITY.md b/SECURITY.md index 635491de..3f6ed588 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,9 +6,9 @@ | Version | Supported | | ------- | ------------------ | +| 0.36.x | :white_check_mark: | | 0.35.x | :white_check_mark: | -| 0.34.x | :white_check_mark: | -| < 0.34 | :x: | +| < 0.35 | :x: | ## Reporting a Vulnerability From ab070efd25ae30ea5132ee7e38a9c74b71cc31d9 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 08:24:28 +0100 Subject: [PATCH 49/54] [fix] removed dead links --- CHANGES.md | 2 +- docs/entrust.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 245f99ff..f7d16b55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,7 @@ and pick the appropriate release branch. **Features and Improvements**: - [ca handler](docs/digicert.md) using the [DigiCert CertCentral API](https://dev.digicert.com/en/certcentral-apis.html) -- [ca handler](docs/entrust.md) using the [Entrust ECS Enterprise APIl](https://www.entrust.com/products/digital-certificates/tls-ssl/entrust-certificate-services) +- [ca handler](docs/entrust.md) using the Entrust ECS Enterprise APIl - [EAB Profiling support](docs/eab_profiling.md) in Microsoft CA handlers - [#187](https://github.com/grindsa/acme2certifier/pull/187) url option for mscertsrv ca handler - subject profiling feature diff --git a/docs/entrust.md b/docs/entrust.md index 51d7f9fd..9fbc2837 100644 --- a/docs/entrust.md +++ b/docs/entrust.md @@ -2,7 +2,7 @@ # Connecting to Entrust ECS Enterprise -This handler can be used to enroll certificates from [Entrust ECS Enterprise APIl](https://www.entrust.com/products/digital-certificates/tls-ssl/entrust-certificate-services). +This handler can be used to enroll certificates from Entrust ECS Enterprise API. ## Prerequisites From cc7a20e233b307f23de62f1c1102237bf1244ad2 Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 3 Nov 2024 09:05:29 +0100 Subject: [PATCH 50/54] [fix] entrust-ca-handler - handling of serials with leading zero --- examples/ca_handler/entrust_ca_handler.py | 11 +++++++++-- test/test_entrust.py | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index 6c7afb53..d854683a 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -8,7 +8,7 @@ import requests from requests_pkcs12 import Pkcs12Adapter # pylint: disable=e0401 -from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get, header_info_get +from acme_srv.helper import load_config, csr_cn_get, cert_pem2der, b64_encode, allowed_domainlist_check, eab_profile_header_info_check, uts_now, uts_to_date_utc, cert_serial_get, config_eab_profile_load, config_headerinfo_load, csr_san_get, header_info_get, b64_url_recode CONTENT_TYPE = 'application/json' @@ -166,6 +166,11 @@ def _certificates_get_from_serial(self, cert_serial: str) -> List[str]: """ get certificates """ self.logger.debug('CAhandler._certificates_get_from_serial()') + # for some reason entrust custs leading zeros from serial number + if cert_serial.startswith('0'): + self.logger.info('CAhandler._certificates_get_from_serial() remove leading zeros from serial number') + cert_serial = cert_serial.lstrip('0') + code, content = self._api_get(self.api_url + f'/certificates?serialNumber={cert_serial}') if code == 200 and 'certificates' in content: @@ -399,7 +404,9 @@ def _trackingid_get(self, cert_raw: str) -> int: tracking_id = None # we misuse header_info_get() to get the tracking id from database - pid_list = header_info_get(self.logger, csr=cert_raw, vlist=['poll_identifier'], field_name='cert_raw') + cert_recode = b64_url_recode(self.logger, cert_raw) + pid_list = header_info_get(self.logger, csr=cert_recode, vlist=['poll_identifier'], field_name='cert_raw') + for ele in pid_list: if 'poll_identifier' in ele: tracking_id = ele['poll_identifier'] diff --git a/test/test_entrust.py b/test/test_entrust.py index 8f9287b0..cb79b09a 100644 --- a/test/test_entrust.py +++ b/test/test_entrust.py @@ -229,6 +229,14 @@ def test_022_certificates_get_from_serial(self, mock_api): self.assertFalse(self.cahandler._certificates_get_from_serial('serial')) self.assertIn('ERROR:test_a2c:CAhandler._certificates_get_from_serial() for serial failed with code: 200', lcm.output) + @patch('examples.ca_handler.entrust_ca_handler.CAhandler._api_get') + def test_023_certificates_get_from_serial(self, mock_api): + """ test certificates_get_from_serial """ + mock_api.return_value = (200, {'certificates': ['foo', 'bar']}) + with self.assertLogs('test_a2c', level='INFO') as lcm: + self.assertEqual(['foo', 'bar'], self.cahandler._certificates_get_from_serial('0serial')) + self.assertIn('INFO:test_a2c:CAhandler._certificates_get_from_serial() remove leading zeros from serial number', lcm.output) + @patch('examples.ca_handler.entrust_ca_handler.config_headerinfo_load') @patch('examples.ca_handler.entrust_ca_handler.config_eab_profile_load') @patch('examples.ca_handler.entrust_ca_handler.CAhandler._config_root_load') From dafab4b85aba43c0df8a11babdc0ecc9b866849a Mon Sep 17 00:00:00 2001 From: grindsa Date: Fri, 8 Nov 2024 07:36:00 +0000 Subject: [PATCH 51/54] [wf] fix validation of tls cert during lego enrollment --- .github/workflows/manual-install-test.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/manual-install-test.yml b/.github/workflows/manual-install-test.yml index af3aca32..f42c3c1c 100644 --- a/.github/workflows/manual-install-test.yml +++ b/.github/workflows/manual-install-test.yml @@ -33,6 +33,8 @@ jobs: - name: "Local modification to get a2c running" run: | + sudo chmod a+w /etc/hosts + sudo echo ${{ env.RUNNER_IP }} acme-srv >> /etc/hosts sudo apt-get install -y socat sudo sed -i "s/Listen 80/Listen 8080/g" /etc/apache2/ports.conf sudo sed -i "s/Listen 443/Listen 1443/g" /etc/apache2/ports.conf @@ -49,7 +51,7 @@ jobs: - name: "Test enrollment" uses: ./.github/actions/acme_clients with: - ACME_SERVER: ${{ env.RUNNER_IP }} + ACME_SERVER: acme-srv HTTP_PORT: 8080 HTTPS_PORT: 1443 @@ -90,6 +92,8 @@ jobs: - name: "Local modification to get a2c running" run: | + sudo chmod a+w /etc/hosts + sudo echo ${{ env.RUNNER_IP }} acme-srv >> /etc/hosts sudo apt-get install -y socat sudo sed -i "s/listen 80/listen 8080/g" /etc/nginx/sites-enabled/acme_srv.conf sudo sed -i "s/listen [::]:80/listen [::]:8080/g" /etc/nginx/sites-enabled/acme_srv.conf @@ -107,7 +111,7 @@ jobs: - name: "Test enrollment" uses: ./.github/actions/acme_clients with: - ACME_SERVER: ${{ env.RUNNER_IP }} + ACME_SERVER: acme-srv HTTP_PORT: 8080 HTTPS_PORT: 1443 @@ -284,6 +288,8 @@ jobs: - name: "Modfiy configuration to allow certifiate enrollment" run: | + sudo chmod a+w /etc/hosts + sudo echo ${{ env.RUNNER_IP }} acme-srv >> /etc/hosts # sudo apt-get install -y socat sudo sed -i "s/Listen 80/Listen 8080/g" /etc/apache2/ports.conf sudo sed -i "s/Listen 443/Listen 1443/g" /etc/apache2/ports.conf @@ -300,7 +306,7 @@ jobs: - name: "Test enrollment" uses: ./.github/actions/acme_clients with: - ACME_SERVER: ${{ env.RUNNER_IP }} + ACME_SERVER: acme-srv HTTP_PORT: 8080 HTTPS_PORT: 1443 @@ -403,6 +409,8 @@ jobs: - name: "Modfiy configuration to allow certifiate enrollment" run: | + sudo chmod a+w /etc/hosts + sudo echo ${{ env.RUNNER_IP }} acme-srv >> /etc/hosts sudo sed -i "s/listen 80/listen 8080/g" /etc/nginx/sites-enabled/acme_srv.conf sudo sed -i "s/listen [::]:80/listen [::]:8080/g" /etc/nginx/sites-enabled/acme_srv.conf sudo sed -i "s/listen 443/listen 1443/g" /etc/nginx/sites-enabled/acme_srv_ssl.conf @@ -418,7 +426,7 @@ jobs: - name: "Test enrollment" uses: ./.github/actions/acme_clients with: - ACME_SERVER: ${{ env.RUNNER_IP }} + ACME_SERVER: acme-srv HTTP_PORT: 8080 HTTPS_PORT: 1443 From 268e2856f9b8ca71216c9a5a0df43ea5f0a5bb73 Mon Sep 17 00:00:00 2001 From: grindsa Date: Fri, 8 Nov 2024 07:39:52 +0000 Subject: [PATCH 52/54] [wf] max-parallel in certifier-wf --- .github/workflows/ca_handler_tests_certifier.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ca_handler_tests_certifier.yml b/.github/workflows/ca_handler_tests_certifier.yml index 9625efd2..2079bf06 100644 --- a/.github/workflows/ca_handler_tests_certifier.yml +++ b/.github/workflows/ca_handler_tests_certifier.yml @@ -13,7 +13,7 @@ jobs: name: "certifier_handler_tests" runs-on: ubuntu-latest strategy: - # max-parallel: 1 + max-parallel: 2 fail-fast: false matrix: websrv: ['apache2', 'nginx'] @@ -276,7 +276,7 @@ jobs: # needs: certifier_handler_tests strategy: fail-fast: false - # max-parallel: 1 + max-parallel: 1 matrix: rhversion: [8, 9] steps: From 78286405dba743dc447e14aa768725285fe76717 Mon Sep 17 00:00:00 2001 From: grindsa Date: Fri, 8 Nov 2024 17:45:18 +0000 Subject: [PATCH 53/54] [fix] msca_handler.py url parameter --- examples/ca_handler/mscertsrv_ca_handler.py | 2 +- tools/entrust_mgr.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/ca_handler/mscertsrv_ca_handler.py b/examples/ca_handler/mscertsrv_ca_handler.py index d89ec688..8783a87c 100644 --- a/examples/ca_handler/mscertsrv_ca_handler.py +++ b/examples/ca_handler/mscertsrv_ca_handler.py @@ -303,7 +303,7 @@ def _enroll(self, csr: str) -> Tuple[str, str, str]: """ enroll certificate """ self.logger.debug('CAhandler._enroll()') # setup certserv - ca_server = Certsrv(self.host, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) + ca_server = Certsrv(self.host, self.url, self.user, self.password, self.auth_method, self.ca_bundle, verify=self.verify, proxies=self.proxy) error = None cert_bundle = None diff --git a/tools/entrust_mgr.py b/tools/entrust_mgr.py index 8b105c82..8feb358e 100644 --- a/tools/entrust_mgr.py +++ b/tools/entrust_mgr.py @@ -44,7 +44,8 @@ def arg_parse(): LOGGER = logger_setup(DEBUG) with CAhandler(logger=LOGGER) as ca_handler: - if not ca_handler.credential_check(): + result = ca_handler.credential_check() + if not result: if CONFIG_DIC['revoke']: print("Revoking certificate with transaction_id: ", CONFIG_DIC['revoke']) @@ -60,5 +61,5 @@ def arg_parse(): if (CONFIG_DIC['filteractive'] and cert['status'] == 'ACTIVE') or not CONFIG_DIC['filteractive']: print(cert) else: - print("Credential check failed") + print("Credential check failed: ", result) sys.exit(1) From 209e44bda393046fe4c8bf82e775114b5a891d4f Mon Sep 17 00:00:00 2001 From: grindsa Date: Sun, 10 Nov 2024 07:27:26 +0100 Subject: [PATCH 54/54] [fix] logging in entrust-handler --- examples/ca_handler/entrust_ca_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ca_handler/entrust_ca_handler.py b/examples/ca_handler/entrust_ca_handler.py index d854683a..b3fddf55 100644 --- a/examples/ca_handler/entrust_ca_handler.py +++ b/examples/ca_handler/entrust_ca_handler.py @@ -265,6 +265,7 @@ def _config_session_load(self, config_dic: Dict[str, str]): else: self._config_passphrase_load(config_dic) if 'client_cert' in config_dic['CAhandler'] and self.cert_passphrase: + self.logger.debug('CAhandler._config_session_load() cert and passphrase') self.session.mount(self.api_url, Pkcs12Adapter(pkcs12_filename=config_dic['CAhandler']['client_cert'], pkcs12_password=self.cert_passphrase)) else: self.logger.warning('CAhandler._config_load() configuration might be incomplete: "client_cert. "client_key" or "client_passphrase[_variable] parameter is missing in config file')