From e321b91cb5b22542d0fe80cd03ea3f59fac8e569 Mon Sep 17 00:00:00 2001 From: Xiaoyu Yao Date: Wed, 23 Jan 2019 19:32:03 -0800 Subject: [PATCH] HDDS-980. Adding getOMCertificate in SCMSecurityProtocol. Contributed by Ajay Kumar. --- .../hdds/protocol/SCMSecurityProtocol.java | 13 +++- ...ecurityProtocolClientSideTranslatorPB.java | 27 +++++++- ...ecurityProtocolServerSideTranslatorPB.java | 38 +++++++++-- .../authority/CertificateApprover.java | 2 +- .../certificate/utils/CertificateCodec.java | 2 +- .../src/main/proto/SCMSecurityProtocol.proto | 38 +++++++---- hadoop-hdds/common/src/main/proto/hdds.proto | 11 ++++ .../scm/server/SCMSecurityProtocolServer.java | 65 ++++++++++++++++--- .../scm/server/StorageContainerManager.java | 10 +-- .../server/TestSCMSecurityProtocolServer.java | 2 +- 10 files changed, 170 insertions(+), 38 deletions(-) diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java index f0ae41cc3c..000e5abe72 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java @@ -19,6 +19,7 @@ import java.io.IOException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto; import org.apache.hadoop.hdds.scm.ScmConfigKeys; import org.apache.hadoop.security.KerberosInfo; @@ -34,11 +35,21 @@ public interface SCMSecurityProtocol { * Get SCM signed certificate for DataNode. * * @param dataNodeDetails - DataNode Details. - * @param certSignReq - Certificate signing request. + * @param certSignReq - Certificate signing request. * @return byte[] - SCM signed certificate. */ String getDataNodeCertificate( DatanodeDetailsProto dataNodeDetails, String certSignReq) throws IOException; + /** + * Get SCM signed certificate for OM. + * + * @param omDetails - DataNode Details. + * @param certSignReq - Certificate signing request. + * @return byte[] - SCM signed certificate. + */ + String getOMCertificate(OzoneManagerDetailsProto omDetails, + String certSignReq) throws IOException; + } diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java index 1726282182..c1895bc188 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java @@ -21,12 +21,15 @@ import java.io.Closeable; import java.io.IOException; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto; import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto; import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol; import org.apache.hadoop.ipc.ProtobufHelper; import org.apache.hadoop.ipc.ProtocolTranslator; import org.apache.hadoop.ipc.RPC; +import static org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetOMCertRequestProto; + /** * This class is the client-side translator that forwards requests for * {@link SCMSecurityProtocol} to the {@link SCMSecurityProtocolPB} proxy. @@ -67,7 +70,7 @@ public void close() throws IOException { * Get SCM signed certificate for DataNode. * * @param dataNodeDetails - DataNode Details. - * @param certSignReq - Certificate signing request. + * @param certSignReq - Certificate signing request. * @return byte[] - SCM signed certificate. */ @Override @@ -87,6 +90,28 @@ public String getDataNodeCertificate(DatanodeDetailsProto dataNodeDetails, } } + /** + * Get SCM signed certificate for OM. + * + * @param omDetails - OzoneManager Details. + * @param certSignReq - Certificate signing request. + * @return byte[] - SCM signed certificate. + */ + @Override + public String getOMCertificate(OzoneManagerDetailsProto omDetails, + String certSignReq) throws IOException { + SCMGetOMCertRequestProto.Builder builder = SCMGetOMCertRequestProto + .newBuilder() + .setCSR(certSignReq) + .setOmDetails(omDetails); + try { + return rpcProxy.getOMCertificate(NULL_RPC_CONTROLLER, builder.build()) + .getX509Certificate(); + } catch (ServiceException e) { + throw ProtobufHelper.getRemoteException(e); + } + } + /** * Return the proxy object underlying this protocol translator. * diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolServerSideTranslatorPB.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolServerSideTranslatorPB.java index 40b9652919..6deb027608 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolServerSideTranslatorPB.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolServerSideTranslatorPB.java @@ -19,10 +19,12 @@ import com.google.protobuf.RpcController; import com.google.protobuf.ServiceException; import java.io.IOException; + +import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto; import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto; -import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertResponseProto; -import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertResponseProto.ResponseCode; +import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto.ResponseCode; import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol; +import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetOMCertRequestProto; /** * This class is the server-side translator that forwards requests received on @@ -46,15 +48,41 @@ public SCMSecurityProtocolServerSideTranslatorPB(SCMSecurityProtocol impl) { * @return SCMGetDataNodeCertResponseProto. */ @Override - public SCMGetDataNodeCertResponseProto getDataNodeCertificate( + public SCMGetCertResponseProto getDataNodeCertificate( RpcController controller, SCMGetDataNodeCertRequestProto request) throws ServiceException { try { String certificate = impl .getDataNodeCertificate(request.getDatanodeDetails(), request.getCSR()); - SCMGetDataNodeCertResponseProto.Builder builder = - SCMGetDataNodeCertResponseProto + SCMGetCertResponseProto.Builder builder = + SCMGetCertResponseProto + .newBuilder() + .setResponseCode(ResponseCode.success) + .setX509Certificate(certificate); + return builder.build(); + } catch (IOException e) { + throw new ServiceException(e); + } + } + + /** + * Get SCM signed certificate for OzoneManager. + * + * @param controller + * @param request + * @return SCMGetCertResponseProto. + */ + @Override + public SCMGetCertResponseProto getOMCertificate( + RpcController controller, SCMGetOMCertRequestProto request) + throws ServiceException { + try { + String certificate = impl + .getOMCertificate(request.getOmDetails(), + request.getCSR()); + SCMGetCertResponseProto.Builder builder = + SCMGetCertResponseProto .newBuilder() .setResponseCode(ResponseCode.success) .setX509Certificate(certificate); diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateApprover.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateApprover.java index fc62f69de1..f9adb61feb 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateApprover.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateApprover.java @@ -32,7 +32,7 @@ /** * Certificate Approver interface is used to inspectCSR a certificate. */ -interface CertificateApprover { +public interface CertificateApprover { /** * Approves a Certificate Request based on the policies of this approver. * diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java index b2a37b55bf..b3c40ef7bb 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/utils/CertificateCodec.java @@ -119,7 +119,7 @@ public Path getLocation() { * @return PEM Encoded Certificate String. * @throws SCMSecurityException - On failure to create a PEM String. */ - public String getPEMEncodedString(X509CertificateHolder x509CertHolder) + public static String getPEMEncodedString(X509CertificateHolder x509CertHolder) throws SCMSecurityException { try { StringWriter stringWriter = new StringWriter(); diff --git a/hadoop-hdds/common/src/main/proto/SCMSecurityProtocol.proto b/hadoop-hdds/common/src/main/proto/SCMSecurityProtocol.proto index 81a93c454f..bd8553d19e 100644 --- a/hadoop-hdds/common/src/main/proto/SCMSecurityProtocol.proto +++ b/hadoop-hdds/common/src/main/proto/SCMSecurityProtocol.proto @@ -34,10 +34,28 @@ package hadoop.hdds; import "hdds.proto"; +/** +* This message is send by data node to prove its identity and get an SCM +* signed certificate. +*/ +message SCMGetDataNodeCertRequestProto { + required DatanodeDetailsProto datanodeDetails = 1; + required string CSR = 2; +} + +/** +* This message is send by OzoneManager to prove its identity and get an SCM +* signed certificate. +*/ +message SCMGetOMCertRequestProto { + required OzoneManagerDetailsProto omDetails = 1; + required string CSR = 2; +} + /** * Returns a certificate signed by SCM. */ -message SCMGetDataNodeCertResponseProto { +message SCMGetCertResponseProto { enum ResponseCode { success = 1; authenticationFailed = 2; @@ -47,20 +65,18 @@ message SCMGetDataNodeCertResponseProto { required string x509Certificate = 2; // Base64 encoded X509 certificate. } -/** -* This message is send by data node to prove its identity and get an SCM -* signed certificate. -*/ -message SCMGetDataNodeCertRequestProto { - required DatanodeDetailsProto datanodeDetails = 1; - required string CSR = 2; -} - service SCMSecurityProtocolService { /** * Get SCM signed certificate for DataNode. */ - rpc getDataNodeCertificate (SCMGetDataNodeCertRequestProto) returns (SCMGetDataNodeCertResponseProto); + rpc getDataNodeCertificate (SCMGetDataNodeCertRequestProto) returns + (SCMGetCertResponseProto); + + /** + * Get SCM signed certificate for DataNode. + */ + rpc getOMCertificate (SCMGetOMCertRequestProto) returns + (SCMGetCertResponseProto); } diff --git a/hadoop-hdds/common/src/main/proto/hdds.proto b/hadoop-hdds/common/src/main/proto/hdds.proto index 5fb50a0b52..ab09669f3c 100644 --- a/hadoop-hdds/common/src/main/proto/hdds.proto +++ b/hadoop-hdds/common/src/main/proto/hdds.proto @@ -35,6 +35,17 @@ message DatanodeDetailsProto { repeated Port ports = 4; } +/** + Proto message encapsulating information required to uniquely identify a + OzoneManager. +*/ +message OzoneManagerDetailsProto { + required string uuid = 1; // UUID assigned to the OzoneManager. + required string ipAddress = 2; // IP address of OM. + required string hostName = 3; // Hostname of OM. + repeated Port ports = 4; +} + message Port { required string name = 1; required uint32 value = 2; diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java index ab29c1ede7..55904d8739 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java @@ -19,21 +19,32 @@ import com.google.protobuf.BlockingService; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto; import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos; import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolPB; import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolServerSideTranslatorPB; import org.apache.hadoop.hdds.scm.HddsServerUtil; import org.apache.hadoop.hdds.scm.ScmConfigKeys; import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol; +import org.apache.hadoop.hdds.security.x509.SecurityConfig; +import org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateServer; +import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec; import org.apache.hadoop.ipc.ProtobufRpcEngine; import org.apache.hadoop.ipc.RPC; import org.apache.hadoop.security.KerberosInfo; +import org.bouncycastle.cert.X509CertificateHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateApprover.ApprovalType.KERBEROS_TRUSTED; + /** * The protocol used to perform security related operations with SCM. */ @@ -44,15 +55,15 @@ public class SCMSecurityProtocolServer implements SCMSecurityProtocol { private static final Logger LOGGER = LoggerFactory .getLogger(SCMClientProtocolServer.class); - private final OzoneConfiguration config; - private final StorageContainerManager scm; + private final SecurityConfig config; + private final CertificateServer certificateServer; private final RPC.Server rpcServer; private final InetSocketAddress rpcAddress; SCMSecurityProtocolServer(OzoneConfiguration conf, - StorageContainerManager scm) throws IOException { - this.config = conf; - this.scm = scm; + CertificateServer certificateServer) throws IOException { + this.config = new SecurityConfig(conf); + this.certificateServer = certificateServer; final int handlerCount = conf.getInt(ScmConfigKeys.OZONE_SCM_SECURITY_HANDLER_COUNT_KEY, @@ -78,9 +89,9 @@ public class SCMSecurityProtocolServer implements SCMSecurityProtocol { /** * Get SCM signed certificate for DataNode. * - * @param dnDetails - DataNode Details. - * @param certSignReq - Certificate signing request. - * @return byte[] - SCM signed certificate. + * @param dnDetails - DataNode Details. + * @param certSignReq - Certificate signing request. + * @return String - SCM signed pem encoded certificate. */ @Override public String getDataNodeCertificate( @@ -88,8 +99,42 @@ public String getDataNodeCertificate( String certSignReq) throws IOException { LOGGER.info("Processing CSR for dn {}, UUID: {}", dnDetails.getHostName(), dnDetails.getUuid()); - // TODO: Call scm to sign the csr. - return null; + Objects.requireNonNull(dnDetails); + Future future = + certificateServer.requestCertificate(certSignReq, + KERBEROS_TRUSTED); + + try { + return CertificateCodec.getPEMEncodedString(future.get()); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("getDataNodeCertificate operation failed. ", e); + throw new IOException("getDataNodeCertificate operation failed. ", e); + } + } + + /** + * Get SCM signed certificate for OM. + * + * @param omDetails - OzoneManager Details. + * @param certSignReq - Certificate signing request. + * @return String - SCM signed pem encoded certificate. + */ + @Override + public String getOMCertificate(OzoneManagerDetailsProto omDetails, + String certSignReq) throws IOException { + LOGGER.info("Processing CSR for om {}, UUID: {}", omDetails.getHostName(), + omDetails.getUuid()); + Objects.requireNonNull(omDetails); + Future future = + certificateServer.requestCertificate(certSignReq, + KERBEROS_TRUSTED); + + try { + return CertificateCodec.getPEMEncodedString(future.get()); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("getOMCertificate operation failed. ", e); + throw new IOException("getOMCertificate operation failed. ", e); + } } public RPC.Server getRpcServer() { diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java index e7941fba2f..a7949db9ba 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java @@ -227,15 +227,16 @@ private StorageContainerManager(OzoneConfiguration conf) // TODO: Support Intermediary CAs in future. certificateServer.init(new SecurityConfig(conf), CertificateServer.CAType.SELF_SIGNED_CA); + securityProtocolServer = new SCMSecurityProtocolServer(conf, + certificateServer); } else { // if no Security, we do not create a Certificate Server at all. // This allows user to boot SCM without security temporarily // and then come back and enable it without any impact. certificateServer = null; + securityProtocolServer = null; } - - eventQueue = new EventQueue(); scmNodeManager = new SCMNodeManager( @@ -309,11 +310,6 @@ private StorageContainerManager(OzoneConfiguration conf) eventQueue); blockProtocolServer = new SCMBlockProtocolServer(conf, this); clientProtocolServer = new SCMClientProtocolServer(conf, this); - if (OzoneSecurityUtil.isSecurityEnabled(conf)) { - securityProtocolServer = new SCMSecurityProtocolServer(conf, this); - } else { - securityProtocolServer = null; - } httpServer = new StorageContainerManagerHttpServer(conf); eventQueue.addHandler(SCMEvents.DATANODE_COMMAND, scmNodeManager); diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/server/TestSCMSecurityProtocolServer.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/server/TestSCMSecurityProtocolServer.java index 8e7d84c4dd..78a6109756 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/server/TestSCMSecurityProtocolServer.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/server/TestSCMSecurityProtocolServer.java @@ -40,7 +40,7 @@ public void setUp() throws Exception { } @After - public void tearDown() throws Exception { + public void tearDown() { if (securityProtocolServer != null) { securityProtocolServer.stop(); securityProtocolServer = null;