From c2c242dd31c8054536680d80eb1c274616c1afbd Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Tue, 12 Nov 2024 14:25:01 -0800 Subject: [PATCH] add support to provide server-side bundles per key-id (#2796) Signed-off-by: Henry Avetisyan --- servers/zts/conf/zts.properties | 30 +- servers/zts/pom.xml | 2 +- .../java/com/yahoo/athenz/zts/ZTSConsts.java | 17 +- .../athenz/zts/cert/InstanceCertManager.java | 85 ++++- .../zts/cert/InstanceCertManagerTest.java | 298 ++++++++++++++++++ servers/zts/src/test/resources/ca-cert-1 | 1 + servers/zts/src/test/resources/ca-cert-2 | 1 + 7 files changed, 410 insertions(+), 24 deletions(-) create mode 100644 servers/zts/src/test/resources/ca-cert-1 create mode 100644 servers/zts/src/test/resources/ca-cert-2 diff --git a/servers/zts/conf/zts.properties b/servers/zts/conf/zts.properties index c4fad5ee6e3..a4fdc1e7205 100644 --- a/servers/zts/conf/zts.properties +++ b/servers/zts/conf/zts.properties @@ -383,23 +383,47 @@ athenz.zts.cert_signer_factory_class=com.yahoo.athenz.zts.cert.impl.SelfCertSign # CA certificates. This is useful when there are multiple Athenz instances # running in different regions/locations and each region/location has its own # CA certificate and during instance register/refresh operation we want to -# return the full set of CA certs +# return the full set of CA certs. This is considered as the default +# CA certificate bundle file name. The admin can also specify additional +# bundles per key-id using the athenz.zts.x509_ca_cert_keyid_fname +# system property. #athenz.zts.x509_ca_cert_fname= +# If configured, specifies a file names that contains the bundle of Athenz +# CA certificates for a given key-id. The format of the value is comma +# separated list of : values. +#athenz.zts.x509_ca_cert_keyid_fname= + # If configured, specifies a file name that contains the SSH Host certificate # Signer certificates. This is useful when there are multiple Athenz instances # running in different regions/locations and each region/location has its own # Signer certificate and during instance register/refresh operation we want to -# return the full set of certs +# return the full set of certs. This is considered as the default +# certificate bundle file name. The admin can also specify additional +# bundles per key-id using the athenz.zts.ssh_host_ca_cert_keyid_fname +# system property. #athenz.zts.ssh_host_ca_cert_fname= +# If configured, specifies a file names that contains the bundle of Athenz +# SSHCA certificates for a given key-id. The format of the value is comma +# separated list of : values. +#athenz.zts.ssh_host_ca_cert_keyid_fname= + # If configured, specifies a file name that contains the SSH User certificate # Signer certificates. This is useful when there are multiple Athenz instances # running in different regions/locations and each region/location has its own # Signer certificate and during instance register/refresh operation we want to -# return the full set of certs +# return the full set of certs. This is considered as the default +## certificate bundle file name. The admin can also specify additional +## bundles per key-id using the athenz.zts.ssh_user_ca_cert_keyid_fname +## system property. #athenz.zts.ssh_user_ca_cert_fname= +# If configured, specifies a file names that contains the bundle of Athenz +# SSHCA certificates for a given key-id. The format of the value is comma +# separated list of : values. +#athenz.zts.ssh_user_ca_cert_keyid_fname= + # The number of milliseconds to sleep between runs of the idle object # evictor thread. When non-positive, no idle object evictor thread # will be run. The pool default is -1, but we're using 30 minutes to diff --git a/servers/zts/pom.xml b/servers/zts/pom.xml index ebb579769c9..b878f5568e1 100644 --- a/servers/zts/pom.xml +++ b/servers/zts/pom.xml @@ -29,7 +29,7 @@ war - 0.9875 + 0.9928 diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java index 8f125b91602..27bdb5e797a 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java @@ -127,13 +127,16 @@ public final class ZTSConsts { public static final String ZTS_PROP_SYSTEM_AUTHZ_DETAILS_PATH = "athenz.zts.system_authz_details_path"; public static final String ZTS_PROP_SERVICE_CERT_DEFAULT_EXPIRY_MINS = "athenz.zts.service_cert_default_expiry_mins"; - public static final String ZTS_PROP_PROVIDER_ENDPOINTS = "athenz.zts.provider_endpoints"; - public static final String ZTS_PROP_INSTANCE_NTOKEN_TIMEOUT = "athenz.zts.instance_token_timeout"; - public static final String ZTS_PROP_X509_CA_CERT_FNAME = "athenz.zts.x509_ca_cert_fname"; - public static final String ZTS_PROP_SSH_HOST_CA_CERT_FNAME = "athenz.zts.ssh_host_ca_cert_fname"; - public static final String ZTS_PROP_SSH_USER_CA_CERT_FNAME = "athenz.zts.ssh_user_ca_cert_fname"; - public static final String ZTS_PROP_RESP_X509_SIGNER_CERTS = "athenz.zts.resp_x509_signer_certs"; - public static final String ZTS_PROP_RESP_SSH_SIGNER_CERTS = "athenz.zts.resp_ssh_signer_certs"; + public static final String ZTS_PROP_PROVIDER_ENDPOINTS = "athenz.zts.provider_endpoints"; + public static final String ZTS_PROP_INSTANCE_NTOKEN_TIMEOUT = "athenz.zts.instance_token_timeout"; + public static final String ZTS_PROP_X509_CA_CERT_FNAME = "athenz.zts.x509_ca_cert_fname"; + public static final String ZTS_PROP_X509_CA_CERT_KEYID_FNAME = "athenz.zts.x509_ca_cert_keyid_fname"; + public static final String ZTS_PROP_SSH_HOST_CA_CERT_FNAME = "athenz.zts.ssh_host_ca_cert_fname"; + public static final String ZTS_PROP_SSH_HOST_CA_CERT_KEYID_FNAME = "athenz.zts.ssh_host_ca_cert_keyid_fname"; + public static final String ZTS_PROP_SSH_USER_CA_CERT_FNAME = "athenz.zts.ssh_user_ca_cert_fname"; + public static final String ZTS_PROP_SSH_USER_CA_CERT_KEYID_FNAME = "athenz.zts.ssh_user_ca_cert_keyid_fname"; + public static final String ZTS_PROP_RESP_X509_SIGNER_CERTS = "athenz.zts.resp_x509_signer_certs"; + public static final String ZTS_PROP_RESP_SSH_SIGNER_CERTS = "athenz.zts.resp_ssh_signer_certs"; public static final String DB_PROP_USER = "user"; public static final String DB_PROP_PASSWORD = "password"; diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java index 2e6aa749c96..de36433debb 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java @@ -200,16 +200,13 @@ private boolean loadCertificateAuthorityBundles() { // if we're not asked to skip sending certificate signers then // check to see if we need to load them from files instead of - // certsigner directly + // cert-signer directly if (responseSendX509SignerCerts) { - caX509CertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); - caX509ProviderCertificateSigners = new ConcurrentHashMap<>(); + initializeX509ProviderCertificateSigners(); } if (responseSendSSHSignerCerts) { - sshUserCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_FNAME); - sshHostCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_FNAME); - caSshProviderCertificateSigners = new ConcurrentHashMap<>(); + initializeSSHProviderCertificateSigners(); } // now let's fetch our configured certificate authority bundles @@ -252,6 +249,63 @@ private boolean loadCertificateAuthorityBundles() { return true; } + void initializeSSHProviderCertificateSigners() { + + // first load our default ssh certificate signers that will be used + // if no key-id specific signer is available + + sshUserCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_FNAME); + sshHostCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_FNAME); + + // initialize our provider bundle map and then check to see + // if we have configured key-id specific providers. These must + // be configured as a list of key-id:filename pairs + + caSshProviderCertificateSigners = new ConcurrentHashMap<>(); + iniitializeProviderCertificateSigners(caSshProviderCertificateSigners, ZTSConsts.ZTS_SSH_HOST, + ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_KEYID_FNAME); + iniitializeProviderCertificateSigners(caSshProviderCertificateSigners, ZTSConsts.ZTS_SSH_USER, + ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_KEYID_FNAME); + } + + void initializeX509ProviderCertificateSigners() { + + // first load our default certificate bundle that will be used + // if no key-id specific bundle is available + + caX509CertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); + + // initialize our provider bundle map and then check to see + // if we have configured key-id specific providers. These must + // be configured as a list of key-id:filename pairs + + caX509ProviderCertificateSigners = new ConcurrentHashMap<>(); + iniitializeProviderCertificateSigners(caX509ProviderCertificateSigners, null, + ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME); + } + + void iniitializeProviderCertificateSigners(Map certSigners, final String reqType, final String propName) { + + final String providerKeyBundles = System.getProperty(propName, ""); + for (String providerKeyBundle : providerKeyBundles.split(",")) { + if (providerKeyBundle.isEmpty()) { + continue; + } + String[] providerKey = providerKeyBundle.split(":"); + if (providerKey.length != 2) { + throw new ResourceException(ResourceException.INTERNAL_SERVER_ERROR, + "Invalid provider certificate configuration value: " + propName + ": " + providerKeyBundle); + } + byte[] data = ZTSUtils.readFileContents(providerKey[1]); + if (data == null) { + throw new ResourceException(ResourceException.INTERNAL_SERVER_ERROR, + "Unable to load Certificate bundle from: " + providerKey[1]); + } + final String keyName = reqType == null ? providerKey[0] : reqType + "." + providerKey[0]; + certSigners.put(keyName, new String(data)); + } + } + private boolean processCertificateAuthorityBundle(CertBundle bundle) { final String name = bundle.getName(); @@ -736,9 +790,7 @@ public String getX509CertificateSigner(final String provider, final String signe return null; } - if (caX509CertificateSigner != null) { - return caX509CertificateSigner; - } + // first check to see if we have a provider specific key configured final String providerKeyName = getSignerPrimaryKey(provider, signerKeyId); String certificateSigner = caX509ProviderCertificateSigners.get(providerKeyName); @@ -746,6 +798,14 @@ public String getX509CertificateSigner(final String provider, final String signe return certificateSigner; } + // check to see if we have a default bundle configured + + if (caX509CertificateSigner != null) { + return caX509CertificateSigner; + } + + // fetch the bundle from the cert-signer and update our provider map + certificateSigner = getCACertificate(provider, signerKeyId); if (certificateSigner != null) { caX509ProviderCertificateSigners.put(providerKeyName, certificateSigner); @@ -954,14 +1014,13 @@ String getSSHCertificateSigner(final String sshReqType, final String signerKeyId return null; } - String certificateSigner = sshReqType.equals(ZTSConsts.ZTS_SSH_HOST) ? - sshHostCertificateSigner : sshUserCertificateSigner; + final String primaryKeyName = sshReqType + "." + (StringUtil.isEmpty(signerKeyId) ? "default" : signerKeyId); + String certificateSigner = caSshProviderCertificateSigners.get(primaryKeyName); if (certificateSigner != null) { return certificateSigner; } - final String primaryKeyName = sshReqType + "." + (StringUtil.isEmpty(signerKeyId) ? "default" : signerKeyId); - certificateSigner = caSshProviderCertificateSigners.get(primaryKeyName); + certificateSigner = sshReqType.equals(ZTSConsts.ZTS_SSH_HOST) ? sshHostCertificateSigner : sshUserCertificateSigner; if (certificateSigner != null) { return certificateSigner; } diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java index ed0a5f779ec..ecfe0400578 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java @@ -177,6 +177,26 @@ public void testGenerateIdentityNullCert() throws ServerResourceException { assertNull(identity); instanceManager.shutdown(); } + + @Test + public void testGenerateIdentityExceptions() throws ServerResourceException { + + System.clearProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); + + CertSigner certSigner = Mockito.mock(com.yahoo.athenz.common.server.cert.CertSigner.class); + when(certSigner.generateX509Certificate(any(), any(), any(), any(), anyInt(), any(), any())) + .thenThrow(new ServerResourceException(400, "invalid get request")); + when(certSigner.getCACertificate(any(), any())) + .thenThrow(new ServerResourceException(400, "invalid ca request")); + + InstanceCertManager instanceManager = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + instanceManager.setCertSigner(certSigner); + + assertNull(instanceManager.generateX509Certificate("aws", "us-west-2", "csr", "sign", 0, + Priority.Unspecified_priority, null)); + assertNull(instanceManager.getX509CertificateSigner("aws", "keyid")); + instanceManager.shutdown(); + } @Test public void testGenerateIdentityEmptyCert() throws ServerResourceException { @@ -402,6 +422,22 @@ public void testGetSSHCertificateSignerCAFiles() { System.clearProperty("athenz.zts.ssh_user_ca_cert_fname"); } + @Test + public void testGetSSHCertificateSignerFailure() throws ServerResourceException { + + System.clearProperty("athenz.zts.ssh_host_ca_cert_fname"); + System.clearProperty("athenz.zts.ssh_user_ca_cert_fname"); + + SSHSigner sshSigner = Mockito.mock(com.yahoo.athenz.common.server.ssh.SSHSigner.class); + when(sshSigner.getSignerCertificate(ZTSConsts.ZTS_SSH_HOST, null)) + .thenThrow(new ServerResourceException(400, "invalid request")); + InstanceCertManager instanceManager = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(true)); + instanceManager.setSSHSigner(sshSigner); + + assertNull(instanceManager.getSSHCertificateSigner("host", null)); + instanceManager.shutdown(); + } + @Test public void testGetSSHCertificateSignerKeyId() throws ServerResourceException { @@ -1288,6 +1324,30 @@ public void testGetSSHCertificates() throws ServerResourceException { instanceCertManager.shutdown(); } + @Test + public void testGetSSHCertificatesException() throws ServerResourceException { + + InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(true)); + instanceCertManager.setSSHSigner(null); + + SSHSigner signer = Mockito.mock(SSHSigner.class); + Principal principal = Mockito.mock(Principal.class); + SSHCertRequest certRequest = new SSHCertRequest(); + certRequest.setCertRequestMeta(new SSHCertRequestMeta()); + SSHCertificates certs = new SSHCertificates(); + when(signer.generateCertificate(principal, certRequest, null, null, null)) + .thenThrow(new ServerResourceException(400, "Invalid request")); + instanceCertManager.setSSHSigner(signer); + + try { + instanceCertManager.generateSSHCertificates(principal, certRequest, null); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid request")); + } + instanceCertManager.shutdown(); + } + @Test public void testInvalidCertSignerClass() { @@ -2196,4 +2256,242 @@ public void testGetSignerPrimaryKey() { instance.shutdown(); } + + @Test + public void testGetX509CertificateSignerPerProviderKey() throws ServerResourceException { + + System.clearProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); + System.setProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME, "key1:src/test/resources/ca-cert-1,key2:src/test/resources/ca-cert-2"); + + InstanceCertManager instanceManager = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + CertSigner certSigner = Mockito.mock(com.yahoo.athenz.common.server.cert.CertSigner.class); + when(certSigner.getCACertificate("aws", "key3")).thenReturn("ca-cert-3"); + instanceManager.setCertSigner(certSigner); + + // first two entries are coming from our configuration + + assertEquals(instanceManager.getX509CertificateSigner("aws", "key1"), "ca-cert-1"); + assertEquals(instanceManager.getX509CertificateSigner("aws", "key2"), "ca-cert-2"); + + // the last one is coming from the provider + + assertEquals(instanceManager.getX509CertificateSigner("aws", "key3"), "ca-cert-3"); + + // others return null + + assertNull(instanceManager.getX509CertificateSigner("aws", "key4")); + + instanceManager.shutdown(); + System.clearProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME); + } + + @Test + public void testGetSSHCertificateSignerPerProviderKey() throws ServerResourceException { + + System.clearProperty(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_FNAME); + System.clearProperty(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_FNAME); + System.setProperty(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_KEYID_FNAME, "key1:src/test/resources/ca-cert-1"); + System.setProperty(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_KEYID_FNAME, "key2:src/test/resources/ca-cert-2"); + + InstanceCertManager instanceManager = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + SSHSigner sshSigner = Mockito.mock(SSHSigner.class); + when(sshSigner.getSignerCertificate("host", "key3")).thenReturn("ca-cert-3"); + instanceManager.setSSHSigner(sshSigner); + + // first two entries are coming from our configuration + + assertEquals(instanceManager.getSSHCertificateSigner("host", "key1"), "ca-cert-1"); + assertEquals(instanceManager.getSSHCertificateSigner("user", "key2"), "ca-cert-2"); + + // the last one is coming from the provider + + assertEquals(instanceManager.getSSHCertificateSigner("host", "key3"), "ca-cert-3"); + + // all others return null + + assertNull(instanceManager.getSSHCertificateSigner("user", "key1")); + assertNull(instanceManager.getSSHCertificateSigner("host", "key2")); + assertNull(instanceManager.getSSHCertificateSigner("host", "key4")); + + instanceManager.shutdown(); + System.clearProperty(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_KEYID_FNAME); + System.clearProperty(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_KEYID_FNAME); + } + + @Test + public void testGetX509CertificateSignerPerProviderKeyInvalidFile() { + + System.setProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME, "key1:invalid-file"); + try { + new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate bundle from: invalid-file")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME, "key1"); + try { + new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid provider certificate configuration value: " + + ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME + ": key1")); + } + System.clearProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_KEYID_FNAME); + } + + @Test + public void testX509CertOperationFailures() throws ServerResourceException, IOException { + + InstanceCertManager instance = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + instance.setCertSigner(null); + + CertRecordStore certStore = Mockito.mock(CertRecordStore.class); + CertRecordStoreConnection certConnection = Mockito.mock(CertRecordStoreConnection.class); + when(certStore.getConnection()).thenReturn(certConnection); + + when(certConnection.getX509CertRecord(anyString(), anyString(), anyString())) + .thenThrow(new ServerResourceException(400, "Invalid get request")); + when(certConnection.updateX509CertRecord(any())).thenThrow(new ServerResourceException(400, "Invalid update request")); + when(certConnection.insertX509CertRecord(any())).thenThrow(new ServerResourceException(400, "Invalid insert request")); + when(certConnection.deleteX509CertRecord(anyString(), anyString(), anyString())) + .thenThrow(new ServerResourceException(400, "Invalid delete request")); + when(certConnection.updateUnrefreshedCertificatesNotificationTimestamp(anyString(), anyLong(), anyString())) + .thenThrow(new ServerResourceException(400, "Invalid update unrefreshed cert request")); + when(certConnection.deleteExpiredX509CertRecords(anyInt())) + .thenThrow(new ServerResourceException(400, "Invalid delete expired certs request")); + instance.setCertStore(certStore); + + // verify cleaner runs without any exceptions + + InstanceCertManager.ExpiredX509CertRecordCleaner cleaner = + new InstanceCertManager.ExpiredX509CertRecordCleaner(certStore, 100, new DynamicConfigBoolean(false)); + cleaner.run(); + + try { + instance.getUnrefreshedCertsNotifications("hostname", "provider"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid update unrefreshed cert request")); + } + + Path path = Paths.get("src/test/resources/athenz.instanceid.pem"); + String pem = new String(Files.readAllBytes(path)); + X509Certificate cert = Crypto.loadX509Certificate(pem); + + try { + instance.getX509CertRecord("ostk", cert); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid get request")); + } + + try { + instance.getX509CertRecord("ostk", "1001", "athenz.production"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid get request")); + } + + X509CertRecord certRecord = new X509CertRecord(); + try { + instance.updateX509CertRecord(certRecord); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid update request")); + } + + try { + instance.insertX509CertRecord(certRecord); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid insert request")); + } + + try { + instance.deleteX509CertRecord("ostk", "1001", "athenz.production"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid delete request")); + } + + instance.shutdown(); + } + + @Test + public void testSSHCertOperationFailures() throws ServerResourceException { + + InstanceCertManager instance = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + instance.setCertSigner(null); + + SSHRecordStore sshRecordStore = Mockito.mock(SSHRecordStore.class); + SSHRecordStoreConnection sshRecordStoreConnection = Mockito.mock(SSHRecordStoreConnection.class); + when(sshRecordStore.getConnection()).thenReturn(sshRecordStoreConnection); + + when(sshRecordStoreConnection.getSSHCertRecord(anyString(), anyString())) + .thenThrow(new ServerResourceException(400, "Invalid get request")); + when(sshRecordStoreConnection.updateSSHCertRecord(any())) + .thenThrow(new ServerResourceException(400, "Invalid update request")); + when(sshRecordStoreConnection.deleteExpiredSSHCertRecords(anyInt())) + .thenThrow(new ServerResourceException(400, "Invalid delete expired certs request")); + instance.setSSHStore(sshRecordStore); + + // verify cleaner runs without any exceptions + + InstanceCertManager.ExpiredSSHCertRecordCleaner cleaner = + new InstanceCertManager.ExpiredSSHCertRecordCleaner(sshRecordStore, 100, new DynamicConfigBoolean(false)); + cleaner.run(); + + try { + instance.getSSHCertRecord("instance", "service"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid get request")); + } + + SSHCertRecord sshCertRecord = new SSHCertRecord(); + assertFalse(instance.updateSSHCertRecord(sshCertRecord, true)); + + instance.shutdown(); + } + + @Test + public void testWorkloadOperationFailures() throws ServerResourceException { + + InstanceCertManager instance = new InstanceCertManager(null, null, null, new DynamicConfigBoolean(false)); + instance.setCertSigner(null); + + WorkloadRecordStore recordStore = Mockito.mock(WorkloadRecordStore.class); + WorkloadRecordStoreConnection storeConnection = Mockito.mock(WorkloadRecordStoreConnection.class); + when(recordStore.getConnection()).thenReturn(storeConnection); + + when(storeConnection.insertWorkloadRecord(any())) + .thenThrow(new ServerResourceException(400, "Invalid insert request")); + when(storeConnection.updateWorkloadRecord(any())) + .thenThrow(new ServerResourceException(400, "Invalid update request")); + when(storeConnection.getWorkloadRecordsByIp(any())) + .thenThrow(new ServerResourceException(400, "Invalid get ip request")); + when(storeConnection.getWorkloadRecordsByService(any(), any())) + .thenThrow(new ServerResourceException(400, "Invalid get service request")); + instance.setWorkloadStore(recordStore); + + assertFalse(instance.insertWorkloadRecord(new WorkloadRecord())); + assertFalse(instance.updateWorkloadRecord(new WorkloadRecord())); + + try { + instance.getWorkloadsByIp("127.0.0.1"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid get ip request")); + } + + try { + instance.getWorkloadsByService("domain", "service"); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("Invalid get service request")); + } + + instance.shutdown(); + } } diff --git a/servers/zts/src/test/resources/ca-cert-1 b/servers/zts/src/test/resources/ca-cert-1 new file mode 100644 index 00000000000..b118110021a --- /dev/null +++ b/servers/zts/src/test/resources/ca-cert-1 @@ -0,0 +1 @@ +ca-cert-1 \ No newline at end of file diff --git a/servers/zts/src/test/resources/ca-cert-2 b/servers/zts/src/test/resources/ca-cert-2 new file mode 100644 index 00000000000..cae553b6c96 --- /dev/null +++ b/servers/zts/src/test/resources/ca-cert-2 @@ -0,0 +1 @@ +ca-cert-2 \ No newline at end of file