Skip to content

Commit 9c61494

Browse files
ajayydvxiaoyuyao
authored andcommitted
HDDS-1065. OM and DN should persist SCM certificate as the trust root. Contributed by Ajay Kumar. (#834)
1 parent a315913 commit 9c61494

File tree

10 files changed

+135
-37
lines changed

10 files changed

+135
-37
lines changed

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public static InetSocketAddress getScmAddressForBlockClients(
178178
* @return {@link SCMSecurityProtocol}
179179
* @throws IOException
180180
*/
181-
public static SCMSecurityProtocol getScmSecurityClient(
181+
public static SCMSecurityProtocolClientSideTranslatorPB getScmSecurityClient(
182182
OzoneConfiguration conf, InetSocketAddress address) throws IOException {
183183
RPC.setProtocolEngine(conf, SCMSecurityProtocolPB.class,
184184
ProtobufRpcEngine.class);

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto;
2424
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto;
2525
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCACertificateRequestProto;
26+
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
2627
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertificateRequestProto;
2728
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertificateRequestProto.Builder;
2829
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto;
@@ -79,18 +80,8 @@ public void close() throws IOException {
7980
@Override
8081
public String getDataNodeCertificate(DatanodeDetailsProto dataNodeDetails,
8182
String certSignReq) throws IOException {
82-
SCMGetDataNodeCertRequestProto.Builder builder =
83-
SCMGetDataNodeCertRequestProto
84-
.newBuilder()
85-
.setCSR(certSignReq)
86-
.setDatanodeDetails(dataNodeDetails);
87-
try {
88-
return rpcProxy
89-
.getDataNodeCertificate(NULL_RPC_CONTROLLER, builder.build())
90-
.getX509Certificate();
91-
} catch (ServiceException e) {
92-
throw ProtobufHelper.getRemoteException(e);
93-
}
83+
return getDataNodeCertificateChain(dataNodeDetails, certSignReq)
84+
.getX509Certificate();
9485
}
9586

9687
/**
@@ -103,13 +94,25 @@ public String getDataNodeCertificate(DatanodeDetailsProto dataNodeDetails,
10394
@Override
10495
public String getOMCertificate(OzoneManagerDetailsProto omDetails,
10596
String certSignReq) throws IOException {
97+
return getOMCertChain(omDetails, certSignReq).getX509Certificate();
98+
}
99+
100+
/**
101+
* Get SCM signed certificate for OM.
102+
*
103+
* @param omDetails - OzoneManager Details.
104+
* @param certSignReq - Certificate signing request.
105+
* @return byte[] - SCM signed certificate.
106+
*/
107+
public SCMGetCertResponseProto getOMCertChain(
108+
OzoneManagerDetailsProto omDetails, String certSignReq)
109+
throws IOException {
106110
SCMGetOMCertRequestProto.Builder builder = SCMGetOMCertRequestProto
107111
.newBuilder()
108112
.setCSR(certSignReq)
109113
.setOmDetails(omDetails);
110114
try {
111-
return rpcProxy.getOMCertificate(NULL_RPC_CONTROLLER, builder.build())
112-
.getX509Certificate();
115+
return rpcProxy.getOMCertificate(NULL_RPC_CONTROLLER, builder.build());
113116
} catch (ServiceException e) {
114117
throw ProtobufHelper.getRemoteException(e);
115118
}
@@ -135,6 +138,28 @@ public String getCertificate(String certSerialId) throws IOException {
135138
}
136139
}
137140

141+
/**
142+
* Get SCM signed certificate for Datanode.
143+
*
144+
* @param dnDetails - Datanode Details.
145+
* @param certSignReq - Certificate signing request.
146+
* @return byte[] - SCM signed certificate.
147+
*/
148+
public SCMGetCertResponseProto getDataNodeCertificateChain(
149+
DatanodeDetailsProto dnDetails, String certSignReq)
150+
throws IOException {
151+
SCMGetDataNodeCertRequestProto.Builder builder =
152+
SCMGetDataNodeCertRequestProto.newBuilder()
153+
.setCSR(certSignReq)
154+
.setDatanodeDetails(dnDetails);
155+
try {
156+
return rpcProxy.getDataNodeCertificate(NULL_RPC_CONTROLLER,
157+
builder.build());
158+
} catch (ServiceException e) {
159+
throw ProtobufHelper.getRemoteException(e);
160+
}
161+
}
162+
138163
/**
139164
* Get CA certificate.
140165
*

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolServerSideTranslatorPB.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ public SCMGetCertResponseProto getDataNodeCertificate(
6161
SCMGetCertResponseProto
6262
.newBuilder()
6363
.setResponseCode(ResponseCode.success)
64-
.setX509Certificate(certificate);
64+
.setX509Certificate(certificate)
65+
.setX509CACertificate(impl.getCACertificate());
66+
6567
return builder.build();
6668
} catch (IOException e) {
6769
throw new ServiceException(e);
@@ -87,7 +89,8 @@ public SCMGetCertResponseProto getOMCertificate(
8789
SCMGetCertResponseProto
8890
.newBuilder()
8991
.setResponseCode(ResponseCode.success)
90-
.setX509Certificate(certificate);
92+
.setX509Certificate(certificate)
93+
.setX509CACertificate(impl.getCACertificate());
9194
return builder.build();
9295
} catch (IOException e) {
9396
throw new ServiceException(e);

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ boolean verifySignature(byte[] data, byte[] signature,
141141
void storeCertificate(String pemEncodedCert, boolean force)
142142
throws CertificateException;
143143

144+
/**
145+
* Stores the Certificate for this client. Don't use this api to add
146+
* trusted certificates of others.
147+
*
148+
* @param pemEncodedCert - pem encoded X509 Certificate
149+
* @param force - override any existing file
150+
* @param caCert - Is CA certificate.
151+
* @throws CertificateException - on Error.
152+
*
153+
*/
154+
void storeCertificate(String pemEncodedCert, boolean force, boolean caCert)
155+
throws CertificateException;
156+
144157
/**
145158
* Stores the trusted chain of certificates.
146159
*

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
public abstract class DefaultCertificateClient implements CertificateClient {
8181

8282
private static final String CERT_FILE_NAME_FORMAT = "%s.crt";
83+
private static final String CA_CERT_PREFIX = "CA-";
8384
private final Logger logger;
8485
private final SecurityConfig securityConfig;
8586
private final KeyCodec keyCodec;
@@ -452,14 +453,30 @@ public X509Certificate queryCertificate(String query) {
452453
* Stores the Certificate for this client. Don't use this api to add trusted
453454
* certificates of others.
454455
*
455-
* @param pemEncodedCert - pem encoded X509 Certificate
456-
* @param force - override any existing file
456+
* @param pemEncodedCert - pem encoded X509 Certificate
457+
* @param force - override any existing file
457458
* @throws CertificateException - on Error.
458459
*
459460
*/
460461
@Override
461462
public void storeCertificate(String pemEncodedCert, boolean force)
462463
throws CertificateException {
464+
this.storeCertificate(pemEncodedCert, force, false);
465+
}
466+
467+
/**
468+
* Stores the Certificate for this client. Don't use this api to add trusted
469+
* certificates of others.
470+
*
471+
* @param pemEncodedCert - pem encoded X509 Certificate
472+
* @param force - override any existing file
473+
* @param caCert - Is CA certificate.
474+
* @throws CertificateException - on Error.
475+
*
476+
*/
477+
@Override
478+
public void storeCertificate(String pemEncodedCert, boolean force,
479+
boolean caCert) throws CertificateException {
463480
CertificateCodec certificateCodec = new CertificateCodec(securityConfig);
464481
try {
465482
Path basePath = securityConfig.getCertificateLocation();
@@ -469,6 +486,10 @@ public void storeCertificate(String pemEncodedCert, boolean force)
469486
String certName = String.format(CERT_FILE_NAME_FORMAT,
470487
cert.getSerialNumber().toString());
471488

489+
if(caCert) {
490+
certName = CA_CERT_PREFIX + certName;
491+
}
492+
472493
certificateCodec.writeCertificate(basePath, certName,
473494
pemEncodedCert, force);
474495
certificateMap.putIfAbsent(cert.getSerialNumber().toString(), cert);

hadoop-hdds/common/src/main/proto/SCMSecurityProtocol.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ message SCMGetCertResponseProto {
7676
}
7777
required ResponseCode responseCode = 1;
7878
required string x509Certificate = 2; // Base64 encoded X509 certificate.
79+
optional string x509CACertificate = 3; // Base64 encoded CA X509 certificate.
7980
}
8081

8182

hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
2727
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
2828
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
29-
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
29+
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
30+
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
3031
import org.apache.hadoop.hdds.scm.HddsServerUtil;
3132
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
3233
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
@@ -271,16 +272,25 @@ private void getSCMSignedCert(OzoneConfiguration config) {
271272
try {
272273
PKCS10CertificationRequest csr = getCSR(config);
273274
// TODO: For SCM CA we should fetch certificate from multiple SCMs.
274-
SCMSecurityProtocol secureScmClient =
275+
SCMSecurityProtocolClientSideTranslatorPB secureScmClient =
275276
HddsUtils.getScmSecurityClient(config,
276277
HddsUtils.getScmAddressForSecurityProtocol(config));
277-
278-
String pemEncodedCert = secureScmClient.getDataNodeCertificate(
279-
datanodeDetails.getProtoBufMessage(), getEncodedString(csr));
280-
dnCertClient.storeCertificate(pemEncodedCert, true);
281-
datanodeDetails.setCertSerialId(getX509Certificate(pemEncodedCert).
282-
getSerialNumber().toString());
283-
persistDatanodeDetails(datanodeDetails);
278+
SCMGetCertResponseProto response = secureScmClient.
279+
getDataNodeCertificateChain(datanodeDetails.getProtoBufMessage(),
280+
getEncodedString(csr));
281+
// Persist certificates.
282+
if(response.hasX509CACertificate()) {
283+
String pemEncodedCert = response.getX509Certificate();
284+
dnCertClient.storeCertificate(pemEncodedCert, true);
285+
dnCertClient.storeCertificate(response.getX509CACertificate(), true,
286+
true);
287+
datanodeDetails.setCertSerialId(getX509Certificate(pemEncodedCert).
288+
getSerialNumber().toString());
289+
persistDatanodeDetails(datanodeDetails);
290+
} else {
291+
throw new RuntimeException("Unable to retrieve datanode certificate " +
292+
"chain");
293+
}
284294
} catch (IOException | CertificateException e) {
285295
LOG.error("Error while storing SCM signed certificate.", e);
286296
throw new RuntimeException(e);

hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
4141
import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
4242
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
43+
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
4344
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
4445
import org.apache.hadoop.hdds.security.x509.keys.KeyCodec;
4546
import org.apache.hadoop.io.Text;
@@ -98,6 +99,7 @@
9899
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED;
99100
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_NOT_FOUND;
100101
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
102+
import static org.junit.Assert.assertEquals;
101103
import static org.junit.Assert.assertFalse;
102104
import static org.junit.Assert.assertNull;
103105
import static org.junit.Assert.assertTrue;
@@ -780,6 +782,12 @@ public void testSecureOmInitSuccess() throws Exception {
780782
"SCM signed certificate"));
781783
X509Certificate certificate = om.getCertificateClient().getCertificate();
782784
validateCertificate(certificate);
785+
String pemEncodedCACert =
786+
scm.getSecurityProtocolServer().getCACertificate();
787+
X509Certificate caCert = CertificateCodec.getX509Cert(pemEncodedCACert);
788+
X509Certificate caCertStored = om.getCertificateClient()
789+
.getCertificate(caCert.getSerialNumber().toString());
790+
assertEquals(caCert, caCertStored);
783791
} finally {
784792
if (scm != null) {
785793
scm.stop();

hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/CertificateClientTestImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ public X509Certificate queryCertificate(String query) {
139139
@Override
140140
public void storeCertificate(String cert, boolean force)
141141
throws CertificateException {
142+
}
142143

144+
@Override
145+
public void storeCertificate(String cert, boolean force, boolean caCert)
146+
throws CertificateException {
143147
}
144148

145149
/**

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
4444
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
4545
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
46+
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
4647
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
4748
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolPB;
4849
import org.apache.hadoop.hdds.scm.ScmInfo;
@@ -785,8 +786,8 @@ private static ScmBlockLocationProtocol getScmBlockClient(
785786
* @return {@link SCMSecurityProtocol}
786787
* @throws IOException
787788
*/
788-
private static SCMSecurityProtocol getScmSecurityClient(
789-
OzoneConfiguration conf) throws IOException {
789+
private static SCMSecurityProtocolClientSideTranslatorPB
790+
getScmSecurityClient(OzoneConfiguration conf) throws IOException {
790791
RPC.setProtocolEngine(conf, SCMSecurityProtocolPB.class,
791792
ProtobufRpcEngine.class);
792793
long scmVersion =
@@ -1455,16 +1456,28 @@ private static void getSCMSignedCert(CertificateClient client,
14551456
HddsProtos.OzoneManagerDetailsProto omDetailsProto =
14561457
omDetailsProtoBuilder.build();
14571458
LOG.info("OzoneManager ports added:{}", omDetailsProto.getPortsList());
1458-
SCMSecurityProtocol secureScmClient = getScmSecurityClient(config);
1459+
SCMSecurityProtocolClientSideTranslatorPB secureScmClient =
1460+
getScmSecurityClient(config);
14591461

1460-
String pemEncodedCert = secureScmClient.getOMCertificate(omDetailsProto,
1461-
getEncodedString(csr));
1462+
SCMGetCertResponseProto response = secureScmClient.
1463+
getOMCertChain(omDetailsProto, getEncodedString(csr));
1464+
String pemEncodedCert = response.getX509Certificate();
14621465

14631466
try {
1464-
client.storeCertificate(pemEncodedCert, true);
1465-
// Persist om cert serial id.
1466-
omStore.setOmCertSerialId(CertificateCodec.
1467-
getX509Certificate(pemEncodedCert).getSerialNumber().toString());
1467+
1468+
1469+
// Store SCM CA certificate.
1470+
if(response.hasX509CACertificate()) {
1471+
String pemEncodedRootCert = response.getX509CACertificate();
1472+
client.storeCertificate(pemEncodedRootCert, true, true);
1473+
client.storeCertificate(pemEncodedCert, true);
1474+
// Persist om cert serial id.
1475+
omStore.setOmCertSerialId(CertificateCodec.
1476+
getX509Certificate(pemEncodedCert).getSerialNumber().toString());
1477+
} else {
1478+
throw new RuntimeException("Unable to retrieve OM certificate " +
1479+
"chain");
1480+
}
14681481
} catch (IOException | CertificateException e) {
14691482
LOG.error("Error while storing SCM signed certificate.", e);
14701483
throw new RuntimeException(e);

0 commit comments

Comments
 (0)