Skip to content

Commit

Permalink
HDDS-7830. SCM API for OM and Datanode to get secret keys (apache#4345)
Browse files Browse the repository at this point in the history
  • Loading branch information
duongkame committed Jun 8, 2023
1 parent 4ef4c79 commit e2709f8
Show file tree
Hide file tree
Showing 17 changed files with 672 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public enum ErrorCode {
MISSING_BLOCK_TOKEN,
BLOCK_TOKEN_VERIFICATION_FAILED,
GET_ROOT_CA_CERT_FAILED,
NOT_A_PRIMARY_SCM
NOT_A_PRIMARY_SCM,
SECRET_KEY_NOT_ENABLED,
SECRET_KEY_NOT_INITIALIZED
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.util;

import org.apache.hadoop.hdds.protocol.proto.HddsProtos;

import java.util.UUID;

/**
* Contains utilities to ease common protobuf to java object conversions.
*/
public final class ProtobufUtils {
private ProtobufUtils() {
}

public static HddsProtos.UUID toProtobuf(UUID uuid) {
return HddsProtos.UUID.newBuilder()
.setMostSigBits(uuid.getMostSignificantBits())
.setLeastSigBits(uuid.getLeastSignificantBits())
.build();
}

public static UUID fromProtobuf(HddsProtos.UUID proto) {
return new UUID(proto.getMostSigBits(), proto.getLeastSigBits());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.utils;

import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.junit.jupiter.api.Test;

import java.util.UUID;

import static org.apache.hadoop.util.ProtobufUtils.fromProtobuf;
import static org.apache.hadoop.util.ProtobufUtils.toProtobuf;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Test-cases for {@link org.apache.hadoop.util.ProtobufUtils}.
*/
public class TestProtobufUtils {
@Test
public void testUuidToProtobuf() {
UUID object = UUID.randomUUID();
HddsProtos.UUID protobuf = toProtobuf(object);
assertEquals(object.getLeastSignificantBits(), protobuf.getLeastSigBits());
assertEquals(object.getMostSignificantBits(), protobuf.getMostSigBits());
}

@Test
public void testUuidConversion() {
UUID original = UUID.randomUUID();
HddsProtos.UUID protobuf = toProtobuf(original);
UUID deserialized = fromProtobuf(protobuf);
assertEquals(original, deserialized);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.IOException;
import java.util.List;
import java.util.UUID;

import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
Expand All @@ -26,6 +27,7 @@
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ScmNodeDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeDetailsProto;
import org.apache.hadoop.hdds.scm.ScmConfig;
import org.apache.hadoop.hdds.security.symmetric.ManagedSecretKey;
import org.apache.hadoop.hdds.security.x509.crl.CRLInfo;
import org.apache.hadoop.security.KerberosInfo;

Expand Down Expand Up @@ -170,4 +172,25 @@ long revokeCertificates(List<String> certIds, int reason, long revocationTime)
*/
String getCertificate(NodeDetailsProto nodeDetails,
String certSignReq) throws IOException;


/**
* Get the current SecretKey that is used for signing tokens.
* @return ManagedSecretKey
*/
ManagedSecretKey getCurrentSecretKey() throws IOException;

/**
* Get a particular SecretKey by ID.
*
* @param id the id to get SecretKey.
* @return ManagedSecretKey.
*/
ManagedSecretKey getSecretKey(UUID id) throws IOException;

/**
* Get all the non-expired SecretKey managed by SCM.
* @return list of ManagedSecretKey.
*/
List<ManagedSecretKey> getAllSecretKeys() throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
Expand All @@ -40,6 +42,8 @@
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetOMCertRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetSecretKeyRequest;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetSecretKeyResponse;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMListCACertificateRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetLatestCrlIdRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMListCertificateRequestProto;
Expand All @@ -51,6 +55,7 @@
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.Type;
import org.apache.hadoop.hdds.scm.proxy.SCMSecurityProtocolFailoverProxyProvider;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.symmetric.ManagedSecretKey;
import org.apache.hadoop.hdds.security.x509.crl.CRLInfo;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.io.retry.RetryProxy;
Expand Down Expand Up @@ -182,6 +187,38 @@ public String getCertificate(NodeDetailsProto nodeDetails,
.getX509Certificate();
}

@Override
public ManagedSecretKey getCurrentSecretKey() throws IOException {
SCMSecurityProtocolProtos.ManagedSecretKey secretKeyProto =
submitRequest(Type.GetCurrentSecretKey, builder -> {
}).getCurrentSecretKeyResponseProto().getSecretKey();
return ManagedSecretKey.fromProtobuf(secretKeyProto);
}

@Override
public ManagedSecretKey getSecretKey(UUID id) throws IOException {
SCMGetSecretKeyRequest request = SCMGetSecretKeyRequest.newBuilder()
.setSecretKeyId(HddsProtos.UUID.newBuilder()
.setMostSigBits(id.getMostSignificantBits())
.setLeastSigBits(id.getLeastSignificantBits())).build();
SCMGetSecretKeyResponse response = submitRequest(Type.GetSecretKey,
builder -> builder.setGetSecretKeyRequest(request))
.getGetSecretKeyResponseProto();

return response.hasSecretKey() ?
ManagedSecretKey.fromProtobuf(response.getSecretKey()) : null;
}

@Override
public List<ManagedSecretKey> getAllSecretKeys() throws IOException {
List<SCMSecurityProtocolProtos.ManagedSecretKey> secretKeysList =
submitRequest(Type.GetAllSecretKeys, builder -> {
}).getSecretKeysListResponseProto().getSecretKeysList();
return secretKeysList.stream()
.map(ManagedSecretKey::fromProtobuf)
.collect(Collectors.toList());
}

/**
* Get signed certificate for SCM node.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
package org.apache.hadoop.hdds.security.symmetric;

import com.google.protobuf.ByteString;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos;
import org.apache.hadoop.util.ProtobufUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
Expand Down Expand Up @@ -92,13 +92,8 @@ public String toString() {
* @return the protobuf message to deserialize this object.
*/
public SCMSecurityProtocolProtos.ManagedSecretKey toProtobuf() {
HddsProtos.UUID uuid = HddsProtos.UUID.newBuilder()
.setMostSigBits(this.id.getMostSignificantBits())
.setLeastSigBits(this.id.getLeastSignificantBits())
.build();

return SCMSecurityProtocolProtos.ManagedSecretKey.newBuilder()
.setId(uuid)
.setId(ProtobufUtils.toProtobuf(id))
.setCreationTime(this.creationTime.toEpochMilli())
.setExpiryTime(this.expiryTime.toEpochMilli())
.setAlgorithm(this.secretKey.getAlgorithm())
Expand All @@ -111,8 +106,7 @@ public SCMSecurityProtocolProtos.ManagedSecretKey toProtobuf() {
*/
public static ManagedSecretKey fromProtobuf(
SCMSecurityProtocolProtos.ManagedSecretKey message) {
UUID id = new UUID(message.getId().getMostSigBits(),
message.getId().getLeastSigBits());
UUID id = ProtobufUtils.fromProtobuf(message.getId());
Instant creationTime = Instant.ofEpochMilli(message.getCreationTime());
Instant expiryTime = Instant.ofEpochMilli(message.getExpiryTime());
SecretKey secretKey = new SecretKeySpec(message.getEncoded().toByteArray(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ public synchronized boolean checkAndRotate() throws TimeoutException {
return false;
}

public ManagedSecretKey getCurrentKey() {
return state.getCurrentKey();
}

public ManagedSecretKey getKey(UUID id) {
return state.getKey(id);
}

public List<ManagedSecretKey> getSortedKeys() {
return state.getSortedKeys();
}

private boolean shouldRotate(ManagedSecretKey currentKey) {
Duration established = between(currentKey.getCreationTime(), Instant.now());
return established.compareTo(rotationDuration) >= 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.hadoop.hdds.scm.metadata.Replicate;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

/**
Expand All @@ -36,6 +37,8 @@ public interface SecretKeyState {
*/
ManagedSecretKey getCurrentKey();

ManagedSecretKey getKey(UUID id);

/**
* Get the keys that managed by this manager.
* The returned keys are sorted by creation time, in the order of latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;

import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

/**
* Default implementation of {@link SecretKeyState}.
Expand All @@ -41,6 +45,7 @@ public final class SecretKeyStateImpl implements SecretKeyState {

private List<ManagedSecretKey> sortedKeys;
private ManagedSecretKey currentKey;
private Map<UUID, ManagedSecretKey> keyById;

private final SecretKeyStore keyStore;

Expand All @@ -66,6 +71,20 @@ public ManagedSecretKey getCurrentKey() {
}
}

@Override
public ManagedSecretKey getKey(UUID id) {
lock.readLock().lock();
try {
// Return null if not initialized yet.
if (keyById == null) {
return null;
}
return keyById.get(id);
} finally {
lock.readLock().unlock();
}
}

/**
* Get the keys that managed by this manager.
* The returned keys are sorted by creation time, in the order of latest
Expand Down Expand Up @@ -98,6 +117,10 @@ public void updateKeys(List<ManagedSecretKey> newKeys) {
.collect(toList())
);
currentKey = sortedKeys.get(0);
keyById = newKeys.stream().collect(toMap(
ManagedSecretKey::getId,
Function.identity()
));
LOG.info("Current key updated {}", currentKey);
keyStore.save(sortedKeys);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ message SCMSecurityRequest {
optional SCMGetLatestCrlIdRequestProto getLatestCrlIdRequest = 11;
optional SCMRevokeCertificatesRequestProto revokeCertificatesRequest = 12;
optional SCMGetCertRequestProto getCertRequest = 13;
optional SCMGetSecretKeyRequest getSecretKeyRequest = 14;
}

message SCMSecurityResponse {
Expand All @@ -81,6 +82,12 @@ message SCMSecurityResponse {

optional SCMRevokeCertificatesResponseProto revokeCertificatesResponseProto = 10;

optional SCMGetCurrentSecretKeyResponse currentSecretKeyResponseProto = 11;

optional SCMGetSecretKeyResponse getSecretKeyResponseProto = 12;

optional SCMSecretKeysListResponse secretKeysListResponseProto = 13;

}

enum Type {
Expand All @@ -96,6 +103,9 @@ enum Type {
GetLatestCrlId = 10;
RevokeCertificates = 11;
GetCert = 12;
GetCurrentSecretKey = 13;
GetSecretKey = 14;
GetAllSecretKeys = 15;
}

enum Status {
Expand All @@ -116,6 +126,8 @@ enum Status {
GET_ROOT_CA_CERTIFICATE_FAILED = 15;
NOT_A_PRIMARY_SCM = 16;
REVOKE_CERTIFICATE_FAILED = 17;
SECRET_KEY_NOT_ENABLED = 18;
SECRET_KEY_NOT_INITIALIZED = 19;
}
/**
* This message is send by data node to prove its identity and get an SCM
Expand Down Expand Up @@ -258,3 +270,21 @@ message ManagedSecretKey {
required bytes encoded = 5;
}

message SCMGetSecretKeyRequest {
required UUID secretKeyId = 1;
}

message SCMGetCurrentSecretKeyResponse {
required ManagedSecretKey secretKey = 1;
}

message SCMGetSecretKeyResponse {
optional ManagedSecretKey secretKey = 1;
}

message SCMSecretKeysListResponse {
repeated ManagedSecretKey secretKeys = 1;
}



Loading

0 comments on commit e2709f8

Please sign in to comment.