You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by xy...@apache.org on 2018/12/04 16:17:41 UTC
[50/50] [abbrv] hadoop git commit: HDDS-9. Add GRPC protocol
interceptors for Ozone Block Token. Contributed by Xiaoyu Yao.
HDDS-9. Add GRPC protocol interceptors for Ozone Block Token. Contributed by Xiaoyu Yao.
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/a3b78ec1
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/a3b78ec1
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/a3b78ec1
Branch: refs/heads/HDDS-4
Commit: a3b78ec19ffb94034d988808fa06ff6b653c943e
Parents: e88f55a
Author: Xiaoyu Yao <xy...@apache.org>
Authored: Tue Nov 20 20:21:08 2018 -0800
Committer: Xiaoyu Yao <xy...@apache.org>
Committed: Tue Dec 4 08:03:16 2018 -0800
----------------------------------------------------------------------
.../hdds/scm/ClientCredentialInterceptor.java | 65 ++++
.../hadoop/hdds/scm/XceiverClientGrpc.java | 64 +++-
.../org/apache/hadoop/hdds/HddsConfigKeys.java | 5 +-
.../exception/SCMSecurityException.java | 52 +++
.../hdds/security/exception/package-info.java | 23 ++
.../security/token/BlockTokenException.java | 53 ++++
.../hdds/security/token/BlockTokenVerifier.java | 113 +++++++
.../token/OzoneBlockTokenIdentifier.java | 199 ++++++++++++
.../security/token/OzoneBlockTokenSelector.java | 55 ++++
.../hdds/security/token/TokenVerifier.java | 38 +++
.../hdds/security/token/package-info.java | 22 ++
.../hdds/security/x509/SecurityConfig.java | 11 +
.../authority/CertificateServer.java | 2 +-
.../certificate/client/CertificateClient.java | 11 +
.../certificates/CertificateSignRequest.java | 2 +-
.../certificates/SelfSignedCertificate.java | 2 +-
.../x509/exceptions/CertificateException.java | 14 +-
.../x509/exceptions/SCMSecurityException.java | 64 ----
.../org/apache/hadoop/ozone/OzoneConsts.java | 14 +
hadoop-hdds/common/src/main/proto/hdds.proto | 2 +-
.../token/TestOzoneBlockTokenIdentifier.java | 313 +++++++++++++++++++
.../hdds/security/token/package-info.java | 22 ++
.../TestCertificateSignRequest.java | 2 +-
.../x509/certificates/TestRootCertificate.java | 2 +-
.../server/ServerCredentialInterceptor.java | 74 +++++
.../transport/server/XceiverServerGrpc.java | 23 +-
.../security/OzoneBlockTokenIdentifier.java | 178 -----------
.../ozone/security/OzoneBlockTokenSelector.java | 55 ----
.../ozoneimpl/TestSecureOzoneContainer.java | 209 +++++++++++++
.../security/TestOzoneBlockTokenIdentifier.java | 255 ---------------
30 files changed, 1363 insertions(+), 581 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/ClientCredentialInterceptor.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/ClientCredentialInterceptor.java b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/ClientCredentialInterceptor.java
new file mode 100644
index 0000000..7a15808
--- /dev/null
+++ b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/ClientCredentialInterceptor.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.scm;
+
+import org.apache.ratis.thirdparty.io.grpc.CallOptions;
+import org.apache.ratis.thirdparty.io.grpc.Channel;
+import org.apache.ratis.thirdparty.io.grpc.ClientCall;
+import org.apache.ratis.thirdparty.io.grpc.ClientInterceptor;
+import org.apache.ratis.thirdparty.io.grpc.ForwardingClientCall;
+import org.apache.ratis.thirdparty.io.grpc.Metadata;
+import org.apache.ratis.thirdparty.io.grpc.MethodDescriptor;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OBT_METADATA_KEY;
+import static org.apache.hadoop.ozone.OzoneConsts.USER_METADATA_KEY;
+
+/**
+ * GRPC client interceptor for ozone block token.
+ */
+public class ClientCredentialInterceptor implements ClientInterceptor {
+
+ private final String user;
+ private final String token;
+
+ public ClientCredentialInterceptor(String user, String token) {
+ this.user = user;
+ this.token = token;
+ }
+
+ @Override
+ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
+ MethodDescriptor<ReqT, RespT> method,
+ CallOptions callOptions,
+ Channel next) {
+
+ return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
+ next.newCall(method, callOptions)) {
+ @Override
+ public void start(Listener<RespT> responseListener, Metadata headers) {
+ if (token != null) {
+ headers.put(OBT_METADATA_KEY, token);
+ }
+ if (user != null) {
+ headers.put(USER_METADATA_KEY, user);
+ }
+ super.start(responseListener, headers);
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
index a824c29..ccc9372 100644
--- a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
+++ b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientGrpc.java
@@ -31,22 +31,32 @@ import org.apache.hadoop.hdds.protocol.datanode.proto.XceiverClientProtocolServi
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
+import org.apache.hadoop.hdds.security.token.OzoneBlockTokenSelector;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.io.Text;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.security.SecurityUtil;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.util.Time;
import org.apache.ratis.thirdparty.io.grpc.ManagedChannel;
+import org.apache.ratis.thirdparty.io.grpc.Status;
import org.apache.ratis.thirdparty.io.grpc.netty.NettyChannelBuilder;
import org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.UUID;
-import java.util.Map;
-import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -93,7 +103,9 @@ public class XceiverClientGrpc extends XceiverClientSpi {
connectToDatanode(dn);
}
- private void connectToDatanode(DatanodeDetails dn) {
+
+ private void connectToDatanode(DatanodeDetails dn) throws IOException,
+ SCMSecurityException {
// read port from the data node, on failure use default configured
// port.
int port = dn.getPort(DatanodeDetails.Port.Name.STANDALONE).getValue();
@@ -101,16 +113,49 @@ public class XceiverClientGrpc extends XceiverClientSpi {
port = config.getInt(OzoneConfigKeys.DFS_CONTAINER_IPC_PORT,
OzoneConfigKeys.DFS_CONTAINER_IPC_PORT_DEFAULT);
}
+
+ // Add credential context to the client call
+ String userName = UserGroupInformation.getCurrentUser()
+ .getShortUserName();
+
+ // Add block token if block token (mutual auth) is required but the client
+ // does not have a mTLS (private key and ca signed certificate)
+ String encodedToken = null;
+ SecurityConfig secConfig = new SecurityConfig(config);
+ if (secConfig.isGrpcBlockTokenEnabled()) {
+ InetSocketAddress addr = new InetSocketAddress(dn.getIpAddress(), port);
+ encodedToken = getEncodedBlockToken(addr);
+ if (encodedToken == null) {
+ throw new SCMSecurityException("No Block token available to access " +
+ "service at : " + addr.toString());
+ }
+ }
LOG.debug("Connecting to server Port : " + dn.getIpAddress());
- ManagedChannel channel =
- NettyChannelBuilder.forAddress(dn.getIpAddress(), port).usePlaintext()
+ NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(dn
+ .getIpAddress(), port).usePlaintext()
.maxInboundMessageSize(OzoneConsts.OZONE_SCM_CHUNK_MAX_SIZE)
- .build();
+ .intercept(new ClientCredentialInterceptor(userName, encodedToken));
+ ManagedChannel channel = channelBuilder.build();
XceiverClientProtocolServiceStub asyncStub =
XceiverClientProtocolServiceGrpc.newStub(channel);
asyncStubs.put(dn.getUuid(), asyncStub);
channels.put(dn.getUuid(), channel);
}
+
+ private String getEncodedBlockToken(InetSocketAddress addr)
+ throws IOException{
+ UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+ OzoneBlockTokenSelector tokenSelector = new OzoneBlockTokenSelector();
+ Text service = SecurityUtil.buildTokenService(addr);
+ Token<OzoneBlockTokenIdentifier> token = tokenSelector.selectToken(
+ service, ugi.getTokens());
+ if (token != null) {
+ token.setService(service);
+ return token.encodeToUrlString();
+ }
+ return null;
+ }
+
/**
* Returns if the xceiver client connects to all servers in the pipeline.
*
@@ -173,6 +218,11 @@ public class XceiverClientGrpc extends XceiverClientSpi {
} catch (ExecutionException | InterruptedException e) {
LOG.warn("Failed to execute command " + request + " on datanode " + dn
.getUuidString(), e);
+ if (Status.fromThrowable(e.getCause()).getCode()
+ == Status.UNAUTHENTICATED.getCode()) {
+ throw new SCMSecurityException("Failed to authenticate with " +
+ "GRPC XceiverServer with Ozone block token.");
+ }
}
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
index ef1194b..553f4aa 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsConfigKeys.java
@@ -145,6 +145,7 @@ public final class HddsConfigKeys {
"hdds.x509.signature.algorithm";
public static final String HDDS_X509_SIGNATURE_ALGO_DEFAULT = "SHA256withRSA";
-
-
+ public static final String HDDS_GRPC_BLOCK_TOKEN_ENABLED = "hdds.grpc.block" +
+ ".token.enabled";
+ public static final boolean HDDS_GRPC_BLOCK_TOKEN_ENABLED_DEFAULT = false;
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
new file mode 100644
index 0000000..21bc45b
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.security.exception;
+
+import java.io.IOException;
+
+/**
+ * Root Security Exception call for all Certificate related Execptions.
+ */
+public class SCMSecurityException extends IOException {
+
+ /**
+ * Ctor.
+ * @param message - Error Message.
+ */
+ public SCMSecurityException(String message) {
+ super(message);
+ }
+
+ /**
+ * Ctor.
+ * @param message - Message.
+ * @param cause - Actual cause.
+ */
+ public SCMSecurityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Ctor.
+ * @param cause - Base Exception.
+ */
+ public SCMSecurityException(Throwable cause) {
+ super(cause);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/package-info.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/package-info.java
new file mode 100644
index 0000000..b980592
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * Exceptions thrown by SCM security classes.
+ */
+package org.apache.hadoop.hdds.security.exception;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenException.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenException.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenException.java
new file mode 100644
index 0000000..7ea0ebc
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenException.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.hadoop.hdds.security.token;
+
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+
+/**
+ * Block Token Exceptions from the SCM Security layer.
+ */
+public class BlockTokenException extends SCMSecurityException {
+
+ /**
+ * Ctor.
+ * @param message - Error Message.
+ */
+ public BlockTokenException(String message) {
+ super(message);
+ }
+
+ /**
+ * Ctor.
+ * @param message - Message.
+ * @param cause - Actual cause.
+ */
+ public BlockTokenException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Ctor.
+ * @param cause - Base Exception.
+ */
+ public BlockTokenException(Throwable cause) {
+ super(cause);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
new file mode 100644
index 0000000..8c0d19e
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/BlockTokenVerifier.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.security.token;
+
+import com.google.common.base.Strings;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.Time;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+
+/**
+ * Verify token and return a UGI with token if authenticated.
+ */
+public class BlockTokenVerifier implements TokenVerifier {
+
+ private final CertificateClient caClient;
+ private final SecurityConfig conf;
+
+ public BlockTokenVerifier(SecurityConfig conf, CertificateClient caClient) {
+ this.conf = conf;
+ this.caClient = caClient;
+ }
+
+ private boolean isExpired(long expiryDate) {
+ return Time.now() > expiryDate;
+ }
+
+ @Override
+ public UserGroupInformation verify(String user, String tokenStr)
+ throws SCMSecurityException {
+ if (conf.isGrpcBlockTokenEnabled()) {
+ if (Strings.isNullOrEmpty(tokenStr)) {
+ throw new BlockTokenException("Fail to find any token (empty or " +
+ "null.");
+ }
+ final Token<OzoneBlockTokenIdentifier> token = new Token();
+ OzoneBlockTokenIdentifier tokenId = new OzoneBlockTokenIdentifier();
+ try {
+ token.decodeFromUrlString(tokenStr);
+ ByteArrayInputStream buf = new ByteArrayInputStream(
+ token.getIdentifier());
+ DataInputStream in = new DataInputStream(buf);
+ tokenId.readFields(in);
+ } catch (IOException ex) {
+ throw new BlockTokenException("Failed to decode token : " + tokenStr);
+ }
+
+ // TODO: revisit this when caClient is ready, skip signature check now.
+ /**
+ * the final code should like
+ * if (caClient == null) {
+ * throw new SCMSecurityException("Certificate client not available to
+ * validate token");
+ * }
+ */
+ if (caClient != null) {
+ X509Certificate singerCert = caClient.queryCertificate(
+ "certId=" + tokenId.getOmCertSerialId());
+ if (singerCert == null) {
+ throw new BlockTokenException("Can't find signer certificate " +
+ "(OmCertSerialId: " + tokenId.getOmCertSerialId() +
+ ") of the block token for user: " + tokenId.getUser());
+ }
+ Boolean validToken = caClient.verifySignature(tokenId.getBytes(),
+ token.getPassword(), singerCert);
+ if (!validToken) {
+ throw new BlockTokenException("Invalid block token for user: " +
+ tokenId.getUser());
+ }
+ }
+ // check expiration
+ if (isExpired(tokenId.getExpiryDate())) {
+ UserGroupInformation tokenUser = tokenId.getUser();
+ tokenUser.setAuthenticationMethod(
+ UserGroupInformation.AuthenticationMethod.TOKEN);
+ throw new BlockTokenException("Expired block token for user: " +
+ tokenUser);
+ }
+ // defer access mode, bcsid and maxLength check to container dispatcher
+ UserGroupInformation ugi = tokenId.getUser();
+ ugi.addToken(token);
+ ugi.setAuthenticationMethod(UserGroupInformation
+ .AuthenticationMethod.TOKEN);
+ return ugi;
+ } else {
+ return UserGroupInformation.createRemoteUser(user);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenIdentifier.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenIdentifier.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenIdentifier.java
new file mode 100644
index 0000000..89457fd
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenIdentifier.java
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.security.token;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.Builder;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.TokenIdentifier;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.EnumSet;
+
+/**
+ * Block token identifier for Ozone/HDDS. Ozone block access token is similar
+ * to HDFS block access token, which is meant to be lightweight and
+ * short-lived. No need to renew or revoke a block access token. when a
+ * cached block access token expires, the client simply get a new one.
+ * Block access token should be cached only in memory and never write to disk.
+ */
+@InterfaceAudience.Private
+public class OzoneBlockTokenIdentifier extends TokenIdentifier {
+
+ static final Text KIND_NAME = new Text("HDDS_BLOCK_TOKEN");
+ private long expiryDate;
+ private String ownerId;
+ private String blockId;
+ private EnumSet<AccessModeProto> modes;
+ private String omCertSerialId;
+ private long maxLength;
+
+ public OzoneBlockTokenIdentifier() {
+ }
+
+ public OzoneBlockTokenIdentifier(String ownerId, String blockId,
+ EnumSet<AccessModeProto> modes, long expiryDate, String omCertSerialId,
+ long maxLength) {
+ this.ownerId = ownerId;
+ this.blockId = blockId;
+ this.expiryDate = expiryDate;
+ this.modes = modes == null ? EnumSet.noneOf(AccessModeProto.class) : modes;
+ this.omCertSerialId = omCertSerialId;
+ this.maxLength = maxLength;
+ }
+
+ @Override
+ public UserGroupInformation getUser() {
+ if (this.getOwnerId() == null || "".equals(this.getOwnerId())) {
+ return UserGroupInformation.createRemoteUser(blockId);
+ }
+ return UserGroupInformation.createRemoteUser(ownerId);
+ }
+
+ public long getExpiryDate() {
+ return expiryDate;
+ }
+
+ public String getOwnerId() {
+ return ownerId;
+ }
+
+ public String getBlockId() {
+ return blockId;
+ }
+
+ public EnumSet<AccessModeProto> getAccessModes() {
+ return modes;
+ }
+
+ public String getOmCertSerialId(){
+ return omCertSerialId;
+ }
+
+ public long getMaxLength() {
+ return maxLength;
+ }
+
+ @Override
+ public Text getKind() {
+ return KIND_NAME;
+ }
+
+ @Override
+ public String toString() {
+ return "block_token_identifier (expiryDate=" + this.getExpiryDate()
+ + ", ownerId=" + this.getOwnerId()
+ + ", omCertSerialId=" + this.getOmCertSerialId()
+ + ", blockId=" + this.getBlockId() + ", access modes="
+ + this.getAccessModes() + ", maxLength=" + this.getMaxLength() + ")";
+ }
+
+ static boolean isEqual(Object a, Object b) {
+ return a == null ? b == null : a.equals(b);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof OzoneBlockTokenIdentifier) {
+ OzoneBlockTokenIdentifier that = (OzoneBlockTokenIdentifier) obj;
+ return new EqualsBuilder()
+ .append(this.expiryDate, that.expiryDate)
+ .append(this.ownerId, that.ownerId)
+ .append(this.blockId, that.blockId)
+ .append(this.modes, that.modes)
+ .append(this.omCertSerialId, that.omCertSerialId)
+ .append(this.maxLength, that.maxLength)
+ .build();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(133, 567)
+ .append(this.expiryDate)
+ .append(this.blockId)
+ .append(this.ownerId)
+ .append(this.modes)
+ .append(this.omCertSerialId)
+ .append(this.maxLength)
+ .build();
+ }
+
+ @Override
+ public void readFields(DataInput in) throws IOException {
+ final DataInputStream dis = (DataInputStream) in;
+ if (!dis.markSupported()) {
+ throw new IOException("Could not peek first byte.");
+ }
+ BlockTokenSecretProto tokenPtoto =
+ BlockTokenSecretProto.parseFrom((DataInputStream) in);
+ this.ownerId = tokenPtoto.getOwnerId();
+ this.blockId = tokenPtoto.getBlockId();
+ this.modes = EnumSet.copyOf(tokenPtoto.getModesList());
+ this.expiryDate = tokenPtoto.getExpiryDate();
+ this.omCertSerialId = tokenPtoto.getOmCertSerialId();
+ this.maxLength = tokenPtoto.getMaxLength();
+ }
+
+ @VisibleForTesting
+ public static OzoneBlockTokenIdentifier readFieldsProtobuf(DataInput in)
+ throws IOException {
+ BlockTokenSecretProto tokenPtoto =
+ BlockTokenSecretProto.parseFrom((DataInputStream) in);
+ return new OzoneBlockTokenIdentifier(tokenPtoto.getOwnerId(),
+ tokenPtoto.getBlockId(), EnumSet.copyOf(tokenPtoto.getModesList()),
+ tokenPtoto.getExpiryDate(), tokenPtoto.getOmCertSerialId(),
+ tokenPtoto.getMaxLength());
+ }
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ writeProtobuf(out);
+ }
+
+ @VisibleForTesting
+ void writeProtobuf(DataOutput out) throws IOException {
+ Builder builder = BlockTokenSecretProto.newBuilder()
+ .setBlockId(this.getBlockId())
+ .setOwnerId(this.getOwnerId())
+ .setOmCertSerialId(this.getOmCertSerialId())
+ .setExpiryDate(this.getExpiryDate())
+ .setMaxLength(this.getMaxLength());
+ // Add access mode allowed
+ for (AccessModeProto mode : this.getAccessModes()) {
+ builder.addModes(AccessModeProto.valueOf(mode.name()));
+ }
+ out.write(builder.build().toByteArray());
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenSelector.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenSelector.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenSelector.java
new file mode 100644
index 0000000..1943cce
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/OzoneBlockTokenSelector.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hdds.security.token;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.security.token.TokenIdentifier;
+import org.apache.hadoop.security.token.TokenSelector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+/**
+ * A block token selector for Ozone.
+ */
+@InterfaceAudience.Private
+public class OzoneBlockTokenSelector implements
+ TokenSelector<OzoneBlockTokenIdentifier> {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(OzoneBlockTokenSelector.class);
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Token<OzoneBlockTokenIdentifier> selectToken(Text service,
+ Collection<Token<? extends TokenIdentifier>> tokens) {
+ if (service == null) {
+ return null;
+ }
+ for (Token<? extends TokenIdentifier> token : tokens) {
+ if (OzoneBlockTokenIdentifier.KIND_NAME.equals(token.getKind())) {
+ LOG.trace("Getting token for service:{}", service);
+ return (Token<OzoneBlockTokenIdentifier>) token;
+ }
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/TokenVerifier.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/TokenVerifier.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/TokenVerifier.java
new file mode 100644
index 0000000..d8170ab
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/TokenVerifier.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.security.token;
+
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.security.UserGroupInformation;
+
+/**
+ * Ozone GRPC token header verifier.
+ */
+public interface TokenVerifier {
+ /**
+ * Given a user and tokenStr header, return a UGI object with token if
+ * verified.
+ * @param user user of the request
+ * @param tokenStr token str of the request
+ * @return UGI
+ * @throws SCMSecurityException
+ */
+ UserGroupInformation verify(String user, String tokenStr)
+ throws SCMSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/package-info.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/package-info.java
new file mode 100644
index 0000000..885bed5
--- /dev/null
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/token/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the block token related test classes.
+ */
+package org.apache.hadoop.hdds.security.token;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
index 9231c8a..97627ca 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/SecurityConfig.java
@@ -34,6 +34,8 @@ import java.time.Duration;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_KEY_ALGORITHM;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_KEY_LEN;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_SECURITY_PROVIDER;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_BLOCK_TOKEN_ENABLED;
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_BLOCK_TOKEN_ENABLED_DEFAULT;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_ALGORITHM;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_DIR_NAME;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_DIR_NAME_DEFAULT;
@@ -70,6 +72,7 @@ public class SecurityConfig {
private final String publicKeyFileName;
private final Duration certDuration;
private final String x509SignatureAlgo;
+ private final Boolean grpcBlockTokenEnabled;
/**
* Constructs a SecurityConfig.
@@ -106,6 +109,10 @@ public class SecurityConfig {
this.x509SignatureAlgo = this.configuration.get(HDDS_X509_SIGNATURE_ALGO,
HDDS_X509_SIGNATURE_ALGO_DEFAULT);
+ this.grpcBlockTokenEnabled = this.configuration.getBoolean(
+ HDDS_GRPC_BLOCK_TOKEN_ENABLED,
+ HDDS_GRPC_BLOCK_TOKEN_ENABLED_DEFAULT);
+
// First Startup -- if the provider is null, check for the provider.
if (SecurityConfig.provider == null) {
synchronized (SecurityConfig.class) {
@@ -213,6 +220,10 @@ public class SecurityConfig {
return this.certDuration;
}
+ public Boolean isGrpcBlockTokenEnabled() {
+ return this.grpcBlockTokenEnabled;
+ }
+
/**
* Adds a security provider dynamically if it is not loaded already.
*
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
index 9332e5b..5b5deef 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/authority/CertificateServer.java
@@ -19,8 +19,8 @@
package org.apache.hadoop.hdds.security.x509.certificate.authority;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.certificates.CertificateSignRequest;
-import org.apache.hadoop.hdds.security.x509.exceptions.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.bouncycastle.cert.X509CertificateHolder;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
index 1b6f576..bfc0576 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/CertificateClient.java
@@ -91,6 +91,17 @@ public interface CertificateClient {
X509Certificate cert);
/**
+ * Verifies a digital Signature, given the signature and the certificate of
+ * the signer.
+ * @param data - Data in byte array.
+ * @param signature - Byte Array containing the signature.
+ * @param cert - Certificate of the Signer.
+ * @return true if verified, false if not.
+ */
+ boolean verifySignature(byte[] data, byte[] signature,
+ X509Certificate cert);
+
+ /**
* Returns a CSR builder that can be used to creates a Certificate sigining
* request.
*
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
index 2e1f9df..0e762a5 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/CertificateSignRequest.java
@@ -20,9 +20,9 @@ package org.apache.hadoop.hdds.security.x509.certificates;
import com.google.common.base.Preconditions;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
-import org.apache.hadoop.hdds.security.x509.exceptions.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
import org.apache.logging.log4j.util.Strings;
import org.bouncycastle.asn1.DEROctetString;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
index f221246..85fba9b 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/certificates/SelfSignedCertificate.java
@@ -22,9 +22,9 @@ package org.apache.hadoop.hdds.security.x509.certificates;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
-import org.apache.hadoop.hdds.security.x509.exceptions.SCMSecurityException;
import org.apache.hadoop.util.Time;
import org.apache.logging.log4j.util.Strings;
import org.bouncycastle.asn1.x500.X500Name;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/CertificateException.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/CertificateException.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/CertificateException.java
index 4d2dcfd..5a8bf86 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/CertificateException.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/CertificateException.java
@@ -19,6 +19,8 @@
package org.apache.hadoop.hdds.security.x509.exceptions;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+
/**
* Certificate Exceptions from the SCM Security layer.
*/
@@ -48,16 +50,4 @@ public class CertificateException extends SCMSecurityException {
public CertificateException(Throwable cause) {
super(cause);
}
-
- /**
- * Ctor.
- * @param message - Error Message
- * @param cause - Cause
- * @param enableSuppression - Enable suppression.
- * @param writableStackTrace - Writable stack trace.
- */
- public CertificateException(String message, Throwable cause,
- boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/SCMSecurityException.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/SCMSecurityException.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/SCMSecurityException.java
deleted file mode 100644
index e1f6296..0000000
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/x509/exceptions/SCMSecurityException.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.apache.hadoop.hdds.security.x509.exceptions;
-
-/**
- * Root Security Exception call for all Certificate related Execptions.
- */
-public class SCMSecurityException extends Exception {
-
- /**
- * Ctor.
- * @param message - Error Message.
- */
- public SCMSecurityException(String message) {
- super(message);
- }
-
- /**
- * Ctor.
- * @param message - Message.
- * @param cause - Actual cause.
- */
- public SCMSecurityException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Ctor.
- * @param cause - Base Exception.
- */
- public SCMSecurityException(Throwable cause) {
- super(cause);
- }
-
-
- /**
- * Ctor.
- * @param message - Error Message
- * @param cause - Cause
- * @param enableSuppression - Enable suppression.
- * @param writableStackTrace - Writable stack trace.
- */
- public SCMSecurityException(String message, Throwable cause,
- boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index e7c354c..dad3bfe 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -19,6 +19,11 @@
package org.apache.hadoop.ozone;
import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ratis.thirdparty.io.grpc.Context;
+import org.apache.ratis.thirdparty.io.grpc.Metadata;
+
+import static org.apache.ratis.thirdparty.io.grpc.Metadata.ASCII_STRING_MARSHALLER;
/**
* Set of constants used in Ozone implementation.
@@ -231,4 +236,13 @@ public final class OzoneConsts {
public static final String OM_METRICS_FILE = "omMetrics";
public static final String OM_METRICS_TEMP_FILE = OM_METRICS_FILE + ".tmp";
+ // GRPC block token metadata header and context key
+ public static final String OZONE_BLOCK_TOKEN = "blocktoken";
+ public static final Context.Key<UserGroupInformation> UGI_CTX_KEY =
+ Context.key("UGI");
+
+ public static final Metadata.Key<String> OBT_METADATA_KEY =
+ Metadata.Key.of(OZONE_BLOCK_TOKEN, ASCII_STRING_MARSHALLER);
+ public static final Metadata.Key<String> USER_METADATA_KEY =
+ Metadata.Key.of(OZONE_USER, ASCII_STRING_MARSHALLER);
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/main/proto/hdds.proto
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/proto/hdds.proto b/hadoop-hdds/common/src/main/proto/hdds.proto
index 8598fbf..53d11a5 100644
--- a/hadoop-hdds/common/src/main/proto/hdds.proto
+++ b/hadoop-hdds/common/src/main/proto/hdds.proto
@@ -210,7 +210,7 @@ message BlockTokenSecretProto {
required uint64 expiryDate = 3;
required string omCertSerialId = 4;
repeated AccessModeProto modes = 5;
-
+ required uint64 maxLength = 6;
}
message BlockID {
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/TestOzoneBlockTokenIdentifier.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/TestOzoneBlockTokenIdentifier.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/TestOzoneBlockTokenIdentifier.java
new file mode 100644
index 0000000..77a2cec
--- /dev/null
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/TestOzoneBlockTokenIdentifier.java
@@ -0,0 +1,313 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hdds.security.token;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.Time;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class for OzoneManagerDelegationToken.
+ */
+public class TestOzoneBlockTokenIdentifier {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(TestOzoneBlockTokenIdentifier.class);
+ private static final String BASEDIR = GenericTestUtils
+ .getTempPath(TestOzoneBlockTokenIdentifier.class.getSimpleName());
+ private static final String KEYSTORES_DIR =
+ new File(BASEDIR).getAbsolutePath();
+ private static long expiryTime;
+ private static KeyPair keyPair;
+ private static X509Certificate cert;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ File base = new File(BASEDIR);
+ FileUtil.fullyDelete(base);
+ base.mkdirs();
+ expiryTime = Time.monotonicNow() + 60 * 60 * 24;
+
+ // Create Ozone Master key pair.
+ keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
+ // Create Ozone Master certificate (SCM CA issued cert) and key store.
+ cert = KeyStoreTestUtil
+ .generateCertificate("CN=OzoneMaster", keyPair, 30, "SHA256withRSA");
+ }
+
+ @After
+ public void cleanUp() throws Exception {
+ // KeyStoreTestUtil.cleanupSSLConfig(KEYSTORES_DIR, sslConfsDir);
+ }
+
+ @Test
+ public void testSignToken() throws GeneralSecurityException, IOException {
+ String keystore = new File(KEYSTORES_DIR, "keystore.jks")
+ .getAbsolutePath();
+ String truststore = new File(KEYSTORES_DIR, "truststore.jks")
+ .getAbsolutePath();
+ String trustPassword = "trustPass";
+ String keyStorePassword = "keyStorePass";
+ String keyPassword = "keyPass";
+
+
+ KeyStoreTestUtil.createKeyStore(keystore, keyStorePassword, keyPassword,
+ "OzoneMaster", keyPair.getPrivate(), cert);
+
+ // Create trust store and put the certificate in the trust store
+ Map<String, X509Certificate> certs = Collections.singletonMap("server",
+ cert);
+ KeyStoreTestUtil.createTrustStore(truststore, trustPassword, certs);
+
+ // Sign the OzoneMaster Token with Ozone Master private key
+ PrivateKey privateKey = keyPair.getPrivate();
+ OzoneBlockTokenIdentifier tokenId = new OzoneBlockTokenIdentifier(
+ "testUser", "84940",
+ EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+ expiryTime, cert.getSerialNumber().toString(), 128L);
+ byte[] signedToken = signTokenAsymmetric(tokenId, privateKey);
+
+ // Verify a valid signed OzoneMaster Token with Ozone Master
+ // public key(certificate)
+ boolean isValidToken = verifyTokenAsymmetric(tokenId, signedToken, cert);
+ LOG.info("{} is {}", tokenId, isValidToken ? "valid." : "invalid.");
+
+ // Verify an invalid signed OzoneMaster Token with Ozone Master
+ // public key(certificate)
+ tokenId = new OzoneBlockTokenIdentifier("", "",
+ EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+ expiryTime, cert.getSerialNumber().toString(), 128L);
+ LOG.info("Unsigned token {} is {}", tokenId,
+ verifyTokenAsymmetric(tokenId, RandomUtils.nextBytes(128), cert));
+
+ }
+
+ @Test
+ public void testTokenSerialization() throws GeneralSecurityException,
+ IOException {
+ String keystore = new File(KEYSTORES_DIR, "keystore.jks")
+ .getAbsolutePath();
+ String truststore = new File(KEYSTORES_DIR, "truststore.jks")
+ .getAbsolutePath();
+ String trustPassword = "trustPass";
+ String keyStorePassword = "keyStorePass";
+ String keyPassword = "keyPass";
+ long maxLength = 128L;
+
+ KeyStoreTestUtil.createKeyStore(keystore, keyStorePassword, keyPassword,
+ "OzoneMaster", keyPair.getPrivate(), cert);
+
+ // Create trust store and put the certificate in the trust store
+ Map<String, X509Certificate> certs = Collections.singletonMap("server",
+ cert);
+ KeyStoreTestUtil.createTrustStore(truststore, trustPassword, certs);
+
+ // Sign the OzoneMaster Token with Ozone Master private key
+ PrivateKey privateKey = keyPair.getPrivate();
+ OzoneBlockTokenIdentifier tokenId = new OzoneBlockTokenIdentifier(
+ "testUser", "84940",
+ EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+ expiryTime, cert.getSerialNumber().toString(), maxLength);
+ byte[] signedToken = signTokenAsymmetric(tokenId, privateKey);
+
+
+ Token<BlockTokenIdentifier> token = new Token(tokenId.getBytes(),
+ signedToken, tokenId.getKind(), new Text("host:port"));
+
+ String encodeToUrlString = token.encodeToUrlString();
+
+ Token<BlockTokenIdentifier>decodedToken = new Token();
+ decodedToken.decodeFromUrlString(encodeToUrlString);
+
+ OzoneBlockTokenIdentifier decodedTokenId = new OzoneBlockTokenIdentifier();
+ decodedTokenId.readFields(new DataInputStream(
+ new ByteArrayInputStream(decodedToken.getIdentifier())));
+
+ Assert.assertEquals(decodedTokenId, tokenId);
+ Assert.assertEquals(decodedTokenId.getMaxLength(), maxLength);
+
+ // Verify a decoded signed Token with public key(certificate)
+ boolean isValidToken = verifyTokenAsymmetric(decodedTokenId, decodedToken
+ .getPassword(), cert);
+ LOG.info("{} is {}", tokenId, isValidToken ? "valid." : "invalid.");
+ }
+
+
+ public byte[] signTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
+ PrivateKey privateKey) throws NoSuchAlgorithmException,
+ InvalidKeyException, SignatureException {
+ Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+ rsaSignature.initSign(privateKey);
+ rsaSignature.update(tokenId.getBytes());
+ byte[] signature = rsaSignature.sign();
+ return signature;
+ }
+
+ public boolean verifyTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
+ byte[] signature, Certificate certificate) throws InvalidKeyException,
+ NoSuchAlgorithmException, SignatureException {
+ Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+ rsaSignature.initVerify(certificate);
+ rsaSignature.update(tokenId.getBytes());
+ boolean isValid = rsaSignature.verify(signature);
+ return isValid;
+ }
+
+ private byte[] signTokenSymmetric(OzoneBlockTokenIdentifier identifier,
+ Mac mac, SecretKey key) {
+ try {
+ mac.init(key);
+ } catch (InvalidKeyException ike) {
+ throw new IllegalArgumentException("Invalid key to HMAC computation",
+ ike);
+ }
+ return mac.doFinal(identifier.getBytes());
+ }
+
+ OzoneBlockTokenIdentifier generateTestToken() {
+ return new OzoneBlockTokenIdentifier(RandomStringUtils.randomAlphabetic(6),
+ RandomStringUtils.randomAlphabetic(5),
+ EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
+ expiryTime, cert.getSerialNumber().toString(), 1024768L);
+ }
+
+ @Test
+ public void testAsymmetricTokenPerf() throws NoSuchAlgorithmException,
+ CertificateEncodingException, NoSuchProviderException,
+ InvalidKeyException, SignatureException {
+ final int testTokenCount = 1000;
+ List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
+ List<byte[]> tokenPasswordAsym = new ArrayList<>();
+ for (int i = 0; i < testTokenCount; i++) {
+ tokenIds.add(generateTestToken());
+ }
+
+ KeyPair kp = KeyStoreTestUtil.generateKeyPair("RSA");
+
+ // Create Ozone Master certificate (SCM CA issued cert) and key store
+ X509Certificate certificate;
+ certificate = KeyStoreTestUtil.generateCertificate("CN=OzoneMaster",
+ kp, 30, "SHA256withRSA");
+
+ long startTime = Time.monotonicNowNanos();
+ for (int i = 0; i < testTokenCount; i++) {
+ tokenPasswordAsym.add(
+ signTokenAsymmetric(tokenIds.get(i), kp.getPrivate()));
+ }
+ long duration = Time.monotonicNowNanos() - startTime;
+ LOG.info("Average token sign time with HmacSha256(RSA/1024 key) is {} ns",
+ duration / testTokenCount);
+
+ startTime = Time.monotonicNowNanos();
+ for (int i = 0; i < testTokenCount; i++) {
+ verifyTokenAsymmetric(tokenIds.get(i), tokenPasswordAsym.get(i),
+ certificate);
+ }
+ duration = Time.monotonicNowNanos() - startTime;
+ LOG.info("Average token verify time with HmacSha256(RSA/1024 key) "
+ + "is {} ns", duration / testTokenCount);
+ }
+
+ @Test
+ public void testSymmetricTokenPerf() {
+ String hmacSHA1 = "HmacSHA1";
+ String hmacSHA256 = "HmacSHA256";
+
+ testSymmetricTokenPerfHelper(hmacSHA1, 64);
+ testSymmetricTokenPerfHelper(hmacSHA256, 1024);
+ }
+
+ public void testSymmetricTokenPerfHelper(String hmacAlgorithm, int keyLen) {
+ final int testTokenCount = 1000;
+ List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
+ List<byte[]> tokenPasswordSym = new ArrayList<>();
+ for (int i = 0; i < testTokenCount; i++) {
+ tokenIds.add(generateTestToken());
+ }
+
+ KeyGenerator keyGen;
+ try {
+ keyGen = KeyGenerator.getInstance(hmacAlgorithm);
+ keyGen.init(keyLen);
+ } catch (NoSuchAlgorithmException nsa) {
+ throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
+ " algorithm.");
+ }
+
+ Mac mac;
+ try {
+ mac = Mac.getInstance(hmacAlgorithm);
+ } catch (NoSuchAlgorithmException nsa) {
+ throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
+ " algorithm.");
+ }
+
+ SecretKey secretKey = keyGen.generateKey();
+
+ long startTime = Time.monotonicNowNanos();
+ for (int i = 0; i < testTokenCount; i++) {
+ tokenPasswordSym.add(
+ signTokenSymmetric(tokenIds.get(i), mac, secretKey));
+ }
+ long duration = Time.monotonicNowNanos() - startTime;
+ LOG.info("Average token sign time with {}({} symmetric key) is {} ns",
+ hmacAlgorithm, keyLen, duration / testTokenCount);
+ }
+
+ // TODO: verify certificate with a trust store
+ public boolean verifyCert(Certificate certificate) {
+ return true;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/package-info.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/package-info.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/package-info.java
new file mode 100644
index 0000000..d056655
--- /dev/null
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/token/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the block token related classes.
+ */
+package org.apache.hadoop.hdds.security.token;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java
index 0b9ef31..e8de1ea 100644
--- a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestCertificateSignRequest.java
@@ -18,8 +18,8 @@
package org.apache.hadoop.hdds.security.x509.certificates;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
-import org.apache.hadoop.hdds.security.x509.exceptions.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil;
import org.bouncycastle.asn1.ASN1Sequence;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
index 3a00ca0..16245a6 100644
--- a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/security/x509/certificates/TestRootCertificate.java
@@ -20,8 +20,8 @@
package org.apache.hadoop.hdds.security.x509.certificates;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
-import org.apache.hadoop.hdds.security.x509.exceptions.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.keys.HDDSKeyGenerator;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ServerCredentialInterceptor.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ServerCredentialInterceptor.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ServerCredentialInterceptor.java
new file mode 100644
index 0000000..968f0c8
--- /dev/null
+++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ServerCredentialInterceptor.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.container.common.transport.server;
+
+import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
+import org.apache.hadoop.hdds.security.token.TokenVerifier;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ratis.thirdparty.io.grpc.Context;
+import org.apache.ratis.thirdparty.io.grpc.Contexts;
+import org.apache.ratis.thirdparty.io.grpc.Metadata;
+import org.apache.ratis.thirdparty.io.grpc.ServerCall;
+import org.apache.ratis.thirdparty.io.grpc.ServerCallHandler;
+import org.apache.ratis.thirdparty.io.grpc.ServerInterceptor;
+import org.apache.ratis.thirdparty.io.grpc.Status;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OBT_METADATA_KEY;
+import static org.apache.hadoop.ozone.OzoneConsts.USER_METADATA_KEY;
+import static org.apache.hadoop.ozone.OzoneConsts.UGI_CTX_KEY;
+/**
+ * Grpc Server Interceptor for Ozone Block token.
+ */
+public class ServerCredentialInterceptor implements ServerInterceptor {
+
+
+ private static final ServerCall.Listener NOOP_LISTENER =
+ new ServerCall.Listener() {
+ };
+
+ private final TokenVerifier verifier;
+
+ ServerCredentialInterceptor(TokenVerifier verifier) {
+ this.verifier = verifier;
+ }
+
+ @Override
+ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
+ ServerCall<ReqT, RespT> call, Metadata headers,
+ ServerCallHandler<ReqT, RespT> next) {
+ String token = headers.get(OBT_METADATA_KEY);
+ String user = headers.get(USER_METADATA_KEY);
+ Context ctx = Context.current();
+ try {
+ UserGroupInformation ugi = verifier.verify(user, token);
+ if (ugi == null) {
+ call.close(Status.UNAUTHENTICATED.withDescription("Missing Block " +
+ "Token from headers when block token is required."), headers);
+ return NOOP_LISTENER;
+ } else {
+ ctx = ctx.withValue(UGI_CTX_KEY, ugi);
+ }
+ } catch (SCMSecurityException e) {
+ call.close(Status.UNAUTHENTICATED.withDescription(e.getMessage())
+ .withCause(e), headers);
+ return NOOP_LISTENER;
+ }
+ return Contexts.interceptCall(ctx, call, headers, next);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/XceiverServerGrpc.java
----------------------------------------------------------------------
diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/XceiverServerGrpc.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/XceiverServerGrpc.java
index 4ed9653..4ff0b61 100644
--- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/XceiverServerGrpc.java
+++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/XceiverServerGrpc.java
@@ -18,6 +18,7 @@
package org.apache.hadoop.ozone.container.common.transport.server;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
@@ -30,6 +31,9 @@ import org.apache.hadoop.hdds.protocol.proto
import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
import org.apache.hadoop.hdds.scm.container.common.helpers.
StorageContainerException;
+import org.apache.hadoop.hdds.security.token.BlockTokenVerifier;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.container.common.interfaces.ContainerDispatcher;
@@ -37,6 +41,7 @@ import org.apache.hadoop.ozone.container.common.interfaces.ContainerDispatcher;
import org.apache.ratis.thirdparty.io.grpc.BindableService;
import org.apache.ratis.thirdparty.io.grpc.Server;
import org.apache.ratis.thirdparty.io.grpc.ServerBuilder;
+import org.apache.ratis.thirdparty.io.grpc.ServerInterceptors;
import org.apache.ratis.thirdparty.io.grpc.netty.NettyServerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -92,8 +97,16 @@ public final class XceiverServerGrpc implements XceiverServerSpi {
DatanodeDetails.newPort(DatanodeDetails.Port.Name.STANDALONE, port));
NettyServerBuilder nettyServerBuilder =
((NettyServerBuilder) ServerBuilder.forPort(port))
- .maxInboundMessageSize(OzoneConsts.OZONE_SCM_CHUNK_MAX_SIZE)
- .addService(new GrpcXceiverService(dispatcher));
+ .maxInboundMessageSize(OzoneConsts.OZONE_SCM_CHUNK_MAX_SIZE);
+
+ // Populate UGI context via ServerCredentialInterceptor
+ SecurityConfig secConfig = new SecurityConfig(conf);
+ ServerCredentialInterceptor credInterceptor =
+ new ServerCredentialInterceptor(
+ new BlockTokenVerifier(secConfig, getCaClient()));
+ nettyServerBuilder.addService(ServerInterceptors.intercept(
+ new GrpcXceiverService(dispatcher), credInterceptor));
+
for (BindableService service : additionalServices) {
nettyServerBuilder.addService(service);
}
@@ -101,6 +114,12 @@ public final class XceiverServerGrpc implements XceiverServerSpi {
storageContainer = dispatcher;
}
+ @VisibleForTesting
+ public CertificateClient getCaClient() {
+ // TODO: instantiate CertificateClient
+ return null;
+ }
+
@Override
public int getIPCPort() {
return this.port;
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenIdentifier.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenIdentifier.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenIdentifier.java
deleted file mode 100644
index 92b6d16..0000000
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenIdentifier.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hadoop.ozone.security;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.hadoop.classification.InterfaceAudience;
-import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto;
-import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto;
-import org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.Builder;
-import org.apache.hadoop.io.Text;
-import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.token.TokenIdentifier;
-
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.util.EnumSet;
-
-/**
- * Block token identifier for Ozone/HDDS. Ozone block access token is similar
- * to HDFS block access token, which is meant to be lightweight and
- * short-lived. No need to renew or revoke a block access token. when a
- * cached block access token expires, the client simply get a new one.
- * Block access token should be cached only in memory and never write to disk.
- */
-@InterfaceAudience.Private
-public class OzoneBlockTokenIdentifier extends TokenIdentifier {
-
- static final Text KIND_NAME = new Text("HDDS_BLOCK_TOKEN");
- private long expiryDate;
- private String ownerId;
- private String blockId;
- private final EnumSet<AccessModeProto> modes;
- private final String omCertSerialId;
-
- public OzoneBlockTokenIdentifier(String ownerId, String blockId,
- EnumSet<AccessModeProto> modes, long expiryDate, String omCertSerialId) {
- this.ownerId = ownerId;
- this.blockId = blockId;
- this.expiryDate = expiryDate;
- this.modes = modes == null ? EnumSet.noneOf(AccessModeProto.class) : modes;
- this.omCertSerialId = omCertSerialId;
- }
-
- @Override
- public UserGroupInformation getUser() {
- if (this.getOwnerId() == null || "".equals(this.getOwnerId())) {
- return UserGroupInformation.createRemoteUser(blockId);
- }
- return UserGroupInformation.createRemoteUser(ownerId);
- }
-
- public long getExpiryDate() {
- return expiryDate;
- }
-
- public String getOwnerId() {
- return ownerId;
- }
-
- public String getBlockId() {
- return blockId;
- }
-
- public EnumSet<AccessModeProto> getAccessModes() {
- return modes;
- }
-
- public String getOmCertSerialId(){
- return omCertSerialId;
- }
-
- @Override
- public Text getKind() {
- return KIND_NAME;
- }
-
- @Override
- public String toString() {
- return "block_token_identifier (expiryDate=" + this.getExpiryDate()
- + ", ownerId=" + this.getOwnerId()
- + ", omCertSerialId=" + this.getOmCertSerialId()
- + ", blockId=" + this.getBlockId() + ", access modes="
- + this.getAccessModes() + ")";
- }
-
- static boolean isEqual(Object a, Object b) {
- return a == null ? b == null : a.equals(b);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
-
- if (obj instanceof OzoneBlockTokenIdentifier) {
- OzoneBlockTokenIdentifier that = (OzoneBlockTokenIdentifier) obj;
- return new EqualsBuilder()
- .append(this.expiryDate, that.expiryDate)
- .append(this.ownerId, that.ownerId)
- .append(this.blockId, that.blockId)
- .append(this.modes, that.modes)
- .append(this.omCertSerialId, that.omCertSerialId)
- .build();
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder(133, 567)
- .append(this.expiryDate)
- .append(this.blockId)
- .append(this.ownerId)
- .append(this.modes)
- .append(this.omCertSerialId)
- .build();
- }
-
- @Override
- public void readFields(DataInput in) throws IOException {
- final DataInputStream dis = (DataInputStream) in;
- if (!dis.markSupported()) {
- throw new IOException("Could not peek first byte.");
- }
- readFieldsProtobuf(dis);
- }
-
- @VisibleForTesting
- public static OzoneBlockTokenIdentifier readFieldsProtobuf(DataInput in)
- throws IOException {
- BlockTokenSecretProto tokenPtoto =
- BlockTokenSecretProto.parseFrom((DataInputStream) in);
- return new OzoneBlockTokenIdentifier(tokenPtoto.getOwnerId(),
- tokenPtoto.getBlockId(), EnumSet.copyOf(tokenPtoto.getModesList()),
- tokenPtoto.getExpiryDate(), tokenPtoto.getOmCertSerialId());
- }
-
- @Override
- public void write(DataOutput out) throws IOException {
- writeProtobuf(out);
- }
-
- @VisibleForTesting
- void writeProtobuf(DataOutput out) throws IOException {
- Builder builder = BlockTokenSecretProto.newBuilder()
- .setBlockId(this.getBlockId())
- .setOwnerId(this.getOwnerId())
- .setOmCertSerialId(this.getOmCertSerialId())
- .setExpiryDate(this.getExpiryDate());
- // Add access mode allowed
- for (AccessModeProto mode : this.getAccessModes()) {
- builder.addModes(AccessModeProto.valueOf(mode.name()));
- }
- out.write(builder.build().toByteArray());
- }
-}
-
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a3b78ec1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSelector.java
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSelector.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSelector.java
deleted file mode 100644
index 086e19b..0000000
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneBlockTokenSelector.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.ozone.security;
-
-import org.apache.hadoop.classification.InterfaceAudience;
-import org.apache.hadoop.io.Text;
-import org.apache.hadoop.security.token.Token;
-import org.apache.hadoop.security.token.TokenIdentifier;
-import org.apache.hadoop.security.token.TokenSelector;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collection;
-
-/**
- * A block token selector for Ozone.
- */
-@InterfaceAudience.Private
-public class OzoneBlockTokenSelector implements
- TokenSelector<OzoneBlockTokenIdentifier> {
-
- private static final Logger LOG = LoggerFactory
- .getLogger(OzoneBlockTokenSelector.class);
-
- @Override
- @SuppressWarnings("unchecked")
- public Token<OzoneBlockTokenIdentifier> selectToken(Text service,
- Collection<Token<? extends TokenIdentifier>> tokens) {
- if (service == null) {
- return null;
- }
- for (Token<? extends TokenIdentifier> token : tokens) {
- if (OzoneBlockTokenIdentifier.KIND_NAME.equals(token.getKind())) {
- LOG.trace("Getting token for service:{}", service);
- return (Token<OzoneBlockTokenIdentifier>) token;
- }
- }
- return null;
- }
-}
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org