Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ public ManagedSecretKey getCurrentSecretKey() {
"SecretKey client must have been initialized already.");
}

@Override
public void refetchSecretKey() {
// pass duration as ZERO to force a refresh.
checkAndRefresh(Duration.ZERO);
}

@Override
public void start(ConfigurationSource conf) throws IOException {
final ManagedSecretKey initialKey =
Expand Down Expand Up @@ -103,7 +109,7 @@ private void scheduleSecretKeyPoller(ConfigurationSource conf,
TimeUnit.MILLISECONDS);
}

private void checkAndRefresh(Duration rotateDuration) {
private synchronized void checkAndRefresh(Duration rotateDuration) {
ManagedSecretKey current = cache.get();
Instant nextRotate = current.getCreationTime().plus(rotateDuration);
// when the current key passes the rotation cycle, fetch the next one
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@ default void start(ConfigurationSource conf) throws IOException {
*/
default void stop() {
}

default void refetchSecretKey() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public static boolean isReadOnly(
case TenantListUser:
case ListSnapshot:
case EchoRPC:
case RefetchSecretKey:
case RangerBGSync:
// RangerBGSync is a read operation in the sense that it doesn't directly
// write to OM DB. And therefore it doesn't need a OMClientRequest.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

import org.apache.hadoop.hdds.scm.container.common.helpers.ExcludeList;
import org.apache.hadoop.ozone.OzoneAcl;
Expand Down Expand Up @@ -995,4 +996,6 @@ EchoRPCResponse echoRPCReq(byte[] payloadReq,
*/
boolean recoverLease(String volumeName, String bucketName,
String keyName) throws IOException;

UUID refetchSecretKey() throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -155,6 +156,8 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RecoverLeaseResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RecoverTrashRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RecoverTrashResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RefetchSecretKeyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RefetchSecretKeyResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RemoveAclRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RemoveAclResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeysArgs;
Expand Down Expand Up @@ -202,6 +205,7 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString;
import org.apache.hadoop.util.ProtobufUtils;

import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
Expand Down Expand Up @@ -1355,6 +1359,19 @@ public S3VolumeContext getS3VolumeContext() throws IOException {
return S3VolumeContext.fromProtobuf(resp);
}

@Override
public UUID refetchSecretKey() throws IOException {
final RefetchSecretKeyRequest.Builder requestBuilder =
RefetchSecretKeyRequest.newBuilder();
final OMRequest omRequest = createOMRequest(Type.RefetchSecretKey)
.setRefetchSecretKeyRequest(requestBuilder)
.build();
final OMResponse omResponse = submitRequest(omRequest);
final RefetchSecretKeyResponse resp =
handleError(omResponse).getRefetchSecretKeyResponse();
return ProtobufUtils.fromProtobuf(resp.getId());
}

/**
* Return the proxy object underlying this protocol translator.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,6 @@ public void blockTokenFailsOnWrongPassword() throws Exception {
assertExceptionContains("Invalid token for user", ex);
}


private UUID extractSecretKeyId(OmKeyInfo keyInfo) throws IOException {
OmKeyLocationInfo locationInfo =
keyInfo.getKeyLocationVersions().get(0).getLocationList().get(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* 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;

import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.cli.OzoneAdmin;
import org.apache.hadoop.hdds.conf.DefaultConfigManager;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.scm.ScmConfig;
import org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig;
import org.apache.hadoop.hdds.security.symmetric.SecretKeyManager;
import org.apache.hadoop.hdds.utils.IOUtils;
import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ozone.test.GenericTestUtils;
import org.apache.ratis.util.ExitUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
import static org.apache.hadoop.hdds.DFSConfigKeysLegacy.DFS_DATANODE_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.hdds.DFSConfigKeysLegacy.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_BLOCK_TOKEN_ENABLED;
import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_CONTAINER_TOKEN_ENABLED;
import static org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY;
import static org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig.ConfigStrings.HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig.ConfigStrings.HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Integration test class to verify block token CLI commands functionality in a
* secure cluster.
*/
@InterfaceAudience.Private
public final class TestBlockTokensCLI {
private static final Logger LOG = LoggerFactory
.getLogger(TestBlockTokensCLI.class);

@Rule
public Timeout timeout = Timeout.seconds(180);

private static MiniKdc miniKdc;
private static OzoneAdmin ozoneAdmin;
private static OzoneConfiguration conf;
private static File workDir;
private static File ozoneKeytab;
private static File spnegoKeytab;
private static String host;
private static String clusterId;
private static String scmId;
private static String omServiceId;
private static String scmServiceId;
private static MiniOzoneHAClusterImpl cluster;
private static OzoneClient client;

@BeforeClass
public static void init() throws Exception {
conf = new OzoneConfiguration();
conf.set(OZONE_SCM_CLIENT_ADDRESS_KEY, "localhost");

ExitUtils.disableSystemExit();

workDir =
GenericTestUtils.getTestDir(TestBlockTokens.class.getSimpleName());
clusterId = UUID.randomUUID().toString();
scmId = UUID.randomUUID().toString();
omServiceId = "om-service-test";
scmServiceId = "scm-service-test";

startMiniKdc();
setSecureConfig();
createCredentialsInKDC();
setSecretKeysConfig();
startCluster();
client = cluster.newClient();
ozoneAdmin = new OzoneAdmin(conf);
}

@AfterClass
public static void stop() {
miniKdc.stop();
IOUtils.close(LOG, client);
if (cluster != null) {
cluster.stop();
}
DefaultConfigManager.clearDefaultConfigs();
}

private SecretKeyManager getScmSecretKeyManager() {
return cluster.getActiveSCM().getSecretKeyManager();
}

private static void setSecretKeysConfig() {
// enable tokens
conf.setBoolean(HDDS_BLOCK_TOKEN_ENABLED, true);
conf.setBoolean(HDDS_CONTAINER_TOKEN_ENABLED, true);
}

private static void createCredentialsInKDC() throws Exception {
ScmConfig scmConfig = conf.getObject(ScmConfig.class);
SCMHTTPServerConfig httpServerConfig =
conf.getObject(SCMHTTPServerConfig.class);
createPrincipal(ozoneKeytab, scmConfig.getKerberosPrincipal());
createPrincipal(spnegoKeytab, httpServerConfig.getKerberosPrincipal());
}

private static void createPrincipal(File keytab, String... principal)
throws Exception {
miniKdc.createPrincipal(keytab, principal);
}

private static void startMiniKdc() throws Exception {
Properties securityProperties = MiniKdc.createConf();
miniKdc = new MiniKdc(securityProperties, workDir);
miniKdc.start();
}

private static void setSecureConfig() throws IOException {
conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
host = InetAddress.getLocalHost().getCanonicalHostName()
.toLowerCase();

conf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS.name());

String curUser = UserGroupInformation.getCurrentUser().getUserName();
conf.set(OZONE_ADMINISTRATORS, curUser);

String realm = miniKdc.getRealm();
String hostAndRealm = host + "@" + realm;
conf.set(HDDS_SCM_KERBEROS_PRINCIPAL_KEY, "scm/" + hostAndRealm);
conf.set(HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_SCM/" + hostAndRealm);
conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY, "scm/" + hostAndRealm);
conf.set(OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_OM/" + hostAndRealm);
conf.set(DFS_DATANODE_KERBEROS_PRINCIPAL_KEY, "scm/" + hostAndRealm);

ozoneKeytab = new File(workDir, "scm.keytab");
spnegoKeytab = new File(workDir, "http.keytab");

conf.set(HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY,
ozoneKeytab.getAbsolutePath());
conf.set(HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY,
spnegoKeytab.getAbsolutePath());
conf.set(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY,
ozoneKeytab.getAbsolutePath());
conf.set(OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE,
spnegoKeytab.getAbsolutePath());
conf.set(DFS_DATANODE_KERBEROS_KEYTAB_FILE_KEY,
ozoneKeytab.getAbsolutePath());
}

@Test
public void testFetchKeyOMAdminCommand() throws UnsupportedEncodingException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream, true, "UTF-8");
System.setOut(printStream);

String[] args =
new String[]{"om", "fetch-key", "--service-id=" + omServiceId};
ozoneAdmin.execute(args);

String actualOutput = outputStream.toString("UTF-8");
System.setOut(System.out);

String actualUUID = testFetchKeyOMAdminCommandUtil(actualOutput);
String expectedUUID =
getScmSecretKeyManager().getCurrentSecretKey().getId().toString();
assertEquals(expectedUUID, actualUUID);
}

private String testFetchKeyOMAdminCommandUtil(String output) {
// Extract the current secret key id from the output
String[] lines = output.split(System.lineSeparator());
for (String line : lines) {
if (line.startsWith("Current Secret Key ID: ")) {
return line.substring("Current Secret Key ID: ".length()).trim();
}
}
return null;
}

private static void startCluster()
throws IOException, TimeoutException, InterruptedException {
OzoneManager.setTestSecureOmFlag(true);
MiniOzoneCluster.Builder builder = MiniOzoneCluster.newHABuilder(conf)
.setClusterId(clusterId)
.setSCMServiceId(scmServiceId)
.setOMServiceId(omServiceId)
.setScmId(scmId)
.setNumDatanodes(3)
.setNumOfStorageContainerManagers(3)
.setNumOfOzoneManagers(1);

cluster = (MiniOzoneHAClusterImpl) builder.build();
cluster.waitForClusterToBeReady();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ enum Type {
TransferLeadership = 117;
SnapshotPurge = 118;
RecoverLease = 119;
RefetchSecretKey = 120;
}

message OMRequest {
Expand Down Expand Up @@ -252,6 +253,7 @@ message OMRequest {
optional SnapshotPurgeRequest SnapshotPurgeRequest = 118;

optional RecoverLeaseRequest RecoverLeaseRequest = 119;
optional RefetchSecretKeyRequest RefetchSecretKeyRequest = 120;
}

message OMResponse {
Expand Down Expand Up @@ -362,6 +364,7 @@ message OMResponse {
optional hdds.TransferLeadershipResponseProto TransferOmLeadershipResponse = 117;
optional SnapshotPurgeResponse SnapshotPurgeResponse = 118;
optional RecoverLeaseResponse RecoverLeaseResponse = 119;
optional RefetchSecretKeyResponse RefetchSecretKeyResponse = 120;
}

enum Status {
Expand Down Expand Up @@ -589,6 +592,14 @@ message SetVolumePropertyResponse {
optional bool response = 1;
}

message RefetchSecretKeyRequest {

}

message RefetchSecretKeyResponse {
optional hdds.UUID id = 1;
}

/**
* Checks if the user has specified permissions for the volume
*/
Expand Down
Loading