Skip to content

Commit 6465125

Browse files
ajayydvxiaoyuyao
authored andcommitted
HDDS-594. SCM CA: DN sends CSR and uses certificate issued by SCM. Contributed by Ajay Kumar. (#547)
(cherry picked from commit 064f38b)
1 parent 118986c commit 6465125

File tree

10 files changed

+552
-39
lines changed

10 files changed

+552
-39
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.apache.hadoop.hdds;
2020

2121
import javax.management.ObjectName;
22+
import java.io.IOException;
2223
import java.lang.reflect.InvocationTargetException;
2324
import java.lang.reflect.Method;
2425
import java.net.InetSocketAddress;
@@ -36,7 +37,15 @@
3637
import org.apache.hadoop.conf.Configuration;
3738
import org.apache.hadoop.fs.CommonConfigurationKeys;
3839
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
40+
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
41+
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolPB;
3942
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
43+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
44+
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
45+
import org.apache.hadoop.hdds.scm.protocolPB.ScmBlockLocationProtocolPB;
46+
import org.apache.hadoop.ipc.Client;
47+
import org.apache.hadoop.ipc.ProtobufRpcEngine;
48+
import org.apache.hadoop.ipc.RPC;
4049
import org.apache.hadoop.metrics2.util.MBeans;
4150
import org.apache.hadoop.net.DNS;
4251
import org.apache.hadoop.net.NetUtils;
@@ -48,6 +57,8 @@
4857
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HOST_NAME_KEY;
4958
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ENABLED;
5059
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ENABLED_DEFAULT;
60+
61+
import org.apache.hadoop.security.UserGroupInformation;
5162
import org.slf4j.Logger;
5263
import org.slf4j.LoggerFactory;
5364

@@ -161,6 +172,29 @@ public static InetSocketAddress getScmAddressForBlockClients(
161172
.orElse(ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_PORT_DEFAULT));
162173
}
163174

175+
/**
176+
* Create a scm security client.
177+
* @param conf - Ozone configuration.
178+
* @param address - inet socket address of scm.
179+
*
180+
* @return {@link SCMSecurityProtocol}
181+
* @throws IOException
182+
*/
183+
public static SCMSecurityProtocol getScmSecurityClient(
184+
OzoneConfiguration conf, InetSocketAddress address) throws IOException {
185+
RPC.setProtocolEngine(conf, SCMSecurityProtocolPB.class,
186+
ProtobufRpcEngine.class);
187+
long scmVersion =
188+
RPC.getProtocolVersion(ScmBlockLocationProtocolPB.class);
189+
SCMSecurityProtocolClientSideTranslatorPB scmSecurityClient =
190+
new SCMSecurityProtocolClientSideTranslatorPB(
191+
RPC.getProxy(SCMSecurityProtocolPB.class, scmVersion,
192+
address, UserGroupInformation.getCurrentUser(),
193+
conf, NetUtils.getDefaultSocketFactory(conf),
194+
Client.getRpcTimeout(conf)));
195+
return scmSecurityClient;
196+
}
197+
164198
/**
165199
* Retrieve the hostname, trying the supplied config keys in order.
166200
* Each config value may be absent, or if present in the format

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateApprover.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,22 @@ public interface CertificateApprover {
6060
* @param validFrom - Begin Date
6161
* @param validTill - End Date
6262
* @param certificationRequest - Certification Request.
63+
* @param scmId - SCM id.
64+
* @param clusterId - Cluster id.
6365
* @return Signed Certificate.
6466
* @throws IOException - On Error
6567
* @throws OperatorCreationException - on Error.
6668
*/
69+
@SuppressWarnings("ParameterNumber")
6770
X509CertificateHolder sign(
6871
SecurityConfig config,
6972
PrivateKey caPrivate,
7073
X509CertificateHolder caCertificate,
7174
Date validFrom,
7275
Date validTill,
73-
PKCS10CertificationRequest certificationRequest)
76+
PKCS10CertificationRequest certificationRequest,
77+
String scmId,
78+
String clusterId)
7479
throws IOException, OperatorCreationException;
7580

7681

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultApprover.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
2323
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
2424
import org.apache.hadoop.hdds.security.x509.certificate.authority.PKIProfiles.PKIProfile;
25+
import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
2526
import org.apache.hadoop.util.Time;
27+
import org.bouncycastle.asn1.x500.X500Name;
28+
import org.bouncycastle.asn1.x500.style.BCStyle;
2629
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
2730
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
2831
import org.bouncycastle.cert.X509CertificateHolder;
@@ -67,18 +70,22 @@ public DefaultApprover(PKIProfile pkiProfile, SecurityConfig config) {
6770
* @param validFrom - Begin Da te
6871
* @param validTill - End Date
6972
* @param certificationRequest - Certification Request.
73+
* @param scmId - SCM id.
74+
* @param clusterId - Cluster id.
7075
* @return Signed Certificate.
7176
* @throws IOException - On Error
7277
* @throws OperatorCreationException - on Error.
7378
*/
79+
@SuppressWarnings("ParameterNumber")
7480
public X509CertificateHolder sign(
7581
SecurityConfig config,
7682
PrivateKey caPrivate,
7783
X509CertificateHolder caCertificate,
7884
Date validFrom,
7985
Date validTill,
80-
PKCS10CertificationRequest certificationRequest)
81-
throws IOException, OperatorCreationException {
86+
PKCS10CertificationRequest certificationRequest,
87+
String scmId,
88+
String clusterId) throws IOException, OperatorCreationException {
8289

8390
AlgorithmIdentifier sigAlgId = new
8491
DefaultSignatureAlgorithmIdentifierFinder().find(
@@ -91,6 +98,29 @@ public X509CertificateHolder sign(
9198
SubjectPublicKeyInfo keyInfo =
9299
certificationRequest.getSubjectPublicKeyInfo();
93100

101+
// Get scmId and cluster Id from subject name.
102+
X500Name x500Name = certificationRequest.getSubject();
103+
String csrScmId = x500Name.getRDNs(BCStyle.OU)[0].getFirst().getValue().
104+
toASN1Primitive().toString();
105+
String csrClusterId = x500Name.getRDNs(BCStyle.O)[0].getFirst().getValue().
106+
toASN1Primitive().toString();
107+
108+
if (!scmId.equals(csrScmId) || !clusterId.equals(csrClusterId)) {
109+
if (csrScmId.equalsIgnoreCase("null") &&
110+
csrClusterId.equalsIgnoreCase("null")) {
111+
// Special case to handle DN certificate generation as DN might not know
112+
// scmId and clusterId before registration. In secure mode registration
113+
// will succeed only after datanode has a valid certificate.
114+
String cn = x500Name.getRDNs(BCStyle.CN)[0].getFirst().getValue()
115+
.toASN1Primitive().toString();
116+
x500Name = SecurityUtil.getDistinguishedName(cn, scmId, clusterId);
117+
} else {
118+
// Throw exception if scmId and clusterId doesn't match.
119+
throw new SCMSecurityException("ScmId and ClusterId in CSR subject" +
120+
" are incorrect.");
121+
}
122+
}
123+
94124
RSAKeyParameters rsa =
95125
(RSAKeyParameters) PublicKeyFactory.createKey(keyInfo);
96126
if (rsa.getModulus().bitLength() < config.getSize()) {
@@ -104,7 +134,7 @@ public X509CertificateHolder sign(
104134
BigInteger.valueOf(Time.monotonicNowNanos()),
105135
validFrom,
106136
validTill,
107-
certificationRequest.getSubject(), keyInfo);
137+
x500Name, keyInfo);
108138

109139
ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
110140
.build(asymmetricKP);

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/DefaultCAServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ public Future<X509CertificateHolder> requestCertificate(
227227
X509CertificateHolder xcert = approver.sign(config,
228228
getCAKeys().getPrivate(),
229229
getCACertificate(), java.sql.Date.valueOf(beginDate),
230-
java.sql.Date.valueOf(endDate), csr);
230+
java.sql.Date.valueOf(endDate), csr, scmID, clusterID);
231231
store.storeValidCertificate(xcert.getSerialNumber(),
232232
CertificateCodec.getX509Certificate(xcert));
233233
xcertHolder.complete(xcert);

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/utils/CertificateSignRequest.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,6 @@ public PKCS10CertificationRequest build() throws SCMSecurityException {
269269
Preconditions.checkNotNull(key, "KeyPair cannot be null");
270270
Preconditions.checkArgument(Strings.isNotBlank(subject), "Subject " +
271271
"cannot be blank");
272-
Preconditions.checkArgument(Strings.isNotBlank(clusterID), "Cluster ID " +
273-
"cannot be blank");
274-
Preconditions.checkArgument(Strings.isNotBlank(scmID), "SCM ID cannot " +
275-
"be blank");
276272

277273
try {
278274
CertificateSignRequest csr = new CertificateSignRequest(subject, scmID,

hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/MockApprover.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ public MockApprover(PKIProfile pkiProfile, SecurityConfig config) {
4747

4848
@Override
4949
public X509CertificateHolder sign(SecurityConfig config, PrivateKey caPrivate,
50-
X509CertificateHolder caCertificate,
51-
Date validFrom, Date validTill,
52-
PKCS10CertificationRequest request)
50+
X509CertificateHolder caCertificate,
51+
Date validFrom, Date validTill,
52+
PKCS10CertificationRequest request,
53+
String scmId, String clusterId)
5354
throws IOException, OperatorCreationException {
5455
return null;
5556
}

hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/authority/TestDefaultCAServer.java

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
2626
import org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest;
2727
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
28+
import org.apache.hadoop.test.LambdaTestUtils;
2829
import org.bouncycastle.cert.X509CertificateHolder;
2930
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
3031
import org.junit.Before;
@@ -139,14 +140,57 @@ public void testMissingKey() {
139140
public void testRequestCertificate() throws IOException,
140141
ExecutionException, InterruptedException,
141142
NoSuchProviderException, NoSuchAlgorithmException {
143+
String scmId = RandomStringUtils.randomAlphabetic(4);
144+
String clusterId = RandomStringUtils.randomAlphabetic(4);
145+
KeyPair keyPair =
146+
new HDDSKeyGenerator(conf).generateKey();
147+
PKCS10CertificationRequest csr = new CertificateSignRequest.Builder()
148+
.addDnsName("hadoop.apache.org")
149+
.addIpAddress("8.8.8.8")
150+
.setCA(false)
151+
.setClusterID(clusterId)
152+
.setScmID(scmId)
153+
.setSubject("Ozone Cluster")
154+
.setConfiguration(conf)
155+
.setKey(keyPair)
156+
.build();
157+
158+
// Let us convert this to a string to mimic the common use case.
159+
String csrString = CertificateSignRequest.getEncodedString(csr);
160+
161+
CertificateServer testCA = new DefaultCAServer("testCA",
162+
clusterId, scmId, caStore);
163+
testCA.init(new SecurityConfig(conf),
164+
CertificateServer.CAType.SELF_SIGNED_CA);
165+
166+
Future<X509CertificateHolder> holder = testCA.requestCertificate(csrString,
167+
CertificateApprover.ApprovalType.TESTING_AUTOMATIC);
168+
// Right now our calls are synchronous. Eventually this will have to wait.
169+
assertTrue(holder.isDone());
170+
assertNotNull(holder.get());
171+
}
172+
173+
/**
174+
* Tests that we are able
175+
* to create a Test CA, creates it own self-Signed CA and then issue a
176+
* certificate based on a CSR when scmId and clusterId are not set in
177+
* csr subject.
178+
* @throws SCMSecurityException - on ERROR.
179+
* @throws ExecutionException - on ERROR.
180+
* @throws InterruptedException - on ERROR.
181+
* @throws NoSuchProviderException - on ERROR.
182+
* @throws NoSuchAlgorithmException - on ERROR.
183+
*/
184+
@Test
185+
public void testRequestCertificateWithInvalidSubject() throws IOException,
186+
ExecutionException, InterruptedException,
187+
NoSuchProviderException, NoSuchAlgorithmException {
142188
KeyPair keyPair =
143189
new HDDSKeyGenerator(conf).generateKey();
144190
PKCS10CertificationRequest csr = new CertificateSignRequest.Builder()
145191
.addDnsName("hadoop.apache.org")
146192
.addIpAddress("8.8.8.8")
147193
.setCA(false)
148-
.setClusterID("ClusterID")
149-
.setScmID("SCMID")
150194
.setSubject("Ozone Cluster")
151195
.setConfiguration(conf)
152196
.setKey(keyPair)
@@ -168,4 +212,40 @@ public void testRequestCertificate() throws IOException,
168212
assertNotNull(holder.get());
169213
}
170214

215+
@Test
216+
public void testRequestCertificateWithInvalidSubjectFailure()
217+
throws Exception {
218+
KeyPair keyPair =
219+
new HDDSKeyGenerator(conf).generateKey();
220+
PKCS10CertificationRequest csr = new CertificateSignRequest.Builder()
221+
.addDnsName("hadoop.apache.org")
222+
.addIpAddress("8.8.8.8")
223+
.setCA(false)
224+
.setScmID("wrong one")
225+
.setClusterID("223432rf")
226+
.setSubject("Ozone Cluster")
227+
.setConfiguration(conf)
228+
.setKey(keyPair)
229+
.build();
230+
231+
// Let us convert this to a string to mimic the common use case.
232+
String csrString = CertificateSignRequest.getEncodedString(csr);
233+
234+
CertificateServer testCA = new DefaultCAServer("testCA",
235+
RandomStringUtils.randomAlphabetic(4),
236+
RandomStringUtils.randomAlphabetic(4), caStore);
237+
testCA.init(new SecurityConfig(conf),
238+
CertificateServer.CAType.SELF_SIGNED_CA);
239+
240+
LambdaTestUtils.intercept(ExecutionException.class, "ScmId and " +
241+
"ClusterId in CSR subject are incorrect",
242+
() -> {
243+
Future<X509CertificateHolder> holder =
244+
testCA.requestCertificate(csrString,
245+
CertificateApprover.ApprovalType.TESTING_AUTOMATIC);
246+
holder.isDone();
247+
holder.get();
248+
});
249+
}
250+
171251
}

hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -213,24 +213,6 @@ public void testGenerateCSRWithInvalidParams() throws NoSuchProviderException,
213213
builder.setSubject(subject);
214214
}
215215

216-
// Now try with blank/null SCM ID
217-
try {
218-
builder.setScmID(null);
219-
builder.build();
220-
Assert.fail("Null/Blank SCM ID should have thrown.");
221-
} catch (IllegalArgumentException e) {
222-
builder.setScmID(scmID);
223-
}
224-
225-
// Now try with blank/null SCM ID
226-
try {
227-
builder.setClusterID(null);
228-
builder.build();
229-
Assert.fail("Null/Blank Cluster ID should have thrown.");
230-
} catch (IllegalArgumentException e) {
231-
builder.setClusterID(clusterID);
232-
}
233-
234216
// Now try with invalid IP address
235217
try {
236218
builder.addIpAddress("255.255.255.*");

0 commit comments

Comments
 (0)