Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.apache.hadoop.hdds.security.x509.certificate.client;

import org.apache.hadoop.hdds.scm.client.ClientTrustManager;
import org.apache.hadoop.hdds.security.exception.OzoneSecurityException;
import org.apache.hadoop.hdds.security.ssl.ReloadingX509KeyManager;
import org.apache.hadoop.hdds.security.ssl.ReloadingX509TrustManager;
Expand Down Expand Up @@ -128,23 +129,6 @@ X509Certificate getCertificate(String certSerialId)
*/
Set<X509Certificate> getAllCaCerts();

/**
* Return the pem encoded CA certificate list.
* <p>
* If initialized return list of pem encoded CA certificates, else return
* null.
*
* @return list of pem encoded CA certificates.
*/
List<String> getCAList();

/**
* Update and returns the pem encoded CA certificate list.
* @return list of pem encoded CA certificates.
* @throws IOException
*/
List<String> updateCAList() throws IOException;

/**
* Verifies a digital Signature, given the signature and the certificate of
* the signer.
Expand Down Expand Up @@ -176,10 +160,32 @@ default void assertValidKeysAndCertificate() throws OzoneSecurityException {
}
}

/**
* Gets a KeyManager containing this CertificateClient's key material and trustchain.
* During certificate rotation this KeyManager is automatically updated with the new keys/certificates.
*
* @return A KeyManager containing keys and the trustchain for this CertificateClient.
* @throws CertificateException
*/
ReloadingX509KeyManager getKeyManager() throws CertificateException;

/**
* Gets a TrustManager containing the trusted certificates of this CertificateClient.
* During certificate rotation this TrustManager is automatically updated with the new certificates.
*
* @return A TrustManager containing trusted certificates for this CertificateClient.
* @throws CertificateException
*/
ReloadingX509TrustManager getTrustManager() throws CertificateException;

/**
* Creates a ClientTrustManager instance using the trusted certificates of this certificate client.
*
* @return The new ClientTrustManager instance.
* @throws IOException
*/
ClientTrustManager createClientTrustManager() throws IOException;

/**
* Register a receiver that will be called after the certificate renewed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.NodeReportProto;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineReportsProto;
import org.apache.hadoop.hdds.scm.client.ClientTrustManager;
import org.apache.hadoop.hdds.security.symmetric.SecretKeyClient;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.upgrade.HDDSLayoutVersionManager;
Expand Down Expand Up @@ -216,7 +217,7 @@ public DatanodeStateMachine(HddsDatanodeService hddsDatanodeService,
ReplicationSupervisorMetrics.create(supervisor);

ecReconstructionMetrics = ECReconstructionMetrics.create();

ClientTrustManager clientTrustManager = null;
ecReconstructionCoordinator = new ECReconstructionCoordinator(
conf, certClient, secretKeyClient, context, ecReconstructionMetrics,
threadNamePrefix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@
import org.apache.hadoop.hdds.scm.XceiverClientManager;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.scm.client.ClientTrustManager;
import org.apache.hadoop.hdds.security.x509.certificate.client.CACertificateProvider;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
import org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.utils.HAUtils;
import org.apache.hadoop.ozone.OzoneSecurityUtil;
import org.apache.hadoop.ozone.container.common.helpers.BlockData;
import org.apache.hadoop.security.token.Token;
Expand Down Expand Up @@ -69,21 +67,17 @@ public ECContainerOperationClient(ConfigurationSource conf,
}

@Nonnull
private static XceiverClientManager createClientManager(
ConfigurationSource conf, CertificateClient certificateClient)
private static XceiverClientManager createClientManager(ConfigurationSource conf, CertificateClient certificateClient)
throws IOException {
ClientTrustManager trustManager = null;
if (OzoneSecurityUtil.isSecurityEnabled(conf)) {
CACertificateProvider localCaCerts =
() -> HAUtils.buildCAX509List(certificateClient, conf);
CACertificateProvider remoteCacerts =
() -> HAUtils.buildCAX509List(null, conf);
trustManager = new ClientTrustManager(remoteCacerts, localCaCerts);
trustManager = certificateClient.createClientTrustManager();
}
return new XceiverClientManager(conf,
new XceiverClientManager.XceiverClientManagerConfigBuilder()
.setMaxCacheSize(256).setStaleThresholdMs(10 * 1000).build(),
trustManager);
XceiverClientManager.ScmClientConfig scmClientConfig = new XceiverClientManager.XceiverClientManagerConfigBuilder()
.setMaxCacheSize(256)
.setStaleThresholdMs(10 * 1000)
.build();
return new XceiverClientManager(conf, scmClientConfig, trustManager);
}

public BlockData[] listBlock(long containerId, DatanodeDetails dn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.scm.client.ClientTrustManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.hdds.security.ssl.ReloadingX509KeyManager;
import org.apache.hadoop.hdds.security.ssl.ReloadingX509TrustManager;
Expand Down Expand Up @@ -983,43 +984,6 @@ public Set<X509Certificate> getAllCaCerts() {
return certs;
}

@Override
public List<String> getCAList() {
pemEncodedCACertsLock.lock();
try {
return pemEncodedCACerts;
} finally {
pemEncodedCACertsLock.unlock();
}
}

public List<String> listCA() throws IOException {
pemEncodedCACertsLock.lock();
try {
if (pemEncodedCACerts == null) {
updateCAList();
}
return pemEncodedCACerts;
} finally {
pemEncodedCACertsLock.unlock();
}
}

@Override
public List<String> updateCAList() throws IOException {
pemEncodedCACertsLock.lock();
try {
pemEncodedCACerts = getScmSecureClient().listCACertificate();
return pemEncodedCACerts;
} catch (Exception e) {
getLogger().error("Error during updating CA list", e);
throw new CertificateException("Error during updating CA list", e,
CERTIFICATE_ERROR);
} finally {
pemEncodedCACertsLock.unlock();
}
}

@Override
public ReloadingX509TrustManager getTrustManager() throws CertificateException {
try {
Expand Down Expand Up @@ -1049,8 +1013,20 @@ public ReloadingX509KeyManager getKeyManager() throws CertificateException {
}
}

@Override
public ClientTrustManager createClientTrustManager() throws IOException {
CACertificateProvider caCertificateProvider = () -> {
List<X509Certificate> caCerts = new ArrayList<>();
caCerts.addAll(getAllCaCerts());
caCerts.addAll(getAllRootCaCerts());
return caCerts;
};
return new ClientTrustManager(caCertificateProvider, caCertificateProvider);
}

/**
* Register a receiver that will be called after the certificate renewed.
*
* @param receiver
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
import org.apache.hadoop.hdds.scm.proxy.SCMClientConfig;
import org.apache.hadoop.hdds.scm.proxy.SCMContainerLocationFailoverProxyProvider;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.hdds.utils.db.DBDefinition;
import org.apache.hadoop.hdds.utils.db.DBColumnFamilyDefinition;
Expand Down Expand Up @@ -373,80 +371,6 @@ public static List<String> getExistingSstFiles(File db) throws IOException {
return sstList;
}

/**
* Build CA list which need to be passed to client.
*
* If certificate client is null, obtain the list of CA using SCM security
* client, else it uses certificate client.
* @return list of CA
*/
public static List<String> buildCAList(CertificateClient certClient,
ConfigurationSource configuration) throws IOException {
long waitDuration =
configuration.getTimeDuration(OZONE_SCM_CA_LIST_RETRY_INTERVAL,
OZONE_SCM_CA_LIST_RETRY_INTERVAL_DEFAULT, TimeUnit.SECONDS);
if (certClient != null) {
if (!SCMHAUtils.isSCMHAEnabled(configuration)) {
return generateCAList(certClient);
} else {
Collection<String> scmNodes = SCMHAUtils.getSCMNodeIds(configuration);
int expectedCount = scmNodes.size() + 1;
if (scmNodes.size() > 1) {
// First check if cert client has ca list initialized.
// This is being done, when this method is called multiple times we
// don't make call to SCM, we return from in-memory.
List<String> caCertPemList = certClient.getCAList();
if (caCertPemList != null && caCertPemList.size() == expectedCount) {
return caCertPemList;
}
return getCAListWithRetry(() ->
waitForCACerts(certClient::updateCAList, expectedCount),
waitDuration);
} else {
return generateCAList(certClient);
}
}
} else {
SCMSecurityProtocolClientSideTranslatorPB scmSecurityProtocolClient =
HddsServerUtil.getScmSecurityClient(configuration);
if (!SCMHAUtils.isSCMHAEnabled(configuration)) {
List<String> caCertPemList = new ArrayList<>();
SCMGetCertResponseProto scmGetCertResponseProto =
scmSecurityProtocolClient.getCACert();
if (scmGetCertResponseProto.hasX509Certificate()) {
caCertPemList.add(scmGetCertResponseProto.getX509Certificate());
}
if (scmGetCertResponseProto.hasX509RootCACertificate()) {
caCertPemList.add(scmGetCertResponseProto.getX509RootCACertificate());
}
return caCertPemList;
} else {
Collection<String> scmNodes = SCMHAUtils.getSCMNodeIds(configuration);
int expectedCount = scmNodes.size() + 1;
if (scmNodes.size() > 1) {
return getCAListWithRetry(() -> waitForCACerts(
scmSecurityProtocolClient::listCACertificate,
expectedCount), waitDuration);
} else {
return scmSecurityProtocolClient.listCACertificate();
}
}
}
}

private static List<String> generateCAList(CertificateClient certClient)
throws IOException {
List<String> caCertPemList = new ArrayList<>();
for (X509Certificate cert : certClient.getAllRootCaCerts()) {
caCertPemList.add(CertificateCodec.getPEMEncodedString(cert));
}
for (X509Certificate cert : certClient.getAllCaCerts()) {
caCertPemList.add(CertificateCodec.getPEMEncodedString(cert));
}
return caCertPemList;
}


/**
* Retry forever until CA list matches expected count.
* @param task - task to get CA list.
Expand Down Expand Up @@ -488,23 +412,37 @@ private static List<String> waitForCACerts(
* Build CA List in the format of X509Certificate.
* If certificate client is null, obtain the list of CA using SCM
* security client, else it uses certificate client.
*
* @return list of CA X509Certificates.
*/
public static List<X509Certificate> buildCAX509List(
CertificateClient certClient,
ConfigurationSource conf) throws IOException {
if (certClient != null) {
// Do this here to avoid extra conversion of X509 to pem and again to
// X509 by buildCAList.
if (!SCMHAUtils.isSCMHAEnabled(conf)) {
List<X509Certificate> x509Certificates = new ArrayList<>();
x509Certificates.addAll(certClient.getAllCaCerts());
x509Certificates.addAll(certClient.getAllRootCaCerts());
return x509Certificates;
public static List<X509Certificate> buildCAX509List(ConfigurationSource conf) throws IOException {
long waitDuration =
conf.getTimeDuration(OZONE_SCM_CA_LIST_RETRY_INTERVAL,
OZONE_SCM_CA_LIST_RETRY_INTERVAL_DEFAULT, TimeUnit.SECONDS);
Collection<String> scmNodes = SCMHAUtils.getSCMNodeIds(conf);
SCMSecurityProtocolClientSideTranslatorPB scmSecurityProtocolClient =
HddsServerUtil.getScmSecurityClient(conf);
if (!SCMHAUtils.isSCMHAEnabled(conf)) {
List<String> caCertPemList = new ArrayList<>();
SCMGetCertResponseProto scmGetCertResponseProto =
scmSecurityProtocolClient.getCACert();
if (scmGetCertResponseProto.hasX509Certificate()) {
caCertPemList.add(scmGetCertResponseProto.getX509Certificate());
}
if (scmGetCertResponseProto.hasX509RootCACertificate()) {
caCertPemList.add(scmGetCertResponseProto.getX509RootCACertificate());
}
return OzoneSecurityUtil.convertToX509(caCertPemList);
} else {
int expectedCount = scmNodes.size() + 1;
if (scmNodes.size() > 1) {
return OzoneSecurityUtil.convertToX509(getCAListWithRetry(() -> waitForCACerts(
scmSecurityProtocolClient::listCACertificate,
expectedCount), waitDuration));
} else {
return OzoneSecurityUtil.convertToX509(scmSecurityProtocolClient.listCACertificate());
}
}
List<String> pemEncodedCerts = HAUtils.buildCAList(certClient, conf);
return OzoneSecurityUtil.convertToX509(pemEncodedCerts);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.scm.client.ClientTrustManager;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.hdds.security.ssl.ReloadingX509KeyManager;
Expand Down Expand Up @@ -257,16 +258,6 @@ public Set<X509Certificate> getAllCaCerts() {
return rootCerts;
}

@Override
public List<String> getCAList() {
return null;
}

@Override
public List<String> updateCAList() throws IOException {
return null;
}

public void renewRootCA() throws Exception {
LocalDateTime start = LocalDateTime.now();
Duration rootCACertDuration = securityConfig.getMaxCertificateDuration();
Expand Down Expand Up @@ -364,6 +355,17 @@ public ReloadingX509TrustManager getTrustManager() throws CertificateException {
}
}

@Override
public ClientTrustManager createClientTrustManager() throws IOException {
CACertificateProvider caCertificateProvider = () -> {
List<X509Certificate> caCerts = new ArrayList<>();
caCerts.addAll(getAllCaCerts());
caCerts.addAll(getAllRootCaCerts());
return caCerts;
};
return new ClientTrustManager(caCertificateProvider, caCertificateProvider);
}

@Override
public void registerNotificationReceiver(CertificateNotification receiver) {
synchronized (notificationReceivers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1613,8 +1613,7 @@ private void persistSCMCertificates() throws IOException {
if (primaryScmNodeId != null && !primaryScmNodeId.equals(
scmStorageConfig.getScmId())) {
List<String> pemEncodedCerts =
scmCertificateClient.listCA();

getScmSecurityClientWithMaxRetry(configuration, getCurrentUser()).listCACertificate();
// Write the primary SCM CA and Root CA during startup.
for (String cert : pemEncodedCerts) {
X509Certificate x509Certificate = CertificateCodec.getX509Certificate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private XceiverClientManager newXCeiverClientManager(ConfigurationSource conf)
throws IOException {
XceiverClientManager manager;
if (OzoneSecurityUtil.isSecurityEnabled(conf)) {
CACertificateProvider caCerts = () -> HAUtils.buildCAX509List(null, conf);
CACertificateProvider caCerts = () -> HAUtils.buildCAX509List(conf);
manager = new XceiverClientManager(conf,
conf.getObject(XceiverClientManager.ScmClientConfig.class),
new ClientTrustManager(caCerts, null));
Expand Down
Loading